static void ListBox_activate(ListBox *self) { signed selection = -1; if(auto position = self->selection()) selection = position(); self->listBox->selection = selection; if(self->onActivate) self->onActivate(); } static void ListBox_change(ListBox *self) { signed selection = -1; if(auto position = self->selection()) selection = position(); if(selection == self->listBox->selection) return; self->listBox->selection = selection; if(self->onChange) self->onChange(); } static void ListBox_tick(GtkCellRendererToggle *cell, gchar *path_string, ListBox *self) { unsigned index = strunsigned(path_string); self->setChecked(index, !self->checked(index)); if(self->onTick) self->onTick(index); } void ListBox::create(Window &parent, unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { listBox->selection = -1; object->widget = gtk_scrolled_window_new(0, 0); widget->parent = &parent; gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(object->widget), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(object->widget), GTK_SHADOW_ETCHED_IN); gtk_widget_set_size_request(object->widget, width, height); lstring list; list.split("\t", string("\t", text)); GType *v = (GType*)malloc(list.size() * sizeof(GType)); for(unsigned i = 0; i < list.size(); i++) v[i] = (i == 0 ? G_TYPE_BOOLEAN : G_TYPE_STRING); listBox->store = gtk_list_store_newv(list.size(), v); free(v); object->subWidget = gtk_tree_view_new_with_model(GTK_TREE_MODEL(listBox->store)); gtk_container_add(GTK_CONTAINER(object->widget), object->subWidget); g_object_unref(G_OBJECT(listBox->store)); for(unsigned i = 0; i < list.size(); i++) { if(i == 0) { listBox->column[i].renderer = gtk_cell_renderer_toggle_new(); listBox->column[i].column = gtk_tree_view_column_new_with_attributes( "", listBox->column[i].renderer, "active", i, (void*)0 ); gtk_tree_view_column_set_resizable(listBox->column[i].column, false); gtk_tree_view_column_set_visible(listBox->column[i].column, listBox->checkable); g_signal_connect(listBox->column[i].renderer, "toggled", G_CALLBACK(ListBox_tick), (gpointer)this); } else { listBox->column[i].renderer = gtk_cell_renderer_text_new(); listBox->column[i].column = gtk_tree_view_column_new_with_attributes( "", listBox->column[i].renderer, "text", i, (void*)0 ); gtk_tree_view_column_set_resizable(listBox->column[i].column, true); } listBox->column[i].label = gtk_label_new(list[i]); gtk_tree_view_column_set_widget(GTK_TREE_VIEW_COLUMN(listBox->column[i].column), listBox->column[i].label); gtk_tree_view_append_column(GTK_TREE_VIEW(object->subWidget), listBox->column[i].column); gtk_widget_show(listBox->column[i].label); } gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(object->subWidget), false); gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(object->subWidget), list.size() >= 3); //>= 2 + one for the checkbox column gtk_tree_view_set_search_column(GTK_TREE_VIEW(object->subWidget), 1); g_signal_connect_swapped(G_OBJECT(object->subWidget), "cursor-changed", G_CALLBACK(ListBox_change), (gpointer)this); g_signal_connect_swapped(G_OBJECT(object->subWidget), "row-activated", G_CALLBACK(ListBox_activate), (gpointer)this); if(parent.window->defaultFont) setFont(*parent.window->defaultFont); gtk_fixed_put(GTK_FIXED(parent.object->formContainer), object->widget, x, y); gtk_widget_show(object->subWidget); gtk_widget_show(object->widget); } void ListBox::setFocused() { gtk_widget_grab_focus(object->subWidget); } void ListBox::setHeaderVisible(bool visible) { gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(object->subWidget), visible); } void ListBox::setCheckable(bool checkable) { listBox->checkable = checkable; if(object->subWidget) gtk_tree_view_column_set_visible(listBox->column[0].column, checkable); } void ListBox::setFont(Font &font) { Widget::setFont(font); unsigned columns = 1; while(true) { if(gtk_tree_view_get_column(GTK_TREE_VIEW(object->subWidget), columns) == 0) break; columns++; } for(unsigned i = 0; i < columns; i++) { gtk_widget_modify_font(listBox->column[i].label, font.font->font); } } void ListBox::reset() { listBox->selection = -1; gtk_list_store_clear(GTK_LIST_STORE(listBox->store)); gtk_tree_view_set_model(GTK_TREE_VIEW(object->subWidget), GTK_TREE_MODEL(listBox->store)); //reset gtk_scrolled_window scrollbar position to 0,0 (top-left), as ListBox is now empty gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(object->widget), 0); gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(object->widget), 0); } void ListBox::resizeColumnsToContent() { gtk_tree_view_columns_autosize(GTK_TREE_VIEW(object->subWidget)); } void ListBox::addItem(const string &text) { lstring list; list.split("\t", text); GtkTreeIter iter; gtk_list_store_append(listBox->store, &iter); unsigned index = 1; foreach(item, list) gtk_list_store_set(listBox->store, &iter, index++, (const char*)item, -1); } void ListBox::setItem(unsigned row, const string &text) { GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(object->subWidget)); GtkTreeIter iter; for(unsigned i = 0; i <= row; i++) { if(i == 0) gtk_tree_model_get_iter_first(model, &iter); else gtk_tree_model_iter_next(model, &iter); } lstring list; list.split("\t", text); unsigned index = 1; foreach(item, list) gtk_list_store_set(listBox->store, &iter, index++, (const char*)item, -1); } bool ListBox::checked(unsigned row) { GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(object->subWidget)); GtkTreePath *path = gtk_tree_path_new_from_string(string(row)); GtkTreeIter iter; bool state; gtk_tree_model_get_iter(model, &iter, path); gtk_tree_model_get(model, &iter, 0, &state, -1); gtk_tree_path_free(path); return state; } void ListBox::setChecked(unsigned row, bool checked) { GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(object->subWidget)); GtkTreePath *path = gtk_tree_path_new_from_string(string(row)); GtkTreeIter iter; gtk_tree_model_get_iter(model, &iter, path); gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, checked, -1); gtk_tree_path_free(path); } optional ListBox::selection() { GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(object->subWidget)); GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(object->subWidget)); GtkTreeIter iter; if(gtk_tree_model_get_iter_first(model, &iter) == false) return { false, 0 }; if(gtk_tree_selection_iter_is_selected(selection, &iter) == true) return { true, 0 }; for(unsigned i = 1;; i++) { if(gtk_tree_model_iter_next(model, &iter) == false) return { false, 0 }; if(gtk_tree_selection_iter_is_selected(selection, &iter) == true) return { true, i }; } return { false, 0 }; } void ListBox::setSelection(unsigned row) { signed current = -1; if(auto position = selection()) current = position(); GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(object->subWidget)); GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(object->subWidget)); gtk_tree_selection_unselect_all(selection); GtkTreeIter iter; if(gtk_tree_model_get_iter_first(model, &iter) == false) return; if(row == 0) { gtk_tree_selection_select_iter(selection, &iter); return; } for(unsigned i = 1;; i++) { if(gtk_tree_model_iter_next(model, &iter) == false) return; if(row == i) { gtk_tree_selection_select_iter(selection, &iter); return; } } } ListBox::ListBox() { listBox = new ListBox::Data; listBox->checkable = false; }