3 * Copyright (C) 2010-2014 Adam Williams <broadcast at earthling dot net>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 #include "bcdisplayinfo.h"
22 #include "bcdragwindow.h"
23 #include "bclistbox.h"
24 #include "bclistboxitem.h"
26 #include "bcresources.h"
27 #include "bcsignals.h"
39 // ====================================================== scrollbars
41 BC_ListBoxYScroll::BC_ListBoxYScroll(BC_ListBox *listbox)
42 : BC_ScrollBar(listbox->get_yscroll_x(), listbox->get_yscroll_y(),
43 listbox->yscroll_orientation, listbox->get_yscroll_height(),
44 listbox->items_h, listbox->yposition, listbox->view_h)
46 this->listbox = listbox;
49 BC_ListBoxYScroll::~BC_ListBoxYScroll()
53 int BC_ListBoxYScroll::handle_event()
55 listbox->set_yposition(get_value());
59 BC_ListBoxXScroll::BC_ListBoxXScroll(BC_ListBox *listbox)
60 : BC_ScrollBar(listbox->get_xscroll_x(), listbox->get_xscroll_y(),
61 listbox->xscroll_orientation, listbox->get_xscroll_width(),
62 listbox->items_w, listbox->xposition, listbox->view_w)
64 this->listbox = listbox;
67 BC_ListBoxXScroll::~BC_ListBoxXScroll()
71 int BC_ListBoxXScroll::handle_event()
73 listbox->set_xposition(get_value());
78 BC_ListBoxToggle::BC_ListBoxToggle(BC_ListBox *listbox,
83 this->listbox = listbox;
87 this->value = item->get_expand();
89 state = BC_Toggle::TOGGLE_CHECKED;
91 state = BC_Toggle::TOGGLE_UP;
94 void BC_ListBoxToggle::update(BC_ListBoxItem *item,
99 this->value = item->get_expand();
108 state = TOGGLE_CHECKED;
113 state = TOGGLE_CHECKEDHI;
124 case TOGGLE_CHECKEDHI:
129 case TOGGLE_DOWN_EXIT:
137 int BC_ListBoxToggle::cursor_motion_event(int *redraw_toggles)
139 int w = listbox->toggle_images[0]->get_w();
140 int h = listbox->toggle_images[0]->get_h();
141 int cursor_inside = listbox->get_cursor_x() >= x &&
142 listbox->get_cursor_x() < x + w &&
143 listbox->get_cursor_y() >= y &&
144 listbox->get_cursor_y() < y + h;
148 case BC_ListBoxToggle::TOGGLE_UPHI:
149 if( !cursor_inside ) {
150 state = BC_ListBoxToggle::TOGGLE_UP;
155 case BC_ListBoxToggle::TOGGLE_CHECKEDHI:
156 if( !cursor_inside ) {
157 state = BC_ListBoxToggle::TOGGLE_CHECKED;
162 case BC_ListBoxToggle::TOGGLE_DOWN:
163 if( !cursor_inside ) {
164 state = BC_ListBoxToggle::TOGGLE_DOWN_EXIT;
170 case BC_ListBoxToggle::TOGGLE_DOWN_EXIT:
171 if( cursor_inside ) {
172 state = BC_ListBoxToggle::TOGGLE_DOWN;
179 if( cursor_inside ) {
181 state = BC_ListBoxToggle::TOGGLE_CHECKEDHI;
183 state = BC_ListBoxToggle::TOGGLE_UPHI;
191 int BC_ListBoxToggle::cursor_leave_event(int *redraw_toggles)
194 state = BC_ListBoxToggle::TOGGLE_CHECKED;
196 state = BC_ListBoxToggle::TOGGLE_UP;
200 int BC_ListBoxToggle::button_press_event()
202 int w = listbox->toggle_images[0]->get_w();
203 int h = listbox->toggle_images[0]->get_h();
205 if( listbox->gui->get_cursor_x() >= x &&
206 listbox->gui->get_cursor_x() < x + w &&
207 listbox->gui->get_cursor_y() >= y &&
208 listbox->gui->get_cursor_y() < y + h ) {
209 state = BC_ListBoxToggle::TOGGLE_DOWN;
215 int BC_ListBoxToggle::button_release_event(int *redraw_toggles)
220 case BC_ListBoxToggle::TOGGLE_DOWN:
223 state = BC_ListBoxToggle::TOGGLE_CHECKEDHI;
225 state = BC_ListBoxToggle::TOGGLE_UPHI;
226 listbox->expand_item(item, value);
230 case BC_ListBoxToggle::TOGGLE_DOWN_EXIT:
232 state = BC_ListBoxToggle::TOGGLE_CHECKED;
234 state = BC_ListBoxToggle::TOGGLE_UP;
242 void BC_ListBoxToggle::draw(int flash)
245 int image_number = 0;
246 int w = listbox->toggle_images[0]->get_w();
247 int h = listbox->toggle_images[0]->get_h();
250 case BC_ListBoxToggle::TOGGLE_UP: image_number = 0; break;
251 case BC_ListBoxToggle::TOGGLE_UPHI: image_number = 1; break;
252 case BC_ListBoxToggle::TOGGLE_CHECKED: image_number = 2; break;
253 case BC_ListBoxToggle::TOGGLE_DOWN: image_number = 3; break;
254 case BC_ListBoxToggle::TOGGLE_CHECKEDHI: image_number = 4; break;
255 case BC_ListBoxToggle::TOGGLE_DOWN_EXIT:
256 image_number = value ? 2 : 0;
260 //printf("BC_ListBoxToggle::draw 1 %d\n", state);
261 listbox->gui->draw_pixmap(listbox->toggle_images[image_number], x, y);
264 listbox->gui->flash(x, y, w, h);
265 listbox->gui->flush();
271 // ====================================================== box
273 BC_ListBox::BC_ListBox(int x, int y, int w, int h,
274 int display_format, ArrayList<BC_ListBoxItem*> *data,
275 const char **column_titles, int *column_width, int columns,
276 int yposition, int is_popup, int selection_mode,
277 int icon_position, int allow_drag)
278 : BC_SubWindow(x, y, w, h, -1)
280 justify = LISTBOX_RIGHT;
282 highlighted_item = -1;
283 highlighted_title = -1;
284 highlighted_division = -1;
288 current_cursor = ARROW_CURSOR;
298 xscroll_orientation = SCROLL_HORIZ;
299 yscroll_orientation = SCROLL_VERT;
303 selection_number1 = -1;
304 selection_number2 = -1;
307 row_height = row_ascent = row_descent = 0;
309 current_operation = NO_OPERATION;
310 button_highlighted = 0;
311 list_highlighted = 0;
315 allow_drag_scroll = 1;
321 allow_drag_column = 0;
328 for( int i=0; i<3; ++i ) column_bg[i] = 0;
329 for( int i=0; i<4; ++i ) button_images[i] = 0;
330 for( int i=0; i<5; ++i ) toggle_images[i] = 0;
335 //printf("BC_ListBox::BC_ListBox 1\n");
337 this->columns = columns;
338 this->yposition = yposition;
339 this->is_popup = is_popup;
340 this->use_button = 1;
341 this->display_format = display_format;
342 this->selection_mode = selection_mode;
343 this->icon_position = icon_position;
344 this->allow_drag = allow_drag;
345 this->column_titles = 0;
346 this->column_width = 0;
347 this->first_in_view = -1;
348 this->last_in_view = 0;
349 //printf("BC_ListBox::BC_ListBox 1\n");
351 if( (!column_titles && column_width) ||
352 (column_titles && !column_width) ) {
353 printf("BC_ListBox::BC_ListBox either column_titles or column_widths == NULL but not both.\n");
355 //printf("BC_ListBox::BC_ListBox 2 %p %p\n", column_titles, column_width);
357 set_columns(column_titles, column_width, columns);
359 //printf("BC_ListBox::BC_ListBox 3\n");
361 drag_icon_vframe = 0;
362 drag_column_icon_vframe = 0;
366 // reset the search engine
367 //printf("BC_ListBox::BC_ListBox 4\n");
370 //printf("BC_ListBox::BC_ListBox 5\n");
373 BC_ListBox::~BC_ListBox()
375 expanders.remove_all_objects();
376 if( bg_surface ) delete bg_surface;
377 if( bg_pixmap ) delete bg_pixmap;
378 if( xscrollbar ) delete xscrollbar;
379 if( yscrollbar ) delete yscrollbar;
380 for( int i=0; i<3; ++i ) delete column_bg[i];
381 for( int i=0; i<4; ++i ) delete button_images[i];
382 for( int i=0; i<5; ++i ) delete toggle_images[i];
383 if( column_sort_up ) delete column_sort_up;
384 if( column_sort_dn ) delete column_sort_dn;
387 if( drag_popup ) delete drag_popup;
390 int BC_ListBox::enable()
397 int BC_ListBox::disable()
404 void BC_ListBox::reset_query()
406 query[0] = 0; // reset query
409 int BC_ListBox::evaluate_query(char *string)
411 for( int i=0; i<data[search_column].size(); ++i ) {
412 if( strcmp(string, data[search_column].get(i)->text) <= 0 &&
413 data[search_column].get(i)->searchable ) {
421 int BC_ListBox::query_list()
423 if( query[0] == 0 ) return 0;
427 int selection_changed = 0;
428 int prev_selection = -1;
429 result = evaluate_query(query);
430 if( result >= 0 ) done = 1;
434 for( int i=0; i<data[0].total; ++i ) {
435 for( int j=0; j<columns; ++j ) {
436 if( data[j].values[i]->selected ) prev_selection = i;
437 data[j].values[i]->selected = 0;
442 if( prev_selection != result )
443 selection_changed = 1;
444 for( int j=0; j<columns; ++j ) {
445 data[j].values[result]->selected = 1;
447 center_selection(result);
450 return selection_changed;
453 void BC_ListBox::init_column_width()
455 if( !column_width && data ) {
457 for( int i=0; i<data[0].total; ++i ) {
458 wd = get_text_w(data[0].values[i]);
459 if( wd > widest ) widest = wd;
461 default_column_width[0] = widest + 2 * LISTBOX_MARGIN;
465 int BC_ListBox::initialize()
469 for( int i=0; i<4; ++i ) {
470 button_images[i] = new BC_Pixmap(parent_window,
471 BC_WindowBase::get_resources()->listbox_button[i],
474 w = button_images[0]->get_w();
475 h = button_images[0]->get_h();
479 current_operation = NO_OPERATION;
484 current_operation = NO_OPERATION;
487 for( int i=0; i<3; ++i ) {
488 column_bg[i] = new BC_Pixmap(parent_window,
489 get_resources()->listbox_column[i],
492 for( int i=0; i<5; ++i ) {
493 toggle_images[i] = new BC_Pixmap(parent_window,
494 get_resources()->listbox_expand[i],
498 column_sort_up = new BC_Pixmap(parent_window,
499 BC_WindowBase::get_resources()->listbox_up,
501 column_sort_dn = new BC_Pixmap(parent_window,
502 BC_WindowBase::get_resources()->listbox_dn,
505 //printf("BC_ListBox::initialize 10\n");
506 drag_icon_vframe = get_resources()->type_to_icon[ICON_UNKNOWN];
507 drag_column_icon_vframe = get_resources()->type_to_icon[ICON_COLUMN];
508 // = new BC_Pixmap(parent_window,
509 // get_resources()->type_to_icon[ICON_UNKNOWN],
511 // drag_column_icon = new BC_Pixmap(parent_window,
512 // get_resources()->type_to_icon[ICON_COLUMN],
514 BC_SubWindow::initialize();
518 if( top_level->get_resources()->listbox_bg )
519 bg_pixmap = new BC_Pixmap(this,
520 get_resources()->listbox_bg,
526 if( !use_button && is_popup ) {
534 void BC_ListBox::deactivate_selection()
536 current_operation = NO_OPERATION;
539 int BC_ListBox::draw_button(int flush)
541 // Draw the button for a popup listbox
542 if( use_button && is_popup ) {
543 int image_number = 0;
545 draw_top_background(parent_window, 0, 0, w, h);
547 if( button_highlighted )
549 if( current_operation == BUTTON_DN )
554 pixmap->draw_pixmap(button_images[image_number],
561 int BC_ListBox::calculate_item_coords()
563 if( !data ) return 0;
569 // Change the display_format to get the right item dimensions for both
571 temp_display_format = display_format;
574 // Scan the first column for lowest y coord of all text
575 // and lowest right x and y coord for all icons which aren't auto placable
576 calculate_last_coords_recursive(data,
577 &icon_x, &next_icon_x, &next_icon_y, &next_text_y, 1);
579 // Reset last column width. It's recalculated based on text width.
580 calculate_item_coords_recursive(data,
581 &icon_x, &next_icon_x, &next_icon_y, &next_text_y, 1);
583 display_format = temp_display_format;
588 void BC_ListBox::calculate_last_coords_recursive(
589 ArrayList<BC_ListBoxItem*> *data,
596 for( int i=0; i<data[0].size(); ++i ) {
597 int current_text_y = 0;
598 int current_icon_x = 0;
599 int current_icon_y = 0;
600 BC_ListBoxItem *item = data[0].get(i);
603 if( !item->autoplace_text ) {
604 // Lowest text coordinate
605 display_format = LISTBOX_TEXT;
606 current_text_y = item->text_y + get_text_h(item);
607 if( current_text_y > *next_text_y )
608 *next_text_y = current_text_y;
610 // Add sublist depth if it is expanded
611 if( item->get_sublist() && item->get_columns() &&
612 item->get_expand() ) {
613 calculate_last_coords_recursive(item->get_sublist(),
614 icon_x, next_icon_x, next_icon_y, next_text_y, 0);
618 // Get next_icon coordinate
620 BC_ListBoxItem *item = data[master_column].get(i);
621 if( !item->autoplace_icon ) {
622 display_format = LISTBOX_ICONS;
623 // Lowest right icon coordinate.
624 current_icon_x = item->icon_x;
625 if( current_icon_x > *icon_x ) *icon_x = current_icon_x;
626 if( current_icon_x + get_item_w(item) > *next_icon_x ) {
627 *next_icon_x = current_icon_x + get_item_w(item);
630 current_icon_y = item->icon_y + get_item_h(item);
631 if( current_icon_y > *next_icon_y )
632 *next_icon_y = current_icon_y;
639 void BC_ListBox::calculate_item_coords_recursive(
640 ArrayList<BC_ListBoxItem*> *data,
641 int *icon_x, int *next_icon_x, int *next_icon_y, int *next_text_y,
644 // get maximum height of an icon
645 row_height = get_text_height(MEDIUMFONT);
646 if( temp_display_format == LISTBOX_ICON_LIST ) {
647 for( int i=0; i<data[0].size(); ++i ) {
648 if( data[0].get(i)->icon ) {
649 if( data[0].get(i)->icon->get_h() > row_height )
650 row_height = data[0].get(i)->icon->get_h();
656 // Set up items which need autoplacement.
657 // Should fill icons down and then across
658 for( int i=0; i<data[0].size(); ++i ) {
659 // Don't increase y unless the row requires autoplacing.
660 int total_autoplaced_columns = 0;
662 // Set up icons in first column
664 BC_ListBoxItem *item = data[master_column].get(i);
665 if( item->autoplace_icon ) {
666 // 1 column only if icons are used
667 display_format = LISTBOX_ICONS;
670 if( *next_icon_y + get_item_h(item) >= get_h() &&
672 *icon_x = *next_icon_x;
676 if( *icon_x + get_item_w(item) > *next_icon_x )
677 *next_icon_x = *icon_x + get_item_w(item);
680 item->set_icon_x(*icon_x);
681 item->set_icon_y(*next_icon_y);
683 *next_icon_y += get_item_h(item);
689 row_ascent = row_descent = 0;
690 // row_height still holds icon max height
691 for( int j=0; j<columns; ++j ) {
692 BC_ListBoxItem *item = data[j].get(i);
693 if( item->autoplace_text ) {
694 display_format = LISTBOX_TEXT;
695 item->set_text_x(next_text_x);
696 item->set_text_y(*next_text_y);
697 int ht = get_text_h(item);
698 if( ht > row_height ) row_height = ht;
699 int bl = get_baseline(item);
700 if( bl > row_ascent ) row_ascent = bl;
702 if( dt > row_descent ) row_ascent = bl;
704 // printf("BC_ListBox::calculate_item_coords_recursive %p %d %d %d %d %s \n",
705 // item->get_sublist(), item->get_columns(), item->get_expand(),
706 // next_text_x, *next_text_y, item->get_text());
707 // Increment position of next column
708 if( j < columns - 1 ) {
709 next_text_x += (column_width ?
711 default_column_width[j]);
713 // Set last column width based on text width
715 int new_w = get_item_w(item);
717 int *previous_w = (column_width ?
719 &default_column_width[j]);
720 if( new_w > *previous_w )
722 //printf("BC_ListBox::calculate_item_coords_recursive 1 %d\n", new_w);
724 total_autoplaced_columns++;
728 // Increase the text vertical position
729 if( total_autoplaced_columns ) {
730 display_format = LISTBOX_TEXT;
731 *next_text_y += row_height;
735 BC_ListBoxItem *item = data[master_column].values[i];
736 if( item->get_sublist() && item->get_columns() &&
737 item->get_expand() ) {
738 calculate_item_coords_recursive( item->get_sublist(),
739 icon_x, next_icon_x, next_icon_y, next_text_y, 0);
744 void BC_ListBox::set_is_suggestions(int value)
746 this->is_suggestions = value;
748 void BC_ListBox::set_scroll_repeat()
750 if( scroll_repeat ) return;
752 set_repeat(get_resources()->scroll_repeat);
755 void BC_ListBox::unset_scroll_repeat()
757 if( !scroll_repeat ) return;
759 unset_repeat(get_resources()->scroll_repeat);
762 void BC_ListBox::set_use_button(int value)
764 this->use_button = value;
767 void BC_ListBox::set_justify(int value)
769 this->justify = value;
772 void BC_ListBox::set_allow_drag_column(int value)
774 this->allow_drag_column = value;
777 void BC_ListBox::set_process_drag(int value)
779 this->process_drag = value;
782 void BC_ListBox::set_master_column(int value, int redraw)
784 this->master_column = value;
790 void BC_ListBox::set_search_column(int value)
792 this->search_column = value;
795 int BC_ListBox::get_sort_column()
800 void BC_ListBox::set_sort_column(int value, int redraw)
808 int BC_ListBox::get_sort_order()
813 void BC_ListBox::set_sort_order(int value, int redraw)
822 int BC_ListBox::get_display_mode()
824 return display_format;
827 int BC_ListBox::get_yposition()
832 int BC_ListBox::get_xposition()
837 int BC_ListBox::get_highlighted_item()
839 return highlighted_item;
843 int BC_ListBox::get_item_x(BC_ListBoxItem *item)
845 if( display_format == LISTBOX_TEXT )
846 return item->text_x - xposition + 2;
847 if( display_format == LISTBOX_ICON_LIST )
848 return item->text_x - xposition + 2;
849 return item->icon_x - xposition + 2;
852 int BC_ListBox::get_item_y(BC_ListBoxItem *item)
854 if( display_format == LISTBOX_TEXT )
855 return item->text_y - yposition + title_h + 2;
856 if( display_format == LISTBOX_ICON_LIST )
857 return item->text_y - yposition + title_h + 2;
858 return item->icon_y - yposition + title_h + 2;
861 int BC_ListBox::get_item_w(BC_ListBoxItem *item)
863 if( display_format == LISTBOX_ICONS ) {
865 get_icon_mask(item, x, y, w, h);
867 get_text_mask(item, x, y, w, h);
870 return icon_position == ICON_LEFT ? icon_w + text_w :
871 icon_w > text_w ? icon_w : text_w;
873 return get_text_w(item) + 2 * LISTBOX_MARGIN;
876 int BC_ListBox::get_item_h(BC_ListBoxItem *item)
878 if( display_format == LISTBOX_ICONS ) {
880 get_icon_mask(item, x, y, w, h);
882 get_text_mask(item, x, y, w, h);
885 return icon_position != ICON_LEFT ? icon_h + text_h :
886 icon_h > text_h ? icon_h : text_h;
888 return get_text_h(item);
892 int BC_ListBox::get_icon_w(BC_ListBoxItem *item)
894 return item->get_icon_w();
897 int BC_ListBox::get_icon_h(BC_ListBoxItem *item)
899 return item->get_icon_h();
902 int BC_ListBox::get_text_w(BC_ListBoxItem *item)
904 int w = item->get_text_w();
905 if( w < 0 ) item->set_text_w(w = get_text_width(MEDIUMFONT, item->get_text()));
909 int BC_ListBox::get_text_h(BC_ListBoxItem *item)
911 int h = item->get_text_h();
912 if( h < 0 ) item->set_text_h(h = get_text_height(MEDIUMFONT, item->get_text()));
916 int BC_ListBox::get_baseline(BC_ListBoxItem *item)
918 int b = item->get_baseline();
919 if( b < 0 ) item->set_baseline(b = get_text_ascent(MEDIUMFONT));
923 int BC_ListBox::get_items_width()
927 if( display_format == LISTBOX_ICONS ) {
928 for( int i=0; i<columns; ++i ) {
929 for( int j=0; j<data[i].total; ++j ) {
931 BC_ListBoxItem *item = data[i].values[j];
934 get_icon_mask(item, x, y, w, h);
935 if( x1 + w > widest ) widest = x1 + w;
937 if( display_format == LISTBOX_ICONS && icon_position == ICON_LEFT )
940 get_text_mask(item, x, y, w, h);
941 if( x1 + w > widest ) widest = x1 + w;
946 if( display_format == LISTBOX_TEXT ) {
947 return get_column_offset(columns);
950 return get_column_offset(columns);
955 int BC_ListBox::get_items_height(ArrayList<BC_ListBoxItem*> *data, int columns,
966 for( int j=0; j<(data?data[master_column].total:0); ++j ) {
968 BC_ListBoxItem *item = data[master_column].values[j];
970 if( display_format == LISTBOX_ICONS ||
971 display_format == LISTBOX_ICON_LIST ) {
972 get_icon_mask(item, x, y, w, h);
973 if( y + h + yposition > highest ) highest = y + h + yposition;
975 get_text_mask(item, x, y, w, h);
976 if( y + h + yposition > highest ) highest = y + h + yposition;
979 get_text_mask(item, x, y, w, h);
981 // Descend into sublist
982 if( item->get_sublist() && item->get_expand() ) {
983 get_items_height(item->get_sublist(),
990 if( display_format == LISTBOX_TEXT && top_level ) {
991 highest = LISTBOX_MARGIN + *result;
997 int BC_ListBox::set_yposition(int position, int draw_items)
999 this->yposition = position;
1001 this->draw_items(1);
1006 int BC_ListBox::set_xposition(int position)
1008 this->xposition = position;
1013 void BC_ListBox::expand_item(BC_ListBoxItem *item, int expand)
1016 item->expand = expand;
1017 // Collapse sublists if this is collapsed to make it easier to calculate
1019 if( item->get_sublist() )
1020 collapse_recursive(item->get_sublist(), master_column);
1022 // Set everything for autoplacement
1023 set_autoplacement(data, 0, 1);
1028 void BC_ListBox::collapse_recursive(ArrayList<BC_ListBoxItem*> *data,
1031 for( int i=0; i<data[master_column].total; ++i ) {
1032 BC_ListBoxItem *item = data[master_column].values[i];
1033 if( item->get_sublist() && item->expand ) {
1035 collapse_recursive(item->get_sublist(), master_column);
1040 void BC_ListBox::set_autoplacement(ArrayList<BC_ListBoxItem*> *data,
1044 for( int i=0; i<data[0].total; ++i ) {
1045 for( int j=0; j<columns; ++j ) {
1046 if( do_icons ) data[j].values[i]->autoplace_icon = 1;
1047 if( do_text ) data[j].values[i]->autoplace_text = 1;
1050 BC_ListBoxItem *item = data[master_column].values[i];
1051 if( item->get_sublist() ) {
1052 set_autoplacement(item->get_sublist(), do_icons, do_text);
1058 void BC_ListBox::set_scroll_stretch(int xv, int yv)
1060 if( xv >= 0 ) xscroll_orientation =
1061 !xv ? SCROLL_HORIZ : SCROLL_HORIZ + SCROLL_STRETCH;
1062 if( yv >= 0 ) yscroll_orientation =
1063 !yv ? SCROLL_VERT : SCROLL_VERT + SCROLL_STRETCH;
1066 int BC_ListBox::get_yscroll_x()
1069 return popup_w - get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w();
1071 return get_x() + popup_w -
1072 get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w();
1075 int BC_ListBox::get_yscroll_y()
1077 return is_popup ? 0 : get_y();
1080 int BC_ListBox::get_yscroll_height()
1082 return popup_h - (need_xscroll ?
1083 get_resources()->hscroll_data[SCROLL_HANDLE_UP]->get_h() :
1087 int BC_ListBox::get_xscroll_x()
1089 return is_popup ? 0 : get_x();
1092 int BC_ListBox::get_xscroll_y()
1094 return (is_popup ? popup_h : get_y() + popup_h) -
1095 get_resources()->hscroll_data[SCROLL_HANDLE_UP]->get_h();
1098 int BC_ListBox::get_xscroll_width()
1100 return popup_w - (need_yscroll ?
1101 get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w() : 0);
1104 int BC_ListBox::get_column_offset(int column)
1107 while( column > 0 ) {
1109 column_width[--column] :
1110 default_column_width[--column];
1115 void BC_ListBox::column_width_boundaries()
1117 if( column_width ) {
1118 for( int i=0; i<columns; ++i ) {
1119 if( column_width[i] < MIN_COLUMN_WIDTH )
1120 column_width[i] = MIN_COLUMN_WIDTH;
1124 for( int i=0; i<columns; ++i ) {
1125 if( default_column_width[i] < MIN_COLUMN_WIDTH )
1126 default_column_width[i] = MIN_COLUMN_WIDTH;
1131 int BC_ListBox::get_column_width(int column, int clamp_right)
1133 if( column < columns - 1 || !clamp_right )
1134 return column_width ? column_width[column] : default_column_width[column];
1135 return popup_w + xposition - get_column_offset(column);
1138 int BC_ListBox::get_icon_mask(BC_ListBoxItem *item,
1139 int &x, int &y, int &w, int &h)
1141 if( display_format == LISTBOX_ICONS ) {
1142 x = get_item_x(item);
1143 y = get_item_y(item);
1144 w = get_icon_w(item) + ICON_MARGIN * 2;
1145 h = get_icon_h(item) + ICON_MARGIN * 2;
1152 int BC_ListBox::get_text_mask(BC_ListBoxItem *item,
1153 int &x, int &y, int &w, int &h)
1155 x = get_item_x(item);
1156 y = get_item_y(item);
1158 if( display_format == LISTBOX_ICONS ) {
1159 if( icon_position == ICON_LEFT ) {
1160 x += get_icon_w(item) + ICON_MARGIN * 2;
1161 y += get_icon_h(item) - get_text_h(item);
1164 y += get_icon_h(item) + ICON_MARGIN;
1167 w = get_text_w(item) + ICON_MARGIN * 2;
1168 h = get_text_h(item) + ICON_MARGIN * 2;
1171 if( display_format == LISTBOX_TEXT ) {
1172 w = get_text_w(item) + LISTBOX_MARGIN * 2;
1173 h = get_text_h(item);
1176 w = get_text_width(MEDIUMFONT, item->text) + LISTBOX_MARGIN * 2;
1178 int ih = get_icon_h(item);
1179 if( h < ih ) h = ih;
1184 int BC_ListBox::get_item_highlight(ArrayList<BC_ListBoxItem*> *data,
1185 int column, int item)
1187 BC_Resources *resources = get_resources();
1188 if( data[column].values[item]->selected )
1189 return resources->listbox_selected;
1190 if( highlighted_item >= 0 &&
1191 highlighted_ptr == data[master_column].values[item] )
1192 return resources->listbox_highlighted;
1193 return resources->listbox_inactive;
1196 int BC_ListBox::get_item_color(ArrayList<BC_ListBoxItem*> *data,
1197 int column, int item)
1199 int color = data[column].values[item]->color;
1200 if( color == -1 ) color = get_resources()->listbox_text;
1201 if( get_item_highlight(data, column, item) == color )
1206 int BC_ListBox::get_from_column()
1208 return dragged_title;
1211 int BC_ListBox::get_to_column()
1213 return highlighted_title;
1217 BC_ListBoxItem* BC_ListBox::get_selection(int column, int selection_number)
1219 return get_selection_recursive(data,
1224 BC_ListBoxItem* BC_ListBox::get_selection_recursive(ArrayList<BC_ListBoxItem*> *data,
1225 int column, int selection_number)
1227 if( !data ) return 0;
1229 for( int i=0; i<data[master_column].total; ++i ) {
1230 BC_ListBoxItem *item = data[master_column].values[i];
1231 if( item->selected ) {
1232 //printf("BC_ListBox::get_selection_recursive %d\n", __LINE__);
1234 if( selection_number < 0 ) {
1236 return data[column].values[i];
1240 if( item->get_sublist() ) {
1241 BC_ListBoxItem *result = get_selection_recursive(item->get_sublist(),
1244 if( result ) return result;
1252 int BC_ListBox::get_selection_number(int column, int selection_number)
1254 return get_selection_number_recursive(data, column, selection_number);
1257 int BC_ListBox::get_selection_number_recursive(ArrayList<BC_ListBoxItem*> *data,
1258 int column, int selection_number, int *counter)
1261 if( !data ) return 0;
1262 if( !counter ) counter = &temp;
1264 for( int i=0; i<data[master_column].total; ++i ) {
1266 BC_ListBoxItem *item = data[master_column].values[i];
1267 if( item->selected ) {
1269 if( selection_number < 0 ) {
1273 if( item->get_sublist() ) {
1274 int result = get_selection_number_recursive(item->get_sublist(),
1275 column, selection_number, counter);
1276 if( result >= 0 ) return result;
1283 int BC_ListBox::set_selection_mode(int mode)
1285 this->selection_mode = mode;
1289 void BC_ListBox::delete_columns()
1291 if( column_titles ) {
1292 for( int i=0; i<columns; ++i ) {
1293 delete [] column_titles[i];
1295 delete [] column_titles;
1298 if( column_width ) delete [] column_width;
1304 // Need to copy titles so EDL can change
1305 void BC_ListBox::set_columns(const char **column_titles, int *column_width, int columns)
1307 if( (!column_titles && column_width) ||
1308 (column_titles && !column_width) ) {
1309 printf("BC_ListBox::set_columns either column_titles or column_width == NULL but not both.\n");
1314 if( column_titles ) {
1315 this->column_titles = new char*[columns];
1316 for( int i=0; i<columns; ++i ) {
1317 this->column_titles[i] = new char[strlen(column_titles[i]) + 1];
1318 strcpy(this->column_titles[i], column_titles[i]);
1321 if( column_width ) {
1322 this->column_width = new int[columns];
1323 for( int i=0; i<columns; ++i ) {
1324 this->column_width[i] = column_width[i];
1328 this->columns = columns;
1333 int BC_ListBox::update(ArrayList<BC_ListBoxItem*> *data,
1334 const char **column_titles, int *column_widths, int columns,
1335 int xposition, int yposition, int highlighted_number,
1336 int recalc_positions, int draw)
1338 set_columns(column_titles, column_widths, columns);
1340 this->yposition = yposition;
1341 this->xposition = xposition;
1342 this->highlighted_item = highlighted_number;
1343 this->highlighted_ptr = index_to_item(data, highlighted_number, 0);
1345 if( recalc_positions )
1346 set_autoplacement(data, 1, 1);
1348 init_column_width();
1352 update_scrollbars(1);
1358 void BC_ListBox::center_selection()
1360 int selection = get_selection_number(0, 0);
1361 calculate_item_coords();
1362 center_selection(selection);
1366 update_scrollbars(0);
1367 gui->show_window(1);
1371 void BC_ListBox::move_vertical(int pixels)
1375 void BC_ListBox::move_horizontal(int pixels)
1379 int BC_ListBox::select_previous(int skip, BC_ListBoxItem *selected_item, int *counter,
1380 ArrayList<BC_ListBoxItem*> *data, int *got_first, int *got_second)
1383 if( !selected_item )
1384 selected_item = get_selection(0, 0);
1395 got_second = &temp3;
1399 // Scan backwards to item pointer. Then count visible items to get
1400 // destination. No wraparound.
1402 for( int i=data[master_column].total-1; i>=0; --i ) {
1403 BC_ListBoxItem *current_item = data[master_column].values[i];
1404 if( current_item->get_sublist() &&
1405 current_item->get_expand() ) {
1406 int result = select_previous(skip, selected_item, counter,
1407 current_item->get_sublist(), got_first, got_second);
1414 if( (*counter) >= skip ) {
1415 for( int j=0; j<columns; ++j )
1416 data[j].values[i]->selected = 1;
1418 return item_to_index(this->data, current_item);
1422 if( current_item->selected ) {
1423 for( int j=0; j<columns; ++j )
1424 data[j].values[i]->selected = 0;
1431 // Hit top of top level without finding a selected item.
1433 // Select first item in top level and quit
1434 BC_ListBoxItem *current_item;
1436 current_item = data[master_column].values[0];
1438 for( int j=0; j<columns; ++j )
1439 data[j].values[0]->selected = 1;
1441 return item_to_index(this->data, current_item);
1443 } while( top_level && data[master_column].total );
1448 int BC_ListBox::select_next(int skip, BC_ListBoxItem *selected_item, int *counter,
1449 ArrayList<BC_ListBoxItem*> *data, int *got_first, int *got_second)
1452 if( !selected_item )
1453 selected_item = get_selection(0, 0);
1464 got_second = &temp3;
1468 // Scan forwards to currently selected item pointer.
1469 // Then count visible items to get destination. No wraparound.
1471 for( int i=0; i<data[master_column].total; ++i ) {
1472 BC_ListBoxItem *current_item = data[master_column].values[i];
1474 // Select next item once the number items after the currently selected item
1475 // have been passed.
1478 if( (*counter) >= skip ) {
1479 for( int j=0; j<columns; ++j )
1480 data[j].values[i]->selected = 1;
1482 return item_to_index(this->data, current_item);
1486 // Got currently selected item. Deselect it.
1487 if( current_item->selected ) {
1488 for( int j=0; j<columns; ++j )
1489 data[j].values[i]->selected = 0;
1495 // Descend into expanded level
1496 if( current_item->get_sublist() &&
1497 current_item->get_expand() ) {
1498 int result = select_next(skip, selected_item, counter,
1499 current_item->get_sublist(), got_first, got_second);
1506 // Hit bottom of top level without finding the next item.
1508 BC_ListBoxItem *current_item;
1509 // Select first item in top level and quit
1510 if( !(*got_first) ) {
1512 current_item = data[master_column].values[0];
1514 for( int j=0; j<columns; ++j )
1515 data[j].values[0]->selected = 1;
1519 // Select last item in top level and quit
1521 int current_row = data[master_column].total - 1;
1522 current_item = data[master_column].values[current_row];
1524 for( int j=0; j<columns; ++j )
1525 data[j].values[current_row]->selected = 1;
1529 return item_to_index(this->data, current_item);
1531 } while( top_level && data[master_column].total );
1537 void BC_ListBox::clamp_positions()
1539 items_w = get_items_width();
1540 if( xscroll_orientation & SCROLL_STRETCH )
1541 items_w += view_w / 4;
1542 items_h = get_items_height(data, columns);
1543 if( yscroll_orientation & SCROLL_STRETCH )
1544 items_h += view_h / 4;
1546 if( yposition < 0 ) yposition = 0;
1548 if( yposition > items_h - view_h )
1549 yposition = items_h - view_h;
1551 if( yposition < 0 ) yposition = 0;
1553 if( xposition < 0 ) xposition = 0;
1555 if( xposition >= items_w - view_w )
1556 xposition = items_w - view_w;
1558 if( xposition < 0 ) xposition = 0;
1561 int BC_ListBox::center_selection(int selection,
1562 ArrayList<BC_ListBoxItem*> *data, int *counter)
1565 if( !data ) data = this->data;
1566 if( !counter ) counter = &temp;
1568 for( int i=0; i<data[master_column].total; ++i ) {
1572 BC_ListBoxItem *item = data[master_column].values[i];
1573 if( (*counter) == selection ) {
1574 BC_ListBoxItem *top_item = this->data[master_column].values[0];
1577 if( display_format == LISTBOX_ICONS ) {
1578 // Icon is out of window
1579 if( item->icon_y-yposition > view_h-get_text_h(item) ||
1580 item->icon_y-yposition < 0 ) {
1581 yposition = item->icon_y - view_h / 2;
1584 if( data[master_column].values[selection]->icon_x - xposition > view_w ||
1585 data[master_column].values[selection]->icon_x - xposition < 0 ) {
1586 xposition = item->icon_x - view_w / 2;
1590 // Text coordinate is out of window
1591 if( item->text_y-yposition > view_h-get_text_h(item) ||
1592 item->text_y-yposition < 0 ) {
1593 yposition = item->text_y -
1602 if( item->get_sublist() ) {
1603 int result = center_selection(selection,
1604 item->get_sublist(),
1606 if( result ) return result;
1612 void BC_ListBox::update_scrollbars(int flush)
1614 int h_needed = items_h = get_items_height(data, columns);
1615 int w_needed = items_w = get_items_width();
1617 // if( columns > 0 && column_width )
1618 // printf("BC_ListBox::update_scrollbars 1 %d %d\n", column_width[columns - 1], w_needed);
1621 if( xposition != xscrollbar->get_value() )
1622 xscrollbar->update_value(xposition);
1624 if( w_needed != xscrollbar->get_length() ||
1625 view_w != xscrollbar->get_handlelength() )
1626 xscrollbar->update_length(w_needed, xposition, view_w, 0);
1630 if( yposition != yscrollbar->get_value() )
1631 yscrollbar->update_value(yposition);
1633 if( h_needed != yscrollbar->get_length() || view_h != yscrollbar->get_handlelength() )
1634 yscrollbar->update_length(h_needed, yposition, view_h, 0);
1637 if( flush ) this->flush();
1640 int BC_ListBox::get_scrollbars()
1642 int h_needed = items_h = get_items_height(data, columns);
1643 int w_needed = items_w = get_items_width();
1646 title_h = get_title_h();
1647 view_h = popup_h - title_h - 4;
1648 view_w = popup_w - 4;
1650 // Create scrollbars as needed
1651 for( int i=0; i<2; ++i ) {
1652 if( w_needed > view_w ) {
1656 get_resources()->hscroll_data[SCROLL_HANDLE_UP]->get_h() -
1663 if( h_needed > view_h ) {
1666 get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w() -
1674 // Update subwindow size
1675 int new_w = popup_w;
1676 int new_h = popup_h;
1677 if( need_xscroll ) new_h -= get_resources()->hscroll_data[SCROLL_HANDLE_UP]->get_h();
1678 if( need_yscroll ) new_w -= get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w();
1681 if( new_w != BC_WindowBase::get_w() || new_h != BC_WindowBase::get_h() )
1682 gui->resize_window(new_w, new_h);
1684 BC_WindowBase *destination = (is_popup ? gui : parent_window);
1685 if( need_xscroll ) {
1687 xscrollbar = new BC_ListBoxXScroll(this);
1688 destination->add_subwindow(xscrollbar);
1689 xscrollbar->show_window(0);
1690 xscrollbar->bound_to = this;
1693 xscrollbar->update_length(w_needed, xposition, view_w, flush);
1694 xscrollbar->reposition_window(get_xscroll_x(),
1696 get_xscroll_width());
1700 if( xscrollbar ) delete xscrollbar;
1705 if( need_yscroll ) {
1707 yscrollbar = new BC_ListBoxYScroll(this);
1708 destination->add_subwindow(yscrollbar);
1709 yscrollbar->show_window(0);
1710 yscrollbar->bound_to = this;
1713 yscrollbar->update_length(h_needed, yposition, view_h, flush);
1714 yscrollbar->reposition_window(get_yscroll_x(),
1716 get_yscroll_height());
1720 if( yscrollbar ) delete yscrollbar;
1726 view_w + 4 != bg_surface->get_w() ||
1727 view_h + 4 != bg_surface->get_h() ) {
1728 if( bg_surface ) delete bg_surface;
1729 bg_surface = new BC_Pixmap(gui, view_w + 4, view_h + 4);
1738 void BC_ListBox::set_drag_scroll(int value)
1740 allow_drag_scroll = value;
1743 // Test for scrolling by dragging
1745 int BC_ListBox::test_drag_scroll(int cursor_x, int cursor_y)
1748 if( allow_drag_scroll ||
1749 current_operation == SELECT_RECT ) {
1751 int top_boundary = get_title_h();
1753 if( cursor_y < top_boundary ||
1754 cursor_y >= view_h + title_h + LISTBOX_BORDER * 2 ||
1755 cursor_x < LISTBOX_BORDER ||
1756 cursor_x >= view_w + LISTBOX_BORDER ) {
1763 int BC_ListBox::drag_scroll_event()
1765 int top_boundary = get_title_h();
1768 if( get_cursor_y() < top_boundary ) {
1769 yposition -= top_boundary - get_cursor_y();
1773 if( get_cursor_y() >= view_h + title_h + 4 ) {
1774 yposition += get_cursor_y() - (view_h + title_h + 4);
1778 if( get_cursor_x() < 2 ) {
1779 xposition -= 2 - get_cursor_x();
1783 if( get_cursor_x() >= view_w + 2 ) {
1784 xposition += get_cursor_x() - (view_w + 2);
1794 int BC_ListBox::rectangle_scroll_event()
1796 int old_xposition = xposition;
1797 int old_yposition = yposition;
1798 int result = drag_scroll_event();
1801 rect_x1 += old_xposition - xposition;
1802 rect_y1 += old_yposition - yposition;
1803 rect_x2 = get_cursor_x();
1804 rect_y2 = get_cursor_y();
1806 int x1 = MIN(rect_x1, rect_x2);
1807 int x2 = MAX(rect_x1, rect_x2);
1808 int y1 = MIN(rect_y1, rect_y2);
1809 int y2 = MAX(rect_y1, rect_y2);
1811 if( select_rectangle(data, x1, y1, x2, y2) ) {
1812 selection_changed();
1817 update_scrollbars(1);
1822 int BC_ListBox::select_scroll_event()
1824 int result = drag_scroll_event();
1826 highlighted_item = selection_number = get_cursor_item(data,
1827 get_cursor_x(), get_cursor_y(), &highlighted_ptr);
1830 update_scrollbars(1);
1831 selection_changed();
1836 int BC_ListBox::select_rectangle(ArrayList<BC_ListBoxItem*> *data,
1837 int x1, int y1, int x2, int y2)
1840 for( int i=0; i<data[master_column].total; ++i ) {
1841 for( int j=0; j<columns; ++j ) {
1842 BC_ListBoxItem *item = data[j].values[i];
1843 if( display_format == LISTBOX_ICONS ) {
1844 int icon_x, icon_y, icon_w, icon_h;
1845 int text_x, text_y, text_w, text_h;
1846 get_icon_mask(item, icon_x, icon_y, icon_w, icon_h);
1847 get_text_mask(item, text_x, text_y, text_w, text_h);
1849 if( (x2 >= icon_x && x1 < icon_x + icon_w &&
1850 y2 >= icon_y && y1 < icon_y + icon_h) ||
1851 (x2 >= text_x && x1 < text_x + text_w &&
1852 y2 >= text_y && y1 < text_y + text_h) ) {
1853 if( !item->selected ) {
1859 if( item->selected ) {
1868 gui->get_w() - get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w() :
1869 gui->get_w()) && y2 > 0 &&
1870 y1 < gui->get_h() && y2 >= get_item_y(item) &&
1871 y1 < get_item_y(item) + get_item_h(item) ) {
1872 if( !item->selected ) {
1878 if( item->selected ) {
1886 BC_ListBoxItem *item = data[master_column].values[i];
1887 if( item->get_sublist() &&
1888 item->get_expand() )
1889 result |= select_rectangle(item->get_sublist(),
1895 int BC_ListBox::reposition_item(ArrayList<BC_ListBoxItem*> *data,
1896 int selection_number, int x, int y, int *counter)
1899 if( !counter ) counter = &temp;
1901 for( int i=0; i<data[master_column].total; ++i ) {
1902 BC_ListBoxItem *item = data[master_column].values[i];
1904 if( (*counter) == selection_number ) {
1909 // Not recursive because it's only used for icons
1914 void BC_ListBox::move_selection(ArrayList<BC_ListBoxItem*> *dst,
1915 ArrayList<BC_ListBoxItem*> *src)
1917 for( int i=0; i<src[master_column].total; ) {
1918 BC_ListBoxItem *item = src[master_column].values[i];
1921 if( item->selected ) {
1922 for( int j=0; j<columns; ++j ) {
1923 dst[j].append(src[j].values[i]);
1924 src[j].remove_number(i);
1928 // Descend into sublist
1929 if( item->get_sublist() ) {
1931 item->get_sublist());
1937 int BC_ListBox::put_selection(ArrayList<BC_ListBoxItem*> *data,
1938 ArrayList<BC_ListBoxItem*> *src, int destination, int *counter)
1941 if( !counter ) counter = &temp;
1943 if( destination < 0 || destination >= data[master_column].total ) {
1944 for( int j=0; j<columns; ++j ) {
1945 for( int i=0; i<src[j].total; ++i ) {
1946 data[j].append(src[j].values[i]);
1952 for( int i=0; i<data[master_column].total; ++i ) {
1954 if( (*counter) == destination ) {
1955 for( int j=0; j<columns; ++j ) {
1956 for( int k=0; k<src[j].total; ++k ) {
1957 data[j].insert(src[j].values[k], destination + k);
1963 BC_ListBoxItem *item = data[master_column].values[i];
1964 if( item->get_sublist() ) {
1965 if( put_selection(item->get_sublist(),
1977 int BC_ListBox::item_to_index(ArrayList<BC_ListBoxItem*> *data,
1978 BC_ListBoxItem *item, int *counter)
1981 if( !counter ) counter = &temp;
1983 for( int i=0; i<data[master_column].total; ++i ) {
1985 for( int j=0; j<columns; ++j ) {
1986 BC_ListBoxItem *new_item = data[j].values[i];
1987 //printf("BC_ListBox::item_to_index 1 %d %d %p\n", j, i, new_item);
1988 if( new_item == item ) {
1993 BC_ListBoxItem *new_item = data[master_column].values[i];
1994 if( new_item->get_sublist() ) {
1995 if( item_to_index(new_item->get_sublist(),
2005 BC_ListBoxItem* BC_ListBox::index_to_item(ArrayList<BC_ListBoxItem*> *data,
2006 int number, int column, int *counter)
2009 if( !counter ) counter = &temp;
2010 for( int i=0; i<data[master_column].total; ++i ) {
2012 if( (*counter) == number ) {
2013 return data[column].values[i];
2015 BC_ListBoxItem *item = data[master_column].values[i];
2016 if( item->get_sublist() ) {
2017 BC_ListBoxItem *result = index_to_item(item->get_sublist(),
2018 number, column, counter);
2019 if( result ) return result;
2025 int BC_ListBox::get_cursor_item(ArrayList<BC_ListBoxItem*> *data, int cursor_x, int cursor_y,
2026 BC_ListBoxItem **item_return, int *counter, int expanded)
2029 if( !data ) return -1;
2030 if( !counter ) counter = &temp;
2032 // Icons are not treed
2033 if( display_format == LISTBOX_ICONS ) {
2034 for( int j=data[master_column].total-1; j>=0; --j ) {
2035 int icon_x, icon_y, icon_w, icon_h;
2036 int text_x, text_y, text_w, text_h;
2037 BC_ListBoxItem *item = data[master_column].values[j];
2038 get_icon_mask(item, icon_x, icon_y, icon_w, icon_h);
2039 get_text_mask(item, text_x, text_y, text_w, text_h);
2041 if( (cursor_x >= icon_x && cursor_x < icon_x + icon_w &&
2042 cursor_y >= icon_y && cursor_y < icon_y + icon_h) ||
2043 (cursor_x >= text_x && cursor_x < text_x + text_w &&
2044 cursor_y >= text_y && cursor_y < text_y + text_h) ) {
2045 if( item_return ) (*item_return) = item;
2052 // Cursor is inside items rectangle
2053 if( cursor_x >= 0 &&
2054 cursor_x < (yscrollbar ?
2055 gui->get_w() - get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w() :
2057 // Only clamp y if we're not in a SELECT operation.
2058 (current_operation == BC_ListBox::SELECT ||
2059 (cursor_y > get_title_h() + LISTBOX_BORDER &&
2060 cursor_y < gui->get_h())) ) {
2061 // Search table for cursor obstruction
2062 for( int i=0; i<data[master_column].total; ++i ) {
2063 BC_ListBoxItem *item = data[master_column].values[i];
2066 // Cursor is inside item on current level
2067 if( expanded && item->selectable &&
2068 cursor_y >= get_item_y(item) &&
2069 cursor_y < get_item_y(item) + get_item_h(item) ) {
2070 //printf("BC_ListBox::get_cursor_item %d %d %p\n", master_column, i, item);
2071 if( item_return ) (*item_return) = item;
2075 // Descend into sublist
2076 if( item->get_sublist() ) {
2077 if( get_cursor_item(item->get_sublist(),
2078 cursor_x, cursor_y, item_return, counter,
2079 item->get_expand()) >= 0 )
2089 int BC_ListBox::repeat_event(int64_t duration)
2091 switch( current_operation ) {
2092 // Repeat out of bounds selection
2094 if( duration != get_resources()->scroll_repeat ) break;
2095 return rectangle_scroll_event();
2098 if( duration != get_resources()->scroll_repeat ) break;
2099 if( !drag_scroll_event() ) break;
2102 update_scrollbars(1);
2106 if( duration != get_resources()->scroll_repeat ) break;
2107 return select_scroll_event();
2111 if( duration != get_resources()->tooltip_delay ) break;
2112 if( !button_highlighted || !is_popup ) break;
2113 if( !tooltip_text || !tooltip_text[0] ) break;
2121 int BC_ListBox::cursor_enter_event()
2125 switch( current_operation ) {
2126 // Cursor moved over button, pressed, and exited.
2127 case BUTTON_DOWN_SELECT:
2128 if( top_level->event_win == win ) {
2129 current_operation = BUTTON_DN;
2131 button_highlighted = 1;
2137 // Cursor entered button
2138 if( is_popup && top_level->event_win == win ) {
2139 button_highlighted = 1;
2144 // TODO: Need to get the highlighted column title or item
2145 if( gui && top_level->event_win == gui->win ) {
2146 list_highlighted = 1;
2156 int BC_ListBox::cursor_leave_event()
2158 if( current_operation == COLUMN_DRAG ) return 0;
2161 if( button_highlighted ) {
2162 button_highlighted = 0;
2167 if( list_highlighted ) {
2168 list_highlighted = 0;
2169 highlighted_item = -1;
2170 highlighted_ptr = 0;
2171 highlighted_title = -1;
2172 int redraw_toggles = 0;
2173 for( int i=0; i<expanders.total; ++i )
2174 expanders.values[i]->cursor_leave_event(&redraw_toggles);
2182 int BC_ListBox::get_first_selection(ArrayList<BC_ListBoxItem*> *data, int *result)
2185 if( !result ) result = &temp;
2187 for( int i=0; i<data[master_column].total; ++i ) {
2188 BC_ListBoxItem *item = data[master_column].values[i];
2190 if( item->selected ) return (*result);
2191 if( item->get_sublist() ) {
2192 if( get_first_selection(item->get_sublist(), result) >= 0 )
2199 int BC_ListBox::get_total_items(ArrayList<BC_ListBoxItem*> *data,
2204 if( !result ) result = &temp;
2206 for( int i=0; i<data[master_column].total; ++i ) {
2208 if( data[master_column].values[i]->get_sublist() )
2209 get_total_items(data[master_column].values[i]->get_sublist(),
2218 int BC_ListBox::get_last_selection(ArrayList<BC_ListBoxItem*> *data,
2228 for( int i=data[master_column].total-1; i>=0; --i ) {
2229 BC_ListBoxItem *item = data[master_column].values[i];
2231 if( item->selected ) {
2233 return get_total_items(data, 0, master_column) - (*result) /* - 1 */;
2238 if( item->get_sublist() ) {
2239 if( get_last_selection(item->get_sublist(), result) >= 0 ) {
2241 return get_total_items(data, 0, master_column) - (*result) /* - 1 */;
2250 void BC_ListBox::select_range(ArrayList<BC_ListBoxItem*> *data,
2251 int start, int end, int *current)
2254 if( !current ) current = &temp;
2256 for( int i=0; i<data[master_column].total; ++i ) {
2258 if( (*current) >= start && (*current) < end ) {
2259 for( int j=0; j<columns; ++j )
2260 data[j].values[i]->selected = 1;
2262 BC_ListBoxItem *item = data[master_column].values[i];
2263 if( item->get_sublist() )
2264 select_range(item->get_sublist(), start, end, current);
2269 // Fill items between current selection and new selection
2270 int BC_ListBox::expand_selection(int button_press, int selection_number)
2272 int old_selection_start = selection_start;
2273 int old_selection_end = selection_end;
2275 // printf("BC_ListBox::expand_selection %d %d\n",
2276 // selection_center,
2277 // selection_number);
2279 // Calculate the range to select based on selection_center and selection_number
2280 if( selection_number < selection_center ) {
2281 selection_start = selection_number;
2284 selection_end = selection_number + 1;
2287 //printf("BC_ListBox::expand_selection %d %d %d %d\n", old_selection_start, old_selection_end, selection_start, selection_end);
2288 // Recurse through all the items and select the desired range
2289 select_range(data, selection_start, selection_end);
2292 return (old_selection_start != selection_start ||
2293 old_selection_end != selection_end);
2296 int BC_ListBox::toggle_item_selection(ArrayList<BC_ListBoxItem*> *data,
2297 int selection_number, int *counter)
2300 if( !counter ) counter = &temp;
2302 for( int i=0; i<data[master_column].total; ++i ) {
2303 BC_ListBoxItem *item = data[master_column].values[i];
2305 if( (*counter) == selection_number ) {
2306 // Get new value for selection
2307 int selected = !item->selected;
2309 for( int j=0; j<columns; ++j )
2310 data[j].values[i]->selected = selected;
2314 // Descend into sublist
2315 if( item->get_sublist() ) {
2316 if( toggle_item_selection(item->get_sublist(),
2317 selection_number, counter) )
2326 void BC_ListBox::set_all_selected(ArrayList<BC_ListBoxItem*> *data, int value)
2328 for( int i=0; i<data[master_column].total; ++i ) {
2329 for( int j=0; j<columns; ++j ) {
2330 BC_ListBoxItem *item = data[j].values[i];
2331 item->selected = value;
2333 BC_ListBoxItem *item = data[master_column].values[i];
2334 if( item->get_sublist() ) {
2335 set_all_selected(item->get_sublist(), value);
2340 void BC_ListBox::set_selected(ArrayList<BC_ListBoxItem*> *data,
2341 int item_number, int value, int *counter)
2344 if( !counter ) counter = &temp;
2345 for( int i=0; i<data[master_column].total&&(*counter)!=item_number; ++i ) {
2347 if( (*counter) == item_number ) {
2348 for( int j=0; j<columns; ++j ) {
2349 BC_ListBoxItem *item = data[j].values[i];
2350 item->selected = value;
2355 BC_ListBoxItem *item = data[master_column].values[i];
2356 if( item->get_sublist() ) {
2357 set_selected(item->get_sublist(), item_number, value, counter);
2362 int BC_ListBox::update_selection(ArrayList<BC_ListBoxItem*> *data,
2363 int selection_number, int *counter)
2367 if( !counter ) counter = &temp;
2369 for( int i=0; i<data[master_column].total; ++i ) {
2370 BC_ListBoxItem *item = data[master_column].values[i];
2372 if( (*counter) == selection_number && !item->selected ) {
2374 for( int j=0; j<columns; ++j )
2375 data[j].values[i]->selected = 1;
2378 if( (*counter) != selection_number && item->selected ) {
2380 for( int j=0; j<columns; ++j )
2381 data[j].values[i]->selected = 0;
2383 if( item->get_sublist() )
2384 result |= update_selection(item->get_sublist(),
2391 void BC_ListBox::promote_selections(ArrayList<BC_ListBoxItem*> *data,
2392 int old_value, int new_value)
2394 for( int i=0; i<data[master_column].total; ++i ) {
2395 for( int j=0; j<columns; ++j ) {
2396 BC_ListBoxItem *item = data[j].values[i];
2397 if( item->selected == old_value ) item->selected = new_value;
2399 BC_ListBoxItem *item = data[master_column].values[i];
2400 if( item->get_sublist() )
2401 promote_selections(item->get_sublist(), old_value, new_value);
2405 int BC_ListBox::focus_out_event()
2411 int BC_ListBox::button_press_event()
2414 BC_ListBoxItem *current_item = 0;
2416 int do_selection_change = 0;
2417 const int debug = 0;
2420 if( debug ) printf("BC_ListBox::button_press_event %d this=%p event_win=%p %p %p %p\n",
2421 __LINE__, this, (void*)top_level->event_win,
2422 (void*)(gui ? gui->win : 0), (void*)win, (void*)parent_window->win);
2424 // Pressed in button
2425 if( is_popup && top_level->event_win == win ) {
2426 if( debug ) printf("BC_ListBox::button_press_event %d\n", __LINE__);
2427 current_operation = BUTTON_DN;
2431 if( !active && !disabled ) {
2432 top_level->deactivate();
2439 // Pressed in scrollbar
2440 if( (xscrollbar && top_level->event_win == xscrollbar->win) ||
2441 (yscrollbar && top_level->event_win == yscrollbar->win) ) {
2442 if( debug ) printf("BC_ListBox::button_press_event %d\n", __LINE__);
2447 if( gui && top_level->event_win == gui->win ) {
2448 if( debug ) printf("BC_ListBox::button_press_event %d\n", __LINE__);
2450 // Activate list items
2451 // If it is a suggestion popup, it is visible without being active
2453 if( debug ) printf("BC_ListBox::button_press_event %d\n", __LINE__);
2454 if( !is_suggestions ) top_level->deactivate();
2455 if( debug ) printf("BC_ListBox::button_press_event %d\n", __LINE__);
2457 if( debug ) printf("BC_ListBox::button_press_event %d\n", __LINE__);
2460 if( current_operation != NO_OPERATION ) {
2461 switch( current_operation ) {
2464 return drag_stop_event();
2468 // Wheel mouse pressed
2469 if( get_buttonpress() == 4 ) {
2470 if( current_operation == NO_OPERATION ) {
2471 current_operation = WHEEL;
2473 set_yposition(yposition - gui->get_h() / 10, 0);
2475 update_scrollbars(0);
2476 highlighted_ptr = 0;
2477 highlighted_item = get_cursor_item(data,
2478 top_level->cursor_x, top_level->cursor_y,
2486 if( get_buttonpress() == 5 ) {
2487 if( current_operation == NO_OPERATION ) {
2488 current_operation = WHEEL;
2490 set_yposition(yposition + gui->get_h() / 10, 0);
2492 update_scrollbars(0);
2493 highlighted_ptr = 0;
2494 highlighted_item = get_cursor_item(data,
2495 top_level->cursor_x, top_level->cursor_y,
2503 // Pressed over column title division
2504 if( test_column_divisions(gui->get_cursor_x(),
2505 gui->get_cursor_y(),
2507 drag_cursor_x = gui->get_cursor_x() + xposition;
2509 drag_column_w = column_width[highlighted_division - 1];
2511 drag_column_w = default_column_width[highlighted_division - 1];
2513 current_operation = DRAG_DIVISION;
2517 // Pressed in column title
2518 if( test_column_titles(gui->get_cursor_x(), gui->get_cursor_y()) ) {
2519 current_operation = COLUMN_DN;
2520 button_highlighted = 0;
2521 list_highlighted = 1;
2526 // Pressed in expander
2527 if( test_expanders() ) {
2528 current_operation = EXPAND_DN;
2529 // Need to redraw items because of alpha
2534 // Pressed over item
2535 if( (selection_number = get_cursor_item(data,
2536 gui->get_cursor_x(), gui->get_cursor_y(),
2537 ¤t_item)) >= 0 ) {
2538 if( debug ) printf("BC_ListBox::button_press_event %d\n", __LINE__);
2540 // Get item button was pressed over
2541 selection_number2 = selection_number1;
2542 selection_number1 = selection_number;
2544 selection_start = -1;
2548 // Multiple item selection is possible
2549 if( selection_mode == LISTBOX_MULTIPLE &&
2550 (ctrl_down() || shift_down()) ) {
2551 // Expand text selection.
2552 // Fill items between selected region and current item.
2554 (display_format == LISTBOX_TEXT ||
2555 display_format == LISTBOX_ICON_LIST) ) {
2556 // Get first item selected
2557 selection_start = get_first_selection(data);
2558 // Get last item selected
2559 selection_end = get_last_selection(data);
2560 // Get center of selected region
2561 if( selection_end > selection_start ) {
2562 selection_center = (selection_end + selection_start) >> 1;
2565 selection_center = selection_number;
2567 // Deselect everything.
2568 set_all_selected(data, 0);
2569 // Select just the items
2570 expand_selection(1, selection_number);
2573 // Toggle a single item on or off
2575 toggle_item_selection(data, selection_number);
2576 new_value = current_item->selected;
2579 // Select single item
2581 if( !current_item->selected ) {
2582 set_all_selected(data, 0);
2591 current_operation = SELECT;
2592 highlighted_item = selection_number;
2593 highlighted_ptr = current_item;
2594 button_highlighted = 0;
2595 list_highlighted = 1;
2598 do_selection_change = 1;
2602 // Pressed over nothing. Start rectangle selection.
2604 if( get_buttonpress() == 1 &&
2605 selection_mode == LISTBOX_MULTIPLE ) {
2606 if( !shift_down() ) {
2607 // Deselect all and redraw if anything was selected
2608 if( get_selection_number(0, 0) >= 0 ) {
2609 set_all_selected(data, 0);
2611 do_selection_change = 1;
2616 // Promote selections to protect from a rectangle selection
2617 promote_selections(data, 1, 2);
2620 // Start rectangle selection
2621 current_operation = SELECT_RECT;
2622 rect_x1 = rect_x2 = get_cursor_x();
2623 rect_y1 = rect_y2 = get_cursor_y();
2631 // Suggestion box is not active but visible, so lie to get it to deactivate
2632 if( is_popup && (active || (is_suggestions && gui)) ) {
2634 if( debug ) printf("BC_ListBox::button_press_event %d\n", __LINE__);
2640 if( do_selection_change ) selection_changed();
2641 if( debug ) printf("BC_ListBox::button_press_event %d %d\n", __LINE__, result);
2646 int BC_ListBox::button_release_event()
2649 int cursor_x, cursor_y;
2652 unset_scroll_repeat();
2654 //printf("BC_ListBox::button_release_event 1 %d\n", current_operation);
2655 switch( current_operation ) {
2657 current_operation = NO_OPERATION;
2662 current_operation = NO_OPERATION;
2666 // Release item selection
2667 case BUTTON_DOWN_SELECT:
2669 //printf("BC_ListBox::button_release_event 10\n");
2670 current_operation = NO_OPERATION;
2672 translate_coordinates(top_level->event_win, gui->win,
2673 gui->get_cursor_x(), gui->get_cursor_y(),
2674 &cursor_x, &cursor_y);
2675 selection_number1 = selection_number =
2676 get_cursor_item(data, cursor_x, cursor_y);
2677 //printf("BC_ListBox::button_release_event %d %d\n", selection_number2, selection_number1);
2682 if( selection_number >= 0 ) {
2687 // Second button release outside button
2688 if( button_releases > 1 ) {
2693 if( top_level->get_double_click() &&
2694 selection_number2 == selection_number1 &&
2695 selection_number2 >= 0 && selection_number1 >= 0 ) {
2705 // Demote selections from rectangle selection
2706 promote_selections(data, 2, 1);
2709 // Hide rectangle overlay
2711 current_operation = NO_OPERATION;
2715 // Release popup button
2718 current_operation = NO_OPERATION;
2722 // Second button release inside button
2723 if( button_releases > 1 ) {
2730 current_operation = NO_OPERATION;
2731 // Update the sort column and the sort order for the user only if the existing
2732 // sort column is valid.
2733 if( sort_column >= 0 ) {
2734 // Invert order only if column is the same
2735 if( highlighted_title == sort_column )
2736 sort_order = sort_order == SORT_ASCENDING ?
2737 SORT_DESCENDING : SORT_ASCENDING;
2738 // Set the new sort column
2739 sort_column = highlighted_title;
2740 if( !sort_order_event() ) {
2745 // Sorting not enabled. Redraw the title state.
2753 int redraw_toggles = 0;
2754 for( int i=0; i<expanders.total&&!result; ++i ) {
2755 if( expanders.values[i]->button_release_event(&redraw_toggles) ) {
2759 // Need to redraw items because of alpha
2760 if( redraw_toggles ) draw_items(1);
2761 current_operation = NO_OPERATION;
2765 // Can't default to NO_OPERATION because it may be used in a drag event.
2770 if( do_event ) handle_event();
2772 //printf("BC_ListBox::button_release_event %d %d\n", __LINE__, get_window_lock());
2776 int BC_ListBox::get_title_h()
2778 if( display_format == LISTBOX_TEXT ||
2779 display_format == LISTBOX_ICON_LIST )
2780 return column_titles ? column_bg[0]->get_h() : 0;
2784 void BC_ListBox::reset_cursor(int new_cursor)
2787 if( gui->get_cursor() != new_cursor ) {
2788 gui->set_cursor(new_cursor, 0, 0);
2792 if( get_cursor() != new_cursor ) {
2793 set_cursor(new_cursor, 0, 0);
2797 int BC_ListBox::test_column_divisions(int cursor_x, int cursor_y, int &new_cursor)
2799 if( gui && column_titles &&
2800 cursor_y >= 0 && cursor_y < get_title_h() &&
2801 cursor_x >= 0 && cursor_x < gui->get_w() ) {
2802 for( int i=1; i<columns; ++i ) {
2803 if( cursor_x >= -xposition + get_column_offset(i) - 5 &&
2804 cursor_x < -xposition + get_column_offset(i) +
2805 get_resources()->listbox_title_hotspot ) {
2806 highlighted_item = -1;
2807 highlighted_ptr = 0;
2808 highlighted_division = i;
2809 highlighted_title = -1;
2810 list_highlighted = 1;
2811 new_cursor = HSEPARATE_CURSOR;
2816 highlighted_division = -1;
2820 int BC_ListBox::test_column_titles(int cursor_x, int cursor_y)
2822 if( gui && column_titles &&
2823 cursor_y >= 0 && cursor_y < get_title_h() &&
2824 cursor_x >= 0 && cursor_x < gui->get_w() ) {
2825 for( int i=0; i<columns; ++i ) {
2826 if( cursor_x >= -xposition + get_column_offset(i) &&
2827 (cursor_x < -xposition + get_column_offset(i + 1) ||
2828 i == columns - 1) ) {
2829 highlighted_item = -1;
2830 highlighted_ptr = 0;
2831 highlighted_division = -1;
2832 highlighted_title = i;
2833 list_highlighted = 1;
2838 highlighted_title = -1;
2842 int BC_ListBox::test_expanders()
2844 for( int i=0; i<expanders.total; ++i ) {
2845 if( expanders.values[i]->button_press_event() ) {
2846 current_operation = EXPAND_DN;
2854 int BC_ListBox::cursor_motion_event()
2856 int redraw = 0, result = 0;
2857 int new_cursor = ARROW_CURSOR;
2859 selection_number = -1;
2862 switch( current_operation ) {
2864 // Button pressed and slid off button
2865 if( !cursor_inside() ) {
2866 current_operation = BUTTON_DOWN_SELECT;
2872 case DRAG_DIVISION: {
2873 // int new_w = get_cursor_x() +
2875 // get_column_offset(highlighted_division - 1);
2876 int difference = get_cursor_x() + xposition - drag_cursor_x;
2877 int new_w = drag_column_w + difference;
2879 new_cursor = HSEPARATE_CURSOR;
2881 if( column_width ) {
2882 column_width[highlighted_division - 1] = new_w;
2885 default_column_width[highlighted_division - 1] = new_w;
2888 column_width_boundaries();
2890 // Force update of coords
2891 set_autoplacement(data, 0, 1);
2892 column_resize_event();
2896 update_scrollbars(1);
2901 if( test_drag_scroll(get_cursor_x(), get_cursor_y()) )
2902 set_scroll_repeat();
2904 int old_x1 = MIN(rect_x1, rect_x2);
2905 int old_x2 = MAX(rect_x1, rect_x2);
2906 int old_y1 = MIN(rect_y1, rect_y2);
2907 int old_y2 = MAX(rect_y1, rect_y2);
2909 int new_rect_x2 = get_cursor_x();
2910 int new_rect_y2 = get_cursor_y();
2912 int x1 = MIN(rect_x1, new_rect_x2);
2913 int x2 = MAX(rect_x1, new_rect_x2);
2914 int y1 = MIN(rect_y1, new_rect_y2);
2915 int y2 = MAX(rect_y1, new_rect_y2);
2917 // Adjust rectangle coverage
2918 if( old_x1 != x1 || old_x2 != x2 ||
2919 old_y1 != y1 || old_y2 != y2 ) {
2921 redraw = select_rectangle(data, x1, y1, x2, y2);
2926 //printf("BC_ListBox::cursor_motion_event %d\n", __LINE__);
2931 rect_x2 = get_cursor_x();
2932 rect_y2 = get_cursor_y();
2936 update_scrollbars(1);
2937 selection_changed();
2938 //printf("BC_ListBox::cursor_motion_event %d\n", __LINE__);
2941 if( old_x1 != x1 || old_x2 != x2 ||
2942 old_y1 != y1 || old_y2 != y2 ) {
2943 //printf("BC_ListBox::cursor_motion_event %d\n", __LINE__);
2951 int old_highlighted_item = highlighted_item;
2953 if( test_drag_scroll(get_cursor_x(), get_cursor_y()) )
2954 set_scroll_repeat();
2956 highlighted_item = selection_number = get_cursor_item(data,
2957 get_cursor_x(), get_cursor_y(), &highlighted_ptr);
2960 // Deselect all items and select just the one we're over
2961 if( selection_number >= 0 && !allow_drag &&
2962 ((!shift_down() && !ctrl_down()) ||
2963 selection_mode == LISTBOX_SINGLE) ) {
2964 redraw = update_selection(data, selection_number);
2967 // Expand multiple selection
2968 if( selection_mode == LISTBOX_MULTIPLE &&
2969 (shift_down() || ctrl_down()) ) {
2970 // Expand selected region in text mode centered around initial range
2971 if( (display_format == LISTBOX_TEXT ||
2972 display_format == LISTBOX_ICON_LIST) &&
2974 // Deselect everything.
2975 set_all_selected(data, 0);
2977 // Select just the items
2978 redraw = expand_selection(0, selection_number);
2980 // Set the one item we're over to the selection value determined in
2981 // button_press_event.
2983 set_selected(data, selection_number, new_value);
2987 if( highlighted_item != old_highlighted_item ) {
2990 update_scrollbars(1);
2991 //printf("BC_ListBox::cursor_motion_event %d %d\n", highlighted_item, old_highlighted_item);
2992 selection_changed();
2996 case BUTTON_DOWN_SELECT:
2997 // Went back into button area
2998 if( cursor_inside() ) {
2999 current_operation = BUTTON_DN;
3004 // Went into item area
3006 int cursor_x = 0, cursor_y = 0;
3007 translate_coordinates(top_level->event_win, gui->win,
3008 top_level->cursor_x, top_level->cursor_y,
3009 &cursor_x, &cursor_y);
3010 int old_highlighted_item = highlighted_item;
3011 highlighted_item = selection_number = get_cursor_item(data,
3016 if( highlighted_item != old_highlighted_item ) {
3017 update_selection(data, selection_number);
3019 selection_changed();
3025 int redraw_toggles = 0;
3026 for( int i=0; i<expanders.total&&!result; ++i ) {
3027 result = expanders.values[i]->cursor_motion_event(
3030 if( redraw_toggles ) {
3031 // Need to redraw items because of the alpha
3036 case NO_OPERATION: {
3037 int cursor_x = get_cursor_x(), cursor_y = get_cursor_y();
3038 if( gui && top_level->event_win == gui->win ) {
3039 int old_highlighted_title = highlighted_title;
3040 int old_list_highlighted = list_highlighted;
3041 int old_highlighted_item = highlighted_item;
3042 int redraw_titles = 0;
3043 int redraw_border = 0;
3044 int redraw_items = 0;
3045 int redraw_toggles = 0;
3048 // Test if cursor moved over a title division
3049 test_column_divisions(cursor_x, cursor_y, new_cursor);
3051 // Test if cursor moved over a title
3052 if( highlighted_division < 0 ) {
3053 test_column_titles(cursor_x, cursor_y);
3056 // Test if cursor moved over expander
3057 if( highlighted_division < 0 && highlighted_title < 0 &&
3058 (display_format == LISTBOX_TEXT ||
3059 display_format == LISTBOX_ICON_LIST) ) {
3060 for( int i=0; i<expanders.total; ++i ) {
3061 expanders.values[i]->cursor_motion_event(
3064 //printf("BC_ListBox::cursor_motion_event %d\n", redraw_toggles);
3067 // Test if cursor moved over an item
3068 if( highlighted_division < 0 && highlighted_title < 0 ) {
3069 highlighted_item = get_cursor_item(data,
3076 // Clear title highlighting if moved over division
3077 if( old_highlighted_title != highlighted_title ) {
3081 // Highlight list border
3082 if( old_list_highlighted != list_highlighted ) {
3086 // Moved out of item area
3087 if( old_highlighted_item != highlighted_item ) {
3091 //printf("BC_ListBox::cursor_motion_event 1 %d\n", highlighted_item);
3093 // Change cursor to title division adjustment
3094 reset_cursor(new_cursor);
3096 if( redraw_items ) {
3104 if( redraw_toggles )
3108 if( redraw_items || redraw_titles ||
3109 redraw_border || redraw_toggles ) {
3116 if( !result && list_highlighted ) {
3117 list_highlighted = 0;
3118 highlighted_item = -1;
3119 highlighted_ptr = 0;
3120 highlighted_title = -1;
3121 highlighted_division = -1;
3131 int BC_ListBox::drag_start_event()
3133 switch( current_operation ) {
3135 if( gui && gui->is_event_win() && allow_drag ) {
3136 BC_ListBoxItem *item_return = 0;
3137 selection_number = get_cursor_item(data,
3138 top_level->cursor_x,
3139 top_level->cursor_y,
3142 if( selection_number >= 0 ) {
3144 get_abs_cursor(cx, cy);
3145 if( item_return->icon_vframe ) {
3146 drag_popup = new BC_DragWindow(this,
3147 item_return->icon_vframe, cx, cy);
3150 if( item_return->icon ) {
3151 drag_popup = new BC_DragWindow(this,
3152 item_return->icon, cx, cy);
3155 drag_popup = new BC_DragWindow(this,
3156 drag_icon_vframe, cx, cy);
3158 current_operation = DRAG_ITEM;
3159 // require shift down for scrolling
3160 if( allow_drag < 0 && shift_down() )
3161 set_scroll_repeat();
3168 if( gui && gui->is_event_win() && allow_drag_column ) {
3170 get_abs_cursor(cx, cy);
3171 cx -= drag_column_icon_vframe->get_w() / 2,
3172 cy -= drag_column_icon_vframe->get_h() / 2;
3173 drag_popup = new BC_DragWindow(this,
3174 drag_column_icon_vframe, cx, cy);
3175 dragged_title = highlighted_title;
3176 current_operation = COLUMN_DRAG;
3186 int BC_ListBox::drag_motion_event()
3188 //printf("BC_ListBox::drag_motion_event 1 %d\n", current_operation);
3189 switch( current_operation ) {
3192 int new_highlighted_item = -1;
3193 BC_ListBoxItem *new_highlighted_ptr = 0;
3194 new_highlighted_item = get_cursor_item(data,
3195 top_level->cursor_x, top_level->cursor_y,
3196 &new_highlighted_ptr);
3198 if( new_highlighted_item != highlighted_item ) {
3202 // Always update highlighted value for drag_stop
3203 highlighted_item = new_highlighted_item;
3204 highlighted_ptr = new_highlighted_ptr;
3205 //printf("BC_ListBox::drag_motion_event 1 %p\n", highlighted_ptr);
3209 update_scrollbars(1);
3212 return drag_popup->cursor_motion_event(); }
3215 int old_highlighted_title = highlighted_title;
3216 test_column_titles(get_cursor_x(), get_cursor_y());
3217 if( old_highlighted_title != highlighted_title ) {
3220 return drag_popup->cursor_motion_event(); }
3226 int BC_ListBox::drag_stop_event()
3229 unset_scroll_repeat();
3230 switch( current_operation ) {
3232 // Inside window boundary
3233 if( top_level->cursor_x > 0 &&
3234 top_level->cursor_x < gui->get_w() - drag_popup->get_w() / 2 &&
3235 top_level->cursor_y > 0 &&
3236 top_level->cursor_y < gui->get_h() - drag_popup->get_h() / 2 ) {
3240 if( display_format == LISTBOX_ICONS ) {
3241 reposition_item(data,
3243 top_level->cursor_x - drag_popup->get_w() / 2 -
3244 LISTBOX_MARGIN - 2 + xposition,
3245 top_level->cursor_y - drag_popup->get_h() / 2 -
3246 LISTBOX_MARGIN - 2 + yposition);
3250 if( process_drag ) {
3251 // Move selected items from data to temporary
3252 ArrayList<BC_ListBoxItem*> *src_items =
3253 new ArrayList<BC_ListBoxItem*>[columns];
3254 move_selection(src_items, data);
3256 int destination = highlighted_item = item_to_index(data,
3258 // Insert items from temporary to data
3259 put_selection(data, src_items, destination);
3261 delete [] src_items;
3262 set_autoplacement(data, 0, 1);
3268 drag_popup->drag_failure_event();
3273 if( dragged_title != highlighted_title ) {
3274 if( highlighted_title >= 0 ) {
3275 if( !move_column_event() ) draw_titles(1);
3278 drag_popup->drag_failure_event();
3284 current_operation = NO_OPERATION;
3294 BC_DragWindow* BC_ListBox::get_drag_popup()
3299 int BC_ListBox::translation_event()
3301 if( is_popup && gui ) {
3302 int new_x = gui->get_x() +
3303 (top_level->last_translate_x - top_level->prev_x -
3304 BC_DisplayInfo::get_left_border());
3305 int new_y = gui->get_y() +
3306 (top_level->last_translate_y - top_level->prev_y -
3307 BC_DisplayInfo::get_top_border());
3309 gui->reposition_window(new_x, new_y);
3315 int BC_ListBox::reposition_window(int x, int y, int w, int h, int flush)
3319 if( h != -1 ) popup_h = h;
3320 //printf("BC_ListBox::reposition_window %d %d\n", popup_w, popup_h);
3324 xscrollbar->reposition_window(get_xscroll_x(),
3326 get_xscroll_width());
3328 yscrollbar->reposition_window(get_yscroll_x(),
3330 get_yscroll_height());
3334 BC_WindowBase::reposition_window(x, y, w, h);
3340 int BC_ListBox::deactivate()
3343 // printf("BC_ListBox::deactivate %d this=%p gui=%p active=%d\n",
3344 // __LINE__, this, gui, active);
3347 //printf("BC_ListBox::deactivate %d this=%p gui=%p\n", __LINE__, this, gui);
3349 delete gui; gui = 0;
3354 highlighted_item = -1;
3355 highlighted_ptr = 0;
3358 //printf("BC_ListBox::deactivate %d this=%p\n", __LINE__, this);
3360 top_level->active_subwindow = 0;
3366 int BC_ListBox::activate(int take_focus)
3368 if( active ) return 0;
3371 set_active_subwindow(this);
3372 button_releases = 0;
3373 if( !is_popup || gui ) return 0;
3374 int wx = get_x(), wy = get_y() + get_h();
3375 if( justify == LISTBOX_RIGHT ) wx += get_w() - popup_w;
3378 XTranslateCoordinates(top_level->display,
3379 parent_window->win, top_level->rootwin,
3380 wx, wy, &abs_x, &abs_y, &xwin);
3383 return activate(abs_x, abs_y);
3386 int BC_ListBox::activate(int x, int y, int w, int h)
3388 if( !is_popup || gui ) return 0;
3390 if( w != -1 ) popup_w = w;
3391 if( h != -1 ) popup_h = h;
3393 if( y + popup_h > top_level->get_root_h(0) )
3394 y -= get_h() + popup_h;
3395 add_subwindow(gui = new BC_Popup(this,
3396 x, y, popup_w, popup_h, -1, 0, 0));
3398 gui->show_window(1);
3402 int BC_ListBox::is_active()
3407 int BC_ListBox::keypress_event()
3409 if( !active ) return 0;
3411 //printf("BC_ListBox::keypress_event %d\n", __LINE__);
3413 int result = 0, redraw = 0;
3414 int view_items = last_in_view - first_in_view + 1;
3415 if( view_items <= 0 ) view_items = view_h / get_text_height(MEDIUMFONT);
3416 int new_item = -1, new_selection = -1;
3418 switch( top_level->get_keypress() ) {
3421 top_level->deactivate();
3423 // If user is manipulating popup with keyboard, don't pass on event.
3425 //printf("BC_ListBox::keypress_event %d %p\n", __LINE__, get_selection(0, 0));
3426 if( top_level->get_keypress() == RETURN )
3428 //printf("BC_ListBox::keypress_event %d %p\n", __LINE__, get_selection(0, 0));
3436 new_selection = new_item = select_previous(0);
3438 //printf("BC_ListBox::keypress_event 1 %d\n", new_item);
3439 if( new_item >= 0 ) {
3440 center_selection(new_item);
3448 new_selection = new_item = select_next(0);
3450 if( new_item >= 0 ) {
3451 center_selection(new_item);
3459 new_selection = new_item = select_previous(view_items - 1);
3461 if( new_item >= 0 ) {
3462 center_selection(new_item);
3470 new_selection = new_item = select_next(view_items - 1);
3472 if( new_item >= 0 ) {
3473 center_selection(new_item);
3499 if( !ctrl_down() ) {
3500 int query_len = strlen(query);
3501 if( query_len < (int)sizeof(query)-1 &&
3502 top_level->get_keypress() > 30 &&
3503 top_level->get_keypress() < 127 ) {
3504 query[query_len++] = top_level->get_keypress();
3505 query[query_len] = 0;
3506 new_selection = query_list();
3509 if( top_level->get_keypress() == BACKSPACE ) {
3510 if( query_len > 0 ) query[--query_len] = 0;
3511 new_selection = query_list();
3515 show_tooltip(query);
3528 update_scrollbars(1);
3531 //printf("BC_ListBox::keypress_event %d new_selection=%d\n", __LINE__, new_selection);
3532 if( new_selection >= 0 && !is_suggestions ) {
3533 //printf("BC_ListBox::keypress_event %d\n", __LINE__);
3534 selection_changed();
3535 //printf("BC_ListBox::keypress_event %d\n", __LINE__);
3542 BC_Pixmap* BC_ListBox::get_bg_surface()
3548 void BC_ListBox::draw_background()
3550 if( !bg_draw ) return;
3552 // White background pixmap
3553 set_color(top_level->get_resources()->listbox_inactive);
3554 draw_box(0, 0, bg_surface->get_w(), bg_surface->get_h(), bg_surface);
3556 // Optional heroine pixmap
3558 bg_surface->draw_pixmap(bg_pixmap,
3559 bg_surface->get_w() - top_level->get_resources()->listbox_bg->get_w(),
3563 void BC_ListBox::clear_listbox(int x, int y, int w, int h)
3565 gui->draw_pixmap(bg_surface, x, y, w, h, x, y - title_h);
3568 void BC_ListBox::update_format(int display_format, int redraw)
3570 this->display_format = display_format;
3571 xposition = 0; yposition = 0;
3572 if( redraw && gui ) draw_items(1, 1);
3575 int BC_ListBox::get_format()
3577 return display_format;
3582 int BC_ListBox::draw_items(int flush, int draw_bg)
3585 BC_Resources *resources = get_resources();
3587 //dump(data, columns);
3589 // Calculate items width
3590 calculate_item_coords();
3591 // Create and destroy scrollbars as needed
3594 if( bg_draw ) this->bg_draw = 1;
3600 if( display_format == LISTBOX_ICONS ) {
3601 clear_listbox(2, 2 + title_h, view_w, view_h);
3603 set_font(MEDIUMFONT);
3604 for( int i=0; i<data[master_column].size(); ++i ) {
3605 BC_ListBoxItem *item = data[master_column].get(i);
3606 if( get_item_x(item) >= -get_item_w(item) &&
3607 get_item_x(item) < view_w &&
3608 get_item_y(item) >= -get_item_h(item) + title_h &&
3609 get_item_y(item) < view_h + title_h ) {
3610 item->set_in_view(1);
3611 if( first_in_view < 0 ) first_in_view = i;
3613 int item_color = get_item_highlight(data, 0, i);
3614 int icon_x, icon_y, icon_w, icon_h;
3615 int text_x, text_y, text_w, text_h;
3618 get_icon_mask(item, icon_x, icon_y, icon_w, icon_h);
3619 get_text_mask(item, text_x, text_y, text_w, text_h);
3621 if( item_color != resources->listbox_inactive ) {
3622 gui->set_color(BLACK);
3623 gui->draw_rectangle(icon_x, icon_y, icon_w, icon_h);
3624 gui->set_color(item_color);
3625 gui->draw_box(icon_x + 1, icon_y + 1, icon_w - 2, icon_h - 2);
3626 gui->set_color(BLACK);
3627 gui->draw_rectangle(text_x, text_y, text_w, text_h);
3628 gui->set_color(item_color);
3629 gui->draw_box(text_x + 1, text_y + 1, text_w - 2, text_h - 2);
3631 if( icon_position == ICON_LEFT )
3632 gui->draw_box(text_x - 1, text_y + 1, 2, text_h - 2);
3634 if( icon_position == ICON_TOP )
3635 gui->draw_line(text_x + 1, text_y, text_x + icon_w - 2, text_y);
3636 if( text_x + text_w < icon_x + icon_w ) {
3637 gui->set_color(BLACK);
3638 gui->draw_line(text_x + text_w, icon_y + icon_h,
3639 icon_x + icon_w, icon_y + icon_h);
3643 gui->set_color(get_item_color(data, 0, i));
3645 gui->pixmap->draw_pixmap(item->icon,
3646 icon_x + ICON_MARGIN, icon_y + ICON_MARGIN);
3648 gui->draw_text(text_x + ICON_MARGIN,
3649 text_y + ICON_MARGIN + get_baseline(item), item->text);
3652 item->set_in_view(0);
3657 // Draw one column at a time so text overruns don't go into the next column
3658 // clear column backgrounds
3659 int current_toggle = 0;
3660 for( int j=0; j<columns; ++j ) {
3661 clear_listbox(LISTBOX_BORDER + get_column_offset(j) - xposition,
3662 LISTBOX_BORDER + title_h,
3663 get_column_width(j, 1),
3665 // Draw rows in the column recursively
3666 draw_text_recursive(data, j, 0, ¤t_toggle);
3669 // Delete excess expanders
3670 while( expanders.total > current_toggle ) {
3671 expanders.remove_object();
3675 // draw user images if available
3677 // Draw titles on top of rows for superposition effect
3680 // Clear garbage from bottom right corner
3681 if( xscrollbar && yscrollbar && is_popup ) {
3682 gui->draw_top_background(parent_window,
3683 popup_w - get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w(),
3684 popup_h - get_resources()->hscroll_data[SCROLL_HANDLE_UP]->get_h(),
3685 get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w(),
3686 get_resources()->hscroll_data[SCROLL_HANDLE_UP]->get_h());
3693 if( current_operation == SELECT_RECT )
3703 void BC_ListBox::draw_text_recursive(ArrayList<BC_ListBoxItem*> *data,
3704 int column, int indent, int *current_toggle)
3707 BC_Resources *resources = get_resources();
3709 set_font(MEDIUMFONT);
3712 // Search for a branch and make room for toggle if there is one
3714 for( int i=0; i<data[column].size(); ++i ) {
3715 if( data[column].get(i)->get_sublist() ) {
3716 subindent = BC_WindowBase::get_resources()->listbox_expand[0]->get_w();
3722 row_height = row_ascent = row_descent = 0;
3723 for( int i=0; i<data[column].total; ++i ) {
3724 BC_ListBoxItem *item = data[column].values[i];
3725 int ht = get_text_h(item);
3726 if( ht > row_height ) row_height = ht;
3727 int bl = get_baseline(item);
3728 if( bl > row_ascent ) row_ascent = bl;
3730 if( dt > row_descent ) row_ascent = bl;
3733 for( int i=0; i<data[column].size(); ++i ) {
3735 BC_ListBoxItem *item = data[column].values[i];
3736 BC_ListBoxItem *first_item = data[master_column].values[i];
3738 if( get_item_y(item) >= -get_item_h(item) + title_h &&
3739 get_item_y(item) < view_h + title_h ) {
3740 int row_color = get_item_highlight(data, 0, i);
3741 int x, y, w, h, column_width;
3743 get_text_mask(item, x, y, w, h);
3744 column_width = get_column_width(column, 1);
3745 if( x + column_width > view_w + LISTBOX_BORDER * 2 )
3746 column_width = view_w + LISTBOX_BORDER * 2 - x;
3748 if( row_color != resources->listbox_inactive ) {
3749 gui->set_color(row_color);
3750 gui->draw_box(x, y, column_width, h);
3751 gui->set_color(BLACK);
3752 int xx = x + column_width-1;
3753 gui->draw_line(x, y, xx, y);
3755 if( display_format == LISTBOX_ICON_LIST ) {
3756 int ih = get_icon_h(item);
3757 if( ih > hh ) hh = ih;
3760 gui->draw_line(x, yy, xx, yy);
3763 gui->set_color(get_item_color(data, column, i));
3766 if( column == 0 && display_format == LISTBOX_ICON_LIST ) {
3768 gui->pixmap->draw_pixmap(item->icon, x, y);
3769 x += item->icon->get_w() + ICON_MARGIN;
3774 // Indent only applies to first column
3776 x + LISTBOX_BORDER + LISTBOX_MARGIN +
3777 (column == 0 ? indent + subindent : 0),
3778 y + get_baseline(item), item->text);
3779 item->set_in_view(1);
3781 if( first_in_view < 0 ) first_in_view = i;
3786 if( column == 0 && item->get_sublist() && item->get_columns() ) {
3787 // Create new expander
3788 if( *current_toggle >= expanders.total ) {
3789 BC_ListBoxToggle *toggle =
3790 new BC_ListBoxToggle(this, item,
3791 x + LISTBOX_BORDER + LISTBOX_MARGIN + indent, y);
3793 expanders.append(toggle);
3795 // Reposition existing expander
3797 BC_ListBoxToggle *toggle = expanders.values[*current_toggle];
3798 //printf("BC_ListBox::draw_text_recursive 1 %d\n", *current_toggle);
3799 toggle->update(item,
3800 x + LISTBOX_BORDER + LISTBOX_MARGIN + indent, y, 0);
3802 (*current_toggle)++;
3809 item->set_in_view(0);
3811 // Descend into sublist
3812 if( first_item->get_expand() ) {
3813 draw_text_recursive(first_item->get_sublist(),
3815 indent + LISTBOX_INDENT,
3821 int BC_ListBox::draw_border(int flash)
3823 BC_Resources *resources = top_level->get_resources();
3824 gui->draw_3d_border(0, 0,
3825 view_w + LISTBOX_BORDER * 2, view_h + title_h + LISTBOX_BORDER * 2,
3826 resources->listbox_border1,
3828 resources->listbox_border2_hi : resources->listbox_border2,
3830 resources->listbox_border3_hi : resources->listbox_border3,
3831 resources->listbox_border4);
3840 void BC_ListBox::draw_title(int number)
3842 // Column title background
3843 int image_number = 0;
3844 if( number == highlighted_title ) {
3846 if( current_operation == COLUMN_DN )
3850 gui->draw_3segmenth(get_column_offset(number) - xposition + LISTBOX_BORDER,
3852 get_column_width(number, 1) + get_resources()->listbox_title_overlap,
3853 column_bg[image_number]);
3855 int title_x = -xposition + get_column_offset(number) +
3856 LISTBOX_MARGIN + LISTBOX_BORDER;
3857 title_x += get_resources()->listbox_title_margin;
3859 gui->set_color(get_resources()->listbox_title_color);
3860 gui->draw_text(title_x,
3861 LISTBOX_MARGIN + LISTBOX_BORDER + get_text_ascent(MEDIUMFONT),
3862 column_titles[number]);
3864 // Column sort order
3865 if( number == sort_column ) {
3866 BC_Pixmap *src = sort_order == SORT_ASCENDING ?
3867 column_sort_dn : column_sort_up;
3869 // int column_offset = get_column_offset(number) - xposition + LISTBOX_BORDER;
3870 // int column_width = get_column_width(number, 1);
3871 // int toggle_x = column_offset + column_width - LISTBOX_BORDER;
3872 // if( toggle_x > items_w ) toggle_x = items_w;
3873 // toggle_x -= 5 + src->get_w();
3876 get_text_width(MEDIUMFONT, column_titles[number]) +
3879 gui->draw_pixmap(src,
3881 title_h / 2 - src->get_h() / 2 + LISTBOX_BORDER);
3885 int BC_ListBox::draw_titles(int flash)
3887 if( column_titles &&
3888 (display_format == LISTBOX_TEXT ||
3889 display_format == LISTBOX_ICON_LIST) ) {
3890 //printf("BC_ListBox::draw_titles 1 %d\n", highlighted_title);
3891 for( int i=0; i<columns; ++i ) {
3892 if( i != highlighted_title )
3896 if( highlighted_title >= 0 ) draw_title(highlighted_title);
3906 void BC_ListBox::draw_toggles(int flash)
3908 for( int i=0; i<expanders.total; ++i )
3909 expanders.values[i]->draw(0);
3911 //printf("BC_ListBox::draw_toggles 1 %d\n", flash);
3912 if( flash && expanders.total ) {
3917 int BC_ListBox::draw_rectangle(int flash)
3919 int x1 = MIN(rect_x1, rect_x2);
3920 int x2 = MAX(rect_x1, rect_x2);
3921 int y1 = MIN(rect_y1, rect_y2);
3922 int y2 = MAX(rect_y1, rect_y2);
3924 if( x1 == x2 || y1 == y2 ) return 0;
3927 gui->set_color(WHITE);
3928 gui->draw_rectangle(x1, y1, x2 - x1, y2 - y1);
3938 void BC_ListBox::dump(ArrayList<BC_ListBoxItem*> *data, int columns,
3939 int indent, int master_column)
3942 printf("BC_ListBox::dump 1\n");
3945 for( int i=0; i<data[master_column].total; ++i ) {
3946 for( int k=0; k<indent; ++k )
3948 for( int j=0; j<columns; ++j ) {
3949 BC_ListBoxItem *item = data[j].values[i];
3950 printf("%d,%d,%d=%s ",
3951 item->get_text_x(), item->get_text_y(),
3952 item->autoplace_text, item->get_text());
3956 if( data[master_column].values[i]->get_sublist() ) {
3957 dump(data[master_column].values[i]->get_sublist(),
3958 data[master_column].values[i]->get_columns(),