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