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