hexchat/src/fe-gtk/fkeys.c

1830 lines
46 KiB
C
Raw Permalink Normal View History

2011-02-23 22:14:30 -05:00
/* X-Chat
* Copyright (C) 1998 Peter Zelezny.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
2012-12-23 14:36:54 -05:00
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
2011-02-23 22:14:30 -05:00
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>
#include <ctype.h>
2012-07-21 08:26:19 -04:00
#ifdef WIN32
#include <io.h>
#else
#include <unistd.h>
#endif
#define GLIB_DISABLE_DEPRECATION_WARNINGS
2011-02-23 22:14:30 -05:00
#include "fe-gtk.h"
2012-10-24 15:33:02 -04:00
#include "../common/hexchat.h"
#include "../common/hexchatc.h"
2011-02-23 22:14:30 -05:00
#include "../common/cfgfiles.h"
#include "../common/fe.h"
#include "../common/userlist.h"
#include "../common/outbound.h"
#include "../common/util.h"
#include "../common/text.h"
#include "../common/plugin.h"
#include "../common/typedef.h"
2011-02-23 22:14:30 -05:00
#include <gdk/gdkkeysyms.h>
#include "gtkutil.h"
#include "menu.h"
#include "xtext.h"
#include "palette.h"
#include "maingui.h"
#include "textgui.h"
#include "fkeys.h"
static void replace_handle (GtkWidget * wid);
void key_action_tab_clean (void);
/***************** Key Binding Code ******************/
/* NOTES:
To add a new action:
1) inc KEY_MAX_ACTIONS
2) write the function at the bottom of this file (with all the others)
FIXME: Write about calling and returning
3) Add it to key_actions
--AGL
*/
/* Remember that the *number* of actions is this *plus* 1 --AGL */
#define KEY_MAX_ACTIONS 14
struct key_binding
{
guint keyval; /* keyval from gdk */
GdkModifierType mod; /* Modifier, always ran through key_modifier_get_valid() */
2011-02-23 22:14:30 -05:00
int action; /* Index into key_actions */
char *data1, *data2; /* Pointers to strings, these must be freed */
};
struct key_action
{
int (*handler) (GtkWidget * wid, GdkEventKey * evt, char *d1, char *d2,
struct session * sess);
char *name;
char *help;
};
struct gcomp_data
{
char data[CHANLEN];
int elen;
};
static int key_load_kbs ();
static int key_save_kbs ();
2011-02-23 22:14:30 -05:00
static int key_action_handle_command (GtkWidget * wid, GdkEventKey * evt,
char *d1, char *d2,
struct session *sess);
static int key_action_page_switch (GtkWidget * wid, GdkEventKey * evt,
char *d1, char *d2, struct session *sess);
int key_action_insert (GtkWidget * wid, GdkEventKey * evt, char *d1, char *d2,
struct session *sess);
static int key_action_scroll_page (GtkWidget * wid, GdkEventKey * evt,
char *d1, char *d2, struct session *sess);
static int key_action_set_buffer (GtkWidget * wid, GdkEventKey * evt,
char *d1, char *d2, struct session *sess);
static int key_action_history_up (GtkWidget * wid, GdkEventKey * evt,
char *d1, char *d2, struct session *sess);
static int key_action_history_down (GtkWidget * wid, GdkEventKey * evt,
char *d1, char *d2, struct session *sess);
static int key_action_tab_comp (GtkWidget * wid, GdkEventKey * evt, char *d1,
char *d2, struct session *sess);
static int key_action_comp_chng (GtkWidget * wid, GdkEventKey * evt, char *d1,
char *d2, struct session *sess);
static int key_action_replace (GtkWidget * wid, GdkEventKey * evt, char *d1,
char *d2, struct session *sess);
static int key_action_move_tab_left (GtkWidget * wid, GdkEventKey * evt,
char *d1, char *d2,
struct session *sess);
static int key_action_move_tab_right (GtkWidget * wid, GdkEventKey * evt,
char *d1, char *d2,
struct session *sess);
static int key_action_move_tab_family_left (GtkWidget * wid, GdkEventKey * evt,
char *d1, char *d2,
struct session *sess);
static int key_action_move_tab_family_right (GtkWidget * wid, GdkEventKey * evt,
char *d1, char *d2,
struct session *sess);
static int key_action_put_history (GtkWidget * wid, GdkEventKey * evt,
char *d1, char *d2,
struct session *sess);
static GSList *keybind_list = NULL;
2011-02-23 22:14:30 -05:00
static const struct key_action key_actions[KEY_MAX_ACTIONS + 1] = {
{key_action_handle_command, "Run Command",
2013-04-04 11:11:05 -04:00
N_("The \002Run Command\002 action runs the data in Data 1 as if it had been typed into the entry box where you pressed the key sequence. Thus it can contain text (which will be sent to the channel/person), commands or user commands. When run all \002\\n\002 characters in Data 1 are used to deliminate separate commands so it is possible to run more than one command. If you want a \002\\\002 in the actual text run then enter \002\\\\\002")},
2011-02-23 22:14:30 -05:00
{key_action_page_switch, "Change Page",
N_("The \002Change Page\002 command switches between pages in the notebook. Set Data 1 to the page you want to switch to. If Data 2 is set to anything then the switch will be relative to the current position. Set Data 1 to auto to switch to the page with the most recent and important activity (queries first, then channels with hilight, channels with dialogue, channels with other data)")},
2011-02-23 22:14:30 -05:00
{key_action_insert, "Insert in Buffer",
N_("The \002Insert in Buffer\002 command will insert the contents of Data 1 into the entry where the key sequence was pressed at the current cursor position")},
{key_action_scroll_page, "Scroll Page",
N_("The \002Scroll Page\002 command scrolls the text widget up or down one page or one line. Set Data 1 to either Top, Bottom, Up, Down, +1 or -1.")},
2011-02-23 22:14:30 -05:00
{key_action_set_buffer, "Set Buffer",
N_("The \002Set Buffer\002 command sets the entry where the key sequence was entered to the contents of Data 1")},
{key_action_history_up, "Last Command",
N_("The \002Last Command\002 command sets the entry to contain the last command entered - the same as pressing up in a shell")},
{key_action_history_down, "Next Command",
N_("The \002Next Command\002 command sets the entry to contain the next command entered - the same as pressing down in a shell")},
{key_action_tab_comp, "Complete nick/command",
N_("This command changes the text in the entry to finish an incomplete nickname or command. If Data 1 is set then double-tabbing in a string will select the last nick, not the next")},
{key_action_comp_chng, "Change Selected Nick",
N_("This command scrolls up and down through the list of nicks. If Data 1 is set to anything it will scroll up, else it scrolls down")},
{key_action_replace, "Check For Replace",
N_("This command checks the last word entered in the entry against the replace list and replaces it if it finds a match")},
{key_action_move_tab_left, "Move front tab left",
N_("This command moves the front tab left by one")},
{key_action_move_tab_right, "Move front tab right",
N_("This command moves the front tab right by one")},
{key_action_move_tab_family_left, "Move tab family left",
N_("This command moves the current tab family to the left")},
{key_action_move_tab_family_right, "Move tab family right",
N_("This command moves the current tab family to the right")},
{key_action_put_history, "Push input line into history",
N_("Push input line into history but doesn't send to server")},
};
#define default_kb_cfg \
"ACCEL=<Primary>Page_Up\nChange Page\nD1:-1\nD2:Relative\n\n"\
"ACCEL=<Primary>Page_Down\nChange Page\nD1:1\nD2:Relative\n\n"\
"ACCEL=<Alt>9\nChange Page\nD1:9\nD2!\n\n"\
"ACCEL=<Alt>8\nChange Page\nD1:8\nD2!\n\n"\
"ACCEL=<Alt>7\nChange Page\nD1:7\nD2!\n\n"\
"ACCEL=<Alt>6\nChange Page\nD1:6\nD2!\n\n"\
"ACCEL=<Alt>5\nChange Page\nD1:5\nD2!\n\n"\
"ACCEL=<Alt>4\nChange Page\nD1:4\nD2!\n\n"\
"ACCEL=<Alt>3\nChange Page\nD1:3\nD2!\n\n"\
"ACCEL=<Alt>2\nChange Page\nD1:2\nD2!\n\n"\
"ACCEL=<Alt>1\nChange Page\nD1:1\nD2!\n\n"\
"ACCEL=<Alt>grave\nChange Page\nD1:auto\nD2!\n\n"\
"ACCEL=<Primary>o\nInsert in Buffer\nD1:\nD2!\n\n"\
"ACCEL=<Primary>b\nInsert in Buffer\nD1:\nD2!\n\n"\
"ACCEL=<Primary>k\nInsert in Buffer\nD1:\nD2!\n\n"\
"ACCEL=<Primary>i\nInsert in Buffer\nD1:\nD2!\n\n"\
"ACCEL=<Primary>u\nInsert in Buffer\nD1:\nD2!\n\n"\
"ACCEL=<Shift>Page_Down\nChange Selected Nick\nD1!\nD2!\n\n"\
"ACCEL=<Shift>Page_Up\nChange Selected Nick\nD1:Up\nD2!\n\n"\
"ACCEL=Page_Down\nScroll Page\nD1:Down\nD2!\n\n"\
"ACCEL=<Primary>Home\nScroll Page\nD1:Top\nD2!\n\n"\
"ACCEL=<Primary>End\nScroll Page\nD1:Bottom\nD2!\n\n"\
"ACCEL=Page_Up\nScroll Page\nD1:Up\nD2!\n\n"\
"ACCEL=<Shift>Down\nScroll Page\nD1:+1\nD2!\n\n"\
"ACCEL=<Shift>Up\nScroll Page\nD1:-1\nD2!\n\n"\
"ACCEL=Down\nNext Command\nD1!\nD2!\n\n"\
"ACCEL=Up\nLast Command\nD1!\nD2!\n\n"\
"ACCEL=Tab\nComplete nick/command\nD1!\nD2!\n\n"\
"ACCEL=<Shift>ISO_Left_Tab\nComplete nick/command\nD1:Previous\nD2!\n\n"\
"ACCEL=space\nCheck For Replace\nD1!\nD2!\n\n"\
"ACCEL=Return\nCheck For Replace\nD1!\nD2!\n\n"\
"ACCEL=KP_Enter\nCheck For Replace\nD1!\nD2!\n\n"\
"ACCEL=<Primary>Tab\nComplete nick/command\nD1:Up\nD2!\n\n"\
"ACCEL=<Alt>Left\nMove front tab left\nD1!\nD2!\n\n"\
"ACCEL=<Alt>Right\nMove front tab right\nD1!\nD2!\n\n"\
"ACCEL=<Primary><Shift>Page_Up\nMove tab family left\nD1!\nD2!\n\n"\
"ACCEL=<Primary><Shift>Page_Down\nMove tab family right\nD1!\nD2!\n\n"\
"ACCEL=F9\nRun Command\nD1:/GUI MENU TOGGLE\nD2!\n\n"
2011-02-23 22:14:30 -05:00
void
key_init ()
{
if (key_load_kbs () == 1)
2011-02-23 22:14:30 -05:00
{
fe_message (_("There was an error loading key"
2011-02-23 22:14:30 -05:00
" bindings configuration"), FE_MSG_ERROR);
}
}
static inline int
key_get_action_from_string (char *text)
2011-02-23 22:14:30 -05:00
{
int i;
for (i = 0; i < KEY_MAX_ACTIONS + 1; i++)
{
if (strcmp (key_actions[i].name, text) == 0)
{
return i;
}
}
return 0;
}
static void
key_free (gpointer data)
{
struct key_binding *kb = (struct key_binding*)data;
g_return_if_fail (kb != NULL);
g_free (kb->data1);
g_free (kb->data2);
g_free (kb);
2011-02-23 22:14:30 -05:00
}
/* Ok, here are the NOTES
key_handle_key_press now handles all the key presses and history_keypress is
now defunct. It goes thru the linked list keys_root and finds a matching
key. It runs the action func and switches on these values:
0) Return
1) Find next
2) stop signal and return
* history_keypress is now dead (and gone)
* key_handle_key_press now takes its role
* All the possible actions are in a struct called key_actions (in fkeys.c)
* it is made of {function, name, desc}
* key bindings can pass 2 *text* strings to the handler. If more options are nee
ded a format can be put on one of these options
* key actions are passed {
the entry widget
the Gdk event
data 1
data 2
session struct
}
* key bindings are stored in a linked list of key_binding structs
* which looks like {
guint keyval; GDK keynumber
2011-02-23 22:14:30 -05:00
int action; Index into key_actions
GdkModiferType mod; modifier, only ones from key_modifer_get_valid()
2011-02-23 22:14:30 -05:00
char *data1, *data2; Pointers to strings, these must be freed
struct key_binding *next;
}
* remember that is (data1 || data2) != NULL then they need to be free()'ed
--AGL
*/
static inline GdkModifierType
key_modifier_get_valid (GdkModifierType mod)
{
GdkModifierType ret;
#ifdef __APPLE__
ret = mod & (GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_MOD1_MASK | GDK_META_MASK);
#else
/* These masks work on both Windows and Unix */
ret = mod & (GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_MOD1_MASK);
#endif
return ret;
}
2011-02-23 22:14:30 -05:00
gboolean
key_handle_key_press (GtkWidget *wid, GdkEventKey *evt, session *sess)
{
struct key_binding *kb;
int n;
2011-02-23 22:14:30 -05:00
GSList *list;
/* where did this event come from? */
list = sess_list;
while (list)
{
sess = list->data;
if (sess->gui->input_box == wid)
{
if (sess->gui->is_tab)
sess = current_tab;
break;
}
list = list->next;
}
if (!list)
return FALSE;
current_sess = sess;
if (plugin_emit_keypress (sess, evt->state, evt->keyval, gdk_keyval_to_unicode (evt->keyval)))
2011-02-23 22:14:30 -05:00
return 1;
/* maybe the plugin closed this tab? */
if (!is_session (sess))
return 1;
list = keybind_list;
while (list)
2011-02-23 22:14:30 -05:00
{
kb = (struct key_binding*)list->data;
if (kb->keyval == evt->keyval && kb->mod == key_modifier_get_valid (evt->state))
2011-02-23 22:14:30 -05:00
{
if (kb->action < 0 || kb->action > KEY_MAX_ACTIONS)
return 0;
/* Run the function */
n = key_actions[kb->action].handler (wid, evt, kb->data1,
kb->data2, sess);
switch (n)
{
case 0:
return 1;
case 2:
g_signal_stop_emission_by_name (G_OBJECT (wid),
"key_press_event");
return 1;
}
}
list = g_slist_next (list);
2011-02-23 22:14:30 -05:00
}
switch (evt->keyval)
2011-02-23 22:14:30 -05:00
{
2013-09-15 03:21:56 -04:00
case GDK_KEY_space:
2011-02-23 22:14:30 -05:00
key_action_tab_clean ();
break;
}
return 0;
}
/* ***** GUI code here ******************* */
2011-02-23 22:14:30 -05:00
enum
2011-02-23 22:14:30 -05:00
{
KEY_COLUMN,
ACCEL_COLUMN,
ACTION_COLUMN,
D1_COLUMN,
D2_COLUMN,
N_COLUMNS
};
2011-02-23 22:14:30 -05:00
static GtkWidget *key_dialog = NULL;
2011-02-23 22:14:30 -05:00
static inline GtkTreeModel *
get_store (void)
{
return gtk_tree_view_get_model (g_object_get_data (G_OBJECT (key_dialog), "view"));
}
2011-02-23 22:14:30 -05:00
static void
key_dialog_print_text (GtkXText *xtext, char *text)
{
unsigned int old = prefs.hex_stamp_text;
prefs.hex_stamp_text = 0; /* temporarily disable stamps */
gtk_xtext_clear (GTK_XTEXT (xtext)->buffer, 0);
PrintTextRaw (GTK_XTEXT (xtext)->buffer, text, 0, 0);
prefs.hex_stamp_text = old;
}
2011-02-23 22:14:30 -05:00
static void
key_dialog_set_key (GtkCellRendererAccel *accel, gchar *pathstr, guint accel_key,
GdkModifierType accel_mods, guint hardware_keycode, gpointer userdata)
2011-02-23 22:14:30 -05:00
{
GtkTreeModel *model = get_store ();
GtkTreePath *path = gtk_tree_path_new_from_string (pathstr);
GtkTreeIter iter;
gchar *label_name, *accel_name;
/* Shift tab requires an exception, hopefully that list ends here.. */
if (accel_key == GDK_KEY_Tab && accel_mods & GDK_SHIFT_MASK)
accel_key = GDK_KEY_ISO_Left_Tab;
label_name = gtk_accelerator_get_label (accel_key, key_modifier_get_valid (accel_mods));
accel_name = gtk_accelerator_name (accel_key, key_modifier_get_valid (accel_mods));
gtk_tree_model_get_iter (model, &iter, path);
gtk_list_store_set (GTK_LIST_STORE (model), &iter, KEY_COLUMN, label_name,
ACCEL_COLUMN, accel_name, -1);
gtk_tree_path_free (path);
g_free (label_name);
g_free (accel_name);
2011-02-23 22:14:30 -05:00
}
static void
key_dialog_combo_changed (GtkCellRendererCombo *combo, gchar *pathstr,
GtkTreeIter *new_iter, gpointer data)
2011-02-23 22:14:30 -05:00
{
GtkTreeModel *model;
GtkXText *xtext;
gchar *actiontext = NULL;
gint action;
2011-02-23 22:14:30 -05:00
xtext = GTK_XTEXT (g_object_get_data (G_OBJECT (key_dialog), "xtext"));
model = GTK_TREE_MODEL (data);
2011-02-23 22:14:30 -05:00
gtk_tree_model_get (model, new_iter, 0, &actiontext, -1);
2011-02-23 22:14:30 -05:00
if (actiontext)
{
#ifdef WIN32
/* We need to manually update the store */
GtkTreePath *path;
GtkTreeIter iter;
2011-02-23 22:14:30 -05:00
path = gtk_tree_path_new_from_string (pathstr);
model = get_store ();
2011-02-23 22:14:30 -05:00
gtk_tree_model_get_iter (model, &iter, path);
gtk_list_store_set (GTK_LIST_STORE (model), &iter, ACTION_COLUMN, actiontext, -1);
2011-02-23 22:14:30 -05:00
gtk_tree_path_free (path);
#endif
action = key_get_action_from_string (actiontext);
key_dialog_print_text (xtext, key_actions[action].help);
g_free (actiontext);
}
2011-02-23 22:14:30 -05:00
}
static void
key_dialog_entry_edited (GtkCellRendererText *render, gchar *pathstr, gchar *new_text, gpointer data)
2011-02-23 22:14:30 -05:00
{
GtkTreeModel *model = get_store ();
GtkTreePath *path = gtk_tree_path_new_from_string (pathstr);
GtkTreeIter iter;
gint column = GPOINTER_TO_INT (data);
2011-02-23 22:14:30 -05:00
gtk_tree_model_get_iter (model, &iter, path);
gtk_list_store_set (GTK_LIST_STORE (model), &iter, column, new_text, -1);
gtk_tree_path_free (path);
}
static gboolean
key_dialog_keypress (GtkWidget *wid, GdkEventKey *evt, gpointer userdata)
{
GtkTreeView *view = g_object_get_data (G_OBJECT (key_dialog), "view");
GtkTreeModel *store;
GtkTreeIter iter1, iter2;
GtkTreeSelection *sel;
GtkTreePath *path;
gboolean handled = FALSE;
int delta;
if (evt->state & GDK_SHIFT_MASK)
2011-02-23 22:14:30 -05:00
{
if (evt->keyval == GDK_KEY_Up)
2011-02-23 22:14:30 -05:00
{
handled = TRUE;
delta = -1;
}
else if (evt->keyval == GDK_KEY_Down)
{
handled = TRUE;
delta = 1;
2011-02-23 22:14:30 -05:00
}
}
if (handled)
{
sel = gtk_tree_view_get_selection (view);
gtk_tree_selection_get_selected (sel, &store, &iter1);
path = gtk_tree_model_get_path (store, &iter1);
if (delta == 1)
gtk_tree_path_next (path);
else
gtk_tree_path_prev (path);
gtk_tree_model_get_iter (store, &iter2, path);
gtk_tree_path_free (path);
gtk_list_store_swap (GTK_LIST_STORE (store), &iter1, &iter2);
}
return handled;
2011-02-23 22:14:30 -05:00
}
static void
key_dialog_selection_changed (GtkTreeSelection *sel, gpointer userdata)
2011-02-23 22:14:30 -05:00
{
GtkTreeModel *model;
GtkTreeIter iter;
GtkXText *xtext;
char *actiontext;
int action;
2011-02-23 22:14:30 -05:00
if (!gtk_tree_selection_get_selected (sel, &model, &iter) || model == NULL)
return;
xtext = GTK_XTEXT (g_object_get_data (G_OBJECT (key_dialog), "xtext"));
gtk_tree_model_get (model, &iter, ACTION_COLUMN, &actiontext, -1);
if (actiontext)
2011-02-23 22:14:30 -05:00
{
action = key_get_action_from_string (actiontext);
key_dialog_print_text (xtext, key_actions[action].help);
g_free (actiontext);
2011-02-23 22:14:30 -05:00
}
else
key_dialog_print_text (xtext, _("Select a row to get help information on its Action."));
2011-02-23 22:14:30 -05:00
}
static void
key_dialog_close (GtkWidget *wid, gpointer userdata)
2011-02-23 22:14:30 -05:00
{
gtk_widget_destroy (key_dialog);
key_dialog = NULL;
}
2011-02-23 22:14:30 -05:00
static void
key_dialog_save (GtkWidget *wid, gpointer userdata)
{
GtkTreeModel *store = get_store ();
GtkTreeIter iter;
struct key_binding *kb;
char *data1, *data2, *accel, *actiontext;
guint keyval;
GdkModifierType mod;
if (keybind_list)
2011-02-23 22:14:30 -05:00
{
g_slist_free_full (keybind_list, key_free);
keybind_list = NULL;
2011-02-23 22:14:30 -05:00
}
if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter))
2011-02-23 22:14:30 -05:00
{
do
2011-02-23 22:14:30 -05:00
{
kb = g_new0 (struct key_binding, 1);
2011-02-23 22:14:30 -05:00
gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, ACCEL_COLUMN, &accel,
ACTION_COLUMN, &actiontext,
D1_COLUMN, &data1,
D2_COLUMN, &data2,
-1);
kb->data1 = data1;
kb->data2 = data2;
2011-02-23 22:14:30 -05:00
if (accel)
{
gtk_accelerator_parse (accel, &keyval, &mod);
2011-02-23 22:14:30 -05:00
kb->keyval = keyval;
kb->mod = key_modifier_get_valid (mod);
2011-02-23 22:14:30 -05:00
g_free (accel);
}
2011-02-23 22:14:30 -05:00
if (actiontext)
{
kb->action = key_get_action_from_string (actiontext);
g_free (actiontext);
}
2011-02-23 22:14:30 -05:00
if (!accel || !actiontext)
key_free (kb);
else
keybind_list = g_slist_append (keybind_list, kb);
2014-12-09 00:24:59 -05:00
}
while (gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter));
}
2011-02-23 22:14:30 -05:00
if (key_save_kbs () == 0)
key_dialog_close (wid, NULL);
2011-02-23 22:14:30 -05:00
}
static void
key_dialog_add (GtkWidget *wid, gpointer userdata)
2011-02-23 22:14:30 -05:00
{
GtkTreeView *view = g_object_get_data (G_OBJECT (key_dialog), "view");
GtkTreeViewColumn *col;
GtkListStore *store = GTK_LIST_STORE (get_store ());
GtkTreeIter iter;
GtkTreePath *path;
gtk_list_store_append (store, &iter);
/* make sure the new row is visible and selected */
path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter);
col = gtk_tree_view_get_column (view, ACTION_COLUMN);
gtk_tree_view_scroll_to_cell (view, path, NULL, FALSE, 0.0, 0.0);
gtk_tree_view_set_cursor (view, path, col, TRUE);
gtk_tree_path_free (path);
}
2011-02-23 22:14:30 -05:00
static void
key_dialog_delete (GtkWidget *wid, gpointer userdata)
{
GtkTreeView *view = g_object_get_data (G_OBJECT (key_dialog), "view");
GtkListStore *store = GTK_LIST_STORE (gtk_tree_view_get_model (view));
GtkTreeIter iter;
GtkTreePath *path;
2011-02-23 22:14:30 -05:00
if (gtkutil_treeview_get_selected (view, &iter, -1))
{
/* delete this row, select next one */
if (gtk_list_store_remove (store, &iter))
{
path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter);
gtk_tree_view_scroll_to_cell (view, path, NULL, TRUE, 1.0, 0.0);
gtk_tree_view_set_cursor (view, path, NULL, FALSE);
gtk_tree_path_free (path);
}
}
2011-02-23 22:14:30 -05:00
}
static GtkWidget *
key_dialog_treeview_new (GtkWidget *box)
2011-02-23 22:14:30 -05:00
{
GtkWidget *scroll;
GtkListStore *store, *combostore;
GtkTreeViewColumn *col;
GtkWidget *view;
GtkCellRenderer *render;
int i;
2011-02-23 22:14:30 -05:00
scroll = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroll), GTK_SHADOW_IN);
2011-02-23 22:14:30 -05:00
store = gtk_list_store_new (N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
G_TYPE_STRING, G_TYPE_STRING);
g_return_val_if_fail (store != NULL, NULL);
2011-02-23 22:14:30 -05:00
view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (view), TRUE);
gtk_tree_view_set_enable_search (GTK_TREE_VIEW (view), FALSE);
gtk_tree_view_set_reorderable (GTK_TREE_VIEW (view), TRUE);
2011-02-23 22:14:30 -05:00
g_signal_connect (G_OBJECT (view), "key-press-event",
G_CALLBACK (key_dialog_keypress), NULL);
g_signal_connect (G_OBJECT (gtk_tree_view_get_selection (GTK_TREE_VIEW(view))),
"changed", G_CALLBACK (key_dialog_selection_changed), NULL);
2011-02-23 22:14:30 -05:00
gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (view), TRUE);
2011-02-23 22:14:30 -05:00
render = gtk_cell_renderer_accel_new ();
g_object_set (render, "editable", TRUE,
#ifndef WIN32
"accel-mode", GTK_CELL_RENDERER_ACCEL_MODE_OTHER,
#endif
NULL);
g_signal_connect (G_OBJECT (render), "accel-edited",
G_CALLBACK (key_dialog_set_key), NULL);
gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view), KEY_COLUMN,
"Key", render,
"text", KEY_COLUMN,
NULL);
render = gtk_cell_renderer_text_new ();
gtk_tree_view_insert_column_with_attributes (
GTK_TREE_VIEW (view), ACCEL_COLUMN,
"Accel", render,
"text", ACCEL_COLUMN,
NULL);
combostore = gtk_list_store_new (1, G_TYPE_STRING);
for (i = 0; i <= KEY_MAX_ACTIONS; i++)
{
GtkTreeIter iter;
if (key_actions[i].name[0])
{
gtk_list_store_append (combostore, &iter);
gtk_list_store_set (combostore, &iter, 0, key_actions[i].name, -1);
}
}
2011-02-23 22:14:30 -05:00
render = gtk_cell_renderer_combo_new ();
g_object_set (G_OBJECT (render), "model", combostore,
"has-entry", FALSE,
"editable", TRUE,
"text-column", 0,
NULL);
g_signal_connect (G_OBJECT (render), "edited",
G_CALLBACK (key_dialog_entry_edited), GINT_TO_POINTER (ACTION_COLUMN));
g_signal_connect (G_OBJECT (render), "changed",
G_CALLBACK (key_dialog_combo_changed), combostore);
gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view), ACTION_COLUMN,
"Action", render,
"text", ACTION_COLUMN,
NULL);
render = gtk_cell_renderer_text_new ();
g_object_set (render, "editable", TRUE, NULL);
g_signal_connect (G_OBJECT (render), "edited",
G_CALLBACK (key_dialog_entry_edited), GINT_TO_POINTER (D1_COLUMN));
gtk_tree_view_insert_column_with_attributes (
GTK_TREE_VIEW (view), D1_COLUMN,
"Data1", render,
"text", D1_COLUMN,
NULL);
render = gtk_cell_renderer_text_new ();
g_object_set (render, "editable", TRUE, NULL);
g_signal_connect (G_OBJECT (render), "edited",
G_CALLBACK (key_dialog_entry_edited), GINT_TO_POINTER (D2_COLUMN));
gtk_tree_view_insert_column_with_attributes (
GTK_TREE_VIEW (view), D2_COLUMN,
"Data2", render,
"text", D2_COLUMN,
NULL);
col = gtk_tree_view_get_column (GTK_TREE_VIEW (view), KEY_COLUMN);
gtk_tree_view_column_set_fixed_width (col, 200);
gtk_tree_view_column_set_resizable (col, TRUE);
col = gtk_tree_view_get_column (GTK_TREE_VIEW (view), ACCEL_COLUMN);
gtk_tree_view_column_set_visible (col, FALSE);
col = gtk_tree_view_get_column (GTK_TREE_VIEW (view), ACTION_COLUMN);
gtk_tree_view_column_set_fixed_width (col, 160);
col = gtk_tree_view_get_column (GTK_TREE_VIEW (view), D1_COLUMN);
gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
gtk_tree_view_column_set_min_width (col, 80);
gtk_tree_view_column_set_resizable (col, TRUE);
col = gtk_tree_view_get_column (GTK_TREE_VIEW (view), D2_COLUMN);
gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
gtk_tree_view_column_set_min_width (col, 80);
gtk_tree_view_column_set_resizable (col, TRUE);
gtk_container_add (GTK_CONTAINER (scroll), view);
gtk_container_add (GTK_CONTAINER (box), scroll);
return view;
2011-02-23 22:14:30 -05:00
}
static void
key_dialog_load (GtkListStore *store)
2011-02-23 22:14:30 -05:00
{
struct key_binding *kb = NULL;
char *label_text, *accel_text;
GtkTreeIter iter;
GSList *list = keybind_list;
2011-02-23 22:14:30 -05:00
while (list)
{
kb = (struct key_binding*)list->data;
2011-02-23 22:14:30 -05:00
label_text = gtk_accelerator_get_label (kb->keyval, kb->mod);
accel_text = gtk_accelerator_name (kb->keyval, kb->mod);
2011-02-23 22:14:30 -05:00
gtk_list_store_append (store, &iter);
gtk_list_store_set (store, &iter,
KEY_COLUMN, label_text,
ACCEL_COLUMN, accel_text,
ACTION_COLUMN, key_actions[kb->action].name,
D1_COLUMN, kb->data1,
D2_COLUMN, kb->data2, -1);
g_free (accel_text);
g_free (label_text);
list = g_slist_next (list);
2011-02-23 22:14:30 -05:00
}
}
void
key_dialog_show ()
{
GtkWidget *vbox, *box;
GtkWidget *view, *xtext;
GtkListStore *store;
2011-02-23 22:14:30 -05:00
if (key_dialog)
{
mg_bring_tofront (key_dialog);
return;
}
key_dialog = mg_create_generic_tab ("editkeys", _(DISPLAY_NAME": Keyboard Shortcuts"),
TRUE, FALSE, key_dialog_close, NULL, 600, 360, &vbox, 0);
2011-02-23 22:14:30 -05:00
view = key_dialog_treeview_new (vbox);
xtext = gtk_xtext_new (colors, 0);
gtk_box_pack_start (GTK_BOX (vbox), xtext, FALSE, TRUE, 2);
gtk_xtext_set_font (GTK_XTEXT (xtext), prefs.hex_text_font);
2011-02-23 22:14:30 -05:00
g_object_set_data (G_OBJECT (key_dialog), "view", view);
g_object_set_data (G_OBJECT (key_dialog), "xtext", xtext);
2011-02-23 22:14:30 -05:00
box = gtk_hbutton_box_new ();
gtk_button_box_set_layout (GTK_BUTTON_BOX (box), GTK_BUTTONBOX_SPREAD);
gtk_box_pack_start (GTK_BOX (vbox), box, FALSE, FALSE, 2);
gtk_container_set_border_width (GTK_CONTAINER (box), 5);
2011-02-23 22:14:30 -05:00
gtkutil_button (box, GTK_STOCK_NEW, NULL, key