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 #include "recode.h"
30 
31 #undef i_toupper
32 #undef i_tolower
33 #undef i_isalnum
34 
35 #define KILL_RING_MAX 10
36 
i_toupper(unichar c)37 static unichar i_toupper(unichar c)
38 {
39 	if (term_type == TERM_TYPE_UTF8)
40 		return g_unichar_toupper(c);
41 	return c <= 255 ? toupper(c) : c;
42 }
43 
i_tolower(unichar c)44 static unichar i_tolower(unichar c)
45 {
46 	if (term_type == TERM_TYPE_UTF8)
47 		return g_unichar_tolower(c);
48 	return c <= 255 ? tolower(c) : c;
49 }
50 
i_isalnum(unichar c)51 static int i_isalnum(unichar c)
52 {
53 	if (term_type == TERM_TYPE_UTF8)
54 		return (g_unichar_isalnum(c) || i_wcwidth(c) == 0);
55 	return c <= 255 ? isalnum(c) : 0;
56 }
57 
58 GUI_ENTRY_REC *active_entry;
59 
entry_text_grow(GUI_ENTRY_REC * entry,int grow_size)60 static void entry_text_grow(GUI_ENTRY_REC *entry, int grow_size)
61 {
62 	if (entry->text_len+grow_size < entry->text_alloc)
63 		return;
64 
65 	entry->text_alloc = nearest_power(entry->text_alloc+grow_size);
66 	entry->text = g_realloc(entry->text,
67 				sizeof(unichar) * entry->text_alloc);
68 
69 	if (entry->uses_extents)
70 		entry->extents = g_realloc(entry->extents,
71 				   sizeof(char *) * entry->text_alloc);
72 }
73 
gui_entry_create(int xpos,int ypos,int width,int utf8)74 GUI_ENTRY_REC *gui_entry_create(int xpos, int ypos, int width, int utf8)
75 {
76 	GUI_ENTRY_REC *rec;
77 
78 	rec = g_new0(GUI_ENTRY_REC, 1);
79 	rec->xpos = xpos;
80 	rec->ypos = ypos;
81 	rec->width = width;
82 	rec->text_alloc = 1024;
83 	rec->text = g_new(unichar, rec->text_alloc);
84 	rec->extents = NULL;
85 	rec->text[0] = '\0';
86 	rec->utf8 = utf8;
87 	return rec;
88 }
89 
destroy_extents(GUI_ENTRY_REC * entry)90 static void destroy_extents(GUI_ENTRY_REC *entry)
91 {
92 	if (entry->uses_extents) {
93 		int i;
94 		for (i = 0; i < entry->text_alloc; i++) {
95 			if (entry->extents[i] != NULL) {
96 				g_free(entry->extents[i]);
97 			}
98 		}
99 	}
100 	g_free(entry->extents);
101 	entry->extents = NULL;
102 	entry->uses_extents = FALSE;
103 }
104 
gui_entry_destroy(GUI_ENTRY_REC * entry)105 void gui_entry_destroy(GUI_ENTRY_REC *entry)
106 {
107 	GSList *tmp;
108 
109         g_return_if_fail(entry != NULL);
110 
111 	if (active_entry == entry)
112 		gui_entry_set_active(NULL);
113 
114 	for (tmp = entry->kill_ring; tmp != NULL; tmp = tmp->next) {
115 		GUI_ENTRY_CUTBUFFER_REC *rec = tmp->data;
116 		if (rec != NULL) {
117 			g_free(rec->cutbuffer);
118 			g_free(rec);
119 		}
120 	}
121 	g_slist_free(entry->kill_ring);
122 
123 	destroy_extents(entry);
124 	g_free(entry->text);
125 	g_free(entry->prompt);
126 	g_free(entry);
127 }
128 
129 /* big5 functions */
130 #define big5_width(ch) ((ch)>0xff ? 2:1)
131 
unichars_to_big5(const unichar * str,char * out)132 void unichars_to_big5(const unichar *str, char *out)
133 {
134 	for (; *str != '\0'; str++) {
135 		if (*str > 0xff)
136 			*out++ = (*str >> 8) & 0xff;
137 		*out++ = *str & 0xff;
138 	}
139 	*out = '\0';
140 }
141 
strlen_big5(const unsigned char * str)142 int strlen_big5(const unsigned char *str)
143 {
144 	int len=0;
145 
146 	while (*str != '\0') {
147 		if (is_big5(str[0], str[1]))
148 			str++;
149 		len++;
150 		str++;
151 	}
152 	return len;
153 }
154 
unichars_to_big5_with_pos(const unichar * str,int spos,char * out,int * opos)155 void unichars_to_big5_with_pos(const unichar *str, int spos, char *out, int *opos)
156 {
157 	const unichar *sstart = str;
158 	char *ostart = out;
159 
160 	*opos = 0;
161 	while(*str != '\0')
162 	{
163 		if(*str > 0xff)
164 			*out ++ = (*str >> 8) & 0xff;
165 		*out ++ = *str & 0xff;
166 		str ++;
167 		if(str - sstart == spos)
168 			*opos = out - ostart;
169 	}
170 	*out = '\0';
171 }
172 
big5_to_unichars(const char * str,unichar * out)173 void big5_to_unichars(const char *str, unichar *out)
174 {
175 	const unsigned char *p = (const unsigned char *) str;
176 
177 	while (*p != '\0') {
178 		if (is_big5(p[0], p[1])) {
179 			*out++ = p[0] << 8 | p[1];
180 			p += 2;
181 		} else {
182 			*out++ = *p++;
183 		}
184 	}
185 	*out = '\0';
186 }
187 
188 /* Return screen length of plain string */
scrlen_str(const char * str,int utf8)189 static int scrlen_str(const char *str, int utf8)
190 {
191 	int len = 0;
192 	char *stripped;
193 	g_return_val_if_fail(str != NULL, 0);
194 
195 	stripped = strip_codes(str);
196 	len = string_width(stripped, utf8 ? TREAT_STRING_AS_UTF8 : TREAT_STRING_AS_BYTES);
197 	g_free(stripped);
198 	return len;
199 }
200 
201 /* ----------------------------- */
202 
pos2scrpos(GUI_ENTRY_REC * entry,int pos,int cursor)203 static int pos2scrpos(GUI_ENTRY_REC *entry, int pos, int cursor)
204 {
205 	int i;
206 	int xpos = 0;
207 
208 	if (!cursor && pos <= 0)
209 		return 0;
210 
211 	if (entry->uses_extents && entry->extents[0] != NULL) {
212 		xpos += scrlen_str(entry->extents[0], entry->utf8);
213 	}
214 
215 	for (i = 0; i < entry->text_len && i < pos; i++) {
216 		unichar c = entry->text[i];
217 		const char *extent = entry->uses_extents ? entry->extents[i+1] : NULL;
218 
219 		if (term_type == TERM_TYPE_BIG5)
220 			xpos += big5_width(c);
221 		else if (entry->utf8)
222 			xpos += unichar_isprint(c) ? i_wcwidth(c) : 1;
223 		else
224 			xpos++;
225 
226 		if (extent != NULL) {
227 			xpos += scrlen_str(extent, entry->utf8);
228 		}
229 	}
230 	return xpos + pos - i;
231 }
232 
scrpos2pos(GUI_ENTRY_REC * entry,int pos)233 static int scrpos2pos(GUI_ENTRY_REC *entry, int pos)
234 {
235 	int i, width, xpos = 0;
236 
237 	if (entry->uses_extents && entry->extents[0] != NULL) {
238 		xpos += scrlen_str(entry->extents[0], entry->utf8);
239 	}
240 
241 	for (i = 0; i < entry->text_len && xpos < pos; i++) {
242 		unichar c = entry->text[i];
243 		const char *extent = entry->uses_extents ? entry->extents[i+1] : NULL;
244 
245 		if (term_type == TERM_TYPE_BIG5)
246 			width = big5_width(c);
247 		else if (entry->utf8)
248 			width = unichar_isprint(c) ? i_wcwidth(c) : 1;
249 		else
250 			width = 1;
251 
252 		xpos += width;
253 
254 		if (extent != NULL) {
255 			xpos += scrlen_str(extent, entry->utf8);
256 		}
257 	}
258 	return i;
259 }
260 
261 /* Fixes the cursor position in screen */
gui_entry_fix_cursor(GUI_ENTRY_REC * entry)262 static void gui_entry_fix_cursor(GUI_ENTRY_REC *entry)
263 {
264 	int old_scrstart;
265 
266 	/* assume prompt len == prompt scrlen */
267 	int start = pos2scrpos(entry, entry->scrstart, FALSE);
268 	int now = pos2scrpos(entry, entry->pos, TRUE);
269 
270 	old_scrstart = entry->scrstart;
271 	if (now-start < entry->width - 2 - entry->promptlen && now-start > 0) {
272 		entry->scrpos = now-start;
273 	} else if (now < entry->width - 1 - entry->promptlen) {
274 		entry->scrstart = 0;
275 		entry->scrpos = now;
276 	} else {
277 		entry->scrstart = scrpos2pos(entry, now-(entry->width -
278 							 entry->promptlen)*2/3);
279 		start = pos2scrpos(entry, entry->scrstart, FALSE);
280 		entry->scrpos = now - start;
281 	}
282 
283 	if (old_scrstart != entry->scrstart)
284                 entry->redraw_needed_from = 0;
285 }
286 
text_effects_only(const char * p)287 static char *text_effects_only(const char *p)
288 {
289 	GString *str;
290 
291 	str = g_string_sized_new(strlen(p));
292 	for (; *p != '\0'; p++) {
293 		if (*p == 4 && p[1] != '\0') {
294 			if (p[1] >= FORMAT_STYLE_SPECIAL) {
295 				g_string_append_len(str, p, 2);
296 				p++;
297 				continue;
298 			}
299 
300 			/* irssi color */
301 			if (p[2] != '\0') {
302 #ifdef TERM_TRUECOLOR
303 				if (p[1] == FORMAT_COLOR_24) {
304 					if (p[3] == '\0') p += 2;
305 					else if (p[4] == '\0') p += 3;
306 					else if (p[5] == '\0') p += 4;
307 					else {
308 						g_string_append_len(str, p, 6);
309 						p += 5;
310 					}
311 				} else {
312 #endif /* TERM_TRUECOLOR */
313 					g_string_append_len(str, p, 3);
314 					p += 2;
315 #ifdef TERM_TRUECOLOR
316 				}
317 #endif /* TERM_TRUECOLOR */
318 				continue;
319 			}
320 		}
321 	}
322 
323 	return g_string_free(str, FALSE);
324 }
325 
gui_entry_draw_from(GUI_ENTRY_REC * entry,int pos)326 static void gui_entry_draw_from(GUI_ENTRY_REC *entry, int pos)
327 {
328 	int i, start;
329 	int start_xpos, xpos, new_xpos, end_xpos;
330 	char *tmp;
331 	GString *str;
332 
333 	start = entry->scrstart + pos;
334 
335 	start_xpos = xpos = entry->xpos + entry->promptlen +
336 		pos2scrpos(entry, start, FALSE) -
337 		pos2scrpos(entry, entry->scrstart, FALSE);
338         end_xpos = entry->xpos + entry->width;
339 
340 	if (xpos > end_xpos)
341                 return;
342 
343 	str = g_string_sized_new(entry->text_alloc);
344 
345 	term_set_color(root_window, ATTR_RESET);
346 	/* term_move(root_window, xpos, entry->ypos); */
347 
348 	if (entry->uses_extents && entry->extents[0] != NULL) {
349 		g_string_append(str, entry->extents[0]);
350 	}
351 	for (i = 0; i < start && i < entry->text_len; i++) {
352 		const char *extent = entry->uses_extents ? entry->extents[i+1] : NULL;
353 		if (extent != NULL) {
354 			g_string_append(str, extent);
355 		}
356 	}
357 	if (i == 0) {
358 		xpos += scrlen_str(str->str, entry->utf8);
359 	} else {
360 		tmp = text_effects_only(str->str);
361 		g_string_assign(str, tmp);
362 		g_free(tmp);
363 	}
364 
365 	for (; i < entry->text_len; i++) {
366 		unichar c = entry->text[i];
367 		const char *extent = entry->uses_extents ? entry->extents[i+1] : NULL;
368 		new_xpos = xpos;
369 
370 		if (entry->hidden)
371 			new_xpos++;
372 		else if (term_type == TERM_TYPE_BIG5)
373 			new_xpos += big5_width(c);
374 		else if (entry->utf8)
375 			new_xpos += unichar_isprint(c) ? i_wcwidth(c) : 1;
376 		else
377 			new_xpos++;
378 
379 		if (new_xpos > end_xpos)
380 			break;
381 
382 		if (entry->hidden) {
383                         g_string_append_c(str, ' ');
384 		} else if (unichar_isprint(c)) {
385 			if (entry->utf8) {
386 				g_string_append_unichar(str, c);
387 			} else if (term_type == TERM_TYPE_BIG5) {
388 				if(c > 0xff)
389 					g_string_append_c(str, (c >> 8) & 0xff);
390 				g_string_append_c(str, c & 0xff);
391 			} else {
392 				g_string_append_c(str, c);
393 			}
394 		} else {
395 			g_string_append_c(str, 4);
396 			g_string_append_c(str, FORMAT_STYLE_REVERSE);
397 			g_string_append_c(str, (c & 127)+'A'-1);
398 			g_string_append_c(str, 4);
399 			g_string_append_c(str, FORMAT_STYLE_REVERSE);
400 		}
401 		xpos = new_xpos;
402 
403 		if (extent != NULL) {
404 			new_xpos += scrlen_str(extent, entry->utf8);
405 
406 			if (new_xpos > end_xpos)
407 				break;
408 
409 			g_string_append(str, extent);
410 			xpos = new_xpos;
411 		}
412 	}
413 
414         /* clear the rest of the input line */
415 	if (xpos < end_xpos) {
416 		if (end_xpos == term_width) {
417 			g_string_append_c(str, 4);
418 			g_string_append_c(str, FORMAT_STYLE_CLRTOEOL);
419 		} else {
420 			while (xpos < end_xpos) {
421 				g_string_append_c(str, ' ');
422 				xpos++;
423 			}
424 		}
425 	}
426 
427 	gui_printtext_internal(start_xpos, entry->ypos, str->str);
428 	g_string_free(str, TRUE);
429 }
430 
gui_entry_draw(GUI_ENTRY_REC * entry)431 static void gui_entry_draw(GUI_ENTRY_REC *entry)
432 {
433 	if (entry->redraw_needed_from >= 0) {
434 		gui_entry_draw_from(entry, entry->redraw_needed_from);
435                 entry->redraw_needed_from = -1;
436 	}
437 
438 	term_move_cursor(entry->xpos + entry->scrpos + entry->promptlen,
439 			 entry->ypos);
440 	term_refresh(NULL);
441 }
442 
gui_entry_redraw_from(GUI_ENTRY_REC * entry,int pos)443 static void gui_entry_redraw_from(GUI_ENTRY_REC *entry, int pos)
444 {
445 	pos -= entry->scrstart;
446 	if (pos < 0) pos = 0;
447 
448 	if (entry->redraw_needed_from == -1 ||
449 	    entry->redraw_needed_from > pos)
450 		entry->redraw_needed_from = pos;
451 }
452 
gui_entry_move(GUI_ENTRY_REC * entry,int xpos,int ypos,int width)453 void gui_entry_move(GUI_ENTRY_REC *entry, int xpos, int ypos, int width)
454 {
455 	int old_width;
456 
457         g_return_if_fail(entry != NULL);
458 
459 	if (entry->xpos != xpos || entry->ypos != ypos) {
460                 /* position in screen changed - needs a full redraw */
461 		entry->xpos = xpos;
462 		entry->ypos = ypos;
463 		entry->width = width;
464 		gui_entry_redraw(entry);
465                 return;
466 	}
467 
468 	if (entry->width == width)
469                 return; /* no changes */
470 
471 	if (width > entry->width) {
472                 /* input line grew - need to draw text at the end */
473                 old_width = width;
474 		entry->width = width;
475 		gui_entry_redraw_from(entry, old_width);
476 	} else {
477 		/* input line shrinked - make sure the cursor
478 		   is inside the input line */
479 		entry->width = width;
480 		if (entry->pos - entry->scrstart >
481 		    entry->width-2 - entry->promptlen) {
482 			gui_entry_fix_cursor(entry);
483 		}
484 	}
485 
486 	gui_entry_draw(entry);
487 }
488 
gui_entry_set_active(GUI_ENTRY_REC * entry)489 void gui_entry_set_active(GUI_ENTRY_REC *entry)
490 {
491 	active_entry = entry;
492 
493 	if (entry != NULL) {
494 		term_move_cursor(entry->xpos + entry->scrpos +
495 				 entry->promptlen, entry->ypos);
496 		term_refresh(NULL);
497 	}
498 }
499 
gui_entry_set_prompt(GUI_ENTRY_REC * entry,const char * str)500 void gui_entry_set_prompt(GUI_ENTRY_REC *entry, const char *str)
501 {
502 	int oldlen;
503 
504         g_return_if_fail(entry != NULL);
505 
506         oldlen = entry->promptlen;
507 	if (str != NULL) {
508 		g_free_not_null(entry->prompt);
509 		entry->prompt = g_strdup(str);
510 		entry->promptlen = scrlen_str(str, entry->utf8);
511 	}
512 
513         if (entry->prompt != NULL)
514 		gui_printtext_internal(entry->xpos, entry->ypos, entry->prompt);
515 
516 	if (entry->promptlen != oldlen) {
517 		gui_entry_fix_cursor(entry);
518 		gui_entry_draw(entry);
519 	}
520 }
521 
gui_entry_set_hidden(GUI_ENTRY_REC * entry,int hidden)522 void gui_entry_set_hidden(GUI_ENTRY_REC *entry, int hidden)
523 {
524         g_return_if_fail(entry != NULL);
525 
526         entry->hidden = hidden;
527 }
528 
gui_entry_set_utf8(GUI_ENTRY_REC * entry,int utf8)529 void gui_entry_set_utf8(GUI_ENTRY_REC *entry, int utf8)
530 {
531         g_return_if_fail(entry != NULL);
532 
533         entry->utf8 = utf8;
534 }
535 
gui_entry_set_text(GUI_ENTRY_REC * entry,const char * str)536 void gui_entry_set_text(GUI_ENTRY_REC *entry, const char *str)
537 {
538 	g_return_if_fail(entry != NULL);
539 	g_return_if_fail(str != NULL);
540 
541 	entry->text_len = 0;
542 	entry->pos = 0;
543 	entry->text[0] = '\0';
544 	destroy_extents(entry);
545 
546 	gui_entry_insert_text(entry, str);
547 }
548 
gui_entry_get_text(GUI_ENTRY_REC * entry)549 char *gui_entry_get_text(GUI_ENTRY_REC *entry)
550 {
551 	char *buf;
552         int i;
553 
554 	g_return_val_if_fail(entry != NULL, NULL);
555 
556 	if (entry->utf8)
557 		buf = g_ucs4_to_utf8(entry->text, -1, NULL, NULL, NULL);
558 	else {
559 		buf = g_malloc(entry->text_len*6 + 1);
560 		if (term_type == TERM_TYPE_BIG5)
561 			unichars_to_big5(entry->text, buf);
562 		else
563 			for (i = 0; i <= entry->text_len; i++)
564 				buf[i] = entry->text[i];
565 	}
566 	return buf;
567 }
568 
gui_entry_get_text_and_pos(GUI_ENTRY_REC * entry,int * pos)569 char *gui_entry_get_text_and_pos(GUI_ENTRY_REC *entry, int *pos)
570 {
571 	char *buf;
572         int i;
573 
574 	g_return_val_if_fail(entry != NULL, NULL);
575 
576 	if (entry->utf8) {
577 		buf = g_ucs4_to_utf8(entry->text, -1, NULL, NULL, NULL);
578 		*pos = g_utf8_offset_to_pointer(buf, entry->pos) - buf;
579 	} else {
580 		buf = g_malloc(entry->text_len*6 + 1);
581 		if(term_type==TERM_TYPE_BIG5)
582 			unichars_to_big5_with_pos(entry->text, entry->pos, buf, pos);
583 		else
584 		{
585 			for (i = 0; i <= entry->text_len; i++)
586 				buf[i] = entry->text[i];
587 			*pos = entry->pos;
588 		}
589 	}
590 	return buf;
591 }
592 
gui_entry_insert_text(GUI_ENTRY_REC * entry,const char * str)593 void gui_entry_insert_text(GUI_ENTRY_REC *entry, const char *str)
594 {
595         unichar chr;
596 	int i, len;
597 	const char *ptr;
598 
599         g_return_if_fail(entry != NULL);
600 	g_return_if_fail(str != NULL);
601 
602         gui_entry_redraw_from(entry, entry->pos);
603 
604 	if (entry->utf8) {
605 		g_utf8_validate(str, -1, &ptr);
606 		len = g_utf8_pointer_to_offset(str, ptr);
607 	} else if (term_type == TERM_TYPE_BIG5)
608 		len = strlen_big5((const unsigned char *)str);
609 	else
610 		len = strlen(str);
611         entry_text_grow(entry, len);
612 
613         /* make space for the string */
614 	g_memmove(entry->text + entry->pos + len, entry->text + entry->pos,
615 		  (entry->text_len-entry->pos + 1) * sizeof(unichar));
616 
617 	/* make space for the color */
618 	if (entry->uses_extents) {
619 		g_memmove(entry->extents + entry->pos + len + 1, entry->extents + entry->pos + 1,
620 			  (entry->text_len-entry->pos) * sizeof(char *));
621 		for (i = 0; i < len; i++) {
622 			entry->extents[entry->pos + i + 1] = NULL;
623 		}
624 	}
625 
626 	if (!entry->utf8) {
627 		if (term_type == TERM_TYPE_BIG5) {
628 			chr = entry->text[entry->pos + len];
629 			big5_to_unichars(str, entry->text + entry->pos);
630 			entry->text[entry->pos + len] = chr;
631 		} else {
632 			for (i = 0; i < len; i++)
633 				entry->text[entry->pos + i] = str[i];
634 		}
635 	} else {
636 		ptr = str;
637 		for (i = 0; i < len; i++) {
638 			entry->text[entry->pos + i] = g_utf8_get_char(ptr);
639 			ptr = g_utf8_next_char(ptr);
640 		}
641 	}
642 
643 	entry->text_len += len;
644         entry->pos += len;
645 
646 	gui_entry_fix_cursor(entry);
647 	gui_entry_draw(entry);
648 }
649 
gui_entry_insert_char(GUI_ENTRY_REC * entry,unichar chr)650 void gui_entry_insert_char(GUI_ENTRY_REC *entry, unichar chr)
651 {
652 	g_return_if_fail(entry != NULL);
653 
654 	if (chr == 0 || chr == 13 || chr == 10)
655 		return; /* never insert NUL, CR or LF characters */
656 
657 	if (entry->utf8 && entry->pos == 0 && unichar_isprint(chr) && i_wcwidth(chr) == 0)
658 		return;
659 
660 	gui_entry_redraw_from(entry, entry->pos);
661 
662 	entry_text_grow(entry, 1);
663 
664 	/* make space for the string */
665 	g_memmove(entry->text + entry->pos + 1, entry->text + entry->pos,
666 		  (entry->text_len-entry->pos + 1) * sizeof(unichar));
667 
668 	if (entry->uses_extents) {
669 		g_memmove(entry->extents + entry->pos + 1 + 1, entry->extents + entry->pos + 1,
670 			  (entry->text_len-entry->pos) * sizeof(char *));
671 		entry->extents[entry->pos + 1] = NULL;
672 	}
673 
674 	entry->text[entry->pos] = chr;
675 	entry->text_len++;
676 	entry->pos++;
677 
678 	gui_entry_fix_cursor(entry);
679 	gui_entry_draw(entry);
680 }
681 
gui_entry_get_cutbuffer(GUI_ENTRY_REC * entry)682 char *gui_entry_get_cutbuffer(GUI_ENTRY_REC *entry)
683 {
684 	GUI_ENTRY_CUTBUFFER_REC *tmp;
685 	char *buf;
686         int i;
687 
688 	g_return_val_if_fail(entry != NULL, NULL);
689 
690 	if (entry->kill_ring == NULL || entry->kill_ring->data == NULL)
691 		return NULL;
692 
693 	tmp = entry->kill_ring->data;
694 
695 	if (tmp->cutbuffer == NULL)
696                 return NULL;
697 
698 	if (entry->utf8)
699 		buf = g_ucs4_to_utf8(tmp->cutbuffer, -1, NULL, NULL, NULL);
700 	else {
701 		buf = g_malloc(tmp->cutbuffer_len*6 + 1);
702 		if (term_type == TERM_TYPE_BIG5)
703 			unichars_to_big5(tmp->cutbuffer, buf);
704 		else
705 			for (i = 0; i <= tmp->cutbuffer_len; i++)
706 				buf[i] = tmp->cutbuffer[i];
707 	}
708 	return buf;
709 }
710 
gui_entry_get_next_cutbuffer(GUI_ENTRY_REC * entry)711 char *gui_entry_get_next_cutbuffer(GUI_ENTRY_REC *entry)
712 {
713 	GUI_ENTRY_CUTBUFFER_REC *tmp;
714 
715 	g_return_val_if_fail(entry != NULL, NULL);
716 
717 	if (entry->kill_ring == NULL)
718 		return NULL;
719 
720 	tmp = entry->kill_ring->data;
721 
722 	entry->kill_ring = g_slist_remove(entry->kill_ring, tmp);
723 	entry->kill_ring = g_slist_append(entry->kill_ring, tmp);
724 
725 	return gui_entry_get_cutbuffer(entry);
726 }
727 
gui_entry_erase_to(GUI_ENTRY_REC * entry,int pos,CUTBUFFER_UPDATE_OP update_cutbuffer)728 void gui_entry_erase_to(GUI_ENTRY_REC *entry, int pos, CUTBUFFER_UPDATE_OP update_cutbuffer)
729 {
730 	int newpos, size = 0;
731 
732 	g_return_if_fail(entry != NULL);
733 
734 	for (newpos = gui_entry_get_pos(entry); newpos > pos; size++)
735 		newpos = newpos - 1;
736 	gui_entry_erase(entry, size, update_cutbuffer);
737 }
738 
get_cutbuffer_rec(GUI_ENTRY_REC * entry,CUTBUFFER_UPDATE_OP update_cutbuffer)739 static GUI_ENTRY_CUTBUFFER_REC *get_cutbuffer_rec(GUI_ENTRY_REC *entry, CUTBUFFER_UPDATE_OP update_cutbuffer)
740 {
741 	GUI_ENTRY_CUTBUFFER_REC *tmp;
742 
743 	g_return_val_if_fail(entry != NULL, NULL);
744 
745 	if (entry->kill_ring == NULL) {
746 		/* no kill ring exists */
747 		entry->kill_ring = g_slist_prepend(entry->kill_ring, (void *)NULL);
748 	} else {
749 		tmp = entry->kill_ring->data;
750 
751 		if (tmp != NULL && tmp->cutbuffer_len > 0
752 		    && (!entry->previous_append_next_kill
753 			|| update_cutbuffer == CUTBUFFER_UPDATE_REPLACE)) {
754 			/* a cutbuffer exists and should be replaced */
755 			entry->kill_ring = g_slist_prepend(entry->kill_ring, (void *)NULL);
756 		}
757 	}
758 
759 	if (g_slist_length(entry->kill_ring) > KILL_RING_MAX) {
760 		GUI_ENTRY_CUTBUFFER_REC *rec = g_slist_last(entry->kill_ring)->data;
761 		entry->kill_ring = g_slist_remove(entry->kill_ring, rec);
762 		if (rec != NULL) g_free(rec->cutbuffer);
763 		g_free(rec);
764 	}
765 
766 	if (entry->kill_ring->data == NULL) {
767 		entry->kill_ring->data = g_new0(GUI_ENTRY_CUTBUFFER_REC, 1);
768 	}
769 
770 	return entry->kill_ring->data;
771 }
772 
gui_entry_erase(GUI_ENTRY_REC * entry,int size,CUTBUFFER_UPDATE_OP update_cutbuffer)773 void gui_entry_erase(GUI_ENTRY_REC *entry, int size, CUTBUFFER_UPDATE_OP update_cutbuffer)
774 {
775 	size_t i, w = 0;
776 
777         g_return_if_fail(entry != NULL);
778 
779 	if (size == 0 || entry->pos < size)
780 		return;
781 
782 	if (update_cutbuffer != CUTBUFFER_UPDATE_NOOP) {
783 		int cutbuffer_new_size;
784 		unichar *tmpcutbuffer;
785 		GUI_ENTRY_CUTBUFFER_REC *tmp = get_cutbuffer_rec(entry, update_cutbuffer);
786 
787 		if (tmp->cutbuffer_len == 0) {
788 			update_cutbuffer = CUTBUFFER_UPDATE_REPLACE;
789 		}
790 
791 		cutbuffer_new_size = tmp->cutbuffer_len + size;
792 		tmpcutbuffer = tmp->cutbuffer;
793 		entry->append_next_kill = TRUE;
794 		switch (update_cutbuffer) {
795 			case CUTBUFFER_UPDATE_APPEND:
796 				tmp->cutbuffer = g_new(unichar, cutbuffer_new_size+1);
797 				memcpy(tmp->cutbuffer, tmpcutbuffer,
798 				       tmp->cutbuffer_len * sizeof(unichar));
799 				memcpy(tmp->cutbuffer + tmp->cutbuffer_len,
800 				       entry->text + entry->pos - size, size * sizeof(unichar));
801 
802 				tmp->cutbuffer_len = cutbuffer_new_size;
803 				tmp->cutbuffer[cutbuffer_new_size] = '\0';
804 				g_free(tmpcutbuffer);
805 				break;
806 
807 			case CUTBUFFER_UPDATE_PREPEND:
808 				tmp->cutbuffer = g_new(unichar, cutbuffer_new_size+1);
809 				memcpy(tmp->cutbuffer, entry->text + entry->pos - size,
810 				       size * sizeof(unichar));
811 				memcpy(tmp->cutbuffer + size, tmpcutbuffer,
812 				       tmp->cutbuffer_len * sizeof(unichar));
813 
814 				tmp->cutbuffer_len = cutbuffer_new_size;
815 				tmp->cutbuffer[cutbuffer_new_size] = '\0';
816 				g_free(tmpcutbuffer);
817 				break;
818 
819 			case CUTBUFFER_UPDATE_REPLACE:
820 				/* put erased text to cutbuffer */
821 				if (tmp->cutbuffer_len < size) {
822 					g_free(tmp->cutbuffer);
823 					tmp->cutbuffer = g_new(unichar, size+1);
824 				}
825 
826 				tmp->cutbuffer_len = size;
827 				tmp->cutbuffer[size] = '\0';
828 				memcpy(tmp->cutbuffer, entry->text + entry->pos - size, size * sizeof(unichar));
829 				break;
830 
831 			case CUTBUFFER_UPDATE_NOOP:
832 				/* cannot happen, handled in "if" */
833 				break;
834 		}
835 	}
836 
837 	if (entry->utf8)
838 		while (entry->pos > size + w && i_wcwidth(entry->text[entry->pos - size - w]) == 0)
839 			w++;
840 
841 	g_memmove(entry->text + entry->pos - size, entry->text + entry->pos,
842 		  (entry->text_len-entry->pos+1) * sizeof(unichar));
843 
844 	if (entry->uses_extents) {
845 		for (i = entry->pos - size; i < entry->pos; i++) {
846 			if (entry->extents[i+1] != NULL) {
847 				g_free(entry->extents[i+1]);
848 			}
849 		}
850 		g_memmove(entry->extents + entry->pos - size + 1, entry->extents + entry->pos + 1,
851 			  (entry->text_len - entry->pos) * sizeof(void *)); /* no null terminator here */
852 		for (i = 0; i < size; i++) {
853 			entry->extents[entry->text_len - i] = NULL;
854 		}
855 		if (entry->text_len == size && entry->extents[0] != NULL) {
856 			g_free(entry->extents[0]);
857 			entry->extents[0] = NULL;
858 		}
859 	}
860 
861 	entry->pos -= size;
862         entry->text_len -= size;
863 
864 	gui_entry_redraw_from(entry, entry->pos-w);
865 	gui_entry_fix_cursor(entry);
866 	gui_entry_draw(entry);
867 }
868 
gui_entry_erase_cell(GUI_ENTRY_REC * entry)869 void gui_entry_erase_cell(GUI_ENTRY_REC *entry)
870 {
871 	int size = 1;
872 
873 	g_return_if_fail(entry != NULL);
874 
875 	if (entry->utf8)
876 		while (entry->pos+size < entry->text_len &&
877 		       i_wcwidth(entry->text[entry->pos+size]) == 0) size++;
878 
879 	g_memmove(entry->text + entry->pos, entry->text + entry->pos + size,
880 	          (entry->text_len-entry->pos-size+1) * sizeof(unichar));
881 
882 	if (entry->uses_extents) {
883 		int i;
884 		for (i = 0; i < size; i++) {
885 			g_free(entry->extents[entry->pos + i + 1]);
886 		}
887 		g_memmove(entry->extents + entry->pos + 1, entry->extents + entry->pos + size + 1,
888 			  (entry->text_len-entry->pos-size) * sizeof(char *));
889 		for (i = 0; i < size; i++) {
890 			entry->extents[entry->text_len - i] = NULL;
891 		}
892 		if (entry->text_len == size && entry->extents[0] != NULL) {
893 			g_free(entry->extents[0]);
894 			entry->extents[0] = NULL;
895 		}
896 	}
897 
898 	entry->text_len -= size;
899 
900 	gui_entry_redraw_from(entry, entry->pos);
901 	gui_entry_fix_cursor(entry);
902 	gui_entry_draw(entry);
903 }
904 
gui_entry_erase_word(GUI_ENTRY_REC * entry,int to_space,CUTBUFFER_UPDATE_OP cutbuffer_op)905 void gui_entry_erase_word(GUI_ENTRY_REC *entry, int to_space, CUTBUFFER_UPDATE_OP cutbuffer_op)
906 {
907 	int to;
908 
909         g_return_if_fail(entry != NULL);
910 	if (entry->pos == 0)
911 		return;
912 
913 	to = entry->pos - 1;
914 
915 	if (to_space) {
916 		while (entry->text[to] == ' ' && to > 0)
917 			to--;
918 		while (entry->text[to] != ' ' && to > 0)
919 			to--;
920 	} else {
921 		while (!i_isalnum(entry->text[to]) && to > 0)
922 			to--;
923 		while (i_isalnum(entry->text[to]) && to > 0)
924 			to--;
925 	}
926 	if (to > 0) to++;
927 
928 	gui_entry_erase(entry, entry->pos-to, cutbuffer_op);
929 }
930 
gui_entry_erase_next_word(GUI_ENTRY_REC * entry,int to_space,CUTBUFFER_UPDATE_OP cutbuffer_op)931 void gui_entry_erase_next_word(GUI_ENTRY_REC *entry, int to_space, CUTBUFFER_UPDATE_OP cutbuffer_op)
932 {
933 	int to, size;
934 
935         g_return_if_fail(entry != NULL);
936 	if (entry->pos == entry->text_len)
937 		return;
938 
939         to = entry->pos;
940 	if (to_space) {
941 		while (entry->text[to] == ' ' && to < entry->text_len)
942 			to++;
943 		while (entry->text[to] != ' ' && to < entry->text_len)
944 			to++;
945 	} else {
946 		while (!i_isalnum(entry->text[to]) && to < entry->text_len)
947 			to++;
948 		while (i_isalnum(entry->text[to]) && to < entry->text_len)
949 			to++;
950 	}
951 
952         size = to-entry->pos;
953 	entry->pos = to;
954         gui_entry_erase(entry, size, cutbuffer_op);
955 }
956 
gui_entry_transpose_chars(GUI_ENTRY_REC * entry)957 void gui_entry_transpose_chars(GUI_ENTRY_REC *entry)
958 {
959         unichar chr;
960 	char *extent;
961 
962 	if (entry->pos == 0 || entry->text_len < 2)
963                 return;
964 
965 	if (entry->pos == entry->text_len)
966                 entry->pos--;
967 
968         /* swap chars */
969 	chr = entry->text[entry->pos];
970 	entry->text[entry->pos] = entry->text[entry->pos-1];
971         entry->text[entry->pos-1] = chr;
972 
973 	if (entry->uses_extents) {
974 		extent = entry->extents[entry->pos+1];
975 		entry->extents[entry->pos+1] = entry->extents[entry->pos];
976 		entry->extents[entry->pos] = extent;
977 	}
978 
979         entry->pos++;
980 
981 	gui_entry_redraw_from(entry, entry->pos-2);
982 	gui_entry_fix_cursor(entry);
983 	gui_entry_draw(entry);
984 }
985 
gui_entry_transpose_words(GUI_ENTRY_REC * entry)986 void gui_entry_transpose_words(GUI_ENTRY_REC *entry)
987 {
988 	int spos1, epos1, spos2, epos2;
989 
990 	/* find last position */
991 	epos2 = entry->pos;
992 	while (epos2 < entry->text_len && !i_isalnum(entry->text[epos2]))
993 		epos2++;
994 	while (epos2 < entry->text_len &&  i_isalnum(entry->text[epos2]))
995 		epos2++;
996 
997 	/* find other position */
998 	spos2 = epos2;
999 	while (spos2 > 0 && !i_isalnum(entry->text[spos2-1]))
1000 		spos2--;
1001 	while (spos2 > 0 &&  i_isalnum(entry->text[spos2-1]))
1002 		spos2--;
1003 
1004 	epos1 = spos2;
1005 	while (epos1 > 0 && !i_isalnum(entry->text[epos1-1]))
1006 		epos1--;
1007 
1008 	spos1 = epos1;
1009 	while (spos1 > 0 && i_isalnum(entry->text[spos1-1]))
1010 		spos1--;
1011 
1012 	/* do wordswap if any found */
1013 	if (spos1 < epos1 && epos1 < spos2 && spos2 < epos2) {
1014 		unichar *first, *sep, *second;
1015 		char **first_extent, **sep_extent, **second_extent;
1016 		int i;
1017 
1018 		first  = (unichar *) g_malloc( (epos1 - spos1) * sizeof(unichar) );
1019 		sep    = (unichar *) g_malloc( (spos2 - epos1) * sizeof(unichar) );
1020 		second = (unichar *) g_malloc( (epos2 - spos2) * sizeof(unichar) );
1021 
1022 		first_extent  = (char **) g_malloc( (epos1 - spos1) * sizeof(char *) );
1023 		sep_extent    = (char **) g_malloc( (spos2 - epos1) * sizeof(char *) );
1024 		second_extent = (char **) g_malloc( (epos2 - spos2) * sizeof(char *) );
1025 
1026 		for (i = spos1; i < epos1; i++) {
1027 			first[i-spos1] = entry->text[i];
1028 			if (entry->uses_extents)
1029 				first_extent[i-spos1] = entry->extents[i+1];
1030 		}
1031 		for (i = epos1; i < spos2; i++) {
1032 			sep[i-epos1] = entry->text[i];
1033 			if (entry->uses_extents)
1034 				sep_extent[i-epos1] = entry->extents[i+1];
1035 		}
1036 		for (i = spos2; i < epos2; i++) {
1037 			second[i-spos2] = entry->text[i];
1038 			if (entry->uses_extents)
1039 				second_extent[i-spos2] = entry->extents[i+1];
1040 		}
1041 
1042 		entry->pos = spos1;
1043 		for (i = 0; i < epos2-spos2; i++) {
1044 			entry->text[entry->pos] = second[i];
1045 			if (entry->uses_extents)
1046 				entry->extents[entry->pos+1] = second_extent[i];
1047 			entry->pos++;
1048 		}
1049 		for (i = 0; i < spos2-epos1; i++) {
1050 			entry->text[entry->pos] = sep[i];
1051 			if (entry->uses_extents)
1052 				entry->extents[entry->pos+1] = sep_extent[i];
1053 			entry->pos++;
1054 		}
1055 		for (i = 0; i < epos1-spos1; i++) {
1056 			entry->text[entry->pos] = first[i];
1057 			if (entry->uses_extents)
1058 				entry->extents[entry->pos+1] = first_extent[i];
1059 			entry->pos++;
1060 		}
1061 
1062 		g_free(first);
1063 		g_free(sep);
1064 		g_free(second);
1065 
1066 		g_free(first_extent);
1067 		g_free(sep_extent);
1068 		g_free(second_extent);
1069 	}
1070 
1071 	gui_entry_redraw_from(entry, spos1);
1072 	gui_entry_fix_cursor(entry);
1073 	gui_entry_draw(entry);
1074 }
1075 
gui_entry_capitalize_word(GUI_ENTRY_REC * entry)1076 void gui_entry_capitalize_word(GUI_ENTRY_REC *entry)
1077 {
1078 	int pos = entry->pos;
1079 	while (pos < entry->text_len && !i_isalnum(entry->text[pos]))
1080 		pos++;
1081 
1082 	if (pos < entry->text_len) {
1083 		entry->text[pos] = i_toupper(entry->text[pos]);
1084 		pos++;
1085 	}
1086 
1087 	while (pos < entry->text_len && i_isalnum(entry->text[pos])) {
1088 		entry->text[pos] = i_tolower(entry->text[pos]);
1089 		pos++;
1090 	}
1091 
1092 	gui_entry_redraw_from(entry, entry->pos);
1093 	entry->pos = pos;
1094 	gui_entry_fix_cursor(entry);
1095 	gui_entry_draw(entry);
1096 }
1097 
gui_entry_downcase_word(GUI_ENTRY_REC * entry)1098 void gui_entry_downcase_word(GUI_ENTRY_REC *entry)
1099 {
1100 	int pos = entry->pos;
1101 	while (pos < entry->text_len && !i_isalnum(entry->text[pos]))
1102 		pos++;
1103 
1104 	while (pos < entry->text_len && i_isalnum(entry->text[pos])) {
1105 		entry->text[pos] = i_tolower(entry->text[pos]);
1106 		pos++;
1107 	}
1108 
1109 	gui_entry_redraw_from(entry, entry->pos);
1110 	entry->pos = pos;
1111 	gui_entry_fix_cursor(entry);
1112 	gui_entry_draw(entry);
1113 }
1114 
gui_entry_upcase_word(GUI_ENTRY_REC * entry)1115 void gui_entry_upcase_word(GUI_ENTRY_REC *entry)
1116 {
1117 	int pos = entry->pos;
1118 	while (pos < entry->text_len && !i_isalnum(entry->text[pos]))
1119 		pos++;
1120 
1121 	while (pos < entry->text_len && i_isalnum(entry->text[pos])) {
1122 		entry->text[pos] = i_toupper(entry->text[pos]);
1123 		pos++;
1124 	}
1125 
1126 	gui_entry_redraw_from(entry, entry->pos);
1127 	entry->pos = pos;
1128 	gui_entry_fix_cursor(entry);
1129 	gui_entry_draw(entry);
1130 }
1131 
gui_entry_get_pos(GUI_ENTRY_REC * entry)1132 int gui_entry_get_pos(GUI_ENTRY_REC *entry)
1133 {
1134         g_return_val_if_fail(entry != NULL, 0);
1135 
1136 	return entry->pos;
1137 }
1138 
gui_entry_set_pos(GUI_ENTRY_REC * entry,int pos)1139 void gui_entry_set_pos(GUI_ENTRY_REC *entry, int pos)
1140 {
1141         g_return_if_fail(entry != NULL);
1142 
1143 	if (pos >= 0 && pos <= entry->text_len)
1144 		entry->pos = pos;
1145 
1146 	gui_entry_fix_cursor(entry);
1147 	gui_entry_draw(entry);
1148 }
1149 
gui_entry_set_text_and_pos_bytes(GUI_ENTRY_REC * entry,const char * str,int pos_bytes)1150 void gui_entry_set_text_and_pos_bytes(GUI_ENTRY_REC *entry, const char *str, int pos_bytes)
1151 {
1152 	int pos, extents_alloc;
1153 	char **extents;
1154 	const char *ptr;
1155 
1156 	g_return_if_fail(entry != NULL);
1157 
1158 	extents = entry->extents;
1159 	extents_alloc = entry->text_alloc;
1160 	entry->extents = NULL;
1161 	entry->uses_extents = FALSE;
1162 
1163 	gui_entry_set_text(entry, str);
1164 
1165 	if (entry->utf8) {
1166 		g_utf8_validate(str, pos_bytes, &ptr);
1167 		pos = g_utf8_pointer_to_offset(str, ptr);
1168 	} else if (term_type == TERM_TYPE_BIG5)
1169 		pos = strlen_big5((const unsigned char *)str) - strlen_big5((const unsigned char *)(str + pos_bytes));
1170 	else
1171 		pos = pos_bytes;
1172 
1173 	if (extents != NULL) {
1174 		entry->uses_extents = TRUE;
1175 		entry->extents = extents;
1176 		if (extents_alloc < entry->text_alloc) {
1177 			int i;
1178 			entry->extents = g_realloc(entry->extents,
1179 				   sizeof(char *) * entry->text_alloc);
1180 			for (i = extents_alloc; i < entry->text_alloc; i++) {
1181 				entry->extents[i] = NULL;
1182 			}
1183 		}
1184 	}
1185 	gui_entry_redraw_from(entry, 0);
1186 	gui_entry_set_pos(entry, pos);
1187 }
1188 
gui_entry_move_pos(GUI_ENTRY_REC * entry,int pos)1189 void gui_entry_move_pos(GUI_ENTRY_REC *entry, int pos)
1190 {
1191         g_return_if_fail(entry != NULL);
1192 
1193 	if (entry->pos + pos >= 0 && entry->pos + pos <= entry->text_len)
1194 		entry->pos += pos;
1195 
1196 	if (entry->utf8) {
1197 		int step = pos < 0 ? -1 : 1;
1198 		while(i_wcwidth(entry->text[entry->pos]) == 0 &&
1199 		      entry->pos + step >= 0 && entry->pos + step <= entry->text_len)
1200 			entry->pos += step;
1201 	}
1202 
1203 	gui_entry_fix_cursor(entry);
1204 	gui_entry_draw(entry);
1205 }
1206 
gui_entry_move_words_left(GUI_ENTRY_REC * entry,int count,int to_space)1207 static void gui_entry_move_words_left(GUI_ENTRY_REC *entry, int count, int to_space)
1208 {
1209 	int pos;
1210 
1211 	pos = entry->pos;
1212 	while (count > 0 && pos > 0) {
1213 		if (to_space) {
1214 			while (pos > 0 && entry->text[pos-1] == ' ')
1215 				pos--;
1216 			while (pos > 0 && entry->text[pos-1] != ' ')
1217 				pos--;
1218 		} else {
1219 			while (pos > 0 && !i_isalnum(entry->text[pos-1]))
1220 				pos--;
1221 			while (pos > 0 &&  i_isalnum(entry->text[pos-1]))
1222 				pos--;
1223 		}
1224 		count--;
1225 	}
1226 
1227         entry->pos = pos;
1228 }
1229 
gui_entry_move_words_right(GUI_ENTRY_REC * entry,int count,int to_space)1230 static void gui_entry_move_words_right(GUI_ENTRY_REC *entry, int count, int to_space)
1231 {
1232 	int pos;
1233 
1234 	pos = entry->pos;
1235 	while (count > 0 && pos < entry->text_len) {
1236 		if (to_space) {
1237 			while (pos < entry->text_len && entry->text[pos] == ' ')
1238 				pos++;
1239 			while (pos < entry->text_len && entry->text[pos] != ' ')
1240 				pos++;
1241 		} else {
1242 			while (pos < entry->text_len && !i_isalnum(entry->text[pos]))
1243 				pos++;
1244 			while (pos < entry->text_len &&  i_isalnum(entry->text[pos]))
1245 				pos++;
1246 		}
1247 		count--;
1248 	}
1249 
1250         entry->pos = pos;
1251 }
1252 
gui_entry_move_words(GUI_ENTRY_REC * entry,int count,int to_space)1253 void gui_entry_move_words(GUI_ENTRY_REC *entry, int count, int to_space)
1254 {
1255         g_return_if_fail(entry != NULL);
1256 
1257 	if (count < 0)
1258 		gui_entry_move_words_left(entry, -count, to_space);
1259 	else if (count > 0)
1260 		gui_entry_move_words_right(entry, count, to_space);
1261 
1262 	gui_entry_fix_cursor(entry);
1263 	gui_entry_draw(entry);
1264 }
1265 
gui_entry_redraw(GUI_ENTRY_REC * entry)1266 void gui_entry_redraw(GUI_ENTRY_REC *entry)
1267 {
1268         g_return_if_fail(entry != NULL);
1269 
1270 	gui_entry_set_prompt(entry, NULL);
1271         gui_entry_redraw_from(entry, 0);
1272 	gui_entry_fix_cursor(entry);
1273 	gui_entry_draw(entry);
1274 }
1275 
gui_entry_alloc_extents(GUI_ENTRY_REC * entry)1276 static void gui_entry_alloc_extents(GUI_ENTRY_REC *entry)
1277 {
1278 	entry->uses_extents = TRUE;
1279 	entry->extents = g_new0(char *, entry->text_alloc);
1280 }
1281 
gui_entry_set_extent(GUI_ENTRY_REC * entry,int pos,const char * text)1282 void gui_entry_set_extent(GUI_ENTRY_REC *entry, int pos, const char *text)
1283 {
1284 	int update = FALSE;
1285 
1286 	g_return_if_fail(entry != NULL);
1287 
1288 	if (pos < 0 || pos > entry->text_len)
1289 		return;
1290 
1291 	if (text == NULL)
1292 		return;
1293 
1294 	if (!entry->uses_extents) {
1295 		gui_entry_alloc_extents(entry);
1296 	}
1297 
1298 	if (g_strcmp0(entry->extents[pos], text) != 0) {
1299 		g_free(entry->extents[pos]);
1300 		if (*text == '\0') {
1301 			entry->extents[pos] = NULL;
1302 		} else {
1303 			entry->extents[pos] = g_strdup(text);
1304 		}
1305 		update = TRUE;
1306 	}
1307 
1308 	if (update) {
1309 		gui_entry_redraw_from(entry, pos - 1);
1310 		gui_entry_fix_cursor(entry);
1311 		gui_entry_draw(entry);
1312 	}
1313 }
1314 
gui_entry_set_extents(GUI_ENTRY_REC * entry,int pos,int len,const char * left,const char * right)1315 void gui_entry_set_extents(GUI_ENTRY_REC *entry, int pos, int len, const char *left, const char *right)
1316 {
1317 	int end, update = FALSE;
1318 
1319 	g_return_if_fail(entry != NULL);
1320 
1321 	if (pos < 0 || len < 0 || pos > entry->text_len)
1322 		return;
1323 
1324 	end = pos + len;
1325 
1326 	if (end > entry->text_len)
1327 		end = entry->text_len;
1328 
1329 	if (!entry->uses_extents) {
1330 		gui_entry_alloc_extents(entry);
1331 	}
1332 
1333 	if (g_strcmp0(entry->extents[pos], left) != 0) {
1334 		g_free(entry->extents[pos]);
1335 		if (*left == '\0') {
1336 			entry->extents[pos] = NULL;
1337 		} else {
1338 			entry->extents[pos] = g_strdup(left);
1339 		}
1340 		update = TRUE;
1341 	}
1342 
1343 	if (pos != end && g_strcmp0(entry->extents[end], right) != 0) {
1344 		g_free(entry->extents[end]);
1345 		if (*right == '\0') {
1346 			entry->extents[end] = NULL;
1347 		} else {
1348 			entry->extents[end] = g_strdup(right);
1349 		}
1350 		update = TRUE;
1351 	}
1352 
1353 	if (update) {
1354 		gui_entry_redraw_from(entry, pos - 1);
1355 		gui_entry_fix_cursor(entry);
1356 		gui_entry_draw(entry);
1357 	}
1358 }
1359 
gui_entry_clear_extents(GUI_ENTRY_REC * entry,int pos,int len)1360 void gui_entry_clear_extents(GUI_ENTRY_REC *entry, int pos, int len)
1361 {
1362 	int i, end, update = FALSE;
1363 
1364 	g_return_if_fail(entry != NULL);
1365 
1366 	if (pos < 0 || len < 0 || pos > entry->text_len)
1367 		return;
1368 
1369 	end = pos + len;
1370 
1371 	if (end > entry->text_len)
1372 		end = entry->text_len;
1373 
1374 	if (!entry->uses_extents) {
1375 		return;
1376 	}
1377 
1378 	for (i = pos; i <= end; i++) {
1379 		if (entry->extents[i] != NULL) {
1380 			g_free(entry->extents[i]);
1381 			entry->extents[i] = NULL;
1382 			update = TRUE;
1383 		}
1384 	}
1385 
1386 	if (update) {
1387 		gui_entry_redraw_from(entry, pos);
1388 		gui_entry_fix_cursor(entry);
1389 		gui_entry_draw(entry);
1390 	}
1391 }
1392 
gui_entry_get_extent(GUI_ENTRY_REC * entry,int pos)1393 char *gui_entry_get_extent(GUI_ENTRY_REC *entry, int pos)
1394 {
1395 	g_return_val_if_fail(entry != NULL, NULL);
1396 
1397 	if (!entry->uses_extents)
1398 		return NULL;
1399 
1400 	if (pos < 0 || pos >= entry->text_len)
1401 		return NULL;
1402 
1403 	return entry->extents[pos];
1404 }
1405 
1406 #define POS_FLAG "%|"
gui_entry_get_text_and_extents(GUI_ENTRY_REC * entry)1407 GSList *gui_entry_get_text_and_extents(GUI_ENTRY_REC *entry)
1408 {
1409 	GSList *list = NULL;
1410 	GString *str;
1411 	int i;
1412 
1413 	g_return_val_if_fail(entry != NULL, NULL);
1414 
1415 	if (entry->uses_extents && entry->extents[0] != NULL) {
1416 		if (entry->pos == 0) {
1417 			list = g_slist_prepend(list, g_strconcat(entry->extents[0], POS_FLAG, NULL));
1418 		} else {
1419 			list = g_slist_prepend(list, g_strdup(entry->extents[0]));
1420 		}
1421 	} else {
1422 		if (entry->pos == 0) {
1423 			list = g_slist_prepend(list, g_strdup(POS_FLAG));
1424 		} else {
1425 			list = g_slist_prepend(list, NULL);
1426 		}
1427 	}
1428 
1429 	str = g_string_sized_new(entry->text_alloc);
1430 	for (i = 0; i < entry->text_len; i++) {
1431 		if (entry->utf8) {
1432 			g_string_append_unichar(str, entry->text[i]);
1433 		} else if (term_type == TERM_TYPE_BIG5) {
1434 			if(entry->text[i] > 0xff)
1435 				g_string_append_c(str, (entry->text[i] >> 8) & 0xff);
1436 			g_string_append_c(str, entry->text[i] & 0xff);
1437 		} else {
1438 			g_string_append_c(str, entry->text[i]);
1439 		}
1440 		if (entry->pos == i+1 || (entry->uses_extents && entry->extents[i+1] != NULL)) {
1441 			list = g_slist_prepend(list, g_strdup(str->str));
1442 			g_string_truncate(str, 0);
1443 			if (entry->uses_extents && entry->extents[i+1] != NULL) {
1444 				if (entry->pos == i+1) {
1445 					list = g_slist_prepend(list, g_strconcat(entry->extents[i+1], POS_FLAG, NULL));
1446 				} else {
1447 					list = g_slist_prepend(list, g_strdup(entry->extents[i+1]));
1448 				}
1449 			} else if (entry->pos == i+1) {
1450 				list = g_slist_prepend(list, g_strdup(POS_FLAG));
1451 			}
1452 		}
1453 	}
1454 	if (str->len > 0) {
1455 		list = g_slist_prepend(list, g_strdup(str->str));
1456 	}
1457 	list = g_slist_reverse(list);
1458 	g_string_free(str, TRUE);
1459 
1460 	return list;
1461 }
1462 
gui_entry_set_text_and_extents(GUI_ENTRY_REC * entry,GSList * list)1463 void gui_entry_set_text_and_extents(GUI_ENTRY_REC *entry, GSList *list)
1464 {
1465 	GSList *tmp;
1466 	int pos = -1;
1467 	int is_extent = 1;
1468 
1469 	gui_entry_set_text(entry, "");
1470 	for (tmp = list, is_extent = TRUE; tmp != NULL; tmp = tmp->next, is_extent ^= 1) {
1471 		if (is_extent) {
1472 			char *extent;
1473 			int len;
1474 
1475 			if (tmp->data == NULL)
1476 				continue;
1477 
1478 			extent = g_strdup(tmp->data);
1479 			len = strlen(extent);
1480 			if (len >= strlen(POS_FLAG) && g_strcmp0(&extent[len-strlen(POS_FLAG)], POS_FLAG) == 0) {
1481 				char *tmp;
1482 				tmp = extent;
1483 				extent = g_strndup(tmp, len - strlen(POS_FLAG));
1484 				g_free(tmp);
1485 				pos = entry->pos;
1486 			}
1487 
1488 			if (strlen(extent) > 0) {
1489 				gui_entry_set_extent(entry, entry->pos, extent);
1490 			}
1491 			g_free(extent);
1492 		} else {
1493 			gui_entry_insert_text(entry, tmp->data);
1494 		}
1495 	}
1496 	gui_entry_set_pos(entry, pos);
1497 }
1498 
gui_entry_init(void)1499 void gui_entry_init(void)
1500 {
1501 }
1502 
gui_entry_deinit(void)1503 void gui_entry_deinit(void)
1504 {
1505 }
1506