merged hv7 mod
[goodguy/history.git] / cinelerra-5.1 / guicast / bctextbox.C
1 /*
2  * CINELERRA
3  * Copyright (C) 1997-2014 Adam Williams <broadcast at earthling dot net>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  */
20
21 #include "bcclipboard.h"
22 #include "bclistboxitem.h"
23 #include "bcresources.h"
24 #include "bcsignals.h"
25 #include "bctextbox.h"
26 #include "clip.h"
27 #include "bccolors.h"
28 #include <ctype.h>
29 #include "cursors.h"
30 #include "filesystem.h"
31 #include "keys.h"
32 #include "language.h"
33 #include <math.h>
34 #include "bctimer.h"
35 #include "vframe.h"
36
37 #include <string.h>
38 #include <unistd.h>
39 #include <wchar.h>
40 #include <wctype.h>
41
42 #define VERTICAL_MARGIN 2
43 #define VERTICAL_MARGIN_NOBORDER 0
44 #define HORIZONTAL_MARGIN 4
45 #define HORIZONTAL_MARGIN_NOBORDER 2
46
47 BC_TextBox::BC_TextBox(int x, int y, int w, int rows,
48         int size, char *text, int has_border, int font)
49  : BC_SubWindow(x, y, w, 0, -1)
50 {
51         is_utf8 = 1;
52         skip_cursor = 0;
53         reset_parameters(rows, has_border, font, size);
54         if( size > 0 )
55                 tstrcpy(text);
56         else    // text referenced directly
57                 this->text = text;
58 }
59
60 BC_TextBox::BC_TextBox(int x, int y, int w, int rows,
61         int size, wchar_t *wtext, int has_border, int font)
62  : BC_SubWindow(x, y, w, 0, -1)
63 {
64         is_utf8 = 1;
65         skip_cursor = 0;
66         wsize = size > 0 ? size : wcslen(wtext);
67         if( size <= 0 ) size = 2*wsize;
68         reset_parameters(rows, has_border, font, size);
69         this->wtext = new wchar_t[wsize+1];
70         wcsncpy(this->wtext, wtext, wsize);
71         this->wtext[wsize] = 0;
72 }
73
74 BC_TextBox::BC_TextBox(int x, int y, int w, int rows,
75         const char *text, int has_border, int font, int is_utf8)
76  : BC_SubWindow(x, y, w, 0, -1)
77 {
78         this->is_utf8 = is_utf8;
79         skip_cursor = 0;
80         reset_parameters(rows, has_border, font, BCTEXTLEN);
81         tstrcpy(text);
82 }
83
84 BC_TextBox::BC_TextBox(int x, int y, int w, int rows,
85         const wchar_t *wtext, int has_border, int font, int is_utf8)
86  : BC_SubWindow(x, y, w, 0, -1)
87 {
88         this->is_utf8 = is_utf8;
89         skip_cursor = 0;
90         reset_parameters(rows, has_border, font, BCTEXTLEN);
91         wsize = BCTEXTLEN;
92         wtext = new wchar_t[wsize+1];
93         wcsncpy(this->wtext, wtext, wsize);
94         this->wtext[wsize] = 0;
95 }
96
97 BC_TextBox::BC_TextBox(int x, int y, int w, int rows,
98         int64_t text, int has_border, int font)
99  : BC_SubWindow(x, y, w, 0, -1)
100 {
101         is_utf8 = 1;
102         skip_cursor = 0;
103         reset_parameters(rows, has_border, font, BCSTRLEN);
104         snprintf(this->text, this->tsize, "%jd", text);
105         dirty = 1;  wtext_update();
106 }
107
108 BC_TextBox::BC_TextBox(int x, int y, int w, int rows,
109         float text, int has_border, int font, int precision)
110  : BC_SubWindow(x, y, w, 0, -1)
111 {
112         is_utf8 = 1;
113         skip_cursor = 0;
114         reset_parameters(rows, has_border, font, BCSTRLEN);
115         this->precision = precision;
116         snprintf(this->text, this->tsize, "%0.*f", precision, text);
117         dirty = 1;  wtext_update();
118 }
119
120 BC_TextBox::BC_TextBox(int x, int y, int w, int rows,
121         int text, int has_border, int font)
122  : BC_SubWindow(x, y, w, 0, -1)
123 {
124         is_utf8 = 1;
125         skip_cursor = 0;
126         reset_parameters(rows, has_border, font, BCSTRLEN);
127         snprintf(this->text, this->tsize, "%d", text);
128         dirty = 1;  wtext_update();
129 }
130
131 BC_TextBox::~BC_TextBox()
132 {
133         if(skip_cursor) delete skip_cursor;
134         delete suggestions_popup;
135         suggestions->remove_all_objects();
136         delete suggestions;
137         delete menu;
138         delete [] wtext;
139         if( size > 0 )
140                 delete [] text;
141 }
142
143 int BC_TextBox::reset_parameters(int rows, int has_border, int font, int size)
144 {
145         suggestions = new ArrayList<BC_ListBoxItem*>;
146         suggestions_popup = 0;
147         suggestion_column = 0;
148
149         this->rows = rows;
150         this->has_border = has_border;
151         this->font = font;
152         this->size = size;
153         this->tsize = size >= 0 ? size : -size;
154         this->text = size > 0 ? new char[size+1] : 0;
155         if( this->text ) this->text[0] = 0;
156         text_start = 0;
157         text_end = 0;
158         highlight_letter1 = highlight_letter2 = 0;
159         highlight_letter3 = highlight_letter4 = 0;
160         ibeam_letter = 0;
161         active = 0;
162         text_selected = 0;
163         word_selected = 0;
164         line_selected = 0;
165         unicode_active = -1;
166         text_x = 0;
167         enabled = 1;
168         highlighted = 0;
169         precision = 4;
170         if (!skip_cursor)
171                 skip_cursor = new Timer;
172         wtext = 0;
173         wsize = 0;
174         wlen = 0;
175         keypress_draw = 1;
176         last_keypress = 0;
177         separators = 0;
178         xscroll = 0;
179         yscroll = 0;
180         menu = 0;
181         dirty = 1;
182         selection_active = 0;
183         return 0;
184 }
185
186 int BC_TextBox::tstrlen()
187 {
188         if( !tsize ) return strlen(text);
189         return strnlen(text, tsize);
190 }
191
192 int BC_TextBox::tstrcmp(const char *cp)
193 {
194         if( !tsize ) return strcmp(text, cp);
195         return strncmp(text, cp, tsize);
196 }
197
198 char *BC_TextBox::tstrcpy(const char *cp)
199 {
200         dirty = 1;
201         if( cp ) {
202                 if( !tsize )
203                         return strcpy(text, cp);
204                 strncpy(text, cp, tsize);
205                 text[tsize-1] = 0;
206         }
207         else
208                 text[0] = 0;
209         return text;
210 }
211
212 char *BC_TextBox::tstrcat(const char *cp)
213 {
214         dirty = 1;
215         if( !tsize ) return strcat(text, cp);
216         char *result = strncat(text, cp, tsize);
217         text[tsize-1] = 0;
218         return result;
219 }
220
221 int BC_TextBox::wtext_update()
222 {
223         if( dirty ) {
224                 const char *src_enc = is_utf8 ? "UTF8" : BC_Resources::encoding;
225                 const char *dst_enc = BC_Resources::wide_encoding;
226                 int nsize = tsize > 0 ? tsize : strlen(text) + BCTEXTLEN;
227                 if( nsize > wsize || !wtext ) {
228                         wchar_t *ntext = new wchar_t[nsize+1];
229                         memcpy(ntext, wtext, wsize*sizeof(wtext[0]));
230                         delete [] wtext;  wtext = ntext;  wsize = nsize;
231                 }
232                 wlen = BC_Resources::encode(src_enc, dst_enc, text, strlen(text),
233                         (char*)wtext, wsize*sizeof(wchar_t)) / sizeof(wchar_t);
234                 dirty = 0;
235         }
236         wtext[wlen] = 0;
237         return wlen;
238 }
239
240 int BC_TextBox::text_update(const wchar_t *wcp, int wsz, char *tcp, int tsz)
241 {
242         const char *src_enc = BC_Resources::wide_encoding;
243         const char *dst_enc = BC_Resources::encoding;
244         if( wsz < 0 ) wsz = wcslen(wcp);
245         int len = BC_Resources::encode(src_enc, dst_enc,
246                 (char*)wcp, wsz*sizeof(wchar_t), tcp, tsz);
247         tcp[len] = 0;
248         return len;
249 }
250
251 int BC_TextBox::initialize()
252 {
253
254         if (!skip_cursor)
255                 skip_cursor = new Timer;
256         skip_cursor->update();
257 // Get dimensions
258         text_ascent = get_text_ascent(font) + 1;
259         text_descent = get_text_descent(font) + 1;
260         text_height = text_ascent + text_descent;
261         ibeam_letter = wtext_update();
262         if(has_border)
263         {
264                 left_margin = right_margin = HORIZONTAL_MARGIN;
265                 top_margin = bottom_margin = VERTICAL_MARGIN;
266         }
267         else
268         {
269                 left_margin = right_margin = HORIZONTAL_MARGIN_NOBORDER;
270                 top_margin = bottom_margin = VERTICAL_MARGIN_NOBORDER;
271         }
272         h = get_row_h(rows);
273         text_x = left_margin;
274         text_y = top_margin;
275         find_ibeam(0);
276
277 // Create the subwindow
278         BC_SubWindow::initialize();
279
280         BC_Resources *resources = get_resources();
281         if(has_border)
282         {
283                 back_color = resources->text_background;
284                 high_color = resources->text_background_hi;
285         }
286         else
287         {
288                 high_color = resources->text_background_noborder_hi;
289                 back_color = bg_color;
290         }
291
292         draw(0);
293         set_cursor(IBEAM_CURSOR, 0, 0);
294         show_window(0);
295
296         add_subwindow(menu = new BC_TextMenu(this));
297         menu->create_objects();
298
299         return 0;
300 }
301
302 int BC_TextBox::calculate_h(BC_WindowBase *gui,
303         int font,
304         int has_border,
305         int rows)
306 {
307         return rows * (gui->get_text_ascent(font) + 1 +
308                 gui->get_text_descent(font) + 1) +
309                 2 * (has_border ? VERTICAL_MARGIN : VERTICAL_MARGIN_NOBORDER);
310 }
311
312 void BC_TextBox::set_precision(int precision)
313 {
314         this->precision = precision;
315 }
316
317 // Compute suggestions for a path
318 int BC_TextBox::calculate_suggestions(ArrayList<BC_ListBoxItem*> *entries, const char *filter)
319 {
320 // Let user delete suggestion
321         if(get_last_keypress() != BACKSPACE) {
322 // Compute suggestions
323                 FileSystem fs;
324                 ArrayList<char*> suggestions;
325                 const char *current_text = get_text();
326                 int suggestion_column = 0;
327                 char dirname[BCTEXTLEN];  dirname[0] = 0;
328                 if( current_text[0] == '/' || current_text[0] == '~' )
329                         strncpy(dirname, current_text, sizeof(dirname));
330                 else if( !entries )
331                         getcwd(dirname, sizeof(dirname));
332 // If directory, tabulate it
333                 if( dirname[0] ) {
334                         if( filter ) fs.set_filter(filter);
335                         char *prefix, *cp;
336                         strncpy(dirname, current_text, sizeof(dirname));
337                         if( (cp=strrchr(dirname, '/')) ||
338                             (cp=strrchr(dirname, '~')) ) *++cp = 0;
339                         fs.parse_tildas(dirname);
340                         fs.update(dirname);
341                         cp = (char *)current_text;
342                         if( (prefix=strrchr(cp, '/')) ||
343                             (prefix=strrchr(cp, '~')) ) ++prefix;
344                         suggestion_column = !prefix ? 0 : prefix - cp;
345                         int prefix_len = prefix ? strlen(prefix) : 0;
346 // only include items where the filename matches the basename prefix
347                         for(int i = 0; i < fs.total_files(); i++) {
348                                 char *current_name = fs.get_entry(i)->name;
349                                 if( prefix_len>0 && strncmp(prefix, current_name, prefix_len) ) continue;
350                                 suggestions.append(current_name);
351                         }
352                 }
353                 else if(entries) {
354                         char *prefix = (char *)current_text;
355                         int prefix_len = strlen(prefix);
356                         for(int i = 0; i < entries->size(); i++) {
357                                 char *current_name = entries->get(i)->get_text();
358                                 if( prefix_len>0 && strncmp(prefix, current_name, prefix_len) ) continue;
359                                 suggestions.append(current_name);
360                         }
361                 }
362                 set_suggestions(&suggestions, suggestion_column);
363         }
364
365         return 1;
366 }
367
368 void BC_TextBox::no_suggestions()
369 {
370         if( suggestions_popup ) {
371                 delete suggestions_popup;
372                 suggestions_popup = 0;
373                 activate();
374         }
375 }
376
377 void BC_TextBox::set_suggestions(ArrayList<char*> *suggestions, int column)
378 {
379 // Copy the array
380         this->suggestions->remove_all_objects();
381         this->suggestion_column = column;
382
383         if(suggestions) {
384                 for(int i = 0; i < suggestions->size(); i++) {
385                         this->suggestions->append(new BC_ListBoxItem(suggestions->get(i)));
386                 }
387
388 // Show the popup without taking focus
389                 if( suggestions->size() > 1 ) {
390                         if( !suggestions_popup ) {
391                                 suggestions_popup = new BC_TextBoxSuggestions(this, x, y);
392                                 get_parent()->add_subwindow(suggestions_popup);
393                                 suggestions_popup->set_is_suggestions(1);
394                         }
395                         else {
396                                 suggestions_popup->update(this->suggestions, 0, 0, 1);
397                         }
398                         suggestions_popup->activate(0);
399                 }
400                 else
401 // Show the highlighted text
402                 if( suggestions->size() == 1 ) {
403                         highlight_letter1 = wtext_update();
404                         text_update(wtext,wlen, text,tsize);
405                         char *current_suggestion = suggestions->get(0);
406                         int col = highlight_letter1 - suggestion_column;
407                         if( col < 0 ) col = 0;
408                         char *cur = current_suggestion + col;
409                         tstrcat(cur);
410                         highlight_letter2 = wtext_update();
411 //printf("BC_TextBox::set_suggestions %d %d\n", __LINE__, suggestion_column);
412
413                         draw(1);
414                         no_suggestions();
415                 }
416         }
417
418 // Clear the popup
419         if( !suggestions || !this->suggestions->size() )
420                 no_suggestions();
421 }
422
423 void BC_TextBox::wset_selection(int char1, int char2, int ibeam)
424 {
425         highlight_letter1 = char1;
426         highlight_letter2 = char2;
427         ibeam_letter = ibeam;
428         draw(1);
429 }
430
431 // count utf8 chars in text which occur before cp
432 int BC_TextBox::wcpos(const char *text, const char *cp)
433 {
434         int len = 0;
435         const unsigned char *bp = (const unsigned char *)text;
436         const unsigned char *ep = (const unsigned char *)cp;
437         while( bp < ep && *bp != 0 ) {
438                 ++len;
439                 int ch = *bp++;
440                 if( ch < 0x80 ) continue;
441                 int i = ch - 0x0c0;
442                 int n = i<0? 0 : i<32? 1 : i<48? 2 : i<56? 3 : i<60? 4 : 5;
443                 for( i=n; bp < ep && --i>=0 && (*bp&0xc0) == 0x80; ++bp );
444         }
445         return len;
446 }
447
448 void BC_TextBox::set_selection(int char1, int char2, int ibeam)
449 {
450         const char *cp = get_text();
451         wset_selection(wcpos(cp, cp+char1), wcpos(cp, cp+char2), wcpos(cp, cp+ibeam));
452 }
453
454 int BC_TextBox::update(const char *text)
455 {
456 //printf("BC_TextBox::update 1 %d %s %s\n", tstrcmp(text), text, this->text);
457 // Don't update if contents are the same
458         if(!tstrcmp(text)) return 0;
459         tstrcpy(text);
460         int wtext_len = wtext_update();
461         if(highlight_letter1 > wtext_len) highlight_letter1 = wtext_len;
462         if(highlight_letter2 > wtext_len) highlight_letter2 = wtext_len;
463         if(ibeam_letter > wtext_len) ibeam_letter = wtext_len;
464         draw(1);
465         return 0;
466 }
467
468 int BC_TextBox::update(const wchar_t *wtext)
469 {
470         int wtext_len = wcslen(wtext);
471         if( wtext_len >= wsize ) wtext_len = wsize;
472         wcsncpy(this->wtext, wtext, wtext_len);
473         wlen = wtext_len;
474         if(highlight_letter1 > wtext_len) highlight_letter1 = wtext_len;
475         if(highlight_letter2 > wtext_len) highlight_letter2 = wtext_len;
476         if(ibeam_letter > wtext_len) ibeam_letter = wtext_len;
477         draw(1);
478         return 0;
479 }
480
481 int BC_TextBox::update(int64_t value)
482 {
483         char string[BCTEXTLEN];
484         sprintf(string, "%jd", value);
485         update(string);
486         return 0;
487 }
488
489 int BC_TextBox::update(float value)
490 {
491         char string[BCTEXTLEN];
492         sprintf(string, "%0.*f", precision, value);
493         update(string);
494         return 0;
495 }
496
497 void BC_TextBox::disable()
498 {
499         if(enabled) {
500                 enabled = 0;
501                 deactivate();
502                 draw(1);
503         }
504 }
505
506 void BC_TextBox::enable()
507 {
508         if(!enabled) {
509                 enabled = 1;
510                 if(top_level) {
511                         draw(1);
512                 }
513         }
514 }
515
516 int BC_TextBox::get_enabled() { return enabled; }
517 int BC_TextBox::get_text_x() { return text_x; }
518 int BC_TextBox::get_text_y() { return text_y; }
519 void BC_TextBox::set_text_x(int v) { text_x = v; }
520 void BC_TextBox::set_text_y(int v) { text_y = v; }
521
522 int BC_TextBox::pixels_to_rows(BC_WindowBase *window, int font, int pixels)
523 {
524         return (pixels - 4) /
525                 (window->get_text_ascent(font) + 1 +
526                         window->get_text_descent(font) + 1);
527 }
528
529 int BC_TextBox::calculate_row_h(int rows,
530         BC_WindowBase *parent_window,
531         int has_border,
532         int font)
533 {
534         return rows *
535                 (parent_window->get_text_ascent(font) + 1 +
536                 parent_window->get_text_descent(font) + 1) +
537                 (has_border ? 4 : 0);
538 }
539
540 const char* BC_TextBox::get_text()
541 {
542         int wtext_len = wtext_update();
543         text_update(wtext,wtext_len, text,tsize);
544         return text;
545 }
546
547 const wchar_t* BC_TextBox::get_wtext()
548 {
549         wtext_update();
550         return wtext;
551 }
552
553 void BC_TextBox::set_text(char *text, int isz)
554 {
555         if( size > 0 || isz < 0 ) return;
556         this->text = text;
557         tsize = isz;
558         size = -isz;
559         dirty = 1;
560         wtext_update();
561         draw(1);
562 }
563
564 int BC_TextBox::get_text_rows()
565 {
566         int wtext_len = wtext_update();
567         int result = 1;
568         for(int i = 0; i < wtext_len; i++) {
569                 if(wtext[i] == '\n') result++;
570         }
571         return result;
572 }
573
574
575 int BC_TextBox::get_row_h(int rows)
576 {
577         return rows * text_height + top_margin + bottom_margin;
578 }
579
580 int BC_TextBox::reposition_window(int x, int y, int w, int rows)
581 {
582         int new_h = get_h();
583         if(w < 0) w = get_w();
584         if(rows != -1)
585         {
586                 new_h = get_row_h(rows);
587                 this->rows = rows;
588         }
589
590         if(x != get_x() ||
591                 y != get_y() ||
592                 w != get_w() ||
593                 new_h != get_h())
594         {
595 // printf("BC_TextBox::reposition_window 1 %d %d %d %d %d %d %d %d\n",
596 // x, get_x(), y, get_y(), w, get_w(), new_h, get_h());
597                 BC_WindowBase::reposition_window(x, y, w, new_h);
598                 draw(0);
599         }
600         return 0;
601 }
602
603 void BC_TextBox::draw_border()
604 {
605         BC_Resources *resources = get_resources();
606 // Clear margins
607         set_color(background_color);
608         draw_box(0, 0, left_margin, get_h());
609         draw_box(get_w() - right_margin, 0, right_margin, get_h());
610
611         if(has_border)
612         {
613                 if(highlighted)
614                         draw_3d_border(0, 0, w, h,
615                                 resources->text_border1,
616                                 resources->text_border2_hi,
617                                 resources->text_border3_hi,
618                                 resources->text_border4);
619                 else
620                         draw_3d_border(0, 0, w, h,
621                                 resources->text_border1,
622                                 resources->text_border2,
623                                 resources->text_border3,
624                                 resources->text_border4);
625         }
626 }
627
628 void BC_TextBox::draw_cursor()
629 {
630 //      set_color(background_color);
631
632         if(ibeam_x >= 0 &&
633                 ibeam_y >= 0)
634         {
635         set_color(WHITE);
636         set_inverse();
637
638         draw_box(ibeam_x + text_x,
639                 ibeam_y + text_y,
640                 BCCURSORW,
641                 text_height);
642         set_opaque();
643         }
644 }
645
646
647 void BC_TextBox::draw(int flush)
648 {
649         int i, j, k;
650         int row_begin, row_end;
651         int highlight_x1, highlight_x2;
652         int need_ibeam = 1;
653         BC_Resources *resources = get_resources();
654
655 //printf("BC_TextBox::draw %d %s\n", __LINE__, text);
656 // Background
657         if(has_border)
658                 background_color = resources->text_background;
659         else if(highlighted)
660                 background_color = high_color;
661         else
662                 background_color = back_color;
663         set_color(background_color);
664         draw_box(0, 0, w, h);
665
666         int wtext_len = wtext_update();
667
668 // Draw text with selection
669         set_font(font);
670
671         for(i=0, j=0, k=text_y; i < wtext_len && k < get_h(); k += text_height) {
672 // Draw row of text
673                 row_begin = i;
674                 wchar_t *wtext_row = &wtext[i];
675                 for(j=0; j<BCTEXTLEN-1 && i<wtext_len && wtext[i]!='\n'; ++i, ++j);
676                 if( (row_end=i) < wtext_len ) ++i;
677
678                 if(k > top_margin-text_height && k < get_h()-bottom_margin) {
679 // Draw highlighted region of row
680                         if( highlight_letter2 > highlight_letter1 &&
681                             highlight_letter2 > row_begin &&
682                             highlight_letter1 <= row_end ) {
683                                 int color = active && enabled && get_has_focus() ?
684                                         resources->text_highlight :
685                                     selection_active ?
686                                         resources->text_selected_highlight :
687                                         resources->text_inactive_highlight;
688                                 if( unicode_active >= 0 )
689                                         color ^= LTBLUE;
690                                 set_color(color);
691                                 if(highlight_letter1 >= row_begin &&
692                                         highlight_letter1 <= row_end)
693                                         highlight_x1 = get_x_position(highlight_letter1, row_begin);
694                                 else
695                                         highlight_x1 = 0;
696
697                                 if(highlight_letter2 > row_begin &&
698                                         highlight_letter2 <= row_end)
699                                         highlight_x2 = get_x_position(highlight_letter2, row_begin);
700                                 else
701                                         highlight_x2 = get_w();
702
703                                 draw_box(highlight_x1 + text_x, k,
704                                         highlight_x2 - highlight_x1, text_height);
705                         }
706
707 // Draw text over highlight
708                         int len = row_end - row_begin;
709                         if( len > 0 ) {
710                                 set_color(enabled ? resources->text_default : DMGREY);
711                                 draw_single_text(1, font, text_x, k + text_ascent, wtext_row, len);
712                         }
713
714 // Get ibeam location
715                         if(ibeam_letter >= row_begin && ibeam_letter <= row_end) {
716                                 need_ibeam = 0;
717                                 ibeam_y = k - text_y;
718                                 ibeam_x = get_x_position(ibeam_letter, row_begin);
719                         }
720                 }
721         }
722
723 //printf("BC_TextBox::draw 3 %d\n", ibeam_y);
724         if(need_ibeam) {
725 //              ibeam_x = ibeam_y = !wtext_len ? 0 : -1;
726                 ibeam_x = 0;  ibeam_y = k - text_y;
727         }
728
729 //printf("BC_TextBox::draw 4 %d\n", ibeam_y);
730 // Draw solid cursor
731         if (active)
732                 draw_cursor();
733
734 // Border
735         draw_border();
736         flash(flush);
737 }
738
739 int BC_TextBox::focus_in_event()
740 {
741         draw(1);
742         return 1;
743 }
744
745 int BC_TextBox::focus_out_event()
746 {
747         draw(1);
748         return 1;
749 }
750
751 int BC_TextBox::cursor_enter_event()
752 {
753         if( top_level->event_win == win && enabled &&
754             !(top_level->get_resources()->textbox_focus_policy & CLICK_ACTIVATE) )
755         {
756                 if( !active ) {
757                         top_level->deactivate();
758                         activate();
759                 }
760                 if(!highlighted)
761                 {
762                         highlighted = 1;
763                         draw_border();
764                         flash(1);
765                 }
766         }
767         return 0;
768 }
769
770 int BC_TextBox::cursor_leave_event()
771 {
772         if(highlighted)
773         {
774                 highlighted = 0;
775                 hide_tooltip();
776                 draw_border();
777                 flash(1);
778         }
779         if( !suggestions_popup && !get_button_down() &&
780             !(top_level->get_resources()->textbox_focus_policy & CLICK_DEACTIVATE) )
781                 deactivate();
782         return 0;
783 }
784
785 int BC_TextBox::button_press_event()
786 {
787         const int debug = 0;
788
789         if(!enabled) return 0;
790 //      if(get_buttonpress() != WHEEL_UP &&
791 //              get_buttonpress() != WHEEL_DOWN &&
792 //              get_buttonpress() != LEFT_BUTTON &&
793 //              get_buttonpress() != MIDDLE_BUTTON) return 0;
794
795         if(debug) printf("BC_TextBox::button_press_event %d\n", __LINE__);
796
797         int cursor_letter = 0;
798         int wtext_len = wtext_update();
799         int update_scroll = 0;
800
801
802         if(top_level->event_win == win)
803         {
804                 if(!active)
805                 {
806                         hide_tooltip();
807                         top_level->deactivate();
808                         activate();
809                 }
810
811
812                 if(get_buttonpress() == WHEEL_UP)
813                 {
814                         text_y += text_height;
815                         text_y = MIN(text_y, top_margin);
816                         update_scroll = 1;
817                 }
818                 else
819                 if(get_buttonpress() == WHEEL_DOWN)
820                 {
821                         int min_y = -(get_text_rows() *
822                                 text_height -
823                                 get_h() +
824                                 bottom_margin);
825                         text_y -= text_height;
826                         text_y = MAX(text_y, min_y);
827                         text_y = MIN(text_y, top_margin);
828                         update_scroll = 1;
829                 }
830                 else
831                 if(get_buttonpress() == RIGHT_BUTTON)
832                 {
833                         menu->activate_menu();
834                 }
835                 else
836                 {
837
838                 cursor_letter = get_cursor_letter(top_level->cursor_x, top_level->cursor_y);
839
840         //printf("BC_TextBox::button_press_event %d %d\n", __LINE__, cursor_letter);
841
842
843                 if(get_triple_click())
844                 {
845         //printf("BC_TextBox::button_press_event %d\n", __LINE__);
846                         line_selected = 1;
847                         select_line(highlight_letter1, highlight_letter2, cursor_letter);
848                         highlight_letter3 = highlight_letter1;
849                         highlight_letter4 = highlight_letter2;
850                         ibeam_letter = highlight_letter2;
851                         copy_selection(PRIMARY_SELECTION);
852                 }
853                 else
854                 if(get_double_click())
855                 {
856                         word_selected = 1;
857                         select_word(highlight_letter1, highlight_letter2, cursor_letter);
858                         highlight_letter3 = highlight_letter1;
859                         highlight_letter4 = highlight_letter2;
860                         ibeam_letter = highlight_letter2;
861                         copy_selection(PRIMARY_SELECTION);
862                 }
863                 else
864                 if(get_buttonpress() == MIDDLE_BUTTON)
865                 {
866                         highlight_letter3 = highlight_letter4 =
867                                 ibeam_letter = highlight_letter1 =
868                                 highlight_letter2 = cursor_letter;
869                         paste_selection(PRIMARY_SELECTION);
870                 }
871                 else
872                 {
873                         text_selected = 1;
874                         highlight_letter3 = highlight_letter4 =
875                                 ibeam_letter = highlight_letter1 =
876                                 highlight_letter2 = cursor_letter;
877                 }
878
879
880         // Handle scrolling by highlighting text
881                 if(text_selected || word_selected || line_selected)
882                 {
883                         set_repeat(top_level->get_resources()->scroll_repeat);
884                 }
885
886                 if(ibeam_letter < 0) ibeam_letter = 0;
887                 if(ibeam_letter > wtext_len) ibeam_letter = wtext_len;
888                 }
889
890                 draw(1);
891                 if(update_scroll && yscroll)
892                 {
893                         yscroll->update_length(get_text_rows(),
894                                 get_text_row(),
895                                 yscroll->get_handlelength(),
896                                 1);
897                 }
898                 handle_event();
899                 return 1;
900         }
901         else
902         if( active ) {
903                 if( suggestions_popup && (!yscroll || !yscroll->is_event_win())) {
904                         if( suggestions_popup->button_press_event() )
905                                 return suggestions_popup->handle_event();
906                 }
907                 else if( (top_level->get_resources()->textbox_focus_policy & CLICK_DEACTIVATE) )
908                         deactivate();
909         }
910
911         return 0;
912 }
913
914 int BC_TextBox::button_release_event()
915 {
916         if(active)
917         {
918                 hide_tooltip();
919                 if(text_selected || word_selected || line_selected)
920                 {
921                         text_selected = 0;
922                         word_selected = 0;
923                         line_selected = 0;
924
925 // Stop scrolling by highlighting text
926                         unset_repeat(top_level->get_resources()->scroll_repeat);
927                 }
928         }
929         return 0;
930 }
931
932 int BC_TextBox::cursor_motion_event()
933 {
934         int cursor_letter, letter1, letter2;
935         if(active)
936         {
937                 if(text_selected || word_selected || line_selected)
938                 {
939                         cursor_letter = get_cursor_letter(top_level->cursor_x,
940                                 top_level->cursor_y);
941
942 //printf("BC_TextBox::cursor_motion_event %d cursor_letter=%d\n", __LINE__, cursor_letter);
943
944                         if(line_selected)
945                         {
946                                 select_line(letter1, letter2, cursor_letter);
947                         }
948                         else
949                         if(word_selected)
950                         {
951                                 select_word(letter1, letter2, cursor_letter);
952                         }
953                         else
954                         if(text_selected)
955                         {
956                                 letter1 = letter2 = cursor_letter;
957                         }
958
959                         if(letter1 <= highlight_letter3)
960                         {
961                                 highlight_letter1 = letter1;
962                                 highlight_letter2 = highlight_letter4;
963                                 ibeam_letter = letter1;
964                         }
965                         else
966                         if(letter2 >= highlight_letter4)
967                         {
968                                 highlight_letter2 = letter2;
969                                 highlight_letter1 = highlight_letter3;
970                                 ibeam_letter = letter2;
971                         }
972
973                         copy_selection(PRIMARY_SELECTION);
974
975
976
977
978                         draw(1);
979                         return 1;
980                 }
981         }
982
983         return 0;
984 }
985
986 int BC_TextBox::activate()
987 {
988         if( !active ) {
989                 active = 1;
990                 top_level->set_active_subwindow(this);
991                 top_level->set_repeat(top_level->get_resources()->blink_rate);
992         }
993         draw(1);
994         return 0;
995 }
996
997 int BC_TextBox::deactivate()
998 {
999         if( active ) {
1000                 active = 0;
1001                 text_selected = word_selected = line_selected = 0;
1002                 top_level->set_active_subwindow(0);
1003                 top_level->unset_repeat(top_level->get_resources()->blink_rate);
1004         }
1005         draw(1);
1006         return 0;
1007 }
1008
1009 int BC_TextBox::repeat_event(int64_t duration)
1010 {
1011         int result = 0;
1012         int cursor_y = get_cursor_y();
1013         //int cursor_x = get_cursor_x();
1014
1015         if(duration == top_level->get_resources()->tooltip_delay &&
1016                 tooltip_text && tooltip_text[0] != 0 && highlighted)
1017         {
1018                 show_tooltip();
1019                 result = 1;
1020         }
1021
1022         if(duration == top_level->get_resources()->blink_rate &&
1023                 active &&
1024                 get_has_focus())
1025         {
1026 // don't flash if keypress
1027                 if(skip_cursor->get_difference() < 500)
1028                 {
1029 // printf("BC_TextBox::repeat_event 1 %lld %lld\n",
1030 // skip_cursor->get_difference(),
1031 // duration);
1032                         result = 1;
1033                 }
1034                 else
1035                 {
1036                         if(!(text_selected || word_selected || line_selected))
1037                         {
1038                                 draw_cursor();
1039                                 flash(1);
1040                         }
1041                         result = 1;
1042                 }
1043         }
1044
1045         if(duration == top_level->get_resources()->scroll_repeat &&
1046                 (text_selected || word_selected || line_selected))
1047         {
1048                 int difference = 0;
1049                 if(get_cursor_y() < top_margin)
1050                 {
1051                         difference = get_cursor_y() - top_margin ;
1052                 }
1053                 else
1054                 if(get_cursor_y() > get_h() - bottom_margin)
1055                 {
1056                         difference = get_cursor_y() -
1057                                 (get_h() - bottom_margin);
1058                 }
1059                 if(difference != 0) {
1060                         int min_y = -(get_text_rows() * text_height -
1061                                 get_h() + bottom_margin);
1062
1063                         text_y -= difference;
1064 // printf("BC_TextBox::repeat_event %d %d %d\n",
1065 // __LINE__,
1066 // text_y,
1067 // min_y);
1068                         text_y = MAX(min_y, text_y);
1069                         text_y = MIN(text_y, top_margin);
1070
1071                         draw(1);
1072                         motion_event();
1073                         result = 1;
1074                 }
1075
1076                 if(get_cursor_x() < left_margin)
1077                 {
1078                         int difference = left_margin - get_cursor_x();
1079
1080                         text_x += difference;
1081                         text_x = MIN(text_x, left_margin);
1082                         draw(1);
1083                         result = 1;
1084                 }
1085                 else if(get_cursor_x() > get_w() - right_margin)
1086                 {
1087                         int difference = get_cursor_x() - (get_w() - right_margin);
1088                         int new_text_x = text_x - difference;
1089
1090 // Get width of current row
1091                         int min_x = 0;
1092                         int row_width = 0;
1093                         int wtext_len = wtext_update();
1094                         int row_begin = 0;
1095                         int row_end = 0;
1096                         for(int i = 0, k = text_y; i < wtext_len; k += text_height)
1097                         {
1098                                 row_begin = i;
1099                                 while(wtext[i] != '\n' && i < wtext_len) {
1100                                         i++;
1101                                 }
1102                                 row_end = i;
1103                                 if(wtext[i] == '\n') i++;
1104
1105                                 if(cursor_y >= k && cursor_y < k + text_height) {
1106                                         row_width = get_text_width(font,
1107                                                 wtext + row_begin,
1108                                                 row_end - row_begin);
1109
1110                                         break;
1111                                 }
1112                         }
1113
1114                         min_x = -row_width + get_w() - left_margin - BCCURSORW;
1115                         new_text_x = MAX(new_text_x, min_x);
1116                         new_text_x = MIN(new_text_x, left_margin);
1117
1118                         if(new_text_x < text_x) text_x = new_text_x;
1119                         draw(1);
1120                         result = 1;
1121                 }
1122         }
1123
1124         return result;
1125 }
1126
1127 void BC_TextBox::default_keypress(int &dispatch_event, int &result)
1128 {
1129         int key = top_level->get_keypress(), len;
1130         if( (key == RETURN) || ( key >= 32 && key <= 255 ) ) {
1131                 wchar_t *wkeys = top_level->get_wkeystring(&len);
1132                 if( key == RETURN ) { wkeys[0] = '\n';  wkeys[1] = 0;  len = 1; }
1133                 insert_text(wkeys, len);
1134                 find_ibeam(1);
1135                 draw(1);
1136                 dispatch_event = 1;
1137                 result = 1;
1138         }
1139 }
1140
1141 int BC_TextBox::keypress_event()
1142 {
1143 // Result == 2 contents changed
1144 // Result == 1 trapped keypress
1145 // Result == 0 nothing
1146         int result = 0;
1147         int dispatch_event = 0;
1148
1149         if(!active || !enabled) return 0;
1150
1151         int wtext_len = wtext_update();
1152         last_keypress = get_keypress();
1153
1154         if( unicode_active >= 0 ) {
1155                 wchar_t wch = 0;
1156                 int wlen =  -1;
1157                 switch( last_keypress ) {
1158 //unicode active acitons
1159                 case RETURN: {
1160                         for( int i=highlight_letter1+1; i<highlight_letter2; ++i ) {
1161                                 int ch = nib(wtext[i]);
1162                                 if( ch < 0 ) return 1;
1163                                 wch = (wch<<4) + ch;
1164                         }
1165                         if( wch ) {
1166                                 dispatch_event = 1;
1167                                 unicode_active = -1;
1168                                 result = 1;
1169                                 wlen = 1;
1170                                 break;
1171                         } } // fall through
1172                 case ESC: {
1173                         unicode_active = -1;
1174                         result = 1;
1175                         wlen = 0;
1176                         break; }
1177                 case BACKSPACE: {
1178                         if(ibeam_letter > 0) {
1179                                 delete_selection(ibeam_letter - 1, ibeam_letter, wtext_len);
1180                                 highlight_letter2 = --ibeam_letter;
1181                                 if( highlight_letter1 >= highlight_letter2 )
1182                                         unicode_active = -1;
1183                         }
1184                         result = 1;
1185                         wlen = 0;
1186                         break; }
1187                 case '0': case '1': case '2': case '3': case '4':
1188                 case '5': case '6': case '7': case '8': case '9':
1189                 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
1190                 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': {
1191                         int n = nib(last_keypress);
1192                         wch = n < 10 ? '0'+n : 'A'+n-10;
1193                         result = 1;
1194                         wlen = 1;
1195                         break; }
1196 //normal actions
1197                 case TAB:
1198                 case LEFTTAB:
1199                         break;
1200 //ignored actions
1201                 default:
1202                         result = 1;
1203                         break;
1204                 }
1205                 if( wlen >= 0 ) {
1206                         insert_text(&wch, wlen);
1207                         find_ibeam(1);
1208                         if( unicode_active >= 0 )
1209                                 highlight_letter2 = ibeam_letter;
1210                         else
1211                                 highlight_letter1 = highlight_letter2 = 0;
1212                         draw(1);
1213                 }
1214         }
1215
1216         if( !result ) {
1217 //printf("BC_TextBox::keypress_event %d %x\n", __LINE__, last_keypress)
1218                 switch(last_keypress) {
1219                 case ESC: {
1220 // Deactivate the suggestions
1221                         if( suggestions_popup ) {
1222                                 no_suggestions();
1223                                 result = 1;
1224                         }
1225                         else {
1226                                 top_level->deactivate();
1227                                 result = 0;
1228                         }
1229                         break; }
1230
1231                 case RETURN: {
1232                         if( rows == 1 ) {
1233                                 highlight_letter1 = highlight_letter2 = 0;
1234                                 ibeam_letter = wtext_update();
1235                                 top_level->deactivate();
1236                                 dispatch_event = 1;
1237                                 result = 0;
1238                         }
1239                         else {
1240                                 default_keypress(dispatch_event, result);
1241                         }
1242                         break; }
1243
1244
1245 // Handle like a default keypress
1246                 case TAB: {
1247                         top_level->cycle_textboxes(1);
1248                         result = 1;
1249                         break; }
1250
1251                 case LEFTTAB: {
1252                         top_level->cycle_textboxes(-1);
1253                         result = 1;
1254                         break; }
1255
1256                 case LEFT: {
1257                         if(ibeam_letter > 0) {
1258                                 int old_ibeam_letter = ibeam_letter;
1259 // Single character
1260                                 if(!ctrl_down()) {
1261                                         ibeam_letter--;
1262                                 }
1263                                 else {
1264 // Word
1265                                         ibeam_letter--;
1266                                         while(ibeam_letter > 0 && isalnum(wtext[ibeam_letter - 1]))
1267                                                 ibeam_letter--;
1268                                 }
1269
1270 // Extend selection
1271                                 if(top_level->shift_down()) {
1272 // Initialize highlighting
1273                                         if(highlight_letter1 == highlight_letter2) {
1274                                                 highlight_letter1 = ibeam_letter;
1275                                                 highlight_letter2 = old_ibeam_letter;
1276                                         }
1277                                         else if(highlight_letter1 == old_ibeam_letter) {
1278 // Extend left highlight
1279                                                 highlight_letter1 = ibeam_letter;
1280                                         }
1281                                         else if(highlight_letter2 == old_ibeam_letter) {
1282 // Shrink right highlight
1283                                                 highlight_letter2 = ibeam_letter;
1284                                         }
1285                                 }
1286                                 else {
1287                                         highlight_letter1 = highlight_letter2 = ibeam_letter;
1288                                 }
1289
1290
1291                                 find_ibeam(1);
1292                                 if(keypress_draw) draw(1);
1293                         }
1294                         result = 1;
1295                         break; }
1296
1297                 case RIGHT: {
1298                         if(ibeam_letter < wtext_len) {
1299                                 int old_ibeam_letter = ibeam_letter;
1300 // Single character
1301                                 if(!ctrl_down()) {
1302                                         ibeam_letter++;
1303                                 }
1304                                 else {
1305 // Word
1306                                         while(ibeam_letter < wtext_len && isalnum(wtext[ibeam_letter++]));
1307                                 }
1308
1309
1310
1311 // Extend selection
1312                                 if(top_level->shift_down()) {
1313 // Initialize highlighting
1314                                         if(highlight_letter1 == highlight_letter2) {
1315                                                 highlight_letter1 = old_ibeam_letter;
1316                                                 highlight_letter2 = ibeam_letter;
1317                                         }
1318                                         else if(highlight_letter1 == old_ibeam_letter) {
1319 // Shrink left highlight
1320                                                 highlight_letter1 = ibeam_letter;
1321                                         }
1322                                         else if(highlight_letter2 == old_ibeam_letter) {
1323 // Expand right highlight
1324                                                 highlight_letter2 = ibeam_letter;
1325                                         }
1326                                 }
1327                                 else {
1328                                         highlight_letter1 = highlight_letter2 = ibeam_letter;
1329                                 }
1330
1331                                 find_ibeam(1);
1332                                 if(keypress_draw) draw(1);
1333                         }
1334                         result = 1;
1335                         break; }
1336
1337                 case UP: {
1338                         if( suggestions && suggestions_popup ) {
1339 // Pass to suggestions popup
1340 //printf("BC_TextBox::keypress_event %d\n", __LINE__);
1341                                 suggestions_popup->activate(1);
1342                                 suggestions_popup->keypress_event();
1343                                 result = 1;
1344                         }
1345                         else if(ibeam_letter > 0) {
1346 //printf("BC_TextBox::keypress_event 1 %d %d %d\n", ibeam_x, ibeam_y, ibeam_letter);
1347                                 int new_letter = get_cursor_letter2(ibeam_x + text_x,
1348                                         ibeam_y + text_y - text_height);
1349 //printf("BC_TextBox::keypress_event 2 %d %d %d\n", ibeam_x, ibeam_y, new_letter);
1350
1351 // Extend selection
1352                                 if(top_level->shift_down()) {
1353 // Initialize highlighting
1354                                         if(highlight_letter1 == highlight_letter2) {
1355                                                 highlight_letter1 = new_letter;
1356                                                 highlight_letter2 = ibeam_letter;
1357                                         }
1358                                         else if(highlight_letter1 == ibeam_letter) {
1359 // Expand left highlight
1360                                                 highlight_letter1 = new_letter;
1361                                         }
1362                                         else if(highlight_letter2 == ibeam_letter) {
1363 // Shrink right highlight
1364                                                 highlight_letter2 = new_letter;
1365                                         }
1366                                 }
1367                                 else
1368                                         highlight_letter1 = highlight_letter2 = new_letter;
1369
1370                                 if(highlight_letter1 > highlight_letter2) {
1371                                         int temp = highlight_letter1;
1372                                         highlight_letter1 = highlight_letter2;
1373                                         highlight_letter2 = temp;
1374                                 }
1375                                 ibeam_letter = new_letter;
1376
1377                                 find_ibeam(1);
1378                                 if(keypress_draw) draw(1);
1379                         }
1380                         result = 1;
1381                         break; }
1382
1383                 case PGUP: {
1384                         if(ibeam_letter > 0) {
1385                                 int new_letter = get_cursor_letter2(ibeam_x + text_x,
1386                                         ibeam_y + text_y - get_h());
1387
1388 // Extend selection
1389                                 if(top_level->shift_down()) {
1390 // Initialize highlighting
1391                                         if(highlight_letter1 == highlight_letter2) {
1392                                                 highlight_letter1 = new_letter;
1393                                                 highlight_letter2 = ibeam_letter;
1394                                         }
1395                                         else if(highlight_letter1 == ibeam_letter) {
1396 // Expand left highlight
1397                                                 highlight_letter1 = new_letter;
1398                                         }
1399                                         else if(highlight_letter2 == ibeam_letter) {
1400 // Shrink right highlight
1401                                                 highlight_letter2 = new_letter;
1402                                         }
1403                                 }
1404                                 else
1405                                         highlight_letter1 = highlight_letter2 = new_letter;
1406
1407                                 if(highlight_letter1 > highlight_letter2) {
1408                                         int temp = highlight_letter1;
1409                                         highlight_letter1 = highlight_letter2;
1410                                         highlight_letter2 = temp;
1411                                 }
1412                                 ibeam_letter = new_letter;
1413
1414                                 find_ibeam(1);
1415                                 if(keypress_draw) draw(1);
1416                         }
1417                         result = 1;
1418                         break; }
1419
1420                 case DOWN: {
1421 // printf("BC_TextBox::keypress_event %d %p %p\n",
1422 // __LINE__,
1423 // suggestions,
1424 // suggestions_popup);
1425                         if( suggestions && suggestions_popup ) {
1426 // Pass to suggestions popup
1427                                 suggestions_popup->activate(1);
1428                                 suggestions_popup->keypress_event();
1429                                 result = 1;
1430                         }
1431                         else
1432 //                      if(ibeam_letter > 0)
1433                         {
1434 // Extend selection
1435                                 int new_letter = get_cursor_letter2(ibeam_x + text_x,
1436                                         ibeam_y + text_y + text_height);
1437 //printf("BC_TextBox::keypress_event 10 %d\n", new_letter);
1438
1439                                 if(top_level->shift_down()) {
1440 // Initialize highlighting
1441                                         if(highlight_letter1 == highlight_letter2) {
1442                                                 highlight_letter1 = new_letter;
1443                                                 highlight_letter2 = ibeam_letter;
1444                                         }
1445                                         else if(highlight_letter1 == ibeam_letter) {
1446 // Shrink left highlight
1447                                                 highlight_letter1 = new_letter;
1448                                         }
1449                                         else if(highlight_letter2 == ibeam_letter) {
1450 // Expand right highlight
1451                                                 highlight_letter2 = new_letter;
1452                                         }
1453                                 }
1454                                 else
1455                                         highlight_letter1 = highlight_letter2 = new_letter;
1456
1457                                 if(highlight_letter1 > highlight_letter2) {
1458                                         int temp = highlight_letter1;
1459                                         highlight_letter1 = highlight_letter2;
1460                                         highlight_letter2 = temp;
1461                                 }
1462                                 ibeam_letter = new_letter;
1463
1464                                 find_ibeam(1);
1465                                 if(keypress_draw) draw(1);
1466
1467 //printf("BC_TextBox::keypress_event 20 %d\n", ibeam_letter);
1468                         }
1469                         result = 1;
1470                         break; }
1471
1472                 case PGDN: {
1473 // Extend selection
1474                         int new_letter = get_cursor_letter2(ibeam_x + text_x,
1475                                 ibeam_y + text_y + get_h());
1476 //printf("BC_TextBox::keypress_event 10 %d\n", new_letter);
1477
1478                         if(top_level->shift_down()) {
1479 // Initialize highlighting
1480                                 if(highlight_letter1 == highlight_letter2) {
1481                                         highlight_letter1 = new_letter;
1482                                         highlight_letter2 = ibeam_letter;
1483                                 }
1484                                 else if(highlight_letter1 == ibeam_letter) {
1485 // Shrink left highlight
1486                                         highlight_letter1 = new_letter;
1487                                 }
1488                                 else if(highlight_letter2 == ibeam_letter) {
1489 // Expand right highlight
1490                                         highlight_letter2 = new_letter;
1491                                 }
1492                         }
1493                         else
1494                                 highlight_letter1 = highlight_letter2 = new_letter;
1495
1496                         if(highlight_letter1 > highlight_letter2) {
1497                                 int temp = highlight_letter1;
1498                                 highlight_letter1 = highlight_letter2;
1499                                 highlight_letter2 = temp;
1500                         }
1501                         ibeam_letter = new_letter;
1502
1503                         find_ibeam(1);
1504                         if(keypress_draw) draw(1);
1505
1506 //printf("BC_TextBox::keypress_event 20 %d\n", ibeam_letter);
1507                         result = 1;
1508                         break; }
1509
1510                 case END: {
1511                         no_suggestions();
1512
1513                         int old_ibeam_letter = ibeam_letter;
1514
1515                         while(ibeam_letter < wtext_len && wtext[ibeam_letter] != '\n')
1516                                 ibeam_letter++;
1517
1518                         if(top_level->shift_down()) {
1519 // Begin selection
1520                                 if(highlight_letter1 == highlight_letter2) {
1521                                         highlight_letter2 = ibeam_letter;
1522                                         highlight_letter1 = old_ibeam_letter;
1523                                 }
1524                                 else if(highlight_letter1 == old_ibeam_letter) {
1525 // Shrink selection
1526                                         highlight_letter1 = highlight_letter2;
1527                                         highlight_letter2 = ibeam_letter;
1528                                 }
1529                                 else if(highlight_letter2 == old_ibeam_letter) {
1530 // Extend selection
1531                                         highlight_letter2 = ibeam_letter;
1532                                 }
1533                         }
1534                         else
1535                                 highlight_letter1 = highlight_letter2 = ibeam_letter;
1536
1537                         find_ibeam(1);
1538                         if(keypress_draw) draw(1);
1539                         result = 1;
1540                         break; }
1541
1542                 case HOME: {
1543                         no_suggestions();
1544
1545                         int old_ibeam_letter = ibeam_letter;
1546
1547                         while(ibeam_letter > 0 && wtext[ibeam_letter - 1] != '\n')
1548                                 ibeam_letter--;
1549
1550                         if(top_level->shift_down())
1551                         {
1552 // Begin selection
1553                                 if(highlight_letter1 == highlight_letter2)
1554                                 {
1555                                         highlight_letter2 = old_ibeam_letter;
1556                                         highlight_letter1 = ibeam_letter;
1557                                 }
1558                                 else
1559 // Extend selection
1560                                 if(highlight_letter1 == old_ibeam_letter)
1561                                 {
1562                                         highlight_letter1 = ibeam_letter;
1563                                 }
1564                                 else
1565 // Shrink selection
1566                                 if(highlight_letter2 == old_ibeam_letter)
1567                                 {
1568                                         highlight_letter2 = highlight_letter1;
1569                                         highlight_letter1 = ibeam_letter;
1570                                 }
1571                         }
1572                         else
1573                                 highlight_letter1 = highlight_letter2 = ibeam_letter;
1574
1575                         find_ibeam(1);
1576                         if(keypress_draw) draw(1);
1577                         result = 1;
1578                         break; }
1579
1580                 case BACKSPACE: {
1581                         no_suggestions();
1582
1583                         if(highlight_letter1 == highlight_letter2) {
1584                                 if(ibeam_letter > 0) {
1585                                         delete_selection(ibeam_letter - 1, ibeam_letter, wtext_len);
1586                                         ibeam_letter--;
1587                                 }
1588                         }
1589                         else {
1590                                 delete_selection(highlight_letter1, highlight_letter2, wtext_len);
1591                                 highlight_letter2 = ibeam_letter = highlight_letter1;
1592                         }
1593
1594                         find_ibeam(1);
1595                         if(keypress_draw) draw(1);
1596                         dispatch_event = 1;
1597                         result = 1;
1598                         break; }
1599
1600                 case DELETE: {
1601 //printf("BC_TextBox::keypress_event %d\n", __LINE__);
1602                         if(highlight_letter1 == highlight_letter2) {
1603                                 if(ibeam_letter < wtext_len) {
1604                                         delete_selection(ibeam_letter, ibeam_letter + 1, wtext_len);
1605                                 }
1606                         }
1607                         else {
1608                                 delete_selection(highlight_letter1, highlight_letter2, wtext_len);
1609                                 highlight_letter2 = ibeam_letter = highlight_letter1;
1610                         }
1611
1612                         find_ibeam(1);
1613                         if(keypress_draw) draw(1);
1614                         dispatch_event = 1;
1615                         result = 1;
1616                         break; }
1617
1618                 default: {
1619                         if( ctrl_down() ) {
1620                                 switch( get_keypress() ) {
1621                                 case 'c': case 'C': {
1622                                         result = copy(0);
1623                                         break; }
1624                                 case 'v': case 'V': {
1625                                         result = paste(0);
1626                                         dispatch_event = 1;
1627                                         break; }
1628                                 case 'x': case 'X': {
1629                                         result = cut(0);
1630                                         dispatch_event = 1;
1631                                         break; }
1632                                 case 'u': case 'U': {
1633                                         if( shift_down() ) {
1634                                                 unicode_active = ibeam_letter;
1635                                                 wchar_t wkey = 'U';
1636                                                 insert_text(&wkey, 1);
1637                                                 find_ibeam(1);
1638                                                 highlight_letter1 = unicode_active;
1639                                                 highlight_letter2 = ibeam_letter;
1640                                                 draw(1);
1641                                                 result = 1;
1642                                         }
1643                                         break; }
1644                                 }
1645                                 break;
1646                         }
1647
1648                         default_keypress(dispatch_event, result);
1649                         break; }
1650                 }
1651         }
1652
1653         if(result) skip_cursor->update();
1654         if(dispatch_event && handle_event())
1655                 result = 1;
1656
1657         return result;
1658 }
1659
1660
1661 int BC_TextBox::cut(int do_update)
1662 {
1663         if( highlight_letter1 != highlight_letter2 ) {
1664                 int wtext_len = wtext_update();
1665                 copy_selection(SECONDARY_SELECTION);
1666                 delete_selection(highlight_letter1, highlight_letter2, wtext_len);
1667                 highlight_letter2 = ibeam_letter = highlight_letter1;
1668         }
1669
1670         find_ibeam(1);
1671         if( keypress_draw )
1672                 draw(1);
1673         
1674         if( do_update ) {
1675                 skip_cursor->update();
1676                 handle_event();
1677         }
1678         return 1;
1679 }
1680
1681 int BC_TextBox::copy(int do_update)
1682 {
1683         int result = 0;
1684         if( highlight_letter1 != highlight_letter2 ) {
1685                 copy_selection(SECONDARY_SELECTION);
1686                 result = 1;
1687                 if( do_update ) {
1688                         skip_cursor->update();
1689                 }
1690         }
1691         return result;
1692 }
1693
1694 int BC_TextBox::paste(int do_update)
1695 {
1696         paste_selection(SECONDARY_SELECTION);
1697         find_ibeam(1);
1698         if( keypress_draw )
1699                 draw(1);
1700         if( do_update ) {
1701                 skip_cursor->update();
1702                 handle_event();
1703         }
1704         return 1;
1705 }
1706
1707
1708 int BC_TextBox::uses_text()
1709 {
1710         return 1;
1711 }
1712
1713
1714 void BC_TextBox::delete_selection(int letter1, int letter2, int wtext_len)
1715 {
1716         int i, j;
1717         for(i=letter1, j=letter2; j<wtext_len; i++, j++) {
1718                 wtext[i] = wtext[j];
1719         }
1720         wtext[i] = 0;
1721         wlen = i;
1722
1723         do_separators(1);
1724 }
1725
1726 void BC_TextBox::insert_text(const wchar_t *wcp, int len)
1727 {
1728         if( len < 0 ) len = wcslen(wcp);
1729         int wtext_len = wtext_update();
1730         if( unicode_active < 0 && highlight_letter1 < highlight_letter2 ) {
1731                 delete_selection(highlight_letter1, highlight_letter2, wtext_len);
1732                 highlight_letter2 = ibeam_letter = highlight_letter1;
1733                 wtext_len = wtext_update();
1734         }
1735
1736         int i, j;
1737         for(i=wtext_len-1, j=wtext_len+len-1; i>=ibeam_letter; i--, j--) {
1738                 if( j >= wsize ) continue;
1739                 wtext[j] = wtext[i];
1740         }
1741
1742         for(i = ibeam_letter, j = 0; j < len; j++, i++) {
1743                 if( i >= wsize ) break;
1744                 wtext[i] = wcp[j];
1745         }
1746         if( (wlen+=len) > wsize ) wlen = wsize;
1747         if( (ibeam_letter+=len) > wsize ) ibeam_letter = wsize;
1748         wtext[wlen] = 0;  // wtext allocated wsize+1
1749         do_separators(0);
1750 }
1751
1752 int BC_TextBox::is_separator(const char *txt, int i)
1753 {
1754         if( i != 0 || separators[0] != '+' ) return !isalnum(txt[i]);
1755         return txt[0] != '+' && txt[0] != '-' && !isalnum(txt[0]);
1756 }
1757
1758 // used for time entry
1759 void BC_TextBox::do_separators(int ibeam_left)
1760 {
1761         if(separators)
1762         {
1763 // Remove separators from text
1764                 int wtext_len = wtext_update();
1765                 for(int i = 0; i < wtext_len; ) {
1766                         if( !iswalnum(wtext[i]) ) {
1767                                 for(int j = i; j < wtext_len - 1; j++)
1768                                         wtext[j] = wtext[j + 1];
1769                                 if(!ibeam_left && i < ibeam_letter) ibeam_letter--;
1770                                 wlen = --wtext_len;
1771                                 continue;
1772                         }
1773                         ++i;
1774                 }
1775                 wtext[wtext_len] = 0;
1776
1777
1778
1779
1780
1781 // Insert separators into text
1782                 int separator_len = strlen(separators);
1783                 for(int i = 0; i < separator_len; i++) {
1784                         if(i < wtext_len) {
1785 // Insert a separator
1786                                 if( is_separator(separators,i) ) {
1787                                         for(int j = wtext_len; j >= i; j--) {
1788                                                 wtext[j + 1] = wtext[j];
1789                                         }
1790                                         if(!ibeam_left && i < ibeam_letter) ibeam_letter++;
1791                                         ++wtext_len;
1792                                         wtext[i] = separators[i];
1793                                 }
1794                         }
1795                         else {
1796                                 wtext[i] = separators[i];
1797                         }
1798                 }
1799
1800 // Truncate text
1801                 wtext[separator_len] = 0;
1802                 wlen = separator_len;
1803         }
1804 }
1805
1806 int BC_TextBox::get_x_position(int i, int start)
1807 {
1808         return get_text_width(font, &wtext[start], i - start);
1809 }
1810
1811 void BC_TextBox::get_ibeam_position(int &x, int &y)
1812 {
1813         int i, row_begin, row_end;
1814         int wtext_len = wtext_update();
1815         x = y = 0;
1816
1817         for( i=0; i<wtext_len; ) {
1818                 row_begin = i;
1819                 for(; i<wtext_len && wtext[i]!='\n'; i++);
1820                 row_end = i;
1821
1822                 if( ibeam_letter >= row_begin && ibeam_letter <= row_end ) {
1823                         x = get_x_position(ibeam_letter, row_begin);
1824 //printf("BC_TextBox::get_ibeam_position %d %d %d %d %d\n", ibeam_letter, row_begin, row_end, x, y);
1825                         return;
1826                 }
1827
1828                 if( i < wtext_len && wtext[i] == '\n' ) {
1829                         i++;
1830                         y += text_height;
1831                 }
1832         }
1833 //printf("BC_TextBox::get_ibeam_position 10 %d %d\n", x, y);
1834
1835         x = 0;
1836         return;
1837 }
1838
1839 void BC_TextBox::set_text_row(int row)
1840 {
1841         text_x = left_margin;
1842         text_y = -(row * text_height) + top_margin;
1843         draw(1);
1844 }
1845
1846 int BC_TextBox::get_text_row()
1847 {
1848         return -(text_y - top_margin) / text_height;
1849 }
1850
1851 void BC_TextBox::find_ibeam(int dispatch_event)
1852 {
1853         int x, y;
1854         int old_x = text_x, old_y = text_y;
1855
1856         get_ibeam_position(x, y);
1857
1858         if(left_margin + text_x + x >= get_w() - right_margin - BCCURSORW)
1859         {
1860                 text_x = -(x - (get_w() - get_w() / 4)) + left_margin;
1861                 if(text_x > left_margin) text_x = left_margin;
1862         }
1863         else
1864         if(left_margin + text_x + x < left_margin)
1865         {
1866                 text_x = -(x - (get_w() / 4)) + left_margin;
1867                 if(text_x > left_margin) text_x = left_margin;
1868         }
1869
1870         int text_row = y / text_height;
1871         if( text_row < rows ) text_y = top_margin;
1872
1873         int pix_rows = get_h() - bottom_margin - (y + text_y);
1874         if( pix_rows < text_height )
1875                 text_y -= text_height * ((2*text_height-1-pix_rows) / text_height);
1876
1877         pix_rows = y + text_y - top_margin;
1878         if( pix_rows < 0 ) {
1879                 text_y += text_height * ((text_height-1-pix_rows) / text_height);
1880                 if( text_y > top_margin ) text_y = top_margin;
1881         }
1882
1883         if(dispatch_event && (old_x != text_x || old_y != text_y)) motion_event();
1884 }
1885
1886 // New algorithm
1887 int BC_TextBox::get_cursor_letter(int cursor_x, int cursor_y)
1888 {
1889         int i, j, k, row_begin, row_end, result = 0, done = 0;
1890         int column1, column2;
1891         int got_visible_row = 0;
1892
1893 // Select complete row if cursor above the window
1894 //printf("BC_TextBox::get_cursor_letter %d %d\n", __LINE__, text_y);
1895         if(cursor_y < text_y - text_height)
1896         {
1897                 result = 0;
1898                 done = 1;
1899         }
1900
1901         int wtext_len = wtext_update();
1902
1903         for(i=0, k=text_y; i<wtext_len && k<get_h() && !done; k+=text_height) {
1904 // Simulate drawing of 1 row
1905                 row_begin = i;
1906                 for(j = 0; wtext[i]!='\n' && i<wtext_len; i++);
1907                 row_end = i;
1908
1909 // check visibility
1910                 int first_visible_row = 0;
1911                 int last_visible_row = 0;
1912                 if( k+text_height > top_margin && !got_visible_row) {
1913                         first_visible_row = 1;
1914                         got_visible_row = 1;
1915                 }
1916
1917                 if( (k+text_height >= get_h() - bottom_margin ||
1918                         (row_end >= wtext_len && k < get_h() - bottom_margin &&
1919                                 k + text_height > 0)) )
1920                         last_visible_row = 1;
1921
1922 // Cursor is inside vertical range of row
1923                 if((cursor_y >= top_margin &&
1924                         cursor_y < get_h() - bottom_margin &&
1925                         cursor_y >= k && cursor_y < k + text_height) ||
1926 // Cursor is above 1st row
1927                         (cursor_y < k + text_height && first_visible_row) ||
1928 // Cursor is below last row
1929                         (cursor_y >= k && last_visible_row))
1930                 {
1931                         column1 = column2 = 0;
1932                         for(j = row_begin; j<wsize && j<=row_end && !done; j++) {
1933                                 column2 = get_text_width(font, &wtext[row_begin], j-row_begin) + text_x;
1934                                 if((column2 + column1) / 2 >= cursor_x) {
1935                                         result = j - 1;
1936                                         done = 1;
1937 // printf("BC_TextBox::get_cursor_letter %d %d %d %d\n",
1938 // __LINE__, result, first_visible_row, last_visible_row);
1939                                 }
1940                                 column1 = column2;
1941                         }
1942
1943                         if(!done) {
1944                                 result = row_end;
1945                                 done = 1;
1946                         }
1947                 }
1948
1949                 if(wtext[i] == '\n') i++;
1950
1951 // Select complete row if last visible & cursor is below window
1952                 if(last_visible_row && cursor_y > k + text_height * 2)
1953                         result = row_end;
1954
1955                 if(i >= wtext_len && !done) {
1956                         result = wtext_len;
1957                 }
1958         }
1959
1960
1961 // printf("BC_TextBox::get_cursor_letter %d cursor_y=%d k=%d h=%d %d %d\n",
1962 //  __LINE__, cursor_y, k, get_h(), first_visible_row, last_visible_row);
1963         if(result < 0) result = 0;
1964         if(result > wtext_len) {
1965 //printf("BC_TextBox::get_cursor_letter %d\n", __LINE__);
1966                 result = wtext_len;
1967         }
1968
1969         return result;
1970 }
1971
1972 // Old algorithm
1973 int BC_TextBox::get_cursor_letter2(int cursor_x, int cursor_y)
1974 {
1975         int i, j, k, row_begin, row_end, result = 0, done = 0;
1976         int column1, column2;
1977         int wtext_len = wtext_update();
1978
1979         if(cursor_y < text_y) {
1980                 result = 0;
1981                 done = 1;
1982         }
1983
1984         for(i = 0, k = text_y; i < wtext_len && !done; k += text_height) {
1985                 row_begin = i;
1986                 for(; wtext[i] != '\n' && i < wtext_len; i++);
1987                 row_end = i;
1988
1989                 if(cursor_y >= k && cursor_y < k + text_height) {
1990                         column1 = column2 = 0;
1991                         for(j = 0; j <= row_end - row_begin && !done; j++) {
1992                                 column2 = get_text_width(font, &wtext[row_begin], j) + text_x;
1993                                 if((column2 + column1) / 2 >= cursor_x) {
1994                                         result = row_begin + j - 1;
1995                                         done = 1;
1996                                 }
1997                                 column1 = column2;
1998                         }
1999                         if(!done)
2000                         {
2001                                 result = row_end;
2002                                 done = 1;
2003                         }
2004                 }
2005                 if(wtext[i] == '\n') i++;
2006
2007                 if(i >= wtext_len && !done) {
2008                         result = wtext_len;
2009                 }
2010         }
2011         if(result < 0) result = 0;
2012         if(result > wtext_len) result = wtext_len;
2013         return result;
2014 }
2015
2016
2017 void BC_TextBox::select_word(int &letter1, int &letter2, int ibeam_letter)
2018 {
2019         int wtext_len = wtext_update();
2020         letter1 = letter2 = ibeam_letter;
2021         if( letter1 < 0 ) letter1 = 0;
2022         if( letter2 < 0 ) letter2 = 0;
2023         if( letter1 > wtext_len ) letter1 = wtext_len;
2024         if( letter2 > wtext_len ) letter2 = wtext_len;
2025         if( !wtext_len ) return;
2026         for( int i=letter1; i>=0 && iswalnum(wtext[i]); --i ) letter1 = i;
2027         for( int i=letter2; i<wtext_len && iswalnum(wtext[i]); ) letter2 = ++i;
2028         if( letter2 < wtext_len && wtext[letter2] == ' ' ) ++letter2;
2029 }
2030
2031
2032 void BC_TextBox::select_line(int &letter1, int &letter2, int ibeam_letter)
2033 {
2034         int wtext_len = wtext_update();
2035         letter1 = letter2 = ibeam_letter;
2036         if( letter1 < 0 ) letter1 = 0;
2037         if( letter2 < 0 ) letter2 = 0;
2038         if( letter1 > wtext_len ) letter1 = wtext_len;
2039         if( letter2 > wtext_len ) letter2 = wtext_len;
2040         if( !wtext_len ) return;
2041         for( int i=letter1; i>=0 && wtext[i]!='\n'; --i ) letter1 = i;
2042         for( int i=letter2; i<wtext_len && wtext[i]!='\n'; ) letter2 = ++i;
2043 }
2044
2045 void BC_TextBox::copy_selection(int clipboard_num)
2046 {
2047         int wtext_len = wtext_update();
2048         if(!wtext_len) return;
2049
2050         if(highlight_letter1 >= wtext_len || highlight_letter2 > wtext_len ||
2051                 highlight_letter1 < 0 || highlight_letter2 < 0 ||
2052                 highlight_letter2 - highlight_letter1 <= 0) return;
2053         int clip_len = highlight_letter2 - highlight_letter1;
2054 //printf(" BC_TextBox::copy_selection %d %d %d\n",highlight_letter1, highlight_letter2, clip_len);
2055         char ctext[4*clip_len+4];
2056         clip_len = text_update(&wtext[highlight_letter1],clip_len, ctext,4*clip_len+4);
2057         to_clipboard(ctext, clip_len, clipboard_num);
2058         selection_active = 1;
2059 }
2060
2061 int BC_TextBox::selection_clear_event()
2062 {
2063         if( !is_event_win() ) return 0;
2064         selection_active = 0;
2065         draw(1);
2066         return 1;
2067 }
2068
2069 void BC_TextBox::paste_selection(int clipboard_num)
2070 {
2071         int len = clipboard_len(clipboard_num);
2072         if( len > 0 )
2073         {
2074                 char cstring[len];  wchar_t wstring[len];
2075                 from_clipboard(cstring, len, clipboard_num);  --len;
2076 //printf("BC_TextBox::paste_selection %d '%*.*s'\n",len,len,len,cstring);
2077                 len = BC_Resources::encode(BC_Resources::encoding, BC_Resources::wide_encoding,
2078                         cstring,len, (char *)wstring,(len+1)*sizeof(wchar_t)) / sizeof(wchar_t);
2079                 insert_text(wstring, len);
2080         }
2081 }
2082
2083 void BC_TextBox::set_keypress_draw(int value)
2084 {
2085         keypress_draw = value;
2086 }
2087
2088 int BC_TextBox::get_last_keypress()
2089 {
2090         return last_keypress;
2091 }
2092
2093 int BC_TextBox::get_ibeam_letter()
2094 {
2095         return ibeam_letter;
2096 }
2097
2098 void BC_TextBox::set_ibeam_letter(int number, int redraw)
2099 {
2100         this->ibeam_letter = number;
2101         if(redraw)
2102         {
2103                 draw(1);
2104         }
2105 }
2106
2107 void BC_TextBox::set_separators(const char *separators)
2108 {
2109         this->separators = (char*)separators;
2110 }
2111
2112 int BC_TextBox::get_rows()
2113 {
2114         return rows;
2115 }
2116
2117
2118
2119
2120
2121
2122
2123 BC_TextBoxSuggestions::BC_TextBoxSuggestions(BC_TextBox *text_box, int x, int y)
2124  : BC_ListBox(x, y, text_box->get_w(), 200, LISTBOX_TEXT,
2125         text_box->suggestions, 0, 0, 1, 0, 1)
2126 {
2127         this->text_box = text_box;
2128         set_use_button(0);
2129         set_justify(LISTBOX_LEFT);
2130 }
2131
2132 BC_TextBoxSuggestions::~BC_TextBoxSuggestions()
2133 {
2134 }
2135
2136 int BC_TextBoxSuggestions::handle_event()
2137 {
2138         char *current_suggestion = 0;
2139         BC_ListBoxItem *item = get_selection(0, 0);
2140 //printf("BC_TextBoxSuggestions::handle_event %d\n", __LINE__);
2141         if(item && (current_suggestion=item->get_text()) != 0)
2142         {
2143                 int col = text_box->suggestion_column;
2144                 int len = BCTEXTLEN-1 - col;
2145                 char *cp = &text_box->text[col];
2146 //printf("BC_TextBoxSuggestions::handle_event %d\n", __LINE__);
2147                 strncpy(cp, current_suggestion, len);
2148 //printf("BC_TextBoxSuggestions::handle_event %d\n", __LINE__);
2149                 if( (col=strlen(current_suggestion)) >= len )
2150                         cp[len-1] = 0;
2151                 text_box->dirty = 1;
2152         }
2153
2154
2155 //printf("BC_TextBoxSuggestions::handle_event %d\n", __LINE__);
2156         text_box->highlight_letter1 =
2157                 text_box->highlight_letter2 =
2158                 text_box->ibeam_letter = text_box->tstrlen();
2159         text_box->wtext_update();
2160         text_box->draw(1);
2161         text_box->handle_event();
2162 //printf("BC_TextBoxSuggestions::handle_event %d\n", __LINE__);
2163         return 1;
2164 }
2165
2166
2167 BC_ScrollTextBox::BC_ScrollTextBox(BC_WindowBase *parent_window,
2168         int x, int y, int w, int rows,
2169         const char *default_text, int default_size)
2170 {
2171         this->parent_window = parent_window;
2172         this->x = x;
2173         this->y = y;
2174         this->w = w;
2175         this->rows = rows;
2176         xscroll = 0;  yscroll = 0;
2177         this->default_text = default_text;
2178         this->default_wtext = 0;
2179         this->default_size = default_size;
2180 }
2181
2182 BC_ScrollTextBox::BC_ScrollTextBox(BC_WindowBase *parent_window,
2183         int x, int y, int w, int rows,
2184         const wchar_t *default_wtext, int default_size)
2185 {
2186         this->parent_window = parent_window;
2187         this->x = x;
2188         this->y = y;
2189         this->w = w;
2190         this->rows = rows;
2191         xscroll = 0;  yscroll = 0;
2192         this->default_text = 0;
2193         this->default_wtext = default_wtext;
2194         this->default_size = default_size;
2195 }
2196
2197 BC_ScrollTextBox::~BC_ScrollTextBox()
2198 {
2199         delete xscroll;
2200         delete yscroll;
2201         if(text) {
2202                 text->gui = 0;
2203                 delete text;
2204         }
2205 }
2206
2207 void BC_ScrollTextBox::create_objects()
2208 {
2209 // Must be created first
2210         parent_window->add_subwindow(text = default_wtext ?
2211                 new BC_ScrollTextBoxText(this, default_wtext) :
2212                 new BC_ScrollTextBoxText(this, default_text));
2213         set_text_row(0);
2214 }
2215
2216 void BC_ScrollTextBox::set_text(char *text, int isz)
2217 {
2218         this->text->set_text(text, isz);
2219         update_scrollbars();
2220 }
2221
2222 int BC_ScrollTextBox::set_text_row(int n)
2223 {
2224         text->set_text_row(n);
2225         update_scrollbars();
2226         return 1;
2227 }
2228
2229 void BC_ScrollTextBox::update(const char *text)
2230 {
2231         this->text->update(text);
2232         update_scrollbars();
2233 }
2234
2235 void BC_ScrollTextBox::update(const wchar_t *wtext)
2236 {
2237         this->text->update(wtext);
2238         update_scrollbars();
2239 }
2240
2241 void BC_ScrollTextBox::reposition_window(int x, int y, int w, int rows)
2242 {
2243         this->x = x;
2244         this->y = y;
2245         this->w = w;
2246         this->rows = rows;
2247         update_scrollbars();
2248 }
2249
2250 int BC_ScrollTextBox::button_press_event()
2251 {
2252         return text->BC_TextBox::button_press_event();
2253 }
2254 int BC_ScrollTextBox::button_release_event()
2255 {
2256         return text->BC_TextBox::button_release_event();
2257 }
2258
2259 int BC_ScrollTextBox::get_h() { return text->get_h(); }
2260 const char *BC_ScrollTextBox::get_text() { return text->get_text(); }
2261 const wchar_t *BC_ScrollTextBox::get_wtext() { return text->get_wtext(); }
2262
2263 int BC_ScrollTextBox::get_buttonpress()
2264 {
2265         return text->BC_TextBox::get_buttonpress();
2266 }
2267 void BC_ScrollTextBox::wset_selection(int char1, int char2, int ibeam)
2268 {
2269         text->wset_selection(char1, char2, ibeam);
2270 }
2271 void BC_ScrollTextBox::set_selection(int char1, int char2, int ibeam)
2272 {
2273         text->set_selection(char1, char2, ibeam);
2274 }
2275 int BC_ScrollTextBox::get_ibeam_letter()
2276 {
2277         return text->get_ibeam_letter();
2278 }
2279 int BC_ScrollTextBox::get_x_pos()
2280 {
2281         return text->left_margin - text->get_text_x();
2282 }
2283 void BC_ScrollTextBox::set_x_pos(int x)
2284 {
2285         text->set_text_x(text->left_margin - x);
2286         text->draw(1);
2287 }
2288
2289 BC_ScrollTextBoxText::BC_ScrollTextBoxText(BC_ScrollTextBox *gui, const char *text)
2290  : BC_TextBox(gui->x, gui->y,
2291         gui->w - get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w(),
2292         gui->rows, gui->default_size, (char*)text, 1, MEDIUMFONT)
2293 {
2294         this->gui = gui;
2295 }
2296
2297 BC_ScrollTextBoxText::BC_ScrollTextBoxText(BC_ScrollTextBox *gui, const wchar_t *wtext)
2298  : BC_TextBox(gui->x, gui->y,
2299         gui->w - get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w(),
2300         gui->rows, gui->default_size, (wchar_t*)wtext, 1, MEDIUMFONT)
2301 {
2302         this->gui = gui;
2303 }
2304
2305 BC_ScrollTextBoxText::~BC_ScrollTextBoxText()
2306 {
2307         if(gui)
2308         {
2309                 gui->text = 0;
2310                 delete gui;
2311         }
2312 }
2313
2314 void BC_ScrollTextBox::update_scrollbars()
2315 {
2316         int view_w = w, view_rows = rows;
2317         int view_h = BC_TextBox::calculate_row_h(view_rows, parent_window);
2318         int text_rows = text->get_text_rows();
2319         int text_width = parent_window->get_text_width(text->font, text->get_wtext());
2320         BC_Resources *resources = parent_window->get_resources();
2321         int need_xscroll = 0, need_yscroll = 0;
2322
2323 // Create scrollbars as needed
2324         int resize = 1;
2325         while( resize ) {
2326                 resize = 0;
2327                 if( !need_xscroll && (text->get_text_x() != text->left_margin ||
2328                       text_width >= view_w - text->left_margin - text->right_margin) ) {
2329                         resize = need_xscroll = 1;
2330                         view_h -= resources->hscroll_data[SCROLL_HANDLE_UP]->get_h();
2331                         view_rows = BC_TextBox::pixels_to_rows(parent_window, text->font, view_h);
2332                 }
2333                 if( !need_yscroll && (text->get_text_y() != text->top_margin ||
2334                       text_rows > view_rows) ) {
2335                         resize = need_yscroll = 1;
2336                         view_w -= resources->vscroll_data[SCROLL_HANDLE_UP]->get_w();
2337                 }
2338         }
2339
2340         if( !need_xscroll && xscroll ) {
2341                 text->yscroll = 0;
2342                 delete xscroll;  xscroll = 0;
2343         }
2344         if( !need_yscroll && yscroll ) {
2345                 text->yscroll = 0;
2346                 delete yscroll;  yscroll = 0;
2347         }
2348
2349         if( view_rows != text->get_rows() || view_w != text->get_w() ) {
2350                 text->reposition_window(x, y, view_w, view_rows);
2351         }
2352         if( need_xscroll && !xscroll ) {
2353                 xscroll = new BC_ScrollTextBoxXScroll(this);
2354                 parent_window->add_subwindow(xscroll);
2355                 text->xscroll = xscroll;
2356                 xscroll->bound_to = text;
2357                 xscroll->show_window();
2358         }
2359         if( need_yscroll && !yscroll ) {
2360                 yscroll = new BC_ScrollTextBoxYScroll(this);
2361                 parent_window->add_subwindow(yscroll);
2362                 text->yscroll = yscroll;
2363                 yscroll->bound_to = text;
2364                 yscroll->show_window();
2365         }
2366         if( xscroll ) {
2367                 xscroll->reposition_window(x, y + text->get_h(), view_w);
2368                 int xpos = get_x_pos();
2369                 if( xpos != xscroll->get_value() )
2370                         xscroll->update_value(xpos);
2371                 if( text_width != xscroll->get_length() ||
2372                     view_w != xscroll->get_handlelength() )
2373                         xscroll->update_length(text_width, xpos, view_w, 0);
2374         }
2375         if( yscroll ) {
2376                 yscroll->reposition_window(x + w - yscroll->get_span(), y, text->get_h());
2377                 int text_row = text->get_text_row();
2378                 if( text_row != yscroll->get_value() )
2379                         yscroll->update_value(text_row);
2380                 if( text_rows != yscroll->get_length() ||
2381                     view_rows != yscroll->get_handlelength() )
2382                         yscroll->update_length(text_rows, text_row, view_rows, 0);
2383         }
2384 }
2385
2386 int BC_ScrollTextBoxText::handle_event()
2387 {
2388         gui->update_scrollbars();
2389         return gui->handle_event();
2390 }
2391
2392 int BC_ScrollTextBoxText::motion_event()
2393 {
2394         gui->update_scrollbars();
2395         return 1;
2396 }
2397
2398 BC_ScrollTextBoxXScroll::BC_ScrollTextBoxXScroll(BC_ScrollTextBox *gui)
2399  : BC_ScrollBar(gui->x, gui->y + gui->text->get_h(), SCROLL_HORIZ + SCROLL_STRETCH,
2400         gui->text->get_w(), gui->text->get_text_width(MEDIUMFONT, gui->get_wtext()),
2401         0, gui->w)
2402 {
2403         this->gui = gui;
2404 }
2405
2406 BC_ScrollTextBoxXScroll::~BC_ScrollTextBoxXScroll()
2407 {
2408 }
2409
2410 int BC_ScrollTextBoxXScroll::handle_event()
2411 {
2412         gui->set_x_pos(get_position());
2413         return 1;
2414 }
2415
2416 BC_ScrollTextBoxYScroll::BC_ScrollTextBoxYScroll(BC_ScrollTextBox *gui)
2417  : BC_ScrollBar(gui->x + gui->text->get_w(), gui->y, SCROLL_VERT,
2418         gui->text->get_h(), gui->text->get_text_rows(), 0, gui->rows)
2419 {
2420         this->gui = gui;
2421 }
2422
2423 BC_ScrollTextBoxYScroll::~BC_ScrollTextBoxYScroll()
2424 {
2425 }
2426
2427 int BC_ScrollTextBoxYScroll::handle_event()
2428 {
2429         gui->text->set_text_row(get_position());
2430         return 1;
2431 }
2432
2433
2434
2435 BC_PopupTextBoxText::BC_PopupTextBoxText(BC_PopupTextBox *popup, int x, int y, const char *text)
2436  : BC_TextBox(x, y, popup->text_w, 1, text, BCTEXTLEN)
2437 {
2438         this->popup = popup;
2439 }
2440
2441 BC_PopupTextBoxText::BC_PopupTextBoxText(BC_PopupTextBox *popup, int x, int y, const wchar_t *wtext)
2442  : BC_TextBox(x, y, popup->text_w, 1, wtext, BCTEXTLEN)
2443 {
2444         this->popup = popup;
2445 }
2446
2447 BC_PopupTextBoxText::~BC_PopupTextBoxText()
2448 {
2449         if(popup) {
2450                 popup->textbox = 0;
2451                 delete popup;
2452                 popup = 0;
2453         }
2454 }
2455
2456
2457 int BC_PopupTextBoxText::handle_event()
2458 {
2459         popup->handle_event();
2460         return 1;
2461 }
2462
2463 BC_PopupTextBoxList::BC_PopupTextBoxList(BC_PopupTextBox *popup, int x, int y)
2464  : BC_ListBox(x, y,
2465         popup->text_w + BC_WindowBase::get_resources()->listbox_button[0]->get_w(),
2466         popup->list_h, popup->list_format, popup->list_items, 0, 0, 1, 0, 1)
2467 {
2468         this->popup = popup;
2469 }
2470 int BC_PopupTextBoxList::handle_event()
2471 {
2472         BC_ListBoxItem *item = get_selection(0, 0);
2473         if(item)
2474         {
2475                 popup->textbox->update(item->get_text());
2476                 popup->textbox->set_text_row(0);
2477                 popup->handle_event();
2478         }
2479         return 1;
2480 }
2481
2482
2483
2484
2485 BC_PopupTextBox::BC_PopupTextBox(BC_WindowBase *parent_window,
2486                 ArrayList<BC_ListBoxItem*> *list_items,
2487                 const char *default_text, int x, int y,
2488                 int text_w, int list_h, int list_format)
2489 {
2490         this->x = x;
2491         this->y = y;
2492         this->list_h = list_h;
2493         this->list_format = list_format;
2494         this->default_text = (char*)default_text;
2495         this->default_wtext = 0;
2496         this->text_w = text_w;
2497         this->parent_window = parent_window;
2498         this->list_items = list_items;
2499 }
2500
2501 BC_PopupTextBox::~BC_PopupTextBox()
2502 {
2503         delete listbox;
2504         if(textbox)
2505         {
2506                 textbox->popup = 0;
2507                 delete textbox;
2508         }
2509 }
2510
2511 int BC_PopupTextBox::create_objects()
2512 {
2513         int x = this->x, y = this->y;
2514         parent_window->add_subwindow(textbox = default_wtext ?
2515                  new BC_PopupTextBoxText(this, x, y, default_wtext) :
2516                  new BC_PopupTextBoxText(this, x, y, default_text));
2517         x += textbox->get_w();
2518         parent_window->add_subwindow(listbox = new BC_PopupTextBoxList(this, x, y));
2519         return 0;
2520 }
2521
2522 void BC_PopupTextBox::update(const char *text)
2523 {
2524         textbox->update(text);
2525         textbox->set_text_row(0);
2526 }
2527
2528 void BC_PopupTextBox::update_list(ArrayList<BC_ListBoxItem*> *data)
2529 {
2530         listbox->update(data, 0, 0, 1);
2531 }
2532
2533
2534 const char* BC_PopupTextBox::get_text()
2535 {
2536         return textbox->get_text();
2537 }
2538
2539 const wchar_t* BC_PopupTextBox::get_wtext()
2540 {
2541         return textbox->get_wtext();
2542 }
2543
2544 int BC_PopupTextBox::get_number()
2545 {
2546         return listbox->get_selection_number(0, 0);
2547 }
2548
2549 int BC_PopupTextBox::get_x()
2550 {
2551         return x;
2552 }
2553
2554 int BC_PopupTextBox::get_y()
2555 {
2556         return y;
2557 }
2558
2559 int BC_PopupTextBox::get_w()
2560 {
2561         return textbox->get_w() + listbox->get_w();
2562 }
2563
2564 int BC_PopupTextBox::get_h()
2565 {
2566         return textbox->get_h();
2567 }
2568
2569 int BC_PopupTextBox::get_show_query()
2570 {
2571         return listbox->get_show_query();
2572 }
2573
2574 void BC_PopupTextBox::set_show_query(int v)
2575 {
2576         listbox->set_show_query(v);
2577 }
2578
2579 int BC_PopupTextBox::handle_event()
2580 {
2581         return 1;
2582 }
2583
2584 void BC_PopupTextBox::reposition_window(int x, int y)
2585 {
2586         this->x = x;
2587         this->y = y;
2588         int x1 = x, y1 = y;
2589         textbox->reposition_window(x1,
2590                 y1,
2591                 textbox->get_w(),
2592                 textbox->get_rows());
2593         x1 += textbox->get_w();
2594         listbox->reposition_window(x1, y1, -1, -1, 0);
2595 //      if(flush) parent_window->flush();
2596 }
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611 BC_TumbleTextBoxText::BC_TumbleTextBoxText(BC_TumbleTextBox *popup,
2612         int64_t default_value, int x, int y)
2613  : BC_TextBox(x, y, popup->text_w, 1, default_value)
2614 {
2615         this->popup = popup;
2616 }
2617
2618 BC_TumbleTextBoxText::BC_TumbleTextBoxText(BC_TumbleTextBox *popup,
2619         float default_value, int x, int y, int precision)
2620  : BC_TextBox(x, y, popup->text_w, 1, default_value, 1, MEDIUMFONT, precision)
2621 {
2622         this->popup = popup;
2623 }
2624
2625 BC_TumbleTextBoxText::~BC_TumbleTextBoxText()
2626 {
2627         if(popup)
2628         {
2629                 popup->textbox = 0;
2630                 delete popup;
2631                 popup = 0;
2632         }
2633 }
2634
2635
2636
2637 int BC_TumbleTextBoxText::handle_event()
2638 {
2639         popup->handle_event();
2640         return 1;
2641 }
2642
2643 int BC_TumbleTextBoxText::button_press_event()
2644 {
2645         if( get_enabled() && is_event_win() ) {
2646                 if( get_buttonpress() < 4 ) return BC_TextBox::button_press_event();
2647                 if( get_buttonpress() == 4 )      popup->tumbler->handle_up_event();
2648                 else if( get_buttonpress() == 5 ) popup->tumbler->handle_down_event();
2649                 return 1;
2650         }
2651         return 0;
2652 }
2653
2654
2655
2656
2657 BC_TumbleTextBox::BC_TumbleTextBox(BC_WindowBase *parent_window,
2658                 int64_t default_value,
2659                 int64_t min,
2660                 int64_t max,
2661                 int x,
2662                 int y,
2663                 int text_w)
2664 {
2665         reset();
2666         this->x = x;
2667         this->y = y;
2668         this->min = min;
2669         this->max = max;
2670         this->default_value = default_value;
2671         this->text_w = text_w;
2672         this->parent_window = parent_window;
2673         use_float = 0;
2674         precision = 4;
2675         increment = 1;
2676 }
2677
2678 BC_TumbleTextBox::BC_TumbleTextBox(BC_WindowBase *parent_window,
2679                 int default_value,
2680                 int min,
2681                 int max,
2682                 int x,
2683                 int y,
2684                 int text_w)
2685 {
2686         reset();
2687         this->x = x;
2688         this->y = y;
2689         this->min = min;
2690         this->max = max;
2691         this->default_value = default_value;
2692         this->text_w = text_w;
2693         this->parent_window = parent_window;
2694         use_float = 0;
2695         precision = 4;
2696         increment = 1;
2697 }
2698
2699 BC_TumbleTextBox::BC_TumbleTextBox(BC_WindowBase *parent_window,
2700                 float default_value_f,
2701                 float min_f,
2702                 float max_f,
2703                 int x,
2704                 int y,
2705                 int text_w)
2706 {
2707         reset();
2708         this->x = x;
2709         this->y = y;
2710         this->min_f = min_f;
2711         this->max_f = max_f;
2712         this->default_value_f = default_value_f;
2713         this->text_w = text_w;
2714         this->parent_window = parent_window;
2715         use_float = 1;
2716         precision = 4;
2717         increment = 1;
2718 }
2719
2720 BC_TumbleTextBox::~BC_TumbleTextBox()
2721 {
2722 // Recursive delete.  Normally ~BC_TumbleTextBox is never called but textbox
2723 // is deleted anyway by the windowbase so textbox deletes this.
2724         if(tumbler) delete tumbler;
2725         tumbler = 0;
2726 // Don't delete text here if we were called by ~BC_TumbleTextBoxText
2727         if(textbox)
2728         {
2729                 textbox->popup = 0;
2730                 delete textbox;
2731         }
2732         textbox = 0;
2733 }
2734
2735 void BC_TumbleTextBox::reset()
2736 {
2737         textbox = 0;
2738         tumbler = 0;
2739         increment = 1.0;
2740 }
2741
2742 void BC_TumbleTextBox::set_precision(int precision)
2743 {
2744         this->precision = precision;
2745 }
2746
2747 void BC_TumbleTextBox::set_increment(float value)
2748 {
2749         this->increment = value;
2750         if(tumbler) tumbler->set_increment(value);
2751 }
2752
2753 void BC_TumbleTextBox::set_log_floatincrement(int value)
2754 {
2755         this->log_floatincrement = value;
2756         if(tumbler) tumbler->set_log_floatincrement(value);
2757 }
2758
2759 int BC_TumbleTextBox::create_objects()
2760 {
2761         int x = this->x, y = this->y;
2762
2763         textbox = use_float ?
2764                 new BC_TumbleTextBoxText(this, default_value_f, x, y, precision) :
2765                 new BC_TumbleTextBoxText(this, default_value, x, y);
2766
2767         parent_window->add_subwindow(textbox);
2768         x += textbox->get_w();
2769
2770         tumbler = use_float ?
2771                 (BC_Tumbler *)new BC_FTumbler(textbox, min_f, max_f, x, y) :
2772                 (BC_Tumbler *)new BC_ITumbler(textbox, min, max, x, y);
2773         parent_window->add_subwindow(tumbler);
2774         tumbler->set_increment(increment);
2775         return 0;
2776 }
2777
2778 const char* BC_TumbleTextBox::get_text()
2779 {
2780         return textbox->get_text();
2781 }
2782
2783 const wchar_t* BC_TumbleTextBox::get_wtext()
2784 {
2785         return textbox->get_wtext();
2786 }
2787
2788 BC_TextBox* BC_TumbleTextBox::get_textbox()
2789 {
2790         return textbox;
2791 }
2792
2793 int BC_TumbleTextBox::update(const char *value)
2794 {
2795         textbox->update(value);
2796         textbox->set_text_row(0);
2797         return 0;
2798 }
2799
2800 int BC_TumbleTextBox::update(int64_t value)
2801 {
2802         textbox->update(value);
2803         textbox->set_text_row(0);
2804         return 0;
2805 }
2806
2807 int BC_TumbleTextBox::update(float value)
2808 {
2809         textbox->update(value);
2810         textbox->set_text_row(0);
2811         return 0;
2812 }
2813
2814
2815 int BC_TumbleTextBox::get_x()
2816 {
2817         return x;
2818 }
2819
2820 int BC_TumbleTextBox::get_y()
2821 {
2822         return y;
2823 }
2824
2825 int BC_TumbleTextBox::get_w()
2826 {
2827         return textbox->get_w() + tumbler->get_w();
2828 }
2829
2830 int BC_TumbleTextBox::get_h()
2831 {
2832         return textbox->get_h();
2833 }
2834
2835 void BC_TumbleTextBox::disable(int hide_text)
2836 {
2837         if( hide_text && !textbox->is_hidden() )
2838                 textbox->hide_window(0);
2839         if( !tumbler->is_hidden() )
2840                 tumbler->hide_window(0);
2841         if( !get_enabled() ) return;
2842         return textbox->disable();
2843 }
2844
2845 void BC_TumbleTextBox::enable()
2846 {
2847         if( textbox->is_hidden() )
2848                 textbox->show_window(0);
2849         if( tumbler->is_hidden() )
2850                 tumbler->show_window(0);
2851         if( get_enabled() ) return;
2852         return textbox->enable();
2853 }
2854
2855 int BC_TumbleTextBox::get_enabled()
2856 {
2857         return textbox->get_enabled();
2858 }
2859
2860 int BC_TumbleTextBox::handle_event()
2861 {
2862         return 1;
2863 }
2864
2865 void BC_TumbleTextBox::reposition_window(int x, int y)
2866 {
2867         this->x = x;
2868         this->y = y;
2869
2870         textbox->reposition_window(x,
2871                 y,
2872                 text_w,
2873                 1);
2874         tumbler->reposition_window(x + textbox->get_w(),
2875                 y);
2876 //      if(flush) parent_window->flush();
2877 }
2878
2879
2880 void BC_TumbleTextBox::set_boundaries(int64_t min, int64_t max)
2881 {
2882         tumbler->set_boundaries(min, max);
2883 }
2884
2885 void BC_TumbleTextBox::set_boundaries(float min, float max)
2886 {
2887         tumbler->set_boundaries(min, max);
2888 }
2889
2890
2891
2892 BC_TextMenu::BC_TextMenu(BC_TextBox *textbox)
2893  : BC_PopupMenu(0, 0, 0, "", 0)
2894 {
2895         this->textbox = textbox;
2896 }
2897
2898 BC_TextMenu::~BC_TextMenu()
2899 {
2900 }
2901
2902 void BC_TextMenu::create_objects()
2903 {
2904         add_item(new BC_TextMenuCut(this));
2905         add_item(new BC_TextMenuCopy(this));
2906         add_item(new BC_TextMenuPaste(this));
2907 }
2908
2909
2910 BC_TextMenuCut::BC_TextMenuCut(BC_TextMenu *menu) 
2911  : BC_MenuItem(_("Cut"))
2912 {
2913         this->menu = menu;
2914 }
2915
2916 int BC_TextMenuCut::handle_event()
2917 {
2918         menu->textbox->cut(1);
2919         
2920         return 0;
2921 }
2922
2923
2924 BC_TextMenuCopy::BC_TextMenuCopy(BC_TextMenu *menu) 
2925  : BC_MenuItem(_("Copy"))
2926 {
2927         this->menu = menu;
2928 }
2929
2930 int BC_TextMenuCopy::handle_event()
2931 {
2932         menu->textbox->copy(1);
2933         return 0;
2934 }
2935
2936
2937 BC_TextMenuPaste::BC_TextMenuPaste(BC_TextMenu *menu) 
2938  : BC_MenuItem(_("Paste"))
2939 {
2940         this->menu = menu;
2941 }
2942
2943 int BC_TextMenuPaste::handle_event()
2944 {
2945         menu->textbox->paste(1);
2946         return 0;
2947 }
2948
2949
2950 void BC_TumbleTextBox::set_tooltip(const char *text)
2951 {
2952         textbox->set_tooltip(text);
2953 }
2954