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