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