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