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)
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:\x0f\nD2!\n\n"\
406 "C\nb\nInsert in Buffer\nD1:\x02\nD2!\n\n"\
407 "C\nk\nInsert in Buffer\nD1:\x03\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 list
= g_completion_complete_utf8 (gcomp
, comp
? old_gcomp
.data
: ent
, &result
);
1546 if (result
== NULL
) /* No matches found */
1548 g_completion_free(gcomp
);
1552 if (comp
) /* existing completion */
1554 while(list
) /* find the current entry */
1556 if(rfc_ncasecmp(list
->data
, ent
, elen
) == 0)
1566 if (!(d1
&& d1
[0])) /* not holding down shift */
1568 if (g_list_next(list
) == NULL
)
1569 list
= g_list_first(list
);
1571 list
= g_list_next(list
);
1575 if (g_list_previous(list
) == NULL
)
1576 list
= g_list_last(list
);
1578 list
= g_list_previous(list
);
1581 result
= (char*)list
->data
;
1586 g_completion_free(gcomp
);
1592 strcpy(old_gcomp
.data
, ent
);
1593 old_gcomp
.elen
= elen
;
1595 /* Get the first nick and put out the data for future nickcompletes */
1596 if (prefs
.completion_amount
&& g_list_length (list
) <= prefs
.completion_amount
)
1599 result
= (char*)list
->data
;
1603 /* bash style completion */
1604 if (g_list_next(list
) != NULL
)
1606 if (strlen (result
) > elen
) /* the largest common prefix is larger than nick, change the data */
1609 g_utf8_strncpy (buf
, text
, prefix_len
);
1610 strncat (buf
, result
, COMP_BUF
- prefix_len
);
1611 cursor_pos
= strlen (buf
);
1613 #if !GLIB_CHECK_VERSION(2,4,0)
1614 g_utf8_validate (buf
, -1, (const gchar
**)&result
);
1620 strncat (buf
, postfix
, COMP_BUF
- cursor_pos
-1);
1622 SPELL_ENTRY_SET_TEXT (t
, buf
);
1623 SPELL_ENTRY_SET_POS (t
, g_utf8_pointer_to_offset(buf
, buf
+ cursor_pos
));
1630 len
= strlen (buf
); /* current buffer */
1631 elen
= strlen (list
->data
); /* next item to add */
1632 if (len
+ elen
+ 2 >= COMP_BUF
) /* +2 is space + null */
1634 PrintText (sess
, buf
);
1638 strcpy (buf
+ len
, (char *) list
->data
);
1639 strcpy (buf
+ len
+ elen
, " ");
1642 PrintText (sess
, buf
);
1643 g_completion_free(gcomp
);
1646 /* Only one matching entry */
1648 result
= list
->data
;
1656 g_utf8_strncpy(buf
, text
, prefix_len
);
1657 strncat (buf
, result
, COMP_BUF
- (prefix_len
+ 3)); /* make sure nicksuffix and space fits */
1658 if(!prefix_len
&& is_nick
)
1659 strcat (buf
, &prefs
.nick_suffix
[0]);
1661 cursor_pos
= strlen (buf
);
1663 strncat (buf
, postfix
, COMP_BUF
- cursor_pos
- 2);
1664 SPELL_ENTRY_SET_TEXT (t
, buf
);
1665 SPELL_ENTRY_SET_POS (t
, g_utf8_pointer_to_offset(buf
, buf
+ cursor_pos
));
1668 g_completion_free(gcomp
);
1674 key_action_comp_chng (GtkWidget
* wid
, GdkEventKey
* ent
, char *d1
, char *d2
,
1675 struct session
*sess
)
1677 key_action_tab_comp(wid
, ent
, d1
, d2
, sess
);
1683 key_action_replace (GtkWidget
* wid
, GdkEventKey
* ent
, char *d1
, char *d2
,
1684 struct session
*sess
)
1686 replace_handle (wid
);
1692 key_action_move_tab_left (GtkWidget
* wid
, GdkEventKey
* ent
, char *d1
,
1693 char *d2
, struct session
*sess
)
1695 mg_move_tab (sess
, +1);
1696 return 2; /* don't allow default action */
1700 key_action_move_tab_right (GtkWidget
* wid
, GdkEventKey
* ent
, char *d1
,
1701 char *d2
, struct session
*sess
)
1703 mg_move_tab (sess
, -1);
1704 return 2; /* -''- */
1708 key_action_move_tab_family_left (GtkWidget
* wid
, GdkEventKey
* ent
, char *d1
,
1709 char *d2
, struct session
*sess
)
1711 mg_move_tab_family (sess
, +1);
1712 return 2; /* don't allow default action */
1716 key_action_move_tab_family_right (GtkWidget
* wid
, GdkEventKey
* ent
, char *d1
,
1717 char *d2
, struct session
*sess
)
1719 mg_move_tab_family (sess
, -1);
1720 return 2; /* -''- */
1724 key_action_put_history (GtkWidget
* wid
, GdkEventKey
* ent
, char *d1
,
1725 char *d2
, struct session
*sess
)
1727 history_add (&sess
->history
, SPELL_ENTRY_GET_TEXT (wid
));
1728 SPELL_ENTRY_SET_TEXT (wid
, "");
1729 return 2; /* -''- */
1736 #define STATE_SHIFT GDK_SHIFT_MASK
1737 #define STATE_ALT GDK_MOD1_MASK
1738 #define STATE_CTRL GDK_CONTROL_MASK
1741 replace_handle (GtkWidget
*t
)
1743 const char *text
, *postfix_pnt
;
1745 GSList
*list
= replace_list
;
1751 text
= SPELL_ENTRY_GET_TEXT (t
);
1753 len
= strlen (text
);
1757 for (c
= len
- 1; c
> 0; c
--)
1765 if (len
- c
>= (sizeof (word
) - 12))
1769 memcpy (word
, &text
[c
], len
- c
);
1771 len
= strlen (word
);
1772 if (word
[0] == '\'' && word
[len
] == '\'')
1775 for (c
= 0; c
< len
; c
++)
1777 if (word
[c
] == '\'')
1779 postfix_pnt
= &word
[c
+ 1];
1785 if (postfix_pnt
!= NULL
)
1787 if (strlen (postfix_pnt
) > sizeof (postfix
) - 12)
1789 strcpy (postfix
, postfix_pnt
);
1793 pop
= (struct popup
*) list
->data
;
1794 if (strcmp (pop
->name
, word
) == 0)
1796 memcpy (outbuf
, text
, xlen
);
1798 if (postfix_pnt
== NULL
)
1799 snprintf (word
, sizeof (word
), "%s", pop
->cmd
);
1801 snprintf (word
, sizeof (word
), "%s%s", pop
->cmd
, postfix
);
1802 strcat (outbuf
, word
);
1803 SPELL_ENTRY_SET_TEXT (t
, outbuf
);
1804 SPELL_ENTRY_SET_POS (t
, -1);