~jordunn/meenix/Master

« back to all changes in this revision

Viewing changes to src/gtk/fkeys.c

  • Committer: Jordan Dunn
  • Date: 2011-05-01 22:42:27 UTC
  • Revision ID: git-v1:1ba1788f6af88891278ff2926d536c40144bc581
MovingĀ filesĀ around

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* X-Chat
 
2
 * Copyright (C) 1998 Peter Zelezny.
 
3
 *
 
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.
 
8
 *
 
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.
 
13
 *
 
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
 
17
 */
 
18
 
 
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>
 
27
 
 
28
#include "fe-gtk.h"
 
29
 
 
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>
 
42
 
 
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"
 
60
 
 
61
#ifdef USE_GTKSPELL
 
62
#include <gtk/gtktextview.h>
 
63
#endif
 
64
 
 
65
static void replace_handle (GtkWidget * wid);
 
66
void key_action_tab_clean (void);
 
67
 
 
68
/***************** Key Binding Code ******************/
 
69
 
 
70
/* NOTES:
 
71
 
 
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
 
77
 
 
78
   --AGL
 
79
 
 
80
 */
 
81
 
 
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
 
88
 
 
89
struct key_binding
 
90
{
 
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;
 
97
};
 
98
 
 
99
struct key_action
 
100
{
 
101
        int (*handler) (GtkWidget * wid, GdkEventKey * evt, char *d1, char *d2,
 
102
                                                 struct session * sess);
 
103
        char *name;
 
104
        char *help;
 
105
};
 
106
 
 
107
struct gcomp_data
 
108
{
 
109
        char data[CHANLEN];
 
110
        int elen;
 
111
};
 
112
 
 
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);
 
152
 
 
153
static GtkWidget *key_dialog;
 
154
static struct key_binding *keys_root = NULL;
 
155
 
 
156
static const struct key_action key_actions[KEY_MAX_ACTIONS + 1] = {
 
157
 
 
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")},
 
188
};
 
189
 
 
190
void
 
191
key_init ()
 
192
{
 
193
        keys_root = NULL;
 
194
        if (key_load_kbs (NULL) == 1)
 
195
        {
 
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);
 
200
        }
 
201
}
 
202
 
 
203
static char *
 
204
key_get_key_name (int keyval)
 
205
{
 
206
        return gdk_keyval_name (gdk_keyval_to_lower (keyval));
 
207
}
 
208
 
 
209
/* Ok, here are the NOTES
 
210
 
 
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
 
217
 
 
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
 
230
   }
 
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;
 
239
   }
 
240
   * remember that is (data1 || data2) != NULL then they need to be free()'ed
 
241
 
 
242
   --AGL
 
243
 
 
244
 */
 
245
 
 
246
gboolean
 
247
key_handle_key_press (GtkWidget *wid, GdkEventKey *evt, session *sess)
 
248
{
 
249
        struct key_binding *kb, *last = NULL;
 
250
        int keyval = evt->keyval;
 
251
        int mod, n;
 
252
        GSList *list;
 
253
 
 
254
        /* where did this event come from? */
 
255
        list = sess_list;
 
256
        while (list)
 
257
        {
 
258
                sess = list->data;
 
259
                if (sess->gui->input_box == wid)
 
260
                {
 
261
                        if (sess->gui->is_tab)
 
262
                                sess = current_tab;
 
263
                        break;
 
264
                }
 
265
                list = list->next;
 
266
        }
 
267
        if (!list)
 
268
                return FALSE;
 
269
        current_sess = sess;
 
270
 
 
271
        if (plugin_emit_keypress (sess, evt->state, evt->keyval, evt->length, evt->string))
 
272
                return 1;
 
273
 
 
274
        /* maybe the plugin closed this tab? */
 
275
        if (!is_session (sess))
 
276
                return 1;
 
277
 
 
278
        mod = evt->state & (STATE_CTRL | STATE_ALT | STATE_SHIFT);
 
279
 
 
280
        kb = keys_root;
 
281
        while (kb)
 
282
        {
 
283
                if (kb->keyval == keyval && kb->mod == mod)
 
284
                {
 
285
                        if (kb->action < 0 || kb->action > KEY_MAX_ACTIONS)
 
286
                                return 0;
 
287
 
 
288
                        /* Bump this binding to the top of the list */
 
289
                        if (last != NULL)
 
290
                        {
 
291
                                last->next = kb->next;
 
292
                                kb->next = keys_root;
 
293
                                keys_root = kb;
 
294
                        }
 
295
                        /* Run the function */
 
296
                        n = key_actions[kb->action].handler (wid, evt, kb->data1,
 
297
                                                                                                                         kb->data2, sess);
 
298
                        switch (n)
 
299
                        {
 
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;
 
306
                        }
 
307
                }
 
308
                last = kb;
 
309
                kb = kb->next;
 
310
        }
 
311
 
 
312
        switch (keyval)
 
313
        {
 
314
        case GDK_space:
 
315
                key_action_tab_clean ();
 
316
                break;
 
317
 
 
318
#if defined(USE_GTKSPELL) && !defined(WIN32)
 
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))
 
323
                {
 
324
                        g_signal_stop_emission_by_name (G_OBJECT (wid), "key_press_event");
 
325
                        mg_inputbox_cb (wid, sess->gui);
 
326
                }
 
327
#endif
 
328
        }
 
329
 
 
330
        return 0;
 
331
}
 
332
 
 
333
/* Walks keys_root and free()'s everything */
 
334
/*static void
 
335
key_free_all ()
 
336
{
 
337
        struct key_binding *cur, *next;
 
338
 
 
339
        cur = keys_root;
 
340
        while (cur)
 
341
        {
 
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;
 
349
        }
 
350
        keys_root = NULL;
 
351
}*/
 
