r1015: Add a shortcut 'e' that toggles between selection and drag&drop edit modes.
[cinelerra_cv/ct.git] / guicast / bclistbox.C
blob8c9bc70e4df5dff3a1940b37a928db4bd21037e4
1 #include "bcdragwindow.h"
2 #include "bclistbox.h"
3 #include "bclistboxitem.h"
4 #include "bcpixmap.h"
5 #include "bcresources.h"
6 #include "bcsignals.h"
7 #include "clip.h"
8 #include "cursors.h"
9 #include "fonts.h"
10 #include "keys.h"
11 #include "language.h"
12 #include "bctimer.h"
13 #include "vframe.h"
15 #include <string.h>
16 #include <unistd.h>
18 // ====================================================== scrollbars
21 BC_ListBoxYScroll::BC_ListBoxYScroll(BC_ListBox *listbox, 
22                           int total_height, 
23                                           int view_height, 
24                           int position)
25  : BC_ScrollBar(listbox->get_yscroll_x(), 
26         listbox->get_yscroll_y(), 
27         SCROLL_VERT, 
28         listbox->get_yscroll_height(), 
29         total_height, 
30         position, 
31         view_height)
33         this->listbox = listbox;
36 BC_ListBoxYScroll::~BC_ListBoxYScroll()
40 int BC_ListBoxYScroll::handle_event()
42         listbox->set_yposition(get_value());
43         return 1;
52 BC_ListBoxXScroll::BC_ListBoxXScroll(BC_ListBox *listbox, 
53                           int total_width, 
54                                           int view_width,
55                           int position)
56  : BC_ScrollBar(listbox->get_xscroll_x(), 
57         listbox->get_xscroll_y(), 
58         SCROLL_HORIZ, 
59         listbox->get_xscroll_width(), 
60         total_width, 
61         position, 
62         view_width)
64         this->listbox = listbox;
67 BC_ListBoxXScroll::~BC_ListBoxXScroll()
71 int BC_ListBoxXScroll::handle_event()
73         listbox->set_xposition(get_value());
74         return 1;
84 BC_ListBoxToggle::BC_ListBoxToggle(BC_ListBox *listbox, 
85         BC_ListBoxItem *item, 
86         int x, 
87         int y)
89         this->listbox = listbox;
90         this->item = item;
91         this->x = x;
92         this->y = y;
93         this->value = item->get_expand();
94         if(this->value) 
95                 state = BC_Toggle::TOGGLE_CHECKED;
96         else
97                 state = BC_Toggle::TOGGLE_UP;
100 void BC_ListBoxToggle::update(BC_ListBoxItem *item, 
101         int x, 
102         int y,
103         int flash)
105         this->value = item->get_expand();
106         this->item = item;
107         this->x = x;
108         this->y = y;
110 // update state
111         switch(state)
112         {
113                 case TOGGLE_UP:
114                         if(value)
115                                 state = TOGGLE_CHECKED;
116                         break;
118                 case TOGGLE_UPHI:
119                         if(value)
120                                 state = TOGGLE_CHECKEDHI;
121                         break;
123                 case TOGGLE_CHECKED:
124                         if(!value)
125                                 state = TOGGLE_UP;
126                         break;
128                 case TOGGLE_DOWN:
129                         break;
131                 case TOGGLE_CHECKEDHI:
132                         if(!value)
133                                 state = TOGGLE_UPHI;
134                         break;
136                 case TOGGLE_DOWN_EXIT:
137                         break;
138         }
141         draw(flash);
144 int BC_ListBoxToggle::cursor_motion_event(int *redraw_toggles)
146         int w = listbox->toggle_images[0]->get_w();
147         int h = listbox->toggle_images[0]->get_h();
148         int cursor_inside = listbox->get_cursor_x() >= x && 
149                 listbox->get_cursor_x() < x + w &&
150                 listbox->get_cursor_y() >= y && 
151                 listbox->get_cursor_y() < y + h;
152         int result = 0;
154         switch(state)
155         {
156                 case BC_ListBoxToggle::TOGGLE_UPHI:
157                         if(!cursor_inside)
158                         {
159                                 state = BC_ListBoxToggle::TOGGLE_UP;
160                                 *redraw_toggles = 1;
161                         }
162                         break;
164                 case BC_ListBoxToggle::TOGGLE_CHECKEDHI:
165                         if(!cursor_inside)
166                         {
167                                 state = BC_ListBoxToggle::TOGGLE_CHECKED;
168                                 *redraw_toggles = 1;
169                         }
170                         break;
172                 case BC_ListBoxToggle::TOGGLE_DOWN:
173                         if(!cursor_inside)
174                         {
175                                 state = BC_ListBoxToggle::TOGGLE_DOWN_EXIT;
176                                 *redraw_toggles = 1;
177                         }
178                         result = 1;
179                         break;
181                 case BC_ListBoxToggle::TOGGLE_DOWN_EXIT:
182                         if(cursor_inside)
183                         {
184                                 state = BC_ListBoxToggle::TOGGLE_DOWN;
185                                 *redraw_toggles = 1;
186                         }
187                         result = 1;
188                         break;
190                 default:
191                         if(cursor_inside)
192                         {
193                                 if(value)
194                                         state = BC_ListBoxToggle::TOGGLE_CHECKEDHI;
195                                 else
196                                         state = BC_ListBoxToggle::TOGGLE_UPHI;
197                                 *redraw_toggles = 1;
198                         }
199                         break;
200         }
201         return result;
204 int BC_ListBoxToggle::cursor_leave_event(int *redraw_toggles)
206         if(value)
207                 state = BC_ListBoxToggle::TOGGLE_CHECKED;
208         else
209                 state = BC_ListBoxToggle::TOGGLE_UP;
212 int BC_ListBoxToggle::button_press_event()
214         int w = listbox->toggle_images[0]->get_w();
215         int h = listbox->toggle_images[0]->get_h();
217         if(listbox->gui->get_cursor_x() >= x && 
218                 listbox->gui->get_cursor_x() < x + w &&
219                 listbox->gui->get_cursor_y() >= y && 
220                 listbox->gui->get_cursor_y() < y + h)
221         {
222                 state = BC_ListBoxToggle::TOGGLE_DOWN;
223                 return 1;
224         }
225         return 0;
228 int BC_ListBoxToggle::button_release_event(int *redraw_toggles)
230         int result = 0;
231         switch(state)
232         {
233                 case BC_ListBoxToggle::TOGGLE_DOWN:
234                         value = !value;
235                         if(value)
236                                 state = BC_ListBoxToggle::TOGGLE_CHECKEDHI;
237                         else
238                                 state = BC_ListBoxToggle::TOGGLE_UPHI;
239                         listbox->expand_item(item, value);
240                         result = 1;
241                         break;
243                 case BC_ListBoxToggle::TOGGLE_DOWN_EXIT:
244                         if(value)
245                                 state = BC_ListBoxToggle::TOGGLE_CHECKED;
246                         else
247                                 state = BC_ListBoxToggle::TOGGLE_UP;
248                         *redraw_toggles = 1;
249                         result = 1;
250                         break;
251         }
252         return result;
255 void BC_ListBoxToggle::draw(int flash)
257         if(listbox->gui)
258         {
259                 int image_number = 0;
260                 int w = listbox->toggle_images[0]->get_w();
261                 int h = listbox->toggle_images[0]->get_h();
263                 switch(state)
264                 {
265                         case BC_ListBoxToggle::TOGGLE_UP: image_number = 0; break;
266                         case BC_ListBoxToggle::TOGGLE_UPHI: image_number = 1; break;
267                         case BC_ListBoxToggle::TOGGLE_CHECKED: image_number = 2; break;
268                         case BC_ListBoxToggle::TOGGLE_DOWN: image_number = 3; break;
269                         case BC_ListBoxToggle::TOGGLE_CHECKEDHI: image_number = 4; break;
270                         case BC_ListBoxToggle::TOGGLE_DOWN_EXIT:
271                                 if(value)
272                                         image_number = 2;
273                                 else
274                                         image_number = 0;
275                                 break;
276                 }
278 //printf("BC_ListBoxToggle::draw 1 %d\n", state);
279                 listbox->gui->draw_pixmap(listbox->toggle_images[image_number],
280                         x,
281                         y);
284                 if(flash)
285                 {
286                         listbox->gui->flash(x, y, w, h);
287                         listbox->gui->flush();
288                 }
289         }
305 // ====================================================== box
307 BC_ListBox::BC_ListBox(int x, 
308         int y, 
309         int w, 
310         int h,
311         int display_format,
312         ArrayList<BC_ListBoxItem*> *data,
313         char **column_titles,
314         int *column_width,
315         int columns,
316         int yposition,
317         int is_popup,
318         int selection_mode,
319         int icon_position,
320         int allow_drag)
321  : BC_SubWindow(x, y, w, h, -1)
323         justify = LISTBOX_RIGHT;
324         xposition = 0;
325         highlighted_item = -1;
326         highlighted_title = -1;
327         highlighted_division = -1;
328         highlighted_ptr = 0;
329         xscrollbar = 0;
330         yscrollbar = 0;
331         current_cursor = ARROW_CURSOR;
332         gui = 0;
333         view_h = 0;
334         view_w = 0;
335         title_h = 0;
336         active = 0;
337         new_value = 0;
338         need_xscroll = 0;
339         need_yscroll = 0;
340         bg_tile = 0;
341         drag_popup = 0;
342         selection_number1 = -1;
343         selection_number2 = -1;
344         bg_surface = 0;
345         bg_pixmap = 0;
347         current_operation = NO_OPERATION;
348         button_highlighted = 0;
350         disabled = 0;
352         list_highlighted = 0;
354         allow_drag_scroll = 1;
355         process_drag = 1;
357         sort_column = -1;
358         sort_order = 0;
360         allow_drag_column = 0;
361         master_column = 0;
362         search_column = 0;
364         popup_w = w;
365         popup_h = h;
367         for(int i = 0; i < 3; i++)
368                 column_bg[i] = 0;
370         for(int i = 0; i < 4; i++)
371                 button_images[i] = 0;
373         for(int i = 0; i < 5; i++)
374                 toggle_images[i] = 0;
376         column_sort_up = 0;
377         column_sort_dn = 0;
379 //printf("BC_ListBox::BC_ListBox 1\n");
380         this->data = data;
381         this->columns = columns;
382         this->yposition = yposition;
383         this->is_popup = is_popup;
384         this->display_format = display_format;
385         this->selection_mode = selection_mode;
386         this->icon_position = icon_position;
387         this->allow_drag = allow_drag;
388         this->column_titles = 0;
389         this->column_width = 0;
390 //printf("BC_ListBox::BC_ListBox 1\n");
392         if((!column_titles && column_width) ||
393                 (column_titles && !column_width))
394         {
395                 printf("BC_ListBox::BC_ListBox either column_titles or column_widths == NULL but not both.\n");
396         }
397 //printf("BC_ListBox::BC_ListBox 2 %p %p\n", column_titles, column_width);
398         set_columns(column_titles, 
399                 column_width, 
400                 columns);
402 //printf("BC_ListBox::BC_ListBox 3\n");
404         drag_icon_vframe = 0;
405         drag_column_icon_vframe = 0;
409 // reset the search engine
410 //printf("BC_ListBox::BC_ListBox 4\n");
411         reset_query();
412 //printf("BC_ListBox::BC_ListBox 5\n");
415 BC_ListBox::~BC_ListBox()
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++)
423                 if(column_bg[i]) delete column_bg[i];
424         for(int i = 0; i < 4; i++)
425                 if(button_images[i]) delete button_images[i];
426         for(int i = 0; i < 5; i++)
427                 if(toggle_images[i]) delete toggle_images[i];
428         if(column_sort_up) delete column_sort_up;
429         if(column_sort_dn) delete column_sort_dn;
431         delete_columns();
432         if(drag_popup) delete drag_popup;
435 int BC_ListBox::enable()
437   disabled = 0;
438   draw_button();
439   return 1;
442 int BC_ListBox::disable()
444   disabled = 1;
445   draw_button();
446   return 1;
449 void BC_ListBox::reset_query()
451         query[0] = 0;  // reset query
454 int BC_ListBox::evaluate_query(int list_item, char *string)
456         return(strcmp(string, data[search_column].values[list_item]->text) <= 0 && 
457                 data[search_column].values[list_item]->searchable);
460 int BC_ListBox::query_list()
462         if(query[0] == 0) return 0;
464         int done = 0;
465         int result;
466         int selection_changed = 0;
467         int prev_selection = -1;
468         for(int i = 0; !done && i < data[0].total; i++)
469         {
470                 if(evaluate_query(i, query))
471                 {
472                         result = i;
473                         done = 1;
474                 }
475         }
477         if(done)
478         {
479 // Deselect all
480                 for(int i = 0; i < data[0].total; i++)
481                 {
482                         for(int j = 0; j < columns; j++)
483                         {
484                                 if(data[j].values[i]->selected) prev_selection = i;
485                                 data[j].values[i]->selected = 0;
486                         }
487                 }
489 // Select one
490                 if(prev_selection != result)
491                         selection_changed = 1;
492                 for(int j = 0; j < columns; j++)
493                 {
494                         data[j].values[result]->selected = 1;
495                 }
496                 center_selection(result);
497         }
499         return selection_changed;
502 void BC_ListBox::init_column_width()
504         if(!column_width && data)
505         {
506                 int widest = 5, w;
507                 for(int i = 0; i < data[0].total; i++)
508                 {
509                 w = get_text_width(MEDIUMFONT, data[0].values[i]->get_text()) + 2 * LISTBOX_MARGIN;
510                         if(w > widest) widest = w;
511                 }
512                 default_column_width[0] = widest;
513         }
516 int BC_ListBox::initialize()
518         if(is_popup)
519         {
520                 for(int i = 0; i < 4; i++)
521                 {
522                         button_images[i] = new BC_Pixmap(parent_window, 
523                                 BC_WindowBase::get_resources()->listbox_button[i], 
524                                 PIXMAP_ALPHA);
525                 }
526                 w = button_images[0]->get_w();
527                 h = button_images[0]->get_h();
528                 
529                 gui = 0;
530                 current_operation = NO_OPERATION;
531         }
532         else
533         {
534                 gui = this;
535                 current_operation = NO_OPERATION;
536         }
538         for(int i = 0; i < 3; i++)
539         {
540                 column_bg[i] = new BC_Pixmap(parent_window,
541                         get_resources()->listbox_column[i],
542                         PIXMAP_ALPHA);
543         }
544         for(int i = 0; i < 5; i++)
545         {
546                 toggle_images[i] = new BC_Pixmap(parent_window,
547                         get_resources()->listbox_expand[i],
548                         PIXMAP_ALPHA);
549         }
551         column_sort_up = new BC_Pixmap(parent_window, 
552                 BC_WindowBase::get_resources()->listbox_up, 
553                 PIXMAP_ALPHA);
554         column_sort_dn = new BC_Pixmap(parent_window, 
555                 BC_WindowBase::get_resources()->listbox_dn, 
556                 PIXMAP_ALPHA);
558 //printf("BC_ListBox::initialize 10\n");
559         drag_icon_vframe = get_resources()->type_to_icon[ICON_UNKNOWN];
560         drag_column_icon_vframe = get_resources()->type_to_icon[ICON_COLUMN];
561 // = new BC_Pixmap(parent_window, 
562 //              get_resources()->type_to_icon[ICON_UNKNOWN], 
563 //              PIXMAP_ALPHA);
564 //      drag_column_icon = new BC_Pixmap(parent_window,
565 //              get_resources()->type_to_icon[ICON_COLUMN],
566 //              PIXMAP_ALPHA);
567         BC_SubWindow::initialize();
569         init_column_width();
571         if(top_level->get_resources()->listbox_bg)
572                 bg_pixmap = new BC_Pixmap(this, 
573                         get_resources()->listbox_bg, 
574                         PIXMAP_OPAQUE);
576         draw_button();
577         draw_items(1);
578         return 0;
581 void BC_ListBox::deactivate_selection()
583         current_operation = NO_OPERATION;
586 int BC_ListBox::draw_button()
588 // Draw the button for a popup listbox
589         if(is_popup)
590         {
591                 int image_number = 0;
593                 draw_top_background(parent_window, 0, 0, w, h);
595                 if(button_highlighted)
596                         image_number = 1;
597                 if(current_operation == BUTTON_DN)
598                         image_number = 2;
599                 if(disabled)
600                         image_number = 3;
603                 pixmap->draw_pixmap(button_images[image_number], 
604                         0, 
605                         0,
606                         w,
607                         h,
608                         0,
609                         0);
610                 flash();
611         }
612         return 0;
615 int BC_ListBox::calculate_item_coords()
617         if(!data) return 0;
619         int icon_x = 0;
620         int next_icon_x = 0;
621         int next_icon_y = 0;
622         int next_text_y = 0;
623 // Change the display_format to get the right item dimensions for both
624 // text and icons.
625         int display_format_temp = display_format;
628 // Scan the first column for lowest y coord of all text
629 // and lowest right x and y coord for all icons which aren't auto placable
630         calculate_last_coords_recursive(data,
631                 &icon_x,
632                 &next_icon_x, 
633                 &next_icon_y,
634                 &next_text_y,
635                 1);
637 // Reset last column width.  It's recalculated based on text width.
639         calculate_item_coords_recursive(data,
640                 &icon_x,
641                 &next_icon_x, 
642                 &next_icon_y,
643                 &next_text_y,
644                 1);
648         display_format = display_format_temp;
650         return 0;
653 void BC_ListBox::calculate_last_coords_recursive(
654         ArrayList<BC_ListBoxItem*> *data,
655         int *icon_x,
656         int *next_icon_x,
657         int *next_icon_y,
658         int *next_text_y,
659         int top_level)
661         for(int i = 0; i < data[0].total; i++)
662         {
663                 int current_text_y = 0;
664                 int current_icon_x = 0;
665                 int current_icon_y = 0;
666                 BC_ListBoxItem *item = data[0].values[i];
668 // Get next_text_y
669                 if(!item->autoplace_text)
670                 {
671 // Lowest text coordinate
672                         display_format = LISTBOX_TEXT;
673                         current_text_y = item->text_y + 
674                                 get_text_height(MEDIUMFONT);
675                         if(current_text_y > *next_text_y)
676                                 *next_text_y = current_text_y;
678 // Add sublist depth if it is expanded
679                         if(item->get_sublist() && 
680                                 item->get_columns() &&
681                                 item->get_expand())
682                         {
683                                 calculate_last_coords_recursive(item->get_sublist(),
684                                         icon_x,
685                                         next_icon_x, 
686                                         next_icon_y,
687                                         next_text_y,
688                                         0);
689                         }
690                 }
692 // Get next_icon coordinate
693                 if(top_level)
694                 {
695                         BC_ListBoxItem *item = data[master_column].values[i];
696                         if(!item->autoplace_icon)
697                         {
698                                 display_format = LISTBOX_ICONS;
699 // Lowest right icon coordinate.
700                                 current_icon_x = item->icon_x;
701                                 if(current_icon_x > *icon_x) *icon_x = current_icon_x;
702                                 if(current_icon_x + get_item_w(item) > *next_icon_x)
703                                         *next_icon_x = current_icon_x + get_item_w(item);
705                                 current_icon_y = item->icon_y + get_item_h(item);
706                                 if(current_icon_y > *next_icon_y) 
707                                         *next_icon_y = current_icon_y;
708                         }
709                 }
710         }
714 void BC_ListBox::calculate_item_coords_recursive(
715         ArrayList<BC_ListBoxItem*> *data,
716         int *icon_x,
717         int *next_icon_x,
718         int *next_icon_y,
719         int *next_text_y,
720         int top_level)
725 // Set up items which need autoplacement.
726 // Should fill icons down and then across
727         for(int i = 0; i < data[0].total; i++)
728         {
729 // Don't increase y unless the row requires autoplacing.
730                 int total_autoplaced_columns = 0;
732 // Set up icons in first column
733                 if(top_level)
734                 {
735                         BC_ListBoxItem *item = data[master_column].values[i];
736                         if(item->autoplace_icon)
737                         {
738 // 1 column only if icons are used
739                                 display_format = LISTBOX_ICONS;
740 // Test row height
741 // Start new row.
742                                 if(*next_icon_y + get_item_h(item) >= get_h() && 
743                                         *next_icon_y > 0)
744                                 {
745                                         *icon_x = *next_icon_x;
746                                         *next_icon_y = 0;
747                                 }
749                                 if(*icon_x + get_item_w(item) > *next_icon_x)
750                                         *next_icon_x = *icon_x + get_item_w(item);
753                                 item->set_icon_x(*icon_x);
754                                 item->set_icon_y(*next_icon_y);
756                                 *next_icon_y += get_item_h(item);
757                         }
758                 }
762 // Set up a text row
763                 int next_text_x = 0;
764                 for(int j = 0; j < columns; j++)
765                 {
766                         BC_ListBoxItem *item = data[j].values[i];
767                         if(item->autoplace_text)
768                         {
769                                 display_format = LISTBOX_TEXT;
770                                 item->set_text_x(next_text_x);
771                                 item->set_text_y(*next_text_y);
773 // printf("BC_ListBox::calculate_item_coords_recursive %p %d %d %d %d %s \n", 
774 // item->get_sublist(), 
775 // item->get_columns(), 
776 // item->get_expand(), 
777 // next_text_x, 
778 // *next_text_y,
779 // item->get_text());
780 // Increment position of next column
781                                 if(j < columns - 1)
782                                         next_text_x += (column_width ? 
783                                                 column_width[j] : 
784                                                 default_column_width[j]);
785                                 else
786 // Set last column width based on text width
787                                 {
788                                         int new_w = get_item_w(item);
790                                         int *previous_w = (column_width ? 
791                                                 &column_width[j] : 
792                                                 &default_column_width[j]);
793                                         if(new_w > *previous_w)
794                                                 *previous_w = new_w;
795 //printf("BC_ListBox::calculate_item_coords_recursive 1 %d\n", new_w);
796                                 }
797                                 total_autoplaced_columns++;
798                         }
799                 }
801 // Increase the text vertical position
802                 if(total_autoplaced_columns)
803                 {
804                         display_format = LISTBOX_TEXT;
805                         *next_text_y += get_text_height(MEDIUMFONT);
806                 }
808 // Set up a sublist
809                 BC_ListBoxItem *item = data[master_column].values[i];
810                 if(item->get_sublist() &&
811                         item->get_columns() &&
812                         item->get_expand())
813                 {
814                         calculate_item_coords_recursive(
815                                 item->get_sublist(),
816                                 icon_x,
817                                 next_icon_x,
818                                 next_icon_y,
819                                 next_text_y,
820                                 0);
821                 }
822         }
825 void BC_ListBox::set_justify(int value)
827         this->justify = value;
830 void BC_ListBox::set_allow_drag_column(int value)
832         this->allow_drag_column = value;
835 void BC_ListBox::set_process_drag(int value)
837         this->process_drag = value;
840 void BC_ListBox::set_master_column(int value, int redraw)
842         this->master_column = value;
843         if(redraw)
844         {
845                 draw_items(1);
846         }
849 void BC_ListBox::set_search_column(int value)
851         this->search_column = value;
854 int BC_ListBox::get_sort_column()
856         return sort_column;
859 void BC_ListBox::set_sort_column(int value, int redraw)
861         sort_column = value;
862         if(redraw)
863         {
864                 draw_titles(1);
865         }
868 int BC_ListBox::get_sort_order()
870         return sort_order;
873 void BC_ListBox::set_sort_order(int value, int redraw)
875         sort_order = value;
876         if(redraw)
877         {
878                 draw_titles(1);
879         }
886 int BC_ListBox::get_display_mode()
888         return display_format;
891 int BC_ListBox::get_yposition()
893         return yposition;
896 int BC_ListBox::get_xposition()
898         return xposition;
901 int BC_ListBox::get_highlighted_item()
903         return highlighted_item;
907 int BC_ListBox::get_item_x(BC_ListBoxItem *item)
909         if(display_format == LISTBOX_TEXT)
910                 return item->text_x - xposition + 2;
911         else
912                 return item->icon_x - xposition + 2;
915 int BC_ListBox::get_item_y(BC_ListBoxItem *item)
917         int result;
918         if(display_format == LISTBOX_TEXT)
919                 result = item->text_y - yposition + title_h + 2;
920         else
921                 result = item->icon_y - yposition + title_h + 2;
922         return result;
925 int BC_ListBox::get_item_w(BC_ListBoxItem *item)
927         if(display_format == LISTBOX_ICONS)
928         {
929                 int x, y, w, h;
930                 get_icon_mask(item, x, y, w, h);
931                 int icon_w = w;
932                 get_text_mask(item, x, y, w, h);
933                 int text_w = w;
935                 if(icon_position == ICON_LEFT)
936                         return icon_w + text_w;
937                 else
938                         return (icon_w > text_w) ? icon_w : text_w;
939         }
940         else
941         {
942                 return get_text_width(MEDIUMFONT, item->text) + 2 * LISTBOX_MARGIN;
943         }
946 int BC_ListBox::get_item_h(BC_ListBoxItem *item)
948         if(display_format == LISTBOX_ICONS)
949         {
950                 int x, y, w, h;
951                 get_icon_mask(item, x, y, w, h);
952                 int icon_h = h;
953                 get_text_mask(item, x, y, w, h);
954                 int text_h = h;
956                 if(icon_position == ICON_LEFT)
957                         return (icon_h > text_h) ? icon_h : text_h;
958                 else
959                         return icon_h + text_h;
960         }
961         else
962         {
963                 return get_text_height(MEDIUMFONT);
964         }
965         return 0;
969 int BC_ListBox::get_icon_w(BC_ListBoxItem *item)
971         BC_Pixmap *icon = item->icon;
972         if(icon) return icon->get_w();
973         return 0;
976 int BC_ListBox::get_icon_h(BC_ListBoxItem *item)
978         BC_Pixmap *icon = item->icon;
979         if(icon) return icon->get_h();
980         return 0;
983 int BC_ListBox::get_items_width()
985         int widest = 0;
987         if(display_format == LISTBOX_ICONS)
988         {
989                 for(int i = 0; i < columns; i++)
990                 {
991                         for(int j = 0; j < data[i].total; j++)
992                         {
993                                 int x1, x, y, w, h;
994                                 BC_ListBoxItem *item = data[i].values[j];
995                                 x1 = item->icon_x;
997                                 get_icon_mask(item, x, y, w, h);
998                                 if(x1 + w > widest) widest = x1 + w;
1000                                 if(display_format == LISTBOX_ICONS && icon_position == ICON_LEFT)
1001                                         x1 += w;
1003                                 get_text_mask(item, x, y, w, h);
1004                                 if(x1 + w > widest) widest = x1 + w;
1005                         }
1006                 }
1007         }
1008         else
1009         if(display_format == LISTBOX_TEXT)
1010         {
1011                 return get_column_offset(columns);
1012         }
1013         return widest;
1016 int BC_ListBox::get_items_height(ArrayList<BC_ListBoxItem*> *data, 
1017         int columns,
1018         int *result)
1020         int temp = 0;
1021         int top_level = 0;
1022         int highest = 0;
1023         if(!result)
1024         {
1025                 result = &temp;
1026                 top_level = 1;
1027         }
1033         for(int j = 0; j < (data ? data[master_column].total : 0); j++)
1034         {
1035                 int y1, x, y, w, h;
1036                 BC_ListBoxItem *item = data[master_column].values[j];
1038                 if(display_format == LISTBOX_ICONS)
1039                 {
1040                         get_icon_mask(item, x, y, w, h);
1041                         if(y + h + yposition > highest) highest = y + h + yposition;
1043                         get_text_mask(item, x, y, w, h);
1044                         if(y + h + yposition > highest) highest = y + h + yposition;
1045                 }
1046                 else
1047                 {
1048                         get_text_mask(item, x, y, w, h);
1049                         *result += h;
1052 // Descend into sublist
1053                         if(item->get_sublist() &&
1054                                 item->get_expand())
1055                         {
1056                                 get_items_height(item->get_sublist(), 
1057                                         item->get_columns(), 
1058                                         result);
1059                         }
1060                 }
1061         }
1062         if(display_format == LISTBOX_TEXT && top_level) 
1063         {
1064                 highest = LISTBOX_MARGIN + *result;
1065         }
1068         return highest;
1071 int BC_ListBox::set_yposition(int position, int draw_items)
1073         this->yposition = position;
1074         if(draw_items)
1075         {
1076                 this->draw_items(1);
1077         }
1078         return 0;
1081 int BC_ListBox::set_xposition(int position)
1083         this->xposition = position;
1084         draw_items(1);
1085         return 0;
1088 void BC_ListBox::expand_item(BC_ListBoxItem *item, int expand)
1090         if(item)
1091         {
1092                 item->expand = expand;
1093 // Collapse sublists if this is collapsed to make it easier to calculate
1094 // coordinates
1095                 if(item->get_sublist())
1096                         collapse_recursive(item->get_sublist(), master_column);
1099 // Set everything for autoplacement
1100                 
1101                 set_autoplacement(data, 0, 1);
1103                 draw_items(1);
1104         }
1107 void BC_ListBox::collapse_recursive(ArrayList<BC_ListBoxItem*> *data,
1108                 int master_column)
1110         for(int i = 0; i < data[master_column].total; i++)
1111         {
1112                 BC_ListBoxItem *item = data[master_column].values[i];
1113                 if(item->get_sublist() && item->expand)
1114                 {
1115                         item->expand = 0;
1116                         collapse_recursive(item->get_sublist(), master_column);
1117                 }
1118         }
1121 void BC_ListBox::set_autoplacement(ArrayList<BC_ListBoxItem*> *data,
1122         int do_icons, 
1123         int do_text)
1125         for(int i = 0; i < data[0].total; i++)
1126         {
1127                 for(int j = 0; j < columns; j++)
1128                 {
1129                         if(do_icons) data[j].values[i]->autoplace_icon = 1;
1130                         if(do_text) data[j].values[i]->autoplace_text = 1;
1131                 }
1133                 BC_ListBoxItem *item = data[master_column].values[i];
1134                 if(item->get_sublist())
1135                 {
1136                         set_autoplacement(item->get_sublist(), do_icons, do_text);
1137                 }
1138         }
1143 int BC_ListBox::get_w()
1145         if(is_popup)
1146                 return BCPOPUPLISTBOX_W;
1147         else
1148                 return popup_w;
1151 int BC_ListBox::get_h()
1153         if(is_popup)
1154                 return BCPOPUPLISTBOX_H;
1155         else
1156                 return popup_h;
1159 int BC_ListBox::get_yscroll_x()
1161         if(is_popup)
1162                 return popup_w - get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w();
1163         else
1164                 return get_x() + 
1165                         popup_w - 
1166                         get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w();
1169 int BC_ListBox::get_yscroll_y()
1171         if(is_popup)
1172                 return 0;
1173         else
1174                 return get_y();
1177 int BC_ListBox::get_yscroll_height()
1179         return popup_h - (need_xscroll ? 
1180                 get_resources()->hscroll_data[SCROLL_HANDLE_UP]->get_h() : 
1181                 0);
1184 int BC_ListBox::get_xscroll_x()
1186         if(is_popup)
1187                 return 0;
1188         else
1189                 return get_x();
1192 int BC_ListBox::get_xscroll_y()
1194         if(is_popup)
1195                 return popup_h - 
1196                         get_resources()->hscroll_data[SCROLL_HANDLE_UP]->get_h();
1197         else
1198                 return get_y() + 
1199                         popup_h - 
1200                         get_resources()->hscroll_data[SCROLL_HANDLE_UP]->get_h();
1203 int BC_ListBox::get_xscroll_width()
1205         return popup_w - (need_yscroll ? 
1206                 get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w() : 
1207                 0);
1210 int BC_ListBox::get_column_offset(int column)
1212         int x = 0;
1213         while(column > 0)
1214         {
1215                 x += column_width ? 
1216                         column_width[--column] : 
1217                         default_column_width[--column];
1218         }
1219         return x;
1222 void BC_ListBox::column_width_boundaries()
1224         if(column_width)
1225         {
1226                 for(int i = 0; i < columns; i++)
1227                 {
1228                         if(column_width[i] < MIN_COLUMN_WIDTH) column_width[i] = MIN_COLUMN_WIDTH;
1229                 }
1230         }
1231         else
1232         {
1233                 for(int i = 0; i < columns; i++)
1234                 {
1235                         if(default_column_width[i] < MIN_COLUMN_WIDTH) default_column_width[i] = MIN_COLUMN_WIDTH;
1236                 }
1237         }
1240 int BC_ListBox::get_column_width(int column, int clamp_right)
1242         if(column < columns - 1 || !clamp_right)
1243                 return column_width ? 
1244                         column_width[column] : 
1245                         default_column_width[column];
1246         else
1247                 return popup_w + 
1248                         xposition - 
1249                         get_column_offset(column);
1252 int BC_ListBox::get_icon_mask(BC_ListBoxItem *item, 
1253         int &x, 
1254         int &y, 
1255         int &w, 
1256         int &h)
1258         if(display_format == LISTBOX_ICONS)
1259         {
1260                 x = get_item_x(item);
1261                 y = get_item_y(item);
1262                 w = get_icon_w(item) + ICON_MARGIN * 2;
1263                 h = get_icon_h(item) + ICON_MARGIN * 2;
1264         }
1265         else
1266         if(display_format == LISTBOX_TEXT)
1267         {
1268                 x = y = w = h = 0;
1269         }
1270         return 0;
1273 int BC_ListBox::get_text_mask(BC_ListBoxItem *item, 
1274         int &x, 
1275         int &y, 
1276         int &w, 
1277         int &h)
1279         x = get_item_x(item);
1280         y = get_item_y(item);
1282         if(display_format == LISTBOX_ICONS)
1283         {
1284                 if(icon_position == ICON_LEFT)
1285                 {
1286                         x += get_icon_w(item) + ICON_MARGIN * 2;
1287                         y += get_icon_h(item) - get_text_height(MEDIUMFONT);
1288                 }
1289                 else
1290                 {
1291                         y += get_icon_h(item) + ICON_MARGIN;
1292                 }
1294                 w = get_text_width(MEDIUMFONT, item->text) + ICON_MARGIN * 2;
1295                 h = get_text_height(MEDIUMFONT) + ICON_MARGIN * 2;
1296         }
1297         else
1298         if(display_format == LISTBOX_TEXT)
1299         {
1300                 w = get_text_width(MEDIUMFONT, item->text) + LISTBOX_MARGIN * 2;
1301                 h = get_text_height(MEDIUMFONT);
1302         }
1303         return 0;
1306 int BC_ListBox::get_item_highlight(ArrayList<BC_ListBoxItem*> *data, 
1307         int column, 
1308         int item)
1310         BC_Resources *resources = get_resources();
1311         if(data[column].values[item]->selected)
1312                 return resources->listbox_selected;
1313         else
1314         if(highlighted_item >= 0 &&
1315                 highlighted_ptr == data[master_column].values[item])
1316                 return resources->listbox_highlighted;
1317         else
1318                 return resources->listbox_inactive;
1321 int BC_ListBox::get_item_color(ArrayList<BC_ListBoxItem*> *data, 
1322         int column, 
1323         int item)
1325         int color = data[column].values[item]->color;
1326         if(color == -1) color = get_resources()->listbox_text;
1327         if(get_item_highlight(data, column, item) == color)
1328                 return BLACK;
1329         else
1330                 return color;
1333 int BC_ListBox::get_from_column()
1335         return dragged_title;
1338 int BC_ListBox::get_to_column()
1340         return highlighted_title;
1344 BC_ListBoxItem* BC_ListBox::get_selection(int column, 
1345         int selection_number)
1347         return get_selection_recursive(data,
1348                 column,
1349                 selection_number);
1352 BC_ListBoxItem* BC_ListBox::get_selection_recursive(
1353         ArrayList<BC_ListBoxItem*> *data,
1354         int column,
1355         int selection_number)
1357         if(!data) return 0;
1359         for(int i = 0; i < data[master_column].total; i++)
1360         {
1361                 BC_ListBoxItem *item = data[master_column].values[i];
1362                 if(item->selected)
1363                 {
1364                         selection_number--;
1365                         if(selection_number < 0)
1366                         {
1368                                 return data[column].values[i];
1369                         }
1370                 }
1372                 if(item->get_sublist())
1373                 {
1374                         BC_ListBoxItem *result = get_selection_recursive(item->get_sublist(),
1375                                 column,
1376                                 selection_number);
1377                         if(result) return result;
1378                 }
1379         }
1380         return 0;
1384 int BC_ListBox::get_selection_number(int column, 
1385         int selection_number)
1387         return get_selection_number_recursive(data,
1388                 column,
1389                 selection_number);
1392 int BC_ListBox::get_selection_number_recursive(
1393         ArrayList<BC_ListBoxItem*> *data,
1394         int column,
1395         int selection_number,
1396         int *counter)
1398         int temp = -1;
1399         if(!data) return 0;
1400         if(!counter) counter = &temp;
1402         for(int i = 0; i < data[master_column].total; i++)
1403         {
1404                 (*counter)++;
1405                 BC_ListBoxItem *item = data[master_column].values[i];
1406                 if(item->selected)
1407                 {
1408                         selection_number--;
1409                         if(selection_number < 0)
1410                         {
1411                                 return (*counter);
1412                         }
1413                 }
1414                 if(item->get_sublist())
1415                 {
1416                         int result = get_selection_number_recursive(
1417                                 item->get_sublist(),
1418                                 column,
1419                                 selection_number,
1420                                 counter);
1421                         if(result >= 0) return result;
1422                 }
1423         }
1424         return -1;
1428 int BC_ListBox::set_selection_mode(int mode)
1430         this->selection_mode = mode;
1431         return 0;
1434 void BC_ListBox::delete_columns()
1436         if(column_titles)
1437         {
1438                 for(int i = 0; i < columns; i++)
1439                 {
1440                         delete [] column_titles[i];
1441                 }
1442                 delete [] column_titles;
1443         }
1445         if(column_width) delete [] column_width;
1446         
1447         column_titles = 0;
1448         column_width = 0;
1451 // Need to copy titles so EDL can change
1452 void BC_ListBox::set_columns(char **column_titles, 
1453         int *column_width, 
1454         int columns)
1456         if((!column_titles && column_width) ||
1457                 (column_titles && !column_width))
1458         {
1459                 printf("BC_ListBox::set_columns either column_titles or column_width == NULL but not both.\n");
1460                 return;
1461         }
1464         delete_columns();
1466         if(column_titles)
1467         {
1468                 this->column_titles = new char*[columns];
1469                 for(int i = 0; i < columns; i++)
1470                 {
1471                         this->column_titles[i] = new char[strlen(column_titles[i]) + 1];
1472                         strcpy(this->column_titles[i], column_titles[i]);
1473                 }
1474         }
1475         
1476         if(column_width)
1477         {
1478                 this->column_width = new int[columns];
1479                 for(int i = 0; i < columns; i++)
1480                 {
1481                         this->column_width[i] = column_width[i];
1482                 }
1483         }
1484         
1485         this->columns = columns;
1490 int BC_ListBox::update(ArrayList<BC_ListBoxItem*> *data,
1491         char **column_titles,
1492         int *column_widths,
1493         int columns,
1494         int xposition,
1495         int yposition, 
1496         int highlighted_number,
1497         int recalc_positions,
1498         int draw)
1500         set_columns(column_titles, 
1501                 column_widths, 
1502                 columns);
1504         this->data = data;
1506         this->yposition = yposition;
1507         this->xposition = xposition;
1508         this->highlighted_item = highlighted_number;
1509         this->highlighted_ptr = index_to_item(data, highlighted_number, 0);
1511         if(recalc_positions)
1512                 set_autoplacement(data, 1, 1);
1514         init_column_width();
1516         if(gui && draw)
1517         {
1518                 draw_background();
1519                 draw_items(1);
1520                 update_scrollbars();
1521         }
1523         return 0;
1526 void BC_ListBox::center_selection()
1528         int selection = get_selection_number(0, 0);
1530         calculate_item_coords();
1531         center_selection(selection);
1534         if(gui)
1535         {
1536                 draw_background();
1537                 draw_items(1);
1538                 update_scrollbars();
1539         }
1542 void BC_ListBox::move_vertical(int pixels)
1546 void BC_ListBox::move_horizontal(int pixels)
1550 int BC_ListBox::select_previous(int skip, 
1551         BC_ListBoxItem *selected_item,
1552         int *counter,
1553         ArrayList<BC_ListBoxItem*> *data,
1554         int *got_first,
1555         int *got_second)
1557         int top_level = 0;
1558         if(!selected_item)
1559                 selected_item = get_selection(0, 0);
1560         int temp = -1;
1561         if(!counter)
1562                 counter = &temp;
1563         int temp2 = 0;
1564         if(!got_first)
1565         {
1566                 got_first = &temp2;
1567                 top_level = 1;
1568         }
1569         int temp3 = 0;
1570         if(!got_second)
1571                 got_second = &temp3;
1572         if(!data)
1573                 data = this->data;
1574         int done = 0;
1576 // Scan backwards to item pointer.  Then count visible items to get 
1577 // destination.  Repeat to get wraparound.
1578         do
1579         {
1580                 for(int i = data[master_column].total - 1; i >= 0; i--)
1581                 {
1582                         BC_ListBoxItem *current_item = data[master_column].values[i];
1583                         if(current_item->get_sublist() &&
1584                                 current_item->get_expand())
1585                         {
1586                                 int result = select_previous(skip, 
1587                                         selected_item,
1588                                         counter,
1589                                         current_item->get_sublist(),
1590                                         got_first,
1591                                         got_second);
1592                                 if(*got_second)
1593                                 {
1594                                         return result;
1595                                 }
1596                         }
1598                         if(*got_first)
1599                         {
1600                                 (*counter)++;
1601                                 if((*counter) >= skip)
1602                                 {
1603                                         for(int j = 0; j < columns; j++)
1604                                                 data[j].values[i]->selected = 1;
1605                                         (*got_second) = 1;
1606                                         return item_to_index(this->data, current_item);
1607                                 }
1608                         }
1609                         else
1610                         {
1611                                 if(current_item->selected)
1612                                 {
1613                                         for(int j = 0; j < columns; j++)
1614                                                 data[j].values[i]->selected = 0;
1615                                         (*got_first) = 1;
1616                                         (*counter)++;
1617                                 }
1618                         }
1619                 }
1621 // Hit bottom of top level without finding a selected item.
1622                 if(top_level && !(*got_first)) (*got_first) = 1;
1623         }while(top_level && data[master_column].total);
1624         return -1;
1627 int BC_ListBox::select_next(int skip, 
1628         BC_ListBoxItem *selected_item,
1629         int *counter,
1630         ArrayList<BC_ListBoxItem*> *data,
1631         int *got_first,
1632         int *got_second)
1634         int top_level = 0;
1635         if(!selected_item)
1636                 selected_item = get_selection(0, 0);
1637         int temp = -1;
1638         if(!counter)
1639                 counter = &temp;
1640         int temp2 = 0;
1641         if(!got_first)
1642         {
1643                 got_first = &temp2;
1644                 top_level = 1;
1645         }
1646         int temp3 = 0;
1647         if(!got_second)
1648                 got_second = &temp3;
1649         if(!data)
1650                 data = this->data;
1651         int done = 0;
1653 // Scan backwards to item pointer.  Then count visible items to get 
1654 // destination.  Repeat to get wraparound.
1655         do
1656         {
1657                 for(int i = 0; i < data[master_column].total; i++)
1658                 {
1659                         BC_ListBoxItem *current_item = data[master_column].values[i];
1660                         if(*got_first)
1661                         {
1662                                 (*counter)++;
1663                                 if((*counter) >= skip)
1664                                 {
1665                                         for(int j = 0; j < columns; j++)
1666                                                 data[j].values[i]->selected = 1;
1667                                         (*got_second) = 1;
1668                                         return item_to_index(this->data, current_item);
1669                                 }
1670                         }
1671                         else
1672                         {
1673                                 if(current_item->selected)
1674                                 {
1675                                         for(int j = 0; j < columns; j++)
1676                                                 data[j].values[i]->selected = 0;
1677                                         (*got_first) = 1;
1678                                         (*counter)++;
1679                                 }
1680                         }
1682                         if(current_item->get_sublist() &&
1683                                 current_item->get_expand())
1684                         {
1685                                 int result = select_next(skip, 
1686                                         selected_item,
1687                                         counter,
1688                                         current_item->get_sublist(),
1689                                         got_first,
1690                                         got_second);
1691                                 if(*got_second)
1692                                 {
1693                                         return result;
1694                                 }
1695                         }
1696                 }
1698 // Hit bottom of top level without finding a selected item.
1699                 if(top_level && !(*got_first)) (*got_first) = 1;
1700         }while(top_level && data[master_column].total);
1701         return -1;
1705 void BC_ListBox::clamp_positions()
1707         items_w = get_items_width();
1708         items_h = get_items_height(data, columns);
1710         if(yposition < 0) yposition = 0;
1711         else
1712         if(yposition > items_h - view_h)
1713                 yposition = items_h - view_h;
1715         if(yposition < 0) yposition = 0;
1717         if(xposition < 0) xposition = 0;
1718         else
1719         if(xposition >= items_w - view_w)
1720                 xposition = items_w - view_w;
1722         if(xposition < 0) xposition = 0;
1725 int BC_ListBox::center_selection(int selection,
1726         ArrayList<BC_ListBoxItem*> *data,
1727         int *counter)
1729         int temp = -1;
1730         if(!data) data = this->data;
1731         if(!counter) counter = &temp;
1733         for(int i = 0; i < data[master_column].total; i++)
1734         {
1735                 (*counter)++;
1737 // Got it
1738                 BC_ListBoxItem *item = data[master_column].values[i];
1739                 if((*counter) == selection)
1740                 {
1741                         BC_ListBoxItem *top_item = this->data[master_column].values[0];
1744                         if(display_format == LISTBOX_ICONS)
1745                         {
1746 // Icon is out of window
1747                                 if(item->icon_y - yposition  > 
1748                                         view_h - get_text_height(MEDIUMFONT) ||
1749                                         item->icon_y - yposition < 0)
1750                                 {
1751                                         yposition = item->icon_y - view_h / 2;
1752                                 }
1754                                 if(data[master_column].values[selection]->icon_x - xposition > view_w ||
1755                                         data[master_column].values[selection]->icon_x - xposition < 0)
1756                                 {
1757                                         xposition = item->icon_x - view_w / 2;
1758                                 }
1759                         }
1760                         else
1761                         if(display_format == LISTBOX_TEXT)
1762                         {
1763 // Text coordinate is out of window
1764                                 if(item->text_y - yposition  > 
1765                                         view_h - get_text_height(MEDIUMFONT) ||
1766                                         item->text_y - yposition < 0)
1767                                 {
1768                                         yposition = item->text_y - 
1769                                                 top_item->text_y -
1770                                                 view_h / 2;
1771                                 }
1772                         }
1773                         return 1;
1774                 }
1776 // Descend
1777                 if(item->get_sublist())
1778                 {
1779                         int result = center_selection(selection,
1780                                 item->get_sublist(),
1781                                 counter);
1782                         if(result) return result;
1783                 }
1784         }
1785         return 0;
1788 void BC_ListBox::update_scrollbars()
1790         int h_needed = items_h = get_items_height(data, columns);
1791         int w_needed = items_w = get_items_width();
1793 // if(columns > 0 && column_width)
1794 // printf("BC_ListBox::update_scrollbars 1 %d %d\n", column_width[columns - 1], w_needed);
1796         if(xscrollbar)
1797         {
1798                 if(xposition != xscrollbar->get_value())
1799                         xscrollbar->update_value(xposition);
1801                 if(w_needed != xscrollbar->get_length() || 
1802                         view_w != xscrollbar->get_handlelength())
1803                         xscrollbar->update_length(w_needed, xposition, view_w);
1804         }
1806         if(yscrollbar)
1807         {
1808                 if(yposition != yscrollbar->get_value())
1809                         yscrollbar->update_value(yposition);
1811                 if(h_needed != yscrollbar->get_length() || view_h != yscrollbar->get_handlelength())
1812                         yscrollbar->update_length(h_needed, yposition, view_h);
1813         }
1816 int BC_ListBox::get_scrollbars()
1818         int h_needed = items_h = get_items_height(data, columns);
1819         int w_needed = items_w = get_items_width();
1823         title_h = get_title_h();
1825         view_h = popup_h - title_h - 4;
1826         view_w = popup_w - 4;
1828 // Create scrollbars as needed
1829         for(int i = 0; i < 2; i++)
1830         {
1831                 if(w_needed > view_w)
1832                 {
1833                         need_xscroll = 1;
1834                         view_h = popup_h - 
1835                                 title_h - 
1836                                 get_resources()->hscroll_data[SCROLL_HANDLE_UP]->get_h() - 
1837                                 4;
1838                 }
1839                 else
1840                 {
1841                         need_xscroll = 0;
1842                 }
1844                 if(h_needed > view_h)
1845                 {
1846                         need_yscroll = 1;
1847                         view_w = popup_w - 
1848                                 get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w() - 
1849                                 4;
1850                 }
1851                 else
1852                 {
1853                         need_yscroll = 0;
1854                 }
1855         }
1857 // Update subwindow size
1858         int new_w = popup_w;
1859         int new_h = popup_h;
1860         if(need_xscroll) new_h -= get_resources()->hscroll_data[SCROLL_HANDLE_UP]->get_h();
1861         if(need_yscroll) new_w -= get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w();
1863         if(!is_popup)
1864                 if(new_w != BC_WindowBase::get_w() || new_h != BC_WindowBase::get_h())
1865                         gui->resize_window(new_w, new_h);
1867         BC_WindowBase *destination = (is_popup ? gui : parent_window);
1868         if(need_xscroll)
1869         {
1870                 if(!xscrollbar)
1871                 {
1872                         destination->add_subwindow(xscrollbar = 
1873                                 new BC_ListBoxXScroll(this, 
1874                                         w_needed, 
1875                                         view_w, 
1876                                         xposition));
1877                         xscrollbar->bound_to = this;
1878                 }
1879                 else
1880                 {
1881                     xscrollbar->update_length(w_needed, xposition, view_w);
1882                         xscrollbar->reposition_window(get_xscroll_x(),
1883                                 get_xscroll_y(),
1884                                 get_xscroll_width());
1885                 }
1886         }
1887         else
1888         {
1889                 if(xscrollbar) delete xscrollbar;
1890                 xscrollbar = 0;
1891                 xposition = 0;
1892         }
1894         if(need_yscroll)
1895         {
1896                 if(!yscrollbar)
1897                 {
1898                         destination->add_subwindow(yscrollbar = 
1899                                 new BC_ListBoxYScroll(this, 
1900                                         h_needed, 
1901                                         view_h, 
1902                                         yposition));
1903                         yscrollbar->bound_to = this;
1904                 }
1905                 else
1906                 {
1907                         yscrollbar->update_length(h_needed, yposition, view_h);
1908                         yscrollbar->reposition_window(get_yscroll_x(),
1909                                 get_yscroll_y(),
1910                                 get_yscroll_height());
1911                 }
1912         }
1913         else
1914         {
1915                 if(yscrollbar) delete yscrollbar;
1916                 yscrollbar = 0;
1917                 yposition = 0;
1918         }
1919         
1920         if(!bg_surface ||
1921                 view_w + 4 != bg_surface->get_w() ||
1922                 view_h + 4 != bg_surface->get_h())
1923         {
1924                 if(bg_surface) delete bg_surface;
1925                 bg_surface = new BC_Pixmap(gui, view_w + 4, view_h + 4);
1926                 draw_background();
1927         }
1929         return 0;
1934 void BC_ListBox::set_drag_scroll(int value)
1936         allow_drag_scroll = value;
1940 // Test for scrolling by dragging
1942 int BC_ListBox::test_drag_scroll(int cursor_x, int cursor_y)
1944         int result = 0;
1945         if(allow_drag_scroll || 
1946                 current_operation == SELECT_RECT)
1947         {
1949                 int top_boundary = get_title_h();
1951                 if(cursor_y < top_boundary ||
1952                         cursor_y >= view_h + title_h + LISTBOX_BORDER * 2 ||
1953                         cursor_x < LISTBOX_BORDER ||
1954                         cursor_x >= view_w + LISTBOX_BORDER)
1955                 {
1956                         result = 1;
1957                 }
1958         }
1959         return result;
1962 int BC_ListBox::drag_scroll_event()
1964         int top_boundary = get_title_h();
1965         int result = 0;
1967         if(get_cursor_y() < top_boundary)
1968         {
1969                 yposition -= top_boundary - get_cursor_y();
1970                 result = 1;
1971         }
1972         else
1973         if(get_cursor_y() >= view_h + title_h + 4)
1974         {
1975                 yposition += get_cursor_y() - (view_h + title_h + 4);
1976                 result = 1;
1977         }
1979         if(get_cursor_x() < 2)
1980         {
1981                 xposition -= 2 - get_cursor_x();
1982                 result = 1;
1983         }
1984         else
1985         if(get_cursor_x() >= view_w + 2)
1986         {
1987                 xposition += get_cursor_x() - (view_w + 2);
1988                 result = 1;
1989         }
1990         if(result) clamp_positions();
1991         return result;
1994 int BC_ListBox::rectangle_scroll_event()
1996         int old_xposition = xposition;
1997         int old_yposition = yposition;
1998         int result = drag_scroll_event();
2000         if(result)
2001         {
2002                 rect_x1 += old_xposition - xposition;
2003                 rect_y1 += old_yposition - yposition;
2004                 rect_x2 = get_cursor_x();
2005                 rect_y2 = get_cursor_y();
2007                 int x1 = MIN(rect_x1, rect_x2);
2008                 int x2 = MAX(rect_x1, rect_x2);
2009                 int y1 = MIN(rect_y1, rect_y2);
2010                 int y2 = MAX(rect_y1, rect_y2);
2012                 if(select_rectangle(data,
2013                         x1, 
2014                         y1,
2015                         x2, 
2016                         y2))
2017                 {
2018                         selection_changed();
2019                 }
2021                 clamp_positions();
2022                 draw_items(1);
2023                 update_scrollbars();
2024         }
2025         return result;
2028 int BC_ListBox::select_scroll_event()
2030         int result = drag_scroll_event();
2032         if(result)
2033         {
2034                 highlighted_item = selection_number = get_cursor_item(data, 
2035                         get_cursor_x(), 
2036                         get_cursor_y(),
2037                         &highlighted_ptr);
2038                 clamp_positions();
2039                 draw_items(1);
2040                 update_scrollbars();
2041                 selection_changed();
2042         }
2043         return result;
2046 int BC_ListBox::select_rectangle(ArrayList<BC_ListBoxItem*> *data,
2047                 int x1, 
2048                 int y1,
2049                 int x2, 
2050                 int y2)
2052         int result = 0;
2053         for(int i = 0; i < data[master_column].total; i++)
2054         {
2055                 for(int j = 0; j < columns; j++)
2056                 {
2057                         BC_ListBoxItem *item = data[j].values[i];
2058                         if(display_format == LISTBOX_ICONS)
2059                         {
2060                                 int icon_x, icon_y, icon_w, icon_h;
2061                                 int text_x, text_y, text_w, text_h;
2062                                 get_icon_mask(item, icon_x, icon_y, icon_w, icon_h);
2063                                 get_text_mask(item, text_x, text_y, text_w, text_h);
2065                                 if((x2 >= icon_x && x1 < icon_x + icon_w &&
2066                                         y2 >= icon_y && y1 < icon_y + icon_h) ||
2067                                         (x2 >= text_x && x1 < text_x + text_w &&
2068                                         y2 >= text_y && y1 < text_y + text_h))
2069                                 {
2070                                         if(!item->selected)
2071                                         {
2072                                                 item->selected = 1;
2073                                                 result = 1;
2074                                         }
2075                                 }
2076                                 else
2077                                 {
2078                                         if(item->selected)
2079                                         {
2080                                                 item->selected = 0;
2081                                                 result = 1;
2082                                         }
2083                                 }
2084                         }
2085                         else
2086                         {
2087                                 if(x2 >= 0 && 
2088                                         x1 < (yscrollbar ? 
2089                                                 gui->get_w() - get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w() : 
2090                                                 gui->get_w()) &&
2091                                         y2 > 0 && 
2092                                         y1 < gui->get_h() &&
2093                                         y2 >= get_item_y(item) &&
2094                                         y1 < get_item_y(item) + get_item_h(item))
2095                                 {
2096                                         if(!item->selected)
2097                                         {
2098                                                 item->selected = 1;
2099                                                 result = 1;
2100                                         }
2101                                 }
2102                                 else
2103                                 {
2104                                         if(item->selected)
2105                                         {
2106                                                 item->selected = 0;
2107                                                 result = 1;
2108                                         }
2109                                 }
2110                         }
2111                 }
2113                 BC_ListBoxItem *item = data[master_column].values[i];
2114                 if(item->get_sublist() &&
2115                         item->get_expand())
2116                         result |= select_rectangle(item->get_sublist(),
2117                                 x1, 
2118                                 y1,
2119                                 x2, 
2120                                 y2);
2121         }
2122         return result;
2125 int BC_ListBox::reposition_item(ArrayList<BC_ListBoxItem*> *data,
2126                 int selection_number,
2127                 int x,
2128                 int y,
2129                 int *counter)
2131         int temp = -1;
2132         if(!counter) counter = &temp;
2135         for(int i = 0; i < data[master_column].total; i++)
2136         {
2137                 BC_ListBoxItem *item = data[master_column].values[i];
2138                 (*counter)++;
2139                 if((*counter) == selection_number)
2140                 {
2141                         item->icon_x = x;
2142                         item->icon_y = y;
2143                         return 1;
2144                 }
2145 // Not recursive because it's only used for icons
2146         }
2147         return 0;
2150 void BC_ListBox::move_selection(ArrayList<BC_ListBoxItem*> *dst,
2151         ArrayList<BC_ListBoxItem*> *src)
2153         for(int i = 0; i < src[master_column].total; i++)
2154         {
2155                 BC_ListBoxItem *item = src[master_column].values[i];
2157 // Move item to dst
2158                 if(item->selected)
2159                 {
2160                         for(int j = 0; j < columns; j++)
2161                         {
2162                                 dst[j].append(src[j].values[i]);
2163                                 src[j].remove_number(i);
2164                         }
2165                 }
2166                 else
2167 // Descend into sublist
2168                 if(item->get_sublist())
2169                 {
2170                         move_selection(dst, 
2171                                 item->get_sublist());
2172                 }
2173         }
2176 int BC_ListBox::put_selection(ArrayList<BC_ListBoxItem*> *data,
2177         ArrayList<BC_ListBoxItem*> *src,
2178         int destination,
2179         int *counter)
2181         int temp = -1;
2182         if(!counter) counter = &temp;
2184         if(destination < 0)
2185         {
2186                 for(int j = 0; j < columns; j++)
2187                 {
2188                         for(int i = 0; i < src[j].total; i++)
2189                         {
2190                                 data[j].append(src[j].values[i]);
2191                         }
2192                 }
2193                 return 1;
2194         }
2195         else
2196         for(int i = 0; i < data[master_column].total; i++)
2197         {
2198                 (*counter)++;
2199                 if((*counter) == destination)
2200                 {
2201                         for(int j = 0; j < columns; j++)
2202                         {
2203                                 for(int k = 0; k < src[j].total; k++)
2204                                 {
2205                                         data[j].insert(src[j].values[k], destination + k);
2206                                 }
2207                         }
2208                         return 1;
2209                 }
2211                 BC_ListBoxItem *item = data[master_column].values[i];
2212                 if(item->get_sublist())
2213                 {
2214                         if(put_selection(item->get_sublist(),
2215                                 src,
2216                                 destination,
2217                                 counter))
2218                                 return 1;
2219                 }
2220         }
2221         return 0;
2226 int BC_ListBox::item_to_index(ArrayList<BC_ListBoxItem*> *data,
2227                 BC_ListBoxItem *item,
2228                 int *counter)
2230         int temp = -1;
2231         if(!counter) counter = &temp;
2233         for(int i = 0; i < data[master_column].total; i++)
2234         {
2235                 (*counter)++;
2236                 for(int j = 0; j < columns; j++)
2237                 {
2238                         BC_ListBoxItem *new_item = data[j].values[i];
2239 //printf("BC_ListBox::item_to_index 1 %d %d %p\n", j, i, new_item);
2240                         if(new_item == item)
2241                         {
2242                                 return (*counter);
2243                         }
2244                 }
2246                 BC_ListBoxItem *new_item = data[master_column].values[i];
2247                 if(new_item->get_sublist())
2248                 {
2249                         if(item_to_index(new_item->get_sublist(),
2250                                 item,
2251                                 counter) >= 0)
2252                                 return (*counter);
2253                 }
2254         }
2256         return -1;
2259 BC_ListBoxItem* BC_ListBox::index_to_item(ArrayList<BC_ListBoxItem*> *data,
2260                 int number,
2261                 int column,
2262                 int *counter)
2264         int temp = -1;
2265         if(!counter) counter = &temp;
2266         for(int i = 0; i < data[master_column].total; i++)
2267         {
2268                 (*counter)++;
2269                 if((*counter) == number)
2270                 {
2271                         return data[column].values[i];
2272                 }
2273                 BC_ListBoxItem *item = data[master_column].values[i];
2274                 if(item->get_sublist())
2275                 {
2276                         BC_ListBoxItem *result = index_to_item(item->get_sublist(),
2277                                 number,
2278                                 column,
2279                                 counter);
2280                         if(result) return result;
2281                 }
2282         }
2283         return 0;
2286 int BC_ListBox::get_cursor_item(ArrayList<BC_ListBoxItem*> *data,
2287         int cursor_x, 
2288         int cursor_y, 
2289         BC_ListBoxItem **item_return,
2290         int *counter,
2291         int expanded)
2293         int temp = -1;
2294         if(!data) return -1;
2295         if(!counter) counter = &temp;
2297 // Icons are not treed
2298         if(display_format == LISTBOX_ICONS)
2299         {
2300                 for(int j = data[master_column].total - 1; j >= 0; j--)
2301                 {
2302                         int icon_x, icon_y, icon_w, icon_h;
2303                         int text_x, text_y, text_w, text_h;
2304                         BC_ListBoxItem *item = data[master_column].values[j];
2305                         get_icon_mask(item, icon_x, icon_y, icon_w, icon_h);
2306                         get_text_mask(item, text_x, text_y, text_w, text_h);
2308                         if((cursor_x >= icon_x && cursor_x < icon_x + icon_w &&
2309                                 cursor_y >= icon_y && cursor_y < icon_y + icon_h) ||
2310                                 (cursor_x >= text_x && cursor_x < text_x + text_w &&
2311                                 cursor_y >= text_y && cursor_y < text_y + text_h))
2312                         {
2313                                 if(item_return) (*item_return) = item;
2314                                 return j;
2315                         }
2316                 }
2317         }
2318         else
2319 // Text is treed
2320         if(display_format == LISTBOX_TEXT)
2321         {
2322 // Cursor is inside items rectangle
2323                 if(cursor_x >= 0 && 
2324                         cursor_x < (yscrollbar ? 
2325                                 gui->get_w() - get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w() : 
2326                                 gui->get_w()) &&
2327 // Only clamp y if we're not in a SELECT operation.
2328                         (current_operation == BC_ListBox::SELECT ||
2329                                 (cursor_y > get_title_h() + LISTBOX_BORDER && 
2330                                 cursor_y < gui->get_h())))
2331                 {
2332 // Search table for cursor obstruction
2333                         for(int i = 0; i < data[master_column].total; i++)
2334                         {
2335                                 BC_ListBoxItem *item = data[master_column].values[i];
2336                                 (*counter)++;
2338 // Cursor is inside item on current level
2339                                 if(expanded &&
2340                                         item->selectable &&
2341                                         cursor_y >= get_item_y(item) &&
2342                                         cursor_y < get_item_y(item) + get_item_h(item))
2343                                 {
2344 //printf("BC_ListBox::get_cursor_item %d %d %p\n", master_column, i, item);
2345                                         if(item_return) (*item_return) = item;
2346                                         return (*counter);
2347                                 }
2349 // Descend into sublist
2350                                 if(item->get_sublist())
2351                                 {
2352                                         if(get_cursor_item(item->get_sublist(),
2353                                                 cursor_x, 
2354                                                 cursor_y, 
2355                                                 item_return,
2356                                                 counter,
2357                                                 item->get_expand()) >= 0)
2358                                                 return (*counter);
2359                                 }
2360                         }
2361                 }
2362         }
2363         return -1;
2366 int BC_ListBox::repeat_event(int64_t duration)
2368         switch(current_operation)
2369         {
2370 // Repeat out of bounds selection
2371                 case SELECT_RECT:
2372                         if(duration == get_resources()->scroll_repeat)
2373                                 return rectangle_scroll_event();
2374                         break;
2376                 case SELECT:
2377                         if(duration == get_resources()->scroll_repeat)
2378                                 return select_scroll_event();
2379                         break;
2381                 case NO_OPERATION:
2382 // Show tooltip
2383                         if(button_highlighted &&
2384                                 duration == get_resources()->tooltip_delay &&
2385                                 tooltip_text[0] != 0 &&
2386                                 is_popup &&
2387                                 !tooltip_done)
2388                         {
2389                                 show_tooltip();
2390                                 tooltip_done = 1;
2391                                 return 1;
2392                         }
2393                         break;
2394         }
2395         return 0;
2399 int BC_ListBox::cursor_enter_event()
2401         int result = 0;
2403         switch(current_operation)
2404         {
2405 // Cursor moved over button, pressed, and exited.
2406                 case BUTTON_DOWN_SELECT:
2407                         if(top_level->event_win == win)
2408                         {
2409                                 current_operation = BUTTON_DN;
2410                                 result = 1;
2411                                 button_highlighted = 1;
2412                                 draw_button();
2413                         }
2414                         break;
2416                 case NO_OPERATION:
2417 // Cursor entered button
2418                         if(is_popup && top_level->event_win == win)
2419                         {
2420                                 button_highlighted = 1;
2421                                 result = 1;
2422                                 draw_button();
2423                         }
2424                         else
2425 // TODO: Need to get the highlighted column title or item
2426                         if(gui && top_level->event_win == gui->win)
2427                         {
2428                                 list_highlighted = 1;
2429                                 draw_border(1);
2430                                 result = 1;
2431                         }
2432                         break;
2433         }
2435         return result;
2438 int BC_ListBox::cursor_leave_event()
2440         int redraw_button = 0;
2441         int redraw_border = 0;
2442         int redraw_titles = 0;
2443         int redraw_items = 0;
2445         if(current_operation == COLUMN_DRAG) return 0;
2447 // Left button area
2448         if(button_highlighted)
2449         {
2450                 button_highlighted = 0;
2451                 hide_tooltip();
2452                 draw_button();
2453         }
2455         if(list_highlighted)
2456         {
2457                 list_highlighted = 0;
2458                 highlighted_item = -1;
2459                 highlighted_ptr = 0;
2460                 highlighted_title = -1;
2461                 int redraw_toggles = 0;
2462                 for(int i = 0; i < expanders.total; i++)
2463                         expanders.values[i]->cursor_leave_event(&redraw_toggles);
2465                 draw_items(1);
2466         }
2468         return 0;
2471 int BC_ListBox::get_first_selection(ArrayList<BC_ListBoxItem*> *data, int *result)
2473         int temp = -1;
2474         if(!result) result = &temp;
2476         for(int i = 0; i < data[master_column].total; i++)
2477         {
2478                 BC_ListBoxItem *item = data[master_column].values[i];
2479                 (*result)++;
2480                 if(item->selected) return (*result);
2481                 if(item->get_sublist())
2482                 {
2483                         if(get_first_selection(item->get_sublist(), result) >= 0)
2484                                 return (*result);
2485                 }
2486         }
2487         return -1;
2490 int BC_ListBox::get_total_items(ArrayList<BC_ListBoxItem*> *data, 
2491         int *result,
2492         int master_column)
2494         int temp = 0;
2495         if(!result) result = &temp;
2497         for(int i = 0; i < data[master_column].total; i++)
2498         {
2499                 (*result)++;
2500                 if(data[master_column].values[i]->get_sublist())
2501                         get_total_items(data[master_column].values[i]->get_sublist(), 
2502                                 result,
2503                                 master_column);
2504         }
2506         return (*result);
2510 int BC_ListBox::get_last_selection(ArrayList<BC_ListBoxItem*> *data, 
2511         int *result)
2513         int temp = -1;
2514         int top_level = 0;
2515         if(!result)
2516         {
2517                 result = &temp;
2518                 top_level = 1;
2519         }
2521         for(int i = data[master_column].total - 1; i >= 0; i--)
2522         {
2523                 BC_ListBoxItem *item = data[master_column].values[i];
2524                 (*result)++;
2525                 if(item->selected)
2526                 {
2527                         if(top_level)
2528                                 return get_total_items(data, 0, master_column) - (*result) /* - 1 */;
2529                         else
2530                                 return (*result);
2531                 }
2533                 if(item->get_sublist())
2534                 {
2535                         if(get_last_selection(item->get_sublist(), result) >= 0)
2536                         {
2537                                 if(top_level)
2538                                         return get_total_items(data, 0, master_column) - (*result) /* - 1 */;
2539                                 else
2540                                         return (*result);
2541                         }
2542                 }
2543         }
2544         return -1;
2547 void BC_ListBox::select_range(ArrayList<BC_ListBoxItem*> *data,
2548                 int start,
2549                 int end,
2550                 int *current)
2552         int temp = -1;
2553         if(!current) current = &temp;
2555         for(int i = 0; i < data[master_column].total; i++)
2556         {
2557                 (*current)++;
2558                 if((*current) >= start && (*current) < end)
2559                 {
2560                         for(int j = 0; j < columns; j++)
2561                                 data[j].values[i]->selected = 1;
2562                 }
2563                 BC_ListBoxItem *item = data[master_column].values[i];
2564                 if(item->get_sublist())
2565                         select_range(item->get_sublist(),
2566                                 start,
2567                                 end,
2568                                 current);
2569         }
2573 // Fill items between current selection and new selection
2574 int BC_ListBox::expand_selection(int button_press, int selection_number)
2576         int old_selection_start = selection_start;
2577         int old_selection_end = selection_end;
2579 // printf("BC_ListBox::expand_selection %d %d\n", 
2580 // selection_center, 
2581 // selection_number);
2583 // Calculate the range to select based on selection_center and selection_number
2584         if(selection_number < selection_center)
2585         {
2586                 selection_start = selection_number;
2587         }
2588         else
2589         {
2590                 selection_end = selection_number + 1;
2591         }
2593 //printf("BC_ListBox::expand_selection %d %d %d %d\n", old_selection_start, old_selection_end, selection_start, selection_end);
2594 // Recurse through all the items and select the desired range
2595         select_range(data, selection_start, selection_end);
2597 // Trigger redraw
2598         return (old_selection_start != selection_start ||
2599                 old_selection_end != selection_end);
2602 int BC_ListBox::toggle_item_selection(ArrayList<BC_ListBoxItem*> *data,
2603         int selection_number,
2604         int *counter)
2606         int temp = -1;
2607         if(!counter) counter = &temp;
2609         for(int i = 0; i < data[master_column].total; i++)
2610         {
2611                 BC_ListBoxItem *item = data[master_column].values[i];
2612                 (*counter)++;
2613                 if((*counter) == selection_number)
2614                 {
2615 // Get new value for selection
2616                         int selected = !item->selected;
2617 // Set row
2618                         for(int j = 0; j < columns; j++)
2619                                 data[j].values[i]->selected = selected;
2620                         return 1;
2621                 }
2623 // Descend into sublist
2624                 if(item->get_sublist())
2625                 {
2626                         if(toggle_item_selection(item->get_sublist(),
2627                                 selection_number,
2628                                 counter))
2629                                 return 1;
2630                 }
2631         }
2633         return 0;
2637 void BC_ListBox::set_all_selected(ArrayList<BC_ListBoxItem*> *data, int value)
2639         for(int i = 0; i < data[master_column].total; i++)
2640         {
2641                 for(int j = 0; j < columns; j++)
2642                 {
2643                         BC_ListBoxItem *item = data[j].values[i];
2644                         item->selected = value;
2645                 }
2646                 BC_ListBoxItem *item = data[master_column].values[i];
2647                 if(item->get_sublist())
2648                 {
2649                         set_all_selected(item->get_sublist(), value);
2650                 }
2651         }
2654 void BC_ListBox::set_selected(ArrayList<BC_ListBoxItem*> *data, 
2655                 int item_number, 
2656                 int value,
2657                 int *counter)
2659         int temp = -1;
2660         if(!counter) counter = &temp;
2661         for(int i = 0; i < data[master_column].total && (*counter) != item_number; i++)
2662         {
2663                 (*counter)++;
2664                 if((*counter) == item_number)
2665                 {
2666                         for(int j = 0; j < columns; j++)
2667                         {
2668                                 BC_ListBoxItem *item = data[j].values[i];
2669                                 item->selected = value;
2670                         }
2671                         return;
2672                 }
2674                 BC_ListBoxItem *item = data[master_column].values[i];
2675                 if(item->get_sublist())
2676                 {
2677                         set_selected(item->get_sublist(), 
2678                                 item_number, 
2679                                 value,
2680                                 counter);
2681                 }
2682         }
2685 int BC_ListBox::update_selection(ArrayList<BC_ListBoxItem*> *data, 
2686         int selection_number,
2687         int *counter)
2689         int temp = -1;
2690         int result = 0;
2691         if(!counter) counter = &temp;
2693         for(int i = 0; i < data[master_column].total; i++)
2694         {
2695                 BC_ListBoxItem *item = data[master_column].values[i];
2696                 (*counter)++;
2697                 if((*counter) == selection_number && !item->selected)
2698                 {
2699                         result = 1;
2700                         for(int j = 0; j < columns; j++)
2701                                 data[j].values[i]->selected = 1;
2702                 }
2703                 else
2704                 if((*counter) != selection_number && item->selected)
2705                 {
2706                         result = 1;
2707                         for(int j = 0; j < columns; j++)
2708                                 data[j].values[i]->selected = 0;
2709                 }
2710                 if(item->get_sublist())
2711                         result |= update_selection(item->get_sublist(), 
2712                                 selection_number,
2713                                 counter);
2714         }
2715         return result;
2718 void BC_ListBox::promote_selections(ArrayList<BC_ListBoxItem*> *data,
2719         int old_value,
2720         int new_value)
2722         for(int i = 0; i < data[master_column].total; i++)
2723         {
2724                 for(int j = 0; j < columns; j++)
2725                 {
2726                         BC_ListBoxItem *item = data[j].values[i];
2727                         if(item->selected == old_value) item->selected = new_value;
2728                 }
2729                 BC_ListBoxItem *item = data[master_column].values[i];
2730                 if(item->get_sublist())
2731                         promote_selections(item->get_sublist(), old_value, new_value);
2732         }
2735 int BC_ListBox::focus_out_event()
2737         deactivate();
2738         return 0;
2741 int BC_ListBox::button_press_event()
2743         int result = 0;
2744         BC_ListBoxItem *current_item = 0;
2745         int new_cursor;
2746         int do_selection_change = 0;
2748         hide_tooltip();
2751 // Pressed in button
2752         if(is_popup && top_level->event_win == win)
2753         {
2754                 current_operation = BUTTON_DN;
2755                 draw_button();
2757 // Deploy listbox
2758                 if(!active && !disabled)
2759                 {
2760                         top_level->deactivate();
2761                         activate();
2762                 }
2764                 result = 1;
2765         }
2766         else
2767 // Pressed in scrollbar
2768         if((xscrollbar && top_level->event_win == xscrollbar->win) ||
2769                 (yscrollbar && top_level->event_win == yscrollbar->win))
2770         {
2771                 result = 0;
2772         }
2773         else
2774 // Pressed in items
2775         if(gui && top_level->event_win == gui->win)
2776         {
2777 // Activate list items
2778                 if(!active)
2779                 {
2780                         top_level->deactivate();
2781                         activate();
2782                 }
2784 // Wheel mouse pressed
2785                 if(get_buttonpress() == 4)
2786                 {
2787                         if(current_operation == NO_OPERATION)
2788                         {
2789                                 current_operation = WHEEL;
2790                                 if(yscrollbar)
2791                                 {
2792                                         set_yposition(yposition - gui->get_h() / 10, 0);
2793                                         clamp_positions();
2794                                         update_scrollbars();
2795                                         highlighted_ptr = 0;
2796                                         highlighted_item = get_cursor_item(data,
2797                                                 top_level->cursor_x, 
2798                                                 top_level->cursor_y, 
2799                                                 &highlighted_ptr);
2800                                         draw_items(1);
2801                                         result = 1;
2802                                 }
2803                         }
2804                 }
2805                 else
2806                 if(get_buttonpress() == 5)
2807                 {
2808                         if(current_operation == NO_OPERATION)
2809                         {
2810                                 current_operation = WHEEL;
2811                                 if(yscrollbar)
2812                                 {
2813                                         set_yposition(yposition + gui->get_h() / 10, 0);
2814                                         clamp_positions();
2815                                         update_scrollbars();
2816                                         highlighted_ptr = 0;
2817                                         highlighted_item = get_cursor_item(data,
2818                                                 top_level->cursor_x, 
2819                                                 top_level->cursor_y,
2820                                                 &highlighted_ptr);
2821                                         draw_items(1);
2822                                         result = 1;
2823                                 }
2824                         }
2825                 }
2826                 else
2827 // Pressed over column title division
2828                 if(test_column_divisions(gui->get_cursor_x(), 
2829                         gui->get_cursor_y(), 
2830                         new_cursor))
2831                 {
2832                         current_operation = DRAG_DIVISION;
2833                         reset_query();
2834                 }
2835                 else
2836 // Pressed in column title
2837                 if(test_column_titles(gui->get_cursor_x(), gui->get_cursor_y()))
2838                 {
2839                         current_operation = COLUMN_DN;
2840                         button_highlighted = 0;
2841                         list_highlighted = 1;
2842                         draw_items(1);
2843                         result = 1;
2844                 }
2845                 else
2846 // Pressed in expander
2847                 if(test_expanders())
2848                 {
2849                         current_operation = EXPAND_DN;
2850 // Need to redraw items because of alpha
2851                         draw_items(1);
2852                         result = 1;
2853                 }
2854                 else
2855 // Pressed over item
2856                 if((selection_number = get_cursor_item(data, 
2857                                         gui->get_cursor_x(), 
2858                                         gui->get_cursor_y(),
2859                                         &current_item)) >= 0)
2860                 {
2861 // Get item button was pressed over
2862                         selection_number2 = selection_number1;
2863                         selection_number1 = selection_number;
2865                         selection_start = -1;
2866                         selection_end = -1;
2869 // Multiple item selection is possible
2870                         if(selection_mode == LISTBOX_MULTIPLE && 
2871                                 (ctrl_down() || shift_down()))
2872                         {
2873 // Expand text selection.
2874 // Fill items between selected region and current item.
2875                                 if(shift_down() && display_format == LISTBOX_TEXT)
2876                                 {
2877 // Get first item selected
2878                                         selection_start = get_first_selection(data);
2879 // Get last item selected
2880                                         selection_end = get_last_selection(data);
2881 // Get center of selected region
2882                                         if(selection_end > selection_start)
2883                                         {
2884                                                 selection_center = (selection_end + selection_start) >> 1;
2885                                         }
2886                                         else
2887                                         {
2888                                                 selection_center = selection_number;
2889                                         }
2892 // Deselect everything.
2893                                         set_all_selected(data, 0);
2894 // Select just the items
2895                                         expand_selection(1, selection_number);
2896                                         new_value = 1;
2897                                 }
2898                                 else
2899 // Toggle a single item on or off
2900                                 {
2901                                         toggle_item_selection(data, selection_number);
2902                                         new_value = current_item->selected;
2903                                 }
2904                         }
2905                         else
2906 // Select single item
2907                         {
2908                                 if(!current_item->selected)
2909                                 {
2910                                         set_all_selected(data, 0);
2911                                         set_selected(data,
2912                                                 selection_number,
2913                                                 1);
2914                                 }
2915                                 new_value = 1;
2916                         }
2919                         current_operation = SELECT;
2920                         highlighted_item = selection_number;
2921                         highlighted_ptr = current_item;
2922                         button_highlighted = 0;
2923                         list_highlighted = 1;
2924                         reset_query();
2925                         draw_items(1);
2926                         do_selection_change = 1;
2927                         result = 1;
2928                 }
2929                 else
2930                 if(data)
2931 // Pressed over nothing.  Start rectangle selection.
2932                 {
2933                         if(get_buttonpress() == 1 && 
2934                                 selection_mode == LISTBOX_MULTIPLE)
2935                         {
2936                                 if(!shift_down())
2937                                 {
2938 // Deselect all and redraw if anything was selected
2939                                         if(get_selection_number(0, 0) >= 0)
2940                                         {
2941                                                 set_all_selected(data, 0);
2942                                                 draw_items(1);
2943                                                 do_selection_change = 1;
2944                                                 result = 1;
2945                                         }
2946                                 }
2947                                 else
2948                                 {
2949 // Promote selections to protect from a rectangle selection
2950                                         promote_selections(data, 1, 2);
2951                                 }
2953 // Start rectangle selection
2954                                 current_operation = SELECT_RECT;
2955                                 rect_x1 = rect_x2 = get_cursor_x();
2956                                 rect_y1 = rect_y2 = get_cursor_y();
2957                         }
2958                 }
2961                 reset_query();
2962         }
2963         else
2964         if(is_popup && active)
2965         {
2966                 deactivate();
2967                 result = 1;
2968         }
2971         if(do_selection_change) selection_changed();
2973         return result;
2976 int BC_ListBox::button_release_event()
2978         int result = 0;
2979         int cursor_x, cursor_y;
2980         int do_event = 0;
2981         new_value = 0;
2983 //printf("BC_ListBox::button_release_event 1 %d\n", current_operation);
2984         switch(current_operation)
2985         {
2986                 case DRAG_DIVISION:
2987                         current_operation = NO_OPERATION;
2988                         result = 1;
2989                         break;
2991                 case WHEEL:
2992                         current_operation = NO_OPERATION;
2993                         result = 1;
2994                         break;
2996 // Release item selection
2997                 case BUTTON_DOWN_SELECT:
2998                 case SELECT:
2999 //printf("BC_ListBox::button_release_event 10\n");
3000                         unset_repeat(get_resources()->scroll_repeat);
3001                         current_operation = NO_OPERATION;
3002                         translate_coordinates(top_level->event_win,
3003                                 gui->win,
3004                                 gui->get_cursor_x(),
3005                                 gui->get_cursor_y(),
3006                                 &cursor_x,
3007                                 &cursor_y);
3009                         selection_number1 = 
3010                                 selection_number = 
3011                                 get_cursor_item(data, cursor_x, cursor_y);
3012 //printf("BC_ListBox::button_release_event %d %d\n", selection_number2, selection_number1);
3014                         if(is_popup)
3015                         {
3016                                 button_releases++;
3017                                 if(selection_number >= 0)
3018                                 {
3019                                         deactivate();
3020                                         do_event = 1;
3021                                 }
3022                                 else
3023 // Second button release outside button
3024                                 if(button_releases > 1)
3025                                 {
3026                                         deactivate();
3027                                 }
3028                         }
3029                         else
3030                         {
3031                                 if(top_level->get_double_click() &&
3032                                         selection_number2 == selection_number1 &&
3033                                         selection_number2 >= 0 &&
3034                                         selection_number1 >= 0)
3035                                 {
3036                                         do_event = 1;
3037                                 }
3038                                 result = 1;
3039                         }
3040                         break;
3043                 case SELECT_RECT:
3044                         unset_repeat(get_resources()->scroll_repeat);
3045                         if(data)
3046                         {
3047 // Demote selections from rectangle selection
3048                                 promote_selections(data, 2, 1);
3049                         }
3051 // Hide rectangle overlay
3052                         draw_rectangle(1);
3053                         current_operation = NO_OPERATION;
3054                         result = 1;
3055                         break;
3057 // Release popup button
3058                 case BUTTON_DN:
3059                         hide_tooltip();
3060                         current_operation = NO_OPERATION;
3061                         button_releases++;
3062                         draw_button();
3064 // Second button release inside button
3065                         if(button_releases > 1)
3066                         {
3067                                 deactivate();
3068                         }
3069                         result = 1;
3070                         break;
3072                 case COLUMN_DN:
3073                         current_operation = NO_OPERATION;
3074 // Update the sort column and the sort order for the user only if the existing
3075 // sort column is valid.
3076                         if(sort_column >= 0)
3077                         {
3078 // Invert order only if column is the same
3079                                 if(highlighted_title == sort_column)
3080                                         sort_order = 
3081                                                 (sort_order == SORT_ASCENDING) ? 
3082                                                 SORT_DESCENDING : 
3083                                                 SORT_ASCENDING;
3084 // Set the new sort column
3085                                 sort_column = highlighted_title;
3086                                 if(!sort_order_event())
3087                                 {
3088                                         draw_titles(1);
3089                                 }
3090                         }
3091                         else
3092 // Sorting not enabled.  Redraw the title state.
3093                         {
3094                                 draw_titles(1);
3095                         }
3096                         result = 1;
3097                         break;
3099                 case EXPAND_DN:
3100                 {
3101                         int redraw_toggles = 0;
3102                         for(int i = 0; i < expanders.total && !result; i++)
3103                         {
3104                                 if(expanders.values[i]->button_release_event(&redraw_toggles))
3105                                 {
3106                                         result = 1;
3107                                 }
3108                         }
3109 // Need to redraw items because of alpha
3110                         if(redraw_toggles) draw_items(1);
3111                         current_operation = NO_OPERATION;
3112                         break;
3113                 }
3115                 default:
3116 // Can't default to NO_OPERATION because it may be used in a drag event.
3117                         break;
3118         }
3121         if(do_event) handle_event();
3123         return result;
3126 int BC_ListBox::get_title_h()
3128         if(display_format == LISTBOX_TEXT)
3129                 return column_titles ? column_bg[0]->get_h() : 0;
3130         else
3131                 return 0;
3134 void BC_ListBox::reset_cursor(int new_cursor)
3136         if(is_popup)
3137         {
3138                 if(gui->get_cursor() != new_cursor)
3139                 {
3140                         gui->set_cursor(new_cursor);
3141                 }
3142         }
3143         else
3144         if(get_cursor() != new_cursor)
3145         {
3146                 set_cursor(new_cursor);
3147         }
3150 int BC_ListBox::test_column_divisions(int cursor_x, int cursor_y, int &new_cursor)
3152         if(gui &&
3153                 column_titles && 
3154                 cursor_y >= 0 && 
3155                 cursor_y < get_title_h() &&
3156                 cursor_x >= 0 &&
3157                 cursor_x < gui->get_w())
3158         {
3159                 for(int i = 1; i < columns; i++)
3160                 {
3161                         if(cursor_x >= -xposition + get_column_offset(i) - 5 &&
3162                                 cursor_x <  -xposition + get_column_offset(i) + 
3163                                         get_resources()->listbox_title_hotspot)
3164                         {
3165                                 highlighted_item = -1;
3166                                 highlighted_ptr = 0;
3167                                 highlighted_division = i;
3168                                 highlighted_title = -1;
3169                                 list_highlighted = 1;
3170                                 new_cursor = HSEPARATE_CURSOR;
3171                                 return 1;
3172                         }
3173                 }
3174         }
3175         highlighted_division = -1;
3176         return 0;
3179 int BC_ListBox::test_column_titles(int cursor_x, int cursor_y)
3181         if(gui &&
3182                 column_titles && 
3183                 cursor_y >= 0 && 
3184                 cursor_y < get_title_h() &&
3185                 cursor_x >= 0 && 
3186                 cursor_x < gui->get_w())
3187         {
3188                 for(int i = 0; i < columns; i++)
3189                 {
3190                         if(cursor_x >= -xposition + get_column_offset(i) &&
3191                                 (cursor_x < -xposition + get_column_offset(i + 1) ||
3192                                         i == columns - 1))
3193                         {
3194                                 highlighted_item = -1;
3195                                 highlighted_ptr = 0;
3196                                 highlighted_division = -1;
3197                                 highlighted_title = i;
3198                                 list_highlighted = 1;
3199                                 return 1;
3200                         }
3201                 }
3202         }
3203         highlighted_title = -1;
3204         return 0;
3207 int BC_ListBox::test_expanders()
3209         for(int i = 0; i < expanders.total; i++)
3210         {
3211                 if(expanders.values[i]->button_press_event())
3212                 {
3213                         current_operation = EXPAND_DN;
3214                         draw_toggles(1);
3215                         return 1;
3216                 }
3217         }
3218         return 0 ;
3221 int BC_ListBox::cursor_motion_event()
3223         int redraw = 0, result = 0;
3224         int new_cursor = ARROW_CURSOR;
3226         selection_number = -1;
3229         switch(current_operation)
3230         {
3231                 case BUTTON_DN:
3232 // Button pressed and slid off button
3233                         if(!cursor_inside())
3234                         {
3235                                 current_operation = BUTTON_DOWN_SELECT;
3236                                 draw_button();
3237                                 result = 1;
3238                         }
3239                         break;
3241                 case DRAG_DIVISION:
3242                 {
3243                         int new_w = get_cursor_x() + 
3244                                 xposition - 
3245                                 get_column_offset(highlighted_division - 1);
3246                         new_cursor = HSEPARATE_CURSOR;
3248                         if(column_width)
3249                         {
3250                                 column_width[highlighted_division - 1] = new_w;
3251                         }
3252                         else
3253                         {
3254                                 default_column_width[highlighted_division - 1] = new_w;
3255                         }
3257                         column_width_boundaries();
3259 // Force update of coords
3260                         set_autoplacement(data, 0, 1);
3261                         column_resize_event();
3263                         clamp_positions();
3264                         draw_items(1);
3265                         update_scrollbars();
3266                         result = 1;
3267                         break;
3268                 }
3270                 case SELECT_RECT:
3271                 {
3272                         if(test_drag_scroll(get_cursor_x(), get_cursor_y()))
3273                         {
3274                                 set_repeat(get_resources()->scroll_repeat);
3275                         }
3277                         int old_x1 = MIN(rect_x1, rect_x2);
3278                         int old_x2 = MAX(rect_x1, rect_x2);
3279                         int old_y1 = MIN(rect_y1, rect_y2);
3280                         int old_y2 = MAX(rect_y1, rect_y2);
3282                         int new_rect_x2 = get_cursor_x();
3283                         int new_rect_y2 = get_cursor_y();
3285                         int x1 = MIN(rect_x1, new_rect_x2);
3286                         int x2 = MAX(rect_x1, new_rect_x2);
3287                         int y1 = MIN(rect_y1, new_rect_y2);
3288                         int y2 = MAX(rect_y1, new_rect_y2);
3290 // Adjust rectangle coverage
3291                         if(old_x1 != x1 ||
3292                                 old_x2 != x2 ||
3293                                 old_y1 != y1 ||
3294                                 old_y2 != y2)
3295                         {
3296                                 if(data)
3297                                 {
3298                                         redraw = select_rectangle(data,
3299                                                 x1, 
3300                                                 y1,
3301                                                 x2, 
3302                                                 y2);
3303                                 }
3305 // hide rectangle
3306                                 if(!redraw)
3307                                         draw_rectangle(0);
3308                         }
3310                         rect_x2 = get_cursor_x();
3311                         rect_y2 = get_cursor_y();
3312                         if(redraw)
3313                         {
3314                                 clamp_positions();
3315                                 draw_items(1);
3316                                 update_scrollbars();
3317                                 selection_changed();
3318                         }
3319                         else
3320                         {
3321                                 draw_rectangle(1);
3322                         }
3323                         result = 1;
3324                         break;
3325                 }
3327                 case SELECT:
3328                 {
3329                         int old_highlighted_item = highlighted_item;
3330                         int old_highlighted_title = highlighted_title;
3331                         BC_ListBoxItem *old_highlighted_ptr = highlighted_ptr;
3333                         if(test_drag_scroll(get_cursor_x(), 
3334                                 get_cursor_y()))
3335                         {
3336                                 set_repeat(get_resources()->scroll_repeat);
3337                         }
3340                         highlighted_item = selection_number = get_cursor_item(data, 
3341                                 get_cursor_x(), 
3342                                 get_cursor_y(),
3343                                 &highlighted_ptr);
3344                         result = 1;
3346 // Deselect all items and select just the one we're over
3347                         if(selection_number >= 0 && 
3348                                 !allow_drag &&
3349                                 ((!shift_down() &&
3350                                         !ctrl_down()) ||
3351                                         selection_mode == LISTBOX_SINGLE))
3352                         {
3353                                 redraw = update_selection(data, selection_number);
3354                         }
3355                         else
3356                         if(selection_mode == LISTBOX_MULTIPLE &&
3357                                 (shift_down() || ctrl_down()))
3358 // Expand multiple selection
3359                         {
3360 // Expand selected region in text mode centered around initial range
3361                                 if(display_format == LISTBOX_TEXT && shift_down())
3362                                 {
3363 // Deselect everything.
3364                                         set_all_selected(data, 0);
3366 // Select just the items
3367                                         redraw = expand_selection(0, selection_number);
3368                                 }
3369                                 else
3370 // Set the one item we're over to the selection value determined in
3371 // button_press_event.
3372                                 {
3373                                         set_selected(data, 
3374                                                 selection_number, 
3375                                                 new_value);
3376                                 }
3377                         }
3379                         if(highlighted_item != old_highlighted_item)
3380                         {
3381                                 clamp_positions();
3382                                 draw_items(1);
3383                                 update_scrollbars();
3384 //printf("BC_ListBox::cursor_motion_event %d %d\n", highlighted_item, old_highlighted_item);
3385                                 selection_changed();
3386                         }
3387                         break;
3388                 }
3390                 case BUTTON_DOWN_SELECT:
3391 // Went back into button area
3392                         if(cursor_inside())
3393                         {
3394                                 current_operation = BUTTON_DN;
3395                                 draw_button();
3396                                 result = 1;
3397                         }
3398                         else
3399 // Went into item area
3400                         if(gui)
3401                         {
3402                                 int cursor_x = 0, cursor_y = 0;
3403                                 translate_coordinates(top_level->event_win, 
3404                                         gui->win,
3405                                         top_level->cursor_x,
3406                                         top_level->cursor_y,
3407                                         &cursor_x,
3408                                         &cursor_y);
3409                                 int old_highlighted_item = highlighted_item;
3410                                 highlighted_item = selection_number = get_cursor_item(data, 
3411                                                 cursor_x, 
3412                                                 cursor_y,
3413                                                 &highlighted_ptr);
3415                                 if(highlighted_item != old_highlighted_item)
3416                                 {
3417                                         update_selection(data, selection_number);
3418                                         draw_items(1);
3419                                         selection_changed();
3420                                 }
3421                         }
3422                         break;
3424                 case EXPAND_DN:
3425                 {
3426                         int redraw_toggles = 0;
3427                         for(int i = 0; i < expanders.total && !result; i++)
3428                         {
3429                                 result = expanders.values[i]->cursor_motion_event(
3430                                         &redraw_toggles);
3431                         }
3432                         if(redraw_toggles)
3433                         {
3434 // Need to redraw items because of the alpha
3435                                 draw_items(1);
3436                         }
3437                         break;
3438                 }
3440                 case NO_OPERATION:
3441                 {
3442                         int cursor_x = get_cursor_x(), cursor_y = get_cursor_y();
3443                         if(gui && top_level->event_win == gui->win)
3444                         {
3445                                 int old_highlighted_title = highlighted_title;
3446                                 int old_list_highlighted = list_highlighted;
3447                                 int old_highlighted_division = highlighted_division;
3448                                 int old_highlighted_item = highlighted_item;
3449                                 int redraw_titles = 0;
3450                                 int redraw_border = 0;
3451                                 int redraw_items = 0;
3452                                 int redraw_toggles = 0;
3453                                 result = 1;
3456 // Test if cursor moved over a title division
3457                                 test_column_divisions(cursor_x, cursor_y, new_cursor);
3459 // Test if cursor moved over a title
3460                                 if(highlighted_division < 0)
3461                                 {
3462                                         test_column_titles(cursor_x, cursor_y);
3463                                 }
3465 // Test if cursor moved over expander
3466                                 if(highlighted_division < 0 && 
3467                                         highlighted_title < 0 &&
3468                                         display_format == LISTBOX_TEXT)
3469                                 {
3470                                         for(int i = 0; i < expanders.total; i++)
3471                                         {
3472                                                 expanders.values[i]->cursor_motion_event(
3473                                                         &redraw_toggles);
3474                                         }
3475 //printf("BC_ListBox::cursor_motion_event %d\n", redraw_toggles);
3476                                 }
3478 // Test if cursor moved over an item
3479                                 if(highlighted_division < 0 && 
3480                                         highlighted_title < 0)
3481                                 {
3482                                         highlighted_item = get_cursor_item(data, 
3483                                                 cursor_x, 
3484                                                 cursor_y,
3485                                                 &highlighted_ptr);
3486                                 }
3489 // Clear title highlighting if moved over division
3490                                 if(old_highlighted_title != highlighted_title)
3491                                 {
3492                                         redraw_titles = 1;
3493                                 }
3495 // Highlight list border
3496                                 if(old_list_highlighted != list_highlighted)
3497                                 {
3498                                         redraw_border = 1;
3499                                 }
3501 // Moved out of item area
3502                                 if(old_highlighted_item != highlighted_item)
3503                                 {
3504                                         redraw_items = 1;
3505                                 }
3507 //printf("BC_ListBox::cursor_motion_event 1 %d\n", highlighted_item);
3509 // Change cursor to title division adjustment
3510                                 reset_cursor(new_cursor);
3512                                 if(redraw_items)
3513                                 {
3514                                         draw_items(0);
3515                                 }
3516                                 else
3517                                 {
3518                                         if(redraw_titles)
3519                                                 draw_titles(0);
3520                                         if(redraw_border)
3521                                                 draw_border(0);
3522                                         if(redraw_toggles)
3523                                                 draw_toggles(0);
3524                                 }
3526                                 if(redraw_items || 
3527                                         redraw_titles || 
3528                                         redraw_border || 
3529                                         redraw_toggles)
3530                                 {
3531                                         gui->flash();
3532                                         gui->flush();
3533                                 }
3534                         }
3537                         if(!result && list_highlighted)
3538                         {
3539                                 list_highlighted = 0;
3540                                 highlighted_item = -1;
3541                                 highlighted_ptr = 0;
3542                                 highlighted_title = -1;
3543                                 highlighted_division = -1;
3544                                 draw_items(1);
3545                                 result = 0;
3546                         }
3547                         break;
3548                 }
3549         }
3552         return result;
3555 int BC_ListBox::drag_start_event()
3557         switch(current_operation)
3558         {
3559                 case SELECT:
3560                         if(gui && 
3561                                 gui->is_event_win() && 
3562                                 allow_drag)
3563                         {
3564                                 BC_ListBoxItem *item_return = 0;
3565                                 selection_number = get_cursor_item(data, 
3566                                         top_level->cursor_x, 
3567                                         top_level->cursor_y,
3568                                         &item_return);
3570                                 if(selection_number >= 0)
3571                                 {
3572                                         
3573                                         if (item_return->icon_vframe)
3574                                         {
3575                                                 drag_popup = new BC_DragWindow(this, 
3576                                                         item_return->icon_vframe, 
3577                                                         get_abs_cursor_x(0) - item_return->icon_vframe->get_w() / 2,
3578                                                         get_abs_cursor_y(0) - item_return->icon_vframe->get_h() / 2);
3579                                         }
3580                                         else    
3581 // this probably works not!
3582                                         if (item_return->icon)  
3583                                                 drag_popup = new BC_DragWindow(this, 
3584                                                         item_return->icon, 
3585                                                         get_abs_cursor_x(0) - item_return->icon->get_w() / 2,
3586                                                         get_abs_cursor_y(0) - item_return->icon->get_h() / 2);
3587                                         else
3588                                                 drag_popup = new BC_DragWindow(this, 
3589                                                         drag_icon_vframe, 
3590                                                         get_abs_cursor_x(0) - drag_icon_vframe->get_w() / 2,
3591                                                         get_abs_cursor_y(0) - drag_icon_vframe->get_h() / 2);
3592                                         current_operation = DRAG_ITEM;
3593                                         return 1;
3594                                 }
3595                         }
3596                         break;
3598                 case COLUMN_DN:
3599                         if(gui && gui->is_event_win() && allow_drag_column)
3600                         {
3601                                 drag_popup = new BC_DragWindow(this, 
3602                                         drag_column_icon_vframe, 
3603                                         get_abs_cursor_x(0) - drag_column_icon_vframe->get_w() / 2,
3604                                         get_abs_cursor_y(0) - drag_column_icon_vframe->get_h() / 2);
3605                                 dragged_title = highlighted_title;
3606                                 current_operation = COLUMN_DRAG;
3607                                 draw_titles(1);
3608                                 return 1;
3609                         }
3610                         break;
3611         }
3613         return 0;
3616 int BC_ListBox::drag_motion_event()
3618 //printf("BC_ListBox::drag_motion_event 1 %d\n", current_operation);
3619         switch(current_operation)
3620         {
3621                 case DRAG_ITEM:
3622                 {
3623                         int redraw = 0;
3625                         int new_highlighted_item = -1;
3626                         BC_ListBoxItem *new_highlighted_ptr = 0;
3627                         int new_highlight = new_highlighted_item = get_cursor_item(data,
3628                                 top_level->cursor_x, 
3629                                 top_level->cursor_y,
3630                                 &new_highlighted_ptr);
3632                         if(new_highlighted_item != highlighted_item)
3633                         {
3634                                 redraw = 1;
3635                         }
3637 // Always update highlighted value for drag_stop
3638                         highlighted_item = new_highlighted_item;
3639                         highlighted_ptr = new_highlighted_ptr;
3640 //printf("BC_ListBox::drag_motion_event 1 %p\n", highlighted_ptr);
3641                         if(redraw)
3642                         {
3643                                 clamp_positions();
3644                                 draw_items(1);
3645                                 update_scrollbars();
3646                         }
3648                         return drag_popup->cursor_motion_event();
3649                         break;
3650                 }
3652                 case COLUMN_DRAG:
3653                 {
3654                         int old_highlighted_title = highlighted_title;
3655                         test_column_titles(get_cursor_x(), get_cursor_y());
3656                         if(old_highlighted_title != highlighted_title)
3657                         {
3658                                 draw_titles(1);
3659                         }
3660                         return drag_popup->cursor_motion_event();
3661                         break;
3662                 }
3663         }
3664         return 0;
3667 int BC_ListBox::drag_stop_event()
3669         switch(current_operation)
3670         {
3671                 case DRAG_ITEM:
3672 // Inside window boundary
3673                         if(top_level->cursor_x > 0 && 
3674                                 top_level->cursor_x < gui->get_w() - drag_popup->get_w() / 2 && 
3675                                 top_level->cursor_y > 0 &&
3676                                 top_level->cursor_y < gui->get_h() - drag_popup->get_h() / 2)
3677                         {
3678 // Move icon
3681                                 if(display_format == LISTBOX_ICONS)
3682                                 {
3683                                         reposition_item(data, 
3684                                                 selection_number, 
3685                                                 top_level->cursor_x + 
3686                                                         drag_popup->get_offset_x() - 
3687                                                         LISTBOX_MARGIN - 
3688                                                         2 + 
3689                                                         xposition,
3690                                                 top_level->cursor_y + 
3691                                                         drag_popup->get_offset_y() - 
3692                                                         LISTBOX_MARGIN - 
3693                                                         2 + 
3694                                                         yposition);
3695                                 }
3696                                 else
3697 // Move rows
3698                                 if(process_drag)
3699                                 {
3700 // Get destination
3701                                         int destination = highlighted_item = item_to_index(data,
3702                                                 highlighted_ptr);
3703 //printf("BC_ListBox::drag_stop_event 1 %p %d\n", highlighted_ptr, destination);
3705 // Move selected items from data to temporary
3706                                         ArrayList<BC_ListBoxItem*> *src_items = 
3707                                                 new ArrayList<BC_ListBoxItem*>[columns];
3709                                         move_selection(src_items, data);
3711 // Insert items from temporary to data
3712                                         put_selection(data,
3713                                                 src_items,
3714                                                 destination);
3717                                         delete [] src_items;                            
3718                                         set_autoplacement(data, 0, 1);
3719                                 }
3721                         
3722                                 draw_items(1);
3723                         }
3724                         else
3725                                 drag_popup->drag_failure_event();
3727                         delete drag_popup;
3728                         drag_popup = 0;
3729                         current_operation = NO_OPERATION;
3730                         new_value = 0;
3731                         return 1;
3732                         break;
3734                 case COLUMN_DRAG:
3735                         if(dragged_title != highlighted_title)
3736                         {
3737                                 if(highlighted_title >= 0)
3738                                 {
3739                                         if(!move_column_event()) draw_titles(1);
3740                                 }
3741                                 else
3742                                         drag_popup->drag_failure_event();
3743                         }
3744                         current_operation = NO_OPERATION;
3745                         delete drag_popup;
3746                         drag_popup = 0;
3747                         return 1;
3748                         break;
3749         }
3750         return 0;
3753 BC_DragWindow* BC_ListBox::get_drag_popup()
3755         return drag_popup;
3758 int BC_ListBox::translation_event()
3760         if(is_popup && gui)
3761         {
3762                 int new_x = gui->get_x() + 
3763                         (top_level->last_translate_x - 
3764                                 top_level->prev_x - 
3765                                 top_level->get_resources()->get_left_border());
3766                 int new_y = gui->get_y() + 
3767                         (top_level->last_translate_y - 
3768                                 top_level->prev_y -
3769                                 top_level->get_resources()->get_top_border());
3771                 gui->reposition_window(new_x, new_y);
3772                 
3773         }
3774         return 0;
3777 int BC_ListBox::reposition_window(int x, int y, int w, int h)
3779         if(w != -1)
3780         {
3781                 if(w != -1) popup_w = w;
3782                 if(h != -1) popup_h = h;
3783 //printf("BC_ListBox::reposition_window %d %d\n", popup_w, popup_h);
3785                 if(!is_popup)
3786                 {
3787                         if(w != -1) popup_w = w;
3788                         if(h != -1) popup_h = h;
3789                         if(xscrollbar)
3790                                 xscrollbar->reposition_window(get_xscroll_x(), 
3791                                         get_xscroll_y(), 
3792                                         get_xscroll_width());
3793                         if(yscrollbar)
3794                                 yscrollbar->reposition_window(get_yscroll_x(), 
3795                                         get_yscroll_y(), 
3796                                         get_yscroll_height());
3797                 }
3798         }
3801         BC_WindowBase::reposition_window(x, y, w, h);
3802         draw_button();
3803         draw_items(1);
3804         return 0;
3807 int BC_ListBox::deactivate()
3809         if(active)
3810         {
3811                 active = 0;
3812                 if(is_popup)
3813                 {
3814                         if(gui) delete gui;
3815                         xscrollbar = 0;
3816                         yscrollbar = 0;
3817                         gui = 0;
3818                         highlighted_item = -1;
3819                         highlighted_ptr = 0;
3820                 }
3821                 top_level->active_subwindow = 0;
3822         }
3823         return 0;
3826 int BC_ListBox::activate()
3828         if(!active)
3829         {
3830                 top_level->active_subwindow = this;
3831                 active = 1;
3832                 button_releases = 0;
3834                 if(is_popup)
3835                 {
3836                         Window tempwin;
3837                         int x, y;
3838                         int new_x, new_y;
3839                         y = get_y() + get_h();
3840                         if(justify == LISTBOX_RIGHT)
3841                         {
3842                                 x = get_x() - popup_w + get_w();
3843                         }
3844                         else
3845                         {
3846                                 x = get_x();
3847                         }
3849                         
3850                         XTranslateCoordinates(top_level->display, 
3851                                 parent_window->win, 
3852                                 top_level->rootwin, 
3853                                 x, 
3854                                 y, 
3855                                 &new_x, 
3856                                 &new_y, 
3857                                 &tempwin);
3859                         if(new_x < 0) new_x = 0;
3860                         if(new_y + popup_h > top_level->get_root_h(0)) 
3861                                 new_y -= get_h() + popup_h;
3863 //printf("BC_ListBox::activate %d %d\n", popup_w, popup_h);
3864                         add_subwindow(gui = new BC_Popup(this, 
3865                                 new_x, 
3866                                 new_y, 
3867                                 popup_w, 
3868                                 popup_h, 
3869                                 -1,
3870                                 0,
3871                                 0));
3872                         draw_items(1);
3873                 }
3874         }
3875         return 0;
3878 int BC_ListBox::keypress_event()
3880         if(!active) return 0;
3881         
3882         int result = 0, redraw = 0, done, view_items = view_h / get_text_height(MEDIUMFONT);
3883         int new_item = -1, new_selection = 0;
3885         switch(top_level->get_keypress())
3886         {
3887                 case ESC:
3888                 case RETURN:
3889                         top_level->deactivate();
3890                         result = 0;
3891                         break;
3893                 case UP:
3894                         new_selection = new_item = select_previous(0);
3896 //printf("BC_ListBox::keypress_event 1 %d\n", new_item);
3897                         if(new_item >= 0)
3898                         {
3899                                 center_selection(new_item);
3900                                 redraw = 1;
3901                         }
3902                         result = 1;
3903                         break;
3905                 case DOWN:
3906                         new_selection = new_item = select_next(0);
3908                         if(new_item >= 0)
3909                         {
3910                                 center_selection(new_item);
3911                                 redraw = 1;
3912                         }
3913                         result = 1;
3914                         break;
3916                 case PGUP:
3917                         new_selection = new_item = select_previous(view_items - 1);
3919                         if(new_item >= 0)
3920                         {
3921                                 center_selection(new_item);
3922                                 redraw = 1;
3923                         }
3924                         result = 1;
3925                         break;
3927                 case PGDN:
3928                         new_selection = new_item = select_next(view_items - 1);
3930                         if(new_item >= 0)
3931                         {
3932                                 center_selection(new_item);
3933                                 redraw = 1;
3934                         }
3935                         result = 1;
3936                         break;
3938                 case LEFT:
3939                         xposition -= 10;
3940                         redraw = 1;
3941                         result = 1;
3942                         break;
3944                 case RIGHT:
3945                         xposition += 10;
3946                         redraw = 1;
3947                         result = 1;
3948                         break;
3950                 default:
3951                         if(!ctrl_down())
3952                         {
3953                                 if(top_level->get_keypress() > 30 && 
3954                                         top_level->get_keypress() < 127)
3955                                 {
3956                                         int query_len = strlen(query);
3957                                         query[query_len++] = top_level->get_keypress();
3958                                         query[query_len] = 0;
3959                                         new_selection = query_list();
3960                                 }
3961                                 else
3962                                 if(top_level->get_keypress() == BACKSPACE)
3963                                 {
3964                                         int query_len = strlen(query);
3965                                         if(query_len > 0) query[--query_len] = 0;
3966                                         new_selection = query_list();
3967                                 }
3969                                 redraw = 1;
3970                                 result = 1;
3971                         }
3972                         break;
3973         }
3975         if(redraw)
3976         {
3977                 clamp_positions();
3978                 draw_items(1);
3979                 update_scrollbars();
3980         }
3981         
3982         if(new_selection >= 0)
3983         {
3984                 selection_changed();
3985         }
3987         return result;
3991 BC_Pixmap* BC_ListBox::get_bg_surface()
3993         return bg_surface;
3997 void BC_ListBox::draw_background()
3999 // White background pixmap
4000         set_color(top_level->get_resources()->listbox_inactive);
4001         draw_box(0, 0, bg_surface->get_w(), bg_surface->get_h(), bg_surface);
4003 // Optional heroine pixmap
4004         if(bg_pixmap)
4005                 bg_surface->draw_pixmap(bg_pixmap,
4006                         bg_surface->get_w() - top_level->get_resources()->listbox_bg->get_w(),
4007                         0);
4010 void BC_ListBox::clear_listbox(int x, int y, int w, int h)
4012         gui->draw_pixmap(bg_surface, 
4013                 x, 
4014                 y, 
4015                 w, 
4016                 h,
4017                 x,
4018                 y - title_h);
4021 void BC_ListBox::update_format(int display_format, int redraw)
4023         this->display_format = display_format;
4024         if(redraw)
4025         {
4026                 if(gui) draw_items(1);
4027         }
4030 int BC_ListBox::get_format()
4032         return display_format;
4037 int BC_ListBox::draw_items(int flash)
4039         if(gui)
4040         {
4041                 BC_Resources *resources = get_resources();
4043 //dump(data, columns);
4045 // Calculate items width 
4046                 calculate_item_coords();
4049 // Create and destroy scrollbars as needed
4050                 get_scrollbars();
4054 //              draw_background();
4056 // Icon display
4057                 if(display_format == LISTBOX_ICONS)
4058                 {
4059                         clear_listbox(2, 2 + title_h, view_w, view_h);
4061                         set_font(MEDIUMFONT);
4062                         for(int i = 0; i < data[master_column].total; i++)
4063                         {
4064                                 BC_ListBoxItem *item = data[master_column].values[i];
4065                                 if(get_item_x(item) >= -get_item_w(item) && 
4066                                         get_item_x(item) < view_w &&
4067                                         get_item_y(item) >= -get_item_h(item) + title_h &&
4068                                         get_item_y(item) < view_h + title_h)
4069                                 {
4070                                         int item_color = get_item_highlight(data, 0, i);
4071                                         int icon_x, icon_y, icon_w, icon_h;
4072                                         int text_x, text_y, text_w, text_h;
4074 // Draw highlights
4075                                         get_icon_mask(item, icon_x, icon_y, icon_w, icon_h);
4076                                         get_text_mask(item, text_x, text_y, text_w, text_h);
4079                                         if(item_color != resources->listbox_inactive)
4080                                         {
4081                                                 gui->set_color(BLACK);
4082                                                 gui->draw_rectangle(icon_x, icon_y, icon_w, icon_h);
4083                                                 gui->set_color(item_color);
4084                                                 gui->draw_box(icon_x + 1, icon_y + 1, icon_w - 2, icon_h - 2);
4085                                                 gui->set_color(BLACK);
4086                                                 gui->draw_rectangle(text_x, text_y, text_w, text_h);
4087                                                 gui->set_color(item_color);
4088                                                 gui->draw_box(text_x + 1, text_y + 1, text_w - 2, text_h - 2);
4090                                                 if(icon_position == ICON_LEFT)
4091                                                         gui->draw_box(text_x - 1, text_y + 1, 2, text_h - 2);
4092                                                 else
4093                                                 if(icon_position == ICON_TOP)
4094                                                         gui->draw_line(text_x + 1, text_y, text_x + icon_w - 2, text_y);
4095                                                 if(text_x + text_w < icon_x + icon_w)
4096                                                 {
4097                                                         gui->set_color(BLACK);
4098                                                         gui->draw_line(text_x + text_w, 
4099                                                                 icon_y + icon_h,
4100                                                                 icon_x + icon_w,
4101                                                                 icon_y + icon_h);
4102                                                 }
4103                                         }
4105 // Draw icons
4106                                         gui->set_color(get_item_color(data, 0, i));
4107                                         if(item->icon)
4108                                                 gui->pixmap->draw_pixmap(item->icon, 
4109                                                         icon_x + ICON_MARGIN, 
4110                                                         icon_y + ICON_MARGIN);
4111                                         gui->draw_text(text_x + ICON_MARGIN, 
4112                                                 text_y + ICON_MARGIN + get_text_ascent(MEDIUMFONT), 
4113                                                 item->text);
4114                                 }
4115                         }
4116                 }
4117                 else
4118 // Text display
4119                 if(display_format == LISTBOX_TEXT)
4120                 {
4121 // Draw one column at a time so text overruns don't go into the next column
4122 // clear column backgrounds
4123                         int current_toggle = 0;
4124                         for(int j = 0; j < columns; j++)
4125                         {
4126                                 clear_listbox(LISTBOX_BORDER + get_column_offset(j) - xposition, 
4127                                         LISTBOX_BORDER + title_h, 
4128                                         get_column_width(j, 1), 
4129                                         view_h);
4131 // Draw rows in the column recursively
4132                                 draw_text_recursive(data, j, 0, &current_toggle);
4133                         }
4135 // Delete excess expanders
4136                         while(expanders.total > current_toggle)
4137                         {
4138                                 expanders.remove_object();
4139                         }
4140                 }
4142 // Draw titles on top of rows for superposition effect
4143                 draw_titles(0);
4145 // Clear garbage from bottom right corner
4146                 if(xscrollbar && yscrollbar && is_popup)
4147                 {
4148                         gui->draw_top_background(parent_window, 
4149                                 popup_w - get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w(), 
4150                                 popup_h - get_resources()->hscroll_data[SCROLL_HANDLE_UP]->get_h(), 
4151                                 get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w(),
4152                                 get_resources()->hscroll_data[SCROLL_HANDLE_UP]->get_h());
4153                 }
4155 // Draw borders
4156                 draw_border(0);
4159                 if(current_operation == SELECT_RECT)
4160                         draw_rectangle(0);
4162                 if(flash)
4163                 {
4164                         gui->flash();
4165                         gui->flush();
4166                 }
4167         }
4169         return 0;
4173 void BC_ListBox::draw_text_recursive(ArrayList<BC_ListBoxItem*> *data, 
4174         int column,
4175         int indent,
4176         int *current_toggle)
4178         if(!data) return;
4181         BC_Resources *resources = get_resources();
4183         set_font(MEDIUMFONT);
4184         int subindent = 0;
4186 // Search for a branch and make room for toggle if there is one
4187         if(column == 0)
4188         {
4189                 for(int i = 0; i < data[column].total; i++)
4190                 {
4191                         if(data[column].values[i]->get_sublist())
4192                         {
4193                                 subindent = BC_WindowBase::get_resources()->listbox_expand[0]->get_w();
4194                                 break;
4195                         }
4196                 }
4197         }
4199         for(int i = 0; i < data[column].total; i++)
4200         {
4201 // Draw a row
4202                 BC_ListBoxItem *item = data[column].values[i];
4203                 BC_ListBoxItem *first_item = data[master_column].values[i];
4205                 if(get_item_y(item) >= -get_item_h(item) + title_h &&
4206                         get_item_y(item) < view_h + title_h)
4207                 {
4208                         int row_color = get_item_highlight(data, 0, i);
4209                         int x, y, w, h, column_width;
4211                         get_text_mask(item, x, y, w, h);
4212                         column_width = get_column_width(column, 1);
4213                         if(x + column_width > view_w + LISTBOX_BORDER * 2)
4214                                 column_width = view_w + LISTBOX_BORDER * 2 - x;
4216                         if(row_color != resources->listbox_inactive)
4217                         {
4218                                 gui->set_color(row_color);
4219                                 gui->draw_box(x, 
4220                                         y, 
4221                                         column_width, 
4222                                         h);
4223                                 gui->set_color(BLACK);
4224                                 gui->draw_line(x, 
4225                                         y, 
4226                                         x + column_width - 1, 
4227                                         y);
4228                                 gui->draw_line(x, 
4229                                         y + get_text_height(MEDIUMFONT), 
4230                                         x + column_width - 1, 
4231                                         y + get_text_height(MEDIUMFONT));
4232                         }
4234                         gui->set_color(get_item_color(data, column, i));
4237 // Indent only applies to first column
4238                         gui->draw_text(
4239                                 x + 
4240                                         LISTBOX_BORDER + 
4241                                         LISTBOX_MARGIN + 
4242                                         (column == 0 ? indent + subindent : 0), 
4243                                 y + get_text_ascent(MEDIUMFONT), 
4244                                 item->text);
4247 // Update expander
4248                         if(column == 0 &&
4249                                 item->get_sublist() && 
4250                                 item->get_columns())
4251                         {
4252 // Create new expander
4253                                 if(*current_toggle >= expanders.total)
4254                                 {
4255                                         BC_ListBoxToggle *toggle = 
4256                                                 new BC_ListBoxToggle(this, 
4257                                                         item, 
4258                                                         x + LISTBOX_BORDER + LISTBOX_MARGIN + indent,
4259                                                         y);
4260                                         toggle->draw(0);
4261                                         expanders.append(toggle);
4262                                 }
4263                                 else
4264 // Reposition existing expander
4265                                 {
4266                                         BC_ListBoxToggle *toggle = expanders.values[*current_toggle];
4267 //printf("BC_ListBox::draw_text_recursive 1 %d\n", *current_toggle);
4268                                         toggle->update(item, 
4269                                                 x + LISTBOX_BORDER + LISTBOX_MARGIN + indent,
4270                                                 y,
4271                                                 0);
4272                                 }
4273                                 (*current_toggle)++;
4274                         }
4278                 }
4280 // Descend into sublist
4281                 if(first_item->get_expand())
4282                 {
4283                         draw_text_recursive(first_item->get_sublist(), 
4284                                 column, 
4285                                 indent + LISTBOX_INDENT, 
4286                                 current_toggle);
4287                 }
4288         }
4295 int BC_ListBox::draw_border(int flash)
4297         BC_Resources *resources = top_level->get_resources();
4298         gui->draw_3d_border(0, 
4299                 0, 
4300                 view_w + LISTBOX_BORDER * 2, 
4301                 view_h + title_h + LISTBOX_BORDER * 2, 
4302                 resources->listbox_border1, 
4303                 list_highlighted ? 
4304                         resources->listbox_border2_hi : 
4305                         resources->listbox_border2, 
4306                 list_highlighted ? 
4307                         resources->listbox_border3_hi : 
4308                         resources->listbox_border3, 
4309                 resources->listbox_border4);
4311         if(flash)
4312         {
4313                 gui->flash();
4314                 gui->flush();
4315         }
4316         return 0;
4319 int BC_ListBox::draw_titles(int flash)
4321         if(column_titles && display_format == LISTBOX_TEXT)
4322         {
4323 //printf("BC_ListBox::draw_titles 1 %d\n", highlighted_title);
4324                 for(int i = 0; i < columns; i++)
4325                 {
4328 // Column title background
4329                         int image_number = 0;
4330                         if(i == highlighted_title)
4331                         {
4332                                 image_number = 1;
4333                                 if(current_operation == COLUMN_DN)
4334                                         image_number = 2;
4335                         }
4337                         int column_offset = get_column_offset(i) - xposition + LISTBOX_BORDER;
4338                         int column_width = get_column_width(i, 1);
4339                         gui->draw_3segmenth(get_column_offset(i) - xposition + LISTBOX_BORDER,
4340                                 LISTBOX_BORDER,
4341                                 get_column_width(i, 1),
4342                                 column_bg[image_number]);
4344 // Column title sort order
4345                         if(i == sort_column)
4346                         {
4347                                 BC_Pixmap *src;
4348                                 if(sort_order == SORT_ASCENDING) 
4349                                         src = column_sort_dn;
4350                                 else
4351                                         src = column_sort_up;
4353                                 int x = column_offset + 
4354                                         column_width - 
4355                                         LISTBOX_BORDER;
4356                                 if(x > items_w) x = items_w;
4357                                 x -= 5 + src->get_w();
4358                                 gui->draw_pixmap(src,
4359                                         x,
4360                                         title_h / 2 - src->get_h() / 2 + LISTBOX_BORDER);
4361                         }
4364                         int x = -xposition + 
4365                                 get_column_offset(i) + 
4366                                 LISTBOX_MARGIN + 
4367                                 LISTBOX_BORDER;
4368                         x += get_resources()->listbox_title_margin;
4370                         gui->set_color(get_resources()->listbox_title_color);
4371                         gui->draw_text(x, 
4372                                 LISTBOX_MARGIN + LISTBOX_BORDER + get_text_ascent(MEDIUMFONT), 
4373                                 _(column_titles[i]));
4374                 }
4375                 draw_border(0);
4376         }
4378         if(flash)
4379         {
4380                 gui->flash();
4381                 gui->flush();
4382         }
4385 void BC_ListBox::draw_toggles(int flash)
4387         for(int i = 0; i < expanders.total; i++)
4388                 expanders.values[i]->draw(0);
4390 //printf("BC_ListBox::draw_toggles 1 %d\n", flash);
4391         if(flash && expanders.total)
4392         {
4393                 gui->flash();
4394                 gui->flush();
4395         }
4398 int BC_ListBox::draw_rectangle(int flash)
4400         int x1 = MIN(rect_x1, rect_x2);
4401         int x2 = MAX(rect_x1, rect_x2);
4402         int y1 = MIN(rect_y1, rect_y2);
4403         int y2 = MAX(rect_y1, rect_y2);
4405         if(x1 == x2 || y1 == y2) return 0;
4407         gui->set_inverse();
4408         gui->set_color(WHITE);
4409         gui->draw_rectangle(x1, y1, x2 - x1, y2 - y1);
4410         gui->set_opaque();
4413         if(flash)
4414         {
4415                 gui->flash();
4416                 gui->flush();
4417         }
4418         return 0;
4421 void BC_ListBox::dump(ArrayList<BC_ListBoxItem*> *data, 
4422         int columns, 
4423         int indent,
4424         int master_column)
4426         if(!indent)
4427         {
4428                 printf("BC_ListBox::dump 1\n");
4429         }
4431         for(int i = 0; i < data[master_column].total; i++)
4432         {
4433                 for(int k = 0; k < indent; k++)
4434                         printf(" ");
4435                 for(int j = 0; j < columns; j++)
4436                 {
4437                         BC_ListBoxItem *item = data[j].values[i];
4438                         printf("%d,%d,%d=%s ", 
4439                                 item->get_text_x(), 
4440                                 item->get_text_y(),
4441                                 item->autoplace_text, 
4442                                 item->get_text());
4443                 }
4444                 printf("\n");
4446                 if(data[master_column].values[i]->get_sublist())
4447                 {
4448                         dump(data[master_column].values[i]->get_sublist(),
4449                                 data[master_column].values[i]->get_columns(),
4450                                 indent + 4,
4451                                 master_column);
4452                 }
4453         }
4455