This repository has been archived on 2024-07-15. You can view files and clone it, but cannot push or open issues or pull requests.
hedera/vn/vn-grid.c

641 lines
16 KiB
C

/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "vn-grid.h"
static void vn_grid_model_holder_init (DbModelHolderInterface * iface);
static void vn_grid_iterator_init (DbIteratorInterface * iface);
static void vn_grid_on_iter_changed (GtkTreeView * self);
static void vn_grid_set_iter (VnGrid * self, DbIter * iter);
static gboolean vn_grid_move_iter (VnGrid * self, DbIter * iter);
/**
* SECTION: vn-grid
* @Short_description: a grid to display data
* @Title: VnGrid
*
* #VnGrid is used to show data from a #DbModel organized in #VnColumns.
*/
G_DEFINE_TYPE_WITH_CODE (VnGrid, vn_grid, GTK_TYPE_TREE_VIEW,
G_IMPLEMENT_INTERFACE (DB_TYPE_MODEL_HOLDER,
vn_grid_model_holder_init)
G_IMPLEMENT_INTERFACE (DB_TYPE_ITERATOR,
vn_grid_iterator_init)
);
/**
* VnGrid:
* @params: (element-type Db.Param):
**/
/**
* vn_grid_new:
* @model: a #DbModel
*
* Creates a new grid
*
* Return value: (transfer full): #VnGrid created.
**/
VnGrid * vn_grid_new (DbModel * model)
{
return g_object_new (VN_TYPE_GRID, "rules-hint", TRUE, "data-model", model, NULL);
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private
static void vn_grid_unref_param (VnGrid * self, DbParam * param)
{
self->params = g_list_remove (self->params, param);
}
static gboolean vn_grid_on_key_pressed (GtkTreeView * self,
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;
// Remove new not inserted row when Escape is pressed
if (event->type == GDK_KEY_PRESS
&& event->keyval == GDK_KEY_Escape
&& VN_GRID (self)->mode != DB_ITERATOR_MODE_ON_DEMAND)
{
db_iterator_reverse_operations (DB_ITERATOR (self));
return FALSE;
}
// Move cursor when pressing Tab, Shift + Tab or Keypad Enter
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 (self, &path, &col);
if (!col || !path)
{
gtk_tree_path_free (path);
return FALSE;
}
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 (self);
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 && path)
{
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 (self)->model, &iter, gtk_tree_path_get_indices (path)[0])
&& db_model_get_row_operations (VN_GRID (self)->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 (self)->model))
{
if (db_model_get_update_flags (VN_GRID (self)->model) &
DB_MODEL_INSERT)
db_iterator_insert (DB_ITERATOR (self));
else
leave = TRUE;
}
}
}
if (leave)
{
gtk_tree_path_free (path);
return FALSE;
}
}
gtk_tree_view_set_cursor (self, path, GTK_TREE_VIEW_COLUMN (next), TRUE);
gtk_tree_path_free (path);
return TRUE;
}
static void vn_grid_on_cursor_changed (GtkTreeView * self)
{
GtkTreeIter iter;
GtkTreePath * path;
if (gtk_tree_selection_count_selected_rows (gtk_tree_view_get_selection (self)) > 1)
return;
gtk_tree_view_get_cursor (self, &path, NULL);
if (path && gtk_tree_model_get_iter (gtk_tree_view_get_model (self), &iter, path))
{
DbIter dbiter;
vn_gtk_tree_iter_to_db_iter (&iter, &dbiter);
vn_grid_move_iter (VN_GRID (self), &dbiter);
}
gtk_tree_path_free (path);
}
static void vn_grid_on_iter_changed (GtkTreeView * self)
{
GtkTreeSelection * selection = gtk_tree_view_get_selection (self);
if (VN_GRID (self)->iter && gtk_tree_view_get_model (self))
{
GtkTreeIter iter;
GtkTreePath * path;
vn_gtk_tree_iter_from_db_iter (&iter, VN_GRID (self)->iter);
path = gtk_tree_model_get_path (gtk_tree_view_get_model (self), &iter);
g_signal_handlers_block_by_func (self, vn_grid_on_cursor_changed, NULL);
gtk_tree_view_set_cursor (self, path, NULL, FALSE);
g_signal_handlers_unblock_by_func (self, vn_grid_on_cursor_changed, NULL);
gtk_tree_path_free (path);
}
else if (selection)
gtk_tree_selection_unselect_all (selection);
}
static void vn_grid_on_model_changed (VnGrid * self, GParamSpec * spec, gpointer data)
{
GList * n, * columns = gtk_tree_view_get_columns (GTK_TREE_VIEW (self));
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_row_num_changed (VnGrid * self)
{
if (self->iter)
self->row = db_model_get_path (self->model, self->iter);
else if (!self->remember_selection)
self->row = -1;
db_iterator_row_num_changed (DB_ITERATOR (self));
}
static void vn_grid_iter_changed (VnGrid * self)
{
vn_grid_row_num_changed (self);
db_iterator_iter_changed (DB_ITERATOR (self));
vn_grid_on_iter_changed (GTK_TREE_VIEW (self));
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++ DbModelHolder methods
static void vn_grid_set_model_mode (VnGrid * self)
{
if (!self->model)
return;
if (self->mode == DB_ITERATOR_MODE_ON_CHANGE)
db_model_set_mode (self->model, DB_MODEL_MODE_ON_CHANGE);
else
db_model_set_mode (self->model, DB_MODEL_MODE_ON_DEMAND);
}
static void vn_grid_on_model_line_inserted (DbModel * model, DbIter * iter, VnGrid * self)
{
db_iterator_data_changed (DB_ITERATOR (self));
}
static void vn_grid_on_model_line_updated_after (DbModel * model, DbIter * iter, VnGrid * self)
{
if (self->iter && db_iter_compare (iter, self->iter))
vn_grid_iter_changed (self);
db_iterator_data_changed (DB_ITERATOR (self));
}
static void vn_grid_on_model_line_deleted (DbModel * model, gint row, VnGrid * self)
{
if (self->iter && row == self->row)
{
DbIter iter;
if (db_model_get_iter (model, &iter, row + 1)
|| db_model_get_iter (model, &iter, row - 1))
vn_grid_set_iter (self, &iter);
else
vn_grid_set_iter (self, NULL);
}
}
static void vn_grid_on_model_line_deleted_after (DbModel * model, gint row, VnGrid * self)
{
if (self->iter)
vn_grid_row_num_changed (self);
db_iterator_data_changed (DB_ITERATOR (self));
}
static void vn_grid_on_model_lines_reordered (DbModel * model, gint column, gint * new_order, VnGrid * self)
{
if (self->iter)
vn_grid_row_num_changed (self);
}
static void vn_grid_on_model_status_changed (DbModel * model, DbModelStatus status, VnGrid * self)
{
self->model = db_model_holder_get_model (DB_MODEL_HOLDER (self));
vn_grid_on_model_changed (self, NULL, NULL);
if (status == DB_MODEL_STATUS_READY)
{
DbIter iter;
GtkTreeModel * tree_model;
db_iterator_status_changed (DB_ITERATOR (self), TRUE);
tree_model = GTK_TREE_MODEL (vn_list_model_new (self->model));
gtk_tree_view_set_model (GTK_TREE_VIEW (self), tree_model);
vn_grid_on_iter_changed (GTK_TREE_VIEW (self));
if (self->row >= 0 && self->row < db_model_get_nrows (self->model)
&& db_model_get_iter (self->model, &iter, self->row))
vn_grid_set_iter (self, &iter);
else
vn_grid_set_iter (self, NULL);
g_object_unref (tree_model);
}
else
{
vn_grid_set_iter (self, NULL);
db_iterator_status_changed (DB_ITERATOR (self), FALSE);
gtk_tree_view_set_model (GTK_TREE_VIEW (self), NULL);
}
}
static void vn_grid_on_model_operations_done (DbModel * model, VnGrid * self)
{
g_return_val_if_fail (VN_IS_GRID (self), NULL);
db_iterator_operations_done (DB_ITERATOR (self));
}
static DbModel * vn_grid_get_model (VnGrid * self)
{
g_return_val_if_fail (VN_IS_GRID (self), NULL);
return self->model;
}
static void vn_grid_set_model (VnGrid * self, DbModel * model)
{
g_return_if_fail (VN_IS_GRID (self));
if (!model)
return;
if (!self->model)
{
self->model = g_object_ref (model);
g_object_connect (model
,"signal::line-inserted", vn_grid_on_model_line_inserted, self
,"signal-after::line-updated", vn_grid_on_model_line_updated_after, self
,"signal::line-deleted", vn_grid_on_model_line_deleted, self
,"signal-after::line-deleted", vn_grid_on_model_line_deleted_after, self
,"signal::lines-reordered", vn_grid_on_model_lines_reordered, self
,"signal::status-changed", vn_grid_on_model_status_changed, self
,"signal::operations-done", vn_grid_on_model_operations_done, self
,NULL
);
vn_grid_set_model_mode (self);
vn_grid_on_model_status_changed (model,
db_model_get_status (model), self);
}
else
g_warning ("VnGrid: Can't reassign the 'data-model' property");
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++ DbIterator methods
static DbIteratorMode vn_grid_get_mode (VnGrid * self)
{
g_return_val_if_fail (VN_IS_GRID (self), 0);
return self->mode;
}
static void vn_grid_set_mode (VnGrid * self, DbIteratorMode mode)
{
g_return_if_fail (VN_IS_GRID (self));
self->mode = mode;
vn_grid_set_model_mode (self);
}
static gint vn_grid_get_row (VnGrid * self)
{
g_return_val_if_fail (VN_IS_GRID (self), -1);
if (self->iter)
return self->row;
else
return -1;
}
static gboolean vn_grid_get_iter (VnGrid * self, DbIter ** iter)
{
g_return_val_if_fail (VN_IS_GRID (self), NULL);
if (!self->iter)
return FALSE;
*iter = self->iter;
return TRUE;
}
static void vn_grid_set_iter (VnGrid * self, DbIter * iter)
{
if (iter)
{
db_iter_free (self->iter);
self->iter = db_iter_copy (iter);
vn_grid_iter_changed (self);
}
else if (self->iter)
{
if (!self->remember_selection)
self->row = 0;
db_iter_free (self->iter);
self->iter = NULL;
vn_grid_iter_changed (self);
}
}
static gboolean vn_grid_move_iter (VnGrid * self, DbIter * iter)
{
g_return_val_if_fail (VN_IS_GRID (self), FALSE);
if (self->iter && db_iter_compare (self->iter, iter))
return FALSE;
if (self->iter && self->mode != DB_ITERATOR_MODE_ON_DEMAND)
{
if (db_model_get_row_operations (self->model, self->iter) == DB_MODEL_ROW_OP_INSERT)
db_model_reverse_operations (self->model);
db_model_perform_operations (self->model, FALSE);
}
vn_grid_set_iter (self, iter);
return TRUE;
}
static GList * vn_grid_get_param_list (VnGrid * self)
{
g_return_val_if_fail (VN_IS_GRID (self), NULL);
return self->params;
}
static void vn_grid_delete_selection (VnGrid * self)
{
GtkTreeModel * tree_model;
GtkTreeSelection * selection;
GList * selected, * l;
g_return_val_if_fail (VN_IS_GRID (self), NULL);
if (!self->model)
return;
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
selected = gtk_tree_selection_get_selected_rows (selection, NULL);
tree_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
for (l = selected; l; l = l->next)
{
DbIter iter;
GtkTreeIter tree_iter;
gtk_tree_model_get_iter (tree_model, &tree_iter, l->data);
vn_gtk_tree_iter_to_db_iter (&tree_iter, &iter);
db_model_delete (self->model, &iter);
}
g_list_free_full (selected, (GDestroyNotify) gtk_tree_path_free);
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties
typedef enum
{
PROP_MODEL = 1
,PROP_MODE
,PROP_REMEMBER_SELECTION
}
VnGridProp;
static void vn_grid_set_property (VnGrid * self, guint id,
const GValue * value, GParamSpec * pspec)
{
switch (id)
{
case PROP_MODEL:
vn_grid_set_model (self, g_value_get_object (value));
break;
case PROP_MODE:
vn_grid_set_mode (self, g_value_get_enum (value));
break;
case PROP_REMEMBER_SELECTION:
self->remember_selection = g_value_get_boolean (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (self, id, pspec);
}
}
static void vn_grid_get_property (VnGrid * self, guint id,
GValue * value, GParamSpec * pspec)
{
switch (id)
{
case PROP_MODEL:
g_value_set_object (value, self->model);
break;
case PROP_MODE:
g_value_set_enum (value, self->mode);
break;
case PROP_REMEMBER_SELECTION:
g_value_set_boolean (value, self->remember_selection);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (self, id, pspec);
}
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class
static void vn_grid_init (VnGrid * self)
{
GtkTreeSelection * selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
self->mode = 0;
self->iter = 0;
self->row = 0;
self->params = NULL;
self->model = NULL;
gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
g_signal_connect (self, "notify::model", G_CALLBACK (vn_grid_on_model_changed), NULL);
g_object_connect (self
,"signal-after::cursor-changed", vn_grid_on_cursor_changed, NULL
,"signal::key-press-event", vn_grid_on_key_pressed, NULL
,NULL
);
}
static void vn_grid_finalize (VnGrid * self)
{
GList * n;
for (n = self->params; n; n = n->next)
g_object_weak_unref (n->data,
(GWeakNotify) vn_grid_unref_param, self);
g_list_free (self->params);
db_iter_free (self->iter);
self->iter = NULL;
if (self->model)
{
g_object_disconnect (self->model
,"any_signal", vn_grid_on_model_line_inserted, self
,"any_signal", vn_grid_on_model_line_updated_after, self
,"any_signal", vn_grid_on_model_line_deleted, self
,"any_signal", vn_grid_on_model_line_deleted_after, self
,"any_signal", vn_grid_on_model_lines_reordered, self
,"any_signal", vn_grid_on_model_status_changed, self
,"any_signal", vn_grid_on_model_operations_done, self
,NULL
);
g_object_unref (self->model);
}
G_OBJECT_CLASS (vn_grid_parent_class)->finalize (G_OBJECT (self));
}
static void vn_grid_class_init (VnGridClass * klass)
{
GObjectClass * k = G_OBJECT_CLASS (klass);
k->set_property = (GObjectSetPropertyFunc) vn_grid_set_property;
k->get_property = (GObjectGetPropertyFunc) vn_grid_get_property;
k->finalize = (GObjectFinalizeFunc) vn_grid_finalize;
g_object_class_override_property (k, PROP_MODEL, "data-model");
g_object_class_override_property (k, PROP_MODE, "mode");
g_object_class_override_property (k, PROP_REMEMBER_SELECTION, "remember-selection");
}
static void vn_grid_model_holder_init (DbModelHolderInterface * iface)
{
iface->get_model = (DbModelHolderGetModelFunc) vn_grid_get_model;
iface->set_model = (DbModelHolderSetModelFunc) vn_grid_set_model;
}
static void vn_grid_iterator_init (DbIteratorInterface * iface)
{
iface->get_mode = (DbIteratorGetMode) vn_grid_get_mode;
iface->set_mode = (DbIteratorSetMode) vn_grid_set_mode;
iface->get_row = (DbIteratorGetRow) vn_grid_get_row;
iface->get_iter = (DbIteratorGetIter) vn_grid_get_iter;
iface->set_iter = (DbIteratorSetIter) vn_grid_set_iter;
iface->move_iter = (DbIteratorMoveIter) vn_grid_move_iter;
iface->get_param_list = (DbIteratorGetParamList) vn_grid_get_param_list;
iface->delete_selection = (DbIteratorDeleteSelection) vn_grid_delete_selection;
}