/* * Copyright (C) 2012 - Juan Ferrer Toribio * * 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 3 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, see . */ #include "vn-grid.h" G_DEFINE_TYPE (VnGrid, vn_grid, GTK_TYPE_TREE_VIEW); /** * vn_grid_new: * * Creates a new grid * * Return value: #VnGrid created. **/ VnGrid * vn_grid_new () { return g_object_new (VN_TYPE_GRID, "rules-hint", TRUE, NULL); } /** * vn_grid_new_with_iterator: * @iterator: #DbIterator where the grid will be created * * Creates a new grid into a iterator * * Return value: #VnGrid created. **/ VnGrid * vn_grid_new_with_iterator (DbIterator * iterator) { VnGrid * obj = vn_grid_new (); g_object_set (obj, "iterator", iterator, NULL); return obj; } //+++++++++++++++++++++++++++++++++++++++++++++++++++ Private static void vn_grid_on_iter_changed (DbIterator * iterator, GtkTreeView * obj); static void vn_grid_on_cursor_changed (GtkTreeView * obj, DbIterator * iterator) { GtkTreeIter iter; GtkTreeSelection * selection = gtk_tree_view_get_selection (obj); if (selection && gtk_tree_selection_get_selected (selection, NULL, &iter)) { DbIter dbiter; vn_gtk_tree_iter_to_db_iter (&iter, &dbiter); g_signal_handlers_block_by_func (iterator, vn_grid_on_iter_changed, obj); db_iterator_move_iter (iterator, &dbiter); g_signal_handlers_unblock_by_func (iterator, vn_grid_on_iter_changed, obj); } } static void vn_grid_on_iter_changed (DbIterator * iterator, GtkTreeView * obj) { DbIter dbiter; GtkTreeSelection * selection = gtk_tree_view_get_selection (obj); if (db_iterator_get_iter (iterator, &dbiter) && gtk_tree_view_get_model (obj)) { GtkTreeIter iter; GtkTreePath * path; vn_gtk_tree_iter_from_db_iter (&iter, &dbiter); g_signal_handlers_block_by_func (obj, vn_grid_on_cursor_changed, iterator); gtk_tree_selection_select_iter (selection, &iter); g_signal_handlers_unblock_by_func (obj, vn_grid_on_cursor_changed, iterator); path = gtk_tree_model_get_path ( gtk_tree_view_get_model (obj), &iter); gtk_tree_view_scroll_to_cell (obj, path, NULL, FALSE, 0, 0); gtk_tree_path_free (path); } else if (selection) gtk_tree_selection_unselect_all (selection); } static void vn_grid_on_model_changed (VnGrid * obj, GParamSpec * spec, gpointer data) { GList * n, * columns = gtk_tree_view_get_columns (GTK_TREE_VIEW (obj)); for (n = columns; n; n = n->next) if (VN_IS_COLUMN (n->data)) vn_column_model_changed (n->data); g_list_free (columns); } static void vn_grid_on_status_changed (DbIterator * iterator, gboolean ready, VnGrid * obj) { obj->model = db_model_holder_get_model (DB_MODEL_HOLDER (iterator)); vn_grid_on_model_changed (obj, NULL, NULL); if (ready) { GtkTreeModel * tree_model = GTK_TREE_MODEL (vn_model_new (obj->model)); gtk_tree_view_set_model (GTK_TREE_VIEW (obj), tree_model); vn_grid_on_iter_changed (obj->iterator, GTK_TREE_VIEW (obj)); g_object_unref (tree_model); } else gtk_tree_view_set_model (GTK_TREE_VIEW (obj), NULL); } static gboolean vn_grid_on_cursor_key_pressed (GtkTreeView * obj, GdkEventKey * event, gpointer data) { guint count, i; GList * n, * columns; GtkTreePath * path; GtkTreeViewColumn * col, * next_col = NULL; GtkCellArea * area; if (!(event->type == GDK_KEY_PRESS && (event->keyval == GDK_KEY_Tab || event->keyval == GDK_KEY_ISO_Left_Tab))) return FALSE; gtk_tree_view_get_cursor (obj, &path, &col); g_object_get (col, "cell-area", &area, NULL); gtk_cell_editable_editing_done (gtk_cell_area_get_edit_widget (area)); g_object_unref (area); columns = gtk_tree_view_get_columns (obj); count = gtk_tree_view_get_n_columns (obj); if (event->keyval == GDK_KEY_Tab) { i = 0; for (n = columns; n; n = n->next, i++) if (n->data == col) { if (n->next && i + 1 < gtk_tree_view_get_n_columns (obj)) { if (n->next->data && VN_IS_COLUMN (n->next->data) && !vn_column_get_editable ((VN_COLUMN (n->next->data)))) { col = n->next->data; continue; } else next_col = n->next->data; } else { gtk_tree_path_next (path); if (VN_IS_COLUMN (columns->data) && !vn_column_get_editable ((VN_COLUMN (columns->data)))) { for (n = columns->next; n; n = n->next) if (n->data && VN_IS_COLUMN (n->data) && vn_column_get_editable ((VN_COLUMN (n->data)))) { next_col = n->data; break; } } else next_col = columns->data; if (gtk_tree_path_get_indices (path)[0] >= db_model_get_nrows (VN_GRID (obj)->model)) { gtk_tree_path_free (path); return FALSE; } } break; } } else // Shift + Tab pressed { GList * inv_columns = g_list_last (columns); i = count - 1; for (n = inv_columns; n; n = n->prev, i--) if (n->data == col) { if (n->prev && i > 0) { if (n->prev->data && VN_IS_COLUMN (n->prev->data) && !vn_column_get_editable ((VN_COLUMN (n->prev->data)))) { col = n->prev->data; continue; } else next_col = n->prev->data; } else { gint current = gtk_tree_path_get_indices (path)[0]; gtk_tree_path_prev (path); if (VN_IS_COLUMN (inv_columns->data) && !vn_column_get_editable ((VN_COLUMN (inv_columns->data)))) { for (n = inv_columns->prev; n; n = n->prev) if (n->data && VN_IS_COLUMN (n->data) && vn_column_get_editable ((VN_COLUMN (n->data)))) { next_col = n->data; break; } } else next_col = inv_columns->data; if (current == 0 && gtk_tree_path_get_indices (path)[0] == 0) { gtk_tree_path_free (path); return FALSE; } } break; } } g_list_free (columns); gtk_tree_view_set_cursor (obj, path, next_col, TRUE); gtk_tree_path_free (path); return TRUE; } //+++++++++++++++++++++++++++++++++++++++++++++++++++ Public /** * vn_grid_get_iterator: * @obj: a #VnGrid * * Gets the iterator used by @obj. * * Return value: (transfer none): the #DbIterator **/ DbIterator * vn_grid_get_iterator (VnGrid * obj) { g_return_val_if_fail (VN_IS_GRID (obj), NULL); return obj->iterator; } /** * vn_grid_set_iterator: * @obj: a #VnGrid * @iterator: the #DbIterator * * Sets the iterator handled by tree view. **/ void vn_grid_set_iterator (VnGrid * obj, DbIterator * iterator) { g_return_if_fail (VN_IS_GRID (obj)); g_return_if_fail (DB_IS_ITERATOR (iterator) || !iterator); if (obj->iterator) { g_signal_handlers_disconnect_by_func (obj, vn_grid_on_cursor_changed, obj->iterator); g_object_disconnect (obj->iterator ,"any-signal", vn_grid_on_iter_changed, obj ,"any-signal", vn_grid_on_status_changed, obj ,NULL ); g_clear_object (&obj->iterator); obj->model = NULL; } if (iterator) { obj->iterator = g_object_ref (iterator); g_object_connect (iterator ,"signal::iter-changed", vn_grid_on_iter_changed, obj ,"signal::status-changed", vn_grid_on_status_changed, obj ,NULL ); vn_grid_on_status_changed (iterator, db_iterator_is_ready (iterator), obj); g_signal_connect (obj, "cursor-changed", G_CALLBACK (vn_grid_on_cursor_changed), iterator); } } /** * vn_grid_get_model: * @obj: a #VnGrid * * Gets the model used by @obj. * * Return value: (transfer none): the #DbModel **/ DbModel * vn_grid_get_model (VnGrid * obj) { g_return_val_if_fail (VN_IS_GRID (obj), NULL); return obj->model; } /** * vn_grid_insert_column: * @obj: #VnGrid where the new column will be appended * @position: the position where the column will be inserted * @column_index: the index of the source column in the model * @title: the title * @column_type: the #GType of the assigned #VnColumn * @editable: a %gboolean indicating whether the column is editable * @expand: a %gboolean indicating whether the column is expandable * * Creates and inserts a new column in the grid with the given column and * position, if @position is -1 the column will be appended to the end. * * Return value: (transfer none): the #VnColumn assigned to the new column. **/ VnColumn * vn_grid_insert_column (VnGrid * obj, gint position, gint column_index, const gchar * title, GType column_type, gboolean editable, gboolean expand) { GtkTreeViewColumn * column; g_return_val_if_fail (VN_IS_GRID (obj), NULL); g_return_val_if_fail (index >= 0, NULL); g_return_val_if_fail (g_type_is_a (column_type, VN_TYPE_COLUMN), NULL); column = g_object_new (column_type ,"title" ,title ,"expand" ,expand ,"resizable" ,TRUE ,"sort-indicator" ,TRUE ,"sizing" ,GTK_TREE_VIEW_COLUMN_GROW_ONLY ,"column-index" ,column_index ,"editable" ,editable ,NULL ); gtk_tree_view_append_column (GTK_TREE_VIEW (obj), column); return VN_COLUMN (column); } /** * vn_grid_append_column: * @obj: #VnGrid where the new column will be appended * @column_index: the index of the source column in the model * @title: the title * @column_type: the #GType of the assigned #VnCell * @editable: a %gboolean indicating whether the column is editable * @expand: a %gboolean indicating whether the column is expandable * * Creates and appends a new column at the end of the grid with the given column. * * Return value: (transfer none): the #VnCell assigned to the new column. **/ VnColumn * vn_grid_append_column (VnGrid * obj, gint column_index, const gchar * title, GType column_type, gboolean editable, gboolean expand) { g_return_val_if_fail (VN_IS_GRID (obj), NULL); g_return_val_if_fail (index >= 0, NULL); g_return_val_if_fail (g_type_is_a (column_type, VN_TYPE_COLUMN), NULL); return vn_grid_insert_column (obj, -1, column_index, title, column_type, editable, expand); } /** * vn_grid_append_columns: * @obj: the #VnGrid * @...: columns information, see vn_grid_append_column(), terminated with -1 * * Appends columns into a #VnGrid. **/ void vn_grid_append_columns (VnGrid * obj, ...) { va_list vl; gint index; gchar * title; GType column_type; gboolean editable; gboolean expand; g_return_if_fail (VN_IS_GRID (obj)); va_start (vl, obj); while ((index = va_arg (vl, gint)) != -1) { title = va_arg (vl, gchar *); column_type = va_arg (vl, GType); editable = va_arg (vl, gboolean); expand = va_arg (vl, gboolean); vn_grid_append_column (obj, index, title, column_type, editable, expand); } va_end (vl); gtk_tree_view_columns_autosize (GTK_TREE_VIEW (obj)); } //+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties typedef enum { PROP_ITERATOR = 1 } VnGridProp; static void vn_grid_set_property (VnGrid * obj, guint id, const GValue * value, GParamSpec * pspec) { switch (id) { case PROP_ITERATOR: vn_grid_set_iterator (obj, g_value_get_object (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); } } static void vn_grid_get_property (VnGrid * obj, guint id, GValue * value, GParamSpec * pspec) { switch (id) { case PROP_ITERATOR: g_value_set_object (value, obj->iterator); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); } } //+++++++++++++++++++++++++++++++++++++++++++++++++++ Class static void vn_grid_init (VnGrid * obj) { obj->iterator = NULL; obj->model = NULL; gtk_tree_selection_set_mode ( gtk_tree_view_get_selection (GTK_TREE_VIEW (obj)), GTK_SELECTION_SINGLE ); g_signal_connect (obj, "notify::model", G_CALLBACK (vn_grid_on_model_changed), NULL); g_signal_connect (obj, "key-press-event", G_CALLBACK (vn_grid_on_cursor_key_pressed), NULL); } static void vn_grid_finalize (VnGrid * obj) { vn_grid_set_iterator (obj, NULL); G_OBJECT_CLASS (vn_grid_parent_class)->finalize (G_OBJECT (obj)); } static void vn_grid_class_init (VnGridClass * k) { GObjectClass * klass = G_OBJECT_CLASS (k); klass->set_property = (GObjectSetPropertyFunc) vn_grid_set_property; klass->get_property = (GObjectGetPropertyFunc) vn_grid_get_property; klass->finalize = (GObjectFinalizeFunc) vn_grid_finalize; g_object_class_install_property (klass, PROP_ITERATOR, g_param_spec_object ("iterator" ,_("Iterator") ,_("The iterator used by VnGrid") ,DB_TYPE_ITERATOR ,G_PARAM_READWRITE )); }