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