352
 
 
353
/* Turns mod flags into a C-A-S string */
 
354
static char *
 
355
key_make_mod_str (int mod, char *buf)
 
356
{
 
357
        int i = 0;
 
358
 
 
359
        if (mod & STATE_CTRL)
 
360
        {
 
361
                if (i)
 
362
                        buf[i++] = '-';
 
363
                buf[i++] = 'C';
 
364
        }
 
365
        if (mod & STATE_ALT)
 
366
        {
 
367
                if (i)
 
368
                        buf[i++] = '-';
 
369
                buf[i++] = 'A';
 
370
        }
 
371
        if (mod & STATE_SHIFT)
 
372
        {
 
373
                if (i)
 
374
                        buf[i++] = '-';
 
375
                buf[i++] = 'S';
 
376
        }
 
377
        buf[i] = 0;
 
378
        return buf;
 
379
}
 
380
 
 
381
/* ***** GUI code here ******************* */
 
382
 
 
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;
 
388
 
 
389
static void
 
390
key_load_defaults ()
 
391
{
 
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:\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"
 
426
        int fd;
 
427
 
 
428
        fd = xchat_open_file ("keybindings.conf", O_CREAT | O_TRUNC | O_WRONLY, 0x180, XOF_DOMODE);
 
429
        if (fd < 0)
 
430
                /* ???!!! */
 
431
                return;
 
432
 
 
433
        write (fd, defcfg, strlen (defcfg));
 
434
        close (fd);
 
435
}
 
436
 
 
437
static void
 
438
key_dialog_close ()
 
439
{
 
440
        key_dialog = NULL;
 
441
        key_save_kbs (NULL);
 
442
}
 
443
 
 
444
static void
 
445
key_dialog_add_new (GtkWidget * button, GtkCList * list)
 
446
{
 
447
        gchar *strs[] = { "", NULL, NULL, NULL, NULL };
 
448
        struct key_binding *kb;
 
449
 
 
450
        strs[1] = _("<none>");
 
451
        strs[2] = _("<none>");
 
452
        strs[3] = _("<none>");
 
453
        strs[4] = _("<none>");
 
454
 
 
455
        kb = malloc (sizeof (struct key_binding));
 
456
 
 
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;
 
463
 
 
464
        keys_root = kb;
 
465
 
 
466
        gtk_clist_set_row_data (GTK_CLIST (list),
 
467
                                                                        gtk_clist_append (GTK_CLIST (list), strs), kb);
 
468
 
 
469
}
 
470
 
 
471
static void
 
472
key_dialog_delete (GtkWidget * button, GtkCList * list)
 
473
{
 
474
        struct key_binding *kb, *cur, *last;
 
475
        int row = gtkutil_clist_selection ((GtkWidget *) list);
 
476
 
 
477
        if (row != -1)
 
478
        {
 
479
                kb = gtk_clist_get_row_data (list, row);
 
480
                cur = keys_root;
 
481
                last = NULL;
 
482
                while (cur)
 
483
                {
 
484
                        if (cur == kb)
 
485
                        {
 
486
                                if (last)
 
487
                                        last->next = kb->next;
 
488
                                else
 
489
                                        keys_root = kb->next;
 
490
 
 
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;
 
498
                        }
 
499
                        last = cur;
 
500
                        cur = cur->next;
 
501
                }
 
502
                printf ("*** key_dialog_delete: couldn't find kb in list!\n");
 
503
                /*if (getenv ("XCHAT_DEBUG"))
 
504
                        abort ();*/
 
505
        }
 
506
}
 
507
 
 
508
static void
 
509
key_print_text (GtkXText *xtext, char *text)
 
510
{
 
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;
 
516
}
 
517
 
 
518
static void
 
519
key_dialog_sel_act (GtkWidget * un, int num)
 
520
{
 
521
        int row = gtkutil_clist_selection (key_dialog_kb_clist);
 
522
        struct key_binding *kb;
 
523
 
 
524
        if (row != -1)
 
525
        {
 
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)
 
531
                {
 
532
                        key_print_text (GTK_XTEXT (key_dialog_text), _(key_actions[num].help));
 
533
                }
 
534
        }
 
535
}
 
536
 
 
537
static void
 
538
key_dialog_sel_row (GtkWidget * clist, gint row, gint column,
 
539
                                                  GdkEventButton * evt, gpointer data)
 
540
{
 
541
        struct key_binding *kb = gtk_clist_get_row_data (GTK_CLIST (clist), row);
 
542
 
 
543
        if (kb == NULL)
 
544
        {
 
545
                printf ("*** key_dialog_sel_row: kb == NULL\n");
 
546
                abort ();
 
547
        }
 
548
        if (kb->action > -1 && kb->action <= KEY_MAX_ACTIONS)
 
549
        {
 
550
                gtk_option_menu_set_history (GTK_OPTION_MENU (key_dialog_act_menu),
 
551
                                                                                          kb->action);
 
552
                if (key_actions[kb->action].help)
 
553
                {
 
554
                        key_print_text (GTK_XTEXT (key_dialog_text), _(key_actions[kb->action].help));
 
555
                }
 
556
        }
 
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);
 
563
 
 
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), "");
 
568
 
 
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), "");
 
573
 
 
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), "");
 
578
}
 
579
 
 
580
static void
 
581
key_dialog_tog_key (GtkWidget * tog, int kstate)
 
582
{
 
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];
 
587
 
 
588
        if (row == -1)
 
589
                return;
 
590
 
 
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;
 
