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