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