GnuCash  5.6-150-g038405b370+
gnc-plugin-page-sx-list.cpp
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.h"
66 #include "gnc-plugin-page-sx-list.h"
67 #include "gnc-prefs.h"
68 #include "gnc-session.h"
69 #include "gnc-sx-instance-dense-cal-adapter.h"
70 #include "gnc-sx-instance-model.h"
71 #include "gnc-sx-list-tree-model-adapter.h"
72 #include "gnc-tree-view-sx-list.h"
73 #include "gnc-ui-util.h"
74 #include "gnc-ui.h"
75 #include "gnc-window.h"
76 
77 #undef G_LOG_DOMAIN
78 #define G_LOG_DOMAIN "gnc.gui.plugin-page.sx-list"
79 
80 G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_GUI_SX;
81 
82 #define PLUGIN_PAGE_SX_LIST_CM_CLASS "plugin-page-sx-list"
83 #define STATE_SECTION "SX Transaction List"
84 #define GNC_PREF_DIVIDER_POS "divider-position"
85 #define GNC_PREF_NUM_OF_MONTHS "number-of-months"
86 
88 {
89  gboolean disposed;
90 
91  GtkWidget* widget;
92  gint gnc_component_id;
93 
94  GncSxInstanceDenseCalAdapter *dense_cal_model;
95  GncDenseCal* gdcal;
96 
97  GncSxInstanceModel* instances;
98  GtkTreeView* tree_view;
99  GList *selected_list;
100 
102 
103 G_DEFINE_TYPE_WITH_PRIVATE(GncPluginPageSxList, gnc_plugin_page_sx_list, GNC_TYPE_PLUGIN_PAGE)
104 
105 #define GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(o) \
106  ((GncPluginPageSxListPrivate*)gnc_plugin_page_sx_list_get_instance_private((GncPluginPageSxList*)o))
107 
108 /************************************************************
109  * Prototypes *
110  ************************************************************/
111 /* Plugin Actions */
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 (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
123 static void gnc_plugin_page_sx_list_cmd_edit (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
124 static void gnc_plugin_page_sx_list_cmd_delete (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
125 static void gnc_plugin_page_sx_list_cmd_refresh (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
126 static void gnc_plugin_page_sx_list_cmd_save_layout (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
127 static void gnc_plugin_page_sx_list_cmd_edit_tax_options (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
128 
129 /* Command callbacks */
130 static GActionEntry gnc_plugin_page_sx_list_actions [] =
131 {
132  { "SxListAction", NULL, NULL, NULL, NULL },
133  { "SxListNewAction", gnc_plugin_page_sx_list_cmd_new, NULL, NULL, NULL },
134  { "SxListEditAction", gnc_plugin_page_sx_list_cmd_edit, NULL, NULL, NULL },
135  { "SxListDeleteAction", gnc_plugin_page_sx_list_cmd_delete, NULL, NULL, NULL },
136  { "ViewRefreshAction", gnc_plugin_page_sx_list_cmd_refresh, NULL, NULL, NULL },
137  { "ViewSaveLayoutAction", gnc_plugin_page_sx_list_cmd_save_layout, NULL, NULL, NULL },
138  { "EditTaxOptionsAction", gnc_plugin_page_sx_list_cmd_edit_tax_options, NULL, NULL, NULL },
139 };
141 static guint gnc_plugin_page_sx_list_n_actions = G_N_ELEMENTS(gnc_plugin_page_sx_list_actions);
142 
144 static const gchar *gnc_plugin_load_ui_items [] =
145 {
146  "FilePlaceholder3",
147  "EditPlaceholder3",
148  "EditPlaceholder5",
149  "ViewPlaceholder4",
150  "SchedulePlaceholder0",
151  NULL,
152 };
153 
154 
157 {
158  GncPluginPageSxList *plugin_page;
159  const GList *object = gnc_gobject_tracking_get_list (GNC_PLUGIN_PAGE_SX_LIST_NAME);
160  if (object && GNC_IS_PLUGIN_PAGE_SX_LIST (object->data))
161  plugin_page = GNC_PLUGIN_PAGE_SX_LIST (object->data);
162  else
163  {
164  plugin_page = GNC_PLUGIN_PAGE_SX_LIST (g_object_new (GNC_TYPE_PLUGIN_PAGE_SX_LIST, nullptr));
165  }
166  return GNC_PLUGIN_PAGE(plugin_page);
167 }
168 
169 
174 static gboolean
175 gnc_plugin_page_sx_list_focus_widget (GncPluginPage *sx_plugin_page)
176 {
177  if (GNC_IS_PLUGIN_PAGE_SX_LIST(sx_plugin_page))
178  {
179  GncPluginPageSxListPrivate *priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(sx_plugin_page);
180  GtkTreeView *tree_view = priv->tree_view;
181 
182  /* Disable the Transaction Menu */
183  GAction *action = gnc_main_window_find_action (GNC_MAIN_WINDOW(sx_plugin_page->window), "TransactionAction");
184  g_simple_action_set_enabled (G_SIMPLE_ACTION(action), FALSE);
185  /* Enable the Schedule Menu */
186  action = gnc_main_window_find_action (GNC_MAIN_WINDOW(sx_plugin_page->window), "ScheduledAction");
187  g_simple_action_set_enabled (G_SIMPLE_ACTION(action), TRUE);
188  /* Disable the FilePrintAction */
189  action = gnc_main_window_find_action (GNC_MAIN_WINDOW(sx_plugin_page->window), "FilePrintAction");
190  g_simple_action_set_enabled (G_SIMPLE_ACTION(action), FALSE);
191 
192  gnc_main_window_update_menu_and_toolbar (GNC_MAIN_WINDOW(sx_plugin_page->window),
193  sx_plugin_page,
194  gnc_plugin_load_ui_items);
195 
196  if (GTK_IS_TREE_VIEW(tree_view))
197  {
198  if (!gtk_widget_is_focus (GTK_WIDGET(tree_view)))
199  gtk_widget_grab_focus (GTK_WIDGET(tree_view));
200  }
201  }
202  return FALSE;
203 }
204 
205 static void
206 gnc_plugin_page_sx_list_class_init (GncPluginPageSxListClass *klass)
207 {
208  GObjectClass *object_class = G_OBJECT_CLASS(klass);
209  GncPluginPageClass *gnc_plugin_class = GNC_PLUGIN_PAGE_CLASS(klass);
210 
211  object_class->dispose = gnc_plugin_page_sx_list_dispose;
212  object_class->finalize = gnc_plugin_page_sx_list_finalize;
213 
214  gnc_plugin_class->tab_icon = GNC_ICON_ACCOUNT;
215  gnc_plugin_class->plugin_name = GNC_PLUGIN_PAGE_SX_LIST_NAME;
216  gnc_plugin_class->create_widget = gnc_plugin_page_sx_list_create_widget;
217  gnc_plugin_class->destroy_widget = gnc_plugin_page_sx_list_destroy_widget;
218  gnc_plugin_class->save_page = gnc_plugin_page_sx_list_save_page;
219  gnc_plugin_class->recreate_page = gnc_plugin_page_sx_list_recreate_page;
220  gnc_plugin_class->focus_page_function = gnc_plugin_page_sx_list_focus_widget;
221 }
222 
223 
224 static void
225 gnc_plugin_page_sx_list_init (GncPluginPageSxList *plugin_page)
226 {
227  GSimpleActionGroup *simple_action_group;
228  GncPluginPage *parent;
229 
230  /* Init parent declared variables */
231  parent = GNC_PLUGIN_PAGE(plugin_page);
232  g_object_set(G_OBJECT(plugin_page),
233  "page-name", _("Scheduled Transactions"),
234  "ui-description", "gnc-plugin-page-sx-list.ui",
235  NULL);
236 
237  gnc_plugin_page_add_book (parent, gnc_get_current_book());
238  simple_action_group = gnc_plugin_page_create_action_group (parent, "GncPluginPageSxListActions");
239  g_action_map_add_action_entries (G_ACTION_MAP(simple_action_group),
240  gnc_plugin_page_sx_list_actions,
241  gnc_plugin_page_sx_list_n_actions,
242  plugin_page);
243 }
244 
245 
246 static void
247 gnc_plugin_page_sx_list_dispose (GObject *object)
248 {
249  GncPluginPageSxList *page;
251 
252  page = GNC_PLUGIN_PAGE_SX_LIST(object);
253  g_return_if_fail (GNC_IS_PLUGIN_PAGE_SX_LIST(page));
254  priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(page);
255  g_return_if_fail (priv != NULL);
256 
257  g_return_if_fail (!priv->disposed);
258  priv->disposed = TRUE;
259 
260  g_object_unref (G_OBJECT(priv->dense_cal_model));
261  priv->dense_cal_model = NULL;
262  g_object_unref (GTK_WIDGET(priv->gdcal));
263  priv->gdcal = NULL;
264  g_object_unref (G_OBJECT(priv->instances));
265  priv->instances = NULL;
266 
267  G_OBJECT_CLASS(gnc_plugin_page_sx_list_parent_class)->dispose (object);
268 }
269 
270 
271 static void
272 gnc_plugin_page_sx_list_finalize (GObject *object)
273 {
274  GncPluginPageSxList *page;
276 
277  page = GNC_PLUGIN_PAGE_SX_LIST(object);
278  g_return_if_fail (GNC_IS_PLUGIN_PAGE_SX_LIST(page));
279  priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(page);
280  g_return_if_fail (priv != NULL);
281 
282  G_OBJECT_CLASS(gnc_plugin_page_sx_list_parent_class)->finalize (object);
283 }
284 
285 
286 /* Virtual Functions */
287 static void
288 gnc_plugin_page_sx_list_refresh_cb (GHashTable *changes, gpointer user_data)
289 {
290  auto page = GNC_PLUGIN_PAGE_SX_LIST(user_data);
292 
293  g_return_if_fail (GNC_IS_PLUGIN_PAGE_SX_LIST(page));
294 
295  /* We're only looking for forced updates here. */
296  if (changes)
297  return;
298 
299  priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(page);
300  gtk_widget_queue_draw (priv->widget);
301 }
302 
303 
304 static void
305 gnc_plugin_page_sx_list_close_cb (gpointer user_data)
306 {
307  GncPluginPage *plugin_page = GNC_PLUGIN_PAGE(user_data);
308  gnc_main_window_close_page (plugin_page);
309 }
310 
311 
312 static void
313 gppsl_selection_changed_cb (GtkTreeSelection *selection, gpointer user_data)
314 {
315  GncPluginPage *page;
316  GAction *edit_action, *delete_action;
317  gboolean selection_state = TRUE;
318 
319  page = GNC_PLUGIN_PAGE(user_data);
320  edit_action = gnc_plugin_page_get_action (page, "SxListEditAction");
321  delete_action = gnc_plugin_page_get_action (page, "SxListDeleteAction");
322  selection_state = gtk_tree_selection_count_selected_rows (selection) == 0
323  ? FALSE
324  : TRUE;
325  g_simple_action_set_enabled (G_SIMPLE_ACTION(edit_action), selection_state);
326  g_simple_action_set_enabled (G_SIMPLE_ACTION(delete_action), selection_state);
327 }
328 
329 
330 static void
331 gppsl_update_selected_list (GncPluginPageSxList *page, gboolean reset, SchedXaction *sx)
332 {
333  GncPluginPageSxListPrivate *priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(page);
334 
335  if (reset && priv->selected_list)
336  {
337  g_list_free (priv->selected_list);
338  priv->selected_list = NULL;
339  }
340  if (sx)
341  priv->selected_list = g_list_prepend (priv->selected_list, sx);
342 }
343 
344 
345 static void
346 gppsl_model_populated_cb (GtkTreeModel *tree_model, GncPluginPageSxList *page)
347 {
348  GncPluginPageSxListPrivate *priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(page);
349  GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(priv->tree_view));
350  gboolean found = FALSE;
351 
352  if (priv->selected_list)
353  {
354  // walk the list to see if we can reselect the sx
355  for (GList *list = priv->selected_list; list != NULL; list = list->next)
356  {
357  SchedXaction *sx = GNC_SCHEDXACTION(list->data);
358  GtkTreePath *path = gtk_tree_path_new_first ();
359 
360  // loop through the model trying to find selected sx's
361  while (gnc_tree_view_path_is_valid (GNC_TREE_VIEW(priv->tree_view), path))
362  {
363  SchedXaction *sx_tmp = gnc_tree_view_sx_list_get_sx_from_path (
364  GNC_TREE_VIEW_SX_LIST(priv->tree_view), path);
365  if (sx_tmp == sx)
366  {
367  found = TRUE;
368  break;
369  }
370  gtk_tree_path_next (path);
371  }
372  if (found)
373  gtk_tree_selection_select_path (selection, path);
374 
375  gtk_tree_path_free (path);
376  }
377  }
378  // this could be on load or if sx is deleted
379  if (!found)
380  {
381  GtkTreePath *path = gtk_tree_path_new_first ();
382  gtk_tree_selection_select_path (selection, path);
383  gtk_tree_path_free (path);
384  }
385 }
386 
387 
388 static void
389 treeview_popup (GtkTreeView *treeview, GdkEvent *event, GncPluginPageSxList *page)
390 {
391  GncPluginPageSxListPrivate *priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE (page);
392  GtkWidget *menu, *menuitem;
393  gchar *full_action_name;
394  const gchar *group_name = gnc_plugin_page_get_simple_action_group_name (GNC_PLUGIN_PAGE(page));
395 
396  menu = gtk_menu_new();
397 
398  menuitem = gtk_menu_item_new_with_mnemonic (_("_New Schedule"));
399  full_action_name = g_strconcat (group_name, ".SxListNewAction", NULL);
400  gtk_actionable_set_action_name (GTK_ACTIONABLE(menuitem), full_action_name);
401  g_free (full_action_name);
402  gtk_menu_shell_append (GTK_MENU_SHELL(menu), menuitem);
403 
404  menuitem = gtk_menu_item_new_with_mnemonic (_("_Edit Schedule"));
405  full_action_name = g_strconcat (group_name, ".SxListEditAction", NULL);
406  gtk_actionable_set_action_name (GTK_ACTIONABLE(menuitem), full_action_name);
407  g_free (full_action_name);
408  gtk_menu_shell_append (GTK_MENU_SHELL(menu), menuitem);
409 
410  menuitem = gtk_menu_item_new_with_mnemonic (_("_Delete Schedule"));
411  full_action_name = g_strconcat (group_name, ".SxListDeleteAction", NULL);
412  gtk_actionable_set_action_name (GTK_ACTIONABLE(menuitem), full_action_name);
413  g_free (full_action_name);
414  gtk_menu_shell_append (GTK_MENU_SHELL(menu), menuitem);
415 
416  gtk_menu_attach_to_widget (GTK_MENU (menu), GTK_WIDGET (priv->tree_view), NULL);
417  gtk_widget_show_all (menu);
418  gtk_menu_popup_at_pointer (GTK_MENU (menu), event);
419 }
420 
421 static gboolean
422 treeview_button_press (GtkTreeView *treeview, GdkEvent *event,
423  GncPluginPageSxList *page)
424 {
425  GncPluginPageSxListPrivate *priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE (page);
426  GtkTreeView *tree_view = GTK_TREE_VIEW (priv->tree_view);
427 
428  if (event->type == GDK_BUTTON_PRESS)
429  {
430  GdkEventButton *event_button = (GdkEventButton*)event;
431  if (event_button->button == GDK_BUTTON_SECONDARY)
432  {
433  GtkTreePath *path = NULL;
434  if (gtk_tree_view_get_path_at_pos (priv->tree_view, event_button->x, event_button->y,
435  &path, NULL, NULL, NULL))
436  {
437  GtkTreeSelection *selection = gtk_tree_view_get_selection (priv->tree_view);
438 
439  if (!gtk_tree_selection_path_is_selected (selection, path))
440  {
441  gtk_tree_selection_unselect_all (selection);
442  gtk_tree_selection_select_path (selection, path);
443  }
444  }
445  gtk_tree_path_free (path);
446 
447  treeview_popup (tree_view, event, page);
448  return TRUE;
449  }
450  }
451  return FALSE;
452 }
453 
454 static gboolean
455 treeview_popup_menu (GtkTreeView *treeview, GncPluginPageSxList *page)
456 {
457  treeview_popup (treeview, NULL, page);
458  return TRUE;
459 }
460 
461 static GtkWidget *
462 gnc_plugin_page_sx_list_create_widget (GncPluginPage *plugin_page)
463 {
464  GncPluginPageSxList *page;
466  GtkWidget *widget;
467  GtkWidget *vbox;
468  GtkWidget *label;
469  GtkWidget *swin;
470  GtkWindow *window;
471 
472  page = GNC_PLUGIN_PAGE_SX_LIST(plugin_page);
473  priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(page);
474  if (priv->widget != NULL)
475  return priv->widget;
476 
477  window = GTK_WINDOW(gnc_plugin_page_get_window (GNC_PLUGIN_PAGE(page)));
478 
479  /* Create Vpaned widget for top level */
480  widget = gtk_paned_new (GTK_ORIENTATION_VERTICAL);
481  priv->widget = widget;
482  gtk_widget_show (priv->widget);
483 
484  // Set the name for this widget so it can be easily manipulated with css
485  gtk_widget_set_name (GTK_WIDGET(priv->widget), "gnc-id-sx-page");
486 
487  /* Add vbox and label */
488  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
489  gtk_box_set_homogeneous (GTK_BOX(vbox), FALSE);
490  gtk_paned_pack1 (GTK_PANED(widget), vbox, TRUE, FALSE);
491 
492  label = gtk_label_new (_("Transactions"));
493  gnc_widget_style_context_add_class (GTK_WIDGET(label), "gnc-class-strong");
494  gtk_widget_set_margin_start (GTK_WIDGET(label), 6);
495  gnc_label_set_alignment (label, 0.0, 0);
496  gtk_widget_show (label);
497  gtk_box_pack_start (GTK_BOX(vbox), label, FALSE, FALSE, 0);
498  gtk_widget_show (vbox);
499 
500  /* Create scrolled window for top area */
501  swin = gtk_scrolled_window_new (NULL, NULL);
502  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(swin),
503  GTK_POLICY_AUTOMATIC,
504  GTK_POLICY_AUTOMATIC);
505  gtk_box_pack_start (GTK_BOX(vbox), swin, TRUE, TRUE, 5);
506  gtk_widget_show (swin);
507 
508  /* Set the paned position from the preferences, default 160 */
509  gtk_paned_set_position (GTK_PANED(priv->widget),
510  gnc_prefs_get_float (GNC_PREFS_GROUP_SXED,
511  GNC_PREF_DIVIDER_POS));
512 
513  {
514  GDate end;
515  g_date_clear (&end, 1);
516  gnc_gdate_set_today (&end);
517  g_date_add_years (&end, 1);
518  priv->instances = GNC_SX_INSTANCE_MODEL(gnc_sx_get_instances (&end, TRUE));
519  }
520 
521  {
522  GAction *edit_action, *delete_action;
523  edit_action = gnc_plugin_page_get_action (GNC_PLUGIN_PAGE(page), "SxListEditAction");
524  delete_action = gnc_plugin_page_get_action (GNC_PLUGIN_PAGE(page), "SxListDeleteAction");
525  g_simple_action_set_enabled (G_SIMPLE_ACTION(edit_action), FALSE);
526  g_simple_action_set_enabled (G_SIMPLE_ACTION(delete_action), FALSE);
527  }
528 
529  {
530  GtkTreeSelection *selection;
531  GtkTreePath *path = gtk_tree_path_new_first ();
532 
533  priv->tree_view = GTK_TREE_VIEW(gnc_tree_view_sx_list_new (priv->instances));
534  g_object_set (G_OBJECT(priv->tree_view),
535  "state-section", STATE_SECTION,
536  "show-column-menu", TRUE,
537  NULL);
538  gtk_container_add (GTK_CONTAINER( swin ), GTK_WIDGET(priv->tree_view));
539 
540  selection = gtk_tree_view_get_selection (priv->tree_view);
541  gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
542 
543  g_signal_connect (G_OBJECT(selection), "changed", (GCallback)gppsl_selection_changed_cb, (gpointer)page);
544  g_signal_connect (G_OBJECT(priv->tree_view), "row-activated", (GCallback)gppsl_row_activated_cb, (gpointer)page);
545  g_signal_connect (G_OBJECT(gtk_tree_view_get_model (GTK_TREE_VIEW(priv->tree_view))),
546  "model-populated", (GCallback)gppsl_model_populated_cb, (gpointer)page);
547 
548  gtk_tree_selection_select_path (selection, path);
549  gtk_tree_path_free (path);
550  }
551 
552  g_signal_connect (G_OBJECT(priv->tree_view), "button-press-event",
553  G_CALLBACK(treeview_button_press), page);
554  g_signal_connect (G_OBJECT(priv->tree_view), "popup-menu",
555  G_CALLBACK(treeview_popup_menu), page);
556 
557  /* Add vbox and label */
558  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
559  gtk_box_set_homogeneous (GTK_BOX(vbox), FALSE);
560  gtk_paned_pack2 (GTK_PANED(widget), vbox, TRUE, FALSE);
561 
562  label = gtk_label_new (_("Upcoming Transactions"));
563  gnc_widget_style_context_add_class (GTK_WIDGET(label), "gnc-class-strong");
564  gtk_widget_set_margin_start (GTK_WIDGET(label), 6);
565  gnc_label_set_alignment (label, 0.0, 0);
566  gtk_widget_show (label);
567 
568  gtk_box_pack_start (GTK_BOX(vbox), label, FALSE, FALSE, 0);
569  gtk_widget_show (vbox);
570 
571  /* Create scrolled window for bottom area */
572  swin = gtk_scrolled_window_new (NULL, NULL);
573  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(swin),
574  GTK_POLICY_AUTOMATIC,
575  GTK_POLICY_AUTOMATIC);
576  gtk_box_pack_start (GTK_BOX(vbox), swin, TRUE, TRUE, 5);
577  gtk_widget_show (swin);
578 
579  {
580  priv->dense_cal_model = gnc_sx_instance_dense_cal_adapter_new (GNC_SX_INSTANCE_MODEL(priv->instances));
581  priv->gdcal = GNC_DENSE_CAL(gnc_dense_cal_new_with_model (window, GNC_DENSE_CAL_MODEL(priv->dense_cal_model)));
582  g_object_ref_sink (priv->gdcal);
583 
584  /* Set number of months from preference, default 12 */
585  gchar *num_months = gnc_prefs_get_string (GNC_PREFS_GROUP_SXED, GNC_PREF_NUM_OF_MONTHS);
586  gnc_dense_cal_set_num_months (priv->gdcal, atoi (num_months));
587  g_free (num_months);
588 
589  gtk_container_add (GTK_CONTAINER(swin), GTK_WIDGET(priv->gdcal));
590  }
591 
592  priv->gnc_component_id = gnc_register_gui_component ("plugin-page-sx-list",
593  gnc_plugin_page_sx_list_refresh_cb,
594  gnc_plugin_page_sx_list_close_cb,
595  page);
596  gnc_gui_component_set_session (priv->gnc_component_id,
597  gnc_get_current_session ());
598 
599  g_signal_connect (G_OBJECT(plugin_page), "inserted",
600  G_CALLBACK(gnc_plugin_page_inserted_cb),
601  NULL);
602 
603  return priv->widget;
604 }
605 
606 
607 static void
608 gnc_plugin_page_sx_list_destroy_widget (GncPluginPage *plugin_page)
609 {
610  GncPluginPageSxList *page;
612 
613  page = GNC_PLUGIN_PAGE_SX_LIST(plugin_page);
614  priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(page);
615 
616  // Remove the page_changed signal callback
617  gnc_plugin_page_disconnect_page_changed (GNC_PLUGIN_PAGE(plugin_page));
618 
619  // Remove the page focus idle function if present
620  g_idle_remove_by_data (plugin_page);
621 
622  if (priv->widget)
623  {
624  g_object_unref (G_OBJECT(priv->widget));
625  priv->widget = NULL;
626  }
627 
628  if (priv->selected_list)
629  g_list_free (priv->selected_list);
630 
631  if (priv->gnc_component_id)
632  {
633  gnc_unregister_gui_component (priv->gnc_component_id);
634  priv->gnc_component_id = 0;
635  }
636 }
637 
638 
646 static void
647 gnc_plugin_page_sx_list_save_page (GncPluginPage *plugin_page,
648  GKeyFile *key_file,
649  const gchar *group_name)
650 {
651  GncPluginPageSxList *page;
653 
654  g_return_if_fail (GNC_IS_PLUGIN_PAGE_SX_LIST(plugin_page));
655  g_return_if_fail (key_file != NULL);
656  g_return_if_fail (group_name != NULL);
657 
658  page = GNC_PLUGIN_PAGE_SX_LIST(plugin_page);
659  priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(page);
660 
661  g_key_file_set_integer (key_file, group_name, "dense_cal_num_months",
662  gnc_dense_cal_get_num_months (priv->gdcal));
663 
664  g_key_file_set_integer (key_file, group_name, "paned_position",
665  gtk_paned_get_position (GTK_PANED(priv->widget)));
666 }
667 
668 
677 static GncPluginPage *
678 gnc_plugin_page_sx_list_recreate_page (GtkWidget *window,
679  GKeyFile *key_file,
680  const gchar *group_name)
681 {
682  GncPluginPageSxList *page;
684 
685  g_return_val_if_fail (key_file, NULL);
686  g_return_val_if_fail (group_name, NULL);
687 
688  /* Create the new page. */
689  page = GNC_PLUGIN_PAGE_SX_LIST(gnc_plugin_page_sx_list_new ());
690  priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(page);
691 
692  /* Install it now so we can them manipulate the created widget */
693  gnc_main_window_open_page (GNC_MAIN_WINDOW(window), GNC_PLUGIN_PAGE(page));
694 
695  {
696  GError *err = NULL;
697  gint num_months = g_key_file_get_integer (key_file, group_name, "dense_cal_num_months", &err);
698  if (err == NULL)
699  gnc_dense_cal_set_num_months (priv->gdcal, num_months);
700  else
701  g_error_free (err);
702  }
703 
704  {
705  GError *err = NULL;
706  gint paned_position = g_key_file_get_integer (key_file, group_name,
707  "paned_position", &err);
708  if (err == NULL)
709  gtk_paned_set_position (GTK_PANED(priv->widget), paned_position);
710  else
711  g_error_free (err);
712  }
713 
714  return GNC_PLUGIN_PAGE(page);
715 }
716 
717 
718 static void
719 gnc_plugin_page_sx_list_cmd_new (GSimpleAction *simple,
720  GVariant *parameter,
721  gpointer user_data)
722 {
723  auto plugin_page = GNC_PLUGIN_PAGE_SX_LIST(user_data);
724  GtkWindow *window = GTK_WINDOW(gnc_plugin_page_get_window (GNC_PLUGIN_PAGE(plugin_page)));
725  SchedXaction *new_sx;
726  gboolean new_sx_flag = TRUE;
727 
728  new_sx = xaccSchedXactionMalloc (gnc_get_current_book());
729  {
730  GDate now;
731  Recurrence *r = g_new0 (Recurrence, 1);
732  GList *schedule;
733 
734  g_date_clear (&now, 1);
735  gnc_gdate_set_today (&now);
736  recurrenceSet (r, 1, PERIOD_MONTH, &now, WEEKEND_ADJ_NONE);
737  schedule = gnc_sx_get_schedule (new_sx);
738  schedule = g_list_append (schedule, r);
739  gnc_sx_set_schedule (new_sx, schedule);
740  }
741  gnc_ui_scheduled_xaction_editor_dialog_create (window, new_sx, new_sx_flag);
742  gppsl_update_selected_list (plugin_page, TRUE, new_sx);
743 }
744 
745 static void
746 gnc_plugin_page_sx_list_cmd_refresh (GSimpleAction *simple,
747  GVariant *parameter,
748  gpointer user_data)
749 {
750  auto plugin_page = GNC_PLUGIN_PAGE_SX_LIST(user_data);
752 
753  g_return_if_fail (GNC_IS_PLUGIN_PAGE_SX_LIST(plugin_page));
754 
755  priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(plugin_page);
756  gtk_widget_queue_draw (priv->widget);
757 }
758 
759 static void
760 gnc_plugin_page_sx_list_cmd_save_layout (GSimpleAction *simple,
761  GVariant *parameter,
762  gpointer user_data)
763 {
764  auto plugin_page = GNC_PLUGIN_PAGE_SX_LIST(user_data);
766  gchar *num_of_months;
767  gint paned_position;
768 
769  g_return_if_fail (GNC_IS_PLUGIN_PAGE_SX_LIST(plugin_page));
770 
771  priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(plugin_page);
772 
773  num_of_months = g_strdup_printf ("%d", gnc_dense_cal_get_num_months (priv->gdcal));
774  paned_position = gtk_paned_get_position (GTK_PANED(priv->widget));
775 
776  gnc_prefs_set_float (GNC_PREFS_GROUP_SXED, GNC_PREF_DIVIDER_POS,
777  paned_position);
778 
779  gnc_prefs_set_string (GNC_PREFS_GROUP_SXED, GNC_PREF_NUM_OF_MONTHS,
780  num_of_months);
781 
782  g_free (num_of_months);
783 }
784 
785 static void
786 _edit_sx(gpointer data, gpointer user_data)
787 {
788  gnc_ui_scheduled_xaction_editor_dialog_create (GTK_WINDOW(user_data),
789  GNC_SCHEDXACTION(data), FALSE);
790 }
791 
792 static SchedXaction*
793 _argument_reorder_fn (GtkTreePath* list_path_data, GncTreeViewSxList* user_tree_view)
794 {
795  return gnc_tree_view_sx_list_get_sx_from_path (user_tree_view, list_path_data);
796 }
797 
798 
799 static void
800 gnc_plugin_page_sx_list_cmd_edit (GSimpleAction *simple,
801  GVariant *parameter,
802  gpointer user_data)
803 {
804  auto plugin_page = GNC_PLUGIN_PAGE_SX_LIST(user_data);
805  GncPluginPageSxListPrivate *priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(plugin_page);
806  GtkWindow *window = GTK_WINDOW(gnc_plugin_page_get_window (GNC_PLUGIN_PAGE(plugin_page)));
807  GtkTreeSelection *selection;
808  GList *selected_paths, *to_edit;
809  GtkTreeModel *model;
810 
811  selection = gtk_tree_view_get_selection (priv->tree_view);
812  selected_paths = gtk_tree_selection_get_selected_rows (selection, &model);
813  if (!gnc_list_length_cmp (selected_paths, 0))
814  {
815  g_warning ("no selection edit.");
816  return;
817  }
818 
819  to_edit = gnc_g_list_map (selected_paths,
820  (GncGMapFunc)_argument_reorder_fn,
821  priv->tree_view);
822 
823  gppsl_update_selected_list (plugin_page, TRUE, NULL);
824  for (GList *list = to_edit; list != NULL; list = list->next)
825  {
826  DEBUG ("to-edit [%s]\n", xaccSchedXactionGetName (GNC_SCHEDXACTION(list->data)));
827  gppsl_update_selected_list (plugin_page, FALSE, GNC_SCHEDXACTION(list->data));
828  }
829 
830  g_list_foreach (to_edit, (GFunc)_edit_sx, window);
831  g_list_free (to_edit);
832  g_list_foreach (selected_paths, (GFunc)gtk_tree_path_free, NULL);
833  g_list_free (selected_paths);
834 }
835 
836 
837 static void
838 gnc_plugin_page_sx_list_cmd_edit_tax_options (GSimpleAction *simple,
839  GVariant *parameter,
840  gpointer user_data)
841 {
842  auto plugin_page = GNC_PLUGIN_PAGE_SX_LIST(user_data);
843  GtkWidget *window = GTK_WIDGET(gnc_plugin_page_get_window (GNC_PLUGIN_PAGE(plugin_page)));
844 
845  ENTER ("(action %p, page %p)", simple, plugin_page);
846  gnc_tax_info_dialog (window, NULL);
847  LEAVE (" ");
848 }
849 
850 
851 static void
852 gppsl_row_activated_cb (GtkTreeView *tree_view,
853  GtkTreePath *path,
854  GtkTreeViewColumn *column,
855  gpointer user_data)
856 {
857  GncPluginPageSxList *page = GNC_PLUGIN_PAGE_SX_LIST(user_data);
858  GncPluginPageSxListPrivate *priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(page);
859  GtkWindow *window = GTK_WINDOW(gnc_plugin_page_get_window (GNC_PLUGIN_PAGE(page)));
860 
861  SchedXaction *sx = gnc_tree_view_sx_list_get_sx_from_path (
862  GNC_TREE_VIEW_SX_LIST(priv->tree_view), path);
863  gnc_ui_scheduled_xaction_editor_dialog_create (window, sx, FALSE);
864  gppsl_update_selected_list (page, TRUE, sx);
865 }
866 
867 
868 static void
869 _destroy_sx(gpointer data, gpointer user_data)
870 {
871  SchedXactions *sxes;
872  auto sx = GNC_SCHEDXACTION(data);
873  QofBook *book;
874  book = gnc_get_current_book ();
875  sxes = gnc_book_get_schedxactions (book);
876  gnc_sxes_del_sx (sxes, sx);
877  gnc_sx_begin_edit (sx);
879 }
880 
881 
882 static void
883 _destroy_sx_names (gpointer data, gpointer user_data)
884 {
885  auto sx = GNC_SCHEDXACTION(data);
886  GList **to_delete_names = (GList**)user_data;
887  *to_delete_names = g_list_append (*to_delete_names, xaccSchedXactionGetName (sx));
888 }
889 
890 
891 static void
892 gnc_plugin_page_sx_list_cmd_delete (GSimpleAction *simple,
893  GVariant *parameter,
894  gpointer user_data)
895 {
896  auto plugin_page = GNC_PLUGIN_PAGE_SX_LIST(user_data);
897  GncPluginPageSxListPrivate *priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(plugin_page);
898  GtkTreeSelection *selection = gtk_tree_view_get_selection (priv->tree_view);
899  GList *selected_paths, *to_delete = NULL, *to_delete_names = NULL;
900  GtkTreeModel *model;
901  GtkWindow *window;
902  gchar *message = NULL;
903  gint length;
904  gchar *text_list_of_scheduled_transaction_names = NULL;
905 
906  selected_paths = gtk_tree_selection_get_selected_rows (selection, &model);
907  if (!gnc_list_length_cmp (selected_paths, 0))
908  {
909  g_warning ("no selection for delete.");
910  return;
911  }
912 
913  to_delete = gnc_g_list_map (selected_paths,
914  (GncGMapFunc)_argument_reorder_fn,
915  priv->tree_view);
916 
917  g_list_foreach (to_delete, (GFunc)_destroy_sx_names, &to_delete_names);
918 
919  window = GTK_WINDOW(gnc_plugin_page_get_window (GNC_PLUGIN_PAGE(plugin_page)));
920 
921  length = g_list_length (to_delete);
922 
923  text_list_of_scheduled_transaction_names = gnc_g_list_stringjoin (to_delete_names, "\n");
924 
925  /* Translators: This is a ngettext(3) message, question followed by list of scheduled transactions to be deleted */
926  message = g_strdup_printf ("%s\n\n%s",
927  ngettext ("Do you really want to delete this scheduled transaction?",
928  "Do you really want to delete these scheduled transactions?",
929  length),
930  text_list_of_scheduled_transaction_names);
931 
932  g_free (text_list_of_scheduled_transaction_names);
933  g_list_free (to_delete_names);
934 
935  if (gnc_verify_dialog (window, FALSE, "%s", message))
936  {
937  gppsl_update_selected_list (plugin_page, TRUE, NULL);
938  for (GList *list = to_delete; list != NULL; list = list->next)
939  {
940  DEBUG("to-delete [%s]\n", xaccSchedXactionGetName (GNC_SCHEDXACTION(list->data)));
941  gppsl_update_selected_list (plugin_page, FALSE, GNC_SCHEDXACTION(list->data));
942  }
943  g_list_foreach (to_delete, (GFunc)_destroy_sx, NULL);
944  }
945 
946  g_free (message);
947  g_list_free (to_delete);
948  g_list_foreach (selected_paths, (GFunc)gtk_tree_path_free, NULL);
949  g_list_free (selected_paths);
950 }
951 
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)
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)
void gnc_main_window_update_menu_and_toolbar(GncMainWindow *window, GncPluginPage *page, const gchar **ui_updates)
Update the main window menu with the placeholders listed in ui_updates and load the page specific too...
The instance data structure for a content plugin.
gchar * gnc_prefs_get_string(const gchar *group, const gchar *pref_name)
Get a string value from the preferences backend.
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.
gchar * gnc_g_list_stringjoin(GList *list_of_strings, const gchar *sep)
Return a string joining a GList whose elements are gchar* strings.
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:1235
GtkWidget * window
The window that contains the display widget for this plugin.
API for Transactions and Splits (journal entries)
GSimpleActionGroup * gnc_plugin_page_create_action_group(GncPluginPage *page, const gchar *group_name)
Create the GSimpleActionGroup object associated with this page.
gboolean gnc_prefs_set_string(const gchar *group, const gchar *pref_name, const gchar *value)
Store a string into the preferences backend.
Definition: gnc-prefs.c:319
#define ENTER(format, args...)
Print a function entry debugging message.
Definition: qoflog.h:272
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.
GAction * gnc_main_window_find_action(GncMainWindow *window, const gchar *action_name)
Find the GAction in the main window.
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.
gboolean gnc_prefs_set_float(const gchar *group, const gchar *pref_name, gdouble value)
Store a float value into the preferences backend.
Definition: gnc-prefs.c:308
GtkWidget *(* create_widget)(GncPluginPage *plugin_page)
Function called to create the display widget for a particular type of plugin.
const gchar * gnc_plugin_page_get_simple_action_group_name(GncPluginPage *page)
Retrieve the simple action group name associated with this plugin page.
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.
Generic api to store and retrieve preferences.
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.
Functions for adding plugins to a GnuCash window.
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition: qoflog.h:282
#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.
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)
GAction * gnc_plugin_page_get_action(GncPluginPage *page, const gchar *name)
Retrieve a GAction object associated with this page.
void xaccSchedXactionDestroy(SchedXaction *sx)
Cleans up and frees a SchedXaction and its associated data.
Commodity handling public routines.
gdouble gnc_prefs_get_float(const gchar *group, const gchar *pref_name)
Get an float value from the preferences backend.