596
 
 
597
        gtk_clist_set_text (GTK_CLIST (key_dialog_kb_clist), row, 0,
 
598
                                                          key_make_mod_str (kb->mod, buf));
 
599
}
 
600
 
 
601
static GtkWidget *
 
602
key_dialog_make_toggle (char *label, void *callback, void *option,
 
603
                                                                GtkWidget * box)
 
604
{
 
605
        GtkWidget *wid;
 
606
 
 
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);
 
613
 
 
614
        return wid;
 
615
}
 
616
 
 
617
static GtkWidget *
 
618
key_dialog_make_entry (char *label, char *act, void *callback, void *option,
 
619
                                                          GtkWidget * box)
 
620
{
 
621
        GtkWidget *wid, *hbox;;
 
622
 
 
623
        hbox = gtk_hbox_new (0, 2);
 
624
        if (label)
 
625
        {
 
626
                wid = gtk_label_new (label);
 
627
                gtk_widget_show (wid);
 
628
                gtk_box_pack_start (GTK_BOX (hbox), wid, 0, 0, 0);
 
629
        }
 
630
        wid = gtk_entry_new ();
 
631
        if (act)
 
632
        {
 
633
                gtk_signal_connect (GTK_OBJECT (wid), act, GTK_SIGNAL_FUNC (callback),
 
634
                                                                  option);
 
635
        }
 
636
        gtk_box_pack_start (GTK_BOX (hbox), wid, 0, 0, 0);
 
637
        gtk_widget_show (wid);
 
638
        gtk_widget_show (hbox);
 
639
 
 
640
        gtk_box_pack_start (GTK_BOX (box), hbox, 0, 0, 0);
 
641
 
 
642
        return wid;
 
643
}
 
644
 
 
645
static void
 
646
key_dialog_set_key (GtkWidget * entry, GdkEventKey * evt, void *none)
 
647
{
 
648
        int row = gtkutil_clist_selection (key_dialog_kb_clist);
 
649
        struct key_binding *kb;
 
650
 
 
651
        gtk_entry_set_text (GTK_ENTRY (entry), "");
 
652
 
 
653
        if (row == -1)
 
654
                return;
 
655
 
 
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");
 
662
}
 
663
 
 
664
static void
 
665
key_dialog_set_data (GtkWidget * entry, int d)
 
666
{
 
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);
 
672
 
 
673
        len++;
 
674
 
 
675
        if (row == -1)
 
676
                return;
 
677
 
 
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
 
688
        {
 
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);
 
695
        }
 
696
}
 
697
 
 
698
void
 
699
key_dialog_show ()
 
700
{
 
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;
 
706
 
 
707
        titles[0] = _("Mod");
 
708
        titles[1] = _("Key");
 
709
        titles[2] = _("Action");
 
710
 
 
711
        if (key_dialog)
 
712
        {
 
713
                mg_bring_tofront (key_dialog);
 
714
                return;
 
715
        }
 
716
 
 
717
        key_dialog =
 
718
                          mg_create_generic_tab ("editkeys", _("XChat: Keyboard Shortcuts"),
 
719
                                                        TRUE, FALSE, key_dialog_close, NULL, 560, 330, &vbox, 0);
 
720
 
 
721
        hbox = gtk_hbox_new (0, 2);
 
722
        gtk_box_pack_start (GTK_BOX (vbox), hbox, 1, 1, 0);
 
723
 
 
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;
 
728
 
 
729
        gtk_widget_show (hbox);
 
730
 
 
731
        kb = keys_root;
 
732
 
 
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);
 
737
 
 
738
        while (kb)
 
739
        {
 
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>");
 
750
 
 
751
                if (kb->data2)
 
752
                        titles[4] = kb->data2;
 
753
                else
 
754
                        titles[4] = _("<none>");
 
755
 
 
756
                gtk_clist_set_row_data (GTK_CLIST (list),
 
757
                                                                                gtk_clist_append (GTK_CLIST (list), titles),
 
758
                                                                                kb);
 
759
 
 
760
                kb = kb->next;
 
761
        }
 
762
 
 
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);
 
776
 
 
777
        wid = gtk_option_menu_new ();
 
778
        wid2 = gtk_menu_new ();
 
779
 
 
780
        for (i = 0; i <= KEY_MAX_ACTIONS; i++)
 
781
        {
 
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));
 
788
        }
 
789
 
 
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;
 
795
 
 
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);
 
802
 
 
803
        key_dialog_ent_key = key_dialog_make_entry (_("Key"), "key_press_event",
 
804
                                                                                                                          key_dialog_set_key, NULL,
 
805
                                                                                                                          vbox2);
 
806
 
 
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);
 
813
 
 
814
        hbox2 = gtk_hbox_new (0, 2);
 
815
        gtk_box_pack_end (GTK_BOX (vbox), hbox2, 0, 0, 1);
 
816
 
 
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);
 
826
 
 
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);
 
830
 
 
831
        gtk_widget_show (hbox2);
 
832
        key_dialog_text = wid;
 
833
 
 
834
        gtk_widget_show_all (key_dialog);
 
835
}
 
836
 
 
837
static void
 
838
key_save_kbs (char *fn)
 
839
{
 
840
        int fd, i;
 
841
        char buf[512];
 
842
        struct key_binding *kb;
 
843
 
 
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)
 
851
        {
 
852
                fe_message (_("Error opening keys config file\n"), FE_MSG_ERROR);
 
853
                return;
 
854
        }
 
855
        write (fd, buf,
 
856
                         snprintf (buf, 510, "# XChat key bindings config file\n\n"));
 
857
 
 
858
        kb = keys_root;
 
859
        i = 0;
 
860
 
 
861
        while (kb)
 
