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