GnuCash  5.6-150-g038405b370+
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-glib-utils.h"
44 #include "gnc-gnome-utils.h"
45 #include "gnc-gobject-utils.h"
46 #include "gnc-cell-renderer-label.h"
47 #include "gnc-cell-renderer-text-view.h"
48 #include "gnc-state.h"
49 #include "gnc-prefs.h"
50 #include "dialog-utils.h"
51 
52 /* The actual state key for a particular column visibility. This is
53  * attached to the menu items that are in the column selection menu.
54  * Makes it very easy to update saved state when a menu item is toggled. */
55 #define STATE_KEY "state-key"
56 
57 /* State keys within this particular saved state section. */
58 #define STATE_KEY_SORT_COLUMN "sort_column"
59 #define STATE_KEY_SORT_ORDER "sort_order"
60 #define STATE_KEY_COLUMN_ORDER "column_order"
61 
62 /* Partial state keys within this particular saved state section. These
63  are appended to the various column names to create the actual
64  keys. */
65 #define STATE_KEY_SUFF_VISIBLE "visible"
66 #define STATE_KEY_SUFF_WIDTH "width"
67 
68 enum
69 {
70  PROP_0,
71  PROP_STATE_SECTION,
72  PROP_SHOW_COLUMN_MENU,
73 };
74 
77 /* This static indicates the debugging module that this .o belongs to. */
78 static QofLogModule log_module = GNC_MOD_GUI;
79 
80 /**** Declarations ******************************************************/
81 static void gnc_tree_view_constructed (GObject *object);
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 G_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*)gnc_tree_view_get_instance_private((GncTreeView*)o))
136 
137 
138 /************************************************************/
139 /* g_object required functions */
140 /************************************************************/
141 
154 static void
155 gnc_tree_view_class_init (GncTreeViewClass *klass)
156 {
157  GObjectClass *gobject_class;
158  GtkWidgetClass *gtkwidget_class;
159 
160  gobject_class = G_OBJECT_CLASS(klass);
161  gtkwidget_class = GTK_WIDGET_CLASS(klass);
162 
163  gobject_class->set_property = gnc_tree_view_set_property;
164  gobject_class->get_property = gnc_tree_view_get_property;
165 
166  g_object_class_install_property (gobject_class,
167  PROP_STATE_SECTION,
168  g_param_spec_string ("state-section",
169  "State Section",
170  "The section name in the saved state to use for (re)storing the treeview's visual state (visible columns, sort order,...",
171  NULL,
172  G_PARAM_READWRITE));
173  g_object_class_install_property (gobject_class,
174  PROP_SHOW_COLUMN_MENU,
175  g_param_spec_boolean ("show-column-menu",
176  "Show Column Menu",
177  "Show the column menu so user can change what columns are visible.",
178  FALSE,
179  G_PARAM_READWRITE));
180 
181  /* GObject signals */
182  gobject_class->constructed = gnc_tree_view_constructed;
183  gobject_class->finalize = gnc_tree_view_finalize;
184 
185  /* GtkWidget signals */
186  gtkwidget_class->destroy = gnc_tree_view_destroy;
187 }
188 
189 static void
190 gnc_tree_view_update_grid_lines (gpointer prefs, gchar* pref, gpointer user_data)
191 {
192  GncTreeView *view = user_data;
193  gtk_tree_view_set_grid_lines (GTK_TREE_VIEW(view), gnc_tree_view_get_grid_lines_pref ());
194 }
195 
196 static gboolean
197 gnc_tree_view_select_column_icon_cb (GtkWidget *widget, GdkEventButton *event, gpointer user_data)
198 {
199  GncTreeView *view = user_data;
200  GncTreeViewPrivate *priv;
201  GtkStyleContext *stylectxt = gtk_widget_get_style_context (widget);
202  GtkBorder padding;
203 
204  // if the event button is not the right one, leave.
205  if (event->button != 1)
206  return FALSE;
207 
208  priv = GNC_TREE_VIEW_GET_PRIVATE(view);
209 
210  gtk_style_context_get_padding (stylectxt, GTK_STATE_FLAG_NORMAL, &padding);
211 
212  if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
213  {
214  if (event->x < (gtk_widget_get_allocated_width (priv->column_menu_icon_box) + padding.left))
215  gnc_tree_view_select_column_cb (priv->column_menu_column, view);
216  }
217  else
218  {
219  if (event->x > (gtk_widget_get_allocated_width (widget) -
220  (gtk_widget_get_allocated_width (priv->column_menu_icon_box) + padding.right)))
221  gnc_tree_view_select_column_cb (priv->column_menu_column, view);
222  }
223  return FALSE;
224 }
225 
235 static void
236 gnc_tree_view_init (GncTreeView *view)
237 {
238  GncTreeViewPrivate *priv;
239  GtkTreeViewColumn *column;
240  GtkWidget *sep, *icon;
241 
242  priv = GNC_TREE_VIEW_GET_PRIVATE(view);
243  priv->column_menu = NULL;
244  priv->show_column_menu = FALSE;
245  priv->sort_model = NULL;
246  priv->state_section = NULL;
247  priv->seen_state_visibility = FALSE;
248  priv->columns_changed_cb_id = 0;
249  priv->sort_column_changed_cb_id = 0;
250  priv->size_allocate_cb_id = 0;
251 
252  // Set the name for this widget so it can be easily manipulated with css
253  gtk_widget_set_name (GTK_WIDGET(view), "gnc-id-tree-view");
254 
255  /* Handle column drag and drop */
256  gtk_tree_view_set_column_drag_function (GTK_TREE_VIEW(view),
257  gnc_tree_view_drop_ok_cb, NULL, NULL);
258 
259  // Set grid lines option to preference
260  gtk_tree_view_set_grid_lines (GTK_TREE_VIEW(view), gnc_tree_view_get_grid_lines_pref ());
261  gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL, GNC_PREF_GRID_LINES_HORIZONTAL,
262  gnc_tree_view_update_grid_lines, view);
263  gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL, GNC_PREF_GRID_LINES_VERTICAL,
264  gnc_tree_view_update_grid_lines, view);
265 
266  /* Create the last column which contains the column selection
267  * widget. gnc_tree_view_add_text_column will do most of the
268  * work. */
269  icon = gtk_image_new_from_icon_name ("pan-down-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR);
270 
271  priv->column_menu_icon_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
272  gtk_box_set_homogeneous (GTK_BOX(priv->column_menu_icon_box), FALSE);
273 
274  gtk_widget_set_margin_start (GTK_WIDGET(icon), 5);
275 
276  gtk_box_pack_end (GTK_BOX(priv->column_menu_icon_box), icon, FALSE, FALSE, 0);
277 
278  sep = gtk_separator_new (GTK_ORIENTATION_VERTICAL);
279  gtk_box_pack_end (GTK_BOX(priv->column_menu_icon_box), sep, FALSE, FALSE, 0);
280 
281  gtk_widget_show_all (priv->column_menu_icon_box);
282 
283  column = gnc_tree_view_add_text_column (view, NULL, NULL, NULL, NULL,
284  -1, -1, NULL);
285  g_object_set (G_OBJECT(column),
286  "clickable", TRUE,
287  "widget", priv->column_menu_icon_box,
288  "alignment", 1.0,
289  "expand", TRUE,
290  (gchar *)NULL);
291 
292  priv->column_menu_column = column;
293 
294  // get the actual column button by looking at the parents of the column_menu_icon
295  {
296  GtkWidget *mybox = gtk_widget_get_parent (icon);
297  GtkWidget *walign = gtk_widget_get_parent (mybox);
298  GtkWidget *box = gtk_widget_get_parent (walign);
299  GtkWidget *button = gtk_widget_get_parent (box);
300 
301  if (!GTK_IS_BUTTON(button)) // just in case this order changes.
302  {
303  // this will fire for the whole column header
304  g_signal_connect (G_OBJECT(column), "clicked",
305  G_CALLBACK(gnc_tree_view_select_column_cb),
306  view);
307  }
308  else
309  {
310  /* this part will restrict the mouse click to just where the
311  icon is, tried using an eventbox but it would only work
312  some of the time */
313  gtk_widget_set_events (button, GDK_BUTTON_PRESS_MASK);
314 
315  g_signal_connect (G_OBJECT(button), "button_press_event",
316  G_CALLBACK(gnc_tree_view_select_column_icon_cb),
317  view);
318  }
319  }
320  gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
321 }
322 
329  static void
330 gnc_tree_view_constructed (GObject *obj)
331 {
333 
334  G_OBJECT_CLASS (gnc_tree_view_parent_class)->constructed (obj);
335 }
336 
347 static void
348 gnc_tree_view_finalize (GObject *object)
349 {
350  ENTER("view %p", object);
351  g_return_if_fail (object != NULL);
352  g_return_if_fail (GNC_IS_TREE_VIEW(object));
353 
355 
356  G_OBJECT_CLASS(gnc_tree_view_parent_class)->finalize (object);
357  LEAVE(" ");
358 }
359 
371 static void
372 gnc_tree_view_destroy (GtkWidget *widget)
373 {
374  GncTreeView *view;
375  GncTreeViewPrivate *priv;
376 
377  ENTER("view %p", widget);
378  g_return_if_fail (widget != NULL);
379  g_return_if_fail (GNC_IS_TREE_VIEW(widget));
380 
381  view = GNC_TREE_VIEW(widget);
382 
383  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL, GNC_PREF_GRID_LINES_HORIZONTAL,
384  gnc_tree_view_update_grid_lines, view);
385  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL, GNC_PREF_GRID_LINES_VERTICAL,
386  gnc_tree_view_update_grid_lines, view);
387 
388  priv = GNC_TREE_VIEW_GET_PRIVATE(view);
389 
390  if (priv->state_section)
391  {
393  }
394  g_free (priv->state_section);
395  priv->state_section = NULL;
396 
397  if (priv->column_menu)
398  {
399  DEBUG("removing column selection menu");
400  g_object_unref (priv->column_menu);
401  priv->column_menu = NULL;
402  }
403 
404  GTK_WIDGET_CLASS(gnc_tree_view_parent_class)->destroy (widget);
405  LEAVE(" ");
406 }
407 
410 /************************************************************/
411 /* g_object other functions */
412 /************************************************************/
413 
424 static void
425 gnc_tree_view_get_property (GObject *object,
426  guint prop_id,
427  GValue *value,
428  GParamSpec *pspec)
429 {
430  GncTreeView *view = GNC_TREE_VIEW(object);
431  GncTreeViewPrivate *priv;
432 
433  priv = GNC_TREE_VIEW_GET_PRIVATE(view);
434  switch (prop_id)
435  {
436  case PROP_STATE_SECTION:
437  g_value_set_string (value, priv->state_section);
438  break;
439  case PROP_SHOW_COLUMN_MENU:
440  g_value_set_boolean (value, priv->show_column_menu);
441  break;
442  default:
443  G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
444  break;
445  }
446 }
447 
448 
457 static void
458 gnc_tree_view_set_property (GObject *object,
459  guint prop_id,
460  const GValue *value,
461  GParamSpec *pspec)
462 {
463  GncTreeView *view = GNC_TREE_VIEW(object);
464 
465  switch (prop_id)
466  {
467  case PROP_STATE_SECTION:
468  gnc_tree_view_set_state_section (view, g_value_get_string (value));
469  break;
470  case PROP_SHOW_COLUMN_MENU:
471  gnc_tree_view_set_show_column_menu (view, g_value_get_boolean (value));
472  break;
473  default:
474  G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
475  break;
476  }
477 }
478 
481 /************************************************************/
482 /* Auxiliary Functions */
483 /************************************************************/
502 static GtkTreeViewColumn *
503 view_column_find_by_model_id (GncTreeView *view,
504  const gint wanted)
505 {
506  GtkTreeViewColumn *column, *found = NULL;
507  GList *column_list, *tmp;
508  gint id;
509 
510  // ENTER("view %p, name %s", view, name);
511  column_list = gtk_tree_view_get_columns (GTK_TREE_VIEW(view));
512  for (tmp = column_list; tmp; tmp = g_list_next (tmp))
513  {
514  column = tmp->data;
515  id = GPOINTER_TO_INT(g_object_get_data (G_OBJECT(column), MODEL_COLUMN));
516  if (id != wanted)
517  continue;
518  found = column;
519  break;
520  }
521  g_list_free (column_list);
522 
523  // LEAVE("column %p", found);
524  return found;
525 }
526 
537 GtkTreeViewColumn *
539  const gchar *wanted)
540 {
541  GtkTreeViewColumn *column, *found = NULL;
542  GList *column_list, *tmp;
543  const gchar *name;
544 
545  // ENTER("view %p, wanted %s", view, wanted);
546  column_list = gtk_tree_view_get_columns(GTK_TREE_VIEW(view));
547  for (tmp = column_list; tmp; tmp = g_list_next (tmp))
548  {
549  column = tmp->data;
550  name = g_object_get_data (G_OBJECT(column), PREF_NAME);
551  if (!name || (strcmp(name, wanted) != 0))
552  continue;
553  found = column;
554  break;
555  }
556  g_list_free (column_list);
557 
558  // LEAVE("column %p", found);
559  return found;
560 }
561 
564 /************************************************************/
565 /* Tree Callbacks */
566 /************************************************************/
567 
596 static gboolean
597 gnc_tree_view_drop_ok_cb (GtkTreeView *view,
598  GtkTreeViewColumn *column,
599  GtkTreeViewColumn *prev_column,
600  GtkTreeViewColumn *next_column,
601  gpointer data)
602 {
603  const gchar *pref_name;
604 
605  /* Should we allow a drop at the left side of the tree view before
606  * the widget to open a new display level? I can think of cases
607  * where the user might want to do this with a checkbox column. */
608  if (prev_column == NULL)
609  return TRUE;
610 
611  /* Do not allow a drop at the right side of the tree view after the
612  * column selection widget. */
613  if (next_column == NULL)
614  return FALSE;
615 
616  /* Columns without pref names are considered fixed at the right hand
617  * side of the view. At the time of this writing, the only two are
618  * the column where the "column selection widget" is stored, and the
619  * "padding" column to the left of that where extra view space ends
620  * up. */
621  pref_name = g_object_get_data (G_OBJECT(prev_column), PREF_NAME);
622  if (!pref_name)
623  return FALSE;
624 
625  /* Everything else is allowed. */
626  return TRUE;
627 }
628 
631 /************************************************************/
632 /* State Setup / Callbacks */
633 /************************************************************/
634 
657 static gboolean
658 gnc_tree_view_column_visible (GncTreeView *view,
659  GtkTreeViewColumn *column,
660  const gchar *pref_name)
661 {
662  GncTreeViewPrivate *priv;
663  gboolean visible;
664  const gchar *col_name = pref_name;
665 
666  ENTER("column %p, name %s", column, pref_name ? pref_name : "(null)");
667  priv = GNC_TREE_VIEW_GET_PRIVATE(view);
668  if (column)
669  {
670  if (g_object_get_data (G_OBJECT(column), ALWAYS_VISIBLE))
671  {
672  LEAVE("1, first column");
673  return TRUE;
674  }
675  col_name = g_object_get_data (G_OBJECT(column), PREF_NAME);
676  DEBUG("col_name is %s", col_name ? col_name : "(null)");
677  }
678 
679  if (!col_name)
680  {
681  LEAVE("1, no pref name");
682  return TRUE;
683  }
684 
685  /* Using saved state ? */
686  if (priv->state_section)
687  {
688  GKeyFile *state_file = gnc_state_get_current ();
689  gchar *key = g_strdup_printf ("%s_%s", col_name, STATE_KEY_SUFF_VISIBLE);
690 
691  if (g_key_file_has_key (state_file, priv->state_section, key, NULL))
692  {
693  visible = g_key_file_get_boolean (state_file, priv->state_section, key, NULL);
694  g_free (key);
695  LEAVE("%d, state defined visibility", visible);
696  return visible;
697  }
698  }
699 
700  /* Check the default columns list */
701  visible = column ?
702  (g_object_get_data (G_OBJECT(column), DEFAULT_VISIBLE) != NULL) : FALSE;
703  LEAVE("defaults says %d", visible);
704  return visible;
705 }
706 
717 static void
718 gnc_tree_view_update_visibility (GtkTreeViewColumn *column,
719  GncTreeView *view)
720 {
721  gboolean visible;
722 
723  g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN(column));
724  g_return_if_fail (GNC_IS_TREE_VIEW(view));
725 
726  ENTER(" ");
727  visible = gnc_tree_view_column_visible (view, column, NULL);
728  gtk_tree_view_column_set_visible (column, visible);
729  LEAVE("made %s", visible ? "visible" : "invisible");
730 }
731 
742 static gchar *
743 gnc_tree_view_get_sort_order (GncTreeView *view)
744 {
745  GtkTreeModel *s_model;
746  GtkSortType order;
747  gint current;
748  gchar *order_str = NULL;
749 
750  s_model = gtk_tree_view_get_model (GTK_TREE_VIEW(view));
751  if (!s_model)
752  return NULL; /* no model, so sort order doesn't make sense */
753 
754  if (!gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE(s_model),
755  &current, &order))
756  return NULL; /* Model is not sorted, return */
757 
758  gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(s_model),
759  current, order);
760  order_str = g_strdup (order == GTK_SORT_ASCENDING ? "ascending" : "descending");
761  DEBUG("current sort_order is %s", order_str);
762  return order_str;
763 }
764 
774 static gchar *
775 gnc_tree_view_get_sort_column (GncTreeView *view)
776 {
777  GtkTreeModel *s_model;
778  GtkTreeViewColumn *column;
779  GtkSortType order;
780  gint current;
781  const gchar *name;
782 
783  s_model = gtk_tree_view_get_model (GTK_TREE_VIEW(view));
784  if (!s_model)
785  return NULL; /* no model -> no sort column */
786 
787  if (!gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE(s_model),
788  &current, &order))
789  return NULL; /* model not sorted */
790 
791  column = view_column_find_by_model_id (view, current);
792  if (!column)
793  return NULL; /* column not visible, can't be used for sorting */
794 
795  name = g_object_get_data (G_OBJECT(column), PREF_NAME);
796  DEBUG("current sort column is %s", name ? name : "(NULL)");
797  return g_strdup (name);
798 }
799 
800 
801 
812 static gchar **
813 gnc_tree_view_get_column_order (GncTreeView *view,
814  gsize *length)
815 {
816  const GList *tmp;
817  GList *columns;
818  gulong num_cols = 0;
819  gchar *col_names = NULL;
820  gchar **col_str_list;
821 
822  /* First, convert from names to pointers */
823  ENTER(" ");
824 
825  columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(view));
826  for (tmp = columns; tmp; tmp = g_list_next(tmp))
827  {
828  GtkTreeViewColumn *column = tmp->data;
829  const gchar *name = g_object_get_data (G_OBJECT(column), PREF_NAME);
830  if (!col_names)
831  col_names = g_strdup (name);
832  else
833  {
834  gchar *col_names_prev = col_names;
835  col_names = g_strjoin (";", col_names_prev, name, NULL);
836  g_free (col_names_prev);
837  }
838  num_cols++;
839  }
840  //DEBUG ("got %lu columns: %s", num_cols, col_names);
841  col_str_list = g_strsplit (col_names, ";", 0);
842 
843  /* Clean up */
844  g_list_free (columns);
845  g_free (col_names);
846 
847  LEAVE("column order get");
848  *length = num_cols;
849  return col_str_list;
850 }
851 
862 static void
863 gnc_tree_view_set_sort_order (GncTreeView *view,
864  const gchar *name)
865 {
866  GtkTreeModel *s_model;
867  GtkSortType order = GTK_SORT_ASCENDING;
868  gint current;
869 
870  s_model = gtk_tree_view_get_model (GTK_TREE_VIEW(view));
871  if (!s_model)
872  return;
873  if (g_strcmp0 (name, "descending") == 0)
874  order = GTK_SORT_DESCENDING;
875  if (!gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE(s_model),
876  &current, NULL))
877  current = GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID;
878  gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(s_model),
879  current, order);
880  DEBUG("sort_order set to %s", order == GTK_SORT_ASCENDING ? "ascending" : "descending");
881 }
882 
891 static void
892 gnc_tree_view_set_sort_column (GncTreeView *view,
893  const gchar *name)
894 {
895  GtkTreeModel *s_model;
896  GtkTreeViewColumn *column;
897  GtkSortType order;
898  gint model_column, current;
899 
900  s_model = gtk_tree_view_get_model (GTK_TREE_VIEW(view));
901  if (!s_model)
902  return;
903 
904  column = gnc_tree_view_find_column_by_name (view, name);
905  if (!column)
906  {
907  gtk_tree_sortable_set_sort_column_id (
908  GTK_TREE_SORTABLE(s_model), GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID,
909  GTK_SORT_ASCENDING);
910  return;
911  }
912 
913  model_column =
914  GPOINTER_TO_INT(g_object_get_data (G_OBJECT(column), MODEL_COLUMN));
915  if (model_column == GNC_TREE_VIEW_COLUMN_DATA_NONE)
916  return;
917 
918  if (!gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE(s_model),
919  &current, &order))
920  order = GTK_SORT_ASCENDING;
921 
922  gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(s_model),
923  model_column, order);
924  DEBUG("sort column set to %s", name);
925 }
926 
939 static void
940 gnc_tree_view_set_column_order (GncTreeView *view,
941  gchar **column_names,
942  gsize length)
943 {
944  GtkTreeViewColumn *column, *prev;
945  const GSList *tmp;
946  GSList *columns;
947  gsize idx;
948 
949  /* First, convert from names to pointers */
950  ENTER(" ");
951  columns = NULL;
952  for (idx = 0; idx < length; idx++)
953  {
954  const gchar *name = column_names [idx];
955  column = gnc_tree_view_find_column_by_name (view, name);
956  if (!column)
957  continue;
958  columns = g_slist_append (columns, column);
959  }
960 
961  /* Then reorder the columns */
962  for (prev = NULL, tmp = columns; tmp; tmp = g_slist_next (tmp))
963  {
964  column = tmp->data;
965  gtk_tree_view_move_column_after (GTK_TREE_VIEW(view), column, prev);
966  prev = column;
967  }
968 
969  /* Clean up */
970  g_slist_free (columns);
971  LEAVE("column order set");
972 }
973 
983 {
984  GncTreeViewPrivate *priv;
985  GKeyFile *state_file = gnc_state_get_current ();
986 
987  ENTER(" ");
988  priv = GNC_TREE_VIEW_GET_PRIVATE(view);
989  if (!priv->state_section)
990  {
991  LEAVE("no state section");
992  return;
993  }
994 
995  g_key_file_remove_group (state_file, priv->state_section, NULL);
996  g_free (priv->state_section);
997  priv->state_section = NULL;
998  LEAVE(" ");
999 }
1000 
1008 void
1010  const gchar *section)
1011 {
1012  GncTreeViewPrivate *priv;
1013  GKeyFile *state_file;
1014 
1015  g_return_if_fail (GNC_IS_TREE_VIEW(view));
1016 
1017  ENTER("view %p, section %s", view, section);
1018 
1019  priv = GNC_TREE_VIEW_GET_PRIVATE(view);
1020 
1021  /* Drop any previous state section */
1022  if (priv->state_section)
1024 
1025  if (!section)
1026  {
1027  LEAVE("cleared state section");
1028  return;
1029  }
1030 
1031  /* Catch changes in state. Propagate to view. */
1032  priv->state_section = g_strdup (section);
1033 
1034  state_file = gnc_state_get_current ();
1035  if (g_key_file_has_group (state_file, priv->state_section))
1036  {
1037  gsize num_keys, idx;
1038  gchar **keys = g_key_file_get_keys (state_file, priv->state_section, &num_keys, NULL);
1039  for (idx = 0; idx < num_keys; idx++)
1040  {
1041  gchar *key = keys[idx];
1042  if (g_strcmp0 (key, STATE_KEY_SORT_COLUMN) == 0)
1043  {
1044  gchar *name = g_key_file_get_string (state_file, priv->state_section,
1045  key, NULL);
1046  gnc_tree_view_set_sort_column (view, name);
1047  g_free (name);
1048  }
1049  else if (g_strcmp0 (key, STATE_KEY_SORT_ORDER) == 0)
1050  {
1051  gchar *name = g_key_file_get_string (state_file, priv->state_section,
1052  key, NULL);
1053  gnc_tree_view_set_sort_order (view, name);
1054  g_free (name);
1055  }
1056  else if (g_strcmp0 (key, STATE_KEY_COLUMN_ORDER) == 0)
1057  {
1058  gsize length;
1059  gchar **columns = g_key_file_get_string_list (state_file, priv->state_section,
1060  key, &length, NULL);
1061  gnc_tree_view_set_column_order (view, columns, length);
1062  g_strfreev (columns);
1063  }
1064  else
1065  {
1066  /* Make a copy of the local part of the key so it can be split
1067  * into column name and key type */
1068  gboolean known = FALSE;
1069  gchar *column_name = g_strdup (key);
1070  gchar *type_name = g_strrstr (column_name, "_");
1071 
1072  if (type_name != NULL) //guard against not finding '_'
1073  {
1074  *type_name++ = '\0';
1075 
1076  if (g_strcmp0 (type_name, STATE_KEY_SUFF_VISIBLE) == 0)
1077  {
1078  GtkTreeViewColumn *column = gnc_tree_view_find_column_by_name (view, column_name);
1079  if (column)
1080  {
1081  known = TRUE;
1082  if (!g_object_get_data (G_OBJECT (column), ALWAYS_VISIBLE))
1083  {
1084  gtk_tree_view_column_set_visible (column,
1085  g_key_file_get_boolean (state_file, priv->state_section, key, NULL));
1086  }
1087  }
1088  }
1089  else if (g_strcmp0 (type_name, STATE_KEY_SUFF_WIDTH) == 0)
1090  {
1091  gint width = g_key_file_get_integer (state_file, priv->state_section, key, NULL);
1092  GtkTreeViewColumn *column = gnc_tree_view_find_column_by_name (view, column_name);
1093  if (column)
1094  {
1095  known = TRUE;
1096  if (width && (width != gtk_tree_view_column_get_width (column)))
1097  {
1098  gtk_tree_view_column_set_fixed_width (column, width);
1099  }
1100  }
1101  }
1102  if (!known)
1103  DEBUG ("Ignored key %s", key);
1104 
1105  g_free (column_name);
1106  }
1107  }
1108  }
1109  g_strfreev (keys);
1110  }
1111 
1112  /* Rebuild the column visibility menu */
1113  gnc_tree_view_build_column_menu (view);
1114  LEAVE ("set state section");
1115 }
1116 
1123 const gchar *
1125 {
1126  GncTreeViewPrivate *priv;
1127 
1128  g_return_val_if_fail (GNC_IS_TREE_VIEW(view), NULL);
1129 
1130  priv = GNC_TREE_VIEW_GET_PRIVATE (view);
1131  return priv->state_section;
1132 }
1133 
1134 void gnc_tree_view_save_state (GncTreeView *view)
1135 {
1136  GncTreeViewPrivate *priv;
1137 
1138  ENTER("view %p", view);
1139  g_return_if_fail (view != NULL);
1140  g_return_if_fail (GNC_IS_TREE_VIEW(view));
1141 
1142  priv = GNC_TREE_VIEW_GET_PRIVATE(view);
1143 
1144  if (priv->state_section)
1145  {
1146  /* Save state. Only store non-default values when possible. */
1147  GList *column_list, *tmp;
1148  GKeyFile *state_file = gnc_state_get_current();
1149  gsize num_cols = 0;
1150  gchar *sort_column = gnc_tree_view_get_sort_column (view);
1151  gchar *sort_order = gnc_tree_view_get_sort_order (view);
1152  gchar **col_order = gnc_tree_view_get_column_order (view, &num_cols);
1153 
1154  /* Default sort column is the name column */
1155  if (sort_column && (g_strcmp0 (sort_column, "name") != 0))
1156  g_key_file_set_string (state_file, priv->state_section, STATE_KEY_SORT_COLUMN, sort_column);
1157  else if (g_key_file_has_key (state_file, priv->state_section, STATE_KEY_SORT_COLUMN, NULL))
1158  g_key_file_remove_key (state_file, priv->state_section, STATE_KEY_SORT_COLUMN, NULL);
1159  g_free (sort_column);
1160 
1161 
1162  /* Default sort order is "ascending" */
1163  if (g_strcmp0 (sort_order, "descending") == 0)
1164  g_key_file_set_string (state_file, priv->state_section, STATE_KEY_SORT_ORDER, sort_order);
1165  else if (g_key_file_has_key (state_file, priv->state_section, STATE_KEY_SORT_ORDER, NULL))
1166  g_key_file_remove_key (state_file, priv->state_section, STATE_KEY_SORT_ORDER, NULL);
1167  g_free (sort_order);
1168 
1169  if (col_order && (num_cols > 0))
1170  g_key_file_set_string_list (state_file, priv->state_section, STATE_KEY_COLUMN_ORDER,
1171  (const gchar**) col_order, num_cols);
1172  else if (g_key_file_has_key (state_file, priv->state_section, STATE_KEY_COLUMN_ORDER, NULL))
1173  g_key_file_remove_key (state_file, priv->state_section, STATE_KEY_COLUMN_ORDER, NULL);
1174 
1175  g_strfreev (col_order);
1176 
1177 
1178  // ENTER("view %p, wanted %s", view, wanted);
1179  column_list = gtk_tree_view_get_columns (GTK_TREE_VIEW(view));
1180  for (tmp = column_list; tmp; tmp = g_list_next (tmp))
1181  {
1182  GtkTreeViewColumn *column = tmp->data;
1183  gchar *key=NULL;
1184  const gchar *name = g_object_get_data (G_OBJECT(column), PREF_NAME);
1185  if (!name)
1186  continue;
1187 
1188  if (!g_object_get_data (G_OBJECT(column), ALWAYS_VISIBLE))
1189  {
1190  key = g_strjoin ("_", name, STATE_KEY_SUFF_VISIBLE, NULL);
1191  g_key_file_set_boolean (state_file, priv->state_section, key,
1192  gtk_tree_view_column_get_visible (column));
1193  g_free (key);
1194  }
1195 
1196  key = g_strjoin ("_", name, STATE_KEY_SUFF_WIDTH, NULL);
1197  if (g_object_get_data (G_OBJECT(column), "default-width") &&
1198  (GPOINTER_TO_INT((g_object_get_data (G_OBJECT(column), "default-width")))
1199  != gtk_tree_view_column_get_width (column)))
1200  {
1201  g_key_file_set_integer (state_file, priv->state_section, key,
1202  gtk_tree_view_column_get_width (column));
1203  }
1204  else if (g_key_file_has_key (state_file, priv->state_section, key, NULL))
1205  g_key_file_remove_key (state_file, priv->state_section, key, NULL);
1206  g_free (key);
1207  }
1208  g_list_free (column_list);
1209  }
1210 
1211  LEAVE(" ");
1212 }
1213 
1214 
1217 /************************************************************/
1218 /* Column Selection Menu */
1219 /************************************************************/
1220 
1239 static void
1240 gnc_tree_view_create_menu_item (GtkTreeViewColumn *column,
1241  GncTreeView *view)
1242 {
1243  GncTreeViewPrivate *priv;
1244  GtkWidget *widget;
1245  const gchar *column_name, *pref_name;
1246  gchar *key;
1247  GBinding *binding;
1248 
1249  // ENTER("view %p, column %p", view, column);
1250  priv = GNC_TREE_VIEW_GET_PRIVATE(view);
1251  if (!priv->state_section)
1252  {
1253  // LEAVE("no state section");
1254  return;
1255  }
1256 
1257  pref_name = g_object_get_data (G_OBJECT(column), PREF_NAME);
1258  if (!pref_name)
1259  {
1260  // LEAVE("column has no pref_name");
1261  return;
1262  }
1263 
1264  /* Create the menu if we don't have one already */
1265  if (!priv->column_menu)
1266  {
1267  priv->column_menu = gtk_menu_new();
1268  g_object_ref_sink (priv->column_menu);
1269  }
1270 
1271  /* Create the check menu item */
1272  column_name = g_object_get_data (G_OBJECT(column), REAL_TITLE);
1273  if (!column_name)
1274  column_name = gtk_tree_view_column_get_title (column);
1275  widget = gtk_check_menu_item_new_with_label (column_name);
1276  gtk_menu_shell_append (GTK_MENU_SHELL(priv->column_menu), widget);
1277 
1278  /* Should never be able to hide the first column */
1279  if (g_object_get_data (G_OBJECT(column), ALWAYS_VISIBLE))
1280  {
1281  g_object_set_data (G_OBJECT(widget), ALWAYS_VISIBLE, GINT_TO_POINTER(1));
1282  gtk_widget_set_sensitive (widget, FALSE);
1283  }
1284 
1285  binding = g_object_bind_property (G_OBJECT(widget), "active", G_OBJECT(column), "visible", 0);
1286  g_object_set_data (G_OBJECT(widget), "column-binding", binding);
1287 
1288  /* Store data on the widget for callbacks */
1289  key = g_strdup_printf ("%s_%s", pref_name, STATE_KEY_SUFF_VISIBLE);
1290  g_object_set_data_full (G_OBJECT(widget), STATE_KEY, key, g_free);
1291  // LEAVE(" ");
1292 }
1293 
1294 static gint
1295 column_menu_sort (GtkTreeViewColumn *columna, GtkTreeViewColumn *columnb)
1296 {
1297  const gchar *column_namea = g_object_get_data (G_OBJECT(columna), REAL_TITLE);
1298  const gchar *column_nameb = g_object_get_data (G_OBJECT(columnb), REAL_TITLE);
1299 
1300  if (!column_namea)
1301  column_namea = gtk_tree_view_column_get_title (columna);
1302 
1303  if (!column_nameb)
1304  column_nameb = gtk_tree_view_column_get_title (columnb);
1305 
1306  return safe_utf8_collate (column_namea, column_nameb);
1307 }
1308 
1320 static void
1321 gnc_tree_view_build_column_menu (GncTreeView *view)
1322 {
1323  GncTreeViewPrivate *priv;
1324  GList *column_list;
1325 
1326  g_return_if_fail (GNC_IS_TREE_VIEW(view));
1327 
1328  ENTER("view %p", view);
1329  priv = GNC_TREE_VIEW_GET_PRIVATE(view);
1330 
1331  /* Destroy any old menu */
1332  if (priv->column_menu)
1333  {
1334  g_object_unref (priv->column_menu);
1335  priv->column_menu = NULL;
1336  }
1337 
1338  if (priv->show_column_menu && priv->state_section)
1339  {
1340  /* Show the menu popup button */
1341  if (priv->column_menu_column)
1342  gtk_tree_view_column_set_visible (priv->column_menu_column, TRUE);
1343 
1344  /* Now build a new menu */
1345  column_list = gtk_tree_view_get_columns (GTK_TREE_VIEW(view));
1346  column_list = g_list_sort (column_list, (GCompareFunc)column_menu_sort);
1347  g_list_foreach (column_list, (GFunc)gnc_tree_view_create_menu_item, view);
1348  g_list_free (column_list);
1349  }
1350  else
1351  {
1352  /* Hide the menu popup button */
1353  if (priv->column_menu_column)
1354  gtk_tree_view_column_set_visible (priv->column_menu_column, FALSE);
1355  }
1356  LEAVE("menu: show %d, section %s", priv->show_column_menu,
1357  priv->state_section ? priv->state_section : "(null)");
1358 }
1359 
1369 static void
1370 gnc_tree_view_update_column_menu_item (GtkCheckMenuItem *checkmenuitem,
1371  GncTreeView *view)
1372 {
1373  gboolean visible;
1374 
1375  g_return_if_fail (GTK_IS_CHECK_MENU_ITEM(checkmenuitem));
1376  g_return_if_fail (GNC_IS_TREE_VIEW(view));
1377 
1378  if (g_object_get_data (G_OBJECT(checkmenuitem), ALWAYS_VISIBLE))
1379  {
1380  visible = TRUE;
1381  }
1382  else
1383  {
1384  GBinding *binding = g_object_get_data (G_OBJECT(checkmenuitem), "column-binding");
1385  GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN(g_binding_dup_target (binding));
1386 
1387  visible = gtk_tree_view_column_get_visible (column);
1388  g_object_unref(column);
1389  }
1390  gtk_check_menu_item_set_active (checkmenuitem, visible);
1391 }
1392 
1405 static void
1406 gnc_tree_view_select_column_cb (GtkTreeViewColumn *column,
1407  GncTreeView *view)
1408 {
1409  GncTreeViewPrivate *priv;
1410  GtkWidget *menu;
1411 
1412  g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN(column));
1413  g_return_if_fail (GNC_IS_TREE_VIEW(view));
1414 
1415  priv = GNC_TREE_VIEW_GET_PRIVATE(view);
1416  menu = priv->column_menu;
1417  if (!menu)
1418  return;
1419 
1420  /* Synchronize the menu before display */
1421  gtk_container_foreach (GTK_CONTAINER(menu),
1422  (GtkCallback)gnc_tree_view_update_column_menu_item,
1423  view);
1424 
1425  /* Ensure all components are visible */
1426  gtk_widget_show_all (menu);
1427 
1428  /* Pop the menu up at the button */
1429  gtk_menu_popup_at_pointer (GTK_MENU(priv->column_menu), NULL);
1430 }
1431 
1432 
1433 void gnc_tree_view_expand_columns (GncTreeView *view,
1434  gchar *first_column_name,
1435  ...)
1436 {
1437  GtkTreeViewColumn *column;
1438  GList *columns, *tmp;
1439  gchar *name, *pref_name;
1440  va_list args;
1441 
1442  g_return_if_fail (GNC_IS_TREE_VIEW(view));
1443  ENTER(" ");
1444  va_start (args, first_column_name);
1445  name = first_column_name;
1446 
1447  /* First disable the expand property on all (non-infrastructure) columns. */
1448  columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(view));
1449  for (tmp = columns; tmp; tmp = g_list_next (tmp))
1450  {
1451  column = tmp->data;
1452  pref_name = g_object_get_data (G_OBJECT(column), PREF_NAME);
1453  if (pref_name != NULL)
1454  gtk_tree_view_column_set_expand (column, FALSE);
1455  }
1456  g_list_free(columns);
1457 
1458  /* Now enable it on the requested columns. */
1459  while (name != NULL)
1460  {
1461  column = gnc_tree_view_find_column_by_name (view, name);
1462  if (column != NULL)
1463  {
1464  gtk_tree_view_column_set_expand (column, TRUE);
1465  }
1466  name = va_arg (args, gchar*);
1467  }
1468  va_end (args);
1469 
1470  LEAVE(" ");
1471 }
1472 
1473 
1474 /* Links the cell backgrounds of the two control columns to the model or
1475  cell data function */
1476 static void
1477 update_control_cell_renderers_background (GncTreeView *view, GtkTreeViewColumn *col,
1478  gint column, GtkTreeCellDataFunc func )
1479 {
1480  GList *renderers;
1481  GtkCellRenderer *cell;
1482  GList *node;
1483 
1484  renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT(col));
1485 
1486  /* Update the cell background in the list of renderers */
1487  for (node = renderers; node; node = node->next)
1488  {
1489  cell = node->data;
1490  if (func == NULL)
1491  gtk_tree_view_column_add_attribute (col, cell, "cell-background", column);
1492  else
1493  gtk_tree_view_column_set_cell_data_func (col, cell, func, view, NULL);
1494  }
1495  g_list_free (renderers);
1496 }
1497 
1498 
1499 /* This function links the cell backgrounds of the two control columns to a column
1500  in the model that has color strings or a cell data function */
1501 void
1502 gnc_tree_view_set_control_column_background (GncTreeView *view, gint column, GtkTreeCellDataFunc func )
1503 {
1504  GncTreeViewPrivate *priv;
1505 
1506  g_return_if_fail (GNC_IS_TREE_VIEW(view));
1507 
1508  ENTER("view %p, column %d, func %p", view, column, func);
1509  priv = GNC_TREE_VIEW_GET_PRIVATE(view);
1510 
1511  update_control_cell_renderers_background (view, priv->column_menu_column, column, func);
1512 
1513  LEAVE(" ");
1514 }
1515 
1516 
1517 /* This allows the columns to be setup without the model connected */
1518 //FIXME I think this should be specified as a parameter to the add columns functions...
1519 void
1520 gnc_tree_view_set_sort_user_data (GncTreeView *view, GtkTreeModel *s_model)
1521 {
1522  GncTreeViewPrivate *priv;
1523 
1524  g_return_if_fail (GNC_IS_TREE_VIEW(view));
1525 
1526  ENTER("view %p, sort_model %p", view, s_model);
1527  priv = GNC_TREE_VIEW_GET_PRIVATE(view);
1528 
1529  priv->sort_model = s_model;
1530  LEAVE(" ");
1531 }
1532 
1533 
1540 void
1542  gboolean visible)
1543 {
1544  GncTreeViewPrivate *priv;
1545 
1546  g_return_if_fail (GNC_IS_TREE_VIEW(view));
1547 
1548  ENTER("view %p, show menu %d", view, visible);
1549  priv = GNC_TREE_VIEW_GET_PRIVATE(view);
1550  priv->show_column_menu = visible;
1551  gnc_tree_view_build_column_menu (view);
1552  LEAVE(" ");
1553 }
1554 
1561 gboolean
1563 {
1564  GncTreeViewPrivate *priv;
1565 
1566  g_return_val_if_fail (GNC_IS_TREE_VIEW(view), FALSE);
1567 
1568  priv = GNC_TREE_VIEW_GET_PRIVATE(view);
1569  return (priv->show_column_menu);
1570 }
1571 
1574 /************************************************************/
1575 /* Tree View Creation */
1576 /************************************************************/
1577 
1578 static gint
1579 gnc_tree_view_count_visible_columns (GncTreeView *view)
1580 {
1581  GList *columns, *node;
1582  gint count = 0;
1583 
1584  columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(view));
1585  for (node = columns; node; node = node->next)
1586  {
1587  GtkTreeViewColumn *col = GTK_TREE_VIEW_COLUMN(node->data);
1588 
1589  if (g_object_get_data (G_OBJECT(col), DEFAULT_VISIBLE) ||
1590  g_object_get_data (G_OBJECT(col), ALWAYS_VISIBLE))
1591  count++;
1592  }
1593  g_list_free (columns);
1594  return count;
1595 }
1596 
1597 void
1599 {
1600  GncTreeViewPrivate *priv;
1601  GtkTreeViewColumn *column;
1602  GList *columns;
1603  gboolean hide_menu_column;
1604 
1605  g_return_if_fail (GNC_IS_TREE_VIEW(view));
1606 
1607  ENTER(" ");
1608 
1609  /* Update the view and saved state */
1610  columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(view));
1611  g_list_foreach (columns, (GFunc)gnc_tree_view_update_visibility, view);
1612  g_list_free (columns);
1613 
1614  priv = GNC_TREE_VIEW_GET_PRIVATE(view);
1615  if (priv->state_section)
1616  priv->seen_state_visibility = TRUE;
1617 
1618  /* If only the first column is visible, hide the spacer and make that
1619  * column expand. */
1620  hide_menu_column = (gnc_tree_view_count_visible_columns (view) == 1);
1621  column = gtk_tree_view_get_column (GTK_TREE_VIEW(view), 0);
1622  gtk_tree_view_column_set_expand (column, hide_menu_column);
1623  gtk_tree_view_column_set_visible (priv->column_menu_column, !hide_menu_column);
1624 
1625  LEAVE(" ");
1626 }
1627 
1628 
1658 static void
1659 gnc_tree_view_column_properties (GncTreeView *view,
1660  GtkTreeViewColumn *column,
1661  const gchar *pref_name,
1662  gint data_column,
1663  gint default_width,
1664  gboolean resizable,
1665  GtkTreeIterCompareFunc column_sort_fn)
1666 {
1667  GncTreeViewPrivate *priv;
1668  GtkTreeModel *s_model;
1669  gboolean visible;
1670  int width = 0;
1671 
1672  /* Set data used by other functions */
1673  if (pref_name)
1674  g_object_set_data (G_OBJECT(column), PREF_NAME, (gpointer)pref_name);
1675  if (data_column == 0)
1676  g_object_set_data (G_OBJECT(column), ALWAYS_VISIBLE, GINT_TO_POINTER(1));
1677  g_object_set_data (G_OBJECT(column), MODEL_COLUMN,
1678  GINT_TO_POINTER(data_column));
1679 
1680  /* Get visibility */
1681  visible = gnc_tree_view_column_visible (view, NULL, pref_name);
1682 
1683  /* Set column attributes (without the sizing) */
1684  g_object_set (G_OBJECT(column),
1685  "visible", visible,
1686  "resizable", resizable && pref_name != NULL,
1687  "reorderable", pref_name != NULL,
1688  NULL);
1689 
1690  /* Get width */
1691  if (default_width == 0)
1692  {
1693  /* Set the sizing column attributes */
1694  g_object_set (G_OBJECT(column),
1695  "sizing", GTK_TREE_VIEW_COLUMN_AUTOSIZE,
1696  NULL);
1697  }
1698  else
1699  {
1700 
1701  /* If saved state comes back with a width of zero (or there is no saved
1702  * state width) the use the default width for the column. Allow for
1703  * padding L and R of the displayed data. */
1704  if (width == 0)
1705  width = default_width + 10;
1706  if (width == 0)
1707  width = 10;
1708 
1709  /* Set the sizing column attributes (including fixed-width) */
1710  g_object_set (G_OBJECT(column),
1711  "sizing", GTK_TREE_VIEW_COLUMN_FIXED,
1712  "fixed-width", width,
1713  NULL);
1714  /* Save the initially calculated preferred width for later
1715  * comparison to the actual width when saving state. Can't
1716  * use the "fixed-width" property for that because it changes
1717  * when the user resizes the column.
1718  */
1719  g_object_set_data (G_OBJECT(column),
1720  "default-width", GINT_TO_POINTER(width));
1721  }
1722 
1723  s_model = gtk_tree_view_get_model (GTK_TREE_VIEW(view));
1724  if (GTK_IS_TREE_SORTABLE(s_model))
1725  {
1726  gtk_tree_view_column_set_sort_column_id (column, data_column);
1727  if (column_sort_fn)
1728  {
1729  gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(s_model),
1730  data_column, column_sort_fn,
1731  GINT_TO_POINTER(data_column),
1732  NULL /* destroy fn */);
1733  }
1734  }
1735 
1736  // Used in registers, sort model not connected to view yet
1737  priv = GNC_TREE_VIEW_GET_PRIVATE(view);
1738  if (priv->sort_model != NULL)
1739  {
1740  gtk_tree_view_column_set_sort_column_id (column, data_column);
1741  if (column_sort_fn)
1742  {
1743  gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(priv->sort_model),
1744  data_column, column_sort_fn,
1745  view,
1746  NULL /* destroy fn */);
1747  }
1748  }
1749 
1750  /* Add to the column selection menu */
1751  if (pref_name)
1752  {
1753  gnc_tree_view_create_menu_item (column, view);
1754  }
1755 }
1756 
1767 GtkTreeViewColumn *
1769  const gchar *column_title,
1770  const gchar *column_short_title,
1771  const gchar *pref_name,
1772  gint model_data_column,
1773  gint model_visibility_column,
1774  GtkTreeIterCompareFunc column_sort_fn,
1775  renderer_toggled toggle_edited_cb)
1776 {
1777  GtkTreeViewColumn *column;
1778  GtkCellRenderer *renderer;
1779 
1780  g_return_val_if_fail (GNC_IS_TREE_VIEW(view), NULL);
1781 
1782  renderer = gtk_cell_renderer_toggle_new ();
1783  if (!toggle_edited_cb)
1784  {
1785  gtk_cell_renderer_toggle_set_activatable (GTK_CELL_RENDERER_TOGGLE(renderer), FALSE);
1786  }
1787  column =
1788  gtk_tree_view_column_new_with_attributes (column_short_title,
1789  renderer,
1790  "active", model_data_column,
1791  NULL);
1792 
1793  /* Add the full title to the object for menu creation */
1794  g_object_set_data_full (G_OBJECT(column), REAL_TITLE,
1795  g_strdup(column_title), g_free);
1796  if (toggle_edited_cb)
1797  g_signal_connect (G_OBJECT(renderer), "toggled",
1798  G_CALLBACK(toggle_edited_cb), view);
1799 
1800  if (model_visibility_column != GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS)
1801  gtk_tree_view_column_add_attribute (column, renderer,
1802  "visible", model_visibility_column);
1803 
1804 
1805  gnc_tree_view_column_properties (view, column, pref_name, model_data_column,
1806  0, FALSE, column_sort_fn);
1807 
1808  gnc_tree_view_append_column (view, column);
1809 
1810  /* Also add the full title to the object as a tooltip */
1811  gtk_widget_set_tooltip_text (gtk_tree_view_column_get_button (column), column_title);
1812 
1813  return column;
1814 }
1815 
1816 static void
1817 renderer_editing_canceled_cb (GtkCellRenderer *renderer, 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 static void
1826 renderer_editing_started_cb (GtkCellRenderer *renderer,
1827  GtkCellEditable *editable, gchar *path, gpointer user_data)
1828 {
1829  GncTreeView *view = user_data;
1830  GncTreeViewPrivate *priv = GNC_TREE_VIEW_GET_PRIVATE(view);
1831  if (priv->editing_started_cb)
1832  (priv->editing_started_cb)(view, priv->editing_cb_data);
1833 }
1834 
1835 static void
1836 renderer_edited_cb (GtkCellRendererText *renderer, gchar *path,
1837  gchar *new_text, gpointer user_data)
1838 {
1839  GncTreeView *view = user_data;
1840  GncTreeViewPrivate *priv = GNC_TREE_VIEW_GET_PRIVATE(view);
1841  if (priv->editing_finished_cb)
1842  (priv->editing_finished_cb)(view, priv->editing_cb_data);
1843 }
1844 
1845 
1846 static GtkTreeViewColumn *
1847 add_text_column_variant (GncTreeView *view, GtkCellRenderer *renderer,
1848  const gchar *column_title,
1849  const gchar *pref_name,
1850  const gchar *icon_name,
1851  const gchar *sizing_text,
1852  gint model_data_column,
1853  gint model_visibility_column,
1854  GtkTreeIterCompareFunc column_sort_fn)
1855 {
1856  GtkTreeViewColumn *column;
1857  PangoLayout* layout;
1858  int default_width, title_width;
1859 
1860  g_return_val_if_fail (GNC_IS_TREE_VIEW(view), NULL);
1861 
1862  column = gtk_tree_view_column_new ();
1863  gtk_tree_view_column_set_title (column, column_title);
1864 
1865  /* Set up an icon renderer if requested */
1866  if (icon_name)
1867  {
1868  GtkCellRenderer *renderer_pix = gtk_cell_renderer_pixbuf_new ();
1869  g_object_set (renderer_pix, "icon-name", icon_name, NULL);
1870  gtk_tree_view_column_pack_start (column, renderer_pix, FALSE);
1871  }
1872 
1873  /* Set up a text renderer and attributes */
1874  gtk_tree_view_column_pack_start (column, renderer, TRUE);
1875 
1876  /* Set up the callbacks for when editing */
1877  g_signal_connect (G_OBJECT(renderer), "editing-canceled",
1878  (GCallback)renderer_editing_canceled_cb, view);
1879 
1880  g_signal_connect (G_OBJECT(renderer), "editing-started",
1881  (GCallback)renderer_editing_started_cb, view);
1882 
1883  g_signal_connect (G_OBJECT(renderer), "edited",
1884  (GCallback)renderer_edited_cb, view);
1885 
1886  /* Set renderer attributes controlled by the model */
1887  if (model_data_column != GNC_TREE_VIEW_COLUMN_DATA_NONE)
1888  gtk_tree_view_column_add_attribute (column, renderer,
1889  "text", model_data_column);
1890  if (model_visibility_column != GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS)
1891  gtk_tree_view_column_add_attribute (column, renderer,
1892  "visible", model_visibility_column);
1893 
1894  /* Default size is the larger of the column title and the sizing text */
1895  layout = gtk_widget_create_pango_layout (GTK_WIDGET(view), column_title);
1896  pango_layout_get_pixel_size (layout, &title_width, NULL);
1897  g_object_unref (layout);
1898  layout = gtk_widget_create_pango_layout (GTK_WIDGET(view), sizing_text);
1899  pango_layout_get_pixel_size (layout, &default_width, NULL);
1900  g_object_unref (layout);
1901  default_width = MAX(default_width, title_width);
1902  if (default_width)
1903  default_width += 10; /* padding on either side */
1904  gnc_tree_view_column_properties (view, column, pref_name, model_data_column,
1905  default_width, TRUE, column_sort_fn);
1906 
1907  gnc_tree_view_append_column (view, column);
1908  return column;
1909 }
1910 
1911 
1920 GtkTreeViewColumn *
1922  const gchar *column_title,
1923  const gchar *pref_name,
1924  const gchar *icon_name,
1925  const gchar *sizing_text,
1926  gint model_data_column,
1927  gint model_visibility_column,
1928  GtkTreeIterCompareFunc column_sort_fn)
1929 {
1930  GtkCellRenderer *renderer;
1931 
1932  g_return_val_if_fail (GNC_IS_TREE_VIEW(view), NULL);
1933 
1934  renderer = gtk_cell_renderer_text_new ();
1935 
1936  return add_text_column_variant (view, renderer,
1937  column_title, pref_name,
1938  icon_name, sizing_text,
1939  model_data_column,
1940  model_visibility_column,
1941  column_sort_fn);
1942 }
1943 
1952 GtkTreeViewColumn *
1954  const gchar *column_title,
1955  const gchar *pref_name,
1956  const gchar *icon_name,
1957  const gchar *sizing_text,
1958  gint model_data_column,
1959  gint model_visibility_column,
1960  GtkTreeIterCompareFunc column_sort_fn)
1961 {
1962  GtkCellRenderer *renderer;
1963 
1964  g_return_val_if_fail (GNC_IS_TREE_VIEW(view), NULL);
1965 
1966  renderer = gnc_cell_renderer_text_view_new ();
1967 
1968  return add_text_column_variant (view, renderer,
1969  column_title, pref_name,
1970  icon_name, sizing_text,
1971  model_data_column,
1972  model_visibility_column,
1973  column_sort_fn);
1974 }
1975 
1984 GtkTreeViewColumn *
1985 gnc_tree_view_add_pix_column (GncTreeView *view,
1986  const gchar *column_title,
1987  const gchar *pref_name,
1988  const gchar *sizing_text,
1989  gint model_data_column,
1990  gint model_visibility_column,
1991  GtkTreeIterCompareFunc column_sort_fn)
1992 {
1993  GtkTreeViewColumn *column;
1994  PangoLayout* layout;
1995  int default_width, title_width;
1996  GtkCellRenderer *renderer;
1997 
1998  g_return_val_if_fail (GNC_IS_TREE_VIEW(view), NULL);
1999 
2000  renderer = gtk_cell_renderer_pixbuf_new ();
2001 
2002  column = gtk_tree_view_column_new ();
2003  gtk_tree_view_column_set_title (column, column_title);
2004 
2005  /* Set up a text renderer and attributes */
2006  gtk_tree_view_column_pack_start (column, renderer, TRUE);
2007 
2008  /* Set renderer attributes controlled by the model */
2009  if (model_data_column != GNC_TREE_VIEW_COLUMN_DATA_NONE)
2010  gtk_tree_view_column_add_attribute (column, renderer,
2011  "icon-name", model_data_column);
2012  if (model_visibility_column != GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS)
2013  gtk_tree_view_column_add_attribute (column, renderer,
2014  "visible", model_visibility_column);
2015 
2016  /* Default size is the larger of the column title and the sizing text */
2017  layout = gtk_widget_create_pango_layout (GTK_WIDGET(view), column_title);
2018  pango_layout_get_pixel_size (layout, &title_width, NULL);
2019  g_object_unref (layout);
2020  layout = gtk_widget_create_pango_layout (GTK_WIDGET(view), sizing_text);
2021  pango_layout_get_pixel_size (layout, &default_width, NULL);
2022  g_object_unref (layout);
2023  default_width = MAX(default_width, title_width);
2024  if (default_width)
2025  default_width += 10; /* padding on either side */
2026  gnc_tree_view_column_properties (view, column, pref_name, model_data_column,
2027  default_width, TRUE, column_sort_fn);
2028 
2029  gnc_tree_view_append_column (view, column);
2030  return column;
2031 }
2032 
2033 GtkCellRenderer *
2034 gnc_tree_view_column_get_renderer (GtkTreeViewColumn *column)
2035 {
2036  GList *renderers;
2037  GtkCellRenderer *cr = NULL;
2038 
2039  g_return_val_if_fail (GTK_TREE_VIEW_COLUMN(column), NULL);
2040 
2041  /* Get the list of one renderer */
2042  renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT(column));
2043  if (g_list_length (renderers) > 0)
2044  cr = GTK_CELL_RENDERER(renderers->data);
2045  g_list_free (renderers);
2046 
2047  return cr;
2048 }
2049 
2060 GtkTreeViewColumn *
2062  const gchar *column_title,
2063  const gchar *pref_name,
2064  const gchar *sizing_text,
2065  gint model_data_column,
2066  gint model_color_column,
2067  gint model_visibility_column,
2068  GtkTreeIterCompareFunc column_sort_fn)
2069 {
2070  GtkTreeViewColumn *column;
2071  GtkCellRenderer *renderer;
2072  gfloat alignment = 1.0;
2073 
2074  /* Use GncCellRendererLabel so the user can click a cell and select
2075  * its text (e.g. to copy it to the clipboard), without being able
2076  * to modify the underlying data. */
2077  renderer = gnc_cell_renderer_label_new ();
2078  column = add_text_column_variant (view, renderer, column_title, pref_name,
2079  NULL, sizing_text, model_data_column,
2080  model_visibility_column, column_sort_fn);
2081 
2082  /* Right align the column title and data for both ltr and rtl */
2083  if (gtk_widget_get_direction (GTK_WIDGET(view)) == GTK_TEXT_DIR_RTL)
2084  alignment = 0.0;
2085 
2086  g_object_set (G_OBJECT(column), "alignment", alignment, NULL);
2087  g_object_set (G_OBJECT(renderer), "xalign", alignment, NULL);
2088 
2089  /* Change the text color */
2090  if (model_color_column != GNC_TREE_VIEW_COLUMN_COLOR_NONE)
2091  gtk_tree_view_column_add_attribute (column, renderer,
2092  "foreground", model_color_column);
2093 
2094  return column;
2095 }
2096 
2105 gint
2106 gnc_tree_view_append_column (GncTreeView *view,
2107  GtkTreeViewColumn *column)
2108 {
2109  int n = gtk_tree_view_get_n_columns (GTK_TREE_VIEW(view));
2110 
2111  /* Ignore the initial column, the selection menu */
2112  if (n >= 1)
2113  n -= 1;
2114  return gtk_tree_view_insert_column (GTK_TREE_VIEW(view), column, n);
2115 }
2116 
2117 static gboolean
2118 get_column_next_to (GtkTreeView *tv, GtkTreeViewColumn **col, gboolean backward)
2119 {
2120  GList *cols, *node;
2121  GtkTreeViewColumn *c = NULL;
2122  gint seen = 0;
2123  gboolean wrapped = FALSE;
2124 
2125  cols = gtk_tree_view_get_columns (tv);
2126  g_return_val_if_fail (cols != NULL, FALSE);
2127 
2128  node = g_list_find (cols, *col);
2129  g_return_val_if_fail (node, FALSE);
2130  do
2131  {
2132  node = backward ? node->prev : node->next;
2133  if (!node)
2134  {
2135  wrapped = TRUE;
2136  node = backward ? g_list_last (cols) : cols;
2137  }
2138  c = GTK_TREE_VIEW_COLUMN (node->data);
2139  if (c && gtk_tree_view_column_get_visible (c))
2140  seen++;
2141  if (c == *col) break;
2142  }
2143  while (!seen);
2144 
2145  g_list_free (cols);
2146  *col = c;
2147  return wrapped;
2148 }
2149 
2150 gboolean
2151 gnc_tree_view_path_is_valid (GncTreeView *view, GtkTreePath *path)
2152 {
2153  GtkTreeView *tv = GTK_TREE_VIEW(view);
2154  GtkTreeModel *s_model;
2155  GtkTreeIter iter;
2156 
2157  s_model = gtk_tree_view_get_model (tv);
2158  return gtk_tree_model_get_iter (s_model, &iter, path);
2159 }
2160 
2161 void
2162 gnc_tree_view_keynav (GncTreeView *view, GtkTreeViewColumn **col,
2163  GtkTreePath *path, GdkEventKey *event)
2164 {
2165  GtkTreeView *tv = GTK_TREE_VIEW(view);
2166  gint depth;
2167  gboolean shifted;
2168 
2169  if (event->type != GDK_KEY_PRESS) return;
2170 
2171  switch (event->keyval)
2172  {
2173  case GDK_KEY_Tab:
2174  case GDK_KEY_ISO_Left_Tab:
2175  case GDK_KEY_KP_Tab:
2176  shifted = event->state & GDK_SHIFT_MASK;
2177  if (get_column_next_to (tv, col, shifted))
2178  {
2179  /* This is the end (or beginning) of the line, buddy. */
2180  depth = gtk_tree_path_get_depth (path);
2181  if (shifted)
2182  {
2183  if (!gtk_tree_path_prev (path) && depth > 1)
2184  {
2185  gtk_tree_path_up (path);
2186  }
2187  }
2188  else if (gtk_tree_view_row_expanded (tv, path))
2189  {
2190  gtk_tree_path_down (path);
2191  }
2192  else
2193  {
2194  gtk_tree_path_next (path);
2195  if (!gnc_tree_view_path_is_valid (view, path) && depth > 2)
2196  {
2197  gtk_tree_path_prev (path);
2198  gtk_tree_path_up (path);
2199  gtk_tree_path_next (path);
2200  }
2201  if (!gnc_tree_view_path_is_valid (view, path) && depth > 1)
2202  {
2203  gtk_tree_path_prev (path);
2204  gtk_tree_path_up (path);
2205  gtk_tree_path_next (path);
2206  }
2207  }
2208  }
2209  break;
2210 
2211  case GDK_KEY_Return:
2212  case GDK_KEY_KP_Enter:
2213  if (gtk_tree_view_row_expanded (tv, path))
2214  {
2215  gtk_tree_path_down (path);
2216  }
2217  else
2218  {
2219  depth = gtk_tree_path_get_depth (path);
2220  gtk_tree_path_next (path);
2221  if (!gnc_tree_view_path_is_valid (view, path) && depth > 1)
2222  {
2223  gtk_tree_path_prev (path);
2224  gtk_tree_path_up (path);
2225  gtk_tree_path_next (path);
2226  }
2227  }
2228  break;
2229  }
2230  return;
2231 }
2232 
2233 void
2234 gnc_tree_view_set_editing_started_cb (GncTreeView *view, GFunc editing_started_cb, gpointer editing_cb_data)
2235 {
2236  GncTreeViewPrivate *priv;
2237 
2238  if (!view && !editing_started_cb)
2239  return;
2240 
2241  priv = GNC_TREE_VIEW_GET_PRIVATE(view);
2242 
2243  priv->editing_started_cb = editing_started_cb;
2244  priv->editing_cb_data = editing_cb_data;
2245 }
2246 
2247 void
2248 gnc_tree_view_set_editing_finished_cb (GncTreeView *view, GFunc editing_finished_cb, gpointer editing_cb_data)
2249 {
2250  GncTreeViewPrivate *priv;
2251 
2252  if (!view && !editing_finished_cb)
2253  return;
2254 
2255  priv = GNC_TREE_VIEW_GET_PRIVATE(view);
2256 
2257  priv->editing_finished_cb = editing_finished_cb;
2258  priv->editing_cb_data = editing_cb_data;
2259 }
2260 
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.cpp:127
int safe_utf8_collate(const char *da, const char *db)
Collate two UTF-8 strings.
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
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.
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
G_DEFINE_TYPE_WITH_CODE(GncMainWindow, gnc_main_window, GTK_TYPE_APPLICATION_WINDOW, G_IMPLEMENT_INTERFACE(GNC_TYPE_WINDOW, gnc_window_main_window_init)) static guint main_window_signals[LAST_SIGNAL]
A holding place for all the signals generated by the main window code.
gint gnc_tree_view_append_column(GncTreeView *view, GtkTreeViewColumn *column)
Add a column to a view based upon a GncTreeView.
void gnc_gobject_tracking_remember(GObject *object)
Tell gnucash to remember this object in the database.
GtkTreeViewColumn * gnc_tree_view_add_pix_column(GncTreeView *view, const gchar *column_title, const gchar *pref_name, const gchar *sizing_text, gint model_data_column, gint model_visibility_column, GtkTreeIterCompareFunc column_sort_fn)
This function adds a new pixbuf view column to a GncTreeView base view.
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.
GLib helper routines.
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.cpp:142