862
        {
 
863
                if (kb->keyval == -1 || kb->keyname == NULL || kb->action < 0)
 
864
                {
 
865
                        kb = kb->next;
 
866
                        continue;
 
867
                }
 
868
                i = 0;
 
869
                if (kb->mod & STATE_CTRL)
 
870
                {
 
871
                        i++;
 
872
                        write (fd, "C", 1);
 
873
                }
 
874
                if (kb->mod & STATE_ALT)
 
875
                {
 
876
                        i++;
 
877
                        write (fd, "A", 1);
 
878
                }
 
879
                if (kb->mod & STATE_SHIFT)
 
880
                {
 
881
                        i++;
 
882
                        write (fd, "S", 1);
 
883
                }
 
884
                if (i == 0)
 
885
                        write (fd, "None\n", 5);
 
886
                else
 
887
                        write (fd, "\n", 1);
 
888
 
 
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);
 
895
 
 
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);
 
900
 
 
901
                write (fd, "\n", 1);
 
902
 
 
903
                kb = kb->next;
 
904
        }
 
905
 
 
906
        close (fd);
 
907
}
 
908
 
 
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 */
 
911
 
 
912
static inline int
 
913
key_load_kbs_helper_mod (char *in, int *out)
 
914
{
 
915
        int n, len, mod = 0;
 
916
 
 
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')
 
922
        {
 
923
                in[len] = 0;
 
924
                len--;
 
925
        }
 
926
 
 
927
        if (strcmp (in, "None") == 0)
 
928
        {
 
929
                *out = 0;
 
930
                return 0;
 
931
        }
 
932
        for (n = 0; n < len; n++)
 
933
        {
 
934
                switch (in[n])
 
935
                {
 
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;
 
947
                }
 
948
        }
 
949
 
 
950
        *out = mod;
 
951
        return 0;
 
952
}
 
953
 
 
954
/* These are just local defines to keep me sane --AGL */
 
955
 
 
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
 
961
 
 
962
/* *** Warning, Warning! - massive function ahead! --AGL */
 
963
 
 
964
static int
 
965
key_load_kbs (char *filename)
 
966
{
 
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;
 
971
 
 
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);
 
983
 
 
984
        while (buf_get_line (ibuf, &buf, &pnt, st.st_size))
 
985
        {
 
986
                if (buf[0] == '#')
 
987
                        continue;
 
988
                if (strlen (buf) == 0)
 
989
                        continue;
 
990
 
 
991
                switch (state)
 
992
                {
 
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')
 
1005
                        {
 
1006
                                buf[len] = 0;
 
1007
                                len--;
 
1008
                        }
 
1009
 
 
1010
                        n = gdk_keyval_from_name (buf);
 
1011
                        if (n == 0)
 
1012
                        {
 
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;
 
1024
                        }
 
1025
                        kb->keyname = gdk_keyval_name (n);
 
1026
                        kb->keyval = n;
 
1027
 
 
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')
 
1036
                        {
 
1037
                                buf[len] = 0;
 
1038
                                len--;
 
1039
                        }
 
1040
 
 
1041
                        for (n = 0; n < KEY_MAX_ACTIONS + 1; n++)
 
1042
                        {
 
1043
                                if (strcmp (key_actions[n].name, buf) == 0)
 
1044
                                {
 
1045
                                        kb->action = n;
 
1046
                                        break;
 
1047
                                }
 
1048
                        }
 
1049
 
 
1050
                        if (n == KEY_MAX_ACTIONS + 1)
 
1051
                        {
 
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;
 
1062
                        }
 
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;
 
1069
 
 
1070
                        while (buf[0] == ' ' || buf[0] == '\t')
 
1071
                                buf++;
 
1072
 
 
1073
                        if (buf[0] != 'D')
 
1074
                        {
 
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;
 
1083
                        }
 
1084
                        switch (buf[1])
 
1085
                        {
 
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;
 
1096
                        }
 
1097
 
 
1098
                        if (buf[2] == ':')
 
1099
                        {
 
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)
 
1105
                                {
 
1106
                                        kb->data1 = malloc (len);
 
1107
                                        memcpy (kb->data1, &buf[3], len);
 
1108
                                } else
 
1109
                                {
 
1110
                                        kb->data2 = malloc (len);
 
1111
                                        memcpy (kb->data2, &buf[3], len);
 
1112
                                }
 
1113
                        } else if (buf[2] == '!')
 
1114
                        {
 
1115
                                if (state == KBSTATE_DT1)
 
1116
                                        kb->data1 = NULL;
 
1117
                                else
 
1118
                                        kb->data2 = NULL;
 
1119
                        }
 
1120
                        if (state == KBSTATE_DT1)
 
1121
                        {
 
1122
                                state = KBSTATE_DT2;
 
1123
                                continue;
 
1124
                        } else
 
1125
                        {
 
1126
                                if (last)
 
1127
                                        last->next = kb;
 
1128
                                else
 
1129
                                        keys_root = kb;
 
1130
                                last = kb;
 
1131
 
 
1132
                                state = KBSTATE_MOD;
 
1133
                        }
 
1134
 
 
1135
                        continue;
 
1136
                }
 
1137
        }
 
1138
        if (last)
 
1139
                last->next = NULL;
 
1140
        free (ibuf);
 
1141
        return 0;
 
1142
 
 
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;
 
1153
}
 
1154
 
 
1155
/* ***** Key actions start here *********** */
 
1156
 
 
1157
/* See the NOTES above --AGL */
 
1158
 
 
1159
/* "Run command" */
 
1160
static int
 
