GnuCash c935c2f+
Loading...
Searching...
No Matches
dialog-sx-since-last-run.c
1/********************************************************************\
2 * dialog-sx-since-last-run.c : dialog for scheduled transaction *
3 * since-last-run processing. *
4 * Copyright (C) 2006 Joshua Sled <jsled@asynchronous.org> *
5 * *
6 * This program is free software; you can redistribute it and/or *
7 * modify it under the terms of version 2 and/or version 3 of the *
8 * GNU General Public License as published by the Free Software *
9 * Foundation. *
10 * *
11 * As a special exception, permission is granted to link the binary *
12 * module resultant from this code with the OpenSSL project's *
13 * "OpenSSL" library (or modified versions of it that use the same *
14 * license as the "OpenSSL" library), and distribute the linked *
15 * executable. You must obey the GNU General Public License in all *
16 * respects for all of the code used other than "OpenSSL". If you *
17 * modify this file, you may extend this exception to your version *
18 * of the file, but you are not obligated to do so. If you do not *
19 * wish to do so, delete this exception statement from your version *
20 * of this file. *
21 * *
22 * This program is distributed in the hope that it will be useful, *
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
25 * GNU General Public License for more details. *
26 * *
27 * You should have received a copy of the GNU General Public License*
28 * along with this program; if not, contact: *
29 * *
30 * Free Software Foundation Voice: +1-617-542-5942 *
31 * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
32 * Boston, MA 02110-1301, USA gnu@gnu.org *
33\********************************************************************/
34
35#include <config.h>
36#include <glib.h>
37#include <gtk/gtk.h>
38
39#include "dialog-utils.h"
41#include "dialog-sx-since-last-run.h"
42
43#include "gnc-prefs.h"
44#include "gnc-ui.h"
45#include "gnc-ui-util.h"
46#include "gnc-glib-utils.h"
47#include "Query.h"
48#include "qof.h"
49#include "gnc-ledger-display.h"
51#include "gnc-main-window.h"
52#include "gnc-component-manager.h"
53#include "gnc-gui-query.h"
54#include "gnc-session.h"
55
56#undef G_LOG_DOMAIN
57#define G_LOG_DOMAIN "gnc.gui.sx.slr"
58
59G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_GUI_SX;
60
61#define DIALOG_SX_SINCE_LAST_RUN_CM_CLASS "dialog-sx-since-last-run"
62
63#define GNC_PREF_SET_REVIEW "review-transactions"
64#define GNC_PREF_SLR_SORT_COL "sort-column"
65#define GNC_PREF_SLR_SORT_ASC "sort-ascending"
66#define GNC_PREF_SLR_SORT_DEPTH "sort-depth"
67
69{
70 GtkWidget *dialog;
71 gint component_id;
72 GncSxSlrTreeModelAdapter *editing_model;
73 GtkTreeView *instance_view;
74 GtkToggleButton *review_created_txns_toggle;
75 GList *created_txns;
76
77 GtkCellEditable *temp_ce; // used when editing values
78 gint sort_selection_depth; // used when sorting transaction column
79};
80
81/* ------------------------------------------------------------ */
82
83
85{
86 GObject parent;
87
88 /* protected: */
89 gulong updated_cb_id;
90 gboolean disposed;
91
92 GncSxInstanceModel *instances;
93 GtkTreeStore *real;
94};
95
96typedef struct _GncSxSlrTreeModelAdapterClass
97{
98 GObjectClass parent;
100
101GType gnc_sx_slr_tree_model_adapter_get_type (void);
102static void gnc_sx_slr_tree_model_adapter_interface_init (GtkTreeModelIface *tree_model);
103GncSxSlrTreeModelAdapter* gnc_sx_slr_tree_model_adapter_new (GncSxInstanceModel *instances);
104static void gnc_sx_slr_tree_model_adapter_dispose (GObject *obj);
105static void gnc_sx_slr_tree_model_adapter_finalize (GObject *obj);
106
107GncSxInstanceModel* gnc_sx_slr_tree_model_adapter_get_instance_model (GncSxSlrTreeModelAdapter *slr_model);
108
110GncSxInstance* gnc_sx_slr_model_get_instance (GncSxSlrTreeModelAdapter *model, GtkTreeIter *iter);
111static GncSxInstance* _gnc_sx_slr_model_get_instance (GncSxSlrTreeModelAdapter *model,
112 GtkTreeIter *iter,
113 gboolean check_depth);
114
116gboolean gnc_sx_slr_model_get_instance_and_variable (GncSxSlrTreeModelAdapter *model,
117 GtkTreeIter *iter,
118 GncSxInstance **instance_loc,
119 GncSxVariable **var_loc);
120
121void gnc_sx_slr_model_effect_change (GncSxSlrTreeModelAdapter *model,
122 gboolean auto_create_only,
123 GList **created_transaction_guids,
124 GList **creation_errors);
125
126GtkTreeModel* gnc_sx_get_slr_state_model (void);
127
128#define GNC_TYPE_SX_SLR_TREE_MODEL_ADAPTER (gnc_sx_slr_tree_model_adapter_get_type ())
129#define GNC_SX_SLR_TREE_MODEL_ADAPTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNC_TYPE_SX_SLR_TREE_MODEL_ADAPTER, GncSxSlrTreeModelAdapter))
130#define GNC_SX_SLR_TREE_MODEL_ADAPTER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GNC_TYPE_SX_SLR_TREE_MODEL_ADAPTER, GncSxSlrTreeModelAdapterClass))
131#define GNC_IS_SX_SLR_TREE_MODEL_ADAPTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNC_TYPE_SX_SLR_TREE_MODEL_ADAPTER))
132#define GNC_IS_SX_SLR_TREE_MODEL_ADAPTER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GNC_TYPE_SX_SLR_TREE_MODEL_ADAPTER))
133#define GNC_SX_SLR_TREE_MODEL_ADAPTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GNC_TYPE_SX_SLR_TREE_MODEL_ADAPTER, GncSxSlrTreeModelAdapterClass))
134
135/* ------------------------------------------------------------ */
136
137static void _show_created_transactions (GncSxSinceLastRunDialog *app_dialog, GList *created_txn_guids);
138
139static void close_handler (gpointer user_data);
140static void dialog_destroy_cb (GtkWidget *object, GncSxSinceLastRunDialog *app_dialog);
141static void dialog_response_cb (GtkDialog *dialog, gint response_id, GncSxSinceLastRunDialog *app_dialog);
142
143#define debug_path(fn, text, path) {\
144 gchar *path_string = gtk_tree_path_to_string (path);\
145 fn("%s %s", text, path_string? path_string : "NULL");\
146 g_free (path_string);\
147}
148
149/* ------------------------------------------------------------ */
150
151static void
152_var_numeric_to_string (gnc_numeric *value, GString **str)
153{
154 *str = g_string_sized_new (5);
155 g_string_printf (*str, "%0.2f", gnc_numeric_to_double (*value));
156}
157
158/* ------------------------------------------------------------ */
159
160G_DEFINE_TYPE_WITH_CODE (GncSxSlrTreeModelAdapter, gnc_sx_slr_tree_model_adapter, G_TYPE_OBJECT,
161 G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL, gnc_sx_slr_tree_model_adapter_interface_init))
162
163static void
164gnc_sx_slr_tree_model_adapter_class_init (GncSxSlrTreeModelAdapterClass *klass)
165{
166 GObjectClass *obj_class = G_OBJECT_CLASS(klass);
167
168 obj_class->dispose = gnc_sx_slr_tree_model_adapter_dispose;
169 obj_class->finalize = gnc_sx_slr_tree_model_adapter_finalize;
170}
171
172static GtkTreeModelFlags
173gsslrtma_get_flags (GtkTreeModel *tree_model)
174{
175 return gtk_tree_model_get_flags (GTK_TREE_MODEL(GNC_SX_SLR_TREE_MODEL_ADAPTER(tree_model)->real));
176}
177
178static gint
179gsslrtma_get_n_columns (GtkTreeModel *tree_model)
180{
181 return gtk_tree_model_get_n_columns (GTK_TREE_MODEL(GNC_SX_SLR_TREE_MODEL_ADAPTER(tree_model)->real));
182}
183
184static GType
185gsslrtma_get_column_type (GtkTreeModel *tree_model, gint index)
186{
187 return gtk_tree_model_get_column_type (GTK_TREE_MODEL(GNC_SX_SLR_TREE_MODEL_ADAPTER(tree_model)->real), index);
188}
189
190static gboolean
191gsslrtma_get_iter (GtkTreeModel *tree_model,
192 GtkTreeIter *iter,
193 GtkTreePath *path)
194{
195 return gtk_tree_model_get_iter (GTK_TREE_MODEL(GNC_SX_SLR_TREE_MODEL_ADAPTER(tree_model)->real), iter, path);
196}
197
198static GtkTreePath*
199gsslrtma_get_path (GtkTreeModel *tree_model,
200 GtkTreeIter *iter)
201{
202 return gtk_tree_model_get_path (GTK_TREE_MODEL(GNC_SX_SLR_TREE_MODEL_ADAPTER(tree_model)->real), iter);
203}
204
205static void
206gsslrtma_get_value (GtkTreeModel *tree_model,
207 GtkTreeIter *iter,
208 gint column,
209 GValue *value)
210{
211 gtk_tree_model_get_value (GTK_TREE_MODEL(GNC_SX_SLR_TREE_MODEL_ADAPTER(tree_model)->real), iter, column, value);
212}
213
214static gboolean
215gsslrtma_iter_next (GtkTreeModel *tree_model,
216 GtkTreeIter *iter)
217{
218 return gtk_tree_model_iter_next (GTK_TREE_MODEL(GNC_SX_SLR_TREE_MODEL_ADAPTER(tree_model)->real), iter);
219}
220
221static gboolean
222gsslrtma_iter_children (GtkTreeModel *tree_model,
223 GtkTreeIter *iter,
224 GtkTreeIter *parent)
225{
226 return gtk_tree_model_iter_children (GTK_TREE_MODEL(GNC_SX_SLR_TREE_MODEL_ADAPTER(tree_model)->real), iter, parent);
227}
228
229static gboolean
230gsslrtma_iter_has_child (GtkTreeModel *tree_model,
231 GtkTreeIter *iter)
232{
233 return gtk_tree_model_iter_has_child (GTK_TREE_MODEL(GNC_SX_SLR_TREE_MODEL_ADAPTER(tree_model)->real), iter);
234}
235
236static gint
237gsslrtma_iter_n_children (GtkTreeModel *tree_model,
238 GtkTreeIter *iter)
239{
240 return gtk_tree_model_iter_n_children (GTK_TREE_MODEL(GNC_SX_SLR_TREE_MODEL_ADAPTER(tree_model)->real), iter);
241}
242
243static gboolean
244gsslrtma_iter_nth_child (GtkTreeModel *tree_model,
245 GtkTreeIter *iter,
246 GtkTreeIter *parent,
247 gint n)
248{
249 return gtk_tree_model_iter_nth_child (GTK_TREE_MODEL(GNC_SX_SLR_TREE_MODEL_ADAPTER(tree_model)->real), iter, parent, n);
250}
251
252static gboolean
253gsslrtma_iter_parent (GtkTreeModel *tree_model,
254 GtkTreeIter *iter,
255 GtkTreeIter *child)
256{
257 return gtk_tree_model_iter_parent (GTK_TREE_MODEL(GNC_SX_SLR_TREE_MODEL_ADAPTER(tree_model)->real), iter, child);
258}
259
260static void
261gsslrtma_ref_node (GtkTreeModel *tree_model,
262 GtkTreeIter *iter)
263{
264 gtk_tree_model_ref_node (GTK_TREE_MODEL(GNC_SX_SLR_TREE_MODEL_ADAPTER(tree_model)->real), iter);
265}
266
267static void
268gsslrtma_unref_node (GtkTreeModel *tree_model,
269 GtkTreeIter *iter)
270{
271 gtk_tree_model_unref_node (GTK_TREE_MODEL(GNC_SX_SLR_TREE_MODEL_ADAPTER(tree_model)->real), iter);
272}
273
274static void
275gnc_sx_slr_tree_model_adapter_interface_init (GtkTreeModelIface *tree_model)
276{
277 tree_model->get_flags = gsslrtma_get_flags;
278 tree_model->get_n_columns = gsslrtma_get_n_columns;
279 tree_model->get_column_type = gsslrtma_get_column_type;
280 tree_model->get_iter = gsslrtma_get_iter;
281 tree_model->get_path = gsslrtma_get_path;
282 tree_model->get_value = gsslrtma_get_value;
283 tree_model->iter_next = gsslrtma_iter_next;
284 tree_model->iter_children = gsslrtma_iter_children;
285 tree_model->iter_has_child = gsslrtma_iter_has_child;
286 tree_model->iter_n_children = gsslrtma_iter_n_children;
287 tree_model->iter_nth_child = gsslrtma_iter_nth_child;
288 tree_model->iter_parent = gsslrtma_iter_parent;
289 tree_model->ref_node = gsslrtma_ref_node;
290 tree_model->unref_node = gsslrtma_unref_node;
291}
292
293static void
294gsslrtma_proxy_row_changed (GtkTreeModel *treemodel,
295 GtkTreePath *arg1,
296 GtkTreeIter *arg2,
297 gpointer user_data)
298{
299 g_signal_emit_by_name (user_data, "row-changed", arg1, arg2);
300}
301
302static void
303gsslrtma_proxy_row_deleted (GtkTreeModel *treemodel,
304 GtkTreePath *arg1,
305 gpointer user_data)
306{
307 g_signal_emit_by_name (user_data, "row-deleted", arg1);
308}
309
310static void
311gsslrtma_proxy_row_has_child_toggled (GtkTreeModel *treemodel,
312 GtkTreePath *arg1,
313 GtkTreeIter *arg2,
314 gpointer user_data)
315{
316 g_signal_emit_by_name (user_data, "row-has-child-toggled", arg1, arg2);
317}
318
319static void
320gsslrtma_proxy_row_inserted (GtkTreeModel *treemodel,
321 GtkTreePath *arg1,
322 GtkTreeIter *arg2,
323 gpointer user_data)
324{
325 g_signal_emit_by_name (user_data, "row-inserted", arg1, arg2);
326}
327
328static void
329gsslrtma_proxy_rows_reordered (GtkTreeModel *treemodel,
330 GtkTreePath *arg1,
331 GtkTreeIter *arg2,
332 gpointer arg3,
333 gpointer user_data)
334{
335 g_signal_emit_by_name (user_data, "rows-reordered", arg1, arg2, arg3);
336}
337
338// model columns
339enum
340{
341 SLR_MODEL_COL_NAME = 0,
342 SLR_MODEL_COL_INSTANCE_PTR,
343 SLR_MODEL_COL_INSTANCE_STATE,
344 SLR_MODEL_COL_VARAIBLE_VALUE,
345 SLR_MODEL_COL_INSTANCE_VISIBILITY,
346 SLR_MODEL_COL_VARIABLE_VISIBILITY,
347 SLR_MODEL_COL_INSTANCE_STATE_SENSITIVITY,
348 SLR_MODEL_COL_INSTANCE_DATE,
349};
350
351static void
352gnc_sx_slr_tree_model_adapter_init (GncSxSlrTreeModelAdapter *adapter)
353{
354 // columns: thing-name, ptr, instance-state, variable-value, instance-visible, variable-visible, instance_state_sensitivity, date
355 // at depth=0: <sx>, instances, N/A, N/A N/A, N/A N/A N/A
356 // at depth=1: <instance>, instance, <state>, N/A, <valid>, N/A, <valid> <date>
357 // at depth=2: <variable>, var, N/A, <value>, N/A, <valid>, N/A N/A
358 adapter->real = gtk_tree_store_new (8, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_INT64);
359
360 g_signal_connect (adapter->real, "row-changed", G_CALLBACK(gsslrtma_proxy_row_changed), adapter);
361 g_signal_connect (adapter->real, "row-deleted", G_CALLBACK(gsslrtma_proxy_row_deleted), adapter);
362 g_signal_connect (adapter->real, "row-has-child-toggled", G_CALLBACK(gsslrtma_proxy_row_has_child_toggled), adapter);
363 g_signal_connect (adapter->real, "row-inserted", G_CALLBACK(gsslrtma_proxy_row_inserted), adapter);
364 g_signal_connect (adapter->real, "rows-reordered", G_CALLBACK(gsslrtma_proxy_rows_reordered), adapter);
365}
366
367static char* gnc_sx_instance_state_names[] =
368{
369 N_("Ignored"),
370 N_("Postponed"),
371 N_("To-Create"),
372 N_("Reminder"),
373 N_("Created"),
374 NULL
375};
376
377static GtkTreeModel* _singleton_slr_state_model = NULL;
378
379GtkTreeModel*
380gnc_sx_get_slr_state_model (void)
381{
382 if (_singleton_slr_state_model == NULL)
383 {
384 int i;
385 GtkTreeIter iter;
386
387 _singleton_slr_state_model = GTK_TREE_MODEL(gtk_list_store_new (1, G_TYPE_STRING));
388 for (i = 0; i != SX_INSTANCE_STATE_CREATED; i++)
389 {
390 gtk_list_store_insert_with_values (GTK_LIST_STORE(_singleton_slr_state_model),
391 &iter,
392 SX_INSTANCE_STATE_MAX_STATE + 1,
393 0, _(gnc_sx_instance_state_names[i]), -1);
394 }
395 }
396 return _singleton_slr_state_model;
397}
398
399static void
400_consume_excess_rows (GtkTreeStore *store, int last_index, GtkTreeIter *parent_iter, GtkTreeIter *maybe_invalid_iter)
401{
402 if (last_index == -1)
403 {
404 // try to get whatever was there beforehand, if it exists
405 if (!gtk_tree_model_iter_children (GTK_TREE_MODEL(store), maybe_invalid_iter, parent_iter))
406 return;
407 }
408 else
409 {
410 // increment the iter, or bail out.
411 if (!gtk_tree_model_iter_next (GTK_TREE_MODEL(store), maybe_invalid_iter))
412 return;
413 }
414
415 // consume until we're done.
416 while (gtk_tree_store_remove (store, maybe_invalid_iter));
417}
418
419
420static void
421gsslrtma_populate_tree_store (GncSxSlrTreeModelAdapter *model)
422{
423 GtkTreeIter sx_tree_iter;
424 GList *sx_iter;
425 int instances_index = -1;
426
427 for (sx_iter = gnc_sx_instance_model_get_sx_instances_list (model->instances); sx_iter != NULL; sx_iter = sx_iter->next)
428 {
429 GncSxInstances *instances = (GncSxInstances*)sx_iter->data;
430 char last_occur_date_buf[MAX_DATE_LENGTH+1];
431
432 {
433 const GDate *last_occur = xaccSchedXactionGetLastOccurDate (instances->sx);
434 if (last_occur == NULL || !g_date_valid (last_occur))
435 {
436 g_stpcpy (last_occur_date_buf, _("Never"));
437 }
438 else
439 {
440 qof_print_gdate (last_occur_date_buf,
442 last_occur);
443 }
444 }
445
446 // if there are no instances for the instance skip adding
447 if (g_list_length (instances->instance_list) == 0)
448 continue;
449
450 if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL(model->real), &sx_tree_iter, NULL, ++instances_index))
451 {
452 gtk_tree_store_append (model->real, &sx_tree_iter, NULL);
453 }
454
455 gtk_tree_store_set (model->real, &sx_tree_iter,
456 SLR_MODEL_COL_NAME, xaccSchedXactionGetName (instances->sx),
457 SLR_MODEL_COL_INSTANCE_STATE, NULL,
458 SLR_MODEL_COL_VARAIBLE_VALUE, NULL,
459 SLR_MODEL_COL_INSTANCE_VISIBILITY, FALSE,
460 SLR_MODEL_COL_VARIABLE_VISIBILITY, FALSE,
461 SLR_MODEL_COL_INSTANCE_STATE_SENSITIVITY, FALSE,
462 SLR_MODEL_COL_INSTANCE_DATE, INT64_MAX,
463 SLR_MODEL_COL_INSTANCE_PTR, instances,
464 -1);
465
466 if (qof_log_check (GNC_MOD_GUI_SX, QOF_LOG_DEBUG))
467 {
468 gchar *path_str = gtk_tree_path_to_string (gtk_tree_model_get_path (GTK_TREE_MODEL(model->real), &sx_tree_iter));
469 DEBUG("Add schedule [%s], instances %p at path [%s]", xaccSchedXactionGetName (instances->sx), instances, path_str);
470 g_free (path_str);
471 }
472
473 // Insert instance information
474 {
475 GList *inst_iter;
476 GtkTreeIter inst_tree_iter;
477 char instance_date_buf[MAX_DATE_LENGTH+1];
478 int instance_index = -1;
479
480 for (inst_iter = instances->instance_list; inst_iter != NULL; inst_iter = inst_iter->next)
481 {
482 GncSxInstance *inst = (GncSxInstance*)inst_iter->data;
483 qof_print_gdate (instance_date_buf, MAX_DATE_LENGTH, &inst->date);
484 time64 t = gdate_to_time64 (inst->date);
485
486 if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL(model->real), &inst_tree_iter, &sx_tree_iter, ++instance_index))
487 {
488 gtk_tree_store_append (model->real, &inst_tree_iter, &sx_tree_iter);
489 }
490 gtk_tree_store_set (model->real, &inst_tree_iter,
491 SLR_MODEL_COL_NAME, instance_date_buf,
492 SLR_MODEL_COL_INSTANCE_STATE, _(gnc_sx_instance_state_names[inst->state]),
493 SLR_MODEL_COL_VARAIBLE_VALUE, NULL,
494 SLR_MODEL_COL_INSTANCE_VISIBILITY, TRUE,
495 SLR_MODEL_COL_VARIABLE_VISIBILITY, FALSE,
496 SLR_MODEL_COL_INSTANCE_STATE_SENSITIVITY, inst->state != SX_INSTANCE_STATE_CREATED,
497 SLR_MODEL_COL_INSTANCE_DATE, t,
498 SLR_MODEL_COL_INSTANCE_PTR, inst,
499 -1);
500
501 // Insert variable information
502 {
503 GList *vars = NULL, *var_iter;
504 GtkTreeIter var_tree_iter;
505 gint visible_variable_index = -1;
506
507 vars = gnc_sx_instance_get_variables (inst);
508 for (var_iter = vars; var_iter != NULL; var_iter = var_iter->next)
509 {
510 GncSxVariable *var = (GncSxVariable*)var_iter->data;
511 GString *tmp_str;
512
513 if (!var->editable)
514 continue;
515
516 if (gnc_numeric_check (var->value) == GNC_ERROR_OK)
517 {
518 _var_numeric_to_string (&var->value, &tmp_str);
519 }
520 else
521 {
522 tmp_str = g_string_new (_("(Need Value)"));
523 }
524
525 if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL(model->real),
526 &var_tree_iter, &inst_tree_iter,
527 ++visible_variable_index))
528 {
529 gtk_tree_store_append (model->real, &var_tree_iter, &inst_tree_iter);
530 }
531 gtk_tree_store_set (model->real, &var_tree_iter,
532 SLR_MODEL_COL_NAME, var->name,
533 SLR_MODEL_COL_INSTANCE_STATE, NULL,
534 SLR_MODEL_COL_VARAIBLE_VALUE, tmp_str->str,
535 SLR_MODEL_COL_INSTANCE_VISIBILITY, FALSE,
536 SLR_MODEL_COL_VARIABLE_VISIBILITY, TRUE,
537 SLR_MODEL_COL_INSTANCE_STATE_SENSITIVITY, FALSE,
538 SLR_MODEL_COL_INSTANCE_DATE, INT64_MAX,
539 SLR_MODEL_COL_INSTANCE_PTR, var,
540 -1);
541 g_string_free (tmp_str, TRUE);
542 }
543 g_list_free (vars);
544
545 _consume_excess_rows (model->real, visible_variable_index, &inst_tree_iter, &var_tree_iter);
546 }
547 }
548
549 // if there are more instance iters, remove
550 _consume_excess_rows (model->real, instance_index, &sx_tree_iter, &inst_tree_iter);
551 }
552 }
553 _consume_excess_rows (model->real, instances_index, NULL, &sx_tree_iter);
554}
555
556GncSxInstanceModel*
557gnc_sx_slr_tree_model_adapter_get_instance_model (GncSxSlrTreeModelAdapter *slr_model)
558{
559 return slr_model->instances;
560}
561
563gnc_sx_slr_model_get_instance (GncSxSlrTreeModelAdapter *model, GtkTreeIter *iter)
564{
565 return _gnc_sx_slr_model_get_instance (model, iter, TRUE);
566}
567
568static GncSxInstance*
569_gnc_sx_slr_model_get_instance (GncSxSlrTreeModelAdapter *model, GtkTreeIter *iter, gboolean check_depth)
570{
571 GtkTreePath *model_path = gtk_tree_model_get_path (GTK_TREE_MODEL(model), iter);
572 gint *indices, instances_index, instance_index;
573 GncSxInstance *instance = NULL;
574 GtkTreeIter new_iter;
575
576 debug_path (DEBUG, "model path is:", model_path);
577
578 if (check_depth && gtk_tree_path_get_depth (model_path) != 2)
579 {
580 PWARN("path depth not equal to 2");
581 gtk_tree_path_free (model_path);
582 return NULL;
583 }
584
585 if (gtk_tree_path_get_depth (model_path) == 1)
586 {
587 PWARN("path depth equal to 1");
588 gtk_tree_path_free (model_path);
589 return NULL;
590 }
591
592 indices = gtk_tree_path_get_indices (model_path);
593 instances_index = indices[0];
594 instance_index = indices[1];
595
596 gtk_tree_path_free (model_path);
597
598 model_path = gtk_tree_path_new_from_indices (instances_index, instance_index, -1);
599
600 debug_path (DEBUG, "new model path is:", model_path);
601
602 if (gtk_tree_model_get_iter (GTK_TREE_MODEL(model), &new_iter, model_path))
603 gtk_tree_model_get (GTK_TREE_MODEL(model), &new_iter, SLR_MODEL_COL_INSTANCE_PTR, &instance, -1);
604
605 gtk_tree_path_free (model_path);
606
607 DEBUG("instance is %p", instance);
608
609 return instance;
610}
611
612gboolean
613gnc_sx_slr_model_get_instance_and_variable (GncSxSlrTreeModelAdapter *model, GtkTreeIter *iter, GncSxInstance **instance_loc, GncSxVariable **var_loc)
614{
615 GtkTreePath *model_path;
616 gint *indices, variable_index;
617 GncSxInstance *instance;
618 GList *variables;
619
620 instance = _gnc_sx_slr_model_get_instance (model, iter, FALSE);
621 if (instance == NULL)
622 {
623 gchar *iter_str = gtk_tree_model_get_string_from_iter (GTK_TREE_MODEL(model), iter);
624 PWARN("instance is NULL for iter %s", iter_str);
625 g_free (iter_str);
626 return FALSE;
627 }
628 variables = gnc_sx_instance_get_variables (instance);
629
630 model_path = gtk_tree_model_get_path (GTK_TREE_MODEL(model), iter);
631 if (gtk_tree_path_get_depth (model_path) != 3)
632 {
633 gchar *path_str = gtk_tree_path_to_string (model_path);
634 PWARN("invalid path [%s] for variable, not at depth 3", path_str);
635 gtk_tree_path_free (model_path);
636 g_free (path_str);
637 return FALSE;
638 }
639
640 debug_path (DEBUG, "model path is:", model_path);
641
642 indices = gtk_tree_path_get_indices (model_path);
643 variable_index = indices[2];
644
645 gtk_tree_path_free (model_path);
646
647 if (variable_index < 0 || variable_index >= g_list_length (variables))
648 {
649 PWARN("variable index %d out of range", variable_index);
650 g_list_free (variables);
651 return FALSE;
652 }
653
654 if (instance_loc != NULL)
655 {
656 *instance_loc = instance;
657 }
658
659 if (var_loc != NULL)
660 {
661 GncSxVariable *var;
662
663 gtk_tree_model_get (GTK_TREE_MODEL(model), iter, SLR_MODEL_COL_INSTANCE_PTR, &var, -1);
664
665 *var_loc = var;
666 }
667 g_list_free (variables);
668 return TRUE;
669}
670
674static gint
675_variable_list_index (GList *variables, GncSxVariable *variable)
676{
677 gint index = 0;
678 for (; variables != NULL; variables = variables->next)
679 {
680 GncSxVariable *var = (GncSxVariable*)variables->data;
681 if (!var->editable)
682 continue;
683 if (variable == var)
684 return index;
685 index++;
686 }
687 return -1;
688}
689
690typedef struct _findInstanceData
691{
692 gpointer find_item;
693 GtkTreePath *found_path;
695
696static gboolean
697for_each_find_item (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer user_data)
698{
699 FindInstanceData* to_find = (FindInstanceData*)user_data;
700 gpointer item;
701
702 gtk_tree_model_get (model, iter, SLR_MODEL_COL_INSTANCE_PTR, &item, -1);
703
704 if (item == to_find->find_item)
705 {
706 to_find->found_path = gtk_tree_path_copy (path);
707 return TRUE;
708 }
709 return FALSE;
710}
711
712static GtkTreePath*
713_get_model_path_for_item (GtkTreeModel *model, gpointer find_item)
714{
715 GtkTreePath *model_path = NULL;
716 FindInstanceData* to_find_data;
717
718 to_find_data = (FindInstanceData*)g_new0 (FindInstanceData, 1);
719 to_find_data->find_item = find_item;
720 to_find_data->found_path = NULL;
721
722 gtk_tree_model_foreach (model, (GtkTreeModelForeachFunc)for_each_find_item, to_find_data);
723
724 if (to_find_data->found_path)
725 {
726 model_path = gtk_tree_path_copy (to_find_data->found_path);
727 gtk_tree_path_free (to_find_data->found_path);
728 }
729 g_free (to_find_data);
730
731 return model_path;
732}
733
734static GtkTreePath*
735_get_model_path_for_instance (GtkTreeModel *model, GncSxInstance *instance)
736{
737 return _get_model_path_for_item (model, instance);
738}
739
740static GtkTreePath*
741_get_model_path_for_instances (GtkTreeModel *model, GncSxInstances *instances)
742{
743 return _get_model_path_for_item (model, instances);
744}
745
746static GtkTreePath*
747_get_path_for_variable (GncSxSinceLastRunDialog *app_dialog, GncSxInstance *instance, GncSxVariable *variable)
748{
749 GncSxSlrTreeModelAdapter *model = app_dialog->editing_model;
750 GtkTreeModel *sort_model = gtk_tree_view_get_model (app_dialog->instance_view);
751 gint *indices, instances_index, instance_index, variable_index;
752 GtkTreePath *view_path, *model_path;
753 GList *variables;
754
755 model_path = _get_model_path_for_instance (GTK_TREE_MODEL(model), instance);
756
757 if (!model_path)
758 {
759 PWARN("model path is NULL for instance %p", instance);
760 return NULL;
761 }
762
763 debug_path (DEBUG, "instance model path is:", model_path);
764
765 indices = gtk_tree_path_get_indices (model_path);
766 instances_index = indices[0];
767 instance_index = indices[1];
768
769 gtk_tree_path_free (model_path);
770
771 variables = gnc_sx_instance_get_variables (instance);
772 variable_index = _variable_list_index (variables, variable);
773 g_list_free (variables);
774 if (variable_index == -1)
775 return NULL;
776
777 model_path = gtk_tree_path_new_from_indices (instances_index, instance_index, variable_index, -1);
778 debug_path (DEBUG, "model variable path is:", model_path);
779
780 view_path = gtk_tree_model_sort_convert_child_path_to_path (GTK_TREE_MODEL_SORT(sort_model), model_path);
781 gtk_tree_path_free (model_path);
782
783 debug_path (DEBUG, "return view variable path is:", view_path);
784
785 return view_path;
786}
787
788static void
789gsslrtma_added_cb (GncSxInstanceModel *instances, SchedXaction *added_sx, gpointer user_data)
790{
791 GncSxSlrTreeModelAdapter *model = GNC_SX_SLR_TREE_MODEL_ADAPTER(user_data);
792 // this is wasteful, but fine.
793 gsslrtma_populate_tree_store (model);
794}
795
796static void
797gsslrtma_updated_cb (GncSxInstanceModel *instances, SchedXaction *updated_sx, gpointer user_data)
798{
799 GncSxSlrTreeModelAdapter *model = GNC_SX_SLR_TREE_MODEL_ADAPTER(user_data);
800 gnc_sx_instance_model_update_sx_instances (instances, updated_sx);
801 gsslrtma_populate_tree_store (model);
802}
803
804static void
805gsslrtma_removing_cb (GncSxInstanceModel *inst_model, SchedXaction *to_remove_sx, gpointer user_data)
806{
807 GncSxSlrTreeModelAdapter *model = GNC_SX_SLR_TREE_MODEL_ADAPTER(user_data);
808 GtkTreePath *model_path;
809 GtkTreeIter tree_iter;
810 GList *iter;
811 GncSxInstances *instances;
812
813 // get instances, create path, remove
814 for (iter = gnc_sx_instance_model_get_sx_instances_list (inst_model); iter != NULL; iter = iter->next)
815 {
816 instances = (GncSxInstances*)iter->data;
817 if (instances->sx == to_remove_sx)
818 break;
819 }
820 if (iter == NULL)
821 {
822 PWARN("could not find sx %p in the model", to_remove_sx);
823 return; // couldn't find sx in our model, which is weird.
824 }
825
826 model_path = _get_model_path_for_instances (GTK_TREE_MODEL(model), instances);
827
828 debug_path (DEBUG, "remove model_path", model_path);
829
830 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL(model->real), &tree_iter, model_path))
831 {
832 gchar *path_str = gtk_tree_path_to_string (model_path);
833 PWARN("invalid path [%s] for instances %p to remove", path_str, instances);
834 gtk_tree_path_free (model_path);
835 g_free (path_str);
836 return;
837 }
838 gtk_tree_path_free (model_path);
839
840 gtk_tree_store_remove (model->real, &tree_iter);
841
842 gnc_sx_instance_model_remove_sx_instances (inst_model, to_remove_sx);
843}
844
845static void
846gnc_sx_slr_tree_model_adapter_dispose (GObject *obj)
847{
848 GncSxSlrTreeModelAdapter *adapter;
849 g_return_if_fail (obj != NULL);
850 adapter = GNC_SX_SLR_TREE_MODEL_ADAPTER(obj);
851 g_return_if_fail (!adapter->disposed);
852 adapter->disposed = TRUE;
853
854 g_object_unref (G_OBJECT(adapter->instances));
855 adapter->instances = NULL;
856 g_object_unref (G_OBJECT(adapter->real));
857 adapter->real = NULL;
858
859 G_OBJECT_CLASS(gnc_sx_slr_tree_model_adapter_parent_class)->dispose (obj);
860}
861
862static void
863gnc_sx_slr_tree_model_adapter_finalize (GObject *obj)
864{
865 g_return_if_fail (obj != NULL);
866 G_OBJECT_CLASS(gnc_sx_slr_tree_model_adapter_parent_class)->finalize (obj);
867}
868
869GncSxSlrTreeModelAdapter*
870gnc_sx_slr_tree_model_adapter_new (GncSxInstanceModel *instances)
871{
872 GncSxSlrTreeModelAdapter *rtn;
873 rtn = GNC_SX_SLR_TREE_MODEL_ADAPTER(g_object_new (GNC_TYPE_SX_SLR_TREE_MODEL_ADAPTER, NULL));
874 rtn->instances = instances;
875 g_object_ref (G_OBJECT(rtn->instances));
876 gsslrtma_populate_tree_store (rtn);
877 g_signal_connect (G_OBJECT(rtn->instances), "added", (GCallback)gsslrtma_added_cb, (gpointer)rtn);
878 rtn->updated_cb_id = g_signal_connect (G_OBJECT(rtn->instances), "updated", (GCallback)gsslrtma_updated_cb, (gpointer)rtn);
879 g_signal_connect (G_OBJECT(rtn->instances), "removing", (GCallback)gsslrtma_removing_cb, (gpointer)rtn);
880 return rtn;
881}
882
883void
884gnc_ui_sx_creation_error_dialog (GList **creation_errors)
885{
886 GtkWidget *dialog = NULL;
887 gchar *message = NULL;
888 if (*creation_errors == NULL) return;
889 message = gnc_g_list_stringjoin (*creation_errors, "\n");
890 g_list_free_full (*creation_errors, g_free);
891 creation_errors = NULL;
892 dialog = gtk_message_dialog_new (NULL, 0,
893 GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
894 "\t%s\t", _("Invalid Transactions"));
895 gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
896 "%s", message);
897 g_signal_connect_swapped (dialog, "response",
898 G_CALLBACK(gtk_widget_destroy), dialog);
899 gtk_dialog_run (GTK_DIALOG(dialog));
900 g_free (message);
901}
902
903static GtkTreePath *
904instance_get_model_path (GtkTreeView *view, const gchar *path)
905{
906 GtkTreePath *view_path = gtk_tree_path_new_from_string (path);
907 GtkTreeModelSort *sort_model = GTK_TREE_MODEL_SORT(gtk_tree_view_get_model (view));
908
909 GtkTreePath *model_path = gtk_tree_model_sort_convert_path_to_child_path (sort_model, view_path);
910
911 gtk_tree_path_free (view_path);
912
913 return model_path;
914}
915
916static void
917instance_state_changed_cb (GtkCellRendererText *cell,
918 const gchar *path,
919 const gchar *value,
920 GncSxSinceLastRunDialog *dialog)
921{
922 GncSxInstance *inst;
923 int i;
924 GncSxInstanceState new_state;
925 GtkTreePath *model_path = instance_get_model_path (dialog->instance_view, path);
926 GtkTreeIter tree_iter;
927
928 DEBUG("change instance state to [%s] at path [%s]", value, path);
929
930 debug_path (DEBUG, "instance model path is:", model_path);
931
932 for (i = 0; i < SX_INSTANCE_STATE_CREATED; i++)
933 {
934 if (strcmp (value, _(gnc_sx_instance_state_names[i])) == 0)
935 break;
936 }
937 if (i == SX_INSTANCE_STATE_CREATED)
938 {
939 PWARN("unknown value [%s]", value);
940 return;
941 }
942 new_state = i;
943
944 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL(dialog->editing_model), &tree_iter, model_path))
945 {
946 gtk_tree_path_free (model_path);
947 PWARN("unknown path [%s]", path);
948 return;
949 }
950 gtk_tree_path_free (model_path);
951
952 inst = gnc_sx_slr_model_get_instance (dialog->editing_model, &tree_iter);
953
954 if (inst == NULL)
955 {
956 PWARN("invalid path [%s]", path);
957 return;
958 }
959
960 DEBUG("instance is %p", inst);
961
962 gnc_sx_instance_model_change_instance_state (dialog->editing_model->instances, inst, new_state);
963}
964
965static void
966control_scroll_bars (GncSxSinceLastRunDialog *dialog)
967{
968 GtkWidget *sw = gtk_widget_get_parent (GTK_WIDGET(dialog->instance_view));
969 GtkWidget *vsbar = gtk_scrolled_window_get_vscrollbar (GTK_SCROLLED_WINDOW(sw));
970 gboolean enable = TRUE;
971
972 if (dialog->temp_ce)
973 enable = FALSE;
974
975 gtk_widget_set_sensitive (vsbar, enable);
976 gtk_widget_set_visible (vsbar, enable);
977}
978
979static void
980variable_value_changed_cb (GtkCellRendererText *cell,
981 const gchar *path,
982 const gchar *value,
983 GncSxSinceLastRunDialog *dialog)
984{
985 GncSxVariable *var = NULL;
986 GncSxInstance *inst;
987 gnc_numeric parsed_num;
988 char *endStr = NULL;
989 GtkTreePath *model_path = instance_get_model_path (dialog->instance_view, path);
990 GtkTreeIter tree_iter;
991
992 DEBUG("change variable to [%s] at view path [%s]", value, path);
993
994 debug_path (DEBUG, "instance model path is:", model_path);
995
996 dialog->temp_ce = NULL;
997 control_scroll_bars (dialog);
998
999 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL(dialog->editing_model), &tree_iter, model_path))
1000 {
1001 gtk_tree_path_free (model_path);
1002 PWARN("invalid path [%s]", path);
1003 return;
1004 }
1005 gtk_tree_path_free (model_path);
1006
1007 if (!gnc_sx_slr_model_get_instance_and_variable (dialog->editing_model, &tree_iter, &inst, &var))
1008 {
1009 PWARN("path [%s] doesn't correspond to a valid variable", path);
1010 return;
1011 }
1012
1013 if (!xaccParseAmount (value, TRUE, &parsed_num, &endStr)
1014 || gnc_numeric_check (parsed_num) != GNC_ERROR_OK)
1015 {
1016 gchar *value_copy = g_strdup (value);
1017 DEBUG ("value=[%s] endStr[%s]", value, endStr);
1018 if (strlen (g_strstrip (value_copy)) == 0)
1019 {
1020 gnc_numeric invalid_num = gnc_numeric_error (GNC_ERROR_ARG);
1021 gnc_sx_instance_model_set_variable (dialog->editing_model->instances, inst, var, &invalid_num);
1022 }
1023 else
1024 {
1025 PWARN("error parsing value [%s]", value);
1026 }
1027 g_free (value_copy);
1028 return;
1029 }
1030
1031 if (inst->state == SX_INSTANCE_STATE_REMINDER)
1032 {
1033 gnc_sx_instance_model_change_instance_state (dialog->editing_model->instances, inst,
1034 SX_INSTANCE_STATE_TO_CREATE);
1035 }
1036 gnc_sx_instance_model_set_variable (dialog->editing_model->instances, inst, var, &parsed_num);
1037}
1038
1039static void
1040variable_value_start_changed_cb (GtkCellRenderer *renderer, GtkCellEditable *editable,
1041 gchar *path, gpointer user_data)
1042{
1043 GncSxSinceLastRunDialog *dialog = user_data;
1044 dialog->temp_ce = editable;
1045 control_scroll_bars (dialog);
1046}
1047
1048static void
1049variable_value_cancel_changed_cb (GtkCellRenderer *renderer, gpointer user_data)
1050{
1051 GncSxSinceLastRunDialog *dialog = user_data;
1052 dialog->temp_ce = NULL;
1053 control_scroll_bars (dialog);
1054}
1055
1056static gint
1057_sort_text (const gchar *text_a, const gchar *text_b)
1058{
1059 gchar *a_caseless, *b_caseless;
1060 gint rtn = 0;
1061
1062 if (text_a == NULL && text_b == NULL) return 0;
1063 if (text_a == NULL) return 1;
1064 if (text_b == NULL) return -1;
1065
1066 a_caseless = g_utf8_casefold (text_a, -1);
1067 b_caseless = g_utf8_casefold (text_b, -1);
1068 rtn = g_strcmp0 (a_caseless, b_caseless);
1069 g_free (a_caseless);
1070 g_free (b_caseless);
1071
1072 return rtn;
1073}
1074
1075static gint
1076_transaction_sort_func_date (GtkTreeModel *model, GtkTreeIter *iter_a, GtkTreeIter *iter_b)
1077{
1078 GtkTreePath *path_a = gtk_tree_model_get_path (model, iter_a);
1079 gint depth = gtk_tree_path_get_depth (path_a);
1080 gint64 date_a = 0, date_b = 0;
1081 gint rtn = 0;
1082
1083 gtk_tree_path_free (path_a);
1084
1085 if (depth == 3)
1086 return rtn;
1087
1088 // if top level, look at the first date for order
1089 if (depth == 1)
1090 {
1091 GtkTreeIter child_iter_a, child_iter_b;
1092
1093 if (gtk_tree_model_iter_nth_child (model, &child_iter_a, iter_a, 0))
1094 gtk_tree_model_get (model, &child_iter_a, SLR_MODEL_COL_INSTANCE_DATE, &date_a, -1);
1095
1096 if (gtk_tree_model_iter_nth_child (model, &child_iter_b, iter_b, 0))
1097 gtk_tree_model_get (model, &child_iter_b, SLR_MODEL_COL_INSTANCE_DATE, &date_b, -1);
1098
1099 if (date_a > date_b)
1100 rtn = 1;
1101 if (date_b > date_a)
1102 rtn = -1;
1103
1104 if (rtn == 0) // if dates are equal, look at name
1105 {
1106 gchar *name_text_a, *name_text_b;
1107
1108 gtk_tree_model_get (model, iter_a, SLR_MODEL_COL_NAME, &name_text_a, -1);
1109 gtk_tree_model_get (model, iter_b, SLR_MODEL_COL_NAME, &name_text_b, -1);
1110
1111 rtn = _sort_text (name_text_a, name_text_b);
1112
1113 g_free (name_text_a);
1114 g_free (name_text_b);
1115 }
1116 return rtn;
1117 }
1118
1119 gtk_tree_model_get (model, iter_a, SLR_MODEL_COL_INSTANCE_DATE, &date_a, -1);
1120 gtk_tree_model_get (model, iter_b, SLR_MODEL_COL_INSTANCE_DATE, &date_b, -1);
1121
1122 if (date_a > date_b)
1123 rtn = 1;
1124 if (date_b > date_a)
1125 rtn = -1;
1126
1127 return rtn;
1128}
1129
1130static gint
1131_transaction_sort_func_desc (GtkTreeModel *model, GtkTreeIter *iter_a, GtkTreeIter *iter_b)
1132{
1133 GtkTreePath *path_a = gtk_tree_model_get_path (model, iter_a);
1134 gint depth = gtk_tree_path_get_depth (path_a);
1135 gchar *name_text_a, *name_text_b;
1136 gint rtn = 0;
1137
1138 gtk_tree_path_free (path_a);
1139
1140 if (depth == 3)
1141 return rtn;
1142
1143 if (depth == 1)
1144 {
1145 gtk_tree_model_get (model, iter_a, SLR_MODEL_COL_NAME, &name_text_a, -1);
1146 gtk_tree_model_get (model, iter_b, SLR_MODEL_COL_NAME, &name_text_b, -1);
1147
1148 rtn = _sort_text (name_text_a, name_text_b);
1149
1150 g_free (name_text_a);
1151 g_free (name_text_b);
1152 }
1153
1154 if (depth == 2)
1155 {
1156 gint64 date_a = 0, date_b = 0;
1157
1158 gtk_tree_model_get (model, iter_a, SLR_MODEL_COL_INSTANCE_DATE, &date_a, -1);
1159 gtk_tree_model_get (model, iter_b, SLR_MODEL_COL_INSTANCE_DATE, &date_b, -1);
1160
1161 if (date_a > date_b)
1162 rtn = 1;
1163 if (date_b > date_a)
1164 rtn = -1;
1165 }
1166 return rtn;
1167}
1168
1169static gint
1170_transaction_sort_func (GtkTreeModel *model, GtkTreeIter *iter_a, GtkTreeIter *iter_b, gpointer user_data)
1171{
1172 GncSxSinceLastRunDialog *dialog = user_data;
1173
1174 if (dialog->sort_selection_depth == 1)
1175 return _transaction_sort_func_desc (model, iter_a, iter_b);
1176 else
1177 return _transaction_sort_func_date (model, iter_a, iter_b);
1178}
1179
1180static gboolean
1181finish_editing_before_ok_cb (GtkWidget *button, GdkEvent *event,
1182 GncSxSinceLastRunDialog *dialog)
1183{
1184 // finish editing
1185 if (dialog->temp_ce)
1186 gtk_cell_editable_editing_done (dialog->temp_ce);
1187
1188 dialog->temp_ce = NULL;
1189
1190 return FALSE;
1191}
1192
1193static gboolean
1194scroll_event (GtkWidget *widget, GdkEventScroll *event, gpointer user_data)
1195{
1196 GncSxSinceLastRunDialog *dialog = user_data;
1197
1198 if (dialog->temp_ce)
1199 return TRUE;
1200 else
1201 return FALSE;
1202}
1203
1204static void
1205set_transaction_sort_column_tooltip (GncSxSinceLastRunDialog *dialog)
1206{
1207 GtkTreeViewColumn *col = gtk_tree_view_get_column (GTK_TREE_VIEW(dialog->instance_view), 0);
1208 const gchar *date_text = _("Highlight a date first to sort by occurrence date.");
1209 const gchar *sched_text = _("Highlight a schedule first to sort by schedule name.");
1210 gchar *tooltip;
1211
1212 if (dialog->sort_selection_depth == 1)
1213 tooltip = g_strconcat (sched_text, " *\n", date_text, NULL);
1214 else
1215 tooltip = g_strconcat (sched_text, "\n", date_text, " *", NULL);
1216
1217 gtk_widget_set_tooltip_text (gtk_tree_view_column_get_button (col), tooltip);
1218 g_free (tooltip);
1219}
1220
1221static gboolean
1222follow_select_tree_path (GtkTreeView *view)
1223{
1224 GtkTreeSelection *selection = gtk_tree_view_get_selection (view);
1225 GtkTreeModel *sort_model;
1226 GtkTreeIter iter;
1227
1228 if (gtk_tree_selection_get_selected (selection, &sort_model, &iter))
1229 {
1230 GtkTreePath *view_path = gtk_tree_model_get_path (sort_model, &iter);
1231
1232 gtk_tree_view_scroll_to_cell (view, view_path, NULL, TRUE, 0.5, 0.0);
1233
1234 gtk_tree_path_free (view_path);
1235 }
1236 return FALSE;
1237}
1238
1239static void
1240sort_column_changed (GtkTreeSortable* self, gpointer user_data)
1241{
1242 // this is triggered before a sort change
1243 GncSxSinceLastRunDialog *dialog = user_data;
1244 GtkTreeIter iter;
1245 GtkTreeSelection *selection = gtk_tree_view_get_selection (dialog->instance_view);
1246 GtkTreeModel *sort_model;
1247
1248 if (gtk_tree_selection_get_selected (selection, &sort_model, &iter))
1249 {
1250 GtkTreePath *view_path = gtk_tree_model_get_path (sort_model, &iter);
1251
1252 dialog->sort_selection_depth = gtk_tree_path_get_depth (view_path);
1253
1254 gtk_tree_path_free (view_path);
1255 }
1256 else
1257 dialog->sort_selection_depth = 1;
1258
1259 set_transaction_sort_column_tooltip (dialog);
1260
1261 g_idle_add ((GSourceFunc)follow_select_tree_path, dialog->instance_view);
1262}
1263
1264static void
1265since_last_run_dialog (GtkWindow *parent, GncSxInstanceModel *sx_instances, GList *auto_created_txn_guids)
1266{
1267 GncSxSinceLastRunDialog *dialog;
1268 GtkBuilder *builder;
1269 GtkWidget *ok_button;
1270
1271 dialog = g_new0 (GncSxSinceLastRunDialog, 1);
1272
1273 builder = gtk_builder_new ();
1274 gnc_builder_add_from_file (builder, "dialog-sx.glade", "since_last_run_dialog");
1275
1276 dialog->dialog = GTK_WIDGET(gtk_builder_get_object (builder, "since_last_run_dialog"));
1277 gtk_window_set_transient_for (GTK_WINDOW(dialog->dialog), parent);
1278
1279 // Set the name of this dialog so it can be easily manipulated with css
1280 gtk_widget_set_name (GTK_WIDGET(dialog->dialog), "gnc-id-sx-since-last-run");
1281 gnc_widget_style_context_add_class (GTK_WIDGET(dialog->dialog), "gnc-class-sx");
1282
1283 dialog->editing_model = gnc_sx_slr_tree_model_adapter_new (sx_instances);
1284 dialog->review_created_txns_toggle = GTK_TOGGLE_BUTTON(gtk_builder_get_object (builder, "review_txn_toggle"));
1285
1286 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(dialog->review_created_txns_toggle),
1287 gnc_prefs_get_bool (GNC_PREFS_GROUP_STARTUP, GNC_PREF_SET_REVIEW));
1288
1289 dialog->created_txns = auto_created_txn_guids;
1290
1291 ok_button = GTK_WIDGET(gtk_builder_get_object (builder, "okbutton2"));
1292
1293 g_signal_connect (G_OBJECT(ok_button), "button-press-event",
1294 G_CALLBACK(finish_editing_before_ok_cb), dialog);
1295
1296 {
1297 GtkCellRenderer *renderer;
1298 GtkTreeViewColumn *col;
1299 GtkTreeModel *sort_model = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(dialog->editing_model));
1300
1301 dialog->instance_view = GTK_TREE_VIEW(gtk_builder_get_object (builder, "instance_view"));
1302 gtk_tree_view_set_model (dialog->instance_view, GTK_TREE_MODEL(sort_model));
1303 g_object_unref (sort_model);
1304
1305 /* default sort order */
1306 dialog->sort_selection_depth = gnc_prefs_get_int (GNC_PREFS_GROUP_STARTUP, GNC_PREF_SLR_SORT_DEPTH);
1307 gboolean sort_ascending = gnc_prefs_get_bool (GNC_PREFS_GROUP_STARTUP, GNC_PREF_SLR_SORT_ASC);
1308 gint sort_column = gnc_prefs_get_int (GNC_PREFS_GROUP_STARTUP, GNC_PREF_SLR_SORT_COL);
1309 GtkSortType sort_type = sort_ascending ? GTK_SORT_ASCENDING : GTK_SORT_DESCENDING;
1310
1311 if (sort_column != 0)
1312 sort_column = 0;
1313
1314 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sort_model),
1315 sort_column, sort_type);
1316
1317 g_signal_connect (G_OBJECT(dialog->instance_view), "scroll-event",
1318 G_CALLBACK(scroll_event), dialog);
1319
1320 renderer = gtk_cell_renderer_text_new ();
1321 col = gtk_tree_view_column_new_with_attributes (_("Transaction"), renderer,
1322 "text", SLR_MODEL_COL_NAME,
1323 NULL);
1324 gtk_tree_view_append_column (dialog->instance_view, col);
1325
1326 gtk_tree_view_column_set_sort_column_id (col, SLR_MODEL_COL_NAME);
1327
1328 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sort_model), SLR_MODEL_COL_NAME,
1329 _transaction_sort_func, dialog, NULL);
1330
1331 set_transaction_sort_column_tooltip (dialog);
1332
1333 renderer = gtk_cell_renderer_combo_new ();
1334 g_object_set (G_OBJECT(renderer),
1335 "model", gnc_sx_get_slr_state_model (),
1336 "text-column", 0,
1337 "has-entry", FALSE,
1338 "editable", TRUE,
1339 NULL);
1340 g_signal_connect (G_OBJECT(renderer),
1341 "edited",
1342 G_CALLBACK(instance_state_changed_cb),
1343 dialog);
1344 col = gtk_tree_view_column_new_with_attributes (_("Status"), renderer,
1345 "text", SLR_MODEL_COL_INSTANCE_STATE,
1346 "visible", SLR_MODEL_COL_INSTANCE_VISIBILITY,
1347 // you might think only "sensitive" is required to
1348 // control the ability of the combo box to select
1349 // a new state, but you'd be wrong.
1350 "editable", SLR_MODEL_COL_INSTANCE_STATE_SENSITIVITY,
1351 "sensitive", SLR_MODEL_COL_INSTANCE_STATE_SENSITIVITY,
1352 NULL);
1353
1354 g_signal_connect (G_OBJECT(sort_model), "sort-column-changed",
1355 G_CALLBACK(sort_column_changed), dialog);
1356
1357 renderer = gtk_cell_renderer_pixbuf_new ();
1358 g_object_set (G_OBJECT(renderer),
1359 "icon-name", "pan-down-symbolic",
1360 NULL);
1361 gtk_tree_view_column_pack_end (col, renderer, FALSE);
1362 gtk_tree_view_column_set_attributes (col, renderer,
1363 "visible", SLR_MODEL_COL_INSTANCE_VISIBILITY,
1364 "sensitive", SLR_MODEL_COL_INSTANCE_STATE_SENSITIVITY,
1365 NULL);
1366 gtk_tree_view_append_column (dialog->instance_view, col);
1367 gtk_tree_view_column_set_resizable (col, FALSE);
1368
1369 renderer = gtk_cell_renderer_text_new ();
1370 g_object_set (G_OBJECT(renderer),
1371 "editable", TRUE,
1372 NULL);
1373 g_signal_connect (G_OBJECT(renderer),
1374 "edited",
1375 G_CALLBACK(variable_value_changed_cb),
1376 dialog);
1377
1378 g_signal_connect (G_OBJECT(renderer),
1379 "editing-started",
1380 G_CALLBACK(variable_value_start_changed_cb),
1381 dialog);
1382
1383 g_signal_connect (G_OBJECT(renderer),
1384 "editing-canceled",
1385 (GCallback)variable_value_cancel_changed_cb,
1386 dialog);
1387
1388 col = gtk_tree_view_column_new_with_attributes (_("Value"), renderer,
1389 "text", SLR_MODEL_COL_VARAIBLE_VALUE,
1390 "visible", SLR_MODEL_COL_VARIABLE_VISIBILITY,
1391 NULL);
1392 gtk_tree_view_append_column (dialog->instance_view, col);
1393
1394 gtk_tree_view_expand_all (dialog->instance_view);
1395 }
1396
1397 // Set grid lines option to preference
1398 gtk_tree_view_set_grid_lines (GTK_TREE_VIEW(dialog->instance_view), gnc_tree_view_get_grid_lines_pref ());
1399
1400 g_signal_connect (G_OBJECT(dialog->dialog), "response", G_CALLBACK(dialog_response_cb), dialog);
1401 g_signal_connect (G_OBJECT(dialog->dialog), "destroy", G_CALLBACK(dialog_destroy_cb), dialog);
1402
1403 gnc_restore_window_size (GNC_PREFS_GROUP_STARTUP, GTK_WINDOW(dialog->dialog), parent);
1404
1405 dialog->component_id = gnc_register_gui_component (DIALOG_SX_SINCE_LAST_RUN_CM_CLASS,
1406 NULL, close_handler, dialog);
1407 gnc_gui_component_set_session (dialog->component_id,
1408 gnc_get_current_session ());
1409
1410 gtk_widget_show_all (dialog->dialog);
1411
1412 gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, dialog);
1413
1414 g_object_unref (G_OBJECT(builder));
1415}
1416
1417static void
1418sx_since_last_run_dialog (GncSxInstanceModel *sx_instances, int book_opened)
1419{
1420 if (qof_book_is_readonly (gnc_get_current_book ()))
1421 return;
1422
1423 GncSxSummary summary;
1424
1425 gnc_sx_instance_model_summarize (sx_instances, &summary);
1426
1427 if (book_opened)
1428 gnc_sx_summary_print (&summary);
1429
1430 GList *auto_created_txns = NULL;
1431 GList *creation_errors = NULL;
1432
1433 gnc_sx_instance_model_effect_change (sx_instances, TRUE, &auto_created_txns, &creation_errors);
1434
1435 if (auto_created_txns)
1436 gnc_gui_refresh_all ();
1437
1438 if (summary.need_dialog)
1439 since_last_run_dialog (gnc_ui_get_main_window (NULL), sx_instances, auto_created_txns);
1440 else
1441 {
1442 if (summary.num_auto_create_no_notify_instances == 0)
1443 {
1444 if (!book_opened)
1445 {
1446 const char *nothing_to_do_msg = _( "There are no Scheduled Transactions to be entered at this time." );
1447
1448 gnc_info_dialog (gnc_ui_get_main_window (NULL), "%s", nothing_to_do_msg);
1449 }
1450 }
1451 else if (!book_opened || gnc_prefs_get_bool (GNC_PREFS_GROUP_STARTUP, GNC_PREF_SHOW_AT_FOPEN))
1452 {
1453 gnc_info_dialog (gnc_ui_get_main_window (NULL), ngettext
1454 /* Translators: %d is the number of transactions. This is a ngettext(3) message. */
1455 ("There are no Scheduled Transactions to be entered at this time. "
1456 "(%d transaction automatically created)",
1457 "There are no Scheduled Transactions to be entered at this time. "
1458 "(%d transactions automatically created)",
1461 }
1462 g_list_free (auto_created_txns);
1463 }
1464
1465 g_object_unref (G_OBJECT(sx_instances));
1466
1467 if (creation_errors)
1468 gnc_ui_sx_creation_error_dialog (&creation_errors);
1469}
1470
1471void
1472gnc_ui_sx_since_last_run_dialog (GncSxInstanceModel *sx_instances)
1473{
1474 sx_since_last_run_dialog (sx_instances, FALSE);
1475}
1476
1477void
1478gnc_sx_sxsincelast_book_opened (void)
1479{
1480 if (!gnc_prefs_get_bool (GNC_PREFS_GROUP_STARTUP, GNC_PREF_RUN_AT_FOPEN))
1481 return;
1482
1483 sx_since_last_run_dialog (gnc_sx_get_current_instances (), TRUE);
1484
1485}
1486
1487static void
1488_show_created_transactions (GncSxSinceLastRunDialog *app_dialog, GList *created_txn_guids)
1489{
1490 GNCLedgerDisplay *ledger;
1491 GncPluginPage *page;
1492 Query *book_query, *guid_query, *query;
1493 GList *guid_iter;
1494
1495 book_query = qof_query_create_for (GNC_ID_SPLIT);
1496 guid_query = qof_query_create_for (GNC_ID_SPLIT);
1497 qof_query_set_book (book_query, gnc_get_current_book ());
1498 for (guid_iter = created_txn_guids; guid_iter != NULL; guid_iter = guid_iter->next)
1499 {
1500 xaccQueryAddGUIDMatch (guid_query, (GncGUID*)guid_iter->data, GNC_ID_TRANS, QOF_QUERY_OR);
1501 }
1502 query = qof_query_merge (book_query, guid_query, QOF_QUERY_AND);
1503 // inspired by dialog-find-transactions:do_find_cb:
1504 ledger = gnc_ledger_display_query (query, SEARCH_LEDGER, REG_STYLE_JOURNAL);
1507 g_object_set (G_OBJECT(page), "page-name", _("Created Transactions"), NULL);
1508 gnc_main_window_open_page (NULL, page);
1509
1510 qof_query_destroy (query);
1511 qof_query_destroy (book_query);
1512 qof_query_destroy (guid_query);
1513}
1514
1515static void
1516close_handler (gpointer user_data)
1517{
1518 GncSxSinceLastRunDialog *app_dialog = user_data;
1519 GtkSortType order;
1520 gint column;
1521
1522 if (gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE(
1523 gtk_tree_view_get_model (app_dialog->instance_view)),
1524 &column, &order))
1525 {
1526 gboolean sort_ascending = TRUE;
1527 if (order == GTK_SORT_DESCENDING)
1528 sort_ascending = FALSE;
1529
1530 gnc_prefs_set_bool (GNC_PREFS_GROUP_STARTUP, GNC_PREF_SLR_SORT_ASC, sort_ascending);
1531 gnc_prefs_set_int (GNC_PREFS_GROUP_STARTUP, GNC_PREF_SLR_SORT_COL, column);
1532 gnc_prefs_set_int (GNC_PREFS_GROUP_STARTUP, GNC_PREF_SLR_SORT_DEPTH,
1533 app_dialog->sort_selection_depth);
1534 }
1535
1536 gnc_save_window_size (GNC_PREFS_GROUP_STARTUP, GTK_WINDOW(app_dialog->dialog));
1537 gtk_widget_destroy (app_dialog->dialog);
1538 g_free (app_dialog);
1539}
1540
1541static void
1542dialog_destroy_cb (GtkWidget *object, GncSxSinceLastRunDialog *app_dialog)
1543{
1544 gnc_unregister_gui_component (app_dialog->component_id);
1545
1546 g_object_unref (G_OBJECT(app_dialog->editing_model));
1547 app_dialog->editing_model = NULL;
1548}
1549
1550static void
1551dialog_response_cb (GtkDialog *dialog, gint response_id, GncSxSinceLastRunDialog *app_dialog)
1552{
1553 GList* creation_errors = NULL;
1554 switch (response_id)
1555 {
1556 case GTK_RESPONSE_HELP:
1557 gnc_gnome_help (GTK_WINDOW(dialog), DF_MANUAL, DL_SX_SLR);
1558 break;
1559
1560 case GTK_RESPONSE_OK:
1561 // @@fixme validate current state(GError *errs);
1562 // - [ ] instance state constraints
1563 // - [x] required variable binding
1564 // - [?] ability to create transactions
1565 {
1566 GList *unbound_variables;
1567 gint unbound_len;
1568 unbound_variables = gnc_sx_instance_model_check_variables (app_dialog->editing_model->instances);
1569 unbound_len = g_list_length (unbound_variables);
1570 PINFO("%d variables unbound", unbound_len);
1571 if (unbound_len > 0)
1572 {
1573 // focus first variable
1574 GncSxVariableNeeded *first_unbound;
1575 GtkTreePath *variable_path;
1576 GtkTreeViewColumn *variable_col;
1577 gint variable_view_column = 2;
1578 gboolean start_editing = TRUE;
1579
1580 first_unbound = (GncSxVariableNeeded*)unbound_variables->data;
1581
1582 variable_path = _get_path_for_variable (app_dialog, first_unbound->instance, first_unbound->variable);
1583 variable_col = gtk_tree_view_get_column (app_dialog->instance_view, variable_view_column);
1584
1585 gtk_tree_view_set_cursor (app_dialog->instance_view, variable_path, variable_col, start_editing);
1586
1587 gtk_tree_view_scroll_to_cell (app_dialog->instance_view, variable_path, variable_col,
1588 TRUE, 0.5, 0.5);
1589
1590 gtk_tree_path_free (variable_path);
1591 g_list_foreach (unbound_variables, (GFunc)g_free, NULL);
1592 g_list_free (unbound_variables);
1593 return;
1594 }
1595 }
1596 gnc_suspend_gui_refresh ();
1597 gnc_sx_slr_model_effect_change (app_dialog->editing_model, FALSE, &app_dialog->created_txns, &creation_errors);
1598 gnc_resume_gui_refresh ();
1599 gnc_gui_refresh_all (); // force a refresh of all registers
1600 if (creation_errors)
1601 gnc_ui_sx_creation_error_dialog (&creation_errors);
1602
1603 if (gtk_toggle_button_get_active (app_dialog->review_created_txns_toggle)
1604 && g_list_length (app_dialog->created_txns) > 0)
1605 {
1606 _show_created_transactions (app_dialog, app_dialog->created_txns);
1607 }
1608
1609 /* FALL THROUGH */
1610 case GTK_RESPONSE_CANCEL:
1611 case GTK_RESPONSE_DELETE_EVENT:
1612 g_list_free (app_dialog->created_txns);
1613 app_dialog->created_txns = NULL;
1614 gnc_close_gui_component (app_dialog->component_id);
1615 break;
1616 default:
1617 PWARN("unknown response id [%d]", response_id);
1618 break;
1619 }
1620}
1621
1626void
1627gnc_sx_slr_model_effect_change (GncSxSlrTreeModelAdapter *model,
1628 gboolean auto_create_only,
1629 GList **created_transaction_guids,
1630 GList **creation_errors)
1631{
1632 if (qof_book_is_readonly (gnc_get_current_book ()))
1633 {
1634 /* Is the book read-only? Then don't change anything here. */
1635 return;
1636 }
1637
1638 g_signal_handler_block (model->instances, model->updated_cb_id);
1639 gnc_sx_instance_model_effect_change (model->instances, auto_create_only, created_transaction_guids, creation_errors);
1640 g_signal_handler_unblock (model->instances, model->updated_cb_id);
1641}
GLib helper routines.
Public declarations for GncLedgerDisplay class.
Functions for adding content to a window.
Functions providing a register page for the GnuCash UI.
Generic api to store and retrieve preferences.
utility functions for the GnuCash UI
gboolean qof_book_is_readonly(const QofBook *book)
Return whether the book is read only.
Definition qofbook.cpp:497
#define MAX_DATE_LENGTH
The maximum length of a string created by the date printers.
Definition gnc-date.h:108
gint64 time64
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
Definition gnc-date.h:87
time64 gdate_to_time64(GDate d)
Turns a GDate into a time64, returning the first second of the day.
size_t qof_print_gdate(char *buf, size_t len, const GDate *gd)
Convenience; calls through to qof_print_date_dmy_buff().
Definition gnc-date.cpp:598
GtkWindow * gnc_ui_get_main_window(GtkWidget *widget)
Get a pointer to the final GncMainWindow widget is rooted in.
void gnc_main_window_open_page(GncMainWindow *window, GncPluginPage *page)
Display a data plugin page in a window.
void gnc_ledger_display_refresh(GNCLedgerDisplay *ld)
redisplay/redraw only the indicated window.
GNCLedgerDisplay * gnc_ledger_display_query(Query *query, SplitRegisterType type, SplitRegisterStyle style)
display a general ledger for an arbitrary query
void gnc_gnome_help(GtkWindow *parent, const char *file_name, const char *anchor)
Launch the systems default help browser, gnome's yelp for linux, and open to a given link within a gi...
gboolean xaccParseAmount(const char *in_str, gboolean monetary, gnc_numeric *result, char **endstr)
Parses in_str to obtain a numeric result.
gchar * gnc_g_list_stringjoin(GList *list_of_strings, const gchar *sep)
Return a string joining a GList whose elements are gchar* strings.
#define PINFO(format, args...)
Print an informational note.
Definition qoflog.h:256
#define DEBUG(format, args...)
Print a debugging message.
Definition qoflog.h:264
#define PWARN(format, args...)
Log a warning.
Definition qoflog.h:250
gboolean qof_log_check(QofLogModule domain, QofLogLevel level)
Check to see if the given log_module is configured to log at the given log_level.
Definition qoflog.cpp:330
double gnc_numeric_to_double(gnc_numeric in)
Convert numeric to floating-point value.
gnc_numeric gnc_numeric_error(GNCNumericErrorCode error_code)
Create a gnc_numeric object that signals the error condition noted by error_code, rather than a numbe...
@ GNC_ERROR_OK
No error.
@ GNC_ERROR_ARG
Argument is not a valid number.
gboolean gnc_prefs_set_bool(const gchar *group, const gchar *pref_name, gboolean value)
Store a boolean value into the preferences backend.
gint gnc_prefs_get_int(const gchar *group, const gchar *pref_name)
Get an integer value from the preferences backend.
gboolean gnc_prefs_get_bool(const gchar *group, const gchar *pref_name)
Get a boolean value from the preferences backend.
gboolean gnc_prefs_set_int(const gchar *group, const gchar *pref_name, gint value)
Store an integer value into the preferences backend.
QofQuery * qof_query_merge(QofQuery *q1, QofQuery *q2, QofQueryOp op)
Combine two queries together using the Boolean set (logical) operator 'op'.
void qof_query_set_book(QofQuery *query, QofBook *book)
Set the book to be searched.
void qof_query_destroy(QofQuery *query)
Frees the resources associate with a Query object.
GncPluginPage * gnc_plugin_page_register_new_ledger(GNCLedgerDisplay *ledger)
Create a new "register" plugin page, given a pointer to an already created ledger.
The type used to store guids in C.
Definition guid.h:75
The instance data structure for a content plugin.
GncSxInstanceState state
the current state of the instance (during editing)
gint num_auto_create_no_notify_instances
The number of automatically-created instances that do no request notification.
gboolean need_dialog
If the dialog needs to be displayed.
A Query.
Definition qofquery.cpp:75