GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
gnc-account-sel.c
1 
26 #include "config.h"
27 
28 #include <gtk/gtk.h>
29 #include <glib/gi18n.h>
30 
31 #include "dialog-account.h"
32 #include "gnc-account-sel.h"
33 #include "gnc-commodity.h"
34 #include "gnc-gtk-utils.h"
35 #include "gnc-ui-util.h"
36 #include "qof.h"
37 #include "gnc-session.h"
38 
39 #define ACCT_DATA_TAG "gnc-account-sel_acct"
40 
41 /* Signal codes */
42 enum
43 {
44  ACCOUNT_SEL_CHANGED,
45  LAST_SIGNAL
46 };
47 
48 enum account_cols
49 {
50  ACCT_COL_NAME = 0,
51  ACCT_COL_PTR,
52  NUM_ACCT_COLS
53 };
54 
55 static guint account_sel_signals [LAST_SIGNAL] = { 0 };
56 
57 static void gnc_account_sel_init (GNCAccountSel *gas);
58 static void gnc_account_sel_class_init (GNCAccountSelClass *klass);
59 static void gnc_account_sel_finalize (GObject *object);
60 static void gnc_account_sel_dispose (GObject *object);
61 
62 static void gas_filter_accounts (gpointer data, gpointer user_data);
63 
64 static void gas_populate_list (GNCAccountSel *gas);
65 
66 static void gas_new_account_click (GtkButton *b, gpointer ud);
67 
68 static GtkHBox *parent_class;
69 
70 GType
71 gnc_account_sel_get_type (void)
72 {
73  static GType account_sel_type = 0;
74 
75  if (account_sel_type == 0)
76  {
77  GTypeInfo account_sel_info =
78  {
79  sizeof (GNCAccountSelClass),
80  NULL,
81  NULL,
82  (GClassInitFunc) gnc_account_sel_class_init,
83  NULL,
84  NULL,
85  sizeof (GNCAccountSel),
86  0,
87  (GInstanceInitFunc) gnc_account_sel_init
88  };
89 
90  account_sel_type = g_type_register_static (GTK_TYPE_HBOX,
91  "GNCAccountSel",
92  &account_sel_info, 0);
93  }
94 
95  return account_sel_type;
96 }
97 
98 static
99 void
100 gnc_account_sel_event_cb( QofInstance *entity,
101  QofEventId event_type,
102  gpointer user_data,
103  gpointer event_data )
104 {
105  if ( ! ( event_type == QOF_EVENT_CREATE
106  || event_type == QOF_EVENT_MODIFY
107  || event_type == QOF_EVENT_DESTROY )
108  || !GNC_IS_ACCOUNT(entity) )
109  {
110  return;
111  }
112  gas_populate_list( (GNCAccountSel*)user_data );
113 }
114 
115 static
116 void
117 gnc_account_sel_class_init (GNCAccountSelClass *klass)
118 {
119  GObjectClass *object_class = G_OBJECT_CLASS (klass);
120 
121  parent_class = g_type_class_peek_parent (klass);
122 
123  object_class->finalize = gnc_account_sel_finalize;
124  object_class->dispose = gnc_account_sel_dispose;
125 
126  account_sel_signals [ACCOUNT_SEL_CHANGED] =
127  g_signal_new ("account_sel_changed",
128  G_OBJECT_CLASS_TYPE (object_class),
129  G_SIGNAL_RUN_FIRST,
130  G_STRUCT_OFFSET (GNCAccountSelClass, account_sel_changed),
131  NULL,
132  NULL,
133  g_cclosure_marshal_VOID__VOID,
134  G_TYPE_NONE,
135  0);
136 }
137 
138 static void
139 combo_changed_cb(GNCAccountSel *gas, gpointer combo)
140 {
141  g_signal_emit_by_name(gas, "account_sel_changed");
142 }
143 
144 static void
145 gnc_account_sel_init (GNCAccountSel *gas)
146 {
147  GtkWidget *widget;
148 
149  gas->initDone = FALSE;
150  gas->acctTypeFilters = FALSE;
151  gas->newAccountButton = NULL;
152 
153  g_object_set(gas, "spacing", 2, (gchar*)NULL);
154 
155  gas->store = gtk_list_store_new(NUM_ACCT_COLS, G_TYPE_STRING, G_TYPE_POINTER);
156  widget = gtk_combo_box_new_with_model_and_entry(GTK_TREE_MODEL(gas->store));
157  gas->combo = GTK_COMBO_BOX(widget);
158  gtk_combo_box_set_entry_text_column(GTK_COMBO_BOX(widget), ACCT_COL_NAME);
159  g_object_unref(gas->store);
160  g_signal_connect_swapped(gas->combo, "changed",
161  G_CALLBACK(combo_changed_cb), gas);
162  gtk_container_add( GTK_CONTAINER(gas), widget );
163 
164  /* Add completion. */
165  gnc_cbwe_require_list_item(GTK_COMBO_BOX(widget));
166 
167  /* Get the accounts, place into combo list */
168  gas_populate_list( gas );
169 
170  gas->eventHandlerId =
171  qof_event_register_handler( gnc_account_sel_event_cb, gas );
172 
173  gas->initDone = TRUE;
174 }
175 
176 typedef struct
177 {
178  GNCAccountSel *gas;
179  GList **outList;
181 
182 static
183 void
184 gas_populate_list( GNCAccountSel *gas )
185 {
186  account_filter_data atnd;
187  Account *root;
188  Account *acc;
189  GtkTreeIter iter;
190  GtkEntry *entry;
191  gint i, active = -1;
192  GList *accts, *ptr, *filteredAccts;
193  gchar *currentSel, *name;
194 
195  entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(gas->combo)));
196  currentSel = gtk_editable_get_chars(
197  GTK_EDITABLE(entry), 0, -1 );
198 
199  g_signal_handlers_block_by_func( gas->combo, combo_changed_cb , gas );
200 
201  root = gnc_book_get_root_account( gnc_get_current_book() );
202  accts = gnc_account_get_descendants_sorted( root );
203 
204  filteredAccts = NULL;
205  atnd.gas = gas;
206  atnd.outList = &filteredAccts;
207 
208  g_list_foreach( accts, gas_filter_accounts, (gpointer)&atnd );
209  g_list_free( accts );
210 
211  gtk_list_store_clear(gas->store);
212  for (ptr = filteredAccts, i = 0; ptr; ptr = g_list_next(ptr), i++)
213  {
214  acc = ptr->data;
215  name = gnc_account_get_full_name(acc);
216  gtk_list_store_append(gas->store, &iter);
217  gtk_list_store_set(gas->store, &iter,
218  ACCT_COL_NAME, name,
219  ACCT_COL_PTR, acc,
220  -1);
221  if (g_utf8_collate(name, currentSel) == 0)
222  {
223  active = i;
224  g_free(name);
225  }
226  }
227 
228  /* If the account which was in the text box before still exists, then
229  * reset to it. */
230  if (active != -1)
231  gtk_combo_box_set_active(GTK_COMBO_BOX(gas->combo), active);
232 
233  g_signal_handlers_unblock_by_func( gas->combo, combo_changed_cb , gas );
234 
235  g_list_free( filteredAccts );
236  if ( currentSel )
237  {
238  g_free( currentSel );
239  }
240 }
241 
242 static
243 void
244 gas_filter_accounts( gpointer data, gpointer user_data )
245 {
246  account_filter_data *atnd;
247  Account *a;
248 
249  atnd = (account_filter_data*)user_data;
250  a = (Account*)data;
251  /* Filter as we've been configured to do. */
252  if ( atnd->gas->acctTypeFilters )
253  {
254  /* g_list_find is the poor-mans '(member ...)', especially
255  * easy when the data pointers in the list are just casted
256  * account type identifiers. */
257  if ( g_list_find( atnd->gas->acctTypeFilters,
258  GINT_TO_POINTER(xaccAccountGetType( a )) )
259  == NULL )
260  {
261  return;
262  }
263  }
264 
265  if ( atnd->gas->acctCommodityFilters )
266  {
267  if ( g_list_find_custom( atnd->gas->acctCommodityFilters,
268  GINT_TO_POINTER(xaccAccountGetCommodity( a )),
270  == NULL )
271  {
272  return;
273  }
274  }
275 
276 
277  *atnd->outList = g_list_append( *atnd->outList, a );
278 }
279 
280 
281 GtkWidget *
282 gnc_account_sel_new (void)
283 {
284  GNCAccountSel *gas;
285 
286  gas = g_object_new (GNC_TYPE_ACCOUNT_SEL, NULL);
287 
288  return GTK_WIDGET (gas);
289 }
290 
291 typedef struct
292 {
293  GNCAccountSel *gas;
294  Account *acct;
295 } gas_find_data;
296 
297 static
298 gboolean
299 gnc_account_sel_find_account (GtkTreeModel *model,
300  GtkTreePath *path,
301  GtkTreeIter *iter,
302  gas_find_data *data)
303 {
304  Account *model_acc;
305 
306  gtk_tree_model_get(model, iter, ACCT_COL_PTR, &model_acc, -1);
307  if (data->acct != model_acc)
308  return FALSE;
309 
310  gtk_combo_box_set_active_iter(GTK_COMBO_BOX(data->gas->combo), iter);
311  return TRUE;
312 }
313 void
314 gnc_account_sel_set_account( GNCAccountSel *gas, Account *acct, gboolean set_default_acct )
315 {
316  gas_find_data data;
317 
318  if (set_default_acct)
319  {
320  gtk_combo_box_set_active(GTK_COMBO_BOX(gas->combo), 0);
321  if ( !acct )
322  return;
323  }
324  else
325  {
326  gtk_combo_box_set_active( GTK_COMBO_BOX(gas->combo), -1 );
327  if ( !acct )
328  {
329  GtkEntry *entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(gas->combo)));
330  gtk_editable_delete_text(GTK_EDITABLE(entry), 0, -1);
331  return;
332  }
333  }
334  data.gas = gas;
335  data.acct = acct;
336  gtk_tree_model_foreach(GTK_TREE_MODEL(gas->store),
337  (GtkTreeModelForeachFunc)gnc_account_sel_find_account,
338  &data);
339 }
340 
341 Account*
342 gnc_account_sel_get_account( GNCAccountSel *gas )
343 {
344  GtkTreeIter iter;
345  Account *acc;
346 
347  if (!gtk_combo_box_get_active_iter(GTK_COMBO_BOX(gas->combo), &iter))
348  return NULL;
349 
350  gtk_tree_model_get(GTK_TREE_MODEL(gas->store), &iter,
351  ACCT_COL_PTR, &acc,
352  -1);
353  return acc;
354 }
355 
356 
357 void
358 gnc_account_sel_set_acct_filters( GNCAccountSel *gas, GList *typeFilters, GList *commodityFilters )
359 {
360 
361  if ( gas->acctTypeFilters != NULL )
362  {
363  g_list_free( gas->acctTypeFilters );
364  gas->acctTypeFilters = NULL;
365  }
366 
367  if ( gas->acctCommodityFilters != NULL)
368  {
369  g_list_free( gas->acctCommodityFilters );
370  gas->acctCommodityFilters = NULL;
371  }
372 
373  /* If both filters are null, then no filters exist. */
374  if (( ! typeFilters ) && ( ! commodityFilters))
375  {
376  return;
377  }
378 
379  /* This works because the GNCAccountTypes in the list are
380  * ints-casted-as-pointers. */
381  if (typeFilters)
382  {
383  gas->acctTypeFilters = g_list_copy( typeFilters );
384  }
385 
386  /* Save the commodity filter list */
387  if (commodityFilters)
388  {
389  gas->acctCommodityFilters = g_list_copy(commodityFilters);
390  }
391 
392  gas_populate_list( gas );
393 }
394 
395 static void
396 gnc_account_sel_finalize (GObject *object)
397 {
398  GNCAccountSel *gas;
399 
400  g_return_if_fail (object != NULL);
401  g_return_if_fail (GNC_IS_ACCOUNT_SEL (object));
402 
403  gas = GNC_ACCOUNT_SEL (object);
404 
405  if (gas->acctTypeFilters)
406  {
407  g_list_free (gas->acctTypeFilters);
408  }
409 
410  G_OBJECT_CLASS (parent_class)->finalize (object);
411 }
412 
413 static void
414 gnc_account_sel_dispose (GObject *object)
415 {
416  GNCAccountSel *gas;
417 
418  g_return_if_fail (object != NULL);
419  g_return_if_fail (GNC_IS_ACCOUNT_SEL (object));
420 
421  gas = GNC_ACCOUNT_SEL (object);
422 
423  if (gas->eventHandlerId)
424  {
425  qof_event_unregister_handler (gas->eventHandlerId);
426  gas->eventHandlerId = 0;
427  }
428 
429  G_OBJECT_CLASS (parent_class)->dispose (object);
430 }
431 
432 void
433 gnc_account_sel_set_new_account_ability( GNCAccountSel *gas,
434  gboolean state )
435 {
436  g_return_if_fail (gas != NULL);
437 
438  if ( state == (gas->newAccountButton != NULL) )
439  {
440  /* We're already in that state; don't do anything. */
441  return;
442  }
443 
444  if ( gas->newAccountButton )
445  {
446  g_assert( state == TRUE );
447  /* destroy the existing button. */
448  gtk_container_remove( GTK_CONTAINER(gas),
449  gas->newAccountButton );
450  gtk_widget_destroy( gas->newAccountButton );
451  gas->newAccountButton = NULL;
452  return;
453  }
454 
455  /* create the button. */
456  gas->newAccountButton = gtk_button_new_with_label( _("New...") );
457  g_signal_connect( gas->newAccountButton,
458  "clicked",
459  G_CALLBACK( gas_new_account_click ),
460  gas );
461  gtk_box_pack_start( GTK_BOX(gas), gas->newAccountButton,
462  FALSE, FALSE, 0 );
463 }
464 
465 void
466 gnc_account_sel_set_new_account_modal( GNCAccountSel *gas,
467  gboolean state )
468 {
469  g_return_if_fail (gas != NULL);
470  gas->isModal = state;
471 }
472 
473 static void
474 gas_new_account_click( GtkButton *b, gpointer ud )
475 {
476  GNCAccountSel *gas = (GNCAccountSel*)ud;
477  if (gas->isModal)
479  gas->acctTypeFilters );
480  else
481  gnc_ui_new_account_with_types( gnc_get_current_book(), gas->acctTypeFilters );
482 }
483 
484 gint
485 gnc_account_sel_get_num_account( GNCAccountSel *gas )
486 {
487  if (NULL == gas)
488  return 0;
489  return gtk_tree_model_iter_n_children( GTK_TREE_MODEL(gas->store), NULL );
490 }
491 
492 void
493 gnc_account_sel_purge_account( GNCAccountSel *gas,
494  Account *target,
495  gboolean recursive)
496 {
497  GtkTreeModel *model = GTK_TREE_MODEL(gas->store);
498  GtkTreeIter iter;
499  Account *acc;
500  gboolean more;
501 
502  if (!gtk_tree_model_get_iter_first(model, &iter))
503  return;
504 
505  if (!recursive)
506  {
507  do
508  {
509  gtk_tree_model_get(model, &iter, ACCT_COL_PTR, &acc, -1);
510  if (acc == target)
511  {
512  gtk_list_store_remove(gas->store, &iter);
513  break;
514  }
515  }
516  while (gtk_tree_model_iter_next(model, &iter));
517  }
518  else
519  {
520  do
521  {
522  gtk_tree_model_get(model, &iter, ACCT_COL_PTR, &acc, -1);
523  while (acc)
524  {
525  if (acc == target)
526  break;
527  acc = gnc_account_get_parent(acc);
528  }
529 
530  if (acc == target)
531  more = gtk_list_store_remove(gas->store, &iter);
532  else
533  more = gtk_tree_model_iter_next(model, &iter);
534  }
535  while (more);
536  }
537 
538  gtk_combo_box_set_active(GTK_COMBO_BOX(gas->combo), 0);
539 }
540 
541 
Account * gnc_account_get_parent(const Account *acc)
Definition: Account.c:2623
GList * gnc_account_get_descendants_sorted(const Account *account)
Definition: Account.c:2777
Dialog for create/edit an account.
utility functions for the GnuCash UI
GNCAccountType xaccAccountGetType(const Account *acc)
Definition: Account.c:3009
gtk helper routines.
int gnc_commodity_compare_void(const void *a, const void *b)
Account * gnc_ui_new_accounts_from_name_window_with_types(const char *name, GList *valid_types)
gint qof_event_register_handler(QofEventHandler handler, gpointer handler_data)
Register a handler for events.
gchar * gnc_account_get_full_name(const Account *account)
Definition: Account.c:3038
gint QofEventId
Definition: qofevent.h:45
void qof_event_unregister_handler(gint handler_id)
Unregister an event handler.
void gnc_ui_new_account_with_types(QofBook *book, GList *valid_types)
gnc_commodity * xaccAccountGetCommodity(const Account *acc)
Definition: Account.c:3148
Commodity handling public routines.