GnuCash  4.12-558-g06612b8434
gnc-plugin-page-sx-list.c
1 /********************************************************************\
2  * gnc-plugin-page-sx-list.c : scheduled transaction plugin *
3  * *
4  * Copyright (C) 2006 Joshua Sled <jsled@asynchronous.org> *
5  * Copyright (C) 2011 Robert Fewell *
6  * *
7  * This program is free software; you can redistribute it and/or *
8  * modify it under the terms of version 2 and/or version 3 of the *
9  * GNU General Public License as published by the Free Software *
10  * Foundation. *
11  * *
12  * As a special exception, permission is granted to link the binary *
13  * module resultant from this code with the OpenSSL project's *
14  * "OpenSSL" library (or modified versions of it that use the same *
15  * license as the "OpenSSL" library), and distribute the linked *
16  * executable. You must obey the GNU General Public License in all *
17  * respects for all of the code used other than "OpenSSL". If you *
18  * modify this file, you may extend this exception to your version *
19  * of the file, but you are not obligated to do so. If you do not *
20  * wish to do so, delete this exception statement from your version *
21  * of this file. *
22  * *
23  * This program is distributed in the hope that it will be useful, *
24  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
25  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
26  * GNU General Public License for more details. *
27  * *
28  * You should have received a copy of the GNU General Public License*
29  * along with this program; if not, contact: *
30  * *
31  * Free Software Foundation Voice: +1-617-542-5942 *
32  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
33  * Boston, MA 02110-1301, USA gnu@gnu.org *
34 \********************************************************************/
35 
44 #include <config.h>
45 
46 #include <gtk/gtk.h>
47 #include <glib.h>
48 #include <glib/gi18n.h>
49 
50 #include <gnc-gobject-utils.h>
51 #include "SX-book.h"
52 #include "Split.h"
53 #include "Transaction.h"
54 #include "dialog-sx-editor.h"
55 #include "dialog-utils.h"
56 #include "gnc-commodity.h"
57 #include "gnc-component-manager.h"
58 #include "gnc-date.h"
59 #include "gnc-dense-cal.h"
60 #include "gnc-engine.h"
61 #include "gnc-event.h"
62 #include "gnc-glib-utils.h"
63 #include "gnc-icons.h"
64 #include "gnc-main-window.h"
65 #include "gnc-plugin-page-sx-list.h"
66 #include "gnc-session.h"
67 #include "gnc-sx-instance-dense-cal-adapter.h"
68 #include "gnc-sx-instance-model.h"
69 #include "gnc-sx-list-tree-model-adapter.h"
70 #include "gnc-tree-view-sx-list.h"
71 #include "gnc-ui-util.h"
72 #include "gnc-ui.h"
73 #include "gnc-window.h"
74 
75 #undef G_LOG_DOMAIN
76 #define G_LOG_DOMAIN "gnc.gui.plugin-page.sx-list"
77 
78 G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_GUI_SX;
79 
80 #define PLUGIN_PAGE_SX_LIST_CM_CLASS "plugin-page-sx-list"
81 #define STATE_SECTION "SX Transaction List"
82 
84 {
85  gboolean disposed;
86 
87  GtkWidget* widget;
88  gint gnc_component_id;
89 
90  GncSxInstanceDenseCalAdapter *dense_cal_model;
91  GncDenseCal* gdcal;
92 
93  GncSxInstanceModel* instances;
94  GtkTreeView* tree_view;
95  GList *selected_list;
96 
98 
99 G_DEFINE_TYPE_WITH_PRIVATE(GncPluginPageSxList, gnc_plugin_page_sx_list, GNC_TYPE_PLUGIN_PAGE)
100 
101 #define GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(o) \
102  ((GncPluginPageSxListPrivate*)gnc_plugin_page_sx_list_get_instance_private((GncPluginPageSxList*)o))
103 
104 static GObjectClass *parent_class = NULL;
105 
106 /************************************************************
107  * Prototypes *
108  ************************************************************/
109 /* Plugin Actions */
110 static void gnc_plugin_page_sx_list_class_init (GncPluginPageSxListClass *klass);
111 static void gnc_plugin_page_sx_list_init (GncPluginPageSxList *plugin_page);
112 static void gnc_plugin_page_sx_list_dispose (GObject *object);
113 static void gnc_plugin_page_sx_list_finalize (GObject *object);
114 
115 static GtkWidget *gnc_plugin_page_sx_list_create_widget (GncPluginPage *plugin_page);
116 static void gnc_plugin_page_sx_list_destroy_widget (GncPluginPage *plugin_page);
117 static void gnc_plugin_page_sx_list_save_page (GncPluginPage *plugin_page, GKeyFile *file, const gchar *group);
118 static GncPluginPage *gnc_plugin_page_sx_list_recreate_page (GtkWidget *window, GKeyFile *file, const gchar *group);
119 
120 static void gppsl_row_activated_cb (GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, gpointer user_data);
121 
122 static void gnc_plugin_page_sx_list_cmd_new (GtkAction *action, GncPluginPageSxList *page);
123 static void gnc_plugin_page_sx_list_cmd_edit (GtkAction *action, GncPluginPageSxList *page);
124 static void gnc_plugin_page_sx_list_cmd_delete (GtkAction *action, GncPluginPageSxList *page);
125 static void gnc_plugin_page_sx_list_cmd_refresh (GtkAction *action, GncPluginPageSxList *page);
126 
127 /* Command callbacks */
128 static GtkActionEntry gnc_plugin_page_sx_list_actions [] =
129 {
130  { "SxListAction", NULL, N_("_Scheduled"), NULL, NULL, NULL },
131  {
132  "SxListNewAction", GNC_ICON_NEW_ACCOUNT, N_("_New"), NULL,
133  N_("Create a new scheduled transaction"), G_CALLBACK(gnc_plugin_page_sx_list_cmd_new)
134  },
135  {
136  "SxListEditAction", GNC_ICON_EDIT_ACCOUNT, N_("_Edit"), NULL,
137  N_("Edit the selected scheduled transaction"), G_CALLBACK(gnc_plugin_page_sx_list_cmd_edit)
138  },
139  {
140  "SxListDeleteAction", GNC_ICON_DELETE_ACCOUNT, N_("_Delete"), NULL,
141  N_("Delete the selected scheduled transaction"), G_CALLBACK(gnc_plugin_page_sx_list_cmd_delete)
142  },
143 
144  /* View menu */
145 
146  {
147  "ViewRefreshAction", "view-refresh", N_("_Refresh"), "<primary>r",
148  N_("Refresh this window"), G_CALLBACK(gnc_plugin_page_sx_list_cmd_refresh)
149  },
150 };
152 static guint gnc_plugin_page_sx_list_n_actions = G_N_ELEMENTS(gnc_plugin_page_sx_list_actions);
153 
156 {
157  GncPluginPageSxList *plugin_page;
158  const GList *object = gnc_gobject_tracking_get_list (GNC_PLUGIN_PAGE_SX_LIST_NAME);
159  if (object && GNC_IS_PLUGIN_PAGE_SX_LIST (object->data))
160  plugin_page = GNC_PLUGIN_PAGE_SX_LIST (object->data);
161  else
162  {
163  plugin_page = g_object_new (GNC_TYPE_PLUGIN_PAGE_SX_LIST, NULL);
164  }
165  return GNC_PLUGIN_PAGE(plugin_page);
166 }
167 
168 
173 static gboolean
174 gnc_plugin_page_sx_list_focus_widget (GncPluginPage *sx_plugin_page)
175 {
176  if (GNC_IS_PLUGIN_PAGE_SX_LIST(sx_plugin_page))
177  {
178  GncPluginPageSxListPrivate *priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(sx_plugin_page);
179  GtkTreeView *tree_view = priv->tree_view;
180 
181  if (GTK_IS_TREE_VIEW(tree_view))
182  {
183  if (!gtk_widget_is_focus (GTK_WIDGET(tree_view)))
184  gtk_widget_grab_focus (GTK_WIDGET(tree_view));
185  }
186  }
187  return FALSE;
188 }
189 
190 static void
191 gnc_plugin_page_sx_list_class_init (GncPluginPageSxListClass *klass)
192 {
193  GObjectClass *object_class = G_OBJECT_CLASS(klass);
194  GncPluginPageClass *gnc_plugin_class = GNC_PLUGIN_PAGE_CLASS(klass);
195 
196  parent_class = g_type_class_peek_parent(klass);
197 
198  object_class->dispose = gnc_plugin_page_sx_list_dispose;
199  object_class->finalize = gnc_plugin_page_sx_list_finalize;
200 
201  gnc_plugin_class->tab_icon = GNC_ICON_ACCOUNT;
202  gnc_plugin_class->plugin_name = GNC_PLUGIN_PAGE_SX_LIST_NAME;
203  gnc_plugin_class->create_widget = gnc_plugin_page_sx_list_create_widget;
204  gnc_plugin_class->destroy_widget = gnc_plugin_page_sx_list_destroy_widget;
205  gnc_plugin_class->save_page = gnc_plugin_page_sx_list_save_page;
206  gnc_plugin_class->recreate_page = gnc_plugin_page_sx_list_recreate_page;
207  gnc_plugin_class->focus_page_function = gnc_plugin_page_sx_list_focus_widget;
208 }
209 
210 
211 static void
212 gnc_plugin_page_sx_list_init (GncPluginPageSxList *plugin_page)
213 {
214  GtkActionGroup *action_group;
215  GncPluginPage *parent;
216 
217  /* Init parent declared variables */
218  parent = GNC_PLUGIN_PAGE(plugin_page);
219  g_object_set(G_OBJECT(plugin_page),
220  "page-name", _("Scheduled Transactions"),
221  "page-uri", "default:",
222  "ui-description", "gnc-plugin-page-sx-list-ui.xml",
223  NULL);
224 
225  gnc_plugin_page_add_book (parent, gnc_get_current_book());
226  action_group =
228  "GncPluginPageSxListActions");
229  gtk_action_group_add_actions (action_group,
230  gnc_plugin_page_sx_list_actions,
231  gnc_plugin_page_sx_list_n_actions,
232  plugin_page);
233  /* gnc_plugin_init_short_names (action_group, toolbar_labels); */
234 }
235 
236 
237 static void
238 gnc_plugin_page_sx_list_dispose (GObject *object)
239 {
240  GncPluginPageSxList *page;
242 
243  page = GNC_PLUGIN_PAGE_SX_LIST(object);
244  g_return_if_fail (GNC_IS_PLUGIN_PAGE_SX_LIST(page));
245  priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(page);
246  g_return_if_fail (priv != NULL);
247 
248  g_return_if_fail (!priv->disposed);
249  priv->disposed = TRUE;
250 
251  g_object_unref (G_OBJECT(priv->dense_cal_model));
252  priv->dense_cal_model = NULL;
253  g_object_unref (GTK_WIDGET(priv->gdcal));
254  priv->gdcal = NULL;
255  g_object_unref (G_OBJECT(priv->instances));
256  priv->instances = NULL;
257 
258  G_OBJECT_CLASS(parent_class)->dispose (object);
259 }
260 
261 
262 static void
263 gnc_plugin_page_sx_list_finalize (GObject *object)
264 {
265  GncPluginPageSxList *page;
267 
268  page = GNC_PLUGIN_PAGE_SX_LIST(object);
269  g_return_if_fail (GNC_IS_PLUGIN_PAGE_SX_LIST(page));
270  priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(page);
271  g_return_if_fail (priv != NULL);
272 
273  G_OBJECT_CLASS(parent_class)->finalize (object);
274 }
275 
276 
277 /* Virtual Functions */
278 static void
279 gnc_plugin_page_sx_list_refresh_cb (GHashTable *changes, gpointer user_data)
280 {
281  GncPluginPageSxList *page = user_data;
283 
284  g_return_if_fail (GNC_IS_PLUGIN_PAGE_SX_LIST(page));
285 
286  /* We're only looking for forced updates here. */
287  if (changes)
288  return;
289 
290  priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(page);
291  gtk_widget_queue_draw (priv->widget);
292 }
293 
294 
295 static void
296 gnc_plugin_page_sx_list_close_cb (gpointer user_data)
297 {
298  GncPluginPage *plugin_page = GNC_PLUGIN_PAGE(user_data);
299  gnc_main_window_close_page (plugin_page);
300 }
301 
302 
303 static void
304 gppsl_selection_changed_cb (GtkTreeSelection *selection, gpointer user_data)
305 {
306  GncPluginPage *page;
307  GtkAction *edit_action, *delete_action;
308  gboolean selection_state = TRUE;
309 
310  page = GNC_PLUGIN_PAGE(user_data);
311  edit_action = gnc_plugin_page_get_action (page, "SxListEditAction");
312  delete_action = gnc_plugin_page_get_action (page, "SxListDeleteAction");
313  selection_state
314  = gtk_tree_selection_count_selected_rows (selection) == 0
315  ? FALSE
316  : TRUE;
317  gtk_action_set_sensitive (edit_action, selection_state);
318  gtk_action_set_sensitive (delete_action, selection_state);
319 }
320 
321 
322 static void
323 gppsl_update_selected_list (GncPluginPageSxList *page, gboolean reset, SchedXaction *sx)
324 {
325  GncPluginPageSxListPrivate *priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(page);
326 
327  if (reset && priv->selected_list)
328  {
329  g_list_free (priv->selected_list);
330  priv->selected_list = NULL;
331  }
332  if (sx)
333  priv->selected_list = g_list_prepend (priv->selected_list, sx);
334 }
335 
336 
337 static void
338 gppsl_model_populated_cb (GtkTreeModel *tree_model, GncPluginPageSxList *page)
339 {
340  GncPluginPageSxListPrivate *priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(page);
341  GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(priv->tree_view));
342  gboolean found = FALSE;
343 
344  if (priv->selected_list)
345  {
346  // walk the list to see if we can reselect the sx
347  for (GList *list = priv->selected_list; list != NULL; list = list->next)
348  {
349  SchedXaction *sx = list->data;
350  GtkTreePath *path = gtk_tree_path_new_first ();
351 
352  // loop through the model trying to find selected sx's
353  while (gnc_tree_view_path_is_valid (GNC_TREE_VIEW(priv->tree_view), path))
354  {
355  SchedXaction *sx_tmp = gnc_tree_view_sx_list_get_sx_from_path (
356  GNC_TREE_VIEW_SX_LIST(priv->tree_view), path);
357  if (sx_tmp == sx)
358  {
359  found = TRUE;
360  break;
361  }
362  gtk_tree_path_next (path);
363  }
364  if (found)
365  gtk_tree_selection_select_path (selection, path);
366 
367  gtk_tree_path_free (path);
368  }
369  }
370  // this could be on load or if sx is deleted
371  if (!found)
372  {
373  GtkTreePath *path = gtk_tree_path_new_first ();
374  gtk_tree_selection_select_path (selection, path);
375  gtk_tree_path_free (path);
376  }
377 }
378 
379 static void
380 gpps_new_cb (GtkMenuItem *menuitem, GncPluginPageSxList *page)
381 {
382  gnc_plugin_page_sx_list_cmd_new (NULL, page);
383  return;
384 }
385 
386 static void
387 gpps_edit_cb (GtkMenuItem *menuitem, GncPluginPageSxList *page)
388 {
389  gnc_plugin_page_sx_list_cmd_edit (NULL, page);
390  return;
391 }
392 
393 static void
394 gpps_delete_cb (GtkMenuItem *menuitem, GncPluginPageSxList *page)
395 {
396  gnc_plugin_page_sx_list_cmd_delete (NULL, page);
397  return;
398 }
399 
400 static void
401 treeview_popup (GtkTreeView *treeview, GdkEvent *event, GncPluginPageSxList *page)
402 {
403  GncPluginPageSxListPrivate *priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE (page);
404  GtkTreeView *tree_view = GTK_TREE_VIEW (priv->tree_view);
405  GtkTreeSelection *selection = gtk_tree_view_get_selection (tree_view);
406  gint count_selection = gtk_tree_selection_count_selected_rows (selection);
407  GtkWidget *menu, *menuitem;
408 
409  menu = gtk_menu_new();
410 
411  menuitem = gtk_menu_item_new_with_mnemonic (_("_New"));
412  g_signal_connect (menuitem, "activate", G_CALLBACK(gpps_new_cb), page);
413  gtk_menu_shell_append (GTK_MENU_SHELL(menu), menuitem);
414 
415  menuitem = gtk_menu_item_new_with_mnemonic (_("_Edit"));
416  g_signal_connect (menuitem, "activate", G_CALLBACK(gpps_edit_cb), page);
417  gtk_widget_set_sensitive (menuitem, count_selection > 0);
418  gtk_menu_shell_append (GTK_MENU_SHELL(menu), menuitem);
419 
420  menuitem = gtk_menu_item_new_with_mnemonic (_("_Delete"));
421  g_signal_connect (menuitem, "activate", G_CALLBACK(gpps_delete_cb), page);
422  gtk_widget_set_sensitive (menuitem, count_selection > 0);
423  gtk_menu_shell_append (GTK_MENU_SHELL(menu), menuitem);
424 
425  gtk_menu_attach_to_widget (GTK_MENU (menu), GTK_WIDGET (priv->tree_view), NULL);
426  gtk_widget_show_all (menu);
427  gtk_menu_popup_at_pointer (GTK_MENU (menu), event);
428 }
429 
430 static gboolean
431 treeview_button_press (GtkTreeView *treeview, GdkEvent *event,
432  GncPluginPageSxList *page)
433 {
434  GncPluginPageSxListPrivate *priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE (page);
435  GtkTreeView *tree_view = GTK_TREE_VIEW (priv->tree_view);
436  GtkTreeSelection *selection = gtk_tree_view_get_selection (tree_view);
437 
438  if (event->type == GDK_BUTTON_PRESS)
439  {
440  GdkEventButton *event_button = (GdkEventButton*)event;
441  if (event_button->button == GDK_BUTTON_SECONDARY)
442  {
443  treeview_popup (tree_view, event, page);
444  return TRUE;
445  }
446  }
447  return FALSE;
448 }
449 
450 static gboolean
451 treeview_popup_menu (GtkTreeView *treeview, GncPluginPageSxList *page)
452 {
453  treeview_popup (treeview, NULL, page);
454  return TRUE;
455 }
456 
457 static GtkWidget *
458 gnc_plugin_page_sx_list_create_widget (GncPluginPage *plugin_page)
459 {
460  GncPluginPageSxList *page;
462  GtkWidget *widget;
463  GtkWidget *vbox;
464  GtkWidget *label;
465  GtkWidget *swin;
466 
467  page = GNC_PLUGIN_PAGE_SX_LIST(plugin_page);
468  priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(page);
469  if (priv->widget != NULL)
470  return priv->widget;
471 
472  /* Create Vpaned widget for top level */
473  widget = gtk_paned_new (GTK_ORIENTATION_VERTICAL);
474  priv->widget = widget;
475  gtk_widget_show (priv->widget);
476 
477  // Set the name for this widget so it can be easily manipulated with css
478  gtk_widget_set_name (GTK_WIDGET(priv->widget), "gnc-id-sx-page");
479 
480  /* Add vbox and label */
481  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
482  gtk_box_set_homogeneous (GTK_BOX(vbox), FALSE);
483  gtk_paned_pack1 (GTK_PANED(widget), vbox, TRUE, FALSE);
484 
485  label = gtk_label_new (_("Transactions"));
486  gnc_widget_style_context_add_class (GTK_WIDGET(label), "gnc-class-strong");
487  gtk_widget_set_margin_start (GTK_WIDGET(label), 6);
488  gnc_label_set_alignment (label, 0.0, 0);
489  gtk_widget_show (label);
490  gtk_box_pack_start (GTK_BOX(vbox), label, FALSE, FALSE, 0);
491  gtk_widget_show (vbox);
492 
493  /* Create scrolled window for top area */
494  swin = gtk_scrolled_window_new (NULL, NULL);
495  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(swin),
496  GTK_POLICY_AUTOMATIC,
497  GTK_POLICY_AUTOMATIC);
498  gtk_box_pack_start (GTK_BOX(vbox), swin, TRUE, TRUE, 5);
499  gtk_widget_show (swin);
500 
501  {
502  // gint half_way;
503  // half_way = plugin_page->notebook_page->allocation.height * 0.5;
504  // fixme; get a real value:
505  gtk_paned_set_position (GTK_PANED(priv->widget), 160);
506  }
507 
508  {
509  GDate end;
510  g_date_clear (&end, 1);
511  gnc_gdate_set_today (&end);
512  g_date_add_years (&end, 1);
513  priv->instances = GNC_SX_INSTANCE_MODEL(gnc_sx_get_instances (&end, TRUE));
514  }
515 
516  {
517  GtkTreeSelection *selection;
518  GtkTreePath *path = gtk_tree_path_new_first ();
519 
520  priv->tree_view = GTK_TREE_VIEW(gnc_tree_view_sx_list_new (priv->instances));
521  g_object_set (G_OBJECT(priv->tree_view),
522  "state-section", STATE_SECTION,
523  "show-column-menu", TRUE,
524  NULL);
525  gtk_container_add (GTK_CONTAINER( swin ), GTK_WIDGET(priv->tree_view));
526 
527  selection = gtk_tree_view_get_selection (priv->tree_view);
528  gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
529  gtk_tree_selection_select_path (selection, path);
530  gtk_tree_path_free (path);
531 
532  g_signal_connect (G_OBJECT(selection), "changed", (GCallback)gppsl_selection_changed_cb, (gpointer)page);
533  g_signal_connect (G_OBJECT(priv->tree_view), "row-activated", (GCallback)gppsl_row_activated_cb, (gpointer)page);
534  g_signal_connect (G_OBJECT(gtk_tree_view_get_model (GTK_TREE_VIEW(priv->tree_view))),
535  "model-populated", (GCallback)gppsl_model_populated_cb, (gpointer)page);
536 
537  gppsl_selection_changed_cb (selection, page);
538  }
539 
540  g_signal_connect (G_OBJECT(priv->tree_view), "button-press-event",
541  G_CALLBACK(treeview_button_press), page);
542  g_signal_connect (G_OBJECT(priv->tree_view), "popup-menu",
543  G_CALLBACK(treeview_popup_menu), page);
544 
545  /* Add vbox and label */
546  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
547  gtk_box_set_homogeneous (GTK_BOX(vbox), FALSE);
548  gtk_paned_pack2 (GTK_PANED(widget), vbox, TRUE, FALSE);
549 
550  label = gtk_label_new (_("Upcoming Transactions"));
551  gnc_widget_style_context_add_class (GTK_WIDGET(label), "gnc-class-strong");
552  gtk_widget_set_margin_start (GTK_WIDGET(label), 6);
553  gnc_label_set_alignment (label, 0.0, 0);
554  gtk_widget_show (label);
555 
556  gtk_box_pack_start (GTK_BOX(vbox), label, FALSE, FALSE, 0);
557  gtk_widget_show (vbox);
558 
559  /* Create scrolled window for bottom area */
560  swin = gtk_scrolled_window_new (NULL, NULL);
561  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(swin),
562  GTK_POLICY_AUTOMATIC,
563  GTK_POLICY_AUTOMATIC);
564  gtk_box_pack_start (GTK_BOX(vbox), swin, TRUE, TRUE, 5);
565  gtk_widget_show (swin);
566 
567  {
568  priv->dense_cal_model = gnc_sx_instance_dense_cal_adapter_new (GNC_SX_INSTANCE_MODEL(priv->instances));
569  priv->gdcal = GNC_DENSE_CAL(gnc_dense_cal_new_with_model (GNC_DENSE_CAL_MODEL(priv->dense_cal_model)));
570  g_object_ref_sink (priv->gdcal);
571 
572  gnc_dense_cal_set_months_per_col (priv->gdcal, 4);
573  gnc_dense_cal_set_num_months (priv->gdcal, 12);
574 
575  gtk_container_add (GTK_CONTAINER(swin), GTK_WIDGET(priv->gdcal));
576  }
577 
578  priv->gnc_component_id = gnc_register_gui_component ("plugin-page-sx-list",
579  gnc_plugin_page_sx_list_refresh_cb,
580  gnc_plugin_page_sx_list_close_cb,
581  page);
582  gnc_gui_component_set_session (priv->gnc_component_id,
583  gnc_get_current_session ());
584 
585  g_signal_connect (G_OBJECT(plugin_page), "inserted",
586  G_CALLBACK(gnc_plugin_page_inserted_cb),
587  NULL);
588 
589  return priv->widget;
590 }
591 
592 
593 static void
594 gnc_plugin_page_sx_list_destroy_widget (GncPluginPage *plugin_page)
595 {
596  GncPluginPageSxList *page;
598 
599  page = GNC_PLUGIN_PAGE_SX_LIST(plugin_page);
600  priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(page);
601 
602  // Remove the page_changed signal callback
603  gnc_plugin_page_disconnect_page_changed (GNC_PLUGIN_PAGE(plugin_page));
604 
605  // Remove the page focus idle function if present
606  g_idle_remove_by_data (plugin_page);
607 
608  if (priv->widget)
609  {
610  g_object_unref (G_OBJECT(priv->widget));
611  priv->widget = NULL;
612  }
613 
614  if (priv->selected_list)
615  g_list_free (priv->selected_list);
616 
617  if (priv->gnc_component_id)
618  {
619  gnc_unregister_gui_component (priv->gnc_component_id);
620  priv->gnc_component_id = 0;
621  }
622 }
623 
624 
632 static void
633 gnc_plugin_page_sx_list_save_page (GncPluginPage *plugin_page,
634  GKeyFile *key_file,
635  const gchar *group_name)
636 {
637  GncPluginPageSxList *page;
639 
640  g_return_if_fail (GNC_IS_PLUGIN_PAGE_SX_LIST(plugin_page));
641  g_return_if_fail (key_file != NULL);
642  g_return_if_fail (group_name != NULL);
643 
644  page = GNC_PLUGIN_PAGE_SX_LIST(plugin_page);
645  priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(page);
646 
647  g_key_file_set_integer (key_file, group_name, "dense_cal_num_months",
648  gnc_dense_cal_get_num_months (priv->gdcal));
649 
650  g_key_file_set_integer (key_file, group_name, "paned_position",
651  gtk_paned_get_position (GTK_PANED(priv->widget)));
652 }
653 
654 
663 static GncPluginPage *
664 gnc_plugin_page_sx_list_recreate_page (GtkWidget *window,
665  GKeyFile *key_file,
666  const gchar *group_name)
667 {
668  GncPluginPageSxList *page;
670 
671  g_return_val_if_fail (key_file, NULL);
672  g_return_val_if_fail (group_name, NULL);
673 
674  /* Create the new page. */
675  page = GNC_PLUGIN_PAGE_SX_LIST(gnc_plugin_page_sx_list_new ());
676  priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(page);
677 
678  /* Install it now so we can them manipulate the created widget */
679  gnc_main_window_open_page (GNC_MAIN_WINDOW(window), GNC_PLUGIN_PAGE(page));
680 
681  {
682  GError *err = NULL;
683  gint num_months = g_key_file_get_integer (key_file, group_name, "dense_cal_num_months", &err);
684  if (err == NULL)
685  gnc_dense_cal_set_num_months (priv->gdcal, num_months);
686  else
687  g_error_free (err);
688  }
689 
690  {
691  GError *err = NULL;
692  gint paned_position = g_key_file_get_integer (key_file, group_name,
693  "paned_position", &err);
694  if (err == NULL)
695  gtk_paned_set_position (GTK_PANED(priv->widget), paned_position);
696  else
697  g_error_free (err);
698  }
699 
700  return GNC_PLUGIN_PAGE(page);
701 }
702 
703 
704 static void
705 gnc_plugin_page_sx_list_cmd_new (GtkAction *action, GncPluginPageSxList *page)
706 {
707  GtkWindow *window = GTK_WINDOW(gnc_plugin_page_get_window (GNC_PLUGIN_PAGE(page)));
708  SchedXaction *new_sx;
709  gboolean new_sx_flag = TRUE;
710 
711  new_sx = xaccSchedXactionMalloc (gnc_get_current_book());
712  {
713  GDate now;
714  Recurrence *r = g_new0 (Recurrence, 1);
715  GList *schedule;
716 
717  g_date_clear (&now, 1);
718  gnc_gdate_set_today (&now);
719  recurrenceSet (r, 1, PERIOD_MONTH, &now, WEEKEND_ADJ_NONE);
720  schedule = gnc_sx_get_schedule (new_sx);
721  schedule = g_list_append (schedule, r);
722  gnc_sx_set_schedule (new_sx, schedule);
723  }
724  gnc_ui_scheduled_xaction_editor_dialog_create (window, new_sx, new_sx_flag);
725  gppsl_update_selected_list (page, TRUE, new_sx);
726 }
727 
728 static void
729 gnc_plugin_page_sx_list_cmd_refresh (GtkAction *action, GncPluginPageSxList *page)
730 {
732 
733  g_return_if_fail (GNC_IS_PLUGIN_PAGE_SX_LIST(page));
734 
735  priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(page);
736  gtk_widget_queue_draw (priv->widget);
737 }
738 
739 static void
740 _edit_sx(gpointer data, gpointer user_data)
741 {
742  gnc_ui_scheduled_xaction_editor_dialog_create (GTK_WINDOW(user_data),
743  (SchedXaction*)data, FALSE);
744 }
745 
746 static SchedXaction*
747 _argument_reorder_fn (GtkTreePath* list_path_data, GncTreeViewSxList* user_tree_view)
748 {
749  return gnc_tree_view_sx_list_get_sx_from_path (user_tree_view, list_path_data);
750 }
751 
752 
753 static void
754 gnc_plugin_page_sx_list_cmd_edit (GtkAction *action, GncPluginPageSxList *page)
755 {
756  GncPluginPageSxListPrivate *priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(page);
757  GtkWindow *window = GTK_WINDOW(gnc_plugin_page_get_window (GNC_PLUGIN_PAGE(page)));
758  GtkTreeSelection *selection;
759  GList *selected_paths, *to_edit;
760  GtkTreeModel *model;
761 
762  selection = gtk_tree_view_get_selection (priv->tree_view);
763  selected_paths = gtk_tree_selection_get_selected_rows (selection, &model);
764  if (!gnc_list_length_cmp (selected_paths, 0))
765  {
766  g_warning ("no selection edit.");
767  return;
768  }
769 
770  to_edit = gnc_g_list_map (selected_paths,
771  (GncGMapFunc)_argument_reorder_fn,
772  priv->tree_view);
773 
774  gppsl_update_selected_list (page, TRUE, NULL);
775  for (GList *list = to_edit; list != NULL; list = list->next)
776  {
777  DEBUG ("to-edit [%s]\n", xaccSchedXactionGetName ((SchedXaction*)list->data));
778  gppsl_update_selected_list (page, FALSE, list->data);
779  }
780 
781  g_list_foreach (to_edit, (GFunc)_edit_sx, window);
782  g_list_free (to_edit);
783  g_list_foreach (selected_paths, (GFunc)gtk_tree_path_free, NULL);
784  g_list_free (selected_paths);
785 }
786 
787 static void
788 gppsl_row_activated_cb (GtkTreeView *tree_view,
789  GtkTreePath *path,
790  GtkTreeViewColumn *column,
791  gpointer user_data)
792 {
793  GncPluginPageSxList *page = GNC_PLUGIN_PAGE_SX_LIST(user_data);
794  GncPluginPageSxListPrivate *priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(page);
795  GtkWindow *window = GTK_WINDOW(gnc_plugin_page_get_window (GNC_PLUGIN_PAGE(page)));
796 
797  SchedXaction *sx = gnc_tree_view_sx_list_get_sx_from_path (
798  GNC_TREE_VIEW_SX_LIST(priv->tree_view), path);
799  gnc_ui_scheduled_xaction_editor_dialog_create (window, sx, FALSE);
800  gppsl_update_selected_list (page, TRUE, sx);
801 }
802 
803 
804 static void
805 _destroy_sx(gpointer data, gpointer user_data)
806 {
807  SchedXactions *sxes;
808  SchedXaction *sx = (SchedXaction*)data;
809  QofBook *book;
810  book = gnc_get_current_book ();
811  sxes = gnc_book_get_schedxactions (book);
812  gnc_sxes_del_sx (sxes, sx);
813  gnc_sx_begin_edit (sx);
815 }
816 
817 
818 static void
819 gnc_plugin_page_sx_list_cmd_delete (GtkAction *action, GncPluginPageSxList *page)
820 {
821  GncPluginPageSxListPrivate *priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(page);
822  GtkTreeSelection *selection;
823  GList *selected_paths, *to_delete = NULL;
824  GtkTreeModel *model;
825  GtkWindow *window;
826  gchar *message = NULL;
827  gint length;
828 
829  selection = gtk_tree_view_get_selection (priv->tree_view);
830  selected_paths = gtk_tree_selection_get_selected_rows (selection, &model);
831  if (!gnc_list_length_cmp (selected_paths, 0))
832  {
833  g_warning ("no selection for delete.");
834  return;
835  }
836 
837  to_delete = gnc_g_list_map (selected_paths,
838  (GncGMapFunc)_argument_reorder_fn,
839  priv->tree_view);
840 
841  window = GTK_WINDOW(gnc_plugin_page_get_window (GNC_PLUGIN_PAGE(page)));
842 
843  length = g_list_length (to_delete);
844 
845  /* Translators: This is a ngettext(3) message, %d is the number of scheduled transactions deleted */
846  message = g_strdup_printf (ngettext ("Do you really want to delete this scheduled transaction?",
847  "Do you really want to delete %d scheduled transactions?",
848  length), length);
849 
850  if (gnc_verify_dialog (window, FALSE, "%s", message))
851  {
852  gppsl_update_selected_list (page, TRUE, NULL);
853  for (GList *list = to_delete; list != NULL; list = list->next)
854  {
855  DEBUG("to-delete [%s]\n", xaccSchedXactionGetName ((SchedXaction*)list->data));
856  gppsl_update_selected_list (page, FALSE, list->data);
857  }
858  g_list_foreach (to_delete, (GFunc)_destroy_sx, NULL);
859  }
860 
861  g_free (message);
862  g_list_free (to_delete);
863  g_list_foreach (selected_paths, (GFunc)gtk_tree_path_free, NULL);
864  g_list_free (selected_paths);
865 }
866 
GtkWidget * gnc_plugin_page_get_window(GncPluginPage *page)
Retrieve a pointer to the GncMainWindow (GtkWindow) containing this page.
void gnc_sx_set_schedule(SchedXaction *sx, GList *schedule)
Definition: SchedXaction.c:570
GList * gnc_g_list_map(GList *list, GncGMapFunc fn, gpointer user_data)
const gchar * tab_icon
The relative name of the icon that should be shown on the tab for this page.
gboolean(* focus_page_function)(GncPluginPage *plugin_page)
This function performs specific actions to set the focus on a specific widget.
GList * gnc_sx_get_schedule(const SchedXaction *sx)
Definition: SchedXaction.c:564
The instance data structure for a content plugin.
const GList * gnc_gobject_tracking_get_list(const gchar *name)
Get a list of all known objects of a specified type.
Date and Time handling routines.
utility functions for the GnuCash UI
GncTreeView implementation for Scheduled Transaction List.
GncPluginPage *(* recreate_page)(GtkWidget *window, GKeyFile *file, const gchar *group)
Create a new page based on the information saved during a previous instantiation of gnucash...
#define DEBUG(format, args...)
Print a debugging message.
Definition: qoflog.h:264
Functions that are supported by all types of windows.
void gnc_gdate_set_today(GDate *gd)
Set a GDate to the current day.
Definition: gnc-date.cpp:1239
API for Transactions and Splits (journal entries)
void gnc_main_window_open_page(GncMainWindow *window, GncPluginPage *page)
Display a data plugin page in a window.
Functions for adding content to a window.
void(* destroy_widget)(GncPluginPage *plugin_page)
Function called to destroy the display widget for a particular type of plugin.
The class data structure for a content plugin.
Gobject helper routines.
void gnc_plugin_page_disconnect_page_changed(GncPluginPage *page)
Disconnect the page_changed_id signal callback.
Anchor Scheduled Transaction info in a book.
const gchar * plugin_name
The textual name of this plugin.
GtkWidget *(* create_widget)(GncPluginPage *plugin_page)
Function called to create the display widget for a particular type of plugin.
Additional event handling code.
All type declarations for the whole Gnucash engine.
void(* save_page)(GncPluginPage *page, GKeyFile *file, const gchar *group)
Save enough information about this page so that it can be recreated next time the user starts gnucash...
GLib helper routines.
GtkAction * gnc_plugin_page_get_action(GncPluginPage *page, const gchar *name)
Retrieve a GtkAction object associated with this page.
GncSxInstanceModel * gnc_sx_get_instances(const GDate *range_end, gboolean include_disabled)
Allocates a new SxInstanceModel and fills it with generated instances for all scheduled transactions ...
void gnc_plugin_page_inserted_cb(GncPluginPage *page, gpointer user_data)
Set up the page_changed callback for when the current page is changed.
void gnc_main_window_close_page(GncPluginPage *page)
Remove a data plugin page from a window and display the previous page.
#define GNC_TYPE_PLUGIN_PAGE_SX_LIST
Functions providing a list of scheduled transactions as a plugin page.
GncPluginPage * gnc_plugin_page_sx_list_new(void)
void gnc_plugin_page_add_book(GncPluginPage *page, QofBook *book)
Add a book reference to the specified page.
SchedXaction * xaccSchedXactionMalloc(QofBook *book)
Creates and initializes a scheduled transaction.
Definition: SchedXaction.c:405
GtkActionGroup * gnc_plugin_page_create_action_group(GncPluginPage *page, const gchar *group_name)
Create the GtkActionGroup object associated with this page.
gint gnc_list_length_cmp(const GList *list, size_t len)
Scans the GList elements the minimum number of iterations required to test it against a specified siz...
API for Transactions and Splits (journal entries)
void xaccSchedXactionDestroy(SchedXaction *sx)
Cleans up and frees a SchedXaction and its associated data.
Definition: SchedXaction.c:475
Commodity handling public routines.