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_get_target (binding));
1385 
1386  visible = gtk_tree_view_column_get_visible (column);
1387  }
1388  gtk_check_menu_item_set_active (checkmenuitem, visible);
1389 }
1390 
1403 static void
1404 gnc_tree_view_select_column_cb (GtkTreeViewColumn *column,
1405  GncTreeView *view)
1406 {
1407  GncTreeViewPrivate *priv;
1408  GtkWidget *menu;
1409 
1410  g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN(column));
1411  g_return_if_fail (GNC_IS_TREE_VIEW(view));
1412 
1413  priv = GNC_TREE_VIEW_GET_PRIVATE(view);
1414  menu = priv->column_menu;
1415  if (!menu)
1416  return;
1417 
1418  /* Synchronize the menu before display */
1419  gtk_container_foreach (GTK_CONTAINER(menu),
1420  (GtkCallback)gnc_tree_view_update_column_menu_item,
1421  view);
1422 
1423  /* Ensure all components are visible */
1424  gtk_widget_show_all (menu);
1425 
1426  /* Pop the menu up at the button */
1427  gtk_menu_popup_at_pointer (GTK_MENU(priv->column_menu), NULL);
1428 }
1429 
1430 
1431 void gnc_tree_view_expand_columns (GncTreeView *view,
1432  gchar *first_column_name,
1433  ...)
1434 {
1435  GtkTreeViewColumn *column;
1436  GList *columns, *tmp;
1437  gchar *name, *pref_name;
1438  va_list args;
1439 
1440  g_return_if_fail (GNC_IS_TREE_VIEW(view));
1441  ENTER(" ");
1442  va_start (args, first_column_name);
1443  name = first_column_name;
1444 
1445  /* First disable the expand property on all (non-infrastructure) columns. */
1446  columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(view));
1447  for (tmp = columns; tmp; tmp = g_list_next (tmp))
1448  {
1449  column = tmp->data;
1450  pref_name = g_object_get_data (G_OBJECT(column), PREF_NAME);
1451  if (pref_name != NULL)
1452  gtk_tree_view_column_set_expand (column, FALSE);
1453  }
1454  g_list_free(columns);
1455 
1456  /* Now enable it on the requested columns. */
1457  while (name != NULL)
1458  {
1459  column = gnc_tree_view_find_column_by_name (view, name);
1460  if (column != NULL)
1461  {
1462  gtk_tree_view_column_set_expand (column, TRUE);
1463  }
1464  name = va_arg (args, gchar*);
1465  }
1466  va_end (args);
1467 
1468  LEAVE(" ");
1469 }
1470 
1471 
1472 /* Links the cell backgrounds of the two control columns to the model or
1473  cell data function */
1474 static void
1475 update_control_cell_renderers_background (GncTreeView *view, GtkTreeViewColumn *col,
1476  gint column, GtkTreeCellDataFunc func )
1477 {
1478  GList *renderers;
1479  GtkCellRenderer *cell;
1480  GList *node;
1481 
1482  renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT(col));
1483 
1484  /* Update the cell background in the list of renderers */
1485  for (node = renderers; node; node = node->next)
1486  {
1487  cell = node->data;
1488  if (func == NULL)
1489  gtk_tree_view_column_add_attribute (col, cell, "cell-background", column);
1490  else
1491  gtk_tree_view_column_set_cell_data_func (col, cell, func, view, NULL);
1492  }
1493  g_list_free (renderers);
1494 }
1495 
1496 
1497 /* This function links the cell backgrounds of the two control columns to a column
1498  in the model that has color strings or a cell data function */
1499 void
1500 gnc_tree_view_set_control_column_background (GncTreeView *view, gint column, GtkTreeCellDataFunc func )
1501 {
1502  GncTreeViewPrivate *priv;
1503 
1504  g_return_if_fail (GNC_IS_TREE_VIEW(view));
1505 
1506  ENTER("view %p, column %d, func %p", view, column, func);
1507  priv = GNC_TREE_VIEW_GET_PRIVATE(view);
1508 
1509  update_control_cell_renderers_background (view, priv->column_menu_column, column, func);
1510 
1511  LEAVE(" ");
1512 }
1513 
1514 
1515 /* This allows the columns to be setup without the model connected */
1516 //FIXME I think this should be specified as a parameter to the add columns functions...
1517 void
1518 gnc_tree_view_set_sort_user_data (GncTreeView *view, GtkTreeModel *s_model)
1519 {
1520  GncTreeViewPrivate *priv;
1521 
1522  g_return_if_fail (GNC_IS_TREE_VIEW(view));
1523 
1524  ENTER("view %p, sort_model %p", view, s_model);
1525  priv = GNC_TREE_VIEW_GET_PRIVATE(view);
1526 
1527  priv->sort_model = s_model;
1528  LEAVE(" ");
1529 }
1530 
1531 
1538 void
1540  gboolean visible)
1541 {
1542  GncTreeViewPrivate *priv;
1543 
1544  g_return_if_fail (GNC_IS_TREE_VIEW(view));
1545 
1546  ENTER("view %p, show menu %d", view, visible);
1547  priv = GNC_TREE_VIEW_GET_PRIVATE(view);
1548  priv->show_column_menu = visible;
1549  gnc_tree_view_build_column_menu (view);
1550  LEAVE(" ");
1551 }
1552 
1559 gboolean
1561 {
1562  GncTreeViewPrivate *priv;
1563 
1564  g_return_val_if_fail (GNC_IS_TREE_VIEW(view), FALSE);
1565 
1566  priv = GNC_TREE_VIEW_GET_PRIVATE(view);
1567  return (priv->show_column_menu);
1568 }
1569 
1572 /************************************************************/
1573 /* Tree View Creation */
1574 /************************************************************/
1575 
1576 static gint
1577 gnc_tree_view_count_visible_columns (GncTreeView *view)
1578 {
1579  GList *columns, *node;
1580  gint count = 0;
1581 
1582  columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(view));
1583  for (node = columns; node; node = node->next)
1584  {
1585  GtkTreeViewColumn *col = GTK_TREE_VIEW_COLUMN(node->data);
1586 
1587  if (g_object_get_data (G_OBJECT(col), DEFAULT_VISIBLE) ||
1588  g_object_get_data (G_OBJECT(col), ALWAYS_VISIBLE))
1589  count++;
1590  }
1591  g_list_free (columns);
1592  return count;
1593 }
1594 
1595 void
1597 {
1598  GncTreeViewPrivate *priv;
1599  GtkTreeViewColumn *column;
1600  GList *columns;
1601  gboolean hide_menu_column;
1602 
1603  g_return_if_fail (GNC_IS_TREE_VIEW(view));
1604 
1605  ENTER(" ");
1606 
1607  /* Update the view and saved state */
1608  columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(view));
1609  g_list_foreach (columns, (GFunc)gnc_tree_view_update_visibility, view);
1610  g_list_free (columns);
1611 
1612  priv = GNC_TREE_VIEW_GET_PRIVATE(view);
1613  if (priv->state_section)
1614  priv->seen_state_visibility = TRUE;
1615 
1616  /* If only the first column is visible, hide the spacer and make that
1617  * column expand. */
1618  hide_menu_column = (gnc_tree_view_count_visible_columns (view) == 1);
1619  column = gtk_tree_view_get_column (GTK_TREE_VIEW(view), 0);
1620  gtk_tree_view_column_set_expand (column, hide_menu_column);
1621  gtk_tree_view_column_set_visible (priv->column_menu_column, !hide_menu_column);
1622 
1623  LEAVE(" ");
1624 }
1625 
1626 
1656 static void
1657 gnc_tree_view_column_properties (GncTreeView *view,
1658  GtkTreeViewColumn *column,
1659  const gchar *pref_name,
1660  gint data_column,
1661  gint default_width,
1662  gboolean resizable,
1663  GtkTreeIterCompareFunc column_sort_fn)
1664 {
1665  GncTreeViewPrivate *priv;
1666  GtkTreeModel *s_model;
1667  gboolean visible;
1668  int width = 0;
1669 
1670  /* Set data used by other functions */
1671  if (pref_name)
1672  g_object_set_data (G_OBJECT(column), PREF_NAME, (gpointer)pref_name);
1673  if (data_column == 0)
1674  g_object_set_data (G_OBJECT(column), ALWAYS_VISIBLE, GINT_TO_POINTER(1));
1675  g_object_set_data (G_OBJECT(column), MODEL_COLUMN,
1676  GINT_TO_POINTER(data_column));
1677 
1678  /* Get visibility */
1679  visible = gnc_tree_view_column_visible (view, NULL, pref_name);
1680 
1681  /* Set column attributes (without the sizing) */
1682  g_object_set (G_OBJECT(column),
1683  "visible", visible,
1684  "resizable", resizable && pref_name != NULL,
1685  "reorderable", pref_name != NULL,
1686  NULL);
1687 
1688  /* Get width */
1689  if (default_width == 0)
1690  {
1691  /* Set the sizing column attributes */
1692  g_object_set (G_OBJECT(column),
1693  "sizing", GTK_TREE_VIEW_COLUMN_AUTOSIZE,
1694  NULL);
1695  }
1696  else
1697  {
1698 
1699  /* If saved state comes back with a width of zero (or there is no saved
1700  * state width) the use the default width for the column. Allow for
1701  * padding L and R of the displayed data. */
1702  if (width == 0)
1703  width = default_width + 10;
1704  if (width == 0)
1705  width = 10;
1706 
1707  /* Set the sizing column attributes (including fixed-width) */
1708  g_object_set (G_OBJECT(column),
1709  "sizing", GTK_TREE_VIEW_COLUMN_FIXED,
1710  "fixed-width", width,
1711  NULL);
1712  /* Save the initially calculated preferred width for later
1713  * comparison to the actual width when saving state. Can't
1714  * use the "fixed-width" property for that because it changes
1715  * when the user resizes the column.
1716  */
1717  g_object_set_data (G_OBJECT(column),
1718  "default-width", GINT_TO_POINTER(width));
1719  }
1720 
1721  s_model = gtk_tree_view_get_model (GTK_TREE_VIEW(view));
1722  if (GTK_IS_TREE_SORTABLE(s_model))
1723  {
1724  gtk_tree_view_column_set_sort_column_id (column, data_column);
1725  if (column_sort_fn)
1726  {
1727  gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(s_model),
1728  data_column, column_sort_fn,
1729  GINT_TO_POINTER(data_column),
1730  NULL /* destroy fn */);
1731  }
1732  }
1733 
1734  // Used in registers, sort model not connected to view yet
1735  priv = GNC_TREE_VIEW_GET_PRIVATE(view);
1736  if (priv->sort_model != NULL)
1737  {
1738  gtk_tree_view_column_set_sort_column_id (column, data_column);
1739  if (column_sort_fn)
1740  {
1741  gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(priv->sort_model),
1742  data_column, column_sort_fn,
1743  view,
1744  NULL /* destroy fn */);
1745  }
1746  }
1747 
1748  /* Add to the column selection menu */
1749  if (pref_name)
1750  {
1751  gnc_tree_view_create_menu_item (column, view);
1752  }
1753 }
1754 
1765 GtkTreeViewColumn *
1767  const gchar *column_title,
1768  const gchar *column_short_title,
1769  const gchar *pref_name,
1770  gint model_data_column,
1771  gint model_visibility_column,
1772  GtkTreeIterCompareFunc column_sort_fn,
1773  renderer_toggled toggle_edited_cb)
1774 {
1775  GtkTreeViewColumn *column;
1776  GtkCellRenderer *renderer;
1777 
1778  g_return_val_if_fail (GNC_IS_TREE_VIEW(view), NULL);
1779 
1780  renderer = gtk_cell_renderer_toggle_new ();
1781  if (!toggle_edited_cb)
1782  {
1783  gtk_cell_renderer_toggle_set_activatable (GTK_CELL_RENDERER_TOGGLE(renderer), FALSE);
1784  }
1785  column =
1786  gtk_tree_view_column_new_with_attributes (column_short_title,
1787  renderer,
1788  "active", model_data_column,
1789  NULL);
1790 
1791  /* Add the full title to the object for menu creation */
1792  g_object_set_data_full (G_OBJECT(column), REAL_TITLE,
1793  g_strdup(column_title), g_free);
1794  if (toggle_edited_cb)
1795  g_signal_connect (G_OBJECT(renderer), "toggled",
1796  G_CALLBACK(toggle_edited_cb), view);
1797 
1798  if (model_visibility_column != GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS)
1799  gtk_tree_view_column_add_attribute (column, renderer,
1800  "visible", model_visibility_column);
1801 
1802 
1803  gnc_tree_view_column_properties (view, column, pref_name, model_data_column,
1804  0, FALSE, column_sort_fn);
1805 
1806  gnc_tree_view_append_column (view, column);
1807 
1808  /* Also add the full title to the object as a tooltip */
1809  gtk_widget_set_tooltip_text (gtk_tree_view_column_get_button (column), column_title);
1810 
1811  return column;
1812 }
1813 
1814 static void
1815 renderer_editing_canceled_cb (GtkCellRenderer *renderer, gpointer user_data)
1816 {
1817  GncTreeView *view = user_data;
1818  GncTreeViewPrivate *priv = GNC_TREE_VIEW_GET_PRIVATE(view);
1819  if (priv->editing_finished_cb)
1820  (priv->editing_finished_cb)(view, priv->editing_cb_data);
1821 }
1822 
1823 static void
1824 renderer_editing_started_cb (GtkCellRenderer *renderer,
1825  GtkCellEditable *editable, gchar *path, gpointer user_data)
1826 {
1827  GncTreeView *view = user_data;
1828  GncTreeViewPrivate *priv = GNC_TREE_VIEW_GET_PRIVATE(view);
1829  if (priv->editing_started_cb)
1830  (priv->editing_started_cb)(view, priv->editing_cb_data);
1831 }
1832 
1833 static void
1834 renderer_edited_cb (GtkCellRendererText *renderer, gchar *path,
1835  gchar *new_text, gpointer user_data)
1836 {
1837  GncTreeView *view = user_data;
1838  GncTreeViewPrivate *priv = GNC_TREE_VIEW_GET_PRIVATE(view);
1839  if (priv->editing_finished_cb)
1840  (priv->editing_finished_cb)(view, priv->editing_cb_data);
1841 }
1842 
1843 
1844 static GtkTreeViewColumn *
1845 add_text_column_variant (GncTreeView *view, GtkCellRenderer *renderer,
1846  const gchar *column_title,
1847  const gchar *pref_name,
1848  const gchar *icon_name,
1849  const gchar *sizing_text,
1850  gint model_data_column,
1851  gint model_visibility_column,
1852  GtkTreeIterCompareFunc column_sort_fn)
1853 {
1854  GtkTreeViewColumn *column;
1855  PangoLayout* layout;
1856  int default_width, title_width;
1857 
1858  g_return_val_if_fail (GNC_IS_TREE_VIEW(view), NULL);
1859 
1860  column = gtk_tree_view_column_new ();
1861  gtk_tree_view_column_set_title (column, column_title);
1862 
1863  /* Set up an icon renderer if requested */
1864  if (icon_name)
1865  {
1866  GtkCellRenderer *renderer_pix = gtk_cell_renderer_pixbuf_new ();
1867  g_object_set (renderer_pix, "icon-name", icon_name, NULL);
1868  gtk_tree_view_column_pack_start (column, renderer_pix, FALSE);
1869  }
1870 
1871  /* Set up a text renderer and attributes */
1872  gtk_tree_view_column_pack_start (column, renderer, TRUE);
1873 
1874  /* Set up the callbacks for when editing */
1875  g_signal_connect (G_OBJECT(renderer), "editing-canceled",
1876  (GCallback)renderer_editing_canceled_cb, view);
1877 
1878  g_signal_connect (G_OBJECT(renderer), "editing-started",
1879  (GCallback)renderer_editing_started_cb, view);
1880 
1881  g_signal_connect (G_OBJECT(renderer), "edited",
1882  (GCallback)renderer_edited_cb, view);
1883 
1884  /* Set renderer attributes controlled by the model */
1885  if (model_data_column != GNC_TREE_VIEW_COLUMN_DATA_NONE)
1886  gtk_tree_view_column_add_attribute (column, renderer,
1887  "text", model_data_column);
1888  if (model_visibility_column != GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS)
1889  gtk_tree_view_column_add_attribute (column, renderer,
1890  "visible", model_visibility_column);
1891 
1892  /* Default size is the larger of the column title and the sizing text */
1893  layout = gtk_widget_create_pango_layout (GTK_WIDGET(view), column_title);
1894  pango_layout_get_pixel_size (layout, &title_width, NULL);
1895  g_object_unref (layout);
1896  layout = gtk_widget_create_pango_layout (GTK_WIDGET(view), sizing_text);
1897  pango_layout_get_pixel_size (layout, &default_width, NULL);
1898  g_object_unref (layout);
1899  default_width = MAX(default_width, title_width);
1900  if (default_width)
1901  default_width += 10; /* padding on either side */
1902  gnc_tree_view_column_properties (view, column, pref_name, model_data_column,
1903  default_width, TRUE, column_sort_fn);
1904 
1905  gnc_tree_view_append_column (view, column);
1906  return column;
1907 }
1908 
1909 
1918 GtkTreeViewColumn *
1920  const gchar *column_title,
1921  const gchar *pref_name,
1922  const gchar *icon_name,
1923  const gchar *sizing_text,
1924  gint model_data_column,
1925  gint model_visibility_column,
1926  GtkTreeIterCompareFunc column_sort_fn)
1927 {
1928  GtkCellRenderer *renderer;
1929 
1930  g_return_val_if_fail (GNC_IS_TREE_VIEW(view), NULL);
1931 
1932  renderer = gtk_cell_renderer_text_new ();
1933 
1934  return add_text_column_variant (view, renderer,
1935  column_title, pref_name,
1936  icon_name, sizing_text,
1937  model_data_column,
1938  model_visibility_column,
1939  column_sort_fn);
1940 }
1941 
1950 GtkTreeViewColumn *
1952  const gchar *column_title,
1953  const gchar *pref_name,
1954  const gchar *icon_name,
1955  const gchar *sizing_text,
1956  gint model_data_column,
1957  gint model_visibility_column,
1958  GtkTreeIterCompareFunc column_sort_fn)
1959 {
1960  GtkCellRenderer *renderer;
1961 
1962  g_return_val_if_fail (GNC_IS_TREE_VIEW(view), NULL);
1963 
1964  renderer = gnc_cell_renderer_text_view_new ();
1965 
1966  return add_text_column_variant (view, renderer,
1967  column_title, pref_name,
1968  icon_name, sizing_text,
1969  model_data_column,
1970  model_visibility_column,
1971  column_sort_fn);
1972 }
1973 
1982 GtkTreeViewColumn *
1983 gnc_tree_view_add_pix_column (GncTreeView *view,
1984  const gchar *column_title,
1985  const gchar *pref_name,
1986  const gchar *sizing_text,
1987  gint model_data_column,
1988  gint model_visibility_column,
1989  GtkTreeIterCompareFunc column_sort_fn)
1990 {
1991  GtkTreeViewColumn *column;
1992  PangoLayout* layout;
1993  int default_width, title_width;
1994  GtkCellRenderer *renderer;
1995 
1996  g_return_val_if_fail (GNC_IS_TREE_VIEW(view), NULL);
1997 
1998  renderer = gtk_cell_renderer_pixbuf_new ();
1999 
2000  column = gtk_tree_view_column_new ();
2001  gtk_tree_view_column_set_title (column, column_title);
2002 
2003  /* Set up a text renderer and attributes */
2004  gtk_tree_view_column_pack_start (column, renderer, TRUE);
2005 
2006  /* Set renderer attributes controlled by the model */
2007  if (model_data_column != GNC_TREE_VIEW_COLUMN_DATA_NONE)
2008  gtk_tree_view_column_add_attribute (column, renderer,
2009  "icon-name", model_data_column);
2010  if (model_visibility_column != GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS)
2011  gtk_tree_view_column_add_attribute (column, renderer,
2012  "visible", model_visibility_column);
2013 
2014  /* Default size is the larger of the column title and the sizing text */
2015  layout = gtk_widget_create_pango_layout (GTK_WIDGET(view), column_title);
2016  pango_layout_get_pixel_size (layout, &title_width, NULL);
2017  g_object_unref (layout);
2018  layout = gtk_widget_create_pango_layout (GTK_WIDGET(view), sizing_text);
2019  pango_layout_get_pixel_size (layout, &default_width, NULL);
2020  g_object_unref (layout);
2021  default_width = MAX(default_width, title_width);
2022  if (default_width)
2023  default_width += 10; /* padding on either side */
2024  gnc_tree_view_column_properties (view, column, pref_name, model_data_column,
2025  default_width, TRUE, column_sort_fn);
2026 
2027  gnc_tree_view_append_column (view, column);
2028  return column;
2029 }
2030 
2031 GtkCellRenderer *
2032 gnc_tree_view_column_get_renderer (GtkTreeViewColumn *column)
2033 {
2034  GList *renderers;
2035  GtkCellRenderer *cr = NULL;
2036 
2037  g_return_val_if_fail (GTK_TREE_VIEW_COLUMN(column), NULL);
2038 
2039  /* Get the list of one renderer */
2040  renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT(column));
2041  if (g_list_length (renderers) > 0)
2042  cr = GTK_CELL_RENDERER(renderers->data);
2043  g_list_free (renderers);
2044 
2045  return cr;
2046 }
2047 
2058 GtkTreeViewColumn *
2060  const gchar *column_title,
2061  const gchar *pref_name,
2062  const gchar *sizing_text,
2063  gint model_data_column,
2064  gint model_color_column,
2065  gint model_visibility_column,
2066  GtkTreeIterCompareFunc column_sort_fn)
2067 {
2068  GtkTreeViewColumn *column;
2069  GtkCellRenderer *renderer;
2070  gfloat alignment = 1.0;
2071 
2072  column = gnc_tree_view_add_text_column (view, column_title, pref_name,
2073  NULL, sizing_text, model_data_column,
2074  model_visibility_column,
2075  column_sort_fn);
2076 
2077  renderer = gnc_tree_view_column_get_renderer (column);
2078 
2079  /* Right align the column title and data for both ltr and rtl */
2080  if (gtk_widget_get_direction (GTK_WIDGET(view)) == GTK_TEXT_DIR_RTL)
2081  alignment = 0.0;
2082 
2083  g_object_set (G_OBJECT(column), "alignment", alignment, NULL);
2084  g_object_set (G_OBJECT(renderer), "xalign", alignment, NULL);
2085 
2086  /* Change the text color */
2087  if (model_color_column != GNC_TREE_VIEW_COLUMN_COLOR_NONE)
2088  gtk_tree_view_column_add_attribute (column, renderer,
2089  "foreground", model_color_column);
2090 
2091  return column;
2092 }
2093 
2102 gint
2103 gnc_tree_view_append_column (GncTreeView *view,
2104  GtkTreeViewColumn *column)
2105 {
2106  int n = gtk_tree_view_get_n_columns (GTK_TREE_VIEW(view));
2107 
2108  /* Ignore the initial column, the selection menu */
2109  if (n >= 1)
2110  n -= 1;
2111  return gtk_tree_view_insert_column (GTK_TREE_VIEW(view), column, n);
2112 }
2113 
2114 static gboolean
2115 get_column_next_to (GtkTreeView *tv, GtkTreeViewColumn **col, gboolean backward)
2116 {
2117  GList *cols, *node;
2118  GtkTreeViewColumn *c = NULL;
2119  gint seen = 0;
2120  gboolean wrapped = FALSE;
2121 
2122  cols = gtk_tree_view_get_columns (tv);
2123  g_return_val_if_fail (cols != NULL, FALSE);
2124 
2125  node = g_list_find (cols, *col);
2126  g_return_val_if_fail (node, FALSE);
2127  do
2128  {
2129  node = backward ? node->prev : node->next;
2130  if (!node)
2131  {
2132  wrapped = TRUE;
2133  node = backward ? g_list_last (cols) : cols;
2134  }
2135  c = GTK_TREE_VIEW_COLUMN (node->data);
2136  if (c && gtk_tree_view_column_get_visible (c))
2137  seen++;
2138  if (c == *col) break;
2139  }
2140  while (!seen);
2141 
2142  g_list_free (cols);
2143  *col = c;
2144  return wrapped;
2145 }
2146 
2147 gboolean
2148 gnc_tree_view_path_is_valid (GncTreeView *view, GtkTreePath *path)
2149 {
2150  GtkTreeView *tv = GTK_TREE_VIEW(view);
2151  GtkTreeModel *s_model;
2152  GtkTreeIter iter;
2153 
2154  s_model = gtk_tree_view_get_model (tv);
2155  return gtk_tree_model_get_iter (s_model, &iter, path);
2156 }
2157 
2158 void
2159 gnc_tree_view_keynav (GncTreeView *view, GtkTreeViewColumn **col,
2160  GtkTreePath *path, GdkEventKey *event)
2161 {
2162  GtkTreeView *tv = GTK_TREE_VIEW(view);
2163  gint depth;
2164  gboolean shifted;
2165 
2166  if (event->type != GDK_KEY_PRESS) return;
2167 
2168  switch (event->keyval)
2169  {
2170  case GDK_KEY_Tab:
2171  case GDK_KEY_ISO_Left_Tab:
2172  case GDK_KEY_KP_Tab:
2173  shifted = event->state & GDK_SHIFT_MASK;
2174  if (get_column_next_to (tv, col, shifted))
2175  {
2176  /* This is the end (or beginning) of the line, buddy. */
2177  depth = gtk_tree_path_get_depth (path);
2178  if (shifted)
2179  {
2180  if (!gtk_tree_path_prev (path) && depth > 1)
2181  {
2182  gtk_tree_path_up (path);
2183  }
2184  }
2185  else if (gtk_tree_view_row_expanded (tv, path))
2186  {
2187  gtk_tree_path_down (path);
2188  }
2189  else
2190  {
2191  gtk_tree_path_next (path);
2192  if (!gnc_tree_view_path_is_valid (view, path) && depth > 2)
2193  {
2194  gtk_tree_path_prev (path);
2195  gtk_tree_path_up (path);
2196  gtk_tree_path_next (path);
2197  }
2198  if (!gnc_tree_view_path_is_valid (view, path) && depth > 1)
2199  {
2200  gtk_tree_path_prev (path);
2201  gtk_tree_path_up (path);
2202  gtk_tree_path_next (path);
2203  }
2204  }
2205  }
2206  break;
2207 
2208  case GDK_KEY_Return:
2209  case GDK_KEY_KP_Enter:
2210  if (gtk_tree_view_row_expanded (tv, path))
2211  {
2212  gtk_tree_path_down (path);
2213  }
2214  else
2215  {
2216  depth = gtk_tree_path_get_depth (path);
2217  gtk_tree_path_next (path);
2218  if (!gnc_tree_view_path_is_valid (view, path) && depth > 1)
2219  {
2220  gtk_tree_path_prev (path);
2221  gtk_tree_path_up (path);
2222  gtk_tree_path_next (path);
2223  }
2224  }
2225  break;
2226  }
2227  return;
2228 }
2229 
2230 void
2231 gnc_tree_view_set_editing_started_cb (GncTreeView *view, GFunc editing_started_cb, gpointer editing_cb_data)
2232 {
2233  GncTreeViewPrivate *priv;
2234 
2235  if (!view && !editing_started_cb)
2236  return;
2237 
2238  priv = GNC_TREE_VIEW_GET_PRIVATE(view);
2239 
2240  priv->editing_started_cb = editing_started_cb;
2241  priv->editing_cb_data = editing_cb_data;
2242 }
2243 
2244 void
2245 gnc_tree_view_set_editing_finished_cb (GncTreeView *view, GFunc editing_finished_cb, gpointer editing_cb_data)
2246 {
2247  GncTreeViewPrivate *priv;
2248 
2249  if (!view && !editing_finished_cb)
2250  return;
2251 
2252  priv = GNC_TREE_VIEW_GET_PRIVATE(view);
2253 
2254  priv->editing_finished_cb = editing_finished_cb;
2255  priv->editing_cb_data = editing_cb_data;
2256 }
2257 
void gnc_tree_view_set_sort_user_data(GncTreeView *view, GtkTreeModel *s_model)
This allows the columns to be setup without the model connected.
Functions to load, save and get gui state.
void gnc_tree_view_expand_columns(GncTreeView *view, gchar *first_column_name,...)
This function set the columns that will be allocated the free space in the view.
gulong gnc_prefs_register_cb(const char *group, const gchar *pref_name, gpointer func, gpointer user_data)
Register a callback that gets triggered when the given preference changes.
Definition: gnc-prefs.c:128
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.c:143