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