GnuCash c935c2f+
Loading...
Searching...
No Matches
dialog-price-edit-db.cpp
1/********************************************************************\
2 * dialog-price-editor.c -- price selector dialog *
3 * Copyright (C) 2001 Gnumatic, Inc. *
4 * Author: Dave Peticolas <dave@krondo.com> *
5 * Copyright (C) 2003,2005 David Hampton *
6 * Copyright (C) 2011 Robert Fewell *
7 * *
8 * This program is free software; you can redistribute it and/or *
9 * modify it under the terms of the GNU General Public License as *
10 * published by the Free Software Foundation; either version 2 of *
11 * the License, or (at your option) any later version. *
12 * *
13 * This program is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 * GNU General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU General Public License*
19 * along with this program; if not, contact: *
20 * *
21 * Free Software Foundation Voice: +1-617-542-5942 *
22 * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
23 * Boston, MA 02110-1301, USA gnu@gnu.org *
24\********************************************************************/
25
26#include <config.h>
27
28#include <gtk/gtk.h>
29#include <glib/gi18n.h>
30#include <time.h>
31#include <gnc-quotes.hpp>
32
33#include "dialog-utils.h"
34#include "dialog-commodity.h"
36#include "gnc-amount-edit.h"
37#include "gnc-commodity-edit.h"
38#include "gnc-general-select.h"
39#include "gnc-component-manager.h"
40#include "gnc-currency-edit.h"
41#include "gnc-date-edit.h"
42#include "gnc-engine.h"
43#include "gnc-gtk-utils.h"
44#include "gnc-gui-query.h"
45#include "gnc-pricedb.h"
46#include "gnc-session.h"
47#include "gnc-tree-view-price.h"
48#include "gnc-ui.h"
49#include "gnc-ui-util.h"
50#include "gnc-warnings.h"
51#include <gnc-glib-utils.h>
52
53
54#define DIALOG_PRICE_DB_CM_CLASS "dialog-price-edit-db"
55#define STATE_SECTION "dialogs/edit_prices"
56#define GNC_PREFS_GROUP "dialogs.pricedb-editor"
57
58/* This static indicates the debugging module that this .o belongs to. */
59static QofLogModule log_module = GNC_MOD_GUI;
60
61
62extern "C" {
63void gnc_prices_dialog_destroy_cb (GtkWidget *object, gpointer data);
64void gnc_prices_dialog_close_cb (GtkDialog *dialog, gpointer data);
65void gnc_prices_dialog_help_cb (GtkDialog *dialog, gpointer data);
66void gnc_prices_dialog_edit_clicked (GtkWidget *widget, gpointer data);
67void gnc_prices_dialog_remove_clicked (GtkWidget *widget, gpointer data);
68void gnc_prices_dialog_remove_old_clicked (GtkWidget *widget, gpointer data);
69void gnc_prices_dialog_add_clicked (GtkWidget *widget, gpointer data);
70void gnc_prices_dialog_get_quotes_clicked (GtkWidget *widget, gpointer data);
71static gboolean gnc_prices_dialog_key_press_cb (GtkWidget *widget,
72 GdkEventKey *event,
73 gpointer data);
74}
75
76
78{
79 GtkWidget * window;
80 QofSession *session;
81 QofBook *book;
82 GNCPriceDB *price_db;
83
84 GncTreeViewPrice * price_tree;
85
86 GtkWidget * edit_button;
87 GtkWidget * remove_button;
88 GtkWidget * add_button;
89
90 GtkWidget *remove_dialog;
91 GtkTreeView *remove_view;
92 GtkWidget *namespace_cbwe;
93 gchar *target_namespace_name;
94 int remove_source;
95};
96
97
98void
99gnc_prices_dialog_destroy_cb (GtkWidget *object, gpointer data)
100{
101 auto pdb_dialog = static_cast<PricesDialog *> (data);
102
103 ENTER(" ");
104 gnc_unregister_gui_component_by_data (DIALOG_PRICE_DB_CM_CLASS, pdb_dialog);
105
106 if (pdb_dialog->window)
107 {
108 gtk_widget_destroy (pdb_dialog->window);
109 pdb_dialog->window = NULL;
110 }
111
112 g_free (pdb_dialog);
113 LEAVE(" ");
114}
115
116
117static gboolean
118gnc_prices_dialog_delete_event_cb (GtkWidget *widget,
119 GdkEvent *event,
120 gpointer data)
121{
122 auto pdb_dialog = static_cast<PricesDialog *> (data);
123 // this cb allows the window size to be saved on closing with the X
124 gnc_save_window_size (GNC_PREFS_GROUP,
125 GTK_WINDOW(pdb_dialog->window));
126 return FALSE;
127}
128
129
130void
131gnc_prices_dialog_close_cb (GtkDialog *dialog, gpointer data)
132{
133 auto pdb_dialog = static_cast<PricesDialog *> (data);
134
135 ENTER(" ");
136 gnc_close_gui_component_by_data (DIALOG_PRICE_DB_CM_CLASS, pdb_dialog);
137 LEAVE(" ");
138}
139
140
141void
142gnc_prices_dialog_help_cb (GtkDialog *dialog, gpointer data)
143{
144 auto pdb_dialog{static_cast<PricesDialog*>(data)};
145
146 gnc_gnome_help (GTK_WINDOW (pdb_dialog->window), DF_MANUAL, DL_PRICE_DB);
147}
148
149
150void
151gnc_prices_dialog_edit_clicked (GtkWidget *widget, gpointer data)
152{
153 auto pdb_dialog = static_cast<PricesDialog *> (data);
154
155 ENTER(" ");
156 auto price_list = gnc_tree_view_price_get_selected_prices (pdb_dialog->price_tree);
157 if (!price_list)
158 {
159 LEAVE("no price selected");
160 return;
161 }
162 if (g_list_next(price_list))
163 {
164 g_list_free(price_list);
165 LEAVE("too many prices selected");
166 return;
167 }
168
169 auto price = static_cast<GNCPrice *> (price_list->data);
170 gnc_price_edit_dialog (pdb_dialog->window, pdb_dialog->session,
171 price, GNC_PRICE_EDIT);
172 g_list_free (price_list);
173 LEAVE(" ");
174}
175
176
177static void
178remove_helper(GNCPrice *price, GNCPriceDB *pdb)
179{
180 gnc_pricedb_remove_price (pdb, price);
181}
182
183
184void
185gnc_prices_dialog_remove_clicked (GtkWidget *widget, gpointer data)
186{
187 auto pdb_dialog = static_cast<PricesDialog *> (data);
188
189 ENTER(" ");
190 auto price_list = gnc_tree_view_price_get_selected_prices (pdb_dialog->price_tree);
191 if (!price_list)
192 {
193 LEAVE("no price selected");
194 return;
195 }
196
197 gint response;
198 auto length = g_list_length(price_list);
199 if (length > 0)
200 {
201 gchar *message;
202
203 message = g_strdup_printf
204 (/* Translators: %d is the number of prices. This is a ngettext(3) message. */
205 ngettext("Are you sure you want to delete the selected price?",
206 "Are you sure you want to delete the %d selected prices?",
207 length),
208 length);
209 auto dialog = gtk_message_dialog_new (GTK_WINDOW(pdb_dialog->window),
210 GTK_DIALOG_DESTROY_WITH_PARENT,
211 GTK_MESSAGE_QUESTION,
212 GTK_BUTTONS_NONE,
213 "%s", _("Delete prices?"));
214 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
215 "%s", message);
216 g_free(message);
217 gtk_dialog_add_buttons(GTK_DIALOG(dialog),
218 _("_Cancel"), GTK_RESPONSE_CANCEL,
219 _("_Delete"), GTK_RESPONSE_YES,
220 (gchar *)NULL);
221 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_YES);
222 response = gnc_dialog_run(GTK_DIALOG(dialog), GNC_PREF_WARN_PRICE_QUOTES_DEL);
223 gtk_widget_destroy(dialog);
224 }
225 else
226 {
227 response = GTK_RESPONSE_YES;
228 }
229
230 if (response == GTK_RESPONSE_YES)
231 {
232 g_list_foreach(price_list, (GFunc)remove_helper, pdb_dialog->price_db);
233 }
234 g_list_free(price_list);
235 gnc_gui_refresh_all ();
236 LEAVE(" ");
237}
238
239
241enum GncPriceColumn {PRICED_NAMESPACE_NAME, PRICED_FULL_NAME, PRICED_COMM, PRICED_DATE, PRICED_COUNT};
242
243static bool
244continue_namespace_check (const gchar *target_namespace_name, const gchar *namespace_name)
245{
246 if ((g_strcmp0 (target_namespace_name, GNC_COMMODITY_NS_CURRENCY) == 0) &&
247 (g_strcmp0 (namespace_name, GNC_COMMODITY_NS_CURRENCY) != 0))
248 return true;
249
250 if ((g_strcmp0 (target_namespace_name, GNC_COMMODITY_NS_NONISO_GUI) == 0) &&
251 (g_strcmp0 (namespace_name, GNC_COMMODITY_NS_CURRENCY) == 0))
252 return true;
253
254 if ((g_strcmp0 (target_namespace_name, GNC_COMMODITY_NS_NONISO_GUI) != 0) &&
255 (g_strcmp0 (target_namespace_name, GNC_COMMODITY_NS_CURRENCY) != 0) &&
256 (g_strcmp0 (target_namespace_name, namespace_name) != 0))
257 return true;
258
259 return false;
260}
261
262static time64
263gnc_prices_dialog_load_view (GtkTreeView *view, GNCPriceDB *pdb, const gchar *target_namespace_name)
264{
265 auto oldest = gnc_time (nullptr);
266 auto model = gtk_tree_view_get_model (view);
267 const auto commodity_table = gnc_get_current_commodities ();
268 auto namespace_list = gnc_commodity_table_get_namespaces_list (commodity_table);
269
270 // disconnect the model to the price treeview
271 g_object_ref (G_OBJECT(model));
272 gtk_tree_view_set_model (GTK_TREE_VIEW(view), nullptr);
273
274 gtk_list_store_clear (GTK_LIST_STORE(model));
275
276 for (auto node_n = namespace_list; node_n; node_n = g_list_next (node_n))
277 {
278 auto tmp_namespace = static_cast<gnc_commodity_namespace*>(node_n->data);
279 auto tmp_namespace_name_str = gnc_commodity_namespace_get_name (tmp_namespace);
280
281 DEBUG("Restricted to %s, looking at namespace %s", target_namespace_name, tmp_namespace_name_str);
282 if (continue_namespace_check (target_namespace_name, tmp_namespace_name_str))
283 continue;
284
285 auto commodity_list = gnc_commodity_table_get_commodities (commodity_table, tmp_namespace_name_str);
286 for (auto node_c = commodity_list; node_c; node_c = g_list_next (node_c))
287 {
288 auto tmp_commodity = static_cast<gnc_commodity*>(node_c->data);
289 auto num = gnc_pricedb_num_prices (pdb, tmp_commodity);
290 DEBUG("Looking at commodity %s, Number of prices %d", gnc_commodity_get_fullname (tmp_commodity), num);
291
292 if (num > 0)
293 {
294 auto list = gnc_pricedb_get_prices (pdb, tmp_commodity, NULL);
295 auto node = g_list_last (list);
296 auto price = static_cast<GNCPrice*> (node->data);
297 auto price_time = gnc_price_get_time64 (price);
298 auto name_str = gnc_commodity_get_printname (tmp_commodity);
299 auto tmp_namespace_gui_str = gnc_commodity_namespace_get_gui_name (tmp_namespace);
300
301 if (oldest > price_time)
302 oldest = price_time;
303
304 auto date_str = qof_print_date (price_time);
305 auto num_str = g_strdup_printf ("%d", num);
306
307 GtkTreeIter iter;
308 gtk_list_store_append (GTK_LIST_STORE(model), &iter);
309 gtk_list_store_set (GTK_LIST_STORE(model), &iter,
310 PRICED_NAMESPACE_NAME, tmp_namespace_gui_str,
311 PRICED_FULL_NAME, name_str,
312 PRICED_COMM, tmp_commodity,
313 PRICED_DATE, date_str,
314 PRICED_COUNT, num_str,
315 -1);
316
317 g_free (date_str);
318 g_free (num_str);
319 g_list_free_full (list, (GDestroyNotify)gnc_price_unref);
320 }
321 }
322 g_list_free (commodity_list);
323 }
324 g_list_free (namespace_list);
325
326 // reconnect the model to the price treeview
327 gtk_tree_view_set_model (GTK_TREE_VIEW(view), model);
328 g_object_unref (G_OBJECT(model));
329
330 return oldest;
331}
332
333static GList *
334gnc_prices_dialog_get_commodities (GtkTreeView *view)
335{
336 auto model = gtk_tree_view_get_model (GTK_TREE_VIEW(view));
337 auto selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(view));
338 auto list = gtk_tree_selection_get_selected_rows (selection, &model);
339 GList *comm_list = nullptr;
340
341 // Walk the list
342 for (auto row = g_list_first (list); row; row = g_list_next (row))
343 {
344 auto path = static_cast<GtkTreePath *> (row->data);
345 GtkTreeIter iter;
346 if (gtk_tree_model_get_iter (model, &iter, path))
347 {
348 gnc_commodity *comm;
349 gtk_tree_model_get (model, &iter, PRICED_COMM, &comm, -1);
350 comm_list = g_list_prepend (comm_list, comm);
351 }
352 }
353 g_list_free_full (list, (GDestroyNotify) gtk_tree_path_free);
354
355 return g_list_reverse (comm_list);
356}
357
358static void
359change_source_flag (PriceRemoveSourceFlags source, gboolean set, gpointer data)
360{
361 auto pdb_dialog = static_cast<PricesDialog *> (data);
362 auto widget_ok = gtk_dialog_get_widget_for_response (GTK_DIALOG(pdb_dialog->remove_dialog),
363 GTK_RESPONSE_OK);
364 auto widget_apply = gtk_dialog_get_widget_for_response (GTK_DIALOG(pdb_dialog->remove_dialog),
365 GTK_RESPONSE_APPLY);
366
367 if (set)
368 pdb_dialog->remove_source = pdb_dialog->remove_source | source;
369 else
370 pdb_dialog->remove_source = pdb_dialog->remove_source & (~source);
371
372 // Check if we have the required options to enable OK and Apply buttons
373 gboolean enable_button = (pdb_dialog->remove_source > 8 ? TRUE : FALSE); // commodities flag is 8
374 gtk_widget_set_sensitive (widget_ok, enable_button);
375 gtk_widget_set_sensitive (widget_apply, enable_button);
376
377 DEBUG("Source is: %d, remove_source is %d", source, pdb_dialog->remove_source);
378}
379
380static void
381check_event_fq_cb (GtkWidget *widget, gpointer data)
382{
383 auto pdb_dialog = static_cast<PricesDialog *> (data);
384 gboolean active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget));
385
386 change_source_flag (PRICE_REMOVE_SOURCE_FQ, active, pdb_dialog);
387}
388
389static void
390check_event_user_cb (GtkWidget *widget, gpointer data)
391{
392 auto pdb_dialog = static_cast<PricesDialog *> (data);
393 gboolean active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget));
394
395 change_source_flag (PRICE_REMOVE_SOURCE_USER, active, pdb_dialog);
396}
397
398static void
399check_event_app_cb (GtkWidget *widget, gpointer data)
400{
401 auto pdb_dialog = static_cast<PricesDialog *> (data);
402 gboolean active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget));
403
404 change_source_flag (PRICE_REMOVE_SOURCE_APP, active, pdb_dialog);
405}
406
407static void
408selection_changed_cb (GtkTreeSelection *selection, gpointer data)
409{
410 auto pdb_dialog = static_cast<PricesDialog *> (data);
411 auto model = gtk_tree_view_get_model (GTK_TREE_VIEW(pdb_dialog->remove_view));
412 auto rows = gtk_tree_selection_get_selected_rows (selection, &model);
413 gboolean have_rows = (gnc_list_length_cmp (rows, 0));
414
415 change_source_flag (PRICE_REMOVE_SOURCE_COMM, have_rows, pdb_dialog);
416 g_list_free_full (rows, (GDestroyNotify) gtk_tree_path_free);
417}
418
419static GDate
420get_fiscal_end_date (void)
421{
422 time64 end;
423 char datebuff[MAX_DATE_LENGTH + 1];
424 memset (datebuff, 0, sizeof(datebuff));
425 end = gnc_accounting_period_fiscal_end();
427 gnc_accounting_period_fiscal_end());
428 PINFO("Fiscal end date is %s", datebuff);
429
430 return time64_to_gdate (end);
431}
432
433static void
434namespace_changed_cb (GtkComboBox *cbwe, gpointer data)
435{
436 auto pdb_dialog = static_cast<PricesDialog *>(data);
437
438 if (pdb_dialog->target_namespace_name)
439 g_free (pdb_dialog->target_namespace_name);
440 pdb_dialog->target_namespace_name = gnc_ui_namespace_picker_ns (GTK_WIDGET(cbwe));
441
442 gnc_prices_dialog_load_view (pdb_dialog->remove_view,
443 pdb_dialog->price_db,
444 pdb_dialog->target_namespace_name);
445}
446
447static PriceRemoveKeepOptions
448get_keep_options_value (GtkBuilder *builder)
449{
450 if (!builder)
451 return PRICE_REMOVE_KEEP_LAST_WEEKLY;
452
453 auto button = GTK_WIDGET(gtk_builder_get_object (builder, "radiobutton_none"));
454 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(button)))
455 return PRICE_REMOVE_KEEP_NONE;
456 button = GTK_WIDGET(gtk_builder_get_object (builder, "radiobutton_last_month"));
457 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(button)))
458 return PRICE_REMOVE_KEEP_LAST_MONTHLY;
459 button = GTK_WIDGET(gtk_builder_get_object (builder, "radiobutton_last_quarter"));
460 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(button)))
461 return PRICE_REMOVE_KEEP_LAST_QUARTERLY;
462 button = GTK_WIDGET(gtk_builder_get_object (builder, "radiobutton_last_period"));
463 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(button)))
464 return PRICE_REMOVE_KEEP_LAST_PERIOD;
465 button = GTK_WIDGET(gtk_builder_get_object (builder, "radiobutton_scaled"));
466 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(button)))
467 return PRICE_REMOVE_KEEP_SCALED;
468
469 // default which is also "radiobutton_last_week"
470 return PRICE_REMOVE_KEEP_LAST_WEEKLY;
471}
472
473void
474gnc_prices_dialog_remove_old_clicked (GtkWidget *widget, gpointer data)
475{
476 auto pdb_dialog = static_cast<PricesDialog *> (data);
477
478 ENTER(" ");
479 auto builder = gtk_builder_new();
480 gnc_builder_add_from_file (builder, "dialog-price.glade", "liststore3");
481 gnc_builder_add_from_file (builder, "dialog-price.glade", "liststore4");
482 gnc_builder_add_from_file (builder, "dialog-price.glade", "deletion_date_dialog");
483
484 pdb_dialog->remove_dialog = GTK_WIDGET(gtk_builder_get_object (builder, "deletion_date_dialog"));
485
486 auto box = GTK_WIDGET(gtk_builder_get_object (builder, "date_hbox"));
487 auto date = gnc_date_edit_new (time (NULL), FALSE, FALSE);
488
489 gtk_box_pack_start (GTK_BOX (box), date, FALSE, FALSE, 0);
490 gtk_widget_show (date);
491 gtk_entry_set_activates_default(GTK_ENTRY(GNC_DATE_EDIT(date)->date_entry), TRUE);
492 auto label = GTK_WIDGET(gtk_builder_get_object (builder, "date_label"));
493 gnc_date_make_mnemonic_target (GNC_DATE_EDIT(date), label);
494
495 // Setup namespace
496 pdb_dialog->namespace_cbwe = GTK_WIDGET(gtk_builder_get_object (builder, "namespace_combo_we"));
497 gnc_ui_update_namespace_picker (pdb_dialog->namespace_cbwe, nullptr, DIAG_COMM_ALL);
498 gnc_cbwe_require_list_item (GTK_COMBO_BOX(pdb_dialog->namespace_cbwe));
499 gtk_combo_box_set_active (GTK_COMBO_BOX(pdb_dialog->namespace_cbwe), 1);
500 g_signal_connect (G_OBJECT(pdb_dialog->namespace_cbwe), "changed",
501 G_CALLBACK(namespace_changed_cb), pdb_dialog);
502
503 // Setup the commodity view
504 pdb_dialog->remove_view = GTK_TREE_VIEW(gtk_builder_get_object (builder, "commodty_treeview"));
505 auto selection = gtk_tree_view_get_selection (pdb_dialog->remove_view);
506 gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
507
508 // Add Entries column this way as align does not seem to work from builder
509 auto tree_column = gtk_tree_view_column_new();
510 gtk_tree_view_column_set_title (tree_column, _("Entries"));
511 gtk_tree_view_append_column (GTK_TREE_VIEW(pdb_dialog->remove_view), tree_column);
512 gtk_tree_view_column_set_alignment (tree_column, 0.5);
513 gtk_tree_view_column_set_expand (tree_column, TRUE);
514 auto cr = gtk_cell_renderer_text_new();
515 gtk_tree_view_column_pack_start (tree_column, cr, TRUE);
516 // set 'xalign' property of the cell renderer
517 gtk_tree_view_column_set_attributes (tree_column, cr, "text", PRICED_COUNT, NULL);
518 gtk_cell_renderer_set_alignment (cr, 0.5, 0.5);
519
520 // Load the view and get the earliest date
521 pdb_dialog->target_namespace_name = g_strdup (GNC_COMMODITY_NS_NONISO_GUI);
522 gnc_prices_dialog_load_view (pdb_dialog->remove_view,
523 pdb_dialog->price_db,
524 pdb_dialog->target_namespace_name);
525
526 g_signal_connect (selection, "changed", G_CALLBACK(selection_changed_cb), pdb_dialog);
527
528 gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, pdb_dialog);
529
530 gtk_window_set_transient_for (GTK_WINDOW (pdb_dialog->remove_dialog), GTK_WINDOW (pdb_dialog->window));
531
532 pdb_dialog->remove_source = PRICE_REMOVE_SOURCE_FQ;
533 change_source_flag (PRICE_REMOVE_SOURCE_FQ, TRUE, pdb_dialog);
534
535 auto button = GTK_WIDGET(gtk_builder_get_object (builder, "checkbutton_fq"));
536 g_signal_connect (button, "toggled", G_CALLBACK (check_event_fq_cb), pdb_dialog);
537 button = GTK_WIDGET(gtk_builder_get_object (builder, "checkbutton_user"));
538 g_signal_connect (button, "toggled", G_CALLBACK (check_event_user_cb), pdb_dialog);
539 button = GTK_WIDGET(gtk_builder_get_object (builder, "checkbutton_app"));
540 g_signal_connect (button, "toggled", G_CALLBACK (check_event_app_cb), pdb_dialog);
541
542 bool leave = false;
543 int response = 0;
544 while (!leave && (response = gtk_dialog_run (GTK_DIALOG(pdb_dialog->remove_dialog))))
545 {
546 if ((response == GTK_RESPONSE_CLOSE) || (response == GTK_RESPONSE_DELETE_EVENT))
547 leave = true;
548
549 if ((response == GTK_RESPONSE_OK) || (response == GTK_RESPONSE_APPLY))
550 {
551 const char *fmt = _("Are you sure you want to delete these prices?");
552 auto comm_list = gnc_prices_dialog_get_commodities (pdb_dialog->remove_view);
553 bool delete_entries = false;
554
555 // Are you sure you want to delete the entries and we have commodities
556 if ((g_list_length (comm_list) != 0) &&
557 (gnc_verify_dialog (GTK_WINDOW(pdb_dialog->remove_dialog), FALSE, fmt, NULL)))
558 {
559 time64 last;
560 GDate fiscal_end_date = get_fiscal_end_date ();
561 PriceRemoveKeepOptions keep = get_keep_options_value (builder);
562 delete_entries = true;
563
564 // disconnect the model to the price treeview
565 auto model = gtk_tree_view_get_model (GTK_TREE_VIEW(pdb_dialog->price_tree));
566 g_object_ref (G_OBJECT(model));
567 gtk_tree_view_set_model (GTK_TREE_VIEW(pdb_dialog->price_tree), nullptr);
568
569 DEBUG("deleting prices for keep option %d", keep);
570 last = gnc_date_edit_get_date (GNC_DATE_EDIT (date));
571
572 if (keep != PRICE_REMOVE_KEEP_SCALED)
573 gnc_pricedb_remove_old_prices (pdb_dialog->price_db, comm_list,
574 &fiscal_end_date, last,
575 static_cast<PriceRemoveSourceFlags> (pdb_dialog->remove_source),
576 keep);
577 else
578 {
579 auto tmp_date = time64_to_gdate (last);
580 g_date_subtract_months (&tmp_date, 6);
581 auto tmp = gdate_to_time64 (tmp_date);
582
583 gnc_pricedb_remove_old_prices (pdb_dialog->price_db, comm_list,
584 &fiscal_end_date, tmp,
585 static_cast<PriceRemoveSourceFlags> (pdb_dialog->remove_source),
586 PRICE_REMOVE_KEEP_LAST_WEEKLY);
587
588 g_date_subtract_months (&tmp_date, 6);
589 tmp = gdate_to_time64 (tmp_date);
590
591 gnc_pricedb_remove_old_prices (pdb_dialog->price_db, comm_list,
592 &fiscal_end_date, tmp,
593 static_cast<PriceRemoveSourceFlags> (pdb_dialog->remove_source),
594 PRICE_REMOVE_KEEP_LAST_MONTHLY);
595 }
596 // reconnect the model to the price treeview
597 gtk_tree_view_set_model (GTK_TREE_VIEW(pdb_dialog->price_tree), model);
598 g_object_unref (G_OBJECT(model));
599 }
600 g_list_free (comm_list);
601
602 if (response == GTK_RESPONSE_OK)
603 {
604 if (delete_entries)
605 leave = true;
606 }
607 else
608 {
609 if (delete_entries)
610 gnc_prices_dialog_load_view (pdb_dialog->remove_view,
611 pdb_dialog->price_db,
612 pdb_dialog->target_namespace_name);
613 }
614 }
615 }
616 gnc_gui_refresh_all ();
617
618 if (pdb_dialog->target_namespace_name)
619 g_free (pdb_dialog->target_namespace_name);
620
621 gtk_widget_destroy (pdb_dialog->remove_dialog);
622 g_object_unref (G_OBJECT(builder));
623 LEAVE(" ");
624}
625
626
627void
628gnc_prices_dialog_add_clicked (GtkWidget *widget, gpointer data)
629{
630 auto pdb_dialog = static_cast<PricesDialog *> (data);
631 GNCPrice *price = nullptr;
632 gboolean unref_price = FALSE;
633
634 ENTER(" ");
635 auto price_list = gnc_tree_view_price_get_selected_prices (pdb_dialog->price_tree);
636 auto comm_list = gnc_tree_view_price_get_selected_commodities (pdb_dialog->price_tree);
637
638 if (price_list) // selected row is on a price
639 {
640 price = static_cast<GNCPrice *> (price_list->data);
641 g_list_free (price_list);
642 }
643 else if (comm_list) // selection contains price parent rows
644 {
645 if (!gnc_list_length_cmp (comm_list, 1)) // make sure it is only one parent
646 {
647 auto comm = GNC_COMMODITY (comm_list->data);
648 auto latest_price = gnc_pricedb_lookup_latest_any_currency (pdb_dialog->price_db, comm);
649
650 if (latest_price)
651 {
652 price = GNC_PRICE (latest_price->data);
653 gnc_price_ref (price);
654
655 gnc_price_list_destroy (latest_price);
656 }
657
658 if (!price)
659 {
660 price = gnc_price_create (pdb_dialog->book);
661 gnc_price_set_commodity (price, comm);
662 }
663
664 unref_price = TRUE;
665 }
666 g_list_free (comm_list);
667 }
668 gnc_price_edit_dialog (pdb_dialog->window, pdb_dialog->session,
669 price, GNC_PRICE_NEW);
670
671 if (unref_price)
672 gnc_price_unref (price);
673 LEAVE(" ");
674}
675
676
677void
678gnc_prices_dialog_get_quotes_clicked (GtkWidget *widget, gpointer data)
679{
680 auto pdb_dialog = static_cast<PricesDialog *> (data);
681
682 ENTER(" ");
683 try {
684 GncQuotes quotes;
685 gnc_set_busy_cursor (NULL, TRUE);
686 quotes.fetch (pdb_dialog->book);
687 gnc_unset_busy_cursor (NULL);
688 if (quotes.had_failures())
689 gnc_warning_dialog(GTK_WINDOW(pdb_dialog->window), "%s",
690 quotes.report_failures().c_str());
691 }
692 catch (const GncQuoteException& err)
693 {
694 gnc_unset_busy_cursor(nullptr);
695 PERR("Price retrieval failed: %s", err.what());
696 gnc_error_dialog(GTK_WINDOW(pdb_dialog->window), _("Price retrieval failed: %s"), err.what());
697 }
698
699 /* Without this, the summary bar on the accounts tab
700 * won't reflect the new prices (bug #522095). */
701 gnc_gui_refresh_all ();
702
703 LEAVE(" ");
704}
705
706
707static void
708gnc_prices_dialog_selection_changed (GtkTreeSelection *treeselection,
709 gpointer data)
710{
711 auto pdb_dialog = static_cast<PricesDialog *> (data);
712
713 ENTER(" ");
714 auto price_list = gnc_tree_view_price_get_selected_prices (pdb_dialog->price_tree);
715 auto length = g_list_length (price_list);
716 g_list_free (price_list);
717
718 auto model = gtk_tree_view_get_model (GTK_TREE_VIEW(pdb_dialog->price_tree));
719 auto rows = gtk_tree_selection_get_selected_rows (treeselection, &model);
720
721 // if selected rows greater than length, parents must of been selected also
722 if (g_list_length (rows) > length)
723 length = 0;
724
725 g_list_free_full (rows, (GDestroyNotify) gtk_tree_path_free);
726
727 gtk_widget_set_sensitive (pdb_dialog->edit_button,
728 length == 1);
729 gtk_widget_set_sensitive (pdb_dialog->remove_button,
730 length >= 1);
731 gtk_widget_set_sensitive (pdb_dialog->add_button,
732 length <= 1);
733 LEAVE("%d prices selected", length);
734}
735
736
737static gboolean
738gnc_price_dialog_filter_ns_func (gnc_commodity_namespace *name_space,
739 gpointer data)
740{
741 auto pdb_dialog = static_cast<PricesDialog *> (data);
742
743 /* Never show the template list */
744 auto name = gnc_commodity_namespace_get_name (name_space);
745 if (g_strcmp0 (name, GNC_COMMODITY_NS_TEMPLATE) == 0)
746 return FALSE;
747
748 /* See if this namespace has commodities */
749 auto cm_list = gnc_commodity_namespace_get_commodity_list (name_space);
750 auto rv = false;
751 for (auto item = cm_list; !rv && item; item = g_list_next (item))
752 {
753 /* For each commodity, see if there are prices */
754 auto comm = static_cast<gnc_commodity *> (item->data);
755 if (gnc_pricedb_has_prices (pdb_dialog->price_db, comm, nullptr))
756 rv = true;
757 }
758
759 g_list_free (cm_list);
760 return rv;
761}
762
763
764static gboolean
765gnc_price_dialog_filter_cm_func (gnc_commodity *commodity,
766 gpointer data)
767{
768 auto pdb_dialog = static_cast<PricesDialog *> (data);
769
770 /* Show any commodity that has prices */
771 return gnc_pricedb_has_prices(pdb_dialog->price_db, commodity, NULL);
772}
773
774
775static void
776row_activated_cb (GtkTreeView *view, GtkTreePath *path,
777 GtkTreeViewColumn *column, gpointer data)
778{
779 GtkTreeModel *model;
780 GtkTreeIter iter;
781
782 g_return_if_fail(view);
783
784 model = gtk_tree_view_get_model(view);
785 if (gtk_tree_model_get_iter(model, &iter, path))
786 {
787 if (gtk_tree_model_iter_has_child(model, &iter))
788 {
789 /* There are children, so it's not a price.
790 * Just expand or collapse the row. */
791 if (gtk_tree_view_row_expanded(view, path))
792 gtk_tree_view_collapse_row(view, path);
793 else
794 gtk_tree_view_expand_row(view, path, FALSE);
795 }
796 else
797 /* It's a price, so click the Edit button. */
798 gnc_prices_dialog_edit_clicked(GTK_WIDGET(view), data);
799 }
800}
801
802
803static void
804gnc_prices_dialog_create (GtkWidget * parent, PricesDialog *pdb_dialog)
805{
806 GtkWidget *window, *scrolled_window;
807 GtkBuilder *builder;
808 GtkTreeView *view;
809 GtkTreeSelection *selection;
810
811 ENTER(" ");
812 builder = gtk_builder_new();
813 gnc_builder_add_from_file (builder, "dialog-price.glade", "prices_window");
814
815 window = GTK_WIDGET(gtk_builder_get_object (builder, "prices_window"));
816 pdb_dialog->window = window;
817
818 // Set the name for this dialog so it can be easily manipulated with css
819 gtk_widget_set_name (GTK_WIDGET(window), "gnc-id-price-edit");
820 gnc_widget_style_context_add_class (GTK_WIDGET(window), "gnc-class-securities");
821
822 pdb_dialog->session = gnc_get_current_session();
823 pdb_dialog->book = qof_session_get_book(pdb_dialog->session);
824 pdb_dialog->price_db = gnc_pricedb_get_db(pdb_dialog->book);
825
826 g_signal_connect (pdb_dialog->window, "delete-event",
827 G_CALLBACK(gnc_prices_dialog_delete_event_cb), pdb_dialog);
828
829 g_signal_connect (pdb_dialog->window, "key_press_event",
830 G_CALLBACK (gnc_prices_dialog_key_press_cb), pdb_dialog);
831
832 /* price tree */
833 scrolled_window = GTK_WIDGET(gtk_builder_get_object (builder, "price_list_window"));
834 view = gnc_tree_view_price_new(pdb_dialog->book,
835 "state-section", STATE_SECTION,
836 "show-column-menu", TRUE,
837 NULL);
838 pdb_dialog->price_tree = GNC_TREE_VIEW_PRICE(view);
839 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET(view));
840 gnc_tree_view_price_set_filter (pdb_dialog->price_tree,
841 gnc_price_dialog_filter_ns_func,
842 gnc_price_dialog_filter_cm_func,
843 NULL,
844 pdb_dialog, NULL);
845
846 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
847 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
848 g_signal_connect (G_OBJECT (selection), "changed",
849 G_CALLBACK (gnc_prices_dialog_selection_changed), pdb_dialog);
850
851 g_signal_connect (G_OBJECT (view), "row-activated",
852 G_CALLBACK (row_activated_cb), pdb_dialog);
853
854 /* buttons */
855 {
856 GtkWidget *button;
857
858 button = GTK_WIDGET(gtk_builder_get_object (builder, "edit_button"));
859 pdb_dialog->edit_button = button;
860
861 button = GTK_WIDGET(gtk_builder_get_object (builder, "remove_button"));
862 pdb_dialog->remove_button = button;
863
864 button = GTK_WIDGET(gtk_builder_get_object (builder, "add_button"));
865 pdb_dialog->add_button = button;
866
868 {
869 button = GTK_WIDGET(gtk_builder_get_object (builder, "get_quotes_button"));
870 gtk_widget_set_sensitive(button, FALSE);
871 }
872 /* default to 'close' button */
873 button = GTK_WIDGET(gtk_builder_get_object (builder, "close_button"));
874 gtk_widget_grab_default (button);
875 gtk_widget_grab_focus (button);
876
877 }
878
879 g_signal_connect (pdb_dialog->window, "destroy",
880 G_CALLBACK(gnc_prices_dialog_destroy_cb), pdb_dialog);
881
882 gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, pdb_dialog);
883 g_object_unref(G_OBJECT(builder));
884
885 gnc_restore_window_size (GNC_PREFS_GROUP, GTK_WINDOW(pdb_dialog->window), GTK_WINDOW (parent));
886 LEAVE(" ");
887}
888
889
890static void
891close_handler (gpointer user_data)
892{
893 auto pdb_dialog = static_cast<PricesDialog *> (user_data);
894
895 ENTER(" ");
896 gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(pdb_dialog->window));
897 gtk_widget_destroy (GTK_WIDGET (pdb_dialog->window));
898 LEAVE(" ");
899}
900
901
902static void
903refresh_handler (GHashTable *changes, gpointer user_data)
904{
905 ENTER(" ");
906 LEAVE(" ");
907}
908
909
910static gboolean
911show_handler (const char *klass, gint component_id,
912 gpointer user_data, gpointer iter_data)
913{
914 auto pdb_dialog = static_cast<PricesDialog *> (user_data);
915
916 ENTER(" ");
917 if (!pdb_dialog)
918 {
919 LEAVE("no data structure");
920 return(FALSE);
921 }
922
923 gtk_window_present (GTK_WINDOW(pdb_dialog->window));
924 LEAVE(" ");
925 return(TRUE);
926}
927
928
929static gboolean
930gnc_prices_dialog_key_press_cb (GtkWidget *widget, GdkEventKey *event,
931 gpointer data)
932{
933 auto pdb_dialog = static_cast<PricesDialog *> (data);
934
935 if (event->keyval == GDK_KEY_Escape)
936 {
937 close_handler (pdb_dialog);
938 return TRUE;
939 }
940 else
941 return FALSE;
942}
943
944
945/********************************************************************\
946 * gnc_prices_dialog *
947 * opens up a window showing all price information *
948 * *
949 * Args: parent - the parent of the window to be created *
950 * Return: nothing *
951\********************************************************************/
952void
953gnc_prices_dialog (GtkWidget * parent)
954{
955 PricesDialog *pdb_dialog;
956 gint component_id;
957
958 ENTER(" ");
959 if (gnc_forall_gui_components (DIALOG_PRICE_DB_CM_CLASS, show_handler, NULL))
960 {
961 LEAVE("existing dialog raised");
962 return;
963 }
964
965 pdb_dialog = g_new0 (PricesDialog, 1);
966
967 gnc_prices_dialog_create (parent, pdb_dialog);
968
969 component_id = gnc_register_gui_component (DIALOG_PRICE_DB_CM_CLASS,
970 refresh_handler, close_handler,
971 pdb_dialog);
972 gnc_gui_component_set_session (component_id, pdb_dialog->session);
973
974 gtk_widget_grab_focus (GTK_WIDGET(pdb_dialog->price_tree));
975
976 gtk_widget_show (pdb_dialog->window);
977 LEAVE(" ");
978}
bool had_failures() noexcept
Report if there were quotes requested but not retrieved.
void fetch(QofBook *book)
Fetch quotes for all commodities in our db that have a quote source set.
"select" and "new" commodity windows
General utilities for dealing with accounting periods.
Currency selection widget.
All type declarations for the whole Gnucash engine.
GLib helper routines.
gtk helper routines.
a simple price database for gnucash
GtkTreeView implementation for gnucash price tree.
utility functions for the GnuCash UI
QofBook * qof_session_get_book(const QofSession *session)
Returns the QofBook of this session.
const char * gnc_commodity_get_fullname(const gnc_commodity *cm)
Retrieve the full name for the specified commodity.
gboolean gnc_quote_source_fq_installed(void)
This function indicates whether or not the Finance::Quote module is installed on a user's computer.
GList * gnc_commodity_namespace_get_commodity_list(const gnc_commodity_namespace *name_space)
Return a list of all commodity data structures in the specified namespace.
const char * gnc_commodity_get_printname(const gnc_commodity *cm)
Retrieve the 'print' name for the specified commodity.
GList * gnc_commodity_table_get_namespaces_list(const gnc_commodity_table *table)
Return a list of all namespace data structures in the commodity table.
const char * gnc_commodity_namespace_get_name(const gnc_commodity_namespace *ns)
Return the textual name of a namespace data structure.
const char * gnc_commodity_namespace_get_gui_name(const gnc_commodity_namespace *ns)
Return the textual name of a namespace data structure in a form suitable to present to the user.
CommodityList * gnc_commodity_table_get_commodities(const gnc_commodity_table *table, const char *name_space)
Return a list of all commodities in the commodity table that are in the given namespace.
#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
size_t qof_print_date_buff(char *buff, const size_t len, time64 t)
Convenience: calls through to qof_print_date_dmy_buff().
Definition gnc-date.cpp:574
char * qof_print_date(time64 t)
Convenience; calls through to qof_print_date_dmy_buff().
Definition gnc-date.cpp:610
time64 gdate_to_time64(GDate d)
Turns a GDate into a time64, returning the first second of the day.
GDate time64_to_gdate(time64 t)
Returns the GDate in which the time64 occurs.
time64 gnc_time(time64 *tbuf)
get the current time
Definition gnc-date.cpp:262
gchar * gnc_ui_namespace_picker_ns(GtkWidget *cbwe)
Given a combo box, return the currently selected namespaces.
@ DIAG_COMM_ALL
Dialog box should allow selection of anything.
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...
GtkTreeView * gnc_tree_view_price_new(QofBook *book, const gchar *first_property_name,...)
Create a new price tree view.
GList * gnc_tree_view_price_get_selected_commodities(GncTreeViewPrice *view)
This function returns a list of commodities associated with the selected rows that are not prices but...
GList * gnc_tree_view_price_get_selected_prices(GncTreeViewPrice *view)
This function returns a list of the prices associated with the selected items in the price tree view.
gint gnc_list_length_cmp(const GList *list, size_t len)
Scans the GList elements the minimum number of iterations required to test it against a specified siz...
#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 LEAVE(format, args...)
Print a function exit debugging message.
Definition qoflog.h:282
#define PERR(format, args...)
Log a serious error.
Definition qoflog.h:244
#define ENTER(format, args...)
Print a function entry debugging message.
Definition qoflog.h:272
PriceList * gnc_pricedb_lookup_latest_any_currency(GNCPriceDB *db, const gnc_commodity *commodity)
Find the most recent price between a commodity and all other commodities.
gboolean gnc_pricedb_has_prices(GNCPriceDB *db, const gnc_commodity *commodity, const gnc_commodity *currency)
Report whether the pricedb contains prices for one commodity in another.
gboolean gnc_pricedb_remove_price(GNCPriceDB *db, GNCPrice *p)
Remove a price from the pricedb and unref the price.
gboolean gnc_pricedb_remove_old_prices(GNCPriceDB *db, GList *comm_list, GDate *fiscal_end_date, time64 cutoff, PriceRemoveSourceFlags source, PriceRemoveKeepOptions keep)
Remove and unref prices older than a certain time.
GNCPriceDB * gnc_pricedb_get_db(QofBook *book)
Return the pricedb associated with the book.
int gnc_pricedb_num_prices(GNCPriceDB *db, const gnc_commodity *c)
Get the number of prices, in any currency, for a given commodity.
PriceList * gnc_pricedb_get_prices(GNCPriceDB *db, const gnc_commodity *commodity, const gnc_commodity *currency)
Return all the prices for a given commodity in another.
void gnc_price_list_destroy(PriceList *prices)
gnc_price_list_destroy - destroy the given price list, calling gnc_price_unref on all the prices incl...
void gnc_price_ref(GNCPrice *p)
gnc_price_ref - indicate your need for a given price to stick around (i.e.
GNCPrice * gnc_price_create(QofBook *book)
gnc_price_create - returns a newly allocated and initialized price with a reference count of 1.
void gnc_price_unref(GNCPrice *p)
gnc_price_unref - indicate you're finished with a price (i.e.
QofBook reference.
Definition qofbook-p.hpp:47