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