2
* Copyright (C) 1998 Peter Zelezny.
4
* This program is free software; you can redistribute it and/or modify
5
* it under the terms of the GNU General Public License as published by
6
* the Free Software Foundation; either version 2 of the License, or
7
* (at your option) any later version.
9
* This program is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
* GNU General Public License for more details.
14
* You should have received a copy of the GNU General Public License
15
* along with this program; if not, write to the Free Software
16
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
21
#include <sys/types.h>
30
#include <gtk/gtklabel.h>
31
#include <gtk/gtkeditable.h>
32
#include <gtk/gtkmenu.h>
33
#include <gtk/gtkmenuitem.h>
34
#include <gtk/gtkoptionmenu.h>
35
#include <gtk/gtkvbox.h>
36
#include <gtk/gtkhbox.h>
37
#include <gtk/gtkclist.h>
38
#include <gtk/gtknotebook.h>
39
#include <gtk/gtkcheckbutton.h>
40
#include <gtk/gtkentry.h>
41
#include <gtk/gtkvscrollbar.h>
43
#include "../common/xchat.h"
44
#include "../common/xchatc.h"
45
#include "../common/cfgfiles.h"
46
#include "../common/fe.h"
47
#include "../common/userlist.h"
48
#include "../common/outbound.h"
49
#include "../common/util.h"
50
#include "../common/text.h"
51
#include "../common/plugin.h"
52
#include <gdk/gdkkeysyms.h>
62
#include <gtk/gtktextview.h>
65
static void replace_handle (GtkWidget * wid);
66
void key_action_tab_clean (void);
68
/***************** Key Binding Code ******************/
73
1) inc KEY_MAX_ACTIONS
74
2) write the function at the bottom of this file (with all the others)
75
FIXME: Write about calling and returning
76
3) Add it to key_actions
82
/* Remember that the *number* of actions is this *plus* 1 --AGL */
83
#define KEY_MAX_ACTIONS 14
84
/* These are cp'ed from history.c --AGL */
85
#define STATE_SHIFT GDK_SHIFT_MASK
86
#define STATE_ALT GDK_MOD1_MASK
87
#define STATE_CTRL GDK_CONTROL_MASK
91
int keyval; /* GDK keynumber */
92
char *keyname; /* String with the name of the function */
93
int action; /* Index into key_actions */
94
int mod; /* Flags of STATE_* above */
95
char *data1, *data2; /* Pointers to strings, these must be freed */
96
struct key_binding *next;
101
int (*handler) (GtkWidget * wid, GdkEventKey * evt, char *d1, char *d2,
102
struct session * sess);
113
static int key_load_kbs (char *);
114
static void key_load_defaults ();
115
static void key_save_kbs (char *);
116
static int key_action_handle_command (GtkWidget * wid, GdkEventKey * evt,
118
struct session *sess);
119
static int key_action_page_switch (GtkWidget * wid, GdkEventKey * evt,
120
char *d1, char *d2, struct session *sess);
121
int key_action_insert (GtkWidget * wid, GdkEventKey * evt, char *d1, char *d2,
122
struct session *sess);
123
static int key_action_scroll_page (GtkWidget * wid, GdkEventKey * evt,
124
char *d1, char *d2, struct session *sess);
125
static int key_action_set_buffer (GtkWidget * wid, GdkEventKey * evt,
126
char *d1, char *d2, struct session *sess);
127
static int key_action_history_up (GtkWidget * wid, GdkEventKey * evt,
128
char *d1, char *d2, struct session *sess);
129
static int key_action_history_down (GtkWidget * wid, GdkEventKey * evt,
130
char *d1, char *d2, struct session *sess);
131
static int key_action_tab_comp (GtkWidget * wid, GdkEventKey * evt, char *d1,
132
char *d2, struct session *sess);
133
static int key_action_comp_chng (GtkWidget * wid, GdkEventKey * evt, char *d1,
134
char *d2, struct session *sess);
135
static int key_action_replace (GtkWidget * wid, GdkEventKey * evt, char *d1,
136
char *d2, struct session *sess);
137
static int key_action_move_tab_left (GtkWidget * wid, GdkEventKey * evt,
139
struct session *sess);
140
static int key_action_move_tab_right (GtkWidget * wid, GdkEventKey * evt,
142
struct session *sess);
143
static int key_action_move_tab_family_left (GtkWidget * wid, GdkEventKey * evt,
145
struct session *sess);
146
static int key_action_move_tab_family_right (GtkWidget * wid, GdkEventKey * evt,
148
struct session *sess);
149
static int key_action_put_history (GtkWidget * wid, GdkEventKey * evt,
151
struct session *sess);
153
static GtkWidget *key_dialog;
154
static struct key_binding *keys_root = NULL;
156
static const struct key_action key_actions[KEY_MAX_ACTIONS + 1] = {
158
{key_action_handle_command, "Run Command",
159
N_("The \002Run Command\002 action runs the data in Data 1 as if it has 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 seperate 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")},
160
{key_action_page_switch, "Change Page",
161
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")},
162
{key_action_insert, "Insert in Buffer",
163
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")},
164
{key_action_scroll_page, "Scroll Page",
165
N_("The \002Scroll Page\002 command scrolls the text widget up or down one page or one line. Set Data 1 to either Up, Down, +1 or -1.")},
166
{key_action_set_buffer, "Set Buffer",
167
N_("The \002Set Buffer\002 command sets the entry where the key sequence was entered to the contents of Data 1")},
168
{key_action_history_up, "Last Command",
169
N_("The \002Last Command\002 command sets the entry to contain the last command entered - the same as pressing up in a shell")},
170
{key_action_history_down, "Next Command",
171
N_("The \002Next Command\002 command sets the entry to contain the next command entered - the same as pressing down in a shell")},
172
{key_action_tab_comp, "Complete nick/command",
173
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")},
174
{key_action_comp_chng, "Change Selected Nick",
175
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")},
176
{key_action_replace, "Check For Replace",
177
N_("This command checks the last word entered in the entry against the replace list and replaces it if it finds a match")},
178
{key_action_move_tab_left, "Move front tab left",
179
N_("This command moves the front tab left by one")},
180
{key_action_move_tab_right, "Move front tab right",
181
N_("This command moves the front tab right by one")},
182
{key_action_move_tab_family_left, "Move tab family left",
183
N_("This command moves the current tab family to the left")},
184
{key_action_move_tab_family_right, "Move tab family right",
185
N_("This command moves the current tab family to the right")},
186
{key_action_put_history, "Push input line into history",
187
N_("Push input line into history but doesn't send to server")},
194
if (key_load_kbs (NULL) == 1)
196
key_load_defaults ();
197
if (key_load_kbs (NULL) == 1)
198
fe_message (_("There was an error loading key"
199
" bindings configuration"), FE_MSG_ERROR);
204
key_get_key_name (int keyval)
206
return gdk_keyval_name (gdk_keyval_to_lower (keyval));
209
/* Ok, here are the NOTES
211
key_handle_key_press now handles all the key presses and history_keypress is
212
now defunct. It goes thru the linked list keys_root and finds a matching
213
key. It runs the action func and switches on these values:
216
2) stop signal and return
218
* history_keypress is now dead (and gone)
219
* key_handle_key_press now takes its role
220
* All the possible actions are in a struct called key_actions (in fkeys.c)
221
* it is made of {function, name, desc}
222
* key bindings can pass 2 *text* strings to the handler. If more options are nee
223
ded a format can be put on one of these options
224
* key actions are passed {
231
* key bindings are stored in a linked list of key_binding structs
233
int keyval; GDK keynumber
234
char *keyname; String with the name of the function
235
int action; Index into key_actions
236
int mod; Flags of STATE_* above
237
char *data1, *data2; Pointers to strings, these must be freed
238
struct key_binding *next;
240
* remember that is (data1 || data2) != NULL then they need to be free()'ed
247
key_handle_key_press (GtkWidget *wid, GdkEventKey *evt, session *sess)
249
struct key_binding *kb, *last = NULL;
250
int keyval = evt->keyval;
254
/* where did this event come from? */
259
if (sess->gui->input_box == wid)
261
if (sess->gui->is_tab)
271
if (plugin_emit_keypress (sess, evt->state, evt->keyval, evt->length, evt->string))
274
/* maybe the plugin closed this tab? */
275
if (!is_session (sess))
278
mod = evt->state & (STATE_CTRL | STATE_ALT | STATE_SHIFT);
283
if (kb->keyval == keyval && kb->mod == mod)
285
if (kb->action < 0 || kb->action > KEY_MAX_ACTIONS)
288
/* Bump this binding to the top of the list */
291
last->next = kb->next;
292
kb->next = keys_root;
295
/* Run the function */
296
n = key_actions[kb->action].handler (wid, evt, kb->data1,
303
g_signal_stop_emission_by_name (G_OBJECT (wid),
315
key_action_tab_clean ();
318
#if defined(USE_GTKSPELL) && !defined(WIN32)
319
/* gtktextview has no 'activate' event, so we trap ENTER here */
322
if (!(evt->state & GDK_CONTROL_MASK))
324
g_signal_stop_emission_by_name (G_OBJECT (wid), "key_press_event");
325
mg_inputbox_cb (wid, sess->gui);
333
/* Walks keys_root and free()'s everything */
337
struct key_binding *cur, *next;
353
/* Turns mod flags into a C-A-S string */
355
key_make_mod_str (int mod, char *buf)
359
if (mod & STATE_CTRL)
371
if (mod & STATE_SHIFT)
381
/* ***** GUI code here ******************* */
383
/* NOTE: The key_dialog defin is above --AGL */
384
static GtkWidget *key_dialog_act_menu, *key_dialog_kb_clist;
385
static GtkWidget *key_dialog_tog_c, *key_dialog_tog_s, *key_dialog_tog_a;
386
static GtkWidget *key_dialog_ent_key, *key_dialog_ent_d1, *key_dialog_ent_d2;
387
static GtkWidget *key_dialog_text;
392
/* This is the default config */
394
"C\nPrior\nChange Page\nD1:-1\nD2:Relative\n\n"\
395
"C\nNext\nChange Page\nD1:1\nD2:Relative\n\n"\
396
"A\n9\nChange Page\nD1:9\nD2!\n\n"\
397
"A\n8\nChange Page\nD1:8\nD2!\n\n"\
398
"A\n7\nChange Page\nD1:7\nD2!\n\n"\
399
"A\n6\nChange Page\nD1:6\nD2!\n\n"\
400
"A\n5\nChange Page\nD1:5\nD2!\n\n"\
401
"A\n4\nChange Page\nD1:4\nD2!\n\n"\
402
"A\n3\nChange Page\nD1:3\nD2!\n\n"\
403
"A\n2\nChange Page\nD1:2\nD2!\n\n"\
404
"A\n1\nChange Page\nD1:1\nD2!\n\n"\
405
"C\no\nInsert in Buffer\nD1:\nD2!\n\n"\
406
"C\nb\nInsert in Buffer\nD1:\nD2!\n\n"\
407
"C\nk\nInsert in Buffer\nD1:\nD2!\n\n"\
408
"S\nNext\nChange Selected Nick\nD1!\nD2!\n\n"\
409
"S\nPrior\nChange Selected Nick\nD1:Up\nD2!\n\n"\
410
"None\nNext\nScroll Page\nD1:Down\nD2!\n\n"\
411
"None\nPrior\nScroll Page\nD1:Up\nD2!\n\n"\
412
"S\nDown\nScroll Page\nD1:+1\nD2!\n\n"\
413
"S\nUp\nScroll Page\nD1:-1\nD2!\n\n"\
414
"None\nDown\nNext Command\nD1!\nD2!\n\n"\
415
"None\nUp\nLast Command\nD1!\nD2!\n\n"\
416
"None\nTab\nComplete nick/command\nD1!\nD2!\n\n"\
417
"None\nspace\nCheck For Replace\nD1!\nD2!\n\n"\
418
"None\nReturn\nCheck For Replace\nD1!\nD2!\n\n"\
419
"None\nKP_Enter\nCheck For Replace\nD1!\nD2!\n\n"\
420
"C\nTab\nComplete nick/command\nD1:Up\nD2!\n\n"\
421
"A\nLeft\nMove front tab left\nD1!\nD2!\n\n"\
422
"A\nRight\nMove front tab right\nD1!\nD2!\n\n"\
423
"CS\nPrior\nMove tab family left\nD1!\nD2!\n\n"\
424
"CS\nNext\nMove tab family right\nD1!\nD2!\n\n"\
425
"None\nF9\nRun Command\nD1:/GUI MENU TOGGLE\nD2!\n\n"
428
fd = xchat_open_file ("keybindings.conf", O_CREAT | O_TRUNC | O_WRONLY, 0x180, XOF_DOMODE);
433
write (fd, defcfg, strlen (defcfg));
445
key_dialog_add_new (GtkWidget * button, GtkCList * list)
447
gchar *strs[] = { "", NULL, NULL, NULL, NULL };
448
struct key_binding *kb;
450
strs[1] = _("<none>");
451
strs[2] = _("<none>");
452
strs[3] = _("<none>");
453
strs[4] = _("<none>");
455
kb = malloc (sizeof (struct key_binding));
461
kb->data1 = kb->data2 = NULL;
462
kb->next = keys_root;
466
gtk_clist_set_row_data (GTK_CLIST (list),
467
gtk_clist_append (GTK_CLIST (list), strs), kb);
472
key_dialog_delete (GtkWidget * button, GtkCList * list)
474
struct key_binding *kb, *cur, *last;
475
int row = gtkutil_clist_selection ((GtkWidget *) list);
479
kb = gtk_clist_get_row_data (list, row);
487
last->next = kb->next;
489
keys_root = kb->next;
496
gtk_clist_remove (list, row);
502
printf ("*** key_dialog_delete: couldn't find kb in list!\n");
503
/*if (getenv ("XCHAT_DEBUG"))
509
key_print_text (GtkXText *xtext, char *text)
511
unsigned int old = prefs.timestamp;
512
prefs.timestamp = 0; /* temporarily disable stamps */
513
gtk_xtext_clear (GTK_XTEXT (xtext)->buffer, 0);
514
PrintTextRaw (GTK_XTEXT (xtext)->buffer, text, 0, 0);
515
prefs.timestamp = old;
519
key_dialog_sel_act (GtkWidget * un, int num)
521
int row = gtkutil_clist_selection (key_dialog_kb_clist);
522
struct key_binding *kb;
526
kb = gtk_clist_get_row_data (GTK_CLIST (key_dialog_kb_clist), row);
528
gtk_clist_set_text (GTK_CLIST (key_dialog_kb_clist), row, 2,
529
_(key_actions[num].name));
530
if (key_actions[num].help)
532
key_print_text (GTK_XTEXT (key_dialog_text), _(key_actions[num].help));
538
key_dialog_sel_row (GtkWidget * clist, gint row, gint column,
539
GdkEventButton * evt, gpointer data)
541
struct key_binding *kb = gtk_clist_get_row_data (GTK_CLIST (clist), row);
545
printf ("*** key_dialog_sel_row: kb == NULL\n");
548
if (kb->action > -1 && kb->action <= KEY_MAX_ACTIONS)
550
gtk_option_menu_set_history (GTK_OPTION_MENU (key_dialog_act_menu),
552
if (key_actions[kb->action].help)
554
key_print_text (GTK_XTEXT (key_dialog_text), _(key_actions[kb->action].help));
557
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (key_dialog_tog_c),
558
(kb->mod & STATE_CTRL) == STATE_CTRL);
559
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (key_dialog_tog_s),
560
(kb->mod & STATE_SHIFT) == STATE_SHIFT);
561
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (key_dialog_tog_a),
562
(kb->mod & STATE_ALT) == STATE_ALT);
565
gtk_entry_set_text (GTK_ENTRY (key_dialog_ent_d1), kb->data1);
567
gtk_entry_set_text (GTK_ENTRY (key_dialog_ent_d1), "");
570
gtk_entry_set_text (GTK_ENTRY (key_dialog_ent_d2), kb->data2);
572
gtk_entry_set_text (GTK_ENTRY (key_dialog_ent_d2), "");
575
gtk_entry_set_text (GTK_ENTRY (key_dialog_ent_key), kb->keyname);
577
gtk_entry_set_text (GTK_ENTRY (key_dialog_ent_key), "");
581
key_dialog_tog_key (GtkWidget * tog, int kstate)
583
int state = GTK_TOGGLE_BUTTON (tog)->active;
584
int row = gtkutil_clist_selection (key_dialog_kb_clist);
585
struct key_binding *kb;
591
kb = gtk_clist_get_row_data (GTK_CLIST (key_dialog_kb_clist), row);
597
gtk_clist_set_text (GTK_CLIST (key_dialog_kb_clist), row, 0,
598
key_make_mod_str (kb->mod, buf));
602
key_dialog_make_toggle (char *label, void *callback, void *option,
607
wid = gtk_check_button_new_with_label (label);
608
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (wid), 0);
609
gtk_signal_connect (GTK_OBJECT (wid), "toggled",
610
GTK_SIGNAL_FUNC (callback), option);
611
gtk_box_pack_end (GTK_BOX (box), wid, 0, 0, 0);
612
gtk_widget_show (wid);
618
key_dialog_make_entry (char *label, char *act, void *callback, void *option,
621
GtkWidget *wid, *hbox;;
623
hbox = gtk_hbox_new (0, 2);
626
wid = gtk_label_new (label);
627
gtk_widget_show (wid);
628
gtk_box_pack_start (GTK_BOX (hbox), wid, 0, 0, 0);
630
wid = gtk_entry_new ();
633
gtk_signal_connect (GTK_OBJECT (wid), act, GTK_SIGNAL_FUNC (callback),
636
gtk_box_pack_start (GTK_BOX (hbox), wid, 0, 0, 0);
637
gtk_widget_show (wid);
638
gtk_widget_show (hbox);
640
gtk_box_pack_start (GTK_BOX (box), hbox, 0, 0, 0);
646
key_dialog_set_key (GtkWidget * entry, GdkEventKey * evt, void *none)
648
int row = gtkutil_clist_selection (key_dialog_kb_clist);
649
struct key_binding *kb;
651
gtk_entry_set_text (GTK_ENTRY (entry), "");
656
kb = gtk_clist_get_row_data (GTK_CLIST (key_dialog_kb_clist), row);
657
kb->keyval = evt->keyval;
658
kb->keyname = key_get_key_name (kb->keyval);
659
gtk_clist_set_text (GTK_CLIST (key_dialog_kb_clist), row, 1, kb->keyname);
660
gtk_entry_set_text (GTK_ENTRY (entry), kb->keyname);
661
g_signal_stop_emission_by_name (G_OBJECT (entry), "key_press_event");
665
key_dialog_set_data (GtkWidget * entry, int d)
667
const char *text = gtk_entry_get_text (GTK_ENTRY (entry));
668
int row = gtkutil_clist_selection (key_dialog_kb_clist);
669
struct key_binding *kb;
671
int len = strlen (text);
678
kb = gtk_clist_get_row_data (GTK_CLIST (key_dialog_kb_clist), row);
683
buf = (char *) malloc (len);
684
memcpy (buf, text, len);
686
gtk_clist_set_text (GTK_CLIST (key_dialog_kb_clist), row, 3, text);
691
buf = (char *) malloc (len);
692
memcpy (buf, text, len);
694
gtk_clist_set_text (GTK_CLIST (key_dialog_kb_clist), row, 4, text);
701
GtkWidget *vbox, *hbox, *list, *vbox2, *wid, *wid2, *wid3, *hbox2;
702
struct key_binding *kb;
703
gchar *titles[] = { NULL, NULL, NULL, "1", "2" };
707
titles[0] = _("Mod");
708
titles[1] = _("Key");
709
titles[2] = _("Action");
713
mg_bring_tofront (key_dialog);
718
mg_create_generic_tab ("editkeys", _("XChat: Keyboard Shortcuts"),
719
TRUE, FALSE, key_dialog_close, NULL, 560, 330, &vbox, 0);
721
hbox = gtk_hbox_new (0, 2);
722
gtk_box_pack_start (GTK_BOX (vbox), hbox, 1, 1, 0);
724
list = gtkutil_clist_new (5, titles, hbox, 0, key_dialog_sel_row, 0, NULL,
725
0, GTK_SELECTION_SINGLE);
726
gtk_widget_set_usize (list, 400, 0);
727
key_dialog_kb_clist = list;
729
gtk_widget_show (hbox);
733
gtk_clist_set_column_width (GTK_CLIST (list), 1, 50);
734
gtk_clist_set_column_width (GTK_CLIST (list), 2, 120);
735
gtk_clist_set_column_width (GTK_CLIST (list), 3, 50);
736
gtk_clist_set_column_width (GTK_CLIST (list), 4, 50);
740
titles[0] = key_make_mod_str (kb->mod, temp);
741
titles[1] = kb->keyname;
742
if (kb->action < 0 || kb->action > KEY_MAX_ACTIONS)
743
titles[2] = _("<none>");
745
titles[2] = key_actions[kb->action].name;
747
titles[3] = kb->data1;
749
titles[3] = _("<none>");
752
titles[4] = kb->data2;
754
titles[4] = _("<none>");
756
gtk_clist_set_row_data (GTK_CLIST (list),
757
gtk_clist_append (GTK_CLIST (list), titles),
763
vbox2 = gtk_vbox_new (0, 2);
764
gtk_box_pack_end (GTK_BOX (hbox), vbox2, 1, 1, 0);
765
wid = gtk_button_new_with_label (_("Add New"));
766
gtk_box_pack_start (GTK_BOX (vbox2), wid, 0, 0, 0);
767
gtk_signal_connect (GTK_OBJECT (wid), "clicked",
768
GTK_SIGNAL_FUNC (key_dialog_add_new), list);
769
gtk_widget_show (wid);
770
wid = gtk_button_new_with_label (_("Delete"));
771
gtk_box_pack_start (GTK_BOX (vbox2), wid, 0, 0, 0);
772
gtk_signal_connect (GTK_OBJECT (wid), "clicked",
773
GTK_SIGNAL_FUNC (key_dialog_delete), list);
774
gtk_widget_show (wid);
775
gtk_widget_show (vbox2);
777
wid = gtk_option_menu_new ();
778
wid2 = gtk_menu_new ();
780
for (i = 0; i <= KEY_MAX_ACTIONS; i++)
782
wid3 = gtk_menu_item_new_with_label (_(key_actions[i].name));
783
gtk_widget_show (wid3);
784
gtk_menu_shell_append (GTK_MENU_SHELL (wid2), wid3);
785
gtk_signal_connect (GTK_OBJECT (wid3), "activate",
786
GTK_SIGNAL_FUNC (key_dialog_sel_act),
787
GINT_TO_POINTER (i));
790
gtk_option_menu_set_menu (GTK_OPTION_MENU (wid), wid2);
791
gtk_option_menu_set_history (GTK_OPTION_MENU (wid), 0);
792
gtk_box_pack_end (GTK_BOX (vbox2), wid, 0, 0, 0);
793
gtk_widget_show (wid);
794
key_dialog_act_menu = wid;
796
key_dialog_tog_s = key_dialog_make_toggle (_("Shift"), key_dialog_tog_key,
797
(void *) STATE_SHIFT, vbox2);
798
key_dialog_tog_a = key_dialog_make_toggle (_("Alt"), key_dialog_tog_key,
799
(void *) STATE_ALT, vbox2);
800
key_dialog_tog_c = key_dialog_make_toggle (_("Ctrl"), key_dialog_tog_key,
801
(void *) STATE_CTRL, vbox2);
803
key_dialog_ent_key = key_dialog_make_entry (_("Key"), "key_press_event",
804
key_dialog_set_key, NULL,
807
key_dialog_ent_d1 = key_dialog_make_entry (_("Data 1"), "activate",
808
key_dialog_set_data, NULL,
810
key_dialog_ent_d2 = key_dialog_make_entry (_("Data 2"), "activate",
814
hbox2 = gtk_hbox_new (0, 2);
815
gtk_box_pack_end (GTK_BOX (vbox), hbox2, 0, 0, 1);
817
wid = gtk_xtext_new (colors, 0);
818
gtk_xtext_set_tint (GTK_XTEXT (wid), prefs.tint_red, prefs.tint_green, prefs.tint_blue);
819
gtk_xtext_set_background (GTK_XTEXT (wid),
822
gtk_widget_set_usize (wid, 0, 75);
823
gtk_box_pack_start (GTK_BOX (hbox2), wid, 1, 1, 1);
824
gtk_xtext_set_font (GTK_XTEXT (wid), prefs.font_normal);
825
gtk_widget_show (wid);
827
wid2 = gtk_vscrollbar_new (GTK_XTEXT (wid)->adj);
828
gtk_box_pack_start (GTK_BOX (hbox2), wid2, 0, 0, 0);
829
gtk_widget_show (wid2);
831
gtk_widget_show (hbox2);
832
key_dialog_text = wid;
834
gtk_widget_show_all (key_dialog);
838
key_save_kbs (char *fn)
842
struct key_binding *kb;
845
fd = xchat_open_file ("keybindings.conf", O_CREAT | O_TRUNC | O_WRONLY,
848
fd = xchat_open_file (fn, O_CREAT | O_TRUNC | O_WRONLY,
849
0x180, XOF_DOMODE | XOF_FULLPATH);
852
fe_message (_("Error opening keys config file\n"), FE_MSG_ERROR);
856
snprintf (buf, 510, "# XChat key bindings config file\n\n"));
863
if (kb->keyval == -1 || kb->keyname == NULL || kb->action < 0)
869
if (kb->mod & STATE_CTRL)
874
if (kb->mod & STATE_ALT)
879
if (kb->mod & STATE_SHIFT)
885
write (fd, "None\n", 5);
889
write (fd, buf, snprintf (buf, 510, "%s\n%s\n", kb->keyname,
890
key_actions[kb->action].name));
891
if (kb->data1 && kb->data1[0])
892
write (fd, buf, snprintf (buf, 510, "D1:%s\n", kb->data1));
894
write (fd, "D1!\n", 4);
896
if (kb->data2 && kb->data2[0])
897
write (fd, buf, snprintf (buf, 510, "D2:%s\n", kb->data2));
899
write (fd, "D2!\n", 4);
909
/* I just know this is going to be a nasty parse, if you think it's bugged
910
it almost certainly is so contact the XChat dev team --AGL */
913
key_load_kbs_helper_mod (char *in, int *out)
917
/* First strip off the fluff */
918
while (in[0] == ' ' || in[0] == '\t')
921
while (in[len] == ' ' || in[len] == '\t')
927
if (strcmp (in, "None") == 0)
932
for (n = 0; n < len; n++)
954
/* These are just local defines to keep me sane --AGL */
956
#define KBSTATE_MOD 0
957
#define KBSTATE_KEY 1
958
#define KBSTATE_ACT 2
959
#define KBSTATE_DT1 3
960
#define KBSTATE_DT2 4
962
/* *** Warning, Warning! - massive function ahead! --AGL */
965
key_load_kbs (char *filename)
969
struct key_binding *kb = NULL, *last = NULL;
970
int fd, len, pnt = 0, state = 0, n;
972
if (filename == NULL)
973
fd = xchat_open_file ("keybindings.conf", O_RDONLY, 0, 0);
975
fd = xchat_open_file (filename, O_RDONLY, 0, XOF_FULLPATH);
978
if (fstat (fd, &st) != 0)
980
ibuf = malloc (st.st_size);
981
read (fd, ibuf, st.st_size);
984
while (buf_get_line (ibuf, &buf, &pnt, st.st_size))
988
if (strlen (buf) == 0)
994
kb = (struct key_binding *) malloc (sizeof (struct key_binding));
995
if (key_load_kbs_helper_mod (buf, &kb->mod))
1000
/* First strip off the fluff */
1001
while (buf[0] == ' ' || buf[0] == '\t')
1004
while (buf[len] == ' ' || buf[len] == '\t')
1010
n = gdk_keyval_from_name (buf);
1013
/* Unknown keyname, abort */
1017
ibuf = malloc (1024);
1018
snprintf (ibuf, 1024,
1019
_("Unknown keyname %s in key bindings config file\nLoad aborted, please fix %s/keybindings.conf\n"),
1020
buf, get_xdir_utf8 ());
1021
fe_message (ibuf, FE_MSG_ERROR);
1025
kb->keyname = gdk_keyval_name (n);
1028
state = KBSTATE_ACT;
1031
/* First strip off the fluff */
1032
while (buf[0] == ' ' || buf[0] == '\t')
1035
while (buf[len] == ' ' || buf[len] == '\t')
1041
for (n = 0; n < KEY_MAX_ACTIONS + 1; n++)
1043
if (strcmp (key_actions[n].name, buf) == 0)
1050
if (n == KEY_MAX_ACTIONS + 1)
1055
ibuf = malloc (1024);
1056
snprintf (ibuf, 1024,
1057
_("Unknown action %s in key bindings config file\nLoad aborted, Please fix %s/keybindings\n"),
1058
buf, get_xdir_utf8 ());
1059
fe_message (ibuf, FE_MSG_ERROR);
1063
state = KBSTATE_DT1;
1067
if (state == KBSTATE_DT1)
1068
kb->data1 = kb->data2 = NULL;
1070
while (buf[0] == ' ' || buf[0] == '\t')
1076
ibuf = malloc (1024);
1077
snprintf (ibuf, 1024,
1078
_("Expecting Data line (beginning Dx{:|!}) but got:\n%s\n\nLoad aborted, Please fix %s/keybindings\n"),
1079
buf, get_xdir_utf8 ());
1080
fe_message (ibuf, FE_MSG_ERROR);
1087
if (state != KBSTATE_DT1)
1091
if (state != KBSTATE_DT2)
1101
/* Add one for the NULL, subtract 3 for the "Dx:" */
1104
if (state == KBSTATE_DT1)
1106
kb->data1 = malloc (len);
1107
memcpy (kb->data1, &buf[3], len);
1110
kb->data2 = malloc (len);
1111
memcpy (kb->data2, &buf[3], len);
1113
} else if (buf[2] == '!')
1115
if (state == KBSTATE_DT1)
1120
if (state == KBSTATE_DT1)
1122
state = KBSTATE_DT2;
1132
state = KBSTATE_MOD;
1144
/*if (getenv ("XCHAT_DEBUG"))
1146
snprintf (ibuf, 1024,
1147
_("Key bindings config file is corrupt, load aborted\n"
1148
"Please fix %s/keybindings.conf\n"),
1150
fe_message (ibuf, FE_MSG_ERROR);
1155
/* ***** Key actions start here *********** */
1157
/* See the NOTES above --AGL */
1161
key_action_handle_command (GtkWidget * wid, GdkEventKey * evt, char *d1,
1162
char *d2, struct session *sess)
1165
char out[2048], d = 0;
1172
/* Replace each "\n" substring with '\n' */
1173
for (ii = oi = 0; ii < len; ii++)
1195
handle_multiline (sess, out, 0, 0);
1200
key_action_page_switch (GtkWidget * wid, GdkEventKey * evt, char *d1,
1201
char *d2, struct session *sess)
1212
for (i = 0; i < len; i++)
1214
if (d1[i] < '0' || d1[i] > '9')
1216
if (i == 0 && (d1[i] == '+' || d1[i] == '-'))
1226
if (!d2 || d2[0] == 0)
1227
mg_switch_page (FALSE, num);
1229
mg_switch_page (TRUE, num);
1234
key_action_insert (GtkWidget * wid, GdkEventKey * evt, char *d1, char *d2,
1235
struct session *sess)
1242
tmp_pos = SPELL_ENTRY_GET_POS (wid);
1243
SPELL_ENTRY_INSERT (wid, d1, strlen (d1), &tmp_pos);
1244
SPELL_ENTRY_SET_POS (wid, tmp_pos);
1248
/* handles PageUp/Down keys */
1250
key_action_scroll_page (GtkWidget * wid, GdkEventKey * evt, char *d1,
1251
char *d2, struct session *sess)
1255
enum scroll_type { PAGE_UP, PAGE_DOWN, LINE_UP, LINE_DOWN };
1256
int type = PAGE_DOWN;
1260
if (!strcasecmp (d1, "up"))
1262
else if (!strcasecmp (d1, "+1"))
1264
else if (!strcasecmp (d1, "-1"))
1271
adj = GTK_RANGE (sess->gui->vscrollbar)->adjustment;
1272
end = adj->upper - adj->lower - adj->page_size;
1277
value = adj->value - 1.0;
1281
value = adj->value + 1.0;
1285
value = adj->value - (adj->page_size - 1);
1288
default: /* PAGE_DOWN */
1289
value = adj->value + (adj->page_size - 1);
1298
gtk_adjustment_set_value (adj, value);
1304
key_action_set_buffer (GtkWidget * wid, GdkEventKey * evt, char *d1, char *d2,
1305
struct session *sess)
1312
SPELL_ENTRY_SET_TEXT (wid, d1);
1313
SPELL_ENTRY_SET_POS (wid, -1);
1319
key_action_history_up (GtkWidget * wid, GdkEventKey * ent, char *d1, char *d2,
1320
struct session *sess)
1324
new_line = history_up (&sess->history, SPELL_ENTRY_GET_TEXT (wid));
1327
SPELL_ENTRY_SET_TEXT (wid, new_line);
1328
SPELL_ENTRY_SET_POS (wid, -1);
1335
key_action_history_down (GtkWidget * wid, GdkEventKey * ent, char *d1,
1336
char *d2, struct session *sess)
1340
new_line = history_down (&sess->history);
1343
SPELL_ENTRY_SET_TEXT (wid, new_line);
1344
SPELL_ENTRY_SET_POS (wid, -1);
1350
/* old data that we reuse */
1351
static struct gcomp_data old_gcomp;
1353
/* work on the data, ie return only channels */
1355
double_chan_cb (session *lsess, GList **list)
1357
if (lsess->type == SESS_CHANNEL)
1358
*list = g_list_prepend(*list, lsess->channel);
1362
/* convert a slist -> list. */
1364
chanlist_double_list (GSList *inlist)
1367
g_slist_foreach(inlist, (GFunc)double_chan_cb, &list);
1371
/* handle commands */
1373
double_cmd_cb (struct popup *pop, GList **list)
1375
*list = g_list_prepend(*list, pop->name);
1379
/* convert a slist -> list. */
1381
cmdlist_double_list (GSList *inlist)
1384
g_slist_foreach (inlist, (GFunc)double_cmd_cb, &list);
1389
gcomp_nick_func (char *data)
1392
return ((struct User *)data)->nick;
1397
key_action_tab_clean(void)
1401
old_gcomp.data[0] = 0;
1406
/* Used in the followig completers */
1407
#define COMP_BUF 2048
1409
/* For use in sorting the user list for completion */
1411
talked_recent_cmp (struct User *a, struct User *b)
1413
if (a->lasttalk < b->lasttalk)
1416
if (a->lasttalk > b->lasttalk)
1423
key_action_tab_comp (GtkWidget *t, GdkEventKey *entry, char *d1, char *d2,
1424
struct session *sess)
1426
int len = 0, elen = 0, i = 0, cursor_pos, ent_start = 0, comp = 0, found = 0,
1427
prefix_len, skip_len = 0, is_nick, is_cmd = 0;
1428
char buf[COMP_BUF], ent[CHANLEN], *postfix = NULL, *result, *ch;
1429
GList *list = NULL, *tmp_list = NULL;
1431
GCompletion *gcomp = NULL;
1433
/* force the IM Context to reset */
1434
SPELL_ENTRY_SET_EDITABLE (t, FALSE);
1435
SPELL_ENTRY_SET_EDITABLE (t, TRUE);
1437
text = SPELL_ENTRY_GET_TEXT (t);
1441
len = g_utf8_strlen (text, -1); /* must be null terminated */
1443
cursor_pos = SPELL_ENTRY_GET_POS (t);
1445
buf[0] = 0; /* make sure we don't get garbage in the buffer */
1447
/* handle "nick: " or "nick " or "#channel "*/
1448
ch = g_utf8_find_prev_char(text, g_utf8_offset_to_pointer(text,cursor_pos));
1449
if (ch && ch[0] == ' ')
1452
ch = g_utf8_find_prev_char(text, ch);
1456
cursor_pos = g_utf8_pointer_to_offset(text, ch);
1457
if (cursor_pos && (g_utf8_get_char_validated(ch, -1) == ':' ||
1458
g_utf8_get_char_validated(ch, -1) == ',' ||
1459
g_utf8_get_char_validated(ch, -1) == prefs.nick_suffix[0]))
1464
cursor_pos = g_utf8_pointer_to_offset(text, g_utf8_offset_to_pointer(ch, 1));
1469
/* store the text following the cursor for reinsertion later */
1470
if ((cursor_pos + skip_len) < len)
1471
postfix = g_utf8_offset_to_pointer(text, cursor_pos + skip_len);
1473
for (ent_start = cursor_pos; ; --ent_start)
1477
ch = g_utf8_offset_to_pointer(text, ent_start - 1);
1478
if (ch && ch[0] == ' ')
1482
if (ent_start == 0 && text[0] == prefs.cmdchar[0])
1488
prefix_len = ent_start;
1489
elen = cursor_pos - ent_start;
1491
g_utf8_strncpy (ent, g_utf8_offset_to_pointer (text, prefix_len), elen);
1493
is_nick = (ent[0] == '#' || ent[0] == '&' || is_cmd) ? 0 : 1;
1495
if (sess->type == SESS_DIALOG && is_nick)
1497
/* tab in a dialog completes the other person's name */
1498
if (rfc_ncasecmp (sess->channel, ent, elen) == 0)
1500
result = sess->channel;
1510
gcomp = g_completion_new((GCompletionFunc)gcomp_nick_func);
1511
tmp_list = userlist_double_list(sess); /* create a temp list so we can free the memory */
1512
if (prefs.completion_sort == 1) /* sort in last-talk order? */
1513
tmp_list = g_list_sort (tmp_list, (void *)talked_recent_cmp);
1517
gcomp = g_completion_new (NULL);
1520
tmp_list = cmdlist_double_list (command_list);
1521
for(i = 0; xc_cmds[i].name != NULL ; i++)
1523
tmp_list = g_list_prepend (tmp_list, xc_cmds[i].name);
1525
tmp_list = plugin_command_list(tmp_list);
1528
tmp_list = chanlist_double_list (sess_list);
1530
tmp_list = g_list_reverse(tmp_list); /* make the comp entries turn up in the right order */
1531
g_completion_set_compare (gcomp, (GCompletionStrncmpFunc)rfc_ncasecmp);
1534
g_completion_add_items (gcomp, tmp_list);
1535
g_list_free (tmp_list);
1538
if (comp && !(rfc_ncasecmp(old_gcomp.data, ent, old_gcomp.elen) == 0))
1540
key_action_tab_clean ();
1544
#if GLIB_CHECK_VERSION(2,4,0)
1545
list = g_completion_complete_utf8 (gcomp, comp ? old_gcomp.data : ent, &result);
1547
list = g_completion_complete (gcomp, comp ? old_gcomp.data : ent, &result);
1550
if (result == NULL) /* No matches found */
1552
g_completion_free(gcomp);
1556
if (comp) /* existing completion */
1558
while(list) /* find the current entry */
1560
if(rfc_ncasecmp(list->data, ent, elen) == 0)
1570
if (!(d1 && d1[0])) /* not holding down shift */
1572
if (g_list_next(list) == NULL)
1573
list = g_list_first(list);
1575
list = g_list_next(list);
1579
if (g_list_previous(list) == NULL)
1580
list = g_list_last(list);
1582
list = g_list_previous(list);
1585
result = (char*)list->data;
1590
g_completion_free(gcomp);
1596
strcpy(old_gcomp.data, ent);
1597
old_gcomp.elen = elen;
1599
/* Get the first nick and put out the data for future nickcompletes */
1600
if (prefs.completion_amount && g_list_length (list) <= prefs.completion_amount)
1603
result = (char*)list->data;
1607
/* bash style completion */
1608
if (g_list_next(list) != NULL)
1610
if (strlen (result) > elen) /* the largest common prefix is larger than nick, change the data */
1613
g_utf8_strncpy (buf, text, prefix_len);
1614
strncat (buf, result, COMP_BUF - prefix_len);
1615
cursor_pos = strlen (buf);
1617
#if !GLIB_CHECK_VERSION(2,4,0)
1618
g_utf8_validate (buf, -1, (const gchar **)&result);
1624
strncat (buf, postfix, COMP_BUF - cursor_pos -1);
1626
SPELL_ENTRY_SET_TEXT (t, buf);
1627
SPELL_ENTRY_SET_POS (t, g_utf8_pointer_to_offset(buf, buf + cursor_pos));
1634
len = strlen (buf); /* current buffer */
1635
elen = strlen (list->data); /* next item to add */
1636
if (len + elen + 2 >= COMP_BUF) /* +2 is space + null */
1638
PrintText (sess, buf);
1642
strcpy (buf + len, (char *) list->data);
1643
strcpy (buf + len + elen, " ");
1646
PrintText (sess, buf);
1647
g_completion_free(gcomp);
1650
/* Only one matching entry */
1652
result = list->data;
1660
g_utf8_strncpy(buf, text, prefix_len);
1661
strncat (buf, result, COMP_BUF - (prefix_len + 3)); /* make sure nicksuffix and space fits */
1662
if(!prefix_len && is_nick)
1663
strcat (buf, &prefs.nick_suffix[0]);
1665
cursor_pos = strlen (buf);
1667
strncat (buf, postfix, COMP_BUF - cursor_pos - 2);
1668
SPELL_ENTRY_SET_TEXT (t, buf);
1669
SPELL_ENTRY_SET_POS (t, g_utf8_pointer_to_offset(buf, buf + cursor_pos));
1672
g_completion_free(gcomp);
1678
key_action_comp_chng (GtkWidget * wid, GdkEventKey * ent, char *d1, char *d2,
1679
struct session *sess)
1681
key_action_tab_comp(wid, ent, d1, d2, sess);
1687
key_action_replace (GtkWidget * wid, GdkEventKey * ent, char *d1, char *d2,
1688
struct session *sess)
1690
replace_handle (wid);
1696
key_action_move_tab_left (GtkWidget * wid, GdkEventKey * ent, char *d1,
1697
char *d2, struct session *sess)
1699
mg_move_tab (sess, +1);
1700
return 2; /* don't allow default action */
1704
key_action_move_tab_right (GtkWidget * wid, GdkEventKey * ent, char *d1,
1705
char *d2, struct session *sess)
1707
mg_move_tab (sess, -1);
1708
return 2; /* -''- */
1712
key_action_move_tab_family_left (GtkWidget * wid, GdkEventKey * ent, char *d1,
1713
char *d2, struct session *sess)
1715
mg_move_tab_family (sess, +1);
1716
return 2; /* don't allow default action */
1720
key_action_move_tab_family_right (GtkWidget * wid, GdkEventKey * ent, char *d1,
1721
char *d2, struct session *sess)
1723
mg_move_tab_family (sess, -1);
1724
return 2; /* -''- */
1728
key_action_put_history (GtkWidget * wid, GdkEventKey * ent, char *d1,
1729
char *d2, struct session *sess)
1731
history_add (&sess->history, SPELL_ENTRY_GET_TEXT (wid));
1732
SPELL_ENTRY_SET_TEXT (wid, "");
1733
return 2; /* -''- */
1740
#define STATE_SHIFT GDK_SHIFT_MASK
1741
#define STATE_ALT GDK_MOD1_MASK
1742
#define STATE_CTRL GDK_CONTROL_MASK
1745
replace_handle (GtkWidget *t)
1747
const char *text, *postfix_pnt;
1749
GSList *list = replace_list;
1755
text = SPELL_ENTRY_GET_TEXT (t);
1757
len = strlen (text);
1761
for (c = len - 1; c > 0; c--)
1769
if (len - c >= (sizeof (word) - 12))
1773
memcpy (word, &text[c], len - c);
1775
len = strlen (word);
1776
if (word[0] == '\'' && word[len] == '\'')
1779
for (c = 0; c < len; c++)
1781
if (word[c] == '\'')
1783
postfix_pnt = &word[c + 1];
1789
if (postfix_pnt != NULL)
1791
if (strlen (postfix_pnt) > sizeof (postfix) - 12)
1793
strcpy (postfix, postfix_pnt);
1797
pop = (struct popup *) list->data;
1798
if (strcmp (pop->name, word) == 0)
1800
memcpy (outbuf, text, xlen);
1802
if (postfix_pnt == NULL)
1803
snprintf (word, sizeof (word), "%s", pop->cmd);
1805
snprintf (word, sizeof (word), "%s%s", pop->cmd, postfix);
1806
strcat (outbuf, word);
1807
SPELL_ENTRY_SET_TEXT (t, outbuf);
1808
SPELL_ENTRY_SET_POS (t, -1);