GnuCash  4.11-8-geb48f0830e+
gnc-tree-view.c
Go to the documentation of this file.
1 /*
2  * gnc-tree-view.c -- new GtkTreeView with extra features used by
3  * all the tree views in gnucash
4  *
5  * Copyright (C) 2003,2005 David Hampton <hampton@employees.org>
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License as
9  * published by the Free Software Foundation; either version 2 of
10  * the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, contact:
19  *
20  * Free Software Foundation Voice: +1-617-542-5942
21  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652
22  * Boston, MA 02110-1301, USA gnu@gnu.org
23  */
24 
34 #include <config.h>
35 
36 #include <gtk/gtk.h>
37 #include <glib/gi18n.h>
38 #include <gdk/gdkkeysyms.h>
39 #include <string.h>
40 
41 #include "gnc-tree-view.h"
42 #include "gnc-engine.h"
43 #include "gnc-gnome-utils.h"
44 #include "gnc-gobject-utils.h"
45 #include "gnc-cell-renderer-date.h"
46 #include "gnc-cell-renderer-text-view.h"
47 #include "gnc-state.h"
48 #include "gnc-prefs.h"
49 #include "dialog-utils.h"
50 
51 /* The actual state key for a particular column visibility. This is
52  * attached to the menu items that are in the column selection menu.
53  * Makes it very easy to update saved state when a menu item is toggled. */
54 #define STATE_KEY "state-key"
55 
56 /* State keys within this particular saved state section. */
57 #define STATE_KEY_SORT_COLUMN "sort_column"
58 #define STATE_KEY_SORT_ORDER "sort_order"
59 #define STATE_KEY_COLUMN_ORDER "column_order"
60 
61 /* Partial state keys within this particular saved state section. These
62  are appended to the various column names to create the actual
63  keys. */
64 #define STATE_KEY_SUFF_VISIBLE "visible"
65 #define STATE_KEY_SUFF_WIDTH "width"
66 
67 enum
68 {
69  PROP_0,
70  PROP_STATE_SECTION,
71  PROP_SHOW_COLUMN_MENU,
72 };
73 
76 /* This static indicates the debugging module that this .o belongs to. */
77 static QofLogModule log_module = GNC_MOD_GUI;
78 
79 /**** Declarations ******************************************************/
80 static void gnc_tree_view_class_init (GncTreeViewClass *klass);
81 static void gnc_tree_view_init (GncTreeView *view, void *data);
82 static void gnc_tree_view_finalize (GObject *object);
83 static void gnc_tree_view_destroy (GtkWidget *widget);
84 static void gnc_tree_view_set_property (GObject *object,
85  guint prop_id,
86  const GValue *value,
87  GParamSpec *pspec);
88 static void gnc_tree_view_get_property (GObject *object,
89  guint prop_id,
90  GValue *value,
91  GParamSpec *pspec);
92 static gboolean gnc_tree_view_drop_ok_cb (GtkTreeView *view,
93  GtkTreeViewColumn *column,
94  GtkTreeViewColumn *prev_column,
95  GtkTreeViewColumn *next_column,
96  gpointer data);
97 static void gnc_tree_view_build_column_menu (GncTreeView *view);
98 static void gnc_tree_view_select_column_cb (GtkTreeViewColumn *column,
99  GncTreeView *view);
100 static gchar *gnc_tree_view_get_sort_order (GncTreeView *view);
101 static gchar *gnc_tree_view_get_sort_column (GncTreeView *view);
102 static gchar **gnc_tree_view_get_column_order (GncTreeView *view,
103  gsize *length);
104 
107 typedef struct GncTreeViewPrivate
108 {
109  /* Column selection menu related values */
110  GtkTreeViewColumn *column_menu_column;
111  GtkWidget *column_menu;
112  gboolean show_column_menu;
113  GtkWidget *column_menu_icon_box;
114 
115  /* Sort callback model */
116  GtkTreeModel *sort_model;
117 
118  /* Editing callback functions */
119  GFunc editing_started_cb;
120  GFunc editing_finished_cb;
121  gpointer editing_cb_data;
122 
123  /* State related values */
124  gchar *state_section;
125  gboolean seen_state_visibility;
126  gulong columns_changed_cb_id;
127  gulong sort_column_changed_cb_id;
128  gulong size_allocate_cb_id;
130 
131 GNC_DEFINE_TYPE_WITH_CODE(GncTreeView, gnc_tree_view, GTK_TYPE_TREE_VIEW,
132  G_ADD_PRIVATE(GncTreeView))
133 
134 #define GNC_TREE_VIEW_GET_PRIVATE(o) \
135  ((GncTreeViewPrivate*)g_type_instance_get_private((GTypeInstance*)o, GNC_TYPE_TREE_VIEW))
136 
137 
138 /************************************************************/
139 /* g_object required functions */
140 /************************************************************/
141 
145 static GObjectClass *parent_class = NULL;
146 
156 static void
157 gnc_tree_view_class_init (GncTreeViewClass *klass)
158 {
159  GObjectClass *gobject_class;
160  GtkWidgetClass *gtkwidget_class;
161 
162  parent_class = g_type_class_peek_parent (klass);
163 
164  gobject_class = G_OBJECT_CLASS(klass);
165  gtkwidget_class = GTK_WIDGET_CLASS(klass);
166 
167  gobject_class->set_property = gnc_tree_view_set_property;
168  gobject_class->get_property = gnc_tree_view_get_property;
169 
170  g_object_class_install_property (gobject_class,
171  PROP_STATE_SECTION,
172  g_param_spec_string ("state-section",
173  "State Section",
174  "The section name in the saved state to use for (re)storing the treeview's visual state (visible columns, sort order,...",
175  NULL,
176  G_PARAM_READWRITE));
177  g_object_class_install_property (gobject_class,
178  PROP_SHOW_COLUMN_MENU,
179  g_param_spec_boolean ("show-column-menu",
180  "Show Column Menu",
181  "Show the column menu so user can change what columns are visible.",
182  FALSE,
183  G_PARAM_READWRITE));
184 
185  /* GObject signals */
186  gobject_class->finalize = gnc_tree_view_finalize;
187 
188  /* GtkWidget signals */
189  gtkwidget_class->destroy = gnc_tree_view_destroy;
190 }
191 
192 static void
193 gnc_tree_view_update_grid_lines (gpointer prefs, gchar* pref, gpointer user_data)
194 {
195  GncTreeView *view = user_data;
196  gtk_tree_view_set_grid_lines (GTK_TREE_VIEW(view), gnc_tree_view_get_grid_lines_pref ());
197 }
198 
199 static gboolean
200 gnc_tree_view_select_column_icon_cb (GtkWidget *widget, GdkEventButton *event, gpointer user_data)
201 {
202  GncTreeView *view = user_data;
203  GncTreeViewPrivate *priv;
204  GtkStyleContext *stylectxt = gtk_widget_get_style_context (widget);
205  GtkBorder padding;
206 
207  // if the event button is not the right one, leave.
208  if (event->button != 1)
209  return FALSE;
210 
211  priv = GNC_TREE_VIEW_GET_PRIVATE(view);
212 
213  gtk_style_context_get_padding (stylectxt, GTK_STATE_FLAG_NORMAL, &padding);
214 
215  if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
216  {
217  if (event->x < (gtk_widget_get_allocated_width (priv->column_menu_icon_box) + padding.left))
218  gnc_tree_view_select_column_cb (priv->column_menu_column, view);
219  }
220  else
221  {
222  if (event->x > (gtk_widget_get_allocated_width (widget) -
223  (gtk_widget_get_allocated_width (priv->column_menu_icon_box) + padding.right)))
224  gnc_tree_view_select_column_cb (priv->column_menu_column, view);
225  }
226  return FALSE;
227 }
228 
238 static void
239 gnc_tree_view_init (GncTreeView *view, void *data)
240 {
241  GncTreeViewPrivate *priv;
242  GtkTreeViewColumn *column;
243  GtkWidget *sep, *icon;
244 
245  GncTreeViewClass *klass = (GncTreeViewClass*)data;
246 
247  gnc_gobject_tracking_remember (G_OBJECT(view),
248  G_OBJECT_CLASS(klass));
249 
250  priv = GNC_TREE_VIEW_GET_PRIVATE(view);
251  priv->column_menu = NULL;
252  priv->show_column_menu = FALSE;
253  priv->sort_model = NULL;
254  priv->state_section = NULL;
255  priv->seen_state_visibility = FALSE;
256  priv->columns_changed_cb_id = 0;
257  priv->sort_column_changed_cb_id = 0;
258  priv->size_allocate_cb_id = 0;
259 
260  // Set the name for this widget so it can be easily manipulated with css
261  gtk_widget_set_name (GTK_WIDGET(view), "gnc-id-tree-view");
262 
263  /* Handle column drag and drop */
264  gtk_tree_view_set_column_drag_function (GTK_TREE_VIEW(view),
265  gnc_tree_view_drop_ok_cb, NULL, NULL);
266 
267  // Set grid lines option to preference
268  gtk_tree_view_set_grid_lines (GTK_TREE_VIEW(view), gnc_tree_view_get_grid_lines_pref ());
269  gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL, GNC_PREF_GRID_LINES_HORIZONTAL,
270  gnc_tree_view_update_grid_lines, view);
271  gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL, GNC_PREF_GRID_LINES_VERTICAL,
272  gnc_tree_view_update_grid_lines, view);
273 
274  /* Create the last column which contains the column selection
275  * widget. gnc_tree_view_add_text_column will do most of the
276  * work. */
277  icon = gtk_image_new_from_icon_name ("pan-down-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR);
278 
279  priv->column_menu_icon_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
280  gtk_box_set_homogeneous (GTK_BOX(priv->column_menu_icon_box), FALSE);
281 
282  gtk_widget_set_margin_start (GTK_WIDGET(icon), 5);
283 
284  gtk_box_pack_end (GTK_BOX(priv->column_menu_icon_box), icon, FALSE, FALSE, 0);
285 
286  sep = gtk_separator_new (GTK_ORIENTATION_VERTICAL);
287  gtk_box_pack_end (GTK_BOX(priv->column_menu_icon_box), sep, FALSE, FALSE, 0);
288 
289  gtk_widget_show_all (priv->column_menu_icon_box);
290 
291  column = gnc_tree_view_add_text_column (view, NULL, NULL, NULL, NULL,
292  -1, -1, NULL);
293  g_object_set (G_OBJECT(column),
294  "clickable", TRUE,
295  "widget", priv->column_menu_icon_box,
296  "alignment", 1.0,
297  "expand", TRUE,
298  (gchar *)NULL);
299 
300  priv->column_menu_column = column;
301 
302  // get the actual column button by looking at the parents of the column_menu_icon
303  {
304  GtkWidget *mybox = gtk_widget_get_parent (icon);
305  GtkWidget *walign = gtk_widget_get_parent (mybox);
306  GtkWidget *box = gtk_widget_get_parent (walign);
307  GtkWidget *button = gtk_widget_get_parent (box);
308 
309  if (!GTK_IS_BUTTON(button)) // just in case this order changes.
310  {
311  // this will fire for the whole column header
312  g_signal_connect (G_OBJECT(column), "clicked",
313  G_CALLBACK(gnc_tree_view_select_column_cb),
314  view);
315  }
316  else
317  {
318  /* this part will restrict the mouse click to just where the
319  icon is, tried using an eventbox but it would only work
320  some of the time */
321  gtk_widget_set_events (button, GDK_BUTTON_PRESS_MASK);
322 
323  g_signal_connect (G_OBJECT(button), "button_press_event",
324  G_CALLBACK(gnc_tree_view_select_column_icon_cb),
325  view);
326  }
327  }
328  gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
329 }
330 
341 static void
342 gnc_tree_view_finalize (GObject *object)
343 {
344  ENTER("view %p", object);
345  g_return_if_fail (object != NULL);
346  g_return_if_fail (GNC_IS_TREE_VIEW(object));
347 
349 
350  if (G_OBJECT_CLASS(parent_class)->finalize)
351  G_OBJECT_CLASS(parent_class)->finalize (object);
352  LEAVE(" ");
353 }
354 
366 static void
367 gnc_tree_view_destroy (GtkWidget *widget)
368 {
369  GncTreeView *view;
370  GncTreeViewPrivate *priv;
371 
372  ENTER("view %p", widget);
373  g_return_if_fail (widget != NULL);
374  g_return_if_fail (GNC_IS_TREE_VIEW(widget));
375 
376  view = GNC_TREE_VIEW(widget);
377 
378  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL, GNC_PREF_GRID_LINES_HORIZONTAL,
379  gnc_tree_view_update_grid_lines, view);
380  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL, GNC_PREF_GRID_LINES_VERTICAL,
381  gnc_tree_view_update_grid_lines, view);
382 
383  priv = GNC_TREE_VIEW_GET_PRIVATE(view);
384 
385  if (priv->state_section)
386  {
388  }
389  g_free (priv->state_section);
390  priv->state_section = NULL;
391 
392  if (priv->column_menu)
393  {
394  DEBUG("removing column selection menu");
395  g_object_unref (priv->column_menu);
396  priv->column_menu = NULL;
397  }
398 
399  if (GTK_WIDGET_CLASS(parent_class)->destroy)
400  GTK_WIDGET_CLASS(parent_class)->destroy (widget);
401  LEAVE(" ");
402 }
403 
406 /************************************************************/
407 /* g_object other functions */
408 /************************************************************/
409 
420 static void
421 gnc_tree_view_get_property (GObject *object,
422  guint prop_id,
423  GValue *value,
424  GParamSpec *pspec)
425 {
426  GncTreeView *view = GNC_TREE_VIEW(object);
427  GncTreeViewPrivate *priv;
428 
429  priv = GNC_TREE_VIEW_GET_PRIVATE(view);
430  switch (prop_id)
431  {
432  case PROP_STATE_SECTION:
433  g_value_set_string (value, priv->state_section);
434  break;
435  case PROP_SHOW_COLUMN_MENU:
436  g_value_set_boolean (value, priv->show_column_menu);
437  break;
438  default:
439  G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
440  break;
441  }
442 }
443 
444 
453 static void
454 gnc_tree_view_set_property (GObject *object,
455  guint prop_id,
456  const GValue *value,
457  GParamSpec *pspec)
458 {
459  GncTreeView *view = GNC_TREE_VIEW(object);
460 
461  switch (prop_id)
462  {
463  case PROP_STATE_SECTION:
464  gnc_tree_view_set_state_section (view, g_value_get_string (value));
465  break;
466  case PROP_SHOW_COLUMN_MENU:
467  gnc_tree_view_set_show_column_menu (view, g_value_get_boolean (value));
468  break;
469  default:
470  G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
471  break;
472  }
473 }
474 
477 /************************************************************/
478 /* Auxiliary Functions */
479 /************************************************************/
498 static GtkTreeViewColumn *
499 view_column_find_by_model_id (GncTreeView *view,
500  const gint wanted)
501 {
502  GtkTreeViewColumn *column, *found = NULL;
503  GList *column_list, *tmp;
504  gint id;
505 
506  // ENTER("view %p, name %s", view, name);
507  column_list = gtk_tree_view_get_columns (GTK_TREE_VIEW(view));
508  for (tmp = column_list; tmp; tmp = g_list_next (tmp))
509  {
510  column = tmp->data;
511  id = GPOINTER_TO_INT(g_object_get_data (G_OBJECT(column), MODEL_COLUMN));
512  if (id != wanted)
513  continue;
514  found = column;
515  break;
516  }
517  g_list_free (column_list);
518 
519  // LEAVE("column %p", found);
520  return found;
521 }
522 
533 GtkTreeViewColumn *
535  const gchar *wanted)
536 {
537  GtkTreeViewColumn *column, *found = NULL;
538  GList *column_list, *tmp;
539  const gchar *name;
540 
541  // ENTER("view %p, wanted %s", view, wanted);
542  column_list = gtk_tree_view_get_columns(GTK_TREE_VIEW(view));
543  for (tmp = column_list; tmp; tmp = g_list_next (tmp))
544  {
545  column = tmp->data;
546  name = g_object_get_data (G_OBJECT(column), PREF_NAME);
547  if (!name || (strcmp(name, wanted) != 0))
548  continue;
549  found = column;
550  break;
551  }
552  g_list_free (column_list);
553 
554  // LEAVE("column %p", found);
555  return found;
556 }
557 
560 /************************************************************/
561 /* Tree Callbacks */
562 /************************************************************/
563 
592 static gboolean
593 gnc_tree_view_drop_ok_cb (GtkTreeView *view,
594  GtkTreeViewColumn *column,
595  GtkTreeViewColumn *prev_column,
596  GtkTreeViewColumn *next_column,
597  gpointer data)
598 {
599  const gchar *pref_name;
600 
601  /* Should we allow a drop at the left side of the tree view before
602  * the widget to open a new display level? I can think of cases
603  * where the user might want to do this with a checkbox column. */
604  if (prev_column == NULL)
605  return TRUE;
606 
607  /* Do not allow a drop at the right side of the tree view after the
608  * column selection widget. */
609  if (next_column == NULL)
610  return FALSE;
611 
612  /* Columns without pref names are considered fixed at the right hand
613  * side of the view. At the time of this writing, the only two are
614  * the column where the "column selection widget" is stored, and the
615  * "padding" column to the left of that where extra view space ends
616  * up. */
617  pref_name = g_object_get_data (G_OBJECT(prev_column), PREF_NAME);
618  if (!pref_name)
619  return FALSE;
620 
621  /* Everything else is allowed. */
622  return TRUE;
623 }
624 
627 /************************************************************/
628 /* State Setup / Callbacks */
629 /************************************************************/
630 
653 static gboolean
654 gnc_tree_view_column_visible (GncTreeView *view,
655  GtkTreeViewColumn *column,
656  const gchar *pref_name)
657 {
658  GncTreeViewPrivate *priv;
659  gboolean visible;
660  const gchar *col_name = pref_name;
661 
662  ENTER("column %p, name %s", column, pref_name ? pref_name : "(null)");
663  priv = GNC_TREE_VIEW_GET_PRIVATE(view);
664  if (column)
665  {
666  if (g_object_get_data (G_OBJECT(column), ALWAYS_VISIBLE))
667  {
668  LEAVE("1, first column");
669  return TRUE;
670  }
671  col_name = g_object_get_data (G_OBJECT(column), PREF_NAME);
672  DEBUG("col_name is %s", col_name ? col_name : "(null)");
673  }
674 
675  if (!col_name)
676  {
677  LEAVE("1, no pref name");
678  return TRUE;
679  }
680 
681  /* Using saved state ? */
682  if (priv->state_section)
683  {
684  GKeyFile *state_file = gnc_state_get_current ();
685  gchar *key = g_strdup_printf ("%s_%s", col_name, STATE_KEY_SUFF_VISIBLE);
686 
687  if (g_key_file_has_key (state_file, priv->state_section, key, NULL))
688  {
689  visible = g_key_file_get_boolean (state_file, priv->state_section, key, NULL);
690  g_free (key);
691  LEAVE("%d, state defined visibility", visible);
692  return visible;
693  }
694  }
695 
696  /* Check the default columns list */
697  visible = column ?
698  (g_object_get_data (G_OBJECT(column), DEFAULT_VISIBLE) != NULL) : FALSE;
699  LEAVE("defaults says %d", visible);
700  return visible;
701 }
702 
713 static void
714 gnc_tree_view_update_visibility (GtkTreeViewColumn *column,
715  GncTreeView *view)
716 {
717  gboolean visible;
718 
719  g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN(column));
720  g_return_if_fail (GNC_IS_TREE_VIEW(view));
721 
722  ENTER(" ");
723  visible = gnc_tree_view_column_visible (view, column, NULL);
724  gtk_tree_view_column_set_visible (column, visible);
725  LEAVE("made %s", visible ? "visible" : "invisible");
726 }
727 
738 static gchar *
739 gnc_tree_view_get_sort_order (GncTreeView *view)
740 {
741  GtkTreeModel *s_model;
742  GtkSortType order;
743  gint current;
744  gchar *order_str = NULL;
745 
746  s_model = gtk_tree_view_get_model (GTK_TREE_VIEW(view));
747  if (!s_model)
748  return NULL; /* no model, so sort order doesn't make sense */
749 
750  if (!gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE(s_model),
751  &current, &order))
752  return NULL; /* Model is not sorted, return */
753 
754  gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(s_model),
755  current, order);
756  order_str = g_strdup (order == GTK_SORT_ASCENDING ? "ascending" : "descending");
757  DEBUG("current sort_order is %s", order_str);
758  return order_str;
759 }
760 
770 static gchar *
771 gnc_tree_view_get_sort_column (GncTreeView *view)
772 {
773  GtkTreeModel *s_model;
774  GtkTreeViewColumn *column;
775  GtkSortType order;
776  gint current;
777  const gchar *name;
778 
779  s_model = gtk_tree_view_get_model (GTK_TREE_VIEW(view));
780  if (!s_model)
781  return NULL; /* no model -> no sort column */
782 
783  if (!gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE(s_model),
784  &current, &order))
785  return NULL; /* model not sorted */
786 
787  column = view_column_find_by_model_id (view, current);
788  if (!column)
789  return NULL; /* column not visible, can't be used for sorting */
790 
791  name = g_object_get_data (G_OBJECT(column), PREF_NAME);
792  DEBUG("current sort column is %s", name ? name : "(NULL)");
793  return g_strdup (name);
794 }
795 
796 
797 
808 static gchar **
809 gnc_tree_view_get_column_order (GncTreeView *view,
810  gsize *length)
811 {
812  const GList *tmp;
813  GList *columns;
814  gulong num_cols = 0;
815  gchar *col_names = NULL;
816  gchar **col_str_list;
817 
818  /* First, convert from names to pointers */
819  ENTER(" ");
820 
821  columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(view));
822  for (tmp = columns; tmp; tmp = g_list_next(tmp))
823  {
824  GtkTreeViewColumn *column = tmp->data;
825  const gchar *name = g_object_get_data (G_OBJECT(column), PREF_NAME);
826  if (!col_names)
827  col_names = g_strdup (name);
828  else
829  {
830  gchar *col_names_prev = col_names;
831  col_names = g_strjoin (";", col_names_prev, name, NULL);
832  g_free (col_names_prev);
833  }
834  num_cols++;
835  }
836  //DEBUG ("got %lu columns: %s", num_cols, col_names);
837  col_str_list = g_strsplit (col_names, ";", 0);
838 
839  /* Clean up */
840  g_list_free (columns);
841  g_free (col_names);
842 
843  LEAVE("column order get");
844  *length = num_cols;
845  return col_str_list;
846 }
847 
858 static void
859 gnc_tree_view_set_sort_order (GncTreeView *view,
860  const gchar *name)
861 {
862  GtkTreeModel *s_model;
863  GtkSortType order = GTK_SORT_ASCENDING;
864  gint current;
865 
866  s_model = gtk_tree_view_get_model (GTK_TREE_VIEW(view));
867  if (!s_model)
868  return;
869  if (g_strcmp0 (name, "descending") == 0)
870  order = GTK_SORT_DESCENDING;
871  if (!gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE(s_model),
872  &current, NULL))
873  current = GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID;
874  gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(s_model),
875  current, order);
876  DEBUG("sort_order set to %s", order == GTK_SORT_ASCENDING ? "ascending" : "descending");
877 }
878 
887 static void
888 gnc_tree_view_set_sort_column (GncTreeView *view,
889  const gchar *name)
890 {
891  GtkTreeModel *s_model;
892  GtkTreeViewColumn *column;
893  GtkSortType order;
894  gint model_column, current;
895 
896  s_model = gtk_tree_view_get_model (GTK_TREE_VIEW(view));
897  if (!s_model)
898  return;
899 
900  column = gnc_tree_view_find_column_by_name (view, name);
901  if (!column)
902  {
903  gtk_tree_sortable_set_sort_column_id (
904  GTK_TREE_SORTABLE(s_model), GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID,
905  GTK_SORT_ASCENDING);
906  return;
907  }
908 
909  model_column =
910  GPOINTER_TO_INT(g_object_get_data (G_OBJECT(column), MODEL_COLUMN));
911  if (model_column == GNC_TREE_VIEW_COLUMN_DATA_NONE)
912  return;
913 
914  if (!gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE(s_model),
915  &current, &order))
916  order = GTK_SORT_ASCENDING;
917 
918  gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(s_model),
919  model_column, order);
920  DEBUG("sort column set to %s", name);
921 }
922 
935 static void
936 gnc_tree_view_set_column_order (GncTreeView *view,
937  gchar **column_names,
938  gsize length)
939 {
940  GtkTreeViewColumn *column, *prev;
941  const GSList *tmp;
942  GSList *columns;
943  gsize idx;
944 
945  /* First, convert from names to pointers */
946  ENTER(" ");
947  columns = NULL;
948  for (idx = 0; idx < length; idx++)
949  {
950  const gchar *name = column_names [idx];
951  column = gnc_tree_view_find_column_by_name (view, name);
952  if (!column)
953  continue;
954  columns = g_slist_append (columns, column);
955  }
956 
957  /* Then reorder the columns */
958  for (prev = NULL, tmp = columns; tmp; tmp = g_slist_next (tmp))
959  {
960  column = tmp->data;
961  gtk_tree_view_move_column_after (GTK_TREE_VIEW(view), column, prev);
962  prev = column;
963  }
964 
965  /* Clean up */
966  g_slist_free (columns);
967  LEAVE("column order set");
968 }
969 
979 {
980  GncTreeViewPrivate *priv;
981  GKeyFile *state_file = gnc_state_get_current ();
982 
983  ENTER(" ");
984  priv = GNC_TREE_VIEW_GET_PRIVATE(view);
985  if (!priv->state_section)
986  {
987  LEAVE("no state section");
988  return;
989  }
990 
991  g_key_file_remove_group (state_file, priv->state_section, NULL);
992  g_free (priv->state_section);
993  priv->state_section = NULL;
994  LEAVE(" ");
995 }
996 
1004 void
1006  const gchar *section)
1007 {
1008  GncTreeViewPrivate *priv;
1009  GKeyFile *state_file;
1010 
1011  g_return_if_fail (GNC_IS_TREE_VIEW(view));
1012 
1013  ENTER("view %p, section %s", view, section);
1014 
1015  priv = GNC_TREE_VIEW_GET_PRIVATE(view);
1016 
1017  /* Drop any previous state section */
1018  if (priv->state_section)
1020 
1021  if (!section)
1022  {
1023  LEAVE("cleared state section");
1024  return;
1025  }
1026 
1027  /* Catch changes in state. Propagate to view. */
1028  priv->state_section = g_strdup (section);
1029 
1030  state_file = gnc_state_get_current ();
1031  if (g_key_file_has_group (state_file, priv->state_section))
1032  {
1033  gsize num_keys, idx;
1034  gchar **keys = g_key_file_get_keys (state_file, priv->state_section, &num_keys, NULL);
1035  for (idx = 0; idx < num_keys; idx++)
1036  {
1037  gchar *key = keys[idx];
1038  if (g_strcmp0 (key, STATE_KEY_SORT_COLUMN) == 0)
1039  {
1040  gnc_tree_view_set_sort_column (view,
1041  g_key_file_get_string (state_file, priv->state_section, key, NULL));
1042  }
1043  else if (g_strcmp0 (key, STATE_KEY_SORT_ORDER) == 0)
1044  {
1045  gnc_tree_view_set_sort_order (view,
1046  g_key_file_get_string (state_file, priv->state_section, key, NULL));
1047  }
1048  else if (g_strcmp0 (key, STATE_KEY_COLUMN_ORDER) == 0)
1049  {
1050  gsize length;
1051  gchar **columns = g_key_file_get_string_list (state_file, priv->state_section,
1052  key, &length, NULL);
1053  gnc_tree_view_set_column_order (view, columns, length);
1054  g_strfreev (columns);
1055  }
1056  else
1057  {
1058  /* Make a copy of the local part of the key so it can be split
1059  * into column name and key type */
1060  gboolean known = FALSE;
1061  gchar *column_name = g_strdup (key);
1062  gchar *type_name = g_strrstr (column_name, "_");
1063 
1064  if (type_name != NULL) //guard against not finding '_'
1065  {
1066  *type_name++ = '\0';
1067 
1068  if (g_strcmp0 (type_name, STATE_KEY_SUFF_VISIBLE) == 0)
1069  {
1070  GtkTreeViewColumn *column = gnc_tree_view_find_column_by_name (view, column_name);
1071  if (column)
1072  {
1073  known = TRUE;
1074  if (!g_object_get_data (G_OBJECT (column), ALWAYS_VISIBLE))
1075  {
1076  gtk_tree_view_column_set_visible (column,
1077  g_key_file_get_boolean (state_file, priv->state_section, key, NULL));
1078  }
1079  }
1080  }
1081  else if (g_strcmp0 (type_name, STATE_KEY_SUFF_WIDTH) == 0)
1082  {
1083  gint width = g_key_file_get_integer (state_file, priv->state_section, key, NULL);
1084  GtkTreeViewColumn *column = gnc_tree_view_find_column_by_name (view, column_name);
1085  if (column)
1086  {
1087  known = TRUE;
1088  if (width && (width != gtk_tree_view_column_get_width (column)))
1089  {
1090  gtk_tree_view_column_set_fixed_width (column, width);
1091  }
1092  }
1093  }
1094  if (!known)
1095  DEBUG ("Ignored key %s", key);
1096 
1097  g_free (column_name);
1098  }
1099  }
1100  }
1101  g_strfreev (keys);
1102  }
1103 
1104  /* Rebuild the column visibility menu */
1105  gnc_tree_view_build_column_menu (view);
1106  LEAVE ("set state section");
1107 }
1108 
1115 const gchar *
1117 {
1118  GncTreeViewPrivate *priv;
1119 
1120  g_return_val_if_fail (GNC_IS_TREE_VIEW(view), NULL);
1121 
1122  priv = GNC_TREE_VIEW_GET_PRIVATE (view);
1123  return priv->state_section;
1124 }
1125 
1127 {
1128  GncTreeViewPrivate *priv;
1129 
1130  ENTER("view %p", view);
1131  g_return_if_fail (view != NULL);
1132  g_return_if_fail (GNC_IS_TREE_VIEW(view));
1133 
1134  priv = GNC_TREE_VIEW_GET_PRIVATE(view);
1135 
1136  if (priv->state_section)
1137  {
1138  /* Save state. Only store non-default values when possible. */
1139  GList *column_list, *tmp;
1140  GKeyFile *state_file = gnc_state_get_current();
1141  gsize num_cols = 0;
1142  gchar *sort_column = gnc_tree_view_get_sort_column (view);
1143  gchar *sort_order = gnc_tree_view_get_sort_order (view);
1144  gchar **col_order = gnc_tree_view_get_column_order (view, &num_cols);
1145 
1146  /* Default sort column is the name column */
1147  if (sort_column && (g_strcmp0 (sort_column, "name") != 0))
1148  g_key_file_set_string (state_file, priv->state_section, STATE_KEY_SORT_COLUMN, sort_column);
1149  else if (g_key_file_has_key (state_file, priv->state_section, STATE_KEY_SORT_COLUMN, NULL))
1150  g_key_file_remove_key (state_file, priv->state_section, STATE_KEY_SORT_COLUMN, NULL);
1151  g_free (sort_column);
1152 
1153 
1154  /* Default sort order is "ascending" */
1155  if (g_strcmp0 (sort_order, "descending") == 0)
1156  g_key_file_set_string (state_file, priv->state_section, STATE_KEY_SORT_ORDER, sort_order);
1157  else if (g_key_file_has_key (state_file, priv->state_section, STATE_KEY_SORT_ORDER, NULL))
1158  g_key_file_remove_key (state_file, priv->state_section, STATE_KEY_SORT_ORDER, NULL);
1159  g_free (sort_order);
1160 
1161  if (col_order && (num_cols > 0))
1162  g_key_file_set_string_list (state_file, priv->state_section, STATE_KEY_COLUMN_ORDER,
1163  (const gchar**) col_order, num_cols);
1164  else if (g_key_file_has_key (state_file, priv->state_section, STATE_KEY_COLUMN_ORDER, NULL))
1165  g_key_file_remove_key (state_file, priv->state_section, STATE_KEY_COLUMN_ORDER, NULL);
1166 
1167  g_strfreev (col_order);
1168 
1169 
1170  // ENTER("view %p, wanted %s", view, wanted);
1171  column_list = gtk_tree_view_get_columns (GTK_TREE_VIEW(view));
1172  for (tmp = column_list; tmp; tmp = g_list_next (tmp))
1173  {
1174  GtkTreeViewColumn *column = tmp->data;
1175  gchar *key=NULL;
1176  const gchar *name = g_object_get_data (G_OBJECT(column), PREF_NAME);
1177  if (!name)
1178  continue;
1179 
1180  if (!g_object_get_data (G_OBJECT(column), ALWAYS_VISIBLE))
1181  {
1182  key = g_strjoin ("_", name, STATE_KEY_SUFF_VISIBLE, NULL);
1183  g_key_file_set_boolean (state_file, priv->state_section, key,
1184  gtk_tree_view_column_get_visible (column));
1185  g_free (key);
1186  }
1187 
1188  key = g_strjoin ("_", name, STATE_KEY_SUFF_WIDTH, NULL);
1189  if (g_object_get_data (G_OBJECT(column), "default-width") &&
1190  (GPOINTER_TO_INT((g_object_get_data (G_OBJECT(column), "default-width")))
1191  != gtk_tree_view_column_get_width (column)))
1192  {
1193  g_key_file_set_integer (state_file, priv->state_section, key,
1194  gtk_tree_view_column_get_width (column));
1195  }
1196  else if (g_key_file_has_key (state_file, priv->state_section, key, NULL))
1197  g_key_file_remove_key (state_file, priv->state_section, key, NULL);
1198  g_free (key);
1199  }
1200  g_list_free (column_list);
1201  }
1202 
1203  LEAVE(" ");
1204 }
1205 
1206 
1209 /************************************************************/
1210 /* Column Selection Menu */
1211 /************************************************************/
1212 
1231 static void
1232 gnc_tree_view_create_menu_item (GtkTreeViewColumn *column,
1233  GncTreeView *view)
1234 {
1235  GncTreeViewPrivate *priv;
1236  GtkWidget *widget;
1237  const gchar *column_name, *pref_name;
1238  gchar *key;
1239  GBinding *binding;
1240 
1241  // ENTER("view %p, column %p", view, column);
1242  priv = GNC_TREE_VIEW_GET_PRIVATE(view);
1243  if (!priv->state_section)
1244  {
1245  // LEAVE("no state section");
1246  return;
1247  }
1248 
1249  pref_name = g_object_get_data (G_OBJECT(column), PREF_NAME);
1250  if (!pref_name)
1251  {
1252  // LEAVE("column has no pref_name");
1253  return;
1254  }
1255 
1256  /* Create the menu if we don't have one already */
1257  if (!priv->column_menu)
1258  {
1259  priv->column_menu = gtk_menu_new();
1260  g_object_ref_sink (priv->column_menu);
1261  }
1262 
1263  /* Create the check menu item */
1264  column_name = g_object_get_data (G_OBJECT(column), REAL_TITLE);
1265  if (!column_name)
1266  column_name = gtk_tree_view_column_get_title (column);
1267  widget = gtk_check_menu_item_new_with_label (column_name);
1268  gtk_menu_shell_append (GTK_MENU_SHELL(priv->column_menu), widget);
1269 
1270  /* Should never be able to hide the first column */
1271  if (g_object_get_data (G_OBJECT(column), ALWAYS_VISIBLE))
1272  {
1273  g_object_set_data (G_OBJECT(widget), ALWAYS_VISIBLE, GINT_TO_POINTER(1));
1274  gtk_widget_set_sensitive (widget, FALSE);
1275  }
1276 
1277  binding = g_object_bind_property (G_OBJECT(widget), "active", G_OBJECT(column), "visible", 0);
1278  g_object_set_data (G_OBJECT(widget), "column-binding", binding);
1279 
1280  /* Store data on the widget for callbacks */
1281  key = g_strdup_printf ("%s_%s", pref_name, STATE_KEY_SUFF_VISIBLE);
1282  g_object_set_data_full (G_OBJECT(widget), STATE_KEY, key, g_free);
1283  // LEAVE(" ");
1284 }
1285 
1297 static void
1298 gnc_tree_view_build_column_menu (GncTreeView *view)
1299 {
1300  GncTreeViewPrivate *priv;
1301  GList *column_list;
1302 
1303  g_return_if_fail (GNC_IS_TREE_VIEW(view));
1304 
1305  ENTER("view %p", view);
1306  priv = GNC_TREE_VIEW_GET_PRIVATE(view);
1307 
1308  /* Destroy any old menu */
1309  if (priv->column_menu)
1310  {
1311  g_object_unref (priv->column_menu);
1312  priv->column_menu = NULL;
1313  }
1314 
1315  if (priv->show_column_menu && priv->state_section)
1316  {
1317  /* Show the menu popup button */
1318  if (priv->column_menu_column)
1319  gtk_tree_view_column_set_visible (priv->column_menu_column, TRUE);
1320 
1321  /* Now build a new menu */
1322  column_list = gtk_tree_view_get_columns (GTK_TREE_VIEW(view));
1323  g_list_foreach (column_list, (GFunc)gnc_tree_view_create_menu_item, view);
1324  g_list_free (column_list);
1325  }
1326  else
1327  {
1328  /* Hide the menu popup button */
1329  if (priv->column_menu_column)
1330  gtk_tree_view_column_set_visible (priv->column_menu_column, FALSE);
1331  }
1332  LEAVE("menu: show %d, section %s", priv->show_column_menu,
1333  priv->state_section ? priv->state_section : "(null)");
1334 }
1335 
1345 static void
1346 gnc_tree_view_update_column_menu_item (GtkCheckMenuItem *checkmenuitem,
1347  GncTreeView *view)
1348 {
1349  gboolean visible;
1350 
1351  g_return_if_fail (GTK_IS_CHECK_MENU_ITEM(checkmenuitem));
1352  g_return_if_fail (GNC_IS_TREE_VIEW(view));
1353 
1354  if (g_object_get_data (G_OBJECT(checkmenuitem), ALWAYS_VISIBLE))
1355  {
1356  visible = TRUE;
1357  }
1358  else
1359  {
1360  GBinding *binding = g_object_get_data (G_OBJECT(checkmenuitem), "column-binding");
1361  GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN(g_binding_get_target (binding));
1362 
1363  visible = gtk_tree_view_column_get_visible (column);
1364  }
1365  gtk_check_menu_item_set_active (checkmenuitem, visible);
1366 }
1367 
1380 static void
1381 gnc_tree_view_select_column_cb (GtkTreeViewColumn *column,
1382  GncTreeView *view)
1383 {
1384  GncTreeViewPrivate *priv;
1385  GtkWidget *menu;
1386 
1387  g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN(column));
1388  g_return_if_fail (GNC_IS_TREE_VIEW(view));
1389 
1390  priv = GNC_TREE_VIEW_GET_PRIVATE(view);
1391  menu = priv->column_menu;
1392  if (!menu)
1393  return;
1394 
1395  /* Synchronize the menu before display */
1396  gtk_container_foreach (GTK_CONTAINER(menu),
1397  (GtkCallback)gnc_tree_view_update_column_menu_item,
1398  view);
1399 
1400  /* Ensure all components are visible */
1401  gtk_widget_show_all (menu);
1402 
1403  /* Pop the menu up at the button */
1404  gtk_menu_popup_at_pointer (GTK_MENU(priv->column_menu), NULL);
1405 }
1406 
1407 
1409  gchar *first_column_name,
1410  ...)
1411 {
1412  GncTreeViewPrivate *priv;
1413  GtkTreeViewColumn *column;
1414  gboolean hide_spacer;
1415  GList *columns, *tmp;
1416  gchar *name, *pref_name;
1417  va_list args;
1418 
1419  g_return_if_fail (GNC_IS_TREE_VIEW(view));
1420  ENTER(" ");
1421  va_start (args, first_column_name);
1422  priv = GNC_TREE_VIEW_GET_PRIVATE(view);
1423  name = first_column_name;
1424  hide_spacer = FALSE;
1425 
1426  /* First disable the expand property on all (non-infrastructure) columns. */
1427  columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(view));
1428  for (tmp = columns; tmp; tmp = g_list_next (tmp))
1429  {
1430  column = tmp->data;
1431  pref_name = g_object_get_data (G_OBJECT(column), PREF_NAME);
1432  if (pref_name != NULL)
1433  gtk_tree_view_column_set_expand (column, FALSE);
1434  }
1435  g_list_free(columns);
1436 
1437  /* Now enable it on the requested columns. */
1438  while (name != NULL)
1439  {
1440  column = gnc_tree_view_find_column_by_name (view, name);
1441  if (column != NULL)
1442  {
1443  gtk_tree_view_column_set_expand (column, TRUE);
1444  hide_spacer = TRUE;
1445  }
1446  name = va_arg (args, gchar*);
1447  }
1448  va_end (args);
1449 
1450  LEAVE(" ");
1451 }
1452 
1453 
1454 /* Links the cell backgrounds of the two control columns to the model or
1455  cell data function */
1456 static void
1457 update_control_cell_renderers_background (GncTreeView *view, GtkTreeViewColumn *col,
1458  gint column, GtkTreeCellDataFunc func )
1459 {
1460  GList *renderers;
1461  GtkCellRenderer *cell;
1462  GList *node;
1463 
1464  renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT(col));
1465 
1466  /* Update the cell background in the list of renderers */
1467  for (node = renderers; node; node = node->next)
1468  {
1469  cell = node->data;
1470  if (func == NULL)
1471  gtk_tree_view_column_add_attribute (col, cell, "cell-background", column);
1472  else
1473  gtk_tree_view_column_set_cell_data_func (col, cell, func, view, NULL);
1474  }
1475  g_list_free (renderers);
1476 }
1477 
1478 
1479 /* This function links the cell backgrounds of the two control columns to a column
1480  in the model that has color strings or a cell data function */
1481 void
1482 gnc_tree_view_set_control_column_background (GncTreeView *view, gint column, GtkTreeCellDataFunc func )
1483 {
1484  GncTreeViewPrivate *priv;
1485 
1486  g_return_if_fail (GNC_IS_TREE_VIEW(view));
1487 
1488  ENTER("view %p, column %d, func %p", view, column, func);
1489  priv = GNC_TREE_VIEW_GET_PRIVATE(view);
1490 
1491  update_control_cell_renderers_background (view, priv->column_menu_column, column, func);
1492 
1493  LEAVE(" ");
1494 }
1495 
1496 
1497 /* This allows the columns to be setup without the model connected */
1498 //FIXME I think this should be specified as a parameter to the add columns functions...
1499 void
1500 gnc_tree_view_set_sort_user_data (GncTreeView *view, GtkTreeModel *s_model)
1501 {
1502  GncTreeViewPrivate *priv;
1503 
1504  g_return_if_fail (GNC_IS_TREE_VIEW(view));
1505 
1506  ENTER("view %p, sort_model %p", view, s_model);
1507  priv = GNC_TREE_VIEW_GET_PRIVATE(view);
1508 
1509  priv->sort_model = s_model;
1510  LEAVE(" ");
1511 }
1512 
1513 
1520 void
1522  gboolean visible)
1523 {
1524  GncTreeViewPrivate *priv;
1525 
1526  g_return_if_fail (GNC_IS_TREE_VIEW(view));
1527 
1528  ENTER("view %p, show menu %d", view, visible);
1529  priv = GNC_TREE_VIEW_GET_PRIVATE(view);
1530  priv->show_column_menu = visible;
1531  gnc_tree_view_build_column_menu (view);
1532  LEAVE(" ");
1533 }
1534 
1541 gboolean
1543 {
1544  GncTreeViewPrivate *priv;
1545 
1546  g_return_val_if_fail (GNC_IS_TREE_VIEW(view), FALSE);
1547 
1548  priv = GNC_TREE_VIEW_GET_PRIVATE(view);
1549  return (priv->show_column_menu);
1550 }
1551 
1554 /************************************************************/
1555 /* Tree View Creation */
1556 /************************************************************/
1557 
1558 static gint
1559 gnc_tree_view_count_visible_columns (GncTreeView *view)
1560 {
1561  GList *columns, *node;
1562  gint count = 0;
1563 
1564  columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(view));
1565  for (node = columns; node; node = node->next)
1566  {
1567  GtkTreeViewColumn *col = GTK_TREE_VIEW_COLUMN(node->data);
1568 
1569  if (g_object_get_data (G_OBJECT(col), DEFAULT_VISIBLE) ||
1570  g_object_get_data (G_OBJECT(col), ALWAYS_VISIBLE))
1571  count++;
1572  }
1573  g_list_free (columns);
1574  return count;
1575 }
1576 
1577 void
1579 {
1580  GncTreeViewPrivate *priv;
1581  GtkTreeViewColumn *column;
1582  GList *columns;
1583  gboolean hide_menu_column;
1584 
1585  g_return_if_fail (GNC_IS_TREE_VIEW(view));
1586 
1587  ENTER(" ");
1588 
1589  /* Update the view and saved state */
1590  columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(view));
1591  g_list_foreach (columns, (GFunc)gnc_tree_view_update_visibility, view);
1592  g_list_free (columns);
1593 
1594  priv = GNC_TREE_VIEW_GET_PRIVATE(view);
1595  if (priv->state_section)
1596  priv->seen_state_visibility = TRUE;
1597 
1598  /* If only the first column is visible, hide the spacer and make that
1599  * column expand. */
1600  hide_menu_column = (gnc_tree_view_count_visible_columns (view) == 1);
1601  column = gtk_tree_view_get_column (GTK_TREE_VIEW(view), 0);
1602  gtk_tree_view_column_set_expand (column, hide_menu_column);
1603  gtk_tree_view_column_set_visible (priv->column_menu_column, !hide_menu_column);
1604 
1605  LEAVE(" ");
1606 }
1607 
1608 
1638 static void
1639 gnc_tree_view_column_properties (GncTreeView *view,
1640  GtkTreeViewColumn *column,
1641  const gchar *pref_name,
1642  gint data_column,
1643  gint default_width,
1644  gboolean resizable,
1645  GtkTreeIterCompareFunc column_sort_fn)
1646 {
1647  GncTreeViewPrivate *priv;
1648  GtkTreeModel *s_model;
1649  gboolean visible;
1650  int width = 0;
1651 
1652  /* Set data used by other functions */
1653  if (pref_name)
1654  g_object_set_data (G_OBJECT(column), PREF_NAME, (gpointer)pref_name);
1655  if (data_column == 0)
1656  g_object_set_data (G_OBJECT(column), ALWAYS_VISIBLE, GINT_TO_POINTER(1));
1657  g_object_set_data (G_OBJECT(column), MODEL_COLUMN,
1658  GINT_TO_POINTER(data_column));
1659 
1660  /* Get visibility */
1661  visible = gnc_tree_view_column_visible (view, NULL, pref_name);
1662 
1663  /* Set column attributes (without the sizing) */
1664  g_object_set (G_OBJECT(column),
1665  "visible", visible,
1666  "resizable", resizable && pref_name != NULL,
1667  "reorderable", pref_name != NULL,
1668  NULL);
1669 
1670  /* Get width */
1671  if (default_width == 0)
1672  {
1673  /* Set the sizing column attributes */
1674  g_object_set (G_OBJECT(column),
1675  "sizing", GTK_TREE_VIEW_COLUMN_AUTOSIZE,
1676  NULL);
1677  }
1678  else
1679  {
1680 
1681  /* If saved state comes back with a width of zero (or there is no saved
1682  * state width) the use the default width for the column. Allow for
1683  * padding L and R of the displayed data. */
1684  if (width == 0)
1685  width = default_width + 10;
1686  if (width == 0)
1687  width = 10;
1688 
1689  /* Set the sizing column attributes (including fixed-width) */
1690  g_object_set (G_OBJECT(column),
1691  "sizing", GTK_TREE_VIEW_COLUMN_FIXED,
1692  "fixed-width", width,
1693  NULL);
1694  /* Save the initially calculated preferred width for later
1695  * comparison to the actual width when saving state. Can't
1696  * use the "fixed-width" property for that because it changes
1697  * when the user resizes the column.
1698  */
1699  g_object_set_data (G_OBJECT(column),
1700  "default-width", GINT_TO_POINTER(width));
1701  }
1702 
1703  s_model = gtk_tree_view_get_model (GTK_TREE_VIEW(view));
1704  if (GTK_IS_TREE_SORTABLE(s_model))
1705  {
1706  gtk_tree_view_column_set_sort_column_id (column, data_column);
1707  if (column_sort_fn)
1708  {
1709  gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(s_model),
1710  data_column, column_sort_fn,
1711  GINT_TO_POINTER(data_column),
1712  NULL /* destroy fn */);
1713  }
1714  }
1715 
1716  // Used in registers, sort model not connected to view yet
1717  priv = GNC_TREE_VIEW_GET_PRIVATE(view);
1718  if (priv->sort_model != NULL)
1719  {
1720  gtk_tree_view_column_set_sort_column_id (column, data_column);
1721  if (column_sort_fn)
1722  {
1723  gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(priv->sort_model),
1724  data_column, column_sort_fn,
1725  view,
1726  NULL /* destroy fn */);
1727  }
1728  }
1729 
1730  /* Add to the column selection menu */
1731  if (pref_name)
1732  {
1733  gnc_tree_view_create_menu_item (column, view);
1734  }
1735 }
1736 
1747 GtkTreeViewColumn *
1749  const gchar *column_title,
1750  const gchar *column_short_title,
1751  const gchar *pref_name,
1752  gint model_data_column,
1753  gint model_visibility_column,
1754  GtkTreeIterCompareFunc column_sort_fn,
1755  renderer_toggled toggle_edited_cb)
1756 {
1757  GtkTreeViewColumn *column;
1758  GtkCellRenderer *renderer;
1759 
1760  g_return_val_if_fail (GNC_IS_TREE_VIEW(view), NULL);
1761 
1762  renderer = gtk_cell_renderer_toggle_new ();
1763  if (!toggle_edited_cb)
1764  {
1765  gtk_cell_renderer_toggle_set_activatable (GTK_CELL_RENDERER_TOGGLE(renderer), FALSE);
1766  }
1767  column =
1768  gtk_tree_view_column_new_with_attributes (column_short_title,
1769  renderer,
1770  "active", model_data_column,
1771  NULL);
1772 
1773  /* Add the full title to the object for menu creation */
1774  g_object_set_data_full (G_OBJECT(column), REAL_TITLE,
1775  g_strdup(column_title), g_free);
1776  if (toggle_edited_cb)
1777  g_signal_connect (G_OBJECT(renderer), "toggled",
1778  G_CALLBACK(toggle_edited_cb), view);
1779 
1780  if (model_visibility_column != GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS)
1781  gtk_tree_view_column_add_attribute (column, renderer,
1782  "visible", model_visibility_column);
1783 
1784 
1785  gnc_tree_view_column_properties (view, column, pref_name, model_data_column,
1786  0, FALSE, column_sort_fn);
1787 
1788  gnc_tree_view_append_column (view, column);
1789 
1790  /* Also add the full title to the object as a tooltip */
1791  gtk_widget_set_tooltip_text (gtk_tree_view_column_get_button (column), column_title);
1792 
1793  return column;
1794 }
1795 
1796 static void
1797 renderer_editing_canceled_cb (GtkCellRenderer *renderer, gpointer user_data)
1798 {
1799  GncTreeView *view = user_data;
1800  GncTreeViewPrivate *priv = GNC_TREE_VIEW_GET_PRIVATE(view);
1801  if (priv->editing_finished_cb)
1802  (priv->editing_finished_cb)(view, priv->editing_cb_data);
1803 }
1804 
1805 static void
1806 renderer_editing_started_cb (GtkCellRenderer *renderer,
1807  GtkCellEditable *editable, gchar *path, gpointer user_data)
1808 {
1809  GncTreeView *view = user_data;
1810  GncTreeViewPrivate *priv = GNC_TREE_VIEW_GET_PRIVATE(view);
1811  if (priv->editing_started_cb)
1812  (priv->editing_started_cb)(view, priv->editing_cb_data);
1813 }
1814 
1815 static void
1816 renderer_edited_cb (GtkCellRendererText *renderer, gchar *path,
1817  gchar *new_text, gpointer user_data)
1818 {
1819  GncTreeView *view = user_data;
1820  GncTreeViewPrivate *priv = GNC_TREE_VIEW_GET_PRIVATE(view);
1821  if (priv->editing_finished_cb)
1822  (priv->editing_finished_cb)(view, priv->editing_cb_data);
1823 }
1824 
1825 
1826 static GtkTreeViewColumn *
1827 add_text_column_variant (GncTreeView *view, GtkCellRenderer *renderer,
1828  const gchar *column_title,
1829  const gchar *pref_name,
1830  const gchar *icon_name,
1831  const gchar *sizing_text,
1832  gint model_data_column,
1833  gint model_visibility_column,
1834  GtkTreeIterCompareFunc column_sort_fn)
1835 {
1836  GtkTreeViewColumn *column;
1837  PangoLayout* layout;
1838  int default_width, title_width;
1839 
1840  g_return_val_if_fail (GNC_IS_TREE_VIEW(view), NULL);
1841 
1842  column = gtk_tree_view_column_new ();
1843  gtk_tree_view_column_set_title (column, column_title);
1844 
1845  /* Set up an icon renderer if requested */
1846  if (icon_name)
1847  {
1848  GtkCellRenderer *renderer_pix = gtk_cell_renderer_pixbuf_new ();
1849  g_object_set (renderer_pix, "icon-name", icon_name, NULL);
1850  gtk_tree_view_column_pack_start (column, renderer_pix, FALSE);
1851  }
1852 
1853  /* Set up a text renderer and attributes */
1854  gtk_tree_view_column_pack_start (column, renderer, TRUE);
1855 
1856  /* Set up the callbacks for when editing */
1857  g_signal_connect (G_OBJECT(renderer), "editing-canceled",
1858  (GCallback)renderer_editing_canceled_cb, view);
1859 
1860  g_signal_connect (G_OBJECT(renderer), "editing-started",
1861  (GCallback)renderer_editing_started_cb, view);
1862 
1863  g_signal_connect (G_OBJECT(renderer), "edited",
1864  (GCallback)renderer_edited_cb, view);
1865 
1866  /* Set renderer attributes controlled by the model */
1867  if (model_data_column != GNC_TREE_VIEW_COLUMN_DATA_NONE)
1868  gtk_tree_view_column_add_attribute (column, renderer,
1869  "text", model_data_column);
1870  if (model_visibility_column != GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS)
1871  gtk_tree_view_column_add_attribute (column, renderer,
1872  "visible", model_visibility_column);
1873 
1874  /* Default size is the larger of the column title and the sizing text */
1875  layout = gtk_widget_create_pango_layout (GTK_WIDGET(view), column_title);
1876  pango_layout_get_pixel_size (layout, &title_width, NULL);
1877  g_object_unref (layout);
1878  layout = gtk_widget_create_pango_layout (GTK_WIDGET(view), sizing_text);
1879  pango_layout_get_pixel_size (layout, &default_width, NULL);
1880  g_object_unref (layout);
1881  default_width = MAX(default_width, title_width);
1882  if (default_width)
1883  default_width += 10; /* padding on either side */
1884  gnc_tree_view_column_properties (view, column, pref_name, model_data_column,
1885  default_width, TRUE, column_sort_fn);
1886 
1887  gnc_tree_view_append_column (view, column);
1888  return column;
1889 }
1890 
1891 
1900 GtkTreeViewColumn *
1902  const gchar *column_title,
1903  const gchar *pref_name,
1904  const gchar *icon_name,
1905  const gchar *sizing_text,
1906  gint model_data_column,
1907  gint model_visibility_column,
1908  GtkTreeIterCompareFunc column_sort_fn)
1909 {
1910  GtkCellRenderer *renderer;
1911 
1912  g_return_val_if_fail (GNC_IS_TREE_VIEW(view), NULL);
1913 
1914  renderer = gtk_cell_renderer_text_new ();
1915 
1916  return add_text_column_variant (view, renderer,
1917  column_title, pref_name,
1918  icon_name, sizing_text,
1919  model_data_column,
1920  model_visibility_column,
1921  column_sort_fn);
1922 }
1923 
1932 GtkTreeViewColumn *
1934  const gchar *column_title,
1935  const gchar *pref_name,
1936  const gchar *icon_name,
1937  const gchar *sizing_text,
1938  gint model_data_column,
1939  gint model_visibility_column,
1940  GtkTreeIterCompareFunc column_sort_fn)
1941 {
1942  GtkCellRenderer *renderer;
1943 
1944  g_return_val_if_fail (GNC_IS_TREE_VIEW(view), NULL);
1945 
1946  renderer = gnc_cell_renderer_text_view_new ();
1947 
1948  return add_text_column_variant (view, renderer,
1949  column_title, pref_name,
1950  icon_name, sizing_text,
1951  model_data_column,
1952  model_visibility_column,
1953  column_sort_fn);
1954 }
1955 
1956 
1965 GtkTreeViewColumn *
1967  const gchar *column_title,
1968  const gchar *pref_name,
1969  const gchar *icon_name,
1970  const gchar *sizing_text,
1971  gint model_data_column,
1972  gint model_visibility_column,
1973  GtkTreeIterCompareFunc column_sort_fn)
1974 {
1975  GtkTreeViewColumn *column;
1976  GtkCellRenderer *renderer;
1977  PangoLayout* layout;
1978  int default_width, title_width;
1979 
1980  g_return_val_if_fail (GNC_IS_TREE_VIEW(view), NULL);
1981 
1982  column = gtk_tree_view_column_new ();
1983  gtk_tree_view_column_set_title (column, column_title);
1984 
1985  /* Set up an icon renderer if requested */
1986  if (icon_name)
1987  {
1988  renderer = gtk_cell_renderer_pixbuf_new ();
1989  g_object_set (renderer, "icon-name", icon_name, NULL);
1990  gtk_tree_view_column_pack_start (column, renderer, FALSE);
1991  }
1992 
1993  /* Set up a text renderer and attributes */
1994  renderer = gnc_cell_renderer_date_new (TRUE);
1995  gtk_tree_view_column_pack_start (column, renderer, TRUE);
1996 
1997  /* Set renderer attributes controlled by the model */
1998  if (model_data_column != GNC_TREE_VIEW_COLUMN_DATA_NONE)
1999  gtk_tree_view_column_add_attribute (column, renderer,
2000  "text", model_data_column);
2001  if (model_visibility_column != GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS)
2002  gtk_tree_view_column_add_attribute (column, renderer,
2003  "visible", model_visibility_column);
2004 
2005  /* Default size is the larger of the column title and the sizing text */
2006  layout = gtk_widget_create_pango_layout (GTK_WIDGET(view), column_title);
2007  pango_layout_get_pixel_size (layout, &title_width, NULL);
2008  g_object_unref (layout);
2009  layout = gtk_widget_create_pango_layout (GTK_WIDGET(view), sizing_text);
2010  pango_layout_get_pixel_size (layout, &default_width, NULL);
2011  g_object_unref (layout);
2012  default_width = MAX(default_width, title_width);
2013  if (default_width)
2014  default_width += 10; /* padding on either side */
2015  gnc_tree_view_column_properties (view, column, pref_name, model_data_column,
2016  default_width, TRUE, column_sort_fn);
2017 
2018  gnc_tree_view_append_column (view, column);
2019  return column;
2020 }
2021 
2022 
2023 GtkTreeViewColumn *
2025  const gchar *column_title,
2026  const gchar *pref_name,
2027  const gchar *sizing_text,
2028  gint model_data_column,
2029  gint model_visibility_column,
2030  GtkTreeModel *combo_tree_model,
2031  gint combo_model_text_column,
2032  GtkTreeIterCompareFunc column_sort_fn)
2033 {
2034  GtkTreeViewColumn *column;
2035  GtkCellRenderer *renderer;
2036  PangoLayout* layout;
2037  int default_width, title_width;
2038 
2039  g_return_val_if_fail (GNC_IS_TREE_VIEW(view), NULL);
2040 
2041  column = gtk_tree_view_column_new ();
2042  gtk_tree_view_column_set_title (column, gettext(column_title));
2043 
2044  /* Set up a renderer and attributes */
2045  renderer = gtk_cell_renderer_combo_new ();
2046  gtk_tree_view_column_pack_start (column, renderer, TRUE);
2047 
2048  /* Set renderer attributes controlled by the model */
2049  if (model_data_column != GNC_TREE_VIEW_COLUMN_DATA_NONE)
2050  gtk_tree_view_column_add_attribute (column, renderer,
2051  "text", model_data_column);
2052  if (model_visibility_column != GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS)
2053  gtk_tree_view_column_add_attribute (column, renderer,
2054  "visible", model_visibility_column);
2055 
2056  /* Default size is the larger of the column title and the sizing text */
2057  layout = gtk_widget_create_pango_layout (GTK_WIDGET(view), column_title);
2058  pango_layout_get_pixel_size (layout, &title_width, NULL);
2059  g_object_unref (layout);
2060  layout = gtk_widget_create_pango_layout (GTK_WIDGET(view), sizing_text);
2061  pango_layout_get_pixel_size (layout, &default_width, NULL);
2062  g_object_unref (layout);
2063  default_width = MAX(default_width, title_width);
2064  if (default_width)
2065  default_width += 10; /* padding on either side */
2066 
2067  gnc_tree_view_column_properties (view, column, pref_name, model_data_column,
2068  default_width, TRUE, column_sort_fn);
2069 
2070  /* Stuff specific to combo */
2071  if (combo_tree_model)
2072  {
2073  g_object_set (G_OBJECT(renderer), "model", combo_tree_model,
2074  "text-column", combo_model_text_column, NULL);
2075  }
2076  /* TODO: has-entry? */
2077 
2078  gnc_tree_view_append_column (view, column);
2079  return column;
2080 }
2081 
2082 GtkCellRenderer *
2083 gnc_tree_view_column_get_renderer (GtkTreeViewColumn *column)
2084 {
2085  GList *renderers;
2086  GtkCellRenderer *cr = NULL;
2087 
2088  g_return_val_if_fail (GTK_TREE_VIEW_COLUMN(column), NULL);
2089 
2090  /* Get the list of one renderer */
2091  renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT(column));
2092  if (g_list_length (renderers) > 0)
2093  cr = GTK_CELL_RENDERER(renderers->data);
2094  g_list_free (renderers);
2095 
2096  return cr;
2097 }
2098 
2109 GtkTreeViewColumn *
2111  const gchar *column_title,
2112  const gchar *pref_name,
2113  const gchar *sizing_text,
2114  gint model_data_column,
2115  gint model_color_column,
2116  gint model_visibility_column,
2117  GtkTreeIterCompareFunc column_sort_fn)
2118 {
2119  GtkTreeViewColumn *column;
2120  GtkCellRenderer *renderer;
2121  gfloat alignment = 1.0;
2122 
2123  column = gnc_tree_view_add_text_column (view, column_title, pref_name,
2124  NULL, sizing_text, model_data_column,
2125  model_visibility_column,
2126  column_sort_fn);
2127 
2128  renderer = gnc_tree_view_column_get_renderer (column);
2129 
2130  /* Right align the column title and data for both ltr and rtl */
2131  if (gtk_widget_get_direction (GTK_WIDGET(view)) == GTK_TEXT_DIR_RTL)
2132  alignment = 0.0;
2133 
2134  g_object_set (G_OBJECT(column), "alignment", alignment, NULL);
2135  g_object_set (G_OBJECT(renderer), "xalign", alignment, NULL);
2136 
2137  /* Change the text color */
2138  if (model_color_column != GNC_TREE_VIEW_COLUMN_COLOR_NONE)
2139  gtk_tree_view_column_add_attribute (column, renderer,
2140  "foreground", model_color_column);
2141 
2142  return column;
2143 }
2144 
2153 gint
2155  GtkTreeViewColumn *column)
2156 {
2157  int n = gtk_tree_view_get_n_columns (GTK_TREE_VIEW(view));
2158 
2159  /* Ignore the initial column, the selection menu */
2160  if (n >= 1)
2161  n -= 1;
2162  return gtk_tree_view_insert_column (GTK_TREE_VIEW(view), column, n);
2163 }
2164 
2165 static gboolean
2166 get_column_next_to (GtkTreeView *tv, GtkTreeViewColumn **col, gboolean backward)
2167 {
2168  GList *cols, *node;
2169  GtkTreeViewColumn *c = NULL;
2170  gint seen = 0;
2171  gboolean wrapped = FALSE;
2172 
2173  cols = gtk_tree_view_get_columns (tv);
2174  g_return_val_if_fail (cols != NULL, FALSE);
2175 
2176  node = g_list_find (cols, *col);
2177  g_return_val_if_fail (node, FALSE);
2178  do
2179  {
2180  node = backward ? node->prev : node->next;
2181  if (!node)
2182  {
2183  wrapped = TRUE;
2184  node = backward ? g_list_last (cols) : cols;
2185  }
2186  c = GTK_TREE_VIEW_COLUMN (node->data);
2187  if (c && gtk_tree_view_column_get_visible (c))
2188  seen++;
2189  if (c == *col) break;
2190  }
2191  while (!seen);
2192 
2193  g_list_free (cols);
2194  *col = c;
2195  return wrapped;
2196 }
2197 
2198 gboolean
2199 gnc_tree_view_path_is_valid (GncTreeView *view, GtkTreePath *path)
2200 {
2201  GtkTreeView *tv = GTK_TREE_VIEW(view);
2202  GtkTreeModel *s_model;
2203  GtkTreeIter iter;
2204 
2205  s_model = gtk_tree_view_get_model (tv);
2206  return gtk_tree_model_get_iter (s_model, &iter, path);
2207 }
2208 
2209 void
2210 gnc_tree_view_keynav (GncTreeView *view, GtkTreeViewColumn **col,
2211  GtkTreePath *path, GdkEventKey *event)
2212 {
2213  GtkTreeView *tv = GTK_TREE_VIEW(view);
2214  gint depth;
2215  gboolean shifted;
2216 
2217  if (event->type != GDK_KEY_PRESS) return;
2218 
2219  switch (event->keyval)
2220  {
2221  case GDK_KEY_Tab:
2222  case GDK_KEY_ISO_Left_Tab:
2223  case GDK_KEY_KP_Tab:
2224  shifted = event->state & GDK_SHIFT_MASK;
2225  if (get_column_next_to (tv, col, shifted))
2226  {
2227  /* This is the end (or beginning) of the line, buddy. */
2228  depth = gtk_tree_path_get_depth (path);
2229  if (shifted)
2230  {
2231  if (!gtk_tree_path_prev (path) && depth > 1)
2232  {
2233  gtk_tree_path_up (path);
2234  }
2235  }
2236  else if (gtk_tree_view_row_expanded (tv, path))
2237  {
2238  gtk_tree_path_down (path);
2239  }
2240  else
2241  {
2242  gtk_tree_path_next (path);
2243  if (!gnc_tree_view_path_is_valid (view, path) && depth > 2)
2244  {
2245  gtk_tree_path_prev (path);
2246  gtk_tree_path_up (path);
2247  gtk_tree_path_next (path);
2248  }
2249  if (!gnc_tree_view_path_is_valid (view, path) && depth > 1)
2250  {
2251  gtk_tree_path_prev (path);
2252  gtk_tree_path_up (path);
2253  gtk_tree_path_next (path);
2254  }
2255  }
2256  }
2257  break;
2258 
2259  case GDK_KEY_Return:
2260  case GDK_KEY_KP_Enter:
2261  if (gtk_tree_view_row_expanded (tv, path))
2262  {
2263  gtk_tree_path_down (path);
2264  }
2265  else
2266  {
2267  depth = gtk_tree_path_get_depth (path);
2268  gtk_tree_path_next (path);
2269  if (!gnc_tree_view_path_is_valid (view, path) && depth > 1)
2270  {
2271  gtk_tree_path_prev (path);
2272  gtk_tree_path_up (path);
2273  gtk_tree_path_next (path);
2274  }
2275  }
2276  break;
2277  }
2278  return;
2279 }
2280 
2281 void
2282 gnc_tree_view_set_editing_started_cb (GncTreeView *view, GFunc editing_started_cb, gpointer editing_cb_data)
2283 {
2284  GncTreeViewPrivate *priv;
2285 
2286  if (!view && !editing_started_cb)
2287  return;
2288 
2289  priv = GNC_TREE_VIEW_GET_PRIVATE(view);
2290 
2291  priv->editing_started_cb = editing_started_cb;
2292  priv->editing_cb_data = editing_cb_data;
2293 }
2294 
2295 void
2296 gnc_tree_view_set_editing_finished_cb (GncTreeView *view, GFunc editing_finished_cb, gpointer editing_cb_data)
2297 {
2298  GncTreeViewPrivate *priv;
2299 
2300  if (!view && !editing_finished_cb)
2301  return;
2302 
2303  priv = GNC_TREE_VIEW_GET_PRIVATE(view);
2304 
2305  priv->editing_finished_cb = editing_finished_cb;
2306  priv->editing_cb_data = editing_cb_data;
2307 }
2308 
void gnc_tree_view_set_sort_user_data(GncTreeView *view, GtkTreeModel *s_model)
This allows the columns to be setup without the model connected.
Functions to load, save and get gui state.
void gnc_tree_view_expand_columns(GncTreeView *view, gchar *first_column_name,...)
This function set the columns that will be allocated the free space in the view.
gulong gnc_prefs_register_cb(const char *group, const gchar *pref_name, gpointer func, gpointer user_data)
Register a callback that gets triggered when the given preference changes.
Definition: gnc-prefs.c:128
void gnc_gobject_tracking_remember(GObject *object, GObjectClass *klass)
Tell gnucash to remember this object in the database.
void gnc_tree_view_set_editing_started_cb(GncTreeView *view, GFunc editing_started_cb, gpointer editing_cb_data)
Setup a callback for when the user starts editing so appropriate actions can be taken like disable th...
void gnc_gobject_tracking_forget(GObject *object)
Tell gnucash to remember this object in the database.
common utilities for manipulating a GtkTreeView within gnucash
#define DEBUG(format, args...)
Print a debugging message.
Definition: qoflog.h:264
GtkTreeViewColumn * gnc_tree_view_add_date_column(GncTreeView *view, const gchar *column_title, const gchar *pref_name, const gchar *icon_name, const gchar *sizing_text, gint model_data_column, gint model_visibility_column, GtkTreeIterCompareFunc column_sort_fn)
This function adds a new date column to a GncTreeView base view.
gboolean gnc_tree_view_get_show_column_menu(GncTreeView *view)
This function is called to get the current value of the "show-column-menu" property.
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)
This function adds a new combobox column to a GncTreeView base view.
void gnc_tree_view_remove_state_information(GncTreeView *view)
Completely wipe the treeview&#39;s state information (column visibility, width, sorting order...
void gnc_tree_view_set_show_column_menu(GncTreeView *view, gboolean visible)
This function is called to set the "show-column-menu" property on this view.
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)
This function adds a new numeric column to a GncTreeView base view.
GtkTreeViewColumn * gnc_tree_view_find_column_by_name(GncTreeView *view, const gchar *wanted)
Find a tree column given the "pref name" used with saved state.
#define ENTER(format, args...)
Print a function entry debugging message.
Definition: qoflog.h:272
GKeyFile * gnc_state_get_current(void)
Returns a pointer to the most recently loaded state.
Definition: gnc-state.c:248
gint gnc_tree_view_append_column(GncTreeView *view, GtkTreeViewColumn *column)
Add a column to a view based upon a GncTreeView.
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)
This function adds a new toggle column to a GncTreeView base view.
void gnc_tree_view_set_editing_finished_cb(GncTreeView *view, GFunc editing_finished_cb, gpointer editing_cb_data)
Setup a callback for when the user finishes editing so appropriate actions can be taken like enable t...
GtkCellRenderer * gnc_tree_view_column_get_renderer(GtkTreeViewColumn *column)
Return the "main" cell renderer from a GtkTreeViewColumn added to a GncTreeView my one of the conveni...
GtkTreeViewColumn * gnc_tree_view_add_text_view_column(GncTreeView *view, const gchar *column_title, const gchar *pref_name, const gchar *icon_name, const gchar *sizing_text, gint model_data_column, gint model_visibility_column, GtkTreeIterCompareFunc column_sort_fn)
This function adds a new text view column to a GncTreeView base view.
Gobject helper routines.
void gnc_tree_view_configure_columns(GncTreeView *view)
Make all the correct columns visible, respecting their default visibility setting, their "always" visibility setting, and the last saved state if available.
void gnc_tree_view_save_state(GncTreeView *view)
This function is called to write the treeview&#39;s state information (column visibility, width, sorting order,..) to the state file.
void gnc_tree_view_set_control_column_background(GncTreeView *view, gint column, GtkTreeCellDataFunc func)
This function links the cell backgrounds of the two control columns to a column in the model that has...
Gnome specific utility functions.
All type declarations for the whole Gnucash engine.
const gchar * gnc_tree_view_get_state_section(GncTreeView *view)
Get the name of the state section this tree view is associated with.
Generic api to store and retrieve preferences.
void gnc_tree_view_set_state_section(GncTreeView *view, const gchar *section)
Set up or remove an association between a saved state section and the display of a view...
GtkTreeViewColumn * gnc_tree_view_add_text_column(GncTreeView *view, const gchar *column_title, const gchar *pref_name, const gchar *icon_name, const gchar *sizing_text, gint model_data_column, gint model_visibility_column, GtkTreeIterCompareFunc column_sort_fn)
This function adds a new text column to a GncTreeView base view.
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition: qoflog.h:282
Private Data Structure.
void gnc_prefs_remove_cb_by_func(const gchar *group, const gchar *pref_name, gpointer func, gpointer user_data)
Remove a function that was registered for a callback when the given preference changed.
Definition: gnc-prefs.c:143