1161
key_action_handle_command (GtkWidget * wid, GdkEventKey * evt, char *d1,
 
1162
                                                                        char *d2, struct session *sess)
 
1163
{
 
1164
        int ii, oi, len;
 
1165
        char out[2048], d = 0;
 
1166
 
 
1167
        if (!d1)
 
1168
                return 0;
 
1169
 
 
1170
        len = strlen (d1);
 
1171
 
 
1172
        /* Replace each "\n" substring with '\n' */
 
1173
        for (ii = oi = 0; ii < len; ii++)
 
1174
        {
 
1175
                d = d1[ii];
 
1176
                if (d == '\\')
 
1177
                {
 
1178
                        ii++;
 
1179
                        d = d1[ii];
 
1180
                        if (d == 'n')
 
1181
                                out[oi++] = '\n';
 
1182
                        else if (d == '\\')
 
1183
                                out[oi++] = '\\';
 
1184
                        else
 
1185
                        {
 
1186
                                out[oi++] = '\\';
 
1187
                                out[oi++] = d;
 
1188
                        }
 
1189
                        continue;
 
1190
                }
 
1191
                out[oi++] = d;
 
1192
        }
 
1193
        out[oi] = 0;
 
1194
 
 
1195
        handle_multiline (sess, out, 0, 0);
 
1196
        return 0;
 
1197
}
 
1198
 
 
1199
static int
 
1200
key_action_page_switch (GtkWidget * wid, GdkEventKey * evt, char *d1,
 
1201
                                                                char *d2, struct session *sess)
 
1202
{
 
1203
        int len, i, num;
 
1204
 
 
1205
        if (!d1)
 
1206
                return 1;
 
1207
 
 
1208
        len = strlen (d1);
 
1209
        if (!len)
 
1210
                return 1;
 
1211
 
 
1212
        for (i = 0; i < len; i++)
 
1213
        {
 
1214
                if (d1[i] < '0' || d1[i] > '9')
 
1215
                {
 
1216
                        if (i == 0 && (d1[i] == '+' || d1[i] == '-'))
 
1217
                                continue;
 
1218
                        else
 
1219
                                return 1;
 
1220
                }
 
1221
        }
 
1222
 
 
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;
 
1231
}
 
1232
 
 
1233
int
 
1234
key_action_insert (GtkWidget * wid, GdkEventKey * evt, char *d1, char *d2,
 
1235
                                                 struct session *sess)
 
1236
{
 
1237
        int tmp_pos;
 
1238
 
 
1239
        if (!d1)
 
1240
                return 1;
 
1241
 
 
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;
 
1246
}
 
1247
 
 
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)
 
1252
{
 
1253
        int value, end;
 
1254
        GtkAdjustment *adj;
 
1255
        enum scroll_type { PAGE_UP, PAGE_DOWN, LINE_UP, LINE_DOWN };
 
1256
        int type = PAGE_DOWN;
 
1257
 
 
1258
        if (d1)
 
1259
        {
 
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;
 
1266
        }
 
1267
 
 
1268
        if (!sess)
 
1269
                return 0;
 
1270
 
 
1271
        adj = GTK_RANGE (sess->gui->vscrollbar)->adjustment;
 
1272
        end = adj->upper - adj->lower - adj->page_size;
 
1273
 
 
1274
        switch (type)
 
1275
        {
 
1276
        case LINE_UP:
 
1277
                value = adj->value - 1.0;
 
1278
                break;
 
1279
 
 
1280
        case LINE_DOWN:
 
1281
                value = adj->value + 1.0;
 
1282
                break;
 
1283
 
 
1284
        case PAGE_UP:
 
1285
                value = adj->value - (adj->page_size - 1);
 
1286
                break;
 
1287
 
 
1288
        default:        /* PAGE_DOWN */
 
1289
                value = adj->value + (adj->page_size - 1);
 
1290
                break;
 
1291
        }
 
1292
 
 
1293
        if (value < 0)
 
1294
                value = 0;
 
1295
        if (value > end)
 
1296
                value = end;
 
1297
 
 
1298
        gtk_adjustment_set_value (adj, value);
 
1299
 
 
1300
        return 0;
 
1301
}
 
1302
 
 
1303
static int
 
1304
key_action_set_buffer (GtkWidget * wid, GdkEventKey * evt, char *d1, char *d2,
 
1305
                                                          struct session *sess)
 
1306
{
 
1307
        if (!d1)
 
1308
                return 1;
 
1309
        if (d1[0] == 0)
 
1310
                return 1;
 
1311
 
 
1312
        SPELL_ENTRY_SET_TEXT (wid, d1);
 
1313
        SPELL_ENTRY_SET_POS (wid, -1);
 
1314
 
 
1315
        return 2;
 
1316
}
 
1317
 
 
1318
static int
 
1319
key_action_history_up (GtkWidget * wid, GdkEventKey * ent, char *d1, char *d2,
 
1320
                                                          struct session *sess)
 
1321
{
 
1322
        char *new_line;
 
1323
 
 
1324
        new_line = history_up (&sess->history, SPELL_ENTRY_GET_TEXT (wid));
 
1325
        if (new_line)
 
1326
        {
 
1327
                SPELL_ENTRY_SET_TEXT (wid, new_line);
 
1328
                SPELL_ENTRY_SET_POS (wid, -1);
 
1329
        }
 
1330
 
 
1331
        return 2;
 
1332
}
 
1333
 
 
1334
static int
 
1335
key_action_history_down (GtkWidget * wid, GdkEventKey * ent, char *d1,
 
1336
                                                                 char *d2, struct session *sess)
 
