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