/* * 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_selection_changed (GtkTreeSelection * selection, GtkTreeView * obj) { GtkTreeIter iter; GList * l, * list; if (gtk_tree_selection_count_selected_rows (selection) <= 1) return; list = gtk_tree_selection_get_selected_rows (selection, NULL); for (l = list; l; l = l->next) if (gtk_tree_model_get_iter (gtk_tree_view_get_model (obj), &iter, l->data)) { DbIter dbiter; vn_gtk_tree_iter_to_db_iter (&iter, &dbiter); db_iterator_select_iter (VN_GRID (obj)->iterator, &dbiter); } g_list_free_full (list, (GDestroyNotify) gtk_tree_path_free); } static void vn_grid_on_cursor_changed (GtkTreeView * tree_view, VnGrid * obj) { gint selected_rows; if (!db_iterator_is_ready (obj->iterator)) return; selected_rows = gtk_tree_selection_count_selected_rows (gtk_tree_view_get_selection (tree_view)); if (selected_rows == 1) { GtkTreeIter iter; GtkTreePath * path; gtk_tree_view_get_cursor (tree_view, &path, NULL); if (path && gtk_tree_model_get_iter (gtk_tree_view_get_model (tree_view), &iter, path)) { DbIter dbiter; vn_gtk_tree_iter_to_db_iter (&iter, &dbiter); g_signal_handlers_block_by_func (obj->iterator, vn_grid_on_iter_changed, obj); db_iterator_move_iter (obj->iterator, &dbiter); g_signal_handlers_unblock_by_func (obj->iterator, vn_grid_on_iter_changed, obj); } gtk_tree_path_free (path); } else if (selected_rows == 0) db_iterator_move_iter (obj->iterator, NULL); } 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); path = gtk_tree_model_get_path (gtk_tree_view_get_model (obj), &iter); g_signal_handlers_block_by_func (obj, vn_grid_on_cursor_changed, iterator); gtk_tree_view_set_cursor (obj, path, NULL, FALSE); g_signal_handlers_unblock_by_func (obj, vn_grid_on_cursor_changed, iterator); 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_grid_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) { gboolean inverted, passed = FALSE; gint sel_tab_index; VnColumn * first = NULL; GList * n, * columns; GtkTreePath * path; GtkTreeViewColumn * col; VnColumn * selected, * next = NULL; GtkCellArea * area; GtkCellEditable * editable; if (!(event->type == GDK_KEY_PRESS && (event->keyval == GDK_KEY_Tab || event->keyval == GDK_KEY_ISO_Left_Tab || event->keyval == GDK_KEY_KP_Enter))) return FALSE; gtk_tree_view_get_cursor (obj, &path, &col); selected = VN_COLUMN (col); g_object_get (col, "cell-area", &area, NULL); editable = gtk_cell_area_get_edit_widget (area); if (editable) gtk_cell_editable_editing_done (editable); g_object_unref (area); columns = gtk_tree_view_get_columns (obj); inverted = event->keyval == GDK_KEY_ISO_Left_Tab ? TRUE : FALSE; sel_tab_index = vn_column_get_tab_index (selected); first = NULL; for (n = columns; n; n = n->next) { gint cur_tab_index; VnColumn * current = n->data; if (current == selected) passed = TRUE; else if (VN_IS_COLUMN (current) && vn_column_get_editable (current) && (cur_tab_index = vn_column_get_tab_index (current)) != -1) { if (!inverted) { if (!first || cur_tab_index < vn_column_get_tab_index (first)) first = current; if (cur_tab_index == sel_tab_index && passed) { next = current; break; } if ((!next || cur_tab_index < vn_column_get_tab_index (next)) && cur_tab_index > sel_tab_index) next = current; } else { if (!first || cur_tab_index >= vn_column_get_tab_index (first)) first = current; if ((cur_tab_index == sel_tab_index && !passed) || (cur_tab_index < sel_tab_index && (!next || cur_tab_index >= vn_column_get_tab_index (next)))) next = current; } } } g_list_free (columns); if (!next) { gboolean leave = FALSE; next = first ? first : selected; if (inverted) leave = !gtk_tree_path_prev (path); else { DbIter iter; if (db_model_get_iter (VN_GRID (obj)->model, &iter, gtk_tree_path_get_indices (path)[0]) && db_model_get_row_operations (VN_GRID (obj)->model, &iter) == DB_MODEL_ROW_OP_INSERT) leave = TRUE; else { gtk_tree_path_next (path); if (gtk_tree_path_get_indices (path)[0] >= db_model_get_nrows (VN_GRID (obj)->model)) { if (db_model_get_update_flags (VN_GRID (obj)->model) & DB_MODEL_INSERT) db_iterator_insert (VN_GRID (obj)->iterator); else leave = TRUE; } } } if (leave) { gtk_tree_path_free (path); return FALSE; } } gtk_tree_view_set_cursor (obj, path, GTK_TREE_VIEW_COLUMN (next), TRUE); gtk_tree_path_free (path); return TRUE; } static gboolean vn_grid_on_escape_pressed (VnGrid * obj, GdkEventKey * event, gpointer data) { if (event->type == GDK_KEY_PRESS && event->keyval == GDK_KEY_Escape && db_iterator_get_mode (obj->iterator) != DB_ITERATOR_MODE_ON_DEMAND) db_iterator_reverse_operations (obj->iterator); return FALSE; } //+++++++++++++++++++++++++++++++++++++++++++++++++++ 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_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); } } /** * 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; } //+++++++++++++++++++++++++++++++++++++++++++++++++++ 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) { GtkTreeSelection * selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (obj)); obj->iterator = NULL; obj->model = NULL; gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE); g_signal_connect (selection, "changed", G_CALLBACK (vn_grid_on_selection_changed), obj); g_object_connect (obj ,"signal::notify::model", vn_grid_on_model_changed, obj ,"signal::key-press-event", vn_grid_on_cursor_key_pressed, obj ,"signal::key-press-event", vn_grid_on_escape_pressed, obj ,"signal-after::cursor-changed", vn_grid_on_cursor_changed, obj ,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 )); }