4 Copyright (C) 1999 Timo Sirainen
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.
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.
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.
26 #include "gui-entry.h"
27 #include "gui-printtext.h"
34 static unichar i_toupper(unichar c)
36 if (term_type == TERM_TYPE_UTF8)
37 return g_unichar_toupper(c);
38 return (c >= 0 && c <= 255) ? toupper(c) : c;
41 static unichar i_tolower(unichar c)
43 if (term_type == TERM_TYPE_UTF8)
44 return g_unichar_tolower(c);
45 return (c >= 0 && c <= 255) ? tolower(c) : c;
48 static int i_isalnum(unichar c)
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;
55 GUI_ENTRY_REC *active_entry;
57 static void entry_text_grow(GUI_ENTRY_REC *entry, int grow_size)
59 if (entry->text_len+grow_size < entry->text_alloc)
62 entry->text_alloc = nearest_power(entry->text_alloc+grow_size);
63 entry->text = g_realloc(entry->text,
64 sizeof(unichar) * entry->text_alloc);
67 GUI_ENTRY_REC *gui_entry_create(int xpos, int ypos, int width, int utf8)
71 rec = g_new0(GUI_ENTRY_REC, 1);
75 rec->text_alloc = 1024;
76 rec->text = g_new(unichar, rec->text_alloc);
82 void gui_entry_destroy(GUI_ENTRY_REC *entry)
84 g_return_if_fail(entry != NULL);
86 if (active_entry == entry)
87 gui_entry_set_active(NULL);
90 g_free(entry->prompt);
95 #define big5_width(ch) ((ch)>0xff ? 2:1)
97 void unichars_to_big5(const unichar *str, char *out)
99 for (; *str != '\0'; str++) {
101 *out++ = (*str >> 8) & 0xff;
102 *out++ = *str & 0xff;
107 int strlen_big5(const unsigned char *str)
111 while (*str != '\0') {
112 if (is_big5(str[0], str[1]))
120 void unichars_to_big5_with_pos(const unichar *str, int spos, char *out, int *opos)
122 const unichar *sstart = str;
129 *out ++ = (*str >> 8) & 0xff;
130 *out ++ = *str & 0xff;
132 if(str - sstart == spos)
133 *opos = out - ostart;
138 void big5_to_unichars(const char *str, unichar *out)
140 const unsigned char *p = (const unsigned char *) str;
143 if (is_big5(p[0], p[1])) {
144 *out++ = p[0] << 8 | p[1];
153 /* ----------------------------- */
155 static int pos2scrpos(GUI_ENTRY_REC *entry, int pos)
160 for (i = 0; i < pos; i++) {
161 unichar c = entry->text[i];
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;
173 static int scrpos2pos(GUI_ENTRY_REC *entry, int pos)
177 for (i = 0, xpos = 0; i < entry->text_len; i++) {
178 unichar c = entry->text[i];
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;
187 if (xpos + width > pos)
198 /* Fixes the cursor position in screen */
199 static void gui_entry_fix_cursor(GUI_ENTRY_REC *entry)
203 /* assume prompt len == prompt scrlen */
204 int start = pos2scrpos(entry, entry->scrstart);
205 int now = pos2scrpos(entry, entry->pos);
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) {
214 entry->scrstart = scrpos2pos(entry, now-(entry->width -
215 entry->promptlen)*2/3);
216 start = pos2scrpos(entry, entry->scrstart);
217 entry->scrpos = now - start;
220 if (old_scrstart != entry->scrstart)
221 entry->redraw_needed_from = 0;
224 static void gui_entry_draw_from(GUI_ENTRY_REC *entry, int pos)
229 xpos = entry->xpos + entry->promptlen +
230 pos2scrpos(entry, pos + entry->scrstart) -
231 pos2scrpos(entry, entry->scrstart);
232 end_xpos = entry->xpos + entry->width;
237 term_set_color(root_window, ATTR_RESET);
238 term_move(root_window, xpos, entry->ypos);
240 for (i = entry->scrstart + pos; i < entry->text_len; i++) {
241 unichar c = entry->text[i];
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;
256 term_addch(root_window, ' ');
257 else if (unichar_isprint(c))
258 term_add_unichar(root_window, c);
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);
266 /* clear the rest of the input line */
267 if (xpos < end_xpos) {
268 if (end_xpos == term_width)
269 term_clrtoeol(root_window);
271 while (xpos < end_xpos) {
272 term_addch(root_window, ' ');
279 static void gui_entry_draw(GUI_ENTRY_REC *entry)
281 if (entry->redraw_needed_from >= 0) {
282 gui_entry_draw_from(entry, entry->redraw_needed_from);
283 entry->redraw_needed_from = -1;
286 term_move_cursor(entry->xpos + entry->scrpos + entry->promptlen,
291 static void gui_entry_redraw_from(GUI_ENTRY_REC *entry, int pos)
293 pos -= entry->scrstart;
294 if (pos < 0) pos = 0;
296 if (entry->redraw_needed_from == -1 ||
297 entry->redraw_needed_from > pos)
298 entry->redraw_needed_from = pos;
301 void gui_entry_move(GUI_ENTRY_REC *entry, int xpos, int ypos, int width)
305 g_return_if_fail(entry != NULL);
307 if (entry->xpos != xpos || entry->ypos != ypos) {
308 /* position in screen changed - needs a full redraw */
311 entry->width = width;
312 gui_entry_redraw(entry);
316 if (entry->width == width)
317 return; /* no changes */
319 if (width > entry->width) {
320 /* input line grew - need to draw text at the end */
322 entry->width = width;
323 gui_entry_redraw_from(entry, old_width);
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);
334 gui_entry_draw(entry);
337 void gui_entry_set_active(GUI_ENTRY_REC *entry)
339 active_entry = entry;
342 term_move_cursor(entry->xpos + entry->scrpos +
343 entry->promptlen, entry->ypos);
348 void gui_entry_set_prompt(GUI_ENTRY_REC *entry, const char *str)
352 g_return_if_fail(entry != NULL);
354 oldlen = entry->promptlen;
356 g_free_not_null(entry->prompt);
357 entry->prompt = g_strdup(str);
358 entry->promptlen = format_get_length(str);
361 if (entry->prompt != NULL)
362 gui_printtext(entry->xpos, entry->ypos, entry->prompt);
364 if (entry->promptlen != oldlen) {
365 gui_entry_fix_cursor(entry);
366 gui_entry_draw(entry);
370 void gui_entry_set_hidden(GUI_ENTRY_REC *entry, int hidden)
372 g_return_if_fail(entry != NULL);
374 entry->hidden = hidden;
377 void gui_entry_set_utf8(GUI_ENTRY_REC *entry, int utf8)
379 g_return_if_fail(entry != NULL);
384 void gui_entry_set_text(GUI_ENTRY_REC *entry, const char *str)
386 g_return_if_fail(entry != NULL);
387 g_return_if_fail(str != NULL);
391 entry->text[0] = '\0';
393 gui_entry_insert_text(entry, str);
396 char *gui_entry_get_text(GUI_ENTRY_REC *entry)
401 g_return_val_if_fail(entry != NULL, NULL);
404 buf = g_ucs4_to_utf8(entry->text, -1, NULL, NULL, NULL);
406 buf = g_malloc(entry->text_len*6 + 1);
407 if (term_type == TERM_TYPE_BIG5)
408 unichars_to_big5(entry->text, buf);
410 for (i = 0; i <= entry->text_len; i++)
411 buf[i] = entry->text[i];
416 char *gui_entry_get_text_and_pos(GUI_ENTRY_REC *entry, int *pos)
421 g_return_val_if_fail(entry != NULL, NULL);
424 buf = g_ucs4_to_utf8(entry->text, -1, NULL, NULL, NULL);
425 *pos = g_utf8_offset_to_pointer(buf, entry->pos) - buf;
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);
432 for (i = 0; i <= entry->text_len; i++)
433 buf[i] = entry->text[i];
440 void gui_entry_insert_text(GUI_ENTRY_REC *entry, const char *str)
446 g_return_if_fail(entry != NULL);
447 g_return_if_fail(str != NULL);
449 gui_entry_redraw_from(entry, entry->pos);
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);
458 entry_text_grow(entry, len);
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));
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;
470 for (i = 0; i < len; i++)
471 entry->text[entry->pos + i] = str[i];
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);
481 entry->text_len += len;
484 gui_entry_fix_cursor(entry);
485 gui_entry_draw(entry);
488 void gui_entry_insert_char(GUI_ENTRY_REC *entry, unichar chr)
490 g_return_if_fail(entry != NULL);
492 if (chr == 0 || chr == 13 || chr == 10)
493 return; /* never insert NUL, CR or LF characters */
495 if (entry->utf8 && entry->pos == 0 && mk_wcwidth(chr) == 0)
498 gui_entry_redraw_from(entry, entry->pos);
500 entry_text_grow(entry, 1);
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));
506 entry->text[entry->pos] = chr;
510 gui_entry_fix_cursor(entry);
511 gui_entry_draw(entry);
514 char *gui_entry_get_cutbuffer(GUI_ENTRY_REC *entry)
519 g_return_val_if_fail(entry != NULL, NULL);
521 if (entry->cutbuffer == NULL)
525 buf = g_ucs4_to_utf8(entry->cutbuffer, -1, NULL, NULL, NULL);
527 buf = g_malloc(entry->cutbuffer_len*6 + 1);
528 if (term_type == TERM_TYPE_BIG5)
529 unichars_to_big5(entry->cutbuffer, buf);
531 for (i = 0; i <= entry->cutbuffer_len; i++)
532 buf[i] = entry->cutbuffer[i];
537 void gui_entry_erase_to(GUI_ENTRY_REC *entry, int pos, int update_cutbuffer)
539 int newpos, size = 0;
541 g_return_if_fail(entry != NULL);
543 for (newpos = gui_entry_get_pos(entry); newpos > pos; size++)
545 gui_entry_erase(entry, size, update_cutbuffer);
548 void gui_entry_erase(GUI_ENTRY_REC *entry, int size, int update_cutbuffer)
552 g_return_if_fail(entry != NULL);
554 if (size == 0 || entry->pos < size)
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);
564 entry->cutbuffer_len = size;
565 entry->cutbuffer[size] = '\0';
566 memcpy(entry->cutbuffer, entry->text + entry->pos - size,
567 size * sizeof(unichar));
571 while (entry->pos-size-w > 0 &&
572 mk_wcwidth(entry->text[entry->pos-size-w]) == 0) w++;
574 g_memmove(entry->text + entry->pos - size, entry->text + entry->pos,
575 (entry->text_len-entry->pos+1) * sizeof(unichar));
578 entry->text_len -= size;
580 gui_entry_redraw_from(entry, entry->pos-w);
581 gui_entry_fix_cursor(entry);
582 gui_entry_draw(entry);
585 void gui_entry_erase_cell(GUI_ENTRY_REC *entry)
589 g_return_if_fail(entry != NULL);
592 while (entry->pos+size < entry->text_len &&
593 mk_wcwidth(entry->text[entry->pos+size]) == 0) size++;
595 g_memmove(entry->text + entry->pos, entry->text + entry->pos + size,
596 (entry->text_len-entry->pos-size+1) * sizeof(unichar));
598 entry->text_len -= size;
600 gui_entry_redraw_from(entry, entry->pos);
601 gui_entry_draw(entry);
604 void gui_entry_erase_word(GUI_ENTRY_REC *entry, int to_space)
608 g_return_if_fail(entry != NULL);
615 while (entry->text[to] == ' ' && to > 0)
617 while (entry->text[to] != ' ' && to > 0)
620 while (!i_isalnum(entry->text[to]) && to > 0)
622 while (i_isalnum(entry->text[to]) && to > 0)
627 gui_entry_erase(entry, entry->pos-to, TRUE);
630 void gui_entry_erase_next_word(GUI_ENTRY_REC *entry, int to_space)
634 g_return_if_fail(entry != NULL);
635 if (entry->pos == entry->text_len)
640 while (entry->text[to] == ' ' && to < entry->text_len)
642 while (entry->text[to] != ' ' && to < entry->text_len)
645 while (!i_isalnum(entry->text[to]) && to < entry->text_len)
647 while (i_isalnum(entry->text[to]) && to < entry->text_len)
651 size = to-entry->pos;
653 gui_entry_erase(entry, size, TRUE);
656 void gui_entry_transpose_chars(GUI_ENTRY_REC *entry)
660 if (entry->pos == 0 || entry->text_len < 2)
663 if (entry->pos == entry->text_len)
667 chr = entry->text[entry->pos];
668 entry->text[entry->pos] = entry->text[entry->pos-1];
669 entry->text[entry->pos-1] = chr;
673 gui_entry_redraw_from(entry, entry->pos-2);
674 gui_entry_fix_cursor(entry);
675 gui_entry_draw(entry);
678 void gui_entry_transpose_words(GUI_ENTRY_REC *entry)
680 int spos1, epos1, spos2, epos2;
682 /* find last position */
684 while (epos2 < entry->text_len && !i_isalnum(entry->text[epos2]))
686 while (epos2 < entry->text_len && i_isalnum(entry->text[epos2]))
689 /* find other position */
691 while (spos2 > 0 && !i_isalnum(entry->text[spos2-1]))
693 while (spos2 > 0 && i_isalnum(entry->text[spos2-1]))
697 while (epos1 > 0 && !i_isalnum(entry->text[epos1-1]))
701 while (spos1 > 0 && i_isalnum(entry->text[spos1-1]))
704 /* do wordswap if any found */
705 if (spos1 < epos1 && epos1 < spos2 && spos2 < epos2) {
706 unichar *first, *sep, *second;
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) );
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];
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];
734 gui_entry_redraw_from(entry, spos1);
735 gui_entry_fix_cursor(entry);
736 gui_entry_draw(entry);
739 void gui_entry_capitalize_word(GUI_ENTRY_REC *entry)
741 int pos = entry->pos;
742 while (pos < entry->text_len && !i_isalnum(entry->text[pos]))
745 if (pos < entry->text_len) {
746 entry->text[pos] = i_toupper(entry->text[pos]);
750 while (pos < entry->text_len && i_isalnum(entry->text[pos])) {
751 entry->text[pos] = i_tolower(entry->text[pos]);
755 gui_entry_redraw_from(entry, entry->pos);
757 gui_entry_fix_cursor(entry);
758 gui_entry_draw(entry);
761 void gui_entry_downcase_word(GUI_ENTRY_REC *entry)
763 int pos = entry->pos;
764 while (pos < entry->text_len && !i_isalnum(entry->text[pos]))
767 while (pos < entry->text_len && i_isalnum(entry->text[pos])) {
768 entry->text[pos] = i_tolower(entry->text[pos]);
772 gui_entry_redraw_from(entry, entry->pos);
774 gui_entry_fix_cursor(entry);
775 gui_entry_draw(entry);
778 void gui_entry_upcase_word(GUI_ENTRY_REC *entry)
780 int pos = entry->pos;
781 while (pos < entry->text_len && !i_isalnum(entry->text[pos]))
784 while (pos < entry->text_len && i_isalnum(entry->text[pos])) {
785 entry->text[pos] = i_toupper(entry->text[pos]);
789 gui_entry_redraw_from(entry, entry->pos);
791 gui_entry_fix_cursor(entry);
792 gui_entry_draw(entry);
795 int gui_entry_get_pos(GUI_ENTRY_REC *entry)
797 g_return_val_if_fail(entry != NULL, 0);
802 void gui_entry_set_pos(GUI_ENTRY_REC *entry, int pos)
804 g_return_if_fail(entry != NULL);
806 if (pos >= 0 && pos <= entry->text_len)
809 gui_entry_fix_cursor(entry);
810 gui_entry_draw(entry);
813 void gui_entry_move_pos(GUI_ENTRY_REC *entry, int pos)
815 g_return_if_fail(entry != NULL);
817 if (entry->pos + pos >= 0 && entry->pos + pos <= entry->text_len)
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)
827 gui_entry_fix_cursor(entry);
828 gui_entry_draw(entry);
831 static void gui_entry_move_words_left(GUI_ENTRY_REC *entry, int count, int to_space)
836 while (count > 0 && pos > 0) {
838 while (pos > 0 && entry->text[pos-1] == ' ')
840 while (pos > 0 && entry->text[pos-1] != ' ')
843 while (pos > 0 && !i_isalnum(entry->text[pos-1]))
845 while (pos > 0 && i_isalnum(entry->text[pos-1]))
854 static void gui_entry_move_words_right(GUI_ENTRY_REC *entry, int count, int to_space)
859 while (count > 0 && pos < entry->text_len) {
861 while (pos < entry->text_len && entry->text[pos] == ' ')
863 while (pos < entry->text_len && entry->text[pos] != ' ')
866 while (pos < entry->text_len && !i_isalnum(entry->text[pos]))
868 while (pos < entry->text_len && i_isalnum(entry->text[pos]))
877 void gui_entry_move_words(GUI_ENTRY_REC *entry, int count, int to_space)
879 g_return_if_fail(entry != NULL);
882 gui_entry_move_words_left(entry, -count, to_space);
884 gui_entry_move_words_right(entry, count, to_space);
886 gui_entry_fix_cursor(entry);
887 gui_entry_draw(entry);
890 void gui_entry_redraw(GUI_ENTRY_REC *entry)
892 g_return_if_fail(entry != NULL);
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);