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