cposer/viewer btn1/2/3 fwd/rev/frm playback, misc fixes, leaks, cleanup
[goodguy/history.git] / cinelerra-5.1 / guicast / bclistbox.C
1 /*
2  * CINELERRA
3  * Copyright (C) 2010-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 "bcdisplayinfo.h"
22 #include "bcdragwindow.h"
23 #include "bclistbox.h"
24 #include "bclistboxitem.h"
25 #include "bcpixmap.h"
26 #include "bcresources.h"
27 #include "bcsignals.h"
28 #include "clip.h"
29 #include "cursors.h"
30 #include "fonts.h"
31 #include "keys.h"
32 #include "language.h"
33 #include "bctimer.h"
34 #include "vframe.h"
35
36 #include <string.h>
37 #include <unistd.h>
38
39 // ====================================================== scrollbars
40
41 BC_ListBoxYScroll::BC_ListBoxYScroll(BC_ListBox *listbox)
42  : BC_ScrollBar(listbox->get_yscroll_x(), listbox->get_yscroll_y(),
43         listbox->yscroll_orientation, listbox->get_yscroll_height(),
44         listbox->items_h, listbox->yposition, listbox->view_h)
45 {
46         this->listbox = listbox;
47 }
48
49 BC_ListBoxYScroll::~BC_ListBoxYScroll()
50 {
51 }
52
53 int BC_ListBoxYScroll::handle_event()
54 {
55         listbox->set_yposition(get_value());
56         return 1;
57 }
58
59 BC_ListBoxXScroll::BC_ListBoxXScroll(BC_ListBox *listbox)
60  : BC_ScrollBar(listbox->get_xscroll_x(), listbox->get_xscroll_y(),
61         listbox->xscroll_orientation, listbox->get_xscroll_width(),
62         listbox->items_w, listbox->xposition, listbox->view_w)
63 {
64         this->listbox = listbox;
65 }
66
67 BC_ListBoxXScroll::~BC_ListBoxXScroll()
68 {
69 }
70
71 int BC_ListBoxXScroll::handle_event()
72 {
73         listbox->set_xposition(get_value());
74         return 1;
75 }
76
77
78 BC_ListBoxToggle::BC_ListBoxToggle(BC_ListBox *listbox,
79         BC_ListBoxItem *item,
80         int x,
81         int y)
82 {
83         this->listbox = listbox;
84         this->item = item;
85         this->x = x;
86         this->y = y;
87         this->value = item->get_expand();
88         if( this->value )
89                 state = BC_Toggle::TOGGLE_CHECKED;
90         else
91                 state = BC_Toggle::TOGGLE_UP;
92 }
93
94 void BC_ListBoxToggle::update(BC_ListBoxItem *item,
95         int x,
96         int y,
97         int flash)
98 {
99         this->value = item->get_expand();
100         this->item = item;
101         this->x = x;
102         this->y = y;
103
104 // update state
105         switch( state ) {
106         case TOGGLE_UP:
107                 if( value )
108                         state = TOGGLE_CHECKED;
109                 break;
110
111         case TOGGLE_UPHI:
112                 if( value )
113                         state = TOGGLE_CHECKEDHI;
114                 break;
115
116         case TOGGLE_CHECKED:
117                 if( !value )
118                         state = TOGGLE_UP;
119                 break;
120
121         case TOGGLE_DOWN:
122                 break;
123
124         case TOGGLE_CHECKEDHI:
125                 if( !value )
126                         state = TOGGLE_UPHI;
127                 break;
128
129         case TOGGLE_DOWN_EXIT:
130                 break;
131         }
132
133
134         draw(flash);
135 }
136
137 int BC_ListBoxToggle::cursor_motion_event(int *redraw_toggles)
138 {
139         int w = listbox->toggle_images[0]->get_w();
140         int h = listbox->toggle_images[0]->get_h();
141         int cursor_inside = listbox->get_cursor_x() >= x &&
142                 listbox->get_cursor_x() < x + w &&
143                 listbox->get_cursor_y() >= y &&
144                 listbox->get_cursor_y() < y + h;
145         int result = 0;
146
147         switch( state ) {
148         case BC_ListBoxToggle::TOGGLE_UPHI:
149                 if( !cursor_inside ) {
150                         state = BC_ListBoxToggle::TOGGLE_UP;
151                         *redraw_toggles = 1;
152                 }
153                 break;
154
155         case BC_ListBoxToggle::TOGGLE_CHECKEDHI:
156                 if( !cursor_inside ) {
157                         state = BC_ListBoxToggle::TOGGLE_CHECKED;
158                         *redraw_toggles = 1;
159                 }
160                 break;
161
162         case BC_ListBoxToggle::TOGGLE_DOWN:
163                 if( !cursor_inside ) {
164                         state = BC_ListBoxToggle::TOGGLE_DOWN_EXIT;
165                         *redraw_toggles = 1;
166                 }
167                 result = 1;
168                 break;
169
170         case BC_ListBoxToggle::TOGGLE_DOWN_EXIT:
171                 if( cursor_inside ) {
172                         state = BC_ListBoxToggle::TOGGLE_DOWN;
173                         *redraw_toggles = 1;
174                 }
175                 result = 1;
176                 break;
177
178         default:
179                 if( cursor_inside ) {
180                         if( value )
181                                 state = BC_ListBoxToggle::TOGGLE_CHECKEDHI;
182                         else
183                                 state = BC_ListBoxToggle::TOGGLE_UPHI;
184                         *redraw_toggles = 1;
185                 }
186                 break;
187         }
188         return result;
189 }
190
191 int BC_ListBoxToggle::cursor_leave_event(int *redraw_toggles)
192 {
193         if( value )
194                 state = BC_ListBoxToggle::TOGGLE_CHECKED;
195         else
196                 state = BC_ListBoxToggle::TOGGLE_UP;
197         return 0;
198 }
199
200 int BC_ListBoxToggle::button_press_event()
201 {
202         int w = listbox->toggle_images[0]->get_w();
203         int h = listbox->toggle_images[0]->get_h();
204
205         if( listbox->gui->get_cursor_x() >= x &&
206             listbox->gui->get_cursor_x() < x + w &&
207             listbox->gui->get_cursor_y() >= y &&
208             listbox->gui->get_cursor_y() < y + h ) {
209                 state = BC_ListBoxToggle::TOGGLE_DOWN;
210                 return 1;
211         }
212         return 0;
213 }
214
215 int BC_ListBoxToggle::button_release_event(int *redraw_toggles)
216 {
217         int result = 0;
218
219         switch( state ) {
220         case BC_ListBoxToggle::TOGGLE_DOWN:
221                 value = !value;
222                 if( value )
223                         state = BC_ListBoxToggle::TOGGLE_CHECKEDHI;
224                 else
225                         state = BC_ListBoxToggle::TOGGLE_UPHI;
226                 listbox->expand_item(item, value);
227                 result = 1;
228                 break;
229
230         case BC_ListBoxToggle::TOGGLE_DOWN_EXIT:
231                 if( value )
232                         state = BC_ListBoxToggle::TOGGLE_CHECKED;
233                 else
234                         state = BC_ListBoxToggle::TOGGLE_UP;
235                 *redraw_toggles = 1;
236                 result = 1;
237                 break;
238         }
239         return result;
240 }
241
242 void BC_ListBoxToggle::draw(int flash)
243 {
244         if( listbox->gui ) {
245                 int image_number = 0;
246                 int w = listbox->toggle_images[0]->get_w();
247                 int h = listbox->toggle_images[0]->get_h();
248
249                 switch( state ) {
250                 case BC_ListBoxToggle::TOGGLE_UP:        image_number = 0; break;
251                 case BC_ListBoxToggle::TOGGLE_UPHI:      image_number = 1; break;
252                 case BC_ListBoxToggle::TOGGLE_CHECKED:   image_number = 2; break;
253                 case BC_ListBoxToggle::TOGGLE_DOWN:      image_number = 3; break;
254                 case BC_ListBoxToggle::TOGGLE_CHECKEDHI: image_number = 4; break;
255                 case BC_ListBoxToggle::TOGGLE_DOWN_EXIT:
256                         image_number = value ? 2 : 0;
257                         break;
258                 }
259
260 //printf("BC_ListBoxToggle::draw 1 %d\n", state);
261                 listbox->gui->draw_pixmap(listbox->toggle_images[image_number], x, y);
262
263                 if( flash ) {
264                         listbox->gui->flash(x, y, w, h);
265                         listbox->gui->flush();
266                 }
267         }
268 }
269
270
271 // ====================================================== box
272
273 BC_ListBox::BC_ListBox(int x, int y, int w, int h,
274         int display_format, ArrayList<BC_ListBoxItem*> *data,
275         const char **column_titles, int *column_width, int columns,
276         int yposition, int is_popup, int selection_mode,
277         int icon_position, int allow_drag)
278  : BC_SubWindow(x, y, w, h, -1)
279 {
280         justify = LISTBOX_RIGHT;
281         xposition = 0;
282         highlighted_item = -1;
283         highlighted_title = -1;
284         highlighted_division = -1;
285         highlighted_ptr = 0;
286         xscrollbar = 0;
287         yscrollbar = 0;
288         current_cursor = ARROW_CURSOR;
289         gui = 0;
290         view_h = 0;
291         view_w = 0;
292         title_h = 0;
293         active = 0;
294         is_suggestions = 0;
295         new_value = 0;
296         need_xscroll = 0;
297         need_yscroll = 0;
298         xscroll_orientation = SCROLL_HORIZ;
299         yscroll_orientation = SCROLL_VERT;
300         bg_tile = 0;
301         bg_draw = 1;
302         drag_popup = 0;
303         selection_number1 = -1;
304         selection_number2 = -1;
305         bg_surface = 0;
306         bg_pixmap = 0;
307         row_height = row_ascent = row_descent = 0;
308
309         current_operation = NO_OPERATION;
310         button_highlighted = 0;
311         list_highlighted = 0;
312         disabled = 0;
313
314         scroll_repeat = 0;
315         allow_drag_scroll = 1;
316         process_drag = 1;
317
318         sort_column = -1;
319         sort_order = 0;
320
321         allow_drag_column = 0;
322         master_column = 0;
323         search_column = 0;
324
325         popup_w = w;
326         popup_h = h;
327
328         for( int i=0; i<3; ++i ) column_bg[i] = 0;
329         for( int i=0; i<4; ++i ) button_images[i] = 0;
330         for( int i=0; i<5; ++i ) toggle_images[i] = 0;
331
332         column_sort_up = 0;
333         column_sort_dn = 0;
334
335 //printf("BC_ListBox::BC_ListBox 1\n");
336         this->data = data;
337         this->columns = columns;
338         this->yposition = yposition;
339         this->is_popup = is_popup;
340         this->use_button = 1;
341         this->display_format = display_format;
342         this->selection_mode = selection_mode;
343         this->icon_position = icon_position;
344         this->allow_drag = allow_drag;
345         this->column_titles = 0;
346         this->column_width = 0;
347         this->first_in_view = -1;
348         this->last_in_view = 0;
349 //printf("BC_ListBox::BC_ListBox 1\n");
350
351         if( (!column_titles && column_width) ||
352             (column_titles && !column_width) ) {
353                 printf("BC_ListBox::BC_ListBox either column_titles or column_widths == NULL but not both.\n");
354         }
355 //printf("BC_ListBox::BC_ListBox 2 %p %p\n", column_titles, column_width);
356
357         set_columns(column_titles, column_width, columns);
358
359 //printf("BC_ListBox::BC_ListBox 3\n");
360
361         drag_icon_vframe = 0;
362         drag_column_icon_vframe = 0;
363
364
365
366 // reset the search engine
367 //printf("BC_ListBox::BC_ListBox 4\n");
368         show_query = 0;
369         reset_query();
370 //printf("BC_ListBox::BC_ListBox 5\n");
371 }
372
373 BC_ListBox::~BC_ListBox()
374 {
375         expanders.remove_all_objects();
376         if( bg_surface ) delete bg_surface;
377         if( bg_pixmap ) delete bg_pixmap;
378         if( xscrollbar ) delete xscrollbar;
379         if( yscrollbar ) delete yscrollbar;
380         for( int i=0; i<3; ++i ) delete column_bg[i];
381         for( int i=0; i<4; ++i ) delete button_images[i];
382         for( int i=0; i<5; ++i ) delete toggle_images[i];
383         if( column_sort_up ) delete column_sort_up;
384         if( column_sort_dn ) delete column_sort_dn;
385
386         delete_columns();
387         if( drag_popup ) delete drag_popup;
388 }
389
390 int BC_ListBox::enable()
391 {
392         disabled = 0;
393         draw_button(1);
394         return 1;
395 }
396
397 int BC_ListBox::disable()
398 {
399         disabled = 1;
400         draw_button(1);
401         return 1;
402 }
403
404 void BC_ListBox::reset_query()
405 {
406         query[0] = 0;  // reset query
407 }
408
409 int BC_ListBox::evaluate_query(char *string)
410 {
411         for( int i=0; i<data[search_column].size(); ++i ) {
412                 if( strcmp(string, data[search_column].get(i)->text) <= 0 &&
413                     data[search_column].get(i)->searchable ) {
414                         return i;
415                 }
416         }
417
418         return -1;
419 }
420
421 int BC_ListBox::query_list()
422 {
423         if( query[0] == 0 ) return 0;
424
425         int done = 0;
426         int result;
427         int selection_changed = 0;
428         int prev_selection = -1;
429         result = evaluate_query(query);
430         if( result >= 0 ) done = 1;
431
432         if( done ) {
433 // Deselect all
434                 for( int i=0; i<data[0].total; ++i ) {
435                         for( int j=0; j<columns; ++j ) {
436                                 if( data[j].values[i]->selected ) prev_selection = i;
437                                 data[j].values[i]->selected = 0;
438                         }
439                 }
440
441 // Select one
442                 if( prev_selection != result )
443                         selection_changed = 1;
444                 for( int j=0; j<columns; ++j ) {
445                         data[j].values[result]->selected = 1;
446                 }
447                 center_selection(result);
448         }
449
450         return selection_changed;
451 }
452
453 void BC_ListBox::init_column_width()
454 {
455         if( !column_width && data ) {
456                 int widest = 5, wd;
457                 for( int i=0; i<data[0].total; ++i ) {
458                         wd = get_text_w(data[0].values[i]);
459                         if( wd > widest ) widest = wd;
460                 }
461                 default_column_width[0] = widest + 2 * LISTBOX_MARGIN;
462         }
463 }
464
465 int BC_ListBox::initialize()
466 {
467         if( is_popup ) {
468                 if( use_button ) {
469                         for( int i=0; i<4; ++i ) {
470                                 button_images[i] = new BC_Pixmap(parent_window,
471                                         BC_WindowBase::get_resources()->listbox_button[i],
472                                         PIXMAP_ALPHA);
473                         }
474                         w = button_images[0]->get_w();
475                         h = button_images[0]->get_h();
476                 }
477
478                 gui = 0;
479                 current_operation = NO_OPERATION;
480
481         }
482         else {
483                 gui = this;
484                 current_operation = NO_OPERATION;
485         }
486
487         for( int i=0; i<3; ++i ) {
488                 column_bg[i] = new BC_Pixmap(parent_window,
489                         get_resources()->listbox_column[i],
490                         PIXMAP_ALPHA);
491         }
492         for( int i=0; i<5; ++i ) {
493                 toggle_images[i] = new BC_Pixmap(parent_window,
494                         get_resources()->listbox_expand[i],
495                         PIXMAP_ALPHA);
496         }
497
498         column_sort_up = new BC_Pixmap(parent_window,
499                 BC_WindowBase::get_resources()->listbox_up,
500                 PIXMAP_ALPHA);
501         column_sort_dn = new BC_Pixmap(parent_window,
502                 BC_WindowBase::get_resources()->listbox_dn,
503                 PIXMAP_ALPHA);
504
505 //printf("BC_ListBox::initialize 10\n");
506         drag_icon_vframe = get_resources()->type_to_icon[ICON_UNKNOWN];
507         drag_column_icon_vframe = get_resources()->type_to_icon[ICON_COLUMN];
508 // = new BC_Pixmap(parent_window,
509 //              get_resources()->type_to_icon[ICON_UNKNOWN],
510 //              PIXMAP_ALPHA);
511 //      drag_column_icon = new BC_Pixmap(parent_window,
512 //              get_resources()->type_to_icon[ICON_COLUMN],
513 //              PIXMAP_ALPHA);
514         BC_SubWindow::initialize();
515
516         init_column_width();
517
518         if( top_level->get_resources()->listbox_bg )
519                 bg_pixmap = new BC_Pixmap(this,
520                         get_resources()->listbox_bg,
521                         PIXMAP_OPAQUE);
522
523         draw_button(0);
524         draw_items(0);
525
526         if( !use_button && is_popup ) {
527                 hide_window(1);
528         }
529
530
531         return 0;
532 }
533
534 void BC_ListBox::deactivate_selection()
535 {
536         current_operation = NO_OPERATION;
537 }
538
539 int BC_ListBox::draw_button(int flush)
540 {
541 // Draw the button for a popup listbox
542         if( use_button && is_popup ) {
543                 int image_number = 0;
544
545                 draw_top_background(parent_window, 0, 0, w, h);
546
547                 if( button_highlighted )
548                         image_number = 1;
549                 if( current_operation == BUTTON_DN )
550                         image_number = 2;
551                 if( disabled )
552                         image_number = 3;
553
554                 pixmap->draw_pixmap(button_images[image_number],
555                         0, 0, w, h, 0, 0);
556                 flash(flush);
557         }
558         return 0;
559 }
560
561 int BC_ListBox::calculate_item_coords()
562 {
563         if( !data ) return 0;
564
565         int icon_x = 0;
566         int next_icon_x = 0;
567         int next_icon_y = 0;
568         int next_text_y = 0;
569 // Change the display_format to get the right item dimensions for both
570 // text and icons.
571         temp_display_format = display_format;
572
573
574 // Scan the first column for lowest y coord of all text
575 // and lowest right x and y coord for all icons which aren't auto placable
576         calculate_last_coords_recursive(data,
577                 &icon_x, &next_icon_x, &next_icon_y, &next_text_y, 1);
578
579 // Reset last column width.  It's recalculated based on text width.
580         calculate_item_coords_recursive(data,
581                 &icon_x, &next_icon_x, &next_icon_y, &next_text_y, 1);
582
583         display_format = temp_display_format;
584
585         return 0;
586 }
587
588 void BC_ListBox::calculate_last_coords_recursive(
589         ArrayList<BC_ListBoxItem*> *data,
590         int *icon_x,
591         int *next_icon_x,
592         int *next_icon_y,
593         int *next_text_y,
594         int top_level)
595 {
596         for( int i=0; i<data[0].size(); ++i ) {
597                 int current_text_y = 0;
598                 int current_icon_x = 0;
599                 int current_icon_y = 0;
600                 BC_ListBoxItem *item = data[0].get(i);
601
602 // Get next_text_y
603                 if( !item->autoplace_text ) {
604 // Lowest text coordinate
605                         display_format = LISTBOX_TEXT;
606                         current_text_y = item->text_y + get_text_h(item);
607                         if( current_text_y > *next_text_y )
608                                 *next_text_y = current_text_y;
609
610 // Add sublist depth if it is expanded
611                         if( item->get_sublist() && item->get_columns() &&
612                             item->get_expand() ) {
613                                 calculate_last_coords_recursive(item->get_sublist(),
614                                         icon_x, next_icon_x, next_icon_y, next_text_y, 0);
615                         }
616                 }
617
618 // Get next_icon coordinate
619                 if( top_level ) {
620                         BC_ListBoxItem *item = data[master_column].get(i);
621                         if( !item->autoplace_icon ) {
622                                 display_format = LISTBOX_ICONS;
623 // Lowest right icon coordinate.
624                                 current_icon_x = item->icon_x;
625                                 if( current_icon_x > *icon_x ) *icon_x = current_icon_x;
626                                 if( current_icon_x + get_item_w(item) > *next_icon_x ) {
627                                         *next_icon_x = current_icon_x + get_item_w(item);
628                                         *next_icon_y = 0;
629                                 }
630                                 current_icon_y = item->icon_y + get_item_h(item);
631                                 if( current_icon_y > *next_icon_y )
632                                         *next_icon_y = current_icon_y;
633                         }
634                 }
635         }
636 }
637
638
639 void BC_ListBox::calculate_item_coords_recursive(
640         ArrayList<BC_ListBoxItem*> *data,
641         int *icon_x, int *next_icon_x, int *next_icon_y, int *next_text_y,
642         int top_level)
643 {
644 // get maximum height of an icon
645         row_height = get_text_height(MEDIUMFONT);
646         if( temp_display_format == LISTBOX_ICON_LIST ) {
647                 for( int i=0; i<data[0].size(); ++i ) {
648                         if( data[0].get(i)->icon ) {
649                                 if( data[0].get(i)->icon->get_h() > row_height )
650                                         row_height = data[0].get(i)->icon->get_h();
651                         }
652                 }
653         }
654
655
656 // Set up items which need autoplacement.
657 // Should fill icons down and then across
658         for( int i=0; i<data[0].size(); ++i ) {
659 // Don't increase y unless the row requires autoplacing.
660                 int total_autoplaced_columns = 0;
661
662 // Set up icons in first column
663                 if( top_level ) {
664                         BC_ListBoxItem *item = data[master_column].get(i);
665                         if( item->autoplace_icon ) {
666 // 1 column only if icons are used
667                                 display_format = LISTBOX_ICONS;
668 // Test row height
669 // Start new column.
670                                 if( *next_icon_y + get_item_h(item) >= get_h() &&
671                                     *next_icon_y > 0 ) {
672                                         *icon_x = *next_icon_x;
673                                         *next_icon_y = 0;
674                                 }
675
676                                 if( *icon_x + get_item_w(item) > *next_icon_x )
677                                         *next_icon_x = *icon_x + get_item_w(item);
678
679
680                                 item->set_icon_x(*icon_x);
681                                 item->set_icon_y(*next_icon_y);
682
683                                 *next_icon_y += get_item_h(item);
684                         }
685                 }
686
687 // Set up a text row
688                 int next_text_x = 0;
689                 row_ascent = row_descent = 0;
690 // row_height still holds icon max height
691                 for( int j=0; j<columns; ++j ) {
692                         BC_ListBoxItem *item = data[j].get(i);
693                         if( item->autoplace_text ) {
694                                 display_format = LISTBOX_TEXT;
695                                 item->set_text_x(next_text_x);
696                                 item->set_text_y(*next_text_y);
697                                 int ht = get_text_h(item);
698                                 if( ht > row_height ) row_height = ht;
699                                 int bl = get_baseline(item);
700                                 if( bl > row_ascent ) row_ascent = bl;
701                                 int dt = ht - bl;
702                                 if( dt > row_descent ) row_ascent = bl;
703
704 // printf("BC_ListBox::calculate_item_coords_recursive %p %d %d %d %d %s \n",
705 // item->get_sublist(), item->get_columns(), item->get_expand(),
706 // next_text_x, *next_text_y, item->get_text());
707 // Increment position of next column
708                                 if( j < columns - 1 ) {
709                                         next_text_x += (column_width ?
710                                                 column_width[j] :
711                                                 default_column_width[j]);
712                                 }
713 // Set last column width based on text width
714                                 else {
715                                         int new_w = get_item_w(item);
716
717                                         int *previous_w = (column_width ?
718                                                 &column_width[j] :
719                                                 &default_column_width[j]);
720                                         if( new_w > *previous_w )
721                                                 *previous_w = new_w;
722 //printf("BC_ListBox::calculate_item_coords_recursive 1 %d\n", new_w);
723                                 }
724                                 total_autoplaced_columns++;
725                         }
726                 }
727
728 // Increase the text vertical position
729                 if( total_autoplaced_columns ) {
730                         display_format = LISTBOX_TEXT;
731                         *next_text_y += row_height;
732                 }
733
734 // Set up a sublist
735                 BC_ListBoxItem *item = data[master_column].values[i];
736                 if( item->get_sublist() && item->get_columns() &&
737                     item->get_expand() ) {
738                         calculate_item_coords_recursive( item->get_sublist(),
739                                 icon_x, next_icon_x, next_icon_y, next_text_y, 0);
740                 }
741         }
742 }
743
744 void BC_ListBox::set_is_suggestions(int value)
745 {
746         this->is_suggestions = value;
747 }
748 void BC_ListBox::set_scroll_repeat()
749 {
750         if( scroll_repeat ) return;
751         scroll_repeat = 1;
752         set_repeat(get_resources()->scroll_repeat);
753 }
754
755 void BC_ListBox::unset_scroll_repeat()
756 {
757         if( !scroll_repeat ) return;
758         scroll_repeat = 0;
759         unset_repeat(get_resources()->scroll_repeat);
760 }
761
762 void BC_ListBox::set_use_button(int value)
763 {
764         this->use_button = value;
765 }
766
767 void BC_ListBox::set_justify(int value)
768 {
769         this->justify = value;
770 }
771
772 void BC_ListBox::set_allow_drag_column(int value)
773 {
774         this->allow_drag_column = value;
775 }
776
777 void BC_ListBox::set_process_drag(int value)
778 {
779         this->process_drag = value;
780 }
781
782 void BC_ListBox::set_master_column(int value, int redraw)
783 {
784         this->master_column = value;
785         if( redraw ) {
786                 draw_items(1);
787         }
788 }
789
790 void BC_ListBox::set_search_column(int value)
791 {
792         this->search_column = value;
793 }
794
795 int BC_ListBox::get_sort_column()
796 {
797         return sort_column;
798 }
799
800 void BC_ListBox::set_sort_column(int value, int redraw)
801 {
802         sort_column = value;
803         if( redraw ) {
804                 draw_titles(1);
805         }
806 }
807
808 int BC_ListBox::get_sort_order()
809 {
810         return sort_order;
811 }
812
813 void BC_ListBox::set_sort_order(int value, int redraw)
814 {
815         sort_order = value;
816         if( redraw ) {
817                 draw_titles(1);
818         }
819 }
820
821
822 int BC_ListBox::get_display_mode()
823 {
824         return display_format;
825 }
826
827 int BC_ListBox::get_yposition()
828 {
829         return yposition;
830 }
831
832 int BC_ListBox::get_xposition()
833 {
834         return xposition;
835 }
836
837 int BC_ListBox::get_highlighted_item()
838 {
839         return highlighted_item;
840 }
841
842
843 int BC_ListBox::get_item_x(BC_ListBoxItem *item)
844 {
845         if( display_format == LISTBOX_TEXT )
846                 return item->text_x - xposition + 2;
847         if( display_format == LISTBOX_ICON_LIST )
848                 return item->text_x - xposition + 2;
849         return item->icon_x - xposition + 2;
850 }
851
852 int BC_ListBox::get_item_y(BC_ListBoxItem *item)
853 {
854         if( display_format == LISTBOX_TEXT )
855                 return item->text_y - yposition + title_h + 2;
856         if( display_format == LISTBOX_ICON_LIST )
857                 return item->text_y - yposition + title_h + 2;
858         return item->icon_y - yposition + title_h + 2;
859 }
860
861 int BC_ListBox::get_item_w(BC_ListBoxItem *item)
862 {
863         if( display_format == LISTBOX_ICONS ) {
864                 int x, y, w, h;
865                 get_icon_mask(item, x, y, w, h);
866                 int icon_w = w;
867                 get_text_mask(item, x, y, w, h);
868                 int text_w = w;
869
870                 return icon_position == ICON_LEFT ? icon_w + text_w :
871                         icon_w > text_w ? icon_w : text_w;
872         }
873         return get_text_w(item) + 2 * LISTBOX_MARGIN;
874 }
875
876 int BC_ListBox::get_item_h(BC_ListBoxItem *item)
877 {
878         if( display_format == LISTBOX_ICONS ) {
879                 int x, y, w, h;
880                 get_icon_mask(item, x, y, w, h);
881                 int icon_h = h;
882                 get_text_mask(item, x, y, w, h);
883                 int text_h = h;
884
885                 return icon_position != ICON_LEFT ? icon_h + text_h :
886                         icon_h > text_h ? icon_h : text_h;
887         }
888         return get_text_h(item);
889 }
890
891
892 int BC_ListBox::get_icon_w(BC_ListBoxItem *item)
893 {
894         return item->get_icon_w();
895 }
896
897 int BC_ListBox::get_icon_h(BC_ListBoxItem *item)
898 {
899         return item->get_icon_h();
900 }
901
902 int BC_ListBox::get_text_w(BC_ListBoxItem *item)
903 {
904         int w = item->get_text_w();
905         if( w < 0 ) item->set_text_w(w = get_text_width(MEDIUMFONT, item->get_text()));
906         return w;
907 }
908
909 int BC_ListBox::get_text_h(BC_ListBoxItem *item)
910 {
911         int h = item->get_text_h();
912         if( h < 0 ) item->set_text_h(h = get_text_height(MEDIUMFONT, item->get_text()));
913         return h;
914 }
915
916 int BC_ListBox::get_baseline(BC_ListBoxItem *item)
917 {
918         int b = item->get_baseline();
919         if( b < 0 ) item->set_baseline(b = get_text_ascent(MEDIUMFONT));
920         return b;
921 }
922
923 int BC_ListBox::get_items_width()
924 {
925         int widest = 0;
926
927         if( display_format == LISTBOX_ICONS ) {
928                 for( int i=0; i<columns; ++i ) {
929                         for( int j=0; j<data[i].total; ++j ) {
930                                 int x1, x, y, w, h;
931                                 BC_ListBoxItem *item = data[i].values[j];
932                                 x1 = item->icon_x;
933
934                                 get_icon_mask(item, x, y, w, h);
935                                 if( x1 + w > widest ) widest = x1 + w;
936
937                                 if( display_format == LISTBOX_ICONS && icon_position == ICON_LEFT )
938                                         x1 += w;
939
940                                 get_text_mask(item, x, y, w, h);
941                                 if( x1 + w > widest ) widest = x1 + w;
942                         }
943                 }
944         }
945         else
946         if( display_format == LISTBOX_TEXT ) {
947                 return get_column_offset(columns);
948         }
949         else {
950                 return get_column_offset(columns);
951         }
952         return widest;
953 }
954
955 int BC_ListBox::get_items_height(ArrayList<BC_ListBoxItem*> *data, int columns,
956                 int *result)
957 {
958         int temp = 0;
959         int top_level = 0;
960         int highest = 0;
961         if( !result ) {
962                 result = &temp;
963                 top_level = 1;
964         }
965
966         for( int j=0; j<(data?data[master_column].total:0); ++j ) {
967                 int x, y, w, h;
968                 BC_ListBoxItem *item = data[master_column].values[j];
969
970                 if( display_format == LISTBOX_ICONS ||
971                     display_format == LISTBOX_ICON_LIST ) {
972                         get_icon_mask(item, x, y, w, h);
973                         if( y + h + yposition > highest ) highest = y + h + yposition;
974
975                         get_text_mask(item, x, y, w, h);
976                         if( y + h + yposition > highest ) highest = y + h + yposition;
977                 }
978                 else {
979                         get_text_mask(item, x, y, w, h);
980                         *result += h;
981 // Descend into sublist
982                         if( item->get_sublist() && item->get_expand() ) {
983                                 get_items_height(item->get_sublist(),
984                                         item->get_columns(),
985                                         result);
986                         }
987                 }
988         }
989
990         if( display_format == LISTBOX_TEXT && top_level ) {
991                 highest = LISTBOX_MARGIN + *result;
992         }
993
994         return highest;
995 }
996
997 int BC_ListBox::set_yposition(int position, int draw_items)
998 {
999         this->yposition = position;
1000         if( draw_items ) {
1001                 this->draw_items(1);
1002         }
1003         return 0;
1004 }
1005
1006 int BC_ListBox::set_xposition(int position)
1007 {
1008         this->xposition = position;
1009         draw_items(1);
1010         return 0;
1011 }
1012
1013 void BC_ListBox::expand_item(BC_ListBoxItem *item, int expand)
1014 {
1015         if( item ) {
1016                 item->expand = expand;
1017 // Collapse sublists if this is collapsed to make it easier to calculate
1018 // coordinates
1019                 if( item->get_sublist() )
1020                         collapse_recursive(item->get_sublist(), master_column);
1021
1022 // Set everything for autoplacement
1023                 set_autoplacement(data, 0, 1);
1024                 draw_items(1);
1025         }
1026 }
1027
1028 void BC_ListBox::collapse_recursive(ArrayList<BC_ListBoxItem*> *data,
1029                 int master_column)
1030 {
1031         for( int i=0; i<data[master_column].total; ++i ) {
1032                 BC_ListBoxItem *item = data[master_column].values[i];
1033                 if( item->get_sublist() && item->expand ) {
1034                         item->expand = 0;
1035                         collapse_recursive(item->get_sublist(), master_column);
1036                 }
1037         }
1038 }
1039
1040 void BC_ListBox::set_autoplacement(ArrayList<BC_ListBoxItem*> *data,
1041         int do_icons,
1042         int do_text)
1043 {
1044         for( int i=0; i<data[0].total; ++i ) {
1045                 for( int j=0; j<columns; ++j ) {
1046                         if( do_icons ) data[j].values[i]->autoplace_icon = 1;
1047                         if( do_text ) data[j].values[i]->autoplace_text = 1;
1048                 }
1049
1050                 BC_ListBoxItem *item = data[master_column].values[i];
1051                 if( item->get_sublist() ) {
1052                         set_autoplacement(item->get_sublist(), do_icons, do_text);
1053                 }
1054         }
1055 }
1056
1057
1058 void BC_ListBox::set_scroll_stretch(int xv, int yv)
1059 {
1060         if( xv >= 0 ) xscroll_orientation =
1061                 !xv ? SCROLL_HORIZ : SCROLL_HORIZ + SCROLL_STRETCH;
1062         if( yv >= 0 ) yscroll_orientation =
1063                 !yv ? SCROLL_VERT  : SCROLL_VERT  + SCROLL_STRETCH;
1064 }
1065
1066 int BC_ListBox::get_yscroll_x()
1067 {
1068         if( is_popup )
1069                 return popup_w - get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w();
1070         else
1071                 return get_x() + popup_w -
1072                         get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w();
1073 }
1074
1075 int BC_ListBox::get_yscroll_y()
1076 {
1077         return is_popup ? 0 : get_y();
1078 }
1079
1080 int BC_ListBox::get_yscroll_height()
1081 {
1082         return popup_h - (need_xscroll ?
1083                 get_resources()->hscroll_data[SCROLL_HANDLE_UP]->get_h() :
1084                 0);
1085 }
1086
1087 int BC_ListBox::get_xscroll_x()
1088 {
1089         return is_popup ?  0 : get_x();
1090 }
1091
1092 int BC_ListBox::get_xscroll_y()
1093 {
1094         return (is_popup ? popup_h : get_y() + popup_h) -
1095                 get_resources()->hscroll_data[SCROLL_HANDLE_UP]->get_h();
1096 }
1097
1098 int BC_ListBox::get_xscroll_width()
1099 {
1100         return popup_w - (need_yscroll ?
1101                 get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w() : 0);
1102 }
1103
1104 int BC_ListBox::get_column_offset(int column)
1105 {
1106         int x = 0;
1107         while( column > 0 ) {
1108                 x += column_width ?
1109                         column_width[--column] :
1110                         default_column_width[--column];
1111         }
1112         return x;
1113 }
1114
1115 void BC_ListBox::column_width_boundaries()
1116 {
1117         if( column_width ) {
1118                 for( int i=0; i<columns; ++i ) {
1119                         if( column_width[i] < MIN_COLUMN_WIDTH )
1120                                  column_width[i] = MIN_COLUMN_WIDTH;
1121                 }
1122         }
1123         else {
1124                 for( int i=0; i<columns; ++i ) {
1125                         if( default_column_width[i] < MIN_COLUMN_WIDTH )
1126                                  default_column_width[i] = MIN_COLUMN_WIDTH;
1127                 }
1128         }
1129 }
1130
1131 int BC_ListBox::get_column_width(int column, int clamp_right)
1132 {
1133         if( column < columns - 1 || !clamp_right )
1134                 return column_width ?  column_width[column] : default_column_width[column];
1135         return popup_w + xposition - get_column_offset(column);
1136 }
1137
1138 int BC_ListBox::get_icon_mask(BC_ListBoxItem *item,
1139         int &x, int &y, int &w, int &h)
1140 {
1141         if( display_format == LISTBOX_ICONS ) {
1142                 x = get_item_x(item);
1143                 y = get_item_y(item);
1144                 w = get_icon_w(item) + ICON_MARGIN * 2;
1145                 h = get_icon_h(item) + ICON_MARGIN * 2;
1146         }
1147         else
1148                 x = y = w = h = 0;
1149         return 0;
1150 }
1151
1152 int BC_ListBox::get_text_mask(BC_ListBoxItem *item,
1153         int &x, int &y, int &w, int &h)
1154 {
1155         x = get_item_x(item);
1156         y = get_item_y(item);
1157
1158         if( display_format == LISTBOX_ICONS ) {
1159                 if( icon_position == ICON_LEFT ) {
1160                         x += get_icon_w(item) + ICON_MARGIN * 2;
1161                         y += get_icon_h(item) - get_text_h(item);
1162                 }
1163                 else {
1164                         y += get_icon_h(item) + ICON_MARGIN;
1165                 }
1166
1167                 w = get_text_w(item) + ICON_MARGIN * 2;
1168                 h = get_text_h(item) + ICON_MARGIN * 2;
1169         }
1170         else
1171         if( display_format == LISTBOX_TEXT ) {
1172                 w = get_text_w(item) + LISTBOX_MARGIN * 2;
1173                 h = get_text_h(item);
1174         }
1175         else {
1176                 w = get_text_width(MEDIUMFONT, item->text) + LISTBOX_MARGIN * 2;
1177                 h = row_height;
1178                 int ih = get_icon_h(item);
1179                 if( h < ih ) h = ih;
1180         }
1181         return 0;
1182 }
1183
1184 int BC_ListBox::get_item_highlight(ArrayList<BC_ListBoxItem*> *data,
1185                 int column, int item)
1186 {
1187         BC_Resources *resources = get_resources();
1188         if( data[column].values[item]->selected )
1189                 return resources->listbox_selected;
1190         if( highlighted_item >= 0 &&
1191             highlighted_ptr == data[master_column].values[item] )
1192                 return resources->listbox_highlighted;
1193         return resources->listbox_inactive;
1194 }
1195
1196 int BC_ListBox::get_item_color(ArrayList<BC_ListBoxItem*> *data,
1197                 int column, int item)
1198 {
1199         int color = data[column].values[item]->color;
1200         if( color == -1 ) color = get_resources()->listbox_text;
1201         if( get_item_highlight(data, column, item) == color )
1202                 return BLACK;
1203         return color;
1204 }
1205
1206 int BC_ListBox::get_from_column()
1207 {
1208         return dragged_title;
1209 }
1210
1211 int BC_ListBox::get_to_column()
1212 {
1213         return highlighted_title;
1214 }
1215
1216
1217 BC_ListBoxItem* BC_ListBox::get_selection(int column, int selection_number)
1218 {
1219         return get_selection_recursive(data,
1220                 column,
1221                 selection_number);
1222 }
1223
1224 BC_ListBoxItem* BC_ListBox::get_selection_recursive(ArrayList<BC_ListBoxItem*> *data,
1225                 int column, int selection_number)
1226 {
1227         if( !data ) return 0;
1228
1229         for( int i=0; i<data[master_column].total; ++i ) {
1230                 BC_ListBoxItem *item = data[master_column].values[i];
1231                 if( item->selected ) {
1232 //printf("BC_ListBox::get_selection_recursive %d\n", __LINE__);
1233                         selection_number--;
1234                         if( selection_number < 0 ) {
1235
1236                                 return data[column].values[i];
1237                         }
1238                 }
1239
1240                 if( item->get_sublist() ) {
1241                         BC_ListBoxItem *result = get_selection_recursive(item->get_sublist(),
1242                                 column,
1243                                 selection_number);
1244                         if( result ) return result;
1245                 }
1246         }
1247
1248         return 0;
1249 }
1250
1251
1252 int BC_ListBox::get_selection_number(int column, int selection_number)
1253 {
1254         return get_selection_number_recursive(data, column, selection_number);
1255 }
1256
1257 int BC_ListBox::get_selection_number_recursive(ArrayList<BC_ListBoxItem*> *data,
1258                 int column, int selection_number, int *counter)
1259 {
1260         int temp = -1;
1261         if( !data ) return 0;
1262         if( !counter ) counter = &temp;
1263
1264         for( int i=0; i<data[master_column].total; ++i ) {
1265                 (*counter)++;
1266                 BC_ListBoxItem *item = data[master_column].values[i];
1267                 if( item->selected ) {
1268                         selection_number--;
1269                         if( selection_number < 0 ) {
1270                                 return (*counter);
1271                         }
1272                 }
1273                 if( item->get_sublist() ) {
1274                         int result = get_selection_number_recursive(item->get_sublist(),
1275                                 column, selection_number, counter);
1276                         if( result >= 0 ) return result;
1277                 }
1278         }
1279         return -1;
1280 }
1281
1282
1283 int BC_ListBox::set_selection_mode(int mode)
1284 {
1285         this->selection_mode = mode;
1286         return 0;
1287 }
1288
1289 void BC_ListBox::delete_columns()
1290 {
1291         if( column_titles ) {
1292                 for( int i=0; i<columns; ++i ) {
1293                         delete [] column_titles[i];
1294                 }
1295                 delete [] column_titles;
1296         }
1297
1298         if( column_width ) delete [] column_width;
1299
1300         column_titles = 0;
1301         column_width = 0;
1302 }
1303
1304 // Need to copy titles so EDL can change
1305 void BC_ListBox::set_columns(const char **column_titles, int *column_width, int columns)
1306 {
1307         if( (!column_titles && column_width) ||
1308             (column_titles && !column_width) ) {
1309                 printf("BC_ListBox::set_columns either column_titles or column_width == NULL but not both.\n");
1310                 return;
1311         }
1312
1313         delete_columns();
1314         if( column_titles ) {
1315                 this->column_titles = new char*[columns];
1316                 for( int i=0; i<columns; ++i ) {
1317                         this->column_titles[i] = new char[strlen(column_titles[i]) + 1];
1318                         strcpy(this->column_titles[i], column_titles[i]);
1319                 }
1320         }
1321         if( column_width ) {
1322                 this->column_width = new int[columns];
1323                 for( int i=0; i<columns; ++i ) {
1324                         this->column_width[i] = column_width[i];
1325                 }
1326         }
1327
1328         this->columns = columns;
1329
1330 }
1331
1332
1333 int BC_ListBox::update(ArrayList<BC_ListBoxItem*> *data,
1334         const char **column_titles, int *column_widths, int columns,
1335         int xposition, int yposition, int highlighted_number,
1336         int recalc_positions, int draw)
1337 {
1338         set_columns(column_titles, column_widths, columns);
1339         this->data = data;
1340         this->yposition = yposition;
1341         this->xposition = xposition;
1342         this->highlighted_item = highlighted_number;
1343         this->highlighted_ptr = index_to_item(data, highlighted_number, 0);
1344
1345         if( recalc_positions )
1346                 set_autoplacement(data, 1, 1);
1347
1348         init_column_width();
1349
1350         if( gui && draw ) {
1351                 draw_items(0, 1);
1352                 update_scrollbars(1);
1353         }
1354
1355         return 0;
1356 }
1357
1358 void BC_ListBox::center_selection()
1359 {
1360         int selection = get_selection_number(0, 0);
1361         calculate_item_coords();
1362         center_selection(selection);
1363
1364         if( gui ) {
1365                 draw_items(1, 1);
1366                 update_scrollbars(0);
1367                 gui->show_window(1);
1368         }
1369 }
1370
1371 void BC_ListBox::move_vertical(int pixels)
1372 {
1373 }
1374
1375 void BC_ListBox::move_horizontal(int pixels)
1376 {
1377 }
1378
1379 int BC_ListBox::select_previous(int skip, BC_ListBoxItem *selected_item, int *counter,
1380                 ArrayList<BC_ListBoxItem*> *data, int *got_first, int *got_second)
1381 {
1382         int top_level = 0;
1383         if( !selected_item )
1384                 selected_item = get_selection(0, 0);
1385         int temp = -1;
1386         if( !counter )
1387                 counter = &temp;
1388         int temp2 = 0;
1389         if( !got_first ) {
1390                 got_first = &temp2;
1391                 top_level = 1;
1392         }
1393         int temp3 = 0;
1394         if( !got_second )
1395                 got_second = &temp3;
1396         if( !data )
1397                 data = this->data;
1398
1399 // Scan backwards to item pointer.  Then count visible items to get
1400 // destination.  No wraparound.
1401         do {
1402                 for( int i=data[master_column].total-1; i>=0; --i ) {
1403                         BC_ListBoxItem *current_item = data[master_column].values[i];
1404                         if( current_item->get_sublist() &&
1405                             current_item->get_expand() ) {
1406                                 int result = select_previous(skip, selected_item, counter,
1407                                         current_item->get_sublist(), got_first, got_second);
1408                                 if( *got_second )
1409                                         return result;
1410                         }
1411
1412                         if( *got_first ) {
1413                                 (*counter)++;
1414                                 if( (*counter) >= skip ) {
1415                                         for( int j=0; j<columns; ++j )
1416                                                 data[j].values[i]->selected = 1;
1417                                         (*got_second) = 1;
1418                                         return item_to_index(this->data, current_item);
1419                                 }
1420                         }
1421                         else {
1422                                 if( current_item->selected ) {
1423                                         for( int j=0; j<columns; ++j )
1424                                                 data[j].values[i]->selected = 0;
1425                                         (*got_first) = 1;
1426                                         (*counter)++;
1427                                 }
1428                         }
1429                 }
1430
1431 // Hit top of top level without finding a selected item.
1432                 if( top_level ) {
1433 // Select first item in top level and quit
1434                         BC_ListBoxItem *current_item;
1435                         (*got_first) = 1;
1436                         current_item = data[master_column].values[0];
1437
1438                         for( int j=0; j<columns; ++j )
1439                                 data[j].values[0]->selected = 1;
1440                         (*got_second) = 1;
1441                         return item_to_index(this->data, current_item);
1442                 }
1443         } while( top_level && data[master_column].total );
1444
1445         return -1;
1446 }
1447
1448 int BC_ListBox::select_next(int skip, BC_ListBoxItem *selected_item, int *counter,
1449                 ArrayList<BC_ListBoxItem*> *data, int *got_first, int *got_second)
1450 {
1451         int top_level = 0;
1452         if( !selected_item )
1453                 selected_item = get_selection(0, 0);
1454         int temp = -1;
1455         if( !counter )
1456                 counter = &temp;
1457         int temp2 = 0;
1458         if( !got_first ) {
1459                 got_first = &temp2;
1460                 top_level = 1;
1461         }
1462         int temp3 = 0;
1463         if( !got_second )
1464                 got_second = &temp3;
1465         if( !data )
1466                 data = this->data;
1467
1468 // Scan forwards to currently selected item pointer.
1469 // Then count visible items to get destination.  No wraparound.
1470         do {
1471                 for( int i=0; i<data[master_column].total; ++i ) {
1472                         BC_ListBoxItem *current_item = data[master_column].values[i];
1473
1474 // Select next item once the number items after the currently selected item
1475 // have been passed.
1476                         if( *got_first ) {
1477                                 (*counter)++;
1478                                 if( (*counter) >= skip ) {
1479                                         for( int j=0; j<columns; ++j )
1480                                                 data[j].values[i]->selected = 1;
1481                                         (*got_second) = 1;
1482                                         return item_to_index(this->data, current_item);
1483                                 }
1484                         }
1485                         else {
1486 // Got currently selected item.  Deselect it.
1487                                 if( current_item->selected ) {
1488                                         for( int j=0; j<columns; ++j )
1489                                                 data[j].values[i]->selected = 0;
1490                                         (*got_first) = 1;
1491                                         (*counter)++;
1492                                 }
1493                         }
1494
1495 // Descend into expanded level
1496                         if( current_item->get_sublist() &&
1497                             current_item->get_expand() ) {
1498                                 int result = select_next(skip, selected_item, counter,
1499                                         current_item->get_sublist(), got_first, got_second);
1500                                 if( *got_second ) {
1501                                         return result;
1502                                 }
1503                         }
1504                 }
1505
1506 // Hit bottom of top level without finding the next item.
1507                 if( top_level ) {
1508                         BC_ListBoxItem *current_item;
1509 // Select first item in top level and quit
1510                         if( !(*got_first) ) {
1511                                 (*got_first) = 1;
1512                                 current_item = data[master_column].values[0];
1513
1514                                 for( int j=0; j<columns; ++j )
1515                                         data[j].values[0]->selected = 1;
1516                                 (*got_second) = 1;
1517                         }
1518                         else {
1519 // Select last item in top level and quit
1520                                 (*got_first) = 1;
1521                                 int current_row = data[master_column].total - 1;
1522                                 current_item = data[master_column].values[current_row];
1523
1524                                 for( int j=0; j<columns; ++j )
1525                                         data[j].values[current_row]->selected = 1;
1526                                 (*got_second) = 1;
1527                         }
1528
1529                         return item_to_index(this->data, current_item);
1530                 }
1531         } while( top_level && data[master_column].total );
1532
1533         return -1;
1534 }
1535
1536
1537 void BC_ListBox::clamp_positions()
1538 {
1539         items_w = get_items_width();
1540         if( xscroll_orientation & SCROLL_STRETCH )
1541                 items_w += view_w / 4;
1542         items_h = get_items_height(data, columns);
1543         if( yscroll_orientation & SCROLL_STRETCH )
1544                 items_h += view_h / 4;
1545
1546         if( yposition < 0 ) yposition = 0;
1547         else
1548         if( yposition > items_h - view_h )
1549                 yposition = items_h - view_h;
1550
1551         if( yposition < 0 ) yposition = 0;
1552
1553         if( xposition < 0 ) xposition = 0;
1554         else
1555         if( xposition >= items_w - view_w )
1556                 xposition = items_w - view_w;
1557
1558         if( xposition < 0 ) xposition = 0;
1559 }
1560
1561 int BC_ListBox::center_selection(int selection,
1562                 ArrayList<BC_ListBoxItem*> *data, int *counter)
1563 {
1564         int temp = -1;
1565         if( !data ) data = this->data;
1566         if( !counter ) counter = &temp;
1567
1568         for( int i=0; i<data[master_column].total; ++i ) {
1569                 (*counter)++;
1570
1571 // Got it
1572                 BC_ListBoxItem *item = data[master_column].values[i];
1573                 if( (*counter) == selection ) {
1574                         BC_ListBoxItem *top_item = this->data[master_column].values[0];
1575
1576
1577                         if( display_format == LISTBOX_ICONS ) {
1578 // Icon is out of window
1579                                 if( item->icon_y-yposition  > view_h-get_text_h(item) ||
1580                                     item->icon_y-yposition < 0 ) {
1581                                         yposition = item->icon_y - view_h / 2;
1582                                 }
1583
1584                                 if( data[master_column].values[selection]->icon_x - xposition > view_w ||
1585                                     data[master_column].values[selection]->icon_x - xposition < 0 ) {
1586                                         xposition = item->icon_x - view_w / 2;
1587                                 }
1588                         }
1589                         else {
1590 // Text coordinate is out of window
1591                                 if( item->text_y-yposition  > view_h-get_text_h(item) ||
1592                                     item->text_y-yposition < 0 ) {
1593                                         yposition = item->text_y -
1594                                                 top_item->text_y -
1595                                                 view_h / 2;
1596                                 }
1597                         }
1598                         return 1;
1599                 }
1600
1601 // Descend
1602                 if( item->get_sublist() ) {
1603                         int result = center_selection(selection,
1604                                 item->get_sublist(),
1605                                 counter);
1606                         if( result ) return result;
1607                 }
1608         }
1609         return 0;
1610 }
1611
1612 void BC_ListBox::update_scrollbars(int flush)
1613 {
1614         int h_needed = items_h = get_items_height(data, columns);
1615         int w_needed = items_w = get_items_width();
1616
1617 // if( columns > 0 && column_width )
1618 // printf("BC_ListBox::update_scrollbars 1 %d %d\n", column_width[columns - 1], w_needed);
1619
1620         if( xscrollbar ) {
1621                 if( xposition != xscrollbar->get_value() )
1622                         xscrollbar->update_value(xposition);
1623
1624                 if( w_needed != xscrollbar->get_length() ||
1625                     view_w != xscrollbar->get_handlelength() )
1626                         xscrollbar->update_length(w_needed, xposition, view_w, 0);
1627         }
1628
1629         if( yscrollbar ) {
1630                 if( yposition != yscrollbar->get_value() )
1631                         yscrollbar->update_value(yposition);
1632
1633                 if( h_needed != yscrollbar->get_length() || view_h != yscrollbar->get_handlelength() )
1634                         yscrollbar->update_length(h_needed, yposition, view_h, 0);
1635         }
1636
1637         if( flush ) this->flush();
1638 }
1639
1640 int BC_ListBox::get_scrollbars()
1641 {
1642         int h_needed = items_h = get_items_height(data, columns);
1643         int w_needed = items_w = get_items_width();
1644         int flush = 0;
1645
1646         title_h = get_title_h();
1647         view_h = popup_h - title_h - 4;
1648         view_w = popup_w - 4;
1649
1650 // Create scrollbars as needed
1651         for( int i=0; i<2; ++i ) {
1652                 if( w_needed > view_w ) {
1653                         need_xscroll = 1;
1654                         view_h = popup_h -
1655                                 title_h -
1656                                 get_resources()->hscroll_data[SCROLL_HANDLE_UP]->get_h() -
1657                                 4;
1658                 }
1659                 else {
1660                         need_xscroll = 0;
1661                 }
1662
1663                 if( h_needed > view_h ) {
1664                         need_yscroll = 1;
1665                         view_w = popup_w -
1666                                 get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w() -
1667                                 4;
1668                 }
1669                 else {
1670                         need_yscroll = 0;
1671                 }
1672         }
1673
1674 // Update subwindow size
1675         int new_w = popup_w;
1676         int new_h = popup_h;
1677         if( need_xscroll ) new_h -= get_resources()->hscroll_data[SCROLL_HANDLE_UP]->get_h();
1678         if( need_yscroll ) new_w -= get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w();
1679
1680         if( !is_popup )
1681                 if( new_w != BC_WindowBase::get_w() || new_h != BC_WindowBase::get_h() )
1682                         gui->resize_window(new_w, new_h);
1683
1684         BC_WindowBase *destination = (is_popup ? gui : parent_window);
1685         if( need_xscroll ) {
1686                 if( !xscrollbar ) {
1687                         xscrollbar = new BC_ListBoxXScroll(this);
1688                         destination->add_subwindow(xscrollbar);
1689                         xscrollbar->show_window(0);
1690                         xscrollbar->bound_to = this;
1691                 }
1692                 else {
1693                     xscrollbar->update_length(w_needed, xposition, view_w, flush);
1694                         xscrollbar->reposition_window(get_xscroll_x(),
1695                                 get_xscroll_y(),
1696                                 get_xscroll_width());
1697                 }
1698         }
1699         else {
1700                 if( xscrollbar ) delete xscrollbar;
1701                 xscrollbar = 0;
1702                 xposition = 0;
1703         }
1704
1705         if( need_yscroll ) {
1706                 if( !yscrollbar ) {
1707                         yscrollbar = new BC_ListBoxYScroll(this);
1708                         destination->add_subwindow(yscrollbar);
1709                         yscrollbar->show_window(0);
1710                         yscrollbar->bound_to = this;
1711                 }
1712                 else {
1713                         yscrollbar->update_length(h_needed, yposition, view_h, flush);
1714                         yscrollbar->reposition_window(get_yscroll_x(),
1715                                 get_yscroll_y(),
1716                                 get_yscroll_height());
1717                 }
1718         }
1719         else {
1720                 if( yscrollbar ) delete yscrollbar;
1721                 yscrollbar = 0;
1722                 yposition = 0;
1723         }
1724
1725         if( !bg_surface ||
1726             view_w + 4 != bg_surface->get_w() ||
1727             view_h + 4 != bg_surface->get_h() ) {
1728                 if( bg_surface ) delete bg_surface;
1729                 bg_surface = new BC_Pixmap(gui, view_w + 4, view_h + 4);
1730                 bg_draw = 1;
1731         }
1732
1733
1734         return 0;
1735 }
1736
1737
1738 void BC_ListBox::set_drag_scroll(int value)
1739 {
1740         allow_drag_scroll = value;
1741 }
1742
1743 // Test for scrolling by dragging
1744
1745 int BC_ListBox::test_drag_scroll(int cursor_x, int cursor_y)
1746 {
1747         int result = 0;
1748         if( allow_drag_scroll ||
1749             current_operation == SELECT_RECT ) {
1750
1751                 int top_boundary = get_title_h();
1752
1753                 if( cursor_y < top_boundary ||
1754                     cursor_y >= view_h + title_h + LISTBOX_BORDER * 2 ||
1755                     cursor_x < LISTBOX_BORDER ||
1756                     cursor_x >= view_w + LISTBOX_BORDER ) {
1757                         result = 1;
1758                 }
1759         }
1760         return result;
1761 }
1762
1763 int BC_ListBox::drag_scroll_event()
1764 {
1765         int top_boundary = get_title_h();
1766         int result = 0;
1767
1768         if( get_cursor_y() < top_boundary ) {
1769                 yposition -= top_boundary - get_cursor_y();
1770                 result = 1;
1771         }
1772         else
1773         if( get_cursor_y() >= view_h + title_h + 4 ) {
1774                 yposition += get_cursor_y() - (view_h + title_h + 4);
1775                 result = 1;
1776         }
1777
1778         if( get_cursor_x() < 2 ) {
1779                 xposition -= 2 - get_cursor_x();
1780                 result = 1;
1781         }
1782         else
1783         if( get_cursor_x() >= view_w + 2 ) {
1784                 xposition += get_cursor_x() - (view_w + 2);
1785                 result = 1;
1786         }
1787
1788         if( result )
1789                 clamp_positions();
1790
1791         return result;
1792 }
1793
1794 int BC_ListBox::rectangle_scroll_event()
1795 {
1796         int old_xposition = xposition;
1797         int old_yposition = yposition;
1798         int result = drag_scroll_event();
1799
1800         if( result ) {
1801                 rect_x1 += old_xposition - xposition;
1802                 rect_y1 += old_yposition - yposition;
1803                 rect_x2 = get_cursor_x();
1804                 rect_y2 = get_cursor_y();
1805
1806                 int x1 = MIN(rect_x1, rect_x2);
1807                 int x2 = MAX(rect_x1, rect_x2);
1808                 int y1 = MIN(rect_y1, rect_y2);
1809                 int y2 = MAX(rect_y1, rect_y2);
1810
1811                 if( select_rectangle(data, x1, y1, x2, y2) ) {
1812                         selection_changed();
1813                 }
1814
1815                 clamp_positions();
1816                 draw_items(0);
1817                 update_scrollbars(1);
1818         }
1819         return result;
1820 }
1821
1822 int BC_ListBox::select_scroll_event()
1823 {
1824         int result = drag_scroll_event();
1825         if( result ) {
1826                 highlighted_item = selection_number = get_cursor_item(data,
1827                         get_cursor_x(), get_cursor_y(), &highlighted_ptr);
1828                 clamp_positions();
1829                 draw_items(0);
1830                 update_scrollbars(1);
1831                 selection_changed();
1832         }
1833         return result;
1834 }
1835
1836 int BC_ListBox::select_rectangle(ArrayList<BC_ListBoxItem*> *data,
1837                 int x1, int y1, int x2, int y2)
1838 {
1839         int result = 0;
1840         for( int i=0; i<data[master_column].total; ++i ) {
1841                 for( int j=0; j<columns; ++j ) {
1842                         BC_ListBoxItem *item = data[j].values[i];
1843                         if( display_format == LISTBOX_ICONS ) {
1844                                 int icon_x, icon_y, icon_w, icon_h;
1845                                 int text_x, text_y, text_w, text_h;
1846                                 get_icon_mask(item, icon_x, icon_y, icon_w, icon_h);
1847                                 get_text_mask(item, text_x, text_y, text_w, text_h);
1848
1849                                 if( (x2 >= icon_x && x1 < icon_x + icon_w &&
1850                                     y2 >= icon_y && y1 < icon_y + icon_h) ||
1851                                     (x2 >= text_x && x1 < text_x + text_w &&
1852                                      y2 >= text_y && y1 < text_y + text_h) ) {
1853                                         if( !item->selected ) {
1854                                                 item->selected = 1;
1855                                                 result = 1;
1856                                         }
1857                                 }
1858                                 else {
1859                                         if( item->selected ) {
1860                                                 item->selected = 0;
1861                                                 result = 1;
1862                                         }
1863                                 }
1864                         }
1865                         else {
1866                                 if( x2 >= 0 &&
1867                                     x1 < (yscrollbar ?
1868                                         gui->get_w() - get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w() :
1869                                         gui->get_w()) && y2 > 0 &&
1870                                     y1 < gui->get_h() && y2 >= get_item_y(item) &&
1871                                     y1 < get_item_y(item) + get_item_h(item) ) {
1872                                         if( !item->selected ) {
1873                                                 item->selected = 1;
1874                                                 result = 1;
1875                                         }
1876                                 }
1877                                 else {
1878                                         if( item->selected ) {
1879                                                 item->selected = 0;
1880                                                 result = 1;
1881                                         }
1882                                 }
1883                         }
1884                 }
1885
1886                 BC_ListBoxItem *item = data[master_column].values[i];
1887                 if( item->get_sublist() &&
1888                     item->get_expand() )
1889                         result |= select_rectangle(item->get_sublist(),
1890                                 x1, y1, x2, y2);
1891         }
1892         return result;
1893 }
1894
1895 int BC_ListBox::reposition_item(ArrayList<BC_ListBoxItem*> *data,
1896                 int selection_number, int x, int y, int *counter)
1897 {
1898         int temp = -1;
1899         if( !counter ) counter = &temp;
1900
1901         for( int i=0; i<data[master_column].total; ++i ) {
1902                 BC_ListBoxItem *item = data[master_column].values[i];
1903                 (*counter)++;
1904                 if( (*counter) == selection_number ) {
1905                         item->icon_x = x;
1906                         item->icon_y = y;
1907                         return 1;
1908                 }
1909 // Not recursive because it's only used for icons
1910         }
1911         return 0;
1912 }
1913
1914 void BC_ListBox::move_selection(ArrayList<BC_ListBoxItem*> *dst,
1915         ArrayList<BC_ListBoxItem*> *src)
1916 {
1917         for( int i=0; i<src[master_column].total;  ) {
1918                 BC_ListBoxItem *item = src[master_column].values[i];
1919
1920 // Move item to dst
1921                 if( item->selected ) {
1922                         for( int j=0; j<columns; ++j ) {
1923                                 dst[j].append(src[j].values[i]);
1924                                 src[j].remove_number(i);
1925                         }
1926                         continue;
1927                 }
1928 // Descend into sublist
1929                 if( item->get_sublist() ) {
1930                         move_selection(dst,
1931                                 item->get_sublist());
1932                 }
1933                 ++i;
1934         }
1935 }
1936
1937 int BC_ListBox::put_selection(ArrayList<BC_ListBoxItem*> *data,
1938                 ArrayList<BC_ListBoxItem*> *src, int destination, int *counter)
1939 {
1940         int temp = -1;
1941         if( !counter ) counter = &temp;
1942
1943         if( destination < 0 || destination >= data[master_column].total ) {
1944                 for( int j=0; j<columns; ++j ) {
1945                         for( int i=0; i<src[j].total; ++i ) {
1946                                 data[j].append(src[j].values[i]);
1947                         }
1948                 }
1949                 return 1;
1950         }
1951         else
1952         for( int i=0; i<data[master_column].total; ++i ) {
1953                 (*counter)++;
1954                 if( (*counter) == destination ) {
1955                         for( int j=0; j<columns; ++j ) {
1956                                 for( int k=0; k<src[j].total; ++k ) {
1957                                         data[j].insert(src[j].values[k], destination + k);
1958                                 }
1959                         }
1960                         return 1;
1961                 }
1962
1963                 BC_ListBoxItem *item = data[master_column].values[i];
1964                 if( item->get_sublist() ) {
1965                         if( put_selection(item->get_sublist(),
1966                                 src,
1967                                 destination,
1968                                 counter) )
1969                                 return 1;
1970                 }
1971         }
1972         return 0;
1973 }
1974
1975
1976
1977 int BC_ListBox::item_to_index(ArrayList<BC_ListBoxItem*> *data,
1978                 BC_ListBoxItem *item, int *counter)
1979 {
1980         int temp = -1;
1981         if( !counter ) counter = &temp;
1982
1983         for( int i=0; i<data[master_column].total; ++i ) {
1984                 (*counter)++;
1985                 for( int j=0; j<columns; ++j ) {
1986                         BC_ListBoxItem *new_item = data[j].values[i];
1987 //printf("BC_ListBox::item_to_index 1 %d %d %p\n", j, i, new_item);
1988                         if( new_item == item ) {
1989                                 return (*counter);
1990                         }
1991                 }
1992
1993                 BC_ListBoxItem *new_item = data[master_column].values[i];
1994                 if( new_item->get_sublist() ) {
1995                         if( item_to_index(new_item->get_sublist(),
1996                                 item,
1997                                 counter) >= 0 )
1998                                 return (*counter);
1999                 }
2000         }
2001
2002         return -1;
2003 }
2004
2005 BC_ListBoxItem* BC_ListBox::index_to_item(ArrayList<BC_ListBoxItem*> *data,
2006                 int number, int column, int *counter)
2007 {
2008         int temp = -1;
2009         if( !counter ) counter = &temp;
2010         for( int i=0; i<data[master_column].total; ++i ) {
2011                 (*counter)++;
2012                 if( (*counter) == number ) {
2013                         return data[column].values[i];
2014                 }
2015                 BC_ListBoxItem *item = data[master_column].values[i];
2016                 if( item->get_sublist() ) {
2017                         BC_ListBoxItem *result = index_to_item(item->get_sublist(),
2018                                 number, column, counter);
2019                         if( result ) return result;
2020                 }
2021         }
2022         return 0;
2023 }
2024
2025 int BC_ListBox::get_cursor_item(ArrayList<BC_ListBoxItem*> *data, int cursor_x, int cursor_y,
2026                 BC_ListBoxItem **item_return, int *counter, int expanded)
2027 {
2028         int temp = -1;
2029         if( !data ) return -1;
2030         if( !counter ) counter = &temp;
2031
2032 // Icons are not treed
2033         if( display_format == LISTBOX_ICONS ) {
2034                 for( int j=data[master_column].total-1; j>=0; --j ) {
2035                         int icon_x, icon_y, icon_w, icon_h;
2036                         int text_x, text_y, text_w, text_h;
2037                         BC_ListBoxItem *item = data[master_column].values[j];
2038                         get_icon_mask(item, icon_x, icon_y, icon_w, icon_h);
2039                         get_text_mask(item, text_x, text_y, text_w, text_h);
2040
2041                         if( (cursor_x >= icon_x && cursor_x < icon_x + icon_w &&
2042                              cursor_y >= icon_y && cursor_y < icon_y + icon_h) ||
2043                             (cursor_x >= text_x && cursor_x < text_x + text_w &&
2044                              cursor_y >= text_y && cursor_y < text_y + text_h) ) {
2045                                 if( item_return ) (*item_return) = item;
2046                                 return j;
2047                         }
2048                 }
2049         }
2050         else if( gui ) {
2051 // Text is treed
2052 // Cursor is inside items rectangle
2053                 if( cursor_x >= 0 &&
2054                     cursor_x < (yscrollbar ?
2055                                 gui->get_w() - get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w() :
2056                                 gui->get_w()) &&
2057 // Only clamp y if we're not in a SELECT operation.
2058                     (current_operation == BC_ListBox::SELECT ||
2059                         (cursor_y > get_title_h() + LISTBOX_BORDER &&
2060                          cursor_y < gui->get_h())) ) {
2061 // Search table for cursor obstruction
2062                         for( int i=0; i<data[master_column].total; ++i ) {
2063                                 BC_ListBoxItem *item = data[master_column].values[i];
2064                                 (*counter)++;
2065
2066 // Cursor is inside item on current level
2067                                 if( expanded && item->selectable &&
2068                                     cursor_y >= get_item_y(item) &&
2069                                     cursor_y < get_item_y(item) + get_item_h(item) ) {
2070 //printf("BC_ListBox::get_cursor_item %d %d %p\n", master_column, i, item);
2071                                         if( item_return ) (*item_return) = item;
2072                                         return (*counter);
2073                                 }
2074
2075 // Descend into sublist
2076                                 if( item->get_sublist() ) {
2077                                         if( get_cursor_item(item->get_sublist(),
2078                                               cursor_x, cursor_y, item_return, counter,
2079                                               item->get_expand()) >= 0 )
2080                                                 return (*counter);
2081                                 }
2082                         }
2083                 }
2084         }
2085
2086         return -1;
2087 }
2088
2089 int BC_ListBox::repeat_event(int64_t duration)
2090 {
2091         switch( current_operation ) {
2092 // Repeat out of bounds selection
2093         case SELECT_RECT:
2094                 if( duration != get_resources()->scroll_repeat ) break;
2095                 return rectangle_scroll_event();
2096
2097         case DRAG_ITEM:
2098                 if( duration != get_resources()->scroll_repeat ) break;
2099                 if( !drag_scroll_event() ) break;
2100                 clamp_positions();
2101                 draw_items(0);
2102                 update_scrollbars(1);
2103                 return 1;
2104
2105         case SELECT:
2106                 if( duration != get_resources()->scroll_repeat ) break;
2107                 return select_scroll_event();
2108
2109         case NO_OPERATION:
2110 // Show tooltip
2111                 if( duration != get_resources()->tooltip_delay ) break;
2112                 if( !button_highlighted || !is_popup ) break;
2113                 if( !tooltip_text || !tooltip_text[0] ) break;
2114                 show_tooltip();
2115                 return 1;
2116         }
2117         return 0;
2118 }
2119
2120
2121 int BC_ListBox::cursor_enter_event()
2122 {
2123         int result = 0;
2124
2125         switch( current_operation ) {
2126 // Cursor moved over button, pressed, and exited.
2127         case BUTTON_DOWN_SELECT:
2128                 if( top_level->event_win == win ) {
2129                         current_operation = BUTTON_DN;
2130                         result = 1;
2131                         button_highlighted = 1;
2132                         draw_button(1);
2133                 }
2134                 break;
2135
2136         case NO_OPERATION:
2137 // Cursor entered button
2138                 if( is_popup && top_level->event_win == win ) {
2139                         button_highlighted = 1;
2140                         result = 1;
2141                         draw_button(1);
2142                 }
2143                 else
2144 // TODO: Need to get the highlighted column title or item
2145                 if( gui && top_level->event_win == gui->win ) {
2146                         list_highlighted = 1;
2147                         draw_border(1);
2148                         result = 1;
2149                 }
2150                 break;
2151         }
2152
2153         return result;
2154 }
2155
2156 int BC_ListBox::cursor_leave_event()
2157 {
2158         if( current_operation == COLUMN_DRAG ) return 0;
2159
2160 // Left button area
2161         if( button_highlighted ) {
2162                 button_highlighted = 0;
2163                 hide_tooltip();
2164                 draw_button(1);
2165         }
2166
2167         if( list_highlighted ) {
2168                 list_highlighted = 0;
2169                 highlighted_item = -1;
2170                 highlighted_ptr = 0;
2171                 highlighted_title = -1;
2172                 int redraw_toggles = 0;
2173                 for( int i=0; i<expanders.total; ++i )
2174                         expanders.values[i]->cursor_leave_event(&redraw_toggles);
2175
2176                 draw_items(1);
2177         }
2178
2179         return 0;
2180 }
2181
2182 int BC_ListBox::get_first_selection(ArrayList<BC_ListBoxItem*> *data, int *result)
2183 {
2184         int temp = -1;
2185         if( !result ) result = &temp;
2186
2187         for( int i=0; i<data[master_column].total; ++i ) {
2188                 BC_ListBoxItem *item = data[master_column].values[i];
2189                 (*result)++;
2190                 if( item->selected ) return (*result);
2191                 if( item->get_sublist() ) {
2192                         if( get_first_selection(item->get_sublist(), result) >= 0 )
2193                                 return (*result);
2194                 }
2195         }
2196         return -1;
2197 }
2198
2199 int BC_ListBox::get_total_items(ArrayList<BC_ListBoxItem*> *data,
2200         int *result,
2201         int master_column)
2202 {
2203         int temp = 0;
2204         if( !result ) result = &temp;
2205
2206         for( int i=0; i<data[master_column].total; ++i ) {
2207                 (*result)++;
2208                 if( data[master_column].values[i]->get_sublist() )
2209                         get_total_items(data[master_column].values[i]->get_sublist(),
2210                                 result,
2211                                 master_column);
2212         }
2213
2214         return (*result);
2215 }
2216
2217
2218 int BC_ListBox::get_last_selection(ArrayList<BC_ListBoxItem*> *data,
2219         int *result)
2220 {
2221         int temp = -1;
2222         int top_level = 0;
2223         if( !result ) {
2224                 result = &temp;
2225                 top_level = 1;
2226         }
2227
2228         for( int i=data[master_column].total-1; i>=0; --i ) {
2229                 BC_ListBoxItem *item = data[master_column].values[i];
2230                 (*result)++;
2231                 if( item->selected ) {
2232                         if( top_level )
2233                                 return get_total_items(data, 0, master_column) - (*result) /* - 1 */;
2234                         else
2235                                 return (*result);
2236                 }
2237
2238                 if( item->get_sublist() ) {
2239                         if( get_last_selection(item->get_sublist(), result) >= 0 ) {
2240                                 if( top_level )
2241                                         return get_total_items(data, 0, master_column) - (*result) /* - 1 */;
2242                                 else
2243                                         return (*result);
2244                         }
2245                 }
2246         }
2247         return -1;
2248 }
2249
2250 void BC_ListBox::select_range(ArrayList<BC_ListBoxItem*> *data,
2251                 int start, int end, int *current)
2252 {
2253         int temp = -1;
2254         if( !current ) current = &temp;
2255
2256         for( int i=0; i<data[master_column].total; ++i ) {
2257                 (*current)++;
2258                 if( (*current) >= start && (*current) < end ) {
2259                         for( int j=0; j<columns; ++j )
2260                                 data[j].values[i]->selected = 1;
2261                 }
2262                 BC_ListBoxItem *item = data[master_column].values[i];
2263                 if( item->get_sublist() )
2264                         select_range(item->get_sublist(), start, end, current);
2265         }
2266 }
2267
2268
2269 // Fill items between current selection and new selection
2270 int BC_ListBox::expand_selection(int button_press, int selection_number)
2271 {
2272         int old_selection_start = selection_start;
2273         int old_selection_end = selection_end;
2274
2275 // printf("BC_ListBox::expand_selection %d %d\n",
2276 // selection_center,
2277 // selection_number);
2278
2279 // Calculate the range to select based on selection_center and selection_number
2280         if( selection_number < selection_center ) {
2281                 selection_start = selection_number;
2282         }
2283         else {
2284                 selection_end = selection_number + 1;
2285         }
2286
2287 //printf("BC_ListBox::expand_selection %d %d %d %d\n", old_selection_start, old_selection_end, selection_start, selection_end);
2288 // Recurse through all the items and select the desired range
2289         select_range(data, selection_start, selection_end);
2290
2291 // Trigger redraw
2292         return (old_selection_start != selection_start ||
2293                 old_selection_end != selection_end);
2294 }
2295
2296 int BC_ListBox::toggle_item_selection(ArrayList<BC_ListBoxItem*> *data,
2297                 int selection_number, int *counter)
2298 {
2299         int temp = -1;
2300         if( !counter ) counter = &temp;
2301
2302         for( int i=0; i<data[master_column].total; ++i ) {
2303                 BC_ListBoxItem *item = data[master_column].values[i];
2304                 (*counter)++;
2305                 if( (*counter) == selection_number ) {
2306 // Get new value for selection
2307                         int selected = !item->selected;
2308 // Set row
2309                         for( int j=0; j<columns; ++j )
2310                                 data[j].values[i]->selected = selected;
2311                         return 1;
2312                 }
2313
2314 // Descend into sublist
2315                 if( item->get_sublist() ) {
2316                         if( toggle_item_selection(item->get_sublist(),
2317                               selection_number, counter) )
2318                                 return 1;
2319                 }
2320         }
2321
2322         return 0;
2323 }
2324
2325
2326 void BC_ListBox::set_all_selected(ArrayList<BC_ListBoxItem*> *data, int value)
2327 {
2328         for( int i=0; i<data[master_column].total; ++i ) {
2329                 for( int j=0; j<columns; ++j ) {
2330                         BC_ListBoxItem *item = data[j].values[i];
2331                         item->selected = value;
2332                 }
2333                 BC_ListBoxItem *item = data[master_column].values[i];
2334                 if( item->get_sublist() ) {
2335                         set_all_selected(item->get_sublist(), value);
2336                 }
2337         }
2338 }
2339
2340 void BC_ListBox::set_selected(ArrayList<BC_ListBoxItem*> *data,
2341                 int item_number, int value, int *counter)
2342 {
2343         int temp = -1;
2344         if( !counter ) counter = &temp;
2345         for( int i=0; i<data[master_column].total&&(*counter)!=item_number; ++i ) {
2346                 (*counter)++;
2347                 if( (*counter) == item_number ) {
2348                         for( int j=0; j<columns; ++j ) {
2349                                 BC_ListBoxItem *item = data[j].values[i];
2350                                 item->selected = value;
2351                         }
2352                         return;
2353                 }
2354
2355                 BC_ListBoxItem *item = data[master_column].values[i];
2356                 if( item->get_sublist() ) {
2357                         set_selected(item->get_sublist(), item_number, value, counter);
2358                 }
2359         }
2360 }
2361
2362 int BC_ListBox::update_selection(ArrayList<BC_ListBoxItem*> *data,
2363                 int selection_number, int *counter)
2364 {
2365         int temp = -1;
2366         int result = 0;
2367         if( !counter ) counter = &temp;
2368
2369         for( int i=0; i<data[master_column].total; ++i ) {
2370                 BC_ListBoxItem *item = data[master_column].values[i];
2371                 (*counter)++;
2372                 if( (*counter) == selection_number && !item->selected ) {
2373                         result = 1;
2374                         for( int j=0; j<columns; ++j )
2375                                 data[j].values[i]->selected = 1;
2376                 }
2377                 else
2378                 if( (*counter) != selection_number && item->selected ) {
2379                         result = 1;
2380                         for( int j=0; j<columns; ++j )
2381                                 data[j].values[i]->selected = 0;
2382                 }
2383                 if( item->get_sublist() )
2384                         result |= update_selection(item->get_sublist(),
2385                                 selection_number,
2386                                 counter);
2387         }
2388         return result;
2389 }
2390
2391 void BC_ListBox::promote_selections(ArrayList<BC_ListBoxItem*> *data,
2392                 int old_value, int new_value)
2393 {
2394         for( int i=0; i<data[master_column].total; ++i ) {
2395                 for( int j=0; j<columns; ++j ) {
2396                         BC_ListBoxItem *item = data[j].values[i];
2397                         if( item->selected == old_value ) item->selected = new_value;
2398                 }
2399                 BC_ListBoxItem *item = data[master_column].values[i];
2400                 if( item->get_sublist() )
2401                         promote_selections(item->get_sublist(), old_value, new_value);
2402         }
2403 }
2404
2405 int BC_ListBox::focus_out_event()
2406 {
2407         deactivate();
2408         return 0;
2409 }
2410
2411 int BC_ListBox::button_press_event()
2412 {
2413         int result = 0;
2414         BC_ListBoxItem *current_item = 0;
2415         int new_cursor;
2416         int do_selection_change = 0;
2417         const int debug = 0;
2418
2419         hide_tooltip();
2420         if( debug ) printf("BC_ListBox::button_press_event %d this=%p event_win=%p %p %p %p\n",
2421                 __LINE__, this, (void*)top_level->event_win,
2422                 (void*)(gui ? gui->win : 0), (void*)win, (void*)parent_window->win);
2423
2424 // Pressed in button
2425         if( is_popup && top_level->event_win == win ) {
2426                 if( debug ) printf("BC_ListBox::button_press_event %d\n", __LINE__);
2427                 current_operation = BUTTON_DN;
2428                 draw_button(1);
2429
2430 // Deploy listbox
2431                 if( !active && !disabled ) {
2432                         top_level->deactivate();
2433                         activate();
2434                 }
2435
2436                 result = 1;
2437         }
2438         else
2439 // Pressed in scrollbar
2440         if( (xscrollbar && top_level->event_win == xscrollbar->win) ||
2441             (yscrollbar && top_level->event_win == yscrollbar->win) ) {
2442                 if( debug ) printf("BC_ListBox::button_press_event %d\n", __LINE__);
2443                 result = 0;
2444         }
2445         else
2446 // Pressed in items
2447         if( gui && top_level->event_win == gui->win ) {
2448                 if( debug ) printf("BC_ListBox::button_press_event %d\n", __LINE__);
2449
2450 // Activate list items
2451 // If it is a suggestion popup, it is visible without being active
2452                 if( !active ) {
2453                         if( debug ) printf("BC_ListBox::button_press_event %d\n", __LINE__);
2454                         if( !is_suggestions ) top_level->deactivate();
2455                         if( debug ) printf("BC_ListBox::button_press_event %d\n", __LINE__);
2456                         activate();
2457                         if( debug ) printf("BC_ListBox::button_press_event %d\n", __LINE__);
2458                 }
2459
2460                 if( current_operation != NO_OPERATION ) {
2461                         switch( current_operation ) {
2462                         case DRAG_ITEM:
2463                         case COLUMN_DRAG:
2464                                 return drag_stop_event();
2465                         }
2466                 }
2467
2468 // Wheel mouse pressed
2469                 if( get_buttonpress() == 4 ) {
2470                         if( current_operation == NO_OPERATION ) {
2471                                 current_operation = WHEEL;
2472                                 if( yscrollbar ) {
2473                                         set_yposition(yposition - gui->get_h() / 10, 0);
2474                                         clamp_positions();
2475                                         update_scrollbars(0);
2476                                         highlighted_ptr = 0;
2477                                         highlighted_item = get_cursor_item(data,
2478                                                 top_level->cursor_x, top_level->cursor_y,
2479                                                 &highlighted_ptr);
2480                                         draw_items(1);
2481                                         result = 1;
2482                                 }
2483                         }
2484                 }
2485                 else
2486                 if( get_buttonpress() == 5 ) {
2487                         if( current_operation == NO_OPERATION ) {
2488                                 current_operation = WHEEL;
2489                                 if( yscrollbar ) {
2490                                         set_yposition(yposition + gui->get_h() / 10, 0);
2491                                         clamp_positions();
2492                                         update_scrollbars(0);
2493                                         highlighted_ptr = 0;
2494                                         highlighted_item = get_cursor_item(data,
2495                                                 top_level->cursor_x, top_level->cursor_y,
2496                                                 &highlighted_ptr);
2497                                         draw_items(1);
2498                                         result = 1;
2499                                 }
2500                         }
2501                 }
2502                 else
2503 // Pressed over column title division
2504                 if( test_column_divisions(gui->get_cursor_x(),
2505                         gui->get_cursor_y(),
2506                         new_cursor) ) {
2507                         drag_cursor_x = gui->get_cursor_x() + xposition;
2508                         if( column_width )
2509                                 drag_column_w = column_width[highlighted_division - 1];
2510                         else
2511                                 drag_column_w = default_column_width[highlighted_division - 1];
2512
2513                         current_operation = DRAG_DIVISION;
2514                         reset_query();
2515                 }
2516                 else
2517 // Pressed in column title
2518                 if( test_column_titles(gui->get_cursor_x(), gui->get_cursor_y()) ) {
2519                         current_operation = COLUMN_DN;
2520                         button_highlighted = 0;
2521                         list_highlighted = 1;
2522                         draw_items(1);
2523                         result = 1;
2524                 }
2525                 else
2526 // Pressed in expander
2527                 if( test_expanders() ) {
2528                         current_operation = EXPAND_DN;
2529 // Need to redraw items because of alpha
2530                         draw_items(1);
2531                         result = 1;
2532                 }
2533                 else
2534 // Pressed over item
2535                 if( (selection_number = get_cursor_item(data,
2536                                         gui->get_cursor_x(), gui->get_cursor_y(),
2537                                         &current_item)) >= 0 ) {
2538                         if( debug ) printf("BC_ListBox::button_press_event %d\n", __LINE__);
2539
2540 // Get item button was pressed over
2541                         selection_number2 = selection_number1;
2542                         selection_number1 = selection_number;
2543
2544                         selection_start = -1;
2545                         selection_end = -1;
2546
2547
2548 // Multiple item selection is possible
2549                         if( selection_mode == LISTBOX_MULTIPLE &&
2550                             (ctrl_down() || shift_down()) ) {
2551 // Expand text selection.
2552 // Fill items between selected region and current item.
2553                                 if( shift_down() &&
2554                                     (display_format == LISTBOX_TEXT ||
2555                                      display_format == LISTBOX_ICON_LIST) ) {
2556 // Get first item selected
2557                                         selection_start = get_first_selection(data);
2558 // Get last item selected
2559                                         selection_end = get_last_selection(data);
2560 // Get center of selected region
2561                                         if( selection_end > selection_start ) {
2562                                                 selection_center = (selection_end + selection_start) >> 1;
2563                                         }
2564                                         else {
2565                                                 selection_center = selection_number;
2566                                         }
2567 // Deselect everything.
2568                                         set_all_selected(data, 0);
2569 // Select just the items
2570                                         expand_selection(1, selection_number);
2571                                         new_value = 1;
2572                                 }
2573 // Toggle a single item on or off
2574                                 else {
2575                                         toggle_item_selection(data, selection_number);
2576                                         new_value = current_item->selected;
2577                                 }
2578                         }
2579 // Select single item
2580                         else {
2581                                 if( !current_item->selected ) {
2582                                         set_all_selected(data, 0);
2583                                         set_selected(data,
2584                                                 selection_number,
2585                                                 1);
2586                                 }
2587                                 new_value = 1;
2588                         }
2589
2590
2591                         current_operation = SELECT;
2592                         highlighted_item = selection_number;
2593                         highlighted_ptr = current_item;
2594                         button_highlighted = 0;
2595                         list_highlighted = 1;
2596                         reset_query();
2597                         draw_items(1);
2598                         do_selection_change = 1;
2599                         result = 1;
2600                 }
2601                 else
2602 // Pressed over nothing.  Start rectangle selection.
2603                 if( data ) {
2604                         if( get_buttonpress() == 1 &&
2605                             selection_mode == LISTBOX_MULTIPLE ) {
2606                                 if( !shift_down() ) {
2607 // Deselect all and redraw if anything was selected
2608                                         if( get_selection_number(0, 0) >= 0 ) {
2609                                                 set_all_selected(data, 0);
2610                                                 draw_items(1);
2611                                                 do_selection_change = 1;
2612                                                 result = 1;
2613                                         }
2614                                 }
2615                                 else {
2616 // Promote selections to protect from a rectangle selection
2617                                         promote_selections(data, 1, 2);
2618                                 }
2619
2620 // Start rectangle selection
2621                                 current_operation = SELECT_RECT;
2622                                 rect_x1 = rect_x2 = get_cursor_x();
2623                                 rect_y1 = rect_y2 = get_cursor_y();
2624                         }
2625                 }
2626
2627
2628                 reset_query();
2629         }
2630         else
2631 // Suggestion box is not active but visible, so lie to get it to deactivate
2632         if( is_popup && (active || (is_suggestions && gui)) ) {
2633                 active = 1;
2634                 if( debug ) printf("BC_ListBox::button_press_event %d\n", __LINE__);
2635                 deactivate();
2636                 result = 1;
2637         }
2638
2639
2640         if( do_selection_change ) selection_changed();
2641         if( debug ) printf("BC_ListBox::button_press_event %d %d\n", __LINE__, result);
2642
2643         return result;
2644 }
2645
2646 int BC_ListBox::button_release_event()
2647 {
2648         int result = 0;
2649         int cursor_x, cursor_y;
2650         int do_event = 0;
2651         new_value = 0;
2652         unset_scroll_repeat();
2653
2654 //printf("BC_ListBox::button_release_event 1 %d\n", current_operation);
2655         switch( current_operation ) {
2656         case DRAG_DIVISION:
2657                 current_operation = NO_OPERATION;
2658                 result = 1;
2659                 break;
2660
2661         case WHEEL:
2662                 current_operation = NO_OPERATION;
2663                 result = 1;
2664                 break;
2665
2666 // Release item selection
2667         case BUTTON_DOWN_SELECT:
2668         case SELECT:
2669 //printf("BC_ListBox::button_release_event 10\n");
2670                 current_operation = NO_OPERATION;
2671                 if( gui ) {
2672                         translate_coordinates(top_level->event_win, gui->win,
2673                                 gui->get_cursor_x(), gui->get_cursor_y(),
2674                                 &cursor_x, &cursor_y);
2675                         selection_number1 = selection_number =
2676                                 get_cursor_item(data, cursor_x, cursor_y);
2677 //printf("BC_ListBox::button_release_event %d %d\n", selection_number2, selection_number1);
2678                 }
2679
2680                 if( is_popup ) {
2681                         button_releases++;
2682                         if( selection_number >= 0 ) {
2683                                 deactivate();
2684                                 do_event = 1;
2685                         }
2686                         else
2687 // Second button release outside button
2688                         if( button_releases > 1 ) {
2689                                 deactivate();
2690                         }
2691                 }
2692                 else {
2693                         if( top_level->get_double_click() &&
2694                             selection_number2 == selection_number1 &&
2695                             selection_number2 >= 0 && selection_number1 >= 0 ) {
2696                                 do_event = 1;
2697                         }
2698                         result = 1;
2699                 }
2700                 break;
2701
2702
2703         case SELECT_RECT:
2704                 if( data ) {
2705 // Demote selections from rectangle selection
2706                         promote_selections(data, 2, 1);
2707                 }
2708
2709 // Hide rectangle overlay
2710                 draw_rectangle(1);
2711                 current_operation = NO_OPERATION;
2712                 result = 1;
2713                 break;
2714
2715 // Release popup button
2716         case BUTTON_DN:
2717                 hide_tooltip();
2718                 current_operation = NO_OPERATION;
2719                 button_releases++;
2720                 draw_button(1);
2721
2722 // Second button release inside button
2723                 if( button_releases > 1 ) {
2724                         deactivate();
2725                 }
2726                 result = 1;
2727                 break;
2728
2729         case COLUMN_DN:
2730                 current_operation = NO_OPERATION;
2731 // Update the sort column and the sort order for the user only if the existing
2732 // sort column is valid.
2733                 if( sort_column >= 0 ) {
2734 // Invert order only if column is the same
2735                         if( highlighted_title == sort_column )
2736                                 sort_order = sort_order == SORT_ASCENDING ?
2737                                         SORT_DESCENDING : SORT_ASCENDING;
2738 // Set the new sort column
2739                         sort_column = highlighted_title;
2740                         if( !sort_order_event() ) {
2741                                 draw_titles(1);
2742                         }
2743                 }
2744                 else
2745 // Sorting not enabled.  Redraw the title state.
2746                 {
2747                         draw_titles(1);
2748                 }
2749                 result = 1;
2750                 break;
2751
2752         case EXPAND_DN: {
2753                 int redraw_toggles = 0;
2754                 for( int i=0; i<expanders.total&&!result; ++i ) {
2755                         if( expanders.values[i]->button_release_event(&redraw_toggles) ) {
2756                                 result = 1;
2757                         }
2758                 }
2759 // Need to redraw items because of alpha
2760                 if( redraw_toggles ) draw_items(1);
2761                 current_operation = NO_OPERATION;
2762                 break; }
2763
2764         default:
2765 // Can't default to NO_OPERATION because it may be used in a drag event.
2766                 break;
2767         }
2768
2769
2770         if( do_event ) handle_event();
2771
2772 //printf("BC_ListBox::button_release_event %d %d\n", __LINE__, get_window_lock());
2773         return result;
2774 }
2775
2776 int BC_ListBox::get_title_h()
2777 {
2778         if( display_format == LISTBOX_TEXT ||
2779             display_format == LISTBOX_ICON_LIST )
2780                 return column_titles ? column_bg[0]->get_h() : 0;
2781         return 0;
2782 }
2783
2784 void BC_ListBox::reset_cursor(int new_cursor)
2785 {
2786         if( is_popup ) {
2787                 if( gui->get_cursor() != new_cursor ) {
2788                         gui->set_cursor(new_cursor, 0, 0);
2789                 }
2790         }
2791         else
2792         if( get_cursor() != new_cursor ) {
2793                 set_cursor(new_cursor, 0, 0);
2794         }
2795 }
2796
2797 int BC_ListBox::test_column_divisions(int cursor_x, int cursor_y, int &new_cursor)
2798 {
2799         if( gui && column_titles &&
2800             cursor_y >= 0 && cursor_y < get_title_h() &&
2801             cursor_x >= 0 && cursor_x < gui->get_w() ) {
2802                 for( int i=1; i<columns; ++i ) {
2803                         if( cursor_x >= -xposition + get_column_offset(i) - 5 &&
2804                             cursor_x <  -xposition + get_column_offset(i) +
2805                                         get_resources()->listbox_title_hotspot ) {
2806                                 highlighted_item = -1;
2807                                 highlighted_ptr = 0;
2808                                 highlighted_division = i;
2809                                 highlighted_title = -1;
2810                                 list_highlighted = 1;
2811                                 new_cursor = HSEPARATE_CURSOR;
2812                                 return 1;
2813                         }
2814                 }
2815         }
2816         highlighted_division = -1;
2817         return 0;
2818 }
2819
2820 int BC_ListBox::test_column_titles(int cursor_x, int cursor_y)
2821 {
2822         if( gui && column_titles &&
2823             cursor_y >= 0 && cursor_y < get_title_h() &&
2824             cursor_x >= 0 && cursor_x < gui->get_w() ) {
2825                 for( int i=0; i<columns; ++i ) {
2826                         if( cursor_x >= -xposition + get_column_offset(i) &&
2827                                 (cursor_x < -xposition + get_column_offset(i + 1) ||
2828                                         i == columns - 1) ) {
2829                                 highlighted_item = -1;
2830                                 highlighted_ptr = 0;
2831                                 highlighted_division = -1;
2832                                 highlighted_title = i;
2833                                 list_highlighted = 1;
2834                                 return 1;
2835                         }
2836                 }
2837         }
2838         highlighted_title = -1;
2839         return 0;
2840 }
2841
2842 int BC_ListBox::test_expanders()
2843 {
2844         for( int i=0; i<expanders.total; ++i ) {
2845                 if( expanders.values[i]->button_press_event() ) {
2846                         current_operation = EXPAND_DN;
2847                         draw_toggles(1);
2848                         return 1;
2849                 }
2850         }
2851         return 0 ;
2852 }
2853
2854 int BC_ListBox::cursor_motion_event()
2855 {
2856         int redraw = 0, result = 0;
2857         int new_cursor = ARROW_CURSOR;
2858
2859         selection_number = -1;
2860
2861
2862         switch( current_operation ) {
2863         case BUTTON_DN:
2864 // Button pressed and slid off button
2865                 if( !cursor_inside() ) {
2866                         current_operation = BUTTON_DOWN_SELECT;
2867                         draw_button(1);
2868                         result = 1;
2869                 }
2870                 break;
2871
2872         case DRAG_DIVISION: {
2873 //              int new_w = get_cursor_x() +
2874 //                      xposition -
2875 //                      get_column_offset(highlighted_division - 1);
2876                 int difference = get_cursor_x() + xposition - drag_cursor_x;
2877                 int new_w = drag_column_w + difference;
2878
2879                 new_cursor = HSEPARATE_CURSOR;
2880
2881                 if( column_width ) {
2882                         column_width[highlighted_division - 1] = new_w;
2883                 }
2884                 else {
2885                         default_column_width[highlighted_division - 1] = new_w;
2886                 }
2887
2888                 column_width_boundaries();
2889
2890 // Force update of coords
2891                 set_autoplacement(data, 0, 1);
2892                 column_resize_event();
2893
2894                 clamp_positions();
2895                 draw_items(0);
2896                 update_scrollbars(1);
2897                 result = 1;
2898                 break; }
2899
2900         case SELECT_RECT: {
2901                 if( test_drag_scroll(get_cursor_x(), get_cursor_y()) )
2902                         set_scroll_repeat();
2903
2904                 int old_x1 = MIN(rect_x1, rect_x2);
2905                 int old_x2 = MAX(rect_x1, rect_x2);
2906                 int old_y1 = MIN(rect_y1, rect_y2);
2907                 int old_y2 = MAX(rect_y1, rect_y2);
2908
2909                 int new_rect_x2 = get_cursor_x();
2910                 int new_rect_y2 = get_cursor_y();
2911
2912                 int x1 = MIN(rect_x1, new_rect_x2);
2913                 int x2 = MAX(rect_x1, new_rect_x2);
2914                 int y1 = MIN(rect_y1, new_rect_y2);
2915                 int y2 = MAX(rect_y1, new_rect_y2);
2916
2917 // Adjust rectangle coverage
2918                 if( old_x1 != x1 || old_x2 != x2 ||
2919                     old_y1 != y1 || old_y2 != y2 ) {
2920                         if( data ) {
2921                                 redraw = select_rectangle(data, x1, y1, x2, y2);
2922                         }
2923
2924 // hide rectangle
2925                         if( !redraw ) {
2926 //printf("BC_ListBox::cursor_motion_event %d\n", __LINE__);
2927                                 draw_rectangle(0);
2928                         }
2929                 }
2930
2931                 rect_x2 = get_cursor_x();
2932                 rect_y2 = get_cursor_y();
2933                 if( redraw ) {
2934                         clamp_positions();
2935                         draw_items(0);
2936                         update_scrollbars(1);
2937                         selection_changed();
2938 //printf("BC_ListBox::cursor_motion_event %d\n", __LINE__);
2939                 }
2940                 else
2941                 if( old_x1 != x1 || old_x2 != x2 ||
2942                     old_y1 != y1 || old_y2 != y2 ) {
2943 //printf("BC_ListBox::cursor_motion_event %d\n", __LINE__);
2944                         draw_rectangle(1);
2945                 }
2946
2947                 result = 1;
2948                 break; }
2949
2950         case SELECT: {
2951                 int old_highlighted_item = highlighted_item;
2952
2953                 if( test_drag_scroll(get_cursor_x(), get_cursor_y()) )
2954                         set_scroll_repeat();
2955
2956                 highlighted_item = selection_number = get_cursor_item(data,
2957                         get_cursor_x(), get_cursor_y(), &highlighted_ptr);
2958                 result = 1;
2959
2960 // Deselect all items and select just the one we're over
2961                 if( selection_number >= 0 && !allow_drag &&
2962                     ((!shift_down() && !ctrl_down()) ||
2963                      selection_mode == LISTBOX_SINGLE) ) {
2964                         redraw = update_selection(data, selection_number);
2965                 }
2966                 else
2967 // Expand multiple selection
2968                 if( selection_mode == LISTBOX_MULTIPLE &&
2969                     (shift_down() || ctrl_down()) ) {
2970 // Expand selected region in text mode centered around initial range
2971                         if( (display_format == LISTBOX_TEXT ||
2972                              display_format == LISTBOX_ICON_LIST) &&
2973                                 shift_down() ) {
2974 // Deselect everything.
2975                                 set_all_selected(data, 0);
2976
2977 // Select just the items
2978                                 redraw = expand_selection(0, selection_number);
2979                         }
2980 // Set the one item we're over to the selection value determined in
2981 // button_press_event.
2982                         else {
2983                                 set_selected(data, selection_number, new_value);
2984                         }
2985                 }
2986
2987                 if( highlighted_item != old_highlighted_item ) {
2988                         clamp_positions();
2989                         draw_items(0);
2990                         update_scrollbars(1);
2991 //printf("BC_ListBox::cursor_motion_event %d %d\n", highlighted_item, old_highlighted_item);
2992                         selection_changed();
2993                 }
2994                 break; }
2995
2996         case BUTTON_DOWN_SELECT:
2997 // Went back into button area
2998                 if( cursor_inside() ) {
2999                         current_operation = BUTTON_DN;
3000                         draw_button(1);
3001                         result = 1;
3002                 }
3003                 else
3004 // Went into item area
3005                 if( gui ) {
3006                         int cursor_x = 0, cursor_y = 0;
3007                         translate_coordinates(top_level->event_win, gui->win,
3008                                 top_level->cursor_x, top_level->cursor_y,
3009                                 &cursor_x, &cursor_y);
3010                         int old_highlighted_item = highlighted_item;
3011                         highlighted_item = selection_number = get_cursor_item(data,
3012                                         cursor_x,
3013                                         cursor_y,
3014                                         &highlighted_ptr);
3015
3016                         if( highlighted_item != old_highlighted_item ) {
3017                                 update_selection(data, selection_number);
3018                                 draw_items(1);
3019                                 selection_changed();
3020                         }
3021                 }
3022                 break;
3023
3024         case EXPAND_DN: {
3025                 int redraw_toggles = 0;
3026                 for( int i=0; i<expanders.total&&!result; ++i ) {
3027                         result = expanders.values[i]->cursor_motion_event(
3028                                 &redraw_toggles);
3029                 }
3030                 if( redraw_toggles ) {
3031 // Need to redraw items because of the alpha
3032                         draw_items(1);
3033                 }
3034                 break; }
3035
3036         case NO_OPERATION: {
3037                 int cursor_x = get_cursor_x(), cursor_y = get_cursor_y();
3038                 if( gui && top_level->event_win == gui->win ) {
3039                         int old_highlighted_title = highlighted_title;
3040                         int old_list_highlighted = list_highlighted;
3041                         int old_highlighted_item = highlighted_item;
3042                         int redraw_titles = 0;
3043                         int redraw_border = 0;
3044                         int redraw_items = 0;
3045                         int redraw_toggles = 0;
3046                         result = 1;
3047
3048 // Test if cursor moved over a title division
3049                         test_column_divisions(cursor_x, cursor_y, new_cursor);
3050
3051 // Test if cursor moved over a title
3052                         if( highlighted_division < 0 ) {
3053                                 test_column_titles(cursor_x, cursor_y);
3054                         }
3055
3056 // Test if cursor moved over expander
3057                         if( highlighted_division < 0 && highlighted_title < 0 &&
3058                             (display_format == LISTBOX_TEXT ||
3059                              display_format == LISTBOX_ICON_LIST) ) {
3060                                 for( int i=0; i<expanders.total; ++i ) {
3061                                         expanders.values[i]->cursor_motion_event(
3062                                                 &redraw_toggles);
3063                                 }
3064 //printf("BC_ListBox::cursor_motion_event %d\n", redraw_toggles);
3065                         }
3066
3067 // Test if cursor moved over an item
3068                         if( highlighted_division < 0 && highlighted_title < 0 ) {
3069                                 highlighted_item = get_cursor_item(data,
3070                                         cursor_x,
3071                                         cursor_y,
3072                                         &highlighted_ptr);
3073                         }
3074
3075
3076 // Clear title highlighting if moved over division
3077                         if( old_highlighted_title != highlighted_title ) {
3078                                 redraw_titles = 1;
3079                         }
3080
3081 // Highlight list border
3082                         if( old_list_highlighted != list_highlighted ) {
3083                                 redraw_border = 1;
3084                         }
3085
3086 // Moved out of item area
3087                         if( old_highlighted_item != highlighted_item ) {
3088                                 redraw_items = 1;
3089                         }
3090
3091 //printf("BC_ListBox::cursor_motion_event 1 %d\n", highlighted_item);
3092
3093 // Change cursor to title division adjustment
3094                         reset_cursor(new_cursor);
3095
3096                         if( redraw_items ) {
3097                                 draw_items(0);
3098                         }
3099                         else {
3100                                 if( redraw_titles )
3101                                         draw_titles(0);
3102                                 if( redraw_border )
3103                                         draw_border(0);
3104                                 if( redraw_toggles )
3105                                         draw_toggles(0);
3106                         }
3107
3108                         if( redraw_items || redraw_titles ||
3109                             redraw_border || redraw_toggles ) {
3110                                 gui->flash();
3111                                 gui->flush();
3112                         }
3113                 }
3114
3115
3116                 if( !result && list_highlighted ) {
3117                         list_highlighted = 0;
3118                         highlighted_item = -1;
3119                         highlighted_ptr = 0;
3120                         highlighted_title = -1;
3121                         highlighted_division = -1;
3122                         draw_items(1);
3123                         result = 0;
3124                 }
3125                 break; }
3126         }
3127
3128         return result;
3129 }
3130
3131 int BC_ListBox::drag_start_event()
3132 {
3133         switch( current_operation ) {
3134         case SELECT:
3135                 if( gui && gui->is_event_win() && allow_drag ) {
3136                         BC_ListBoxItem *item_return = 0;
3137                         selection_number = get_cursor_item(data,
3138                                 top_level->cursor_x,
3139                                 top_level->cursor_y,
3140                                 &item_return);
3141
3142                         if( selection_number >= 0 ) {
3143                                 int cx, cy;
3144                                 get_abs_cursor(cx, cy);
3145                                 if( item_return->icon_vframe ) {
3146                                         drag_popup = new BC_DragWindow(this,
3147                                                 item_return->icon_vframe, cx, cy);
3148                                 }
3149                                 else
3150                                 if( item_return->icon ) {
3151                                         drag_popup = new BC_DragWindow(this,
3152                                                 item_return->icon, cx, cy);
3153                                 }
3154                                 else {
3155                                         drag_popup = new BC_DragWindow(this,
3156                                                 drag_icon_vframe, cx, cy);
3157                                 }
3158                                 current_operation = DRAG_ITEM;
3159 // require shift down for scrolling
3160                                 if( allow_drag < 0 && shift_down() )
3161                                         set_scroll_repeat();
3162                                 return 1;
3163                         }
3164                 }
3165                 break;
3166
3167         case COLUMN_DN:
3168                 if( gui && gui->is_event_win() && allow_drag_column ) {
3169                         int cx, cy;
3170                         get_abs_cursor(cx, cy);
3171                         cx -= drag_column_icon_vframe->get_w() / 2,
3172                         cy -= drag_column_icon_vframe->get_h() / 2;
3173                         drag_popup = new BC_DragWindow(this,
3174                                 drag_column_icon_vframe, cx, cy);
3175                         dragged_title = highlighted_title;
3176                         current_operation = COLUMN_DRAG;
3177                         draw_titles(1);
3178                         return 1;
3179                 }
3180                 break;
3181         }
3182
3183         return 0;
3184 }
3185
3186 int BC_ListBox::drag_motion_event()
3187 {
3188 //printf("BC_ListBox::drag_motion_event 1 %d\n", current_operation);
3189         switch( current_operation ) {
3190         case DRAG_ITEM: {
3191                 int redraw = 0;
3192                 int new_highlighted_item = -1;
3193                 BC_ListBoxItem *new_highlighted_ptr = 0;
3194                 new_highlighted_item = get_cursor_item(data,
3195                         top_level->cursor_x, top_level->cursor_y,
3196                         &new_highlighted_ptr);
3197
3198                 if( new_highlighted_item != highlighted_item ) {
3199                         redraw = 1;
3200                 }
3201
3202 // Always update highlighted value for drag_stop
3203                 highlighted_item = new_highlighted_item;
3204                 highlighted_ptr = new_highlighted_ptr;
3205 //printf("BC_ListBox::drag_motion_event 1 %p\n", highlighted_ptr);
3206                 if( redraw ) {
3207                         clamp_positions();
3208                         draw_items(0);
3209                         update_scrollbars(1);
3210                 }
3211
3212                 return drag_popup->cursor_motion_event(); }
3213
3214         case COLUMN_DRAG: {
3215                 int old_highlighted_title = highlighted_title;
3216                 test_column_titles(get_cursor_x(), get_cursor_y());
3217                 if( old_highlighted_title != highlighted_title ) {
3218                         draw_titles(1);
3219                 }
3220                 return drag_popup->cursor_motion_event(); }
3221         }
3222
3223         return 0;
3224 }
3225
3226 int BC_ListBox::drag_stop_event()
3227 {
3228         int result = 0;
3229         unset_scroll_repeat();
3230         switch( current_operation ) {
3231         case DRAG_ITEM:
3232 // Inside window boundary
3233                 if( top_level->cursor_x > 0 &&
3234                     top_level->cursor_x < gui->get_w() - drag_popup->get_w() / 2 &&
3235                     top_level->cursor_y > 0 &&
3236                     top_level->cursor_y < gui->get_h() - drag_popup->get_h() / 2 ) {
3237 // Move icon
3238
3239
3240                         if( display_format == LISTBOX_ICONS ) {
3241                                 reposition_item(data,
3242                                         selection_number,
3243                                         top_level->cursor_x - drag_popup->get_w() / 2 -
3244                                                 LISTBOX_MARGIN - 2 + xposition,
3245                                         top_level->cursor_y - drag_popup->get_h() / 2 -
3246                                                 LISTBOX_MARGIN - 2 + yposition);
3247                         }
3248                         else
3249 // Move rows
3250                         if( process_drag ) {
3251 // Move selected items from data to temporary
3252                                 ArrayList<BC_ListBoxItem*> *src_items =
3253                                         new ArrayList<BC_ListBoxItem*>[columns];
3254                                 move_selection(src_items, data);
3255 // Get destination
3256                                 int destination = highlighted_item = item_to_index(data,
3257                                         highlighted_ptr);
3258 // Insert items from temporary to data
3259                                 put_selection(data, src_items, destination);
3260
3261                                 delete [] src_items;
3262                                 set_autoplacement(data, 0, 1);
3263                         }
3264
3265                         draw_items(1);
3266                 }
3267                 else
3268                         drag_popup->drag_failure_event();
3269                 result = 1;
3270                 break;
3271
3272         case COLUMN_DRAG:
3273                 if( dragged_title != highlighted_title ) {
3274                         if( highlighted_title >= 0 ) {
3275                                 if( !move_column_event() ) draw_titles(1);
3276                         }
3277                         else
3278                                 drag_popup->drag_failure_event();
3279                 }
3280                 result = 1;
3281         }
3282
3283         if( result ) {
3284                 current_operation = NO_OPERATION;
3285                 delete drag_popup;
3286                 flush();
3287                 drag_popup = 0;
3288                 new_value = 0;
3289         }
3290
3291         return result;
3292 }
3293
3294 BC_DragWindow* BC_ListBox::get_drag_popup()
3295 {
3296         return drag_popup;
3297 }
3298
3299 int BC_ListBox::translation_event()
3300 {
3301         if( is_popup && gui ) {
3302                 int new_x = gui->get_x() +
3303                         (top_level->last_translate_x - top_level->prev_x -
3304                                 BC_DisplayInfo::get_left_border());
3305                 int new_y = gui->get_y() +
3306                         (top_level->last_translate_y - top_level->prev_y -
3307                                 BC_DisplayInfo::get_top_border());
3308
3309                 gui->reposition_window(new_x, new_y);
3310
3311         }
3312         return 0;
3313 }
3314
3315 int BC_ListBox::reposition_window(int x, int y, int w, int h, int flush)
3316 {
3317         if( w != -1 ) {
3318                 popup_w = w;
3319                 if( h != -1 ) popup_h = h;
3320 //printf("BC_ListBox::reposition_window %d %d\n", popup_w, popup_h);
3321
3322                 if( !is_popup ) {
3323                         if( xscrollbar )
3324                                 xscrollbar->reposition_window(get_xscroll_x(),
3325                                         get_xscroll_y(),
3326                                         get_xscroll_width());
3327                         if( yscrollbar )
3328                                 yscrollbar->reposition_window(get_yscroll_x(),
3329                                         get_yscroll_y(),
3330                                         get_yscroll_height());
3331                 }
3332         }
3333
3334         BC_WindowBase::reposition_window(x, y, w, h);
3335         draw_button(0);
3336         draw_items(flush);
3337         return 0;
3338 }
3339
3340 int BC_ListBox::deactivate()
3341 {
3342         hide_tooltip();
3343 // printf("BC_ListBox::deactivate %d this=%p gui=%p active=%d\n",
3344 // __LINE__, this, gui, active);
3345         if( active ) {
3346                 if( is_popup ) {
3347 //printf("BC_ListBox::deactivate %d this=%p gui=%p\n", __LINE__, this, gui);
3348                         if( gui ) {
3349                                 delete gui;  gui = 0;
3350                                 flush();
3351                         }
3352                         xscrollbar = 0;
3353                         yscrollbar = 0;
3354                         highlighted_item = -1;
3355                         highlighted_ptr = 0;
3356 //sleep(1);
3357                 }
3358 //printf("BC_ListBox::deactivate %d this=%p\n", __LINE__, this);
3359                 active = 0;
3360                 top_level->active_subwindow = 0;
3361         }
3362
3363         return 0;
3364 }
3365
3366 int BC_ListBox::activate(int take_focus)
3367 {
3368         if( active ) return 0;
3369         active = 1;
3370         if( take_focus )
3371                 set_active_subwindow(this);
3372         button_releases = 0;
3373         if( !is_popup || gui ) return 0;
3374         int wx = get_x(), wy = get_y() + get_h();
3375         if( justify == LISTBOX_RIGHT ) wx += get_w() - popup_w;
3376         Window xwin;
3377         int abs_x, abs_y;
3378         XTranslateCoordinates(top_level->display,
3379                 parent_window->win, top_level->rootwin,
3380                 wx, wy, &abs_x, &abs_y, &xwin);
3381         if( x <= 0 ) x = 2;
3382         if( y <= 0 ) y = 2;
3383         return activate(abs_x, abs_y);
3384 }
3385
3386 int BC_ListBox::activate(int x, int y, int w, int h)
3387 {
3388         if( !is_popup || gui ) return 0;
3389         active = 1;
3390         if( w != -1 ) popup_w = w;
3391         if( h != -1 ) popup_h = h;
3392         reset_query();
3393         if( y + popup_h > top_level->get_root_h(0) )
3394                 y -= get_h() + popup_h;
3395         add_subwindow(gui = new BC_Popup(this,
3396                 x, y, popup_w, popup_h, -1, 0, 0));
3397         draw_items(1);
3398         gui->show_window(1);
3399         return 0;
3400 }
3401
3402 int BC_ListBox::is_active()
3403 {
3404         return active;
3405 }
3406
3407 int BC_ListBox::keypress_event()
3408 {
3409         if( !active ) return 0;
3410
3411 //printf("BC_ListBox::keypress_event %d\n", __LINE__);
3412
3413         int result = 0, redraw = 0;
3414         int view_items = last_in_view - first_in_view + 1;
3415         if( view_items <= 0 ) view_items = view_h / get_text_height(MEDIUMFONT);
3416         int new_item = -1, new_selection = -1;
3417
3418         switch( top_level->get_keypress() ) {
3419         case ESC:
3420         case RETURN:
3421                 top_level->deactivate();
3422
3423 // If user is manipulating popup with keyboard, don't pass on event.
3424                 if( is_popup ) {
3425 //printf("BC_ListBox::keypress_event %d %p\n", __LINE__, get_selection(0, 0));
3426                         if( top_level->get_keypress() == RETURN )
3427                                 handle_event();
3428 //printf("BC_ListBox::keypress_event %d %p\n", __LINE__, get_selection(0, 0));
3429                         result = 1;
3430                 }
3431                 else
3432                         result = 0;
3433                 break;
3434
3435         case UP:
3436                 new_selection = new_item = select_previous(0);
3437
3438 //printf("BC_ListBox::keypress_event 1 %d\n", new_item);
3439                 if( new_item >= 0 ) {
3440                         center_selection(new_item);
3441                         redraw = 1;
3442                 }
3443                 reset_query();
3444                 result = 1;
3445                 break;
3446
3447         case DOWN:
3448                 new_selection = new_item = select_next(0);
3449
3450                 if( new_item >= 0 ) {
3451                         center_selection(new_item);
3452                         redraw = 1;
3453                 }
3454                 reset_query();
3455                 result = 1;
3456                 break;
3457
3458         case PGUP:
3459                 new_selection = new_item = select_previous(view_items - 1);
3460
3461                 if( new_item >= 0 ) {
3462                         center_selection(new_item);
3463                         redraw = 1;
3464                 }
3465                 reset_query();
3466                 result = 1;
3467                 break;
3468
3469         case PGDN:
3470                 new_selection = new_item = select_next(view_items - 1);
3471
3472                 if( new_item >= 0 ) {
3473                         center_selection(new_item);
3474                         redraw = 1;
3475                 }
3476                 reset_query();
3477                 result = 1;
3478                 break;
3479
3480         case LEFT:
3481                 xposition -= 10;
3482                 redraw = 1;
3483                 result = 1;
3484                 break;
3485
3486         case RIGHT:
3487                 xposition += 10;
3488                 redraw = 1;
3489                 result = 1;
3490                 break;
3491
3492         case DELETE:
3493         case HOME:
3494         case END:
3495                 result = 0;
3496                 break;
3497
3498         default:
3499                 if( !ctrl_down() ) {
3500                         int query_len = strlen(query);
3501                         if( query_len < (int)sizeof(query)-1 &&
3502                             top_level->get_keypress() > 30 &&
3503                             top_level->get_keypress() < 127 ) {
3504                                 query[query_len++] = top_level->get_keypress();
3505                                 query[query_len] = 0;
3506                                 new_selection = query_list();
3507                         }
3508                         else
3509                         if( top_level->get_keypress() == BACKSPACE ) {
3510                                 if( query_len > 0 ) query[--query_len] = 0;
3511                                 new_selection = query_list();
3512                         }
3513                         if( show_query ) {
3514                                 if( query_len > 0 )
3515                                         show_tooltip(query);
3516                                 else
3517                                         hide_tooltip();
3518                         }
3519                         redraw = 1;
3520                         result = 1;
3521                 }
3522                 break;
3523         }
3524
3525         if( redraw ) {
3526                 clamp_positions();
3527                 draw_items(0);
3528                 update_scrollbars(1);
3529         }
3530
3531 //printf("BC_ListBox::keypress_event %d new_selection=%d\n", __LINE__, new_selection);
3532         if( new_selection >= 0 && !is_suggestions ) {
3533 //printf("BC_ListBox::keypress_event %d\n", __LINE__);
3534                 selection_changed();
3535 //printf("BC_ListBox::keypress_event %d\n", __LINE__);
3536         }
3537
3538         return result;
3539 }
3540
3541
3542 BC_Pixmap* BC_ListBox::get_bg_surface()
3543 {
3544         return bg_surface;
3545 }
3546
3547
3548 void BC_ListBox::draw_background()
3549 {
3550         if( !bg_draw ) return;
3551         bg_draw = 0;
3552 // White background pixmap
3553         set_color(top_level->get_resources()->listbox_inactive);
3554         draw_box(0, 0, bg_surface->get_w(), bg_surface->get_h(), bg_surface);
3555
3556 // Optional heroine pixmap
3557         if( bg_pixmap )
3558                 bg_surface->draw_pixmap(bg_pixmap,
3559                         bg_surface->get_w() - top_level->get_resources()->listbox_bg->get_w(),
3560                         0);
3561 }
3562
3563 void BC_ListBox::clear_listbox(int x, int y, int w, int h)
3564 {
3565         gui->draw_pixmap(bg_surface, x, y, w, h, x, y - title_h);
3566 }
3567
3568 void BC_ListBox::update_format(int display_format, int redraw)
3569 {
3570         this->display_format = display_format;
3571         xposition = 0;  yposition = 0;
3572         if( redraw && gui ) draw_items(1, 1);
3573 }
3574
3575 int BC_ListBox::get_format()
3576 {
3577         return display_format;
3578 }
3579
3580
3581
3582 int BC_ListBox::draw_items(int flush, int draw_bg)
3583 {
3584         if( gui ) {
3585                 BC_Resources *resources = get_resources();
3586
3587 //dump(data, columns);
3588
3589 // Calculate items width
3590                 calculate_item_coords();
3591 // Create and destroy scrollbars as needed
3592                 get_scrollbars();
3593
3594                 if( bg_draw ) this->bg_draw = 1;
3595                 draw_background();
3596
3597                 first_in_view = -1;
3598                 last_in_view = 0;
3599 // Icon display
3600                 if( display_format == LISTBOX_ICONS ) {
3601                         clear_listbox(2, 2 + title_h, view_w, view_h);
3602
3603                         set_font(MEDIUMFONT);
3604                         for( int i=0; i<data[master_column].size(); ++i ) {
3605                                 BC_ListBoxItem *item = data[master_column].get(i);
3606                                 if( get_item_x(item) >= -get_item_w(item) &&
3607                                     get_item_x(item) < view_w &&
3608                                     get_item_y(item) >= -get_item_h(item) + title_h &&
3609                                     get_item_y(item) < view_h + title_h ) {
3610                                         item->set_in_view(1);
3611                                         if( first_in_view < 0 ) first_in_view = i;
3612                                         last_in_view = i;
3613                                         int item_color = get_item_highlight(data, 0, i);
3614                                         int icon_x, icon_y, icon_w, icon_h;
3615                                         int text_x, text_y, text_w, text_h;
3616
3617 // Draw highlights
3618                                         get_icon_mask(item, icon_x, icon_y, icon_w, icon_h);
3619                                         get_text_mask(item, text_x, text_y, text_w, text_h);
3620
3621                                         if( item_color != resources->listbox_inactive ) {
3622                                                 gui->set_color(BLACK);
3623                                                 gui->draw_rectangle(icon_x, icon_y, icon_w, icon_h);
3624                                                 gui->set_color(item_color);
3625                                                 gui->draw_box(icon_x + 1, icon_y + 1, icon_w - 2, icon_h - 2);
3626                                                 gui->set_color(BLACK);
3627                                                 gui->draw_rectangle(text_x, text_y, text_w, text_h);
3628                                                 gui->set_color(item_color);
3629                                                 gui->draw_box(text_x + 1, text_y + 1, text_w - 2, text_h - 2);
3630
3631                                                 if( icon_position == ICON_LEFT )
3632                                                         gui->draw_box(text_x - 1, text_y + 1, 2, text_h - 2);
3633                                                 else
3634                                                 if( icon_position == ICON_TOP )
3635                                                         gui->draw_line(text_x + 1, text_y, text_x + icon_w - 2, text_y);
3636                                                 if( text_x + text_w < icon_x + icon_w ) {
3637                                                         gui->set_color(BLACK);
3638                                                         gui->draw_line(text_x + text_w, icon_y + icon_h,
3639                                                                 icon_x + icon_w, icon_y + icon_h);
3640                                                 }
3641                                         }
3642 // Draw icons
3643                                         gui->set_color(get_item_color(data, 0, i));
3644                                         if( item->icon )
3645                                                 gui->pixmap->draw_pixmap(item->icon,
3646                                                         icon_x + ICON_MARGIN, icon_y + ICON_MARGIN);
3647
3648                                         gui->draw_text(text_x + ICON_MARGIN,
3649                                                 text_y + ICON_MARGIN + get_baseline(item), item->text);
3650                                 }
3651                                 else
3652                                         item->set_in_view(0);
3653                         }
3654                 }
3655 // Text display
3656                 else {
3657 // Draw one column at a time so text overruns don't go into the next column
3658 // clear column backgrounds
3659                         int current_toggle = 0;
3660                         for( int j=0; j<columns; ++j ) {
3661                                 clear_listbox(LISTBOX_BORDER + get_column_offset(j) - xposition,
3662                                         LISTBOX_BORDER + title_h,
3663                                         get_column_width(j, 1),
3664                                         view_h);
3665 // Draw rows in the column recursively
3666                                 draw_text_recursive(data, j, 0, &current_toggle);
3667                         }
3668
3669 // Delete excess expanders
3670                         while( expanders.total > current_toggle ) {
3671                                 expanders.remove_object();
3672                         }
3673                 }
3674
3675 // draw user images if available
3676                 draw_images();
3677 // Draw titles on top of rows for superposition effect
3678                 draw_titles(0);
3679
3680 // Clear garbage from bottom right corner
3681                 if( xscrollbar && yscrollbar && is_popup ) {
3682                         gui->draw_top_background(parent_window,
3683                                 popup_w - get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w(),
3684                                 popup_h - get_resources()->hscroll_data[SCROLL_HANDLE_UP]->get_h(),
3685                                 get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w(),
3686                                 get_resources()->hscroll_data[SCROLL_HANDLE_UP]->get_h());
3687                 }
3688
3689 // Draw borders
3690                 draw_border(0);
3691
3692
3693                 if( current_operation == SELECT_RECT )
3694                         draw_rectangle(0);
3695
3696                 gui->flash(flush);
3697         }
3698
3699         return 0;
3700 }
3701
3702
3703 void BC_ListBox::draw_text_recursive(ArrayList<BC_ListBoxItem*> *data,
3704                 int column, int indent, int *current_toggle)
3705 {
3706         if( !data ) return;
3707         BC_Resources *resources = get_resources();
3708
3709         set_font(MEDIUMFONT);
3710         int subindent = 0;
3711
3712 // Search for a branch and make room for toggle if there is one
3713         if( column == 0 ) {
3714                 for( int i=0; i<data[column].size(); ++i ) {
3715                         if( data[column].get(i)->get_sublist() ) {
3716                                 subindent = BC_WindowBase::get_resources()->listbox_expand[0]->get_w();
3717                                 break;
3718                         }
3719                 }
3720         }
3721
3722         row_height = row_ascent = row_descent = 0;
3723         for( int i=0; i<data[column].total; ++i ) {
3724                 BC_ListBoxItem *item = data[column].values[i];
3725                 int ht = get_text_h(item);
3726                 if( ht > row_height ) row_height = ht;
3727                 int bl = get_baseline(item);
3728                 if( bl > row_ascent ) row_ascent = bl;
3729                 int dt = ht - bl;
3730                 if( dt > row_descent ) row_ascent = bl;
3731         }
3732
3733         for( int i=0; i<data[column].size(); ++i ) {
3734 // Draw a row
3735                 BC_ListBoxItem *item = data[column].values[i];
3736                 BC_ListBoxItem *first_item = data[master_column].values[i];
3737
3738                 if( get_item_y(item) >= -get_item_h(item) + title_h &&
3739                     get_item_y(item) < view_h + title_h ) {
3740                         int row_color = get_item_highlight(data, 0, i);
3741                         int x, y, w, h, column_width;
3742
3743                         get_text_mask(item, x, y, w, h);
3744                         column_width = get_column_width(column, 1);
3745                         if( x + column_width > view_w + LISTBOX_BORDER * 2 )
3746                                 column_width = view_w + LISTBOX_BORDER * 2 - x;
3747
3748                         if( row_color != resources->listbox_inactive ) {
3749                                 gui->set_color(row_color);
3750                                 gui->draw_box(x, y, column_width, h);
3751                                 gui->set_color(BLACK);
3752                                 int xx = x + column_width-1;
3753                                 gui->draw_line(x, y, xx, y);
3754                                 int hh = h;
3755                                 if( display_format == LISTBOX_ICON_LIST ) {
3756                                         int ih = get_icon_h(item);
3757                                         if( ih > hh ) hh = ih;
3758                                 }
3759                                 int yy = y + hh-1;
3760                                 gui->draw_line(x, yy, xx, yy);
3761                         }
3762
3763                         gui->set_color(get_item_color(data, column, i));
3764
3765
3766                         if( column == 0 && display_format == LISTBOX_ICON_LIST ) {
3767                                 if( item->icon ) {
3768                                         gui->pixmap->draw_pixmap(item->icon, x, y);
3769                                         x += item->icon->get_w() + ICON_MARGIN;
3770                                 }
3771                         }
3772
3773
3774 // Indent only applies to first column
3775                         gui->draw_text(
3776                                 x + LISTBOX_BORDER + LISTBOX_MARGIN +
3777                                         (column == 0 ? indent + subindent : 0),
3778                                 y + get_baseline(item), item->text);
3779                         item->set_in_view(1);
3780                         if( !indent ) {
3781                                 if( first_in_view < 0 ) first_in_view = i;
3782                                 last_in_view = i;
3783                         }
3784
3785 // Update expander
3786                         if( column == 0 && item->get_sublist() && item->get_columns() ) {
3787 // Create new expander
3788                                 if( *current_toggle >= expanders.total ) {
3789                                         BC_ListBoxToggle *toggle =
3790                                                 new BC_ListBoxToggle(this, item,
3791                                                         x + LISTBOX_BORDER + LISTBOX_MARGIN + indent, y);
3792                                         toggle->draw(0);
3793                                         expanders.append(toggle);
3794                                 }
3795 // Reposition existing expander
3796                                 else {
3797                                         BC_ListBoxToggle *toggle = expanders.values[*current_toggle];
3798 //printf("BC_ListBox::draw_text_recursive 1 %d\n", *current_toggle);
3799                                         toggle->update(item,
3800                                                 x + LISTBOX_BORDER + LISTBOX_MARGIN + indent, y, 0);
3801                                 }
3802                                 (*current_toggle)++;
3803                         }
3804
3805
3806
3807                 }
3808                 else
3809                         item->set_in_view(0);
3810
3811 // Descend into sublist
3812                 if( first_item->get_expand() ) {
3813                         draw_text_recursive(first_item->get_sublist(),
3814                                 column,
3815                                 indent + LISTBOX_INDENT,
3816                                 current_toggle);
3817                 }
3818         }
3819 }
3820
3821 int BC_ListBox::draw_border(int flash)
3822 {
3823         BC_Resources *resources = top_level->get_resources();
3824         gui->draw_3d_border(0, 0,
3825                 view_w + LISTBOX_BORDER * 2, view_h + title_h + LISTBOX_BORDER * 2,
3826                 resources->listbox_border1,
3827                 list_highlighted ?
3828                         resources->listbox_border2_hi : resources->listbox_border2,
3829                 list_highlighted ?
3830                         resources->listbox_border3_hi : resources->listbox_border3,
3831                 resources->listbox_border4);
3832
3833         if( flash ) {
3834                 gui->flash();
3835                 gui->flush();
3836         }
3837         return 0;
3838 }
3839
3840 void BC_ListBox::draw_title(int number)
3841 {
3842 // Column title background
3843         int image_number = 0;
3844         if( number == highlighted_title ) {
3845                 image_number = 1;
3846                 if( current_operation == COLUMN_DN )
3847                         image_number = 2;
3848         }
3849
3850         gui->draw_3segmenth(get_column_offset(number) - xposition + LISTBOX_BORDER,
3851                 LISTBOX_BORDER,
3852                 get_column_width(number, 1) + get_resources()->listbox_title_overlap,
3853                 column_bg[image_number]);
3854
3855         int title_x = -xposition + get_column_offset(number) + 
3856                 LISTBOX_MARGIN + LISTBOX_BORDER;
3857         title_x += get_resources()->listbox_title_margin;
3858
3859         gui->set_color(get_resources()->listbox_title_color);
3860         gui->draw_text(title_x, 
3861                 LISTBOX_MARGIN + LISTBOX_BORDER + get_text_ascent(MEDIUMFONT), 
3862                 column_titles[number]);
3863
3864 // Column sort order
3865         if( number == sort_column ) {
3866                 BC_Pixmap *src = sort_order == SORT_ASCENDING ?
3867                         column_sort_dn : column_sort_up;
3868
3869 //              int column_offset = get_column_offset(number) - xposition + LISTBOX_BORDER;
3870 //              int column_width = get_column_width(number, 1);
3871 //              int toggle_x = column_offset + column_width - LISTBOX_BORDER;
3872 //              if( toggle_x > items_w ) toggle_x = items_w;
3873 //              toggle_x -= 5 + src->get_w();
3874
3875                 int x = title_x + 
3876                         get_text_width(MEDIUMFONT, column_titles[number]) +
3877                         LISTBOX_MARGIN;
3878                 
3879                 gui->draw_pixmap(src,
3880                         x,
3881                         title_h / 2 - src->get_h() / 2 + LISTBOX_BORDER);
3882         }
3883 }
3884
3885 int BC_ListBox::draw_titles(int flash)
3886 {
3887         if( column_titles &&
3888             (display_format == LISTBOX_TEXT ||
3889              display_format == LISTBOX_ICON_LIST) ) {
3890 //printf("BC_ListBox::draw_titles 1 %d\n", highlighted_title);
3891                 for( int i=0; i<columns; ++i ) {
3892                         if( i != highlighted_title )
3893                                 draw_title(i);
3894                 }
3895
3896                 if( highlighted_title >= 0 ) draw_title(highlighted_title);
3897                 draw_border(0);
3898         }
3899
3900         if( flash ) {
3901                 gui->flash();
3902         }
3903         return 0;
3904 }
3905
3906 void BC_ListBox::draw_toggles(int flash)
3907 {
3908         for( int i=0; i<expanders.total; ++i )
3909                 expanders.values[i]->draw(0);
3910
3911 //printf("BC_ListBox::draw_toggles 1 %d\n", flash);
3912         if( flash && expanders.total ) {
3913                 gui->flash();
3914         }
3915 }
3916
3917 int BC_ListBox::draw_rectangle(int flash)
3918 {
3919         int x1 = MIN(rect_x1, rect_x2);
3920         int x2 = MAX(rect_x1, rect_x2);
3921         int y1 = MIN(rect_y1, rect_y2);
3922         int y2 = MAX(rect_y1, rect_y2);
3923
3924         if( x1 == x2 || y1 == y2 ) return 0;
3925
3926         gui->set_inverse();
3927         gui->set_color(WHITE);
3928         gui->draw_rectangle(x1, y1, x2 - x1, y2 - y1);
3929         gui->set_opaque();
3930
3931
3932         if( flash ) {
3933                 gui->flash();
3934         }
3935         return 0;
3936 }
3937
3938 void BC_ListBox::dump(ArrayList<BC_ListBoxItem*> *data, int columns,
3939                 int indent, int master_column)
3940 {
3941         if( !indent ) {
3942                 printf("BC_ListBox::dump 1\n");
3943         }
3944
3945         for( int i=0; i<data[master_column].total; ++i ) {
3946                 for( int k=0; k<indent; ++k )
3947                         printf(" ");
3948                 for( int j=0; j<columns; ++j ) {
3949                         BC_ListBoxItem *item = data[j].values[i];
3950                         printf("%d,%d,%d=%s ",
3951                                 item->get_text_x(), item->get_text_y(),
3952                                 item->autoplace_text, item->get_text());
3953                 }
3954                 printf("\n");
3955
3956                 if( data[master_column].values[i]->get_sublist() ) {
3957                         dump(data[master_column].values[i]->get_sublist(),
3958                                 data[master_column].values[i]->get_columns(),
3959                                 indent + 4,
3960                                 master_column);
3961                 }
3962         }
3963 }
3964