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