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