f70bf0dfe04e22279198230f460aed0f90edbdc8
[runtime.git] / apps / irssi / src / fe-text / gui-entry.c
1 /*
2  gui-entry.c : irssi
3
4     Copyright (C) 1999 Timo Sirainen
5
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20
21 #include "module.h"
22 #include "utf8.h"
23 #include "formats.h"
24
25 #include "gui-entry.h"
26 #include "gui-printtext.h"
27 #include "term.h"
28
29 GUI_ENTRY_REC *active_entry;
30
31 GUI_ENTRY_REC *gui_entry_create(int xpos, int ypos, int width, int utf8)
32 {
33         GUI_ENTRY_REC *rec;
34
35         rec = g_new0(GUI_ENTRY_REC, 1);
36         rec->xpos = xpos;
37         rec->ypos = ypos;
38         rec->width = width;
39         rec->text = g_string_new(NULL);
40         rec->utf8 = utf8;
41         return rec;
42 }
43
44 void gui_entry_destroy(GUI_ENTRY_REC *entry)
45 {
46         g_return_if_fail(entry != NULL);
47
48         if (active_entry == entry)
49                 gui_entry_set_active(NULL);
50
51         g_free_not_null(entry->prompt);
52         g_string_free(entry->text, TRUE);
53         g_free(entry);
54 }
55
56 /* Fixes the cursor position in screen */
57 static void gui_entry_fix_cursor(GUI_ENTRY_REC *entry)
58 {
59         int old_scrstart;
60
61         old_scrstart = entry->scrstart;
62         if (entry->pos - entry->scrstart < entry->width-2 - entry->promptlen &&
63             entry->pos - entry->scrstart > 0) {
64                 entry->scrpos = entry->pos - entry->scrstart;
65         } else if (entry->pos < entry->width-1 - entry->promptlen) {
66                 entry->scrstart = 0;
67                 entry->scrpos = entry->pos;
68         } else {
69                 entry->scrpos = (entry->width - entry->promptlen)*2/3;
70                 entry->scrstart = entry->pos - entry->scrpos;
71         }
72
73         if (old_scrstart != entry->scrstart)
74                 entry->redraw_needed_from = 0;
75 }
76
77 static void gui_entry_draw_from(GUI_ENTRY_REC *entry, int pos)
78 {
79         const unsigned char *p, *end;
80         int xpos, end_xpos;
81
82         if (entry->utf8) {
83                 /* FIXME: a stupid kludge to make the chars output correctly */
84                 pos = 0;
85         }
86
87         xpos = entry->xpos + entry->promptlen + pos;
88         end_xpos = entry->xpos + entry->width;
89         if (xpos > end_xpos)
90                 return;
91
92         term_set_color(root_window, ATTR_RESET);
93         term_move(root_window, xpos, entry->ypos);
94
95         p = (unsigned char *) (entry->scrstart + pos >= entry->text->len ? "" :
96                                entry->text->str + entry->scrstart + pos);
97         for (; *p != '\0' && xpos < end_xpos; p++, xpos++) {
98                 end = p;
99                 if (entry->utf8)
100                         get_utf8_char(&end);
101
102                 if (entry->hidden)
103                         term_addch(root_window, ' ');
104                 else if (*p >= 32 && (end != p || (*p & 127) >= 32)) {
105                         for (; p < end; p++)
106                                 term_addch(root_window, *p);
107                         term_addch(root_window, *p);
108                 } else {
109                         term_set_color(root_window, ATTR_RESET|ATTR_REVERSE);
110                         term_addch(root_window, *p+'A'-1);
111                         term_set_color(root_window, ATTR_RESET);
112                 }
113         }
114
115         /* clear the rest of the input line */
116         if (end_xpos == term_width)
117                 term_clrtoeol(root_window);
118         else {
119                 while (xpos < end_xpos) {
120                         term_addch(root_window, ' ');
121                         xpos++;
122                 }
123         }
124 }
125
126 static void gui_entry_draw(GUI_ENTRY_REC *entry)
127 {
128         if (entry->redraw_needed_from >= 0) {
129                 gui_entry_draw_from(entry, entry->redraw_needed_from);
130                 entry->redraw_needed_from = -1;
131         }
132
133         term_move_cursor(entry->xpos + entry->scrpos + entry->promptlen,
134                          entry->ypos);
135         term_refresh(NULL);
136 }
137
138 static void gui_entry_redraw_from(GUI_ENTRY_REC *entry, int pos)
139 {
140         pos -= entry->scrstart;
141         if (pos < 0) pos = 0;
142
143         if (entry->redraw_needed_from == -1 ||
144             entry->redraw_needed_from > pos)
145                 entry->redraw_needed_from = pos;
146 }
147
148 void gui_entry_move(GUI_ENTRY_REC *entry, int xpos, int ypos, int width)
149 {
150         int old_width;
151
152         g_return_if_fail(entry != NULL);
153
154         if (entry->xpos != xpos || entry->ypos != ypos) {
155                 /* position in screen changed - needs a full redraw */
156                 entry->xpos = xpos;
157                 entry->ypos = ypos;
158                 entry->width = width;
159                 gui_entry_redraw(entry);
160                 return;
161         }
162
163         if (entry->width == width)
164                 return; /* no changes */
165
166         if (width > entry->width) {
167                 /* input line grew - need to draw text at the end */
168                 old_width = width;
169                 entry->width = width;
170                 gui_entry_redraw_from(entry, old_width);
171         } else {
172                 /* input line shrinked - make sure the cursor
173                    is inside the input line */
174                 entry->width = width;
175                 if (entry->pos - entry->scrstart >
176                     entry->width-2 - entry->promptlen) {
177                         gui_entry_fix_cursor(entry);
178                 }
179         }
180
181         gui_entry_draw(entry);
182 }
183
184 void gui_entry_set_active(GUI_ENTRY_REC *entry)
185 {
186         active_entry = entry;
187
188         if (entry != NULL) {
189                 term_move_cursor(entry->xpos + entry->scrpos +
190                                  entry->promptlen, entry->ypos);
191                 term_refresh(NULL);
192         }
193 }
194
195 void gui_entry_set_prompt(GUI_ENTRY_REC *entry, const char *str)
196 {
197         int oldlen;
198
199         g_return_if_fail(entry != NULL);
200
201         oldlen = entry->promptlen;
202         if (str != NULL) {
203                 g_free_not_null(entry->prompt);
204                 entry->prompt = g_strdup(str);
205                 entry->promptlen = format_get_length(str);
206         }
207
208         if (entry->prompt != NULL)
209                 gui_printtext(entry->xpos, entry->ypos, entry->prompt);
210
211         if (entry->promptlen != oldlen) {
212                 gui_entry_fix_cursor(entry);
213                 gui_entry_draw(entry);
214         }
215 }
216
217 void gui_entry_set_hidden(GUI_ENTRY_REC *entry, int hidden)
218 {
219         g_return_if_fail(entry != NULL);
220
221         entry->hidden = hidden;
222 }
223
224 void gui_entry_set_utf8(GUI_ENTRY_REC *entry, int utf8)
225 {
226         g_return_if_fail(entry != NULL);
227
228         entry->utf8 = utf8;
229 }
230
231 void gui_entry_set_text(GUI_ENTRY_REC *entry, const char *str)
232 {
233         g_return_if_fail(entry != NULL);
234         g_return_if_fail(str != NULL);
235
236         g_string_assign(entry->text, str);
237         entry->pos = entry->text->len;
238
239         gui_entry_redraw_from(entry, 0);
240         gui_entry_fix_cursor(entry);
241         gui_entry_draw(entry);
242 }
243
244 char *gui_entry_get_text(GUI_ENTRY_REC *entry)
245 {
246         g_return_val_if_fail(entry != NULL, NULL);
247
248         return entry->text->str;
249 }
250
251 void gui_entry_insert_text(GUI_ENTRY_REC *entry, const char *str)
252 {
253         g_return_if_fail(entry != NULL);
254         g_return_if_fail(str != NULL);
255
256         gui_entry_redraw_from(entry, entry->pos);
257         g_string_insert(entry->text, entry->pos, str);
258         entry->pos += strlen(str);
259
260         gui_entry_fix_cursor(entry);
261         gui_entry_draw(entry);
262 }
263
264 void gui_entry_insert_char(GUI_ENTRY_REC *entry, char chr)
265 {
266         g_return_if_fail(entry != NULL);
267
268         if (chr == 0 || chr == 13 || chr == 10)
269                 return; /* never insert NUL, CR or LF characters */
270
271         gui_entry_redraw_from(entry, entry->pos);
272         g_string_insert_c(entry->text, entry->pos, chr);
273         entry->pos++;
274
275         gui_entry_fix_cursor(entry);
276         gui_entry_draw(entry);
277 }
278
279 void gui_entry_erase(GUI_ENTRY_REC *entry, int size)
280 {
281         g_return_if_fail(entry != NULL);
282
283         if (entry->pos < size)
284                 return;
285
286 #ifdef WANT_BIG5
287         if (is_big5(entry->text->str[entry->pos-2],
288                     entry->text->str[entry->pos-1]))
289                 size++;
290 #endif
291
292         entry->pos -= size;
293         g_string_erase(entry->text, entry->pos, size);
294
295         gui_entry_redraw_from(entry, entry->pos);
296         gui_entry_fix_cursor(entry);
297         gui_entry_draw(entry);
298 }
299
300 void gui_entry_erase_word(GUI_ENTRY_REC *entry, int to_space)
301 {
302         int to;
303
304         g_return_if_fail(entry != NULL);
305         if (entry->pos == 0)
306                 return;
307
308         to = entry->pos - 1;
309
310         if (to_space) {
311                 while (entry->text->str[to] == ' ' && to > 0)
312                         to--;
313                 while (entry->text->str[to] != ' ' && to > 0)
314                         to--;
315         } else {
316                 while (!i_isalnum(entry->text->str[to]) && to > 0)
317                         to--;
318                 while (i_isalnum(entry->text->str[to]) && to > 0)
319                         to--;
320         }
321         if (to > 0) to++;
322
323         g_string_erase(entry->text, to, entry->pos - to);
324         entry->pos = to;
325
326         gui_entry_redraw_from(entry, entry->pos);
327         gui_entry_fix_cursor(entry);
328         gui_entry_draw(entry);
329 }
330
331 void gui_entry_erase_next_word(GUI_ENTRY_REC *entry, int to_space)
332 {
333         int to;
334
335         g_return_if_fail(entry != NULL);
336         if (entry->pos == entry->text->len)
337                 return;
338
339         to = entry->pos;
340         if (to_space) {
341                 while (entry->text->str[to] == ' ' && to < entry->text->len)
342                         to++;
343                 while (entry->text->str[to] != ' ' && to < entry->text->len)
344                         to++;
345         } else {
346                 while (!i_isalnum(entry->text->str[to]) && to < entry->text->len)
347                         to++;
348                 while (i_isalnum(entry->text->str[to]) && to < entry->text->len)
349                         to++;
350         }
351
352         g_string_erase(entry->text, entry->pos, to - entry->pos);
353
354         gui_entry_redraw_from(entry, entry->pos);
355         gui_entry_fix_cursor(entry);
356         gui_entry_draw(entry);
357 }
358
359 int gui_entry_get_pos(GUI_ENTRY_REC *entry)
360 {
361         g_return_val_if_fail(entry != NULL, 0);
362
363         return entry->pos;
364 }
365
366 void gui_entry_set_pos(GUI_ENTRY_REC *entry, int pos)
367 {
368         g_return_if_fail(entry != NULL);
369
370         if (pos >= 0 && pos <= entry->text->len)
371                 entry->pos = pos;
372
373         gui_entry_fix_cursor(entry);
374         gui_entry_draw(entry);
375 }
376
377 void gui_entry_move_pos(GUI_ENTRY_REC *entry, int pos)
378 {
379         g_return_if_fail(entry != NULL);
380
381 #ifdef WANT_BIG5
382         if (pos > 0 && is_big5(entry->text->str[entry->pos],
383                                entry->text->str[entry->pos+1]))
384                 pos++;
385         else if (pos < 0 && is_big5(entry->text->str[entry->pos-1],
386                                     entry->text->str[entry->pos]))
387                 pos--;
388 #endif
389
390         if (entry->pos+pos >= 0 && entry->pos+pos <= entry->text->len)
391                 entry->pos += pos;
392
393         gui_entry_fix_cursor(entry);
394         gui_entry_draw(entry);
395 }
396
397 static void gui_entry_move_words_left(GUI_ENTRY_REC *entry, int count, int to_space)
398 {
399         int pos;
400
401         pos = entry->pos;
402         while (count > 0 && pos > 0) {
403                 if (to_space) {
404                         while (pos > 0 && entry->text->str[pos-1] == ' ')
405                                 pos--;
406                         while (pos > 0 && entry->text->str[pos-1] != ' ')
407                                 pos--;
408                 } else {
409                         while (pos > 0 && !i_isalnum(entry->text->str[pos-1]))
410                                 pos--;
411                         while (pos > 0 &&  i_isalnum(entry->text->str[pos-1]))
412                                 pos--;
413                 }
414                 count--;
415         }
416
417         entry->pos = pos;
418 }
419
420 static void gui_entry_move_words_right(GUI_ENTRY_REC *entry, int count, int to_space)
421 {
422         int pos;
423
424         pos = entry->pos;
425         while (count > 0 && pos < entry->text->len) {
426                 if (to_space) {
427                         while (pos < entry->text->len && entry->text->str[pos] == ' ')
428                                 pos++;
429                         while (pos < entry->text->len && entry->text->str[pos] != ' ')
430                                 pos++;
431                 } else {
432                         while (pos < entry->text->len && !i_isalnum(entry->text->str[pos]))
433                                 pos++;
434                         while (pos < entry->text->len &&  i_isalnum(entry->text->str[pos]))
435                                 pos++;
436                 }
437                 count--;
438         }
439
440         entry->pos = pos;
441 }
442
443 void gui_entry_move_words(GUI_ENTRY_REC *entry, int count, int to_space)
444 {
445         g_return_if_fail(entry != NULL);
446
447         if (count < 0)
448                 gui_entry_move_words_left(entry, -count, to_space);
449         else if (count > 0)
450                 gui_entry_move_words_right(entry, count, to_space);
451
452         gui_entry_fix_cursor(entry);
453         gui_entry_draw(entry);
454 }
455
456 void gui_entry_redraw(GUI_ENTRY_REC *entry)
457 {
458         g_return_if_fail(entry != NULL);
459
460         gui_entry_set_prompt(entry, NULL);
461         gui_entry_redraw_from(entry, 0);
462         gui_entry_fix_cursor(entry);
463         gui_entry_draw(entry);
464 }