GnuCash  4.12-68-g5dc52459a1+
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 <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 \********************************************************************/
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 
35 #include "gnc-engine.h"
36 #include "gnucash-item-list.h"
37 
38 /* Item list signals */
39 enum
40 {
41  SELECT_ITEM,
42  CHANGE_ITEM,
43  ACTIVATE_ITEM,
44  LAST_SIGNAL
45 };
46 
47 static GtkEventBoxClass* gnc_item_list_parent_class;
48 static guint gnc_item_list_signals[LAST_SIGNAL];
49 
50 gboolean _gnc_item_find_selection (GtkTreeModel* model, GtkTreePath* path,
51  GtkTreeIter* iter, gpointer data);
52 
53 gint
54 gnc_item_list_num_entries (GncItemList* item_list)
55 {
56  GtkTreeModel* model;
57 
58  g_return_val_if_fail (item_list != NULL, 0);
59  g_return_val_if_fail (IS_GNC_ITEM_LIST (item_list), 0);
60 
61  model = gnc_item_list_using_temp (item_list) ?
62  GTK_TREE_MODEL (item_list->temp_store) :
63  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  gtk_list_store_append (item_list->list_store, &iter);
95  gtk_list_store_set (item_list->list_store, &iter, 0, string, -1);
96 }
97 
98 
99 void
100 gnc_item_list_set_sort_enabled (GncItemList* item_list, gboolean enabled)
101 {
102  if (enabled)
103  {
104  gtk_tree_sortable_set_sort_column_id
105  (GTK_TREE_SORTABLE (item_list->list_store),
106  0,
107  GTK_SORT_ASCENDING);
108  }
109  else
110  {
111  gtk_tree_sortable_set_sort_column_id
112  (GTK_TREE_SORTABLE (item_list->list_store),
113  GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID,
114  GTK_SORT_ASCENDING);
115  }
116 }
117 
118 
119 typedef struct _findSelectionData
120 {
121  GncItemList* item_list;
122  const char* string_to_find;
123  GtkTreePath* found_path;
125 
126 gboolean
127 _gnc_item_find_selection (GtkTreeModel* model, GtkTreePath* path,
128  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,
196  FALSE);
197  gtk_tree_path_free (to_find_data->found_path);
198 
199  gnc_item_list_show_selected (item_list);
200  }
201 
202  g_free (to_find_data);
203 }
204 
205 char*
207 {
208  GtkTreeIter iter;
209  GtkTreeModel* model;
210  gchar* string;
211 
212  GtkTreeSelection *selection =
213  gtk_tree_view_get_selection (item_list->tree_view);
214  if (!gtk_tree_selection_get_selected (selection, &model, &iter))
215  return NULL;
216 
217  gtk_tree_model_get (model, &iter, 0, &string, -1);
218  return string;
219 }
220 
221 
222 void
223 gnc_item_list_show_selected (GncItemList* item_list)
224 {
225  GtkTreeSelection* selection;
226  GtkTreeIter iter;
227  GtkTreeModel* model;
228 
229  g_return_if_fail (item_list != NULL);
230  g_return_if_fail (IS_GNC_ITEM_LIST (item_list));
231 
232  selection = gtk_tree_view_get_selection (item_list->tree_view);
233 
234  if (gtk_tree_selection_get_selected (selection, &model, &iter))
235  {
236  GtkTreePath* path = gtk_tree_model_get_path (model, &iter);
237 
238  gtk_tree_view_scroll_to_cell (item_list->tree_view,
239  path, NULL, TRUE, 0.5, 0.0);
240  gtk_tree_path_free (path);
241  }
242 }
243 
244 int
245 gnc_item_list_autosize (GncItemList* item_list)
246 {
247  g_return_val_if_fail (item_list != NULL, 0);
248  g_return_val_if_fail (IS_GNC_ITEM_LIST (item_list), 0);
249 
250  return 100;
251 }
252 
253 void
254 gnc_item_list_set_temp_store (GncItemList *item_list, GtkListStore *store)
255 {
256 
257  g_return_if_fail (item_list != 0);
258 
259  item_list->temp_store = store;
260  if (store)
261  {
262  gtk_tree_view_set_model (item_list->tree_view,
263  GTK_TREE_MODEL (item_list->temp_store));
264  }
265  else
266  {
267  gtk_tree_view_set_model (item_list->tree_view,
268  GTK_TREE_MODEL (item_list->list_store));
269  item_list->temp_store = NULL;
270  }
271 }
272 
273 gboolean
274 gnc_item_list_using_temp (GncItemList *item_list)
275 {
276  return item_list && item_list->temp_store;
277 }
278 
279 static void
280 gnc_item_list_init (GncItemList* item_list)
281 {
282  item_list->scrollwin = NULL;
283  item_list->tree_view = NULL;
284  item_list->list_store = NULL;
285  item_list->temp_store = NULL;
286  item_list->cell_height = 0;
287 }
288 
289 
290 static gboolean
291 gnc_item_list_button_event (GtkWidget* widget, GdkEventButton* event,
292  gpointer data)
293 {
294  GncItemList* item_list;
295  GtkTreeIter iter;
296  GtkTreePath* path;
297  GtkTreeModel* model;
298  gchar* string;
299  gboolean success;
300 
301  g_return_val_if_fail (IS_GNC_ITEM_LIST (data), FALSE);
302 
303  item_list = GNC_ITEM_LIST (data);
304 
305  switch (event->button)
306  {
307  case 1:
308  if (!gtk_tree_view_get_path_at_pos (item_list->tree_view,
309  event->x,
310  event->y,
311  &path,
312  NULL,
313  NULL,
314  NULL))
315  {
316  return FALSE;
317  }
318 
319  gtk_tree_view_set_cursor (item_list->tree_view, path, NULL, FALSE);
320 
321  model = GTK_TREE_MODEL (item_list->list_store);
322  success = gtk_tree_model_get_iter (model, &iter, path);
323 
324  gtk_tree_path_free (path);
325 
326  if (!success)
327  return FALSE;
328 
329  gtk_tree_model_get (model, &iter, 0, &string, -1);
330 
331  g_signal_emit (G_OBJECT (item_list),
332  gnc_item_list_signals[ACTIVATE_ITEM],
333  0,
334  string);
335  g_free (string);
336  return TRUE;
337  default:
338  return FALSE;
339  }
340 
341  return FALSE;
342 }
343 
344 static gboolean
345 gnc_item_list_key_event (GtkWidget* widget, GdkEventKey* event, gpointer data)
346 {
347  GncItemList* item_list = GNC_ITEM_LIST (data);
348  gchar* string;
349  gboolean retval;
350 
351  switch (event->keyval)
352  {
353  case GDK_KEY_Return:
354  string = gnc_item_list_get_selection (item_list);
355  if (!string) // Nothing selected, might be new value
356  break; // Let the sheet deal with it.
357  g_signal_emit (G_OBJECT (item_list),
358  gnc_item_list_signals[ACTIVATE_ITEM],
359  0,
360  string);
361  g_signal_emit (G_OBJECT (item_list), gnc_item_list_signals[CHANGE_ITEM], 0,
362  string);
363  g_free (string);
364  return TRUE;
365 
366  case GDK_KEY_Page_Up:
367  case GDK_KEY_Page_Down:
368  case GDK_KEY_Up:
369  case GDK_KEY_Down:
370  case GDK_KEY_KP_Up:
371  case GDK_KEY_KP_Down:
372  case GDK_KEY_KP_Page_Up:
373  case GDK_KEY_KP_Page_Down:
374  /* These go to the clist */
375  return FALSE;
376  }
377 
378  /* These go to the sheet */
379  g_signal_stop_emission_by_name (G_OBJECT (widget), "key_press_event");
380 
381  g_signal_emit_by_name (G_OBJECT (item_list), "key_press_event", event,
382  &retval);
383 
384  return retval;
385 }
386 
387 
388 static void
389 gnc_item_list_class_init (GncItemListClass* item_list_class)
390 {
391  GObjectClass* object_class = G_OBJECT_CLASS (item_list_class);
392 
393  gnc_item_list_parent_class = g_type_class_peek_parent (item_list_class);
394 
395  gtk_widget_class_set_css_name (GTK_WIDGET_CLASS(item_list_class), "gnc-id-sheet-list");
396 
397  gnc_item_list_signals[SELECT_ITEM] =
398  g_signal_new ("select_item",
399  G_OBJECT_CLASS_TYPE (object_class),
400  G_SIGNAL_RUN_LAST,
401  G_STRUCT_OFFSET (GncItemListClass, select_item),
402  NULL, NULL,
403  g_cclosure_marshal_VOID__POINTER,
404  G_TYPE_NONE, 1,
405  G_TYPE_POINTER);
406 
407  gnc_item_list_signals[CHANGE_ITEM] =
408  g_signal_new ("change_item",
409  G_OBJECT_CLASS_TYPE (object_class),
410  G_SIGNAL_RUN_LAST,
411  G_STRUCT_OFFSET (GncItemListClass, change_item),
412  NULL, NULL,
413  g_cclosure_marshal_VOID__POINTER,
414  G_TYPE_NONE, 1,
415  G_TYPE_POINTER);
416 
417  gnc_item_list_signals[ACTIVATE_ITEM] =
418  g_signal_new ("activate_item",
419  G_OBJECT_CLASS_TYPE (object_class),
420  G_SIGNAL_RUN_LAST,
421  G_STRUCT_OFFSET (GncItemListClass, activate_item),
422  NULL, NULL,
423  g_cclosure_marshal_VOID__POINTER,
424  G_TYPE_NONE, 1,
425  G_TYPE_POINTER);
426 
427  item_list_class->select_item = NULL;
428  item_list_class->change_item = NULL;
429  item_list_class->activate_item = NULL;
430 }
431 
432 
433 GType
434 gnc_item_list_get_type (void)
435 {
436  static GType gnc_item_list_type = 0;
437 
438  if (gnc_item_list_type == 0)
439  {
440  static const GTypeInfo gnc_item_list_info =
441  {
442  sizeof (GncItemListClass),
443  NULL,
444  NULL,
445  (GClassInitFunc) gnc_item_list_class_init,
446  NULL,
447  NULL,
448  sizeof (GncItemList),
449  0,
450  (GInstanceInitFunc) gnc_item_list_init
451  };
452 
453  gnc_item_list_type =
454  g_type_register_static (GTK_TYPE_EVENT_BOX, "GncItemList",
455  &gnc_item_list_info, 0);
456  }
457 
458  return gnc_item_list_type;
459 }
460 
461 
462 static void
463 tree_view_selection_changed (GtkTreeSelection* selection,
464  gpointer data)
465 {
466  GncItemList* item_list = GNC_ITEM_LIST (data);
467  GtkTreeModel* model;
468  GtkTreeIter iter;
469  char* string;
470 
471  g_return_if_fail (data);
472  g_return_if_fail (selection);
473 
474  if (!gtk_tree_selection_get_selected (selection, &model, &iter))
475  return;
476 
477  gtk_tree_model_get (model, &iter, 0, &string, -1);
478 
479  g_signal_emit (G_OBJECT (item_list), gnc_item_list_signals[CHANGE_ITEM], 0,
480  string);
481 
482  g_free (string);
483 }
484 
485 
486 gint
487 gnc_item_list_get_cell_height (GncItemList *item_list)
488 {
489 
490  gint min_height, nat_height;
491  gtk_cell_renderer_get_preferred_height (item_list->renderer,
492  GTK_WIDGET(item_list->tree_view),
493  &min_height,
494  &nat_height);
495 
496  return min_height;
497 }
498 
499 
500 GtkWidget*
501 gnc_item_list_new (GtkListStore* list_store)
502 {
503  GtkWidget* tree_view;
504  GtkTreeViewColumn* column;
505 
506  GncItemList* item_list =
507  GNC_ITEM_LIST (g_object_new (GNC_TYPE_ITEM_LIST,
508  NULL));
509 
510  item_list->scrollwin =
511  GTK_SCROLLED_WINDOW (gtk_scrolled_window_new(NULL, NULL));
512  gtk_container_add (GTK_CONTAINER (item_list),
513  GTK_WIDGET (item_list->scrollwin));
514 
515  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (item_list->scrollwin),
516  GTK_POLICY_AUTOMATIC,
517  GTK_POLICY_AUTOMATIC);
518 
519  if (NULL == list_store)
520  list_store = gtk_list_store_new (1, G_TYPE_STRING);
521  else
522  g_object_ref (list_store);
523  tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (list_store));
524  g_object_unref (list_store);
525 
526  gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tree_view), FALSE);
527  gtk_tree_selection_set_mode (gtk_tree_view_get_selection (GTK_TREE_VIEW (
528  tree_view)),
529  GTK_SELECTION_BROWSE);
530  gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (list_store),
531  0, GTK_SORT_ASCENDING);
532 
533  item_list->renderer = gtk_cell_renderer_text_new();
534  column = gtk_tree_view_column_new_with_attributes (_ ("List"),
535  item_list->renderer,
536  "text", 0,
537  NULL);
538  gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
539 
540  gtk_container_add (GTK_CONTAINER (item_list->scrollwin), tree_view);
541 
542  item_list->tree_view = GTK_TREE_VIEW (tree_view);
543  item_list->list_store = list_store;
544 
545  g_signal_connect (G_OBJECT (tree_view), "button_press_event",
546  G_CALLBACK (gnc_item_list_button_event), item_list);
547 
548  g_signal_connect (G_OBJECT (tree_view), "key_press_event",
549  G_CALLBACK (gnc_item_list_key_event), item_list);
550 
551  g_signal_connect (G_OBJECT (gtk_tree_view_get_selection (
552  GTK_TREE_VIEW (tree_view))), "changed",
553  G_CALLBACK (tree_view_selection_changed), item_list);
554 
555  return GTK_WIDGET (item_list);
556 }
Public Declarations for GncItemList class.
All type declarations for the whole Gnucash engine.
char * gnc_item_list_get_selection(GncItemList *item_list)
Retrieve the selected string from the item_list&#39;s active GtkListStore.