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