1337
{
 
1338
        char *new_line;
 
1339
 
 
1340
        new_line = history_down (&sess->history);
 
1341
        if (new_line)
 
1342
        {
 
1343
                SPELL_ENTRY_SET_TEXT (wid, new_line);
 
1344
                SPELL_ENTRY_SET_POS (wid, -1);
 
1345
        }
 
1346
 
 
1347
        return 2;
 
1348
}
 
1349
 
 
1350
/* old data that we reuse */
 
1351
static struct gcomp_data old_gcomp;
 
1352
 
 
1353
/* work on the data, ie return only channels */
 
1354
static int
 
1355
double_chan_cb (session *lsess, GList **list)
 
1356
{
 
1357
        if (lsess->type == SESS_CHANNEL)
 
1358
                *list = g_list_prepend(*list, lsess->channel);
 
1359
        return TRUE;
 
1360
}
 
1361
 
 
1362
/* convert a slist -> list. */
 
1363
static GList *
 
1364
chanlist_double_list (GSList *inlist)
 
1365
{
 
1366
        GList *list = NULL;
 
1367
        g_slist_foreach(inlist, (GFunc)double_chan_cb, &list);
 
1368
        return list;
 
1369
}
 
1370
 
 
1371
/* handle commands */
 
1372
static int
 
1373
double_cmd_cb (struct popup *pop, GList **list)
 
1374
{
 
1375
        *list = g_list_prepend(*list, pop->name);
 
1376
        return TRUE;
 
1377
}
 
1378
 
 
1379
/* convert a slist -> list. */
 
1380
static GList *
 
1381
cmdlist_double_list (GSList *inlist)
 
1382
{
 
1383
        GList *list = NULL;
 
1384
        g_slist_foreach (inlist, (GFunc)double_cmd_cb, &list);
 
1385
        return list;
 
1386
}
 
1387
 
 
1388
static char *
 
1389
gcomp_nick_func (char *data)
 
1390
{
 
1391
        if (data)
 
1392
                return ((struct User *)data)->nick;
 
1393
        return "";
 
1394
}
 
1395
 
 
1396
void
 
1397
key_action_tab_clean(void)
 
1398
{
 
1399
        if (old_gcomp.elen)
 
1400
        {
 
1401
                old_gcomp.data[0] = 0;
 
1402
                old_gcomp.elen = 0;
 
1403
        }
 
1404
}
 
1405
 
 
1406
/* Used in the followig completers */
 
1407
#define COMP_BUF 2048
 
1408
 
 
1409
/* For use in sorting the user list for completion */
 
1410
static int
 
1411
talked_recent_cmp (struct User *a, struct User *b)
 
1412
{
 
1413
        if (a->lasttalk < b->lasttalk)
 
1414
                return -1;
 
1415
 
 
1416
        if (a->lasttalk > b->lasttalk)
 
1417
                return 1;
 
1418
 
 
1419
        return 0;
 
1420
}
 
1421
 
 
1422
static int
 
1423
key_action_tab_comp (GtkWidget *t, GdkEventKey *entry, char *d1, char *d2,
 
1424
                                                        struct session *sess)
 
1425
{
 
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;
 
1432
 
 
1433
        /* force the IM Context to reset */
 
1434
        SPELL_ENTRY_SET_EDITABLE (t, FALSE);
 
1435
        SPELL_ENTRY_SET_EDITABLE (t, TRUE);
 
1436
 
 
1437
        text = SPELL_ENTRY_GET_TEXT (t);
 
1438
        if (text[0] == 0)
 
1439
                return 1;
 
1440
 
 
1441
        len = g_utf8_strlen (text, -1); /* must be null terminated */
 
1442
 
 
1443
        cursor_pos = SPELL_ENTRY_GET_POS (t);
 
1444
 
 
1445
        buf[0] = 0; /* make sure we don't get garbage in the buffer */
 
1446
 
 
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] == ' ')
 
1450
        {
 
1451
                skip_len++;
 
1452
                ch = g_utf8_find_prev_char(text, ch);
 
1453
                if (!ch)
 
1454
                        return 2;
 
1455
 
 
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]))
 
1460
                {
 
1461
                        skip_len++;
 
1462
                }
 
1463
                else
 
1464
                        cursor_pos = g_utf8_pointer_to_offset(text, g_utf8_offset_to_pointer(ch, 1));
 
1465
        }
 
1466
 
 
1467
        comp = skip_len;
 
1468
        
 
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);
 
1472
 
 
1473
        for (ent_start = cursor_pos; ; --ent_start)
 
1474
        {
 
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;
 
1480
        }
 
1481
 
 
1482
        if (ent_start == 0 && text[0] == prefs.cmdchar[0])
 
1483
        {
 
1484
                ent_start++;
 
1485
                is_cmd = 1;
 
1486
        }
 
1487
        
 
1488
        prefix_len = ent_start;
 
1489
        elen = cursor_pos - ent_start;
 
1490
 
 
1491
        g_utf8_strncpy (ent, g_utf8_offset_to_pointer (text, prefix_len), elen);
 
1492
 
 
1493
        is_nick = (ent[0] == '#' || ent[0] == '&' || is_cmd) ? 0 : 1;
 
1494
        
 
1495
        if (sess->type == SESS_DIALOG && is_nick)
 
1496
        {
 
1497
                /* tab in a dialog completes the other person's name */
 
1498
                if (rfc_ncasecmp (sess->channel, ent, elen) == 0)
 
1499
                {
 
1500
                        result =  sess->channel;
 
1501
                        is_nick = 0;
 
1502
                }
 
1503
                else
 
1504
                        return 2;
 
1505
        }
 
1506
        else
 
1507
        {
 
1508
                if (is_nick)
 
1509
                {
 
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);
 
1514
                }
 
