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