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