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

523 lines
14 KiB
C
Raw Normal View History

2013-10-11 23:07:35 +00:00
/*
* 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"
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);
}
2013-10-11 23:07:35 +00:00
static void vn_grid_on_cursor_changed (GtkTreeView * obj, DbIterator * iterator)
{
GtkTreeIter iter;
GtkTreePath * path;
2013-10-11 23:07:35 +00:00
if (gtk_tree_selection_count_selected_rows (gtk_tree_view_get_selection (obj)) > 1)
return;
gtk_tree_view_get_cursor (obj, &path, NULL);
if (path && gtk_tree_model_get_iter (gtk_tree_view_get_model (obj), &iter, path))
2013-10-11 23:07:35 +00:00
{
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);
}
gtk_tree_path_free (path);
2013-10-11 23:07:35 +00:00
}
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))
2013-10-11 23:07:35 +00:00
{
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);
2013-10-11 23:07:35 +00:00
g_signal_handlers_block_by_func (obj, vn_grid_on_cursor_changed, iterator);
gtk_tree_view_set_cursor (obj, path, NULL, FALSE);
2013-10-11 23:07:35 +00:00
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);
}
2013-10-14 12:08:06 +00:00
static void vn_grid_on_model_changed (VnGrid * obj, GParamSpec * spec, gpointer data)
2013-10-11 23:07:35 +00:00
{
2013-10-14 12:08:06 +00:00
GList * n, * columns = gtk_tree_view_get_columns (GTK_TREE_VIEW (obj));
2013-10-11 23:07:35 +00:00
for (n = columns; n; n = n->next)
2013-10-14 12:08:06 +00:00
if (VN_IS_COLUMN (n->data))
vn_column_model_changed (n->data);
g_list_free (columns);
2013-10-14 12:08:06 +00:00
}
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);
2013-10-11 23:07:35 +00:00
if (ready)
{
GtkTreeModel * tree_model = GTK_TREE_MODEL (vn_grid_model_new (obj->model));
2013-10-11 23:07:35 +00:00
gtk_tree_view_set_model (GTK_TREE_VIEW (obj), tree_model);
vn_grid_on_iter_changed (obj->iterator, GTK_TREE_VIEW (obj));
2013-10-11 23:07:35 +00:00
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;
}
2013-10-11 23:07:35 +00:00
//+++++++++++++++++++++++++++++++++++++++++++++++++++ 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);
2013-10-11 23:07:35 +00:00
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);
2013-10-11 23:07:35 +00:00
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
);
2013-10-11 23:07:35 +00:00
vn_grid_on_status_changed (iterator,
db_iterator_is_ready (iterator), obj);
g_signal_connect_after (obj, "cursor-changed",
2013-10-11 23:07:35 +00:00
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,
2013-10-14 12:08:06 +00:00
const gchar * title, GType column_type, gboolean editable, gboolean expand)
2013-10-11 23:07:35 +00:00
{
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
);
2013-10-14 12:08:06 +00:00
2013-10-11 23:07:35 +00:00
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)
2013-10-14 12:08:06 +00:00
{
2013-10-11 23:07:35 +00:00
switch (id)
{
case PROP_ITERATOR:
vn_grid_set_iterator (obj, g_value_get_object (value));
break;
default:
2013-10-11 23:07:35 +00:00
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:
2013-10-11 23:07:35 +00:00
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));
2013-10-11 23:07:35 +00:00
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);
2013-10-14 12:08:06 +00:00
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);
g_signal_connect (obj, "key-press-event",
G_CALLBACK (vn_grid_on_escape_pressed), NULL);
2013-10-11 23:07:35 +00:00
}
static void vn_grid_finalize (VnGrid * obj)
{
vn_grid_set_iterator (obj, NULL);
2013-10-14 12:08:06 +00:00
2013-10-11 23:07:35 +00:00
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
));
2013-10-14 12:08:06 +00:00
}