GnuCash c935c2f+
Loading...
Searching...
No Matches
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 */
39enum
40{
41 SELECT_ITEM,
42 CHANGE_ITEM,
43 ACTIVATE_ITEM,
44 LAST_SIGNAL
45};
46
47static guint gnc_item_list_signals[LAST_SIGNAL];
48
49gboolean _gnc_item_find_selection (GtkTreeModel* model, GtkTreePath* path,
50 GtkTreeIter* iter, gpointer data);
51
52G_DEFINE_TYPE (GncItemList, gnc_item_list, GTK_TYPE_EVENT_BOX);
53
54gint
55gnc_item_list_num_entries (GncItemList* item_list)
56{
57 GtkTreeModel* model;
58
59 g_return_val_if_fail (item_list != NULL, 0);
60 g_return_val_if_fail (IS_GNC_ITEM_LIST (item_list), 0);
61
62 model = gnc_item_list_using_temp (item_list) ?
63 GTK_TREE_MODEL (item_list->temp_store) :
64 GTK_TREE_MODEL (item_list->list_store);
65 return gtk_tree_model_iter_n_children (model, NULL);
66}
67
68
69void
70gnc_item_list_clear (GncItemList* item_list)
71{
72 GtkTreeSelection* selection;
73
74 g_return_if_fail (IS_GNC_ITEM_LIST (item_list));
75 g_return_if_fail (item_list->list_store != NULL);
76
77 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (item_list->tree_view));
78
79 g_signal_handlers_block_matched (G_OBJECT (selection), G_SIGNAL_MATCH_DATA,
80 0, 0, NULL, NULL, item_list);
81 gtk_list_store_clear (item_list->list_store);
82 g_signal_handlers_unblock_matched (G_OBJECT (selection), G_SIGNAL_MATCH_DATA,
83 0, 0, NULL, NULL, item_list);
84}
85
86
87void
88gnc_item_list_append (GncItemList* item_list, const char* string)
89{
90 GtkTreeIter iter;
91
92 g_return_if_fail (IS_GNC_ITEM_LIST (item_list));
93 g_return_if_fail (item_list->list_store != NULL);
94 g_return_if_fail (string != NULL);
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
100void
101gnc_item_list_set_sort_column (GncItemList* item_list, gint column_id)
102{
103 g_return_if_fail (IS_GNC_ITEM_LIST (item_list));
104
105 gtk_tree_sortable_set_sort_column_id
106 (GTK_TREE_SORTABLE (item_list->list_store),
107 column_id,
108 GTK_SORT_ASCENDING);
109}
110
111
112typedef struct _findSelectionData
113{
114 GncItemList* item_list;
115 const char* string_to_find;
116 GtkTreePath* found_path;
118
119gboolean
120_gnc_item_find_selection (GtkTreeModel* model, GtkTreePath* path,
121 GtkTreeIter* iter, gpointer data)
122{
123 FindSelectionData* to_find = (FindSelectionData*)data;
124 gchar* iterStr;
125 gboolean found;
126
127 gtk_tree_model_get (model, iter, 0, &iterStr, -1);
128 found = g_strcmp0 (to_find->string_to_find, iterStr) == 0;
129 g_free (iterStr);
130 if (found)
131 {
132 to_find->found_path = gtk_tree_path_copy (path);
133 return TRUE;
134 }
135 return FALSE;
136}
137
138gboolean
139gnc_item_in_list (GncItemList* item_list, const char* string)
140{
141 FindSelectionData* to_find_data;
142 gboolean result;
143
144 g_return_val_if_fail (item_list != NULL, FALSE);
145 g_return_val_if_fail (IS_GNC_ITEM_LIST (item_list), FALSE);
146
147 to_find_data = (FindSelectionData*)g_new0 (FindSelectionData, 1);
148 to_find_data->item_list = item_list;
149 to_find_data->string_to_find = string;
150
151 gtk_tree_model_foreach (GTK_TREE_MODEL (item_list->list_store),
152 _gnc_item_find_selection,
153 to_find_data);
154
155 result = (to_find_data->found_path != NULL);
156 g_free (to_find_data);
157 return result;
158}
159
160
161void
162gnc_item_list_select (GncItemList* item_list, const char* string)
163{
164 GtkTreeSelection* tree_sel = NULL;
165 FindSelectionData* to_find_data;
166
167 g_return_if_fail (item_list != NULL);
168 g_return_if_fail (IS_GNC_ITEM_LIST (item_list));
169
170 tree_sel = gtk_tree_view_get_selection (item_list->tree_view);
171
172 if (string == NULL)
173 {
174 gtk_tree_selection_unselect_all (tree_sel);
175 return;
176 }
177
178 to_find_data = (FindSelectionData*)g_new0 (FindSelectionData, 1);
179 to_find_data->item_list = item_list;
180 to_find_data->string_to_find = string;
181
182 gtk_tree_model_foreach (GTK_TREE_MODEL (item_list->list_store),
183 _gnc_item_find_selection,
184 to_find_data);
185
186 if (to_find_data->found_path != NULL)
187 {
188 gtk_tree_view_set_cursor (item_list->tree_view, to_find_data->found_path, NULL,
189 FALSE);
190 gtk_tree_path_free (to_find_data->found_path);
191
192 gnc_item_list_show_selected (item_list);
193 }
194
195 g_free (to_find_data);
196}
197
198
199char*
201{
202 GtkTreeIter iter;
203 GtkTreeModel* model;
204 gchar* string;
205
206 GtkTreeSelection *selection =
207 gtk_tree_view_get_selection (item_list->tree_view);
208 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
209 return NULL;
210
211 gtk_tree_model_get (model, &iter, 0, &string, -1);
212 return string;
213}
214
215
216void
217gnc_item_list_show_selected (GncItemList* item_list)
218{
219 GtkTreeSelection* selection;
220 GtkTreeIter iter;
221 GtkTreeModel* model;
222
223 g_return_if_fail (item_list != NULL);
224 g_return_if_fail (IS_GNC_ITEM_LIST (item_list));
225
226 selection = gtk_tree_view_get_selection (item_list->tree_view);
227
228 if (gtk_tree_selection_get_selected (selection, &model, &iter))
229 {
230 GtkTreePath* path = gtk_tree_model_get_path (model, &iter);
231
232 gtk_tree_view_scroll_to_cell (item_list->tree_view,
233 path, NULL, TRUE, 0.5, 0.0);
234 gtk_tree_path_free (path);
235 }
236}
237
238int
239gnc_item_list_autosize (GncItemList* item_list)
240{
241 g_return_val_if_fail (item_list != NULL, 0);
242 g_return_val_if_fail (IS_GNC_ITEM_LIST (item_list), 0);
243
244 return 150;
245}
246
247void
248gnc_item_list_set_temp_store (GncItemList *item_list, GtkListStore *store)
249{
250
251 g_return_if_fail (item_list != 0);
252
253 item_list->temp_store = store;
254 if (store)
255 {
256 gtk_tree_view_set_model (item_list->tree_view,
257 GTK_TREE_MODEL (item_list->temp_store));
258 }
259 else
260 {
261 gtk_tree_view_set_model (item_list->tree_view,
262 GTK_TREE_MODEL (item_list->list_store));
263 item_list->temp_store = NULL;
264 }
265}
266
267gboolean
268gnc_item_list_using_temp (GncItemList *item_list)
269{
270 return item_list && item_list->temp_store;
271}
272
273GtkListStore *
274gnc_item_list_disconnect_store (GncItemList *item_list)
275{
276 GtkListStore *store;
277
278 g_return_val_if_fail (item_list != NULL, NULL);
279
280 store = GTK_LIST_STORE(gtk_tree_view_get_model (item_list->tree_view));
281
282 gtk_tree_view_set_model (item_list->tree_view, NULL);
283
284 return store;
285}
286
287void
288gnc_item_list_connect_store (GncItemList *item_list, GtkListStore *list_store)
289{
290 g_return_if_fail (item_list != 0);
291
292 gtk_tree_view_set_model (item_list->tree_view,
293 GTK_TREE_MODEL (list_store));
294}
295
296static void
297gnc_item_list_init (GncItemList* item_list)
298{
299 item_list->scrollwin = NULL;
300 item_list->tree_view = NULL;
301 item_list->list_store = NULL;
302 item_list->temp_store = NULL;
303 item_list->cell_height = 0;
304}
305
306
307static gboolean
308gnc_item_list_button_event (GtkWidget* widget, GdkEventButton* event,
309 gpointer data)
310{
311 GncItemList* item_list;
312 GtkTreeIter iter;
313 GtkTreePath* path;
314 GtkTreeModel* model;
315 gchar* string;
316 gboolean success;
317
318 g_return_val_if_fail (IS_GNC_ITEM_LIST (data), FALSE);
319
320 item_list = GNC_ITEM_LIST (data);
321
322 switch (event->button)
323 {
324 case 1:
325 if (!gtk_tree_view_get_path_at_pos (item_list->tree_view,
326 event->x,
327 event->y,
328 &path,
329 NULL,
330 NULL,
331 NULL))
332 {
333 return FALSE;
334 }
335
336 gtk_tree_view_set_cursor (item_list->tree_view, path, NULL, FALSE);
337
338 model = GTK_TREE_MODEL (item_list->list_store);
339 success = gtk_tree_model_get_iter (model, &iter, path);
340
341 gtk_tree_path_free (path);
342
343 if (!success)
344 return FALSE;
345
346 gtk_tree_model_get (model, &iter, 0, &string, -1);
347
348 g_signal_emit (G_OBJECT (item_list),
349 gnc_item_list_signals[ACTIVATE_ITEM],
350 0,
351 string);
352 g_free (string);
353 return TRUE;
354 default:
355 return FALSE;
356 }
357
358 return FALSE;
359}
360
361static gboolean
362gnc_item_list_key_event (GtkWidget* widget, GdkEventKey* event, gpointer data)
363{
364 GncItemList* item_list = GNC_ITEM_LIST (data);
365 gchar* string;
366 gboolean retval;
367
368 switch (event->keyval)
369 {
370 case GDK_KEY_Return:
371 string = gnc_item_list_get_selection (item_list);
372 if (!string) // Nothing selected, might be new value
373 break; // Let the sheet deal with it.
374 g_signal_emit (G_OBJECT (item_list),
375 gnc_item_list_signals[ACTIVATE_ITEM],
376 0,
377 string);
378 g_signal_emit (G_OBJECT (item_list), gnc_item_list_signals[CHANGE_ITEM], 0,
379 string);
380 g_free (string);
381 return TRUE;
382
383 case GDK_KEY_Page_Up:
384 case GDK_KEY_Page_Down:
385 case GDK_KEY_Up:
386 case GDK_KEY_Down:
387 case GDK_KEY_KP_Up:
388 case GDK_KEY_KP_Down:
389 case GDK_KEY_KP_Page_Up:
390 case GDK_KEY_KP_Page_Down:
391 /* These go to the clist */
392 return FALSE;
393 }
394
395 /* These go to the sheet */
396 g_signal_stop_emission_by_name (G_OBJECT (widget), "key_press_event");
397
398 g_signal_emit_by_name (G_OBJECT (item_list), "key_press_event", event,
399 &retval);
400
401 return retval;
402}
403
404
405static void
406gnc_item_list_class_init (GncItemListClass* item_list_class)
407{
408 GObjectClass* object_class = G_OBJECT_CLASS (item_list_class);
409
410 gtk_widget_class_set_css_name (GTK_WIDGET_CLASS(item_list_class), "gnc-id-sheet-list");
411
412 gnc_item_list_signals[SELECT_ITEM] =
413 g_signal_new ("select_item",
414 G_OBJECT_CLASS_TYPE (object_class),
415 G_SIGNAL_RUN_LAST,
416 G_STRUCT_OFFSET (GncItemListClass, select_item),
417 NULL, NULL,
418 g_cclosure_marshal_VOID__POINTER,
419 G_TYPE_NONE, 1,
420 G_TYPE_POINTER);
421
422 gnc_item_list_signals[CHANGE_ITEM] =
423 g_signal_new ("change_item",
424 G_OBJECT_CLASS_TYPE (object_class),
425 G_SIGNAL_RUN_LAST,
426 G_STRUCT_OFFSET (GncItemListClass, change_item),
427 NULL, NULL,
428 g_cclosure_marshal_VOID__POINTER,
429 G_TYPE_NONE, 1,
430 G_TYPE_POINTER);
431
432 gnc_item_list_signals[ACTIVATE_ITEM] =
433 g_signal_new ("activate_item",
434 G_OBJECT_CLASS_TYPE (object_class),
435 G_SIGNAL_RUN_LAST,
436 G_STRUCT_OFFSET (GncItemListClass, activate_item),
437 NULL, NULL,
438 g_cclosure_marshal_VOID__POINTER,
439 G_TYPE_NONE, 1,
440 G_TYPE_POINTER);
441
442 item_list_class->select_item = NULL;
443 item_list_class->change_item = NULL;
444 item_list_class->activate_item = NULL;
445}
446
447static void
448tree_view_selection_changed (GtkTreeSelection* selection,
449 gpointer data)
450{
451 GncItemList* item_list = GNC_ITEM_LIST (data);
452 GtkTreeModel* model;
453 GtkTreeIter iter;
454 char* string;
455
456 g_return_if_fail (data);
457 g_return_if_fail (selection);
458
459 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
460 return;
461
462 gtk_tree_model_get (model, &iter, 0, &string, -1);
463
464 g_signal_emit (G_OBJECT (item_list), gnc_item_list_signals[CHANGE_ITEM], 0,
465 string);
466
467 g_free (string);
468}
469
470
471static gint
472gnc_item_list_get_cell_height (GncItemList *item_list)
473{
474
475 gint min_height, nat_height;
476 gtk_cell_renderer_get_preferred_height (item_list->renderer,
477 GTK_WIDGET(item_list->tree_view),
478 &min_height,
479 &nat_height);
480
481 return min_height;
482}
483
484gint
485gnc_item_list_get_popup_height (GncItemList *item_list)
486{
487 GtkWidget *hsbar = gtk_scrolled_window_get_hscrollbar (GTK_SCROLLED_WINDOW(item_list->scrollwin));
488 GtkStyleContext *context = gtk_widget_get_style_context (hsbar);
489 /* Note: gtk_scrolled_window_get_overlay_scrolling (scrollwin) always returns
490 TRUE so look for style class "overlay-indicator" on the scrollbar. */
491 gboolean overlay = gtk_style_context_has_class (context, "overlay-indicator");
492 int count = gnc_item_list_num_entries (item_list);
493 int height = count * (gnc_item_list_get_cell_height (item_list) + 2);
494
495 if (!overlay)
496 {
497 gint minh, nath;
498 gtk_widget_get_preferred_height (hsbar, &minh, &nath);
499 height = height + minh;
500 }
501 return height;
502}
503
504
505GtkWidget*
506gnc_item_list_new (GtkListStore* list_store)
507{
508 GtkWidget* tree_view;
509 GtkTreeViewColumn* column;
510
511 GncItemList* item_list =
512 GNC_ITEM_LIST (g_object_new (GNC_TYPE_ITEM_LIST,
513 NULL));
514
515 item_list->scrollwin =
516 GTK_SCROLLED_WINDOW (gtk_scrolled_window_new(NULL, NULL));
517 gtk_container_add (GTK_CONTAINER (item_list),
518 GTK_WIDGET (item_list->scrollwin));
519
520 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (item_list->scrollwin),
521 GTK_POLICY_AUTOMATIC,
522 GTK_POLICY_AUTOMATIC);
523
524 if (NULL == list_store)
525 list_store = gtk_list_store_new (1, G_TYPE_STRING);
526 else
527 g_object_ref (list_store);
528 tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (list_store));
529 g_object_unref (list_store);
530
531 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tree_view), FALSE);
532 gtk_tree_selection_set_mode (
533 gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view)),
534 GTK_SELECTION_BROWSE);
535 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (list_store),
536 0, GTK_SORT_ASCENDING);
537
538 item_list->renderer = gtk_cell_renderer_text_new();
539 column = gtk_tree_view_column_new_with_attributes (_ ("List"),
540 item_list->renderer,
541 "text", 0,
542 NULL);
543 gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
544
545 gtk_container_add (GTK_CONTAINER (item_list->scrollwin), tree_view);
546
547 item_list->tree_view = GTK_TREE_VIEW (tree_view);
548 item_list->list_store = list_store;
549
550 g_signal_connect (G_OBJECT (tree_view), "button_press_event",
551 G_CALLBACK (gnc_item_list_button_event), item_list);
552
553 g_signal_connect (G_OBJECT (tree_view), "key_press_event",
554 G_CALLBACK (gnc_item_list_key_event), item_list);
555
556 g_signal_connect (G_OBJECT (gtk_tree_view_get_selection (
557 GTK_TREE_VIEW (tree_view))), "changed",
558 G_CALLBACK (tree_view_selection_changed), item_list);
559
560 return GTK_WIDGET (item_list);
561}
All type declarations for the whole Gnucash engine.
Public Declarations for GncItemList class.
char * gnc_item_list_get_selection(GncItemList *item_list)
Retrieve the selected string from the item_list's active GtkListStore.