GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
gnucash-item-list.c
1 /********************************************************************\
2  * gnucash-item-list.c -- A scrollable list box *
3  * *
4  * Initial copyright not recorded. *
5  * Copyright (c) 2006 David Hampton <[email protected]> *
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 [email protected] *
23  * *
24 \********************************************************************/
25 
26 /*
27  * A scrollable list box.
28  */
29 
30 #include "config.h"
31 
32 #include <glib/gi18n.h>
33 #include <gdk/gdkkeysyms.h>
34 #include <libgnomecanvas/libgnomecanvas.h>
35 
36 #include "gnc-engine.h"
37 #include "gnucash-item-list.h"
38 #include "gnucash-scrolled-window.h"
39 
40 /* Item list signals */
41 enum
42 {
43  SELECT_ITEM,
44  CHANGE_ITEM,
45  ACTIVATE_ITEM,
46  KEY_PRESS_EVENT,
47  LAST_SIGNAL
48 };
49 
50 static GnomeCanvasWidgetClass *gnc_item_list_parent_class;
51 static guint gnc_item_list_signals[LAST_SIGNAL];
52 
53 gboolean _gnc_item_find_selection(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data);
54 
55 gint
56 gnc_item_list_num_entries (GncItemList *item_list)
57 {
58  GtkTreeModel *model;
59 
60  g_return_val_if_fail(item_list != NULL, 0);
61  g_return_val_if_fail(IS_GNC_ITEM_LIST(item_list), 0);
62 
63  model = GTK_TREE_MODEL(item_list->list_store);
64  return gtk_tree_model_iter_n_children(model, NULL);
65 }
66 
67 
68 void
69 gnc_item_list_clear (GncItemList *item_list)
70 {
71  GtkTreeSelection* selection;
72 
73  g_return_if_fail(IS_GNC_ITEM_LIST(item_list));
74  g_return_if_fail(item_list->list_store != NULL);
75 
76  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (item_list->tree_view));
77 
78  g_signal_handlers_block_matched (G_OBJECT (selection), G_SIGNAL_MATCH_DATA,
79  0, 0, NULL, NULL, item_list);
80  gtk_list_store_clear (item_list->list_store);
81  g_signal_handlers_unblock_matched (G_OBJECT (selection), G_SIGNAL_MATCH_DATA,
82  0, 0, NULL, NULL, item_list);
83 }
84 
85 
86 void
87 gnc_item_list_append (GncItemList *item_list, const char *string)
88 {
89  GtkTreeIter iter;
90 
91  g_return_if_fail(IS_GNC_ITEM_LIST(item_list));
92  g_return_if_fail(item_list->list_store != NULL);
93  g_return_if_fail(string != NULL);
94 
95  gtk_list_store_append (item_list->list_store, &iter);
96  gtk_list_store_set (item_list->list_store, &iter, 0, string, -1);
97 }
98 
99 
100 void
101 gnc_item_list_set_sort_enabled(GncItemList *item_list, gboolean enabled)
102 {
103  if (enabled)
104  {
105  gtk_tree_sortable_set_sort_column_id
106  (GTK_TREE_SORTABLE (item_list->list_store),
107  0,
108  GTK_SORT_ASCENDING);
109  }
110  else
111  {
112  gtk_tree_sortable_set_sort_column_id
113  (GTK_TREE_SORTABLE (item_list->list_store),
114  GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID,
115  GTK_SORT_ASCENDING);
116  }
117 }
118 
119 
120 typedef struct _findSelectionData
121 {
122  GncItemList *item_list;
123  const char *string_to_find;
124  GtkTreePath *found_path;
126 
127 gboolean
128 _gnc_item_find_selection(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
129 {
130  FindSelectionData *to_find = (FindSelectionData*)data;
131  gchar *iterStr;
132  gboolean found;
133 
134  gtk_tree_model_get(model, iter, 0, &iterStr, -1);
135  found = g_strcmp0(to_find->string_to_find, iterStr) == 0;
136  g_free(iterStr);
137  if (found)
138  {
139  to_find->found_path = gtk_tree_path_copy(path);
140  return TRUE;
141  }
142  return FALSE;
143 }
144 
145 gboolean
146 gnc_item_in_list (GncItemList *item_list, const char *string)
147 {
148  FindSelectionData *to_find_data;
149  gboolean result;
150 
151  g_return_val_if_fail(item_list != NULL, FALSE);
152  g_return_val_if_fail(IS_GNC_ITEM_LIST(item_list), FALSE);
153 
154  to_find_data = (FindSelectionData*)g_new0(FindSelectionData, 1);
155  to_find_data->item_list = item_list;
156  to_find_data->string_to_find = string;
157 
158  gtk_tree_model_foreach(GTK_TREE_MODEL(item_list->list_store),
159  _gnc_item_find_selection,
160  to_find_data);
161 
162  result = (to_find_data->found_path != NULL);
163  g_free(to_find_data);
164  return result;
165 }
166 
167 
168 void
169 gnc_item_list_select (GncItemList *item_list, const char *string)
170 {
171  GtkTreeSelection *tree_sel = NULL;
172  FindSelectionData *to_find_data;
173 
174  g_return_if_fail(item_list != NULL);
175  g_return_if_fail(IS_GNC_ITEM_LIST(item_list));
176 
177  tree_sel = gtk_tree_view_get_selection(item_list->tree_view);
178 
179  if (string == NULL)
180  {
181  gtk_tree_selection_unselect_all(tree_sel);
182  return;
183  }
184 
185  to_find_data = (FindSelectionData*)g_new0(FindSelectionData, 1);
186  to_find_data->item_list = item_list;
187  to_find_data->string_to_find = string;
188 
189  gtk_tree_model_foreach(GTK_TREE_MODEL(item_list->list_store),
190  _gnc_item_find_selection,
191  to_find_data);
192 
193  if (to_find_data->found_path != NULL)
194  {
195  gtk_tree_view_set_cursor(item_list->tree_view, to_find_data->found_path, NULL, FALSE);
196  gtk_tree_path_free(to_find_data->found_path);
197 
198  gnc_item_list_show_selected(item_list);
199  }
200 
201  g_free(to_find_data);
202 }
203 
204 
205 void
206 gnc_item_list_show_selected (GncItemList *item_list)
207 {
208  GtkTreeSelection *selection;
209  GtkTreeIter iter;
210  GtkTreePath *path;
211  GtkTreeModel *model;
212 
213  g_return_if_fail(item_list != NULL);
214  g_return_if_fail(IS_GNC_ITEM_LIST(item_list));
215 
216  selection = gtk_tree_view_get_selection (item_list->tree_view);
217 
218  if (gtk_tree_selection_get_selected (selection, &model, &iter))
219  {
220  path = gtk_tree_model_get_path (model, &iter);
221 
222  gtk_tree_view_scroll_to_cell (item_list->tree_view,
223  path, NULL, TRUE, 0.5, 0.0);
224  }
225 }
226 
227 int
228 gnc_item_list_autosize (GncItemList *item_list)
229 {
230  g_return_val_if_fail(item_list != NULL, 0);
231  g_return_val_if_fail(IS_GNC_ITEM_LIST(item_list), 0);
232 
233  return 100;
234 }
235 
236 
237 static void
238 gnc_item_list_init (GncItemList *item_list)
239 {
240  item_list->tree_view = NULL;
241  item_list->list_store = NULL;
242  item_list->frame = NULL;
243 }
244 
245 
246 static gboolean
247 gnc_item_list_button_event(GtkWidget *widget, GdkEventButton *event,
248  gpointer data)
249 {
250  GncItemList *item_list;
251  GtkTreeIter iter;
252  GtkTreePath *path;
253  GtkTreeModel *model;
254  gchar *string;
255  gboolean success;
256 
257  g_return_val_if_fail(IS_GNC_ITEM_LIST (data), FALSE);
258 
259  item_list = GNC_ITEM_LIST (data);
260 
261  switch (event->button)
262  {
263  case 1:
264  if (!gtk_tree_view_get_path_at_pos (item_list->tree_view,
265  event->x,
266  event->y,
267  &path,
268  NULL,
269  NULL,
270  NULL))
271  {
272  return FALSE;
273  }
274 
275  gtk_tree_view_set_cursor (item_list->tree_view, path, NULL, FALSE);
276 
277  model = GTK_TREE_MODEL (item_list->list_store);
278  success = gtk_tree_model_get_iter (model, &iter, path);
279 
280  gtk_tree_path_free (path);
281 
282  if (!success)
283  return FALSE;
284 
285  gtk_tree_model_get (model, &iter, 0, &string, -1);
286 
287  g_signal_emit (G_OBJECT (item_list),
288  gnc_item_list_signals[ACTIVATE_ITEM],
289  0,
290  string);
291  g_free(string);
292  return TRUE;
293  default:
294  return FALSE;
295  }
296 
297  return FALSE;
298 }
299 
300 static gboolean
301 gnc_item_list_key_event (GtkWidget *widget, GdkEventKey *event, gpointer data)
302 {
303  GncItemList *item_list = GNC_ITEM_LIST (data);
304  GtkTreeSelection *selection = NULL;
305  GtkTreeIter iter;
306  GtkTreeModel *model;
307  gchar *string;
308 
309  switch (event->keyval)
310  {
311  case GDK_KEY_Return:
312  selection = gtk_tree_view_get_selection (item_list->tree_view);
313  if (!gtk_tree_selection_get_selected (selection, &model, &iter))
314  return FALSE;
315 
316  gtk_tree_model_get (model, &iter, 0, &string, -1);
317 
318  g_signal_emit (G_OBJECT (item_list),
319  gnc_item_list_signals[ACTIVATE_ITEM],
320  0,
321  string);
322  g_free(string);
323  return TRUE;
324 
325  case GDK_KEY_Page_Up:
326  case GDK_KEY_Page_Down:
327  case GDK_KEY_Up:
328  case GDK_KEY_Down:
329  /* These go to the clist */
330  return FALSE;
331  }
332 
333  /* These go to the sheet */
334  g_signal_stop_emission_by_name (G_OBJECT (widget), "key_press_event");
335 
336  g_signal_emit (G_OBJECT (item_list),
337  gnc_item_list_signals[KEY_PRESS_EVENT], 0, event);
338 
339  return TRUE;
340 }
341 
342 
343 static void
344 gnc_item_list_class_init (GncItemListClass *item_list_class)
345 {
346  GObjectClass *object_class = G_OBJECT_CLASS (item_list_class);
347 
348  gnc_item_list_parent_class = g_type_class_peek_parent (item_list_class);
349 
350 
351  gnc_item_list_signals[SELECT_ITEM] =
352  g_signal_new ("select_item",
353  G_OBJECT_CLASS_TYPE (object_class),
354  G_SIGNAL_RUN_LAST,
355  G_STRUCT_OFFSET(GncItemListClass, select_item),
356  NULL, NULL,
357  g_cclosure_marshal_VOID__POINTER,
358  G_TYPE_NONE, 1,
359  G_TYPE_POINTER);
360 
361  gnc_item_list_signals[CHANGE_ITEM] =
362  g_signal_new ("change_item",
363  G_OBJECT_CLASS_TYPE (object_class),
364  G_SIGNAL_RUN_LAST,
365  G_STRUCT_OFFSET(GncItemListClass, change_item),
366  NULL, NULL,
367  g_cclosure_marshal_VOID__POINTER,
368  G_TYPE_NONE, 1,
369  G_TYPE_POINTER);
370 
371  gnc_item_list_signals[ACTIVATE_ITEM] =
372  g_signal_new ("activate_item",
373  G_OBJECT_CLASS_TYPE (object_class),
374  G_SIGNAL_RUN_LAST,
375  G_STRUCT_OFFSET(GncItemListClass, activate_item),
376  NULL, NULL,
377  g_cclosure_marshal_VOID__POINTER,
378  G_TYPE_NONE, 1,
379  G_TYPE_POINTER);
380 
381  gnc_item_list_signals[KEY_PRESS_EVENT] =
382  g_signal_new ("key_press_event",
383  G_OBJECT_CLASS_TYPE (object_class),
384  G_SIGNAL_RUN_LAST,
385  G_STRUCT_OFFSET(GncItemListClass, key_press_event),
386  NULL, NULL,
387  g_cclosure_marshal_VOID__BOXED,
388  G_TYPE_NONE, 1,
389  GDK_TYPE_EVENT);
390 
391  item_list_class->select_item = NULL;
392  item_list_class->change_item = NULL;
393  item_list_class->activate_item = NULL;
394  item_list_class->key_press_event = NULL;
395 }
396 
397 
398 GType
399 gnc_item_list_get_type (void)
400 {
401  static GType gnc_item_list_type = 0;
402 
403  if (gnc_item_list_type == 0)
404  {
405  static const GTypeInfo gnc_item_list_info =
406  {
407  sizeof(GncItemListClass),
408  NULL,
409  NULL,
410  (GClassInitFunc) gnc_item_list_class_init,
411  NULL,
412  NULL,
413  sizeof(GncItemList),
414  0,
415  (GInstanceInitFunc) gnc_item_list_init
416  };
417 
418  gnc_item_list_type =
419  g_type_register_static (gnome_canvas_widget_get_type(), "GncItemList",
420  &gnc_item_list_info, 0);
421  }
422 
423  return gnc_item_list_type;
424 }
425 
426 
427 static void
428 tree_view_selection_changed (GtkTreeSelection *selection,
429  gpointer data)
430 {
431  GncItemList *item_list = GNC_ITEM_LIST (data);
432  GtkTreeModel *model;
433  GtkTreeIter iter;
434  char *string;
435 
436  g_return_if_fail(data);
437  g_return_if_fail(selection);
438 
439  if (!gtk_tree_selection_get_selected (selection, &model, &iter))
440  return;
441 
442  gtk_tree_model_get (model, &iter, 0, &string, -1);
443 
444  g_signal_emit (G_OBJECT (item_list), gnc_item_list_signals[CHANGE_ITEM], 0, string);
445 
446  g_free (string);
447 }
448 
449 GnomeCanvasItem *
450 gnc_item_list_new(GnomeCanvasGroup *parent, GtkListStore *list_store)
451 {
452  GtkWidget *frame;
453  GtkWidget *tree_view;
454  GtkWidget *scrollwin;
455  GtkCellRenderer *renderer;
456  GtkTreeViewColumn *column;
457  GnomeCanvasItem *item;
458  GncItemList *item_list;
459 
460  frame = gtk_frame_new (NULL);
461 
462  scrollwin = gnc_scrolled_window_new ();
463  gtk_container_add (GTK_CONTAINER (frame), scrollwin);
464 
465  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrollwin),
466  GTK_POLICY_AUTOMATIC,
467  GTK_POLICY_AUTOMATIC);
468 
469  if (NULL == list_store)
470  list_store = gtk_list_store_new (1, G_TYPE_STRING);
471  else
472  g_object_ref(list_store);
473  tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (list_store));
474  g_object_unref(list_store);
475  /* Removed code to enable sorting. Enable it after the list is
476  * fully populated by calling gnc_item_list_finished_loading(). */
477 
478  gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tree_view), FALSE);
479  gtk_tree_selection_set_mode (gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view)),
480  GTK_SELECTION_BROWSE);
481  gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(list_store),
482  0, GTK_SORT_ASCENDING);
483 
484  renderer = gtk_cell_renderer_text_new ();
485  column = gtk_tree_view_column_new_with_attributes (_("List"),
486  renderer,
487  "text", 0,
488  NULL);
489  gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
490 
491  gtk_container_add (GTK_CONTAINER (scrollwin), tree_view);
492  gtk_widget_show_all (frame);
493 
494  item = gnome_canvas_item_new (parent, gnc_item_list_get_type(),
495  "widget", frame,
496  "size_pixels", TRUE,
497  "x", -10000.0,
498  "y", -10000.0,
499  NULL);
500 
501  item_list = GNC_ITEM_LIST (item);
502 
503  item_list->tree_view = GTK_TREE_VIEW (tree_view);
504  item_list->list_store = list_store;
505  item_list->frame = frame;
506 
507  g_signal_connect (G_OBJECT(tree_view), "button_press_event",
508  G_CALLBACK (gnc_item_list_button_event), item_list);
509 
510  g_signal_connect (G_OBJECT (tree_view), "key_press_event",
511  G_CALLBACK (gnc_item_list_key_event), item_list);
512 
513  g_signal_connect (G_OBJECT (gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view))), "changed",
514  G_CALLBACK (tree_view_selection_changed), item_list);
515 
516  return item;
517 }
518 
519 
All type declarations for the whole Gnucash engine.