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