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