Added SILC Thread Queue API
[crypto.git] / apps / irssi / src / fe-text / gui-readline.c
1 /*
2  gui-readline.c : irssi
3
4     Copyright (C) 1999 Timo Sirainen
5
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20
21 #include "module.h"
22 #include "module-formats.h"
23 #include "signals.h"
24 #include "misc.h"
25 #include "settings.h"
26 #include "special-vars.h"
27 #include "levels.h"
28 #include "servers.h"
29
30 #include "completion.h"
31 #include "command-history.h"
32 #include "keyboard.h"
33 #include "translation.h"
34 #include "printtext.h"
35
36 #include "term.h"
37 #include "gui-entry.h"
38 #include "gui-windows.h"
39 #include "utf8.h"
40
41 #include <signal.h>
42
43 typedef void (*ENTRY_REDIRECT_KEY_FUNC) (int key, void *data, SERVER_REC *server, WI_ITEM_REC *item);
44 typedef void (*ENTRY_REDIRECT_ENTRY_FUNC) (const char *line, void *data, SERVER_REC *server, WI_ITEM_REC *item);
45
46 typedef struct {
47         SIGNAL_FUNC func;
48         int flags;
49         void *data;
50 } ENTRY_REDIRECT_REC;
51
52 static KEYBOARD_REC *keyboard;
53 static ENTRY_REDIRECT_REC *redir;
54 static int escape_next_key;
55
56 static int readtag;
57 static unichar prev_key;
58 static GTimeVal last_keypress;
59
60 static int paste_detect_time, paste_detect_keycount, paste_verify_line_count;
61 static int paste_state, paste_keycount;
62 static char *paste_entry, *prev_entry;
63 static int paste_entry_pos, prev_entry_pos;
64 static GArray *paste_buffer;
65
66 static char *paste_old_prompt;
67 static int paste_prompt, paste_line_count;
68 static int paste_join_multiline;
69
70 static void sig_input(void);
71
72 void input_listen_init(int handle)
73 {
74         GIOChannel *stdin_channel;
75
76         stdin_channel = g_io_channel_unix_new(handle);
77         readtag = g_input_add_full(stdin_channel,
78                                    G_PRIORITY_HIGH, G_INPUT_READ,
79                                    (GInputFunction) sig_input, NULL);
80         g_io_channel_unref(stdin_channel);
81 }
82
83 void input_listen_deinit(void)
84 {
85         g_source_remove(readtag);
86         readtag = -1;
87 }
88
89 static void handle_key_redirect(int key)
90 {
91         ENTRY_REDIRECT_KEY_FUNC func;
92         void *data;
93
94         func = (ENTRY_REDIRECT_KEY_FUNC) redir->func;
95         data = redir->data;
96         g_free_and_null(redir);
97
98         gui_entry_set_prompt(active_entry, "");
99
100         if (func != NULL)
101                 func(key, data, active_win->active_server, active_win->active);
102 }
103
104 static void handle_entry_redirect(const char *line)
105 {
106         ENTRY_REDIRECT_ENTRY_FUNC func;
107         void *data;
108
109         gui_entry_set_hidden(active_entry, FALSE);
110
111         func = (ENTRY_REDIRECT_ENTRY_FUNC) redir->func;
112         data = redir->data;
113         g_free_and_null(redir);
114
115         gui_entry_set_prompt(active_entry, "");
116
117         if (func != NULL) {
118                 func(line, data, active_win->active_server,
119                      active_win->active);
120         }
121 }
122
123 static int get_scroll_count(void)
124 {
125         const char *str;
126         double count;
127
128         str = settings_get_str("scroll_page_count");
129         count = atof(str + (*str == '/'));
130         if (count == 0)
131                 count = 1;
132         else if (count < 0)
133                 count = active_mainwin->height-active_mainwin->statusbar_lines+count;
134         else if (count < 1)
135                 count = 1.0/count;
136
137         if (*str == '/') {
138                 count = (active_mainwin->height-active_mainwin->statusbar_lines)/count;
139         }
140         return (int)count;
141 }
142
143 static void window_prev_page(void)
144 {
145         gui_window_scroll(active_win, -get_scroll_count());
146 }
147
148 static void window_next_page(void)
149 {
150         gui_window_scroll(active_win, get_scroll_count());
151 }
152
153 static void paste_buffer_join_lines(GArray *buf)
154 {
155 #define IS_WHITE(c) ((c) == ' ' || (c) == '\t')
156         unsigned int i, count, indent, line_len;
157         unichar *arr, *dest, *last_lf_pos;
158         int last_lf;
159
160         /* first check if we actually want to join anything. This is assuming
161            that we only want to join lines if
162
163            a) first line doesn't begin with whitespace
164            b) subsequent lines begin with same amount of whitespace
165            c) whenever there's no whitespace, goto a)
166
167            For example:
168
169            line 1
170              line 2
171              line 3
172            line 4
173            line 5
174              line 6
175
176            ->
177
178            line1 line2 line 3
179            line4
180            line5 line 6
181         */
182         if (buf->len == 0)
183                 return;
184
185         arr = (unichar *) paste_buffer->data;
186
187         /* first line */
188         if (IS_WHITE(arr[0]))
189                 return;
190
191         /* find the first beginning of indented line */
192         for (i = 1; i < buf->len; i++) {
193                 if (arr[i-1] == '\n' && IS_WHITE(arr[i]))
194                         break;
195         }
196         if (i == buf->len)
197                 return;
198
199         /* get how much indentation we have.. */
200         for (indent = 0; i < buf->len; i++, indent++) {
201                 if (!IS_WHITE(arr[i]))
202                         break;
203         }
204         if (i == buf->len)
205                 return;
206
207         /* now, enforce these to all subsequent lines */
208         count = indent; last_lf = TRUE;
209         for (; i < buf->len; i++) {
210                 if (last_lf) {
211                         if (IS_WHITE(arr[i]))
212                                 count++;
213                         else {
214                                 last_lf = FALSE;
215                                 if (count != 0 && count != indent)
216                                         return;
217                                 count = 0;
218                         }
219                 }
220                 if (arr[i] == '\n')
221                         last_lf = TRUE;
222         }
223
224         /* all looks fine - now remove the whitespace, but don't let lines
225            get longer than 400 chars */
226         dest = arr; last_lf = TRUE; last_lf_pos = NULL; line_len = 0;
227         for (i = 0; i < buf->len; i++) {
228                 if (last_lf && IS_WHITE(arr[i])) {
229                         /* whitespace, ignore */
230                 } else if (arr[i] == '\n') {
231                         if (!last_lf && i+1 != buf->len &&
232                             IS_WHITE(arr[i+1])) {
233                                 last_lf_pos = dest;
234                                 *dest++ = ' ';
235                         } else {
236                                 *dest++ = '\n'; /* double-LF */
237                                 line_len = 0;
238                                 last_lf_pos = NULL;
239                         }
240                         last_lf = TRUE;
241                 } else {
242                         last_lf = FALSE;
243                         if (++line_len >= 400 && last_lf_pos != NULL) {
244                                 memmove(last_lf_pos+1, last_lf_pos,
245                                         dest - last_lf_pos);
246                                 *last_lf_pos = '\n'; last_lf_pos = NULL;
247                                 line_len = 0;
248                                 dest++;
249                         }
250                         *dest++ = arr[i];
251                 }
252         }
253         g_array_set_size(buf, dest - arr);
254 }
255
256 static void paste_send(void)
257 {
258         HISTORY_REC *history;
259         unichar *arr;
260         GString *str;
261         char out[10], *text;
262         unsigned int i;
263
264         if (paste_join_multiline)
265                 paste_buffer_join_lines(paste_buffer);
266
267         arr = (unichar *) paste_buffer->data;
268         if (active_entry->text_len == 0)
269                 i = 0;
270         else {
271                 /* first line has to be kludged kind of to get pasting in the
272                    middle of line right.. */
273                 for (i = 0; i < paste_buffer->len; i++) {
274                         if (arr[i] == '\r' || arr[i] == '\n') {
275                                 i++;
276                                 break;
277                         }
278
279                         gui_entry_insert_char(active_entry, arr[i]);
280                 }
281
282                 text = gui_entry_get_text(active_entry);
283                 history = command_history_current(active_win);
284                 command_history_add(history, text);
285
286                 translate_output(text);
287                 signal_emit("send command", 3, text,
288                             active_win->active_server, active_win->active);
289                 g_free(text);
290         }
291
292         /* rest of the lines */
293         str = g_string_new(NULL);
294         for (; i < paste_buffer->len; i++) {
295                 if (arr[i] == '\r' || arr[i] == '\n') {
296                         history = command_history_current(active_win);
297                         command_history_add(history, str->str);
298
299                         translate_output(str->str);
300                         signal_emit("send command", 3, str->str,
301                                     active_win->active_server,
302                                     active_win->active);
303                         g_string_truncate(str, 0);
304                 } else if (active_entry->utf8) {
305                         out[utf16_char_to_utf8(arr[i], out)] = '\0';
306                         g_string_append(str, out);
307                 } else if (term_type == TERM_TYPE_BIG5) {
308                         if (arr[i] > 0xff)
309                                 g_string_append_c(str, (arr[i] >> 8) & 0xff);
310                         g_string_append_c(str, arr[i] & 0xff);
311                 } else {
312                         g_string_append_c(str, arr[i]);
313                 }
314         }
315
316         gui_entry_set_text(active_entry, str->str);
317         g_string_free(str, TRUE);
318 }
319
320 static void paste_flush(int send)
321 {
322         gui_entry_set_text(active_entry, paste_entry);
323         gui_entry_set_pos(active_entry, paste_entry_pos);
324
325         if (send)
326                 paste_send();
327         g_array_set_size(paste_buffer, 0);
328
329         gui_entry_set_prompt(active_entry,
330                              paste_old_prompt == NULL ? "" : paste_old_prompt);
331         g_free(paste_old_prompt); paste_old_prompt = NULL;
332         paste_prompt = FALSE;
333
334         paste_line_count = 0;
335         paste_state = 0;
336         paste_keycount = 0;
337
338         gui_entry_redraw(active_entry);
339 }
340
341 static gboolean paste_timeout(gpointer data)
342 {
343         GTimeVal now;
344         char *str;
345         int diff;
346
347         if (paste_state == 0) {
348                 /* gone already */
349                 return FALSE;
350         }
351
352         g_get_current_time(&now);
353         diff = (now.tv_sec - last_keypress.tv_sec) * 1000 +
354                 (now.tv_usec - last_keypress.tv_usec)/1000;
355
356         if (diff < paste_detect_time) {
357                 /* still pasting */
358                 return TRUE;
359         }
360
361         if (paste_line_count < paste_verify_line_count ||
362             active_win->active == NULL) {
363                 /* paste without asking */
364                 paste_flush(TRUE);
365         } else if (!paste_prompt) {
366                 paste_prompt = TRUE;
367                 paste_old_prompt = g_strdup(active_entry->prompt);
368                 printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
369                                    TXT_PASTE_WARNING,
370                                    paste_line_count,
371                                    active_win->active == NULL ? "window" :
372                                    active_win->active->visible_name);
373
374                 str = format_get_text(MODULE_NAME, active_win, NULL, NULL,
375                                       TXT_PASTE_PROMPT, 0, 0);
376                 gui_entry_set_prompt(active_entry, str);
377                 gui_entry_set_text(active_entry, "");
378                 g_free(str);
379         }
380         return TRUE;
381 }
382
383 static int check_pasting(unichar key, int diff)
384 {
385         if (paste_state < 0)
386                 return FALSE;
387
388         if (paste_state == 0) {
389                 /* two keys hit together quick. possibly pasting */
390                 if (diff > paste_detect_time)
391                         return FALSE;
392
393                 g_free(paste_entry);
394                 paste_entry = g_strdup(prev_entry);
395                 paste_entry_pos = prev_entry_pos;
396
397                 paste_state++;
398                 paste_line_count = 0;
399                 paste_keycount = 0;
400                 g_array_set_size(paste_buffer, 0);
401                 if (prev_key != '\r' && prev_key != '\n')
402                         g_array_append_val(paste_buffer, prev_key);
403         } else if (paste_state > 0 && diff > paste_detect_time &&
404                    paste_line_count == 0) {
405                 /* reset paste state */
406                 paste_state = 0;
407                 paste_keycount = 0;
408                 return FALSE;
409         }
410
411         /* continuing quick hits */
412         if ((key == 11 || key == 3) && paste_prompt) {
413                 paste_flush(key == 11);
414                 return TRUE;
415         }
416
417         g_array_append_val(paste_buffer, key);
418         if (key == '\r' || key == '\n') {
419                 if (paste_state == 1 &&
420                     paste_keycount < paste_detect_keycount) {
421                         /* not enough keypresses to determine if this is
422                            pasting or not. don't reset paste keycount, but
423                            send this line as non-pasted */
424                         g_array_set_size(paste_buffer, 0);
425                         return FALSE;
426                 }
427
428                 /* newline - assume this line was pasted */
429                 if (paste_state == 1) {
430                         paste_state = 2;
431                         gui_entry_set_text(active_entry, paste_entry);
432                         gui_entry_set_pos(active_entry, paste_entry_pos);
433                         if (paste_verify_line_count > 0)
434                                 g_timeout_add(100, paste_timeout, NULL);
435                 }
436
437                 if (paste_verify_line_count <= 0) {
438                         /* paste previous line */
439                         paste_send();
440                         g_array_set_size(paste_buffer, 0);
441                 } else {
442                         paste_line_count++;
443                 }
444         }
445
446         return paste_state == 2;
447 }
448
449 static void sig_gui_key_pressed(gpointer keyp)
450 {
451         GTimeVal now;
452         unichar key;
453         char str[20];
454         int ret, diff;
455
456         key = GPOINTER_TO_INT(keyp);
457
458         if (redir != NULL && redir->flags & ENTRY_REDIRECT_FLAG_HOTKEY) {
459                 handle_key_redirect(key);
460                 return;
461         }
462
463         g_get_current_time(&now);
464         diff = (now.tv_sec - last_keypress.tv_sec) * 1000 +
465                 (now.tv_usec - last_keypress.tv_usec)/1000;
466
467         if (check_pasting(key, diff)) {
468                 last_keypress = now;
469                 return;
470         }
471
472         if (key < 32) {
473                 /* control key */
474                 str[0] = '^';
475                 str[1] = (char)key+'@';
476                 str[2] = '\0';
477         } else if (key == 127) {
478                 str[0] = '^';
479                 str[1] = '?';
480                 str[2] = '\0';
481         } else if (!active_entry->utf8) {
482                 if (key <= 0xff) {
483                         str[0] = (char)key;
484                         str[1] = '\0';
485                 } else {
486                         str[0] = (char) (key >> 8);
487                         str[1] = (char) (key & 0xff);
488                         str[2] = '\0';
489                 }
490         } else {
491                 /* need to convert to utf8 */
492                 str[utf16_char_to_utf8(key, str)] = '\0';
493         }
494
495         if (strcmp(str, "^") == 0) {
496                 /* change it as ^^ */
497                 str[1] = '^';
498                 str[2] = '\0';
499         }
500
501         g_free(prev_entry);
502         prev_entry = gui_entry_get_text(active_entry);
503         prev_entry_pos = gui_entry_get_pos(active_entry);
504
505         if (escape_next_key) {
506                 escape_next_key = FALSE;
507                 gui_entry_insert_char(active_entry, key);
508                 ret = 1;
509         } else {
510                 ret = key_pressed(keyboard, str);
511                 if (ret < 0) {
512                         /* key wasn't used for anything, print it */
513                         gui_entry_insert_char(active_entry, key);
514                 }
515         }
516
517         /* ret = 0 : some key create multiple characters - we're in the middle
518            of one. try to detect the keycombo as a single keypress rather than
519            multiple small onces to avoid incorrect paste detection.
520
521            don't count repeated keys so paste detection won't go on when
522            you're holding some key down */
523         if (ret != 0 && key != prev_key) {
524                 last_keypress = now;
525                 paste_keycount++;
526         }
527         prev_key = key;
528 }
529
530 static void key_send_line(void)
531 {
532         HISTORY_REC *history;
533         char *str, *add_history;
534
535         str = gui_entry_get_text(active_entry);
536
537         /* we can't use gui_entry_get_text() later, since the entry might
538            have been destroyed after we get back */
539         add_history = *str == '\0' ? NULL : g_strdup(str);
540         history = command_history_current(active_win);
541
542         translate_output(str);
543
544         if (redir == NULL) {
545                 signal_emit("send command", 3, str,
546                             active_win->active_server,
547                             active_win->active);
548         } else {
549                 if (redir->flags & ENTRY_REDIRECT_FLAG_HIDDEN)
550                         g_free_and_null(add_history);
551                 handle_entry_redirect(str);
552         }
553
554         if (add_history != NULL) {
555                 history = command_history_find(history);
556                 if (history != NULL)
557                         command_history_add(history, add_history);
558                 g_free(add_history);
559         }
560
561         if (active_entry != NULL)
562                 gui_entry_set_text(active_entry, "");
563         command_history_clear_pos(active_win);
564
565         g_free(str);
566 }
567
568 static void key_combo(void)
569 {
570 }
571
572 static void key_backward_history(void)
573 {
574         const char *text;
575         char *line;
576
577         line = gui_entry_get_text(active_entry);
578         text = command_history_prev(active_win, line);
579         gui_entry_set_text(active_entry, text);
580         g_free(line);
581 }
582
583 static void key_forward_history(void)
584 {
585         const char *text;
586         char *line;
587
588         line = gui_entry_get_text(active_entry);
589         text = command_history_next(active_win, line);
590         gui_entry_set_text(active_entry, text);
591         g_free(line);
592 }
593
594 static void key_beginning_of_line(void)
595 {
596         gui_entry_set_pos(active_entry, 0);
597 }
598
599 static void key_end_of_line(void)
600 {
601         gui_entry_set_pos(active_entry, active_entry->text_len);
602 }
603
604 static void key_backward_character(void)
605 {
606         gui_entry_move_pos(active_entry, -1);
607 }
608
609 static void key_forward_character(void)
610 {
611         gui_entry_move_pos(active_entry, 1);
612 }
613
614 static void key_backward_word(void)
615 {
616         gui_entry_move_words(active_entry, -1, FALSE);
617 }
618
619 static void key_forward_word(void)
620 {
621         gui_entry_move_words(active_entry, 1, FALSE);
622 }
623
624 static void key_backward_to_space(void)
625 {
626         gui_entry_move_words(active_entry, -1, TRUE);
627 }
628
629 static void key_forward_to_space(void)
630 {
631         gui_entry_move_words(active_entry, 1, TRUE);
632 }
633
634 static void key_erase_line(void)
635 {
636         gui_entry_set_pos(active_entry, active_entry->text_len);
637         gui_entry_erase(active_entry, active_entry->text_len, TRUE);
638 }
639
640 static void key_erase_to_beg_of_line(void)
641 {
642         int pos;
643
644         pos = gui_entry_get_pos(active_entry);
645         gui_entry_erase(active_entry, pos, TRUE);
646 }
647
648 static void key_erase_to_end_of_line(void)
649 {
650         int pos;
651
652         pos = gui_entry_get_pos(active_entry);
653         gui_entry_set_pos(active_entry, active_entry->text_len);
654         gui_entry_erase(active_entry, active_entry->text_len - pos, TRUE);
655 }
656
657 static void key_yank_from_cutbuffer(void)
658 {
659         char *cutbuffer;
660
661         cutbuffer = gui_entry_get_cutbuffer(active_entry);
662         if (cutbuffer != NULL) {
663                 gui_entry_insert_text(active_entry, cutbuffer);
664                 g_free(cutbuffer);
665         }
666 }
667
668 static void key_transpose_characters(void)
669 {
670         gui_entry_transpose_chars(active_entry);
671 }
672
673 static void key_transpose_words(void)
674 {
675         gui_entry_transpose_words(active_entry);
676 }
677
678 static void key_capitalize_word(void)
679 {
680         gui_entry_capitalize_word(active_entry);
681 }
682
683 static void key_downcase_word(void)
684 {
685         gui_entry_downcase_word(active_entry);
686 }
687 static void key_upcase_word(void)
688 {
689         gui_entry_upcase_word(active_entry);
690 }
691
692 static void key_delete_character(void)
693 {
694         if (gui_entry_get_pos(active_entry) < active_entry->text_len) {
695                 gui_entry_move_pos(active_entry, 1);
696                 gui_entry_erase(active_entry, 1, FALSE);
697         }
698 }
699
700 static void key_backspace(void)
701 {
702         gui_entry_erase(active_entry, 1, FALSE);
703 }
704
705 static void key_delete_previous_word(void)
706 {
707         gui_entry_erase_word(active_entry, FALSE);
708 }
709
710 static void key_delete_next_word(void)
711 {
712         gui_entry_erase_next_word(active_entry, FALSE);
713 }
714
715 static void key_delete_to_previous_space(void)
716 {
717         gui_entry_erase_word(active_entry, TRUE);
718 }
719
720 static void key_delete_to_next_space(void)
721 {
722         gui_entry_erase_next_word(active_entry, TRUE);
723 }
724
725 static void sig_input(void)
726 {
727         unichar buffer[128];
728         int ret, i;
729
730         if (!active_entry) {
731                 /* no active entry yet - wait until we have it */
732                 return;
733         }
734
735         ret = term_gets(buffer, sizeof(buffer)/sizeof(buffer[0]));
736         if (ret == -1) {
737                 /* lost terminal */
738                 if (!term_detached)
739                         signal_emit("command quit", 1, "Lost terminal");
740         } else {
741                 for (i = 0; i < ret; i++) {
742                         signal_emit("gui key pressed", 1,
743                                     GINT_TO_POINTER(buffer[i]));
744                 }
745         }
746 }
747
748 time_t get_idle_time(void)
749 {
750         return last_keypress.tv_sec;
751 }
752
753 static void key_scroll_backward(void)
754 {
755         window_prev_page();
756 }
757
758 static void key_scroll_forward(void)
759 {
760         window_next_page();
761 }
762
763 static void key_scroll_start(void)
764 {
765         signal_emit("command scrollback home", 3, NULL, active_win->active_server, active_win->active);
766 }
767
768 static void key_scroll_end(void)
769 {
770         signal_emit("command scrollback end", 3, NULL, active_win->active_server, active_win->active);
771 }
772
773 static void key_change_window(const char *data)
774 {
775         signal_emit("command window goto", 3, data, active_win->active_server, active_win->active);
776 }
777
778 static void key_completion(int erase)
779 {
780         char *text, *line;
781         int pos;
782
783         text = gui_entry_get_text_and_pos(active_entry, &pos);
784         line = word_complete(active_win, text, &pos, erase);
785         g_free(text);
786
787         if (line != NULL) {
788                 gui_entry_set_text(active_entry, line);
789                 gui_entry_set_pos(active_entry, pos);
790                 g_free(line);
791         }
792 }
793
794 static void key_word_completion(void)
795 {
796         key_completion(FALSE);
797 }
798
799 static void key_erase_completion(void)
800 {
801         key_completion(TRUE);
802 }
803
804 static void key_check_replaces(void)
805 {
806         char *text, *line;
807         int pos;
808
809         text = gui_entry_get_text_and_pos(active_entry, &pos);
810         line = auto_word_complete(text, &pos);
811         g_free(text);
812
813         if (line != NULL) {
814                 gui_entry_set_text(active_entry, line);
815                 gui_entry_set_pos(active_entry, pos);
816                 g_free(line);
817         }
818 }
819
820 static void key_previous_window(void)
821 {
822         signal_emit("command window previous", 3, "", active_win->active_server, active_win->active);
823 }
824
825 static void key_next_window(void)
826 {
827         signal_emit("command window next", 3, "", active_win->active_server, active_win->active);
828 }
829
830 static void key_left_window(void)
831 {
832         signal_emit("command window left", 3, "", active_win->active_server, active_win->active);
833 }
834
835 static void key_right_window(void)
836 {
837         signal_emit("command window right", 3, "", active_win->active_server, active_win->active);
838 }
839
840 static void key_upper_window(void)
841 {
842         signal_emit("command window up", 3, "", active_win->active_server, active_win->active);
843 }
844
845 static void key_lower_window(void)
846 {
847         signal_emit("command window down", 3, "", active_win->active_server, active_win->active);
848 }
849
850 static void key_active_window(void)
851 {
852         signal_emit("command window goto", 3, "active", active_win->active_server, active_win->active);
853 }
854
855 static SERVER_REC *get_prev_server(SERVER_REC *current)
856 {
857         int pos;
858
859         if (current == NULL) {
860                 return servers != NULL ? g_slist_last(servers)->data :
861                         lookup_servers != NULL ?
862                         g_slist_last(lookup_servers)->data : NULL;
863         }
864
865         /* connect2 -> connect1 -> server2 -> server1 -> connect2 -> .. */
866
867         pos = g_slist_index(servers, current);
868         if (pos != -1) {
869                 if (pos > 0)
870                         return g_slist_nth(servers, pos-1)->data;
871                 if (lookup_servers != NULL)
872                         return g_slist_last(lookup_servers)->data;
873                 return g_slist_last(servers)->data;
874         }
875
876         pos = g_slist_index(lookup_servers, current);
877         g_assert(pos >= 0);
878
879         if (pos > 0)
880                 return g_slist_nth(lookup_servers, pos-1)->data;
881         if (servers != NULL)
882                 return g_slist_last(servers)->data;
883         return g_slist_last(lookup_servers)->data;
884 }
885
886 static SERVER_REC *get_next_server(SERVER_REC *current)
887 {
888         GSList *pos;
889
890         if (current == NULL) {
891                 return servers != NULL ? servers->data :
892                         lookup_servers != NULL ? lookup_servers->data : NULL;
893         }
894
895         /* server1 -> server2 -> connect1 -> connect2 -> server1 -> .. */
896
897         pos = g_slist_find(servers, current);
898         if (pos != NULL) {
899                 if (pos->next != NULL)
900                         return pos->next->data;
901                 if (lookup_servers != NULL)
902                         return lookup_servers->data;
903                 return servers->data;
904         }
905
906         pos = g_slist_find(lookup_servers, current);
907         g_assert(pos != NULL);
908
909         if (pos->next != NULL)
910                 return pos->next->data;
911         if (servers != NULL)
912                 return servers->data;
913         return lookup_servers->data;
914 }
915
916 static void key_previous_window_item(void)
917 {
918         SERVER_REC *server;
919
920         if (active_win->items != NULL) {
921                 signal_emit("command window item prev", 3, "",
922                             active_win->active_server, active_win->active);
923         } else if (servers != NULL || lookup_servers != NULL) {
924                 /* change server */
925                 server = active_win->active_server;
926                 if (server == NULL)
927                         server = active_win->connect_server;
928                 server = get_prev_server(server);
929                 signal_emit("command window server", 3, server->tag,
930                             active_win->active_server, active_win->active);
931         }
932 }
933
934 static void key_next_window_item(void)
935 {
936         SERVER_REC *server;
937
938         if (active_win->items != NULL) {
939                 signal_emit("command window item next", 3, "",
940                             active_win->active_server, active_win->active);
941         } else if (servers != NULL || lookup_servers != NULL) {
942                 /* change server */
943                 server = active_win->active_server;
944                 if (server == NULL)
945                         server = active_win->connect_server;
946                 server = get_next_server(server);
947                 signal_emit("command window server", 3, server->tag,
948                             active_win->active_server, active_win->active);
949         }
950 }
951
952 static void key_escape(void)
953 {
954         escape_next_key = TRUE;
955 }
956
957 static void key_insert_text(const char *data)
958 {
959         char *str;
960
961         str = parse_special_string(data, active_win->active_server,
962                                    active_win->active, "", NULL, 0);
963         gui_entry_insert_text(active_entry, str);
964         g_free(str);
965 }
966
967 static void key_sig_stop(void)
968 {
969         term_stop();
970 }
971
972 static void sig_window_auto_changed(void)
973 {
974         char *text;
975
976         if (active_entry == NULL)
977                 return;
978
979         text = gui_entry_get_text(active_entry);
980         command_history_next(active_win, text);
981         gui_entry_set_text(active_entry, "");
982         g_free(text);
983 }
984
985 static void sig_gui_entry_redirect(SIGNAL_FUNC func, const char *entry,
986                                    void *flags, void *data)
987 {
988         redir = g_new0(ENTRY_REDIRECT_REC, 1);
989         redir->func = func;
990         redir->flags = GPOINTER_TO_INT(flags);
991         redir->data = data;
992
993         if (redir->flags & ENTRY_REDIRECT_FLAG_HIDDEN)
994                 gui_entry_set_hidden(active_entry, TRUE);
995         gui_entry_set_prompt(active_entry, entry);
996 }
997
998 static void setup_changed(void)
999 {
1000         paste_detect_time = settings_get_time("paste_detect_time");
1001         if (paste_detect_time == 0)
1002                 paste_state = -1;
1003         else if (paste_state == -1)
1004                 paste_state = 0;
1005
1006         paste_detect_keycount = settings_get_int("paste_detect_keycount");
1007         if (paste_detect_keycount < 2)
1008                 paste_state = -1;
1009
1010         paste_verify_line_count = settings_get_int("paste_verify_line_count");
1011         paste_join_multiline = settings_get_bool("paste_join_multiline");
1012 }
1013
1014 void gui_readline_init(void)
1015 {
1016         static char changekeys[] = "1234567890qwertyuio";
1017         char *key, data[MAX_INT_STRLEN];
1018         int n;
1019
1020         escape_next_key = FALSE;
1021         redir = NULL;
1022         prev_entry = NULL;
1023         paste_state = 0;
1024         paste_keycount = 0;
1025         paste_entry = NULL;
1026         paste_entry_pos = 0;
1027         paste_buffer = g_array_new(FALSE, FALSE, sizeof(unichar));
1028         paste_old_prompt = NULL;
1029         g_get_current_time(&last_keypress);
1030         input_listen_init(STDIN_FILENO);
1031
1032         settings_add_str("history", "scroll_page_count", "/2");
1033         settings_add_time("misc", "paste_detect_time", "5msecs");
1034         settings_add_int("misc", "paste_detect_keycount", 5);
1035         /* NOTE: function keys can generate at least 5 characters long
1036            keycodes. this must be larger to allow them to work. */
1037         settings_add_int("misc", "paste_verify_line_count", 5);
1038         settings_add_bool("misc", "paste_join_multiline", TRUE);
1039         setup_changed();
1040
1041         keyboard = keyboard_create(NULL);
1042         key_configure_freeze();
1043
1044         key_bind("key", NULL, " ", "space", (SIGNAL_FUNC) key_combo);
1045         key_bind("key", NULL, "^M", "return", (SIGNAL_FUNC) key_combo);
1046         key_bind("key", NULL, "^J", "return", (SIGNAL_FUNC) key_combo);
1047         key_bind("key", NULL, "^H", "backspace", (SIGNAL_FUNC) key_combo);
1048         key_bind("key", NULL, "^?", "backspace", (SIGNAL_FUNC) key_combo);
1049         key_bind("key", NULL, "^I", "tab", (SIGNAL_FUNC) key_combo);
1050
1051         /* meta */
1052         key_bind("key", NULL, "^[", "meta", (SIGNAL_FUNC) key_combo);
1053         key_bind("key", NULL, "meta-[", "meta2", (SIGNAL_FUNC) key_combo);
1054         key_bind("key", NULL, "meta-O", "meta2", (SIGNAL_FUNC) key_combo);
1055         key_bind("key", NULL, "meta-[O", "meta2", (SIGNAL_FUNC) key_combo);
1056
1057         /* arrow keys */
1058         key_bind("key", NULL, "meta2-A", "up", (SIGNAL_FUNC) key_combo);
1059         key_bind("key", NULL, "meta2-B", "down", (SIGNAL_FUNC) key_combo);
1060         key_bind("key", NULL, "meta2-C", "right", (SIGNAL_FUNC) key_combo);
1061         key_bind("key", NULL, "meta2-D", "left", (SIGNAL_FUNC) key_combo);
1062
1063         key_bind("key", NULL, "meta2-1~", "home", (SIGNAL_FUNC) key_combo);
1064         key_bind("key", NULL, "meta2-7~", "home", (SIGNAL_FUNC) key_combo);
1065         key_bind("key", NULL, "meta2-H", "home", (SIGNAL_FUNC) key_combo);
1066
1067         key_bind("key", NULL, "meta2-4~", "end", (SIGNAL_FUNC) key_combo);
1068         key_bind("key", NULL, "meta2-8~", "end", (SIGNAL_FUNC) key_combo);
1069         key_bind("key", NULL, "meta2-F", "end", (SIGNAL_FUNC) key_combo);
1070
1071         key_bind("key", NULL, "meta2-5~", "prior", (SIGNAL_FUNC) key_combo);
1072         key_bind("key", NULL, "meta2-I", "prior", (SIGNAL_FUNC) key_combo);
1073         key_bind("key", NULL, "meta2-6~", "next", (SIGNAL_FUNC) key_combo);
1074         key_bind("key", NULL, "meta2-G", "next", (SIGNAL_FUNC) key_combo);
1075
1076         key_bind("key", NULL, "meta2-2~", "insert", (SIGNAL_FUNC) key_combo);
1077         key_bind("key", NULL, "meta2-3~", "delete", (SIGNAL_FUNC) key_combo);
1078
1079         key_bind("key", NULL, "meta2-d", "cleft", (SIGNAL_FUNC) key_combo);
1080         key_bind("key", NULL, "meta2-c", "cright", (SIGNAL_FUNC) key_combo);
1081         key_bind("key", NULL, "meta2-5D", "cleft", (SIGNAL_FUNC) key_combo);
1082         key_bind("key", NULL, "meta2-5C", "cright", (SIGNAL_FUNC) key_combo);
1083
1084         /* cursor movement */
1085         key_bind("backward_character", "", "left", NULL, (SIGNAL_FUNC) key_backward_character);
1086         key_bind("forward_character", "", "right", NULL, (SIGNAL_FUNC) key_forward_character);
1087         key_bind("backward_word", "", "cleft", NULL, (SIGNAL_FUNC) key_backward_word);
1088         key_bind("backward_word", NULL, "meta-b", NULL, (SIGNAL_FUNC) key_backward_word);
1089         key_bind("forward_word", "", "cright", NULL, (SIGNAL_FUNC) key_forward_word);
1090         key_bind("forward_word", NULL, "meta-f", NULL, (SIGNAL_FUNC) key_forward_word);
1091         key_bind("backward_to_space", "", NULL, NULL, (SIGNAL_FUNC) key_backward_to_space);
1092         key_bind("forward_to_space", "", NULL, NULL, (SIGNAL_FUNC) key_forward_to_space);
1093         key_bind("beginning_of_line", "", "home", NULL, (SIGNAL_FUNC) key_beginning_of_line);
1094         key_bind("beginning_of_line", NULL, "^A", NULL, (SIGNAL_FUNC) key_beginning_of_line);
1095         key_bind("end_of_line", "", "end", NULL, (SIGNAL_FUNC) key_end_of_line);
1096         key_bind("end_of_line", NULL, "^E", NULL, (SIGNAL_FUNC) key_end_of_line);
1097
1098         /* history */
1099         key_bind("backward_history", "", "up", NULL, (SIGNAL_FUNC) key_backward_history);
1100         key_bind("forward_history", "", "down", NULL, (SIGNAL_FUNC) key_forward_history);
1101
1102         /* line editing */
1103         key_bind("backspace", "", "backspace", NULL, (SIGNAL_FUNC) key_backspace);
1104         key_bind("delete_character", "", "delete", NULL, (SIGNAL_FUNC) key_delete_character);
1105         key_bind("delete_character", NULL, "^D", NULL, (SIGNAL_FUNC) key_delete_character);
1106         key_bind("delete_next_word", "", "meta-d", NULL, (SIGNAL_FUNC) key_delete_next_word);
1107         key_bind("delete_previous_word", "", "meta-backspace", NULL, (SIGNAL_FUNC) key_delete_previous_word);
1108         key_bind("delete_to_previous_space", "", "^W", NULL, (SIGNAL_FUNC) key_delete_to_previous_space);
1109         key_bind("delete_to_next_space", "", "", NULL, (SIGNAL_FUNC) key_delete_to_next_space);
1110         key_bind("erase_line", "", "^U", NULL, (SIGNAL_FUNC) key_erase_line);
1111         key_bind("erase_to_beg_of_line", "", NULL, NULL, (SIGNAL_FUNC) key_erase_to_beg_of_line);
1112         key_bind("erase_to_end_of_line", "", "^K", NULL, (SIGNAL_FUNC) key_erase_to_end_of_line);
1113         key_bind("yank_from_cutbuffer", "", "^Y", NULL, (SIGNAL_FUNC) key_yank_from_cutbuffer);
1114         key_bind("transpose_characters", "Swap current and previous character", "^T", NULL, (SIGNAL_FUNC) key_transpose_characters);
1115         key_bind("transpose_words", "Swap current and previous word", NULL, NULL, (SIGNAL_FUNC) key_transpose_words);
1116         key_bind("capitalize_word", "Capitalize word", NULL, NULL, (SIGNAL_FUNC) key_capitalize_word);
1117         key_bind("downcase_word", "Downcase word", NULL, NULL, (SIGNAL_FUNC) key_downcase_word);
1118         key_bind("upcase_word", "Upcase word", NULL, NULL, (SIGNAL_FUNC) key_upcase_word);
1119
1120         /* line transmitting */
1121         key_bind("send_line", "Execute the input line", "return", NULL, (SIGNAL_FUNC) key_send_line);
1122         key_bind("word_completion", "", "tab", NULL, (SIGNAL_FUNC) key_word_completion);
1123         key_bind("erase_completion", "", "meta-k", NULL, (SIGNAL_FUNC) key_erase_completion);
1124         key_bind("check_replaces", "Check word replaces", NULL, NULL, (SIGNAL_FUNC) key_check_replaces);
1125
1126         /* window managing */
1127         key_bind("previous_window", "Previous window", "^P", NULL, (SIGNAL_FUNC) key_previous_window);
1128         key_bind("next_window", "Next window", "^N", NULL, (SIGNAL_FUNC) key_next_window);
1129         key_bind("upper_window", "Upper window", "meta-up", NULL, (SIGNAL_FUNC) key_upper_window);
1130         key_bind("lower_window", "Lower window", "meta-down", NULL, (SIGNAL_FUNC) key_lower_window);
1131         key_bind("left_window", "Window in left", "meta-left", NULL, (SIGNAL_FUNC) key_left_window);
1132         key_bind("right_window", "Window in right", "meta-right", NULL, (SIGNAL_FUNC) key_right_window);
1133         key_bind("active_window", "Go to next window with the highest activity", "meta-a", NULL, (SIGNAL_FUNC) key_active_window);
1134         key_bind("next_window_item", "Next channel/query", "^X", NULL, (SIGNAL_FUNC) key_next_window_item);
1135         key_bind("previous_window_item", "Previous channel/query", NULL, NULL, (SIGNAL_FUNC) key_previous_window_item);
1136
1137         key_bind("refresh_screen", "Redraw screen", "^L", NULL, (SIGNAL_FUNC) irssi_redraw);
1138         key_bind("scroll_backward", "Previous page", "prior", NULL, (SIGNAL_FUNC) key_scroll_backward);
1139         key_bind("scroll_backward", NULL, "meta-p", NULL, (SIGNAL_FUNC) key_scroll_backward);
1140         key_bind("scroll_forward", "Next page", "next", NULL, (SIGNAL_FUNC) key_scroll_forward);
1141         key_bind("scroll_forward", NULL, "meta-n", NULL, (SIGNAL_FUNC) key_scroll_forward);
1142         key_bind("scroll_start", "Beginning of the window", "", NULL, (SIGNAL_FUNC) key_scroll_start);
1143         key_bind("scroll_end", "End of the window", "", NULL, (SIGNAL_FUNC) key_scroll_end);
1144
1145         /* inserting special input characters to line.. */
1146         key_bind("escape_char", "Escape the next keypress", NULL, NULL, (SIGNAL_FUNC) key_escape);
1147         key_bind("insert_text", "Append text to line", NULL, NULL, (SIGNAL_FUNC) key_insert_text);
1148
1149         /* autoreplaces */
1150         key_bind("multi", NULL, "return", "check_replaces;send_line", NULL);
1151         key_bind("multi", NULL, "space", "check_replaces;insert_text  ", NULL);
1152
1153         /* moving between windows */
1154         for (n = 0; changekeys[n] != '\0'; n++) {
1155                 key = g_strdup_printf("meta-%c", changekeys[n]);
1156                 ltoa(data, n+1);
1157                 key_bind("change_window", "Change window", key, data, (SIGNAL_FUNC) key_change_window);
1158                 g_free(key);
1159         }
1160
1161         /* misc */
1162         key_bind("stop_irc", "Send SIGSTOP to client", "^Z", NULL, (SIGNAL_FUNC) key_sig_stop);
1163
1164         key_configure_thaw();
1165
1166         signal_add("window changed automatic", (SIGNAL_FUNC) sig_window_auto_changed);
1167         signal_add("gui entry redirect", (SIGNAL_FUNC) sig_gui_entry_redirect);
1168         signal_add("gui key pressed", (SIGNAL_FUNC) sig_gui_key_pressed);
1169         signal_add("setup changed", (SIGNAL_FUNC) setup_changed);
1170 }
1171
1172 void gui_readline_deinit(void)
1173 {
1174         input_listen_deinit();
1175
1176         key_configure_freeze();
1177
1178         key_unbind("backward_character", (SIGNAL_FUNC) key_backward_character);
1179         key_unbind("forward_character", (SIGNAL_FUNC) key_forward_character);
1180         key_unbind("backward_word", (SIGNAL_FUNC) key_backward_word);
1181         key_unbind("forward_word", (SIGNAL_FUNC) key_forward_word);
1182         key_unbind("backward_to_space", (SIGNAL_FUNC) key_backward_to_space);
1183         key_unbind("forward_to_space", (SIGNAL_FUNC) key_forward_to_space);
1184         key_unbind("beginning_of_line", (SIGNAL_FUNC) key_beginning_of_line);
1185         key_unbind("end_of_line", (SIGNAL_FUNC) key_end_of_line);
1186
1187         key_unbind("backward_history", (SIGNAL_FUNC) key_backward_history);
1188         key_unbind("forward_history", (SIGNAL_FUNC) key_forward_history);
1189
1190         key_unbind("backspace", (SIGNAL_FUNC) key_backspace);
1191         key_unbind("delete_character", (SIGNAL_FUNC) key_delete_character);
1192         key_unbind("delete_next_word", (SIGNAL_FUNC) key_delete_next_word);
1193         key_unbind("delete_previous_word", (SIGNAL_FUNC) key_delete_previous_word);
1194         key_unbind("delete_to_next_space", (SIGNAL_FUNC) key_delete_to_next_space);
1195         key_unbind("delete_to_previous_space", (SIGNAL_FUNC) key_delete_to_previous_space);
1196         key_unbind("erase_line", (SIGNAL_FUNC) key_erase_line);
1197         key_unbind("erase_to_beg_of_line", (SIGNAL_FUNC) key_erase_to_beg_of_line);
1198         key_unbind("erase_to_end_of_line", (SIGNAL_FUNC) key_erase_to_end_of_line);
1199         key_unbind("yank_from_cutbuffer", (SIGNAL_FUNC) key_yank_from_cutbuffer);
1200         key_unbind("transpose_characters", (SIGNAL_FUNC) key_transpose_characters);
1201         key_unbind("transpose_words", (SIGNAL_FUNC) key_transpose_words);
1202
1203         key_unbind("capitalize_word", (SIGNAL_FUNC) key_capitalize_word);
1204         key_unbind("downcase_word", (SIGNAL_FUNC) key_downcase_word);
1205         key_unbind("upcase_word", (SIGNAL_FUNC) key_upcase_word);
1206
1207         key_unbind("send_line", (SIGNAL_FUNC) key_send_line);
1208         key_unbind("word_completion", (SIGNAL_FUNC) key_word_completion);
1209         key_unbind("erase_completion", (SIGNAL_FUNC) key_erase_completion);
1210         key_unbind("check_replaces", (SIGNAL_FUNC) key_check_replaces);
1211
1212         key_unbind("previous_window", (SIGNAL_FUNC) key_previous_window);
1213         key_unbind("next_window", (SIGNAL_FUNC) key_next_window);
1214         key_unbind("upper_window", (SIGNAL_FUNC) key_upper_window);
1215         key_unbind("lower_window", (SIGNAL_FUNC) key_lower_window);
1216         key_unbind("left_window", (SIGNAL_FUNC) key_left_window);
1217         key_unbind("right_window", (SIGNAL_FUNC) key_right_window);
1218         key_unbind("active_window", (SIGNAL_FUNC) key_active_window);
1219         key_unbind("next_window_item", (SIGNAL_FUNC) key_next_window_item);
1220         key_unbind("previous_window_item", (SIGNAL_FUNC) key_previous_window_item);
1221
1222         key_unbind("refresh_screen", (SIGNAL_FUNC) irssi_redraw);
1223         key_unbind("scroll_backward", (SIGNAL_FUNC) key_scroll_backward);
1224         key_unbind("scroll_forward", (SIGNAL_FUNC) key_scroll_forward);
1225         key_unbind("scroll_start", (SIGNAL_FUNC) key_scroll_start);
1226         key_unbind("scroll_end", (SIGNAL_FUNC) key_scroll_end);
1227
1228         key_unbind("escape_char", (SIGNAL_FUNC) key_escape);
1229         key_unbind("insert_text", (SIGNAL_FUNC) key_insert_text);
1230         key_unbind("change_window", (SIGNAL_FUNC) key_change_window);
1231         key_unbind("stop_irc", (SIGNAL_FUNC) key_sig_stop);
1232         keyboard_destroy(keyboard);
1233         g_array_free(paste_buffer, TRUE);
1234
1235         key_configure_thaw();
1236
1237         signal_remove("window changed automatic", (SIGNAL_FUNC) sig_window_auto_changed);
1238         signal_remove("gui entry redirect", (SIGNAL_FUNC) sig_gui_entry_redirect);
1239         signal_remove("gui key pressed", (SIGNAL_FUNC) sig_gui_key_pressed);
1240         signal_remove("setup changed", (SIGNAL_FUNC) setup_changed);
1241 }