asset drag/drop to viewers, bluebanana bug, listbox fontlist highlight
[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                 int ih = get_icon_h(item);
1366                 if( h < ih ) h = ih;
1367         }
1368         return 0;
1369 }
1370
1371 int BC_ListBox::get_item_highlight(ArrayList<BC_ListBoxItem*> *data,
1372         int column,
1373         int item)
1374 {
1375         BC_Resources *resources = get_resources();
1376         if(data[column].values[item]->selected)
1377                 return resources->listbox_selected;
1378         else if(highlighted_item >= 0 &&
1379                 highlighted_ptr == data[master_column].values[item])
1380                 return resources->listbox_highlighted;
1381         return resources->listbox_inactive;
1382 }
1383
1384 int BC_ListBox::get_item_color(ArrayList<BC_ListBoxItem*> *data,
1385         int column,
1386         int item)
1387 {
1388         int color = data[column].values[item]->color;
1389         if( color == -1 ) color = get_resources()->listbox_text;
1390         if( get_item_highlight(data, column, item) == color )
1391                 return BLACK;
1392         return color;
1393 }
1394
1395 int BC_ListBox::get_from_column()
1396 {
1397         return dragged_title;
1398 }
1399
1400 int BC_ListBox::get_to_column()
1401 {
1402         return highlighted_title;
1403 }
1404
1405
1406 BC_ListBoxItem* BC_ListBox::get_selection(int column,
1407         int selection_number)
1408 {
1409         return get_selection_recursive(data,
1410                 column,
1411                 selection_number);
1412 }
1413
1414 BC_ListBoxItem* BC_ListBox::get_selection_recursive(
1415         ArrayList<BC_ListBoxItem*> *data,
1416         int column,
1417         int selection_number)
1418 {
1419         if(!data) return 0;
1420
1421         for(int i = 0; i < data[master_column].total; i++)
1422         {
1423                 BC_ListBoxItem *item = data[master_column].values[i];
1424                 if(item->selected)
1425                 {
1426 //printf("BC_ListBox::get_selection_recursive %d\n", __LINE__);
1427                         selection_number--;
1428                         if(selection_number < 0)
1429                         {
1430
1431                                 return data[column].values[i];
1432                         }
1433                 }
1434
1435                 if(item->get_sublist())
1436                 {
1437                         BC_ListBoxItem *result = get_selection_recursive(item->get_sublist(),
1438                                 column,
1439                                 selection_number);
1440                         if(result) return result;
1441                 }
1442         }
1443
1444         return 0;
1445 }
1446
1447
1448 int BC_ListBox::get_selection_number(int column,
1449         int selection_number)
1450 {
1451         return get_selection_number_recursive(data,
1452                 column,
1453                 selection_number);
1454 }
1455
1456 int BC_ListBox::get_selection_number_recursive(
1457         ArrayList<BC_ListBoxItem*> *data,
1458         int column,
1459         int selection_number,
1460         int *counter)
1461 {
1462         int temp = -1;
1463         if(!data) return 0;
1464         if(!counter) counter = &temp;
1465
1466         for(int i = 0; i < data[master_column].total; i++)
1467         {
1468                 (*counter)++;
1469                 BC_ListBoxItem *item = data[master_column].values[i];
1470                 if(item->selected)
1471                 {
1472                         selection_number--;
1473                         if(selection_number < 0)
1474                         {
1475                                 return (*counter);
1476                         }
1477                 }
1478                 if(item->get_sublist())
1479                 {
1480                         int result = get_selection_number_recursive(
1481                                 item->get_sublist(),
1482                                 column,
1483                                 selection_number,
1484                                 counter);
1485                         if(result >= 0) return result;
1486                 }
1487         }
1488         return -1;
1489 }
1490
1491
1492 int BC_ListBox::set_selection_mode(int mode)
1493 {
1494         this->selection_mode = mode;
1495         return 0;
1496 }
1497
1498 void BC_ListBox::delete_columns()
1499 {
1500         if(column_titles)
1501         {
1502                 for(int i = 0; i < columns; i++)
1503                 {
1504                         delete [] column_titles[i];
1505                 }
1506                 delete [] column_titles;
1507         }
1508
1509         if(column_width) delete [] column_width;
1510
1511         column_titles = 0;
1512         column_width = 0;
1513 }
1514
1515 // Need to copy titles so EDL can change
1516 void BC_ListBox::set_columns(const char **column_titles,
1517         int *column_width,
1518         int columns)
1519 {
1520         if((!column_titles && column_width) ||
1521                 (column_titles && !column_width))
1522         {
1523                 printf("BC_ListBox::set_columns either column_titles or column_width == NULL but not both.\n");
1524                 return;
1525         }
1526
1527
1528         delete_columns();
1529
1530         if(column_titles)
1531         {
1532                 this->column_titles = new char*[columns];
1533                 for(int i = 0; i < columns; i++)
1534                 {
1535                         this->column_titles[i] = new char[strlen(column_titles[i]) + 1];
1536                         strcpy(this->column_titles[i], column_titles[i]);
1537                 }
1538         }
1539
1540         if(column_width)
1541         {
1542                 this->column_width = new int[columns];
1543                 for(int i = 0; i < columns; i++)
1544                 {
1545                         this->column_width[i] = column_width[i];
1546                 }
1547         }
1548
1549         this->columns = columns;
1550
1551 }
1552
1553
1554 int BC_ListBox::update(ArrayList<BC_ListBoxItem*> *data,
1555         const char **column_titles,
1556         int *column_widths,
1557         int columns,
1558         int xposition,
1559         int yposition,
1560         int highlighted_number,
1561         int recalc_positions,
1562         int draw)
1563 {
1564         set_columns(column_titles,
1565                 column_widths,
1566                 columns);
1567
1568         this->data = data;
1569
1570         this->yposition = yposition;
1571         this->xposition = xposition;
1572         this->highlighted_item = highlighted_number;
1573         this->highlighted_ptr = index_to_item(data, highlighted_number, 0);
1574
1575         if(recalc_positions)
1576                 set_autoplacement(data, 1, 1);
1577
1578         init_column_width();
1579
1580         if(gui && draw)
1581         {
1582                 draw_items(0, 1);
1583                 update_scrollbars(1);
1584         }
1585
1586         return 0;
1587 }
1588
1589 void BC_ListBox::center_selection()
1590 {
1591         int selection = get_selection_number(0, 0);
1592
1593         calculate_item_coords();
1594         center_selection(selection);
1595
1596
1597         if(gui)
1598         {
1599                 draw_items(1, 1);
1600                 update_scrollbars(0);
1601                 gui->show_window(1);
1602         }
1603 }
1604
1605 void BC_ListBox::move_vertical(int pixels)
1606 {
1607 }
1608
1609 void BC_ListBox::move_horizontal(int pixels)
1610 {
1611 }
1612
1613 int BC_ListBox::select_previous(int skip,
1614         BC_ListBoxItem *selected_item,
1615         int *counter,
1616         ArrayList<BC_ListBoxItem*> *data,
1617         int *got_first,
1618         int *got_second)
1619 {
1620         int top_level = 0;
1621         if(!selected_item)
1622                 selected_item = get_selection(0, 0);
1623         int temp = -1;
1624         if(!counter)
1625                 counter = &temp;
1626         int temp2 = 0;
1627         if(!got_first)
1628         {
1629                 got_first = &temp2;
1630                 top_level = 1;
1631         }
1632         int temp3 = 0;
1633         if(!got_second)
1634                 got_second = &temp3;
1635         if(!data)
1636                 data = this->data;
1637
1638 // Scan backwards to item pointer.  Then count visible items to get
1639 // destination.  No wraparound.
1640         do
1641         {
1642                 for(int i = data[master_column].total - 1; i >= 0; i--)
1643                 {
1644                         BC_ListBoxItem *current_item = data[master_column].values[i];
1645                         if(current_item->get_sublist() &&
1646                                 current_item->get_expand())
1647                         {
1648                                 int result = select_previous(skip,
1649                                         selected_item,
1650                                         counter,
1651                                         current_item->get_sublist(),
1652                                         got_first,
1653                                         got_second);
1654                                 if(*got_second)
1655                                 {
1656                                         return result;
1657                                 }
1658                         }
1659
1660                         if(*got_first)
1661                         {
1662                                 (*counter)++;
1663                                 if((*counter) >= skip)
1664                                 {
1665                                         for(int j = 0; j < columns; j++)
1666                                                 data[j].values[i]->selected = 1;
1667                                         (*got_second) = 1;
1668                                         return item_to_index(this->data, current_item);
1669                                 }
1670                         }
1671                         else
1672                         {
1673                                 if(current_item->selected)
1674                                 {
1675                                         for(int j = 0; j < columns; j++)
1676                                                 data[j].values[i]->selected = 0;
1677                                         (*got_first) = 1;
1678                                         (*counter)++;
1679                                 }
1680                         }
1681                 }
1682
1683 // Hit top of top level without finding a selected item.
1684                 if(top_level)
1685                 {
1686 // Select first item in top level and quit
1687                         BC_ListBoxItem *current_item;
1688                         (*got_first) = 1;
1689                         current_item = data[master_column].values[0];
1690
1691                         for(int j = 0; j < columns; j++)
1692                                 data[j].values[0]->selected = 1;
1693                         (*got_second) = 1;
1694                         return item_to_index(this->data, current_item);
1695                 }
1696         }while(top_level && data[master_column].total);
1697         return -1;
1698 }
1699
1700 int BC_ListBox::select_next(int skip,
1701         BC_ListBoxItem *selected_item,
1702         int *counter,
1703         ArrayList<BC_ListBoxItem*> *data,
1704         int *got_first,
1705         int *got_second)
1706 {
1707         int top_level = 0;
1708         if(!selected_item)
1709                 selected_item = get_selection(0, 0);
1710         int temp = -1;
1711         if(!counter)
1712                 counter = &temp;
1713         int temp2 = 0;
1714         if(!got_first)
1715         {
1716                 got_first = &temp2;
1717                 top_level = 1;
1718         }
1719         int temp3 = 0;
1720         if(!got_second)
1721                 got_second = &temp3;
1722         if(!data)
1723                 data = this->data;
1724
1725 // Scan forwards to currently selected item pointer.
1726 // Then count visible items to get destination.  No wraparound.
1727         do
1728         {
1729                 for(int i = 0; i < data[master_column].total; i++)
1730                 {
1731                         BC_ListBoxItem *current_item = data[master_column].values[i];
1732
1733 // Select next item once the number items after the currently selected item
1734 // have been passed.
1735                         if(*got_first)
1736                         {
1737                                 (*counter)++;
1738                                 if((*counter) >= skip)
1739                                 {
1740                                         for(int j = 0; j < columns; j++)
1741                                                 data[j].values[i]->selected = 1;
1742                                         (*got_second) = 1;
1743                                         return item_to_index(this->data, current_item);
1744                                 }
1745                         }
1746                         else
1747                         {
1748 // Got currently selected item.  Deselect it.
1749                                 if(current_item->selected)
1750                                 {
1751                                         for(int j = 0; j < columns; j++)
1752                                                 data[j].values[i]->selected = 0;
1753                                         (*got_first) = 1;
1754                                         (*counter)++;
1755                                 }
1756                         }
1757
1758 // Descend into expanded level
1759                         if(current_item->get_sublist() &&
1760                                 current_item->get_expand())
1761                         {
1762                                 int result = select_next(skip,
1763                                         selected_item,
1764                                         counter,
1765                                         current_item->get_sublist(),
1766                                         got_first,
1767                                         got_second);
1768                                 if(*got_second)
1769                                 {
1770                                         return result;
1771                                 }
1772                         }
1773                 }
1774
1775 // Hit bottom of top level without finding the next item.
1776                 if(top_level)
1777                 {
1778                         BC_ListBoxItem *current_item;
1779 // Select first item in top level and quit
1780                         if(!(*got_first))
1781                         {
1782                                 (*got_first) = 1;
1783                                 current_item = data[master_column].values[0];
1784
1785                                 for(int j = 0; j < columns; j++)
1786                                         data[j].values[0]->selected = 1;
1787                                 (*got_second) = 1;
1788                         }
1789                         else
1790                         {
1791 // Select last item in top level and quit
1792                                 (*got_first) = 1;
1793                                 int current_row = data[master_column].total - 1;
1794                                 current_item = data[master_column].values[current_row];
1795
1796                                 for(int j = 0; j < columns; j++)
1797                                         data[j].values[current_row]->selected = 1;
1798                                 (*got_second) = 1;
1799                         }
1800
1801                         return item_to_index(this->data, current_item);
1802                 }
1803         }while(top_level && data[master_column].total);
1804
1805         return -1;
1806 }
1807
1808
1809 void BC_ListBox::clamp_positions()
1810 {
1811         items_w = get_items_width();
1812         items_h = get_items_height(data, columns);
1813
1814         if(yposition < 0) yposition = 0;
1815         else
1816         if(yposition > items_h - view_h)
1817                 yposition = items_h - view_h;
1818
1819         if(yposition < 0) yposition = 0;
1820
1821         if(xposition < 0) xposition = 0;
1822         else
1823         if(xposition >= items_w - view_w)
1824                 xposition = items_w - view_w;
1825
1826         if(xposition < 0) xposition = 0;
1827 }
1828
1829 int BC_ListBox::center_selection(int selection,
1830         ArrayList<BC_ListBoxItem*> *data,
1831         int *counter)
1832 {
1833         int temp = -1;
1834         if(!data) data = this->data;
1835         if(!counter) counter = &temp;
1836
1837         for(int i = 0; i < data[master_column].total; i++)
1838         {
1839                 (*counter)++;
1840
1841 // Got it
1842                 BC_ListBoxItem *item = data[master_column].values[i];
1843                 if((*counter) == selection)
1844                 {
1845                         BC_ListBoxItem *top_item = this->data[master_column].values[0];
1846
1847
1848                         if(display_format == LISTBOX_ICONS)
1849                         {
1850 // Icon is out of window
1851                                 if( item->icon_y-yposition  > view_h-get_text_h(item) ||
1852                                         item->icon_y-yposition < 0 ) {
1853                                         yposition = item->icon_y - view_h / 2;
1854                                 }
1855
1856                                 if(data[master_column].values[selection]->icon_x - xposition > view_w ||
1857                                         data[master_column].values[selection]->icon_x - xposition < 0)
1858                                 {
1859                                         xposition = item->icon_x - view_w / 2;
1860                                 }
1861                         }
1862                         else
1863                         {
1864 // Text coordinate is out of window
1865                                 if( item->text_y-yposition  > view_h-get_text_h(item) ||
1866                                         item->text_y-yposition < 0 ) {
1867                                         yposition = item->text_y -
1868                                                 top_item->text_y -
1869                                                 view_h / 2;
1870                                 }
1871                         }
1872                         return 1;
1873                 }
1874
1875 // Descend
1876                 if(item->get_sublist())
1877                 {
1878                         int result = center_selection(selection,
1879                                 item->get_sublist(),
1880                                 counter);
1881                         if(result) return result;
1882                 }
1883         }
1884         return 0;
1885 }
1886
1887 void BC_ListBox::update_scrollbars(int flush)
1888 {
1889         int h_needed = items_h = get_items_height(data, columns);
1890         int w_needed = items_w = get_items_width();
1891
1892 // if(columns > 0 && column_width)
1893 // printf("BC_ListBox::update_scrollbars 1 %d %d\n", column_width[columns - 1], w_needed);
1894
1895         if(xscrollbar)
1896         {
1897                 if(xposition != xscrollbar->get_value())
1898                         xscrollbar->update_value(xposition);
1899
1900                 if(w_needed != xscrollbar->get_length() ||
1901                         view_w != xscrollbar->get_handlelength())
1902                         xscrollbar->update_length(w_needed, xposition, view_w, 0);
1903         }
1904
1905         if(yscrollbar)
1906         {
1907                 if(yposition != yscrollbar->get_value())
1908                         yscrollbar->update_value(yposition);
1909
1910                 if(h_needed != yscrollbar->get_length() || view_h != yscrollbar->get_handlelength())
1911                         yscrollbar->update_length(h_needed, yposition, view_h, 0);
1912         }
1913
1914         if(flush) this->flush();
1915 }
1916
1917 int BC_ListBox::get_scrollbars()
1918 {
1919         int h_needed = items_h = get_items_height(data, columns);
1920         int w_needed = items_w = get_items_width();
1921         int flush = 0;
1922
1923
1924         title_h = get_title_h();
1925
1926         view_h = popup_h - title_h - 4;
1927         view_w = popup_w - 4;
1928
1929 // Create scrollbars as needed
1930         for(int i = 0; i < 2; i++)
1931         {
1932                 if(w_needed > view_w)
1933                 {
1934                         need_xscroll = 1;
1935                         view_h = popup_h -
1936                                 title_h -
1937                                 get_resources()->hscroll_data[SCROLL_HANDLE_UP]->get_h() -
1938                                 4;
1939                 }
1940                 else
1941                 {
1942                         need_xscroll = 0;
1943                 }
1944
1945                 if(h_needed > view_h)
1946                 {
1947                         need_yscroll = 1;
1948                         view_w = popup_w -
1949                                 get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w() -
1950                                 4;
1951                 }
1952                 else
1953                 {
1954                         need_yscroll = 0;
1955                 }
1956         }
1957
1958 // Update subwindow size
1959         int new_w = popup_w;
1960         int new_h = popup_h;
1961         if(need_xscroll) new_h -= get_resources()->hscroll_data[SCROLL_HANDLE_UP]->get_h();
1962         if(need_yscroll) new_w -= get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w();
1963
1964         if(!is_popup)
1965                 if(new_w != BC_WindowBase::get_w() || new_h != BC_WindowBase::get_h())
1966                         gui->resize_window(new_w, new_h);
1967
1968         BC_WindowBase *destination = (is_popup ? gui : parent_window);
1969         if(need_xscroll)
1970         {
1971                 if(!xscrollbar)
1972                 {
1973                         destination->add_subwindow(xscrollbar =
1974                                 new BC_ListBoxXScroll(this,
1975                                         w_needed,
1976                                         view_w,
1977                                         xposition));
1978                         xscrollbar->show_window(0);
1979                         xscrollbar->bound_to = this;
1980                 }
1981                 else
1982                 {
1983                     xscrollbar->update_length(w_needed, xposition, view_w, flush);
1984                         xscrollbar->reposition_window(get_xscroll_x(),
1985                                 get_xscroll_y(),
1986                                 get_xscroll_width());
1987                 }
1988         }
1989         else
1990         {
1991                 if(xscrollbar) delete xscrollbar;
1992                 xscrollbar = 0;
1993                 xposition = 0;
1994         }
1995
1996         if(need_yscroll)
1997         {
1998                 if(!yscrollbar)
1999                 {
2000                         destination->add_subwindow(yscrollbar =
2001                                 new BC_ListBoxYScroll(this,
2002                                         h_needed,
2003                                         view_h,
2004                                         yposition));
2005                         yscrollbar->show_window(0);
2006                         yscrollbar->bound_to = this;
2007                 }
2008                 else
2009                 {
2010                         yscrollbar->update_length(h_needed, yposition, view_h, flush);
2011                         yscrollbar->reposition_window(get_yscroll_x(),
2012                                 get_yscroll_y(),
2013                                 get_yscroll_height());
2014                 }
2015         }
2016         else
2017         {
2018                 if(yscrollbar) delete yscrollbar;
2019                 yscrollbar = 0;
2020                 yposition = 0;
2021         }
2022
2023         if(!bg_surface ||
2024                 view_w + 4 != bg_surface->get_w() ||
2025                 view_h + 4 != bg_surface->get_h())
2026         {
2027                 if(bg_surface) delete bg_surface;
2028                 bg_surface = new BC_Pixmap(gui, view_w + 4, view_h + 4);
2029                 bg_draw = 1;
2030         }
2031
2032
2033         return 0;
2034 }
2035
2036
2037
2038 void BC_ListBox::set_drag_scroll(int value)
2039 {
2040         allow_drag_scroll = value;
2041 }
2042
2043
2044 // Test for scrolling by dragging
2045
2046 int BC_ListBox::test_drag_scroll(int cursor_x, int cursor_y)
2047 {
2048         int result = 0;
2049         if(allow_drag_scroll ||
2050                 current_operation == SELECT_RECT)
2051         {
2052
2053                 int top_boundary = get_title_h();
2054
2055                 if(cursor_y < top_boundary ||
2056                         cursor_y >= view_h + title_h + LISTBOX_BORDER * 2 ||
2057                         cursor_x < LISTBOX_BORDER ||
2058                         cursor_x >= view_w + LISTBOX_BORDER)
2059                 {
2060                         result = 1;
2061                 }
2062         }
2063         return result;
2064 }
2065
2066 int BC_ListBox::drag_scroll_event()
2067 {
2068         int top_boundary = get_title_h();
2069         int result = 0;
2070
2071         if(get_cursor_y() < top_boundary)
2072         {
2073                 yposition -= top_boundary - get_cursor_y();
2074                 result = 1;
2075         }
2076         else
2077         if(get_cursor_y() >= view_h + title_h + 4)
2078         {
2079                 yposition += get_cursor_y() - (view_h + title_h + 4);
2080                 result = 1;
2081         }
2082
2083         if(get_cursor_x() < 2)
2084         {
2085                 xposition -= 2 - get_cursor_x();
2086                 result = 1;
2087         }
2088         else
2089         if(get_cursor_x() >= view_w + 2)
2090         {
2091                 xposition += get_cursor_x() - (view_w + 2);
2092                 result = 1;
2093         }
2094         if(result) clamp_positions();
2095         return result;
2096 }
2097
2098 int BC_ListBox::rectangle_scroll_event()
2099 {
2100         int old_xposition = xposition;
2101         int old_yposition = yposition;
2102         int result = drag_scroll_event();
2103
2104         if(result)
2105         {
2106                 rect_x1 += old_xposition - xposition;
2107                 rect_y1 += old_yposition - yposition;
2108                 rect_x2 = get_cursor_x();
2109                 rect_y2 = get_cursor_y();
2110
2111                 int x1 = MIN(rect_x1, rect_x2);
2112                 int x2 = MAX(rect_x1, rect_x2);
2113                 int y1 = MIN(rect_y1, rect_y2);
2114                 int y2 = MAX(rect_y1, rect_y2);
2115
2116                 if(select_rectangle(data,
2117                         x1,
2118                         y1,
2119                         x2,
2120                         y2))
2121                 {
2122                         selection_changed();
2123                 }
2124
2125                 clamp_positions();
2126                 draw_items(0);
2127                 update_scrollbars(1);
2128         }
2129         return result;
2130 }
2131
2132 int BC_ListBox::select_scroll_event()
2133 {
2134         int result = drag_scroll_event();
2135
2136         if(result)
2137         {
2138                 highlighted_item = selection_number = get_cursor_item(data,
2139                         get_cursor_x(),
2140                         get_cursor_y(),
2141                         &highlighted_ptr);
2142                 clamp_positions();
2143                 draw_items(0);
2144                 update_scrollbars(1);
2145                 selection_changed();
2146         }
2147         return result;
2148 }
2149
2150 int BC_ListBox::select_rectangle(ArrayList<BC_ListBoxItem*> *data,
2151                 int x1,
2152                 int y1,
2153                 int x2,
2154                 int y2)
2155 {
2156         int result = 0;
2157         for(int i = 0; i < data[master_column].total; i++)
2158         {
2159                 for(int j = 0; j < columns; j++)
2160                 {
2161                         BC_ListBoxItem *item = data[j].values[i];
2162                         if(display_format == LISTBOX_ICONS)
2163                         {
2164                                 int icon_x, icon_y, icon_w, icon_h;
2165                                 int text_x, text_y, text_w, text_h;
2166                                 get_icon_mask(item, icon_x, icon_y, icon_w, icon_h);
2167                                 get_text_mask(item, text_x, text_y, text_w, text_h);
2168
2169                                 if((x2 >= icon_x && x1 < icon_x + icon_w &&
2170                                         y2 >= icon_y && y1 < icon_y + icon_h) ||
2171                                         (x2 >= text_x && x1 < text_x + text_w &&
2172                                         y2 >= text_y && y1 < text_y + text_h))
2173                                 {
2174                                         if(!item->selected)
2175                                         {
2176                                                 item->selected = 1;
2177                                                 result = 1;
2178                                         }
2179                                 }
2180                                 else
2181                                 {
2182                                         if(item->selected)
2183                                         {
2184                                                 item->selected = 0;
2185                                                 result = 1;
2186                                         }
2187                                 }
2188                         }
2189                         else
2190                         {
2191                                 if(x2 >= 0 &&
2192                                         x1 < (yscrollbar ?
2193                                                 gui->get_w() - get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w() :
2194                                                 gui->get_w()) &&
2195                                         y2 > 0 &&
2196                                         y1 < gui->get_h() &&
2197                                         y2 >= get_item_y(item) &&
2198                                         y1 < get_item_y(item) + get_item_h(item))
2199                                 {
2200                                         if(!item->selected)
2201                                         {
2202                                                 item->selected = 1;
2203                                                 result = 1;
2204                                         }
2205                                 }
2206                                 else
2207                                 {
2208                                         if(item->selected)
2209                                         {
2210                                                 item->selected = 0;
2211                                                 result = 1;
2212                                         }
2213                                 }
2214                         }
2215                 }
2216
2217                 BC_ListBoxItem *item = data[master_column].values[i];
2218                 if(item->get_sublist() &&
2219                         item->get_expand())
2220                         result |= select_rectangle(item->get_sublist(),
2221                                 x1,
2222                                 y1,
2223                                 x2,
2224                                 y2);
2225         }
2226         return result;
2227 }
2228
2229 int BC_ListBox::reposition_item(ArrayList<BC_ListBoxItem*> *data,
2230                 int selection_number,
2231                 int x,
2232                 int y,
2233                 int *counter)
2234 {
2235         int temp = -1;
2236         if(!counter) counter = &temp;
2237
2238
2239         for(int i = 0; i < data[master_column].total; i++)
2240         {
2241                 BC_ListBoxItem *item = data[master_column].values[i];
2242                 (*counter)++;
2243                 if((*counter) == selection_number)
2244                 {
2245                         item->icon_x = x;
2246                         item->icon_y = y;
2247                         return 1;
2248                 }
2249 // Not recursive because it's only used for icons
2250         }
2251         return 0;
2252 }
2253
2254 void BC_ListBox::move_selection(ArrayList<BC_ListBoxItem*> *dst,
2255         ArrayList<BC_ListBoxItem*> *src)
2256 {
2257         for(int i = 0; i < src[master_column].total; i++)
2258         {
2259                 BC_ListBoxItem *item = src[master_column].values[i];
2260
2261 // Move item to dst
2262                 if(item->selected)
2263                 {
2264                         for(int j = 0; j < columns; j++)
2265                         {
2266                                 dst[j].append(src[j].values[i]);
2267                                 src[j].remove_number(i);
2268                         }
2269                 }
2270                 else
2271 // Descend into sublist
2272                 if(item->get_sublist())
2273                 {
2274                         move_selection(dst,
2275                                 item->get_sublist());
2276                 }
2277         }
2278 }
2279
2280 int BC_ListBox::put_selection(ArrayList<BC_ListBoxItem*> *data,
2281         ArrayList<BC_ListBoxItem*> *src,
2282         int destination,
2283         int *counter)
2284 {
2285         int temp = -1;
2286         if(!counter) counter = &temp;
2287
2288         if(destination < 0)
2289         {
2290                 for(int j = 0; j < columns; j++)
2291                 {
2292                         for(int i = 0; i < src[j].total; i++)
2293                         {
2294                                 data[j].append(src[j].values[i]);
2295                         }
2296                 }
2297                 return 1;
2298         }
2299         else
2300         for(int i = 0; i < data[master_column].total; i++)
2301         {
2302                 (*counter)++;
2303                 if((*counter) == destination)
2304                 {
2305                         for(int j = 0; j < columns; j++)
2306                         {
2307                                 for(int k = 0; k < src[j].total; k++)
2308                                 {
2309                                         data[j].insert(src[j].values[k], destination + k);
2310                                 }
2311                         }
2312                         return 1;
2313                 }
2314
2315                 BC_ListBoxItem *item = data[master_column].values[i];
2316                 if(item->get_sublist())
2317                 {
2318                         if(put_selection(item->get_sublist(),
2319                                 src,
2320                                 destination,
2321                                 counter))
2322                                 return 1;
2323                 }
2324         }
2325         return 0;
2326 }
2327
2328
2329
2330 int BC_ListBox::item_to_index(ArrayList<BC_ListBoxItem*> *data,
2331                 BC_ListBoxItem *item,
2332                 int *counter)
2333 {
2334         int temp = -1;
2335         if(!counter) counter = &temp;
2336
2337         for(int i = 0; i < data[master_column].total; i++)
2338         {
2339                 (*counter)++;
2340                 for(int j = 0; j < columns; j++)
2341                 {
2342                         BC_ListBoxItem *new_item = data[j].values[i];
2343 //printf("BC_ListBox::item_to_index 1 %d %d %p\n", j, i, new_item);
2344                         if(new_item == item)
2345                         {
2346                                 return (*counter);
2347                         }
2348                 }
2349
2350                 BC_ListBoxItem *new_item = data[master_column].values[i];
2351                 if(new_item->get_sublist())
2352                 {
2353                         if(item_to_index(new_item->get_sublist(),
2354                                 item,
2355                                 counter) >= 0)
2356                                 return (*counter);
2357                 }
2358         }
2359
2360         return -1;
2361 }
2362
2363 BC_ListBoxItem* BC_ListBox::index_to_item(ArrayList<BC_ListBoxItem*> *data,
2364                 int number,
2365                 int column,
2366                 int *counter)
2367 {
2368         int temp = -1;
2369         if(!counter) counter = &temp;
2370         for(int i = 0; i < data[master_column].total; i++)
2371         {
2372                 (*counter)++;
2373                 if((*counter) == number)
2374                 {
2375                         return data[column].values[i];
2376                 }
2377                 BC_ListBoxItem *item = data[master_column].values[i];
2378                 if(item->get_sublist())
2379                 {
2380                         BC_ListBoxItem *result = index_to_item(item->get_sublist(),
2381                                 number,
2382                                 column,
2383                                 counter);
2384                         if(result) return result;
2385                 }
2386         }
2387         return 0;
2388 }
2389
2390 int BC_ListBox::get_cursor_item(ArrayList<BC_ListBoxItem*> *data,
2391         int cursor_x,
2392         int cursor_y,
2393         BC_ListBoxItem **item_return,
2394         int *counter,
2395         int expanded)
2396 {
2397         int temp = -1;
2398         if(!data) return -1;
2399         if(!counter) counter = &temp;
2400
2401 // Icons are not treed
2402         if(display_format == LISTBOX_ICONS)
2403         {
2404                 for(int j = data[master_column].total - 1; j >= 0; j--)
2405                 {
2406                         int icon_x, icon_y, icon_w, icon_h;
2407                         int text_x, text_y, text_w, text_h;
2408                         BC_ListBoxItem *item = data[master_column].values[j];
2409                         get_icon_mask(item, icon_x, icon_y, icon_w, icon_h);
2410                         get_text_mask(item, text_x, text_y, text_w, text_h);
2411
2412                         if((cursor_x >= icon_x && cursor_x < icon_x + icon_w &&
2413                                 cursor_y >= icon_y && cursor_y < icon_y + icon_h) ||
2414                                 (cursor_x >= text_x && cursor_x < text_x + text_w &&
2415                                 cursor_y >= text_y && cursor_y < text_y + text_h))
2416                         {
2417                                 if(item_return) (*item_return) = item;
2418                                 return j;
2419                         }
2420                 }
2421         }
2422         else if( gui ) {
2423 // Text is treed
2424 // Cursor is inside items rectangle
2425                 if(cursor_x >= 0 &&
2426                         cursor_x < (yscrollbar ?
2427                                 gui->get_w() - get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w() :
2428                                 gui->get_w()) &&
2429 // Only clamp y if we're not in a SELECT operation.
2430                         (current_operation == BC_ListBox::SELECT ||
2431                                 (cursor_y > get_title_h() + LISTBOX_BORDER &&
2432                                 cursor_y < gui->get_h())))
2433                 {
2434 // Search table for cursor obstruction
2435                         for(int i = 0; i < data[master_column].total; i++)
2436                         {
2437                                 BC_ListBoxItem *item = data[master_column].values[i];
2438                                 (*counter)++;
2439
2440 // Cursor is inside item on current level
2441                                 if(expanded &&
2442                                         item->selectable &&
2443                                         cursor_y >= get_item_y(item) &&
2444                                         cursor_y < get_item_y(item) + get_item_h(item))
2445                                 {
2446 //printf("BC_ListBox::get_cursor_item %d %d %p\n", master_column, i, item);
2447                                         if(item_return) (*item_return) = item;
2448                                         return (*counter);
2449                                 }
2450
2451 // Descend into sublist
2452                                 if(item->get_sublist())
2453                                 {
2454                                         if(get_cursor_item(item->get_sublist(),
2455                                                 cursor_x,
2456                                                 cursor_y,
2457                                                 item_return,
2458                                                 counter,
2459                                                 item->get_expand()) >= 0)
2460                                                 return (*counter);
2461                                 }
2462                         }
2463                 }
2464         }
2465         return -1;
2466 }
2467
2468 int BC_ListBox::repeat_event(int64_t duration)
2469 {
2470         switch(current_operation)
2471         {
2472 // Repeat out of bounds selection
2473                 case SELECT_RECT:
2474                         if(duration == get_resources()->scroll_repeat)
2475                                 return rectangle_scroll_event();
2476                         break;
2477
2478                 case SELECT:
2479                         if(duration == get_resources()->scroll_repeat)
2480                                 return select_scroll_event();
2481                         break;
2482
2483                 case NO_OPERATION:
2484 // Show tooltip
2485                         if(button_highlighted && is_popup &&
2486                                 tooltip_text && tooltip_text[0] != 0 &&
2487                                 duration == get_resources()->tooltip_delay)
2488                         {
2489                                 show_tooltip();
2490                                 return 1;
2491                         }
2492                         break;
2493         }
2494         return 0;
2495 }
2496
2497
2498 int BC_ListBox::cursor_enter_event()
2499 {
2500         int result = 0;
2501
2502         switch(current_operation)
2503         {
2504 // Cursor moved over button, pressed, and exited.
2505                 case BUTTON_DOWN_SELECT:
2506                         if(top_level->event_win == win)
2507                         {
2508                                 current_operation = BUTTON_DN;
2509                                 result = 1;
2510                                 button_highlighted = 1;
2511                                 draw_button(1);
2512                         }
2513                         break;
2514
2515                 case NO_OPERATION:
2516 // Cursor entered button
2517                         if(is_popup && top_level->event_win == win)
2518                         {
2519                                 button_highlighted = 1;
2520                                 result = 1;
2521                                 draw_button(1);
2522                         }
2523                         else
2524 // TODO: Need to get the highlighted column title or item
2525                         if(gui && top_level->event_win == gui->win)
2526                         {
2527                                 list_highlighted = 1;
2528                                 draw_border(1);
2529                                 result = 1;
2530                         }
2531                         break;
2532         }
2533
2534         return result;
2535 }
2536
2537 int BC_ListBox::cursor_leave_event()
2538 {
2539         if(current_operation == COLUMN_DRAG) return 0;
2540
2541 // Left button area
2542         if(button_highlighted)
2543         {
2544                 button_highlighted = 0;
2545                 hide_tooltip();
2546                 draw_button(1);
2547         }
2548
2549         if(list_highlighted)
2550         {
2551                 list_highlighted = 0;
2552                 highlighted_item = -1;
2553                 highlighted_ptr = 0;
2554                 highlighted_title = -1;
2555                 int redraw_toggles = 0;
2556                 for(int i = 0; i < expanders.total; i++)
2557                         expanders.values[i]->cursor_leave_event(&redraw_toggles);
2558
2559                 draw_items(1);
2560         }
2561
2562         return 0;
2563 }
2564
2565 int BC_ListBox::get_first_selection(ArrayList<BC_ListBoxItem*> *data, int *result)
2566 {
2567         int temp = -1;
2568         if(!result) result = &temp;
2569
2570         for(int i = 0; i < data[master_column].total; i++)
2571         {
2572                 BC_ListBoxItem *item = data[master_column].values[i];
2573                 (*result)++;
2574                 if(item->selected) return (*result);
2575                 if(item->get_sublist())
2576                 {
2577                         if(get_first_selection(item->get_sublist(), result) >= 0)
2578                                 return (*result);
2579                 }
2580         }
2581         return -1;
2582 }
2583
2584 int BC_ListBox::get_total_items(ArrayList<BC_ListBoxItem*> *data,
2585         int *result,
2586         int master_column)
2587 {
2588         int temp = 0;
2589         if(!result) result = &temp;
2590
2591         for(int i = 0; i < data[master_column].total; i++)
2592         {
2593                 (*result)++;
2594                 if(data[master_column].values[i]->get_sublist())
2595                         get_total_items(data[master_column].values[i]->get_sublist(),
2596                                 result,
2597                                 master_column);
2598         }
2599
2600         return (*result);
2601 }
2602
2603
2604 int BC_ListBox::get_last_selection(ArrayList<BC_ListBoxItem*> *data,
2605         int *result)
2606 {
2607         int temp = -1;
2608         int top_level = 0;
2609         if(!result)
2610         {
2611                 result = &temp;
2612                 top_level = 1;
2613         }
2614
2615         for(int i = data[master_column].total - 1; i >= 0; i--)
2616         {
2617                 BC_ListBoxItem *item = data[master_column].values[i];
2618                 (*result)++;
2619                 if(item->selected)
2620                 {
2621                         if(top_level)
2622                                 return get_total_items(data, 0, master_column) - (*result) /* - 1 */;
2623                         else
2624                                 return (*result);
2625                 }
2626
2627                 if(item->get_sublist())
2628                 {
2629                         if(get_last_selection(item->get_sublist(), result) >= 0)
2630                         {
2631                                 if(top_level)
2632                                         return get_total_items(data, 0, master_column) - (*result) /* - 1 */;
2633                                 else
2634                                         return (*result);
2635                         }
2636                 }
2637         }
2638         return -1;
2639 }
2640
2641 void BC_ListBox::select_range(ArrayList<BC_ListBoxItem*> *data,
2642                 int start,
2643                 int end,
2644                 int *current)
2645 {
2646         int temp = -1;
2647         if(!current) current = &temp;
2648
2649         for(int i = 0; i < data[master_column].total; i++)
2650         {
2651                 (*current)++;
2652                 if((*current) >= start && (*current) < end)
2653                 {
2654                         for(int j = 0; j < columns; j++)
2655                                 data[j].values[i]->selected = 1;
2656                 }
2657                 BC_ListBoxItem *item = data[master_column].values[i];
2658                 if(item->get_sublist())
2659                         select_range(item->get_sublist(),
2660                                 start,
2661                                 end,
2662                                 current);
2663         }
2664 }
2665
2666
2667 // Fill items between current selection and new selection
2668 int BC_ListBox::expand_selection(int button_press, int selection_number)
2669 {
2670         int old_selection_start = selection_start;
2671         int old_selection_end = selection_end;
2672
2673 // printf("BC_ListBox::expand_selection %d %d\n",
2674 // selection_center,
2675 // selection_number);
2676
2677 // Calculate the range to select based on selection_center and selection_number
2678         if(selection_number < selection_center)
2679         {
2680                 selection_start = selection_number;
2681         }
2682         else
2683         {
2684                 selection_end = selection_number + 1;
2685         }
2686
2687 //printf("BC_ListBox::expand_selection %d %d %d %d\n", old_selection_start, old_selection_end, selection_start, selection_end);
2688 // Recurse through all the items and select the desired range
2689         select_range(data, selection_start, selection_end);
2690
2691 // Trigger redraw
2692         return (old_selection_start != selection_start ||
2693                 old_selection_end != selection_end);
2694 }
2695
2696 int BC_ListBox::toggle_item_selection(ArrayList<BC_ListBoxItem*> *data,
2697         int selection_number,
2698         int *counter)
2699 {
2700         int temp = -1;
2701         if(!counter) counter = &temp;
2702
2703         for(int i = 0; i < data[master_column].total; i++)
2704         {
2705                 BC_ListBoxItem *item = data[master_column].values[i];
2706                 (*counter)++;
2707                 if((*counter) == selection_number)
2708                 {
2709 // Get new value for selection
2710                         int selected = !item->selected;
2711 // Set row
2712                         for(int j = 0; j < columns; j++)
2713                                 data[j].values[i]->selected = selected;
2714                         return 1;
2715                 }
2716
2717 // Descend into sublist
2718                 if(item->get_sublist())
2719                 {
2720                         if(toggle_item_selection(item->get_sublist(),
2721                                 selection_number,
2722                                 counter))
2723                                 return 1;
2724                 }
2725         }
2726
2727         return 0;
2728 }
2729
2730
2731 void BC_ListBox::set_all_selected(ArrayList<BC_ListBoxItem*> *data, int value)
2732 {
2733         for(int i = 0; i < data[master_column].total; i++)
2734         {
2735                 for(int j = 0; j < columns; j++)
2736                 {
2737                         BC_ListBoxItem *item = data[j].values[i];
2738                         item->selected = value;
2739                 }
2740                 BC_ListBoxItem *item = data[master_column].values[i];
2741                 if(item->get_sublist())
2742                 {
2743                         set_all_selected(item->get_sublist(), value);
2744                 }
2745         }
2746 }
2747
2748 void BC_ListBox::set_selected(ArrayList<BC_ListBoxItem*> *data,
2749                 int item_number,
2750                 int value,
2751                 int *counter)
2752 {
2753         int temp = -1;
2754         if(!counter) counter = &temp;
2755         for(int i = 0; i < data[master_column].total && (*counter) != item_number; i++)
2756         {
2757                 (*counter)++;
2758                 if((*counter) == item_number)
2759                 {
2760                         for(int j = 0; j < columns; j++)
2761                         {
2762                                 BC_ListBoxItem *item = data[j].values[i];
2763                                 item->selected = value;
2764                         }
2765                         return;
2766                 }
2767
2768                 BC_ListBoxItem *item = data[master_column].values[i];
2769                 if(item->get_sublist())
2770                 {
2771                         set_selected(item->get_sublist(),
2772                                 item_number,
2773                                 value,
2774                                 counter);
2775                 }
2776         }
2777 }
2778
2779 int BC_ListBox::update_selection(ArrayList<BC_ListBoxItem*> *data,
2780         int selection_number,
2781         int *counter)
2782 {
2783         int temp = -1;
2784         int result = 0;
2785         if(!counter) counter = &temp;
2786
2787         for(int i = 0; i < data[master_column].total; i++)
2788         {
2789                 BC_ListBoxItem *item = data[master_column].values[i];
2790                 (*counter)++;
2791                 if((*counter) == selection_number && !item->selected)
2792                 {
2793                         result = 1;
2794                         for(int j = 0; j < columns; j++)
2795                                 data[j].values[i]->selected = 1;
2796                 }
2797                 else
2798                 if((*counter) != selection_number && item->selected)
2799                 {
2800                         result = 1;
2801                         for(int j = 0; j < columns; j++)
2802                                 data[j].values[i]->selected = 0;
2803                 }
2804                 if(item->get_sublist())
2805                         result |= update_selection(item->get_sublist(),
2806                                 selection_number,
2807                                 counter);
2808         }
2809         return result;
2810 }
2811
2812 void BC_ListBox::promote_selections(ArrayList<BC_ListBoxItem*> *data,
2813         int old_value,
2814         int new_value)
2815 {
2816         for(int i = 0; i < data[master_column].total; i++)
2817         {
2818                 for(int j = 0; j < columns; j++)
2819                 {
2820                         BC_ListBoxItem *item = data[j].values[i];
2821                         if(item->selected == old_value) item->selected = new_value;
2822                 }
2823                 BC_ListBoxItem *item = data[master_column].values[i];
2824                 if(item->get_sublist())
2825                         promote_selections(item->get_sublist(), old_value, new_value);
2826         }
2827 }
2828
2829 int BC_ListBox::focus_out_event()
2830 {
2831         deactivate();
2832         return 0;
2833 }
2834
2835 int BC_ListBox::button_press_event()
2836 {
2837         int result = 0;
2838         BC_ListBoxItem *current_item = 0;
2839         int new_cursor;
2840         int do_selection_change = 0;
2841         const int debug = 0;
2842
2843         hide_tooltip();
2844         if(debug) printf("BC_ListBox::button_press_event %d this=%p event_win=%p %p %p %p\n",
2845                 __LINE__,
2846                 this,
2847                 (void*)top_level->event_win,
2848                 (void*)(gui ? gui->win : 0),
2849                 (void*)win,
2850                 (void*)parent_window->win);
2851
2852 // Pressed in button
2853         if(is_popup && top_level->event_win == win)
2854         {
2855                 if(debug) printf("BC_ListBox::button_press_event %d\n", __LINE__);
2856                 current_operation = BUTTON_DN;
2857                 draw_button(1);
2858
2859 // Deploy listbox
2860                 if(!active && !disabled)
2861                 {
2862                         top_level->deactivate();
2863                         activate();
2864                 }
2865
2866                 result = 1;
2867         }
2868         else
2869 // Pressed in scrollbar
2870         if((xscrollbar && top_level->event_win == xscrollbar->win) ||
2871                 (yscrollbar && top_level->event_win == yscrollbar->win))
2872         {
2873                 if(debug) printf("BC_ListBox::button_press_event %d\n", __LINE__);
2874                 result = 0;
2875         }
2876         else
2877 // Pressed in items
2878         if(gui && top_level->event_win == gui->win)
2879         {
2880                 if(debug) printf("BC_ListBox::button_press_event %d\n", __LINE__);
2881
2882 // Activate list items
2883 // If it is a suggestion popup, it is visible without being active
2884                 if(!active)
2885                 {
2886                         if(debug) printf("BC_ListBox::button_press_event %d\n", __LINE__);
2887                         if(!is_suggestions) top_level->deactivate();
2888                         if(debug) printf("BC_ListBox::button_press_event %d\n", __LINE__);
2889                         activate();
2890                         if(debug) printf("BC_ListBox::button_press_event %d\n", __LINE__);
2891                 }
2892
2893 // Wheel mouse pressed
2894                 if(get_buttonpress() == 4)
2895                 {
2896                         if(current_operation == NO_OPERATION)
2897                         {
2898                                 current_operation = WHEEL;
2899                                 if(yscrollbar)
2900                                 {
2901                                         set_yposition(yposition - gui->get_h() / 10, 0);
2902                                         clamp_positions();
2903                                         update_scrollbars(0);
2904                                         highlighted_ptr = 0;
2905                                         highlighted_item = get_cursor_item(data,
2906                                                 top_level->cursor_x,
2907                                                 top_level->cursor_y,
2908                                                 &highlighted_ptr);
2909                                         draw_items(1);
2910                                         result = 1;
2911                                 }
2912                         }
2913                 }
2914                 else
2915                 if(get_buttonpress() == 5)
2916                 {
2917                         if(current_operation == NO_OPERATION)
2918                         {
2919                                 current_operation = WHEEL;
2920                                 if(yscrollbar)
2921                                 {
2922                                         set_yposition(yposition + gui->get_h() / 10, 0);
2923                                         clamp_positions();
2924                                         update_scrollbars(0);
2925                                         highlighted_ptr = 0;
2926                                         highlighted_item = get_cursor_item(data,
2927                                                 top_level->cursor_x,
2928                                                 top_level->cursor_y,
2929                                                 &highlighted_ptr);
2930                                         draw_items(1);
2931                                         result = 1;
2932                                 }
2933                         }
2934                 }
2935                 else
2936 // Pressed over column title division
2937                 if(test_column_divisions(gui->get_cursor_x(),
2938                         gui->get_cursor_y(),
2939                         new_cursor))
2940                 {
2941                         drag_cursor_x = gui->get_cursor_x() + xposition;
2942                         if(column_width)
2943                                 drag_column_w = column_width[highlighted_division - 1];
2944                         else
2945                                 drag_column_w = default_column_width[highlighted_division - 1];
2946
2947                         current_operation = DRAG_DIVISION;
2948                         reset_query();
2949                 }
2950                 else
2951 // Pressed in column title
2952                 if(test_column_titles(gui->get_cursor_x(), gui->get_cursor_y()))
2953                 {
2954                         current_operation = COLUMN_DN;
2955                         button_highlighted = 0;
2956                         list_highlighted = 1;
2957                         draw_items(1);
2958                         result = 1;
2959                 }
2960                 else
2961 // Pressed in expander
2962                 if(test_expanders())
2963                 {
2964                         current_operation = EXPAND_DN;
2965 // Need to redraw items because of alpha
2966                         draw_items(1);
2967                         result = 1;
2968                 }
2969                 else
2970 // Pressed over item
2971                 if((selection_number = get_cursor_item(data,
2972                                         gui->get_cursor_x(),
2973                                         gui->get_cursor_y(),
2974                                         &current_item)) >= 0)
2975                 {
2976                         if(debug) printf("BC_ListBox::button_press_event %d\n", __LINE__);
2977
2978 // Get item button was pressed over
2979                         selection_number2 = selection_number1;
2980                         selection_number1 = selection_number;
2981
2982                         selection_start = -1;
2983                         selection_end = -1;
2984
2985
2986 // Multiple item selection is possible
2987                         if(selection_mode == LISTBOX_MULTIPLE &&
2988                                 (ctrl_down() || shift_down()))
2989                         {
2990 // Expand text selection.
2991 // Fill items between selected region and current item.
2992                                 if(shift_down() &&
2993                                         (display_format == LISTBOX_TEXT ||
2994                                         display_format == LISTBOX_ICON_LIST))
2995                                 {
2996 // Get first item selected
2997                                         selection_start = get_first_selection(data);
2998 // Get last item selected
2999                                         selection_end = get_last_selection(data);
3000 // Get center of selected region
3001                                         if(selection_end > selection_start)
3002                                         {
3003                                                 selection_center = (selection_end + selection_start) >> 1;
3004                                         }
3005                                         else
3006                                         {
3007                                                 selection_center = selection_number;
3008                                         }
3009
3010
3011 // Deselect everything.
3012                                         set_all_selected(data, 0);
3013 // Select just the items
3014                                         expand_selection(1, selection_number);
3015                                         new_value = 1;
3016                                 }
3017                                 else
3018 // Toggle a single item on or off
3019                                 {
3020                                         toggle_item_selection(data, selection_number);
3021                                         new_value = current_item->selected;
3022                                 }
3023                         }
3024                         else
3025 // Select single item
3026                         {
3027                                 if(!current_item->selected)
3028                                 {
3029                                         set_all_selected(data, 0);
3030                                         set_selected(data,
3031                                                 selection_number,
3032                                                 1);
3033                                 }
3034                                 new_value = 1;
3035                         }
3036
3037
3038                         current_operation = SELECT;
3039                         highlighted_item = selection_number;
3040                         highlighted_ptr = current_item;
3041                         button_highlighted = 0;
3042                         list_highlighted = 1;
3043                         reset_query();
3044                         draw_items(1);
3045                         do_selection_change = 1;
3046                         result = 1;
3047                 }
3048                 else
3049                 if(data)
3050 // Pressed over nothing.  Start rectangle selection.
3051                 {
3052                         if(get_buttonpress() == 1 &&
3053                                 selection_mode == LISTBOX_MULTIPLE)
3054                         {
3055                                 if(!shift_down())
3056                                 {
3057 // Deselect all and redraw if anything was selected
3058                                         if(get_selection_number(0, 0) >= 0)
3059                                         {
3060                                                 set_all_selected(data, 0);
3061                                                 draw_items(1);
3062                                                 do_selection_change = 1;
3063                                                 result = 1;
3064                                         }
3065                                 }
3066                                 else
3067                                 {
3068 // Promote selections to protect from a rectangle selection
3069                                         promote_selections(data, 1, 2);
3070                                 }
3071
3072 // Start rectangle selection
3073                                 current_operation = SELECT_RECT;
3074                                 rect_x1 = rect_x2 = get_cursor_x();
3075                                 rect_y1 = rect_y2 = get_cursor_y();
3076                         }
3077                 }
3078
3079
3080                 reset_query();
3081         }
3082         else
3083 // Suggestion box is not active but visible, so lie to get it to deactivate
3084         if(is_popup && (active || (is_suggestions && gui)))
3085         {
3086                 active = 1;
3087                 if(debug) printf("BC_ListBox::button_press_event %d\n", __LINE__);
3088                 deactivate();
3089                 result = 1;
3090         }
3091
3092
3093         if(do_selection_change) selection_changed();
3094         if(debug) printf("BC_ListBox::button_press_event %d %d\n",
3095                 __LINE__,
3096                 result);
3097
3098         return result;
3099 }
3100
3101 int BC_ListBox::button_release_event()
3102 {
3103         int result = 0;
3104         int cursor_x, cursor_y;
3105         int do_event = 0;
3106         new_value = 0;
3107
3108 //printf("BC_ListBox::button_release_event 1 %d\n", current_operation);
3109         switch(current_operation)
3110         {
3111                 case DRAG_DIVISION:
3112                         current_operation = NO_OPERATION;
3113                         result = 1;
3114                         break;
3115
3116                 case WHEEL:
3117                         current_operation = NO_OPERATION;
3118                         result = 1;
3119                         break;
3120
3121 // Release item selection
3122                 case BUTTON_DOWN_SELECT:
3123                 case SELECT:
3124 //printf("BC_ListBox::button_release_event 10\n");
3125                         unset_repeat(get_resources()->scroll_repeat);
3126                         current_operation = NO_OPERATION;
3127                         if( gui ) {
3128                                 translate_coordinates(top_level->event_win, gui->win,
3129                                         gui->get_cursor_x(), gui->get_cursor_y(),
3130                                         &cursor_x, &cursor_y);
3131                                 selection_number1 = selection_number =
3132                                         get_cursor_item(data, cursor_x, cursor_y);
3133 //printf("BC_ListBox::button_release_event %d %d\n", selection_number2, selection_number1);
3134                         }
3135
3136                         if(is_popup)
3137                         {
3138                                 button_releases++;
3139                                 if(selection_number >= 0)
3140                                 {
3141                                         deactivate();
3142                                         do_event = 1;
3143                                 }
3144                                 else
3145 // Second button release outside button
3146                                 if(button_releases > 1)
3147                                 {
3148                                         deactivate();
3149                                 }
3150                         }
3151                         else
3152                         {
3153                                 if(top_level->get_double_click() &&
3154                                         selection_number2 == selection_number1 &&
3155                                         selection_number2 >= 0 &&
3156                                         selection_number1 >= 0)
3157                                 {
3158                                         do_event = 1;
3159                                 }
3160                                 result = 1;
3161                         }
3162                         break;
3163
3164
3165                 case SELECT_RECT:
3166                         unset_repeat(get_resources()->scroll_repeat);
3167                         if(data)
3168                         {
3169 // Demote selections from rectangle selection
3170                                 promote_selections(data, 2, 1);
3171                         }
3172
3173 // Hide rectangle overlay
3174                         draw_rectangle(1);
3175                         current_operation = NO_OPERATION;
3176                         result = 1;
3177                         break;
3178
3179 // Release popup button
3180                 case BUTTON_DN:
3181                         hide_tooltip();
3182                         current_operation = NO_OPERATION;
3183                         button_releases++;
3184                         draw_button(1);
3185
3186 // Second button release inside button
3187                         if(button_releases > 1)
3188                         {
3189                                 deactivate();
3190                         }
3191                         result = 1;
3192                         break;
3193
3194                 case COLUMN_DN:
3195                         current_operation = NO_OPERATION;
3196 // Update the sort column and the sort order for the user only if the existing
3197 // sort column is valid.
3198                         if(sort_column >= 0)
3199                         {
3200 // Invert order only if column is the same
3201                                 if(highlighted_title == sort_column)
3202                                         sort_order =
3203                                                 (sort_order == SORT_ASCENDING) ?
3204                                                 SORT_DESCENDING :
3205                                                 SORT_ASCENDING;
3206 // Set the new sort column
3207                                 sort_column = highlighted_title;
3208                                 if(!sort_order_event())
3209                                 {
3210                                         draw_titles(1);
3211                                 }
3212                         }
3213                         else
3214 // Sorting not enabled.  Redraw the title state.
3215                         {
3216                                 draw_titles(1);
3217                         }
3218                         result = 1;
3219                         break;
3220
3221                 case EXPAND_DN:
3222                 {
3223                         int redraw_toggles = 0;
3224                         for(int i = 0; i < expanders.total && !result; i++)
3225                         {
3226                                 if(expanders.values[i]->button_release_event(&redraw_toggles))
3227                                 {
3228                                         result = 1;
3229                                 }
3230                         }
3231 // Need to redraw items because of alpha
3232                         if(redraw_toggles) draw_items(1);
3233                         current_operation = NO_OPERATION;
3234                         break;
3235                 }
3236
3237                 default:
3238 // Can't default to NO_OPERATION because it may be used in a drag event.
3239                         break;
3240         }
3241
3242
3243         if(do_event) handle_event();
3244
3245 //printf("BC_ListBox::button_release_event %d %d\n", __LINE__, get_window_lock());
3246         return result;
3247 }
3248
3249 int BC_ListBox::get_title_h()
3250 {
3251         if(display_format == LISTBOX_TEXT ||
3252                 display_format == LISTBOX_ICON_LIST)
3253                 return column_titles ? column_bg[0]->get_h() : 0;
3254         else
3255                 return 0;
3256 }
3257
3258 void BC_ListBox::reset_cursor(int new_cursor)
3259 {
3260         if(is_popup)
3261         {
3262                 if(gui->get_cursor() != new_cursor)
3263                 {
3264                         gui->set_cursor(new_cursor, 0, 0);
3265                 }
3266         }
3267         else
3268         if(get_cursor() != new_cursor)
3269         {
3270                 set_cursor(new_cursor, 0, 0);
3271         }
3272 }
3273
3274 int BC_ListBox::test_column_divisions(int cursor_x, int cursor_y, int &new_cursor)
3275 {
3276         if(gui &&
3277                 column_titles &&
3278                 cursor_y >= 0 &&
3279                 cursor_y < get_title_h() &&
3280                 cursor_x >= 0 &&
3281                 cursor_x < gui->get_w())
3282         {
3283                 for(int i = 1; i < columns; i++)
3284                 {
3285                         if(cursor_x >= -xposition + get_column_offset(i) - 5 &&
3286                                 cursor_x <  -xposition + get_column_offset(i) +
3287                                         get_resources()->listbox_title_hotspot)
3288                         {
3289                                 highlighted_item = -1;
3290                                 highlighted_ptr = 0;
3291                                 highlighted_division = i;
3292                                 highlighted_title = -1;
3293                                 list_highlighted = 1;
3294                                 new_cursor = HSEPARATE_CURSOR;
3295                                 return 1;
3296                         }
3297                 }
3298         }
3299         highlighted_division = -1;
3300         return 0;
3301 }
3302
3303 int BC_ListBox::test_column_titles(int cursor_x, int cursor_y)
3304 {
3305         if(gui &&
3306                 column_titles &&
3307                 cursor_y >= 0 &&
3308                 cursor_y < get_title_h() &&
3309                 cursor_x >= 0 &&
3310                 cursor_x < gui->get_w())
3311         {
3312                 for(int i = 0; i < columns; i++)
3313                 {
3314                         if(cursor_x >= -xposition + get_column_offset(i) &&
3315                                 (cursor_x < -xposition + get_column_offset(i + 1) ||
3316                                         i == columns - 1))
3317                         {
3318                                 highlighted_item = -1;
3319                                 highlighted_ptr = 0;
3320                                 highlighted_division = -1;
3321                                 highlighted_title = i;
3322                                 list_highlighted = 1;
3323                                 return 1;
3324                         }
3325                 }
3326         }
3327         highlighted_title = -1;
3328         return 0;
3329 }
3330
3331 int BC_ListBox::test_expanders()
3332 {
3333         for(int i = 0; i < expanders.total; i++)
3334         {
3335                 if(expanders.values[i]->button_press_event())
3336                 {
3337                         current_operation = EXPAND_DN;
3338                         draw_toggles(1);
3339                         return 1;
3340                 }
3341         }
3342         return 0 ;
3343 }
3344
3345 int BC_ListBox::cursor_motion_event()
3346 {
3347         int redraw = 0, result = 0;
3348         int new_cursor = ARROW_CURSOR;
3349
3350         selection_number = -1;
3351
3352
3353         switch(current_operation)
3354         {
3355                 case BUTTON_DN:
3356 // Button pressed and slid off button
3357                         if(!cursor_inside())
3358                         {
3359                                 current_operation = BUTTON_DOWN_SELECT;
3360                                 draw_button(1);
3361                                 result = 1;
3362                         }
3363                         break;
3364
3365                 case DRAG_DIVISION:
3366                 {
3367 //                      int new_w = get_cursor_x() +
3368 //                              xposition -
3369 //                              get_column_offset(highlighted_division - 1);
3370                         int difference = get_cursor_x() + xposition - drag_cursor_x;
3371                         int new_w = drag_column_w + difference;
3372
3373                         new_cursor = HSEPARATE_CURSOR;
3374
3375                         if(column_width)
3376                         {
3377                                 column_width[highlighted_division - 1] = new_w;
3378                         }
3379                         else
3380                         {
3381                                 default_column_width[highlighted_division - 1] = new_w;
3382                         }
3383
3384                         column_width_boundaries();
3385
3386 // Force update of coords
3387                         set_autoplacement(data, 0, 1);
3388                         column_resize_event();
3389
3390                         clamp_positions();
3391                         draw_items(0);
3392                         update_scrollbars(1);
3393                         result = 1;
3394                         break;
3395                 }
3396
3397                 case SELECT_RECT:
3398                 {
3399                         if(test_drag_scroll(get_cursor_x(), get_cursor_y()))
3400                         {
3401                                 set_repeat(get_resources()->scroll_repeat);
3402                         }
3403
3404                         int old_x1 = MIN(rect_x1, rect_x2);
3405                         int old_x2 = MAX(rect_x1, rect_x2);
3406                         int old_y1 = MIN(rect_y1, rect_y2);
3407                         int old_y2 = MAX(rect_y1, rect_y2);
3408
3409                         int new_rect_x2 = get_cursor_x();
3410                         int new_rect_y2 = get_cursor_y();
3411
3412                         int x1 = MIN(rect_x1, new_rect_x2);
3413                         int x2 = MAX(rect_x1, new_rect_x2);
3414                         int y1 = MIN(rect_y1, new_rect_y2);
3415                         int y2 = MAX(rect_y1, new_rect_y2);
3416
3417 // Adjust rectangle coverage
3418                         if(old_x1 != x1 ||
3419                                 old_x2 != x2 ||
3420                                 old_y1 != y1 ||
3421                                 old_y2 != y2)
3422                         {
3423                                 if(data)
3424                                 {
3425                                         redraw = select_rectangle(data,
3426                                                 x1,
3427                                                 y1,
3428                                                 x2,
3429                                                 y2);
3430                                 }
3431
3432 // hide rectangle
3433                                 if(!redraw)
3434                                 {
3435 //printf("BC_ListBox::cursor_motion_event %d\n", __LINE__);
3436                                         draw_rectangle(0);
3437                                 }
3438                         }
3439
3440                         rect_x2 = get_cursor_x();
3441                         rect_y2 = get_cursor_y();
3442                         if(redraw)
3443                         {
3444                                 clamp_positions();
3445                                 draw_items(0);
3446                                 update_scrollbars(1);
3447                                 selection_changed();
3448 //printf("BC_ListBox::cursor_motion_event %d\n", __LINE__);
3449                         }
3450                         else
3451                         if(old_x1 != x1 ||
3452                                 old_x2 != x2 ||
3453                                 old_y1 != y1 ||
3454                                 old_y2 != y2)
3455                         {
3456 //printf("BC_ListBox::cursor_motion_event %d\n", __LINE__);
3457                                 draw_rectangle(1);
3458                         }
3459
3460                         result = 1;
3461                         break;
3462                 }
3463
3464                 case SELECT:
3465                 {
3466                         int old_highlighted_item = highlighted_item;
3467
3468                         if(test_drag_scroll(get_cursor_x(),
3469                                 get_cursor_y()))
3470                         {
3471                                 set_repeat(get_resources()->scroll_repeat);
3472                         }
3473
3474
3475                         highlighted_item = selection_number = get_cursor_item(data,
3476                                 get_cursor_x(),
3477                                 get_cursor_y(),
3478                                 &highlighted_ptr);
3479                         result = 1;
3480
3481 // Deselect all items and select just the one we're over
3482                         if(selection_number >= 0 &&
3483                                 !allow_drag &&
3484                                 ((!shift_down() &&
3485                                         !ctrl_down()) ||
3486                                         selection_mode == LISTBOX_SINGLE))
3487                         {
3488                                 redraw = update_selection(data, selection_number);
3489                         }
3490                         else
3491                         if(selection_mode == LISTBOX_MULTIPLE &&
3492                                 (shift_down() || ctrl_down()))
3493 // Expand multiple selection
3494                         {
3495 // Expand selected region in text mode centered around initial range
3496                                 if((display_format == LISTBOX_TEXT ||
3497                                         display_format == LISTBOX_ICON_LIST) &&
3498                                         shift_down())
3499                                 {
3500 // Deselect everything.
3501                                         set_all_selected(data, 0);
3502
3503 // Select just the items
3504                                         redraw = expand_selection(0, selection_number);
3505                                 }
3506                                 else
3507 // Set the one item we're over to the selection value determined in
3508 // button_press_event.
3509                                 {
3510                                         set_selected(data,
3511                                                 selection_number,
3512                                                 new_value);
3513                                 }
3514                         }
3515
3516                         if(highlighted_item != old_highlighted_item)
3517                         {
3518                                 clamp_positions();
3519                                 draw_items(0);
3520                                 update_scrollbars(1);
3521 //printf("BC_ListBox::cursor_motion_event %d %d\n", highlighted_item, old_highlighted_item);
3522                                 selection_changed();
3523                         }
3524                         break;
3525                 }
3526
3527                 case BUTTON_DOWN_SELECT:
3528 // Went back into button area
3529                         if(cursor_inside())
3530                         {
3531                                 current_operation = BUTTON_DN;
3532                                 draw_button(1);
3533                                 result = 1;
3534                         }
3535                         else
3536 // Went into item area
3537                         if(gui)
3538                         {
3539                                 int cursor_x = 0, cursor_y = 0;
3540                                 translate_coordinates(top_level->event_win,
3541                                         gui->win,
3542                                         top_level->cursor_x,
3543                                         top_level->cursor_y,
3544                                         &cursor_x,
3545                                         &cursor_y);
3546                                 int old_highlighted_item = highlighted_item;
3547                                 highlighted_item = selection_number = get_cursor_item(data,
3548                                                 cursor_x,
3549                                                 cursor_y,
3550                                                 &highlighted_ptr);
3551
3552                                 if(highlighted_item != old_highlighted_item)
3553                                 {
3554                                         update_selection(data, selection_number);
3555                                         draw_items(1);
3556                                         selection_changed();
3557                                 }
3558                         }
3559                         break;
3560
3561                 case EXPAND_DN:
3562                 {
3563                         int redraw_toggles = 0;
3564                         for(int i = 0; i < expanders.total && !result; i++)
3565                         {
3566                                 result = expanders.values[i]->cursor_motion_event(
3567                                         &redraw_toggles);
3568                         }
3569                         if(redraw_toggles)
3570                         {
3571 // Need to redraw items because of the alpha
3572                                 draw_items(1);
3573                         }
3574                         break;
3575                 }
3576
3577                 case NO_OPERATION:
3578                 {
3579                         int cursor_x = get_cursor_x(), cursor_y = get_cursor_y();
3580                         if(gui && top_level->event_win == gui->win)
3581                         {
3582                                 int old_highlighted_title = highlighted_title;
3583                                 int old_list_highlighted = list_highlighted;
3584                                 int old_highlighted_item = highlighted_item;
3585                                 int redraw_titles = 0;
3586                                 int redraw_border = 0;
3587                                 int redraw_items = 0;
3588                                 int redraw_toggles = 0;
3589                                 result = 1;
3590
3591
3592 // Test if cursor moved over a title division
3593                                 test_column_divisions(cursor_x, cursor_y, new_cursor);
3594
3595 // Test if cursor moved over a title
3596                                 if(highlighted_division < 0)
3597                                 {
3598                                         test_column_titles(cursor_x, cursor_y);
3599                                 }
3600
3601 // Test if cursor moved over expander
3602                                 if(highlighted_division < 0 &&
3603                                         highlighted_title < 0 &&
3604                                         (display_format == LISTBOX_TEXT ||
3605                                                 display_format == LISTBOX_ICON_LIST))
3606                                 {
3607                                         for(int i = 0; i < expanders.total; i++)
3608                                         {
3609                                                 expanders.values[i]->cursor_motion_event(
3610                                                         &redraw_toggles);
3611                                         }
3612 //printf("BC_ListBox::cursor_motion_event %d\n", redraw_toggles);
3613                                 }
3614
3615 // Test if cursor moved over an item
3616                                 if(highlighted_division < 0 &&
3617                                         highlighted_title < 0)
3618                                 {
3619                                         highlighted_item = get_cursor_item(data,
3620                                                 cursor_x,
3621                                                 cursor_y,
3622                                                 &highlighted_ptr);
3623                                 }
3624
3625
3626 // Clear title highlighting if moved over division
3627                                 if(old_highlighted_title != highlighted_title)
3628                                 {
3629                                         redraw_titles = 1;
3630                                 }
3631
3632 // Highlight list border
3633                                 if(old_list_highlighted != list_highlighted)
3634                                 {
3635                                         redraw_border = 1;
3636                                 }
3637
3638 // Moved out of item area
3639                                 if(old_highlighted_item != highlighted_item)
3640                                 {
3641                                         redraw_items = 1;
3642                                 }
3643
3644 //printf("BC_ListBox::cursor_motion_event 1 %d\n", highlighted_item);
3645
3646 // Change cursor to title division adjustment
3647                                 reset_cursor(new_cursor);
3648
3649                                 if(redraw_items)
3650                                 {
3651                                         draw_items(0);
3652                                 }
3653                                 else
3654                                 {
3655                                         if(redraw_titles)
3656                                                 draw_titles(0);
3657                                         if(redraw_border)
3658                                                 draw_border(0);
3659                                         if(redraw_toggles)
3660                                                 draw_toggles(0);
3661                                 }
3662
3663                                 if(redraw_items ||
3664                                         redraw_titles ||
3665                                         redraw_border ||
3666                                         redraw_toggles)
3667                                 {
3668                                         gui->flash();
3669                                         gui->flush();
3670                                 }
3671                         }
3672
3673
3674                         if(!result && list_highlighted)
3675                         {
3676                                 list_highlighted = 0;
3677                                 highlighted_item = -1;
3678                                 highlighted_ptr = 0;
3679                                 highlighted_title = -1;
3680                                 highlighted_division = -1;
3681                                 draw_items(1);
3682                                 result = 0;
3683                         }
3684                         break;
3685                 }
3686         }
3687
3688
3689         return result;
3690 }
3691
3692 int BC_ListBox::drag_start_event()
3693 {
3694         switch(current_operation)
3695         {
3696                 case SELECT:
3697                         if(gui &&
3698                                 gui->is_event_win() &&
3699                                 allow_drag)
3700                         {
3701                                 BC_ListBoxItem *item_return = 0;
3702                                 selection_number = get_cursor_item(data,
3703                                         top_level->cursor_x,
3704                                         top_level->cursor_y,
3705                                         &item_return);
3706
3707                                 if(selection_number >= 0)
3708                                 {
3709
3710                                         if (item_return->icon_vframe)
3711                                         {
3712                                                 drag_popup = new BC_DragWindow(this,
3713                                                         item_return->icon_vframe /*,
3714                                                         get_abs_cursor_x(0) - item_return->icon_vframe->get_w() / 2,
3715                                                         get_abs_cursor_y(0) - item_return->icon_vframe->get_h() / 2 */);
3716                                         }
3717                                         else
3718 // this probably works not!
3719                                         if (item_return->icon)
3720                                         {
3721                                                 drag_popup = new BC_DragWindow(this,
3722                                                         item_return->icon /*,
3723                                                         get_abs_cursor_x(0) - item_return->icon->get_w() / 2,
3724                                                         get_abs_cursor_y(0) - item_return->icon->get_h() / 2 */);
3725                                         }
3726                                         else
3727                                         {
3728                                                 drag_popup = new BC_DragWindow(this,
3729                                                         drag_icon_vframe /*,
3730                                                         get_abs_cursor_x(0) - drag_icon_vframe->get_w() / 2,
3731                                                         get_abs_cursor_y(0) - drag_icon_vframe->get_h() / 2 */);
3732                                         }
3733
3734                                         current_operation = DRAG_ITEM;
3735                                         return 1;
3736                                 }
3737                         }
3738                         break;
3739
3740                 case COLUMN_DN:
3741                         if(gui && gui->is_event_win() && allow_drag_column)
3742                         {
3743                                 drag_popup = new BC_DragWindow(this,
3744                                         drag_column_icon_vframe /*,
3745                                         get_abs_cursor_x(0) - drag_column_icon_vframe->get_w() / 2,
3746                                         get_abs_cursor_y(0) - drag_column_icon_vframe->get_h() / 2 */);
3747                                 dragged_title = highlighted_title;
3748                                 current_operation = COLUMN_DRAG;
3749                                 draw_titles(1);
3750                                 return 1;
3751                         }
3752                         break;
3753         }
3754
3755         return 0;
3756 }
3757
3758 int BC_ListBox::drag_motion_event()
3759 {
3760 //printf("BC_ListBox::drag_motion_event 1 %d\n", current_operation);
3761         switch(current_operation)
3762         {
3763                 case DRAG_ITEM:
3764                 {
3765                         int redraw = 0;
3766                         int new_highlighted_item = -1;
3767                         BC_ListBoxItem *new_highlighted_ptr = 0;
3768                         new_highlighted_item = get_cursor_item(data,
3769                                 top_level->cursor_x, top_level->cursor_y,
3770                                 &new_highlighted_ptr);
3771
3772                         if(new_highlighted_item != highlighted_item)
3773                         {
3774                                 redraw = 1;
3775                         }
3776
3777 // Always update highlighted value for drag_stop
3778                         highlighted_item = new_highlighted_item;
3779                         highlighted_ptr = new_highlighted_ptr;
3780 //printf("BC_ListBox::drag_motion_event 1 %p\n", highlighted_ptr);
3781                         if(redraw)
3782                         {
3783                                 clamp_positions();
3784                                 draw_items(0);
3785                                 update_scrollbars(1);
3786                         }
3787
3788                         return drag_popup->cursor_motion_event();
3789                         break;
3790                 }
3791
3792                 case COLUMN_DRAG:
3793                 {
3794                         int old_highlighted_title = highlighted_title;
3795                         test_column_titles(get_cursor_x(), get_cursor_y());
3796                         if(old_highlighted_title != highlighted_title)
3797                         {
3798                                 draw_titles(1);
3799                         }
3800                         return drag_popup->cursor_motion_event();
3801                         break;
3802                 }
3803         }
3804         return 0;
3805 }
3806
3807 int BC_ListBox::drag_stop_event()
3808 {
3809         switch(current_operation)
3810         {
3811                 case DRAG_ITEM:
3812 // Inside window boundary
3813                         if(top_level->cursor_x > 0 &&
3814                                 top_level->cursor_x < gui->get_w() - drag_popup->get_w() / 2 &&
3815                                 top_level->cursor_y > 0 &&
3816                                 top_level->cursor_y < gui->get_h() - drag_popup->get_h() / 2)
3817                         {
3818 // Move icon
3819
3820
3821                                 if(display_format == LISTBOX_ICONS)
3822                                 {
3823                                         reposition_item(data,
3824                                                 selection_number,
3825                                                 top_level->cursor_x +
3826                                                         drag_popup->get_offset_x() -
3827                                                         LISTBOX_MARGIN -
3828                                                         2 +
3829                                                         xposition,
3830                                                 top_level->cursor_y +
3831                                                         drag_popup->get_offset_y() -
3832                                                         LISTBOX_MARGIN -
3833                                                         2 +
3834                                                         yposition);
3835                                 }
3836                                 else
3837 // Move rows
3838                                 if(process_drag)
3839                                 {
3840 // Get destination
3841                                         int destination = highlighted_item = item_to_index(data,
3842                                                 highlighted_ptr);
3843 //printf("BC_ListBox::drag_stop_event 1 %p %d\n", highlighted_ptr, destination);
3844
3845 // Move selected items from data to temporary
3846                                         ArrayList<BC_ListBoxItem*> *src_items =
3847                                                 new ArrayList<BC_ListBoxItem*>[columns];
3848
3849                                         move_selection(src_items, data);
3850
3851 // Insert items from temporary to data
3852                                         put_selection(data,
3853                                                 src_items,
3854                                                 destination);
3855
3856
3857                                         delete [] src_items;
3858                                         set_autoplacement(data, 0, 1);
3859                                 }
3860
3861
3862                                 draw_items(1);
3863                         }
3864                         else
3865                                 drag_popup->drag_failure_event();
3866
3867                         delete drag_popup;
3868                         flush();
3869                         drag_popup = 0;
3870                         current_operation = NO_OPERATION;
3871                         new_value = 0;
3872                         return 1;
3873                         break;
3874
3875                 case COLUMN_DRAG:
3876                         if(dragged_title != highlighted_title)
3877                         {
3878                                 if(highlighted_title >= 0)
3879                                 {
3880                                         if(!move_column_event()) draw_titles(1);
3881                                 }
3882                                 else
3883                                         drag_popup->drag_failure_event();
3884                         }
3885                         current_operation = NO_OPERATION;
3886                         delete drag_popup;
3887                         flush();
3888                         drag_popup = 0;
3889                         return 1;
3890                         break;
3891         }
3892         return 0;
3893 }
3894
3895 BC_DragWindow* BC_ListBox::get_drag_popup()
3896 {
3897         return drag_popup;
3898 }
3899
3900 int BC_ListBox::translation_event()
3901 {
3902         if(is_popup && gui)
3903         {
3904                 int new_x = gui->get_x() +
3905                         (top_level->last_translate_x - top_level->prev_x -
3906                                 BC_DisplayInfo::get_left_border());
3907                 int new_y = gui->get_y() +
3908                         (top_level->last_translate_y - top_level->prev_y -
3909                                 BC_DisplayInfo::get_top_border());
3910
3911                 gui->reposition_window(new_x, new_y);
3912
3913         }
3914         return 0;
3915 }
3916
3917 int BC_ListBox::reposition_window(int x, int y, int w, int h, int flush)
3918 {
3919         if(w != -1)
3920         {
3921                 if(w != -1) popup_w = w;
3922                 if(h != -1) popup_h = h;
3923 //printf("BC_ListBox::reposition_window %d %d\n", popup_w, popup_h);
3924
3925                 if(!is_popup)
3926                 {
3927                         if(xscrollbar)
3928                                 xscrollbar->reposition_window(get_xscroll_x(),
3929                                         get_xscroll_y(),
3930                                         get_xscroll_width());
3931                         if(yscrollbar)
3932                                 yscrollbar->reposition_window(get_yscroll_x(),
3933                                         get_yscroll_y(),
3934                                         get_yscroll_height());
3935                 }
3936         }
3937
3938
3939         BC_WindowBase::reposition_window(x, y, w, h);
3940         draw_button(0);
3941         draw_items(flush);
3942         return 0;
3943 }
3944
3945 int BC_ListBox::deactivate()
3946 {
3947         hide_tooltip();
3948 // printf("BC_ListBox::deactivate %d this=%p gui=%p active=%d\n",
3949 // __LINE__,
3950 // this,
3951 // gui,
3952 // active);
3953         if(active)
3954         {
3955
3956                 if(is_popup)
3957                 {
3958 //printf("BC_ListBox::deactivate %d this=%p gui=%p\n", __LINE__, this, gui);
3959                         if(gui)
3960                         {
3961                                 delete gui;
3962                                 flush();
3963                         }
3964                         gui = 0;
3965                         xscrollbar = 0;
3966                         yscrollbar = 0;
3967                         highlighted_item = -1;
3968                         highlighted_ptr = 0;
3969 //sleep(1);
3970                 }
3971
3972
3973 //printf("BC_ListBox::deactivate %d this=%p\n", __LINE__, this);
3974                 active = 0;
3975                 top_level->active_subwindow = 0;
3976         }
3977
3978         return 0;
3979 }
3980
3981 int BC_ListBox::activate(int take_focus)
3982 {
3983         if( active ) return 0;
3984         active = 1;
3985         if( take_focus )
3986                 set_active_subwindow(this);
3987         button_releases = 0;
3988         if( !is_popup || gui ) return 0;
3989         int wx = get_x(), wy = get_y() + get_h();
3990         if( justify == LISTBOX_RIGHT ) wx += get_w() - popup_w;
3991         Window xwin;
3992         int abs_x, abs_y;
3993         XTranslateCoordinates(top_level->display,
3994                 parent_window->win, top_level->rootwin,
3995                 wx, wy, &abs_x, &abs_y, &xwin);
3996         if( x <= 0 ) x = 2;
3997         if( y <= 0 ) y = 2;
3998         return activate(abs_x, abs_y);
3999 }
4000
4001 int BC_ListBox::activate(int x, int y, int w, int h)
4002 {
4003         if( !is_popup || gui ) return 0;
4004         active = 1;
4005         if(w != -1) popup_w = w;
4006         if(h != -1) popup_h = h;
4007         reset_query();
4008         if( y + popup_h > top_level->get_root_h(0) )
4009                 y -= get_h() + popup_h;
4010         add_subwindow(gui = new BC_Popup(this,
4011                 x, y, popup_w, popup_h, -1, 0, 0));
4012         draw_items(1);
4013         gui->show_window(1);
4014         return 0;
4015 }
4016
4017 int BC_ListBox::is_active()
4018 {
4019         return active;
4020 }
4021
4022 int BC_ListBox::keypress_event()
4023 {
4024         if(!active) return 0;
4025
4026 //printf("BC_ListBox::keypress_event %d\n", __LINE__);
4027
4028         int result = 0, redraw = 0;
4029         int view_items = last_in_view - first_in_view + 1;
4030         if( view_items <= 0 ) view_items = view_h / get_text_height(MEDIUMFONT);
4031         int new_item = -1, new_selection = -1;
4032
4033         switch(top_level->get_keypress())
4034         {
4035                 case ESC:
4036                 case RETURN:
4037                         top_level->deactivate();
4038
4039 // If user is manipulating popup with keyboard, don't pass on event.
4040                         if(is_popup)
4041                         {
4042 //printf("BC_ListBox::keypress_event %d %p\n", __LINE__, get_selection(0, 0));
4043                                 if(top_level->get_keypress() == RETURN)
4044                                         handle_event();
4045 //printf("BC_ListBox::keypress_event %d %p\n", __LINE__, get_selection(0, 0));
4046                                 result = 1;
4047                         }
4048                         else
4049                                 result = 0;
4050                         break;
4051
4052                 case UP:
4053                         new_selection = new_item = select_previous(0);
4054
4055 //printf("BC_ListBox::keypress_event 1 %d\n", new_item);
4056                         if(new_item >= 0)
4057                         {
4058                                 center_selection(new_item);
4059                                 redraw = 1;
4060                         }
4061                         reset_query();
4062                         result = 1;
4063                         break;
4064
4065                 case DOWN:
4066                         new_selection = new_item = select_next(0);
4067
4068                         if(new_item >= 0)
4069                         {
4070                                 center_selection(new_item);
4071                                 redraw = 1;
4072                         }
4073                         reset_query();
4074                         result = 1;
4075                         break;
4076
4077                 case PGUP:
4078                         new_selection = new_item = select_previous(view_items - 1);
4079
4080                         if(new_item >= 0)
4081                         {
4082                                 center_selection(new_item);
4083                                 redraw = 1;
4084                         }
4085                         reset_query();
4086                         result = 1;
4087                         break;
4088
4089                 case PGDN:
4090                         new_selection = new_item = select_next(view_items - 1);
4091
4092                         if(new_item >= 0)
4093                         {
4094                                 center_selection(new_item);
4095                                 redraw = 1;
4096                         }
4097                         reset_query();
4098                         result = 1;
4099                         break;
4100
4101                 case LEFT:
4102                         xposition -= 10;
4103                         redraw = 1;
4104                         result = 1;
4105                         break;
4106
4107                 case RIGHT:
4108                         xposition += 10;
4109                         redraw = 1;
4110                         result = 1;
4111                         break;
4112
4113                 case DELETE:
4114                 case HOME:
4115                 case END:
4116                         result = 0;
4117                         break;
4118
4119                 default:
4120                         if(!ctrl_down())
4121                         {
4122                                 int query_len = strlen(query);
4123                                 if( query_len < (int)sizeof(query)-1 &&
4124                                         top_level->get_keypress() > 30 &&
4125                                         top_level->get_keypress() < 127)
4126                                 {
4127                                         query[query_len++] = top_level->get_keypress();
4128                                         query[query_len] = 0;
4129                                         new_selection = query_list();
4130                                 }
4131                                 else
4132                                 if(top_level->get_keypress() == BACKSPACE)
4133                                 {
4134                                         if(query_len > 0) query[--query_len] = 0;
4135                                         new_selection = query_list();
4136                                 }
4137                                 if( show_query ) {
4138                                         if( query_len > 0 )
4139                                                 show_tooltip(query);
4140                                         else
4141                                                 hide_tooltip();
4142                                 }
4143                                 redraw = 1;
4144                                 result = 1;
4145                         }
4146                         break;
4147         }
4148
4149         if(redraw)
4150         {
4151                 clamp_positions();
4152                 draw_items(0);
4153                 update_scrollbars(1);
4154         }
4155
4156 //printf("BC_ListBox::keypress_event %d new_selection=%d\n", __LINE__, new_selection);
4157         if(new_selection >= 0 && !is_suggestions)
4158         {
4159 //printf("BC_ListBox::keypress_event %d\n", __LINE__);
4160                 selection_changed();
4161 //printf("BC_ListBox::keypress_event %d\n", __LINE__);
4162         }
4163
4164         return result;
4165 }
4166
4167
4168 BC_Pixmap* BC_ListBox::get_bg_surface()
4169 {
4170         return bg_surface;
4171 }
4172
4173
4174 void BC_ListBox::draw_background()
4175 {
4176         if( !bg_draw ) return;
4177         bg_draw = 0;
4178 // White background pixmap
4179         set_color(top_level->get_resources()->listbox_inactive);
4180         draw_box(0, 0, bg_surface->get_w(), bg_surface->get_h(), bg_surface);
4181
4182 // Optional heroine pixmap
4183         if(bg_pixmap)
4184                 bg_surface->draw_pixmap(bg_pixmap,
4185                         bg_surface->get_w() - top_level->get_resources()->listbox_bg->get_w(),
4186                         0);
4187 }
4188
4189 void BC_ListBox::clear_listbox(int x, int y, int w, int h)
4190 {
4191         gui->draw_pixmap(bg_surface, x, y, w, h, x, y - title_h);
4192 }
4193
4194 void BC_ListBox::update_format(int display_format, int redraw)
4195 {
4196         this->display_format = display_format;
4197         if( redraw && gui ) draw_items(1, 1);
4198 }
4199
4200 int BC_ListBox::get_format()
4201 {
4202         return display_format;
4203 }
4204
4205
4206
4207 int BC_ListBox::draw_items(int flush, int draw_bg)
4208 {
4209         if(gui)
4210         {
4211                 BC_Resources *resources = get_resources();
4212
4213 //dump(data, columns);
4214
4215 // Calculate items width
4216                 calculate_item_coords();
4217
4218
4219 // Create and destroy scrollbars as needed
4220                 get_scrollbars();
4221
4222
4223
4224                 if( bg_draw ) this->bg_draw = 1;
4225                 draw_background();
4226
4227                 first_in_view = -1;
4228                 last_in_view = 0;
4229 // Icon display
4230                 if(display_format == LISTBOX_ICONS)
4231                 {
4232                         clear_listbox(2, 2 + title_h, view_w, view_h);
4233
4234                         set_font(MEDIUMFONT);
4235                         for(int i = 0; i < data[master_column].size(); i++)
4236                         {
4237                                 BC_ListBoxItem *item = data[master_column].get(i);
4238                                 if(get_item_x(item) >= -get_item_w(item) &&
4239                                         get_item_x(item) < view_w &&
4240                                         get_item_y(item) >= -get_item_h(item) + title_h &&
4241                                         get_item_y(item) < view_h + title_h)
4242                                 {
4243                                         item->set_in_view(1);
4244                                         if( first_in_view < 0 ) first_in_view = i;
4245                                         last_in_view = i;
4246                                         int item_color = get_item_highlight(data, 0, i);
4247                                         int icon_x, icon_y, icon_w, icon_h;
4248                                         int text_x, text_y, text_w, text_h;
4249
4250 // Draw highlights
4251                                         get_icon_mask(item, icon_x, icon_y, icon_w, icon_h);
4252                                         get_text_mask(item, text_x, text_y, text_w, text_h);
4253
4254
4255                                         if(item_color != resources->listbox_inactive)
4256                                         {
4257                                                 gui->set_color(BLACK);
4258                                                 gui->draw_rectangle(icon_x, icon_y, icon_w, icon_h);
4259                                                 gui->set_color(item_color);
4260                                                 gui->draw_box(icon_x + 1, icon_y + 1, icon_w - 2, icon_h - 2);
4261                                                 gui->set_color(BLACK);
4262                                                 gui->draw_rectangle(text_x, text_y, text_w, text_h);
4263                                                 gui->set_color(item_color);
4264                                                 gui->draw_box(text_x + 1, text_y + 1, text_w - 2, text_h - 2);
4265
4266                                                 if(icon_position == ICON_LEFT)
4267                                                         gui->draw_box(text_x - 1, text_y + 1, 2, text_h - 2);
4268                                                 else
4269                                                 if(icon_position == ICON_TOP)
4270                                                         gui->draw_line(text_x + 1, text_y, text_x + icon_w - 2, text_y);
4271                                                 if(text_x + text_w < icon_x + icon_w)
4272                                                 {
4273                                                         gui->set_color(BLACK);
4274                                                         gui->draw_line(text_x + text_w,
4275                                                                 icon_y + icon_h,
4276                                                                 icon_x + icon_w,
4277                                                                 icon_y + icon_h);
4278                                                 }
4279                                         }
4280
4281 // Draw icons
4282                                         gui->set_color(get_item_color(data, 0, i));
4283                                         if(item->icon)
4284                                                 gui->pixmap->draw_pixmap(item->icon,
4285                                                         icon_x + ICON_MARGIN,
4286                                                         icon_y + ICON_MARGIN);
4287
4288
4289                                         gui->draw_text(text_x + ICON_MARGIN,
4290                                                 text_y + ICON_MARGIN + get_baseline(item),
4291                                                 item->text);
4292                                 }
4293                                 else
4294                                         item->set_in_view(0);
4295                         }
4296                 }
4297                 else
4298 // Text display
4299                 {
4300 // Draw one column at a time so text overruns don't go into the next column
4301 // clear column backgrounds
4302                         int current_toggle = 0;
4303                         for(int j = 0; j < columns; j++)
4304                         {
4305                                 clear_listbox(LISTBOX_BORDER + get_column_offset(j) - xposition,
4306                                         LISTBOX_BORDER + title_h,
4307                                         get_column_width(j, 1),
4308                                         view_h);
4309 // Draw rows in the column recursively
4310                                 draw_text_recursive(data, j, 0, &current_toggle);
4311                         }
4312
4313 // Delete excess expanders
4314                         while(expanders.total > current_toggle)
4315                         {
4316                                 expanders.remove_object();
4317                         }
4318                 }
4319
4320 // draw user images if available
4321                 draw_images();
4322 // Draw titles on top of rows for superposition effect
4323                 draw_titles(0);
4324
4325 // Clear garbage from bottom right corner
4326                 if(xscrollbar && yscrollbar && is_popup)
4327                 {
4328                         gui->draw_top_background(parent_window,
4329                                 popup_w - get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w(),
4330                                 popup_h - get_resources()->hscroll_data[SCROLL_HANDLE_UP]->get_h(),
4331                                 get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w(),
4332                                 get_resources()->hscroll_data[SCROLL_HANDLE_UP]->get_h());
4333                 }
4334
4335 // Draw borders
4336                 draw_border(0);
4337
4338
4339                 if(current_operation == SELECT_RECT)
4340                         draw_rectangle(0);
4341
4342                 gui->flash(flush);
4343         }
4344
4345         return 0;
4346 }
4347
4348
4349 void BC_ListBox::draw_text_recursive(ArrayList<BC_ListBoxItem*> *data,
4350         int column,
4351         int indent,
4352         int *current_toggle)
4353 {
4354         if(!data) return;
4355
4356
4357         BC_Resources *resources = get_resources();
4358
4359         set_font(MEDIUMFONT);
4360         int subindent = 0;
4361
4362 // Search for a branch and make room for toggle if there is one
4363         if(column == 0)
4364         {
4365                 for(int i = 0; i < data[column].size(); i++)
4366                 {
4367                         if(data[column].get(i)->get_sublist())
4368                         {
4369                                 subindent = BC_WindowBase::get_resources()->listbox_expand[0]->get_w();
4370                                 break;
4371                         }
4372                 }
4373         }
4374
4375         row_height = row_ascent = row_descent = 0;
4376         for(int i = 0; i < data[column].total; i++)
4377         {
4378                 BC_ListBoxItem *item = data[column].values[i];
4379                 int ht = get_text_h(item);
4380                 if( ht > row_height ) row_height = ht;
4381                 int bl = get_baseline(item);
4382                 if( bl > row_ascent ) row_ascent = bl;
4383                 int dt = ht - bl;
4384                 if( dt > row_descent ) row_ascent = bl;
4385         }
4386
4387         for(int i = 0; i < data[column].size(); i++)
4388         {
4389 // Draw a row
4390                 BC_ListBoxItem *item = data[column].values[i];
4391                 BC_ListBoxItem *first_item = data[master_column].values[i];
4392
4393                 if(get_item_y(item) >= -get_item_h(item) + title_h &&
4394                         get_item_y(item) < view_h + title_h)
4395                 {
4396                         int row_color = get_item_highlight(data, 0, i);
4397                         int x, y, w, h, column_width;
4398
4399                         get_text_mask(item, x, y, w, h);
4400                         column_width = get_column_width(column, 1);
4401                         if(x + column_width > view_w + LISTBOX_BORDER * 2)
4402                                 column_width = view_w + LISTBOX_BORDER * 2 - x;
4403
4404                         if(row_color != resources->listbox_inactive)
4405                         {
4406                                 gui->set_color(row_color);
4407                                 gui->draw_box(x, y, column_width, h);
4408                                 gui->set_color(BLACK);
4409                                 int xx = x + column_width-1;
4410                                 gui->draw_line(x, y, xx, y);
4411                                 int hh = row_height;
4412                                 if( display_format == LISTBOX_ICON_LIST ) {
4413                                         int ih = get_icon_h(item);
4414                                         if( ih > hh ) hh = ih;
4415                                 }
4416                                 int yy = y + hh-1;
4417                                 gui->draw_line(x, yy, xx, yy);
4418                         }
4419
4420                         gui->set_color(get_item_color(data, column, i));
4421
4422
4423                         if(column == 0 && display_format == LISTBOX_ICON_LIST)
4424                         {
4425                                 if(item->icon)
4426                                 {
4427                                         gui->pixmap->draw_pixmap(item->icon,
4428                                                 x,
4429                                                 y);
4430                                         x += item->icon->get_w() + ICON_MARGIN;
4431                                 }
4432                         }
4433
4434
4435 // Indent only applies to first column
4436                         gui->draw_text(
4437                                 x + LISTBOX_BORDER + LISTBOX_MARGIN +
4438                                         (column == 0 ? indent + subindent : 0),
4439                                 y + get_baseline(item), item->text);
4440                         item->set_in_view(1);
4441                         if( !indent ) {
4442                                 if( first_in_view < 0 ) first_in_view = i;
4443                                 last_in_view = i;
4444                         }
4445
4446 // Update expander
4447                         if(column == 0 &&
4448                                 item->get_sublist() &&
4449                                 item->get_columns())
4450                         {
4451 // Create new expander
4452                                 if(*current_toggle >= expanders.total)
4453                                 {
4454                                         BC_ListBoxToggle *toggle =
4455                                                 new BC_ListBoxToggle(this,
4456                                                         item,
4457                                                         x + LISTBOX_BORDER + LISTBOX_MARGIN + indent,
4458                                                         y);
4459                                         toggle->draw(0);
4460                                         expanders.append(toggle);
4461                                 }
4462                                 else
4463 // Reposition existing expander
4464                                 {
4465                                         BC_ListBoxToggle *toggle = expanders.values[*current_toggle];
4466 //printf("BC_ListBox::draw_text_recursive 1 %d\n", *current_toggle);
4467                                         toggle->update(item,
4468                                                 x + LISTBOX_BORDER + LISTBOX_MARGIN + indent,
4469                                                 y,
4470                                                 0);
4471                                 }
4472                                 (*current_toggle)++;
4473                         }
4474
4475
4476
4477                 }
4478                 else
4479                         item->set_in_view(0);
4480
4481 // Descend into sublist
4482                 if(first_item->get_expand())
4483                 {
4484                         draw_text_recursive(first_item->get_sublist(),
4485                                 column,
4486                                 indent + LISTBOX_INDENT,
4487                                 current_toggle);
4488                 }
4489         }
4490 }
4491
4492
4493
4494
4495 int BC_ListBox::draw_border(int flash)
4496 {
4497         BC_Resources *resources = top_level->get_resources();
4498         gui->draw_3d_border(0,
4499                 0,
4500                 view_w + LISTBOX_BORDER * 2,
4501                 view_h + title_h + LISTBOX_BORDER * 2,
4502                 resources->listbox_border1,
4503                 list_highlighted ?
4504                         resources->listbox_border2_hi :
4505                         resources->listbox_border2,
4506                 list_highlighted ?
4507                         resources->listbox_border3_hi :
4508                         resources->listbox_border3,
4509                 resources->listbox_border4);
4510
4511         if(flash)
4512         {
4513                 gui->flash();
4514                 gui->flush();
4515         }
4516         return 0;
4517 }
4518
4519 void BC_ListBox::draw_title(int number)
4520 {
4521 // Column title background
4522         int image_number = 0;
4523         if(number == highlighted_title)
4524         {
4525                 image_number = 1;
4526                 if(current_operation == COLUMN_DN)
4527                         image_number = 2;
4528         }
4529
4530         int column_offset = get_column_offset(number) - xposition + LISTBOX_BORDER;
4531         int column_width = get_column_width(number, 1);
4532         gui->draw_3segmenth(get_column_offset(number) - xposition + LISTBOX_BORDER,
4533                 LISTBOX_BORDER,
4534                 get_column_width(number, 1) + get_resources()->listbox_title_overlap,
4535                 column_bg[image_number]);
4536
4537 // Column title sort order
4538         if(number == sort_column)
4539         {
4540                 BC_Pixmap *src;
4541                 if(sort_order == SORT_ASCENDING)
4542                         src = column_sort_dn;
4543                 else
4544                         src = column_sort_up;
4545
4546                 int x = column_offset +
4547                         column_width -
4548                         LISTBOX_BORDER;
4549                 if(x > items_w) x = items_w;
4550                 x -= 5 + src->get_w();
4551                 gui->draw_pixmap(src,
4552                         x,
4553                         title_h / 2 - src->get_h() / 2 + LISTBOX_BORDER);
4554         }
4555
4556
4557         int x = -xposition +
4558                 get_column_offset(number) +
4559                 LISTBOX_MARGIN +
4560                 LISTBOX_BORDER;
4561         x += get_resources()->listbox_title_margin;
4562
4563         gui->set_color(get_resources()->listbox_title_color);
4564         gui->draw_text(x,
4565                 LISTBOX_MARGIN + LISTBOX_BORDER + get_text_ascent(MEDIUMFONT),
4566                 _(column_titles[number]));
4567 }
4568
4569 int BC_ListBox::draw_titles(int flash)
4570 {
4571         if(column_titles &&
4572                 (display_format == LISTBOX_TEXT ||
4573                 display_format == LISTBOX_ICON_LIST))
4574         {
4575 //printf("BC_ListBox::draw_titles 1 %d\n", highlighted_title);
4576                 for(int i = 0; i < columns; i++)
4577                 {
4578                         if(i != highlighted_title)
4579                                 draw_title(i);
4580                 }
4581
4582                 if(highlighted_title >= 0) draw_title(highlighted_title);
4583                 draw_border(0);
4584         }
4585
4586         if(flash)
4587         {
4588                 gui->flash();
4589         }
4590         return 0;
4591 }
4592
4593 void BC_ListBox::draw_toggles(int flash)
4594 {
4595         for(int i = 0; i < expanders.total; i++)
4596                 expanders.values[i]->draw(0);
4597
4598 //printf("BC_ListBox::draw_toggles 1 %d\n", flash);
4599         if(flash && expanders.total)
4600         {
4601                 gui->flash();
4602                 gui->flush();
4603         }
4604 }
4605
4606 int BC_ListBox::draw_rectangle(int flash)
4607 {
4608         int x1 = MIN(rect_x1, rect_x2);
4609         int x2 = MAX(rect_x1, rect_x2);
4610         int y1 = MIN(rect_y1, rect_y2);
4611         int y2 = MAX(rect_y1, rect_y2);
4612
4613         if(x1 == x2 || y1 == y2) return 0;
4614
4615         gui->set_inverse();
4616         gui->set_color(WHITE);
4617         gui->draw_rectangle(x1, y1, x2 - x1, y2 - y1);
4618         gui->set_opaque();
4619
4620
4621         if(flash)
4622         {
4623                 gui->flash(1);
4624         }
4625         return 0;
4626 }
4627
4628 void BC_ListBox::dump(ArrayList<BC_ListBoxItem*> *data,
4629         int columns,
4630         int indent,
4631         int master_column)
4632 {
4633         if(!indent)
4634         {
4635                 printf("BC_ListBox::dump 1\n");
4636         }
4637
4638         for(int i = 0; i < data[master_column].total; i++)
4639         {
4640                 for(int k = 0; k < indent; k++)
4641                         printf(" ");
4642                 for(int j = 0; j < columns; j++)
4643                 {
4644                         BC_ListBoxItem *item = data[j].values[i];
4645                         printf("%d,%d,%d=%s ",
4646                                 item->get_text_x(),
4647                                 item->get_text_y(),
4648                                 item->autoplace_text,
4649                                 item->get_text());
4650                 }
4651                 printf("\n");
4652
4653                 if(data[master_column].values[i]->get_sublist())
4654                 {
4655                         dump(data[master_column].values[i]->get_sublist(),
4656                                 data[master_column].values[i]->get_columns(),
4657                                 indent + 4,
4658                                 master_column);
4659                 }
4660         }
4661
4662
4663 }
4664
4665
4666
4667