mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
Ask for GLib 2.6 and GTK+ 2.6. Let the included GOffice 0.0.4 and LibGSF 1.12.3 use their native base dependencies. Remove all #ifdefs and compatibility code that were necessary to make GnuCash, GOffice and LibGSF compile on an older setup (GLib 2.4, Pango 1.6, GTK+ 2.4). git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@15400 57a11ea4-9604-0410-9ed3-97b8803252fd
2160 lines
68 KiB
C
2160 lines
68 KiB
C
/*
|
|
* gnc-tree-view.c -- new GtkTreeView with extra features used by
|
|
* all the tree views in gnucash
|
|
*
|
|
* Copyright (C) 2003,2005 David Hampton <hampton@employees.org>
|
|
*
|
|
* 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, contact:
|
|
*
|
|
* Free Software Foundation Voice: +1-617-542-5942
|
|
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652
|
|
* Boston, MA 02110-1301, USA gnu@gnu.org
|
|
*/
|
|
|
|
/** @addtogroup GUI
|
|
@{ */
|
|
/** @addtogroup GncTreeView
|
|
* @{ */
|
|
/** @file gnc-tree-view.c
|
|
@brief Base GncTreeView implementation for gnucash trees.
|
|
@author David Hampton <hampton@employees.org>
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <gtk/gtk.h>
|
|
#include <glib/gi18n.h>
|
|
#include <gdk/gdkkeysyms.h>
|
|
#include <string.h>
|
|
|
|
#include "gnc-tree-view.h"
|
|
#include "gnc-engine.h"
|
|
#include "gnc-gconf-utils.h"
|
|
#include "gnc-gnome-utils.h"
|
|
#include "gnc-gobject-utils.h"
|
|
|
|
/* The column id refers to a specific column in the tree model. It is
|
|
* also attached to the side of the tree column to allow lookup of a
|
|
* GtkTreeViewColumn when passed a column id from the underlying
|
|
* model. By convention, negative column numbers are used when the
|
|
* visible column has no association with the underlying model.*/
|
|
#define MODEL_COLUMN "model_column"
|
|
|
|
/* For checkbox columns, this contains the real title for the column. */
|
|
#define REAL_TITLE "real_title"
|
|
|
|
/* The name of this column as it should appear in gconf. This is
|
|
* attached to the column when it is created, and used to map back and
|
|
* forth to gconf keys. The actual gconf keys are built from these
|
|
* strings. */
|
|
#define PREF_NAME "pref-name"
|
|
|
|
/* The actual gconf key for a particular column visibility. This is
|
|
* attached to the menu items that are in the column selection menu.
|
|
* Makes it very easy to update gconf when a menu item is toggled. */
|
|
#define GCONF_KEY "gconf-key"
|
|
|
|
/* Gconf keys within this particular section of gconf. */
|
|
#define GCONF_KEY_SORT_COLUMN "sort_column"
|
|
#define GCONF_KEY_SORT_ORDER "sort_order"
|
|
#define GCONF_KEY_COLUMN_ORDER "column_order"
|
|
|
|
/* Partial gconf keys within this particular section of gconf. These
|
|
are appended to the various column names to create the actual
|
|
keys. */
|
|
#define GCONF_KEY_VISIBLE "visible"
|
|
#define GCONF_KEY_WIDTH "width"
|
|
|
|
enum {
|
|
PROP_0,
|
|
PROP_GCONF_SECTION,
|
|
PROP_SHOW_COLUMN_MENU,
|
|
};
|
|
|
|
/** Static Globals *******************************************************/
|
|
|
|
/* This static indicates the debugging module that this .o belongs to. */
|
|
static QofLogModule log_module = GNC_MOD_GUI;
|
|
|
|
/**** Declarations ******************************************************/
|
|
static void gnc_tree_view_class_init (GncTreeViewClass *klass);
|
|
static void gnc_tree_view_init (GncTreeView *view, GncTreeViewClass *klass);
|
|
static void gnc_tree_view_finalize (GObject *object);
|
|
static void gnc_tree_view_destroy (GtkObject *object);
|
|
static void gnc_tree_view_set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec);
|
|
static void gnc_tree_view_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec);
|
|
static void gnc_tree_view_remove_gconf(GncTreeView *view);
|
|
static gboolean gnc_tree_view_drop_ok_cb (GtkTreeView *view,
|
|
GtkTreeViewColumn *column,
|
|
GtkTreeViewColumn *prev_column,
|
|
GtkTreeViewColumn *next_column,
|
|
gpointer data);
|
|
static void gtk_tree_view_sort_column_changed_cb (GtkTreeSortable *treesortable,
|
|
GncTreeView *view);
|
|
static void gnc_tree_view_build_column_menu (GncTreeView *view);
|
|
static void gnc_tree_view_select_column_cb (GtkTreeViewColumn *column,
|
|
GncTreeView *view);
|
|
|
|
/** Private Data Structure ***********************************************/
|
|
|
|
typedef struct GncTreeViewPrivate
|
|
{
|
|
GtkTooltips *title_tips;
|
|
|
|
/* Spacer column */
|
|
GtkTreeViewColumn *spacer_column;
|
|
GtkTreeViewColumn *selection_column;
|
|
|
|
/* Column selection menu related values */
|
|
GtkTreeViewColumn *column_menu_column;
|
|
GtkWidget *column_menu;
|
|
gboolean show_column_menu;
|
|
|
|
/* Gconf related values */
|
|
gchar *gconf_section;
|
|
gboolean seen_gconf_visibility;
|
|
gulong columns_changed_cb_id;
|
|
gulong sort_column_changed_cb_id;
|
|
gulong size_allocate_cb_id;
|
|
} GncTreeViewPrivate;
|
|
|
|
#define GNC_TREE_VIEW_GET_PRIVATE(o) \
|
|
(G_TYPE_INSTANCE_GET_PRIVATE ((o), GNC_TYPE_TREE_VIEW, GncTreeViewPrivate))
|
|
|
|
|
|
/************************************************************/
|
|
/* g_object required functions */
|
|
/************************************************************/
|
|
|
|
/** @name Gnc Tree View Object Implementation
|
|
@{ */
|
|
|
|
static GObjectClass *parent_class = NULL;
|
|
|
|
/** Create a new glib type for the base gnucash tree view.
|
|
*
|
|
* @internal
|
|
*
|
|
* @return The new type value.
|
|
*/
|
|
GType
|
|
gnc_tree_view_get_type (void)
|
|
{
|
|
static GType gnc_tree_view_type = 0;
|
|
|
|
if (gnc_tree_view_type == 0) {
|
|
static const GTypeInfo our_info = {
|
|
sizeof (GncTreeViewClass),
|
|
NULL,
|
|
NULL,
|
|
(GClassInitFunc) gnc_tree_view_class_init,
|
|
NULL,
|
|
NULL,
|
|
sizeof (GncTreeView),
|
|
0,
|
|
(GInstanceInitFunc) gnc_tree_view_init
|
|
};
|
|
|
|
gnc_tree_view_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
|
|
GNC_TREE_VIEW_NAME,
|
|
&our_info, 0);
|
|
}
|
|
|
|
return gnc_tree_view_type;
|
|
}
|
|
|
|
/** Initialize the class for the new base gnucash tree view. This
|
|
* will set up any function pointers that override functions in the
|
|
* parent class, and also installs the proprieties that are unique to
|
|
* this class.
|
|
*
|
|
* @param klass The new class structure created by the object system.
|
|
*
|
|
* @internal
|
|
*/
|
|
static void
|
|
gnc_tree_view_class_init (GncTreeViewClass *klass)
|
|
{
|
|
GObjectClass *gobject_class;
|
|
GtkObjectClass *gtkobject_class;
|
|
|
|
parent_class = g_type_class_peek_parent (klass);
|
|
|
|
gobject_class = G_OBJECT_CLASS (klass);
|
|
gtkobject_class = GTK_OBJECT_CLASS (klass);
|
|
|
|
gobject_class->set_property = gnc_tree_view_set_property;
|
|
gobject_class->get_property = gnc_tree_view_get_property;
|
|
|
|
g_type_class_add_private(klass, sizeof(GncTreeViewPrivate));
|
|
|
|
g_object_class_install_property (gobject_class,
|
|
PROP_GCONF_SECTION,
|
|
g_param_spec_string ("gconf-section",
|
|
"Gconf Section",
|
|
"The Gconf section to use for storing settings",
|
|
NULL,
|
|
G_PARAM_READWRITE));
|
|
g_object_class_install_property (gobject_class,
|
|
PROP_SHOW_COLUMN_MENU,
|
|
g_param_spec_boolean ("show-column-menu",
|
|
"Show Column Menu",
|
|
"Show the column menu so user can change what columns are visible.",
|
|
FALSE,
|
|
G_PARAM_READWRITE));
|
|
|
|
/* GObject signals */
|
|
gobject_class->finalize = gnc_tree_view_finalize;
|
|
|
|
/* GtkObject signals */
|
|
gtkobject_class->destroy = gnc_tree_view_destroy;
|
|
}
|
|
|
|
/** Initialize a new instance of a base gnucash tree view. This
|
|
* function allocates and initializes the object private storage
|
|
* space. It also adds the new object to a list (for memory tracking
|
|
* purposes), and sets up a callback for the column drag function.
|
|
*
|
|
* @param view The new object instance created by the object system.
|
|
*
|
|
* @internal
|
|
*/
|
|
static void
|
|
gnc_tree_view_init (GncTreeView *view, GncTreeViewClass *klass)
|
|
{
|
|
GncTreeViewPrivate *priv;
|
|
GtkTreeViewColumn *column;
|
|
GtkWidget *icon;
|
|
GtkRequisition requisition;
|
|
|
|
gnc_gobject_tracking_remember(G_OBJECT(view), G_OBJECT_CLASS(klass));
|
|
|
|
priv = GNC_TREE_VIEW_GET_PRIVATE(view);
|
|
priv->column_menu = NULL;
|
|
priv->show_column_menu = FALSE;
|
|
priv->gconf_section = NULL;
|
|
priv->seen_gconf_visibility = FALSE;
|
|
priv->columns_changed_cb_id = 0;
|
|
priv->sort_column_changed_cb_id = 0;
|
|
priv->size_allocate_cb_id = 0;
|
|
|
|
/* Ask gtk to help the user keep track of rows. */
|
|
g_object_set(view, "rules-hint", TRUE, NULL);
|
|
|
|
/* Handle column drag and drop */
|
|
gtk_tree_view_set_column_drag_function(GTK_TREE_VIEW(view),
|
|
gnc_tree_view_drop_ok_cb, NULL, NULL);
|
|
|
|
/* Create the next to last column which is always present, visible,
|
|
* and empty. Override the defaults and make this a one pixel wide
|
|
* column, but have it take up any extra space in the window. */
|
|
column = gnc_tree_view_add_text_column (view, NULL, NULL, NULL, NULL,
|
|
-1, -1, NULL);
|
|
g_object_set(G_OBJECT(column),
|
|
"fixed-width", 1,
|
|
"expand", TRUE,
|
|
(gchar *)NULL);
|
|
priv->spacer_column = column;
|
|
|
|
/* Create the last column which contains the column selection
|
|
* widget. gnc_tree_view_add_text_column will do most of the
|
|
* work. */
|
|
icon = gtk_image_new_from_stock(GTK_STOCK_GO_DOWN,
|
|
GTK_ICON_SIZE_SMALL_TOOLBAR);
|
|
gtk_widget_show(icon);
|
|
gtk_widget_size_request(icon, &requisition);
|
|
column = gnc_tree_view_add_text_column (view, NULL, NULL, NULL, NULL,
|
|
-1, -1, NULL);
|
|
g_object_set(G_OBJECT(column),
|
|
"clickable", TRUE,
|
|
"widget", icon,
|
|
"fixed-width", requisition.width + 10,
|
|
(gchar *)NULL);
|
|
priv->selection_column = column;
|
|
g_signal_connect(G_OBJECT(column), "clicked",
|
|
G_CALLBACK (gnc_tree_view_select_column_cb),
|
|
view);
|
|
priv->column_menu_column = column;
|
|
}
|
|
|
|
/** Finalize the GncTreeView object. This function is called from the
|
|
* G_Object level to complete the destruction of the object. It
|
|
* should release any memory not previously released by the destroy
|
|
* function (i.e. the private data structure), then chain up to the
|
|
* parent's destroy function.
|
|
*
|
|
* @param object The object being destroyed.
|
|
*
|
|
* @internal
|
|
*/
|
|
static void
|
|
gnc_tree_view_finalize (GObject *object)
|
|
{
|
|
GncTreeView *view;
|
|
GncTreeViewPrivate *priv;
|
|
|
|
ENTER("view %p", object);
|
|
g_return_if_fail (object != NULL);
|
|
g_return_if_fail (GNC_IS_TREE_VIEW (object));
|
|
|
|
gnc_gobject_tracking_forget(object);
|
|
|
|
view = GNC_TREE_VIEW (object);
|
|
priv = GNC_TREE_VIEW_GET_PRIVATE (view);
|
|
|
|
if (G_OBJECT_CLASS (parent_class)->finalize)
|
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
|
LEAVE(" ");
|
|
}
|
|
|
|
/** Destroy the GncTreeView object. This function is called (possibly
|
|
* multiple times) from the Gtk_Object level to destroy the object.
|
|
* It should release any memory owned by the object that isn't
|
|
* fundamental to the implementation. In this function any active
|
|
* callbacks are disconnected, all memory other than the private data
|
|
* structure are freed.
|
|
*
|
|
* @param object The object being destroyed.
|
|
*
|
|
* @internal
|
|
*/
|
|
static void
|
|
gnc_tree_view_destroy (GtkObject *object)
|
|
{
|
|
GncTreeView *view;
|
|
GncTreeViewPrivate *priv;
|
|
|
|
ENTER("view %p", object);
|
|
g_return_if_fail (object != NULL);
|
|
g_return_if_fail (GNC_IS_TREE_VIEW (object));
|
|
|
|
view = GNC_TREE_VIEW (object);
|
|
|
|
gnc_tree_view_remove_gconf(view);
|
|
|
|
priv = GNC_TREE_VIEW_GET_PRIVATE(view);
|
|
|
|
if (priv->column_menu) {
|
|
DEBUG("removing column selection menu");
|
|
gtk_widget_unref(priv->column_menu);
|
|
priv->column_menu = NULL;
|
|
}
|
|
|
|
if (GTK_OBJECT_CLASS (parent_class)->destroy)
|
|
GTK_OBJECT_CLASS (parent_class)->destroy (object);
|
|
LEAVE(" ");
|
|
}
|
|
|
|
/** @} */
|
|
|
|
/************************************************************/
|
|
/* g_object other functions */
|
|
/************************************************************/
|
|
|
|
/** @name Gnc Tree View Object Implementation
|
|
@{ */
|
|
|
|
/** Retrieve a property specific to this GncTreeView object. This is
|
|
* nothing more than a dispatch function for routines that can be
|
|
* called directly. It has the nice feature of allowing a single
|
|
* function call to retrieve multiple properties.
|
|
*
|
|
* @internal
|
|
*/
|
|
static void
|
|
gnc_tree_view_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GncTreeView *view = GNC_TREE_VIEW (object);
|
|
GncTreeViewPrivate *priv;
|
|
|
|
priv = GNC_TREE_VIEW_GET_PRIVATE(view);
|
|
switch (prop_id)
|
|
{
|
|
case PROP_GCONF_SECTION:
|
|
g_value_set_string (value, priv->gconf_section);
|
|
break;
|
|
case PROP_SHOW_COLUMN_MENU:
|
|
g_value_set_boolean (value, priv->show_column_menu);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/** Set a property specific to this GncTreeView object. This is
|
|
* nothing more than a dispatch function for routines that can be
|
|
* called directly. It has the nice feature of allowing a new view
|
|
* to be created with a varargs list specifying the properties,
|
|
* instead of having to explicitly call each property function.
|
|
*
|
|
* @internal
|
|
*/
|
|
static void
|
|
gnc_tree_view_set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GncTreeView *view = GNC_TREE_VIEW (object);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_GCONF_SECTION:
|
|
gnc_tree_view_set_gconf_section (view, g_value_get_string (value));
|
|
break;
|
|
case PROP_SHOW_COLUMN_MENU:
|
|
gnc_tree_view_set_show_column_menu (view, g_value_get_boolean (value));
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/** @} */
|
|
|
|
/************************************************************/
|
|
/* Auxiliary Functions */
|
|
/************************************************************/
|
|
|
|
/** @name Gnc Tree View Auxiliary Functions
|
|
@{ */
|
|
|
|
/** Find a tree column given a column id number from the underlying
|
|
* data model. This function should only be called by code that has
|
|
* visibility into the data model. The column id numbers shouldn't
|
|
* be used for any other purpose.
|
|
*
|
|
* This function simply runs the list of all (visible and invisible)
|
|
* columns looking for a match. Column id numbers were attached to
|
|
* each column at the time the column was created.
|
|
*
|
|
* @param view The visible tree widget.
|
|
*
|
|
* @param wanted The column id number to find.
|
|
*
|
|
* @internal
|
|
*/
|
|
static GtkTreeViewColumn *
|
|
view_column_find_by_model_id (GncTreeView *view,
|
|
const gint wanted)
|
|
{
|
|
GtkTreeViewColumn *column, *found = NULL;
|
|
GList *column_list, *tmp;
|
|
gint id;
|
|
|
|
// ENTER("view %p, name %s", view, name);
|
|
column_list = gtk_tree_view_get_columns(GTK_TREE_VIEW(view));
|
|
for (tmp = column_list; tmp; tmp = g_list_next(tmp)) {
|
|
column = tmp->data;
|
|
id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), MODEL_COLUMN));
|
|
if (id != wanted)
|
|
continue;
|
|
found = column;
|
|
break;
|
|
}
|
|
g_list_free(column_list);
|
|
|
|
// LEAVE("column %p", found);
|
|
return found;
|
|
}
|
|
|
|
/** Find a tree column given the "pref name" used with gconf. This
|
|
* function simply runs the list of all (visible and invisible)
|
|
* columns looking for a match. Column names were attached to each
|
|
* column at the time the column was created.
|
|
*
|
|
* @param view The visible tree widget.
|
|
*
|
|
* @param id The "pref name" to find.
|
|
*
|
|
*/
|
|
GtkTreeViewColumn *
|
|
gnc_tree_view_find_column_by_name (GncTreeView *view,
|
|
const gchar *wanted)
|
|
{
|
|
GtkTreeViewColumn *column, *found = NULL;
|
|
GList *column_list, *tmp;
|
|
const gchar *name;
|
|
|
|
// ENTER("view %p, wanted %s", view, wanted);
|
|
column_list = gtk_tree_view_get_columns(GTK_TREE_VIEW(view));
|
|
for (tmp = column_list; tmp; tmp = g_list_next(tmp)) {
|
|
column = tmp->data;
|
|
name = g_object_get_data(G_OBJECT(column), PREF_NAME);
|
|
if (!name || (strcmp(name, wanted) != 0))
|
|
continue;
|
|
found = column;
|
|
break;
|
|
}
|
|
g_list_free(column_list);
|
|
|
|
// LEAVE("column %p", found);
|
|
return found;
|
|
}
|
|
|
|
/** @} */
|
|
|
|
/************************************************************/
|
|
/* Tree Callbacks */
|
|
/************************************************************/
|
|
|
|
/** @name Gnc Tree View Callback Functions
|
|
@{ */
|
|
|
|
/** This function is called to determine whether it is acceptable to
|
|
* drop a dragged tree column at a given location. This function
|
|
* will be called multiple times by the GtkTreeView code while the
|
|
* user is dragging a column. Each call is a check to see if the
|
|
* proposed location is an acceptable place to drop the column. In
|
|
* the case of a GncTreeView, the only drop locations that are
|
|
* prohibited are on the right side of the visible tree.
|
|
*
|
|
* @param view The visible tree widget.
|
|
*
|
|
* @param column The column being dragged.
|
|
*
|
|
* @param prev_column The column before (left) of the proposed drop
|
|
* location.
|
|
*
|
|
* @param next_column The column after (right) of the proposed drop
|
|
* location.
|
|
*
|
|
* @param data Unused.
|
|
*
|
|
* @return TRUE if this drop location is acceptable. FALSE if not
|
|
* acceptable.
|
|
*
|
|
* @internal
|
|
*/
|
|
static gboolean
|
|
gnc_tree_view_drop_ok_cb (GtkTreeView *view,
|
|
GtkTreeViewColumn *column,
|
|
GtkTreeViewColumn *prev_column,
|
|
GtkTreeViewColumn *next_column,
|
|
gpointer data)
|
|
{
|
|
const gchar *pref_name;
|
|
|
|
/* Should we allow a drop at the left side of the tree view before
|
|
* the widget to open a new display level? I can think of cases
|
|
* where the user might want to do this with a checkbox column. */
|
|
if (prev_column == NULL)
|
|
return TRUE;
|
|
|
|
/* Do not allow a drop at the right side of the tree view after the
|
|
* column selection widget. */
|
|
if (next_column == NULL)
|
|
return FALSE;
|
|
|
|
/* Columns without pref names are considered fixed at the right hand
|
|
* side of the view. At the time of this writing, the only two are
|
|
* the column where the "column selection widget" is stored, and the
|
|
* "padding" column to the left of that where extra view space ends
|
|
* up. */
|
|
pref_name = g_object_get_data(G_OBJECT(prev_column), PREF_NAME);
|
|
if (!pref_name)
|
|
return FALSE;
|
|
|
|
/* Everything else is allowed. */
|
|
return TRUE;
|
|
}
|
|
|
|
/** This function is called when the sort order has changed on the
|
|
* underlying GtkTreeModel. It propagates these changes to gconf to
|
|
* keep it in sync with the user's view of the tree.
|
|
*
|
|
* @param sortable The underlying sortable model.
|
|
*
|
|
* @param view A pointer to the GncTreeView that displays the model
|
|
* data.
|
|
*
|
|
* @internal
|
|
*/
|
|
static void
|
|
gtk_tree_view_sort_column_changed_cb (GtkTreeSortable *treesortable,
|
|
GncTreeView *view)
|
|
{
|
|
GncTreeViewPrivate *priv;
|
|
GtkTreeViewColumn *column;
|
|
const gchar *gconf_section;
|
|
gchar *column_pref_name;
|
|
GtkSortType order;
|
|
gint id;
|
|
|
|
g_return_if_fail(GTK_IS_TREE_SORTABLE(treesortable));
|
|
g_return_if_fail(GNC_IS_TREE_VIEW(view));
|
|
|
|
ENTER(" ");
|
|
priv = GNC_TREE_VIEW_GET_PRIVATE(view);
|
|
if (!priv->gconf_section) {
|
|
LEAVE("no gconf section");
|
|
return;
|
|
}
|
|
|
|
/* Set defaults, then extract data from the model */
|
|
if (!gtk_tree_sortable_get_sort_column_id(treesortable, &id, &order)) {
|
|
order = GTK_SORT_ASCENDING;
|
|
id = GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID;
|
|
}
|
|
column = view_column_find_by_model_id (view, id);
|
|
column_pref_name = g_object_get_data(G_OBJECT(column), PREF_NAME);
|
|
|
|
if (!column_pref_name)
|
|
column_pref_name = "none";
|
|
|
|
/* Store the values in gconf */
|
|
gconf_section = priv->gconf_section;
|
|
gnc_gconf_set_string(gconf_section, GCONF_KEY_SORT_COLUMN,
|
|
column_pref_name, NULL);
|
|
gnc_gconf_set_string(gconf_section, GCONF_KEY_SORT_ORDER,
|
|
gnc_enum_to_nick(GTK_TYPE_SORT_TYPE, order), NULL);
|
|
LEAVE(" ");
|
|
}
|
|
|
|
/** This function is called when the column order has changed in the
|
|
* gtk tree view. It propagates these changes to gconf to keep it in
|
|
* sync with the user's view of the tree. This function runs the
|
|
* list of all tree column data structures, building a second list of
|
|
* column names. This second list is passed to gconf for storing.
|
|
* Only columns that have names can be saved in this fashion.
|
|
*
|
|
* @param view The tree view.
|
|
*
|
|
* @param data Unused.
|
|
*
|
|
* @internal
|
|
*/
|
|
static void
|
|
gtk_tree_view_columns_changed_cb (GncTreeView *view,
|
|
gpointer data)
|
|
{
|
|
GncTreeViewPrivate *priv;
|
|
GList *column_list, *tmp;
|
|
GSList *column_names = NULL;
|
|
gchar *name;
|
|
|
|
//ENTER("view %p, data %p", view, data);
|
|
priv = GNC_TREE_VIEW_GET_PRIVATE(view);
|
|
column_list = gtk_tree_view_get_columns(GTK_TREE_VIEW(view));
|
|
for (tmp = column_list; tmp; tmp = g_list_next(tmp)) {
|
|
name = g_object_get_data(tmp->data, PREF_NAME);
|
|
if (!name)
|
|
continue;
|
|
column_names = g_slist_append(column_names, name);
|
|
//DEBUG("%s", name);
|
|
}
|
|
g_list_free(column_list);
|
|
|
|
gnc_gconf_set_list(priv->gconf_section, GCONF_KEY_COLUMN_ORDER,
|
|
GCONF_VALUE_STRING, column_names, NULL);
|
|
g_slist_free(column_names);
|
|
//LEAVE(" ");
|
|
}
|
|
|
|
/** This is the helper function for gtk_tree_view_size_allocate_cb().
|
|
* It compares the actual column width to the width as stored in
|
|
* gconf. If the two are different, it the updates gconf with the
|
|
* actual width. This will trigger a notification from gconf that
|
|
* the size has changes, and that code must be smart enough to
|
|
* prevent an infinite loop. Not storing unchanged values prevents
|
|
* spurious callbacks from gconf and just saves processing time.
|
|
*
|
|
* @param column The column whose size should be updated in gconf..
|
|
*
|
|
* @param view The tree view.
|
|
*
|
|
* @internal
|
|
*/
|
|
static void
|
|
gtk_tree_view_size_allocate_helper (GtkTreeViewColumn *column,
|
|
GncTreeView *view)
|
|
{
|
|
GncTreeViewPrivate *priv;
|
|
const gchar *column_pref_name;
|
|
gint new_width, current_width;
|
|
gchar *key;
|
|
|
|
g_return_if_fail(GTK_IS_TREE_VIEW_COLUMN(column));
|
|
g_return_if_fail(GNC_IS_TREE_VIEW(view));
|
|
|
|
/* Any updates to be made? */
|
|
priv = GNC_TREE_VIEW_GET_PRIVATE(view);
|
|
column_pref_name = g_object_get_data(G_OBJECT(column), PREF_NAME);
|
|
if (!column_pref_name)
|
|
return;
|
|
new_width = gtk_tree_view_column_get_width (column);
|
|
if (!new_width)
|
|
return;
|
|
|
|
/* Do it */
|
|
key = g_strdup_printf("%s_%s", column_pref_name, GCONF_KEY_WIDTH);
|
|
current_width = gnc_gconf_get_int(priv->gconf_section, key, NULL);
|
|
if (new_width != current_width) {
|
|
gnc_gconf_set_int(priv->gconf_section, key, new_width, NULL);
|
|
DEBUG("set %s width to %d", column_pref_name, new_width);
|
|
}
|
|
g_free(key);
|
|
}
|
|
|
|
/** This function is called when the tree size has been reallocated.
|
|
* When called, one of two events has occurred. 1) The column sizes
|
|
* within the tree have changed. 2) The window containing the tree
|
|
* has been resized. This code is concerned with case one. Case two
|
|
* is a don't care, unless the window resizing has caused a column
|
|
* resizing.
|
|
*
|
|
* This function simply runs the list of all tree columns, calling the
|
|
* gtk_tree_view_size_allocate_helper() function on each column.
|
|
* There is no callback that can be used for notification of an
|
|
* individual column change. Its this or nothing.
|
|
*
|
|
* @param widget The tree view.
|
|
*
|
|
* @param allocation Unused.
|
|
*
|
|
* @param data Unused.
|
|
*
|
|
* @internal
|
|
*/
|
|
static void
|
|
gtk_tree_view_size_allocate_cb (GtkWidget *widget,
|
|
GtkAllocation *allocation,
|
|
gpointer data)
|
|
{
|
|
GncTreeView *view;
|
|
GList *column_list;
|
|
|
|
g_return_if_fail(GNC_IS_TREE_VIEW(widget));
|
|
|
|
view = GNC_TREE_VIEW(widget);
|
|
column_list = gtk_tree_view_get_columns(GTK_TREE_VIEW(view));
|
|
g_list_foreach(column_list, (GFunc)gtk_tree_view_size_allocate_helper, view);
|
|
g_list_free(column_list);
|
|
}
|
|
|
|
/** @} */
|
|
|
|
/************************************************************/
|
|
/* Gconf Setup / Callbacks */
|
|
/************************************************************/
|
|
|
|
/** @name Gnc Tree View Gconf Callback / Related Functions
|
|
@{ */
|
|
|
|
/** Determine the visibility of a column. This function first looks
|
|
* for columns specially marked to be always visible, or columns
|
|
* without a preference name. These are always shown. Next, this
|
|
* function checks to see if gconf is responsible for this view *and*
|
|
* that gconf visibility keys have been seen. (This handles the
|
|
* 'first run' case where gconf should manage a view but no keys yet
|
|
* exist in gconf.) If so, the gconf visibility key is returned.
|
|
* Otherwise the "'default visible column' list is checked and a
|
|
* value of TRUE returned if the pref name is found, otherwise FALSE.
|
|
*
|
|
* @param view A GncTreeView.
|
|
*
|
|
* @param column The GtkTreeViewColumn in question. Either this
|
|
* value or the pref_name parameter must be non-NULL.
|
|
*
|
|
* @param pref_name The name of the column in question. Either this
|
|
* value or the column parameter must be non-NULL.
|
|
*
|
|
* @returns TRUE if the column should be visible. FALSE otherwise.
|
|
*
|
|
* @internal
|
|
*/
|
|
static gboolean
|
|
gnc_tree_view_column_visible (GncTreeView *view,
|
|
GtkTreeViewColumn *column,
|
|
const gchar *pref_name)
|
|
{
|
|
GncTreeViewPrivate *priv;
|
|
gboolean visible;
|
|
gchar *key;
|
|
|
|
ENTER("column %p, name %s", column, pref_name ? pref_name : "(null)");
|
|
priv = GNC_TREE_VIEW_GET_PRIVATE(view);
|
|
if (column) {
|
|
if (g_object_get_data(G_OBJECT(column), ALWAYS_VISIBLE)) {
|
|
LEAVE("1, first column");
|
|
return TRUE;
|
|
}
|
|
pref_name = (gchar *)g_object_get_data(G_OBJECT(column), PREF_NAME);
|
|
DEBUG("pref_name is %s", pref_name ? pref_name : "(null)");
|
|
}
|
|
|
|
if (!pref_name) {
|
|
LEAVE("1, no pref name");
|
|
return TRUE;
|
|
}
|
|
|
|
/* Using gconf? */
|
|
if (priv->gconf_section) {
|
|
if (priv->seen_gconf_visibility) {
|
|
key = g_strdup_printf("%s_%s", pref_name, GCONF_KEY_VISIBLE);
|
|
visible = gnc_gconf_get_bool(priv->gconf_section, key, NULL);
|
|
g_free(key);
|
|
LEAVE("%d, gconf visibility", visible);
|
|
return visible;
|
|
}
|
|
|
|
visible = column ?
|
|
(g_object_get_data(G_OBJECT(column), DEFAULT_VISIBLE) != NULL) : FALSE;
|
|
LEAVE("%d, gconf but using defaults", visible);
|
|
return visible;
|
|
}
|
|
|
|
/* Check the default columns list */
|
|
visible = column ?
|
|
(g_object_get_data(G_OBJECT(column), DEFAULT_VISIBLE) != NULL) : FALSE;
|
|
LEAVE("defaults says %d", visible);
|
|
return visible;
|
|
}
|
|
|
|
/** This function updates the visibility of a single column. It
|
|
* checks if the column should be visible, and if so tells the view
|
|
* to show the column and (if needed) updates the gconf database.
|
|
*
|
|
* @param column The column whose visibility should be updated.
|
|
*
|
|
* @param view The GncTreeView containing the column.
|
|
*
|
|
* @internal
|
|
*/
|
|
static void
|
|
gnc_tree_view_update_visibility (GtkTreeViewColumn *column,
|
|
GncTreeView *view)
|
|
{
|
|
GncTreeViewPrivate *priv;
|
|
gchar *name, *key;
|
|
gboolean visible;
|
|
|
|
g_return_if_fail(GTK_IS_TREE_VIEW_COLUMN(column));
|
|
g_return_if_fail(GNC_IS_TREE_VIEW(view));
|
|
|
|
ENTER(" ");
|
|
priv = GNC_TREE_VIEW_GET_PRIVATE(view);
|
|
visible = gnc_tree_view_column_visible(view, column, NULL);
|
|
gtk_tree_view_column_set_visible(column, visible);
|
|
if (priv->gconf_section) {
|
|
name = (gchar *)g_object_get_data(G_OBJECT(column), PREF_NAME);
|
|
if (!name) {
|
|
LEAVE("no pref name");
|
|
return;
|
|
}
|
|
key = g_strdup_printf("%s_%s", name, GCONF_KEY_VISIBLE);
|
|
gnc_gconf_set_bool(priv->gconf_section, key, visible, NULL);
|
|
g_free(key);
|
|
LEAVE("made %s, set gconf key", visible ? "visible" : "invisible");
|
|
return;
|
|
}
|
|
LEAVE("made %s", visible ? "visible" : "invisible");
|
|
}
|
|
|
|
/** This function is called via the gconf notification callback when
|
|
* the sort order has changed in gconf. It must update the model
|
|
* sort order to match what is specified in gconf. The sort column
|
|
* used will be whatever is the current sort column. This function
|
|
* is careful to prevent an infinite loop by blocking the
|
|
* "sort-column-changed" callback that is attached to the model.
|
|
*
|
|
* @param widget The tree view.
|
|
*
|
|
* @param name The sort order enum (in string form). Either
|
|
* "ascending" or "descending".
|
|
*
|
|
* @internal
|
|
*/
|
|
static void
|
|
gnc_tree_view_set_sort_order (GncTreeView *view,
|
|
const gchar *name)
|
|
{
|
|
GncTreeViewPrivate *priv;
|
|
GtkTreeModel *s_model;
|
|
GtkSortType order;
|
|
gint current;
|
|
|
|
priv = GNC_TREE_VIEW_GET_PRIVATE(view);
|
|
s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
|
|
if (!s_model)
|
|
return;
|
|
order = gnc_enum_from_nick(GTK_TYPE_SORT_TYPE, name, GTK_SORT_ASCENDING);
|
|
if (!gtk_tree_sortable_get_sort_column_id(GTK_TREE_SORTABLE(s_model),
|
|
¤t, NULL))
|
|
current = GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID;
|
|
g_signal_handler_block(s_model, priv->sort_column_changed_cb_id);
|
|
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(s_model),
|
|
current, order);
|
|
g_signal_handler_unblock(s_model, priv->sort_column_changed_cb_id);
|
|
DEBUG("sort_order set to %s", gnc_enum_to_nick(GTK_TYPE_SORT_TYPE, order));
|
|
}
|
|
|
|
/** This function is called via the gconf notification callback when
|
|
* the sort column has changed in gconf. It must update the model
|
|
* sort column to match what is specified in gconf. The sort order
|
|
* used for this column will be whatever sort order is in use on the
|
|
* current sort column. This function is careful to prevent an
|
|
* infinite loop by blocking the "sort-column-changed" callback that
|
|
* is attached to the model.
|
|
*
|
|
* @param widget The tree view.
|
|
*
|
|
* @param name The name of the column that should be made the sort column.
|
|
*
|
|
* @internal
|
|
*/
|
|
static void
|
|
gnc_tree_view_set_sort_column (GncTreeView *view,
|
|
const gchar *name)
|
|
{
|
|
GncTreeViewPrivate *priv;
|
|
GtkTreeModel *s_model;
|
|
GtkTreeViewColumn *column;
|
|
GtkSortType order;
|
|
gint model_column, current;
|
|
|
|
priv = GNC_TREE_VIEW_GET_PRIVATE(view);
|
|
column = gnc_tree_view_find_column_by_name(view, name);
|
|
if (!column)
|
|
return;
|
|
|
|
model_column =
|
|
GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), MODEL_COLUMN));
|
|
if (model_column == GNC_TREE_VIEW_COLUMN_DATA_NONE)
|
|
return;
|
|
|
|
s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
|
|
if (!gtk_tree_sortable_get_sort_column_id(GTK_TREE_SORTABLE(s_model),
|
|
¤t, &order))
|
|
order = GTK_SORT_ASCENDING;
|
|
g_signal_handler_block(s_model, priv->sort_column_changed_cb_id);
|
|
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(s_model),
|
|
model_column, order);
|
|
g_signal_handler_unblock(s_model, priv->sort_column_changed_cb_id);
|
|
DEBUG("sort column set to %s", name);
|
|
}
|
|
|
|
/** This function is called via the gconf notification callback when
|
|
* the column order has changed in gconf. It must update the order
|
|
* of the columns in the model to match what is specified in gconf.
|
|
* This function is careful to prevent an infinite loop by blocking
|
|
* the "columns-changed" callback that is attached to the view.
|
|
*
|
|
* @param widget The tree view.
|
|
*
|
|
* @param name A list of pointers to strings. These strings are the
|
|
* names of the columns in the order they should appear.
|
|
*
|
|
* @internal
|
|
*/
|
|
static void
|
|
gnc_tree_view_set_column_order (GncTreeView *view,
|
|
const GSList *column_names)
|
|
{
|
|
GncTreeViewPrivate *priv;
|
|
GtkTreeViewColumn *column, *prev;
|
|
const gchar *name;
|
|
const GSList *tmp;
|
|
GSList *columns;
|
|
|
|
/* First, convert from names to pointers */
|
|
ENTER(" ");
|
|
priv = GNC_TREE_VIEW_GET_PRIVATE(view);
|
|
columns = NULL;
|
|
for (tmp = column_names; tmp; tmp = g_slist_next(tmp)) {
|
|
name = gconf_value_get_string(tmp->data);
|
|
column = gnc_tree_view_find_column_by_name(view, name);
|
|
if (!column)
|
|
continue;
|
|
columns = g_slist_append(columns, column);
|
|
}
|
|
|
|
/* Then reorder the columns */
|
|
g_signal_handler_block(view, priv->columns_changed_cb_id);
|
|
for (prev = NULL, tmp = columns; tmp; tmp = g_slist_next(tmp)) {
|
|
column = tmp->data;
|
|
gtk_tree_view_move_column_after(GTK_TREE_VIEW(view), column, prev);
|
|
prev = column;
|
|
}
|
|
g_signal_handler_unblock(view, priv->columns_changed_cb_id);
|
|
|
|
/* Clean up */
|
|
g_slist_free(columns);
|
|
LEAVE("column order set");
|
|
}
|
|
|
|
/** This function is called via the gconf notification callback when
|
|
* any change has occurred. It must update the appropriate aspect of
|
|
* the model/view to match what is specified in gconf. Some of the
|
|
* updates are handled in this function, while others are handled by
|
|
* other helper functions.
|
|
*
|
|
* @param client A pointer to the gconf client object from which
|
|
* detected a change in gconf. Unused by this function.
|
|
*
|
|
* @cnxn_id The identifier for the notification that this callback
|
|
* represents. Unused since this code only has a single notification
|
|
* request.
|
|
*
|
|
* @entry A pointer to the key/value pair in gconf that changed.
|
|
*
|
|
* @param data The tree view.
|
|
*
|
|
* @internal
|
|
*/
|
|
static void
|
|
gnc_tree_view_gconf_changed (GConfClient *client,
|
|
guint cnxn_id,
|
|
GConfEntry *entry,
|
|
gpointer data)
|
|
{
|
|
GncTreeView *view;
|
|
GncTreeViewPrivate *priv;
|
|
GtkTreeViewColumn *column;
|
|
GConfValue *value;
|
|
const gchar *key, *local;
|
|
gchar *column_name, *type_name;
|
|
gboolean known;
|
|
gint width;
|
|
|
|
g_return_if_fail(GNC_IS_TREE_VIEW(data));
|
|
|
|
ENTER(" ");
|
|
view = GNC_TREE_VIEW(data);
|
|
priv = GNC_TREE_VIEW_GET_PRIVATE(view);
|
|
key = gconf_entry_get_key(entry);
|
|
value = gconf_entry_get_value(entry);
|
|
if (!value) {
|
|
/* Values can be unset */
|
|
LEAVE("Unset valued for %s", key);
|
|
return;
|
|
}
|
|
|
|
DEBUG("Key %s, value %p", key, value);
|
|
local = strrchr(key, '/')+1;
|
|
if (strcmp(local, GCONF_KEY_SORT_COLUMN) == 0) {
|
|
gnc_tree_view_set_sort_column(view, gconf_value_get_string(value));
|
|
} else if (strcmp(local, GCONF_KEY_SORT_ORDER) == 0) {
|
|
gnc_tree_view_set_sort_order(view, gconf_value_get_string(value));
|
|
} else if (strcmp(local, GCONF_KEY_COLUMN_ORDER) == 0) {
|
|
gnc_tree_view_set_column_order(view, gconf_value_get_list(value));
|
|
} else {
|
|
/* Make a copy of the local part of the key so it can be split
|
|
* into column name and key type */
|
|
known = FALSE;
|
|
column_name = strdup(local);
|
|
type_name = strrchr(column_name, '_');
|
|
*type_name++ = '\0';
|
|
|
|
if (strcmp(type_name, GCONF_KEY_VISIBLE) == 0) {
|
|
priv->seen_gconf_visibility = TRUE;
|
|
column = gnc_tree_view_find_column_by_name(view, column_name);
|
|
if (column) {
|
|
known = TRUE;
|
|
if (!g_object_get_data(G_OBJECT(column), ALWAYS_VISIBLE)) {
|
|
gtk_tree_view_column_set_visible(column, gconf_value_get_bool(value));
|
|
}
|
|
}
|
|
} else if (strcmp(type_name, GCONF_KEY_WIDTH) == 0) {
|
|
width = gconf_value_get_int(value);
|
|
column = gnc_tree_view_find_column_by_name(view, column_name);
|
|
if (column) {
|
|
known = TRUE;
|
|
if (width && (width != gtk_tree_view_column_get_width(column))) {
|
|
gtk_tree_view_column_set_fixed_width(column, width);
|
|
}
|
|
}
|
|
}
|
|
if (!known) {
|
|
DEBUG("Ignored key %s", key);
|
|
}
|
|
g_free(column_name);
|
|
}
|
|
LEAVE(" ");
|
|
}
|
|
|
|
/** This function is called force synchronization between the gconf
|
|
* database and a tree view. It will be called for major events like
|
|
* assigning a gconf section to a tree view. All values in gconf
|
|
* will be propagated into the view. This function simply requests a
|
|
* list of all key in the section from gconf, then feeds them one at
|
|
* a time to gnc_tree_view_gconf_changed().
|
|
*
|
|
* @param view The tree view.
|
|
*
|
|
* @internal
|
|
*/
|
|
static void
|
|
gnc_tree_view_gconf_force_update (GncTreeView *view)
|
|
{
|
|
GncTreeViewPrivate *priv;
|
|
GSList *all_entries, *etmp;
|
|
GList *columns;
|
|
|
|
ENTER("view %p", view);
|
|
priv = GNC_TREE_VIEW_GET_PRIVATE(view);
|
|
all_entries = gnc_gconf_client_all_entries(priv->gconf_section);
|
|
|
|
/* Set a flag indicating that the gconf data section may be empty.
|
|
* It will be checked later on and appropriate action taken if its
|
|
* still set. */
|
|
priv->seen_gconf_visibility = FALSE;
|
|
|
|
/* Pull all the entries from gconf */
|
|
for (etmp = all_entries; etmp; etmp = g_slist_next(etmp)) {
|
|
gnc_tree_view_gconf_changed(NULL, 0, etmp->data, view);
|
|
gconf_entry_free(etmp->data);
|
|
}
|
|
g_slist_free(all_entries);
|
|
|
|
/* No visibilities seen yet. Write out any columns we may have */
|
|
if (!priv->seen_gconf_visibility) {
|
|
columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(view));
|
|
g_list_foreach(columns, (GFunc)gnc_tree_view_update_visibility, view);
|
|
g_list_free(columns);
|
|
}
|
|
|
|
LEAVE(" ");
|
|
}
|
|
|
|
/** This internal function is called to disconnect a tree view from a
|
|
* gconf section. It first disconnects any signals that are in
|
|
* effect on the model/view, then removes the gconf notification
|
|
* callback, then clears the gconf section string. This function may
|
|
* be called at any time; either when the user wants to disconnect or
|
|
* when the view object is being destroyed.
|
|
*
|
|
* @param view The tree view.
|
|
*
|
|
* @internal
|
|
*/
|
|
static void
|
|
gnc_tree_view_remove_gconf(GncTreeView *view)
|
|
{
|
|
GncTreeViewPrivate *priv;
|
|
GtkTreeModel *model;
|
|
|
|
ENTER(" ");
|
|
priv = GNC_TREE_VIEW_GET_PRIVATE(view);
|
|
if (!priv->gconf_section) {
|
|
LEAVE("no gconf section");
|
|
return;
|
|
}
|
|
|
|
priv = GNC_TREE_VIEW_GET_PRIVATE(view);
|
|
if (priv->sort_column_changed_cb_id) {
|
|
model = gtk_tree_view_get_model (GTK_TREE_VIEW(view));
|
|
if (model) {
|
|
DEBUG("removing sort_column_changed callback (# %ld)",
|
|
priv->sort_column_changed_cb_id);
|
|
g_signal_handler_disconnect(GTK_TREE_SORTABLE(model),
|
|
priv->sort_column_changed_cb_id);
|
|
priv->sort_column_changed_cb_id = 0;
|
|
}
|
|
}
|
|
|
|
if (priv->columns_changed_cb_id) {
|
|
DEBUG("removing columns_changed callback (# %ld)",
|
|
priv->columns_changed_cb_id);
|
|
g_signal_handler_disconnect(view, priv->columns_changed_cb_id);
|
|
priv->columns_changed_cb_id = 0;
|
|
}
|
|
|
|
if (priv->size_allocate_cb_id) {
|
|
DEBUG("removing size_allocate callback (# %ld)",
|
|
priv->size_allocate_cb_id);
|
|
g_signal_handler_disconnect(view, priv->size_allocate_cb_id);
|
|
priv->size_allocate_cb_id = 0;
|
|
}
|
|
|
|
DEBUG("removing gconf notification");
|
|
gnc_gconf_remove_notification(G_OBJECT(view), priv->gconf_section,
|
|
GNC_TREE_VIEW_NAME);
|
|
g_free(priv->gconf_section);
|
|
priv->gconf_section = NULL;
|
|
LEAVE(" ");
|
|
}
|
|
|
|
/** This function is called to set up or remove an association between
|
|
* a gconf section and the display of a view. It will first remove
|
|
* any existing association, and then install the new one. This
|
|
* involves storing the gconf section value, requesting notification
|
|
* from gconf of any changes to keys in that section, then attaching
|
|
* several signals to catch user changes to the view.
|
|
*
|
|
* Parameters are defined in gnc-tree-view.h
|
|
*/
|
|
void
|
|
gnc_tree_view_set_gconf_section (GncTreeView *view,
|
|
const gchar *section)
|
|
{
|
|
GncTreeViewPrivate *priv;
|
|
GtkTreeModel *model;
|
|
gulong id;
|
|
|
|
g_return_if_fail(GNC_IS_TREE_VIEW(view));
|
|
|
|
ENTER("view %p, section %s", view, section);
|
|
gnc_tree_view_remove_gconf(view);
|
|
|
|
if (!section) {
|
|
LEAVE("cleared gconf section");
|
|
return;
|
|
}
|
|
|
|
/* Catch changes in gconf. Propagate to view. */
|
|
priv = GNC_TREE_VIEW_GET_PRIVATE(view);
|
|
priv->gconf_section = g_strdup(section);
|
|
gnc_gconf_add_notification(G_OBJECT(view), section,
|
|
gnc_tree_view_gconf_changed,
|
|
GNC_TREE_VIEW_NAME);
|
|
|
|
/* Catch changes to the sort column. Propagate to gconf. The key can
|
|
* be set before the model, so the code must handle that case. */
|
|
model = gtk_tree_view_get_model (GTK_TREE_VIEW(view));
|
|
if (model)
|
|
priv->sort_column_changed_cb_id =
|
|
g_signal_connect(GTK_TREE_SORTABLE(model), "sort-column-changed",
|
|
(GCallback)gtk_tree_view_sort_column_changed_cb, view);
|
|
|
|
/* Catch changes to the column order. Propagate to gconf */
|
|
id = g_signal_connect(view, "columns-changed",
|
|
(GCallback)gtk_tree_view_columns_changed_cb, NULL);
|
|
priv->columns_changed_cb_id = id;
|
|
|
|
/* Catch changes to the column width. Propagate to gconf */
|
|
id = g_signal_connect(view, "size-allocate",
|
|
(GCallback)gtk_tree_view_size_allocate_cb, NULL);
|
|
priv->size_allocate_cb_id = id;
|
|
|
|
/* Force an update of the view with all items from gconf. */
|
|
gnc_tree_view_gconf_force_update(view);
|
|
|
|
/* Rebuild the column visibility menu */
|
|
gnc_tree_view_build_column_menu(view);
|
|
LEAVE("set gconf section");
|
|
}
|
|
|
|
/** This function is called to get the current association between a
|
|
* gconf section and the display of a view. It returns the same
|
|
* value passed to gnc_tree_view_set_gconf_section(); i.e. a string
|
|
* like "dialogs/edit_prices".
|
|
*
|
|
* Parameters are defined in gnc-tree-view.h
|
|
*/
|
|
const gchar *
|
|
gnc_tree_view_get_gconf_section (GncTreeView *view)
|
|
{
|
|
GncTreeViewPrivate *priv;
|
|
|
|
g_return_val_if_fail(GNC_IS_TREE_VIEW(view), NULL);
|
|
|
|
priv = GNC_TREE_VIEW_GET_PRIVATE(view);
|
|
return(priv->gconf_section);
|
|
}
|
|
|
|
|
|
/** @} */
|
|
|
|
/************************************************************/
|
|
/* Column Selection Menu */
|
|
/************************************************************/
|
|
|
|
/** @name Gnc Tree View Column Selection Menu Related Functions
|
|
@{ */
|
|
|
|
/** This function is called when an entry in the column selection menu
|
|
* is toggled. It must update gconf with the current value of the
|
|
* checkbox. This will trigger a callback to other functions which
|
|
* will then change the actual state of the view.
|
|
*
|
|
* @param checkmenuitem A checkbox visible in the menu.
|
|
*
|
|
* @param view The tree view.
|
|
*
|
|
* @internal
|
|
*/
|
|
static void
|
|
gnc_tree_view_menu_item_toggled (GtkCheckMenuItem *checkmenuitem,
|
|
GncTreeView *view)
|
|
{
|
|
GncTreeViewPrivate *priv;
|
|
gboolean value;
|
|
gchar *key;
|
|
|
|
g_return_if_fail(GTK_IS_CHECK_MENU_ITEM(checkmenuitem));
|
|
g_return_if_fail(GNC_IS_TREE_VIEW(view));
|
|
|
|
ENTER("checkmenuitem %p, view %p", checkmenuitem, view);
|
|
priv = GNC_TREE_VIEW_GET_PRIVATE(view);
|
|
if (!priv->gconf_section) {
|
|
LEAVE("no gconf section");
|
|
return;
|
|
}
|
|
|
|
key = g_object_get_data(G_OBJECT(checkmenuitem), GCONF_KEY);
|
|
value = gtk_check_menu_item_get_active(checkmenuitem);
|
|
gnc_gconf_set_bool(priv->gconf_section, key, value, NULL);
|
|
LEAVE("set gconf section %s, key %s, visible %d",
|
|
priv->gconf_section, key, value);
|
|
}
|
|
|
|
/** This function is called to create a single checkmenuitem in the
|
|
* column selection menu. It is called once for each column in the
|
|
* view. It creates a menu item for the corresponding column, and
|
|
* attaches to it a copy of the gconf key for this column's
|
|
* visibility. This makes the toggle callback function trivial.
|
|
*
|
|
* This function will create the column selection menu if one doesn't
|
|
* already exist.
|
|
*
|
|
* @param column Create a menu item for this column.
|
|
*
|
|
* @param view The tree view.
|
|
*
|
|
* @internal
|
|
*/
|
|
static void
|
|
gnc_tree_view_create_menu_item (GtkTreeViewColumn *column,
|
|
GncTreeView *view)
|
|
{
|
|
GncTreeViewPrivate *priv;
|
|
GtkWidget *widget;
|
|
const gchar *column_name, *pref_name;
|
|
gulong id;
|
|
gchar *key;
|
|
|
|
// ENTER("view %p, column %p", view, column);
|
|
priv = GNC_TREE_VIEW_GET_PRIVATE(view);
|
|
if (!priv->gconf_section) {
|
|
// LEAVE("no gconf section");
|
|
return;
|
|
}
|
|
|
|
pref_name = g_object_get_data(G_OBJECT(column), PREF_NAME);
|
|
if (!pref_name) {
|
|
// LEAVE("column has no pref_name");
|
|
return;
|
|
}
|
|
|
|
/* Create the menu if we don't have one already */
|
|
if (!priv->column_menu) {
|
|
priv->column_menu = gtk_menu_new();
|
|
g_object_ref(priv->column_menu);
|
|
gtk_object_sink(GTK_OBJECT(priv->column_menu));
|
|
}
|
|
|
|
/* Create the check menu item */
|
|
column_name = g_object_get_data(G_OBJECT(column), REAL_TITLE);
|
|
if (!column_name)
|
|
column_name = gtk_tree_view_column_get_title(column);
|
|
widget = gtk_check_menu_item_new_with_label(column_name);
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(priv->column_menu), widget);
|
|
|
|
/* Should never be able to hide the first column */
|
|
if (g_object_get_data(G_OBJECT(column), ALWAYS_VISIBLE)) {
|
|
g_object_set_data(G_OBJECT(widget), ALWAYS_VISIBLE, GINT_TO_POINTER(1));
|
|
gtk_widget_set_sensitive(widget, FALSE);
|
|
}
|
|
|
|
/* Set up the callback */
|
|
id = g_signal_connect(widget, "toggled",
|
|
(GCallback)gnc_tree_view_menu_item_toggled, view);
|
|
|
|
/* Store data on the widget for callbacks */
|
|
key = g_strdup_printf("%s_%s", pref_name, GCONF_KEY_VISIBLE);
|
|
g_object_set_data_full(G_OBJECT(widget), GCONF_KEY, key, g_free);
|
|
// LEAVE(" ");
|
|
}
|
|
|
|
/** This function is called to build the column selection menu. It
|
|
* first destroys any old column selection menu, then checks to see
|
|
* if a new menu should be built. If so, it calls the
|
|
* gnc_tree_view_create_menu_item() for each column in the view.
|
|
* This function is invoked then either the "gconf-section" or the
|
|
* "show-column-menu" property is changed on the view.
|
|
*
|
|
* @param view Build a selection menu for this tree view.
|
|
*
|
|
* @internal
|
|
*/
|
|
static void
|
|
gnc_tree_view_build_column_menu (GncTreeView *view)
|
|
{
|
|
GncTreeViewPrivate *priv;
|
|
GList *column_list;
|
|
|
|
g_return_if_fail(GNC_IS_TREE_VIEW(view));
|
|
|
|
ENTER("view %p", view);
|
|
priv = GNC_TREE_VIEW_GET_PRIVATE(view);
|
|
|
|
/* Destroy any old menu */
|
|
if (priv->column_menu) {
|
|
gtk_widget_unref(priv->column_menu);
|
|
priv->column_menu = NULL;
|
|
}
|
|
|
|
if (priv->show_column_menu && priv->gconf_section) {
|
|
/* Show the menu popup button */
|
|
if (priv->column_menu_column)
|
|
gtk_tree_view_column_set_visible(priv->column_menu_column, TRUE);
|
|
|
|
/* Now build a new menu */
|
|
column_list = gtk_tree_view_get_columns(GTK_TREE_VIEW(view));
|
|
g_list_foreach(column_list, (GFunc)gnc_tree_view_create_menu_item, view);
|
|
g_list_free(column_list);
|
|
} else {
|
|
/* Hide the menu popup button */
|
|
if (priv->column_menu_column)
|
|
gtk_tree_view_column_set_visible(priv->column_menu_column, FALSE);
|
|
}
|
|
LEAVE("menu: show %d, section %s", priv->show_column_menu,
|
|
priv->gconf_section ? priv->gconf_section : "(null)");
|
|
}
|
|
|
|
/** This function is called to synchronize the checkbox on a menu item
|
|
* with the current gconf visibility value for the corresponding
|
|
* column (which will be the visibility of the column if the rest of
|
|
* the code in this file is working correctly). It simply takes the
|
|
* gconf key attached to the menu item, reads it, and sets the menu
|
|
* item to the retrieved value. It does take care to block signals
|
|
* from the menu item to prevent updating gconf (since the value was
|
|
* just read from gconf).
|
|
*
|
|
* @param checkmenuitem The menu item to update.
|
|
*
|
|
* @param view The tree view.
|
|
*
|
|
* @internal
|
|
*/
|
|
static void
|
|
gnc_tree_view_update_column_menu_item (GtkCheckMenuItem *checkmenuitem,
|
|
GncTreeView *view)
|
|
{
|
|
GncTreeViewPrivate *priv;
|
|
gchar *key;
|
|
gboolean visible;
|
|
|
|
g_return_if_fail(GTK_IS_CHECK_MENU_ITEM(checkmenuitem));
|
|
g_return_if_fail(GNC_IS_TREE_VIEW(view));
|
|
|
|
priv = GNC_TREE_VIEW_GET_PRIVATE(view);
|
|
key = g_object_get_data(G_OBJECT(checkmenuitem), GCONF_KEY);
|
|
if (g_object_get_data(G_OBJECT(checkmenuitem), ALWAYS_VISIBLE)) {
|
|
visible = TRUE;
|
|
} else {
|
|
visible = gnc_gconf_get_bool(priv->gconf_section, key, NULL);
|
|
}
|
|
|
|
g_signal_handlers_block_by_func(checkmenuitem,
|
|
gnc_tree_view_menu_item_toggled, view);
|
|
gtk_check_menu_item_set_active(checkmenuitem, visible);
|
|
g_signal_handlers_unblock_by_func(checkmenuitem,
|
|
gnc_tree_view_menu_item_toggled, view);
|
|
}
|
|
|
|
/** This function when the user clicks on the button to show the
|
|
* column selection menu. It first synchronize the checkboxes on all
|
|
* menu item with the gconf visibility values. It then pops up the
|
|
* menu for the user to choose from.
|
|
*
|
|
* @param column The tree column containing the column selection
|
|
* button.
|
|
*
|
|
* @param view The tree view.
|
|
*
|
|
* @internal
|
|
*/
|
|
static void
|
|
gnc_tree_view_select_column_cb (GtkTreeViewColumn *column,
|
|
GncTreeView *view)
|
|
{
|
|
GncTreeViewPrivate *priv;
|
|
GtkWidget *widget, *menu;
|
|
|
|
g_return_if_fail(GTK_IS_TREE_VIEW_COLUMN(column));
|
|
g_return_if_fail(GNC_IS_TREE_VIEW(view));
|
|
|
|
priv = GNC_TREE_VIEW_GET_PRIVATE(view);
|
|
menu = priv->column_menu;
|
|
if (!menu)
|
|
return;
|
|
|
|
/* Synchronize the menu before display */
|
|
gtk_container_foreach(GTK_CONTAINER(menu),
|
|
(GtkCallback)gnc_tree_view_update_column_menu_item,
|
|
view);
|
|
|
|
/* Ensure all components are visible */
|
|
gtk_widget_show_all(menu);
|
|
|
|
/* Pop the menu up at the button */
|
|
widget = gtk_tree_view_column_get_widget(column);
|
|
gtk_menu_popup(GTK_MENU(priv->column_menu), NULL, GTK_WIDGET(menu),
|
|
NULL, NULL, 0, gtk_get_current_event_time());
|
|
}
|
|
|
|
|
|
void gnc_tree_view_expand_columns (GncTreeView *view,
|
|
gchar *first_column_name,
|
|
...)
|
|
{
|
|
GncTreeViewPrivate *priv;
|
|
GtkTreeViewColumn *column;
|
|
gboolean hide_spacer;
|
|
GList *columns, *tmp;
|
|
gchar *name, *pref_name;
|
|
va_list args;
|
|
|
|
g_return_if_fail (GNC_IS_TREE_VIEW (view));
|
|
ENTER(" ");
|
|
va_start (args, first_column_name);
|
|
priv = GNC_TREE_VIEW_GET_PRIVATE (view);
|
|
name = first_column_name;
|
|
hide_spacer = FALSE;
|
|
|
|
/* First disable the expand property on all (non-infrastructure) columns. */
|
|
columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(view));
|
|
for (tmp = columns; tmp; tmp = g_list_next(tmp)) {
|
|
column = tmp->data;
|
|
pref_name = g_object_get_data(G_OBJECT(column), PREF_NAME);
|
|
if (pref_name != NULL)
|
|
gtk_tree_view_column_set_expand(column, FALSE);
|
|
}
|
|
g_list_free(columns);
|
|
|
|
/* Now enable it on the requested columns. */
|
|
while (name != NULL) {
|
|
column = gnc_tree_view_find_column_by_name(view, name);
|
|
if (column != NULL) {
|
|
gtk_tree_view_column_set_expand(column, TRUE);
|
|
hide_spacer = TRUE;
|
|
}
|
|
name = va_arg (args, gchar*);
|
|
}
|
|
va_end (args);
|
|
|
|
gtk_tree_view_column_set_visible (priv->spacer_column, !hide_spacer);
|
|
gtk_tree_view_column_set_visible (priv->selection_column, !hide_spacer);
|
|
|
|
LEAVE(" ");
|
|
}
|
|
|
|
/** This function is called to set the "show-column-menu" property on
|
|
* this view. This function has no visible effect if the
|
|
* "gconf-section" property has not been set.
|
|
*
|
|
* Parameters are defined in gnc-tree-view.h
|
|
*/
|
|
void
|
|
gnc_tree_view_set_show_column_menu (GncTreeView *view,
|
|
gboolean visible)
|
|
{
|
|
GncTreeViewPrivate *priv;
|
|
|
|
g_return_if_fail(GNC_IS_TREE_VIEW(view));
|
|
|
|
ENTER("view %p, show menu %d", view, visible);
|
|
priv = GNC_TREE_VIEW_GET_PRIVATE(view);
|
|
priv->show_column_menu = visible;
|
|
gnc_tree_view_build_column_menu(view);
|
|
LEAVE(" ");
|
|
}
|
|
|
|
/** This function is called to get the current value of the
|
|
* "show-column-menu" property. It returns the same value passed to
|
|
* gnc_tree_view_set_show_menu_column().
|
|
*
|
|
* Parameters are defined in gnc-tree-view.h
|
|
*/
|
|
gboolean
|
|
gnc_tree_view_get_show_column_menu (GncTreeView *view)
|
|
{
|
|
GncTreeViewPrivate *priv;
|
|
|
|
g_return_val_if_fail(GNC_IS_TREE_VIEW(view), FALSE);
|
|
|
|
priv = GNC_TREE_VIEW_GET_PRIVATE(view);
|
|
return(priv->show_column_menu);
|
|
}
|
|
|
|
/** @} */
|
|
|
|
/************************************************************/
|
|
/* Tree View Creation */
|
|
/************************************************************/
|
|
|
|
/** Attach a data model to a visible GncTreeView widget. Users of
|
|
* this view object must use this function instead of directly
|
|
* calling the gtk_tree_view_set_model function. This function takes
|
|
* the additional step of attaching a callback function to the model
|
|
* to catch any changes to the sorting of the model. These changes
|
|
* are propagated into gconf by the callback function.
|
|
*
|
|
* Parameters are defined in gnc-tree-view.h
|
|
*/
|
|
void
|
|
gnc_tree_view_set_model(GncTreeView *view, GtkTreeModel *model)
|
|
{
|
|
GncTreeViewPrivate *priv;
|
|
GtkTreeModel *old_model;
|
|
|
|
/* Remove existing callback */
|
|
priv = GNC_TREE_VIEW_GET_PRIVATE(view);
|
|
if (priv->sort_column_changed_cb_id) {
|
|
old_model = gtk_tree_view_get_model (GTK_TREE_VIEW(view));
|
|
g_signal_handler_disconnect (old_model, priv->sort_column_changed_cb_id);
|
|
priv->sort_column_changed_cb_id = 0;
|
|
}
|
|
|
|
gtk_tree_view_set_model (GTK_TREE_VIEW(view), model);
|
|
|
|
/* Maybe add a new callback */
|
|
if (model && priv->gconf_section) {
|
|
priv->sort_column_changed_cb_id =
|
|
g_signal_connect(GTK_TREE_SORTABLE(model), "sort-column-changed",
|
|
(GCallback)gtk_tree_view_sort_column_changed_cb, view);
|
|
}
|
|
}
|
|
|
|
static gint
|
|
gnc_tree_view_count_visible_columns(GncTreeView *view)
|
|
{
|
|
GList *columns, *node;
|
|
gint count = 0;
|
|
|
|
columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(view));
|
|
for (node = columns; node; node = node->next) {
|
|
GtkTreeViewColumn *col = GTK_TREE_VIEW_COLUMN(node->data);
|
|
|
|
if (g_object_get_data(G_OBJECT(col), DEFAULT_VISIBLE) ||
|
|
g_object_get_data(G_OBJECT(col), ALWAYS_VISIBLE))
|
|
count++;
|
|
}
|
|
g_list_free(columns);
|
|
return count;
|
|
}
|
|
|
|
void
|
|
gnc_tree_view_configure_columns (GncTreeView *view)
|
|
{
|
|
GncTreeViewPrivate *priv;
|
|
GtkTreeViewColumn *column;
|
|
GList *columns;
|
|
gboolean hide_spacer;
|
|
|
|
g_return_if_fail(GNC_IS_TREE_VIEW(view));
|
|
|
|
ENTER(" ");
|
|
|
|
/* Update the view and gconf */
|
|
columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(view));
|
|
g_list_foreach(columns, (GFunc)gnc_tree_view_update_visibility, view);
|
|
g_list_free(columns);
|
|
|
|
priv = GNC_TREE_VIEW_GET_PRIVATE(view);
|
|
if (priv->gconf_section)
|
|
priv->seen_gconf_visibility = TRUE;
|
|
|
|
/* If only the first column is visible, hide the spacer and make that
|
|
* column expand. */
|
|
hide_spacer = (gnc_tree_view_count_visible_columns(view) == 1);
|
|
column = gtk_tree_view_get_column(GTK_TREE_VIEW(view), 0);
|
|
gtk_tree_view_column_set_expand(column, hide_spacer);
|
|
gtk_tree_view_column_set_visible(priv->spacer_column, !hide_spacer);
|
|
gtk_tree_view_column_set_visible(priv->selection_column, !hide_spacer);
|
|
|
|
LEAVE(" ");
|
|
}
|
|
|
|
|
|
/** This internal function sets a variety of common properties on a
|
|
* newly created GtkTreeViewColumn and its renderer.
|
|
*
|
|
* @param view A pointer to a generic GncTreeView.
|
|
*
|
|
* @param column The newly created tree view column.
|
|
*
|
|
* @param pref_name The internal name of this column. This name is
|
|
* used in several functions to look up the column, and it is also
|
|
* used to create the keys used to record the column width and
|
|
* visibility in gconf.
|
|
*
|
|
* @param data_column The index of the GtkTreeModel data column used
|
|
* to determine the data that will be displayed in this column for
|
|
* each row in the view. Use GNC_TREE_VIEW_COLUMN_DATA_NONE if you
|
|
* plan on using an non-model data source for this column.
|
|
*
|
|
* @param default_width The width this column should be if not
|
|
* specified by gconf. If the this value is zero, the column will
|
|
* be marked as automatically sized.
|
|
*
|
|
* @param resizable Whether to mark the column as user resizable.
|
|
* This marking is only relevant for fixed width columns.
|
|
*
|
|
* @param column_sort_function The function that GtkTreeModelSort
|
|
* will call to compare two rows to determine their displayed order.
|
|
*
|
|
* @internal
|
|
*/
|
|
static void
|
|
gnc_tree_view_column_properties (GncTreeView *view,
|
|
GtkTreeViewColumn *column,
|
|
const gchar *pref_name,
|
|
gint data_column,
|
|
gint default_width,
|
|
gboolean resizable,
|
|
GtkTreeIterCompareFunc column_sort_fn)
|
|
{
|
|
GncTreeViewPrivate *priv;
|
|
GtkTreeModel *s_model;
|
|
gboolean visible;
|
|
int width = 0;
|
|
gchar *key;
|
|
|
|
/* Set data used by other functions */
|
|
if (pref_name)
|
|
g_object_set_data(G_OBJECT(column), PREF_NAME, (gpointer)pref_name);
|
|
if (data_column == 0)
|
|
g_object_set_data(G_OBJECT(column), ALWAYS_VISIBLE, GINT_TO_POINTER(1));
|
|
g_object_set_data(G_OBJECT(column), MODEL_COLUMN,
|
|
GINT_TO_POINTER(data_column));
|
|
|
|
/* Get visibility */
|
|
visible = gnc_tree_view_column_visible(view, NULL, pref_name);
|
|
|
|
/* Get width */
|
|
priv = GNC_TREE_VIEW_GET_PRIVATE(view);
|
|
if (priv->gconf_section) {
|
|
key = g_strdup_printf("%s_%s", pref_name, GCONF_KEY_WIDTH);
|
|
width = gnc_gconf_get_int(priv->gconf_section, key, NULL);
|
|
g_free(key);
|
|
}
|
|
|
|
/* If gconf comes back with a width of zero (or there is no gconf
|
|
* width) the use the default width for the column. Allow for
|
|
* padding L and R of the displayed data. */
|
|
if (width == 0)
|
|
width = default_width + 10;
|
|
if (width == 0)
|
|
width = 10;
|
|
|
|
/* Set column attributes */
|
|
g_object_set(G_OBJECT(column),
|
|
"visible", visible,
|
|
"sizing", GTK_TREE_VIEW_COLUMN_FIXED,
|
|
"fixed-width", width,
|
|
"resizable", resizable && pref_name != NULL,
|
|
"reorderable", pref_name != NULL,
|
|
NULL);
|
|
|
|
s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
|
|
if (GTK_IS_TREE_SORTABLE(s_model)) {
|
|
gtk_tree_view_column_set_sort_column_id (column, data_column);
|
|
if (column_sort_fn) {
|
|
gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(s_model),
|
|
data_column, column_sort_fn,
|
|
GINT_TO_POINTER(data_column),
|
|
NULL /* destroy fn */);
|
|
}
|
|
}
|
|
|
|
/* Add to the column selection menu */
|
|
if (pref_name) {
|
|
gnc_tree_view_create_menu_item(column, view);
|
|
}
|
|
}
|
|
|
|
/** This function adds a new toggle column to a GncTreeView base view.
|
|
* It takes all the parameters necessary to hook a GtkTreeModel
|
|
* column to a GtkTreeViewColumn. It handles creating a tooltip to
|
|
* show the full title name, and setting the sort and edit callback
|
|
* functions. If the tree has a gconf section associated with it,
|
|
* this function also wires up the column so that its visibility and
|
|
* width are remembered.
|
|
*
|
|
* Parameters are defined in gnc-tree-view.h
|
|
*/
|
|
GtkTreeViewColumn *
|
|
gnc_tree_view_add_toggle_column (GncTreeView *view,
|
|
const gchar *column_title,
|
|
const gchar *column_short_title,
|
|
const gchar *pref_name,
|
|
gint model_data_column,
|
|
gint model_visibility_column,
|
|
GtkTreeIterCompareFunc column_sort_fn,
|
|
renderer_toggled toggle_edited_cb)
|
|
{
|
|
GncTreeViewPrivate *priv;
|
|
GtkTreeViewColumn *column;
|
|
GtkCellRenderer *renderer;
|
|
PangoLayout* layout;
|
|
int title_width;
|
|
|
|
g_return_val_if_fail (GNC_IS_TREE_VIEW(view), NULL);
|
|
|
|
priv = GNC_TREE_VIEW_GET_PRIVATE(view);
|
|
renderer = gtk_cell_renderer_toggle_new ();
|
|
column =
|
|
gtk_tree_view_column_new_with_attributes (column_short_title,
|
|
renderer,
|
|
"active", model_data_column,
|
|
NULL);
|
|
|
|
/* Add the full title to the object for menu creation */
|
|
g_object_set_data_full(G_OBJECT(column), REAL_TITLE,
|
|
g_strdup(column_title), g_free);
|
|
if (toggle_edited_cb)
|
|
g_signal_connect(G_OBJECT(renderer), "toggled",
|
|
G_CALLBACK(toggle_edited_cb), view);
|
|
|
|
if (model_visibility_column != GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS)
|
|
gtk_tree_view_column_add_attribute (column, renderer,
|
|
"visible", model_visibility_column);
|
|
|
|
layout = gtk_widget_create_pango_layout (GTK_WIDGET(view),
|
|
column_short_title);
|
|
pango_layout_get_pixel_size(layout, &title_width, NULL);
|
|
g_object_unref(layout);
|
|
|
|
gnc_tree_view_column_properties (view, column, pref_name, model_data_column,
|
|
title_width, FALSE, column_sort_fn);
|
|
|
|
gnc_tree_view_append_column (view, column);
|
|
|
|
/* Also add the full title to the object as a tooltip */
|
|
if (!priv->title_tips)
|
|
priv->title_tips = gtk_tooltips_new();
|
|
gtk_tooltips_set_tip(priv->title_tips, column->button, column_title, NULL);
|
|
|
|
return column;
|
|
}
|
|
|
|
/** This function adds a new text column to a GncTreeView base view.
|
|
* It takes all the parameters necessary to hook a GtkTreeModel
|
|
* column to a GtkTreeViewColumn. If the tree has a gconf section
|
|
* associated with it, this function also wires up the column so that
|
|
* its visibility and width are remembered.
|
|
*
|
|
* Parameters are defined in gnc-tree-view.h
|
|
*/
|
|
GtkTreeViewColumn *
|
|
gnc_tree_view_add_text_column (GncTreeView *view,
|
|
const gchar *column_title,
|
|
const gchar *pref_name,
|
|
const gchar *stock_icon_name,
|
|
const gchar *sizing_text,
|
|
gint model_data_column,
|
|
gint model_visibility_column,
|
|
GtkTreeIterCompareFunc column_sort_fn)
|
|
{
|
|
GtkTreeViewColumn *column;
|
|
GtkCellRenderer *renderer;
|
|
PangoLayout* layout;
|
|
int default_width, title_width;
|
|
|
|
g_return_val_if_fail (GNC_IS_TREE_VIEW(view), NULL);
|
|
|
|
column = gtk_tree_view_column_new ();
|
|
gtk_tree_view_column_set_title (column, column_title);
|
|
|
|
/* Set up an icon renderer if requested */
|
|
if (stock_icon_name) {
|
|
renderer = gtk_cell_renderer_pixbuf_new ();
|
|
g_object_set (renderer, "stock-id", stock_icon_name, NULL);
|
|
gtk_tree_view_column_pack_start (column, renderer, FALSE);
|
|
}
|
|
|
|
/* Set up a text renderer and attributes */
|
|
renderer = gtk_cell_renderer_text_new ();
|
|
gtk_tree_view_column_pack_start (column, renderer, TRUE);
|
|
|
|
/* Set renderer attributes controlled by the model */
|
|
if (model_data_column != GNC_TREE_VIEW_COLUMN_DATA_NONE)
|
|
gtk_tree_view_column_add_attribute (column, renderer,
|
|
"text", model_data_column);
|
|
if (model_visibility_column != GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS)
|
|
gtk_tree_view_column_add_attribute (column, renderer,
|
|
"visible", model_visibility_column);
|
|
|
|
/* Default size is the larger of the column title and the sizing text */
|
|
layout = gtk_widget_create_pango_layout (GTK_WIDGET(view), column_title);
|
|
pango_layout_get_pixel_size(layout, &title_width, NULL);
|
|
g_object_unref(layout);
|
|
layout = gtk_widget_create_pango_layout (GTK_WIDGET(view), sizing_text);
|
|
pango_layout_get_pixel_size(layout, &default_width, NULL);
|
|
g_object_unref(layout);
|
|
default_width = MAX(default_width, title_width);
|
|
if (default_width)
|
|
default_width += 10; /* padding on either side */
|
|
gnc_tree_view_column_properties (view, column, pref_name, model_data_column,
|
|
default_width, TRUE, column_sort_fn);
|
|
|
|
gnc_tree_view_append_column (view, column);
|
|
return column;
|
|
}
|
|
|
|
GtkTreeViewColumn *
|
|
gnc_tree_view_add_combo_column (GncTreeView *view,
|
|
const gchar *column_title,
|
|
const gchar *pref_name,
|
|
const gchar *sizing_text,
|
|
gint model_data_column,
|
|
gint model_visibility_column,
|
|
GtkTreeModel *combo_tree_model,
|
|
gint combo_model_text_column,
|
|
GtkTreeIterCompareFunc column_sort_fn)
|
|
{
|
|
GtkTreeViewColumn *column;
|
|
GtkCellRenderer *renderer;
|
|
PangoLayout* layout;
|
|
int default_width, title_width;
|
|
|
|
g_return_val_if_fail (GNC_IS_TREE_VIEW(view), NULL);
|
|
|
|
column = gtk_tree_view_column_new ();
|
|
gtk_tree_view_column_set_title (column, gettext(column_title));
|
|
|
|
/* Set up a renderer and attributes */
|
|
renderer = gtk_cell_renderer_combo_new ();
|
|
gtk_tree_view_column_pack_start (column, renderer, TRUE);
|
|
|
|
/* Set renderer attributes controlled by the model */
|
|
if (model_data_column != GNC_TREE_VIEW_COLUMN_DATA_NONE)
|
|
gtk_tree_view_column_add_attribute (column, renderer,
|
|
"text", model_data_column);
|
|
if (model_visibility_column != GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS)
|
|
gtk_tree_view_column_add_attribute (column, renderer,
|
|
"visible", model_visibility_column);
|
|
|
|
/* Default size is the larger of the column title and the sizing text */
|
|
layout = gtk_widget_create_pango_layout (GTK_WIDGET(view), column_title);
|
|
pango_layout_get_pixel_size(layout, &title_width, NULL);
|
|
g_object_unref(layout);
|
|
layout = gtk_widget_create_pango_layout (GTK_WIDGET(view), sizing_text);
|
|
pango_layout_get_pixel_size(layout, &default_width, NULL);
|
|
g_object_unref(layout);
|
|
default_width = MAX(default_width, title_width);
|
|
if (default_width)
|
|
default_width += 10; /* padding on either side */
|
|
|
|
gnc_tree_view_column_properties (view, column, pref_name, model_data_column,
|
|
default_width, TRUE, column_sort_fn);
|
|
|
|
/* Stuff specific to combo */
|
|
if (combo_tree_model) {
|
|
g_object_set(G_OBJECT(renderer), "model", combo_tree_model,
|
|
"text-column", combo_model_text_column, NULL);
|
|
}
|
|
/* TODO: has-entry? */
|
|
|
|
gnc_tree_view_append_column (view, column);
|
|
return column;
|
|
}
|
|
|
|
GtkCellRenderer *
|
|
gnc_tree_view_column_get_renderer(GtkTreeViewColumn *column)
|
|
{
|
|
GList *renderers;
|
|
GtkCellRenderer *cr = NULL;
|
|
|
|
g_return_val_if_fail(GTK_TREE_VIEW_COLUMN(column), NULL);
|
|
|
|
/* Get the list of one renderer */
|
|
renderers = gtk_tree_view_column_get_cell_renderers(column);
|
|
if (g_list_length(renderers) > 0)
|
|
cr = GTK_CELL_RENDERER(renderers->data);
|
|
g_list_free(renderers);
|
|
|
|
return cr;
|
|
}
|
|
|
|
/** This function adds a new numeric column to a GncTreeView base
|
|
* view. It takes all the parameters necessary to hook a
|
|
* GtkTreeModel column to a GtkTreeViewColumn. If the tree has a
|
|
* gconf section associated with it, this function also wires up the
|
|
* column so that its visibility and width are remembered. A numeric
|
|
* column is nothing more then a text column with a few extra
|
|
* attributes.
|
|
*
|
|
* Parameters are defined in gnc-tree-view.h
|
|
*/
|
|
GtkTreeViewColumn *
|
|
gnc_tree_view_add_numeric_column (GncTreeView *view,
|
|
const gchar *column_title,
|
|
const gchar *pref_name,
|
|
const gchar *sizing_text,
|
|
gint model_data_column,
|
|
gint model_color_column,
|
|
gint model_visibility_column,
|
|
GtkTreeIterCompareFunc column_sort_fn)
|
|
{
|
|
GtkTreeViewColumn *column;
|
|
GtkCellRenderer *renderer;
|
|
|
|
column = gnc_tree_view_add_text_column (view, column_title, pref_name,
|
|
NULL, sizing_text, model_data_column,
|
|
model_visibility_column,
|
|
column_sort_fn);
|
|
|
|
renderer = gnc_tree_view_column_get_renderer(column);
|
|
|
|
/* Right align the column title and data */
|
|
g_object_set(G_OBJECT(column), "alignment", 1.0, NULL);
|
|
g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
|
|
|
|
/* Change the text color */
|
|
if (model_color_column != GNC_TREE_VIEW_COLUMN_COLOR_NONE)
|
|
gtk_tree_view_column_add_attribute (column, renderer,
|
|
"foreground", model_color_column);
|
|
|
|
return column;
|
|
}
|
|
|
|
/** Add a column to a view based upon a GncTreeView. This function
|
|
* knows about the two special columns on the right side of this type
|
|
* of view, and adds the new column before these two columns. You
|
|
* could say that it appends to the data columns and ignores the
|
|
* infrastructure columns.
|
|
*
|
|
* Parameters are defined in gnc-tree-view.h
|
|
*/
|
|
gint
|
|
gnc_tree_view_append_column (GncTreeView *view,
|
|
GtkTreeViewColumn *column)
|
|
{
|
|
GList *columns;
|
|
int n;
|
|
|
|
/* There's no easy way to get this number. */
|
|
columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(view));
|
|
n = g_list_length(columns);
|
|
g_list_free(columns);
|
|
|
|
/* Ignore the initial two columns (the spacer and the selection menu) */
|
|
if (n >= 2)
|
|
n -= 2;
|
|
return gtk_tree_view_insert_column (GTK_TREE_VIEW(view), column, n);
|
|
}
|
|
|
|
static gboolean
|
|
get_column_next_to(GtkTreeView *tv, GtkTreeViewColumn **col, gboolean backward)
|
|
{
|
|
GList *cols, *node;
|
|
GtkTreeViewColumn *c = NULL;
|
|
gint seen = 0;
|
|
gboolean wrapped = FALSE;
|
|
|
|
cols = gtk_tree_view_get_columns(tv);
|
|
g_return_val_if_fail(g_list_length(cols) > 0, FALSE);
|
|
|
|
node = g_list_find(cols, *col);
|
|
g_return_val_if_fail(node, FALSE);
|
|
do {
|
|
node = backward ? node->prev : node->next;
|
|
if (!node) {
|
|
wrapped = TRUE;
|
|
node = backward ? g_list_last(cols) : cols;
|
|
}
|
|
c = GTK_TREE_VIEW_COLUMN(node->data);
|
|
if (c && gtk_tree_view_column_get_visible(c))
|
|
seen++;
|
|
if (c == *col) break;
|
|
} while (!seen);
|
|
|
|
g_list_free(cols);
|
|
*col = c;
|
|
return wrapped;
|
|
}
|
|
|
|
gboolean
|
|
gnc_tree_view_path_is_valid(GncTreeView *view, GtkTreePath *path)
|
|
{
|
|
GtkTreeView *tv = GTK_TREE_VIEW(view);
|
|
GtkTreeModel *s_model;
|
|
GtkTreeIter iter;
|
|
|
|
s_model = gtk_tree_view_get_model(tv);
|
|
return gtk_tree_model_get_iter(s_model, &iter, path);
|
|
}
|
|
|
|
void
|
|
gnc_tree_view_keynav(GncTreeView *view, GtkTreeViewColumn **col,
|
|
GtkTreePath *path, GdkEventKey *event)
|
|
{
|
|
GtkTreeView *tv = GTK_TREE_VIEW(view);
|
|
gint depth;
|
|
gboolean shifted;
|
|
|
|
if (event->type != GDK_KEY_PRESS) return;
|
|
|
|
switch (event->keyval) {
|
|
case GDK_Tab:
|
|
case GDK_ISO_Left_Tab:
|
|
case GDK_KP_Tab:
|
|
shifted = event->state & GDK_SHIFT_MASK;
|
|
if (get_column_next_to(tv, col, shifted)) {
|
|
/* This is the end (or beginning) of the line, buddy. */
|
|
depth = gtk_tree_path_get_depth(path);
|
|
if (shifted) {
|
|
if (!gtk_tree_path_prev(path) && depth > 1) {
|
|
gtk_tree_path_up(path);
|
|
}
|
|
} else if (gtk_tree_view_row_expanded(tv, path)) {
|
|
gtk_tree_path_down(path);
|
|
} else {
|
|
gtk_tree_path_next(path);
|
|
if (!gnc_tree_view_path_is_valid(view, path) && depth > 1) {
|
|
gtk_tree_path_prev(path);
|
|
gtk_tree_path_up(path);
|
|
gtk_tree_path_next(path);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case GDK_Return:
|
|
case GDK_KP_Enter:
|
|
if (gtk_tree_view_row_expanded(tv, path)) {
|
|
gtk_tree_path_down(path);
|
|
} else {
|
|
depth = gtk_tree_path_get_depth(path);
|
|
gtk_tree_path_next(path);
|
|
if (!gnc_tree_view_path_is_valid(view, path) && depth > 1) {
|
|
gtk_tree_path_prev(path);
|
|
gtk_tree_path_up(path);
|
|
gtk_tree_path_next(path);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
/** @} */
|