remove all remaining WIN32 code
[rofl0r-ixchat.git] / src / fe-gtk / fkeys.c
blob339c80b2db0dc06a4bcce71ac3d8ec4032dea530
1 /* X-Chat
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
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <unistd.h>
24 #include <string.h>
25 #include <fcntl.h>
26 #include <ctype.h>
28 #include "fe-gtk.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>
53 #include "gtkutil.h"
54 #include "menu.h"
55 #include "xtext.h"
56 #include "palette.h"
57 #include "maingui.h"
58 #include "textgui.h"
59 #include "fkeys.h"
61 #ifdef USE_GTKSPELL
62 #include <gtk/gtktextview.h>
63 #endif
65 static void replace_handle (GtkWidget * wid);
66 void key_action_tab_clean (void);
68 /***************** Key Binding Code ******************/
70 /* NOTES:
72 To add a new action:
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
78 --AGL
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
89 struct key_binding
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;
99 struct key_action
101 int (*handler) (GtkWidget * wid, GdkEventKey * evt, char *d1, char *d2,
102 struct session * sess);
103 char *name;
104 char *help;
107 struct gcomp_data
109 char data[CHANLEN];
110 int elen;
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,
117 char *d1, char *d2,
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,
138 char *d1, char *d2,
139 struct session *sess);
140 static int key_action_move_tab_right (GtkWidget * wid, GdkEventKey * evt,
141 char *d1, char *d2,
142 struct session *sess);
143 static int key_action_move_tab_family_left (GtkWidget * wid, GdkEventKey * evt,
144 char *d1, char *d2,
145 struct session *sess);
146 static int key_action_move_tab_family_right (GtkWidget * wid, GdkEventKey * evt,
147 char *d1, char *d2,
148 struct session *sess);
149 static int key_action_put_history (GtkWidget * wid, GdkEventKey * evt,
150 char *d1, char *d2,
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")},
190 void
191 key_init ()
193 keys_root = NULL;
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);
203 static char *
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:
214 0) Return
215 1) Find next
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 {
225 the entry widget
226 the Gdk event
227 data 1
228 data 2
229 session struct
231 * key bindings are stored in a linked list of key_binding structs
232 * which looks like {
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
242 --AGL
246 gboolean
247 key_handle_key_press (GtkWidget *wid, GdkEventKey *evt, session *sess)
249 struct key_binding *kb, *last = NULL;
250 int keyval = evt->keyval;
251 int mod, n;
252 GSList *list;
254 /* where did this event come from? */
255 list = sess_list;
256 while (list)
258 sess = list->data;
259 if (sess->gui->input_box == wid)
261 if (sess->gui->is_tab)
262 sess = current_tab;
263 break;
265 list = list->next;
267 if (!list)
268 return FALSE;
269 current_sess = sess;
271 if (plugin_emit_keypress (sess, evt->state, evt->keyval, evt->length, evt->string))
272 return 1;
274 /* maybe the plugin closed this tab? */
275 if (!is_session (sess))
276 return 1;
278 mod = evt->state & (STATE_CTRL | STATE_ALT | STATE_SHIFT);
280 kb = keys_root;
281 while (kb)
283 if (kb->keyval == keyval && kb->mod == mod)
285 if (kb->action < 0 || kb->action > KEY_MAX_ACTIONS)
286 return 0;
288 /* Bump this binding to the top of the list */
289 if (last != NULL)
291 last->next = kb->next;
292 kb->next = keys_root;
293 keys_root = kb;
295 /* Run the function */
296 n = key_actions[kb->action].handler (wid, evt, kb->data1,
297 kb->data2, sess);
298 switch (n)
300 case 0:
301 return 1;
302 case 2:
303 g_signal_stop_emission_by_name (G_OBJECT (wid),
304 "key_press_event");
305 return 1;
308 last = kb;
309 kb = kb->next;
312 switch (keyval)
314 case GDK_space:
315 key_action_tab_clean ();
316 break;
318 #if defined(USE_GTKSPELL)
319 /* gtktextview has no 'activate' event, so we trap ENTER here */
320 case GDK_Return:
321 case GDK_KP_Enter:
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);
327 #endif
330 return 0;
333 /* Walks keys_root and free()'s everything */
334 /*static void
335 key_free_all ()
337 struct key_binding *cur, *next;
339 cur = keys_root;
340 while (cur)
342 next = cur->next;
343 if (cur->data1)
344 free (cur->data1);
345 if (cur->data2)
346 free (cur->data2);
347 free (cur);
348 cur = next;
350 keys_root = NULL;
353 /* Turns mod flags into a C-A-S string */
354 static char *
355 key_make_mod_str (int mod, char *buf)
357 int i = 0;
359 if (mod & STATE_CTRL)
361 if (i)
362 buf[i++] = '-';
363 buf[i++] = 'C';
365 if (mod & STATE_ALT)
367 if (i)
368 buf[i++] = '-';
369 buf[i++] = 'A';
371 if (mod & STATE_SHIFT)
373 if (i)
374 buf[i++] = '-';
375 buf[i++] = 'S';
377 buf[i] = 0;
378 return buf;
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;
389 static void
390 key_load_defaults ()
392 /* This is the default config */
393 #define defcfg \
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"
426 int fd;
428 fd = xchat_open_file ("keybindings.conf", O_CREAT | O_TRUNC | O_WRONLY, 0x180, XOF_DOMODE);
429 if (fd < 0)
430 /* ???!!! */
431 return;
433 write (fd, defcfg, strlen (defcfg));
434 close (fd);
437 static void
438 key_dialog_close ()
440 key_dialog = NULL;
441 key_save_kbs (NULL);
444 static void
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));
457 kb->keyval = 0;
458 kb->keyname = NULL;
459 kb->action = -1;
460 kb->mod = 0;
461 kb->data1 = kb->data2 = NULL;
462 kb->next = keys_root;
464 keys_root = kb;
466 gtk_clist_set_row_data (GTK_CLIST (list),
467 gtk_clist_append (GTK_CLIST (list), strs), kb);
471 static void
472 key_dialog_delete (GtkWidget * button, GtkCList * list)
474 struct key_binding *kb, *cur, *last;
475 int row = gtkutil_clist_selection ((GtkWidget *) list);
477 if (row != -1)
479 kb = gtk_clist_get_row_data (list, row);
480 cur = keys_root;
481 last = NULL;
482 while (cur)
484 if (cur == kb)
486 if (last)
487 last->next = kb->next;
488 else
489 keys_root = kb->next;
491 if (kb->data1)
492 free (kb->data1);
493 if (kb->data2)
494 free (kb->data2);
495 free (kb);
496 gtk_clist_remove (list, row);
497 return;
499 last = cur;
500 cur = cur->next;
502 printf ("*** key_dialog_delete: couldn't find kb in list!\n");
503 /*if (getenv ("XCHAT_DEBUG"))
504 abort ();*/
508 static void
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;
518 static void
519 key_dialog_sel_act (GtkWidget * un, int num)
521 int row = gtkutil_clist_selection (key_dialog_kb_clist);
522 struct key_binding *kb;
524 if (row != -1)
526 kb = gtk_clist_get_row_data (GTK_CLIST (key_dialog_kb_clist), row);
527 kb->action = num;
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));
537 static void
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);
543 if (kb == NULL)
545 printf ("*** key_dialog_sel_row: kb == NULL\n");
546 abort ();
548 if (kb->action > -1 && kb->action <= KEY_MAX_ACTIONS)
550 gtk_option_menu_set_history (GTK_OPTION_MENU (key_dialog_act_menu),
551 kb->action);
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);
564 if (kb->data1)
565 gtk_entry_set_text (GTK_ENTRY (key_dialog_ent_d1), kb->data1);
566 else
567 gtk_entry_set_text (GTK_ENTRY (key_dialog_ent_d1), "");
569 if (kb->data2)
570 gtk_entry_set_text (GTK_ENTRY (key_dialog_ent_d2), kb->data2);
571 else
572 gtk_entry_set_text (GTK_ENTRY (key_dialog_ent_d2), "");
574 if (kb->keyname)
575 gtk_entry_set_text (GTK_ENTRY (key_dialog_ent_key), kb->keyname);
576 else
577 gtk_entry_set_text (GTK_ENTRY (key_dialog_ent_key), "");
580 static void
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;
586 char buf[32];
588 if (row == -1)
589 return;
591 kb = gtk_clist_get_row_data (GTK_CLIST (key_dialog_kb_clist), row);
592 if (state)
593 kb->mod |= kstate;
594 else
595 kb->mod &= ~kstate;
597 gtk_clist_set_text (GTK_CLIST (key_dialog_kb_clist), row, 0,
598 key_make_mod_str (kb->mod, buf));
601 static GtkWidget *
602 key_dialog_make_toggle (char *label, void *callback, void *option,
603 GtkWidget * box)
605 GtkWidget *wid;
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);
614 return wid;
617 static GtkWidget *
618 key_dialog_make_entry (char *label, char *act, void *callback, void *option,
619 GtkWidget * box)
621 GtkWidget *wid, *hbox;;
623 hbox = gtk_hbox_new (0, 2);
624 if (label)
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 ();
631 if (act)
633 gtk_signal_connect (GTK_OBJECT (wid), act, GTK_SIGNAL_FUNC (callback),
634 option);
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);
642 return wid;
645 static void
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), "");
653 if (row == -1)
654 return;
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");
664 static void
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;
670 char *buf;
671 int len = strlen (text);
673 len++;
675 if (row == -1)
676 return;
678 kb = gtk_clist_get_row_data (GTK_CLIST (key_dialog_kb_clist), row);
679 if (d == 0)
680 { /* using data1 */
681 if (kb->data1)
682 free (kb->data1);
683 buf = (char *) malloc (len);
684 memcpy (buf, text, len);
685 kb->data1 = buf;
686 gtk_clist_set_text (GTK_CLIST (key_dialog_kb_clist), row, 3, text);
687 } else
689 if (kb->data2)
690 free (kb->data2);
691 buf = (char *) malloc (len);
692 memcpy (buf, text, len);
693 kb->data2 = buf;
694 gtk_clist_set_text (GTK_CLIST (key_dialog_kb_clist), row, 4, text);
698 void
699 key_dialog_show ()
701 GtkWidget *vbox, *hbox, *list, *vbox2, *wid, *wid2, *wid3, *hbox2;
702 struct key_binding *kb;
703 gchar *titles[] = { NULL, NULL, NULL, "1", "2" };
704 char temp[32];
705 int i;
707 titles[0] = _("Mod");
708 titles[1] = _("Key");
709 titles[2] = _("Action");
711 if (key_dialog)
713 mg_bring_tofront (key_dialog);
714 return;
717 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);
731 kb = keys_root;
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);
738 while (kb)
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>");
744 else
745 titles[2] = key_actions[kb->action].name;
746 if (kb->data1)
747 titles[3] = kb->data1;
748 else
749 titles[3] = _("<none>");
751 if (kb->data2)
752 titles[4] = kb->data2;
753 else
754 titles[4] = _("<none>");
756 gtk_clist_set_row_data (GTK_CLIST (list),
757 gtk_clist_append (GTK_CLIST (list), titles),
758 kb);
760 kb = kb->next;
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,
805 vbox2);
807 key_dialog_ent_d1 = key_dialog_make_entry (_("Data 1"), "activate",
808 key_dialog_set_data, NULL,
809 vbox2);
810 key_dialog_ent_d2 = key_dialog_make_entry (_("Data 2"), "activate",
811 key_dialog_set_data,
812 (void *) 1, vbox2);
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),
820 channelwin_pix,
821 prefs.transparent);
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);
837 static void
838 key_save_kbs (char *fn)
840 int fd, i;
841 char buf[512];
842 struct key_binding *kb;
844 if (!fn)
845 fd = xchat_open_file ("keybindings.conf", O_CREAT | O_TRUNC | O_WRONLY,
846 0x180, XOF_DOMODE);
847 else
848 fd = xchat_open_file (fn, O_CREAT | O_TRUNC | O_WRONLY,
849 0x180, XOF_DOMODE | XOF_FULLPATH);
850 if (fd < 0)
852 fe_message (_("Error opening keys config file\n"), FE_MSG_ERROR);
853 return;
855 write (fd, buf,
856 snprintf (buf, 510, "# XChat key bindings config file\n\n"));
858 kb = keys_root;
859 i = 0;
861 while (kb)
863 if (kb->keyval == -1 || kb->keyname == NULL || kb->action < 0)
865 kb = kb->next;
866 continue;
868 i = 0;
869 if (kb->mod & STATE_CTRL)
871 i++;
872 write (fd, "C", 1);
874 if (kb->mod & STATE_ALT)
876 i++;
877 write (fd, "A", 1);
879 if (kb->mod & STATE_SHIFT)
881 i++;
882 write (fd, "S", 1);
884 if (i == 0)
885 write (fd, "None\n", 5);
886 else
887 write (fd, "\n", 1);
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));
893 else
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));
898 else
899 write (fd, "D2!\n", 4);
901 write (fd, "\n", 1);
903 kb = kb->next;
906 close (fd);
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 */
912 static inline int
913 key_load_kbs_helper_mod (char *in, int *out)
915 int n, len, mod = 0;
917 /* First strip off the fluff */
918 while (in[0] == ' ' || in[0] == '\t')
919 in++;
920 len = strlen (in);
921 while (in[len] == ' ' || in[len] == '\t')
923 in[len] = 0;
924 len--;
927 if (strcmp (in, "None") == 0)
929 *out = 0;
930 return 0;
932 for (n = 0; n < len; n++)
934 switch (in[n])
936 case 'C':
937 mod |= STATE_CTRL;
938 break;
939 case 'A':
940 mod |= STATE_ALT;
941 break;
942 case 'S':
943 mod |= STATE_SHIFT;
944 break;
945 default:
946 return 1;
950 *out = mod;
951 return 0;
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 */
964 static int
965 key_load_kbs (char *filename)
967 char *buf, *ibuf;
968 struct stat st;
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);
974 else
975 fd = xchat_open_file (filename, O_RDONLY, 0, XOF_FULLPATH);
976 if (fd < 0)
977 return 1;
978 if (fstat (fd, &st) != 0)
979 return 1;
980 ibuf = malloc (st.st_size);
981 read (fd, ibuf, st.st_size);
982 close (fd);
984 while (buf_get_line (ibuf, &buf, &pnt, st.st_size))
986 if (buf[0] == '#')
987 continue;
988 if (strlen (buf) == 0)
989 continue;
991 switch (state)
993 case KBSTATE_MOD:
994 kb = (struct key_binding *) malloc (sizeof (struct key_binding));
995 if (key_load_kbs_helper_mod (buf, &kb->mod))
996 goto corrupt_file;
997 state = KBSTATE_KEY;
998 continue;
999 case KBSTATE_KEY:
1000 /* First strip off the fluff */
1001 while (buf[0] == ' ' || buf[0] == '\t')
1002 buf++;
1003 len = strlen (buf);
1004 while (buf[len] == ' ' || buf[len] == '\t')
1006 buf[len] = 0;
1007 len--;
1010 n = gdk_keyval_from_name (buf);
1011 if (n == 0)
1013 /* Unknown keyname, abort */
1014 if (last)
1015 last->next = NULL;
1016 free (ibuf);
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);
1022 free (ibuf);
1023 return 2;
1025 kb->keyname = gdk_keyval_name (n);
1026 kb->keyval = n;
1028 state = KBSTATE_ACT;
1029 continue;
1030 case KBSTATE_ACT:
1031 /* First strip off the fluff */
1032 while (buf[0] == ' ' || buf[0] == '\t')
1033 buf++;
1034 len = strlen (buf);
1035 while (buf[len] == ' ' || buf[len] == '\t')
1037 buf[len] = 0;
1038 len--;
1041 for (n = 0; n < KEY_MAX_ACTIONS + 1; n++)
1043 if (strcmp (key_actions[n].name, buf) == 0)
1045 kb->action = n;
1046 break;
1050 if (n == KEY_MAX_ACTIONS + 1)
1052 if (last)
1053 last->next = NULL;
1054 free (ibuf);
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);
1060 free (ibuf);
1061 return 3;
1063 state = KBSTATE_DT1;
1064 continue;
1065 case KBSTATE_DT1:
1066 case KBSTATE_DT2:
1067 if (state == KBSTATE_DT1)
1068 kb->data1 = kb->data2 = NULL;
1070 while (buf[0] == ' ' || buf[0] == '\t')
1071 buf++;
1073 if (buf[0] != 'D')
1075 free (ibuf);
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);
1081 free (ibuf);
1082 return 4;
1084 switch (buf[1])
1086 case '1':
1087 if (state != KBSTATE_DT1)
1088 goto corrupt_file;
1089 break;
1090 case '2':
1091 if (state != KBSTATE_DT2)
1092 goto corrupt_file;
1093 break;
1094 default:
1095 goto corrupt_file;
1098 if (buf[2] == ':')
1100 len = strlen (buf);
1101 /* Add one for the NULL, subtract 3 for the "Dx:" */
1102 len++;
1103 len -= 3;
1104 if (state == KBSTATE_DT1)
1106 kb->data1 = malloc (len);
1107 memcpy (kb->data1, &buf[3], len);
1108 } else
1110 kb->data2 = malloc (len);
1111 memcpy (kb->data2, &buf[3], len);
1113 } else if (buf[2] == '!')
1115 if (state == KBSTATE_DT1)
1116 kb->data1 = NULL;
1117 else
1118 kb->data2 = NULL;
1120 if (state == KBSTATE_DT1)
1122 state = KBSTATE_DT2;
1123 continue;
1124 } else
1126 if (last)
1127 last->next = kb;
1128 else
1129 keys_root = kb;
1130 last = kb;
1132 state = KBSTATE_MOD;
1135 continue;
1138 if (last)
1139 last->next = NULL;
1140 free (ibuf);
1141 return 0;
1143 corrupt_file:
1144 /*if (getenv ("XCHAT_DEBUG"))
1145 abort ();*/
1146 snprintf (ibuf, 1024,
1147 _("Key bindings config file is corrupt, load aborted\n"
1148 "Please fix %s/keybindings.conf\n"),
1149 get_xdir_utf8 ());
1150 fe_message (ibuf, FE_MSG_ERROR);
1151 free (ibuf);
1152 return 5;
1155 /* ***** Key actions start here *********** */
1157 /* See the NOTES above --AGL */
1159 /* "Run command" */
1160 static int
1161 key_action_handle_command (GtkWidget * wid, GdkEventKey * evt, char *d1,
1162 char *d2, struct session *sess)
1164 int ii, oi, len;
1165 char out[2048], d = 0;
1167 if (!d1)
1168 return 0;
1170 len = strlen (d1);
1172 /* Replace each "\n" substring with '\n' */
1173 for (ii = oi = 0; ii < len; ii++)
1175 d = d1[ii];
1176 if (d == '\\')
1178 ii++;
1179 d = d1[ii];
1180 if (d == 'n')
1181 out[oi++] = '\n';
1182 else if (d == '\\')
1183 out[oi++] = '\\';
1184 else
1186 out[oi++] = '\\';
1187 out[oi++] = d;
1189 continue;
1191 out[oi++] = d;
1193 out[oi] = 0;
1195 handle_multiline (sess, out, 0, 0);
1196 return 0;
1199 static int
1200 key_action_page_switch (GtkWidget * wid, GdkEventKey * evt, char *d1,
1201 char *d2, struct session *sess)
1203 int len, i, num;
1205 if (!d1)
1206 return 1;
1208 len = strlen (d1);
1209 if (!len)
1210 return 1;
1212 for (i = 0; i < len; i++)
1214 if (d1[i] < '0' || d1[i] > '9')
1216 if (i == 0 && (d1[i] == '+' || d1[i] == '-'))
1217 continue;
1218 else
1219 return 1;
1223 num = atoi (d1);
1224 if (!d2)
1225 num--;
1226 if (!d2 || d2[0] == 0)
1227 mg_switch_page (FALSE, num);
1228 else
1229 mg_switch_page (TRUE, num);
1230 return 0;
1234 key_action_insert (GtkWidget * wid, GdkEventKey * evt, char *d1, char *d2,
1235 struct session *sess)
1237 int tmp_pos;
1239 if (!d1)
1240 return 1;
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);
1245 return 2;
1248 /* handles PageUp/Down keys */
1249 static int
1250 key_action_scroll_page (GtkWidget * wid, GdkEventKey * evt, char *d1,
1251 char *d2, struct session *sess)
1253 int value, end;
1254 GtkAdjustment *adj;
1255 enum scroll_type { PAGE_UP, PAGE_DOWN, LINE_UP, LINE_DOWN };
1256 int type = PAGE_DOWN;
1258 if (d1)
1260 if (!strcasecmp (d1, "up"))
1261 type = PAGE_UP;
1262 else if (!strcasecmp (d1, "+1"))
1263 type = LINE_DOWN;
1264 else if (!strcasecmp (d1, "-1"))
1265 type = LINE_UP;
1268 if (!sess)
1269 return 0;
1271 adj = GTK_RANGE (sess->gui->vscrollbar)->adjustment;
1272 end = adj->upper - adj->lower - adj->page_size;
1274 switch (type)
1276 case LINE_UP:
1277 value = adj->value - 1.0;
1278 break;
1280 case LINE_DOWN:
1281 value = adj->value + 1.0;
1282 break;
1284 case PAGE_UP:
1285 value = adj->value - (adj->page_size - 1);
1286 break;
1288 default: /* PAGE_DOWN */
1289 value = adj->value + (adj->page_size - 1);
1290 break;
1293 if (value < 0)
1294 value = 0;
1295 if (value > end)
1296 value = end;
1298 gtk_adjustment_set_value (adj, value);
1300 return 0;
1303 static int
1304 key_action_set_buffer (GtkWidget * wid, GdkEventKey * evt, char *d1, char *d2,
1305 struct session *sess)
1307 if (!d1)
1308 return 1;
1309 if (d1[0] == 0)
1310 return 1;
1312 SPELL_ENTRY_SET_TEXT (wid, d1);
1313 SPELL_ENTRY_SET_POS (wid, -1);
1315 return 2;
1318 static int
1319 key_action_history_up (GtkWidget * wid, GdkEventKey * ent, char *d1, char *d2,
1320 struct session *sess)
1322 char *new_line;
1324 new_line = history_up (&sess->history, SPELL_ENTRY_GET_TEXT (wid));
1325 if (new_line)
1327 SPELL_ENTRY_SET_TEXT (wid, new_line);
1328 SPELL_ENTRY_SET_POS (wid, -1);
1331 return 2;
1334 static int
1335 key_action_history_down (GtkWidget * wid, GdkEventKey * ent, char *d1,
1336 char *d2, struct session *sess)
1338 char *new_line;
1340 new_line = history_down (&sess->history);
1341 if (new_line)
1343 SPELL_ENTRY_SET_TEXT (wid, new_line);
1344 SPELL_ENTRY_SET_POS (wid, -1);
1347 return 2;
1350 /* old data that we reuse */
1351 static struct gcomp_data old_gcomp;
1353 /* work on the data, ie return only channels */
1354 static int
1355 double_chan_cb (session *lsess, GList **list)
1357 if (lsess->type == SESS_CHANNEL)
1358 *list = g_list_prepend(*list, lsess->channel);
1359 return TRUE;
1362 /* convert a slist -> list. */
1363 static GList *
1364 chanlist_double_list (GSList *inlist)
1366 GList *list = NULL;
1367 g_slist_foreach(inlist, (GFunc)double_chan_cb, &list);
1368 return list;
1371 /* handle commands */
1372 static int
1373 double_cmd_cb (struct popup *pop, GList **list)
1375 *list = g_list_prepend(*list, pop->name);
1376 return TRUE;
1379 /* convert a slist -> list. */
1380 static GList *
1381 cmdlist_double_list (GSList *inlist)
1383 GList *list = NULL;
1384 g_slist_foreach (inlist, (GFunc)double_cmd_cb, &list);
1385 return list;
1388 static char *
1389 gcomp_nick_func (char *data)
1391 if (data)
1392 return ((struct User *)data)->nick;
1393 return "";
1396 void
1397 key_action_tab_clean(void)
1399 if (old_gcomp.elen)
1401 old_gcomp.data[0] = 0;
1402 old_gcomp.elen = 0;
1406 /* Used in the followig completers */
1407 #define COMP_BUF 2048
1409 /* For use in sorting the user list for completion */
1410 static int
1411 talked_recent_cmp (struct User *a, struct User *b)
1413 if (a->lasttalk < b->lasttalk)
1414 return -1;
1416 if (a->lasttalk > b->lasttalk)
1417 return 1;
1419 return 0;
1422 static int
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;
1430 const char *text;
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);
1438 if (text[0] == 0)
1439 return 1;
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] == ' ')
1451 skip_len++;
1452 ch = g_utf8_find_prev_char(text, ch);
1453 if (!ch)
1454 return 2;
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]))
1461 skip_len++;
1463 else
1464 cursor_pos = g_utf8_pointer_to_offset(text, g_utf8_offset_to_pointer(ch, 1));
1467 comp = skip_len;
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)
1475 if (ent_start == 0)
1476 break;
1477 ch = g_utf8_offset_to_pointer(text, ent_start - 1);
1478 if (ch && ch[0] == ' ')
1479 break;
1482 if (ent_start == 0 && text[0] == prefs.cmdchar[0])
1484 ent_start++;
1485 is_cmd = 1;
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;
1501 is_nick = 0;
1503 else
1504 return 2;
1506 else
1508 if (is_nick)
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);
1515 else
1517 gcomp = g_completion_new (NULL);
1518 if (is_cmd)
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);
1527 else
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);
1532 if (tmp_list)
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 ();
1541 comp = 0;
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);
1549 return 2;
1552 if (comp) /* existing completion */
1554 while(list) /* find the current entry */
1556 if(rfc_ncasecmp(list->data, ent, elen) == 0)
1558 found = 1;
1559 break;
1561 list = list->next;
1564 if (found)
1566 if (!(d1 && d1[0])) /* not holding down shift */
1568 if (g_list_next(list) == NULL)
1569 list = g_list_first(list);
1570 else
1571 list = g_list_next(list);
1573 else
1575 if (g_list_previous(list) == NULL)
1576 list = g_list_last(list);
1577 else
1578 list = g_list_previous(list);
1580 g_free(result);
1581 result = (char*)list->data;
1583 else
1585 g_free(result);
1586 g_completion_free(gcomp);
1587 return 2;
1590 else
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)
1598 g_free(result);
1599 result = (char*)list->data;
1601 else
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 */
1608 if (prefix_len)
1609 g_utf8_strncpy (buf, text, prefix_len);
1610 strncat (buf, result, COMP_BUF - prefix_len);
1611 cursor_pos = strlen (buf);
1612 g_free(result);
1613 #if !GLIB_CHECK_VERSION(2,4,0)
1614 g_utf8_validate (buf, -1, (const gchar **)&result);
1615 (*result) = 0;
1616 #endif
1617 if (postfix)
1619 strcat (buf, " ");
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));
1624 buf[0] = 0;
1626 else
1627 g_free(result);
1628 while (list)
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);
1635 buf[0] = 0;
1636 len = 0;
1638 strcpy (buf + len, (char *) list->data);
1639 strcpy (buf + len + elen, " ");
1640 list = list->next;
1642 PrintText (sess, buf);
1643 g_completion_free(gcomp);
1644 return 2;
1646 /* Only one matching entry */
1647 g_free(result);
1648 result = list->data;
1653 if(result)
1655 if (prefix_len)
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]);
1660 strcat (buf, " ");
1661 cursor_pos = strlen (buf);
1662 if (postfix)
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));
1667 if (gcomp)
1668 g_completion_free(gcomp);
1669 return 2;
1671 #undef COMP_BUF
1673 static int
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);
1678 return 2;
1682 static int
1683 key_action_replace (GtkWidget * wid, GdkEventKey * ent, char *d1, char *d2,
1684 struct session *sess)
1686 replace_handle (wid);
1687 return 1;
1691 static int
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 */
1699 static int
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; /* -''- */
1707 static int
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 */
1715 static int
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; /* -''- */
1723 static int
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; /* -''- */
1733 /* -------- */
1736 #define STATE_SHIFT GDK_SHIFT_MASK
1737 #define STATE_ALT GDK_MOD1_MASK
1738 #define STATE_CTRL GDK_CONTROL_MASK
1740 static void
1741 replace_handle (GtkWidget *t)
1743 const char *text, *postfix_pnt;
1744 struct popup *pop;
1745 GSList *list = replace_list;
1746 char word[256];
1747 char postfix[256];
1748 char outbuf[4096];
1749 int c, len, xlen;
1751 text = SPELL_ENTRY_GET_TEXT (t);
1753 len = strlen (text);
1754 if (len < 1)
1755 return;
1757 for (c = len - 1; c > 0; c--)
1759 if (text[c] == ' ')
1760 break;
1762 if (text[c] == ' ')
1763 c++;
1764 xlen = c;
1765 if (len - c >= (sizeof (word) - 12))
1766 return;
1767 if (len - c < 1)
1768 return;
1769 memcpy (word, &text[c], len - c);
1770 word[len - c] = 0;
1771 len = strlen (word);
1772 if (word[0] == '\'' && word[len] == '\'')
1773 return;
1774 postfix_pnt = NULL;
1775 for (c = 0; c < len; c++)
1777 if (word[c] == '\'')
1779 postfix_pnt = &word[c + 1];
1780 word[c] = 0;
1781 break;
1785 if (postfix_pnt != NULL)
1787 if (strlen (postfix_pnt) > sizeof (postfix) - 12)
1788 return;
1789 strcpy (postfix, postfix_pnt);
1791 while (list)
1793 pop = (struct popup *) list->data;
1794 if (strcmp (pop->name, word) == 0)
1796 memcpy (outbuf, text, xlen);
1797 outbuf[xlen] = 0;
1798 if (postfix_pnt == NULL)
1799 snprintf (word, sizeof (word), "%s", pop->cmd);
1800 else
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);
1805 return;
1807 list = list->next;