asset menu size fixups, new picons+prefs, more timecode del, stretch scrollbars,...
[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         items_h = get_items_height(data, columns);
1795
1796         if(yposition < 0) yposition = 0;
1797         else
1798         if(yposition > items_h - view_h)
1799                 yposition = items_h - view_h;
1800
1801         if(yposition < 0) yposition = 0;
1802
1803         if(xposition < 0) xposition = 0;
1804         else
1805         if(xposition >= items_w - view_w)
1806                 xposition = items_w - view_w;
1807
1808         if(xposition < 0) xposition = 0;
1809 }
1810
1811 int BC_ListBox::center_selection(int selection,
1812         ArrayList<BC_ListBoxItem*> *data,
1813         int *counter)
1814 {
1815         int temp = -1;
1816         if(!data) data = this->data;
1817         if(!counter) counter = &temp;
1818
1819         for(int i = 0; i < data[master_column].total; i++)
1820         {
1821                 (*counter)++;
1822
1823 // Got it
1824                 BC_ListBoxItem *item = data[master_column].values[i];
1825                 if((*counter) == selection)
1826                 {
1827                         BC_ListBoxItem *top_item = this->data[master_column].values[0];
1828
1829
1830                         if(display_format == LISTBOX_ICONS)
1831                         {
1832 // Icon is out of window
1833                                 if( item->icon_y-yposition  > view_h-get_text_h(item) ||
1834                                         item->icon_y-yposition < 0 ) {
1835                                         yposition = item->icon_y - view_h / 2;
1836                                 }
1837
1838                                 if(data[master_column].values[selection]->icon_x - xposition > view_w ||
1839                                         data[master_column].values[selection]->icon_x - xposition < 0)
1840                                 {
1841                                         xposition = item->icon_x - view_w / 2;
1842                                 }
1843                         }
1844                         else
1845                         {
1846 // Text coordinate is out of window
1847                                 if( item->text_y-yposition  > view_h-get_text_h(item) ||
1848                                         item->text_y-yposition < 0 ) {
1849                                         yposition = item->text_y -
1850                                                 top_item->text_y -
1851                                                 view_h / 2;
1852                                 }
1853                         }
1854                         return 1;
1855                 }
1856
1857 // Descend
1858                 if(item->get_sublist())
1859                 {
1860                         int result = center_selection(selection,
1861                                 item->get_sublist(),
1862                                 counter);
1863                         if(result) return result;
1864                 }
1865         }
1866         return 0;
1867 }
1868
1869 void BC_ListBox::update_scrollbars(int flush)
1870 {
1871         int h_needed = items_h = get_items_height(data, columns);
1872         int w_needed = items_w = get_items_width();
1873
1874 // if(columns > 0 && column_width)
1875 // printf("BC_ListBox::update_scrollbars 1 %d %d\n", column_width[columns - 1], w_needed);
1876
1877         if(xscrollbar)
1878         {
1879                 if(xposition != xscrollbar->get_value())
1880                         xscrollbar->update_value(xposition);
1881
1882                 if(w_needed != xscrollbar->get_length() ||
1883                         view_w != xscrollbar->get_handlelength())
1884                         xscrollbar->update_length(w_needed, xposition, view_w, 0);
1885         }
1886
1887         if(yscrollbar)
1888         {
1889                 if(yposition != yscrollbar->get_value())
1890                         yscrollbar->update_value(yposition);
1891
1892                 if(h_needed != yscrollbar->get_length() || view_h != yscrollbar->get_handlelength())
1893                         yscrollbar->update_length(h_needed, yposition, view_h, 0);
1894         }
1895
1896         if(flush) this->flush();
1897 }
1898
1899 int BC_ListBox::get_scrollbars()
1900 {
1901         int h_needed = items_h = get_items_height(data, columns);
1902         int w_needed = items_w = get_items_width();
1903         int flush = 0;
1904
1905
1906         title_h = get_title_h();
1907
1908         view_h = popup_h - title_h - 4;
1909         view_w = popup_w - 4;
1910
1911 // Create scrollbars as needed
1912         for(int i = 0; i < 2; i++)
1913         {
1914                 if(w_needed > view_w)
1915                 {
1916                         need_xscroll = 1;
1917                         view_h = popup_h -
1918                                 title_h -
1919                                 get_resources()->hscroll_data[SCROLL_HANDLE_UP]->get_h() -
1920                                 4;
1921                 }
1922                 else
1923                 {
1924                         need_xscroll = 0;
1925                 }
1926
1927                 if(h_needed > view_h)
1928                 {
1929                         need_yscroll = 1;
1930                         view_w = popup_w -
1931                                 get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w() -
1932                                 4;
1933                 }
1934                 else
1935                 {
1936                         need_yscroll = 0;
1937                 }
1938         }
1939
1940 // Update subwindow size
1941         int new_w = popup_w;
1942         int new_h = popup_h;
1943         if(need_xscroll) new_h -= get_resources()->hscroll_data[SCROLL_HANDLE_UP]->get_h();
1944         if(need_yscroll) new_w -= get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w();
1945
1946         if(!is_popup)
1947                 if(new_w != BC_WindowBase::get_w() || new_h != BC_WindowBase::get_h())
1948                         gui->resize_window(new_w, new_h);
1949
1950         BC_WindowBase *destination = (is_popup ? gui : parent_window);
1951         if(need_xscroll)
1952         {
1953                 if(!xscrollbar)
1954                 {
1955                         xscrollbar = new BC_ListBoxXScroll(this);
1956                         destination->add_subwindow(xscrollbar);
1957                         xscrollbar->show_window(0);
1958                         xscrollbar->bound_to = this;
1959                 }
1960                 else
1961                 {
1962                     xscrollbar->update_length(w_needed, xposition, view_w, flush);
1963                         xscrollbar->reposition_window(get_xscroll_x(),
1964                                 get_xscroll_y(),
1965                                 get_xscroll_width());
1966                 }
1967         }
1968         else
1969         {
1970                 if(xscrollbar) delete xscrollbar;
1971                 xscrollbar = 0;
1972                 xposition = 0;
1973         }
1974
1975         if(need_yscroll)
1976         {
1977                 if(!yscrollbar)
1978                 {
1979                         yscrollbar = new BC_ListBoxYScroll(this);
1980                         destination->add_subwindow(yscrollbar);
1981                         yscrollbar->show_window(0);
1982                         yscrollbar->bound_to = this;
1983                 }
1984                 else
1985                 {
1986                         yscrollbar->update_length(h_needed, yposition, view_h, flush);
1987                         yscrollbar->reposition_window(get_yscroll_x(),
1988                                 get_yscroll_y(),
1989                                 get_yscroll_height());
1990                 }
1991         }
1992         else
1993         {
1994                 if(yscrollbar) delete yscrollbar;
1995                 yscrollbar = 0;
1996                 yposition = 0;
1997         }
1998
1999         if(!bg_surface ||
2000                 view_w + 4 != bg_surface->get_w() ||
2001                 view_h + 4 != bg_surface->get_h())
2002         {
2003                 if(bg_surface) delete bg_surface;
2004                 bg_surface = new BC_Pixmap(gui, view_w + 4, view_h + 4);
2005                 bg_draw = 1;
2006         }
2007
2008
2009         return 0;
2010 }
2011
2012
2013
2014 void BC_ListBox::set_drag_scroll(int value)
2015 {
2016         allow_drag_scroll = value;
2017 }
2018
2019
2020 // Test for scrolling by dragging
2021
2022 int BC_ListBox::test_drag_scroll(int cursor_x, int cursor_y)
2023 {
2024         int result = 0;
2025         if(allow_drag_scroll ||
2026                 current_operation == SELECT_RECT)
2027         {
2028
2029                 int top_boundary = get_title_h();
2030
2031                 if(cursor_y < top_boundary ||
2032                         cursor_y >= view_h + title_h + LISTBOX_BORDER * 2 ||
2033                         cursor_x < LISTBOX_BORDER ||
2034                         cursor_x >= view_w + LISTBOX_BORDER)
2035                 {
2036                         result = 1;
2037                 }
2038         }
2039         return result;
2040 }
2041
2042 int BC_ListBox::drag_scroll_event()
2043 {
2044         int top_boundary = get_title_h();
2045         int result = 0;
2046
2047         if(get_cursor_y() < top_boundary)
2048         {
2049                 yposition -= top_boundary - get_cursor_y();
2050                 result = 1;
2051         }
2052         else
2053         if(get_cursor_y() >= view_h + title_h + 4)
2054         {
2055                 yposition += get_cursor_y() - (view_h + title_h + 4);
2056                 result = 1;
2057         }
2058
2059         if(get_cursor_x() < 2)
2060         {
2061                 xposition -= 2 - get_cursor_x();
2062                 result = 1;
2063         }
2064         else
2065         if(get_cursor_x() >= view_w + 2)
2066         {
2067                 xposition += get_cursor_x() - (view_w + 2);
2068                 result = 1;
2069         }
2070         if(result) clamp_positions();
2071         return result;
2072 }
2073
2074 int BC_ListBox::rectangle_scroll_event()
2075 {
2076         int old_xposition = xposition;
2077         int old_yposition = yposition;
2078         int result = drag_scroll_event();
2079
2080         if(result)
2081         {
2082                 rect_x1 += old_xposition - xposition;
2083                 rect_y1 += old_yposition - yposition;
2084                 rect_x2 = get_cursor_x();
2085                 rect_y2 = get_cursor_y();
2086
2087                 int x1 = MIN(rect_x1, rect_x2);
2088                 int x2 = MAX(rect_x1, rect_x2);
2089                 int y1 = MIN(rect_y1, rect_y2);
2090                 int y2 = MAX(rect_y1, rect_y2);
2091
2092                 if(select_rectangle(data,
2093                         x1,
2094                         y1,
2095                         x2,
2096                         y2))
2097                 {
2098                         selection_changed();
2099                 }
2100
2101                 clamp_positions();
2102                 draw_items(0);
2103                 update_scrollbars(1);
2104         }
2105         return result;
2106 }
2107
2108 int BC_ListBox::select_scroll_event()
2109 {
2110         int result = drag_scroll_event();
2111
2112         if(result)
2113         {
2114                 highlighted_item = selection_number = get_cursor_item(data,
2115                         get_cursor_x(),
2116                         get_cursor_y(),
2117                         &highlighted_ptr);
2118                 clamp_positions();
2119                 draw_items(0);
2120                 update_scrollbars(1);
2121                 selection_changed();
2122         }
2123         return result;
2124 }
2125
2126 int BC_ListBox::select_rectangle(ArrayList<BC_ListBoxItem*> *data,
2127                 int x1,
2128                 int y1,
2129                 int x2,
2130                 int y2)
2131 {
2132         int result = 0;
2133         for(int i = 0; i < data[master_column].total; i++)
2134         {
2135                 for(int j = 0; j < columns; j++)
2136                 {
2137                         BC_ListBoxItem *item = data[j].values[i];
2138                         if(display_format == LISTBOX_ICONS)
2139                         {
2140                                 int icon_x, icon_y, icon_w, icon_h;
2141                                 int text_x, text_y, text_w, text_h;
2142                                 get_icon_mask(item, icon_x, icon_y, icon_w, icon_h);
2143                                 get_text_mask(item, text_x, text_y, text_w, text_h);
2144
2145                                 if((x2 >= icon_x && x1 < icon_x + icon_w &&
2146                                         y2 >= icon_y && y1 < icon_y + icon_h) ||
2147                                         (x2 >= text_x && x1 < text_x + text_w &&
2148                                         y2 >= text_y && y1 < text_y + text_h))
2149                                 {
2150                                         if(!item->selected)
2151                                         {
2152                                                 item->selected = 1;
2153                                                 result = 1;
2154                                         }
2155                                 }
2156                                 else
2157                                 {
2158                                         if(item->selected)
2159                                         {
2160                                                 item->selected = 0;
2161                                                 result = 1;
2162                                         }
2163                                 }
2164                         }
2165                         else
2166                         {
2167                                 if(x2 >= 0 &&
2168                                         x1 < (yscrollbar ?
2169                                                 gui->get_w() - get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w() :
2170                                                 gui->get_w()) &&
2171                                         y2 > 0 &&
2172                                         y1 < gui->get_h() &&
2173                                         y2 >= get_item_y(item) &&
2174                                         y1 < get_item_y(item) + get_item_h(item))
2175                                 {
2176                                         if(!item->selected)
2177                                         {
2178                                                 item->selected = 1;
2179                                                 result = 1;
2180                                         }
2181                                 }
2182                                 else
2183                                 {
2184                                         if(item->selected)
2185                                         {
2186                                                 item->selected = 0;
2187                                                 result = 1;
2188                                         }
2189                                 }
2190                         }
2191                 }
2192
2193                 BC_ListBoxItem *item = data[master_column].values[i];
2194                 if(item->get_sublist() &&
2195                         item->get_expand())
2196                         result |= select_rectangle(item->get_sublist(),
2197                                 x1,
2198                                 y1,
2199                                 x2,
2200                                 y2);
2201         }
2202         return result;
2203 }
2204
2205 int BC_ListBox::reposition_item(ArrayList<BC_ListBoxItem*> *data,
2206                 int selection_number,
2207                 int x,
2208                 int y,
2209                 int *counter)
2210 {
2211         int temp = -1;
2212         if(!counter) counter = &temp;
2213
2214
2215         for(int i = 0; i < data[master_column].total; i++)
2216         {
2217                 BC_ListBoxItem *item = data[master_column].values[i];
2218                 (*counter)++;
2219                 if((*counter) == selection_number)
2220                 {
2221                         item->icon_x = x;
2222                         item->icon_y = y;
2223                         return 1;
2224                 }
2225 // Not recursive because it's only used for icons
2226         }
2227         return 0;
2228 }
2229
2230 void BC_ListBox::move_selection(ArrayList<BC_ListBoxItem*> *dst,
2231         ArrayList<BC_ListBoxItem*> *src)
2232 {
2233         for(int i = 0; i < src[master_column].total; i++)
2234         {
2235                 BC_ListBoxItem *item = src[master_column].values[i];
2236
2237 // Move item to dst
2238                 if(item->selected)
2239                 {
2240                         for(int j = 0; j < columns; j++)
2241                         {
2242                                 dst[j].append(src[j].values[i]);
2243                                 src[j].remove_number(i);
2244                         }
2245                 }
2246                 else
2247 // Descend into sublist
2248                 if(item->get_sublist())
2249                 {
2250                         move_selection(dst,
2251                                 item->get_sublist());
2252                 }
2253         }
2254 }
2255
2256 int BC_ListBox::put_selection(ArrayList<BC_ListBoxItem*> *data,
2257         ArrayList<BC_ListBoxItem*> *src,
2258         int destination,
2259         int *counter)
2260 {
2261         int temp = -1;
2262         if(!counter) counter = &temp;
2263
2264         if(destination < 0)
2265         {
2266                 for(int j = 0; j < columns; j++)
2267                 {
2268                         for(int i = 0; i < src[j].total; i++)
2269                         {
2270                                 data[j].append(src[j].values[i]);
2271                         }
2272                 }
2273                 return 1;
2274         }
2275         else
2276         for(int i = 0; i < data[master_column].total; i++)
2277         {
2278                 (*counter)++;
2279                 if((*counter) == destination)
2280                 {
2281                         for(int j = 0; j < columns; j++)
2282                         {
2283                                 for(int k = 0; k < src[j].total; k++)
2284                                 {
2285                                         data[j].insert(src[j].values[k], destination + k);
2286                                 }
2287                         }
2288                         return 1;
2289                 }
2290
2291                 BC_ListBoxItem *item = data[master_column].values[i];
2292                 if(item->get_sublist())
2293                 {
2294                         if(put_selection(item->get_sublist(),
2295                                 src,
2296                                 destination,
2297                                 counter))
2298                                 return 1;
2299                 }
2300         }
2301         return 0;
2302 }
2303
2304
2305
2306 int BC_ListBox::item_to_index(ArrayList<BC_ListBoxItem*> *data,
2307                 BC_ListBoxItem *item,
2308                 int *counter)
2309 {
2310         int temp = -1;
2311         if(!counter) counter = &temp;
2312
2313         for(int i = 0; i < data[master_column].total; i++)
2314         {
2315                 (*counter)++;
2316                 for(int j = 0; j < columns; j++)
2317                 {
2318                         BC_ListBoxItem *new_item = data[j].values[i];
2319 //printf("BC_ListBox::item_to_index 1 %d %d %p\n", j, i, new_item);
2320                         if(new_item == item)
2321                         {
2322                                 return (*counter);
2323                         }
2324                 }
2325
2326                 BC_ListBoxItem *new_item = data[master_column].values[i];
2327                 if(new_item->get_sublist())
2328                 {
2329                         if(item_to_index(new_item->get_sublist(),
2330                                 item,
2331                                 counter) >= 0)
2332                                 return (*counter);
2333                 }
2334         }
2335
2336         return -1;
2337 }
2338
2339 BC_ListBoxItem* BC_ListBox::index_to_item(ArrayList<BC_ListBoxItem*> *data,
2340                 int number,
2341                 int column,
2342                 int *counter)
2343 {
2344         int temp = -1;
2345         if(!counter) counter = &temp;
2346         for(int i = 0; i < data[master_column].total; i++)
2347         {
2348                 (*counter)++;
2349                 if((*counter) == number)
2350                 {
2351                         return data[column].values[i];
2352                 }
2353                 BC_ListBoxItem *item = data[master_column].values[i];
2354                 if(item->get_sublist())
2355                 {
2356                         BC_ListBoxItem *result = index_to_item(item->get_sublist(),
2357                                 number,
2358                                 column,
2359                                 counter);
2360                         if(result) return result;
2361                 }
2362         }
2363         return 0;
2364 }
2365
2366 int BC_ListBox::get_cursor_item(ArrayList<BC_ListBoxItem*> *data,
2367         int cursor_x,
2368         int cursor_y,
2369         BC_ListBoxItem **item_return,
2370         int *counter,
2371         int expanded)
2372 {
2373         int temp = -1;
2374         if(!data) return -1;
2375         if(!counter) counter = &temp;
2376
2377 // Icons are not treed
2378         if(display_format == LISTBOX_ICONS)
2379         {
2380                 for(int j = data[master_column].total - 1; j >= 0; j--)
2381                 {
2382                         int icon_x, icon_y, icon_w, icon_h;
2383                         int text_x, text_y, text_w, text_h;
2384                         BC_ListBoxItem *item = data[master_column].values[j];
2385                         get_icon_mask(item, icon_x, icon_y, icon_w, icon_h);
2386                         get_text_mask(item, text_x, text_y, text_w, text_h);
2387
2388                         if((cursor_x >= icon_x && cursor_x < icon_x + icon_w &&
2389                                 cursor_y >= icon_y && cursor_y < icon_y + icon_h) ||
2390                                 (cursor_x >= text_x && cursor_x < text_x + text_w &&
2391                                 cursor_y >= text_y && cursor_y < text_y + text_h))
2392                         {
2393                                 if(item_return) (*item_return) = item;
2394                                 return j;
2395                         }
2396                 }
2397         }
2398         else if( gui ) {
2399 // Text is treed
2400 // Cursor is inside items rectangle
2401                 if(cursor_x >= 0 &&
2402                         cursor_x < (yscrollbar ?
2403                                 gui->get_w() - get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w() :
2404                                 gui->get_w()) &&
2405 // Only clamp y if we're not in a SELECT operation.
2406                         (current_operation == BC_ListBox::SELECT ||
2407                                 (cursor_y > get_title_h() + LISTBOX_BORDER &&
2408                                 cursor_y < gui->get_h())))
2409                 {
2410 // Search table for cursor obstruction
2411                         for(int i = 0; i < data[master_column].total; i++)
2412                         {
2413                                 BC_ListBoxItem *item = data[master_column].values[i];
2414                                 (*counter)++;
2415
2416 // Cursor is inside item on current level
2417                                 if(expanded &&
2418                                         item->selectable &&
2419                                         cursor_y >= get_item_y(item) &&
2420                                         cursor_y < get_item_y(item) + get_item_h(item))
2421                                 {
2422 //printf("BC_ListBox::get_cursor_item %d %d %p\n", master_column, i, item);
2423                                         if(item_return) (*item_return) = item;
2424                                         return (*counter);
2425                                 }
2426
2427 // Descend into sublist
2428                                 if(item->get_sublist())
2429                                 {
2430                                         if(get_cursor_item(item->get_sublist(),
2431                                                 cursor_x,
2432                                                 cursor_y,
2433                                                 item_return,
2434                                                 counter,
2435                                                 item->get_expand()) >= 0)
2436                                                 return (*counter);
2437                                 }
2438                         }
2439                 }
2440         }
2441         return -1;
2442 }
2443
2444 int BC_ListBox::repeat_event(int64_t duration)
2445 {
2446         switch(current_operation)
2447         {
2448 // Repeat out of bounds selection
2449                 case SELECT_RECT:
2450                         if(duration == get_resources()->scroll_repeat)
2451                                 return rectangle_scroll_event();
2452                         break;
2453
2454                 case SELECT:
2455                         if(duration == get_resources()->scroll_repeat)
2456                                 return select_scroll_event();
2457                         break;
2458
2459                 case NO_OPERATION:
2460 // Show tooltip
2461                         if(button_highlighted && is_popup &&
2462                                 tooltip_text && tooltip_text[0] != 0 &&
2463                                 duration == get_resources()->tooltip_delay)
2464                         {
2465                                 show_tooltip();
2466                                 return 1;
2467                         }
2468                         break;
2469         }
2470         return 0;
2471 }
2472
2473
2474 int BC_ListBox::cursor_enter_event()
2475 {
2476         int result = 0;
2477
2478         switch(current_operation)
2479         {
2480 // Cursor moved over button, pressed, and exited.
2481                 case BUTTON_DOWN_SELECT:
2482                         if(top_level->event_win == win)
2483                         {
2484                                 current_operation = BUTTON_DN;
2485                                 result = 1;
2486                                 button_highlighted = 1;
2487                                 draw_button(1);
2488                         }
2489                         break;
2490
2491                 case NO_OPERATION:
2492 // Cursor entered button
2493                         if(is_popup && top_level->event_win == win)
2494                         {
2495                                 button_highlighted = 1;
2496                                 result = 1;
2497                                 draw_button(1);
2498                         }
2499                         else
2500 // TODO: Need to get the highlighted column title or item
2501                         if(gui && top_level->event_win == gui->win)
2502                         {
2503                                 list_highlighted = 1;
2504                                 draw_border(1);
2505                                 result = 1;
2506                         }
2507                         break;
2508         }
2509
2510         return result;
2511 }
2512
2513 int BC_ListBox::cursor_leave_event()
2514 {
2515         if(current_operation == COLUMN_DRAG) return 0;
2516
2517 // Left button area
2518         if(button_highlighted)
2519         {
2520                 button_highlighted = 0;
2521                 hide_tooltip();
2522                 draw_button(1);
2523         }
2524
2525         if(list_highlighted)
2526         {
2527                 list_highlighted = 0;
2528                 highlighted_item = -1;
2529                 highlighted_ptr = 0;
2530                 highlighted_title = -1;
2531                 int redraw_toggles = 0;
2532                 for(int i = 0; i < expanders.total; i++)
2533                         expanders.values[i]->cursor_leave_event(&redraw_toggles);
2534
2535                 draw_items(1);
2536         }
2537
2538         return 0;
2539 }
2540
2541 int BC_ListBox::get_first_selection(ArrayList<BC_ListBoxItem*> *data, int *result)
2542 {
2543         int temp = -1;
2544         if(!result) result = &temp;
2545
2546         for(int i = 0; i < data[master_column].total; i++)
2547         {
2548                 BC_ListBoxItem *item = data[master_column].values[i];
2549                 (*result)++;
2550                 if(item->selected) return (*result);
2551                 if(item->get_sublist())
2552                 {
2553                         if(get_first_selection(item->get_sublist(), result) >= 0)
2554                                 return (*result);
2555                 }
2556         }
2557         return -1;
2558 }
2559
2560 int BC_ListBox::get_total_items(ArrayList<BC_ListBoxItem*> *data,
2561         int *result,
2562         int master_column)
2563 {
2564         int temp = 0;
2565         if(!result) result = &temp;
2566
2567         for(int i = 0; i < data[master_column].total; i++)
2568         {
2569                 (*result)++;
2570                 if(data[master_column].values[i]->get_sublist())
2571                         get_total_items(data[master_column].values[i]->get_sublist(),
2572                                 result,
2573                                 master_column);
2574         }
2575
2576         return (*result);
2577 }
2578
2579
2580 int BC_ListBox::get_last_selection(ArrayList<BC_ListBoxItem*> *data,
2581         int *result)
2582 {
2583         int temp = -1;
2584         int top_level = 0;
2585         if(!result)
2586         {
2587                 result = &temp;
2588                 top_level = 1;
2589         }
2590
2591         for(int i = data[master_column].total - 1; i >= 0; i--)
2592         {
2593                 BC_ListBoxItem *item = data[master_column].values[i];
2594                 (*result)++;
2595                 if(item->selected)
2596                 {
2597                         if(top_level)
2598                                 return get_total_items(data, 0, master_column) - (*result) /* - 1 */;
2599                         else
2600                                 return (*result);
2601                 }
2602
2603                 if(item->get_sublist())
2604                 {
2605                         if(get_last_selection(item->get_sublist(), result) >= 0)
2606                         {
2607                                 if(top_level)
2608                                         return get_total_items(data, 0, master_column) - (*result) /* - 1 */;
2609                                 else
2610                                         return (*result);
2611                         }
2612                 }
2613         }
2614         return -1;
2615 }
2616
2617 void BC_ListBox::select_range(ArrayList<BC_ListBoxItem*> *data,
2618                 int start,
2619                 int end,
2620                 int *current)
2621 {
2622         int temp = -1;
2623         if(!current) current = &temp;
2624
2625         for(int i = 0; i < data[master_column].total; i++)
2626         {
2627                 (*current)++;
2628                 if((*current) >= start && (*current) < end)
2629                 {
2630                         for(int j = 0; j < columns; j++)
2631                                 data[j].values[i]->selected = 1;
2632                 }
2633                 BC_ListBoxItem *item = data[master_column].values[i];
2634                 if(item->get_sublist())
2635                         select_range(item->get_sublist(),
2636                                 start,
2637                                 end,
2638                                 current);
2639         }
2640 }
2641
2642
2643 // Fill items between current selection and new selection
2644 int BC_ListBox::expand_selection(int button_press, int selection_number)
2645 {
2646         int old_selection_start = selection_start;
2647         int old_selection_end = selection_end;
2648
2649 // printf("BC_ListBox::expand_selection %d %d\n",
2650 // selection_center,
2651 // selection_number);
2652
2653 // Calculate the range to select based on selection_center and selection_number
2654         if(selection_number < selection_center)
2655         {
2656                 selection_start = selection_number;
2657         }
2658         else
2659         {
2660                 selection_end = selection_number + 1;
2661         }
2662
2663 //printf("BC_ListBox::expand_selection %d %d %d %d\n", old_selection_start, old_selection_end, selection_start, selection_end);
2664 // Recurse through all the items and select the desired range
2665         select_range(data, selection_start, selection_end);
2666
2667 // Trigger redraw
2668         return (old_selection_start != selection_start ||
2669                 old_selection_end != selection_end);
2670 }
2671
2672 int BC_ListBox::toggle_item_selection(ArrayList<BC_ListBoxItem*> *data,
2673         int selection_number,
2674         int *counter)
2675 {
2676         int temp = -1;
2677         if(!counter) counter = &temp;
2678
2679         for(int i = 0; i < data[master_column].total; i++)
2680         {
2681                 BC_ListBoxItem *item = data[master_column].values[i];
2682                 (*counter)++;
2683                 if((*counter) == selection_number)
2684                 {
2685 // Get new value for selection
2686                         int selected = !item->selected;
2687 // Set row
2688                         for(int j = 0; j < columns; j++)
2689                                 data[j].values[i]->selected = selected;
2690                         return 1;
2691                 }
2692
2693 // Descend into sublist
2694                 if(item->get_sublist())
2695                 {
2696                         if(toggle_item_selection(item->get_sublist(),
2697                                 selection_number,
2698                                 counter))
2699                                 return 1;
2700                 }
2701         }
2702
2703         return 0;
2704 }
2705
2706
2707 void BC_ListBox::set_all_selected(ArrayList<BC_ListBoxItem*> *data, int value)
2708 {
2709         for(int i = 0; i < data[master_column].total; i++)
2710         {
2711                 for(int j = 0; j < columns; j++)
2712                 {
2713                         BC_ListBoxItem *item = data[j].values[i];
2714                         item->selected = value;
2715                 }
2716                 BC_ListBoxItem *item = data[master_column].values[i];
2717                 if(item->get_sublist())
2718                 {
2719                         set_all_selected(item->get_sublist(), value);
2720                 }
2721         }
2722 }
2723
2724 void BC_ListBox::set_selected(ArrayList<BC_ListBoxItem*> *data,
2725                 int item_number,
2726                 int value,
2727                 int *counter)
2728 {
2729         int temp = -1;
2730         if(!counter) counter = &temp;
2731         for(int i = 0; i < data[master_column].total && (*counter) != item_number; i++)
2732         {
2733                 (*counter)++;
2734                 if((*counter) == item_number)
2735                 {
2736                         for(int j = 0; j < columns; j++)
2737                         {
2738                                 BC_ListBoxItem *item = data[j].values[i];
2739                                 item->selected = value;
2740                         }
2741                         return;
2742                 }
2743
2744                 BC_ListBoxItem *item = data[master_column].values[i];
2745                 if(item->get_sublist())
2746                 {
2747                         set_selected(item->get_sublist(),
2748                                 item_number,
2749                                 value,
2750                                 counter);
2751                 }
2752         }
2753 }
2754
2755 int BC_ListBox::update_selection(ArrayList<BC_ListBoxItem*> *data,
2756         int selection_number,
2757         int *counter)
2758 {
2759         int temp = -1;
2760         int result = 0;
2761         if(!counter) counter = &temp;
2762
2763         for(int i = 0; i < data[master_column].total; i++)
2764         {
2765                 BC_ListBoxItem *item = data[master_column].values[i];
2766                 (*counter)++;
2767                 if((*counter) == selection_number && !item->selected)
2768                 {
2769                         result = 1;
2770                         for(int j = 0; j < columns; j++)
2771                                 data[j].values[i]->selected = 1;
2772                 }
2773                 else
2774                 if((*counter) != selection_number && item->selected)
2775                 {
2776                         result = 1;
2777                         for(int j = 0; j < columns; j++)
2778                                 data[j].values[i]->selected = 0;
2779                 }
2780                 if(item->get_sublist())
2781                         result |= update_selection(item->get_sublist(),
2782                                 selection_number,
2783                                 counter);
2784         }
2785         return result;
2786 }
2787
2788 void BC_ListBox::promote_selections(ArrayList<BC_ListBoxItem*> *data,
2789         int old_value,
2790         int new_value)
2791 {
2792         for(int i = 0; i < data[master_column].total; i++)
2793         {
2794                 for(int j = 0; j < columns; j++)
2795                 {
2796                         BC_ListBoxItem *item = data[j].values[i];
2797                         if(item->selected == old_value) item->selected = new_value;
2798                 }
2799                 BC_ListBoxItem *item = data[master_column].values[i];
2800                 if(item->get_sublist())
2801                         promote_selections(item->get_sublist(), old_value, new_value);
2802         }
2803 }
2804
2805 int BC_ListBox::focus_out_event()
2806 {
2807         deactivate();
2808         return 0;
2809 }
2810
2811 int BC_ListBox::button_press_event()
2812 {
2813         int result = 0;
2814         BC_ListBoxItem *current_item = 0;
2815         int new_cursor;
2816         int do_selection_change = 0;
2817         const int debug = 0;
2818
2819         hide_tooltip();
2820         if(debug) printf("BC_ListBox::button_press_event %d this=%p event_win=%p %p %p %p\n",
2821                 __LINE__,
2822                 this,
2823                 (void*)top_level->event_win,
2824                 (void*)(gui ? gui->win : 0),
2825                 (void*)win,
2826                 (void*)parent_window->win);
2827
2828 // Pressed in button
2829         if(is_popup && top_level->event_win == win)
2830         {
2831                 if(debug) printf("BC_ListBox::button_press_event %d\n", __LINE__);
2832                 current_operation = BUTTON_DN;
2833                 draw_button(1);
2834
2835 // Deploy listbox
2836                 if(!active && !disabled)
2837                 {
2838                         top_level->deactivate();
2839                         activate();
2840                 }
2841
2842                 result = 1;
2843         }
2844         else
2845 // Pressed in scrollbar
2846         if((xscrollbar && top_level->event_win == xscrollbar->win) ||
2847                 (yscrollbar && top_level->event_win == yscrollbar->win))
2848         {
2849                 if(debug) printf("BC_ListBox::button_press_event %d\n", __LINE__);
2850                 result = 0;
2851         }
2852         else
2853 // Pressed in items
2854         if(gui && top_level->event_win == gui->win)
2855         {
2856                 if(debug) printf("BC_ListBox::button_press_event %d\n", __LINE__);
2857
2858 // Activate list items
2859 // If it is a suggestion popup, it is visible without being active
2860                 if(!active)
2861                 {
2862                         if(debug) printf("BC_ListBox::button_press_event %d\n", __LINE__);
2863                         if(!is_suggestions) top_level->deactivate();
2864                         if(debug) printf("BC_ListBox::button_press_event %d\n", __LINE__);
2865                         activate();
2866                         if(debug) printf("BC_ListBox::button_press_event %d\n", __LINE__);
2867                 }
2868
2869 // Wheel mouse pressed
2870                 if(get_buttonpress() == 4)
2871                 {
2872                         if(current_operation == NO_OPERATION)
2873                         {
2874                                 current_operation = WHEEL;
2875                                 if(yscrollbar)
2876                                 {
2877                                         set_yposition(yposition - gui->get_h() / 10, 0);
2878                                         clamp_positions();
2879                                         update_scrollbars(0);
2880                                         highlighted_ptr = 0;
2881                                         highlighted_item = get_cursor_item(data,
2882                                                 top_level->cursor_x,
2883                                                 top_level->cursor_y,
2884                                                 &highlighted_ptr);
2885                                         draw_items(1);
2886                                         result = 1;
2887                                 }
2888                         }
2889                 }
2890                 else
2891                 if(get_buttonpress() == 5)
2892                 {
2893                         if(current_operation == NO_OPERATION)
2894                         {
2895                                 current_operation = WHEEL;
2896                                 if(yscrollbar)
2897                                 {
2898                                         set_yposition(yposition + gui->get_h() / 10, 0);
2899                                         clamp_positions();
2900                                         update_scrollbars(0);
2901                                         highlighted_ptr = 0;
2902                                         highlighted_item = get_cursor_item(data,
2903                                                 top_level->cursor_x,
2904                                                 top_level->cursor_y,
2905                                                 &highlighted_ptr);
2906                                         draw_items(1);
2907                                         result = 1;
2908                                 }
2909                         }
2910                 }
2911                 else
2912 // Pressed over column title division
2913                 if(test_column_divisions(gui->get_cursor_x(),
2914                         gui->get_cursor_y(),
2915                         new_cursor))
2916                 {
2917                         drag_cursor_x = gui->get_cursor_x() + xposition;
2918                         if(column_width)
2919                                 drag_column_w = column_width[highlighted_division - 1];
2920                         else
2921                                 drag_column_w = default_column_width[highlighted_division - 1];
2922
2923                         current_operation = DRAG_DIVISION;
2924                         reset_query();
2925                 }
2926                 else
2927 // Pressed in column title
2928                 if(test_column_titles(gui->get_cursor_x(), gui->get_cursor_y()))
2929                 {
2930                         current_operation = COLUMN_DN;
2931                         button_highlighted = 0;
2932                         list_highlighted = 1;
2933                         draw_items(1);
2934                         result = 1;
2935                 }
2936                 else
2937 // Pressed in expander
2938                 if(test_expanders())
2939                 {
2940                         current_operation = EXPAND_DN;
2941 // Need to redraw items because of alpha
2942                         draw_items(1);
2943                         result = 1;
2944                 }
2945                 else
2946 // Pressed over item
2947                 if((selection_number = get_cursor_item(data,
2948                                         gui->get_cursor_x(),
2949                                         gui->get_cursor_y(),
2950                                         &current_item)) >= 0)
2951                 {
2952                         if(debug) printf("BC_ListBox::button_press_event %d\n", __LINE__);
2953
2954 // Get item button was pressed over
2955                         selection_number2 = selection_number1;
2956                         selection_number1 = selection_number;
2957
2958                         selection_start = -1;
2959                         selection_end = -1;
2960
2961
2962 // Multiple item selection is possible
2963                         if(selection_mode == LISTBOX_MULTIPLE &&
2964                                 (ctrl_down() || shift_down()))
2965                         {
2966 // Expand text selection.
2967 // Fill items between selected region and current item.
2968                                 if(shift_down() &&
2969                                         (display_format == LISTBOX_TEXT ||
2970                                         display_format == LISTBOX_ICON_LIST))
2971                                 {
2972 // Get first item selected
2973                                         selection_start = get_first_selection(data);
2974 // Get last item selected
2975                                         selection_end = get_last_selection(data);
2976 // Get center of selected region
2977                                         if(selection_end > selection_start)
2978                                         {
2979                                                 selection_center = (selection_end + selection_start) >> 1;
2980                                         }
2981                                         else
2982                                         {
2983                                                 selection_center = selection_number;
2984                                         }
2985
2986
2987 // Deselect everything.
2988                                         set_all_selected(data, 0);
2989 // Select just the items
2990                                         expand_selection(1, selection_number);
2991                                         new_value = 1;
2992                                 }
2993                                 else
2994 // Toggle a single item on or off
2995                                 {
2996                                         toggle_item_selection(data, selection_number);
2997                                         new_value = current_item->selected;
2998                                 }
2999                         }
3000                         else
3001 // Select single item
3002                         {
3003                                 if(!current_item->selected)
3004                                 {
3005                                         set_all_selected(data, 0);
3006                                         set_selected(data,
3007                                                 selection_number,
3008                                                 1);
3009                                 }
3010                                 new_value = 1;
3011                         }
3012
3013
3014                         current_operation = SELECT;
3015                         highlighted_item = selection_number;
3016                         highlighted_ptr = current_item;
3017                         button_highlighted = 0;
3018                         list_highlighted = 1;
3019                         reset_query();
3020                         draw_items(1);
3021                         do_selection_change = 1;
3022                         result = 1;
3023                 }
3024                 else
3025                 if(data)
3026 // Pressed over nothing.  Start rectangle selection.
3027                 {
3028                         if(get_buttonpress() == 1 &&
3029                                 selection_mode == LISTBOX_MULTIPLE)
3030                         {
3031                                 if(!shift_down())
3032                                 {
3033 // Deselect all and redraw if anything was selected
3034                                         if(get_selection_number(0, 0) >= 0)
3035                                         {
3036                                                 set_all_selected(data, 0);
3037                                                 draw_items(1);
3038                                                 do_selection_change = 1;
3039                                                 result = 1;
3040                                         }
3041                                 }
3042                                 else
3043                                 {
3044 // Promote selections to protect from a rectangle selection
3045                                         promote_selections(data, 1, 2);
3046                                 }
3047
3048 // Start rectangle selection
3049                                 current_operation = SELECT_RECT;
3050                                 rect_x1 = rect_x2 = get_cursor_x();
3051                                 rect_y1 = rect_y2 = get_cursor_y();
3052                         }
3053                 }
3054
3055
3056                 reset_query();
3057         }
3058         else
3059 // Suggestion box is not active but visible, so lie to get it to deactivate
3060         if(is_popup && (active || (is_suggestions && gui)))
3061         {
3062                 active = 1;
3063                 if(debug) printf("BC_ListBox::button_press_event %d\n", __LINE__);
3064                 deactivate();
3065                 result = 1;
3066         }
3067
3068
3069         if(do_selection_change) selection_changed();
3070         if(debug) printf("BC_ListBox::button_press_event %d %d\n",
3071                 __LINE__,
3072                 result);
3073
3074         return result;
3075 }
3076
3077 int BC_ListBox::button_release_event()
3078 {
3079         int result = 0;
3080         int cursor_x, cursor_y;
3081         int do_event = 0;
3082         new_value = 0;
3083
3084 //printf("BC_ListBox::button_release_event 1 %d\n", current_operation);
3085         switch(current_operation)
3086         {
3087                 case DRAG_DIVISION:
3088                         current_operation = NO_OPERATION;
3089                         result = 1;
3090                         break;
3091
3092                 case WHEEL:
3093                         current_operation = NO_OPERATION;
3094                         result = 1;
3095                         break;
3096
3097 // Release item selection
3098                 case BUTTON_DOWN_SELECT:
3099                 case SELECT:
3100 //printf("BC_ListBox::button_release_event 10\n");
3101                         unset_repeat(get_resources()->scroll_repeat);
3102                         current_operation = NO_OPERATION;
3103                         if( gui ) {
3104                                 translate_coordinates(top_level->event_win, gui->win,
3105                                         gui->get_cursor_x(), gui->get_cursor_y(),
3106                                         &cursor_x, &cursor_y);
3107                                 selection_number1 = selection_number =
3108                                         get_cursor_item(data, cursor_x, cursor_y);
3109 //printf("BC_ListBox::button_release_event %d %d\n", selection_number2, selection_number1);
3110                         }
3111
3112                         if(is_popup)
3113                         {
3114                                 button_releases++;
3115                                 if(selection_number >= 0)
3116                                 {
3117                                         deactivate();
3118                                         do_event = 1;
3119                                 }
3120                                 else
3121 // Second button release outside button
3122                                 if(button_releases > 1)
3123                                 {
3124                                         deactivate();
3125                                 }
3126                         }
3127                         else
3128                         {
3129                                 if(top_level->get_double_click() &&
3130                                         selection_number2 == selection_number1 &&
3131                                         selection_number2 >= 0 &&
3132                                         selection_number1 >= 0)
3133                                 {
3134                                         do_event = 1;
3135                                 }
3136                                 result = 1;
3137                         }
3138                         break;
3139
3140
3141                 case SELECT_RECT:
3142                         unset_repeat(get_resources()->scroll_repeat);
3143                         if(data)
3144                         {
3145 // Demote selections from rectangle selection
3146                                 promote_selections(data, 2, 1);
3147                         }
3148
3149 // Hide rectangle overlay
3150                         draw_rectangle(1);
3151                         current_operation = NO_OPERATION;
3152                         result = 1;
3153                         break;
3154
3155 // Release popup button
3156                 case BUTTON_DN:
3157                         hide_tooltip();
3158                         current_operation = NO_OPERATION;
3159                         button_releases++;
3160                         draw_button(1);
3161
3162 // Second button release inside button
3163                         if(button_releases > 1)
3164                         {
3165                                 deactivate();
3166                         }
3167                         result = 1;
3168                         break;
3169
3170                 case COLUMN_DN:
3171                         current_operation = NO_OPERATION;
3172 // Update the sort column and the sort order for the user only if the existing
3173 // sort column is valid.
3174                         if(sort_column >= 0)
3175                         {
3176 // Invert order only if column is the same
3177                                 if(highlighted_title == sort_column)
3178                                         sort_order =
3179                                                 (sort_order == SORT_ASCENDING) ?
3180                                                 SORT_DESCENDING :
3181                                                 SORT_ASCENDING;
3182 // Set the new sort column
3183                                 sort_column = highlighted_title;
3184                                 if(!sort_order_event())
3185                                 {
3186                                         draw_titles(1);
3187                                 }
3188                         }
3189                         else
3190 // Sorting not enabled.  Redraw the title state.
3191                         {
3192                                 draw_titles(1);
3193                         }
3194                         result = 1;
3195                         break;
3196
3197                 case EXPAND_DN:
3198                 {
3199                         int redraw_toggles = 0;
3200                         for(int i = 0; i < expanders.total && !result; i++)
3201                         {
3202                                 if(expanders.values[i]->button_release_event(&redraw_toggles))
3203                                 {
3204                                         result = 1;
3205                                 }
3206                         }
3207 // Need to redraw items because of alpha
3208                         if(redraw_toggles) draw_items(1);
3209                         current_operation = NO_OPERATION;
3210                         break;
3211                 }
3212
3213                 default:
3214 // Can't default to NO_OPERATION because it may be used in a drag event.
3215                         break;
3216         }
3217
3218
3219         if(do_event) handle_event();
3220
3221 //printf("BC_ListBox::button_release_event %d %d\n", __LINE__, get_window_lock());
3222         return result;
3223 }
3224
3225 int BC_ListBox::get_title_h()
3226 {
3227         if(display_format == LISTBOX_TEXT ||
3228                 display_format == LISTBOX_ICON_LIST)
3229                 return column_titles ? column_bg[0]->get_h() : 0;
3230         else
3231                 return 0;
3232 }
3233
3234 void BC_ListBox::reset_cursor(int new_cursor)
3235 {
3236         if(is_popup)
3237         {
3238                 if(gui->get_cursor() != new_cursor)
3239                 {
3240                         gui->set_cursor(new_cursor, 0, 0);
3241                 }
3242         }
3243         else
3244         if(get_cursor() != new_cursor)
3245         {
3246                 set_cursor(new_cursor, 0, 0);
3247         }
3248 }
3249
3250 int BC_ListBox::test_column_divisions(int cursor_x, int cursor_y, int &new_cursor)
3251 {
3252         if(gui &&
3253                 column_titles &&
3254                 cursor_y >= 0 &&
3255                 cursor_y < get_title_h() &&
3256                 cursor_x >= 0 &&
3257                 cursor_x < gui->get_w())
3258         {
3259                 for(int i = 1; i < columns; i++)
3260                 {
3261                         if(cursor_x >= -xposition + get_column_offset(i) - 5 &&
3262                                 cursor_x <  -xposition + get_column_offset(i) +
3263                                         get_resources()->listbox_title_hotspot)
3264                         {
3265                                 highlighted_item = -1;
3266                                 highlighted_ptr = 0;
3267                                 highlighted_division = i;
3268                                 highlighted_title = -1;
3269                                 list_highlighted = 1;
3270                                 new_cursor = HSEPARATE_CURSOR;
3271                                 return 1;
3272                         }
3273                 }
3274         }
3275         highlighted_division = -1;
3276         return 0;
3277 }
3278
3279 int BC_ListBox::test_column_titles(int cursor_x, int cursor_y)
3280 {
3281         if(gui &&
3282                 column_titles &&
3283                 cursor_y >= 0 &&
3284                 cursor_y < get_title_h() &&
3285                 cursor_x >= 0 &&
3286                 cursor_x < gui->get_w())
3287         {
3288                 for(int i = 0; i < columns; i++)
3289                 {
3290                         if(cursor_x >= -xposition + get_column_offset(i) &&
3291                                 (cursor_x < -xposition + get_column_offset(i + 1) ||
3292                                         i == columns - 1))
3293                         {
3294                                 highlighted_item = -1;
3295                                 highlighted_ptr = 0;
3296                                 highlighted_division = -1;
3297                                 highlighted_title = i;
3298                                 list_highlighted = 1;
3299                                 return 1;
3300                         }
3301                 }
3302         }
3303         highlighted_title = -1;
3304         return 0;
3305 }
3306
3307 int BC_ListBox::test_expanders()
3308 {
3309         for(int i = 0; i < expanders.total; i++)
3310         {
3311                 if(expanders.values[i]->button_press_event())
3312                 {
3313                         current_operation = EXPAND_DN;
3314                         draw_toggles(1);
3315                         return 1;
3316                 }
3317         }
3318         return 0 ;
3319 }
3320
3321 int BC_ListBox::cursor_motion_event()
3322 {
3323         int redraw = 0, result = 0;
3324         int new_cursor = ARROW_CURSOR;
3325
3326         selection_number = -1;
3327
3328
3329         switch(current_operation)
3330         {
3331                 case BUTTON_DN:
3332 // Button pressed and slid off button
3333                         if(!cursor_inside())
3334                         {
3335                                 current_operation = BUTTON_DOWN_SELECT;
3336                                 draw_button(1);
3337                                 result = 1;
3338                         }
3339                         break;
3340
3341                 case DRAG_DIVISION:
3342                 {
3343 //                      int new_w = get_cursor_x() +
3344 //                              xposition -
3345 //                              get_column_offset(highlighted_division - 1);
3346                         int difference = get_cursor_x() + xposition - drag_cursor_x;
3347                         int new_w = drag_column_w + difference;
3348
3349                         new_cursor = HSEPARATE_CURSOR;
3350
3351                         if(column_width)
3352                         {
3353                                 column_width[highlighted_division - 1] = new_w;
3354                         }
3355                         else
3356                         {
3357                                 default_column_width[highlighted_division - 1] = new_w;
3358                         }
3359
3360                         column_width_boundaries();
3361
3362 // Force update of coords
3363                         set_autoplacement(data, 0, 1);
3364                         column_resize_event();
3365
3366                         clamp_positions();
3367                         draw_items(0);
3368                         update_scrollbars(1);
3369                         result = 1;
3370                         break;
3371                 }
3372
3373                 case SELECT_RECT:
3374                 {
3375                         if(test_drag_scroll(get_cursor_x(), get_cursor_y()))
3376                         {
3377                                 set_repeat(get_resources()->scroll_repeat);
3378                         }
3379
3380                         int old_x1 = MIN(rect_x1, rect_x2);
3381                         int old_x2 = MAX(rect_x1, rect_x2);
3382                         int old_y1 = MIN(rect_y1, rect_y2);
3383                         int old_y2 = MAX(rect_y1, rect_y2);
3384
3385                         int new_rect_x2 = get_cursor_x();
3386                         int new_rect_y2 = get_cursor_y();
3387
3388                         int x1 = MIN(rect_x1, new_rect_x2);
3389                         int x2 = MAX(rect_x1, new_rect_x2);
3390                         int y1 = MIN(rect_y1, new_rect_y2);
3391                         int y2 = MAX(rect_y1, new_rect_y2);
3392
3393 // Adjust rectangle coverage
3394                         if(old_x1 != x1 ||
3395                                 old_x2 != x2 ||
3396                                 old_y1 != y1 ||
3397                                 old_y2 != y2)
3398                         {
3399                                 if(data)
3400                                 {
3401                                         redraw = select_rectangle(data,
3402                                                 x1,
3403                                                 y1,
3404                                                 x2,
3405                                                 y2);
3406                                 }
3407
3408 // hide rectangle
3409                                 if(!redraw)
3410                                 {
3411 //printf("BC_ListBox::cursor_motion_event %d\n", __LINE__);
3412                                         draw_rectangle(0);
3413                                 }
3414                         }
3415
3416                         rect_x2 = get_cursor_x();
3417                         rect_y2 = get_cursor_y();
3418                         if(redraw)
3419                         {
3420                                 clamp_positions();
3421                                 draw_items(0);
3422                                 update_scrollbars(1);
3423                                 selection_changed();
3424 //printf("BC_ListBox::cursor_motion_event %d\n", __LINE__);
3425                         }
3426                         else
3427                         if(old_x1 != x1 ||
3428                                 old_x2 != x2 ||
3429                                 old_y1 != y1 ||
3430                                 old_y2 != y2)
3431                         {
3432 //printf("BC_ListBox::cursor_motion_event %d\n", __LINE__);
3433                                 draw_rectangle(1);
3434                         }
3435
3436                         result = 1;
3437                         break;
3438                 }
3439
3440                 case SELECT:
3441                 {
3442                         int old_highlighted_item = highlighted_item;
3443
3444                         if(test_drag_scroll(get_cursor_x(),
3445                                 get_cursor_y()))
3446                         {
3447                                 set_repeat(get_resources()->scroll_repeat);
3448                         }
3449
3450
3451                         highlighted_item = selection_number = get_cursor_item(data,
3452                                 get_cursor_x(),
3453                                 get_cursor_y(),
3454                                 &highlighted_ptr);
3455                         result = 1;
3456
3457 // Deselect all items and select just the one we're over
3458                         if(selection_number >= 0 &&
3459                                 !allow_drag &&
3460                                 ((!shift_down() &&
3461                                         !ctrl_down()) ||
3462                                         selection_mode == LISTBOX_SINGLE))
3463                         {
3464                                 redraw = update_selection(data, selection_number);
3465                         }
3466                         else
3467                         if(selection_mode == LISTBOX_MULTIPLE &&
3468                                 (shift_down() || ctrl_down()))
3469 // Expand multiple selection
3470                         {
3471 // Expand selected region in text mode centered around initial range
3472                                 if((display_format == LISTBOX_TEXT ||
3473                                         display_format == LISTBOX_ICON_LIST) &&
3474                                         shift_down())
3475                                 {
3476 // Deselect everything.
3477                                         set_all_selected(data, 0);
3478
3479 // Select just the items
3480                                         redraw = expand_selection(0, selection_number);
3481                                 }
3482                                 else
3483 // Set the one item we're over to the selection value determined in
3484 // button_press_event.
3485                                 {
3486                                         set_selected(data,
3487                                                 selection_number,
3488                                                 new_value);
3489                                 }
3490                         }
3491
3492                         if(highlighted_item != old_highlighted_item)
3493                         {
3494                                 clamp_positions();
3495                                 draw_items(0);
3496                                 update_scrollbars(1);
3497 //printf("BC_ListBox::cursor_motion_event %d %d\n", highlighted_item, old_highlighted_item);
3498                                 selection_changed();
3499                         }
3500                         break;
3501                 }
3502
3503                 case BUTTON_DOWN_SELECT:
3504 // Went back into button area
3505                         if(cursor_inside())
3506                         {
3507                                 current_operation = BUTTON_DN;
3508                                 draw_button(1);
3509                                 result = 1;
3510                         }
3511                         else
3512 // Went into item area
3513                         if(gui)
3514                         {
3515                                 int cursor_x = 0, cursor_y = 0;
3516                                 translate_coordinates(top_level->event_win,
3517                                         gui->win,
3518                                         top_level->cursor_x,
3519                                         top_level->cursor_y,
3520                                         &cursor_x,
3521                                         &cursor_y);
3522                                 int old_highlighted_item = highlighted_item;
3523                                 highlighted_item = selection_number = get_cursor_item(data,
3524                                                 cursor_x,
3525                                                 cursor_y,
3526                                                 &highlighted_ptr);
3527
3528                                 if(highlighted_item != old_highlighted_item)
3529                                 {
3530                                         update_selection(data, selection_number);
3531                                         draw_items(1);
3532                                         selection_changed();
3533                                 }
3534                         }
3535                         break;
3536
3537                 case EXPAND_DN:
3538                 {
3539                         int redraw_toggles = 0;
3540                         for(int i = 0; i < expanders.total && !result; i++)
3541                         {
3542                                 result = expanders.values[i]->cursor_motion_event(
3543                                         &redraw_toggles);
3544                         }
3545                         if(redraw_toggles)
3546                         {
3547 // Need to redraw items because of the alpha
3548                                 draw_items(1);
3549                         }
3550                         break;
3551                 }
3552
3553                 case NO_OPERATION:
3554                 {
3555                         int cursor_x = get_cursor_x(), cursor_y = get_cursor_y();
3556                         if(gui && top_level->event_win == gui->win)
3557                         {
3558                                 int old_highlighted_title = highlighted_title;
3559                                 int old_list_highlighted = list_highlighted;
3560                                 int old_highlighted_item = highlighted_item;
3561                                 int redraw_titles = 0;
3562                                 int redraw_border = 0;
3563                                 int redraw_items = 0;
3564                                 int redraw_toggles = 0;
3565                                 result = 1;
3566
3567
3568 // Test if cursor moved over a title division
3569                                 test_column_divisions(cursor_x, cursor_y, new_cursor);
3570
3571 // Test if cursor moved over a title
3572                                 if(highlighted_division < 0)
3573                                 {
3574                                         test_column_titles(cursor_x, cursor_y);
3575                                 }
3576
3577 // Test if cursor moved over expander
3578                                 if(highlighted_division < 0 &&
3579                                         highlighted_title < 0 &&
3580                                         (display_format == LISTBOX_TEXT ||
3581                                                 display_format == LISTBOX_ICON_LIST))
3582                                 {
3583                                         for(int i = 0; i < expanders.total; i++)
3584                                         {
3585                                                 expanders.values[i]->cursor_motion_event(
3586                                                         &redraw_toggles);
3587                                         }
3588 //printf("BC_ListBox::cursor_motion_event %d\n", redraw_toggles);
3589                                 }
3590
3591 // Test if cursor moved over an item
3592                                 if(highlighted_division < 0 &&
3593                                         highlighted_title < 0)
3594                                 {
3595                                         highlighted_item = get_cursor_item(data,
3596                                                 cursor_x,
3597                                                 cursor_y,
3598                                                 &highlighted_ptr);
3599                                 }
3600
3601
3602 // Clear title highlighting if moved over division
3603                                 if(old_highlighted_title != highlighted_title)
3604                                 {
3605                                         redraw_titles = 1;
3606                                 }
3607
3608 // Highlight list border
3609                                 if(old_list_highlighted != list_highlighted)
3610                                 {
3611                                         redraw_border = 1;
3612                                 }
3613
3614 // Moved out of item area
3615                                 if(old_highlighted_item != highlighted_item)
3616                                 {
3617                                         redraw_items = 1;
3618                                 }
3619
3620 //printf("BC_ListBox::cursor_motion_event 1 %d\n", highlighted_item);
3621
3622 // Change cursor to title division adjustment
3623                                 reset_cursor(new_cursor);
3624
3625                                 if(redraw_items)
3626                                 {
3627                                         draw_items(0);
3628                                 }
3629                                 else
3630                                 {
3631                                         if(redraw_titles)
3632                                                 draw_titles(0);
3633                                         if(redraw_border)
3634                                                 draw_border(0);
3635                                         if(redraw_toggles)
3636                                                 draw_toggles(0);
3637                                 }
3638
3639                                 if(redraw_items ||
3640                                         redraw_titles ||
3641                                         redraw_border ||
3642                                         redraw_toggles)
3643                                 {
3644                                         gui->flash();
3645                                         gui->flush();
3646                                 }
3647                         }
3648
3649
3650                         if(!result && list_highlighted)
3651                         {
3652                                 list_highlighted = 0;
3653                                 highlighted_item = -1;
3654                                 highlighted_ptr = 0;
3655                                 highlighted_title = -1;
3656                                 highlighted_division = -1;
3657                                 draw_items(1);
3658                                 result = 0;
3659                         }
3660                         break;
3661                 }
3662         }
3663
3664
3665         return result;
3666 }
3667
3668 int BC_ListBox::drag_start_event()
3669 {
3670         switch(current_operation)
3671         {
3672                 case SELECT:
3673                         if(gui &&
3674                                 gui->is_event_win() &&
3675                                 allow_drag)
3676                         {
3677                                 BC_ListBoxItem *item_return = 0;
3678                                 selection_number = get_cursor_item(data,
3679                                         top_level->cursor_x,
3680                                         top_level->cursor_y,
3681                                         &item_return);
3682
3683                                 if(selection_number >= 0)
3684                                 {
3685
3686                                         if (item_return->icon_vframe)
3687                                         {
3688                                                 drag_popup = new BC_DragWindow(this,
3689                                                         item_return->icon_vframe /*,
3690                                                         get_abs_cursor_x(0) - item_return->icon_vframe->get_w() / 2,
3691                                                         get_abs_cursor_y(0) - item_return->icon_vframe->get_h() / 2 */);
3692                                         }
3693                                         else
3694 // this probably works not!
3695                                         if (item_return->icon)
3696                                         {
3697                                                 drag_popup = new BC_DragWindow(this,
3698                                                         item_return->icon /*,
3699                                                         get_abs_cursor_x(0) - item_return->icon->get_w() / 2,
3700                                                         get_abs_cursor_y(0) - item_return->icon->get_h() / 2 */);
3701                                         }
3702                                         else
3703                                         {
3704                                                 drag_popup = new BC_DragWindow(this,
3705                                                         drag_icon_vframe /*,
3706                                                         get_abs_cursor_x(0) - drag_icon_vframe->get_w() / 2,
3707                                                         get_abs_cursor_y(0) - drag_icon_vframe->get_h() / 2 */);
3708                                         }
3709
3710                                         current_operation = DRAG_ITEM;
3711                                         return 1;
3712                                 }
3713                         }
3714                         break;
3715
3716                 case COLUMN_DN:
3717                         if(gui && gui->is_event_win() && allow_drag_column)
3718                         {
3719                                 drag_popup = new BC_DragWindow(this,
3720                                         drag_column_icon_vframe /*,
3721                                         get_abs_cursor_x(0) - drag_column_icon_vframe->get_w() / 2,
3722                                         get_abs_cursor_y(0) - drag_column_icon_vframe->get_h() / 2 */);
3723                                 dragged_title = highlighted_title;
3724                                 current_operation = COLUMN_DRAG;
3725                                 draw_titles(1);
3726                                 return 1;
3727                         }
3728                         break;
3729         }
3730
3731         return 0;
3732 }
3733
3734 int BC_ListBox::drag_motion_event()
3735 {
3736 //printf("BC_ListBox::drag_motion_event 1 %d\n", current_operation);
3737         switch(current_operation)
3738         {
3739                 case DRAG_ITEM:
3740                 {
3741                         int redraw = 0;
3742                         int new_highlighted_item = -1;
3743                         BC_ListBoxItem *new_highlighted_ptr = 0;
3744                         new_highlighted_item = get_cursor_item(data,
3745                                 top_level->cursor_x, top_level->cursor_y,
3746                                 &new_highlighted_ptr);
3747
3748                         if(new_highlighted_item != highlighted_item)
3749                         {
3750                                 redraw = 1;
3751                         }
3752
3753 // Always update highlighted value for drag_stop
3754                         highlighted_item = new_highlighted_item;
3755                         highlighted_ptr = new_highlighted_ptr;
3756 //printf("BC_ListBox::drag_motion_event 1 %p\n", highlighted_ptr);
3757                         if(redraw)
3758                         {
3759                                 clamp_positions();
3760                                 draw_items(0);
3761                                 update_scrollbars(1);
3762                         }
3763
3764                         return drag_popup->cursor_motion_event();
3765                         break;
3766                 }
3767
3768                 case COLUMN_DRAG:
3769                 {
3770                         int old_highlighted_title = highlighted_title;
3771                         test_column_titles(get_cursor_x(), get_cursor_y());
3772                         if(old_highlighted_title != highlighted_title)
3773                         {
3774                                 draw_titles(1);
3775                         }
3776                         return drag_popup->cursor_motion_event();
3777                         break;
3778                 }
3779         }
3780         return 0;
3781 }
3782
3783 int BC_ListBox::drag_stop_event()
3784 {
3785         switch(current_operation)
3786         {
3787                 case DRAG_ITEM:
3788 // Inside window boundary
3789                         if(top_level->cursor_x > 0 &&
3790                                 top_level->cursor_x < gui->get_w() - drag_popup->get_w() / 2 &&
3791                                 top_level->cursor_y > 0 &&
3792                                 top_level->cursor_y < gui->get_h() - drag_popup->get_h() / 2)
3793                         {
3794 // Move icon
3795
3796
3797                                 if(display_format == LISTBOX_ICONS)
3798                                 {
3799                                         reposition_item(data,
3800                                                 selection_number,
3801                                                 top_level->cursor_x +
3802                                                         drag_popup->get_offset_x() -
3803                                                         LISTBOX_MARGIN -
3804                                                         2 +
3805                                                         xposition,
3806                                                 top_level->cursor_y +
3807                                                         drag_popup->get_offset_y() -
3808                                                         LISTBOX_MARGIN -
3809                                                         2 +
3810                                                         yposition);
3811                                 }
3812                                 else
3813 // Move rows
3814                                 if(process_drag)
3815                                 {
3816 // Get destination
3817                                         int destination = highlighted_item = item_to_index(data,
3818                                                 highlighted_ptr);
3819 //printf("BC_ListBox::drag_stop_event 1 %p %d\n", highlighted_ptr, destination);
3820
3821 // Move selected items from data to temporary
3822                                         ArrayList<BC_ListBoxItem*> *src_items =
3823                                                 new ArrayList<BC_ListBoxItem*>[columns];
3824
3825                                         move_selection(src_items, data);
3826
3827 // Insert items from temporary to data
3828                                         put_selection(data,
3829                                                 src_items,
3830                                                 destination);
3831
3832
3833                                         delete [] src_items;
3834                                         set_autoplacement(data, 0, 1);
3835                                 }
3836
3837
3838                                 draw_items(1);
3839                         }
3840                         else
3841                                 drag_popup->drag_failure_event();
3842
3843                         delete drag_popup;
3844                         flush();
3845                         drag_popup = 0;
3846                         current_operation = NO_OPERATION;
3847                         new_value = 0;
3848                         return 1;
3849                         break;
3850
3851                 case COLUMN_DRAG:
3852                         if(dragged_title != highlighted_title)
3853                         {
3854                                 if(highlighted_title >= 0)
3855                                 {
3856                                         if(!move_column_event()) draw_titles(1);
3857                                 }
3858                                 else
3859                                         drag_popup->drag_failure_event();
3860                         }
3861                         current_operation = NO_OPERATION;
3862                         delete drag_popup;
3863                         flush();
3864                         drag_popup = 0;
3865                         return 1;
3866                         break;
3867         }
3868         return 0;
3869 }
3870
3871 BC_DragWindow* BC_ListBox::get_drag_popup()
3872 {
3873         return drag_popup;
3874 }
3875
3876 int BC_ListBox::translation_event()
3877 {
3878         if(is_popup && gui)
3879         {
3880                 int new_x = gui->get_x() +
3881                         (top_level->last_translate_x - top_level->prev_x -
3882                                 BC_DisplayInfo::get_left_border());
3883                 int new_y = gui->get_y() +
3884                         (top_level->last_translate_y - top_level->prev_y -
3885                                 BC_DisplayInfo::get_top_border());
3886
3887                 gui->reposition_window(new_x, new_y);
3888
3889         }
3890         return 0;
3891 }
3892
3893 int BC_ListBox::reposition_window(int x, int y, int w, int h, int flush)
3894 {
3895         if(w != -1)
3896         {
3897                 if(w != -1) popup_w = w;
3898                 if(h != -1) popup_h = h;
3899 //printf("BC_ListBox::reposition_window %d %d\n", popup_w, popup_h);
3900
3901                 if(!is_popup)
3902                 {
3903                         if(xscrollbar)
3904                                 xscrollbar->reposition_window(get_xscroll_x(),
3905                                         get_xscroll_y(),
3906                                         get_xscroll_width());
3907                         if(yscrollbar)
3908                                 yscrollbar->reposition_window(get_yscroll_x(),
3909                                         get_yscroll_y(),
3910                                         get_yscroll_height());
3911                 }
3912         }
3913
3914
3915         BC_WindowBase::reposition_window(x, y, w, h);
3916         draw_button(0);
3917         draw_items(flush);
3918         return 0;
3919 }
3920
3921 int BC_ListBox::deactivate()
3922 {
3923         hide_tooltip();
3924 // printf("BC_ListBox::deactivate %d this=%p gui=%p active=%d\n",
3925 // __LINE__,
3926 // this,
3927 // gui,
3928 // active);
3929         if(active)
3930         {
3931
3932                 if(is_popup)
3933                 {
3934 //printf("BC_ListBox::deactivate %d this=%p gui=%p\n", __LINE__, this, gui);
3935                         if(gui)
3936                         {
3937                                 delete gui;
3938                                 flush();
3939                         }
3940                         gui = 0;
3941                         xscrollbar = 0;
3942                         yscrollbar = 0;
3943                         highlighted_item = -1;
3944                         highlighted_ptr = 0;
3945 //sleep(1);
3946                 }
3947
3948
3949 //printf("BC_ListBox::deactivate %d this=%p\n", __LINE__, this);
3950                 active = 0;
3951                 top_level->active_subwindow = 0;
3952         }
3953
3954         return 0;
3955 }
3956
3957 int BC_ListBox::activate(int take_focus)
3958 {
3959         if( active ) return 0;
3960         active = 1;
3961         if( take_focus )
3962                 set_active_subwindow(this);
3963         button_releases = 0;
3964         if( !is_popup || gui ) return 0;
3965         int wx = get_x(), wy = get_y() + get_h();
3966         if( justify == LISTBOX_RIGHT ) wx += get_w() - popup_w;
3967         Window xwin;
3968         int abs_x, abs_y;
3969         XTranslateCoordinates(top_level->display,
3970                 parent_window->win, top_level->rootwin,
3971                 wx, wy, &abs_x, &abs_y, &xwin);
3972         if( x <= 0 ) x = 2;
3973         if( y <= 0 ) y = 2;
3974         return activate(abs_x, abs_y);
3975 }
3976
3977 int BC_ListBox::activate(int x, int y, int w, int h)
3978 {
3979         if( !is_popup || gui ) return 0;
3980         active = 1;
3981         if(w != -1) popup_w = w;
3982         if(h != -1) popup_h = h;
3983         reset_query();
3984         if( y + popup_h > top_level->get_root_h(0) )
3985                 y -= get_h() + popup_h;
3986         add_subwindow(gui = new BC_Popup(this,
3987                 x, y, popup_w, popup_h, -1, 0, 0));
3988         draw_items(1);
3989         gui->show_window(1);
3990         return 0;
3991 }
3992
3993 int BC_ListBox::is_active()
3994 {
3995         return active;
3996 }
3997
3998 int BC_ListBox::keypress_event()
3999 {
4000         if(!active) return 0;
4001
4002 //printf("BC_ListBox::keypress_event %d\n", __LINE__);
4003
4004         int result = 0, redraw = 0;
4005         int view_items = last_in_view - first_in_view + 1;
4006         if( view_items <= 0 ) view_items = view_h / get_text_height(MEDIUMFONT);
4007         int new_item = -1, new_selection = -1;
4008
4009         switch(top_level->get_keypress())
4010         {
4011                 case ESC:
4012                 case RETURN:
4013                         top_level->deactivate();
4014
4015 // If user is manipulating popup with keyboard, don't pass on event.
4016                         if(is_popup)
4017                         {
4018 //printf("BC_ListBox::keypress_event %d %p\n", __LINE__, get_selection(0, 0));
4019                                 if(top_level->get_keypress() == RETURN)
4020                                         handle_event();
4021 //printf("BC_ListBox::keypress_event %d %p\n", __LINE__, get_selection(0, 0));
4022                                 result = 1;
4023                         }
4024                         else
4025                                 result = 0;
4026                         break;
4027
4028                 case UP:
4029                         new_selection = new_item = select_previous(0);
4030
4031 //printf("BC_ListBox::keypress_event 1 %d\n", new_item);
4032                         if(new_item >= 0)
4033                         {
4034                                 center_selection(new_item);
4035                                 redraw = 1;
4036                         }
4037                         reset_query();
4038                         result = 1;
4039                         break;
4040
4041                 case DOWN:
4042                         new_selection = new_item = select_next(0);
4043
4044                         if(new_item >= 0)
4045                         {
4046                                 center_selection(new_item);
4047                                 redraw = 1;
4048                         }
4049                         reset_query();
4050                         result = 1;
4051                         break;
4052
4053                 case PGUP:
4054                         new_selection = new_item = select_previous(view_items - 1);
4055
4056                         if(new_item >= 0)
4057                         {
4058                                 center_selection(new_item);
4059                                 redraw = 1;
4060                         }
4061                         reset_query();
4062                         result = 1;
4063                         break;
4064
4065                 case PGDN:
4066                         new_selection = new_item = select_next(view_items - 1);
4067
4068                         if(new_item >= 0)
4069                         {
4070                                 center_selection(new_item);
4071                                 redraw = 1;
4072                         }
4073                         reset_query();
4074                         result = 1;
4075                         break;
4076
4077                 case LEFT:
4078                         xposition -= 10;
4079                         redraw = 1;
4080                         result = 1;
4081                         break;
4082
4083                 case RIGHT:
4084                         xposition += 10;
4085                         redraw = 1;
4086                         result = 1;
4087                         break;
4088
4089                 case DELETE:
4090                 case HOME:
4091                 case END:
4092                         result = 0;
4093                         break;
4094
4095                 default:
4096                         if(!ctrl_down())
4097                         {
4098                                 int query_len = strlen(query);
4099                                 if( query_len < (int)sizeof(query)-1 &&
4100                                         top_level->get_keypress() > 30 &&
4101                                         top_level->get_keypress() < 127)
4102                                 {
4103                                         query[query_len++] = top_level->get_keypress();
4104                                         query[query_len] = 0;
4105                                         new_selection = query_list();
4106                                 }
4107                                 else
4108                                 if(top_level->get_keypress() == BACKSPACE)
4109                                 {
4110                                         if(query_len > 0) query[--query_len] = 0;
4111                                         new_selection = query_list();
4112                                 }
4113                                 if( show_query ) {
4114                                         if( query_len > 0 )
4115                                                 show_tooltip(query);
4116                                         else
4117                                                 hide_tooltip();
4118                                 }
4119                                 redraw = 1;
4120                                 result = 1;
4121                         }
4122                         break;
4123         }
4124
4125         if(redraw)
4126         {
4127                 clamp_positions();
4128                 draw_items(0);
4129                 update_scrollbars(1);
4130         }
4131
4132 //printf("BC_ListBox::keypress_event %d new_selection=%d\n", __LINE__, new_selection);
4133         if(new_selection >= 0 && !is_suggestions)
4134         {
4135 //printf("BC_ListBox::keypress_event %d\n", __LINE__);
4136                 selection_changed();
4137 //printf("BC_ListBox::keypress_event %d\n", __LINE__);
4138         }
4139
4140         return result;
4141 }
4142
4143
4144 BC_Pixmap* BC_ListBox::get_bg_surface()
4145 {
4146         return bg_surface;
4147 }
4148
4149
4150 void BC_ListBox::draw_background()
4151 {
4152         if( !bg_draw ) return;
4153         bg_draw = 0;
4154 // White background pixmap
4155         set_color(top_level->get_resources()->listbox_inactive);
4156         draw_box(0, 0, bg_surface->get_w(), bg_surface->get_h(), bg_surface);
4157
4158 // Optional heroine pixmap
4159         if(bg_pixmap)
4160                 bg_surface->draw_pixmap(bg_pixmap,
4161                         bg_surface->get_w() - top_level->get_resources()->listbox_bg->get_w(),
4162                         0);
4163 }
4164
4165 void BC_ListBox::clear_listbox(int x, int y, int w, int h)
4166 {
4167         gui->draw_pixmap(bg_surface, x, y, w, h, x, y - title_h);
4168 }
4169
4170 void BC_ListBox::update_format(int display_format, int redraw)
4171 {
4172         this->display_format = display_format;
4173         if( redraw && gui ) draw_items(1, 1);
4174 }
4175
4176 int BC_ListBox::get_format()
4177 {
4178         return display_format;
4179 }
4180
4181
4182
4183 int BC_ListBox::draw_items(int flush, int draw_bg)
4184 {
4185         if(gui)
4186         {
4187                 BC_Resources *resources = get_resources();
4188
4189 //dump(data, columns);
4190
4191 // Calculate items width
4192                 calculate_item_coords();
4193
4194
4195 // Create and destroy scrollbars as needed
4196                 get_scrollbars();
4197
4198
4199
4200                 if( bg_draw ) this->bg_draw = 1;
4201                 draw_background();
4202
4203                 first_in_view = -1;
4204                 last_in_view = 0;
4205 // Icon display
4206                 if(display_format == LISTBOX_ICONS)
4207                 {
4208                         clear_listbox(2, 2 + title_h, view_w, view_h);
4209
4210                         set_font(MEDIUMFONT);
4211                         for(int i = 0; i < data[master_column].size(); i++)
4212                         {
4213                                 BC_ListBoxItem *item = data[master_column].get(i);
4214                                 if(get_item_x(item) >= -get_item_w(item) &&
4215                                         get_item_x(item) < view_w &&
4216                                         get_item_y(item) >= -get_item_h(item) + title_h &&
4217                                         get_item_y(item) < view_h + title_h)
4218                                 {
4219                                         item->set_in_view(1);
4220                                         if( first_in_view < 0 ) first_in_view = i;
4221                                         last_in_view = i;
4222                                         int item_color = get_item_highlight(data, 0, i);
4223                                         int icon_x, icon_y, icon_w, icon_h;
4224                                         int text_x, text_y, text_w, text_h;
4225
4226 // Draw highlights
4227                                         get_icon_mask(item, icon_x, icon_y, icon_w, icon_h);
4228                                         get_text_mask(item, text_x, text_y, text_w, text_h);
4229
4230
4231                                         if(item_color != resources->listbox_inactive)
4232                                         {
4233                                                 gui->set_color(BLACK);
4234                                                 gui->draw_rectangle(icon_x, icon_y, icon_w, icon_h);
4235                                                 gui->set_color(item_color);
4236                                                 gui->draw_box(icon_x + 1, icon_y + 1, icon_w - 2, icon_h - 2);
4237                                                 gui->set_color(BLACK);
4238                                                 gui->draw_rectangle(text_x, text_y, text_w, text_h);
4239                                                 gui->set_color(item_color);
4240                                                 gui->draw_box(text_x + 1, text_y + 1, text_w - 2, text_h - 2);
4241
4242                                                 if(icon_position == ICON_LEFT)
4243                                                         gui->draw_box(text_x - 1, text_y + 1, 2, text_h - 2);
4244                                                 else
4245                                                 if(icon_position == ICON_TOP)
4246                                                         gui->draw_line(text_x + 1, text_y, text_x + icon_w - 2, text_y);
4247                                                 if(text_x + text_w < icon_x + icon_w)
4248                                                 {
4249                                                         gui->set_color(BLACK);
4250                                                         gui->draw_line(text_x + text_w,
4251                                                                 icon_y + icon_h,
4252                                                                 icon_x + icon_w,
4253                                                                 icon_y + icon_h);
4254                                                 }
4255                                         }
4256
4257 // Draw icons
4258                                         gui->set_color(get_item_color(data, 0, i));
4259                                         if(item->icon)
4260                                                 gui->pixmap->draw_pixmap(item->icon,
4261                                                         icon_x + ICON_MARGIN,
4262                                                         icon_y + ICON_MARGIN);
4263
4264
4265                                         gui->draw_text(text_x + ICON_MARGIN,
4266                                                 text_y + ICON_MARGIN + get_baseline(item),
4267                                                 item->text);
4268                                 }
4269                                 else
4270                                         item->set_in_view(0);
4271                         }
4272                 }
4273                 else
4274 // Text display
4275                 {
4276 // Draw one column at a time so text overruns don't go into the next column
4277 // clear column backgrounds
4278                         int current_toggle = 0;
4279                         for(int j = 0; j < columns; j++)
4280                         {
4281                                 clear_listbox(LISTBOX_BORDER + get_column_offset(j) - xposition,
4282                                         LISTBOX_BORDER + title_h,
4283                                         get_column_width(j, 1),
4284                                         view_h);
4285 // Draw rows in the column recursively
4286                                 draw_text_recursive(data, j, 0, &current_toggle);
4287                         }
4288
4289 // Delete excess expanders
4290                         while(expanders.total > current_toggle)
4291                         {
4292                                 expanders.remove_object();
4293                         }
4294                 }
4295
4296 // draw user images if available
4297                 draw_images();
4298 // Draw titles on top of rows for superposition effect
4299                 draw_titles(0);
4300
4301 // Clear garbage from bottom right corner
4302                 if(xscrollbar && yscrollbar && is_popup)
4303                 {
4304                         gui->draw_top_background(parent_window,
4305                                 popup_w - get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w(),
4306                                 popup_h - get_resources()->hscroll_data[SCROLL_HANDLE_UP]->get_h(),
4307                                 get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w(),
4308                                 get_resources()->hscroll_data[SCROLL_HANDLE_UP]->get_h());
4309                 }
4310
4311 // Draw borders
4312                 draw_border(0);
4313
4314
4315                 if(current_operation == SELECT_RECT)
4316                         draw_rectangle(0);
4317
4318                 gui->flash(flush);
4319         }
4320
4321         return 0;
4322 }
4323
4324
4325 void BC_ListBox::draw_text_recursive(ArrayList<BC_ListBoxItem*> *data,
4326         int column,
4327         int indent,
4328         int *current_toggle)
4329 {
4330         if(!data) return;
4331
4332
4333         BC_Resources *resources = get_resources();
4334
4335         set_font(MEDIUMFONT);
4336         int subindent = 0;
4337
4338 // Search for a branch and make room for toggle if there is one
4339         if(column == 0)
4340         {
4341                 for(int i = 0; i < data[column].size(); i++)
4342                 {
4343                         if(data[column].get(i)->get_sublist())
4344                         {
4345                                 subindent = BC_WindowBase::get_resources()->listbox_expand[0]->get_w();
4346                                 break;
4347                         }
4348                 }
4349         }
4350
4351         row_height = row_ascent = row_descent = 0;
4352         for(int i = 0; i < data[column].total; i++)
4353         {
4354                 BC_ListBoxItem *item = data[column].values[i];
4355                 int ht = get_text_h(item);
4356                 if( ht > row_height ) row_height = ht;
4357                 int bl = get_baseline(item);
4358                 if( bl > row_ascent ) row_ascent = bl;
4359                 int dt = ht - bl;
4360                 if( dt > row_descent ) row_ascent = bl;
4361         }
4362
4363         for(int i = 0; i < data[column].size(); i++)
4364         {
4365 // Draw a row
4366                 BC_ListBoxItem *item = data[column].values[i];
4367                 BC_ListBoxItem *first_item = data[master_column].values[i];
4368
4369                 if(get_item_y(item) >= -get_item_h(item) + title_h &&
4370                         get_item_y(item) < view_h + title_h)
4371                 {
4372                         int row_color = get_item_highlight(data, 0, i);
4373                         int x, y, w, h, column_width;
4374
4375                         get_text_mask(item, x, y, w, h);
4376                         column_width = get_column_width(column, 1);
4377                         if(x + column_width > view_w + LISTBOX_BORDER * 2)
4378                                 column_width = view_w + LISTBOX_BORDER * 2 - x;
4379
4380                         if(row_color != resources->listbox_inactive)
4381                         {
4382                                 gui->set_color(row_color);
4383                                 gui->draw_box(x, y, column_width, h);
4384                                 gui->set_color(BLACK);
4385                                 int xx = x + column_width-1;
4386                                 gui->draw_line(x, y, xx, y);
4387                                 int hh = row_height;
4388                                 if( display_format == LISTBOX_ICON_LIST ) {
4389                                         int ih = get_icon_h(item);
4390                                         if( ih > hh ) hh = ih;
4391                                 }
4392                                 int yy = y + hh-1;
4393                                 gui->draw_line(x, yy, xx, yy);
4394                         }
4395
4396                         gui->set_color(get_item_color(data, column, i));
4397
4398
4399                         if(column == 0 && display_format == LISTBOX_ICON_LIST)
4400                         {
4401                                 if(item->icon)
4402                                 {
4403                                         gui->pixmap->draw_pixmap(item->icon,
4404                                                 x,
4405                                                 y);
4406                                         x += item->icon->get_w() + ICON_MARGIN;
4407                                 }
4408                         }
4409
4410
4411 // Indent only applies to first column
4412                         gui->draw_text(
4413                                 x + LISTBOX_BORDER + LISTBOX_MARGIN +
4414                                         (column == 0 ? indent + subindent : 0),
4415                                 y + get_baseline(item), item->text);
4416                         item->set_in_view(1);
4417                         if( !indent ) {
4418                                 if( first_in_view < 0 ) first_in_view = i;
4419                                 last_in_view = i;
4420                         }
4421
4422 // Update expander
4423                         if(column == 0 &&
4424                                 item->get_sublist() &&
4425                                 item->get_columns())
4426                         {
4427 // Create new expander
4428                                 if(*current_toggle >= expanders.total)
4429                                 {
4430                                         BC_ListBoxToggle *toggle =
4431                                                 new BC_ListBoxToggle(this,
4432                                                         item,
4433                                                         x + LISTBOX_BORDER + LISTBOX_MARGIN + indent,
4434                                                         y);
4435                                         toggle->draw(0);
4436                                         expanders.append(toggle);
4437                                 }
4438                                 else
4439 // Reposition existing expander
4440                                 {
4441                                         BC_ListBoxToggle *toggle = expanders.values[*current_toggle];
4442 //printf("BC_ListBox::draw_text_recursive 1 %d\n", *current_toggle);
4443                                         toggle->update(item,
4444                                                 x + LISTBOX_BORDER + LISTBOX_MARGIN + indent,
4445                                                 y,
4446                                                 0);
4447                                 }
4448                                 (*current_toggle)++;
4449                         }
4450
4451
4452
4453                 }
4454                 else
4455                         item->set_in_view(0);
4456
4457 // Descend into sublist
4458                 if(first_item->get_expand())
4459                 {
4460                         draw_text_recursive(first_item->get_sublist(),
4461                                 column,
4462                                 indent + LISTBOX_INDENT,
4463                                 current_toggle);
4464                 }
4465         }
4466 }
4467
4468
4469
4470
4471 int BC_ListBox::draw_border(int flash)
4472 {
4473         BC_Resources *resources = top_level->get_resources();
4474         gui->draw_3d_border(0,
4475                 0,
4476                 view_w + LISTBOX_BORDER * 2,
4477                 view_h + title_h + LISTBOX_BORDER * 2,
4478                 resources->listbox_border1,
4479                 list_highlighted ?
4480                         resources->listbox_border2_hi :
4481                         resources->listbox_border2,
4482                 list_highlighted ?
4483                         resources->listbox_border3_hi :
4484                         resources->listbox_border3,
4485                 resources->listbox_border4);
4486
4487         if(flash)
4488         {
4489                 gui->flash();
4490                 gui->flush();
4491         }
4492         return 0;
4493 }
4494
4495 void BC_ListBox::draw_title(int number)
4496 {
4497 // Column title background
4498         int image_number = 0;
4499         if(number == highlighted_title)
4500         {
4501                 image_number = 1;
4502                 if(current_operation == COLUMN_DN)
4503                         image_number = 2;
4504         }
4505
4506         int column_offset = get_column_offset(number) - xposition + LISTBOX_BORDER;
4507         int column_width = get_column_width(number, 1);
4508         gui->draw_3segmenth(get_column_offset(number) - xposition + LISTBOX_BORDER,
4509                 LISTBOX_BORDER,
4510                 get_column_width(number, 1) + get_resources()->listbox_title_overlap,
4511                 column_bg[image_number]);
4512
4513 // Column title sort order
4514         if(number == sort_column)
4515         {
4516                 BC_Pixmap *src;
4517                 if(sort_order == SORT_ASCENDING)
4518                         src = column_sort_dn;
4519                 else
4520                         src = column_sort_up;
4521
4522                 int x = column_offset +
4523                         column_width -
4524                         LISTBOX_BORDER;
4525                 if(x > items_w) x = items_w;
4526                 x -= 5 + src->get_w();
4527                 gui->draw_pixmap(src,
4528                         x,
4529                         title_h / 2 - src->get_h() / 2 + LISTBOX_BORDER);
4530         }
4531
4532
4533         int x = -xposition +
4534                 get_column_offset(number) +
4535                 LISTBOX_MARGIN +
4536                 LISTBOX_BORDER;
4537         x += get_resources()->listbox_title_margin;
4538
4539         gui->set_color(get_resources()->listbox_title_color);
4540         gui->draw_text(x,
4541                 LISTBOX_MARGIN + LISTBOX_BORDER + get_text_ascent(MEDIUMFONT),
4542                 _(column_titles[number]));
4543 }
4544
4545 int BC_ListBox::draw_titles(int flash)
4546 {
4547         if(column_titles &&
4548                 (display_format == LISTBOX_TEXT ||
4549                 display_format == LISTBOX_ICON_LIST))
4550         {
4551 //printf("BC_ListBox::draw_titles 1 %d\n", highlighted_title);
4552                 for(int i = 0; i < columns; i++)
4553                 {
4554                         if(i != highlighted_title)
4555                                 draw_title(i);
4556                 }
4557
4558                 if(highlighted_title >= 0) draw_title(highlighted_title);
4559                 draw_border(0);
4560         }
4561
4562         if(flash)
4563         {
4564                 gui->flash();
4565         }
4566         return 0;
4567 }
4568
4569 void BC_ListBox::draw_toggles(int flash)
4570 {
4571         for(int i = 0; i < expanders.total; i++)
4572                 expanders.values[i]->draw(0);
4573
4574 //printf("BC_ListBox::draw_toggles 1 %d\n", flash);
4575         if(flash && expanders.total)
4576         {
4577                 gui->flash();
4578                 gui->flush();
4579         }
4580 }
4581
4582 int BC_ListBox::draw_rectangle(int flash)
4583 {
4584         int x1 = MIN(rect_x1, rect_x2);
4585         int x2 = MAX(rect_x1, rect_x2);
4586         int y1 = MIN(rect_y1, rect_y2);
4587         int y2 = MAX(rect_y1, rect_y2);
4588
4589         if(x1 == x2 || y1 == y2) return 0;
4590
4591         gui->set_inverse();
4592         gui->set_color(WHITE);
4593         gui->draw_rectangle(x1, y1, x2 - x1, y2 - y1);
4594         gui->set_opaque();
4595
4596
4597         if(flash)
4598         {
4599                 gui->flash(1);
4600         }
4601         return 0;
4602 }
4603
4604 void BC_ListBox::dump(ArrayList<BC_ListBoxItem*> *data,
4605         int columns,
4606         int indent,
4607         int master_column)
4608 {
4609         if(!indent)
4610         {
4611                 printf("BC_ListBox::dump 1\n");
4612         }
4613
4614         for(int i = 0; i < data[master_column].total; i++)
4615         {
4616                 for(int k = 0; k < indent; k++)
4617                         printf(" ");
4618                 for(int j = 0; j < columns; j++)
4619                 {
4620                         BC_ListBoxItem *item = data[j].values[i];
4621                         printf("%d,%d,%d=%s ",
4622                                 item->get_text_x(),
4623                                 item->get_text_y(),
4624                                 item->autoplace_text,
4625                                 item->get_text());
4626                 }
4627                 printf("\n");
4628
4629                 if(data[master_column].values[i]->get_sublist())
4630                 {
4631                         dump(data[master_column].values[i]->get_sublist(),
4632                                 data[master_column].values[i]->get_columns(),
4633                                 indent + 4,
4634                                 master_column);
4635                 }
4636         }
4637
4638
4639 }
4640
4641
4642
4643