/** \file treeview.c * Basic treeview functionality for dropbox and listbox */ /* XTrkCad - Model Railroad CAD * * Copyright 2016 Martin Fischer * * 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., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. * * */ #include #include #include #include #define GTK_DISABLE_SINGLE_INCLUDES #define GDK_DISABLE_DEPRECATED #define GTK_DISABLE_DEPRECATED #define GSEAL_ENABLE #include #include #include #include "gtkint.h" //#include "i18n.h" #define ROW_HEIGHT (15) /** * Get the count of columns in list * * \param b IN widget * \returns count row */ int wTreeViewGetCount(wList_p b) { return b->count; } /** * Clear the list * * \param b IN widget */ void wTreeViewClear(wList_p b) { assert(b != NULL); wlibListStoreClear(b->listStore); } /** * Get the user data for a list element * * \param b IN widget * \param inx IN row * \returns the user data for the specified row */ void * wTreeViewGetItemContext(wList_p b, int row) { wListItem_p id_p; id_p = wlibListItemGet(b->listStore, row, NULL); if (id_p) { return id_p->itemData; } else { return NULL; } } /** * Returns the current selected list entry. * If if '-1' then no entry is selected. * * \param b IN widget * \returns row of selected entry or -1 if none is selected */ wIndex_t wListGetIndex( wList_p b) { assert(b!=NULL); return b->last; } /** * Set an entry in the list to selected. * * \param b IN widget * \param index IN entry if -1 the current selection is cleared * */ void wlibTreeViewSetSelected(wList_p b, int index) { GtkTreeSelection *sel; GtkTreeIter iter; wListItem_p id_p; sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(b->treeView)); if (gtk_tree_selection_count_selected_rows(sel)) { int inx; gtk_tree_selection_unselect_all(sel); // and synchronize the internal data structures wTreeViewGetCount(b); for (inx=0; inxcount; inx++) { id_p = wlibListItemGet(b->listStore, inx, NULL); id_p->selected = FALSE; } } if (index != -1) { gint childs; childs = gtk_tree_model_iter_n_children (GTK_TREE_MODEL(b->listStore), NULL ); if(index < childs) { gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(b->listStore), &iter, NULL, index); gtk_tree_selection_select_iter(sel, &iter); id_p = wlibListItemGet(b->listStore, index, NULL); if (id_p) { id_p->selected = TRUE; } } } } /** * Create a new tree view for a list store. Titles are enabled optionally. * * \param ls IN list store * \param showTitles IN add column titles * \param multiSelection IN enable selecting multiple rows * \returns */ GtkWidget * wlibNewTreeView(GtkListStore *ls, int showTitles, int multiSelection) { GtkWidget *treeView; GtkTreeSelection *sel; assert(ls != NULL); /* create and configure the tree view */ treeView = gtk_tree_view_new_with_model(GTK_TREE_MODEL(ls)); gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeView), showTitles); /* set up selection handling */ sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeView)); gtk_tree_selection_set_mode(sel, (multiSelection)?GTK_SELECTION_MULTIPLE:GTK_SELECTION_BROWSE); return (treeView); } static int changeListColumnWidth( GtkTreeViewColumn * column, void * width) { //wList_p bl = (wList_p)data; //if (bl->recursion) //return 0; //if ( col >= 0 && col < bl->colCnt ) //bl->colWidths[col] = width; return 0; } /** * Create and initialize a column in treeview. Initially all columns are * invisible. Visibility is set when values are added to the specific * column * * \param tv IN treeview * \param renderer IN renderer to use * \param attribute IN attribute for column * \param value IN value to set */ static void wlibAddColumn(GtkWidget *tv, int visibility, GtkCellRenderer *renderer, char *attribute, int value) { GtkTreeViewColumn *column; column = gtk_tree_view_column_new(); gtk_tree_view_column_pack_start(column, renderer, TRUE); gtk_tree_view_column_add_attribute(column, renderer, attribute, value); gtk_tree_view_column_set_visible(column, visibility); gtk_tree_view_column_set_resizable(column, TRUE); gtk_tree_view_append_column(GTK_TREE_VIEW(tv), column); // g_signal_connect( column, "notify::width", G_CALLBACK(changeListColumnWidth), tv ); } /** * Add a number of columns to the text view. This includes the bitmap * columns and a given number of text columns. * * \param tv IN tree view * \param count IN number of text columns * \returns number of columns */ int wlibTreeViewAddColumns(GtkWidget *tv, int count) { GtkCellRenderer *renderer; int i; assert(tv != NULL); renderer = gtk_cell_renderer_pixbuf_new(); /* first visible column is used for bitmaps */ wlibAddColumn(tv, FALSE, renderer, "pixbuf", LISTCOL_BITMAP); renderer = gtk_cell_renderer_text_new(); /* add renderers to all columns */ for (i = 0; i < count; i++) { wlibAddColumn(tv, TRUE, renderer, "text", i + LISTCOL_TEXT); } return i; } /** * Add the titles to all columns in a tree view. * * \param tv IN treeview * \param titles IN titles * \returns number of titles set */ int wlibAddColumnTitles(GtkWidget *tv, const char **titles) { int i = 0; assert(tv != NULL); if (titles) { while (*titles) { GtkTreeViewColumn *column; column = gtk_tree_view_get_column(GTK_TREE_VIEW(tv), i + 1); if (column) { gtk_tree_view_column_set_title(column, titles[ i ]); i++; } else { break; } } } return i; } /** * Add text to the text columns of the tree view and update the context * information * * \param tv IN treeview * \param cols IN number of cols to change * \param label IN tab separated string of values * \param userData IN additional context information * \returns */ int wlibTreeViewAddData(GtkWidget *tv, int cols, char *label, GdkPixbuf *pixbuf, wListItem_p userData) { GtkListStore *listStore = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW( tv))); wlibListStoreAddData(listStore, pixbuf, cols, userData); if (pixbuf) { GtkTreeViewColumn *column; // first column in list store has pixbuf column = gtk_tree_view_get_column(GTK_TREE_VIEW(tv), 0); gtk_tree_view_column_set_visible(column, TRUE); } return 0; } /** * Add a row to the tree view. As necessary the adjustment is update in * order to make sure, that the list box is fully visible or has a * scrollbar. * * \param b IN the list box * \param label IN the text labels * \param bm IN bitmap to show at start * \param id_p IN user data */ void wlibTreeViewAddRow(wList_p b, char *label, wIcon_p bm, wListItem_p id_p) { GtkAdjustment *adj; GdkPixbuf *pixbuf = NULL; if (bm) { pixbuf = wlibMakePixbuf(bm); } wlibTreeViewAddData(b->treeView, b->colCnt, (char *)label, pixbuf, id_p); adj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(b->widget)); if (gtk_adjustment_get_upper(adj) < gtk_adjustment_get_step_increment(adj) * (b->count+1)) { gtk_adjustment_set_upper(adj, gtk_adjustment_get_upper(adj) + gtk_adjustment_get_step_increment(adj)); gtk_adjustment_changed(adj); } b->last = gtk_tree_model_iter_n_children(gtk_tree_view_get_model(GTK_TREE_VIEW( b->treeView)), NULL); } /** * Function for handling a selection change. The internal data structure * for the changed row is updated. If a handler function for the list * is given, the data for the row are retrieved and passed to that * function. This is used to update other fields in a dialog (see Price * List for an example). * * \param selection IN current selection * \param model IN * \param path IN * \param path_currently_selected IN * \param data IN the list widget */ gboolean changeSelection(GtkTreeSelection *selection, GtkTreeModel *model, GtkTreePath *path, gboolean path_currently_selected, gpointer data) { GtkTreeIter iter; GValue value = { 0 }; wListItem_p id_p = NULL; wList_p bl = (wList_p)data; int row; char *text; text = gtk_tree_path_to_string(path); row = atoi(text); g_free(text); gtk_tree_model_get_iter(model, &iter, path); gtk_tree_model_get_value(model, &iter, LISTCOL_DATA, &value); id_p = g_value_get_pointer(&value); id_p->selected = !path_currently_selected; if (id_p->selected) { bl->last = row; if (bl->valueP) { *bl->valueP = row; } if (bl->action) { bl->action(row, id_p->label, 1, bl->data, id_p->itemData); } } return TRUE; } /** * Create a multi column list box. * * \param parent IN parent window * \param x IN X-position * \param y IN Y-position * \param helpStr IN Help string * \param labelStr IN Label * \param option IN Options * \param number IN Number of displayed entries * \param width IN Width of list * \param colCnt IN Number of columns * \param colWidths IN Width of columns * \param colRightJust IN justification of columns * \param colTitles IN array of titles for columns * \param valueP IN Selected index * \param action IN Callback * \param data IN Context * \returns created list box */ wList_p wListCreate( wWin_p parent, wPos_t x, wPos_t y, const char * helpStr, const char * labelStr, long option, long number, wPos_t width, int colCnt, wPos_t * colWidths, wBool_t * colRightJust, const char ** colTitles, long *valueP, wListCallBack_p action, void *data) { GtkTreeSelection *sel; wList_p bl; static wPos_t zeroPos = 0; assert(width != 0); bl = (wList_p)wlibAlloc(parent, B_LIST, x, y, labelStr, sizeof *bl, data); bl->option = option; bl->number = number; bl->count = 0; bl->last = -1; bl->valueP = valueP; bl->action = action; bl->listX = bl->realX; if (colCnt <= 0) { colCnt = 1; colWidths = &zeroPos; } bl->colCnt = colCnt; bl->colWidths = (wPos_t*)malloc(colCnt * sizeof *(wPos_t*)0); memcpy(bl->colWidths, colWidths, colCnt * sizeof *(wPos_t*)0); /* create the data structure for data */ bl->listStore = wlibNewListStore(colCnt); /* create the widget for the list store */ bl->treeView = wlibNewTreeView(bl->listStore, colTitles != NULL, option & BL_MANY); sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(bl->treeView)); gtk_tree_selection_set_select_function(sel, changeSelection, bl, NULL); wlibTreeViewAddColumns(bl->treeView, colCnt); wlibAddColumnTitles(bl->treeView, colTitles); wlibComputePos((wControl_p)bl); bl->widget = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(bl->widget), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(bl->widget), bl->treeView); gtk_widget_set_size_request(bl->widget, width, (number+1)*ROW_HEIGHT); /// g_signal_connect( GTK_OBJECT(bl->list), "resize_column", G_CALLBACK(changeListColumnWidth), bl ); gtk_widget_show_all(bl->widget); gtk_fixed_put(GTK_FIXED(parent->widget), bl->widget, bl->realX, bl->realY); wlibControlGetSize((wControl_p)bl); if (labelStr) { bl->labelW = wlibAddLabel((wControl_p)bl, labelStr); } wlibAddButton((wControl_p)bl); wlibAddHelpString(bl->widget, helpStr); return bl; }