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 
36 /** @addtogroup ContentPlugins
37     @{ */
38 /** @addtogroup GncPluginPageSxList A SX List Plugin Page
39     @{ */
40 /** @brief Functions providing the SX List as a plugin page.
41     @author Josh Sled <jsled@asynchronous.org>
42 */
43 
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 /*################## Added for Reg2 #################*/
56 #include "dialog-sx-editor2.h"
57 /*################## Added for Reg2 #################*/
58 #include "dialog-utils.h"
59 #include "gnc-commodity.h"
60 #include "gnc-component-manager.h"
61 #include "gnc-date.h"
62 #include "gnc-dense-cal.h"
63 #include "gnc-engine.h"
64 #include "gnc-event.h"
65 #include "gnc-glib-utils.h"
66 #include "gnc-icons.h"
67 #include "gnc-main-window.h"
68 #include "gnc-plugin-page-sx-list.h"
69 #include "gnc-session.h"
70 #include "gnc-sx-instance-dense-cal-adapter.h"
71 #include "gnc-sx-instance-model.h"
72 #include "gnc-sx-list-tree-model-adapter.h"
73 #include "gnc-tree-view-sx-list.h"
74 #include "gnc-ui-util.h"
75 #include "gnc-ui.h"
76 #include "gnc-window.h"
77 
78 #undef G_LOG_DOMAIN
79 #define G_LOG_DOMAIN "gnc.gui.plugin-page.sx-list"
80 
81 G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_GUI_SX;
82 
83 #define PLUGIN_PAGE_SX_LIST_CM_CLASS "plugin-page-sx-list"
84 #define STATE_SECTION "SX Transaction List"
85 
86 typedef struct GncPluginPageSxListPrivate
87 {
88     gboolean disposed;
89 
90     GtkWidget* widget;
91     gint gnc_component_id;
92 
93     GncSxInstanceDenseCalAdapter *dense_cal_model;
94     GncDenseCal* gdcal;
95 
96     GncSxInstanceModel* instances;
97     GtkTreeView* tree_view;
98     GList *selected_list;
99 
100 } GncPluginPageSxListPrivate;
101 
102 #define GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(o)  \
103    ((GncPluginPageSxListPrivate*)g_type_instance_get_private ((GTypeInstance*)o, GNC_TYPE_PLUGIN_PAGE_SX_LIST))
104 
105 static GObjectClass *parent_class = NULL;
106 
107 /************************************************************
108  *                        Prototypes                        *
109  ************************************************************/
110 /* Plugin Actions */
111 static void gnc_plugin_page_sx_list_class_init (GncPluginPageSxListClass *klass);
112 static void gnc_plugin_page_sx_list_init (GncPluginPageSxList *plugin_page);
113 static void gnc_plugin_page_sx_list_dispose (GObject *object);
114 static void gnc_plugin_page_sx_list_finalize (GObject *object);
115 
116 static GtkWidget *gnc_plugin_page_sx_list_create_widget (GncPluginPage *plugin_page);
117 static void gnc_plugin_page_sx_list_destroy_widget (GncPluginPage *plugin_page);
118 static void gnc_plugin_page_sx_list_save_page (GncPluginPage *plugin_page, GKeyFile *file, const gchar *group);
119 static GncPluginPage *gnc_plugin_page_sx_list_recreate_page (GtkWidget *window, GKeyFile *file, const gchar *group);
120 
121 static void gppsl_row_activated_cb (GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, gpointer user_data);
122 
123 static void gnc_plugin_page_sx_list_cmd_new (GtkAction *action, GncPluginPageSxList *page);
124 static void gnc_plugin_page_sx_list_cmd_edit (GtkAction *action, GncPluginPageSxList *page);
125 #ifdef REGISTER2_ENABLED
126 /*################## Added for Reg2 #################*/
127 static void gnc_plugin_page_sx_list_cmd_new2 (GtkAction *action, GncPluginPageSxList *page);
128 static void gnc_plugin_page_sx_list_cmd_edit2 (GtkAction *action, GncPluginPageSxList *page);
129 /*################## Added for Reg2 #################*/
130 #endif
131 static void gnc_plugin_page_sx_list_cmd_delete (GtkAction *action, GncPluginPageSxList *page);
132 static void gnc_plugin_page_sx_list_cmd_refresh (GtkAction *action, GncPluginPageSxList *page);
133 
134 /* Command callbacks */
135 static GtkActionEntry gnc_plugin_page_sx_list_actions [] =
136 {
137     { "SxListAction", NULL, N_("_Scheduled"), NULL, NULL, NULL },
138     {
139         "SxListNewAction", GNC_ICON_NEW_ACCOUNT, N_("_New"), NULL,
140         N_("Create a new scheduled transaction"), G_CALLBACK(gnc_plugin_page_sx_list_cmd_new)
141     },
142 #ifdef REGISTER2_ENABLED
143 /*################## Added for Reg2 #################*/
144     {
145         "SxListNewAction2", GNC_ICON_NEW_ACCOUNT, N_("_New 2"), NULL,
146         N_("Create a new scheduled transaction 2"), G_CALLBACK(gnc_plugin_page_sx_list_cmd_new2)
147     },
148 /*################## Added for Reg2 #################*/
149 #endif
150     {
151         "SxListEditAction", GNC_ICON_EDIT_ACCOUNT, N_("_Edit"), NULL,
152         N_("Edit the selected scheduled transaction"), G_CALLBACK(gnc_plugin_page_sx_list_cmd_edit)
153     },
154 #ifdef REGISTER2_ENABLED
155 /*################## Added for Reg2 #################*/
156     {
157         "SxListEditAction2", GNC_ICON_EDIT_ACCOUNT, N_("_Edit 2"), NULL,
158         N_("Edit the selected scheduled transaction 2"), G_CALLBACK(gnc_plugin_page_sx_list_cmd_edit2)
159     },
160 /*################## Added for Reg2 #################*/
161 #endif
162     {
163         "SxListDeleteAction", GNC_ICON_DELETE_ACCOUNT, N_("_Delete"), NULL,
164         N_("Delete the selected scheduled transaction"), G_CALLBACK(gnc_plugin_page_sx_list_cmd_delete)
165     },
166 
167     /* View menu */
168 
169     {
170         "ViewRefreshAction", "view-refresh", N_("_Refresh"), "<primary>r",
171         N_("Refresh this window"), G_CALLBACK(gnc_plugin_page_sx_list_cmd_refresh)
172     },
173 };
174 /** The number of actions provided by this plugin. */
175 static guint gnc_plugin_page_sx_list_n_actions = G_N_ELEMENTS(gnc_plugin_page_sx_list_actions);
176 
177 GncPluginPage *
gnc_plugin_page_sx_list_new(void)178 gnc_plugin_page_sx_list_new (void)
179 {
180     GncPluginPageSxList *plugin_page;
181     const GList *object = gnc_gobject_tracking_get_list (GNC_PLUGIN_PAGE_SX_LIST_NAME);
182     if (object && GNC_IS_PLUGIN_PAGE_SX_LIST (object->data))
183         plugin_page = GNC_PLUGIN_PAGE_SX_LIST (object->data);
184     else
185     {
186         plugin_page = g_object_new (GNC_TYPE_PLUGIN_PAGE_SX_LIST, NULL);
187     }
188     return GNC_PLUGIN_PAGE(plugin_page);
189 }
190 
191 
192 /**
193  * Whenever the current page is changed, if a sx page is
194  * the current page, set focus on the tree view.
195  */
196 static gboolean
gnc_plugin_page_sx_list_focus_widget(GncPluginPage * sx_plugin_page)197 gnc_plugin_page_sx_list_focus_widget (GncPluginPage *sx_plugin_page)
198 {
199     if (GNC_IS_PLUGIN_PAGE_SX_LIST(sx_plugin_page))
200     {
201         GncPluginPageSxListPrivate *priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(sx_plugin_page);
202         GtkTreeView *tree_view = priv->tree_view;
203 
204         if (GTK_IS_TREE_VIEW(tree_view))
205         {
206             if (!gtk_widget_is_focus (GTK_WIDGET(tree_view)))
207                 gtk_widget_grab_focus (GTK_WIDGET(tree_view));
208         }
209     }
210     return FALSE;
211 }
212 
G_DEFINE_TYPE_WITH_PRIVATE(GncPluginPageSxList,gnc_plugin_page_sx_list,GNC_TYPE_PLUGIN_PAGE)213 G_DEFINE_TYPE_WITH_PRIVATE(GncPluginPageSxList, gnc_plugin_page_sx_list, GNC_TYPE_PLUGIN_PAGE)
214 
215 static void
216 gnc_plugin_page_sx_list_class_init (GncPluginPageSxListClass *klass)
217 {
218     GObjectClass *object_class = G_OBJECT_CLASS(klass);
219     GncPluginPageClass *gnc_plugin_class = GNC_PLUGIN_PAGE_CLASS(klass);
220 
221     parent_class = g_type_class_peek_parent(klass);
222 
223     object_class->dispose = gnc_plugin_page_sx_list_dispose;
224     object_class->finalize = gnc_plugin_page_sx_list_finalize;
225 
226     gnc_plugin_class->tab_icon        = GNC_ICON_ACCOUNT;
227     gnc_plugin_class->plugin_name     = GNC_PLUGIN_PAGE_SX_LIST_NAME;
228     gnc_plugin_class->create_widget   = gnc_plugin_page_sx_list_create_widget;
229     gnc_plugin_class->destroy_widget  = gnc_plugin_page_sx_list_destroy_widget;
230     gnc_plugin_class->save_page       = gnc_plugin_page_sx_list_save_page;
231     gnc_plugin_class->recreate_page   = gnc_plugin_page_sx_list_recreate_page;
232     gnc_plugin_class->focus_page_function = gnc_plugin_page_sx_list_focus_widget;
233 }
234 
235 
236 static void
gnc_plugin_page_sx_list_init(GncPluginPageSxList * plugin_page)237 gnc_plugin_page_sx_list_init (GncPluginPageSxList *plugin_page)
238 {
239     GtkActionGroup *action_group;
240     GncPluginPage *parent;
241 
242     /* Init parent declared variables */
243     parent = GNC_PLUGIN_PAGE(plugin_page);
244 #ifdef REGISTER2_ENABLED
245     g_object_set(G_OBJECT(plugin_page),
246                  "page-name",      _("Scheduled Transactions"),
247                  "page-uri",       "default:",
248                  "ui-description", "gnc-plugin-page-sx-list2-ui.xml",
249                  NULL);
250 #else
251     g_object_set(G_OBJECT(plugin_page),
252                  "page-name",      _("Scheduled Transactions"),
253                  "page-uri",       "default:",
254                  "ui-description", "gnc-plugin-page-sx-list-ui.xml",
255                  NULL);
256 #endif
257 
258     gnc_plugin_page_add_book (parent, gnc_get_current_book());
259     action_group =
260         gnc_plugin_page_create_action_group (parent,
261                                              "GncPluginPageSxListActions");
262     gtk_action_group_add_actions (action_group,
263                                   gnc_plugin_page_sx_list_actions,
264                                   gnc_plugin_page_sx_list_n_actions,
265                                   plugin_page);
266     /* gnc_plugin_init_short_names (action_group, toolbar_labels); */
267 }
268 
269 
270 static void
gnc_plugin_page_sx_list_dispose(GObject * object)271 gnc_plugin_page_sx_list_dispose (GObject *object)
272 {
273     GncPluginPageSxList *page;
274     GncPluginPageSxListPrivate *priv;
275 
276     page = GNC_PLUGIN_PAGE_SX_LIST(object);
277     g_return_if_fail (GNC_IS_PLUGIN_PAGE_SX_LIST(page));
278     priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(page);
279     g_return_if_fail (priv != NULL);
280 
281     g_return_if_fail (!priv->disposed);
282     priv->disposed = TRUE;
283 
284     g_object_unref (G_OBJECT(priv->dense_cal_model));
285     priv->dense_cal_model = NULL;
286     g_object_unref (GTK_WIDGET(priv->gdcal));
287     priv->gdcal = NULL;
288     g_object_unref (G_OBJECT(priv->instances));
289     priv->instances = NULL;
290 
291     G_OBJECT_CLASS(parent_class)->dispose (object);
292 }
293 
294 
295 static void
gnc_plugin_page_sx_list_finalize(GObject * object)296 gnc_plugin_page_sx_list_finalize (GObject *object)
297 {
298     GncPluginPageSxList *page;
299     GncPluginPageSxListPrivate *priv;
300 
301     page = GNC_PLUGIN_PAGE_SX_LIST(object);
302     g_return_if_fail (GNC_IS_PLUGIN_PAGE_SX_LIST(page));
303     priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(page);
304     g_return_if_fail (priv != NULL);
305 
306     G_OBJECT_CLASS(parent_class)->finalize (object);
307 }
308 
309 
310 /* Virtual Functions */
311 static void
gnc_plugin_page_sx_list_refresh_cb(GHashTable * changes,gpointer user_data)312 gnc_plugin_page_sx_list_refresh_cb (GHashTable *changes, gpointer user_data)
313 {
314     GncPluginPageSxList *page = user_data;
315     GncPluginPageSxListPrivate *priv;
316 
317     g_return_if_fail (GNC_IS_PLUGIN_PAGE_SX_LIST(page));
318 
319     /* We're only looking for forced updates here. */
320     if (changes)
321         return;
322 
323     priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(page);
324     gtk_widget_queue_draw (priv->widget);
325 }
326 
327 
328 static void
gnc_plugin_page_sx_list_close_cb(gpointer user_data)329 gnc_plugin_page_sx_list_close_cb (gpointer user_data)
330 {
331     GncPluginPage *plugin_page = GNC_PLUGIN_PAGE(user_data);
332     gnc_main_window_close_page (plugin_page);
333 }
334 
335 
336 static void
gppsl_selection_changed_cb(GtkTreeSelection * selection,gpointer user_data)337 gppsl_selection_changed_cb (GtkTreeSelection *selection, gpointer user_data)
338 {
339     GncPluginPage *page;
340     GtkAction *edit_action, *delete_action;
341     gboolean selection_state = TRUE;
342 
343     page = GNC_PLUGIN_PAGE(user_data);
344     edit_action = gnc_plugin_page_get_action (page, "SxListEditAction");
345     delete_action = gnc_plugin_page_get_action (page, "SxListDeleteAction");
346     selection_state
347     = gtk_tree_selection_count_selected_rows (selection) == 0
348       ? FALSE
349       : TRUE;
350     gtk_action_set_sensitive (edit_action, selection_state);
351     gtk_action_set_sensitive (delete_action, selection_state);
352 }
353 
354 
355 static void
gppsl_update_selected_list(GncPluginPageSxList * page,gboolean reset,SchedXaction * sx)356 gppsl_update_selected_list (GncPluginPageSxList *page, gboolean reset, SchedXaction *sx)
357 {
358     GncPluginPageSxListPrivate *priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(page);
359 
360     if (reset && priv->selected_list)
361     {
362         g_list_free (priv->selected_list);
363         priv->selected_list = NULL;
364     }
365     if (sx)
366         priv->selected_list = g_list_prepend (priv->selected_list, sx);
367 }
368 
369 
370 static void
gppsl_model_populated_cb(GtkTreeModel * tree_model,GncPluginPageSxList * page)371 gppsl_model_populated_cb (GtkTreeModel *tree_model, GncPluginPageSxList *page)
372 {
373     GncPluginPageSxListPrivate *priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(page);
374     GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(priv->tree_view));
375     gboolean found = FALSE;
376 
377     if (priv->selected_list)
378     {
379         // walk the list to see if we can reselect the sx
380         for (GList *list = priv->selected_list; list != NULL; list = list->next)
381         {
382             SchedXaction *sx = list->data;
383             GtkTreePath *path = gtk_tree_path_new_first ();
384 
385             // loop through the model trying to find selected sx's
386             while (gnc_tree_view_path_is_valid (GNC_TREE_VIEW(priv->tree_view), path))
387             {
388                 SchedXaction *sx_tmp = gnc_tree_view_sx_list_get_sx_from_path (
389                                            GNC_TREE_VIEW_SX_LIST(priv->tree_view), path);
390                 if (sx_tmp == sx)
391                 {
392                     found = TRUE;
393                     break;
394                 }
395                 gtk_tree_path_next (path);
396             }
397             if (found)
398                 gtk_tree_selection_select_path (selection, path);
399 
400             gtk_tree_path_free (path);
401         }
402     }
403     // this could be on load or if sx is deleted
404     if (!found)
405     {
406         GtkTreePath *path = gtk_tree_path_new_first ();
407         gtk_tree_selection_select_path (selection, path);
408         gtk_tree_path_free (path);
409     }
410 }
411 
412 
413 static GtkWidget *
gnc_plugin_page_sx_list_create_widget(GncPluginPage * plugin_page)414 gnc_plugin_page_sx_list_create_widget (GncPluginPage *plugin_page)
415 {
416     GncPluginPageSxList *page;
417     GncPluginPageSxListPrivate *priv;
418     GtkWidget *widget;
419     GtkWidget *vbox;
420     GtkWidget *label;
421     GtkWidget *swin;
422 
423     page = GNC_PLUGIN_PAGE_SX_LIST(plugin_page);
424     priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(page);
425     if (priv->widget != NULL)
426         return priv->widget;
427 
428     /* Create Vpaned widget for top level */
429     widget = gtk_paned_new (GTK_ORIENTATION_VERTICAL);
430     priv->widget = widget;
431     gtk_widget_show (priv->widget);
432 
433     // Set the name for this widget so it can be easily manipulated with css
434     gtk_widget_set_name (GTK_WIDGET(priv->widget), "gnc-id-sx-page");
435 
436     /* Add vbox and label */
437     vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
438     gtk_box_set_homogeneous (GTK_BOX(vbox), FALSE);
439     gtk_paned_pack1 (GTK_PANED(widget), vbox, TRUE, FALSE);
440 
441     label = gtk_label_new (_("Transactions"));
442     gnc_widget_style_context_add_class (GTK_WIDGET(label), "gnc-class-strong");
443     gtk_widget_set_margin_start (GTK_WIDGET(label), 6);
444     gnc_label_set_alignment (label, 0.0, 0);
445     gtk_widget_show (label);
446     gtk_box_pack_start (GTK_BOX(vbox), label, FALSE, FALSE, 0);
447     gtk_widget_show (vbox);
448 
449     /* Create scrolled window for top area */
450     swin = gtk_scrolled_window_new (NULL, NULL);
451     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(swin),
452                                     GTK_POLICY_AUTOMATIC,
453                                     GTK_POLICY_AUTOMATIC);
454     gtk_box_pack_start (GTK_BOX(vbox), swin, TRUE, TRUE, 5);
455     gtk_widget_show (swin);
456 
457     {
458         // gint half_way;
459         // half_way = plugin_page->notebook_page->allocation.height * 0.5;
460         // fixme; get a real value:
461         gtk_paned_set_position (GTK_PANED(priv->widget), 160);
462     }
463 
464     {
465         GDate end;
466         g_date_clear (&end, 1);
467         gnc_gdate_set_today (&end);
468         g_date_add_years (&end, 1);
469         priv->instances = GNC_SX_INSTANCE_MODEL(gnc_sx_get_instances (&end, TRUE));
470     }
471 
472     {
473         GtkAction *edit_action, *delete_action;
474         edit_action = gnc_plugin_page_get_action (GNC_PLUGIN_PAGE(page), "SxListEditAction");
475         delete_action = gnc_plugin_page_get_action (GNC_PLUGIN_PAGE(page), "SxListDeleteAction");
476         gtk_action_set_sensitive (edit_action, FALSE);
477         gtk_action_set_sensitive (delete_action, FALSE);
478     }
479 
480     {
481         GtkTreeSelection *selection;
482         GtkTreePath *path = gtk_tree_path_new_first ();
483 
484         priv->tree_view = GTK_TREE_VIEW(gnc_tree_view_sx_list_new (priv->instances));
485         g_object_set (G_OBJECT(priv->tree_view),
486                       "state-section", STATE_SECTION,
487                       "show-column-menu", TRUE,
488                       NULL);
489         gtk_container_add (GTK_CONTAINER( swin ), GTK_WIDGET(priv->tree_view));
490 
491         selection = gtk_tree_view_get_selection (priv->tree_view);
492         gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
493         gtk_tree_selection_select_path (selection, path);
494         gtk_tree_path_free (path);
495 
496         g_signal_connect (G_OBJECT(selection), "changed", (GCallback)gppsl_selection_changed_cb, (gpointer)page);
497         g_signal_connect (G_OBJECT(priv->tree_view), "row-activated", (GCallback)gppsl_row_activated_cb, (gpointer)page);
498         g_signal_connect (G_OBJECT(gtk_tree_view_get_model (GTK_TREE_VIEW(priv->tree_view))),
499                           "model-populated", (GCallback)gppsl_model_populated_cb, (gpointer)page);
500     }
501 
502     /* Add vbox and label */
503     vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
504     gtk_box_set_homogeneous (GTK_BOX(vbox), FALSE);
505     gtk_paned_pack2 (GTK_PANED(widget), vbox, TRUE, FALSE);
506 
507     label = gtk_label_new (_("Upcoming Transactions"));
508     gnc_widget_style_context_add_class (GTK_WIDGET(label), "gnc-class-strong");
509     gtk_widget_set_margin_start (GTK_WIDGET(label), 6);
510     gnc_label_set_alignment (label, 0.0, 0);
511     gtk_widget_show (label);
512 
513     gtk_box_pack_start (GTK_BOX(vbox), label, FALSE, FALSE, 0);
514     gtk_widget_show (vbox);
515 
516     /* Create scrolled window for bottom area */
517     swin = gtk_scrolled_window_new (NULL, NULL);
518     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(swin),
519                                     GTK_POLICY_AUTOMATIC,
520                                     GTK_POLICY_AUTOMATIC);
521     gtk_box_pack_start (GTK_BOX(vbox), swin, TRUE, TRUE, 5);
522     gtk_widget_show (swin);
523 
524     {
525         priv->dense_cal_model = gnc_sx_instance_dense_cal_adapter_new (GNC_SX_INSTANCE_MODEL(priv->instances));
526         priv->gdcal = GNC_DENSE_CAL(gnc_dense_cal_new_with_model (GNC_DENSE_CAL_MODEL(priv->dense_cal_model)));
527         g_object_ref_sink (priv->gdcal);
528 
529         gnc_dense_cal_set_months_per_col (priv->gdcal, 4);
530         gnc_dense_cal_set_num_months (priv->gdcal, 12);
531 
532         gtk_container_add (GTK_CONTAINER(swin), GTK_WIDGET(priv->gdcal));
533     }
534 
535     priv->gnc_component_id = gnc_register_gui_component ("plugin-page-sx-list",
536                              gnc_plugin_page_sx_list_refresh_cb,
537                              gnc_plugin_page_sx_list_close_cb,
538                              page);
539     gnc_gui_component_set_session (priv->gnc_component_id,
540                                    gnc_get_current_session ());
541 
542     g_signal_connect (G_OBJECT(plugin_page), "inserted",
543                       G_CALLBACK(gnc_plugin_page_inserted_cb),
544                       NULL);
545 
546     return priv->widget;
547 }
548 
549 
550 static void
gnc_plugin_page_sx_list_destroy_widget(GncPluginPage * plugin_page)551 gnc_plugin_page_sx_list_destroy_widget (GncPluginPage *plugin_page)
552 {
553     GncPluginPageSxList *page;
554     GncPluginPageSxListPrivate *priv;
555 
556     page = GNC_PLUGIN_PAGE_SX_LIST(plugin_page);
557     priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(page);
558 
559     // Remove the page_changed signal callback
560     gnc_plugin_page_disconnect_page_changed (GNC_PLUGIN_PAGE(plugin_page));
561 
562     // Remove the page focus idle function if present
563     g_idle_remove_by_data (plugin_page);
564 
565     if (priv->widget)
566     {
567         g_object_unref (G_OBJECT(priv->widget));
568         priv->widget = NULL;
569     }
570 
571     if (priv->selected_list)
572         g_list_free (priv->selected_list);
573 
574     if (priv->gnc_component_id)
575     {
576         gnc_unregister_gui_component (priv->gnc_component_id);
577         priv->gnc_component_id = 0;
578     }
579 }
580 
581 
582 /** Save enough information about this page that it can be recreated next time
583  * the user starts gnucash.
584  * @param plugin_page The page to save.
585  * @param key_file A pointer to the GKeyFile data structure where the
586  * page information should be written.
587  * @param group_name The group name to use when saving data.
588  **/
589 static void
gnc_plugin_page_sx_list_save_page(GncPluginPage * plugin_page,GKeyFile * key_file,const gchar * group_name)590 gnc_plugin_page_sx_list_save_page (GncPluginPage *plugin_page,
591                                    GKeyFile *key_file,
592                                    const gchar *group_name)
593 {
594     GncPluginPageSxList *page;
595     GncPluginPageSxListPrivate *priv;
596 
597     g_return_if_fail (GNC_IS_PLUGIN_PAGE_SX_LIST(plugin_page));
598     g_return_if_fail (key_file != NULL);
599     g_return_if_fail (group_name != NULL);
600 
601     page = GNC_PLUGIN_PAGE_SX_LIST(plugin_page);
602     priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(page);
603 
604     g_key_file_set_integer (key_file, group_name, "dense_cal_num_months",
605                             gnc_dense_cal_get_num_months (priv->gdcal));
606 
607     g_key_file_set_integer (key_file, group_name, "paned_position",
608                             gtk_paned_get_position (GTK_PANED(priv->widget)));
609 }
610 
611 
612 /**
613  * Create a new sx list page based on the information saved during a previous
614  * instantiation of gnucash.
615  *  @param window The window where this page should be installed.
616  *  @param key_file A pointer to the GKeyFile data structure where the
617  *  page information should be read.
618  *  @param group_name The group name to use when restoring data.
619  **/
620 static GncPluginPage *
gnc_plugin_page_sx_list_recreate_page(GtkWidget * window,GKeyFile * key_file,const gchar * group_name)621 gnc_plugin_page_sx_list_recreate_page (GtkWidget *window,
622                                        GKeyFile *key_file,
623                                        const gchar *group_name)
624 {
625     GncPluginPageSxList *page;
626     GncPluginPageSxListPrivate *priv;
627 
628     g_return_val_if_fail (key_file, NULL);
629     g_return_val_if_fail (group_name, NULL);
630 
631     /* Create the new page. */
632     page = GNC_PLUGIN_PAGE_SX_LIST(gnc_plugin_page_sx_list_new ());
633     priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(page);
634 
635     /* Install it now so we can them manipulate the created widget */
636     gnc_main_window_open_page (GNC_MAIN_WINDOW(window), GNC_PLUGIN_PAGE(page));
637 
638     {
639         GError *err = NULL;
640         gint num_months = g_key_file_get_integer (key_file, group_name, "dense_cal_num_months", &err);
641         if (err == NULL)
642             gnc_dense_cal_set_num_months (priv->gdcal, num_months);
643         else
644             g_error_free (err);
645     }
646 
647     {
648         GError *err = NULL;
649         gint paned_position = g_key_file_get_integer (key_file, group_name,
650                               "paned_position", &err);
651         if (err == NULL)
652             gtk_paned_set_position (GTK_PANED(priv->widget), paned_position);
653         else
654             g_error_free (err);
655     }
656 
657     return GNC_PLUGIN_PAGE(page);
658 }
659 
660 
661 static void
gnc_plugin_page_sx_list_cmd_new(GtkAction * action,GncPluginPageSxList * page)662 gnc_plugin_page_sx_list_cmd_new (GtkAction *action, GncPluginPageSxList *page)
663 {
664     GtkWindow *window = GTK_WINDOW(gnc_plugin_page_get_window (GNC_PLUGIN_PAGE(page)));
665     SchedXaction *new_sx;
666     gboolean new_sx_flag = TRUE;
667 
668     new_sx = xaccSchedXactionMalloc (gnc_get_current_book());
669     {
670         GDate now;
671         Recurrence *r = g_new0 (Recurrence, 1);
672         GList *schedule;
673 
674         g_date_clear (&now, 1);
675         gnc_gdate_set_today (&now);
676         recurrenceSet (r, 1, PERIOD_MONTH, &now, WEEKEND_ADJ_NONE);
677         schedule = gnc_sx_get_schedule (new_sx);
678         schedule = g_list_append (schedule, r);
679         gnc_sx_set_schedule (new_sx, schedule);
680     }
681     gnc_ui_scheduled_xaction_editor_dialog_create (window, new_sx, new_sx_flag);
682     gppsl_update_selected_list (page, TRUE, new_sx);
683 }
684 
685 #ifdef REGISTER2_ENABLED
686 /*################## Added for Reg2 #################*/
687 static void
gnc_plugin_page_sx_list_cmd_new2(GtkAction * action,GncPluginPageSxList * page)688 gnc_plugin_page_sx_list_cmd_new2 (GtkAction *action, GncPluginPageSxList *page)
689 {
690     GtkWindow *window = GTK_WINDOW (gnc_plugin_page_get_window (GNC_PLUGIN_PAGE(page)));
691     SchedXaction *new_sx;
692     gboolean new_sx_flag = TRUE;
693 
694     new_sx = xaccSchedXactionMalloc (gnc_get_current_book());
695     {
696         GDate now;
697         Recurrence *r = g_new0 (Recurrence, 1);
698         GList *schedule;
699 
700         g_date_clear (&now, 1);
701         gnc_gdate_set_today (&now);
702         recurrenceSet (r, 1, PERIOD_MONTH, &now, WEEKEND_ADJ_NONE);
703         schedule = gnc_sx_get_schedule (new_sx);
704         schedule = g_list_append (schedule, r);
705         gnc_sx_set_schedule (new_sx, schedule);
706     }
707     gnc_ui_scheduled_xaction_editor_dialog_create2 (window, new_sx, new_sx_flag);
708 }
709 /*################## Added for Reg2 #################*/
710 #endif
711 
712 static void
gnc_plugin_page_sx_list_cmd_refresh(GtkAction * action,GncPluginPageSxList * page)713 gnc_plugin_page_sx_list_cmd_refresh (GtkAction *action, GncPluginPageSxList *page)
714 {
715     GncPluginPageSxListPrivate *priv;
716 
717     g_return_if_fail (GNC_IS_PLUGIN_PAGE_SX_LIST(page));
718 
719     priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(page);
720     gtk_widget_queue_draw (priv->widget);
721 }
722 
723 static void
_edit_sx(gpointer data,gpointer user_data)724 _edit_sx(gpointer data, gpointer user_data)
725 {
726     gnc_ui_scheduled_xaction_editor_dialog_create (GTK_WINDOW(user_data),
727         (SchedXaction*)data, FALSE);
728 }
729 
730 #ifdef REGISTER2_ENABLED
731 /*################## Added for Reg2 #################*/
732 static void
_edit_sx2(gpointer data,gpointer user_data)733 _edit_sx2 (gpointer data, gpointer user_data)
734 {
735     gnc_ui_scheduled_xaction_editor_dialog_create2 (GTK_WINDOW(user_data),
736         (SchedXaction*)data, FALSE);
737 }
738 /*################## Added for Reg2 #################*/
739 #endif
740 
741 static SchedXaction*
_argument_reorder_fn(GtkTreePath * list_path_data,GncTreeViewSxList * user_tree_view)742 _argument_reorder_fn (GtkTreePath* list_path_data, GncTreeViewSxList* user_tree_view)
743 {
744     return gnc_tree_view_sx_list_get_sx_from_path (user_tree_view, list_path_data);
745 }
746 
747 
748 static void
gnc_plugin_page_sx_list_cmd_edit(GtkAction * action,GncPluginPageSxList * page)749 gnc_plugin_page_sx_list_cmd_edit (GtkAction *action, GncPluginPageSxList *page)
750 {
751     GncPluginPageSxListPrivate *priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(page);
752     GtkWindow *window = GTK_WINDOW(gnc_plugin_page_get_window (GNC_PLUGIN_PAGE(page)));
753     GtkTreeSelection *selection;
754     GList *selected_paths, *to_edit;
755     GtkTreeModel *model;
756 
757     selection = gtk_tree_view_get_selection (priv->tree_view);
758     selected_paths = gtk_tree_selection_get_selected_rows (selection, &model);
759     if (!gnc_list_length_cmp (selected_paths, 0))
760     {
761         g_warning ("no selection edit.");
762         return;
763     }
764 
765     to_edit = gnc_g_list_map (selected_paths,
766                               (GncGMapFunc)_argument_reorder_fn,
767                               priv->tree_view);
768 
769     gppsl_update_selected_list (page, TRUE, NULL);
770     for (GList *list = to_edit; list != NULL; list = list->next)
771     {
772         DEBUG ("to-edit [%s]\n", xaccSchedXactionGetName ((SchedXaction*)list->data));
773         gppsl_update_selected_list (page, FALSE, list->data);
774     }
775 
776     g_list_foreach (to_edit, (GFunc)_edit_sx, window);
777     g_list_free (to_edit);
778     g_list_foreach (selected_paths, (GFunc)gtk_tree_path_free, NULL);
779     g_list_free (selected_paths);
780 }
781 
782 #ifdef REGISTER2_ENABLED
783 /*################## Added for Reg2 #################*/
784 static void
gnc_plugin_page_sx_list_cmd_edit2(GtkAction * action,GncPluginPageSxList * page)785 gnc_plugin_page_sx_list_cmd_edit2 (GtkAction *action, GncPluginPageSxList *page)
786 {
787     GncPluginPageSxListPrivate *priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(page);
788     GtkWindow *window = GTK_WINDOW(gnc_plugin_page_get_window (GNC_PLUGIN_PAGE(page)));
789     GtkTreeSelection *selection;
790     GList *selected_paths, *to_edit;
791     GtkTreeModel *model;
792 
793     selection = gtk_tree_view_get_selection (priv->tree_view);
794     selected_paths = gtk_tree_selection_get_selected_rows (selection, &model);
795     if (!gnc_list_length_cmp (selected_paths, 0))
796     {
797         g_warning ("no selection edit.");
798         return;
799     }
800 
801     to_edit = gnc_g_list_map (selected_paths,
802                              (GncGMapFunc)_argument_reorder_fn,
803                               priv->tree_view);
804     g_list_foreach (to_edit, (GFunc)_edit_sx2, window);
805     g_list_free (to_edit);
806     g_list_foreach (selected_paths, (GFunc)gtk_tree_path_free, NULL);
807     g_list_free (selected_paths);
808 }
809 /*################## Added for Reg2 #################*/
810 #endif
811 
812 static void
gppsl_row_activated_cb(GtkTreeView * tree_view,GtkTreePath * path,GtkTreeViewColumn * column,gpointer user_data)813 gppsl_row_activated_cb (GtkTreeView *tree_view,
814                         GtkTreePath *path,
815                         GtkTreeViewColumn *column,
816                         gpointer user_data)
817 {
818     GncPluginPageSxList *page = GNC_PLUGIN_PAGE_SX_LIST(user_data);
819     GncPluginPageSxListPrivate *priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(page);
820     GtkWindow *window = GTK_WINDOW(gnc_plugin_page_get_window (GNC_PLUGIN_PAGE(page)));
821 
822     SchedXaction *sx = gnc_tree_view_sx_list_get_sx_from_path (
823                            GNC_TREE_VIEW_SX_LIST(priv->tree_view), path);
824     gnc_ui_scheduled_xaction_editor_dialog_create (window, sx, FALSE);
825     gppsl_update_selected_list (page, TRUE, sx);
826 }
827 
828 
829 static void
_destroy_sx(gpointer data,gpointer user_data)830 _destroy_sx(gpointer data, gpointer user_data)
831 {
832     SchedXactions *sxes;
833     SchedXaction *sx = (SchedXaction*)data;
834     QofBook *book;
835     book = gnc_get_current_book ();
836     sxes = gnc_book_get_schedxactions (book);
837     gnc_sxes_del_sx (sxes, sx);
838     gnc_sx_begin_edit (sx);
839     xaccSchedXactionDestroy (sx);
840 }
841 
842 
843 static void
gnc_plugin_page_sx_list_cmd_delete(GtkAction * action,GncPluginPageSxList * page)844 gnc_plugin_page_sx_list_cmd_delete (GtkAction *action, GncPluginPageSxList *page)
845 {
846     GncPluginPageSxListPrivate *priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(page);
847     GtkTreeSelection *selection;
848     GList *selected_paths, *to_delete = NULL;
849     GtkTreeModel *model;
850     GtkWindow *window;
851     gchar *message = NULL;
852     gint length;
853 
854     selection = gtk_tree_view_get_selection (priv->tree_view);
855     selected_paths = gtk_tree_selection_get_selected_rows (selection, &model);
856     if (!gnc_list_length_cmp (selected_paths, 0))
857     {
858         g_warning ("no selection for delete.");
859         return;
860     }
861 
862     to_delete = gnc_g_list_map (selected_paths,
863                                 (GncGMapFunc)_argument_reorder_fn,
864                                  priv->tree_view);
865 
866     window = GTK_WINDOW(gnc_plugin_page_get_window (GNC_PLUGIN_PAGE(page)));
867 
868     length = g_list_length (to_delete);
869 
870     /* Translators: This is a ngettext(3) message, %d is the number of scheduled transactions deleted */
871     message = g_strdup_printf (ngettext ("Do you really want to delete this scheduled transaction?",
872                                          "Do you really want to delete %d scheduled transactions?",
873                                           length), length);
874 
875     if (gnc_verify_dialog (window, FALSE, "%s", message))
876     {
877         gppsl_update_selected_list (page, TRUE, NULL);
878         for (GList *list = to_delete; list != NULL; list = list->next)
879         {
880             DEBUG("to-delete [%s]\n", xaccSchedXactionGetName ((SchedXaction*)list->data));
881             gppsl_update_selected_list (page, FALSE, list->data);
882         }
883         g_list_foreach (to_delete, (GFunc)_destroy_sx, NULL);
884     }
885 
886     g_free (message);
887     g_list_free (to_delete);
888     g_list_foreach (selected_paths, (GFunc)gtk_tree_path_free, NULL);
889     g_list_free (selected_paths);
890 }
891 
892 /** @} */
893 /** @} */
894