1515
                else
 
1516
                {
 
1517
                        gcomp = g_completion_new (NULL);
 
1518
                        if (is_cmd)
 
1519
                        {
 
1520
                                tmp_list = cmdlist_double_list (command_list);
 
1521
                                for(i = 0; xc_cmds[i].name != NULL ; i++)
 
1522
                                {
 
1523
                                        tmp_list = g_list_prepend (tmp_list, xc_cmds[i].name);
 
1524
                                }
 
1525
                                tmp_list = plugin_command_list(tmp_list);
 
1526
                        }
 
1527
                        else
 
1528
                                tmp_list = chanlist_double_list (sess_list);
 
1529
                }
 
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)
 
1533
                {
 
1534
                        g_completion_add_items (gcomp, tmp_list);
 
1535
                        g_list_free (tmp_list);
 
1536
                }
 
1537
 
 
1538
                if (comp && !(rfc_ncasecmp(old_gcomp.data, ent, old_gcomp.elen) == 0))
 
1539
                {
 
1540
                        key_action_tab_clean ();
 
1541
                        comp = 0;
 
1542
                }
 
1543
        
 
1544
#if GLIB_CHECK_VERSION(2,4,0)
 
1545
                list = g_completion_complete_utf8 (gcomp, comp ? old_gcomp.data : ent, &result);
 
1546
#else
 
1547
                list = g_completion_complete (gcomp, comp ? old_gcomp.data : ent, &result);
 
1548
#endif
 
1549
                
 
1550
                if (result == NULL) /* No matches found */
 
1551
                {
 
1552
                        g_completion_free(gcomp);
 
1553
                        return 2;
 
1554
                }
 
1555
 
 
1556
                if (comp) /* existing completion */
 
1557
                {
 
1558
                        while(list) /* find the current entry */
 
1559
                        {
 
1560
                                if(rfc_ncasecmp(list->data, ent, elen) == 0)
 
1561
                                {
 
1562
                                        found = 1;
 
1563
                                        break;
 
1564
                                }
 
1565
                                list = list->next;
 
1566
                        }
 
1567
 
 
1568
                        if (found)
 
1569
                        {
 
1570
                                if (!(d1 && d1[0])) /* not holding down shift */
 
1571
                                {
 
1572
                                        if (g_list_next(list) == NULL)
 
1573
                                                list = g_list_first(list);
 
1574
                                        else
 
1575
                                                list = g_list_next(list);
 
1576
                                }
 
1577
                                else
 
1578
                                {
 
1579
                                        if (g_list_previous(list) == NULL)
 
1580
                                                list = g_list_last(list);
 
1581
                                        else
 
1582
                                                list = g_list_previous(list);
 
1583
                                }
 
1584
                                g_free(result);
 
1585
                                result = (char*)list->data;
 
1586
                        }
 
1587
                        else
 
1588
                        {
 
1589
                                g_free(result);
 
1590
                                g_completion_free(gcomp);
 
1591
                                return 2;
 
1592
                        }
 
1593
                }
 
1594
                else
 
1595
                {
 
1596
                        strcpy(old_gcomp.data, ent);
 
1597
                        old_gcomp.elen = elen;
 
1598
 
 
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)
 
1601
                        {
 
1602
                                g_free(result);
 
1603
                                result = (char*)list->data;
 
1604
                        }
 
1605
                        else
 
1606
                        {
 
1607
                                /* bash style completion */
 
1608
                                if (g_list_next(list) != NULL)
 
1609
                                {
 
1610
                                        if (strlen (result) > elen) /* the largest common prefix is larger than nick, change the data */
 
1611
                                        {
 
1612
                                                if (prefix_len)
 
1613
                                                        g_utf8_strncpy (buf, text, prefix_len);
 
1614
                                                strncat (buf, result, COMP_BUF - prefix_len);
 
1615
                                                cursor_pos = strlen (buf);
 
1616
                                                g_free(result);
 
1617
#if !GLIB_CHECK_VERSION(2,4,0)
 
1618
                                                g_utf8_validate (buf, -1, (const gchar **)&result);
 
1619
                                                (*result) = 0;
 
1620
#endif
 
1621
                                                if (postfix)
 
1622
                                                {
 
1623
                                                        strcat (buf, " ");
 
1624
                                                        strncat (buf, postfix, COMP_BUF - cursor_pos -1);
 
1625
                                                }
 
1626
                                                SPELL_ENTRY_SET_TEXT (t, buf);
 
1627
                                                SPELL_ENTRY_SET_POS (t, g_utf8_pointer_to_offset(buf, buf + cursor_pos));
 
1628
                                                buf[0] = 0;
 
1629
                                        }
 
1630
                                        else
 
1631
                                                g_free(result);
 
1632
                                        while (list)
 
1633
                                        {
 
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 */
 
1637
                                                {
 
1638
                                                        PrintText (sess, buf);
 
1639
                                                        buf[0] = 0;
 
1640
                                                        len = 0;
 
1641
                                                }
 
1642
                                                strcpy (buf + len, (char *) list->data);
 
1643
                                                strcpy (buf + len + elen, " ");
 
1644
                                                list = list->next;
 
1645
                                        }
 
1646
                                        PrintText (sess, buf);
 
1647
                                        g_completion_free(gcomp);
 
1648
                                        return 2;
 
1649
                                }
 
1650
                                /* Only one matching entry */
 
1651
                                g_free(result);
 
1652
                                result = list->data;
 
1653
                        }
 
1654
                }
 
1655
        }
 
1656
        
 
1657
        if(result)
 
1658
        {
 
1659
                if (prefix_len)
 
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]);
 
