/* * CINELERRA * Copyright (C) 2010-2014 Adam Williams * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "bcdisplayinfo.h" #include "bcdragwindow.h" #include "bclistbox.h" #include "bclistboxitem.h" #include "bcpixmap.h" #include "bcresources.h" #include "bcsignals.h" #include "clip.h" #include "cursors.h" #include "fonts.h" #include "keys.h" #include "language.h" #include "bctimer.h" #include "vframe.h" #include #include // ====================================================== scrollbars BC_ListBoxYScroll::BC_ListBoxYScroll(BC_ListBox *listbox) : BC_ScrollBar(listbox->get_yscroll_x(), listbox->get_yscroll_y(), listbox->yscroll_orientation, listbox->get_yscroll_height(), listbox->items_h, listbox->yposition, listbox->view_h) { this->listbox = listbox; } BC_ListBoxYScroll::~BC_ListBoxYScroll() { } int BC_ListBoxYScroll::handle_event() { listbox->set_yposition(get_value()); return 1; } BC_ListBoxXScroll::BC_ListBoxXScroll(BC_ListBox *listbox) : BC_ScrollBar(listbox->get_xscroll_x(), listbox->get_xscroll_y(), listbox->xscroll_orientation, listbox->get_xscroll_width(), listbox->items_w, listbox->xposition, listbox->view_w) { this->listbox = listbox; } BC_ListBoxXScroll::~BC_ListBoxXScroll() { } int BC_ListBoxXScroll::handle_event() { listbox->set_xposition(get_value()); return 1; } BC_ListBoxToggle::BC_ListBoxToggle(BC_ListBox *listbox, BC_ListBoxItem *item, int x, int y) { this->listbox = listbox; this->item = item; this->x = x; this->y = y; this->value = item->get_expand(); if( this->value ) state = BC_Toggle::TOGGLE_CHECKED; else state = BC_Toggle::TOGGLE_UP; } void BC_ListBoxToggle::update(BC_ListBoxItem *item, int x, int y, int flash) { this->value = item->get_expand(); this->item = item; this->x = x; this->y = y; // update state switch( state ) { case TOGGLE_UP: if( value ) state = TOGGLE_CHECKED; break; case TOGGLE_UPHI: if( value ) state = TOGGLE_CHECKEDHI; break; case TOGGLE_CHECKED: if( !value ) state = TOGGLE_UP; break; case TOGGLE_DOWN: break; case TOGGLE_CHECKEDHI: if( !value ) state = TOGGLE_UPHI; break; case TOGGLE_DOWN_EXIT: break; } draw(flash); } int BC_ListBoxToggle::cursor_motion_event(int *redraw_toggles) { int w = listbox->toggle_images[0]->get_w(); int h = listbox->toggle_images[0]->get_h(); int cursor_inside = listbox->get_cursor_x() >= x && listbox->get_cursor_x() < x + w && listbox->get_cursor_y() >= y && listbox->get_cursor_y() < y + h; int result = 0; switch( state ) { case BC_ListBoxToggle::TOGGLE_UPHI: if( !cursor_inside ) { state = BC_ListBoxToggle::TOGGLE_UP; *redraw_toggles = 1; } break; case BC_ListBoxToggle::TOGGLE_CHECKEDHI: if( !cursor_inside ) { state = BC_ListBoxToggle::TOGGLE_CHECKED; *redraw_toggles = 1; } break; case BC_ListBoxToggle::TOGGLE_DOWN: if( !cursor_inside ) { state = BC_ListBoxToggle::TOGGLE_DOWN_EXIT; *redraw_toggles = 1; } result = 1; break; case BC_ListBoxToggle::TOGGLE_DOWN_EXIT: if( cursor_inside ) { state = BC_ListBoxToggle::TOGGLE_DOWN; *redraw_toggles = 1; } result = 1; break; default: if( cursor_inside ) { if( value ) state = BC_ListBoxToggle::TOGGLE_CHECKEDHI; else state = BC_ListBoxToggle::TOGGLE_UPHI; *redraw_toggles = 1; } break; } return result; } int BC_ListBoxToggle::cursor_leave_event(int *redraw_toggles) { if( value ) state = BC_ListBoxToggle::TOGGLE_CHECKED; else state = BC_ListBoxToggle::TOGGLE_UP; return 0; } int BC_ListBoxToggle::button_press_event() { int w = listbox->toggle_images[0]->get_w(); int h = listbox->toggle_images[0]->get_h(); if( listbox->gui->get_cursor_x() >= x && listbox->gui->get_cursor_x() < x + w && listbox->gui->get_cursor_y() >= y && listbox->gui->get_cursor_y() < y + h ) { state = BC_ListBoxToggle::TOGGLE_DOWN; return 1; } return 0; } int BC_ListBoxToggle::button_release_event(int *redraw_toggles) { int result = 0; switch( state ) { case BC_ListBoxToggle::TOGGLE_DOWN: value = !value; if( value ) state = BC_ListBoxToggle::TOGGLE_CHECKEDHI; else state = BC_ListBoxToggle::TOGGLE_UPHI; listbox->expand_item(item, value); result = 1; break; case BC_ListBoxToggle::TOGGLE_DOWN_EXIT: if( value ) state = BC_ListBoxToggle::TOGGLE_CHECKED; else state = BC_ListBoxToggle::TOGGLE_UP; *redraw_toggles = 1; result = 1; break; } return result; } void BC_ListBoxToggle::draw(int flash) { if( listbox->gui ) { int image_number = 0; int w = listbox->toggle_images[0]->get_w(); int h = listbox->toggle_images[0]->get_h(); switch( state ) { case BC_ListBoxToggle::TOGGLE_UP: image_number = 0; break; case BC_ListBoxToggle::TOGGLE_UPHI: image_number = 1; break; case BC_ListBoxToggle::TOGGLE_CHECKED: image_number = 2; break; case BC_ListBoxToggle::TOGGLE_DOWN: image_number = 3; break; case BC_ListBoxToggle::TOGGLE_CHECKEDHI: image_number = 4; break; case BC_ListBoxToggle::TOGGLE_DOWN_EXIT: image_number = value ? 2 : 0; break; } //printf("BC_ListBoxToggle::draw 1 %d\n", state); listbox->gui->draw_pixmap(listbox->toggle_images[image_number], x, y); if( flash ) { listbox->gui->flash(x, y, w, h); listbox->gui->flush(); } } } // ====================================================== box BC_ListBox::BC_ListBox(int x, int y, int w, int h, int display_format, ArrayList *data, const char **column_titles, int *column_width, int columns, int yposition, int is_popup, int selection_mode, int icon_position, int allow_drag) : BC_SubWindow(x, y, w, h, -1) { justify = LISTBOX_RIGHT; xposition = 0; highlighted_item = -1; highlighted_title = -1; highlighted_division = -1; highlighted_ptr = 0; xscrollbar = 0; yscrollbar = 0; current_cursor = ARROW_CURSOR; gui = 0; view_w = items_w = 0; view_h = items_h = 0; title_h = 0; active = 0; is_suggestions = 0; new_value = 0; need_xscroll = 0; need_yscroll = 0; xscroll_orientation = SCROLL_HORIZ; yscroll_orientation = SCROLL_VERT; bg_tile = 0; bg_draw = 1; drag_popup = 0; dragged_title = 0; selection_number1 = -1; selection_number2 = -1; bg_surface = 0; bg_pixmap = 0; row_height = row_ascent = row_descent = 0; selection_start = 0; selection_center = 0; selection_end = -1; selection_number = -1; current_operation = NO_OPERATION; button_highlighted = 0; button_releases = 0; list_highlighted = 0; disabled = 0; scroll_repeat = 0; allow_drag_scroll = 1; process_drag = 1; for( int i=0; i<32; ++i ) default_column_width[i] = 0; sort_column = -1; sort_order = 0; allow_drag_column = 0; master_column = 0; search_column = 0; popup_w = w; popup_h = h; for( int i=0; i<3; ++i ) column_bg[i] = 0; for( int i=0; i<4; ++i ) button_images[i] = 0; for( int i=0; i<5; ++i ) toggle_images[i] = 0; column_sort_up = 0; column_sort_dn = 0; //printf("BC_ListBox::BC_ListBox 1\n"); this->data = data; this->columns = columns; this->yposition = yposition; this->is_popup = is_popup; this->use_button = 1; this->display_format = display_format; this->selection_mode = selection_mode; this->icon_position = icon_position; this->allow_drag = allow_drag; this->column_titles = 0; this->column_width = 0; this->first_in_view = -1; this->last_in_view = 0; //printf("BC_ListBox::BC_ListBox 1\n"); if( (!column_titles && column_width) || (column_titles && !column_width) ) { printf("BC_ListBox::BC_ListBox either column_titles or column_widths == NULL but not both.\n"); } //printf("BC_ListBox::BC_ListBox 2 %p %p\n", column_titles, column_width); set_columns(column_titles, column_width, columns); //printf("BC_ListBox::BC_ListBox 3\n"); drag_icon_vframe = 0; drag_column_icon_vframe = 0; drag_cursor_x = 0; drag_column_w = 0; temp_display_format = display_format; packed_icons = display_format == LISTBOX_ICONS_PACKED ? 1 : 0; rect_x1 = rect_x2 = 0; rect_y1 = rect_y2 = 0; // reset the search engine //printf("BC_ListBox::BC_ListBox 4\n"); show_query = 0; reset_query(); //printf("BC_ListBox::BC_ListBox 5\n"); } BC_ListBox::~BC_ListBox() { expanders.remove_all_objects(); if( bg_surface ) delete bg_surface; if( bg_pixmap ) delete bg_pixmap; if( xscrollbar ) delete xscrollbar; if( yscrollbar ) delete yscrollbar; for( int i=0; i<3; ++i ) delete column_bg[i]; for( int i=0; i<4; ++i ) delete button_images[i]; for( int i=0; i<5; ++i ) delete toggle_images[i]; if( column_sort_up ) delete column_sort_up; if( column_sort_dn ) delete column_sort_dn; delete_columns(); if( drag_popup ) delete drag_popup; } int BC_ListBox::enable() { disabled = 0; draw_button(1); return 1; } int BC_ListBox::disable() { disabled = 1; draw_button(1); return 1; } void BC_ListBox::reset_query() { query[0] = 0; // reset query } int BC_ListBox::evaluate_query(char *string) { for( int i=0; itext) <= 0 && data[search_column].get(i)->searchable ) { return i; } } return -1; } int BC_ListBox::query_list() { if( query[0] == 0 ) return 0; int done = 0; int result; int selection_changed = 0; int prev_selection = -1; result = evaluate_query(query); if( result >= 0 ) done = 1; if( done ) { // Deselect all for( int i=0; iselected ) prev_selection = i; data[j].values[i]->selected = 0; } } // Select one if( prev_selection != result ) selection_changed = 1; for( int j=0; jselected = 1; } center_selection(result); } return selection_changed; } void BC_ListBox::init_column_width() { if( !column_width && data ) { int widest = 5, wd; for( int i=0; i widest ) widest = wd; } default_column_width[0] = widest + 2 * LISTBOX_MARGIN; } } int BC_ListBox::initialize() { if( is_popup ) { if( use_button ) { for( int i=0; i<4; ++i ) { button_images[i] = new BC_Pixmap(parent_window, BC_WindowBase::get_resources()->listbox_button[i], PIXMAP_ALPHA); } w = button_images[0]->get_w(); h = button_images[0]->get_h(); } gui = 0; current_operation = NO_OPERATION; } else { gui = this; current_operation = NO_OPERATION; } for( int i=0; i<3; ++i ) { column_bg[i] = new BC_Pixmap(parent_window, get_resources()->listbox_column[i], PIXMAP_ALPHA); } for( int i=0; i<5; ++i ) { toggle_images[i] = new BC_Pixmap(parent_window, get_resources()->listbox_expand[i], PIXMAP_ALPHA); } column_sort_up = new BC_Pixmap(parent_window, BC_WindowBase::get_resources()->listbox_up, PIXMAP_ALPHA); column_sort_dn = new BC_Pixmap(parent_window, BC_WindowBase::get_resources()->listbox_dn, PIXMAP_ALPHA); //printf("BC_ListBox::initialize 10\n"); drag_icon_vframe = get_resources()->type_to_icon[ICON_UNKNOWN]; drag_column_icon_vframe = get_resources()->type_to_icon[ICON_COLUMN]; // = new BC_Pixmap(parent_window, // get_resources()->type_to_icon[ICON_UNKNOWN], // PIXMAP_ALPHA); // drag_column_icon = new BC_Pixmap(parent_window, // get_resources()->type_to_icon[ICON_COLUMN], // PIXMAP_ALPHA); BC_SubWindow::initialize(); init_column_width(); if( top_level->get_resources()->listbox_bg ) bg_pixmap = new BC_Pixmap(this, get_resources()->listbox_bg, PIXMAP_OPAQUE); draw_button(0); draw_items(0); if( !use_button && is_popup ) { hide_window(1); } return 0; } void BC_ListBox::deactivate_selection() { current_operation = NO_OPERATION; } int BC_ListBox::draw_button(int flush) { // Draw the button for a popup listbox if( use_button && is_popup ) { int image_number = 0; draw_top_background(parent_window, 0, 0, w, h); if( button_highlighted ) image_number = 1; if( current_operation == BUTTON_DN ) image_number = 2; if( disabled ) image_number = 3; pixmap->draw_pixmap(button_images[image_number], 0, 0, w, h, 0, 0); flash(flush); } return 0; } int BC_ListBox::calculate_item_coords() { if( !data ) return 0; int icon_x = 0; int next_icon_x = 0; int next_icon_y = 0; int next_text_y = 0; // Change the display_format to get the right item dimensions for both // text and icons. temp_display_format = display_format; // Scan the first column for lowest y coord of all text // and lowest right x and y coord for all icons which aren't auto placable calculate_last_coords_recursive(data, &icon_x, &next_icon_x, &next_icon_y, &next_text_y, 1); // Reset last column width. It's recalculated based on text width. calculate_item_coords_recursive(data, &icon_x, &next_icon_x, &next_icon_y, &next_text_y, 1); display_format = temp_display_format; return 0; } void BC_ListBox::calculate_last_coords_recursive( ArrayList *data, int *icon_x, int *next_icon_x, int *next_icon_y, int *next_text_y, int top_level) { for( int i=0; iautoplace_text ) { // Lowest text coordinate display_format = LISTBOX_TEXT; current_text_y = item->text_y + get_text_h(item); if( current_text_y > *next_text_y ) *next_text_y = current_text_y; // Add sublist depth if it is expanded if( item->sublist_active() && item->get_columns() ) { calculate_last_coords_recursive(item->get_sublist(), icon_x, next_icon_x, next_icon_y, next_text_y, 0); } } // Get next_icon coordinate if( top_level ) { BC_ListBoxItem *item = data[master_column].get(i); if( !item->autoplace_icon ) { display_format = LISTBOX_ICONS; // Lowest right icon coordinate. current_icon_x = item->icon_x; if( current_icon_x > *icon_x ) *icon_x = current_icon_x; if( current_icon_x + get_item_w(item) > *next_icon_x ) { *next_icon_x = current_icon_x + get_item_w(item); *next_icon_y = 0; } current_icon_y = item->icon_y + get_item_h(item); if( current_icon_y > *next_icon_y ) *next_icon_y = current_icon_y; } } } } void BC_ListBox::calculate_item_coords_recursive( ArrayList *data, int *icon_x, int *next_icon_x, int *next_icon_y, int *next_text_y, int top_level) { // get maximum height of an icon row_height = get_text_height(MEDIUMFONT); if( temp_display_format == LISTBOX_ICON_LIST ) { for( int i=0; iicon ) { int icon_h = data[0].get(i)->icon->get_h() + 2*ICON_MARGIN; if( row_height < icon_h ) row_height = icon_h; } } } // Set up items which need autoplacement. // Should fill icons down and then across for( int i=0; iautoplace_icon ) { // 1 column only if icons are used display_format = LISTBOX_ICONS; // Test row height // Start new column. if( *next_icon_y + get_item_h(item) >= get_h() && *next_icon_y > 0 ) { *icon_x = *next_icon_x; *next_icon_y = 0; } if( *icon_x + get_item_w(item) > *next_icon_x ) *next_icon_x = *icon_x + get_item_w(item); item->set_icon_x(*icon_x); item->set_icon_y(*next_icon_y); *next_icon_y += get_item_h(item); } } // Set up a text row int next_text_x = 0; row_ascent = row_descent = 0; // row_height still holds icon max height for( int j=0; jautoplace_text ) { display_format = LISTBOX_TEXT; item->set_text_x(next_text_x); item->set_text_y(*next_text_y); int ht = get_text_h(item); if( ht > row_height ) row_height = ht; int bl = get_baseline(item); if( bl > row_ascent ) row_ascent = bl; int dt = ht - bl; if( dt > row_descent ) row_descent = dt; // printf("BC_ListBox::calculate_item_coords_recursive %p %d %d %d %d %s \n", // item->sublist, item->get_columns(), item->get_expand(), // next_text_x, *next_text_y, item->get_text()); // Increment position of next column if( j < columns - 1 ) { next_text_x += (column_width ? column_width[j] : default_column_width[j]); } // Set last column width based on text width else { int new_w = get_item_w(item); int *previous_w = (column_width ? &column_width[j] : &default_column_width[j]); if( new_w > *previous_w ) *previous_w = new_w; //printf("BC_ListBox::calculate_item_coords_recursive 1 %d\n", new_w); } total_autoplaced_columns++; } } // Increase the text vertical position if( total_autoplaced_columns ) { display_format = LISTBOX_TEXT; *next_text_y += row_height; } // Set up a sublist BC_ListBoxItem *item = data[master_column].values[i]; if( item->sublist_active() && item->get_columns() ) { calculate_item_coords_recursive( item->get_sublist(), icon_x, next_icon_x, next_icon_y, next_text_y, 0); } } } void BC_ListBox::set_is_suggestions(int value) { this->is_suggestions = value; } void BC_ListBox::set_scroll_repeat() { if( scroll_repeat ) return; scroll_repeat = 1; set_repeat(get_resources()->scroll_repeat); } void BC_ListBox::unset_scroll_repeat() { if( !scroll_repeat ) return; scroll_repeat = 0; unset_repeat(get_resources()->scroll_repeat); } void BC_ListBox::set_use_button(int value) { this->use_button = value; } void BC_ListBox::set_justify(int value) { this->justify = value; } void BC_ListBox::set_allow_drag_column(int value) { this->allow_drag_column = value; } void BC_ListBox::set_process_drag(int value) { this->process_drag = value; } void BC_ListBox::set_master_column(int value, int redraw) { this->master_column = value; if( redraw ) { draw_items(1); } } void BC_ListBox::set_search_column(int value) { this->search_column = value; } int BC_ListBox::get_sort_column() { return sort_column; } void BC_ListBox::set_sort_column(int value, int redraw) { sort_column = value; if( redraw ) { draw_titles(1); } } int BC_ListBox::get_sort_order() { return sort_order; } void BC_ListBox::set_sort_order(int value, int redraw) { sort_order = value; if( redraw ) { draw_titles(1); } } int BC_ListBox::get_display_mode() { return display_format; } int BC_ListBox::get_yposition() { return yposition; } int BC_ListBox::get_xposition() { return xposition; } int BC_ListBox::get_highlighted_item() { return highlighted_item; } int BC_ListBox::get_item_x(BC_ListBoxItem *item) { switch( display_format ) { case LISTBOX_TEXT: case LISTBOX_ICON_LIST: return item->text_x - xposition + 2; case LISTBOX_ICONS: case LISTBOX_ICONS_PACKED: return item->icon_x - xposition + 2; } return 0; } int BC_ListBox::get_item_y(BC_ListBoxItem *item) { switch( display_format ) { case LISTBOX_TEXT: case LISTBOX_ICON_LIST: return item->text_y - yposition + title_h + 2; case LISTBOX_ICONS: case LISTBOX_ICONS_PACKED: return item->icon_y - yposition + 2; } return 0; } int BC_ListBox::get_item_w(BC_ListBoxItem *item) { switch( display_format ) { case LISTBOX_TEXT: case LISTBOX_ICON_LIST: { return get_text_w(item) + 2 * LISTBOX_MARGIN; } case LISTBOX_ICONS: case LISTBOX_ICONS_PACKED: { int x, y, w, h; get_icon_mask(item, x, y, w, h); int icon_w = w; get_text_mask(item, x, y, w, h); int text_w = w; return icon_position == ICON_LEFT ? icon_w + text_w : icon_w > text_w ? icon_w : text_w; } } return 0; } int BC_ListBox::get_item_h(BC_ListBoxItem *item) { switch( display_format ) { case LISTBOX_TEXT: case LISTBOX_ICON_LIST: return get_text_h(item); case LISTBOX_ICONS: case LISTBOX_ICONS_PACKED: { int x, y, w, h; get_icon_mask(item, x, y, w, h); int icon_h = h; get_text_mask(item, x, y, w, h); int text_h = h; return icon_position != ICON_LEFT ? icon_h + text_h : icon_h > text_h ? icon_h : text_h; } } return 0; } int BC_ListBox::get_icon_x(BC_ListBoxItem *item) { return get_item_x(item) + ICON_MARGIN; } int BC_ListBox::get_icon_y(BC_ListBoxItem *item) { return get_item_y(item) + ICON_MARGIN; } int BC_ListBox::get_icon_w(BC_ListBoxItem *item) { return item->get_icon_w() + 2*ICON_MARGIN; } int BC_ListBox::get_icon_h(BC_ListBoxItem *item) { return item->get_icon_h() + 2*ICON_MARGIN; } int BC_ListBox::get_text_w(BC_ListBoxItem *item) { int w = item->get_text_w(); if( w < 0 ) item->set_text_w(w = get_text_width(MEDIUMFONT, item->get_text())); return w; } int BC_ListBox::get_text_h(BC_ListBoxItem *item) { int h = item->get_text_h(); if( h < 0 ) item->set_text_h(h = get_text_height(MEDIUMFONT, item->get_text())); return h; } int BC_ListBox::get_baseline(BC_ListBoxItem *item) { int b = item->get_baseline(); if( b < 0 ) item->set_baseline(b = get_text_ascent(MEDIUMFONT)); return b; } int BC_ListBox::get_items_width() { switch( display_format ) { case LISTBOX_TEXT: return get_column_offset(columns); case LISTBOX_ICONS: case LISTBOX_ICONS_PACKED: { int widest = 0; for( int i=0; iicon_x; get_icon_mask(item, x, y, w, h); if( x1 + w > widest ) widest = x1 + w; if( icon_position == ICON_LEFT ) x1 += w; get_text_mask(item, x, y, w, h); if( x1 + w > widest ) widest = x1 + w; } } return widest; } case LISTBOX_ICON_LIST: return get_column_offset(columns); } return 0; } int BC_ListBox::get_items_height(ArrayList *data, int columns, int *result) { int temp = 0; int top_level = 0; int highest = 0; if( !result ) { result = &temp; top_level = 1; } for( int j=0; j<(data?data[master_column].total:0); ++j ) { int x, y, w, h; BC_ListBoxItem *item = data[master_column].values[j]; switch( display_format ) { case LISTBOX_TEXT: get_text_mask(item, x, y, w, h); *result += h; // Descend into sublist if( item->sublist_active() ) get_items_height(item->get_sublist(), item->get_columns(), result); break; case LISTBOX_ICONS: case LISTBOX_ICONS_PACKED: case LISTBOX_ICON_LIST: get_icon_mask(item, x, y, w, h); if( y + h + yposition > highest ) highest = y + h + yposition; get_text_mask(item, x, y, w, h); if( y + h + yposition > highest ) highest = y + h + yposition; break; } } if( display_format == LISTBOX_TEXT && top_level ) { highest = LISTBOX_MARGIN + *result; } return highest; } int BC_ListBox::set_yposition(int position, int draw_items) { this->yposition = position; if( draw_items ) { this->draw_items(1); } return 0; } int BC_ListBox::set_xposition(int position) { this->xposition = position; draw_items(1); return 0; } int BC_ListBox::is_highlighted() { return list_highlighted; } void BC_ListBox::expand_item(BC_ListBoxItem *item, int expand) { if( item ) { item->expand = expand; // Collapse sublists if this is collapsed to make it easier to calculate // coordinates if( item->get_sublist() ) collapse_recursive(item->get_sublist(), master_column); // Set everything for autoplacement set_autoplacement(data, 0, 1); draw_items(1); } } void BC_ListBox::collapse_recursive(ArrayList *data, int master_column) { for( int i=0; isublist_active() ) { item->expand = 0; collapse_recursive(item->get_sublist(), master_column); } } } void BC_ListBox::set_autoplacement(ArrayList *data, int do_icons, int do_text) { for( int i=0; iautoplace_icon = 1; if( do_text ) data[j].values[i]->autoplace_text = 1; } BC_ListBoxItem *item = data[master_column].values[i]; if( item->sublist_active() ) { set_autoplacement(item->get_sublist(), do_icons, do_text); } } } void BC_ListBox::set_scroll_stretch(int xv, int yv) { if( xv >= 0 ) xscroll_orientation = !xv ? SCROLL_HORIZ : SCROLL_HORIZ + SCROLL_STRETCH; if( yv >= 0 ) yscroll_orientation = !yv ? SCROLL_VERT : SCROLL_VERT + SCROLL_STRETCH; } int BC_ListBox::get_yscroll_x() { if( is_popup ) return popup_w - get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w(); else return get_x() + popup_w - get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w(); } int BC_ListBox::get_yscroll_y() { return is_popup ? 0 : get_y(); } int BC_ListBox::get_yscroll_height() { return popup_h - (need_xscroll ? get_resources()->hscroll_data[SCROLL_HANDLE_UP]->get_h() : 0); } int BC_ListBox::get_xscroll_x() { return is_popup ? 0 : get_x(); } int BC_ListBox::get_xscroll_y() { return (is_popup ? popup_h : get_y() + popup_h) - get_resources()->hscroll_data[SCROLL_HANDLE_UP]->get_h(); } int BC_ListBox::get_xscroll_width() { return popup_w - (need_yscroll ? get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w() : 0); } int BC_ListBox::get_column_offset(int column) { int x = 0; while( column > 0 ) { x += column_width ? column_width[--column] : default_column_width[--column]; } return x; } void BC_ListBox::column_width_boundaries() { if( column_width ) { for( int i=0; itext) + LISTBOX_MARGIN * 2; h = row_height; break; } default: w = h = 0; } return 0; } int BC_ListBox::get_item_highlight(ArrayList *data, int column, int item) { BC_Resources *resources = get_resources(); if( data[column].values[item]->selected ) return resources->listbox_selected; if( highlighted_item >= 0 && highlighted_ptr == data[master_column].values[item] ) return resources->listbox_highlighted; return resources->listbox_inactive; } int BC_ListBox::get_item_color(ArrayList *data, int column, int item) { int color = data[column].values[item]->color; if( color == -1 ) color = get_resources()->listbox_text; if( get_item_highlight(data, column, item) == color ) return BLACK; return color; } int BC_ListBox::get_from_column() { return dragged_title; } int BC_ListBox::get_to_column() { return highlighted_title; } BC_ListBoxItem* BC_ListBox::get_selection(int column, int selection_number) { return get_selection_recursive(data, column, selection_number); } BC_ListBoxItem* BC_ListBox::get_selection_recursive(ArrayList *data, int column, int selection_number) { if( !data ) return 0; for( int i=0; iselected ) { //printf("BC_ListBox::get_selection_recursive %d\n", __LINE__); selection_number--; if( selection_number < 0 ) { return data[column].values[i]; } } if( item->sublist_active() ) { BC_ListBoxItem *result = get_selection_recursive(item->get_sublist(), column, selection_number); if( result ) return result; } } return 0; } int BC_ListBox::get_selection_number(int column, int selection_number) { return get_selection_number_recursive(data, column, selection_number); } int BC_ListBox::get_selection_number_recursive(ArrayList *data, int column, int selection_number, int *counter) { int temp = -1; if( !data ) return 0; if( !counter ) counter = &temp; for( int i=0; iselected ) { selection_number--; if( selection_number < 0 ) { return (*counter); } } if( item->sublist_active() ) { int result = get_selection_number_recursive(item->get_sublist(), column, selection_number, counter); if( result >= 0 ) return result; } } return -1; } int BC_ListBox::set_selection_mode(int mode) { this->selection_mode = mode; return 0; } void BC_ListBox::delete_columns() { if( column_titles ) { for( int i=0; icolumn_titles = new char*[columns]; for( int i=0; icolumn_titles[i] = new char[strlen(column_titles[i]) + 1]; strcpy(this->column_titles[i], column_titles[i]); } } if( column_width ) { this->column_width = new int[columns]; for( int i=0; icolumn_width[i] = column_width[i]; } } this->columns = columns; } int BC_ListBox::update(ArrayList *data, const char **column_titles, int *column_widths, int columns, int xposition, int yposition, int highlighted_number, int recalc_positions, int draw) { set_columns(column_titles, column_widths, columns); this->data = data; this->yposition = yposition; this->xposition = xposition; this->highlighted_item = highlighted_number; this->highlighted_ptr = index_to_item(data, highlighted_number, 0); if( recalc_positions ) set_autoplacement(data, 1, 1); init_column_width(); if( gui && draw ) { draw_items(0, 1); update_scrollbars(1); } return 0; } void BC_ListBox::center_selection() { int selection = get_selection_number(0, 0); calculate_item_coords(); center_selection(selection); if( gui ) { draw_items(1, 1); update_scrollbars(0); gui->show_window(1); } } void BC_ListBox::move_vertical(int pixels) { } void BC_ListBox::move_horizontal(int pixels) { } int BC_ListBox::select_previous(int skip, BC_ListBoxItem *selected_item, int *counter, ArrayList *data, int *got_first, int *got_second) { int top_level = 0; if( !selected_item ) selected_item = get_selection(0, 0); int temp = -1; if( !counter ) counter = &temp; int temp2 = 0; if( !got_first ) { got_first = &temp2; top_level = 1; } int temp3 = 0; if( !got_second ) got_second = &temp3; if( !data ) data = this->data; // Scan backwards to item pointer. Then count visible items to get // destination. No wraparound. do { for( int i=data[master_column].total-1; i>=0; --i ) { BC_ListBoxItem *current_item = data[master_column].values[i]; if( current_item->sublist_active() ) { int result = select_previous(skip, selected_item, counter, current_item->get_sublist(), got_first, got_second); if( *got_second ) return result; } if( *got_first ) { (*counter)++; if( (*counter) >= skip ) { for( int j=0; jselected = 1; (*got_second) = 1; return item_to_index(this->data, current_item); } } else { if( current_item->selected ) { for( int j=0; jselected = 0; (*got_first) = 1; (*counter)++; } } } // Hit top of top level without finding a selected item. if( top_level ) { // Select first item in top level and quit BC_ListBoxItem *current_item; (*got_first) = 1; current_item = data[master_column].values[0]; for( int j=0; jselected = 1; (*got_second) = 1; return item_to_index(this->data, current_item); } } while( top_level && data[master_column].total ); return -1; } int BC_ListBox::select_next(int skip, BC_ListBoxItem *selected_item, int *counter, ArrayList *data, int *got_first, int *got_second) { int top_level = 0; if( !selected_item ) selected_item = get_selection(0, 0); int temp = -1; if( !counter ) counter = &temp; int temp2 = 0; if( !got_first ) { got_first = &temp2; top_level = 1; } int temp3 = 0; if( !got_second ) got_second = &temp3; if( !data ) data = this->data; // Scan forwards to currently selected item pointer. // Then count visible items to get destination. No wraparound. do { for( int i=0; i= skip ) { for( int j=0; jselected = 1; (*got_second) = 1; return item_to_index(this->data, current_item); } } else { // Got currently selected item. Deselect it. if( current_item->selected ) { for( int j=0; jselected = 0; (*got_first) = 1; (*counter)++; } } // Descend into expanded level if( current_item->sublist_active() ) { int result = select_next(skip, selected_item, counter, current_item->get_sublist(), got_first, got_second); if( *got_second ) { return result; } } } // Hit bottom of top level without finding the next item. if( top_level ) { BC_ListBoxItem *current_item; // Select first item in top level and quit if( !(*got_first) ) { (*got_first) = 1; current_item = data[master_column].values[0]; for( int j=0; jselected = 1; (*got_second) = 1; } else { // Select last item in top level and quit (*got_first) = 1; int current_row = data[master_column].total - 1; current_item = data[master_column].values[current_row]; for( int j=0; jselected = 1; (*got_second) = 1; } return item_to_index(this->data, current_item); } } while( top_level && data[master_column].total ); return -1; } void BC_ListBox::clamp_positions() { items_w = get_items_width(); if( xscroll_orientation & SCROLL_STRETCH ) items_w += view_w / 4; items_h = get_items_height(data, columns); if( yscroll_orientation & SCROLL_STRETCH ) items_h += view_h / 4; if( yposition < 0 ) yposition = 0; else if( yposition > items_h - view_h ) yposition = items_h - view_h; if( yposition < 0 ) yposition = 0; if( xposition < 0 ) xposition = 0; else if( xposition >= items_w - view_w ) xposition = items_w - view_w; if( xposition < 0 ) xposition = 0; } int BC_ListBox::center_selection(int selection, ArrayList *data, int *counter) { int temp = -1; if( !data ) data = this->data; if( !counter ) counter = &temp; for( int i=0; idata[master_column].values[0]; switch( display_format ) { case LISTBOX_ICONS: case LISTBOX_ICONS_PACKED: { // Icon is out of window if( item->icon_y-yposition > view_h-get_text_h(item) || item->icon_y-yposition < 0 ) { yposition = item->icon_y - view_h / 2; } if( data[master_column].values[selection]->icon_x - xposition > view_w || data[master_column].values[selection]->icon_x - xposition < 0 ) { xposition = item->icon_x - view_w / 2; } break; } case LISTBOX_TEXT: case LISTBOX_ICON_LIST: // Text coordinate is out of window if( item->text_y-yposition > view_h-get_text_h(item) || item->text_y-yposition < 0 ) { yposition = item->text_y - top_item->text_y - view_h / 2; } } return 1; } // Descend if( item->sublist_active() ) { int result = center_selection(selection, item->get_sublist(), counter); if( result ) return result; } } return 0; } void BC_ListBox::update_scrollbars(int flush) { int h_needed = items_h = get_items_height(data, columns); int w_needed = items_w = get_items_width(); // if( columns > 0 && column_width ) // printf("BC_ListBox::update_scrollbars 1 %d %d\n", column_width[columns - 1], w_needed); if( xscrollbar ) { if( xposition != xscrollbar->get_value() ) xscrollbar->update_value(xposition); if( w_needed != xscrollbar->get_length() || view_w != xscrollbar->get_handlelength() ) xscrollbar->update_length(w_needed, xposition, view_w, 0); } if( yscrollbar ) { if( yposition != yscrollbar->get_value() ) yscrollbar->update_value(yposition); if( h_needed != yscrollbar->get_length() || view_h != yscrollbar->get_handlelength() ) yscrollbar->update_length(h_needed, yposition, view_h, 0); } if( flush ) this->flush(); } int BC_ListBox::get_scrollbars() { int h_needed = items_h = get_items_height(data, columns); int w_needed = items_w = get_items_width(); int flush = 0; title_h = get_title_h(); view_h = popup_h - title_h - 4; view_w = popup_w - 4; // Create scrollbars as needed for( int i=0; i<2; ++i ) { if( w_needed > view_w ) { need_xscroll = 1; view_h = popup_h - title_h - get_resources()->hscroll_data[SCROLL_HANDLE_UP]->get_h() - 4; } else { need_xscroll = 0; } if( h_needed > view_h ) { need_yscroll = 1; view_w = popup_w - get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w() - 4; } else { need_yscroll = 0; } } // Update subwindow size int new_w = popup_w; int new_h = popup_h; if( need_xscroll ) new_h -= get_resources()->hscroll_data[SCROLL_HANDLE_UP]->get_h(); if( need_yscroll ) new_w -= get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w(); if( !is_popup ) if( new_w != BC_WindowBase::get_w() || new_h != BC_WindowBase::get_h() ) gui->resize_window(new_w, new_h); BC_WindowBase *destination = (is_popup ? gui : parent_window); if( need_xscroll ) { if( !xscrollbar ) { xscrollbar = new BC_ListBoxXScroll(this); destination->add_subwindow(xscrollbar); xscrollbar->show_window(0); xscrollbar->bound_to = this; } else { xscrollbar->update_length(w_needed, xposition, view_w, flush); xscrollbar->reposition_window(get_xscroll_x(), get_xscroll_y(), get_xscroll_width()); } } else { if( xscrollbar ) delete xscrollbar; xscrollbar = 0; xposition = 0; } if( need_yscroll ) { if( !yscrollbar ) { yscrollbar = new BC_ListBoxYScroll(this); destination->add_subwindow(yscrollbar); yscrollbar->show_window(0); yscrollbar->bound_to = this; } else { yscrollbar->update_length(h_needed, yposition, view_h, flush); yscrollbar->reposition_window(get_yscroll_x(), get_yscroll_y(), get_yscroll_height()); } } else { if( yscrollbar ) delete yscrollbar; yscrollbar = 0; yposition = 0; } if( !bg_surface || view_w + 4 != bg_surface->get_w() || view_h + 4 != bg_surface->get_h() ) { if( bg_surface ) delete bg_surface; bg_surface = new BC_Pixmap(gui, view_w + 4, view_h + 4); bg_draw = 1; } return 0; } void BC_ListBox::set_drag_scroll(int value) { allow_drag_scroll = value; } // Test for scrolling by dragging int BC_ListBox::test_drag_scroll(int cursor_x, int cursor_y) { int result = 0; if( allow_drag_scroll || current_operation == SELECT_RECT ) { int top_boundary = get_title_h(); if( cursor_y < top_boundary || cursor_y >= view_h + title_h + LISTBOX_BORDER * 2 || cursor_x < LISTBOX_BORDER || cursor_x >= view_w + LISTBOX_BORDER ) { result = 1; } } return result; } int BC_ListBox::drag_scroll_event() { int top_boundary = get_title_h(); int result = 0; if( get_cursor_y() < top_boundary ) { yposition -= top_boundary - get_cursor_y(); result = 1; } else if( get_cursor_y() >= view_h + title_h + 4 ) { yposition += get_cursor_y() - (view_h + title_h + 4); result = 1; } if( get_cursor_x() < 2 ) { xposition -= 2 - get_cursor_x(); result = 1; } else if( get_cursor_x() >= view_w + 2 ) { xposition += get_cursor_x() - (view_w + 2); result = 1; } if( result ) clamp_positions(); return result; } int BC_ListBox::rectangle_scroll_event() { int old_xposition = xposition; int old_yposition = yposition; int result = drag_scroll_event(); if( result ) { rect_x1 += old_xposition - xposition; rect_y1 += old_yposition - yposition; rect_x2 = get_cursor_x(); rect_y2 = get_cursor_y(); int x1 = MIN(rect_x1, rect_x2); int x2 = MAX(rect_x1, rect_x2); int y1 = MIN(rect_y1, rect_y2); int y2 = MAX(rect_y1, rect_y2); if( select_rectangle(data, x1, y1, x2, y2) ) { selection_changed(); } clamp_positions(); draw_items(0); update_scrollbars(1); } return result; } int BC_ListBox::select_scroll_event() { int result = drag_scroll_event(); if( result ) { highlighted_item = selection_number = get_cursor_item(data, get_cursor_x(), get_cursor_y(), &highlighted_ptr); clamp_positions(); draw_items(0); update_scrollbars(1); selection_changed(); } return result; } int BC_ListBox::select_rectangle(ArrayList *data, int x1, int y1, int x2, int y2) { int result = 0; for( int i=0; i= icon_x && x1 < icon_x + icon_w && y2 >= icon_y && y1 < icon_y + icon_h) || (x2 >= text_x && x1 < text_x + text_w && y2 >= text_y && y1 < text_y + text_h) ) { if( !item->selected ) { item->selected = 1; result = 1; } } else { if( item->selected ) { item->selected = 0; result = 1; } } break; } case LISTBOX_TEXT: case LISTBOX_ICON_LIST: { if( x2 >= 0 && x1 < (yscrollbar ? gui->get_w() - get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w() : gui->get_w()) && y2 > 0 && y1 < gui->get_h() && y2 >= get_item_y(item) && y1 < get_item_y(item) + get_item_h(item) ) { if( !item->selected ) { item->selected = 1; result = 1; } } else { if( item->selected ) { item->selected = 0; result = 1; } } break; } } } BC_ListBoxItem *item = data[master_column].values[i]; if( item->sublist_active() ) { result |= select_rectangle(item->get_sublist(), x1, y1, x2, y2); } } return result; } int BC_ListBox::reposition_item(ArrayList *data, int selection_number, int x, int y, int *counter) { int temp = -1; if( !counter ) counter = &temp; for( int i=0; iicon_x = x; item->icon_y = y; return 1; } // Not recursive because it's only used for icons } return 0; } void BC_ListBox::move_selection(ArrayList *dst, ArrayList *src) { for( int i=0; iselected ) { for( int j=0; jsublist_active() ) { move_selection(dst, item->get_sublist()); } ++i; } } int BC_ListBox::put_selection(ArrayList *data, ArrayList *src, int destination, int *counter) { int temp = -1; if( !counter ) counter = &temp; if( destination < 0 || destination >= data[master_column].total ) { for( int j=0; jsublist_active() ) { if( put_selection(item->get_sublist(), src, destination, counter) ) return 1; } } return 0; } int BC_ListBox::item_to_index(ArrayList *data, BC_ListBoxItem *item, int *counter) { int temp = -1; if( !counter ) counter = &temp; for( int i=0; isublist_active() ) { if( item_to_index(new_item->get_sublist(), item, counter) >= 0 ) return (*counter); } } return -1; } BC_ListBoxItem* BC_ListBox::index_to_item(ArrayList *data, int number, int column, int *counter) { int temp = -1; if( !counter ) counter = &temp; for( int i=0; isublist_active() ) { BC_ListBoxItem *result = index_to_item(item->get_sublist(), number, column, counter); if( result ) return result; } } return 0; } int BC_ListBox::get_cursor_item(ArrayList *data, int cursor_x, int cursor_y, BC_ListBoxItem **item_return, int *counter, int expanded) { int temp = -1; if( !data ) return -1; if( !counter ) counter = &temp; // Icons are not treed switch( display_format ) { case LISTBOX_ICONS: case LISTBOX_ICONS_PACKED: case LISTBOX_ICON_LIST: { for( int j=data[master_column].total; --j>=0; ) { int icon_x, icon_y, icon_w, icon_h; int text_x, text_y, text_w, text_h; BC_ListBoxItem *item = data[master_column].values[j]; get_icon_mask(item, icon_x, icon_y, icon_w, icon_h); get_text_mask(item, text_x, text_y, text_w, text_h); if( (cursor_x >= icon_x && cursor_x < icon_x + icon_w && cursor_y >= icon_y && cursor_y < icon_y + icon_h) || (cursor_x >= text_x && cursor_x < text_x + text_w && cursor_y >= text_y && cursor_y < text_y + text_h) ) { if( item_return ) (*item_return) = item; return j; } } return -1; } case LISTBOX_TEXT: if( !gui ) break; // Text is treed // Cursor is inside items rectangle if( cursor_x >= 0 && cursor_x < (yscrollbar ? gui->get_w() - get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w() : gui->get_w()) && // Only clamp y if we're not in a SELECT operation. (current_operation == BC_ListBox::SELECT || (cursor_y > get_title_h() + LISTBOX_BORDER && cursor_y < gui->get_h())) ) { // Search table for cursor obstruction for( int i=0; iselectable && cursor_y >= get_item_y(item) && cursor_y < get_item_y(item) + get_item_h(item) ) { //printf("BC_ListBox::get_cursor_item %d %d %p\n", master_column, i, item); if( item_return ) (*item_return) = item; if( expanded < 0 ) (*counter) = i; return (*counter); } // Descend into sublist if( item->sublist_active() ) { if( get_cursor_item(item->get_sublist(), cursor_x, cursor_y, item_return, counter, item->get_expand()) >= 0 ) { if( expanded < 0 ) (*counter) = i; return (*counter); } } } } break; } return -1; } // short version int BC_ListBox::get_cursor_item_no() { int rx, ry; gui->get_relative_cursor(rx, ry); return get_cursor_item(data, rx, ry, 0, 0); } int BC_ListBox::get_cursor_data_item_no(BC_ListBoxItem **item_return) { int rx, ry; gui->get_relative_cursor(rx, ry); return get_cursor_item(data, rx, ry, item_return, 0, -1); } int BC_ListBox::repeat_event(int64_t duration) { switch( current_operation ) { // Repeat out of bounds selection case SELECT_RECT: if( duration != get_resources()->scroll_repeat ) break; return rectangle_scroll_event(); case DRAG_ITEM: if( duration != get_resources()->scroll_repeat ) break; if( !drag_scroll_event() ) break; clamp_positions(); draw_items(0); update_scrollbars(1); return 1; case SELECT: if( duration != get_resources()->scroll_repeat ) break; return select_scroll_event(); case NO_OPERATION: // Show tooltip if( duration != get_resources()->tooltip_delay ) break; if( !button_highlighted || !is_popup ) break; if( !tooltip_text || !tooltip_text[0] ) break; show_tooltip(); return 1; } return 0; } int BC_ListBox::cursor_enter_event() { int result = 0; switch( current_operation ) { // Cursor moved over button, pressed, and exited. case BUTTON_DOWN_SELECT: if( top_level->event_win == win ) { current_operation = BUTTON_DN; result = 1; button_highlighted = 1; draw_button(1); } break; case NO_OPERATION: // Cursor entered button if( is_popup && top_level->event_win == win ) { button_highlighted = 1; result = 1; draw_button(1); } else // TODO: Need to get the highlighted column title or item if( gui && top_level->event_win == gui->win ) { list_highlighted = 1; draw_border(1); result = 1; } break; } return result; } int BC_ListBox::cursor_leave_event() { if( current_operation == COLUMN_DRAG ) return 0; // Left button area if( button_highlighted ) { button_highlighted = 0; hide_tooltip(); draw_button(1); } if( list_highlighted ) { list_highlighted = 0; highlighted_item = -1; highlighted_ptr = 0; highlighted_title = -1; int redraw_toggles = 0; for( int i=0; icursor_leave_event(&redraw_toggles); draw_items(1); } return 0; } int BC_ListBox::get_first_selection(ArrayList *data, int *result) { int temp = -1; if( !result ) result = &temp; for( int i=0; iselected ) return (*result); if( item->sublist_active() ) { if( get_first_selection(item->get_sublist(), result) >= 0 ) return (*result); } } return -1; } int BC_ListBox::get_total_items(ArrayList *data, int *result, int master_column) { int temp = 0; if( !result ) result = &temp; for( int i=0; isublist_active() ) get_total_items(data[master_column].values[i]->get_sublist(), result, master_column); } return (*result); } int BC_ListBox::get_last_selection(ArrayList *data, int *result) { int temp = -1; int top_level = 0; if( !result ) { result = &temp; top_level = 1; } for( int i=data[master_column].total-1; i>=0; --i ) { BC_ListBoxItem *item = data[master_column].values[i]; (*result)++; if( item->selected ) { if( top_level ) return get_total_items(data, 0, master_column) - (*result) /* - 1 */; else return (*result); } if( item->sublist_active() ) { if( get_last_selection(item->get_sublist(), result) >= 0 ) { if( top_level ) return get_total_items(data, 0, master_column) - (*result) /* - 1 */; else return (*result); } } } return -1; } void BC_ListBox::select_range(ArrayList *data, int start, int end, int *current) { int temp = -1; if( !current ) current = &temp; for( int i=0; i= start && (*current) < end ) { for( int j=0; jselected = 1; } BC_ListBoxItem *item = data[master_column].values[i]; if( item->sublist_active() ) select_range(item->get_sublist(), start, end, current); } } // Fill items between current selection and new selection int BC_ListBox::expand_selection(int button_press, int selection_number) { int old_selection_start = selection_start; int old_selection_end = selection_end; // printf("BC_ListBox::expand_selection %d %d\n", // selection_center, // selection_number); // Calculate the range to select based on selection_center and selection_number if( selection_number < selection_center ) { selection_start = selection_number; } else { selection_end = selection_number + 1; } //printf("BC_ListBox::expand_selection %d %d %d %d\n", old_selection_start, old_selection_end, selection_start, selection_end); // Recurse through all the items and select the desired range select_range(data, selection_start, selection_end); // Trigger redraw return (old_selection_start != selection_start || old_selection_end != selection_end); } int BC_ListBox::toggle_item_selection(ArrayList *data, int selection_number, int *counter) { int temp = -1; if( !counter ) counter = &temp; for( int i=0; iselected; // Set row for( int j=0; jselected = selected; return 1; } // Descend into sublist if( item->sublist_active() ) { if( toggle_item_selection(item->get_sublist(), selection_number, counter) ) return 1; } } return 0; } void BC_ListBox::set_all_selected(ArrayList *data, int value) { for( int i=0; iselected = value; } BC_ListBoxItem *item = data[master_column].values[i]; if( item->get_sublist() ) { set_all_selected(item->get_sublist(), value); } } } void BC_ListBox::set_selected(ArrayList *data, int item_number, int value, int *counter) { int temp = -1; if( !counter ) counter = &temp; for( int i=0; iselected = value; } return; } BC_ListBoxItem *item = data[master_column].values[i]; if( item->sublist_active() ) { set_selected(item->get_sublist(), item_number, value, counter); } } } int BC_ListBox::update_selection(ArrayList *data, int selection_number, int *counter) { int temp = -1; int result = 0; if( !counter ) counter = &temp; for( int i=0; iselected ) { result = 1; for( int j=0; jselected = 1; } else if( (*counter) != selection_number && item->selected ) { result = 1; for( int j=0; jselected = 0; } if( item->sublist_active() ) result |= update_selection(item->get_sublist(), selection_number, counter); } return result; } void BC_ListBox::promote_selections(ArrayList *data, int old_value, int new_value) { for( int i=0; iselected == old_value ) item->selected = new_value; } BC_ListBoxItem *item = data[master_column].values[i]; if( item->sublist_active() ) promote_selections(item->get_sublist(), old_value, new_value); } } int BC_ListBox::focus_out_event() { deactivate(); return 0; } int BC_ListBox::button_press_event() { int result = 0; BC_ListBoxItem *current_item = 0; int new_cursor; int do_selection_change = 0; const int debug = 0; hide_tooltip(); if( debug ) printf("BC_ListBox::button_press_event %d this=%p event_win=%p %p %p %p\n", __LINE__, this, (void*)top_level->event_win, (void*)(gui ? gui->win : 0), (void*)win, (void*)parent_window->win); // Pressed in button if( is_popup && top_level->event_win == win ) { if( debug ) printf("BC_ListBox::button_press_event %d\n", __LINE__); current_operation = BUTTON_DN; draw_button(1); // Deploy listbox if( !active && !disabled ) { top_level->deactivate(); activate(); } result = 1; } else // Pressed in scrollbar if( (xscrollbar && top_level->event_win == xscrollbar->win) || (yscrollbar && top_level->event_win == yscrollbar->win) ) { if( debug ) printf("BC_ListBox::button_press_event %d\n", __LINE__); result = 0; } else // Pressed in items if( gui && top_level->event_win == gui->win ) { if( debug ) printf("BC_ListBox::button_press_event %d\n", __LINE__); // Activate list items // If it is a suggestion popup, it is visible without being active if( !active ) { if( debug ) printf("BC_ListBox::button_press_event %d\n", __LINE__); if( !is_suggestions ) top_level->deactivate(); if( debug ) printf("BC_ListBox::button_press_event %d\n", __LINE__); activate(); if( debug ) printf("BC_ListBox::button_press_event %d\n", __LINE__); } if( current_operation != NO_OPERATION ) { switch( current_operation ) { case DRAG_ITEM: case COLUMN_DRAG: return drag_stop_event(); } } // Wheel mouse pressed if( get_buttonpress() == 4 ) { if( current_operation == NO_OPERATION ) { current_operation = WHEEL; if( yscrollbar ) { set_yposition(yposition - gui->get_h() / 10, 0); clamp_positions(); update_scrollbars(0); highlighted_ptr = 0; highlighted_item = get_cursor_item(data, top_level->cursor_x, top_level->cursor_y, &highlighted_ptr); draw_items(1); result = 1; } } } else if( get_buttonpress() == 5 ) { if( current_operation == NO_OPERATION ) { current_operation = WHEEL; if( yscrollbar ) { set_yposition(yposition + gui->get_h() / 10, 0); clamp_positions(); update_scrollbars(0); highlighted_ptr = 0; highlighted_item = get_cursor_item(data, top_level->cursor_x, top_level->cursor_y, &highlighted_ptr); draw_items(1); result = 1; } } } else // Pressed over column title division if( test_column_divisions(gui->get_cursor_x(), gui->get_cursor_y(), new_cursor) ) { drag_cursor_x = gui->get_cursor_x() + xposition; if( column_width ) drag_column_w = column_width[highlighted_division - 1]; else drag_column_w = default_column_width[highlighted_division - 1]; current_operation = DRAG_DIVISION; reset_query(); } else // Pressed in column title if( test_column_titles(gui->get_cursor_x(), gui->get_cursor_y()) ) { current_operation = COLUMN_DN; button_highlighted = 0; list_highlighted = 1; draw_items(1); result = 1; } else // Pressed in expander if( test_expanders() ) { current_operation = EXPAND_DN; // Need to redraw items because of alpha draw_items(1); result = 1; } else // Pressed over item if( (selection_number = get_cursor_item(data, gui->get_cursor_x(), gui->get_cursor_y(), ¤t_item)) >= 0 ) { if( debug ) printf("BC_ListBox::button_press_event %d\n", __LINE__); // Get item button was pressed over selection_number2 = selection_number1; selection_number1 = selection_number; selection_start = -1; selection_end = -1; // Multiple item selection is possible if( selection_mode == LISTBOX_MULTIPLE && (ctrl_down() || shift_down()) ) { // Expand text selection. // Fill items between selected region and current item. if( shift_down() && (display_format == LISTBOX_TEXT || display_format == LISTBOX_ICON_LIST) ) { // Get first item selected selection_start = get_first_selection(data); // Get last item selected selection_end = get_last_selection(data); // Get center of selected region if( selection_end > selection_start ) { selection_center = (selection_end + selection_start) >> 1; } else { selection_center = selection_number; } // Deselect everything. set_all_selected(data, 0); // Select just the items expand_selection(1, selection_number); new_value = 1; } // Toggle a single item on or off else { toggle_item_selection(data, selection_number); new_value = current_item->selected; } } // Select single item else { if( !current_item->selected ) { set_all_selected(data, 0); set_selected(data, selection_number, 1); } new_value = 1; } current_operation = SELECT; highlighted_item = selection_number; highlighted_ptr = current_item; button_highlighted = 0; list_highlighted = 1; reset_query(); draw_items(1); do_selection_change = 1; result = 1; } else // Pressed over nothing. Start rectangle selection. if( data ) { if( get_buttonpress() == 1 && selection_mode == LISTBOX_MULTIPLE ) { if( !shift_down() ) { // Deselect all and redraw if anything was selected if( get_selection_number(0, 0) >= 0 ) { set_all_selected(data, 0); draw_items(1); do_selection_change = 1; result = 1; } } else { // Promote selections to protect from a rectangle selection promote_selections(data, 1, 2); } // Start rectangle selection current_operation = SELECT_RECT; rect_x1 = rect_x2 = get_cursor_x(); rect_y1 = rect_y2 = get_cursor_y(); } } reset_query(); } else // Suggestion box is not active but visible, so lie to get it to deactivate if( is_popup && (active || (is_suggestions && gui)) ) { active = 1; if( debug ) printf("BC_ListBox::button_press_event %d\n", __LINE__); deactivate(); result = 1; } if( do_selection_change ) selection_changed(); if( debug ) printf("BC_ListBox::button_press_event %d %d\n", __LINE__, result); return result; } int BC_ListBox::button_release_event() { int result = 0; int do_event = 0; new_value = 0; unset_scroll_repeat(); //printf("BC_ListBox::button_release_event 1 %d\n", current_operation); switch( current_operation ) { case DRAG_DIVISION: current_operation = NO_OPERATION; result = 1; break; case WHEEL: current_operation = NO_OPERATION; result = 1; break; // Release item selection case BUTTON_DOWN_SELECT: case SELECT: //printf("BC_ListBox::button_release_event 10\n"); current_operation = NO_OPERATION; if( gui ) { selection_number1 = selection_number = get_cursor_item_no(); //printf("BC_ListBox::button_release_event %d %d\n", selection_number2, selection_number1); } if( is_popup ) { button_releases++; if( selection_number >= 0 ) { deactivate(); do_event = 1; } else // Second button release outside button if( button_releases > 1 ) { deactivate(); } } else { if( top_level->get_double_click() && selection_number2 == selection_number1 && selection_number2 >= 0 && selection_number1 >= 0 ) { do_event = 1; } result = 1; } break; case SELECT_RECT: if( data ) { // Demote selections from rectangle selection promote_selections(data, 2, 1); } // Hide rectangle overlay draw_rectangle(1); current_operation = NO_OPERATION; result = 1; break; // Release popup button case BUTTON_DN: hide_tooltip(); current_operation = NO_OPERATION; button_releases++; draw_button(1); // Second button release inside button if( button_releases > 1 ) { deactivate(); } result = 1; break; case COLUMN_DN: current_operation = NO_OPERATION; // Update the sort column and the sort order for the user only if the existing // sort column is valid. if( sort_column >= 0 ) { // Invert order only if column is the same if( highlighted_title == sort_column ) sort_order = sort_order == SORT_ASCENDING ? SORT_DESCENDING : SORT_ASCENDING; // Set the new sort column sort_column = highlighted_title; if( !sort_order_event() ) { draw_titles(1); } } else // Sorting not enabled. Redraw the title state. { draw_titles(1); } result = 1; break; case EXPAND_DN: { int redraw_toggles = 0; for( int i=0; ibutton_release_event(&redraw_toggles) ) { result = 1; } } // Need to redraw items because of alpha if( redraw_toggles ) draw_items(1); current_operation = NO_OPERATION; break; } default: // Can't default to NO_OPERATION because it may be used in a drag event. break; } if( do_event ) handle_event(); //printf("BC_ListBox::button_release_event %d %d\n", __LINE__, get_window_lock()); return result; } int BC_ListBox::get_title_h() { if( display_format == LISTBOX_TEXT || display_format == LISTBOX_ICON_LIST ) return column_titles ? column_bg[0]->get_h() : 0; return 0; } void BC_ListBox::reset_cursor(int new_cursor) { if( is_popup ) { if( gui->get_cursor() != new_cursor ) { gui->set_cursor(new_cursor, 0, 0); } } else if( get_cursor() != new_cursor ) { set_cursor(new_cursor, 0, 0); } } int BC_ListBox::test_column_divisions(int cursor_x, int cursor_y, int &new_cursor) { if( gui && column_titles && cursor_y >= 0 && cursor_y < get_title_h() && cursor_x >= 0 && cursor_x < gui->get_w() ) { for( int i=1; i= -xposition + get_column_offset(i) - 5 && cursor_x < -xposition + get_column_offset(i) + get_resources()->listbox_title_hotspot ) { highlighted_item = -1; highlighted_ptr = 0; highlighted_division = i; highlighted_title = -1; list_highlighted = 1; new_cursor = HSEPARATE_CURSOR; return 1; } } } highlighted_division = -1; return 0; } int BC_ListBox::test_column_titles(int cursor_x, int cursor_y) { if( gui && column_titles && cursor_y >= 0 && cursor_y < get_title_h() && cursor_x >= 0 && cursor_x < gui->get_w() ) { for( int i=0; i= -xposition + get_column_offset(i) && (cursor_x < -xposition + get_column_offset(i + 1) || i == columns - 1) ) { highlighted_item = -1; highlighted_ptr = 0; highlighted_division = -1; highlighted_title = i; list_highlighted = 1; return 1; } } } highlighted_title = -1; return 0; } int BC_ListBox::test_expanders() { for( int i=0; ibutton_press_event() ) { current_operation = EXPAND_DN; draw_toggles(1); return 1; } } return 0 ; } int BC_ListBox::cursor_motion_event() { int redraw = 0, result = 0; int new_cursor = ARROW_CURSOR; selection_number = -1; switch( current_operation ) { case BUTTON_DN: // Button pressed and slid off button if( !cursor_inside() ) { current_operation = BUTTON_DOWN_SELECT; draw_button(1); result = 1; } break; case DRAG_DIVISION: { // int new_w = get_cursor_x() + // xposition - // get_column_offset(highlighted_division - 1); int difference = get_cursor_x() + xposition - drag_cursor_x; int new_w = drag_column_w + difference; new_cursor = HSEPARATE_CURSOR; if( column_width ) { column_width[highlighted_division - 1] = new_w; } else { default_column_width[highlighted_division - 1] = new_w; } column_width_boundaries(); // Force update of coords set_autoplacement(data, 0, 1); column_resize_event(); clamp_positions(); draw_items(0); update_scrollbars(1); result = 1; break; } case SELECT_RECT: { if( test_drag_scroll(get_cursor_x(), get_cursor_y()) ) set_scroll_repeat(); int old_x1 = MIN(rect_x1, rect_x2); int old_x2 = MAX(rect_x1, rect_x2); int old_y1 = MIN(rect_y1, rect_y2); int old_y2 = MAX(rect_y1, rect_y2); int new_rect_x2 = get_cursor_x(); int new_rect_y2 = get_cursor_y(); int x1 = MIN(rect_x1, new_rect_x2); int x2 = MAX(rect_x1, new_rect_x2); int y1 = MIN(rect_y1, new_rect_y2); int y2 = MAX(rect_y1, new_rect_y2); // Adjust rectangle coverage if( old_x1 != x1 || old_x2 != x2 || old_y1 != y1 || old_y2 != y2 ) { if( data ) { redraw = select_rectangle(data, x1, y1, x2, y2); } // hide rectangle if( !redraw ) { //printf("BC_ListBox::cursor_motion_event %d\n", __LINE__); draw_rectangle(0); } } rect_x2 = get_cursor_x(); rect_y2 = get_cursor_y(); if( redraw ) { clamp_positions(); draw_items(0); update_scrollbars(1); selection_changed(); //printf("BC_ListBox::cursor_motion_event %d\n", __LINE__); } else if( old_x1 != x1 || old_x2 != x2 || old_y1 != y1 || old_y2 != y2 ) { //printf("BC_ListBox::cursor_motion_event %d\n", __LINE__); draw_rectangle(1); } result = 1; break; } case SELECT: { int old_highlighted_item = highlighted_item; if( test_drag_scroll(get_cursor_x(), get_cursor_y()) ) set_scroll_repeat(); highlighted_item = selection_number = get_cursor_item(data, get_cursor_x(), get_cursor_y(), &highlighted_ptr); result = 1; // Deselect all items and select just the one we're over if( selection_number >= 0 && !allow_drag && ((!shift_down() && !ctrl_down()) || selection_mode == LISTBOX_SINGLE) ) { redraw = update_selection(data, selection_number); } else // Expand multiple selection if( selection_mode == LISTBOX_MULTIPLE && (shift_down() || ctrl_down()) ) { // Expand selected region in text mode centered around initial range if( (display_format == LISTBOX_TEXT || display_format == LISTBOX_ICON_LIST) && shift_down() ) { // Deselect everything. set_all_selected(data, 0); // Select just the items redraw = expand_selection(0, selection_number); } // Set the one item we're over to the selection value determined in // button_press_event. else { set_selected(data, selection_number, new_value); } } if( highlighted_item != old_highlighted_item ) { clamp_positions(); draw_items(0); update_scrollbars(1); //printf("BC_ListBox::cursor_motion_event %d %d\n", highlighted_item, old_highlighted_item); selection_changed(); } break; } case BUTTON_DOWN_SELECT: // Went back into button area if( cursor_inside() ) { current_operation = BUTTON_DN; draw_button(1); result = 1; } else // Went into item area if( gui ) { int cursor_x = 0, cursor_y = 0; translate_coordinates(top_level->event_win, gui->win, top_level->cursor_x, top_level->cursor_y, &cursor_x, &cursor_y); int old_highlighted_item = highlighted_item; highlighted_item = selection_number = get_cursor_item(data, cursor_x, cursor_y, &highlighted_ptr); if( highlighted_item != old_highlighted_item ) { update_selection(data, selection_number); draw_items(1); selection_changed(); } } break; case EXPAND_DN: { int redraw_toggles = 0; for( int i=0; icursor_motion_event( &redraw_toggles); } if( redraw_toggles ) { // Need to redraw items because of the alpha draw_items(1); } break; } case NO_OPERATION: { int cursor_x = get_cursor_x(), cursor_y = get_cursor_y(); if( gui && top_level->event_win == gui->win ) { int old_highlighted_title = highlighted_title; int old_list_highlighted = list_highlighted; int old_highlighted_item = highlighted_item; int redraw_titles = 0; int redraw_border = 0; int redraw_items = 0; int redraw_toggles = 0; result = 1; // Test if cursor moved over a title division test_column_divisions(cursor_x, cursor_y, new_cursor); // Test if cursor moved over a title if( highlighted_division < 0 ) { test_column_titles(cursor_x, cursor_y); } // Test if cursor moved over expander if( highlighted_division < 0 && highlighted_title < 0 && (display_format == LISTBOX_TEXT || display_format == LISTBOX_ICON_LIST) ) { for( int i=0; icursor_motion_event( &redraw_toggles); } //printf("BC_ListBox::cursor_motion_event %d\n", redraw_toggles); } // Test if cursor moved over an item if( highlighted_division < 0 && highlighted_title < 0 ) { highlighted_item = get_cursor_item(data, cursor_x, cursor_y, &highlighted_ptr); } // Clear title highlighting if moved over division if( old_highlighted_title != highlighted_title ) { redraw_titles = 1; } // Highlight list border if( old_list_highlighted != list_highlighted ) { redraw_border = 1; } // Moved out of item area if( old_highlighted_item != highlighted_item ) { if( !mouse_over_event(highlighted_item) ) redraw_items = 1; } //printf("BC_ListBox::cursor_motion_event 1 %d\n", highlighted_item); // Change cursor to title division adjustment reset_cursor(new_cursor); if( redraw_items ) { draw_items(0); } else { if( redraw_titles ) draw_titles(0); if( redraw_border ) draw_border(0); if( redraw_toggles ) draw_toggles(0); } if( redraw_items || redraw_titles || redraw_border || redraw_toggles ) { gui->flash(); gui->flush(); } } if( !result && list_highlighted ) { list_highlighted = 0; highlighted_item = -1; highlighted_ptr = 0; highlighted_title = -1; highlighted_division = -1; draw_items(1); result = 0; } break; } } return result; } int BC_ListBox::drag_start_event() { switch( current_operation ) { case SELECT: if( expander_active() ) break; if( gui && gui->is_event_win() && allow_drag ) { BC_ListBoxItem *item_return = 0; selection_number = get_cursor_item(data, top_level->cursor_x, top_level->cursor_y, &item_return); if( selection_number >= 0 ) { int cx, cy; get_abs_cursor(cx, cy); if( item_return->icon_vframe ) { drag_popup = new BC_DragWindow(this, item_return->icon_vframe, cx, cy); } else if( item_return->icon ) { drag_popup = new BC_DragWindow(this, item_return->icon, cx, cy); } else { drag_popup = new BC_DragWindow(this, drag_icon_vframe, cx, cy); } current_operation = DRAG_ITEM; // require shift down for scrolling if( allow_drag < 0 && shift_down() ) set_scroll_repeat(); return 1; } } break; case COLUMN_DN: if( gui && gui->is_event_win() && allow_drag_column ) { int cx, cy; get_abs_cursor(cx, cy); cx -= drag_column_icon_vframe->get_w() / 2, cy -= drag_column_icon_vframe->get_h() / 2; drag_popup = new BC_DragWindow(this, drag_column_icon_vframe, cx, cy); dragged_title = highlighted_title; current_operation = COLUMN_DRAG; draw_titles(1); return 1; } break; } return 0; } int BC_ListBox::drag_motion_event() { //printf("BC_ListBox::drag_motion_event 1 %d\n", current_operation); switch( current_operation ) { case DRAG_ITEM: { int redraw = 0; int new_highlighted_item = -1; BC_ListBoxItem *new_highlighted_ptr = 0; new_highlighted_item = get_cursor_item(data, top_level->cursor_x, top_level->cursor_y, &new_highlighted_ptr); if( new_highlighted_item != highlighted_item ) { redraw = 1; } // Always update highlighted value for drag_stop highlighted_item = new_highlighted_item; highlighted_ptr = new_highlighted_ptr; //printf("BC_ListBox::drag_motion_event 1 %p\n", highlighted_ptr); if( redraw ) { clamp_positions(); draw_items(0); update_scrollbars(1); } return drag_popup->cursor_motion_event(); } case COLUMN_DRAG: { int old_highlighted_title = highlighted_title; test_column_titles(get_cursor_x(), get_cursor_y()); if( old_highlighted_title != highlighted_title ) { draw_titles(1); } return drag_popup->cursor_motion_event(); } } return 0; } int BC_ListBox::drag_stop_event() { int result = 0; unset_scroll_repeat(); switch( current_operation ) { case DRAG_ITEM: // Inside window boundary if( top_level->cursor_x > 0 && top_level->cursor_x < gui->get_w() - drag_popup->get_w() / 2 && top_level->cursor_y > 0 && top_level->cursor_y < gui->get_h() - drag_popup->get_h() / 2 ) { // Move icon if( display_format == LISTBOX_ICONS || display_format == LISTBOX_ICONS_PACKED ) { reposition_item(data, selection_number, top_level->cursor_x - drag_popup->get_w() / 2 - LISTBOX_MARGIN - 2 + xposition, top_level->cursor_y - drag_popup->get_h() / 2 - LISTBOX_MARGIN - 2 + yposition); } else // Move rows if( process_drag ) { // Move selected items from data to temporary ArrayList *src_items = new ArrayList[columns]; move_selection(src_items, data); // Get destination int destination = highlighted_item = item_to_index(data, highlighted_ptr); // Insert items from temporary to data put_selection(data, src_items, destination); delete [] src_items; set_autoplacement(data, 0, 1); } draw_items(1); } else drag_popup->drag_failure_event(); result = 1; break; case COLUMN_DRAG: if( dragged_title != highlighted_title ) { if( highlighted_title >= 0 ) { if( !move_column_event() ) draw_titles(1); } else drag_popup->drag_failure_event(); } result = 1; } if( result ) { current_operation = NO_OPERATION; delete drag_popup; flush(); drag_popup = 0; new_value = 0; } return result; } BC_DragWindow* BC_ListBox::get_drag_popup() { return drag_popup; } int BC_ListBox::translation_event() { if( is_popup && gui ) { int new_x = gui->get_x() + (top_level->last_translate_x - top_level->prev_x - BC_DisplayInfo::get_left_border()); int new_y = gui->get_y() + (top_level->last_translate_y - top_level->prev_y - BC_DisplayInfo::get_top_border()); gui->reposition_window(new_x, new_y); } return 0; } int BC_ListBox::reposition_window(int x, int y, int w, int h, int flush) { if( w != -1 ) { popup_w = w; if( h != -1 ) popup_h = h; //printf("BC_ListBox::reposition_window %d %d\n", popup_w, popup_h); if( !is_popup ) { if( xscrollbar ) xscrollbar->reposition_window(get_xscroll_x(), get_xscroll_y(), get_xscroll_width()); if( yscrollbar ) yscrollbar->reposition_window(get_yscroll_x(), get_yscroll_y(), get_yscroll_height()); } } BC_WindowBase::reposition_window(x, y, w, h); draw_button(0); draw_items(flush); return 0; } int BC_ListBox::deactivate() { hide_tooltip(); // printf("BC_ListBox::deactivate %d this=%p gui=%p active=%d\n", // __LINE__, this, gui, active); if( active ) { if( is_popup ) { //printf("BC_ListBox::deactivate %d this=%p gui=%p\n", __LINE__, this, gui); if( gui ) { delete gui; gui = 0; flush(); } xscrollbar = 0; yscrollbar = 0; highlighted_item = -1; highlighted_ptr = 0; //sleep(1); } //printf("BC_ListBox::deactivate %d this=%p\n", __LINE__, this); active = 0; top_level->active_subwindow = 0; } return 0; } int BC_ListBox::activate(int take_focus) { if( active ) return 0; active = 1; if( take_focus ) set_active_subwindow(this); button_releases = 0; if( !is_popup || gui ) return 0; int wx = get_x(), wy = get_y() + get_h(); if( justify == LISTBOX_RIGHT ) wx += get_w() - popup_w; Window xwin; int abs_x, abs_y; XTranslateCoordinates(top_level->display, parent_window->win, top_level->rootwin, wx, wy, &abs_x, &abs_y, &xwin); if( x <= 0 ) x = 2; if( y <= 0 ) y = 2; return activate(abs_x, abs_y); } int BC_ListBox::activate(int x, int y, int w, int h) { if( !is_popup || gui ) return 0; active = 1; if( w != -1 ) popup_w = w; if( h != -1 ) popup_h = h; reset_query(); if( y + popup_h > top_level->get_root_h(0) ) y -= get_h() + popup_h; add_subwindow(gui = new BC_Popup(this, x, y, popup_w, popup_h, -1, 0, 0)); draw_items(1); gui->show_window(1); return 0; } int BC_ListBox::is_active() { return active; } int BC_ListBox::expander_active() { for( int i=0; ivalue ) return 1; } return 0 ; } int BC_ListBox::keypress_event() { if( !active ) return 0; //printf("BC_ListBox::keypress_event %d\n", __LINE__); int result = 0, redraw = 0; int view_items = last_in_view - first_in_view + 1; if( view_items <= 0 ) view_items = view_h / get_text_height(MEDIUMFONT); int new_item = -1, new_selection = -1; switch( top_level->get_keypress() ) { case ESC: case RETURN: top_level->deactivate(); // If user is manipulating popup with keyboard, don't pass on event. if( is_popup ) { //printf("BC_ListBox::keypress_event %d %p\n", __LINE__, get_selection(0, 0)); if( top_level->get_keypress() == RETURN ) handle_event(); //printf("BC_ListBox::keypress_event %d %p\n", __LINE__, get_selection(0, 0)); result = 1; } else result = 0; break; case UP: new_selection = new_item = select_previous(0); //printf("BC_ListBox::keypress_event 1 %d\n", new_item); if( new_item >= 0 ) { center_selection(new_item); redraw = 1; } reset_query(); result = 1; break; case DOWN: new_selection = new_item = select_next(0); if( new_item >= 0 ) { center_selection(new_item); redraw = 1; } reset_query(); result = 1; break; case PGUP: new_selection = new_item = select_previous(view_items - 1); if( new_item >= 0 ) { center_selection(new_item); redraw = 1; } reset_query(); result = 1; break; case PGDN: new_selection = new_item = select_next(view_items - 1); if( new_item >= 0 ) { center_selection(new_item); redraw = 1; } reset_query(); result = 1; break; case LEFT: xposition -= 10; redraw = 1; result = 1; break; case RIGHT: xposition += 10; redraw = 1; result = 1; break; case DELETE: case HOME: case END: result = 0; break; default: if( show_query && !ctrl_down() ) { int query_len = strlen(query); if( query_len < (int)sizeof(query)-1 && top_level->get_keypress() > 30 && top_level->get_keypress() < 127 ) { query[query_len++] = top_level->get_keypress(); query[query_len] = 0; new_selection = query_list(); } else if( top_level->get_keypress() == BACKSPACE ) { if( query_len > 0 ) query[--query_len] = 0; new_selection = query_list(); } if( query_len > 0 ) show_tooltip(query); else hide_tooltip(); redraw = 1; result = 1; } break; } if( redraw ) { clamp_positions(); draw_items(0); update_scrollbars(1); } //printf("BC_ListBox::keypress_event %d new_selection=%d\n", __LINE__, new_selection); if( new_selection >= 0 && !is_suggestions ) { //printf("BC_ListBox::keypress_event %d\n", __LINE__); selection_changed(); //printf("BC_ListBox::keypress_event %d\n", __LINE__); } return result; } BC_Pixmap* BC_ListBox::get_bg_surface() { return bg_surface; } void BC_ListBox::draw_background() { if( !bg_draw ) return; bg_draw = 0; // White background pixmap set_color(top_level->get_resources()->listbox_inactive); draw_box(0, 0, bg_surface->get_w(), bg_surface->get_h(), bg_surface); // Optional heroine pixmap if( bg_pixmap ) bg_surface->draw_pixmap(bg_pixmap, bg_surface->get_w() - top_level->get_resources()->listbox_bg->get_w(), 0); } void BC_ListBox::clear_listbox(int x, int y, int w, int h) { gui->draw_pixmap(bg_surface, x, y, w, h, x, y - title_h); } void BC_ListBox::update_format(int display_format, int redraw) { this->display_format = display_format; packed_icons = display_format == LISTBOX_ICONS_PACKED ? 1 : 0; xposition = 0; yposition = 0; if( redraw && gui ) draw_items(1, 1); } int BC_ListBox::get_format() { return display_format; } int BC_ListBox::draw_items(int flush, int draw_bg) { if( gui ) { BC_Resources *resources = get_resources(); //dump(data, columns); // Calculate items width calculate_item_coords(); // Create and destroy scrollbars as needed get_scrollbars(); if( bg_draw ) this->bg_draw = 1; draw_background(); first_in_view = -1; last_in_view = 0; // Icon display if( display_format == LISTBOX_ICONS || display_format == LISTBOX_ICONS_PACKED ) { clear_listbox(2, 2 + title_h, view_w, view_h); set_font(MEDIUMFONT); for( int i=0; i= -get_item_w(item) && get_item_x(item) < view_w && get_item_y(item) >= -get_item_h(item) + title_h && get_item_y(item) < view_h + title_h ) { item->set_in_view(1); if( first_in_view < 0 ) first_in_view = i; last_in_view = i; int item_color = get_item_highlight(data, 0, i); int icon_x, icon_y, icon_w, icon_h; int text_x, text_y, text_w, text_h; // Draw highlights get_icon_mask(item, icon_x, icon_y, icon_w, icon_h); get_text_mask(item, text_x, text_y, text_w, text_h); if( item_color != resources->listbox_inactive ) { gui->set_color(BLACK); gui->draw_rectangle(icon_x, icon_y, icon_w, icon_h); gui->set_color(item_color); gui->draw_box(icon_x + 1, icon_y + 1, icon_w - 2, icon_h - 2); gui->set_color(BLACK); gui->draw_rectangle(text_x, text_y, text_w, text_h); gui->set_color(item_color); gui->draw_box(text_x + 1, text_y + 1, text_w - 2, text_h - 2); if( icon_position == ICON_LEFT ) gui->draw_box(text_x - 1, text_y + 1, 2, text_h - 2); else if( icon_position == ICON_TOP ) gui->draw_line(text_x + 1, text_y, text_x + icon_w - 2, text_y); if( text_x + text_w < icon_x + icon_w ) { gui->set_color(BLACK); gui->draw_line(text_x + text_w, icon_y + icon_h, icon_x + icon_w, icon_y + icon_h); } } // Draw icons gui->set_color(get_item_color(data, 0, i)); VFrame *vicon = item->get_vicon_frame(); if( vicon ) gui->pixmap->draw_vframe(vicon, icon_x, icon_y); else if( item->icon ) gui->pixmap->draw_pixmap(item->icon, icon_x, icon_y); char item_text[BCTEXTLEN]; if( display_format == LISTBOX_ICONS_PACKED ) gui->truncate_text(item_text, item->text, text_w); else strcpy(item_text, item->text); gui->draw_text(text_x, text_y + get_baseline(item), item_text); } else item->set_in_view(0); } } // Text display else { // Draw one column at a time so text overruns don't go into the next column // clear column backgrounds int current_toggle = 0; for( int j=0; j current_toggle ) { expanders.remove_object(); } } // draw user images if available draw_images(); // Draw titles on top of rows for superposition effect draw_titles(0); // Clear garbage from bottom right corner if( xscrollbar && yscrollbar && is_popup ) { gui->draw_top_background(parent_window, popup_w - get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w(), popup_h - get_resources()->hscroll_data[SCROLL_HANDLE_UP]->get_h(), get_resources()->vscroll_data[SCROLL_HANDLE_UP]->get_w(), get_resources()->hscroll_data[SCROLL_HANDLE_UP]->get_h()); } // Draw borders draw_border(0); if( current_operation == SELECT_RECT ) draw_rectangle(0); gui->flash(flush); } return 0; } void BC_ListBox::draw_text_recursive(ArrayList *data, int column, int indent, int *current_toggle) { if( !data ) return; BC_Resources *resources = get_resources(); set_font(MEDIUMFONT); int subindent = 0; // Search for a branch and make room for toggle if there is one if( column == 0 ) { for( int i=0; iget_sublist() ) { subindent = BC_WindowBase::get_resources()->listbox_expand[0]->get_w(); break; } } } row_height = row_ascent = row_descent = 0; for( int i=0; i row_height ) row_height = ht; int bl = get_baseline(item); if( bl > row_ascent ) row_ascent = bl; int dt = ht - bl; if( dt > row_descent ) row_descent = dt; } for( int i=0; i= -get_item_h(item) + title_h && get_item_y(item) < view_h + title_h ) { int row_color = get_item_highlight(data, 0, i); int x, y, w, h, column_width; get_text_mask(item, x, y, w, h); column_width = get_column_width(column, 1); if( x + column_width > view_w + LISTBOX_BORDER * 2 ) column_width = view_w + LISTBOX_BORDER * 2 - x; if( row_color != resources->listbox_inactive ) { gui->set_color(row_color); gui->draw_box(x, y, column_width, h); gui->set_color(BLACK); int xx = x + column_width-1; gui->draw_line(x, y, xx, y); int hh = h; if( display_format == LISTBOX_ICON_LIST ) { int ih = get_icon_h(item); if( ih > hh ) hh = ih; } int yy = y + hh-1; gui->draw_line(x, yy, xx, yy); } gui->set_color(get_item_color(data, column, i)); if( column == 0 && display_format == LISTBOX_ICON_LIST ) { int ix = get_icon_x(item), iy = get_icon_y(item); VFrame *vicon = item->get_vicon_frame(); if( vicon ) { gui->pixmap->draw_vframe(vicon, ix, iy); x += vicon->get_w() + ICON_MARGIN; } else if( item->icon ) { gui->pixmap->draw_pixmap(item->icon, ix, iy); x += item->icon->get_w() + ICON_MARGIN; } } // Indent only applies to first column gui->draw_text( x + LISTBOX_BORDER + LISTBOX_MARGIN + (column == 0 ? indent + subindent : 0), y + get_baseline(item), item->text); item->set_in_view(1); if( !indent ) { if( first_in_view < 0 ) first_in_view = i; last_in_view = i; } // Update expander if( column == 0 && item->get_sublist() && item->get_columns() ) { // Create new expander if( *current_toggle >= expanders.total ) { BC_ListBoxToggle *toggle = new BC_ListBoxToggle(this, item, x + LISTBOX_BORDER + LISTBOX_MARGIN + indent, y); toggle->draw(0); expanders.append(toggle); } // Reposition existing expander else { BC_ListBoxToggle *toggle = expanders.values[*current_toggle]; //printf("BC_ListBox::draw_text_recursive 1 %d\n", *current_toggle); toggle->update(item, x + LISTBOX_BORDER + LISTBOX_MARGIN + indent, y, 0); } (*current_toggle)++; } } else item->set_in_view(0); // Descend into sublist if( first_item->sublist_active() ) { draw_text_recursive(first_item->get_sublist(), column, indent + LISTBOX_INDENT, current_toggle); } } } int BC_ListBox::draw_border(int flash) { BC_Resources *resources = top_level->get_resources(); gui->draw_3d_border(0, 0, view_w + LISTBOX_BORDER * 2, view_h + title_h + LISTBOX_BORDER * 2, resources->listbox_border1, list_highlighted ? resources->listbox_border2_hi : resources->listbox_border2, list_highlighted ? resources->listbox_border3_hi : resources->listbox_border3, resources->listbox_border4); if( flash ) { gui->flash(); gui->flush(); } return 0; } void BC_ListBox::draw_title(int number) { // Column title background int image_number = 0; if( number == highlighted_title ) { image_number = 1; if( current_operation == COLUMN_DN ) image_number = 2; } gui->draw_3segmenth(get_column_offset(number) - xposition + LISTBOX_BORDER, LISTBOX_BORDER, get_column_width(number, 1) + get_resources()->listbox_title_overlap, column_bg[image_number]); int title_x = -xposition + get_column_offset(number) + LISTBOX_MARGIN + LISTBOX_BORDER; title_x += get_resources()->listbox_title_margin; gui->set_color(get_resources()->listbox_title_color); gui->draw_text(title_x, LISTBOX_MARGIN + LISTBOX_BORDER + get_text_ascent(MEDIUMFONT), column_titles[number]); // Column sort order if( number == sort_column ) { BC_Pixmap *src = sort_order == SORT_ASCENDING ? column_sort_dn : column_sort_up; // int column_offset = get_column_offset(number) - xposition + LISTBOX_BORDER; // int column_width = get_column_width(number, 1); // int toggle_x = column_offset + column_width - LISTBOX_BORDER; // if( toggle_x > items_w ) toggle_x = items_w; // toggle_x -= 5 + src->get_w(); int x = title_x + get_text_width(MEDIUMFONT, column_titles[number]) + LISTBOX_MARGIN; gui->draw_pixmap(src, x, title_h / 2 - src->get_h() / 2 + LISTBOX_BORDER); } } int BC_ListBox::draw_titles(int flash) { if( column_titles && (display_format == LISTBOX_TEXT || display_format == LISTBOX_ICON_LIST) ) { //printf("BC_ListBox::draw_titles 1 %d\n", highlighted_title); for( int i=0; i= 0 ) draw_title(highlighted_title); draw_border(0); } if( flash ) { gui->flash(); } return 0; } void BC_ListBox::draw_toggles(int flash) { for( int i=0; idraw(0); //printf("BC_ListBox::draw_toggles 1 %d\n", flash); if( flash && expanders.total ) { gui->flash(); } } int BC_ListBox::draw_rectangle(int flash) { int x1 = MIN(rect_x1, rect_x2); int x2 = MAX(rect_x1, rect_x2); int y1 = MIN(rect_y1, rect_y2); int y2 = MAX(rect_y1, rect_y2); if( x1 == x2 || y1 == y2 ) return 0; gui->set_inverse(); gui->set_color(WHITE); gui->draw_rectangle(x1, y1, x2 - x1, y2 - y1); gui->set_opaque(); if( flash ) { gui->flash(); } return 0; } void BC_ListBox::dump(ArrayList *data, int columns, int indent, int master_column) { if( !indent ) { printf("BC_ListBox::dump 1\n"); } for( int i=0; iget_text_x(), item->get_text_y(), item->autoplace_text, item->get_text()); } printf("\n"); if( data[master_column].values[i]->get_sublist() ) { dump(data[master_column].values[i]->get_sublist(), data[master_column].values[i]->get_columns(), indent + 4, master_column); } } }