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