GnuCash  5.6-150-g038405b370+
gnc-plugin-page-register-filter.cpp
Go to the documentation of this file.
1 /**********************************************************************
2  * gnc-plugin-page-register-filter.cpp -- register page filter *
3  * *
4  * Copyright (C) 2026, Robert Fewell *
5  * *
6  * This program is free software; you can redistribute it and/or *
7  * modify it under the terms of the GNU General Public License as *
8  * published by the Free Software Foundation; either version 2 of *
9  * the License, or (at your option) any later version. *
10  * *
11  * This program is distributed in the hope that it will be useful, *
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14  * GNU General Public License for more details. *
15  * *
16  * You should have received a copy of the GNU General Public License *
17  * along with this program; if not, contact: *
18  * *
19  * Free Software Foundation Voice: +1-617-542-5942 *
20  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
21  * Boston, MA 02110-1301, USA gnu@gnu.org *
22  **********************************************************************/
23 
33 #include <config.h>
34 
35 #include <gtk/gtk.h>
36 #include <glib/gi18n.h>
37 #include "dialog-utils.h"
38 #include "gnc-date.h"
39 #include "gnc-date-edit.h"
40 #include "gnc-glib-utils.h"
41 #include "gnc-ui.h"
42 #include "gnc-state.h"
43 #include "gnc-period-select.h"
44 #include "gnc-prefs.h"
45 #include "gnc-ui-util.h"
46 #include "gnc-window.h"
47 #include "gnc-main-window.h"
48 #include "engine-helpers.h"
49 #include "qofbookslots.h"
50 #include "qof.h"
51 #include "Query.h"
52 
53 #include <algorithm>
54 #include <array>
55 #include <cstdio>
56 #include <iostream>
57 #include <sstream>
58 #include <string>
59 #include <vector>
60 
63 
64 static std::string DEFAULT_FILTER_NUM_DAYS_GL = "30";
65 static std::string DEFAULT_FILTER = "0x001f";
66 
67 /* This static indicates the debugging module that this .o belongs to. */
68 static QofLogModule log_module = GNC_MOD_GUI;
69 
71 {
72  GncPluginPage* plugin_page;
73  GtkWidget* dialog;
74  GtkWidget* table;
75  GtkWidget* start_earliest; //label
76  GtkWidget* start_relative_check; //checkbutton
77  GtkWidget* start_relative; //account period
78  GtkWidget* start_date_check; //checkbutton
79  GtkWidget* start_date; //date
80  GtkWidget* start_days_check; //checkbutton
81  GtkWidget* start_days; //spin
82 
83  GtkWidget* end_latest;
84  GtkWidget* end_relative_check;
85  GtkWidget* end_relative;
86  GtkWidget* end_date_check;
87  GtkWidget* end_date;
88  GtkWidget* end_days_check;
89  GtkWidget* end_days;
90 
91  GtkWidget* num_days;
92 
93  cleared_match_t original_cleared_match;
94  GncAccountingPeriod original_start_ap;
95  time64 original_start_time;
96  int original_start_days;
97  GncAccountingPeriod original_end_ap;
98  time64 original_end_time;
99  int original_end_days;
100  int original_days;
101  bool original_save_filter;
102 
103  bool show_save_button;
104 };
105 
106 extern "C"
107 {
108 // These functions are the dialog callbacks. They're connected to their
109 // signals in gnc-plugin-page-register.glade so they mustn't be name-mangled.
110 void
111 gnc_ppr_filter_select_range_cb (GtkRadioButton* button,
112  RegisterFilterDialog* rfd);
113 void
114 gnc_ppr_filter_start_cb (GtkWidget* radio,
115  RegisterFilterDialog* rfd);
116 void
117 gnc_ppr_filter_end_cb (GtkWidget* radio,
118  RegisterFilterDialog* rfd);
119 void
120 gnc_ppr_filter_response_cb (GtkDialog* dialog,
121  gint response,
122  RegisterFilterDialog* rfd);
123 void
124 gnc_ppr_filter_status_select_all_cb (GtkButton* button,
125  RegisterFilterDialog* rfd);
126 void
127 gnc_ppr_filter_status_clear_all_cb (GtkButton* button,
128  RegisterFilterDialog* rfd);
129 void
130 gnc_ppr_filter_status_one_cb (GtkToggleButton* button,
131  RegisterFilterDialog* rfd);
132 void
133 gnc_ppr_filter_save_cb (GtkToggleButton* button,
134  RegisterFilterDialog* rfd);
135 void
136 gnc_ppr_filter_days_changed_cb (GtkSpinButton* button,
137  RegisterFilterDialog* rfd);
138 void
139 gnc_ppr_filter_start_toggle_cb (GtkToggleButton* button,
140  RegisterFilterDialog* rfd);
141 void
142 gnc_ppr_filter_end_toggle_cb (GtkToggleButton* button,
143  RegisterFilterDialog* rfd);
144 void
145 gnc_ppr_filter_start_end_days_changed_cb (GtkSpinButton* button,
146  RegisterFilterDialog* rfd);
147 }
148 
150 {
151  std::string action_name;
152  int value;
153  GtkWidget* widget;
154 };
155 
156 static std::array<status_action, 5> status_actions {{
157  { "filter_status_reconciled", CLEARED_RECONCILED, nullptr },
158  { "filter_status_cleared", CLEARED_CLEARED, nullptr },
159  { "filter_status_voided", CLEARED_VOIDED, nullptr },
160  { "filter_status_frozen", CLEARED_FROZEN, nullptr },
161  { "filter_status_unreconciled", CLEARED_NO, nullptr }
162 }};
163 
164 #ifdef skip
165 static inline bool
166 gboolean_to_bool (gboolean value)
167 {
168  return value ? true : false;
169 }
170 #endif
171 static inline gboolean
172 bool_to_gboolean (bool value)
173 {
174  return value ? TRUE : FALSE;
175 }
176 
177 static std::string
178 get_filter_default_num_of_days (GNCLedgerDisplayType ledger_type)
179 {
180  if (ledger_type == LD_GL)
181  return DEFAULT_FILTER_NUM_DAYS_GL;
182  else
183  return "0";
184 }
185 
186 /* This function converts a time64 value date to a string */
187 static std::string
188 ppr_filter_time2dmy (time64 raw_time)
189 {
190  struct tm* timeinfo;
191  char date_string[11];
192 
193  timeinfo = gnc_localtime (&raw_time);
194  strftime (date_string, 11, "%d-%m-%Y", timeinfo);
195  PINFO("Date to string is %s", date_string);
196  gnc_tm_free (timeinfo);
197 
198  return (date_string);
199 }
200 
201 /* This function converts a string date to a time64 value */
202 static time64
203 ppr_filter_dmy2time (std::string date_string)
204 {
205  struct tm when;
206 
207  PINFO("Date from string is %s", date_string.c_str());
208  memset (&when, 0, sizeof (when));
209 
210  std::sscanf (date_string.c_str(), "%d-%d-%d", &when.tm_mday,
211  &when.tm_mon, &when.tm_year);
212 
213  when.tm_mon -= 1;
214  when.tm_year -= 1900;
215 
216  return gnc_mktime (&when);
217 }
218 
219 /* This function subtracts a number of days from now to a time64 value */
220 static time64
221 get_time_for_days_ago (int days, bool use_day_start)
222 {
223  time64 time_val = 0;
224 
225  if (days > 0)
226  {
227  struct tm tm;
228  if (use_day_start)
230  else
231  gnc_tm_get_today_end (&tm);
232  tm.tm_mday = tm.tm_mday - days;
233  time_val = gnc_mktime (&tm);
234  }
235  return time_val;
236 }
237 
238 static std::vector<std::string>
239 split_filter_by_delimiter (std::string str, char delimiter)
240 {
241  std::istringstream ss;
242  std::vector<std::string> res;
243  std::string token;
244  ss.str (str);
245  while (std::getline (ss, token, delimiter))
246  {
247  res.push_back (token);
248  }
249  return res;
250 }
251 
252 static void
253 ppr_check_for_empty_group (GKeyFile *state_file,
254  const gchar *state_section)
255 {
256  gsize num_keys;
257  gchar **keys = g_key_file_get_keys (state_file, state_section, &num_keys, nullptr);
258 
259  if (num_keys == 0)
260  gnc_state_drop_sections_for (state_section);
261 
262  g_strfreev (keys);
263 }
264 
265 static std::string
266 ppr_filter_load_filter (GNCSplitReg *gsr, GNCLedgerDisplayType ledger_type)
267 {
268  // get the filter from the .gcm file
269  GKeyFile* state_file = gnc_state_get_current();
270  auto state_section = gsr_get_register_state_section (gsr);
271  GError* error = nullptr;
272 
273  auto filter = g_key_file_get_string (state_file, state_section,
274  KEY_PAGE_FILTER, &error);
275  std::string filter_str;
276 
277  if (error)
278  g_clear_error (&error);
279  else
280  filter_str = std::string (filter);
281 
282  g_free (filter);
283  g_free (state_section);
284 
285  if (!filter_str.empty())
286  return filter_str;
287 
288  return DEFAULT_FILTER + ";0;0;" + get_filter_default_num_of_days (ledger_type);
289 }
290 
291 static void
292 set_filterdata_to_defaults (FilterData *fd, bool date_parts_only)
293 {
294  if (!date_parts_only)
295  {
296  fd->cleared_match = (cleared_match_t)std::stol (DEFAULT_FILTER, nullptr, 16);
297  fd->save_filter = false;
298  }
299  fd->start_ap = GNC_ACCOUNTING_PERIOD_INVALID;
300  fd->start_time = 0;
301  fd->start_days = 0;
302  fd->end_ap = GNC_ACCOUNTING_PERIOD_INVALID;
303  fd->end_time = 0;
304  fd->end_days = 0;
305  fd->days = 0;
306 }
307 
308 static int
309 get_trailing_int (const std::string split_filter, const std::string find_text)
310 {
311  int ret_int = -1;
312  std::size_t found = split_filter.find (find_text);
313 
314  if (found != std::string::npos)
315  {
316  std::string found_str = split_filter.substr (found + find_text.length(), std::string::npos);
317  ret_int = std::stol (found_str, nullptr, 10);
318  }
319  return ret_int;
320 }
321 
322 static void
323 update_fd_with_date_filter_parts (FilterData *fd, const std::string filter_part,
324  bool start_filter, int ap_trailing_int, int days_trailing_int)
325 {
326  if (ap_trailing_int != -1)
327  {
328  GDate *tmp_date = nullptr;
329 
330  if (start_filter)
331  {
332  fd->start_ap = (GncAccountingPeriod)ap_trailing_int;
333  tmp_date = gnc_accounting_period_start_gdate (fd->start_ap, nullptr, nullptr);
334  }
335  else
336  {
337  fd->end_ap = (GncAccountingPeriod)ap_trailing_int;
338  tmp_date = gnc_accounting_period_end_gdate (fd->end_ap, nullptr, nullptr);
339  }
340 
341  if (tmp_date)
342  {
343  if (start_filter)
344  fd->start_time = gnc_time64_get_day_start_gdate (tmp_date);
345  else
346  fd->end_time = gnc_time64_get_day_end_gdate (tmp_date);
347 
348  g_date_free (tmp_date);
349  }
350  }
351  else
352  {
353  if (days_trailing_int != -1)
354  {
355  if (start_filter)
356  {
357  fd->start_days = days_trailing_int;
358  fd->start_time = get_time_for_days_ago (fd->start_days, true);
359  }
360  else
361  {
362  fd->end_days = days_trailing_int;
363  fd->end_time = get_time_for_days_ago (fd->end_days, false);
364  }
365  }
366  else
367  {
368  time64 tmp_time = ppr_filter_dmy2time (filter_part);
369  if (start_filter)
370  fd->start_time = gnc_time64_get_day_start (tmp_time);
371  else
372  fd->end_time = gnc_time64_get_day_end (tmp_time);
373  }
374  }
375  fd->save_filter = true;
376 }
377 
378 static void
379 ppr_filter_load_filter_parts (GNCSplitReg *gsr, GNCLedgerDisplayType ledger_type, FilterData *fd)
380 {
381  set_filterdata_to_defaults (fd, false);
382  fd->dialog = nullptr;
383 
384  if (!gsr)
385  return;
386 
387  std::string filter_str = ppr_filter_load_filter (gsr, ledger_type);
388 
389  PINFO("Loaded Filter String is %s", filter_str.c_str());
390 
391  std::vector<std::string> split_filter = split_filter_by_delimiter (filter_str, ';');
392  int split_filter_size = split_filter.size();
393 
394  if (split_filter_size > 0 && (split_filter[0].compare (DEFAULT_FILTER)) != 0)
395  {
396  PINFO("Loaded Filter Status is %s", split_filter[0].c_str());
397 
398  fd->cleared_match = (cleared_match_t)std::stol (split_filter[0], nullptr, 16);
399  fd->save_filter = true;
400  }
401 
402  if (split_filter_size > 1 && (split_filter[1].compare (std::string ("0"))) != 0)
403  {
404  PINFO("Loaded Filter Start Date is %s", split_filter[1].c_str());
405 
406  int ap_trailing_int = get_trailing_int (split_filter[1], "SAP");
407  int days_trailing_int = get_trailing_int (split_filter[1], "SDAY");
408 
409  update_fd_with_date_filter_parts (fd, split_filter[1], true, ap_trailing_int, days_trailing_int);
410  }
411 
412  if (split_filter_size > 2 && (split_filter[2].compare (std::string ("0"))) != 0)
413  {
414  PINFO("Loaded Filter End Date is %s", split_filter[2].c_str());
415 
416  int ap_trailing_int = get_trailing_int (split_filter[2], "EAP");
417  int days_trailing_int = get_trailing_int (split_filter[2], "EDAY");
418 
419  update_fd_with_date_filter_parts (fd, split_filter[2], false, ap_trailing_int, days_trailing_int);
420  }
421 
422  // set the default for the number of days
423  fd->days = (int)std::stol (get_filter_default_num_of_days (ledger_type), nullptr, 10);
424 
425  if (split_filter_size > 3 &&
426  (split_filter[3].compare (get_filter_default_num_of_days (ledger_type)) != 0))
427  {
428  PINFO("Loaded Filter Days is %s", split_filter[3].c_str());
429 
430  fd->days = (int)std::stol (split_filter[3], nullptr, 10);
431  fd->save_filter = true;
432  }
433 }
434 
435 static void
436 ppr_filter_save_filter (GNCSplitReg *gsr, std::string filter)
437 
438 {
439  GNCLedgerDisplayType ledger_type = gnc_ledger_display_type (gsr->ledger);
440 
441  std::string default_filter_str = DEFAULT_FILTER + ";0;0;" +
442  get_filter_default_num_of_days (ledger_type);
443 
444  // save the filter to the .gcm file also
445  GKeyFile* state_file = gnc_state_get_current();
446  auto state_section = gsr_get_register_state_section (gsr);
447 
448  if (filter.empty() || (filter.compare (default_filter_str) == 0))
449  {
450  if (g_key_file_has_key (state_file, state_section, KEY_PAGE_FILTER, nullptr))
451  g_key_file_remove_key (state_file, state_section, KEY_PAGE_FILTER, nullptr);
452 
453  ppr_check_for_empty_group (state_file, state_section);
454  }
455  else
456  {
457  PINFO("The filter to save is %s", filter.c_str());
458  g_key_file_set_string (state_file, state_section, KEY_PAGE_FILTER,
459  filter.c_str());
460  }
461  g_free (state_section);
462 }
463 
464 static void
465 ppr_filter_save_filter_parts (GNCSplitReg *gsr, FilterData *fd)
466 {
467  if (!gsr)
468  return;
469 
470  std::string save_filter_str;
471 
472  if (fd->save_filter)
473  {
474  static const size_t buffer_size = 10;
475  char buffer [buffer_size];
476 
477  // cleared match
478  std::snprintf (buffer, buffer_size, "0x%04x", fd->cleared_match);
479  save_filter_str.append (buffer);
480 
481  // start time
482  if (fd->start_ap != GNC_ACCOUNTING_PERIOD_INVALID)
483  save_filter_str.append (";SAP" + std::to_string (fd->start_ap));
484  else if (fd->start_days > 0)
485  save_filter_str.append (";SDAY" + std::to_string (fd->start_days));
486  else if (fd->start_time != 0)
487  save_filter_str.append (";" + ppr_filter_time2dmy (fd->start_time));
488  else
489  save_filter_str.append (";0");
490 
491  // end time
492  if (fd->end_ap != GNC_ACCOUNTING_PERIOD_INVALID)
493  save_filter_str.append (";EAP" + std::to_string (fd->end_ap));
494  else if (fd->end_days > 0)
495  save_filter_str.append (";EDAY" + std::to_string (fd->end_days));
496  else if (fd->end_time != 0)
497  save_filter_str.append (";" + ppr_filter_time2dmy (fd->end_time));
498  else
499  save_filter_str.append (";0");
500 
501  // number of days
502  if (fd->days > 0)
503  {
504  save_filter_str.append (";" + std::to_string (fd->days));
505  }
506  else
507  save_filter_str.append (";0");
508  }
509  ppr_filter_save_filter (gsr, save_filter_str);
510 }
511 
512 static void
513 update_match_filter_text (cleared_match_t match, const guint mask,
514  const gchar* filter_name, GList **show, GList **hide)
515 {
516  if ((match & mask) == mask)
517  *show = g_list_prepend (*show, g_strdup (filter_name));
518  else
519  *hide = g_list_prepend (*hide, g_strdup (filter_name));
520 }
521 
530 void
532 {
533  GList *t_list = nullptr;
534 
535  ENTER(" ");
536 
537  auto gsr = gnc_plugin_page_register_get_gsr (plugin_page);
538 
539  // filtered start time
540  if (fd->start_time != 0)
541  {
542  auto sdate = qof_print_date (fd->start_time);
543  t_list = g_list_prepend
544  (t_list, g_strdup_printf ("%s %s", _("Start Date:"), sdate));
545  g_free (sdate);
546  }
547 
548  // filtered number of days
549  if (fd->days > 0)
550  {
551  t_list = g_list_prepend
552  (t_list, g_strdup_printf ("%s %d", _("Show previous number of days:"),
553  fd->days));
554  }
555  // filtered end time
556  if (fd->end_time != 0)
557  {
558  auto edate = qof_print_date (fd->end_time);
559  t_list = g_list_prepend
560  (t_list, g_strdup_printf ("%s %s", _("End Date:"), edate));
561  g_free (edate);
562  }
563 
564  // filtered match items
565  if (fd->cleared_match != CLEARED_ALL)
566  {
567  GList *show = nullptr;
568  GList *hide = nullptr;
569 
570  update_match_filter_text (fd->cleared_match, 0x01, _("Unreconciled"),
571  &show, &hide);
572  update_match_filter_text (fd->cleared_match, 0x02, _("Cleared"),
573  &show, &hide);
574  update_match_filter_text (fd->cleared_match, 0x04, _("Reconciled"),
575  &show, &hide);
576  update_match_filter_text (fd->cleared_match, 0x08, _("Frozen"),
577  &show, &hide);
578  update_match_filter_text (fd->cleared_match, 0x10, _("Voided"),
579  &show, &hide);
580 
581  show = g_list_reverse (show);
582  hide = g_list_reverse (hide);
583 
584  if (show)
585  {
586  auto str = gnc_list_formatter (show);
587  t_list = g_list_prepend
588  (t_list, g_strdup_printf ("%s %s", _("Show:"), str));
589  g_free (str);
590  }
591 
592  if (hide)
593  {
594  auto str = gnc_list_formatter (hide);
595  t_list = g_list_prepend
596  (t_list, g_strdup_printf ("%s %s", _("Hide:"), str));
597  g_free (str);
598  }
599 
600  g_list_free_full (show, g_free);
601  g_list_free_full (hide, g_free);
602  }
603 
604  t_list = g_list_reverse (t_list);
605 
606  if (t_list)
607  t_list = g_list_prepend (t_list, g_strdup (_("Filter By:")));
608 
609  // free the existing text if present
610  if (gsr->filter_text)
611  g_free (gsr->filter_text);
612 
613  // set the tooltip text variable in the gsr
614  gsr->filter_text = gnc_g_list_stringjoin (t_list, "\n");
615 
616  g_list_free_full (t_list, g_free);
617 
618  LEAVE(" ");
619 }
620 
621 static void
622 ppr_filter_update_status_query (GncPluginPage* plugin_page)
623 {
624  ENTER(" ");
625 
626  auto gsr = gnc_plugin_page_register_get_gsr (plugin_page);
627  if (!gsr->ledger)
628  {
629  LEAVE("no ledger");
630  return;
631  }
632 
633  // check if this a search register and save query
634  gnc_plugin_page_register_update_for_search_query (GNC_PLUGIN_PAGE_REGISTER(plugin_page));
635 
636  auto query = gnc_plugin_page_register_get_query (plugin_page);
637  if (!query)
638  {
639  LEAVE("no query found");
640  return;
641  }
642 
643  auto fd = gnc_plugin_page_register_get_filter_data (plugin_page);
644  auto reg = gnc_ledger_display_get_split_register (gsr->ledger);
645 
646  /* Remove the old status match */
647  if (reg->type != SEARCH_LEDGER)
648  {
649  GSList *param_list = qof_query_build_param_list (SPLIT_RECONCILE, nullptr);
650  qof_query_purge_terms (query, param_list);
651  g_slist_free (param_list);
652  }
653 
654  /* Install the new status match */
655  if (fd->cleared_match != CLEARED_ALL)
656  xaccQueryAddClearedMatch (query, fd->cleared_match, QOF_QUERY_AND);
657 
658  // Set filter tooltip for summary bar
659  gnc_ppr_filter_set_tooltip (plugin_page, fd);
660 
661  gnc_plugin_page_register_query_update (GNC_PLUGIN_PAGE_REGISTER(plugin_page), query);
662  LEAVE (" ");
663 }
664 
665 static void
666 ppr_filter_update_date_query (GncPluginPage* plugin_page)
667 {
668  ENTER(" ");
669 
670  auto gsr = gnc_plugin_page_register_get_gsr (plugin_page);
671  if (!gsr->ledger)
672  {
673  LEAVE("no ledger");
674  return;
675  }
676 
677  // check if this a search register and save query
678  gnc_plugin_page_register_update_for_search_query (GNC_PLUGIN_PAGE_REGISTER(plugin_page));
679 
680  auto query = gnc_plugin_page_register_get_query (plugin_page);
681  if (!query)
682  {
683  LEAVE("no query found");
684  return;
685  }
686 
687  auto fd = gnc_plugin_page_register_get_filter_data (plugin_page);
688  auto reg = gnc_ledger_display_get_split_register (gsr->ledger);
689 
690  /* Delete any existing old date spec. */
691  if (reg->type != SEARCH_LEDGER)
692  {
693  GSList *param_list = qof_query_build_param_list (SPLIT_TRANS,
694  TRANS_DATE_POSTED, nullptr);
695  qof_query_purge_terms (query, param_list);
696  g_slist_free (param_list);
697  }
698 
699  if (fd->start_time || fd->end_time)
700  {
701  /* Build a new spec */
702  xaccQueryAddDateMatchTT (query,
703  fd->start_time != 0, fd->start_time,
704  fd->end_time != 0, fd->end_time,
705  QOF_QUERY_AND);
706  }
707 
708  if (fd->days > 0)
709  {
710  time64 start = get_time_for_days_ago (fd->days, true);
711  xaccQueryAddDateMatchTT (query, TRUE, start, FALSE, 0, QOF_QUERY_AND);
712  }
713 
714  // Set filter tooltip for summary bar
715  gnc_ppr_filter_set_tooltip (plugin_page, fd);
716 
717  gnc_plugin_page_register_query_update (GNC_PLUGIN_PAGE_REGISTER(plugin_page), query);
718  LEAVE(" ");
719 }
720 
727 void
729 {
730  g_return_if_fail (GNC_IS_PLUGIN_PAGE_REGISTER(plugin_page));
731 
732  auto fd = gnc_plugin_page_register_get_filter_data (plugin_page);
733 
734  set_filterdata_to_defaults (fd, false);
735 
736  ppr_filter_update_date_query (plugin_page);
737 }
738 
744 void
746 {
747  g_return_if_fail (GNC_IS_PLUGIN_PAGE_REGISTER(plugin_page));
748 
749  auto gsr = gnc_plugin_page_register_get_gsr (plugin_page);
750 
751  if (!gsr)
752  return;
753 
754  auto fd = gnc_plugin_page_register_get_filter_data (plugin_page);
755  GNCLedgerDisplayType ledger_type = gnc_ledger_display_type (gsr->ledger);
756 
757  /* Set the filter for the split register and status of save filter button */
758  fd->save_filter = false;
759 
760  ppr_filter_load_filter_parts (gsr, ledger_type, fd);
761 
762  if (ledger_type == LD_GL)
763  {
764  SplitRegister *reg = gnc_ledger_display_get_split_register (gsr->ledger);
765 
766  if (reg->type != GENERAL_JOURNAL) // search ledger and the like
767  set_filterdata_to_defaults (fd, false);
768  }
769  /* Update Query with Filter Status and Dates */
770  ppr_filter_update_status_query (plugin_page);
771  ppr_filter_update_date_query (plugin_page);
772 }
773 
783 void
784 gnc_ppr_filter_status_one_cb (GtkToggleButton* button,
786 {
787  g_return_if_fail (GTK_IS_CHECK_BUTTON(button));
788  g_return_if_fail (GNC_IS_PLUGIN_PAGE_REGISTER(rfd->plugin_page));
789 
790  auto name = gtk_buildable_get_name (GTK_BUILDABLE(button));
791 
792  ENTER("toggle button %s (%p), plugin_page %p", name, button, rfd->plugin_page);
793 
794  auto fd = gnc_plugin_page_register_get_filter_data (rfd->plugin_page);
795 
796  /* Determine what status bit to change */
797  int value = CLEARED_NONE;
798  for (const auto& action : status_actions)
799  {
800  if (action.action_name.compare (name) == 0)
801  {
802  value = action.value;
803  break;
804  }
805  }
806 
807  /* Compute the new match status */
808  if (gtk_toggle_button_get_active (button))
809  fd->cleared_match = (cleared_match_t)(fd->cleared_match | value);
810  else
811  fd->cleared_match = (cleared_match_t)(fd->cleared_match & ~value);
812 
813  ppr_filter_update_status_query (rfd->plugin_page);
814 
815  LEAVE(" ");
816 }
817 
818 static void
819 set_sensitive_start_widget (RegisterFilterDialog *rfd, GtkWidget *enable_widget, gboolean active)
820 {
821  gtk_widget_set_sensitive (GTK_WIDGET(rfd->start_earliest), !active);
822  gtk_widget_set_sensitive (GTK_WIDGET(rfd->start_relative), FALSE);
823  gtk_widget_set_sensitive (GTK_WIDGET(rfd->start_date), FALSE);
824  gtk_widget_set_sensitive (GTK_WIDGET(rfd->start_days), FALSE);
825  gtk_widget_set_sensitive (GTK_WIDGET(enable_widget), active);
826 }
827 
828 static void
829 set_sensitive_end_widget (RegisterFilterDialog *rfd, GtkWidget *enable_widget, gboolean active)
830 {
831  gtk_widget_set_sensitive (GTK_WIDGET(rfd->end_latest), !active);
832  gtk_widget_set_sensitive (GTK_WIDGET(rfd->end_relative), FALSE);
833  gtk_widget_set_sensitive (GTK_WIDGET(rfd->end_date), FALSE);
834  gtk_widget_set_sensitive (GTK_WIDGET(rfd->end_days), FALSE);
835  gtk_widget_set_sensitive (GTK_WIDGET(enable_widget), active);
836 }
837 
838 static void
839 set_checkbutton_with_blocking (GtkWidget *widget1, GtkWidget *widget2,
840  GFunc function,
842  gboolean active)
843 {
844  PINFO("Block GtkToggleButton %p for setting active %s",
845  widget1, active ? "TRUE" : "FALSE");
846  g_signal_handlers_block_by_func (widget1,
847  (gpointer)function, rfd);
848  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(widget1), active);
849  g_signal_handlers_unblock_by_func (widget1,
850  (gpointer)function, rfd);
851 
852  if (widget2)
853  {
854  PINFO("Block GtkToggleButton %p for setting active %s",
855  widget2, active ? "TRUE" : "FALSE");
856  g_signal_handlers_block_by_func (widget2,
857  (gpointer)function, rfd);
858  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(widget2), active);
859  g_signal_handlers_unblock_by_func (widget2,
860  (gpointer)function, rfd);
861  }
862 }
863 
872 void
875 {
876  g_return_if_fail (GTK_IS_BUTTON(button));
877  g_return_if_fail (GNC_IS_PLUGIN_PAGE_REGISTER(rfd->plugin_page));
878 
879  ENTER("(button %p, page %p)", button, rfd->plugin_page);
880 
881  auto fd = gnc_plugin_page_register_get_filter_data (rfd->plugin_page);
882 
883  /* Turn on all the check menu items */
884  for (const auto& action : status_actions)
885  {
886  set_checkbutton_with_blocking (action.widget, nullptr,
888  rfd, TRUE);
889  }
890 
891  /* Set the requested status */
892  fd->cleared_match = CLEARED_ALL;
893  ppr_filter_update_status_query (rfd->plugin_page);
894  LEAVE(" ");
895 }
896 
905 void
908 {
909  g_return_if_fail (GTK_IS_BUTTON(button));
910  g_return_if_fail (GNC_IS_PLUGIN_PAGE_REGISTER(rfd->plugin_page));
911 
912  ENTER("(button %p, page %p)", button, rfd->plugin_page);
913 
914  auto fd = gnc_plugin_page_register_get_filter_data (rfd->plugin_page);
915 
916  /* Turn off all the check menu items */
917  for (const auto& action : status_actions)
918  {
919  set_checkbutton_with_blocking (action.widget, nullptr,
921  rfd, FALSE);
922  }
923 
924  /* Set the requested status */
925  fd->cleared_match = CLEARED_NONE;
926  ppr_filter_update_status_query (rfd->plugin_page);
927  LEAVE(" ");
928 }
929 
930 static void
931 print_info_time64_date (const gchar *text, time64 date)
932 {
933  gchar *date_txt = qof_print_date (date);
934  PINFO("%s, %s", text, date_txt);
935  g_free (date_txt);
936 }
937 
938 static void
939 get_filter_times (RegisterFilterDialog* rfd)
940 {
941  time64 time_val;
942 
943  auto fd = gnc_plugin_page_register_get_filter_data (rfd->plugin_page);
944 
945  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(rfd->start_date_check)))
946  {
947  time_val = gnc_date_edit_get_date (GNC_DATE_EDIT(rfd->start_date));
948  time_val = gnc_time64_get_day_start (time_val);
949  fd->start_time = time_val;
950  fd->start_ap = GNC_ACCOUNTING_PERIOD_INVALID;
951  print_info_time64_date ("Start date is", fd->start_time);
952  }
953  else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(rfd->start_relative_check)))
954  {
955  auto *sdate = gnc_period_select_get_date (GNC_PERIOD_SELECT(rfd->start_relative));
956  fd->start_time = gnc_time64_get_day_start_gdate (sdate);
957  fd->start_ap = gnc_period_select_get_active (GNC_PERIOD_SELECT(rfd->start_relative));
958  print_info_time64_date ("Start date relative is", fd->start_time);
959  g_date_free (sdate);
960  }
961  else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(rfd->start_days_check)))
962  {
963  fd->start_days = gtk_spin_button_get_value (GTK_SPIN_BUTTON(rfd->start_days));
964  fd->start_time = get_time_for_days_ago (fd->start_days, true);
965  fd->start_ap = GNC_ACCOUNTING_PERIOD_INVALID;
966  print_info_time64_date ("Start date using days is", fd->start_time);
967  }
968  else
969  fd->start_time = 0;
970 
971  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(rfd->end_date_check)))
972  {
973  time_val = gnc_date_edit_get_date (GNC_DATE_EDIT(rfd->end_date));
974  time_val = gnc_time64_get_day_end (time_val);
975  fd->end_time = time_val;
976  fd->end_ap = GNC_ACCOUNTING_PERIOD_INVALID;
977  print_info_time64_date ("End date is", fd->end_time);
978  }
979  else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(rfd->end_relative_check)))
980  {
981  auto *edate = gnc_period_select_get_date (GNC_PERIOD_SELECT(rfd->end_relative));
982  fd->end_time = gnc_time64_get_day_end_gdate (edate);
983  fd->end_ap = gnc_period_select_get_active (GNC_PERIOD_SELECT(rfd->end_relative));
984  print_info_time64_date ("End date relative is", fd->end_time);
985  g_date_free (edate);
986  }
987  else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(rfd->end_days_check)))
988  {
989  fd->end_days = gtk_spin_button_get_value (GTK_SPIN_BUTTON(rfd->end_days));
990  fd->end_time = get_time_for_days_ago (fd->end_days, false);
991  fd->end_ap = GNC_ACCOUNTING_PERIOD_INVALID;
992  print_info_time64_date ("End date using days is", fd->end_time);
993  }
994  else
995  fd->end_time = 0;
996 }
997 
1008 void
1009 gnc_ppr_filter_select_range_cb (GtkRadioButton* button,
1010  RegisterFilterDialog* rfd)
1011 {
1012  g_return_if_fail (GTK_IS_RADIO_BUTTON(button));
1013  g_return_if_fail (GNC_IS_PLUGIN_PAGE_REGISTER(rfd->plugin_page));
1014 
1015  ENTER("(button %p, page %p)", button, rfd->plugin_page);
1016 
1017  auto fd = gnc_plugin_page_register_get_filter_data (rfd->plugin_page);
1018 
1019  auto name = gtk_buildable_get_name (GTK_BUILDABLE(button));
1020  gboolean active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(button));
1021 
1022  if (active && g_strcmp0 (name, "filter_show_range") == 0)
1023  {
1024  gtk_widget_set_sensitive (rfd->table, active);
1025  gtk_widget_set_sensitive (rfd->num_days, !active);
1026  get_filter_times (rfd);
1027  }
1028  else if (active && g_strcmp0 (name, "filter_show_days") == 0)
1029  {
1030  gtk_widget_set_sensitive (rfd->table, !active);
1031  gtk_widget_set_sensitive (rfd->num_days, active);
1032  fd->days = gtk_spin_button_get_value (GTK_SPIN_BUTTON(rfd->num_days));
1033  }
1034  else
1035  {
1036  gtk_widget_set_sensitive (rfd->table, FALSE);
1037  gtk_widget_set_sensitive (rfd->num_days, FALSE);
1038  set_filterdata_to_defaults (fd, true);
1039  }
1040  ppr_filter_update_date_query (rfd->plugin_page);
1041  LEAVE(" ");
1042 }
1043 
1053 void
1054 gnc_ppr_filter_days_changed_cb (GtkSpinButton* button,
1055  RegisterFilterDialog* rfd)
1056 {
1057  g_return_if_fail (GTK_IS_SPIN_BUTTON(button));
1058  g_return_if_fail (GNC_IS_PLUGIN_PAGE_REGISTER(rfd->plugin_page));
1059 
1060  ENTER("(button %p, page %p)", button, rfd->plugin_page);
1061 
1062  auto fd = gnc_plugin_page_register_get_filter_data (rfd->plugin_page);
1063 
1064  fd->days = gtk_spin_button_get_value (GTK_SPIN_BUTTON(button));
1065  ppr_filter_update_date_query (rfd->plugin_page);
1066 
1067  LEAVE(" ");
1068 }
1069 
1070 static void
1071 ppr_filter_gde_changed_cb (GtkWidget* unused,
1072  RegisterFilterDialog* rfd)
1073 {
1074  g_return_if_fail (GNC_IS_PLUGIN_PAGE_REGISTER(rfd->plugin_page));
1075 
1076  ENTER("(widget %s(%p), page %p)",
1077  gtk_buildable_get_name (GTK_BUILDABLE(unused)), unused, rfd->plugin_page);
1078 
1079  get_filter_times (rfd);
1080  ppr_filter_update_date_query (rfd->plugin_page);
1081 
1082  LEAVE(" ");
1083 }
1084 
1103 void
1104 gnc_ppr_filter_start_cb (GtkWidget* radio,
1105  RegisterFilterDialog* rfd)
1106 {
1107  g_return_if_fail (GTK_IS_RADIO_BUTTON(radio));
1108  g_return_if_fail (GNC_IS_PLUGIN_PAGE_REGISTER(rfd->plugin_page));
1109 
1110  ENTER("(radio %s(%p), page %p)",
1111  gtk_buildable_get_name (GTK_BUILDABLE(radio)), radio, rfd->plugin_page);
1112 
1113  if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(radio)))
1114  {
1115  LEAVE("1st callback of pair. Defer to 2nd callback.");
1116  return;
1117  }
1118 
1119  auto name = gtk_buildable_get_name (GTK_BUILDABLE(radio));
1120  gboolean active = !g_strcmp0 (name, "start_date_choose");
1121  gtk_widget_set_sensitive (rfd->start_date, active);
1122  get_filter_times (rfd);
1123  ppr_filter_update_date_query (rfd->plugin_page);
1124 
1125  LEAVE(" ");
1126 }
1127 
1146 void
1147 gnc_ppr_filter_end_cb (GtkWidget* radio,
1148  RegisterFilterDialog* rfd)
1149 {
1150  g_return_if_fail (GTK_IS_RADIO_BUTTON(radio));
1151  g_return_if_fail (GNC_IS_PLUGIN_PAGE_REGISTER(rfd->plugin_page));
1152 
1153  ENTER("(radio %s(%p), page %p)",
1154  gtk_buildable_get_name (GTK_BUILDABLE(radio)), radio, rfd->plugin_page);
1155 
1156  if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(radio)))
1157  {
1158  LEAVE("1st callback of pair. Defer to 2nd callback.");
1159  return;
1160  }
1161 
1162  auto name = gtk_buildable_get_name (GTK_BUILDABLE(radio));
1163  gboolean active = !g_strcmp0 (name, "end_date_choose");
1164  gtk_widget_set_sensitive (rfd->end_date, active);
1165  get_filter_times (rfd);
1166  ppr_filter_update_date_query (rfd->plugin_page);
1167 
1168  LEAVE(" ");
1169 }
1170 
1171 static void
1172 ppr_filter_relative_changed_cb (GtkWidget *widget,
1173  RegisterFilterDialog* rfd)
1174 {
1175  g_return_if_fail (GNC_IS_PERIOD_SELECT(widget));
1176  g_return_if_fail (GNC_IS_PLUGIN_PAGE_REGISTER(rfd->plugin_page));
1177 
1178  ENTER("Period Select (%p), active_id %d, plugin_page %p",
1179  widget, gnc_period_select_get_active (GNC_PERIOD_SELECT(widget)),
1180  rfd->plugin_page);
1181 
1182  get_filter_times (rfd);
1183  ppr_filter_update_date_query (rfd->plugin_page);
1184 
1185  LEAVE(" ");
1186 }
1187 
1197 void
1199  RegisterFilterDialog* rfd)
1200 {
1201  g_return_if_fail (GTK_IS_SPIN_BUTTON(button));
1202  g_return_if_fail (GNC_IS_PLUGIN_PAGE_REGISTER(rfd->plugin_page));
1203 
1204  ENTER("(button %p, page %p)", button, rfd->plugin_page);
1205 
1206  auto fd = gnc_plugin_page_register_get_filter_data (rfd->plugin_page);
1207 
1208  auto name = gtk_buildable_get_name (GTK_BUILDABLE(button));
1209 
1210  if (g_strcmp0 (name, "start_days_spin") == 0)
1211  fd->start_days = gtk_spin_button_get_value (GTK_SPIN_BUTTON(button));
1212 
1213  if (g_strcmp0 (name, "end_days_spin") == 0)
1214  fd->end_days = gtk_spin_button_get_value (GTK_SPIN_BUTTON(button));
1215 
1216  get_filter_times (rfd);
1217  ppr_filter_update_date_query (rfd->plugin_page);
1218 
1219  LEAVE(" ");
1220 }
1221 
1230 void
1231 gnc_ppr_filter_start_toggle_cb (GtkToggleButton* button,
1232  RegisterFilterDialog* rfd)
1233 {
1234  g_return_if_fail (GTK_IS_CHECK_BUTTON(button));
1235  g_return_if_fail (GNC_IS_PLUGIN_PAGE_REGISTER(rfd->plugin_page));
1236 
1237  ENTER("Start toggle button (%p), plugin_page %p", button, rfd->plugin_page);
1238 
1239  auto name = gtk_buildable_get_name (GTK_BUILDABLE(button));
1240 
1241  gboolean active = gtk_toggle_button_get_active (button);
1242 
1243  gtk_widget_set_sensitive (rfd->start_earliest, !active);
1244 
1245  if (g_strcmp0 (name, "start_date_check") == 0)
1246  {
1247  set_sensitive_start_widget (rfd, rfd->start_date, active);
1248 
1249  set_checkbutton_with_blocking (rfd->start_relative_check,
1250  rfd->start_days_check,
1252  rfd, FALSE);
1253  }
1254  if (g_strcmp0 (name, "start_relative_check") == 0)
1255  {
1256  set_sensitive_start_widget (rfd, rfd->start_relative, active);
1257 
1258  set_checkbutton_with_blocking (rfd->start_date_check,
1259  rfd->start_days_check,
1261  rfd, FALSE);
1262  }
1263  if (g_strcmp0 (name, "start_days_check") == 0)
1264  {
1265  set_sensitive_start_widget (rfd, rfd->start_days, active);
1266 
1267  set_checkbutton_with_blocking (rfd->start_date_check,
1268  rfd->start_relative_check,
1270  rfd, FALSE);
1271  }
1272  get_filter_times (rfd);
1273  ppr_filter_update_date_query (rfd->plugin_page);
1274 
1275  LEAVE(" ");
1276 }
1277 
1286 void
1287 gnc_ppr_filter_end_toggle_cb (GtkToggleButton* button,
1288  RegisterFilterDialog* rfd)
1289 {
1290  g_return_if_fail (GTK_IS_CHECK_BUTTON(button));
1291  g_return_if_fail (GNC_IS_PLUGIN_PAGE_REGISTER(rfd->plugin_page));
1292 
1293  ENTER("End toggle button (%p), plugin_page %p", button, rfd->plugin_page);
1294 
1295  auto name = gtk_buildable_get_name (GTK_BUILDABLE(button));
1296 
1297  gboolean active = gtk_toggle_button_get_active (button);
1298 
1299  gtk_widget_set_sensitive (rfd->end_latest, !active);
1300 
1301  if (g_strcmp0 (name, "end_date_check") == 0)
1302  {
1303  set_sensitive_end_widget (rfd, rfd->end_date, active);
1304 
1305  set_checkbutton_with_blocking (rfd->end_relative_check,
1306  rfd->end_days_check,
1308  rfd, FALSE);
1309  }
1310  if (g_strcmp0 (name, "end_relative_check") == 0)
1311  {
1312  set_sensitive_end_widget (rfd, rfd->end_relative, active);
1313 
1314  set_checkbutton_with_blocking (rfd->end_date_check,
1315  rfd->end_days_check,
1317  rfd, FALSE);
1318  }
1319  if (g_strcmp0 (name, "end_days_check") == 0)
1320  {
1321  set_sensitive_end_widget (rfd, rfd->end_days, active);
1322 
1323  set_checkbutton_with_blocking (rfd->end_date_check,
1324  rfd->end_relative_check,
1326  rfd, FALSE);
1327  }
1328  get_filter_times (rfd);
1329  ppr_filter_update_date_query (rfd->plugin_page);
1330 
1331  LEAVE(" ");
1332 }
1333 
1341 void
1342 gnc_ppr_filter_save_cb (GtkToggleButton* button,
1343  RegisterFilterDialog* rfd)
1344 {
1345  g_return_if_fail (GTK_IS_CHECK_BUTTON(button));
1346  g_return_if_fail (GNC_IS_PLUGIN_PAGE_REGISTER(rfd->plugin_page));
1347 
1348  ENTER("Save toggle button (%p), plugin_page %p", button, rfd->plugin_page);
1349 
1350  auto fd = gnc_plugin_page_register_get_filter_data (rfd->plugin_page);
1351 
1352  /* Compute the new save filter status */
1353  if (gtk_toggle_button_get_active (button))
1354  fd->save_filter = true;
1355  else
1356  fd->save_filter = false;
1357 
1358  LEAVE(" ");
1359 }
1360 
1371 void
1372 gnc_ppr_filter_response_cb (GtkDialog* dialog,
1373  gint response,
1374  RegisterFilterDialog* rfd)
1375 {
1376  g_return_if_fail (GTK_IS_DIALOG(dialog));
1377  g_return_if_fail (GNC_IS_PLUGIN_PAGE_REGISTER(rfd->plugin_page));
1378 
1379  ENTER(" ");
1380 
1381  auto fd = gnc_plugin_page_register_get_filter_data (rfd->plugin_page);
1382  auto gsr = gnc_plugin_page_register_get_gsr (rfd->plugin_page);
1383 
1384  if ((fd->start_time > 0 && fd->end_time > 0) && (fd->start_time > fd->end_time))
1385  {
1386  auto response = gnc_ok_cancel_dialog (GTK_WINDOW(rfd->dialog),
1387  GTK_RESPONSE_CANCEL,
1388  _("The Start date is after the End date.\n"
1389  "Select Cancel to change dates.\n"));
1390  if (response == GTK_RESPONSE_CANCEL)
1391  return;
1392  }
1393 
1394  if (response != GTK_RESPONSE_OK)
1395  {
1396  /* Remove the old status match */
1397  fd->cleared_match = rfd->original_cleared_match;
1398  gnc_plugin_register_set_enable_refresh (GNC_PLUGIN_PAGE_REGISTER(rfd->plugin_page), FALSE);
1399  ppr_filter_update_status_query (rfd->plugin_page);
1400  gnc_plugin_register_set_enable_refresh (GNC_PLUGIN_PAGE_REGISTER(rfd->plugin_page), TRUE);
1401 
1402  fd->start_ap = rfd->original_start_ap;
1403  fd->start_time = rfd->original_start_time;
1404  fd->start_days = rfd->original_start_days;
1405  fd->end_ap = rfd->original_end_ap;
1406  fd->end_time = rfd->original_end_time;
1407  fd->end_days = rfd->original_end_days;
1408 
1409  fd->days = rfd->original_days;
1410  fd->save_filter = rfd->original_save_filter;
1411  ppr_filter_update_date_query (rfd->plugin_page);
1412  }
1413  else
1414  {
1415  // clear the filter when unticking the save option
1416  if (!fd->save_filter && rfd->original_save_filter)
1417  ppr_filter_save_filter (gsr, "");
1418 
1419  rfd->original_save_filter = fd->save_filter;
1420 
1421  if (fd->save_filter)
1422  ppr_filter_save_filter_parts (gsr, fd);
1423  }
1424  rfd->dialog = nullptr;
1425  fd->dialog = nullptr;
1426  g_free (rfd);
1427  gtk_widget_destroy (GTK_WIDGET(dialog));
1428  LEAVE(" ");
1429 }
1430 
1431 static GtkWidget *
1432 setup_period_select (GtkBuilder *builder, gboolean start_type, const gchar *hbox_txt)
1433 {
1434  GtkWidget *period_select = GTK_WIDGET(gnc_period_select_new (start_type));
1435 
1436  auto hbox = GTK_WIDGET(gtk_builder_get_object (builder, hbox_txt));
1437  gtk_box_pack_start (GTK_BOX(hbox), period_select, TRUE, TRUE, 0);
1438  gtk_widget_show (period_select);
1439  gnc_period_select_set_active (GNC_PERIOD_SELECT(period_select), GNC_ACCOUNTING_PERIOD_TODAY);
1440  gtk_widget_set_sensitive (GTK_WIDGET(period_select), FALSE);
1441  return period_select;
1442 }
1443 
1444 static GtkWidget *
1445 setup_date_edit (GtkBuilder *builder, const gchar *hbox_txt)
1446 {
1447  GtkWidget *date_widget = gnc_date_edit_new (gnc_time (nullptr), FALSE, FALSE);
1448  auto hbox = GTK_WIDGET(gtk_builder_get_object (builder, hbox_txt));
1449  gtk_box_pack_start (GTK_BOX(hbox), date_widget, TRUE, TRUE, 0);
1450  gtk_widget_show (date_widget);
1451  gtk_widget_set_sensitive (GTK_WIDGET(date_widget), FALSE);
1452  return date_widget;
1453 }
1454 
1455 static void
1456 ppr_filter_dialog_create (RegisterFilterDialog* rfd, FilterData *fd, Query *query)
1457 {
1458  time64 start_time, end_time, time_val;
1459 
1460  /* Create the dialog */
1461  auto builder = gtk_builder_new();
1462  gnc_builder_add_from_file (builder, "gnc-plugin-page-register.glade", "days_adjustment");
1463  gnc_builder_add_from_file (builder, "gnc-plugin-page-register.glade", "start_days_adjustment");
1464  gnc_builder_add_from_file (builder, "gnc-plugin-page-register.glade", "end_days_adjustment");
1465  gnc_builder_add_from_file (builder, "gnc-plugin-page-register.glade", "filter_by_dialog");
1466  auto dialog = GTK_WIDGET(gtk_builder_get_object (builder, "filter_by_dialog"));
1467  rfd->dialog = dialog;
1468  fd->dialog = rfd->dialog;
1469 
1470  gtk_window_set_transient_for (GTK_WINDOW(dialog),
1471  gnc_window_get_gtk_window (GNC_WINDOW(
1472  GNC_PLUGIN_PAGE(rfd->plugin_page)->window)));
1473 
1474  /* Translators: The %s is the name of the plugin page */
1475  auto title = g_strdup_printf (_ ("Filter %s by…"),
1476  gnc_plugin_page_get_page_name (rfd->plugin_page));
1477  gtk_window_set_title (GTK_WINDOW(dialog), title);
1478  g_free (title);
1479 
1480  /* Set the check buttons for the current status */
1481  for (auto& action : status_actions)
1482  {
1483  auto toggle = GTK_WIDGET(gtk_builder_get_object (builder,
1484  action.action_name.c_str()));
1485  bool value = fd->cleared_match & action.value;
1486  action.widget = toggle;
1487  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(toggle), bool_to_gboolean (value));
1488  }
1489  rfd->original_cleared_match = fd->cleared_match;
1490 
1491  auto button = GTK_WIDGET(gtk_builder_get_object (builder, "filter_save"));
1492  if (fd->save_filter)
1493  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(button), TRUE);
1494 
1495  rfd->original_save_filter = fd->save_filter;
1496 
1497  // hide the save button if appropriate
1498  gtk_widget_set_visible (GTK_WIDGET(button), bool_to_gboolean (rfd->show_save_button));
1499 
1500  /* Set up number of days */
1501  rfd->num_days = GTK_WIDGET(gtk_builder_get_object (builder, "filter_show_num_days"));
1502  button = GTK_WIDGET(gtk_builder_get_object (builder, "filter_show_days"));
1503 
1504  if (fd->days > 0) // using number of days
1505  {
1506  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(button), TRUE);
1507  gtk_widget_set_sensitive (GTK_WIDGET(rfd->num_days), TRUE);
1508  gtk_spin_button_set_value (GTK_SPIN_BUTTON(rfd->num_days), fd->days);
1509  rfd->original_days = fd->days;
1510 
1511  /* Set the start_time and end_time to 0 */
1512  start_time = 0;
1513  end_time = 0;
1514  }
1515  else
1516  {
1517  gtk_widget_set_sensitive (GTK_WIDGET(rfd->num_days), FALSE);
1518  rfd->original_days = 0;
1519  fd->days = 0;
1520 
1521  /* Get the start and end times */
1522  xaccQueryGetDateMatchTT (query, &start_time, &end_time);
1523  }
1524 
1525  /* Set the date info */
1526  rfd->original_start_time = start_time;
1527  fd->start_time = start_time;
1528  rfd->original_end_time = end_time;
1529  fd->end_time = end_time;
1530 
1531  button = GTK_WIDGET(gtk_builder_get_object (builder, "filter_show_range"));
1532  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(button), start_time || end_time);
1533  auto table = GTK_WIDGET(gtk_builder_get_object (builder, "select_range_table"));
1534  rfd->table = table;
1535  gtk_widget_set_sensitive (GTK_WIDGET(table), start_time || end_time);
1536 
1537  rfd->start_earliest = GTK_WIDGET(gtk_builder_get_object (builder, "earliest_label"));
1538  rfd->start_date_check = GTK_WIDGET(gtk_builder_get_object (builder, "start_date_check"));
1539  rfd->start_relative_check = GTK_WIDGET(gtk_builder_get_object (builder, "start_relative_check"));
1540  rfd->start_days_check = GTK_WIDGET(gtk_builder_get_object (builder, "start_days_check"));
1541 
1542  {
1543  rfd->start_relative = setup_period_select (builder, TRUE, "start_relative_hbox");
1544  rfd->start_date = setup_date_edit (builder, "start_date_hbox");
1545  rfd->start_days = GTK_WIDGET(gtk_builder_get_object (builder, "start_days_spin"));
1546 
1547  /* Start date info */
1548  if (start_time == 0)
1549  {
1550  set_sensitive_start_widget (rfd, rfd->start_earliest, TRUE);
1551  time_val = xaccQueryGetEarliestDateFound (query);
1552  }
1553  else
1554  {
1555  rfd->original_start_ap = fd->start_ap;
1556  if (fd->start_ap != GNC_ACCOUNTING_PERIOD_INVALID)
1557  {
1558  set_sensitive_start_widget (rfd, rfd->start_relative, TRUE);
1559  gnc_period_select_set_active (GNC_PERIOD_SELECT(rfd->start_relative), fd->start_ap);
1560  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(rfd->start_relative_check), TRUE);
1561  }
1562  else if (fd->start_days != 0)
1563  {
1564  set_sensitive_start_widget (rfd, rfd->start_days, TRUE);
1565  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(rfd->start_days_check), TRUE);
1566  gtk_spin_button_set_value (GTK_SPIN_BUTTON(rfd->start_days), fd->start_days);
1567  rfd->original_start_days = fd->start_days;
1568 
1569  }
1570  else
1571  {
1572  set_sensitive_start_widget (rfd, rfd->start_date, TRUE);
1573  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(rfd->start_date_check), TRUE);
1574  }
1575  time_val = start_time;
1576  }
1577  g_signal_connect (G_OBJECT(rfd->start_relative), "changed",
1578  G_CALLBACK(ppr_filter_relative_changed_cb), rfd);
1579 
1580  if (time_val == 0)
1581  time_val = gnc_time64_get_today_start();
1582  gnc_date_edit_set_time (GNC_DATE_EDIT(rfd->start_date), time_val);
1583  g_signal_connect (G_OBJECT(rfd->start_date), "date-changed",
1584  G_CALLBACK(ppr_filter_gde_changed_cb), rfd);
1585  }
1586 
1587  rfd->end_latest = GTK_WIDGET(gtk_builder_get_object (builder, "latest_label"));
1588  rfd->end_date_check = GTK_WIDGET(gtk_builder_get_object (builder, "end_date_check"));
1589  rfd->end_relative_check = GTK_WIDGET(gtk_builder_get_object (builder, "end_relative_check"));
1590  rfd->end_days_check = GTK_WIDGET(gtk_builder_get_object (builder, "end_days_check"));
1591 
1592  {
1593  rfd->end_relative = setup_period_select (builder, FALSE, "end_relative_hbox");
1594  rfd->end_date = setup_date_edit (builder, "end_date_hbox");
1595  rfd->end_days = GTK_WIDGET(gtk_builder_get_object (builder, "end_days_spin"));
1596 
1597  /* End date info */
1598  if (end_time == 0)
1599  {
1600  set_sensitive_end_widget (rfd, rfd->end_latest, TRUE);
1601  time_val = xaccQueryGetLatestDateFound (query);
1602  }
1603  else
1604  {
1605  rfd->original_end_ap = fd->end_ap;
1606  if (fd->end_ap != GNC_ACCOUNTING_PERIOD_INVALID)
1607  {
1608  set_sensitive_end_widget (rfd, rfd->end_relative, TRUE);
1609  gnc_period_select_set_active (GNC_PERIOD_SELECT(rfd->end_relative), fd->end_ap);
1610  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(rfd->end_relative_check), TRUE);
1611  }
1612  else if (fd->end_days != 0)
1613  {
1614  set_sensitive_end_widget (rfd, rfd->end_days, TRUE);
1615  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(rfd->end_days_check), TRUE);
1616  gtk_spin_button_set_value (GTK_SPIN_BUTTON(rfd->end_days), fd->end_days);
1617  rfd->original_end_days = fd->end_days;
1618  }
1619  else
1620  {
1621  set_sensitive_end_widget (rfd, rfd->end_date, TRUE);
1622  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(rfd->end_date_check), TRUE);
1623  }
1624  time_val = end_time;
1625  }
1626  g_signal_connect (G_OBJECT(rfd->end_relative), "changed",
1627  G_CALLBACK(ppr_filter_relative_changed_cb), rfd);
1628 
1629  if (time_val == 0)
1630  time_val = gnc_time64_get_today_end();
1631  gnc_date_edit_set_time (GNC_DATE_EDIT(rfd->end_date), time_val);
1632  g_signal_connect (G_OBJECT(rfd->end_date), "date-changed",
1633  G_CALLBACK(ppr_filter_gde_changed_cb), rfd);
1634  }
1635 
1636  /* Wire it up */
1637  gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, rfd);
1638 
1639  /* Show it */
1640  gtk_widget_show (dialog);
1641  g_object_unref (G_OBJECT(builder));
1642  LEAVE (" ");
1643 }
1644 
1656 void
1657 gnc_ppr_filter_by (GncPluginPage *plugin_page, Query *query,
1658  FilterData *fd, bool show_save_button)
1659 {
1660  RegisterFilterDialog *rfd;
1661 
1662  ENTER(" ");
1663 
1664  rfd = g_new0 (RegisterFilterDialog, 1);
1665 
1666  rfd->plugin_page = plugin_page;
1667  rfd->show_save_button = show_save_button;
1668 
1669  ppr_filter_dialog_create (rfd, fd, query);
1670 
1671  LEAVE(" ");
1672 }
Functions to load, save and get gui state.
Functions providing a register page filter for the GnuCash UI.
void gnc_plugin_register_set_enable_refresh(GncPluginPageRegister *page, gboolean enable_refresh)
This allows controlling when refreshes happen, used to reduce refreshes when different aspects of the...
The instance data structure for a content plugin.
Date and Time handling routines.
gchar * gnc_g_list_stringjoin(GList *list_of_strings, const gchar *sep)
Return a string joining a GList whose elements are gchar* strings.
utility functions for the GnuCash UI
#define PINFO(format, args...)
Print an informational note.
Definition: qoflog.h:256
void qof_query_purge_terms(QofQuery *q, QofQueryParamList *param_list)
Remove query terms of a particular type from q.
Definition: qofquery.cpp:705
FilterData * gnc_plugin_page_register_get_filter_data(GncPluginPage *plugin_page)
Get the FilterData data structure associated with this register page.
gint gnc_state_drop_sections_for(const gchar *partial_name)
Drop all sections from the state file whose name contains partial_name.
Definition: gnc-state.c:260
void gnc_ppr_filter_end_cb(GtkWidget *radio, RegisterFilterDialog *rfd)
This function is called when one of the end date radio buttons is selected.
const gchar * gnc_plugin_page_get_page_name(GncPluginPage *page)
Retrieve the name of this page.
Functions that are supported by all types of windows.
void gnc_ppr_filter_save_cb(GtkToggleButton *button, RegisterFilterDialog *rfd)
This function is called whenever the save status is checked or unchecked.
void gnc_ppr_filter_response_cb(GtkDialog *dialog, gint response, RegisterFilterDialog *rfd)
This function is called when the "Filter By…" dialog is closed.
GncAccountingPeriod gnc_period_select_get_active(GncPeriodSelect *period)
Get the currently selected accounting period from a GncPeriodSelect widget.
void gnc_ppr_filter_start_toggle_cb(GtkToggleButton *button, RegisterFilterDialog *rfd)
This function is called when one of the check buttons for start relative or start date is changed...
void gnc_tm_get_today_start(struct tm *tm)
The gnc_tm_get_today_start() routine takes a pointer to a struct tm and fills it in with the first se...
Definition: gnc-date.cpp:1399
#define ENTER(format, args...)
Print a function entry debugging message.
Definition: qoflog.h:272
GKeyFile * gnc_state_get_current(void)
Returns a pointer to the most recently loaded state.
Definition: gnc-state.c:248
GDate * gnc_period_select_get_date(GncPeriodSelect *period)
Get the currently selected accounting period choice from a GncPeriodSelect widget.
Functions for adding content to a window.
void gnc_tm_free(struct tm *time)
free a struct tm* created with gnc_localtime() or gnc_gmtime()
Definition: gnc-date.cpp:97
void gnc_ppr_filter_status_clear_all_cb(GtkButton *button, RegisterFilterDialog *rfd)
This function is called whenever the "clear all" status button is clicked.
void gnc_ppr_filter_select_range_cb(GtkRadioButton *button, RegisterFilterDialog *rfd)
This function is called when the radio buttons changes state.
char * qof_print_date(time64 secs)
Convenience; calls through to qof_print_date_dmy_buff().
Definition: gnc-date.cpp:610
Functions providing a register page for the GnuCash UI.
time64 gnc_time64_get_day_start(time64 time_val)
The gnc_time64_get_day_start() routine will take the given time in seconds and adjust it to the first...
Definition: gnc-date.cpp:1366
void gnc_ppr_filter_set_tooltip(GncPluginPage *plugin_page, FilterData *fd)
This function is used to update the tooltip shown in the register which shows a summary of the curren...
gchar * gnc_list_formatter(GList *strings)
This function takes a GList of char*, and uses locale-sensitive list formatter.
time64 gnc_time64_get_today_start(void)
The gnc_time64_get_today_start() routine returns a time64 value corresponding to the first second of ...
Definition: gnc-date.cpp:1417
void gnc_ppr_filter_update_register(GncPluginPage *plugin_page)
This function is called to update the register.
GncAccountingPeriod
This specifies a time interval.
void gnc_ppr_filter_status_select_all_cb(GtkButton *button, RegisterFilterDialog *rfd)
This function is called whenever the "select all" status button is clicked.
time64 gnc_mktime(struct tm *time)
calculate seconds from the epoch given a time struct
Definition: gnc-date.cpp:219
void gnc_ppr_filter_by(GncPluginPage *plugin_page, Query *query, FilterData *fd, bool show_save_button)
This function is called for the filter dialog.
void gnc_ppr_filter_start_cb(GtkWidget *radio, RegisterFilterDialog *rfd)
This function is called when one of the start date radio buttons is selected.
void gnc_ppr_filter_start_end_days_changed_cb(GtkSpinButton *button, RegisterFilterDialog *rfd)
This function is called when the "days ago" spin button is changed which is then saved and updates th...
struct tm * gnc_localtime(const time64 *secs)
fill out a time struct from a 64-bit time value.
Definition: gnc-date.cpp:103
GLib helper routines.
Generic api to store and retrieve preferences.
Query * gnc_plugin_page_register_get_query(GncPluginPage *plugin_page)
This function is called to get the query associated with this plugin page.
GDate * gnc_accounting_period_end_gdate(GncAccountingPeriod which, const GDate *fy_end, const GDate *contains)
This function returns the ending date for an accounting period.
GDate * gnc_accounting_period_start_gdate(GncAccountingPeriod which, const GDate *fy_end, const GDate *contains)
This function returns the starting date for an accounting period.
time64 gnc_time64_get_day_end_gdate(const GDate *date)
The gnc_time64_get_day_end() routine will take the given time in GLib GDate format and adjust it to t...
Definition: gnc-date.cpp:1502
time64 gnc_time64_get_today_end(void)
The gnc_time64_get_today_end() routine returns a time64 value corresponding to the last second of tod...
Definition: gnc-date.cpp:1426
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition: qoflog.h:282
time64 gnc_time(time64 *tbuf)
get the current time
Definition: gnc-date.cpp:262
GtkWidget * gnc_period_select_new(gboolean starting_labels)
Create a new GncPeriodSelect widget which is used to select a accounting period like "previous month"...
gint64 time64
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
Definition: gnc-date.h:87
void gnc_plugin_page_register_query_update(GncPluginPageRegister *page, Query *query)
This updates the query after the filters have been applied.
void gnc_period_select_set_active(GncPeriodSelect *period, GncAccountingPeriod which)
Set which item in the GncPeriodSelect is initially selected.
void gnc_ppr_filter_end_toggle_cb(GtkToggleButton *button, RegisterFilterDialog *rfd)
This function is called when one of the check buttons for end relative or end date is changed...
time64 gnc_time64_get_day_end(time64 time_val)
The gnc_time64_get_day_end() routine will take the given time in seconds and adjust it to the last se...
Definition: gnc-date.cpp:1386
SplitRegister * gnc_ledger_display_get_split_register(GNCLedgerDisplay *ld)
return the split register associated with a ledger display
void gnc_tm_get_today_end(struct tm *tm)
The gnc_tm_get_today_end() routine takes a pointer to a struct tm and fills it in with the last secon...
Definition: gnc-date.cpp:1411
void gnc_plugin_page_register_update_for_search_query(GncPluginPageRegister *page)
This checks if the register is a search register and if so saves the query.
A custom widget for selecting accounting periods.
GNCSplitReg * gnc_plugin_page_register_get_gsr(GncPluginPage *plugin_page)
Get the GNCSplitReg data structure associated with this register page.
time64 gnc_time64_get_day_start_gdate(const GDate *date)
The gnc_time64_get_day_start() routine will take the given time in GLib GDate format and adjust it to...
Definition: gnc-date.cpp:1488
A Query.
Definition: qofquery.cpp:74
void gnc_ppr_filter_days_changed_cb(GtkSpinButton *button, RegisterFilterDialog *rfd)
This function is called when the "number of days" spin button is changed which is then saved and upda...
void gnc_ppr_filter_status_one_cb(GtkToggleButton *button, RegisterFilterDialog *rfd)
This function is called whenever one of the status entries is checked or unchecked.
void gnc_ppr_filter_clear_current_filter(GncPluginPage *plugin_page)
This function is used to clear the current filter so that a specific split can be shown in the regist...