change thread join strategy, fix a few leaks, fix a few bugs
[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         reset_query();
436 //printf("BC_ListBox::BC_ListBox 5\n");
437 }
438
439 BC_ListBox::~BC_ListBox()
440 {
441         expanders.remove_all_objects();
442         if(bg_surface) delete bg_surface;
443         if(bg_pixmap) delete bg_pixmap;
444         if(xscrollbar) delete xscrollbar;
445         if(yscrollbar) delete yscrollbar;
446         for(int i = 0; i < 3; i++) delete column_bg[i];
447         for(int i = 0; i < 4; i++) delete button_images[i];
448         for(int i = 0; i < 5; i++) delete toggle_images[i];
449         if(column_sort_up) delete column_sort_up;
450         if(column_sort_dn) delete column_sort_dn;
451
452         delete_columns();
453         if(drag_popup) delete drag_popup;
454 }
455
456 int BC_ListBox::enable()
457 {
458         disabled = 0;
459         draw_button(1);
460         return 1;
461 }
462
463 int BC_ListBox::disable()
464 {
465         disabled = 1;
466         draw_button(1);
467         return 1;
468 }
469
470 void BC_ListBox::reset_query()
471 {
472         query[0] = 0;  // reset query
473 }
474
475 int BC_ListBox::evaluate_query(char *string)
476 {
477         for(int i = 0; i < data[search_column].size(); i++)
478         {
479                 if(strcmp(string, data[search_column].get(i)->text) <= 0 &&
480                         data[search_column].get(i)->searchable)
481                 {
482                         return i;
483                 }
484         }
485
486         return -1;
487 }
488
489 int BC_ListBox::query_list()
490 {
491         if(query[0] == 0) return 0;
492
493         int done = 0;
494         int result;
495         int selection_changed = 0;
496         int prev_selection = -1;
497         result = evaluate_query(query);
498         if(result >= 0) done = 1;
499
500         if(done)
501         {
502 // Deselect all
503                 for(int i = 0; i < data[0].total; i++)
504                 {
505                         for(int j = 0; j < columns; j++)
506                         {
507                                 if(data[j].values[i]->selected) prev_selection = i;
508                                 data[j].values[i]->selected = 0;
509                         }
510                 }
511
512 // Select one
513                 if(prev_selection != result)
514                         selection_changed = 1;
515                 for(int j = 0; j < columns; j++)
516                 {
517                         data[j].values[result]->selected = 1;
518                 }
519                 center_selection(result);
520         }
521
522         return selection_changed;
523 }
524
525 void BC_ListBox::init_column_width()
526 {
527         if(!column_width && data)
528         {
529                 int widest = 5, wd;
530                 for(int i = 0; i < data[0].total; i++)
531                 {
532                         wd = get_text_w(data[0].values[i]);
533                         if( wd > widest ) widest = wd;
534                 }
535                 default_column_width[0] = widest + 2 * LISTBOX_MARGIN;
536         }
537 }
538
539 int BC_ListBox::initialize()
540 {
541         if(is_popup)
542         {
543                 if(use_button)
544                 {
545                         for( int i = 0; i < 4; ++i )
546                         {
547                                 button_images[i] = new BC_Pixmap(parent_window,
548                                         BC_WindowBase::get_resources()->listbox_button[i],
549                                         PIXMAP_ALPHA);
550                         }
551                         w = button_images[0]->get_w();
552                         h = button_images[0]->get_h();
553                 }
554
555                 gui = 0;
556                 current_operation = NO_OPERATION;
557
558         }
559         else
560         {
561                 gui = this;
562                 current_operation = NO_OPERATION;
563         }
564
565         for(int i = 0; i < 3; i++)
566         {
567                 column_bg[i] = new BC_Pixmap(parent_window,
568                         get_resources()->listbox_column[i],
569                         PIXMAP_ALPHA);
570         }
571         for(int i = 0; i < 5; i++)
572         {
573                 toggle_images[i] = new BC_Pixmap(parent_window,
574                         get_resources()->listbox_expand[i],
575                         PIXMAP_ALPHA);
576         }
577
578         column_sort_up = new BC_Pixmap(parent_window,
579                 BC_WindowBase::get_resources()->listbox_up,
580                 PIXMAP_ALPHA);
581         column_sort_dn = new BC_Pixmap(parent_window,
582                 BC_WindowBase::get_resources()->listbox_dn,
583                 PIXMAP_ALPHA);
584
585 //printf("BC_ListBox::initialize 10\n");
586         drag_icon_vframe = get_resources()->type_to_icon[ICON_UNKNOWN];
587         drag_column_icon_vframe = get_resources()->type_to_icon[ICON_COLUMN];
588 // = new BC_Pixmap(parent_window,
589 //              get_resources()->type_to_icon[ICON_UNKNOWN],
590 //              PIXMAP_ALPHA);
591 //      drag_column_icon = new BC_Pixmap(parent_window,
592 //              get_resources()->type_to_icon[ICON_COLUMN],
593 //              PIXMAP_ALPHA);
594         BC_SubWindow::initialize();
595
596         init_column_width();
597
598         if(top_level->get_resources()->listbox_bg)
599                 bg_pixmap = new BC_Pixmap(this,
600                         get_resources()->listbox_bg,
601                         PIXMAP_OPAQUE);
602
603         draw_button(0);
604         draw_items(0);
605
606         if(!use_button && is_popup)
607         {
608                 hide_window(1);
609         }
610
611
612         return 0;
613 }
614
615 void BC_ListBox::deactivate_selection()
616 {
617         current_operation = NO_OPERATION;
618 }
619
620 int BC_ListBox::draw_button(int flush)
621 {
622 // Draw the button for a popup listbox
623         if(use_button && is_popup)
624         {
625                 int image_number = 0;
626
627                 draw_top_background(parent_window, 0, 0, w, h);
628
629                 if(button_highlighted)
630                         image_number = 1;
631                 if(current_operation == BUTTON_DN)
632                         image_number = 2;
633                 if(disabled)
634                         image_number = 3;
635
636
637                 pixmap->draw_pixmap(button_images[image_number],
638                         0,
639                         0,
640                         w,
641                         h,
642                         0,
643                         0);
644                 flash(flush);
645         }
646         return 0;
647 }
648
649 int BC_ListBox::calculate_item_coords()
650 {
651         if(!data) return 0;
652
653         int icon_x = 0;
654         int next_icon_x = 0;
655         int next_icon_y = 0;
656         int next_text_y = 0;
657 // Change the display_format to get the right item dimensions for both
658 // text and icons.
659         temp_display_format = display_format;
660
661
662 // Scan the first column for lowest y coord of all text
663 // and lowest right x and y coord for all icons which aren't auto placable
664         calculate_last_coords_recursive(data,
665                 &icon_x,
666                 &next_icon_x,
667                 &next_icon_y,
668                 &next_text_y,
669                 1);
670
671 // Reset last column width.  It's recalculated based on text width.
672
673         calculate_item_coords_recursive(data,
674                 &icon_x,
675                 &next_icon_x,
676                 &next_icon_y,
677                 &next_text_y,
678                 1);
679
680
681
682         display_format = temp_display_format;
683
684         return 0;
685 }
686
687 void BC_ListBox::calculate_last_coords_recursive(
688         ArrayList<BC_ListBoxItem*> *data,
689         int *icon_x,
690         int *next_icon_x,
691         int *next_icon_y,
692         int *next_text_y,
693         int top_level)
694 {
695         for(int i = 0; i < data[0].size(); i++)
696         {
697                 int current_text_y = 0;
698                 int current_icon_x = 0;
699                 int current_icon_y = 0;
700                 BC_ListBoxItem *item = data[0].get(i);
701
702 // Get next_text_y
703                 if(!item->autoplace_text)
704                 {
705 // Lowest text coordinate
706                         display_format = LISTBOX_TEXT;
707                         current_text_y = item->text_y + get_text_h(item);
708                         if(current_text_y > *next_text_y)
709                                 *next_text_y = current_text_y;
710
711 // Add sublist depth if it is expanded
712                         if(item->get_sublist() &&
713                                 item->get_columns() &&
714                                 item->get_expand())
715                         {
716                                 calculate_last_coords_recursive(item->get_sublist(),
717                                         icon_x,
718                                         next_icon_x,
719                                         next_icon_y,
720                                         next_text_y,
721                                         0);
722                         }
723                 }
724
725 // Get next_icon coordinate
726                 if(top_level)
727                 {
728                         BC_ListBoxItem *item = data[master_column].get(i);
729                         if(!item->autoplace_icon)
730                         {
731                                 display_format = LISTBOX_ICONS;
732 // Lowest right icon coordinate.
733                                 current_icon_x = item->icon_x;
734                                 if(current_icon_x > *icon_x) *icon_x = current_icon_x;
735                                 if(current_icon_x + get_item_w(item) > *next_icon_x)
736                                         *next_icon_x = current_icon_x + get_item_w(item);
737
738                                 current_icon_y = item->icon_y + get_item_h(item);
739                                 if(current_icon_y > *next_icon_y)
740                                         *next_icon_y = current_icon_y;
741                         }
742                 }
743         }
744 }
745
746
747 void BC_ListBox::calculate_item_coords_recursive(
748         ArrayList<BC_ListBoxItem*> *data,
749         int *icon_x,
750         int *next_icon_x,
751         int *next_icon_y,
752         int *next_text_y,
753         int top_level)
754 {
755 // get maximum height of an icon
756         row_height = get_text_height(MEDIUMFONT);
757         if(temp_display_format == LISTBOX_ICON_LIST)
758         {
759                 for(int i = 0; i < data[0].size(); i++)
760                 {
761                         if(data[0].get(i)->icon)
762                         {
763                                 if(data[0].get(i)->icon->get_h() > row_height)
764                                         row_height = data[0].get(i)->icon->get_h();
765                         }
766                 }
767         }
768
769
770 // Set up items which need autoplacement.
771 // Should fill icons down and then across
772         for(int i = 0; i < data[0].size(); i++)
773         {
774 // Don't increase y unless the row requires autoplacing.
775                 int total_autoplaced_columns = 0;
776
777 // Set up icons in first column
778                 if(top_level)
779                 {
780                         BC_ListBoxItem *item = data[master_column].get(i);
781                         if(item->autoplace_icon)
782                         {
783 // 1 column only if icons are used
784                                 display_format = LISTBOX_ICONS;
785 // Test row height
786 // Start new column.
787                                 if(*next_icon_y + get_item_h(item) >= get_h() &&
788                                         *next_icon_y > 0)
789                                 {
790                                         *icon_x = *next_icon_x;
791                                         *next_icon_y = 0;
792                                 }
793
794                                 if(*icon_x + get_item_w(item) > *next_icon_x)
795                                         *next_icon_x = *icon_x + get_item_w(item);
796
797
798                                 item->set_icon_x(*icon_x);
799                                 item->set_icon_y(*next_icon_y);
800
801                                 *next_icon_y += get_item_h(item);
802                         }
803                 }
804
805
806
807 // Set up a text row
808                 int next_text_x = 0;
809                 row_ascent = row_descent = 0;
810 // row_height still holds icon max height
811                 for(int j = 0; j < columns; j++)
812                 {
813                         BC_ListBoxItem *item = data[j].get(i);
814                         if(item->autoplace_text)
815                         {
816                                 display_format = LISTBOX_TEXT;
817                                 item->set_text_x(next_text_x);
818                                 item->set_text_y(*next_text_y);
819                                 int ht = get_text_h(item);
820                                 if( ht > row_height ) row_height = ht;
821                                 int bl = get_baseline(item);
822                                 if( bl > row_ascent ) row_ascent = bl;
823                                 int dt = ht - bl;
824                                 if( dt > row_descent ) row_ascent = bl;
825
826 // printf("BC_ListBox::calculate_item_coords_recursive %p %d %d %d %d %s \n",
827 // item->get_sublist(),
828 // item->get_columns(),
829 // item->get_expand(),
830 // next_text_x,
831 // *next_text_y,
832 // item->get_text());
833 // Increment position of next column
834                                 if(j < columns - 1)
835                                 {
836                                         next_text_x += (column_width ?
837                                                 column_width[j] :
838                                                 default_column_width[j]);
839                                 }
840                                 else
841 // Set last column width based on text width
842                                 {
843                                         int new_w = get_item_w(item);
844
845                                         int *previous_w = (column_width ?
846                                                 &column_width[j] :
847                                                 &default_column_width[j]);
848                                         if(new_w > *previous_w)
849                                                 *previous_w = new_w;
850 //printf("BC_ListBox::calculate_item_coords_recursive 1 %d\n", new_w);
851                                 }
852                                 total_autoplaced_columns++;
853                         }
854                 }
855
856 // Increase the text vertical position
857                 if(total_autoplaced_columns)
858                 {
859                         display_format = LISTBOX_TEXT;
860                         *next_text_y += row_height;
861                 }
862
863 // Set up a sublist
864                 BC_ListBoxItem *item = data[master_column].values[i];
865                 if(item->get_sublist() &&
866                         item->get_columns() &&
867                         item->get_expand())
868                 {
869                         calculate_item_coords_recursive(
870                                 item->get_sublist(),
871                                 icon_x,
872                                 next_icon_x,
873                                 next_icon_y,
874                                 next_text_y,
875                                 0);
876                 }
877         }
878 }
879
880 void BC_ListBox::set_is_suggestions(int value)
881 {
882         this->is_suggestions = value;
883 }
884
885 void BC_ListBox::set_use_button(int value)
886 {
887         this->use_button = value;
888 }
889
890 void BC_ListBox::set_justify(int value)
891 {
892         this->justify = value;
893 }
894
895 void BC_ListBox::set_allow_drag_column(int value)
896 {
897         this->allow_drag_column = value;
898 }
899
900 void BC_ListBox::set_process_drag(int value)
901 {
902         this->process_drag = value;
903 }
904
905 void BC_ListBox::set_master_column(int value, int redraw)
906 {
907         this->master_column = value;
908         if(redraw)
909         {
910                 draw_items(1);
911         }
912 }
913
914 void BC_ListBox::set_search_column(int value)
915 {
916         this->search_column = value;
917 }
918
919 int BC_ListBox::get_sort_column()
920 {
921         return sort_column;
922 }
923
924 void BC_ListBox::set_sort_column(int value, int redraw)
925 {
926         sort_column = value;
927         if(redraw)
928         {
929                 draw_titles(1);
930         }
931 }
932
933 int BC_ListBox::get_sort_order()
934 {
935         return sort_order;
936 }
937
938 void BC_ListBox::set_sort_order(int value, int redraw)
939 {
940         sort_order = value;
941         if(redraw)
942         {
943                 draw_titles(1);
944         }
945 }
946
947
948
949
950
951 int BC_ListBox::get_display_mode()
952 {
953         return display_format;
954 }
955
956 int BC_ListBox::get_yposition()
957 {
958         return yposition;
959 }
960
961 int BC_ListBox::get_xposition()
962 {
963         return xposition;
964 }
965
966 int BC_ListBox::get_highlighted_item()
967 {
968         return highlighted_item;
969 }
970
971
972 int BC_ListBox::get_item_x(BC_ListBoxItem *item)
973 {
974         if(display_format == LISTBOX_TEXT)
975         {
976                 return item->text_x - xposition + 2;
977         }
978         else
979         if(display_format == LISTBOX_ICON_LIST)
980         {
981                 return item->text_x - xposition + 2;
982         }
983         else
984         {
985                 return item->icon_x - xposition + 2;
986         }
987 }
988
989 int BC_ListBox::get_item_y(BC_ListBoxItem *item)
990 {
991         int result;
992         if(display_format == LISTBOX_TEXT)
993         {
994                 result = item->text_y - yposition + title_h + 2;
995         }
996         else
997         if(display_format == LISTBOX_ICON_LIST)
998         {
999                 result = item->text_y - yposition + title_h + 2;
1000         }
1001         else
1002         {
1003                 result = item->icon_y - yposition + title_h + 2;
1004         }
1005
1006         return result;
1007 }
1008
1009 int BC_ListBox::get_item_w(BC_ListBoxItem *item)
1010 {
1011         if(display_format == LISTBOX_ICONS)
1012         {
1013                 int x, y, w, h;
1014                 get_icon_mask(item, x, y, w, h);
1015                 int icon_w = w;
1016                 get_text_mask(item, x, y, w, h);
1017                 int text_w = w;
1018
1019                 return icon_position == ICON_LEFT ? icon_w + text_w :
1020                         icon_w > text_w ? icon_w : text_w;
1021         }
1022         return get_text_w(item) + 2 * LISTBOX_MARGIN;
1023 }
1024
1025 int BC_ListBox::get_item_h(BC_ListBoxItem *item)
1026 {
1027         if(display_format == LISTBOX_ICONS)
1028         {
1029                 int x, y, w, h;
1030                 get_icon_mask(item, x, y, w, h);
1031                 int icon_h = h;
1032                 get_text_mask(item, x, y, w, h);
1033                 int text_h = h;
1034
1035                 return icon_position != ICON_LEFT ? icon_h + text_h :
1036                         icon_h > text_h ? icon_h : text_h;
1037         }
1038         return get_text_h(item);
1039 }
1040
1041
1042 int BC_ListBox::get_icon_w(BC_ListBoxItem *item)
1043 {
1044         return item->get_icon_w();
1045 }
1046
1047 int BC_ListBox::get_icon_h(BC_ListBoxItem *item)
1048 {
1049         return item->get_icon_h();
1050 }
1051
1052 int BC_ListBox::get_text_w(BC_ListBoxItem *item)
1053 {
1054         int w = item->get_text_w();
1055         if( w < 0 ) item->set_text_w(w = get_text_width(MEDIUMFONT, item->get_text()));
1056         return w;
1057 }
1058
1059 int BC_ListBox::get_text_h(BC_ListBoxItem *item)
1060 {
1061         int h = item->get_text_h();
1062         if( h < 0 ) item->set_text_h(h = get_text_height(MEDIUMFONT));
1063         return h;
1064 }
1065
1066 int BC_ListBox::get_baseline(BC_ListBoxItem *item)
1067 {
1068         int b = item->get_baseline();
1069         if( b < 0 ) item->set_baseline(b = get_text_ascent(MEDIUMFONT));
1070         return b;
1071 }
1072
1073 int BC_ListBox::get_items_width()
1074 {
1075         int widest = 0;
1076
1077         if(display_format == LISTBOX_ICONS)
1078         {
1079                 for(int i = 0; i < columns; i++)
1080                 {
1081                         for(int j = 0; j < data[i].total; j++)
1082                         {
1083                                 int x1, x, y, w, h;
1084                                 BC_ListBoxItem *item = data[i].values[j];
1085                                 x1 = item->icon_x;
1086
1087                                 get_icon_mask(item, x, y, w, h);
1088                                 if(x1 + w > widest) widest = x1 + w;
1089
1090                                 if(display_format == LISTBOX_ICONS && icon_position == ICON_LEFT)
1091                                         x1 += w;
1092
1093                                 get_text_mask(item, x, y, w, h);
1094                                 if(x1 + w > widest) widest = x1 + w;
1095                         }
1096                 }
1097         }
1098         else
1099         if(display_format == LISTBOX_TEXT)
1100         {
1101                 return get_column_offset(columns);
1102         }
1103         else
1104         {
1105                 return get_column_offset(columns);
1106         }
1107         return widest;
1108 }
1109
1110 int BC_ListBox::get_items_height(ArrayList<BC_ListBoxItem*> *data,
1111         int columns,
1112         int *result)
1113 {
1114         int temp = 0;
1115         int top_level = 0;
1116         int highest = 0;
1117         if(!result)
1118         {
1119                 result = &temp;
1120                 top_level = 1;
1121         }
1122
1123
1124
1125
1126
1127         for(int j = 0; j < (data ? data[master_column].total : 0); j++)
1128         {
1129                 int x, y, w, h;
1130                 BC_ListBoxItem *item = data[master_column].values[j];
1131
1132                 if(display_format == LISTBOX_ICONS)
1133                 {
1134                         get_icon_mask(item, x, y, w, h);
1135                         if(y + h + yposition > highest) highest = y + h + yposition;
1136
1137                         get_text_mask(item, x, y, w, h);
1138                         if(y + h + yposition > highest) highest = y + h + yposition;
1139                 }
1140                 else
1141                 {
1142                         get_text_mask(item, x, y, w, h);
1143                         *result += h;
1144
1145
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 ||
1158                 display_format == LISTBOX_ICON_LIST) &&
1159                 top_level)
1160         {
1161                 highest = LISTBOX_MARGIN + *result;
1162         }
1163
1164
1165         return highest;
1166 }
1167
1168 int BC_ListBox::set_yposition(int position, int draw_items)
1169 {
1170         this->yposition = position;
1171         if(draw_items)
1172         {
1173                 this->draw_items(1);
1174         }
1175         return 0;
1176 }
1177
1178 int BC_ListBox::set_xposition(int position)
1179 {
1180         this->xposition = position;
1181         draw_items(1);
1182         return 0;
1183 }
1184
1185 void BC_ListBox::expand_item(BC_ListBoxItem *item, int expand)
1186 {
1187         if(item)
1188         {
1189                 item->expand = expand;
1190 // Collapse sublists if this is collapsed to make it easier to calculate
1191 // coordinates
1192                 if(item->get_sublist())
1193                         collapse_recursive(item->get_sublist(), master_column);
1194
1195
1196 // Set everything for autoplacement
1197
1198                 set_autoplacement(data, 0, 1);
1199
1200                 draw_items(1);
1201         }
1202 }
1203
1204 void BC_ListBox::collapse_recursive(ArrayList<BC_ListBoxItem*> *data,
1205                 int master_column)
1206 {
1207         for(int i = 0; i < data[master_column].total; i++)
1208         {
1209                 BC_ListBoxItem *item = data[master_column].values[i];
1210                 if(item->get_sublist() && item->expand)
1211                 {
1212                         item->expand = 0;
1213                         collapse_recursive(item->get_sublist(), master_column);
1214                 }
1215         }
1216 }
1217
1218 void BC_ListBox::set_autoplacement(ArrayList<BC_ListBoxItem*> *data,
1219         int do_icons,
1220         int do_text)
1221 {
1222         for(int i = 0; i < data[0].total; i++)
1223         {
1224                 for(int j = 0; j < columns; j++)
1225                 {
1226                         if(do_icons) data[j].values[i]->autoplace_icon = 1;
1227                         if(do_text) data[j].values[i]->autoplace_text = 1;
1228                 }
1229
1230                 BC_ListBoxItem *item = data[master_column].values[i];
1231                 if(item->get_sublist())
1232                 {
1233                         set_autoplacement(item->get_sublist(), do_icons, do_text);
1234                 }
1235         }
1236 }
1237
1238
1239
1240 int BC_ListBox::get_yscroll_x()
1241 {
1242         if(is_popup)
1243                 return popup_w - get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w();
1244         else
1245                 return get_x() +
1246                         popup_w -
1247                         get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w();
1248 }
1249
1250 int BC_ListBox::get_yscroll_y()
1251 {
1252         if(is_popup)
1253                 return 0;
1254         else
1255                 return get_y();
1256 }
1257
1258 int BC_ListBox::get_yscroll_height()
1259 {
1260         return popup_h - (need_xscroll ?
1261                 get_resources()->hscroll_data[SCROLL_HANDLE_UP]->get_h() :
1262                 0);
1263 }
1264
1265 int BC_ListBox::get_xscroll_x()
1266 {
1267         if(is_popup)
1268                 return 0;
1269         else
1270                 return get_x();
1271 }
1272
1273 int BC_ListBox::get_xscroll_y()
1274 {
1275         if(is_popup)
1276                 return popup_h -
1277                         get_resources()->hscroll_data[SCROLL_HANDLE_UP]->get_h();
1278         else
1279                 return get_y() +
1280                         popup_h -
1281                         get_resources()->hscroll_data[SCROLL_HANDLE_UP]->get_h();
1282 }
1283
1284 int BC_ListBox::get_xscroll_width()
1285 {
1286         return popup_w - (need_yscroll ?
1287                 get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w() :
1288                 0);
1289 }
1290
1291 int BC_ListBox::get_column_offset(int column)
1292 {
1293         int x = 0;
1294         while(column > 0)
1295         {
1296                 x += column_width ?
1297                         column_width[--column] :
1298                         default_column_width[--column];
1299         }
1300         return x;
1301 }
1302
1303 void BC_ListBox::column_width_boundaries()
1304 {
1305         if(column_width) {
1306                 for(int i = 0; i < columns; i++) {
1307                         if(column_width[i] < MIN_COLUMN_WIDTH)
1308                                  column_width[i] = MIN_COLUMN_WIDTH;
1309                 }
1310         }
1311         else {
1312                 for(int i = 0; i < columns; i++) {
1313                         if(default_column_width[i] < MIN_COLUMN_WIDTH)
1314                                  default_column_width[i] = MIN_COLUMN_WIDTH;
1315                 }
1316         }
1317 }
1318
1319 int BC_ListBox::get_column_width(int column, int clamp_right)
1320 {
1321         if(column < columns - 1 || !clamp_right)
1322                 return column_width ?  column_width[column] : default_column_width[column];
1323         return popup_w + xposition - get_column_offset(column);
1324 }
1325
1326 int BC_ListBox::get_icon_mask(BC_ListBoxItem *item,
1327         int &x, int &y, int &w, int &h)
1328 {
1329         if( display_format == LISTBOX_ICONS ) {
1330                 x = get_item_x(item);
1331                 y = get_item_y(item);
1332                 w = get_icon_w(item) + ICON_MARGIN * 2;
1333                 h = get_icon_h(item) + ICON_MARGIN * 2;
1334         }
1335         else
1336                 x = y = w = h = 0;
1337         return 0;
1338 }
1339
1340 int BC_ListBox::get_text_mask(BC_ListBoxItem *item,
1341         int &x, int &y, int &w, int &h)
1342 {
1343         x = get_item_x(item);
1344         y = get_item_y(item);
1345
1346         if(display_format == LISTBOX_ICONS) {
1347                 if(icon_position == ICON_LEFT) {
1348                         x += get_icon_w(item) + ICON_MARGIN * 2;
1349                         y += get_icon_h(item) - get_text_h(item);
1350                 }
1351                 else {
1352                         y += get_icon_h(item) + ICON_MARGIN;
1353                 }
1354
1355                 w = get_text_w(item) + ICON_MARGIN * 2;
1356                 h = get_text_h(item) + ICON_MARGIN * 2;
1357         }
1358         else
1359         if(display_format == LISTBOX_TEXT) {
1360                 w = get_text_w(item) + LISTBOX_MARGIN * 2;
1361                 h = get_text_h(item);
1362         }
1363         else
1364         {
1365                 w = get_text_width(MEDIUMFONT, item->text) + LISTBOX_MARGIN * 2;
1366                 h = row_height;
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
2423 // Text is treed
2424         {
2425 // Cursor is inside items rectangle
2426                 if(cursor_x >= 0 &&
2427                         cursor_x < (yscrollbar ?
2428                                 gui->get_w() - get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w() :
2429                                 gui->get_w()) &&
2430 // Only clamp y if we're not in a SELECT operation.
2431                         (current_operation == BC_ListBox::SELECT ||
2432                                 (cursor_y > get_title_h() + LISTBOX_BORDER &&
2433                                 cursor_y < gui->get_h())))
2434                 {
2435 // Search table for cursor obstruction
2436                         for(int i = 0; i < data[master_column].total; i++)
2437                         {
2438                                 BC_ListBoxItem *item = data[master_column].values[i];
2439                                 (*counter)++;
2440
2441 // Cursor is inside item on current level
2442                                 if(expanded &&
2443                                         item->selectable &&
2444                                         cursor_y >= get_item_y(item) &&
2445                                         cursor_y < get_item_y(item) + get_item_h(item))
2446                                 {
2447 //printf("BC_ListBox::get_cursor_item %d %d %p\n", master_column, i, item);
2448                                         if(item_return) (*item_return) = item;
2449                                         return (*counter);
2450                                 }
2451
2452 // Descend into sublist
2453                                 if(item->get_sublist())
2454                                 {
2455                                         if(get_cursor_item(item->get_sublist(),
2456                                                 cursor_x,
2457                                                 cursor_y,
2458                                                 item_return,
2459                                                 counter,
2460                                                 item->get_expand()) >= 0)
2461                                                 return (*counter);
2462                                 }
2463                         }
2464                 }
2465         }
2466         return -1;
2467 }
2468
2469 int BC_ListBox::repeat_event(int64_t duration)
2470 {
2471         switch(current_operation)
2472         {
2473 // Repeat out of bounds selection
2474                 case SELECT_RECT:
2475                         if(duration == get_resources()->scroll_repeat)
2476                                 return rectangle_scroll_event();
2477                         break;
2478
2479                 case SELECT:
2480                         if(duration == get_resources()->scroll_repeat)
2481                                 return select_scroll_event();
2482                         break;
2483
2484                 case NO_OPERATION:
2485 // Show tooltip
2486                         if(button_highlighted && is_popup && !tooltip_done &&
2487                                 tooltip_text && tooltip_text[0] != 0 &&
2488                                 duration == get_resources()->tooltip_delay)
2489                         {
2490                                 show_tooltip();
2491                                 tooltip_done = 1;
2492                                 return 1;
2493                         }
2494                         break;
2495         }
2496         return 0;
2497 }
2498
2499
2500 int BC_ListBox::cursor_enter_event()
2501 {
2502         int result = 0;
2503
2504         switch(current_operation)
2505         {
2506 // Cursor moved over button, pressed, and exited.
2507                 case BUTTON_DOWN_SELECT:
2508                         if(top_level->event_win == win)
2509                         {
2510                                 current_operation = BUTTON_DN;
2511                                 result = 1;
2512                                 button_highlighted = 1;
2513                                 draw_button(1);
2514                         }
2515                         break;
2516
2517                 case NO_OPERATION:
2518 // Cursor entered button
2519                         if(is_popup && top_level->event_win == win)
2520                         {
2521                                 button_highlighted = 1;
2522                                 result = 1;
2523                                 draw_button(1);
2524                         }
2525                         else
2526 // TODO: Need to get the highlighted column title or item
2527                         if(gui && top_level->event_win == gui->win)
2528                         {
2529                                 list_highlighted = 1;
2530                                 draw_border(1);
2531                                 result = 1;
2532                         }
2533                         break;
2534         }
2535
2536         return result;
2537 }
2538
2539 int BC_ListBox::cursor_leave_event()
2540 {
2541         if(current_operation == COLUMN_DRAG) return 0;
2542
2543 // Left button area
2544         if(button_highlighted)
2545         {
2546                 button_highlighted = 0;
2547                 hide_tooltip();
2548                 draw_button(1);
2549         }
2550
2551         if(list_highlighted)
2552         {
2553                 list_highlighted = 0;
2554                 highlighted_item = -1;
2555                 highlighted_ptr = 0;
2556                 highlighted_title = -1;
2557                 int redraw_toggles = 0;
2558                 for(int i = 0; i < expanders.total; i++)
2559                         expanders.values[i]->cursor_leave_event(&redraw_toggles);
2560
2561                 draw_items(1);
2562         }
2563
2564         return 0;
2565 }
2566
2567 int BC_ListBox::get_first_selection(ArrayList<BC_ListBoxItem*> *data, int *result)
2568 {
2569         int temp = -1;
2570         if(!result) result = &temp;
2571
2572         for(int i = 0; i < data[master_column].total; i++)
2573         {
2574                 BC_ListBoxItem *item = data[master_column].values[i];
2575                 (*result)++;
2576                 if(item->selected) return (*result);
2577                 if(item->get_sublist())
2578                 {
2579                         if(get_first_selection(item->get_sublist(), result) >= 0)
2580                                 return (*result);
2581                 }
2582         }
2583         return -1;
2584 }
2585
2586 int BC_ListBox::get_total_items(ArrayList<BC_ListBoxItem*> *data,
2587         int *result,
2588         int master_column)
2589 {
2590         int temp = 0;
2591         if(!result) result = &temp;
2592
2593         for(int i = 0; i < data[master_column].total; i++)
2594         {
2595                 (*result)++;
2596                 if(data[master_column].values[i]->get_sublist())
2597                         get_total_items(data[master_column].values[i]->get_sublist(),
2598                                 result,
2599                                 master_column);
2600         }
2601
2602         return (*result);
2603 }
2604
2605
2606 int BC_ListBox::get_last_selection(ArrayList<BC_ListBoxItem*> *data,
2607         int *result)
2608 {
2609         int temp = -1;
2610         int top_level = 0;
2611         if(!result)
2612         {
2613                 result = &temp;
2614                 top_level = 1;
2615         }
2616
2617         for(int i = data[master_column].total - 1; i >= 0; i--)
2618         {
2619                 BC_ListBoxItem *item = data[master_column].values[i];
2620                 (*result)++;
2621                 if(item->selected)
2622                 {
2623                         if(top_level)
2624                                 return get_total_items(data, 0, master_column) - (*result) /* - 1 */;
2625                         else
2626                                 return (*result);
2627                 }
2628
2629                 if(item->get_sublist())
2630                 {
2631                         if(get_last_selection(item->get_sublist(), result) >= 0)
2632                         {
2633                                 if(top_level)
2634                                         return get_total_items(data, 0, master_column) - (*result) /* - 1 */;
2635                                 else
2636                                         return (*result);
2637                         }
2638                 }
2639         }
2640         return -1;
2641 }
2642
2643 void BC_ListBox::select_range(ArrayList<BC_ListBoxItem*> *data,
2644                 int start,
2645                 int end,
2646                 int *current)
2647 {
2648         int temp = -1;
2649         if(!current) current = &temp;
2650
2651         for(int i = 0; i < data[master_column].total; i++)
2652         {
2653                 (*current)++;
2654                 if((*current) >= start && (*current) < end)
2655                 {
2656                         for(int j = 0; j < columns; j++)
2657                                 data[j].values[i]->selected = 1;
2658                 }
2659                 BC_ListBoxItem *item = data[master_column].values[i];
2660                 if(item->get_sublist())
2661                         select_range(item->get_sublist(),
2662                                 start,
2663                                 end,
2664                                 current);
2665         }
2666 }
2667
2668
2669 // Fill items between current selection and new selection
2670 int BC_ListBox::expand_selection(int button_press, int selection_number)
2671 {
2672         int old_selection_start = selection_start;
2673         int old_selection_end = selection_end;
2674
2675 // printf("BC_ListBox::expand_selection %d %d\n",
2676 // selection_center,
2677 // selection_number);
2678
2679 // Calculate the range to select based on selection_center and selection_number
2680         if(selection_number < selection_center)
2681         {
2682                 selection_start = selection_number;
2683         }
2684         else
2685         {
2686                 selection_end = selection_number + 1;
2687         }
2688
2689 //printf("BC_ListBox::expand_selection %d %d %d %d\n", old_selection_start, old_selection_end, selection_start, selection_end);
2690 // Recurse through all the items and select the desired range
2691         select_range(data, selection_start, selection_end);
2692
2693 // Trigger redraw
2694         return (old_selection_start != selection_start ||
2695                 old_selection_end != selection_end);
2696 }
2697
2698 int BC_ListBox::toggle_item_selection(ArrayList<BC_ListBoxItem*> *data,
2699         int selection_number,
2700         int *counter)
2701 {
2702         int temp = -1;
2703         if(!counter) counter = &temp;
2704
2705         for(int i = 0; i < data[master_column].total; i++)
2706         {
2707                 BC_ListBoxItem *item = data[master_column].values[i];
2708                 (*counter)++;
2709                 if((*counter) == selection_number)
2710                 {
2711 // Get new value for selection
2712                         int selected = !item->selected;
2713 // Set row
2714                         for(int j = 0; j < columns; j++)
2715                                 data[j].values[i]->selected = selected;
2716                         return 1;
2717                 }
2718
2719 // Descend into sublist
2720                 if(item->get_sublist())
2721                 {
2722                         if(toggle_item_selection(item->get_sublist(),
2723                                 selection_number,
2724                                 counter))
2725                                 return 1;
2726                 }
2727         }
2728
2729         return 0;
2730 }
2731
2732
2733 void BC_ListBox::set_all_selected(ArrayList<BC_ListBoxItem*> *data, int value)
2734 {
2735         for(int i = 0; i < data[master_column].total; i++)
2736         {
2737                 for(int j = 0; j < columns; j++)
2738                 {
2739                         BC_ListBoxItem *item = data[j].values[i];
2740                         item->selected = value;
2741                 }
2742                 BC_ListBoxItem *item = data[master_column].values[i];
2743                 if(item->get_sublist())
2744                 {
2745                         set_all_selected(item->get_sublist(), value);
2746                 }
2747         }
2748 }
2749
2750 void BC_ListBox::set_selected(ArrayList<BC_ListBoxItem*> *data,
2751                 int item_number,
2752                 int value,
2753                 int *counter)
2754 {
2755         int temp = -1;
2756         if(!counter) counter = &temp;
2757         for(int i = 0; i < data[master_column].total && (*counter) != item_number; i++)
2758         {
2759                 (*counter)++;
2760                 if((*counter) == item_number)
2761                 {
2762                         for(int j = 0; j < columns; j++)
2763                         {
2764                                 BC_ListBoxItem *item = data[j].values[i];
2765                                 item->selected = value;
2766                         }
2767                         return;
2768                 }
2769
2770                 BC_ListBoxItem *item = data[master_column].values[i];
2771                 if(item->get_sublist())
2772                 {
2773                         set_selected(item->get_sublist(),
2774                                 item_number,
2775                                 value,
2776                                 counter);
2777                 }
2778         }
2779 }
2780
2781 int BC_ListBox::update_selection(ArrayList<BC_ListBoxItem*> *data,
2782         int selection_number,
2783         int *counter)
2784 {
2785         int temp = -1;
2786         int result = 0;
2787         if(!counter) counter = &temp;
2788
2789         for(int i = 0; i < data[master_column].total; i++)
2790         {
2791                 BC_ListBoxItem *item = data[master_column].values[i];
2792                 (*counter)++;
2793                 if((*counter) == selection_number && !item->selected)
2794                 {
2795                         result = 1;
2796                         for(int j = 0; j < columns; j++)
2797                                 data[j].values[i]->selected = 1;
2798                 }
2799                 else
2800                 if((*counter) != selection_number && item->selected)
2801                 {
2802                         result = 1;
2803                         for(int j = 0; j < columns; j++)
2804                                 data[j].values[i]->selected = 0;
2805                 }
2806                 if(item->get_sublist())
2807                         result |= update_selection(item->get_sublist(),
2808                                 selection_number,
2809                                 counter);
2810         }
2811         return result;
2812 }
2813
2814 void BC_ListBox::promote_selections(ArrayList<BC_ListBoxItem*> *data,
2815         int old_value,
2816         int new_value)
2817 {
2818         for(int i = 0; i < data[master_column].total; i++)
2819         {
2820                 for(int j = 0; j < columns; j++)
2821                 {
2822                         BC_ListBoxItem *item = data[j].values[i];
2823                         if(item->selected == old_value) item->selected = new_value;
2824                 }
2825                 BC_ListBoxItem *item = data[master_column].values[i];
2826                 if(item->get_sublist())
2827                         promote_selections(item->get_sublist(), old_value, new_value);
2828         }
2829 }
2830
2831 int BC_ListBox::focus_out_event()
2832 {
2833         deactivate();
2834         return 0;
2835 }
2836
2837 int BC_ListBox::button_press_event()
2838 {
2839         int result = 0;
2840         BC_ListBoxItem *current_item = 0;
2841         int new_cursor;
2842         int do_selection_change = 0;
2843         const int debug = 0;
2844
2845         hide_tooltip();
2846         if(debug) printf("BC_ListBox::button_press_event %d this=%p event_win=%p %p %p %p\n",
2847                 __LINE__,
2848                 this,
2849                 (void*)top_level->event_win,
2850                 (void*)(gui ? gui->win : 0),
2851                 (void*)win,
2852                 (void*)parent_window->win);
2853
2854 // Pressed in button
2855         if(is_popup && top_level->event_win == win)
2856         {
2857                 if(debug) printf("BC_ListBox::button_press_event %d\n", __LINE__);
2858                 current_operation = BUTTON_DN;
2859                 draw_button(1);
2860
2861 // Deploy listbox
2862                 if(!active && !disabled)
2863                 {
2864                         top_level->deactivate();
2865                         activate();
2866                 }
2867
2868                 result = 1;
2869         }
2870         else
2871 // Pressed in scrollbar
2872         if((xscrollbar && top_level->event_win == xscrollbar->win) ||
2873                 (yscrollbar && top_level->event_win == yscrollbar->win))
2874         {
2875                 if(debug) printf("BC_ListBox::button_press_event %d\n", __LINE__);
2876                 result = 0;
2877         }
2878         else
2879 // Pressed in items
2880         if(gui && top_level->event_win == gui->win)
2881         {
2882                 if(debug) printf("BC_ListBox::button_press_event %d\n", __LINE__);
2883
2884 // Activate list items
2885 // If it is a suggestion popup, it is visible without being active
2886                 if(!active)
2887                 {
2888                         if(debug) printf("BC_ListBox::button_press_event %d\n", __LINE__);
2889                         if(!is_suggestions) top_level->deactivate();
2890                         if(debug) printf("BC_ListBox::button_press_event %d\n", __LINE__);
2891                         activate();
2892                         if(debug) printf("BC_ListBox::button_press_event %d\n", __LINE__);
2893                 }
2894
2895 // Wheel mouse pressed
2896                 if(get_buttonpress() == 4)
2897                 {
2898                         if(current_operation == NO_OPERATION)
2899                         {
2900                                 current_operation = WHEEL;
2901                                 if(yscrollbar)
2902                                 {
2903                                         set_yposition(yposition - gui->get_h() / 10, 0);
2904                                         clamp_positions();
2905                                         update_scrollbars(0);
2906                                         highlighted_ptr = 0;
2907                                         highlighted_item = get_cursor_item(data,
2908                                                 top_level->cursor_x,
2909                                                 top_level->cursor_y,
2910                                                 &highlighted_ptr);
2911                                         draw_items(1);
2912                                         result = 1;
2913                                 }
2914                         }
2915                 }
2916                 else
2917                 if(get_buttonpress() == 5)
2918                 {
2919                         if(current_operation == NO_OPERATION)
2920                         {
2921                                 current_operation = WHEEL;
2922                                 if(yscrollbar)
2923                                 {
2924                                         set_yposition(yposition + gui->get_h() / 10, 0);
2925                                         clamp_positions();
2926                                         update_scrollbars(0);
2927                                         highlighted_ptr = 0;
2928                                         highlighted_item = get_cursor_item(data,
2929                                                 top_level->cursor_x,
2930                                                 top_level->cursor_y,
2931                                                 &highlighted_ptr);
2932                                         draw_items(1);
2933                                         result = 1;
2934                                 }
2935                         }
2936                 }
2937                 else
2938 // Pressed over column title division
2939                 if(test_column_divisions(gui->get_cursor_x(),
2940                         gui->get_cursor_y(),
2941                         new_cursor))
2942                 {
2943                         drag_cursor_x = gui->get_cursor_x() + xposition;
2944                         if(column_width)
2945                                 drag_column_w = column_width[highlighted_division - 1];
2946                         else
2947                                 drag_column_w = default_column_width[highlighted_division - 1];
2948
2949                         current_operation = DRAG_DIVISION;
2950                         reset_query();
2951                 }
2952                 else
2953 // Pressed in column title
2954                 if(test_column_titles(gui->get_cursor_x(), gui->get_cursor_y()))
2955                 {
2956                         current_operation = COLUMN_DN;
2957                         button_highlighted = 0;
2958                         list_highlighted = 1;
2959                         draw_items(1);
2960                         result = 1;
2961                 }
2962                 else
2963 // Pressed in expander
2964                 if(test_expanders())
2965                 {
2966                         current_operation = EXPAND_DN;
2967 // Need to redraw items because of alpha
2968                         draw_items(1);
2969                         result = 1;
2970                 }
2971                 else
2972 // Pressed over item
2973                 if((selection_number = get_cursor_item(data,
2974                                         gui->get_cursor_x(),
2975                                         gui->get_cursor_y(),
2976                                         &current_item)) >= 0)
2977                 {
2978                         if(debug) printf("BC_ListBox::button_press_event %d\n", __LINE__);
2979
2980 // Get item button was pressed over
2981                         selection_number2 = selection_number1;
2982                         selection_number1 = selection_number;
2983
2984                         selection_start = -1;
2985                         selection_end = -1;
2986
2987
2988 // Multiple item selection is possible
2989                         if(selection_mode == LISTBOX_MULTIPLE &&
2990                                 (ctrl_down() || shift_down()))
2991                         {
2992 // Expand text selection.
2993 // Fill items between selected region and current item.
2994                                 if(shift_down() &&
2995                                         (display_format == LISTBOX_TEXT ||
2996                                         display_format == LISTBOX_ICON_LIST))
2997                                 {
2998 // Get first item selected
2999                                         selection_start = get_first_selection(data);
3000 // Get last item selected
3001                                         selection_end = get_last_selection(data);
3002 // Get center of selected region
3003                                         if(selection_end > selection_start)
3004                                         {
3005                                                 selection_center = (selection_end + selection_start) >> 1;
3006                                         }
3007                                         else
3008                                         {
3009                                                 selection_center = selection_number;
3010                                         }
3011
3012
3013 // Deselect everything.
3014                                         set_all_selected(data, 0);
3015 // Select just the items
3016                                         expand_selection(1, selection_number);
3017                                         new_value = 1;
3018                                 }
3019                                 else
3020 // Toggle a single item on or off
3021                                 {
3022                                         toggle_item_selection(data, selection_number);
3023                                         new_value = current_item->selected;
3024                                 }
3025                         }
3026                         else
3027 // Select single item
3028                         {
3029                                 if(!current_item->selected)
3030                                 {
3031                                         set_all_selected(data, 0);
3032                                         set_selected(data,
3033                                                 selection_number,
3034                                                 1);
3035                                 }
3036                                 new_value = 1;
3037                         }
3038
3039
3040                         current_operation = SELECT;
3041                         highlighted_item = selection_number;
3042                         highlighted_ptr = current_item;
3043                         button_highlighted = 0;
3044                         list_highlighted = 1;
3045                         reset_query();
3046                         draw_items(1);
3047                         do_selection_change = 1;
3048                         result = 1;
3049                 }
3050                 else
3051                 if(data)
3052 // Pressed over nothing.  Start rectangle selection.
3053                 {
3054                         if(get_buttonpress() == 1 &&
3055                                 selection_mode == LISTBOX_MULTIPLE)
3056                         {
3057                                 if(!shift_down())
3058                                 {
3059 // Deselect all and redraw if anything was selected
3060                                         if(get_selection_number(0, 0) >= 0)
3061                                         {
3062                                                 set_all_selected(data, 0);
3063                                                 draw_items(1);
3064                                                 do_selection_change = 1;
3065                                                 result = 1;
3066                                         }
3067                                 }
3068                                 else
3069                                 {
3070 // Promote selections to protect from a rectangle selection
3071                                         promote_selections(data, 1, 2);
3072                                 }
3073
3074 // Start rectangle selection
3075                                 current_operation = SELECT_RECT;
3076                                 rect_x1 = rect_x2 = get_cursor_x();
3077                                 rect_y1 = rect_y2 = get_cursor_y();
3078                         }
3079                 }
3080
3081
3082                 reset_query();
3083         }
3084         else
3085 // Suggestion box is not active but visible, so lie to get it to deactivate
3086         if(is_popup && (active || (is_suggestions && gui)))
3087         {
3088                 active = 1;
3089                 if(debug) printf("BC_ListBox::button_press_event %d\n", __LINE__);
3090                 deactivate();
3091                 result = 1;
3092         }
3093
3094
3095         if(do_selection_change) selection_changed();
3096         if(debug) printf("BC_ListBox::button_press_event %d %d\n",
3097                 __LINE__,
3098                 result);
3099
3100         return result;
3101 }
3102
3103 int BC_ListBox::button_release_event()
3104 {
3105         int result = 0;
3106         int cursor_x, cursor_y;
3107         int do_event = 0;
3108         new_value = 0;
3109
3110 //printf("BC_ListBox::button_release_event 1 %d\n", current_operation);
3111         switch(current_operation)
3112         {
3113                 case DRAG_DIVISION:
3114                         current_operation = NO_OPERATION;
3115                         result = 1;
3116                         break;
3117
3118                 case WHEEL:
3119                         current_operation = NO_OPERATION;
3120                         result = 1;
3121                         break;
3122
3123 // Release item selection
3124                 case BUTTON_DOWN_SELECT:
3125                 case SELECT:
3126 //printf("BC_ListBox::button_release_event 10\n");
3127                         unset_repeat(get_resources()->scroll_repeat);
3128                         current_operation = NO_OPERATION;
3129                         translate_coordinates(top_level->event_win,
3130                                 gui->win,
3131                                 gui->get_cursor_x(),
3132                                 gui->get_cursor_y(),
3133                                 &cursor_x,
3134                                 &cursor_y);
3135
3136                         selection_number1 =
3137                                 selection_number =
3138                                 get_cursor_item(data, cursor_x, cursor_y);
3139 //printf("BC_ListBox::button_release_event %d %d\n", selection_number2, selection_number1);
3140
3141                         if(is_popup)
3142                         {
3143                                 button_releases++;
3144                                 if(selection_number >= 0)
3145                                 {
3146                                         deactivate();
3147                                         do_event = 1;
3148                                 }
3149                                 else
3150 // Second button release outside button
3151                                 if(button_releases > 1)
3152                                 {
3153                                         deactivate();
3154                                 }
3155                         }
3156                         else
3157                         {
3158                                 if(top_level->get_double_click() &&
3159                                         selection_number2 == selection_number1 &&
3160                                         selection_number2 >= 0 &&
3161                                         selection_number1 >= 0)
3162                                 {
3163                                         do_event = 1;
3164                                 }
3165                                 result = 1;
3166                         }
3167                         break;
3168
3169
3170                 case SELECT_RECT:
3171                         unset_repeat(get_resources()->scroll_repeat);
3172                         if(data)
3173                         {
3174 // Demote selections from rectangle selection
3175                                 promote_selections(data, 2, 1);
3176                         }
3177
3178 // Hide rectangle overlay
3179                         draw_rectangle(1);
3180                         current_operation = NO_OPERATION;
3181                         result = 1;
3182                         break;
3183
3184 // Release popup button
3185                 case BUTTON_DN:
3186                         hide_tooltip();
3187                         current_operation = NO_OPERATION;
3188                         button_releases++;
3189                         draw_button(1);
3190
3191 // Second button release inside button
3192                         if(button_releases > 1)
3193                         {
3194                                 deactivate();
3195                         }
3196                         result = 1;
3197                         break;
3198
3199                 case COLUMN_DN:
3200                         current_operation = NO_OPERATION;
3201 // Update the sort column and the sort order for the user only if the existing
3202 // sort column is valid.
3203                         if(sort_column >= 0)
3204                         {
3205 // Invert order only if column is the same
3206                                 if(highlighted_title == sort_column)
3207                                         sort_order =
3208                                                 (sort_order == SORT_ASCENDING) ?
3209                                                 SORT_DESCENDING :
3210                                                 SORT_ASCENDING;
3211 // Set the new sort column
3212                                 sort_column = highlighted_title;
3213                                 if(!sort_order_event())
3214                                 {
3215                                         draw_titles(1);
3216                                 }
3217                         }
3218                         else
3219 // Sorting not enabled.  Redraw the title state.
3220                         {
3221                                 draw_titles(1);
3222                         }
3223                         result = 1;
3224                         break;
3225
3226                 case EXPAND_DN:
3227                 {
3228                         int redraw_toggles = 0;
3229                         for(int i = 0; i < expanders.total && !result; i++)
3230                         {
3231                                 if(expanders.values[i]->button_release_event(&redraw_toggles))
3232                                 {
3233                                         result = 1;
3234                                 }
3235                         }
3236 // Need to redraw items because of alpha
3237                         if(redraw_toggles) draw_items(1);
3238                         current_operation = NO_OPERATION;
3239                         break;
3240                 }
3241
3242                 default:
3243 // Can't default to NO_OPERATION because it may be used in a drag event.
3244                         break;
3245         }
3246
3247
3248         if(do_event) handle_event();
3249
3250 //printf("BC_ListBox::button_release_event %d %d\n", __LINE__, get_window_lock());
3251         return result;
3252 }
3253
3254 int BC_ListBox::get_title_h()
3255 {
3256         if(display_format == LISTBOX_TEXT ||
3257                 display_format == LISTBOX_ICON_LIST)
3258                 return column_titles ? column_bg[0]->get_h() : 0;
3259         else
3260                 return 0;
3261 }
3262
3263 void BC_ListBox::reset_cursor(int new_cursor)
3264 {
3265         if(is_popup)
3266         {
3267                 if(gui->get_cursor() != new_cursor)
3268                 {
3269                         gui->set_cursor(new_cursor, 0, 0);
3270                 }
3271         }
3272         else
3273         if(get_cursor() != new_cursor)
3274         {
3275                 set_cursor(new_cursor, 0, 0);
3276         }
3277 }
3278
3279 int BC_ListBox::test_column_divisions(int cursor_x, int cursor_y, int &new_cursor)
3280 {
3281         if(gui &&
3282                 column_titles &&
3283                 cursor_y >= 0 &&
3284                 cursor_y < get_title_h() &&
3285                 cursor_x >= 0 &&
3286                 cursor_x < gui->get_w())
3287         {
3288                 for(int i = 1; i < columns; i++)
3289                 {
3290                         if(cursor_x >= -xposition + get_column_offset(i) - 5 &&
3291                                 cursor_x <  -xposition + get_column_offset(i) +
3292                                         get_resources()->listbox_title_hotspot)
3293                         {
3294                                 highlighted_item = -1;
3295                                 highlighted_ptr = 0;
3296                                 highlighted_division = i;
3297                                 highlighted_title = -1;
3298                                 list_highlighted = 1;
3299                                 new_cursor = HSEPARATE_CURSOR;
3300                                 return 1;
3301                         }
3302                 }
3303         }
3304         highlighted_division = -1;
3305         return 0;
3306 }
3307
3308 int BC_ListBox::test_column_titles(int cursor_x, int cursor_y)
3309 {
3310         if(gui &&
3311                 column_titles &&
3312                 cursor_y >= 0 &&
3313                 cursor_y < get_title_h() &&
3314                 cursor_x >= 0 &&
3315                 cursor_x < gui->get_w())
3316         {
3317                 for(int i = 0; i < columns; i++)
3318                 {
3319                         if(cursor_x >= -xposition + get_column_offset(i) &&
3320                                 (cursor_x < -xposition + get_column_offset(i + 1) ||
3321                                         i == columns - 1))
3322                         {
3323                                 highlighted_item = -1;
3324                                 highlighted_ptr = 0;
3325                                 highlighted_division = -1;
3326                                 highlighted_title = i;
3327                                 list_highlighted = 1;
3328                                 return 1;
3329                         }
3330                 }
3331         }
3332         highlighted_title = -1;
3333         return 0;
3334 }
3335
3336 int BC_ListBox::test_expanders()
3337 {
3338         for(int i = 0; i < expanders.total; i++)
3339         {
3340                 if(expanders.values[i]->button_press_event())
3341                 {
3342                         current_operation = EXPAND_DN;
3343                         draw_toggles(1);
3344                         return 1;
3345                 }
3346         }
3347         return 0 ;
3348 }
3349
3350 int BC_ListBox::cursor_motion_event()
3351 {
3352         int redraw = 0, result = 0;
3353         int new_cursor = ARROW_CURSOR;
3354
3355         selection_number = -1;
3356
3357
3358         switch(current_operation)
3359         {
3360                 case BUTTON_DN:
3361 // Button pressed and slid off button
3362                         if(!cursor_inside())
3363                         {
3364                                 current_operation = BUTTON_DOWN_SELECT;
3365                                 draw_button(1);
3366                                 result = 1;
3367                         }
3368                         break;
3369
3370                 case DRAG_DIVISION:
3371                 {
3372 //                      int new_w = get_cursor_x() +
3373 //                              xposition -
3374 //                              get_column_offset(highlighted_division - 1);
3375                         int difference = get_cursor_x() + xposition - drag_cursor_x;
3376                         int new_w = drag_column_w + difference;
3377
3378                         new_cursor = HSEPARATE_CURSOR;
3379
3380                         if(column_width)
3381                         {
3382                                 column_width[highlighted_division - 1] = new_w;
3383                         }
3384                         else
3385                         {
3386                                 default_column_width[highlighted_division - 1] = new_w;
3387                         }
3388
3389                         column_width_boundaries();
3390
3391 // Force update of coords
3392                         set_autoplacement(data, 0, 1);
3393                         column_resize_event();
3394
3395                         clamp_positions();
3396                         draw_items(0);
3397                         update_scrollbars(1);
3398                         result = 1;
3399                         break;
3400                 }
3401
3402                 case SELECT_RECT:
3403                 {
3404                         if(test_drag_scroll(get_cursor_x(), get_cursor_y()))
3405                         {
3406                                 set_repeat(get_resources()->scroll_repeat);
3407                         }
3408
3409                         int old_x1 = MIN(rect_x1, rect_x2);
3410                         int old_x2 = MAX(rect_x1, rect_x2);
3411                         int old_y1 = MIN(rect_y1, rect_y2);
3412                         int old_y2 = MAX(rect_y1, rect_y2);
3413
3414                         int new_rect_x2 = get_cursor_x();
3415                         int new_rect_y2 = get_cursor_y();
3416
3417                         int x1 = MIN(rect_x1, new_rect_x2);
3418                         int x2 = MAX(rect_x1, new_rect_x2);
3419                         int y1 = MIN(rect_y1, new_rect_y2);
3420                         int y2 = MAX(rect_y1, new_rect_y2);
3421
3422 // Adjust rectangle coverage
3423                         if(old_x1 != x1 ||
3424                                 old_x2 != x2 ||
3425                                 old_y1 != y1 ||
3426                                 old_y2 != y2)
3427                         {
3428                                 if(data)
3429                                 {
3430                                         redraw = select_rectangle(data,
3431                                                 x1,
3432                                                 y1,
3433                                                 x2,
3434                                                 y2);
3435                                 }
3436
3437 // hide rectangle
3438                                 if(!redraw)
3439                                 {
3440 //printf("BC_ListBox::cursor_motion_event %d\n", __LINE__);
3441                                         draw_rectangle(0);
3442                                 }
3443                         }
3444
3445                         rect_x2 = get_cursor_x();
3446                         rect_y2 = get_cursor_y();
3447                         if(redraw)
3448                         {
3449                                 clamp_positions();
3450                                 draw_items(0);
3451                                 update_scrollbars(1);
3452                                 selection_changed();
3453 //printf("BC_ListBox::cursor_motion_event %d\n", __LINE__);
3454                         }
3455                         else
3456                         if(old_x1 != x1 ||
3457                                 old_x2 != x2 ||
3458                                 old_y1 != y1 ||
3459                                 old_y2 != y2)
3460                         {
3461 //printf("BC_ListBox::cursor_motion_event %d\n", __LINE__);
3462                                 draw_rectangle(1);
3463                         }
3464
3465                         result = 1;
3466                         break;
3467                 }
3468
3469                 case SELECT:
3470                 {
3471                         int old_highlighted_item = highlighted_item;
3472
3473                         if(test_drag_scroll(get_cursor_x(),
3474                                 get_cursor_y()))
3475                         {
3476                                 set_repeat(get_resources()->scroll_repeat);
3477                         }
3478
3479
3480                         highlighted_item = selection_number = get_cursor_item(data,
3481                                 get_cursor_x(),
3482                                 get_cursor_y(),
3483                                 &highlighted_ptr);
3484                         result = 1;
3485
3486 // Deselect all items and select just the one we're over
3487                         if(selection_number >= 0 &&
3488                                 !allow_drag &&
3489                                 ((!shift_down() &&
3490                                         !ctrl_down()) ||
3491                                         selection_mode == LISTBOX_SINGLE))
3492                         {
3493                                 redraw = update_selection(data, selection_number);
3494                         }
3495                         else
3496                         if(selection_mode == LISTBOX_MULTIPLE &&
3497                                 (shift_down() || ctrl_down()))
3498 // Expand multiple selection
3499                         {
3500 // Expand selected region in text mode centered around initial range
3501                                 if((display_format == LISTBOX_TEXT ||
3502                                         display_format == LISTBOX_ICON_LIST) &&
3503                                         shift_down())
3504                                 {
3505 // Deselect everything.
3506                                         set_all_selected(data, 0);
3507
3508 // Select just the items
3509                                         redraw = expand_selection(0, selection_number);
3510                                 }
3511                                 else
3512 // Set the one item we're over to the selection value determined in
3513 // button_press_event.
3514                                 {
3515                                         set_selected(data,
3516                                                 selection_number,
3517                                                 new_value);
3518                                 }
3519                         }
3520
3521                         if(highlighted_item != old_highlighted_item)
3522                         {
3523                                 clamp_positions();
3524                                 draw_items(0);
3525                                 update_scrollbars(1);
3526 //printf("BC_ListBox::cursor_motion_event %d %d\n", highlighted_item, old_highlighted_item);
3527                                 selection_changed();
3528                         }
3529                         break;
3530                 }
3531
3532                 case BUTTON_DOWN_SELECT:
3533 // Went back into button area
3534                         if(cursor_inside())
3535                         {
3536                                 current_operation = BUTTON_DN;
3537                                 draw_button(1);
3538                                 result = 1;
3539                         }
3540                         else
3541 // Went into item area
3542                         if(gui)
3543                         {
3544                                 int cursor_x = 0, cursor_y = 0;
3545                                 translate_coordinates(top_level->event_win,
3546                                         gui->win,
3547                                         top_level->cursor_x,
3548                                         top_level->cursor_y,
3549                                         &cursor_x,
3550                                         &cursor_y);
3551                                 int old_highlighted_item = highlighted_item;
3552                                 highlighted_item = selection_number = get_cursor_item(data,
3553                                                 cursor_x,
3554                                                 cursor_y,
3555                                                 &highlighted_ptr);
3556
3557                                 if(highlighted_item != old_highlighted_item)
3558                                 {
3559                                         update_selection(data, selection_number);
3560                                         draw_items(1);
3561                                         selection_changed();
3562                                 }
3563                         }
3564                         break;
3565
3566                 case EXPAND_DN:
3567                 {
3568                         int redraw_toggles = 0;
3569                         for(int i = 0; i < expanders.total && !result; i++)
3570                         {
3571                                 result = expanders.values[i]->cursor_motion_event(
3572                                         &redraw_toggles);
3573                         }
3574                         if(redraw_toggles)
3575                         {
3576 // Need to redraw items because of the alpha
3577                                 draw_items(1);
3578                         }
3579                         break;
3580                 }
3581
3582                 case NO_OPERATION:
3583                 {
3584                         int cursor_x = get_cursor_x(), cursor_y = get_cursor_y();
3585                         if(gui && top_level->event_win == gui->win)
3586                         {
3587                                 int old_highlighted_title = highlighted_title;
3588                                 int old_list_highlighted = list_highlighted;
3589                                 int old_highlighted_item = highlighted_item;
3590                                 int redraw_titles = 0;
3591                                 int redraw_border = 0;
3592                                 int redraw_items = 0;
3593                                 int redraw_toggles = 0;
3594                                 result = 1;
3595
3596
3597 // Test if cursor moved over a title division
3598                                 test_column_divisions(cursor_x, cursor_y, new_cursor);
3599
3600 // Test if cursor moved over a title
3601                                 if(highlighted_division < 0)
3602                                 {
3603                                         test_column_titles(cursor_x, cursor_y);
3604                                 }
3605
3606 // Test if cursor moved over expander
3607                                 if(highlighted_division < 0 &&
3608                                         highlighted_title < 0 &&
3609                                         (display_format == LISTBOX_TEXT ||
3610                                                 display_format == LISTBOX_ICON_LIST))
3611                                 {
3612                                         for(int i = 0; i < expanders.total; i++)
3613                                         {
3614                                                 expanders.values[i]->cursor_motion_event(
3615                                                         &redraw_toggles);
3616                                         }
3617 //printf("BC_ListBox::cursor_motion_event %d\n", redraw_toggles);
3618                                 }
3619
3620 // Test if cursor moved over an item
3621                                 if(highlighted_division < 0 &&
3622                                         highlighted_title < 0)
3623                                 {
3624                                         highlighted_item = get_cursor_item(data,
3625                                                 cursor_x,
3626                                                 cursor_y,
3627                                                 &highlighted_ptr);
3628                                 }
3629
3630
3631 // Clear title highlighting if moved over division
3632                                 if(old_highlighted_title != highlighted_title)
3633                                 {
3634                                         redraw_titles = 1;
3635                                 }
3636
3637 // Highlight list border
3638                                 if(old_list_highlighted != list_highlighted)
3639                                 {
3640                                         redraw_border = 1;
3641                                 }
3642
3643 // Moved out of item area
3644                                 if(old_highlighted_item != highlighted_item)
3645                                 {
3646                                         redraw_items = 1;
3647                                 }
3648
3649 //printf("BC_ListBox::cursor_motion_event 1 %d\n", highlighted_item);
3650
3651 // Change cursor to title division adjustment
3652                                 reset_cursor(new_cursor);
3653
3654                                 if(redraw_items)
3655                                 {
3656                                         draw_items(0);
3657                                 }
3658                                 else
3659                                 {
3660                                         if(redraw_titles)
3661                                                 draw_titles(0);
3662                                         if(redraw_border)
3663                                                 draw_border(0);
3664                                         if(redraw_toggles)
3665                                                 draw_toggles(0);
3666                                 }
3667
3668                                 if(redraw_items ||
3669                                         redraw_titles ||
3670                                         redraw_border ||
3671                                         redraw_toggles)
3672                                 {
3673                                         gui->flash();
3674                                         gui->flush();
3675                                 }
3676                         }
3677
3678
3679                         if(!result && list_highlighted)
3680                         {
3681                                 list_highlighted = 0;
3682                                 highlighted_item = -1;
3683                                 highlighted_ptr = 0;
3684                                 highlighted_title = -1;
3685                                 highlighted_division = -1;
3686                                 draw_items(1);
3687                                 result = 0;
3688                         }
3689                         break;
3690                 }
3691         }
3692
3693
3694         return result;
3695 }
3696
3697 int BC_ListBox::drag_start_event()
3698 {
3699         switch(current_operation)
3700         {
3701                 case SELECT:
3702                         if(gui &&
3703                                 gui->is_event_win() &&
3704                                 allow_drag)
3705                         {
3706                                 BC_ListBoxItem *item_return = 0;
3707                                 selection_number = get_cursor_item(data,
3708                                         top_level->cursor_x,
3709                                         top_level->cursor_y,
3710                                         &item_return);
3711
3712                                 if(selection_number >= 0)
3713                                 {
3714
3715                                         if (item_return->icon_vframe)
3716                                         {
3717                                                 drag_popup = new BC_DragWindow(this,
3718                                                         item_return->icon_vframe /*,
3719                                                         get_abs_cursor_x(0) - item_return->icon_vframe->get_w() / 2,
3720                                                         get_abs_cursor_y(0) - item_return->icon_vframe->get_h() / 2 */);
3721                                         }
3722                                         else
3723 // this probably works not!
3724                                         if (item_return->icon)
3725                                         {
3726                                                 drag_popup = new BC_DragWindow(this,
3727                                                         item_return->icon /*,
3728                                                         get_abs_cursor_x(0) - item_return->icon->get_w() / 2,
3729                                                         get_abs_cursor_y(0) - item_return->icon->get_h() / 2 */);
3730                                         }
3731                                         else
3732                                         {
3733                                                 drag_popup = new BC_DragWindow(this,
3734                                                         drag_icon_vframe /*,
3735                                                         get_abs_cursor_x(0) - drag_icon_vframe->get_w() / 2,
3736                                                         get_abs_cursor_y(0) - drag_icon_vframe->get_h() / 2 */);
3737                                         }
3738
3739                                         current_operation = DRAG_ITEM;
3740                                         return 1;
3741                                 }
3742                         }
3743                         break;
3744
3745                 case COLUMN_DN:
3746                         if(gui && gui->is_event_win() && allow_drag_column)
3747                         {
3748                                 drag_popup = new BC_DragWindow(this,
3749                                         drag_column_icon_vframe /*,
3750                                         get_abs_cursor_x(0) - drag_column_icon_vframe->get_w() / 2,
3751                                         get_abs_cursor_y(0) - drag_column_icon_vframe->get_h() / 2 */);
3752                                 dragged_title = highlighted_title;
3753                                 current_operation = COLUMN_DRAG;
3754                                 draw_titles(1);
3755                                 return 1;
3756                         }
3757                         break;
3758         }
3759
3760         return 0;
3761 }
3762
3763 int BC_ListBox::drag_motion_event()
3764 {
3765 //printf("BC_ListBox::drag_motion_event 1 %d\n", current_operation);
3766         switch(current_operation)
3767         {
3768                 case DRAG_ITEM:
3769                 {
3770                         int redraw = 0;
3771                         int new_highlighted_item = -1;
3772                         BC_ListBoxItem *new_highlighted_ptr = 0;
3773                         new_highlighted_item = get_cursor_item(data,
3774                                 top_level->cursor_x, top_level->cursor_y,
3775                                 &new_highlighted_ptr);
3776
3777                         if(new_highlighted_item != highlighted_item)
3778                         {
3779                                 redraw = 1;
3780                         }
3781
3782 // Always update highlighted value for drag_stop
3783                         highlighted_item = new_highlighted_item;
3784                         highlighted_ptr = new_highlighted_ptr;
3785 //printf("BC_ListBox::drag_motion_event 1 %p\n", highlighted_ptr);
3786                         if(redraw)
3787                         {
3788                                 clamp_positions();
3789                                 draw_items(0);
3790                                 update_scrollbars(1);
3791                         }
3792
3793                         return drag_popup->cursor_motion_event();
3794                         break;
3795                 }
3796
3797                 case COLUMN_DRAG:
3798                 {
3799                         int old_highlighted_title = highlighted_title;
3800                         test_column_titles(get_cursor_x(), get_cursor_y());
3801                         if(old_highlighted_title != highlighted_title)
3802                         {
3803                                 draw_titles(1);
3804                         }
3805                         return drag_popup->cursor_motion_event();
3806                         break;
3807                 }
3808         }
3809         return 0;
3810 }
3811
3812 int BC_ListBox::drag_stop_event()
3813 {
3814         switch(current_operation)
3815         {
3816                 case DRAG_ITEM:
3817 // Inside window boundary
3818                         if(top_level->cursor_x > 0 &&
3819                                 top_level->cursor_x < gui->get_w() - drag_popup->get_w() / 2 &&
3820                                 top_level->cursor_y > 0 &&
3821                                 top_level->cursor_y < gui->get_h() - drag_popup->get_h() / 2)
3822                         {
3823 // Move icon
3824
3825
3826                                 if(display_format == LISTBOX_ICONS)
3827                                 {
3828                                         reposition_item(data,
3829                                                 selection_number,
3830                                                 top_level->cursor_x +
3831                                                         drag_popup->get_offset_x() -
3832                                                         LISTBOX_MARGIN -
3833                                                         2 +
3834                                                         xposition,
3835                                                 top_level->cursor_y +
3836                                                         drag_popup->get_offset_y() -
3837                                                         LISTBOX_MARGIN -
3838                                                         2 +
3839                                                         yposition);
3840                                 }
3841                                 else
3842 // Move rows
3843                                 if(process_drag)
3844                                 {
3845 // Get destination
3846                                         int destination = highlighted_item = item_to_index(data,
3847                                                 highlighted_ptr);
3848 //printf("BC_ListBox::drag_stop_event 1 %p %d\n", highlighted_ptr, destination);
3849
3850 // Move selected items from data to temporary
3851                                         ArrayList<BC_ListBoxItem*> *src_items =
3852                                                 new ArrayList<BC_ListBoxItem*>[columns];
3853
3854                                         move_selection(src_items, data);
3855
3856 // Insert items from temporary to data
3857                                         put_selection(data,
3858                                                 src_items,
3859                                                 destination);
3860
3861
3862                                         delete [] src_items;
3863                                         set_autoplacement(data, 0, 1);
3864                                 }
3865
3866
3867                                 draw_items(1);
3868                         }
3869                         else
3870                                 drag_popup->drag_failure_event();
3871
3872                         delete drag_popup;
3873                         flush();
3874                         drag_popup = 0;
3875                         current_operation = NO_OPERATION;
3876                         new_value = 0;
3877                         return 1;
3878                         break;
3879
3880                 case COLUMN_DRAG:
3881                         if(dragged_title != highlighted_title)
3882                         {
3883                                 if(highlighted_title >= 0)
3884                                 {
3885                                         if(!move_column_event()) draw_titles(1);
3886                                 }
3887                                 else
3888                                         drag_popup->drag_failure_event();
3889                         }
3890                         current_operation = NO_OPERATION;
3891                         delete drag_popup;
3892                         flush();
3893                         drag_popup = 0;
3894                         return 1;
3895                         break;
3896         }
3897         return 0;
3898 }
3899
3900 BC_DragWindow* BC_ListBox::get_drag_popup()
3901 {
3902         return drag_popup;
3903 }
3904
3905 int BC_ListBox::translation_event()
3906 {
3907         if(is_popup && gui)
3908         {
3909                 int new_x = gui->get_x() +
3910                         (top_level->last_translate_x - top_level->prev_x -
3911                                 BC_DisplayInfo::get_left_border());
3912                 int new_y = gui->get_y() +
3913                         (top_level->last_translate_y - top_level->prev_y -
3914                                 BC_DisplayInfo::get_top_border());
3915
3916                 gui->reposition_window(new_x, new_y);
3917
3918         }
3919         return 0;
3920 }
3921
3922 int BC_ListBox::reposition_window(int x, int y, int w, int h, int flush)
3923 {
3924         if(w != -1)
3925         {
3926                 if(w != -1) popup_w = w;
3927                 if(h != -1) popup_h = h;
3928 //printf("BC_ListBox::reposition_window %d %d\n", popup_w, popup_h);
3929
3930                 if(!is_popup)
3931                 {
3932                         if(w != -1) popup_w = w;
3933                         if(h != -1) popup_h = h;
3934                         if(xscrollbar)
3935                                 xscrollbar->reposition_window(get_xscroll_x(),
3936                                         get_xscroll_y(),
3937                                         get_xscroll_width());
3938                         if(yscrollbar)
3939                                 yscrollbar->reposition_window(get_yscroll_x(),
3940                                         get_yscroll_y(),
3941                                         get_yscroll_height());
3942                 }
3943         }
3944
3945
3946         BC_WindowBase::reposition_window(x, y, w, h);
3947         draw_button(0);
3948         draw_items(flush);
3949         return 0;
3950 }
3951
3952 int BC_ListBox::deactivate()
3953 {
3954 // printf("BC_ListBox::deactivate %d this=%p gui=%p active=%d\n",
3955 // __LINE__,
3956 // this,
3957 // gui,
3958 // active);
3959         if(active)
3960         {
3961
3962                 if(is_popup)
3963                 {
3964 //printf("BC_ListBox::deactivate %d this=%p gui=%p\n", __LINE__, this, gui);
3965                         if(gui)
3966                         {
3967                                 delete gui;
3968                                 flush();
3969                         }
3970                         gui = 0;
3971                         xscrollbar = 0;
3972                         yscrollbar = 0;
3973                         highlighted_item = -1;
3974                         highlighted_ptr = 0;
3975 //sleep(1);
3976                 }
3977
3978
3979 //printf("BC_ListBox::deactivate %d this=%p\n", __LINE__, this);
3980                 active = 0;
3981                 top_level->active_subwindow = 0;
3982         }
3983
3984         return 0;
3985 }
3986
3987 int BC_ListBox::activate(int take_focus)
3988 {
3989 //printf("BC_ListBox::activate %d %p\n", __LINE__, this);
3990         if(!active)
3991         {
3992                 if(take_focus)
3993                 {
3994                         top_level->active_subwindow = this;
3995                         active = 1;
3996                 }
3997
3998                 button_releases = 0;
3999
4000 // Test for existence of GUI in case this was previously called without
4001 // take_focus & again with take_focus
4002                 if(is_popup && !gui)
4003                 {
4004                         Window tempwin;
4005                         int x, y;
4006                         int new_x, new_y;
4007                         y = get_y() + get_h();
4008                         if(justify == LISTBOX_RIGHT)
4009                         {
4010                                 x = get_x() - popup_w + get_w();
4011                         }
4012                         else
4013                         {
4014                                 x = get_x();
4015                         }
4016
4017
4018                         XTranslateCoordinates(top_level->display,
4019                                 parent_window->win,
4020                                 top_level->rootwin,
4021                                 x,
4022                                 y,
4023                                 &new_x,
4024                                 &new_y,
4025                                 &tempwin);
4026
4027                         if(new_x < 0) new_x = 0;
4028                         if(new_y + popup_h > top_level->get_root_h(0))
4029                                 new_y -= get_h() + popup_h;
4030
4031                         add_subwindow(gui = new BC_Popup(this,
4032                                 new_x,
4033                                 new_y,
4034                                 popup_w,
4035                                 popup_h,
4036                                 -1,
4037                                 0,
4038                                 0));
4039 // Avoid top going out of screen
4040                         if(new_y < 0 )
4041                                 new_y = 2;
4042 //printf("BC_ListBox::activate %d this=%p %p\n", __LINE__, this, gui->win);
4043                         draw_items(1);
4044                         gui->show_window(1);
4045                 }
4046 //printf("BC_ListBox::activate %d %p\n", __LINE__, this);
4047 //sleep(1);
4048         }
4049         return 0;
4050 }
4051
4052 int BC_ListBox::keypress_event()
4053 {
4054         if(!active) return 0;
4055
4056 //printf("BC_ListBox::keypress_event %d\n", __LINE__);
4057
4058         int result = 0, redraw = 0;
4059         int view_items = last_in_view - first_in_view + 1;
4060         if( view_items <= 0 ) view_items = view_h / get_text_height(MEDIUMFONT);
4061         int new_item = -1, new_selection = -1;
4062
4063         switch(top_level->get_keypress())
4064         {
4065                 case ESC:
4066                 case RETURN:
4067                 case BACKSPACE:
4068                         top_level->deactivate();
4069
4070 // If user is manipulating popup with keyboard, don't pass on event.
4071                         if(is_popup)
4072                         {
4073 //printf("BC_ListBox::keypress_event %d %p\n", __LINE__, get_selection(0, 0));
4074                                 if(top_level->get_keypress() == RETURN)
4075                                         handle_event();
4076 //printf("BC_ListBox::keypress_event %d %p\n", __LINE__, get_selection(0, 0));
4077                                 result = 1;
4078                         }
4079                         else
4080                                 result = 0;
4081                         break;
4082
4083                 case UP:
4084                         new_selection = new_item = select_previous(0);
4085
4086 //printf("BC_ListBox::keypress_event 1 %d\n", new_item);
4087                         if(new_item >= 0)
4088                         {
4089                                 center_selection(new_item);
4090                                 redraw = 1;
4091                         }
4092                         reset_query();
4093                         result = 1;
4094                         break;
4095
4096                 case DOWN:
4097                         new_selection = new_item = select_next(0);
4098
4099                         if(new_item >= 0)
4100                         {
4101                                 center_selection(new_item);
4102                                 redraw = 1;
4103                         }
4104                         reset_query();
4105                         result = 1;
4106                         break;
4107
4108                 case PGUP:
4109                         new_selection = new_item = select_previous(view_items - 1);
4110
4111                         if(new_item >= 0)
4112                         {
4113                                 center_selection(new_item);
4114                                 redraw = 1;
4115                         }
4116                         reset_query();
4117                         result = 1;
4118                         break;
4119
4120                 case PGDN:
4121                         new_selection = new_item = select_next(view_items - 1);
4122
4123                         if(new_item >= 0)
4124                         {
4125                                 center_selection(new_item);
4126                                 redraw = 1;
4127                         }
4128                         reset_query();
4129                         result = 1;
4130                         break;
4131
4132                 case LEFT:
4133                         xposition -= 10;
4134                         redraw = 1;
4135                         result = 1;
4136                         break;
4137
4138                 case RIGHT:
4139                         xposition += 10;
4140                         redraw = 1;
4141                         result = 1;
4142                         break;
4143
4144                 case DELETE:
4145                 case HOME:
4146                 case END:
4147                         result = 0;
4148                         break;
4149
4150                 default:
4151                         if(!ctrl_down())
4152                         {
4153                                 int query_len = strlen(query);
4154                                 if( query_len < (int)sizeof(query)-1 &&
4155                                         top_level->get_keypress() > 30 &&
4156                                         top_level->get_keypress() < 127)
4157                                 {
4158                                         query[query_len++] = top_level->get_keypress();
4159                                         query[query_len] = 0;
4160                                         new_selection = query_list();
4161                                 }
4162                                 else
4163                                 if(top_level->get_keypress() == BACKSPACE)
4164                                 {
4165                                         if(query_len > 0) query[--query_len] = 0;
4166                                         new_selection = query_list();
4167                                 }
4168
4169                                 redraw = 1;
4170                                 result = 1;
4171                         }
4172                         break;
4173         }
4174
4175         if(redraw)
4176         {
4177                 clamp_positions();
4178                 draw_items(0);
4179                 update_scrollbars(1);
4180         }
4181
4182 //printf("BC_ListBox::keypress_event %d new_selection=%d\n", __LINE__, new_selection);
4183         if(new_selection >= 0 && !is_suggestions)
4184         {
4185 //printf("BC_ListBox::keypress_event %d\n", __LINE__);
4186                 selection_changed();
4187 //printf("BC_ListBox::keypress_event %d\n", __LINE__);
4188         }
4189
4190         return result;
4191 }
4192
4193
4194 BC_Pixmap* BC_ListBox::get_bg_surface()
4195 {
4196         return bg_surface;
4197 }
4198
4199
4200 void BC_ListBox::draw_background()
4201 {
4202         if( !bg_draw ) return;
4203         bg_draw = 0;
4204 // White background pixmap
4205         set_color(top_level->get_resources()->listbox_inactive);
4206         draw_box(0, 0, bg_surface->get_w(), bg_surface->get_h(), bg_surface);
4207
4208 // Optional heroine pixmap
4209         if(bg_pixmap)
4210                 bg_surface->draw_pixmap(bg_pixmap,
4211                         bg_surface->get_w() - top_level->get_resources()->listbox_bg->get_w(),
4212                         0);
4213 }
4214
4215 void BC_ListBox::clear_listbox(int x, int y, int w, int h)
4216 {
4217         gui->draw_pixmap(bg_surface, x, y, w, h, x, y - title_h);
4218 }
4219
4220 void BC_ListBox::update_format(int display_format, int redraw)
4221 {
4222         this->display_format = display_format;
4223         if( redraw && gui ) draw_items(1, 1);
4224 }
4225
4226 int BC_ListBox::get_format()
4227 {
4228         return display_format;
4229 }
4230
4231
4232
4233 int BC_ListBox::draw_items(int flush, int draw_bg)
4234 {
4235         if(gui)
4236         {
4237                 BC_Resources *resources = get_resources();
4238
4239 //dump(data, columns);
4240
4241 // Calculate items width
4242                 calculate_item_coords();
4243
4244
4245 // Create and destroy scrollbars as needed
4246                 get_scrollbars();
4247
4248
4249
4250                 if( bg_draw ) this->bg_draw = 1;
4251                 draw_background();
4252
4253                 first_in_view = -1;
4254                 last_in_view = 0;
4255 // Icon display
4256                 if(display_format == LISTBOX_ICONS)
4257                 {
4258                         clear_listbox(2, 2 + title_h, view_w, view_h);
4259
4260                         set_font(MEDIUMFONT);
4261                         for(int i = 0; i < data[master_column].size(); i++)
4262                         {
4263                                 BC_ListBoxItem *item = data[master_column].get(i);
4264                                 if(get_item_x(item) >= -get_item_w(item) &&
4265                                         get_item_x(item) < view_w &&
4266                                         get_item_y(item) >= -get_item_h(item) + title_h &&
4267                                         get_item_y(item) < view_h + title_h)
4268                                 {
4269                                         item->set_in_view(1);
4270                                         if( first_in_view < 0 ) first_in_view = i;
4271                                         last_in_view = i;
4272                                         int item_color = get_item_highlight(data, 0, i);
4273                                         int icon_x, icon_y, icon_w, icon_h;
4274                                         int text_x, text_y, text_w, text_h;
4275
4276 // Draw highlights
4277                                         get_icon_mask(item, icon_x, icon_y, icon_w, icon_h);
4278                                         get_text_mask(item, text_x, text_y, text_w, text_h);
4279
4280
4281                                         if(item_color != resources->listbox_inactive)
4282                                         {
4283                                                 gui->set_color(BLACK);
4284                                                 gui->draw_rectangle(icon_x, icon_y, icon_w, icon_h);
4285                                                 gui->set_color(item_color);
4286                                                 gui->draw_box(icon_x + 1, icon_y + 1, icon_w - 2, icon_h - 2);
4287                                                 gui->set_color(BLACK);
4288                                                 gui->draw_rectangle(text_x, text_y, text_w, text_h);
4289                                                 gui->set_color(item_color);
4290                                                 gui->draw_box(text_x + 1, text_y + 1, text_w - 2, text_h - 2);
4291
4292                                                 if(icon_position == ICON_LEFT)
4293                                                         gui->draw_box(text_x - 1, text_y + 1, 2, text_h - 2);
4294                                                 else
4295                                                 if(icon_position == ICON_TOP)
4296                                                         gui->draw_line(text_x + 1, text_y, text_x + icon_w - 2, text_y);
4297                                                 if(text_x + text_w < icon_x + icon_w)
4298                                                 {
4299                                                         gui->set_color(BLACK);
4300                                                         gui->draw_line(text_x + text_w,
4301                                                                 icon_y + icon_h,
4302                                                                 icon_x + icon_w,
4303                                                                 icon_y + icon_h);
4304                                                 }
4305                                         }
4306
4307 // Draw icons
4308                                         gui->set_color(get_item_color(data, 0, i));
4309                                         if(item->icon)
4310                                                 gui->pixmap->draw_pixmap(item->icon,
4311                                                         icon_x + ICON_MARGIN,
4312                                                         icon_y + ICON_MARGIN);
4313
4314
4315                                         gui->draw_text(text_x + ICON_MARGIN,
4316                                                 text_y + ICON_MARGIN + get_baseline(item),
4317                                                 item->text);
4318                                 }
4319                                 else
4320                                         item->set_in_view(0);
4321                         }
4322                 }
4323                 else
4324 // Text display
4325                 {
4326 // Draw one column at a time so text overruns don't go into the next column
4327 // clear column backgrounds
4328                         int current_toggle = 0;
4329                         for(int j = 0; j < columns; j++)
4330                         {
4331                                 clear_listbox(LISTBOX_BORDER + get_column_offset(j) - xposition,
4332                                         LISTBOX_BORDER + title_h,
4333                                         get_column_width(j, 1),
4334                                         view_h);
4335 // Draw rows in the column recursively
4336                                 draw_text_recursive(data, j, 0, &current_toggle);
4337                         }
4338
4339 // Delete excess expanders
4340                         while(expanders.total > current_toggle)
4341                         {
4342                                 expanders.remove_object();
4343                         }
4344                 }
4345
4346 // draw user images if available
4347                 draw_images();
4348 // Draw titles on top of rows for superposition effect
4349                 draw_titles(0);
4350
4351 // Clear garbage from bottom right corner
4352                 if(xscrollbar && yscrollbar && is_popup)
4353                 {
4354                         gui->draw_top_background(parent_window,
4355                                 popup_w - get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w(),
4356                                 popup_h - get_resources()->hscroll_data[SCROLL_HANDLE_UP]->get_h(),
4357                                 get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w(),
4358                                 get_resources()->hscroll_data[SCROLL_HANDLE_UP]->get_h());
4359                 }
4360
4361 // Draw borders
4362                 draw_border(0);
4363
4364
4365                 if(current_operation == SELECT_RECT)
4366                         draw_rectangle(0);
4367
4368                 gui->flash(flush);
4369         }
4370
4371         return 0;
4372 }
4373
4374
4375 void BC_ListBox::draw_text_recursive(ArrayList<BC_ListBoxItem*> *data,
4376         int column,
4377         int indent,
4378         int *current_toggle)
4379 {
4380         if(!data) return;
4381
4382
4383         BC_Resources *resources = get_resources();
4384
4385         set_font(MEDIUMFONT);
4386         int subindent = 0;
4387
4388 // Search for a branch and make room for toggle if there is one
4389         if(column == 0)
4390         {
4391                 for(int i = 0; i < data[column].size(); i++)
4392                 {
4393                         if(data[column].get(i)->get_sublist())
4394                         {
4395                                 subindent = BC_WindowBase::get_resources()->listbox_expand[0]->get_w();
4396                                 break;
4397                         }
4398                 }
4399         }
4400
4401         row_height = row_ascent = row_descent = 0;
4402         for(int i = 0; i < data[column].total; i++)
4403         {
4404                 BC_ListBoxItem *item = data[column].values[i];
4405                 int ht = get_text_h(item);
4406                 if( ht > row_height ) row_height = ht;
4407                 int bl = get_baseline(item);
4408                 if( bl > row_ascent ) row_ascent = bl;
4409                 int dt = ht - bl;
4410                 if( dt > row_descent ) row_ascent = bl;
4411         }
4412
4413         for(int i = 0; i < data[column].size(); i++)
4414         {
4415 // Draw a row
4416                 BC_ListBoxItem *item = data[column].values[i];
4417                 BC_ListBoxItem *first_item = data[master_column].values[i];
4418
4419                 if(get_item_y(item) >= -get_item_h(item) + title_h &&
4420                         get_item_y(item) < view_h + title_h)
4421                 {
4422                         int row_color = get_item_highlight(data, 0, i);
4423                         int x, y, w, h, column_width;
4424
4425                         get_text_mask(item, x, y, w, h);
4426                         column_width = get_column_width(column, 1);
4427                         if(x + column_width > view_w + LISTBOX_BORDER * 2)
4428                                 column_width = view_w + LISTBOX_BORDER * 2 - x;
4429
4430                         if(row_color != resources->listbox_inactive)
4431                         {
4432                                 gui->set_color(row_color);
4433                                 gui->draw_box(x, y, column_width, h);
4434                                 gui->set_color(BLACK);
4435                                 int yy = y, xx = x + column_width-1;
4436                                 gui->draw_line(x, yy, xx, yy);
4437                                 yy = y + row_height;
4438                                 gui->draw_line(x, yy, xx, yy);
4439                         }
4440
4441                         gui->set_color(get_item_color(data, column, i));
4442
4443
4444                         if(column == 0 && display_format == LISTBOX_ICON_LIST)
4445                         {
4446                                 if(item->icon)
4447                                 {
4448                                         gui->pixmap->draw_pixmap(item->icon,
4449                                                 x,
4450                                                 y);
4451                                         x += item->icon->get_w() + ICON_MARGIN;
4452                                 }
4453                         }
4454
4455
4456 // Indent only applies to first column
4457                         gui->draw_text(
4458                                 x + LISTBOX_BORDER + LISTBOX_MARGIN +
4459                                         (column == 0 ? indent + subindent : 0),
4460                                 y + get_baseline(item), item->text);
4461                         item->set_in_view(1);
4462                         if( !indent ) {
4463                                 if( first_in_view < 0 ) first_in_view = i;
4464                                 last_in_view = i;
4465                         }
4466
4467 // Update expander
4468                         if(column == 0 &&
4469                                 item->get_sublist() &&
4470                                 item->get_columns())
4471                         {
4472 // Create new expander
4473                                 if(*current_toggle >= expanders.total)
4474                                 {
4475                                         BC_ListBoxToggle *toggle =
4476                                                 new BC_ListBoxToggle(this,
4477                                                         item,
4478                                                         x + LISTBOX_BORDER + LISTBOX_MARGIN + indent,
4479                                                         y);
4480                                         toggle->draw(0);
4481                                         expanders.append(toggle);
4482                                 }
4483                                 else
4484 // Reposition existing expander
4485                                 {
4486                                         BC_ListBoxToggle *toggle = expanders.values[*current_toggle];
4487 //printf("BC_ListBox::draw_text_recursive 1 %d\n", *current_toggle);
4488                                         toggle->update(item,
4489                                                 x + LISTBOX_BORDER + LISTBOX_MARGIN + indent,
4490                                                 y,
4491                                                 0);
4492                                 }
4493                                 (*current_toggle)++;
4494                         }
4495
4496
4497
4498                 }
4499                 else
4500                         item->set_in_view(0);
4501
4502 // Descend into sublist
4503                 if(first_item->get_expand())
4504                 {
4505                         draw_text_recursive(first_item->get_sublist(),
4506                                 column,
4507                                 indent + LISTBOX_INDENT,
4508                                 current_toggle);
4509                 }
4510         }
4511 }
4512
4513
4514
4515
4516 int BC_ListBox::draw_border(int flash)
4517 {
4518         BC_Resources *resources = top_level->get_resources();
4519         gui->draw_3d_border(0,
4520                 0,
4521                 view_w + LISTBOX_BORDER * 2,
4522                 view_h + title_h + LISTBOX_BORDER * 2,
4523                 resources->listbox_border1,
4524                 list_highlighted ?
4525                         resources->listbox_border2_hi :
4526                         resources->listbox_border2,
4527                 list_highlighted ?
4528                         resources->listbox_border3_hi :
4529                         resources->listbox_border3,
4530                 resources->listbox_border4);
4531
4532         if(flash)
4533         {
4534                 gui->flash();
4535                 gui->flush();
4536         }
4537         return 0;
4538 }
4539
4540 void BC_ListBox::draw_title(int number)
4541 {
4542 // Column title background
4543         int image_number = 0;
4544         if(number == highlighted_title)
4545         {
4546                 image_number = 1;
4547                 if(current_operation == COLUMN_DN)
4548                         image_number = 2;
4549         }
4550
4551         int column_offset = get_column_offset(number) - xposition + LISTBOX_BORDER;
4552         int column_width = get_column_width(number, 1);
4553         gui->draw_3segmenth(get_column_offset(number) - xposition + LISTBOX_BORDER,
4554                 LISTBOX_BORDER,
4555                 get_column_width(number, 1) + get_resources()->listbox_title_overlap,
4556                 column_bg[image_number]);
4557
4558 // Column title sort order
4559         if(number == sort_column)
4560         {
4561                 BC_Pixmap *src;
4562                 if(sort_order == SORT_ASCENDING)
4563                         src = column_sort_dn;
4564                 else
4565                         src = column_sort_up;
4566
4567                 int x = column_offset +
4568                         column_width -
4569                         LISTBOX_BORDER;
4570                 if(x > items_w) x = items_w;
4571                 x -= 5 + src->get_w();
4572                 gui->draw_pixmap(src,
4573                         x,
4574                         title_h / 2 - src->get_h() / 2 + LISTBOX_BORDER);
4575         }
4576
4577
4578         int x = -xposition +
4579                 get_column_offset(number) +
4580                 LISTBOX_MARGIN +
4581                 LISTBOX_BORDER;
4582         x += get_resources()->listbox_title_margin;
4583
4584         gui->set_color(get_resources()->listbox_title_color);
4585         gui->draw_text(x,
4586                 LISTBOX_MARGIN + LISTBOX_BORDER + get_text_ascent(MEDIUMFONT),
4587                 _(column_titles[number]));
4588 }
4589
4590 int BC_ListBox::draw_titles(int flash)
4591 {
4592         if(column_titles &&
4593                 (display_format == LISTBOX_TEXT ||
4594                 display_format == LISTBOX_ICON_LIST))
4595         {
4596 //printf("BC_ListBox::draw_titles 1 %d\n", highlighted_title);
4597                 for(int i = 0; i < columns; i++)
4598                 {
4599                         if(i != highlighted_title)
4600                                 draw_title(i);
4601                 }
4602
4603                 if(highlighted_title >= 0) draw_title(highlighted_title);
4604                 draw_border(0);
4605         }
4606
4607         if(flash)
4608         {
4609                 gui->flash();
4610         }
4611         return 0;
4612 }
4613
4614 void BC_ListBox::draw_toggles(int flash)
4615 {
4616         for(int i = 0; i < expanders.total; i++)
4617                 expanders.values[i]->draw(0);
4618
4619 //printf("BC_ListBox::draw_toggles 1 %d\n", flash);
4620         if(flash && expanders.total)
4621         {
4622                 gui->flash();
4623                 gui->flush();
4624         }
4625 }
4626
4627 int BC_ListBox::draw_rectangle(int flash)
4628 {
4629         int x1 = MIN(rect_x1, rect_x2);
4630         int x2 = MAX(rect_x1, rect_x2);
4631         int y1 = MIN(rect_y1, rect_y2);
4632         int y2 = MAX(rect_y1, rect_y2);
4633
4634         if(x1 == x2 || y1 == y2) return 0;
4635
4636         gui->set_inverse();
4637         gui->set_color(WHITE);
4638         gui->draw_rectangle(x1, y1, x2 - x1, y2 - y1);
4639         gui->set_opaque();
4640
4641
4642         if(flash)
4643         {
4644                 gui->flash(1);
4645         }
4646         return 0;
4647 }
4648
4649 void BC_ListBox::dump(ArrayList<BC_ListBoxItem*> *data,
4650         int columns,
4651         int indent,
4652         int master_column)
4653 {
4654         if(!indent)
4655         {
4656                 printf("BC_ListBox::dump 1\n");
4657         }
4658
4659         for(int i = 0; i < data[master_column].total; i++)
4660         {
4661                 for(int k = 0; k < indent; k++)
4662                         printf(" ");
4663                 for(int j = 0; j < columns; j++)
4664                 {
4665                         BC_ListBoxItem *item = data[j].values[i];
4666                         printf("%d,%d,%d=%s ",
4667                                 item->get_text_x(),
4668                                 item->get_text_y(),
4669                                 item->autoplace_text,
4670                                 item->get_text());
4671                 }
4672                 printf("\n");
4673
4674                 if(data[master_column].values[i]->get_sublist())
4675                 {
4676                         dump(data[master_column].values[i]->get_sublist(),
4677                                 data[master_column].values[i]->get_columns(),
4678                                 indent + 4,
4679                                 master_column);
4680                 }
4681         }
4682
4683
4684 }
4685
4686
4687
4688