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