1664
                strcat (buf, " ");
 
1665
                cursor_pos = strlen (buf);
 
1666
                if (postfix)
 
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));
 
1670
        }
 
1671
        if (gcomp)
 
1672
                g_completion_free(gcomp);
 
1673
        return 2;
 
1674
}
 
1675
#undef COMP_BUF
 
1676
 
 
1677
static int
 
1678
key_action_comp_chng (GtkWidget * wid, GdkEventKey * ent, char *d1, char *d2,
 
1679
                struct session *sess)
 
1680
{
 
1681
        key_action_tab_comp(wid, ent, d1, d2, sess);
 
1682
        return 2;
 
1683
}
 
1684
 
 
1685
 
 
1686
static int
 
1687
key_action_replace (GtkWidget * wid, GdkEventKey * ent, char *d1, char *d2,
 
1688
                                                  struct session *sess)
 
1689
{
 
1690
        replace_handle (wid);
 
1691
        return 1;
 
1692
}
 
1693
 
 
1694
 
 
1695
static int
 
1696
key_action_move_tab_left (GtkWidget * wid, GdkEventKey * ent, char *d1,
 
1697
                                                                  char *d2, struct session *sess)
 
1698
{
 
1699
        mg_move_tab (sess, +1);
 
1700
        return 2;                                                 /* don't allow default action */
 
1701
}
 
1702
 
 
1703
static int
 
1704
key_action_move_tab_right (GtkWidget * wid, GdkEventKey * ent, char *d1,
 
1705
                                                                        char *d2, struct session *sess)
 
1706
{
 
1707
        mg_move_tab (sess, -1);
 
1708
        return 2;                                                 /* -''- */
 
1709
}
 
1710
 
 
1711
static int
 
1712
key_action_move_tab_family_left (GtkWidget * wid, GdkEventKey * ent, char *d1,
 
1713
                                                                  char *d2, struct session *sess)
 
1714
{
 
1715
        mg_move_tab_family (sess, +1);
 
1716
        return 2;                                                 /* don't allow default action */
 
1717
}
 
1718
 
 
1719
static int
 
1720
key_action_move_tab_family_right (GtkWidget * wid, GdkEventKey * ent, char *d1,
 
1721
                                                                        char *d2, struct session *sess)
 
1722
{
 
1723
        mg_move_tab_family (sess, -1);
 
1724
        return 2;                                                 /* -''- */
 
1725
}
 
1726
 
 
1727
static int
 
1728
key_action_put_history (GtkWidget * wid, GdkEventKey * ent, char *d1,
 
1729
                                                                        char *d2, struct session *sess)
 
1730
{
 
1731
        history_add (&sess->history, SPELL_ENTRY_GET_TEXT (wid));
 
1732
        SPELL_ENTRY_SET_TEXT (wid, "");
 
1733
        return 2;                                                 /* -''- */
 
1734
}
 
1735
 
 
1736
 
 
1737
/* -------- */
 
1738
 
 
1739
 
 
1740
#define STATE_SHIFT     GDK_SHIFT_MASK
 
1741
#define STATE_ALT               GDK_MOD1_MASK
 
1742
#define STATE_CTRL      GDK_CONTROL_MASK
 
1743
 
 
1744
static void
 
1745
replace_handle (GtkWidget *t)
 
1746
{
 
1747
        const char *text, *postfix_pnt;
 
1748
        struct popup *pop;
 
1749
        GSList *list = replace_list;
 
1750
        char word[256];
 
1751
        char postfix[256];
 
1752
        char outbuf[4096];
 
1753
        int c, len, xlen;
 
1754
 
 
1755
        text = SPELL_ENTRY_GET_TEXT (t);
 
1756
 
 
1757
        len = strlen (text);
 
1758
        if (len < 1)
 
1759
                return;
 
1760
 
 
1761
        for (c = len - 1; c > 0; c--)
 
1762
        {
 
1763
                if (text[c] == ' ')
 
1764
                        break;
 
1765
        }
 
1766
        if (text[c] == ' ')
 
1767
                c++;
 
1768
        xlen = c;
 
1769
        if (len - c >= (sizeof (word) - 12))
 
1770
                return;
 
1771
        if (len - c < 1)
 
1772
                return;
 
1773
        memcpy (word, &text[c], len - c);
 
1774
        word[len - c] = 0;
 
1775
        len = strlen (word);
 
1776
        if (word[0] == '\'' && word[len] == '\'')
 
1777
                return;
 
1778
        postfix_pnt = NULL;
 
1779
        for (c = 0; c < len; c++)
 
1780
        {
 
1781
                if (word[c] == '\'')
 
1782
                {
 
1783
                        postfix_pnt = &word[c + 1];
 
1784
                        word[c] = 0;
 
1785
                        break;
 
1786
                }
 
1787
        }
 
1788
 
 
1789
        if (postfix_pnt != NULL)
 
1790
        {
 
1791
                if (strlen (postfix_pnt) > sizeof (postfix) - 12)
 
1792
                        return;
 
1793
                strcpy (postfix, postfix_pnt);
 
1794
        }
 
1795
        while (list)
 
1796
        {
 
1797
                pop = (struct popup *) list->data;
 
1798
                if (strcmp (pop->name, word) == 0)
 
1799
                {
 
1800
                        memcpy (outbuf, text, xlen);
 
1801
                        outbuf[xlen] = 0;
 
1802
                        if (postfix_pnt == NULL)
 
1803
                                snprintf (word, sizeof (word), "%s", pop->cmd);
 
1804
                        else
 
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);
 
1809
                        return;
 
1810
                }
 
1811
                list = list->next;
 
1812
        }
 
1813
}
 
1814