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