Added SILC Thread Queue API
[runtime.git] / apps / irssi / src / fe-text / gui-entry.c
1 /*
2  gui-entry.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 "misc.h"
23 #include "utf8.h"
24 #include "formats.h"
25
26 #include "gui-entry.h"
27 #include "gui-printtext.h"
28 #include "term.h"
29
30 const unichar empty_str[] = { 0 };
31
32 GUI_ENTRY_REC *active_entry;
33
34 static void entry_text_grow(GUI_ENTRY_REC *entry, int grow_size)
35 {
36         if (entry->text_len+grow_size < entry->text_alloc)
37                 return;
38
39         entry->text_alloc = nearest_power(entry->text_alloc+grow_size);
40         entry->text = g_realloc(entry->text,
41                                 sizeof(unichar) * entry->text_alloc);
42 }
43
44 GUI_ENTRY_REC *gui_entry_create(int xpos, int ypos, int width, int utf8)
45 {
46         GUI_ENTRY_REC *rec;
47
48         rec = g_new0(GUI_ENTRY_REC, 1);
49         rec->xpos = xpos;
50         rec->ypos = ypos;
51         rec->width = width;
52         rec->text_alloc = 1024;
53         rec->text = g_new(unichar, rec->text_alloc);
54         rec->text[0] = '\0';
55         rec->utf8 = utf8;
56         return rec;
57 }
58
59 void gui_entry_destroy(GUI_ENTRY_REC *entry)
60 {
61         g_return_if_fail(entry != NULL);
62
63         if (active_entry == entry)
64                 gui_entry_set_active(NULL);
65
66         g_free(entry->text);
67         g_free(entry->prompt);
68         g_free(entry);
69 }
70
71 /* big5 functions */
72 #define big5_width(ch) ((ch)>0xff ? 2:1)
73
74 void unichars_to_big5(const unichar *str, char *out)
75 {
76         for (; *str != '\0'; str++) {
77                 if (*str > 0xff)
78                         *out++ = (*str >> 8) & 0xff;
79                 *out++ = *str & 0xff;
80         }
81         *out = '\0';
82 }
83
84 int strlen_big5(const unsigned char *str)
85 {
86         int len=0;
87
88         if (term_type != TERM_TYPE_BIG5)
89                 return strlen(str);
90
91         while (*str != '\0') {
92                 if (is_big5(str[0], str[1]))
93                         str++;
94                 len++;
95                 str++;
96         }
97         return len;
98 }
99
100 void unichars_to_big5_with_pos(const unichar *str, int spos, char *out, int *opos)
101 {
102         const unichar *sstart = str;
103         char *ostart = out;
104
105         *opos = 0;
106         while(*str != '\0')
107         {
108                 if(*str > 0xff)
109                         *out ++ = (*str >> 8) & 0xff;
110                 *out ++ = *str & 0xff;
111                 str ++;
112                 if(str - sstart == spos)
113                         *opos = out - ostart;
114         }
115         *out = '\0';
116 }
117
118 void big5_to_unichars(const char *str, unichar *out)
119 {
120         const unsigned char *p = (const unsigned char *) str;
121
122         while (*p != '\0') {
123                 if (is_big5(p[0], p[1])) {
124                         *out++ = p[0] << 8 | p[1];
125                         p += 2;
126                 } else {
127                         *out++ = *p++;
128                 }
129         }
130         *out = '\0';
131 }
132
133 /* ----------------------------- */
134
135 static int pos2scrpos(GUI_ENTRY_REC *entry, int pos)
136 {
137         unichar *p;
138         int xpos = 0;
139
140         for (p = entry->text; p - entry->text < pos; p++) {
141                 if (term_type == TERM_TYPE_BIG5)
142                         xpos += big5_width(*p);
143                 else if (entry->utf8)
144                         xpos += utf8_width(*p);
145                 else
146                         xpos++;
147         }
148         return xpos;
149 }
150
151 static int scrpos2pos(GUI_ENTRY_REC *entry, int pos)
152 {
153         int i, width, xpos;
154
155         for (i = 0, xpos = 0; entry->text[i]; i++) {
156                 unichar *p = entry->text+i;
157
158                 if (term_type == TERM_TYPE_BIG5)
159                         width = big5_width(*p);
160                 else if (entry->utf8)
161                         width = utf8_width(*p);
162                 else
163                         width = 1;
164
165                 if (xpos + width > pos)
166                         break;
167                 xpos += width;
168         }
169
170         if (xpos == pos)
171                 return i;
172         else
173                 return i-1;
174 }
175
176 /* Fixes the cursor position in screen */
177 static void gui_entry_fix_cursor(GUI_ENTRY_REC *entry)
178 {
179         int old_scrstart;
180
181         /* assume prompt len == prompt scrlen */
182         int start = pos2scrpos(entry, entry->scrstart);
183         int now = pos2scrpos(entry, entry->pos);
184
185         old_scrstart = entry->scrstart;
186         if (now-start < entry->width - 2 - entry->promptlen && now-start > 0)
187                 entry->scrpos = now-start;
188         else if (now < entry->width - 1 - entry->promptlen) {
189                 entry->scrstart = 0;
190                 entry->scrpos = now;
191         } else {
192                 entry->scrstart = scrpos2pos(entry, now-(entry->width -
193                                                          entry->promptlen)*2/3);
194                 start = pos2scrpos(entry, entry->scrstart);
195                 entry->scrpos = now - start;
196         }
197
198         if (old_scrstart != entry->scrstart)
199                 entry->redraw_needed_from = 0;
200 }
201
202 static void gui_entry_draw_from(GUI_ENTRY_REC *entry, int pos)
203 {
204         const unichar *p;
205         int xpos, end_xpos;
206
207         xpos = entry->xpos + entry->promptlen + 
208                 pos2scrpos(entry, pos + entry->scrstart) - 
209                 pos2scrpos(entry, entry->scrstart);
210         end_xpos = entry->xpos + entry->width;
211
212         if (xpos > end_xpos)
213                 return;
214
215         term_set_color(root_window, ATTR_RESET);
216         term_move(root_window, xpos, entry->ypos);
217
218         p = entry->scrstart + pos < entry->text_len ?
219                 entry->text + entry->scrstart + pos : empty_str;
220         for (; *p != '\0'; p++) {
221                 if (entry->hidden)
222                         xpos++;
223                 else if (term_type == TERM_TYPE_BIG5)
224                         xpos += big5_width(*p);
225                 else if (entry->utf8)
226                         xpos += utf8_width(*p);
227                 else
228                         xpos++;
229
230                 if (xpos > end_xpos)
231                         break;
232
233                 if (entry->hidden)
234                         term_addch(root_window, ' ');
235                 else if (*p >= 32 && (entry->utf8 || (*p & 127) >= 32))
236                         term_add_unichar(root_window, *p);
237                 else {
238                         term_set_color(root_window, ATTR_RESET|ATTR_REVERSE);
239                         term_addch(root_window, *p+'A'-1);
240                         term_set_color(root_window, ATTR_RESET);
241                 }
242         }
243
244         /* clear the rest of the input line */
245         if (end_xpos == term_width-1)
246                 term_clrtoeol(root_window);
247         else {
248                 while (xpos < end_xpos) {
249                         term_addch(root_window, ' ');
250                         xpos++;
251                 }
252         }
253 }
254
255 static void gui_entry_draw(GUI_ENTRY_REC *entry)
256 {
257         if (entry->redraw_needed_from >= 0) {
258                 gui_entry_draw_from(entry, entry->redraw_needed_from);
259                 entry->redraw_needed_from = -1;
260         }
261
262         term_move_cursor(entry->xpos + entry->scrpos + entry->promptlen,
263                          entry->ypos);
264         term_refresh(NULL);
265 }
266
267 static void gui_entry_redraw_from(GUI_ENTRY_REC *entry, int pos)
268 {
269         pos -= entry->scrstart;
270         if (pos < 0) pos = 0;
271
272         if (entry->redraw_needed_from == -1 ||
273             entry->redraw_needed_from > pos)
274                 entry->redraw_needed_from = pos;
275 }
276
277 void gui_entry_move(GUI_ENTRY_REC *entry, int xpos, int ypos, int width)
278 {
279         int old_width;
280
281         g_return_if_fail(entry != NULL);
282
283         if (entry->xpos != xpos || entry->ypos != ypos) {
284                 /* position in screen changed - needs a full redraw */
285                 entry->xpos = xpos;
286                 entry->ypos = ypos;
287                 entry->width = width;
288                 gui_entry_redraw(entry);
289                 return;
290         }
291
292         if (entry->width == width)
293                 return; /* no changes */
294
295         if (width > entry->width) {
296                 /* input line grew - need to draw text at the end */
297                 old_width = width;
298                 entry->width = width;
299                 gui_entry_redraw_from(entry, old_width);
300         } else {
301                 /* input line shrinked - make sure the cursor
302                    is inside the input line */
303                 entry->width = width;
304                 if (entry->pos - entry->scrstart >
305                     entry->width-2 - entry->promptlen) {
306                         gui_entry_fix_cursor(entry);
307                 }
308         }
309
310         gui_entry_draw(entry);
311 }
312
313 void gui_entry_set_active(GUI_ENTRY_REC *entry)
314 {
315         active_entry = entry;
316
317         if (entry != NULL) {
318                 term_move_cursor(entry->xpos + entry->scrpos +
319                                  entry->promptlen, entry->ypos);
320                 term_refresh(NULL);
321         }
322 }
323
324 void gui_entry_set_prompt(GUI_ENTRY_REC *entry, const char *str)
325 {
326         int oldlen;
327
328         g_return_if_fail(entry != NULL);
329
330         oldlen = entry->promptlen;
331         if (str != NULL) {
332                 g_free_not_null(entry->prompt);
333                 entry->prompt = g_strdup(str);
334                 entry->promptlen = format_get_length(str);
335         }
336
337         if (entry->prompt != NULL)
338                 gui_printtext(entry->xpos, entry->ypos, entry->prompt);
339
340         if (entry->promptlen != oldlen) {
341                 gui_entry_fix_cursor(entry);
342                 gui_entry_draw(entry);
343         }
344 }
345
346 void gui_entry_set_hidden(GUI_ENTRY_REC *entry, int hidden)
347 {
348         g_return_if_fail(entry != NULL);
349
350         entry->hidden = hidden;
351 }
352
353 void gui_entry_set_utf8(GUI_ENTRY_REC *entry, int utf8)
354 {
355         g_return_if_fail(entry != NULL);
356
357         entry->utf8 = utf8;
358 }
359
360 void gui_entry_set_text(GUI_ENTRY_REC *entry, const char *str)
361 {
362         g_return_if_fail(entry != NULL);
363         g_return_if_fail(str != NULL);
364
365         entry->text_len = 0;
366         entry->pos = 0;
367         entry->text[0] = '\0';
368
369         gui_entry_insert_text(entry, str);
370 }
371
372 char *gui_entry_get_text(GUI_ENTRY_REC *entry)
373 {
374         char *buf;
375         int i;
376
377         g_return_val_if_fail(entry != NULL, NULL);
378
379         buf = g_malloc(entry->text_len*6 + 1);
380         if (entry->utf8)
381                 utf16_to_utf8(entry->text, buf);
382         else {
383                 if (term_type == TERM_TYPE_BIG5)
384                         unichars_to_big5(entry->text, buf);
385                 else
386                         for (i = 0; i <= entry->text_len; i++)
387                                 buf[i] = entry->text[i];
388         }
389         return buf;
390 }
391
392 char *gui_entry_get_text_and_pos(GUI_ENTRY_REC *entry, int *pos)
393 {
394         char *buf;
395         int i;
396
397         g_return_val_if_fail(entry != NULL, NULL);
398
399         buf = g_malloc(entry->text_len*6 + 1);
400         if (entry->utf8)
401                 utf16_to_utf8_with_pos(entry->text, entry->pos, buf, pos);
402         else {
403                 if(term_type==TERM_TYPE_BIG5)
404                         unichars_to_big5_with_pos(entry->text, entry->pos, buf, pos);
405                 else
406                 {
407                         for (i = 0; i <= entry->text_len; i++)
408                                 buf[i] = entry->text[i];
409                         *pos = entry->pos;
410                 }
411         }
412         return buf;
413 }
414
415 void gui_entry_insert_text(GUI_ENTRY_REC *entry, const char *str)
416 {
417         unichar chr;
418         int i, len;
419
420         g_return_if_fail(entry != NULL);
421         g_return_if_fail(str != NULL);
422
423         gui_entry_redraw_from(entry, entry->pos);
424
425         len = !entry->utf8 ? strlen_big5(str) : strlen_utf8(str);
426         entry_text_grow(entry, len);
427
428         /* make space for the string */
429         g_memmove(entry->text + entry->pos + len, entry->text + entry->pos,
430                   (entry->text_len-entry->pos + 1) * sizeof(unichar));
431
432         if (!entry->utf8) {
433                 if (term_type == TERM_TYPE_BIG5) {
434                         chr = entry->text[entry->pos + len];
435                         big5_to_unichars(str, entry->text + entry->pos);
436                         entry->text[entry->pos + len] = chr;
437                 } else {
438                         for (i = 0; i < len; i++)
439                                 entry->text[entry->pos + i] = str[i];
440                 }
441         } else {
442                 chr = entry->text[entry->pos+len];
443                 utf8_to_utf16(str, entry->text+entry->pos);
444                 entry->text[entry->pos+len] = chr;
445         }
446
447         entry->text_len += len;
448         entry->pos += len;
449
450         gui_entry_fix_cursor(entry);
451         gui_entry_draw(entry);
452 }
453
454 void gui_entry_insert_char(GUI_ENTRY_REC *entry, unichar chr)
455 {
456         g_return_if_fail(entry != NULL);
457
458         if (chr == 0 || chr == 13 || chr == 10)
459                 return; /* never insert NUL, CR or LF characters */
460
461         gui_entry_redraw_from(entry, entry->pos);
462
463         entry_text_grow(entry, 1);
464
465         /* make space for the string */
466         g_memmove(entry->text + entry->pos + 1, entry->text + entry->pos,
467                   (entry->text_len-entry->pos + 1) * sizeof(unichar));
468
469         entry->text[entry->pos] = chr;
470         entry->text_len++;
471         entry->pos++;
472
473         gui_entry_fix_cursor(entry);
474         gui_entry_draw(entry);
475 }
476
477 char *gui_entry_get_cutbuffer(GUI_ENTRY_REC *entry)
478 {
479         char *buf;
480         int i;
481
482         g_return_val_if_fail(entry != NULL, NULL);
483
484         if (entry->cutbuffer == NULL)
485                 return NULL;
486
487         buf = g_malloc(entry->cutbuffer_len*6 + 1);
488         if (entry->utf8)
489                 utf16_to_utf8(entry->cutbuffer, buf);
490         else if (term_type == TERM_TYPE_BIG5) {
491                 unichars_to_big5(entry->cutbuffer, buf);
492         } else {
493                 for (i = 0; i <= entry->cutbuffer_len; i++)
494                         buf[i] = entry->cutbuffer[i];
495         }
496         return buf;
497 }
498
499 void gui_entry_erase_to(GUI_ENTRY_REC *entry, int pos, int update_cutbuffer)
500 {
501         int newpos, size = 0;
502
503         g_return_if_fail(entry != NULL);
504
505         for (newpos = gui_entry_get_pos(entry); newpos > pos; size++)
506                 newpos = newpos - 1;
507         gui_entry_erase(entry, size, update_cutbuffer);
508 }
509
510 void gui_entry_erase(GUI_ENTRY_REC *entry, int size, int update_cutbuffer)
511 {
512         g_return_if_fail(entry != NULL);
513
514         if (entry->pos < size)
515                 return;
516
517         if (update_cutbuffer) {
518                 /* put erased text to cutbuffer */
519                 if (entry->cutbuffer == NULL || entry->cutbuffer_len < size) {
520                         g_free(entry->cutbuffer);
521                         entry->cutbuffer = g_new(unichar, size+1);
522                 }
523
524                 entry->cutbuffer_len = size;
525                 entry->cutbuffer[size] = '\0';
526                 memcpy(entry->cutbuffer, entry->text + entry->pos - size,
527                        size * sizeof(unichar));
528         }
529
530         if (size == 0) {
531                 /* we just wanted to clear the cutbuffer */
532                 return;
533         }
534
535         g_memmove(entry->text + entry->pos - size, entry->text + entry->pos,
536                   (entry->text_len-entry->pos+1) * sizeof(unichar));
537
538         entry->pos -= size;
539         entry->text_len -= size;
540
541         gui_entry_redraw_from(entry, entry->pos);
542         gui_entry_fix_cursor(entry);
543         gui_entry_draw(entry);
544 }
545
546 void gui_entry_erase_word(GUI_ENTRY_REC *entry, int to_space)
547 {
548         int to;
549
550         g_return_if_fail(entry != NULL);
551         if (entry->pos == 0)
552                 return;
553
554         to = entry->pos - 1;
555
556         if (to_space) {
557                 while (entry->text[to] == ' ' && to > 0)
558                         to--;
559                 while (entry->text[to] != ' ' && to > 0)
560                         to--;
561         } else {
562                 while (!i_isalnum(entry->text[to]) && to > 0)
563                         to--;
564                 while (i_isalnum(entry->text[to]) && to > 0)
565                         to--;
566         }
567         if (to > 0) to++;
568
569         gui_entry_erase(entry, entry->pos-to, TRUE);
570 }
571
572 void gui_entry_erase_next_word(GUI_ENTRY_REC *entry, int to_space)
573 {
574         int to, size;
575
576         g_return_if_fail(entry != NULL);
577         if (entry->pos == entry->text_len)
578                 return;
579
580         to = entry->pos;
581         if (to_space) {
582                 while (entry->text[to] == ' ' && to < entry->text_len)
583                         to++;
584                 while (entry->text[to] != ' ' && to < entry->text_len)
585                         to++;
586         } else {
587                 while (!i_isalnum(entry->text[to]) && to < entry->text_len)
588                         to++;
589                 while (i_isalnum(entry->text[to]) && to < entry->text_len)
590                         to++;
591         }
592
593         size = to-entry->pos;
594         entry->pos = to;
595         gui_entry_erase(entry, size, TRUE);
596 }
597
598 void gui_entry_transpose_chars(GUI_ENTRY_REC *entry)
599 {
600         unichar chr;
601
602         if (entry->pos == 0 || entry->text_len < 2)
603                 return;
604
605         if (entry->pos == entry->text_len)
606                 entry->pos--;
607
608         /* swap chars */
609         chr = entry->text[entry->pos];
610         entry->text[entry->pos] = entry->text[entry->pos-1];
611         entry->text[entry->pos-1] = chr;
612
613         entry->pos++;
614
615         gui_entry_redraw_from(entry, entry->pos-2);
616         gui_entry_fix_cursor(entry);
617         gui_entry_draw(entry);
618 }
619
620 void gui_entry_transpose_words(GUI_ENTRY_REC *entry)
621 {
622         int spos1, epos1, spos2, epos2;
623
624         /* find last position */
625         epos2 = entry->pos;
626         while (epos2 < entry->text_len && !i_isalnum(entry->text[epos2]))
627                 epos2++;
628         while (epos2 < entry->text_len &&  i_isalnum(entry->text[epos2]))
629                 epos2++;
630
631         /* find other position */
632         spos2 = epos2;
633         while (spos2 > 0 && !i_isalnum(entry->text[spos2-1]))
634                 spos2--;
635         while (spos2 > 0 &&  i_isalnum(entry->text[spos2-1]))
636                 spos2--;
637
638         epos1 = spos2;
639         while (epos1 > 0 && !i_isalnum(entry->text[epos1-1]))
640                 epos1--;
641
642         spos1 = epos1;
643         while (spos1 > 0 && i_isalnum(entry->text[spos1-1]))
644                 spos1--;
645
646         /* do wordswap if any found */
647         if (spos1 < epos1 && epos1 < spos2 && spos2 < epos2) {
648                 unichar *first, *sep, *second;
649                 int i;
650
651                 first  = (unichar *) g_malloc( (epos1 - spos1) * sizeof(unichar) );
652                 sep    = (unichar *) g_malloc( (spos2 - epos1) * sizeof(unichar) );
653                 second = (unichar *) g_malloc( (epos2 - spos2) * sizeof(unichar) );
654
655                 for (i = spos1; i < epos1; i++)
656                         first[i-spos1] = entry->text[i];
657                 for (i = epos1; i < spos2; i++)
658                         sep[i-epos1] = entry->text[i];
659                 for (i = spos2; i < epos2; i++)
660                         second[i-spos2] = entry->text[i];
661
662                 entry->pos = spos1;
663                 for (i = 0; i < epos2-spos2; i++)
664                         entry->text[entry->pos++] = second[i];
665                 for (i = 0; i < spos2-epos1; i++)
666                         entry->text[entry->pos++] = sep[i];
667                 for (i = 0; i < epos1-spos1; i++)
668                         entry->text[entry->pos++] = first[i];
669
670                 g_free(first);
671                 g_free(sep);
672                 g_free(second);
673
674         }
675         
676         gui_entry_redraw_from(entry, spos1);
677         gui_entry_fix_cursor(entry);
678         gui_entry_draw(entry);
679 }
680
681 void gui_entry_capitalize_word(GUI_ENTRY_REC *entry)
682 {
683         int pos = entry->pos;
684         while (pos < entry->text_len && !i_isalnum(entry->text[pos]))
685                 pos++;
686
687         if (pos < entry->text_len) {
688                 entry->text[pos] = i_toupper(entry->text[pos]);
689                 pos++;
690         }
691
692         while (pos < entry->text_len && i_isalnum(entry->text[pos])) {
693                 entry->text[pos] = i_tolower(entry->text[pos]);
694                 pos++;
695         }
696
697         gui_entry_redraw_from(entry, entry->pos);
698         entry->pos = pos;
699         gui_entry_fix_cursor(entry);
700         gui_entry_draw(entry);
701 }
702
703 void gui_entry_downcase_word(GUI_ENTRY_REC *entry)
704 {
705         int pos = entry->pos;
706         while (pos < entry->text_len && !i_isalnum(entry->text[pos]))
707                 pos++;
708
709         while (pos < entry->text_len && i_isalnum(entry->text[pos])) {
710                 entry->text[pos] = i_tolower(entry->text[pos]);
711                 pos++;
712         }
713
714         gui_entry_redraw_from(entry, entry->pos);
715         entry->pos = pos;
716         gui_entry_fix_cursor(entry);
717         gui_entry_draw(entry);
718 }
719
720 void gui_entry_upcase_word(GUI_ENTRY_REC *entry)
721 {
722         int pos = entry->pos;
723         while (pos < entry->text_len && !i_isalnum(entry->text[pos]))
724                 pos++;
725
726         while (pos < entry->text_len && i_isalnum(entry->text[pos])) {
727                 entry->text[pos] = i_toupper(entry->text[pos]);
728                 pos++;
729         }
730
731         gui_entry_redraw_from(entry, entry->pos);
732         entry->pos = pos;
733         gui_entry_fix_cursor(entry);
734         gui_entry_draw(entry);
735 }
736
737 int gui_entry_get_pos(GUI_ENTRY_REC *entry)
738 {
739         g_return_val_if_fail(entry != NULL, 0);
740
741         return entry->pos;
742 }
743
744 void gui_entry_set_pos(GUI_ENTRY_REC *entry, int pos)
745 {
746         g_return_if_fail(entry != NULL);
747
748         if (pos >= 0 && pos <= entry->text_len)
749                 entry->pos = pos;
750
751         gui_entry_fix_cursor(entry);
752         gui_entry_draw(entry);
753 }
754
755 void gui_entry_move_pos(GUI_ENTRY_REC *entry, int pos)
756 {
757         g_return_if_fail(entry != NULL);
758
759         if (entry->pos + pos >= 0 && entry->pos + pos <= entry->text_len)
760                 entry->pos += pos;
761
762         gui_entry_fix_cursor(entry);
763         gui_entry_draw(entry);
764 }
765
766 static void gui_entry_move_words_left(GUI_ENTRY_REC *entry, int count, int to_space)
767 {
768         int pos;
769
770         pos = entry->pos;
771         while (count > 0 && pos > 0) {
772                 if (to_space) {
773                         while (pos > 0 && entry->text[pos-1] == ' ')
774                                 pos--;
775                         while (pos > 0 && entry->text[pos-1] != ' ')
776                                 pos--;
777                 } else {
778                         while (pos > 0 && !i_isalnum(entry->text[pos-1]))
779                                 pos--;
780                         while (pos > 0 &&  i_isalnum(entry->text[pos-1]))
781                                 pos--;
782                 }
783                 count--;
784         }
785
786         entry->pos = pos;
787 }
788
789 static void gui_entry_move_words_right(GUI_ENTRY_REC *entry, int count, int to_space)
790 {
791         int pos;
792
793         pos = entry->pos;
794         while (count > 0 && pos < entry->text_len) {
795                 if (to_space) {
796                         while (pos < entry->text_len && entry->text[pos] == ' ')
797                                 pos++;
798                         while (pos < entry->text_len && entry->text[pos] != ' ')
799                                 pos++;
800                 } else {
801                         while (pos < entry->text_len && !i_isalnum(entry->text[pos]))
802                                 pos++;
803                         while (pos < entry->text_len &&  i_isalnum(entry->text[pos]))
804                                 pos++;
805                 }
806                 count--;
807         }
808
809         entry->pos = pos;
810 }
811
812 void gui_entry_move_words(GUI_ENTRY_REC *entry, int count, int to_space)
813 {
814         g_return_if_fail(entry != NULL);
815
816         if (count < 0)
817                 gui_entry_move_words_left(entry, -count, to_space);
818         else if (count > 0)
819                 gui_entry_move_words_right(entry, count, to_space);
820
821         gui_entry_fix_cursor(entry);
822         gui_entry_draw(entry);
823 }
824
825 void gui_entry_redraw(GUI_ENTRY_REC *entry)
826 {
827         g_return_if_fail(entry != NULL);
828
829         gui_entry_set_prompt(entry, NULL);
830         gui_entry_redraw_from(entry, 0);
831         gui_entry_fix_cursor(entry);
832         gui_entry_draw(entry);
833 }