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