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