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