683985feec863488fb4969639fef2f083d1423cb
[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 if( gui ) {
2423 // Text is treed
2424 // Cursor is inside items rectangle
2425                 if(cursor_x >= 0 &&
2426                         cursor_x < (yscrollbar ?
2427                                 gui->get_w() - get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w() :
2428                                 gui->get_w()) &&
2429 // Only clamp y if we're not in a SELECT operation.
2430                         (current_operation == BC_ListBox::SELECT ||
2431                                 (cursor_y > get_title_h() + LISTBOX_BORDER &&
2432                                 cursor_y < gui->get_h())))
2433                 {
2434 // Search table for cursor obstruction
2435                         for(int i = 0; i < data[master_column].total; i++)
2436                         {
2437                                 BC_ListBoxItem *item = data[master_column].values[i];
2438                                 (*counter)++;
2439
2440 // Cursor is inside item on current level
2441                                 if(expanded &&
2442                                         item->selectable &&
2443                                         cursor_y >= get_item_y(item) &&
2444                                         cursor_y < get_item_y(item) + get_item_h(item))
2445                                 {
2446 //printf("BC_ListBox::get_cursor_item %d %d %p\n", master_column, i, item);
2447                                         if(item_return) (*item_return) = item;
2448                                         return (*counter);
2449                                 }
2450
2451 // Descend into sublist
2452                                 if(item->get_sublist())
2453                                 {
2454                                         if(get_cursor_item(item->get_sublist(),
2455                                                 cursor_x,
2456                                                 cursor_y,
2457                                                 item_return,
2458                                                 counter,
2459                                                 item->get_expand()) >= 0)
2460                                                 return (*counter);
2461                                 }
2462                         }
2463                 }
2464         }
2465         return -1;
2466 }
2467
2468 int BC_ListBox::repeat_event(int64_t duration)
2469 {
2470         switch(current_operation)
2471         {
2472 // Repeat out of bounds selection
2473                 case SELECT_RECT:
2474                         if(duration == get_resources()->scroll_repeat)
2475                                 return rectangle_scroll_event();
2476                         break;
2477
2478                 case SELECT:
2479                         if(duration == get_resources()->scroll_repeat)
2480                                 return select_scroll_event();
2481                         break;
2482
2483                 case NO_OPERATION:
2484 // Show tooltip
2485                         if(button_highlighted && is_popup && !tooltip_done &&
2486                                 tooltip_text && tooltip_text[0] != 0 &&
2487                                 duration == get_resources()->tooltip_delay)
2488                         {
2489                                 show_tooltip();
2490                                 tooltip_done = 1;
2491                                 return 1;
2492                         }
2493                         break;
2494         }
2495         return 0;
2496 }
2497
2498
2499 int BC_ListBox::cursor_enter_event()
2500 {
2501         int result = 0;
2502
2503         switch(current_operation)
2504         {
2505 // Cursor moved over button, pressed, and exited.
2506                 case BUTTON_DOWN_SELECT:
2507                         if(top_level->event_win == win)
2508                         {
2509                                 current_operation = BUTTON_DN;
2510                                 result = 1;
2511                                 button_highlighted = 1;
2512                                 draw_button(1);
2513                         }
2514                         break;
2515
2516                 case NO_OPERATION:
2517 // Cursor entered button
2518                         if(is_popup && top_level->event_win == win)
2519                         {
2520                                 button_highlighted = 1;
2521                                 result = 1;
2522                                 draw_button(1);
2523                         }
2524                         else
2525 // TODO: Need to get the highlighted column title or item
2526                         if(gui && top_level->event_win == gui->win)
2527                         {
2528                                 list_highlighted = 1;
2529                                 draw_border(1);
2530                                 result = 1;
2531                         }
2532                         break;
2533         }
2534
2535         return result;
2536 }
2537
2538 int BC_ListBox::cursor_leave_event()
2539 {
2540         if(current_operation == COLUMN_DRAG) return 0;
2541
2542 // Left button area
2543         if(button_highlighted)
2544         {
2545                 button_highlighted = 0;
2546                 hide_tooltip();
2547                 draw_button(1);
2548         }
2549
2550         if(list_highlighted)
2551         {
2552                 list_highlighted = 0;
2553                 highlighted_item = -1;
2554                 highlighted_ptr = 0;
2555                 highlighted_title = -1;
2556                 int redraw_toggles = 0;
2557                 for(int i = 0; i < expanders.total; i++)
2558                         expanders.values[i]->cursor_leave_event(&redraw_toggles);
2559
2560                 draw_items(1);
2561         }
2562
2563         return 0;
2564 }
2565
2566 int BC_ListBox::get_first_selection(ArrayList<BC_ListBoxItem*> *data, int *result)
2567 {
2568         int temp = -1;
2569         if(!result) result = &temp;
2570
2571         for(int i = 0; i < data[master_column].total; i++)
2572         {
2573                 BC_ListBoxItem *item = data[master_column].values[i];
2574                 (*result)++;
2575                 if(item->selected) return (*result);
2576                 if(item->get_sublist())
2577                 {
2578                         if(get_first_selection(item->get_sublist(), result) >= 0)
2579                                 return (*result);
2580                 }
2581         }
2582         return -1;
2583 }
2584
2585 int BC_ListBox::get_total_items(ArrayList<BC_ListBoxItem*> *data,
2586         int *result,
2587         int master_column)
2588 {
2589         int temp = 0;
2590         if(!result) result = &temp;
2591
2592         for(int i = 0; i < data[master_column].total; i++)
2593         {
2594                 (*result)++;
2595                 if(data[master_column].values[i]->get_sublist())
2596                         get_total_items(data[master_column].values[i]->get_sublist(),
2597                                 result,
2598                                 master_column);
2599         }
2600
2601         return (*result);
2602 }
2603
2604
2605 int BC_ListBox::get_last_selection(ArrayList<BC_ListBoxItem*> *data,
2606         int *result)
2607 {
2608         int temp = -1;
2609         int top_level = 0;
2610         if(!result)
2611         {
2612                 result = &temp;
2613                 top_level = 1;
2614         }
2615
2616         for(int i = data[master_column].total - 1; i >= 0; i--)
2617         {
2618                 BC_ListBoxItem *item = data[master_column].values[i];
2619                 (*result)++;
2620                 if(item->selected)
2621                 {
2622                         if(top_level)
2623                                 return get_total_items(data, 0, master_column) - (*result) /* - 1 */;
2624                         else
2625                                 return (*result);
2626                 }
2627
2628                 if(item->get_sublist())
2629                 {
2630                         if(get_last_selection(item->get_sublist(), result) >= 0)
2631                         {
2632                                 if(top_level)
2633                                         return get_total_items(data, 0, master_column) - (*result) /* - 1 */;
2634                                 else
2635                                         return (*result);
2636                         }
2637                 }
2638         }
2639         return -1;
2640 }
2641
2642 void BC_ListBox::select_range(ArrayList<BC_ListBoxItem*> *data,
2643                 int start,
2644                 int end,
2645                 int *current)
2646 {
2647         int temp = -1;
2648         if(!current) current = &temp;
2649
2650         for(int i = 0; i < data[master_column].total; i++)
2651         {
2652                 (*current)++;
2653                 if((*current) >= start && (*current) < end)
2654                 {
2655                         for(int j = 0; j < columns; j++)
2656                                 data[j].values[i]->selected = 1;
2657                 }
2658                 BC_ListBoxItem *item = data[master_column].values[i];
2659                 if(item->get_sublist())
2660                         select_range(item->get_sublist(),
2661                                 start,
2662                                 end,
2663                                 current);
2664         }
2665 }
2666
2667
2668 // Fill items between current selection and new selection
2669 int BC_ListBox::expand_selection(int button_press, int selection_number)
2670 {
2671         int old_selection_start = selection_start;
2672         int old_selection_end = selection_end;
2673
2674 // printf("BC_ListBox::expand_selection %d %d\n",
2675 // selection_center,
2676 // selection_number);
2677
2678 // Calculate the range to select based on selection_center and selection_number
2679         if(selection_number < selection_center)
2680         {
2681                 selection_start = selection_number;
2682         }
2683         else
2684         {
2685                 selection_end = selection_number + 1;
2686         }
2687
2688 //printf("BC_ListBox::expand_selection %d %d %d %d\n", old_selection_start, old_selection_end, selection_start, selection_end);
2689 // Recurse through all the items and select the desired range
2690         select_range(data, selection_start, selection_end);
2691
2692 // Trigger redraw
2693         return (old_selection_start != selection_start ||
2694                 old_selection_end != selection_end);
2695 }
2696
2697 int BC_ListBox::toggle_item_selection(ArrayList<BC_ListBoxItem*> *data,
2698         int selection_number,
2699         int *counter)
2700 {
2701         int temp = -1;
2702         if(!counter) counter = &temp;
2703
2704         for(int i = 0; i < data[master_column].total; i++)
2705         {
2706                 BC_ListBoxItem *item = data[master_column].values[i];
2707                 (*counter)++;
2708                 if((*counter) == selection_number)
2709                 {
2710 // Get new value for selection
2711                         int selected = !item->selected;
2712 // Set row
2713                         for(int j = 0; j < columns; j++)
2714                                 data[j].values[i]->selected = selected;
2715                         return 1;
2716                 }
2717
2718 // Descend into sublist
2719                 if(item->get_sublist())
2720                 {
2721                         if(toggle_item_selection(item->get_sublist(),
2722                                 selection_number,
2723                                 counter))
2724                                 return 1;
2725                 }
2726         }
2727
2728         return 0;
2729 }
2730
2731
2732 void BC_ListBox::set_all_selected(ArrayList<BC_ListBoxItem*> *data, int value)
2733 {
2734         for(int i = 0; i < data[master_column].total; i++)
2735         {
2736                 for(int j = 0; j < columns; j++)
2737                 {
2738                         BC_ListBoxItem *item = data[j].values[i];
2739                         item->selected = value;
2740                 }
2741                 BC_ListBoxItem *item = data[master_column].values[i];
2742                 if(item->get_sublist())
2743                 {
2744                         set_all_selected(item->get_sublist(), value);
2745                 }
2746         }
2747 }
2748
2749 void BC_ListBox::set_selected(ArrayList<BC_ListBoxItem*> *data,
2750                 int item_number,
2751                 int value,
2752                 int *counter)
2753 {
2754         int temp = -1;
2755         if(!counter) counter = &temp;
2756         for(int i = 0; i < data[master_column].total && (*counter) != item_number; i++)
2757         {
2758                 (*counter)++;
2759                 if((*counter) == item_number)
2760                 {
2761                         for(int j = 0; j < columns; j++)
2762                         {
2763                                 BC_ListBoxItem *item = data[j].values[i];
2764                                 item->selected = value;
2765                         }
2766                         return;
2767                 }
2768
2769                 BC_ListBoxItem *item = data[master_column].values[i];
2770                 if(item->get_sublist())
2771                 {
2772                         set_selected(item->get_sublist(),
2773                                 item_number,
2774                                 value,
2775                                 counter);
2776                 }
2777         }
2778 }
2779
2780 int BC_ListBox::update_selection(ArrayList<BC_ListBoxItem*> *data,
2781         int selection_number,
2782         int *counter)
2783 {
2784         int temp = -1;
2785         int result = 0;
2786         if(!counter) counter = &temp;
2787
2788         for(int i = 0; i < data[master_column].total; i++)
2789         {
2790                 BC_ListBoxItem *item = data[master_column].values[i];
2791                 (*counter)++;
2792                 if((*counter) == selection_number && !item->selected)
2793                 {
2794                         result = 1;
2795                         for(int j = 0; j < columns; j++)
2796                                 data[j].values[i]->selected = 1;
2797                 }
2798                 else
2799                 if((*counter) != selection_number && item->selected)
2800                 {
2801                         result = 1;
2802                         for(int j = 0; j < columns; j++)
2803                                 data[j].values[i]->selected = 0;
2804                 }
2805                 if(item->get_sublist())
2806                         result |= update_selection(item->get_sublist(),
2807                                 selection_number,
2808                                 counter);
2809         }
2810         return result;
2811 }
2812
2813 void BC_ListBox::promote_selections(ArrayList<BC_ListBoxItem*> *data,
2814         int old_value,
2815         int new_value)
2816 {
2817         for(int i = 0; i < data[master_column].total; i++)
2818         {
2819                 for(int j = 0; j < columns; j++)
2820                 {
2821                         BC_ListBoxItem *item = data[j].values[i];
2822                         if(item->selected == old_value) item->selected = new_value;
2823                 }
2824                 BC_ListBoxItem *item = data[master_column].values[i];
2825                 if(item->get_sublist())
2826                         promote_selections(item->get_sublist(), old_value, new_value);
2827         }
2828 }
2829
2830 int BC_ListBox::focus_out_event()
2831 {
2832         deactivate();
2833         return 0;
2834 }
2835
2836 int BC_ListBox::button_press_event()
2837 {
2838         int result = 0;
2839         BC_ListBoxItem *current_item = 0;
2840         int new_cursor;
2841         int do_selection_change = 0;
2842         const int debug = 0;
2843
2844         hide_tooltip();
2845         if(debug) printf("BC_ListBox::button_press_event %d this=%p event_win=%p %p %p %p\n",
2846                 __LINE__,
2847                 this,
2848                 (void*)top_level->event_win,
2849                 (void*)(gui ? gui->win : 0),
2850                 (void*)win,
2851                 (void*)parent_window->win);
2852
2853 // Pressed in button
2854         if(is_popup && top_level->event_win == win)
2855         {
2856                 if(debug) printf("BC_ListBox::button_press_event %d\n", __LINE__);
2857                 current_operation = BUTTON_DN;
2858                 draw_button(1);
2859
2860 // Deploy listbox
2861                 if(!active && !disabled)
2862                 {
2863                         top_level->deactivate();
2864                         activate();
2865                 }
2866
2867                 result = 1;
2868         }
2869         else
2870 // Pressed in scrollbar
2871         if((xscrollbar && top_level->event_win == xscrollbar->win) ||
2872                 (yscrollbar && top_level->event_win == yscrollbar->win))
2873         {
2874                 if(debug) printf("BC_ListBox::button_press_event %d\n", __LINE__);
2875                 result = 0;
2876         }
2877         else
2878 // Pressed in items
2879         if(gui && top_level->event_win == gui->win)
2880         {
2881                 if(debug) printf("BC_ListBox::button_press_event %d\n", __LINE__);
2882
2883 // Activate list items
2884 // If it is a suggestion popup, it is visible without being active
2885                 if(!active)
2886                 {
2887                         if(debug) printf("BC_ListBox::button_press_event %d\n", __LINE__);
2888                         if(!is_suggestions) top_level->deactivate();
2889                         if(debug) printf("BC_ListBox::button_press_event %d\n", __LINE__);
2890                         activate();
2891                         if(debug) printf("BC_ListBox::button_press_event %d\n", __LINE__);
2892                 }
2893
2894 // Wheel mouse pressed
2895                 if(get_buttonpress() == 4)
2896                 {
2897                         if(current_operation == NO_OPERATION)
2898                         {
2899                                 current_operation = WHEEL;
2900                                 if(yscrollbar)
2901                                 {
2902                                         set_yposition(yposition - gui->get_h() / 10, 0);
2903                                         clamp_positions();
2904                                         update_scrollbars(0);
2905                                         highlighted_ptr = 0;
2906                                         highlighted_item = get_cursor_item(data,
2907                                                 top_level->cursor_x,
2908                                                 top_level->cursor_y,
2909                                                 &highlighted_ptr);
2910                                         draw_items(1);
2911                                         result = 1;
2912                                 }
2913                         }
2914                 }
2915                 else
2916                 if(get_buttonpress() == 5)
2917                 {
2918                         if(current_operation == NO_OPERATION)
2919                         {
2920                                 current_operation = WHEEL;
2921                                 if(yscrollbar)
2922                                 {
2923                                         set_yposition(yposition + gui->get_h() / 10, 0);
2924                                         clamp_positions();
2925                                         update_scrollbars(0);
2926                                         highlighted_ptr = 0;
2927                                         highlighted_item = get_cursor_item(data,
2928                                                 top_level->cursor_x,
2929                                                 top_level->cursor_y,
2930                                                 &highlighted_ptr);
2931                                         draw_items(1);
2932                                         result = 1;
2933                                 }
2934                         }
2935                 }
2936                 else
2937 // Pressed over column title division
2938                 if(test_column_divisions(gui->get_cursor_x(),
2939                         gui->get_cursor_y(),
2940                         new_cursor))
2941                 {
2942                         drag_cursor_x = gui->get_cursor_x() + xposition;
2943                         if(column_width)
2944                                 drag_column_w = column_width[highlighted_division - 1];
2945                         else
2946                                 drag_column_w = default_column_width[highlighted_division - 1];
2947
2948                         current_operation = DRAG_DIVISION;
2949                         reset_query();
2950                 }
2951                 else
2952 // Pressed in column title
2953                 if(test_column_titles(gui->get_cursor_x(), gui->get_cursor_y()))
2954                 {
2955                         current_operation = COLUMN_DN;
2956                         button_highlighted = 0;
2957                         list_highlighted = 1;
2958                         draw_items(1);
2959                         result = 1;
2960                 }
2961                 else
2962 // Pressed in expander
2963                 if(test_expanders())
2964                 {
2965                         current_operation = EXPAND_DN;
2966 // Need to redraw items because of alpha
2967                         draw_items(1);
2968                         result = 1;
2969                 }
2970                 else
2971 // Pressed over item
2972                 if((selection_number = get_cursor_item(data,
2973                                         gui->get_cursor_x(),
2974                                         gui->get_cursor_y(),
2975                                         &current_item)) >= 0)
2976                 {
2977                         if(debug) printf("BC_ListBox::button_press_event %d\n", __LINE__);
2978
2979 // Get item button was pressed over
2980                         selection_number2 = selection_number1;
2981                         selection_number1 = selection_number;
2982
2983                         selection_start = -1;
2984                         selection_end = -1;
2985
2986
2987 // Multiple item selection is possible
2988                         if(selection_mode == LISTBOX_MULTIPLE &&
2989                                 (ctrl_down() || shift_down()))
2990                         {
2991 // Expand text selection.
2992 // Fill items between selected region and current item.
2993                                 if(shift_down() &&
2994                                         (display_format == LISTBOX_TEXT ||
2995                                         display_format == LISTBOX_ICON_LIST))
2996                                 {
2997 // Get first item selected
2998                                         selection_start = get_first_selection(data);
2999 // Get last item selected
3000                                         selection_end = get_last_selection(data);
3001 // Get center of selected region
3002                                         if(selection_end > selection_start)
3003                                         {
3004                                                 selection_center = (selection_end + selection_start) >> 1;
3005                                         }
3006                                         else
3007                                         {
3008                                                 selection_center = selection_number;
3009                                         }
3010
3011
3012 // Deselect everything.
3013                                         set_all_selected(data, 0);
3014 // Select just the items
3015                                         expand_selection(1, selection_number);
3016                                         new_value = 1;
3017                                 }
3018                                 else
3019 // Toggle a single item on or off
3020                                 {
3021                                         toggle_item_selection(data, selection_number);
3022                                         new_value = current_item->selected;
3023                                 }
3024                         }
3025                         else
3026 // Select single item
3027                         {
3028                                 if(!current_item->selected)
3029                                 {
3030                                         set_all_selected(data, 0);
3031                                         set_selected(data,
3032                                                 selection_number,
3033                                                 1);
3034                                 }
3035                                 new_value = 1;
3036                         }
3037
3038
3039                         current_operation = SELECT;
3040                         highlighted_item = selection_number;
3041                         highlighted_ptr = current_item;
3042                         button_highlighted = 0;
3043                         list_highlighted = 1;
3044                         reset_query();
3045                         draw_items(1);
3046                         do_selection_change = 1;
3047                         result = 1;
3048                 }
3049                 else
3050                 if(data)
3051 // Pressed over nothing.  Start rectangle selection.
3052                 {
3053                         if(get_buttonpress() == 1 &&
3054                                 selection_mode == LISTBOX_MULTIPLE)
3055                         {
3056                                 if(!shift_down())
3057                                 {
3058 // Deselect all and redraw if anything was selected
3059                                         if(get_selection_number(0, 0) >= 0)
3060                                         {
3061                                                 set_all_selected(data, 0);
3062                                                 draw_items(1);
3063                                                 do_selection_change = 1;
3064                                                 result = 1;
3065                                         }
3066                                 }
3067                                 else
3068                                 {
3069 // Promote selections to protect from a rectangle selection
3070                                         promote_selections(data, 1, 2);
3071                                 }
3072
3073 // Start rectangle selection
3074                                 current_operation = SELECT_RECT;
3075                                 rect_x1 = rect_x2 = get_cursor_x();
3076                                 rect_y1 = rect_y2 = get_cursor_y();
3077                         }
3078                 }
3079
3080
3081                 reset_query();
3082         }
3083         else
3084 // Suggestion box is not active but visible, so lie to get it to deactivate
3085         if(is_popup && (active || (is_suggestions && gui)))
3086         {
3087                 active = 1;
3088                 if(debug) printf("BC_ListBox::button_press_event %d\n", __LINE__);
3089                 deactivate();
3090                 result = 1;
3091         }
3092
3093
3094         if(do_selection_change) selection_changed();
3095         if(debug) printf("BC_ListBox::button_press_event %d %d\n",
3096                 __LINE__,
3097                 result);
3098
3099         return result;
3100 }
3101
3102 int BC_ListBox::button_release_event()
3103 {
3104         int result = 0;
3105         int cursor_x, cursor_y;
3106         int do_event = 0;
3107         new_value = 0;
3108
3109 //printf("BC_ListBox::button_release_event 1 %d\n", current_operation);
3110         switch(current_operation)
3111         {
3112                 case DRAG_DIVISION:
3113                         current_operation = NO_OPERATION;
3114                         result = 1;
3115                         break;
3116
3117                 case WHEEL:
3118                         current_operation = NO_OPERATION;
3119                         result = 1;
3120                         break;
3121
3122 // Release item selection
3123                 case BUTTON_DOWN_SELECT:
3124                 case SELECT:
3125 //printf("BC_ListBox::button_release_event 10\n");
3126                         unset_repeat(get_resources()->scroll_repeat);
3127                         current_operation = NO_OPERATION;
3128                         if( gui ) {
3129                                 translate_coordinates(top_level->event_win, gui->win,
3130                                         gui->get_cursor_x(), gui->get_cursor_y(),
3131                                         &cursor_x, &cursor_y);
3132                                 selection_number1 = selection_number =
3133                                         get_cursor_item(data, cursor_x, cursor_y);
3134 //printf("BC_ListBox::button_release_event %d %d\n", selection_number2, selection_number1);
3135                         }
3136
3137                         if(is_popup)
3138                         {
3139                                 button_releases++;
3140                                 if(selection_number >= 0)
3141                                 {
3142                                         deactivate();
3143                                         do_event = 1;
3144                                 }
3145                                 else
3146 // Second button release outside button
3147                                 if(button_releases > 1)
3148                                 {
3149                                         deactivate();
3150                                 }
3151                         }
3152                         else
3153                         {
3154                                 if(top_level->get_double_click() &&
3155                                         selection_number2 == selection_number1 &&
3156                                         selection_number2 >= 0 &&
3157                                         selection_number1 >= 0)
3158                                 {
3159                                         do_event = 1;
3160                                 }
3161                                 result = 1;
3162                         }
3163                         break;
3164
3165
3166                 case SELECT_RECT:
3167                         unset_repeat(get_resources()->scroll_repeat);
3168                         if(data)
3169                         {
3170 // Demote selections from rectangle selection
3171                                 promote_selections(data, 2, 1);
3172                         }
3173
3174 // Hide rectangle overlay
3175                         draw_rectangle(1);
3176                         current_operation = NO_OPERATION;
3177                         result = 1;
3178                         break;
3179
3180 // Release popup button
3181                 case BUTTON_DN:
3182                         hide_tooltip();
3183                         current_operation = NO_OPERATION;
3184                         button_releases++;
3185                         draw_button(1);
3186
3187 // Second button release inside button
3188                         if(button_releases > 1)
3189                         {
3190                                 deactivate();
3191                         }
3192                         result = 1;
3193                         break;
3194
3195                 case COLUMN_DN:
3196                         current_operation = NO_OPERATION;
3197 // Update the sort column and the sort order for the user only if the existing
3198 // sort column is valid.
3199                         if(sort_column >= 0)
3200                         {
3201 // Invert order only if column is the same
3202                                 if(highlighted_title == sort_column)
3203                                         sort_order =
3204                                                 (sort_order == SORT_ASCENDING) ?
3205                                                 SORT_DESCENDING :
3206                                                 SORT_ASCENDING;
3207 // Set the new sort column
3208                                 sort_column = highlighted_title;
3209                                 if(!sort_order_event())
3210                                 {
3211                                         draw_titles(1);
3212                                 }
3213                         }
3214                         else
3215 // Sorting not enabled.  Redraw the title state.
3216                         {
3217                                 draw_titles(1);
3218                         }
3219                         result = 1;
3220                         break;
3221
3222                 case EXPAND_DN:
3223                 {
3224                         int redraw_toggles = 0;
3225                         for(int i = 0; i < expanders.total && !result; i++)
3226                         {
3227                                 if(expanders.values[i]->button_release_event(&redraw_toggles))
3228                                 {
3229                                         result = 1;
3230                                 }
3231                         }
3232 // Need to redraw items because of alpha
3233                         if(redraw_toggles) draw_items(1);
3234                         current_operation = NO_OPERATION;
3235                         break;
3236                 }
3237
3238                 default:
3239 // Can't default to NO_OPERATION because it may be used in a drag event.
3240                         break;
3241         }
3242
3243
3244         if(do_event) handle_event();
3245
3246 //printf("BC_ListBox::button_release_event %d %d\n", __LINE__, get_window_lock());
3247         return result;
3248 }
3249
3250 int BC_ListBox::get_title_h()
3251 {
3252         if(display_format == LISTBOX_TEXT ||
3253                 display_format == LISTBOX_ICON_LIST)
3254                 return column_titles ? column_bg[0]->get_h() : 0;
3255         else
3256                 return 0;
3257 }
3258
3259 void BC_ListBox::reset_cursor(int new_cursor)
3260 {
3261         if(is_popup)
3262         {
3263                 if(gui->get_cursor() != new_cursor)
3264                 {
3265                         gui->set_cursor(new_cursor, 0, 0);
3266                 }
3267         }
3268         else
3269         if(get_cursor() != new_cursor)
3270         {
3271                 set_cursor(new_cursor, 0, 0);
3272         }
3273 }
3274
3275 int BC_ListBox::test_column_divisions(int cursor_x, int cursor_y, int &new_cursor)
3276 {
3277         if(gui &&
3278                 column_titles &&
3279                 cursor_y >= 0 &&
3280                 cursor_y < get_title_h() &&
3281                 cursor_x >= 0 &&
3282                 cursor_x < gui->get_w())
3283         {
3284                 for(int i = 1; i < columns; i++)
3285                 {
3286                         if(cursor_x >= -xposition + get_column_offset(i) - 5 &&
3287                                 cursor_x <  -xposition + get_column_offset(i) +
3288                                         get_resources()->listbox_title_hotspot)
3289                         {
3290                                 highlighted_item = -1;
3291                                 highlighted_ptr = 0;
3292                                 highlighted_division = i;
3293                                 highlighted_title = -1;
3294                                 list_highlighted = 1;
3295                                 new_cursor = HSEPARATE_CURSOR;
3296                                 return 1;
3297                         }
3298                 }
3299         }
3300         highlighted_division = -1;
3301         return 0;
3302 }
3303
3304 int BC_ListBox::test_column_titles(int cursor_x, int cursor_y)
3305 {
3306         if(gui &&
3307                 column_titles &&
3308                 cursor_y >= 0 &&
3309                 cursor_y < get_title_h() &&
3310                 cursor_x >= 0 &&
3311                 cursor_x < gui->get_w())
3312         {
3313                 for(int i = 0; i < columns; i++)
3314                 {
3315                         if(cursor_x >= -xposition + get_column_offset(i) &&
3316                                 (cursor_x < -xposition + get_column_offset(i + 1) ||
3317                                         i == columns - 1))
3318                         {
3319                                 highlighted_item = -1;
3320                                 highlighted_ptr = 0;
3321                                 highlighted_division = -1;
3322                                 highlighted_title = i;
3323                                 list_highlighted = 1;
3324                                 return 1;
3325                         }
3326                 }
3327         }
3328         highlighted_title = -1;
3329         return 0;
3330 }
3331
3332 int BC_ListBox::test_expanders()
3333 {
3334         for(int i = 0; i < expanders.total; i++)
3335         {
3336                 if(expanders.values[i]->button_press_event())
3337                 {
3338                         current_operation = EXPAND_DN;
3339                         draw_toggles(1);
3340                         return 1;
3341                 }
3342         }
3343         return 0 ;
3344 }
3345
3346 int BC_ListBox::cursor_motion_event()
3347 {
3348         int redraw = 0, result = 0;
3349         int new_cursor = ARROW_CURSOR;
3350
3351         selection_number = -1;
3352
3353
3354         switch(current_operation)
3355         {
3356                 case BUTTON_DN:
3357 // Button pressed and slid off button
3358                         if(!cursor_inside())
3359                         {
3360                                 current_operation = BUTTON_DOWN_SELECT;
3361                                 draw_button(1);
3362                                 result = 1;
3363                         }
3364                         break;
3365
3366                 case DRAG_DIVISION:
3367                 {
3368 //                      int new_w = get_cursor_x() +
3369 //                              xposition -
3370 //                              get_column_offset(highlighted_division - 1);
3371                         int difference = get_cursor_x() + xposition - drag_cursor_x;
3372                         int new_w = drag_column_w + difference;
3373
3374                         new_cursor = HSEPARATE_CURSOR;
3375
3376                         if(column_width)
3377                         {
3378                                 column_width[highlighted_division - 1] = new_w;
3379                         }
3380                         else
3381                         {
3382                                 default_column_width[highlighted_division - 1] = new_w;
3383                         }
3384
3385                         column_width_boundaries();
3386
3387 // Force update of coords
3388                         set_autoplacement(data, 0, 1);
3389                         column_resize_event();
3390
3391                         clamp_positions();
3392                         draw_items(0);
3393                         update_scrollbars(1);
3394                         result = 1;
3395                         break;
3396                 }
3397
3398                 case SELECT_RECT:
3399                 {
3400                         if(test_drag_scroll(get_cursor_x(), get_cursor_y()))
3401                         {
3402                                 set_repeat(get_resources()->scroll_repeat);
3403                         }
3404
3405                         int old_x1 = MIN(rect_x1, rect_x2);
3406                         int old_x2 = MAX(rect_x1, rect_x2);
3407                         int old_y1 = MIN(rect_y1, rect_y2);
3408                         int old_y2 = MAX(rect_y1, rect_y2);
3409
3410                         int new_rect_x2 = get_cursor_x();
3411                         int new_rect_y2 = get_cursor_y();
3412
3413                         int x1 = MIN(rect_x1, new_rect_x2);
3414                         int x2 = MAX(rect_x1, new_rect_x2);
3415                         int y1 = MIN(rect_y1, new_rect_y2);
3416                         int y2 = MAX(rect_y1, new_rect_y2);
3417
3418 // Adjust rectangle coverage
3419                         if(old_x1 != x1 ||
3420                                 old_x2 != x2 ||
3421                                 old_y1 != y1 ||
3422                                 old_y2 != y2)
3423                         {
3424                                 if(data)
3425                                 {
3426                                         redraw = select_rectangle(data,
3427                                                 x1,
3428                                                 y1,
3429                                                 x2,
3430                                                 y2);
3431                                 }
3432
3433 // hide rectangle
3434                                 if(!redraw)
3435                                 {
3436 //printf("BC_ListBox::cursor_motion_event %d\n", __LINE__);
3437                                         draw_rectangle(0);
3438                                 }
3439                         }
3440
3441                         rect_x2 = get_cursor_x();
3442                         rect_y2 = get_cursor_y();
3443                         if(redraw)
3444                         {
3445                                 clamp_positions();
3446                                 draw_items(0);
3447                                 update_scrollbars(1);
3448                                 selection_changed();
3449 //printf("BC_ListBox::cursor_motion_event %d\n", __LINE__);
3450                         }
3451                         else
3452                         if(old_x1 != x1 ||
3453                                 old_x2 != x2 ||
3454                                 old_y1 != y1 ||
3455                                 old_y2 != y2)
3456                         {
3457 //printf("BC_ListBox::cursor_motion_event %d\n", __LINE__);
3458                                 draw_rectangle(1);
3459                         }
3460
3461                         result = 1;
3462                         break;
3463                 }
3464
3465                 case SELECT:
3466                 {
3467                         int old_highlighted_item = highlighted_item;
3468
3469                         if(test_drag_scroll(get_cursor_x(),
3470                                 get_cursor_y()))
3471                         {
3472                                 set_repeat(get_resources()->scroll_repeat);
3473                         }
3474
3475
3476                         highlighted_item = selection_number = get_cursor_item(data,
3477                                 get_cursor_x(),
3478                                 get_cursor_y(),
3479                                 &highlighted_ptr);
3480                         result = 1;
3481
3482 // Deselect all items and select just the one we're over
3483                         if(selection_number >= 0 &&
3484                                 !allow_drag &&
3485                                 ((!shift_down() &&
3486                                         !ctrl_down()) ||
3487                                         selection_mode == LISTBOX_SINGLE))
3488                         {
3489                                 redraw = update_selection(data, selection_number);
3490                         }
3491                         else
3492                         if(selection_mode == LISTBOX_MULTIPLE &&
3493                                 (shift_down() || ctrl_down()))
3494 // Expand multiple selection
3495                         {
3496 // Expand selected region in text mode centered around initial range
3497                                 if((display_format == LISTBOX_TEXT ||
3498                                         display_format == LISTBOX_ICON_LIST) &&
3499                                         shift_down())
3500                                 {
3501 // Deselect everything.
3502                                         set_all_selected(data, 0);
3503
3504 // Select just the items
3505                                         redraw = expand_selection(0, selection_number);
3506                                 }
3507                                 else
3508 // Set the one item we're over to the selection value determined in
3509 // button_press_event.
3510                                 {
3511                                         set_selected(data,
3512                                                 selection_number,
3513                                                 new_value);
3514                                 }
3515                         }
3516
3517                         if(highlighted_item != old_highlighted_item)
3518                         {
3519                                 clamp_positions();
3520                                 draw_items(0);
3521                                 update_scrollbars(1);
3522 //printf("BC_ListBox::cursor_motion_event %d %d\n", highlighted_item, old_highlighted_item);
3523                                 selection_changed();
3524                         }
3525                         break;
3526                 }
3527
3528                 case BUTTON_DOWN_SELECT:
3529 // Went back into button area
3530                         if(cursor_inside())
3531                         {
3532                                 current_operation = BUTTON_DN;
3533                                 draw_button(1);
3534                                 result = 1;
3535                         }
3536                         else
3537 // Went into item area
3538                         if(gui)
3539                         {
3540                                 int cursor_x = 0, cursor_y = 0;
3541                                 translate_coordinates(top_level->event_win,
3542                                         gui->win,
3543                                         top_level->cursor_x,
3544                                         top_level->cursor_y,
3545                                         &cursor_x,
3546                                         &cursor_y);
3547                                 int old_highlighted_item = highlighted_item;
3548                                 highlighted_item = selection_number = get_cursor_item(data,
3549                                                 cursor_x,
3550                                                 cursor_y,
3551                                                 &highlighted_ptr);
3552
3553                                 if(highlighted_item != old_highlighted_item)
3554                                 {
3555                                         update_selection(data, selection_number);
3556                                         draw_items(1);
3557                                         selection_changed();
3558                                 }
3559                         }
3560                         break;
3561
3562                 case EXPAND_DN:
3563                 {
3564                         int redraw_toggles = 0;
3565                         for(int i = 0; i < expanders.total && !result; i++)
3566                         {
3567                                 result = expanders.values[i]->cursor_motion_event(
3568                                         &redraw_toggles);
3569                         }
3570                         if(redraw_toggles)
3571                         {
3572 // Need to redraw items because of the alpha
3573                                 draw_items(1);
3574                         }
3575                         break;
3576                 }
3577
3578                 case NO_OPERATION:
3579                 {
3580                         int cursor_x = get_cursor_x(), cursor_y = get_cursor_y();
3581                         if(gui && top_level->event_win == gui->win)
3582                         {
3583                                 int old_highlighted_title = highlighted_title;
3584                                 int old_list_highlighted = list_highlighted;
3585                                 int old_highlighted_item = highlighted_item;
3586                                 int redraw_titles = 0;
3587                                 int redraw_border = 0;
3588                                 int redraw_items = 0;
3589                                 int redraw_toggles = 0;
3590                                 result = 1;
3591
3592
3593 // Test if cursor moved over a title division
3594                                 test_column_divisions(cursor_x, cursor_y, new_cursor);
3595
3596 // Test if cursor moved over a title
3597                                 if(highlighted_division < 0)
3598                                 {
3599                                         test_column_titles(cursor_x, cursor_y);
3600                                 }
3601
3602 // Test if cursor moved over expander
3603                                 if(highlighted_division < 0 &&
3604                                         highlighted_title < 0 &&
3605                                         (display_format == LISTBOX_TEXT ||
3606                                                 display_format == LISTBOX_ICON_LIST))
3607                                 {
3608                                         for(int i = 0; i < expanders.total; i++)
3609                                         {
3610                                                 expanders.values[i]->cursor_motion_event(
3611                                                         &redraw_toggles);
3612                                         }
3613 //printf("BC_ListBox::cursor_motion_event %d\n", redraw_toggles);
3614                                 }
3615
3616 // Test if cursor moved over an item
3617                                 if(highlighted_division < 0 &&
3618                                         highlighted_title < 0)
3619                                 {
3620                                         highlighted_item = get_cursor_item(data,
3621                                                 cursor_x,
3622                                                 cursor_y,
3623                                                 &highlighted_ptr);
3624                                 }
3625
3626
3627 // Clear title highlighting if moved over division
3628                                 if(old_highlighted_title != highlighted_title)
3629                                 {
3630                                         redraw_titles = 1;
3631                                 }
3632
3633 // Highlight list border
3634                                 if(old_list_highlighted != list_highlighted)
3635                                 {
3636                                         redraw_border = 1;
3637                                 }
3638
3639 // Moved out of item area
3640                                 if(old_highlighted_item != highlighted_item)
3641                                 {
3642                                         redraw_items = 1;
3643                                 }
3644
3645 //printf("BC_ListBox::cursor_motion_event 1 %d\n", highlighted_item);
3646
3647 // Change cursor to title division adjustment
3648                                 reset_cursor(new_cursor);
3649
3650                                 if(redraw_items)
3651                                 {
3652                                         draw_items(0);
3653                                 }
3654                                 else
3655                                 {
3656                                         if(redraw_titles)
3657                                                 draw_titles(0);
3658                                         if(redraw_border)
3659                                                 draw_border(0);
3660                                         if(redraw_toggles)
3661                                                 draw_toggles(0);
3662                                 }
3663
3664                                 if(redraw_items ||
3665                                         redraw_titles ||
3666                                         redraw_border ||
3667                                         redraw_toggles)
3668                                 {
3669                                         gui->flash();
3670                                         gui->flush();
3671                                 }
3672                         }
3673
3674
3675                         if(!result && list_highlighted)
3676                         {
3677                                 list_highlighted = 0;
3678                                 highlighted_item = -1;
3679                                 highlighted_ptr = 0;
3680                                 highlighted_title = -1;
3681                                 highlighted_division = -1;
3682                                 draw_items(1);
3683                                 result = 0;
3684                         }
3685                         break;
3686                 }
3687         }
3688
3689
3690         return result;
3691 }
3692
3693 int BC_ListBox::drag_start_event()
3694 {
3695         switch(current_operation)
3696         {
3697                 case SELECT:
3698                         if(gui &&
3699                                 gui->is_event_win() &&
3700                                 allow_drag)
3701                         {
3702                                 BC_ListBoxItem *item_return = 0;
3703                                 selection_number = get_cursor_item(data,
3704                                         top_level->cursor_x,
3705                                         top_level->cursor_y,
3706                                         &item_return);
3707
3708                                 if(selection_number >= 0)
3709                                 {
3710
3711                                         if (item_return->icon_vframe)
3712                                         {
3713                                                 drag_popup = new BC_DragWindow(this,
3714                                                         item_return->icon_vframe /*,
3715                                                         get_abs_cursor_x(0) - item_return->icon_vframe->get_w() / 2,
3716                                                         get_abs_cursor_y(0) - item_return->icon_vframe->get_h() / 2 */);
3717                                         }
3718                                         else
3719 // this probably works not!
3720                                         if (item_return->icon)
3721                                         {
3722                                                 drag_popup = new BC_DragWindow(this,
3723                                                         item_return->icon /*,
3724                                                         get_abs_cursor_x(0) - item_return->icon->get_w() / 2,
3725                                                         get_abs_cursor_y(0) - item_return->icon->get_h() / 2 */);
3726                                         }
3727                                         else
3728                                         {
3729                                                 drag_popup = new BC_DragWindow(this,
3730                                                         drag_icon_vframe /*,
3731                                                         get_abs_cursor_x(0) - drag_icon_vframe->get_w() / 2,
3732                                                         get_abs_cursor_y(0) - drag_icon_vframe->get_h() / 2 */);
3733                                         }
3734
3735                                         current_operation = DRAG_ITEM;
3736                                         return 1;
3737                                 }
3738                         }
3739                         break;
3740
3741                 case COLUMN_DN:
3742                         if(gui && gui->is_event_win() && allow_drag_column)
3743                         {
3744                                 drag_popup = new BC_DragWindow(this,
3745                                         drag_column_icon_vframe /*,
3746                                         get_abs_cursor_x(0) - drag_column_icon_vframe->get_w() / 2,
3747                                         get_abs_cursor_y(0) - drag_column_icon_vframe->get_h() / 2 */);
3748                                 dragged_title = highlighted_title;
3749                                 current_operation = COLUMN_DRAG;
3750                                 draw_titles(1);
3751                                 return 1;
3752                         }
3753                         break;
3754         }
3755
3756         return 0;
3757 }
3758
3759 int BC_ListBox::drag_motion_event()
3760 {
3761 //printf("BC_ListBox::drag_motion_event 1 %d\n", current_operation);
3762         switch(current_operation)
3763         {
3764                 case DRAG_ITEM:
3765                 {
3766                         int redraw = 0;
3767                         int new_highlighted_item = -1;
3768                         BC_ListBoxItem *new_highlighted_ptr = 0;
3769                         new_highlighted_item = get_cursor_item(data,
3770                                 top_level->cursor_x, top_level->cursor_y,
3771                                 &new_highlighted_ptr);
3772
3773                         if(new_highlighted_item != highlighted_item)
3774                         {
3775                                 redraw = 1;
3776                         }
3777
3778 // Always update highlighted value for drag_stop
3779                         highlighted_item = new_highlighted_item;
3780                         highlighted_ptr = new_highlighted_ptr;
3781 //printf("BC_ListBox::drag_motion_event 1 %p\n", highlighted_ptr);
3782                         if(redraw)
3783                         {
3784                                 clamp_positions();
3785                                 draw_items(0);
3786                                 update_scrollbars(1);
3787                         }
3788
3789                         return drag_popup->cursor_motion_event();
3790                         break;
3791                 }
3792
3793                 case COLUMN_DRAG:
3794                 {
3795                         int old_highlighted_title = highlighted_title;
3796                         test_column_titles(get_cursor_x(), get_cursor_y());
3797                         if(old_highlighted_title != highlighted_title)
3798                         {
3799                                 draw_titles(1);
3800                         }
3801                         return drag_popup->cursor_motion_event();
3802                         break;
3803                 }
3804         }
3805         return 0;
3806 }
3807
3808 int BC_ListBox::drag_stop_event()
3809 {
3810         switch(current_operation)
3811         {
3812                 case DRAG_ITEM:
3813 // Inside window boundary
3814                         if(top_level->cursor_x > 0 &&
3815                                 top_level->cursor_x < gui->get_w() - drag_popup->get_w() / 2 &&
3816                                 top_level->cursor_y > 0 &&
3817                                 top_level->cursor_y < gui->get_h() - drag_popup->get_h() / 2)
3818                         {
3819 // Move icon
3820
3821
3822                                 if(display_format == LISTBOX_ICONS)
3823                                 {
3824                                         reposition_item(data,
3825                                                 selection_number,
3826                                                 top_level->cursor_x +
3827                                                         drag_popup->get_offset_x() -
3828                                                         LISTBOX_MARGIN -
3829                                                         2 +
3830                                                         xposition,
3831                                                 top_level->cursor_y +
3832                                                         drag_popup->get_offset_y() -
3833                                                         LISTBOX_MARGIN -
3834                                                         2 +
3835                                                         yposition);
3836                                 }
3837                                 else
3838 // Move rows
3839                                 if(process_drag)
3840                                 {
3841 // Get destination
3842                                         int destination = highlighted_item = item_to_index(data,
3843                                                 highlighted_ptr);
3844 //printf("BC_ListBox::drag_stop_event 1 %p %d\n", highlighted_ptr, destination);
3845
3846 // Move selected items from data to temporary
3847                                         ArrayList<BC_ListBoxItem*> *src_items =
3848                                                 new ArrayList<BC_ListBoxItem*>[columns];
3849
3850                                         move_selection(src_items, data);
3851
3852 // Insert items from temporary to data
3853                                         put_selection(data,
3854                                                 src_items,
3855                                                 destination);
3856
3857
3858                                         delete [] src_items;
3859                                         set_autoplacement(data, 0, 1);
3860                                 }
3861
3862
3863                                 draw_items(1);
3864                         }
3865                         else
3866                                 drag_popup->drag_failure_event();
3867
3868                         delete drag_popup;
3869                         flush();
3870                         drag_popup = 0;
3871                         current_operation = NO_OPERATION;
3872                         new_value = 0;
3873                         return 1;
3874                         break;
3875
3876                 case COLUMN_DRAG:
3877                         if(dragged_title != highlighted_title)
3878                         {
3879                                 if(highlighted_title >= 0)
3880                                 {
3881                                         if(!move_column_event()) draw_titles(1);
3882                                 }
3883                                 else
3884                                         drag_popup->drag_failure_event();
3885                         }
3886                         current_operation = NO_OPERATION;
3887                         delete drag_popup;
3888                         flush();
3889                         drag_popup = 0;
3890                         return 1;
3891                         break;
3892         }
3893         return 0;
3894 }
3895
3896 BC_DragWindow* BC_ListBox::get_drag_popup()
3897 {
3898         return drag_popup;
3899 }
3900
3901 int BC_ListBox::translation_event()
3902 {
3903         if(is_popup && gui)
3904         {
3905                 int new_x = gui->get_x() +
3906                         (top_level->last_translate_x - top_level->prev_x -
3907                                 BC_DisplayInfo::get_left_border());
3908                 int new_y = gui->get_y() +
3909                         (top_level->last_translate_y - top_level->prev_y -
3910                                 BC_DisplayInfo::get_top_border());
3911
3912                 gui->reposition_window(new_x, new_y);
3913
3914         }
3915         return 0;
3916 }
3917
3918 int BC_ListBox::reposition_window(int x, int y, int w, int h, int flush)
3919 {
3920         if(w != -1)
3921         {
3922                 if(w != -1) popup_w = w;
3923                 if(h != -1) popup_h = h;
3924 //printf("BC_ListBox::reposition_window %d %d\n", popup_w, popup_h);
3925
3926                 if(!is_popup)
3927                 {
3928                         if(w != -1) popup_w = w;
3929                         if(h != -1) popup_h = h;
3930                         if(xscrollbar)
3931                                 xscrollbar->reposition_window(get_xscroll_x(),
3932                                         get_xscroll_y(),
3933                                         get_xscroll_width());
3934                         if(yscrollbar)
3935                                 yscrollbar->reposition_window(get_yscroll_x(),
3936                                         get_yscroll_y(),
3937                                         get_yscroll_height());
3938                 }
3939         }
3940
3941
3942         BC_WindowBase::reposition_window(x, y, w, h);
3943         draw_button(0);
3944         draw_items(flush);
3945         return 0;
3946 }
3947
3948 int BC_ListBox::deactivate()
3949 {
3950 // printf("BC_ListBox::deactivate %d this=%p gui=%p active=%d\n",
3951 // __LINE__,
3952 // this,
3953 // gui,
3954 // active);
3955         if(active)
3956         {
3957
3958                 if(is_popup)
3959                 {
3960 //printf("BC_ListBox::deactivate %d this=%p gui=%p\n", __LINE__, this, gui);
3961                         if(gui)
3962                         {
3963                                 delete gui;
3964                                 flush();
3965                         }
3966                         gui = 0;
3967                         xscrollbar = 0;
3968                         yscrollbar = 0;
3969                         highlighted_item = -1;
3970                         highlighted_ptr = 0;
3971 //sleep(1);
3972                 }
3973
3974
3975 //printf("BC_ListBox::deactivate %d this=%p\n", __LINE__, this);
3976                 active = 0;
3977                 top_level->active_subwindow = 0;
3978         }
3979
3980         return 0;
3981 }
3982
3983 int BC_ListBox::activate(int take_focus)
3984 {
3985 //printf("BC_ListBox::activate %d %p\n", __LINE__, this);
3986         if(!active)
3987         {
3988                 if(take_focus)
3989                 {
3990                         top_level->active_subwindow = this;
3991                         active = 1;
3992                 }
3993
3994                 button_releases = 0;
3995
3996 // Test for existence of GUI in case this was previously called without
3997 // take_focus & again with take_focus
3998                 if(is_popup && !gui)
3999                 {
4000                         Window tempwin;
4001                         int x, y;
4002                         int new_x, new_y;
4003                         y = get_y() + get_h();
4004                         if(justify == LISTBOX_RIGHT)
4005                         {
4006                                 x = get_x() - popup_w + get_w();
4007                         }
4008                         else
4009                         {
4010                                 x = get_x();
4011                         }
4012
4013
4014                         XTranslateCoordinates(top_level->display,
4015                                 parent_window->win,
4016                                 top_level->rootwin,
4017                                 x,
4018                                 y,
4019                                 &new_x,
4020                                 &new_y,
4021                                 &tempwin);
4022
4023                         if(new_x < 0) new_x = 0;
4024                         if(new_y + popup_h > top_level->get_root_h(0))
4025                                 new_y -= get_h() + popup_h;
4026
4027                         add_subwindow(gui = new BC_Popup(this,
4028                                 new_x,
4029                                 new_y,
4030                                 popup_w,
4031                                 popup_h,
4032                                 -1,
4033                                 0,
4034                                 0));
4035 // Avoid top going out of screen
4036                         if(new_y < 0 )
4037                                 new_y = 2;
4038 //printf("BC_ListBox::activate %d this=%p %p\n", __LINE__, this, gui->win);
4039                         draw_items(1);
4040                         gui->show_window(1);
4041                 }
4042 //printf("BC_ListBox::activate %d %p\n", __LINE__, this);
4043 //sleep(1);
4044         }
4045         return 0;
4046 }
4047
4048 int BC_ListBox::keypress_event()
4049 {
4050         if(!active) return 0;
4051
4052 //printf("BC_ListBox::keypress_event %d\n", __LINE__);
4053
4054         int result = 0, redraw = 0;
4055         int view_items = last_in_view - first_in_view + 1;
4056         if( view_items <= 0 ) view_items = view_h / get_text_height(MEDIUMFONT);
4057         int new_item = -1, new_selection = -1;
4058
4059         switch(top_level->get_keypress())
4060         {
4061                 case ESC:
4062                 case RETURN:
4063                 case BACKSPACE:
4064                         top_level->deactivate();
4065
4066 // If user is manipulating popup with keyboard, don't pass on event.
4067                         if(is_popup)
4068                         {
4069 //printf("BC_ListBox::keypress_event %d %p\n", __LINE__, get_selection(0, 0));
4070                                 if(top_level->get_keypress() == RETURN)
4071                                         handle_event();
4072 //printf("BC_ListBox::keypress_event %d %p\n", __LINE__, get_selection(0, 0));
4073                                 result = 1;
4074                         }
4075                         else
4076                                 result = 0;
4077                         break;
4078
4079                 case UP:
4080                         new_selection = new_item = select_previous(0);
4081
4082 //printf("BC_ListBox::keypress_event 1 %d\n", new_item);
4083                         if(new_item >= 0)
4084                         {
4085                                 center_selection(new_item);
4086                                 redraw = 1;
4087                         }
4088                         reset_query();
4089                         result = 1;
4090                         break;
4091
4092                 case DOWN:
4093                         new_selection = new_item = select_next(0);
4094
4095                         if(new_item >= 0)
4096                         {
4097                                 center_selection(new_item);
4098                                 redraw = 1;
4099                         }
4100                         reset_query();
4101                         result = 1;
4102                         break;
4103
4104                 case PGUP:
4105                         new_selection = new_item = select_previous(view_items - 1);
4106
4107                         if(new_item >= 0)
4108                         {
4109                                 center_selection(new_item);
4110                                 redraw = 1;
4111                         }
4112                         reset_query();
4113                         result = 1;
4114                         break;
4115
4116                 case PGDN:
4117                         new_selection = new_item = select_next(view_items - 1);
4118
4119                         if(new_item >= 0)
4120                         {
4121                                 center_selection(new_item);
4122                                 redraw = 1;
4123                         }
4124                         reset_query();
4125                         result = 1;
4126                         break;
4127
4128                 case LEFT:
4129                         xposition -= 10;
4130                         redraw = 1;
4131                         result = 1;
4132                         break;
4133
4134                 case RIGHT:
4135                         xposition += 10;
4136                         redraw = 1;
4137                         result = 1;
4138                         break;
4139
4140                 case DELETE:
4141                 case HOME:
4142                 case END:
4143                         result = 0;
4144                         break;
4145
4146                 default:
4147                         if(!ctrl_down())
4148                         {
4149                                 int query_len = strlen(query);
4150                                 if( query_len < (int)sizeof(query)-1 &&
4151                                         top_level->get_keypress() > 30 &&
4152                                         top_level->get_keypress() < 127)
4153                                 {
4154                                         query[query_len++] = top_level->get_keypress();
4155                                         query[query_len] = 0;
4156                                         new_selection = query_list();
4157                                 }
4158                                 else
4159                                 if(top_level->get_keypress() == BACKSPACE)
4160                                 {
4161                                         if(query_len > 0) query[--query_len] = 0;
4162                                         new_selection = query_list();
4163                                 }
4164
4165                                 redraw = 1;
4166                                 result = 1;
4167                         }
4168                         break;
4169         }
4170
4171         if(redraw)
4172         {
4173                 clamp_positions();
4174                 draw_items(0);
4175                 update_scrollbars(1);
4176         }
4177
4178 //printf("BC_ListBox::keypress_event %d new_selection=%d\n", __LINE__, new_selection);
4179         if(new_selection >= 0 && !is_suggestions)
4180         {
4181 //printf("BC_ListBox::keypress_event %d\n", __LINE__);
4182                 selection_changed();
4183 //printf("BC_ListBox::keypress_event %d\n", __LINE__);
4184         }
4185
4186         return result;
4187 }
4188
4189
4190 BC_Pixmap* BC_ListBox::get_bg_surface()
4191 {
4192         return bg_surface;
4193 }
4194
4195
4196 void BC_ListBox::draw_background()
4197 {
4198         if( !bg_draw ) return;
4199         bg_draw = 0;
4200 // White background pixmap
4201         set_color(top_level->get_resources()->listbox_inactive);
4202         draw_box(0, 0, bg_surface->get_w(), bg_surface->get_h(), bg_surface);
4203
4204 // Optional heroine pixmap
4205         if(bg_pixmap)
4206                 bg_surface->draw_pixmap(bg_pixmap,
4207                         bg_surface->get_w() - top_level->get_resources()->listbox_bg->get_w(),
4208                         0);
4209 }
4210
4211 void BC_ListBox::clear_listbox(int x, int y, int w, int h)
4212 {
4213         gui->draw_pixmap(bg_surface, x, y, w, h, x, y - title_h);
4214 }
4215
4216 void BC_ListBox::update_format(int display_format, int redraw)
4217 {
4218         this->display_format = display_format;
4219         if( redraw && gui ) draw_items(1, 1);
4220 }
4221
4222 int BC_ListBox::get_format()
4223 {
4224         return display_format;
4225 }
4226
4227
4228
4229 int BC_ListBox::draw_items(int flush, int draw_bg)
4230 {
4231         if(gui)
4232         {
4233                 BC_Resources *resources = get_resources();
4234
4235 //dump(data, columns);
4236
4237 // Calculate items width
4238                 calculate_item_coords();
4239
4240
4241 // Create and destroy scrollbars as needed
4242                 get_scrollbars();
4243
4244
4245
4246                 if( bg_draw ) this->bg_draw = 1;
4247                 draw_background();
4248
4249                 first_in_view = -1;
4250                 last_in_view = 0;
4251 // Icon display
4252                 if(display_format == LISTBOX_ICONS)
4253                 {
4254                         clear_listbox(2, 2 + title_h, view_w, view_h);
4255
4256                         set_font(MEDIUMFONT);
4257                         for(int i = 0; i < data[master_column].size(); i++)
4258                         {
4259                                 BC_ListBoxItem *item = data[master_column].get(i);
4260                                 if(get_item_x(item) >= -get_item_w(item) &&
4261                                         get_item_x(item) < view_w &&
4262                                         get_item_y(item) >= -get_item_h(item) + title_h &&
4263                                         get_item_y(item) < view_h + title_h)
4264                                 {
4265                                         item->set_in_view(1);
4266                                         if( first_in_view < 0 ) first_in_view = i;
4267                                         last_in_view = i;
4268                                         int item_color = get_item_highlight(data, 0, i);
4269                                         int icon_x, icon_y, icon_w, icon_h;
4270                                         int text_x, text_y, text_w, text_h;
4271
4272 // Draw highlights
4273                                         get_icon_mask(item, icon_x, icon_y, icon_w, icon_h);
4274                                         get_text_mask(item, text_x, text_y, text_w, text_h);
4275
4276
4277                                         if(item_color != resources->listbox_inactive)
4278                                         {
4279                                                 gui->set_color(BLACK);
4280                                                 gui->draw_rectangle(icon_x, icon_y, icon_w, icon_h);
4281                                                 gui->set_color(item_color);
4282                                                 gui->draw_box(icon_x + 1, icon_y + 1, icon_w - 2, icon_h - 2);
4283                                                 gui->set_color(BLACK);
4284                                                 gui->draw_rectangle(text_x, text_y, text_w, text_h);
4285                                                 gui->set_color(item_color);
4286                                                 gui->draw_box(text_x + 1, text_y + 1, text_w - 2, text_h - 2);
4287
4288                                                 if(icon_position == ICON_LEFT)
4289                                                         gui->draw_box(text_x - 1, text_y + 1, 2, text_h - 2);
4290                                                 else
4291                                                 if(icon_position == ICON_TOP)
4292                                                         gui->draw_line(text_x + 1, text_y, text_x + icon_w - 2, text_y);
4293                                                 if(text_x + text_w < icon_x + icon_w)
4294                                                 {
4295                                                         gui->set_color(BLACK);
4296                                                         gui->draw_line(text_x + text_w,
4297                                                                 icon_y + icon_h,
4298                                                                 icon_x + icon_w,
4299                                                                 icon_y + icon_h);
4300                                                 }
4301                                         }
4302
4303 // Draw icons
4304                                         gui->set_color(get_item_color(data, 0, i));
4305                                         if(item->icon)
4306                                                 gui->pixmap->draw_pixmap(item->icon,
4307                                                         icon_x + ICON_MARGIN,
4308                                                         icon_y + ICON_MARGIN);
4309
4310
4311                                         gui->draw_text(text_x + ICON_MARGIN,
4312                                                 text_y + ICON_MARGIN + get_baseline(item),
4313                                                 item->text);
4314                                 }
4315                                 else
4316                                         item->set_in_view(0);
4317                         }
4318                 }
4319                 else
4320 // Text display
4321                 {
4322 // Draw one column at a time so text overruns don't go into the next column
4323 // clear column backgrounds
4324                         int current_toggle = 0;
4325                         for(int j = 0; j < columns; j++)
4326                         {
4327                                 clear_listbox(LISTBOX_BORDER + get_column_offset(j) - xposition,
4328                                         LISTBOX_BORDER + title_h,
4329                                         get_column_width(j, 1),
4330                                         view_h);
4331 // Draw rows in the column recursively
4332                                 draw_text_recursive(data, j, 0, &current_toggle);
4333                         }
4334
4335 // Delete excess expanders
4336                         while(expanders.total > current_toggle)
4337                         {
4338                                 expanders.remove_object();
4339                         }
4340                 }
4341
4342 // draw user images if available
4343                 draw_images();
4344 // Draw titles on top of rows for superposition effect
4345                 draw_titles(0);
4346
4347 // Clear garbage from bottom right corner
4348                 if(xscrollbar && yscrollbar && is_popup)
4349                 {
4350                         gui->draw_top_background(parent_window,
4351                                 popup_w - get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w(),
4352                                 popup_h - get_resources()->hscroll_data[SCROLL_HANDLE_UP]->get_h(),
4353                                 get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w(),
4354                                 get_resources()->hscroll_data[SCROLL_HANDLE_UP]->get_h());
4355                 }
4356
4357 // Draw borders
4358                 draw_border(0);
4359
4360
4361                 if(current_operation == SELECT_RECT)
4362                         draw_rectangle(0);
4363
4364                 gui->flash(flush);
4365         }
4366
4367         return 0;
4368 }
4369
4370
4371 void BC_ListBox::draw_text_recursive(ArrayList<BC_ListBoxItem*> *data,
4372         int column,
4373         int indent,
4374         int *current_toggle)
4375 {
4376         if(!data) return;
4377
4378
4379         BC_Resources *resources = get_resources();
4380
4381         set_font(MEDIUMFONT);
4382         int subindent = 0;
4383
4384 // Search for a branch and make room for toggle if there is one
4385         if(column == 0)
4386         {
4387                 for(int i = 0; i < data[column].size(); i++)
4388                 {
4389                         if(data[column].get(i)->get_sublist())
4390                         {
4391                                 subindent = BC_WindowBase::get_resources()->listbox_expand[0]->get_w();
4392                                 break;
4393                         }
4394                 }
4395         }
4396
4397         row_height = row_ascent = row_descent = 0;
4398         for(int i = 0; i < data[column].total; i++)
4399         {
4400                 BC_ListBoxItem *item = data[column].values[i];
4401                 int ht = get_text_h(item);
4402                 if( ht > row_height ) row_height = ht;
4403                 int bl = get_baseline(item);
4404                 if( bl > row_ascent ) row_ascent = bl;
4405                 int dt = ht - bl;
4406                 if( dt > row_descent ) row_ascent = bl;
4407         }
4408
4409         for(int i = 0; i < data[column].size(); i++)
4410         {
4411 // Draw a row
4412                 BC_ListBoxItem *item = data[column].values[i];
4413                 BC_ListBoxItem *first_item = data[master_column].values[i];
4414
4415                 if(get_item_y(item) >= -get_item_h(item) + title_h &&
4416                         get_item_y(item) < view_h + title_h)
4417                 {
4418                         int row_color = get_item_highlight(data, 0, i);
4419                         int x, y, w, h, column_width;
4420
4421                         get_text_mask(item, x, y, w, h);
4422                         column_width = get_column_width(column, 1);
4423                         if(x + column_width > view_w + LISTBOX_BORDER * 2)
4424                                 column_width = view_w + LISTBOX_BORDER * 2 - x;
4425
4426                         if(row_color != resources->listbox_inactive)
4427                         {
4428                                 gui->set_color(row_color);
4429                                 gui->draw_box(x, y, column_width, h);
4430                                 gui->set_color(BLACK);
4431                                 int yy = y, xx = x + column_width-1;
4432                                 gui->draw_line(x, yy, xx, yy);
4433                                 yy = y + row_height;
4434                                 gui->draw_line(x, yy, xx, yy);
4435                         }
4436
4437                         gui->set_color(get_item_color(data, column, i));
4438
4439
4440                         if(column == 0 && display_format == LISTBOX_ICON_LIST)
4441                         {
4442                                 if(item->icon)
4443                                 {
4444                                         gui->pixmap->draw_pixmap(item->icon,
4445                                                 x,
4446                                                 y);
4447                                         x += item->icon->get_w() + ICON_MARGIN;
4448                                 }
4449                         }
4450
4451
4452 // Indent only applies to first column
4453                         gui->draw_text(
4454                                 x + LISTBOX_BORDER + LISTBOX_MARGIN +
4455                                         (column == 0 ? indent + subindent : 0),
4456                                 y + get_baseline(item), item->text);
4457                         item->set_in_view(1);
4458                         if( !indent ) {
4459                                 if( first_in_view < 0 ) first_in_view = i;
4460                                 last_in_view = i;
4461                         }
4462
4463 // Update expander
4464                         if(column == 0 &&
4465                                 item->get_sublist() &&
4466                                 item->get_columns())
4467                         {
4468 // Create new expander
4469                                 if(*current_toggle >= expanders.total)
4470                                 {
4471                                         BC_ListBoxToggle *toggle =
4472                                                 new BC_ListBoxToggle(this,
4473                                                         item,
4474                                                         x + LISTBOX_BORDER + LISTBOX_MARGIN + indent,
4475                                                         y);
4476                                         toggle->draw(0);
4477                                         expanders.append(toggle);
4478                                 }
4479                                 else
4480 // Reposition existing expander
4481                                 {
4482                                         BC_ListBoxToggle *toggle = expanders.values[*current_toggle];
4483 //printf("BC_ListBox::draw_text_recursive 1 %d\n", *current_toggle);
4484                                         toggle->update(item,
4485                                                 x + LISTBOX_BORDER + LISTBOX_MARGIN + indent,
4486                                                 y,
4487                                                 0);
4488                                 }
4489                                 (*current_toggle)++;
4490                         }
4491
4492
4493
4494                 }
4495                 else
4496                         item->set_in_view(0);
4497
4498 // Descend into sublist
4499                 if(first_item->get_expand())
4500                 {
4501                         draw_text_recursive(first_item->get_sublist(),
4502                                 column,
4503                                 indent + LISTBOX_INDENT,
4504                                 current_toggle);
4505                 }
4506         }
4507 }
4508
4509
4510
4511
4512 int BC_ListBox::draw_border(int flash)
4513 {
4514         BC_Resources *resources = top_level->get_resources();
4515         gui->draw_3d_border(0,
4516                 0,
4517                 view_w + LISTBOX_BORDER * 2,
4518                 view_h + title_h + LISTBOX_BORDER * 2,
4519                 resources->listbox_border1,
4520                 list_highlighted ?
4521                         resources->listbox_border2_hi :
4522                         resources->listbox_border2,
4523                 list_highlighted ?
4524                         resources->listbox_border3_hi :
4525                         resources->listbox_border3,
4526                 resources->listbox_border4);
4527
4528         if(flash)
4529         {
4530                 gui->flash();
4531                 gui->flush();
4532         }
4533         return 0;
4534 }
4535
4536 void BC_ListBox::draw_title(int number)
4537 {
4538 // Column title background
4539         int image_number = 0;
4540         if(number == highlighted_title)
4541         {
4542                 image_number = 1;
4543                 if(current_operation == COLUMN_DN)
4544                         image_number = 2;
4545         }
4546
4547         int column_offset = get_column_offset(number) - xposition + LISTBOX_BORDER;
4548         int column_width = get_column_width(number, 1);
4549         gui->draw_3segmenth(get_column_offset(number) - xposition + LISTBOX_BORDER,
4550                 LISTBOX_BORDER,
4551                 get_column_width(number, 1) + get_resources()->listbox_title_overlap,
4552                 column_bg[image_number]);
4553
4554 // Column title sort order
4555         if(number == sort_column)
4556         {
4557                 BC_Pixmap *src;
4558                 if(sort_order == SORT_ASCENDING)
4559                         src = column_sort_dn;
4560                 else
4561                         src = column_sort_up;
4562
4563                 int x = column_offset +
4564                         column_width -
4565                         LISTBOX_BORDER;
4566                 if(x > items_w) x = items_w;
4567                 x -= 5 + src->get_w();
4568                 gui->draw_pixmap(src,
4569                         x,
4570                         title_h / 2 - src->get_h() / 2 + LISTBOX_BORDER);
4571         }
4572
4573
4574         int x = -xposition +
4575                 get_column_offset(number) +
4576                 LISTBOX_MARGIN +
4577                 LISTBOX_BORDER;
4578         x += get_resources()->listbox_title_margin;
4579
4580         gui->set_color(get_resources()->listbox_title_color);
4581         gui->draw_text(x,
4582                 LISTBOX_MARGIN + LISTBOX_BORDER + get_text_ascent(MEDIUMFONT),
4583                 _(column_titles[number]));
4584 }
4585
4586 int BC_ListBox::draw_titles(int flash)
4587 {
4588         if(column_titles &&
4589                 (display_format == LISTBOX_TEXT ||
4590                 display_format == LISTBOX_ICON_LIST))
4591         {
4592 //printf("BC_ListBox::draw_titles 1 %d\n", highlighted_title);
4593                 for(int i = 0; i < columns; i++)
4594                 {
4595                         if(i != highlighted_title)
4596                                 draw_title(i);
4597                 }
4598
4599                 if(highlighted_title >= 0) draw_title(highlighted_title);
4600                 draw_border(0);
4601         }
4602
4603         if(flash)
4604         {
4605                 gui->flash();
4606         }
4607         return 0;
4608 }
4609
4610 void BC_ListBox::draw_toggles(int flash)
4611 {
4612         for(int i = 0; i < expanders.total; i++)
4613                 expanders.values[i]->draw(0);
4614
4615 //printf("BC_ListBox::draw_toggles 1 %d\n", flash);
4616         if(flash && expanders.total)
4617         {
4618                 gui->flash();
4619                 gui->flush();
4620         }
4621 }
4622
4623 int BC_ListBox::draw_rectangle(int flash)
4624 {
4625         int x1 = MIN(rect_x1, rect_x2);
4626         int x2 = MAX(rect_x1, rect_x2);
4627         int y1 = MIN(rect_y1, rect_y2);
4628         int y2 = MAX(rect_y1, rect_y2);
4629
4630         if(x1 == x2 || y1 == y2) return 0;
4631
4632         gui->set_inverse();
4633         gui->set_color(WHITE);
4634         gui->draw_rectangle(x1, y1, x2 - x1, y2 - y1);
4635         gui->set_opaque();
4636
4637
4638         if(flash)
4639         {
4640                 gui->flash(1);
4641         }
4642         return 0;
4643 }
4644
4645 void BC_ListBox::dump(ArrayList<BC_ListBoxItem*> *data,
4646         int columns,
4647         int indent,
4648         int master_column)
4649 {
4650         if(!indent)
4651         {
4652                 printf("BC_ListBox::dump 1\n");
4653         }
4654
4655         for(int i = 0; i < data[master_column].total; i++)
4656         {
4657                 for(int k = 0; k < indent; k++)
4658                         printf(" ");
4659                 for(int j = 0; j < columns; j++)
4660                 {
4661                         BC_ListBoxItem *item = data[j].values[i];
4662                         printf("%d,%d,%d=%s ",
4663                                 item->get_text_x(),
4664                                 item->get_text_y(),
4665                                 item->autoplace_text,
4666                                 item->get_text());
4667                 }
4668                 printf("\n");
4669
4670                 if(data[master_column].values[i]->get_sublist())
4671                 {
4672                         dump(data[master_column].values[i]->get_sublist(),
4673                                 data[master_column].values[i]->get_columns(),
4674                                 indent + 4,
4675                                 master_column);
4676                 }
4677         }
4678
4679
4680 }
4681
4682
4683
4684