GnuCash c935c2f+
Loading...
Searching...
No Matches
gnc-plugin-page-register-sort.cpp
1/**********************************************************************
2 * gnc-plugin-page-register-sort.c -- register page sort *
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
39#include "gnc-plugin-page-register-sort.hpp"
40#include "dialog-utils.h"
41#include "gnc-state.h"
42#include "gnc-prefs.h"
43#include "gnc-ui-util.h"
44#include "gnc-window.h"
45#include "gnc-main-window.h"
46#include "engine-helpers.h"
47#include "qofbookslots.h"
48#include "qof.h"
49
50#include <algorithm>
51#include <string>
52
53/* This static indicates the debugging module that this .o belongs to. */
54static QofLogModule log_module = GNC_MOD_GUI;
55
56static std::string DEFAULT_SORT_ORDER = "BY_STANDARD";
57
59{
60 GncPluginPage* plugin_page;
61 SplitRegister* reg;
62 GtkWidget* dialog;
63 GtkWidget* num_radio;
64 GtkWidget* act_radio;
65
66 SortType original_sort_type;
67 bool original_reverse_order;
68 bool original_save_order;
69
70 bool show_save_button;
71};
72
73extern "C"
74{
75// These functions are the dialog callbacks. They're connected to their
76// signals in gnc-plugin-page-register.glade so they mustn't be name-mangled.
77void
78gnc_ppr_sort_response_cb (GtkDialog* dialog,
79 gint response,
81
82void
83gnc_ppr_sort_button_cb (GtkToggleButton* button,
85
86void
87gnc_ppr_sort_order_save_cb (GtkToggleButton* button,
89
90void
91gnc_ppr_sort_order_reverse_cb (GtkToggleButton* button,
93}
94
95static inline bool
96gboolean_to_bool (gboolean value)
97{
98 return value ? true : false;
99}
100
101static inline gboolean
102bool_to_gboolean (bool value)
103{
104 return value ? TRUE : FALSE;
105}
106
107static void
108gnc_ppr_check_for_empty_group (GKeyFile *state_file,
109 const gchar *state_section)
110{
111 gsize num_keys;
112 gchar **keys = g_key_file_get_keys (state_file, state_section, &num_keys, nullptr);
113
114 if (num_keys == 0)
115 gnc_state_drop_sections_for (state_section);
116
117 g_strfreev (keys);
118}
119
120static std::string
121gnc_ppr_sort_get_order (GNCSplitReg *gsr)
122 {
123 if (!gsr)
124 return _("unknown");
125
126 // get the sort_order from the .gcm file
127 GKeyFile* state_file = gnc_state_get_current();
128 auto state_section = gsr_get_register_state_section (gsr);
129 GError* error = nullptr;
130
131 auto sort_text = g_key_file_get_string (state_file, state_section,
132 KEY_PAGE_SORT, &error);
133 std::string sort_order;
134
135 if (error)
136 g_clear_error (&error);
137 else
138 {
139 sort_order = (sort_text);
140 g_free (sort_text);
141 }
142 g_free (state_section);
143
144 return !sort_order.empty() ? sort_order : (DEFAULT_SORT_ORDER);
145}
146
147static void
148gnc_ppr_sort_set_order (GNCSplitReg *gsr, std::string sort_order)
149{
150 if (!gsr)
151 return;
152
153 // save sort_order to the .gcm file also
154 GKeyFile* state_file = gnc_state_get_current();
155 auto state_section = gsr_get_register_state_section (gsr);
156
157 if (sort_order.compare (DEFAULT_SORT_ORDER) == 0)
158 {
159 if (g_key_file_has_key (state_file, state_section, KEY_PAGE_SORT, nullptr))
160 g_key_file_remove_key (state_file, state_section, KEY_PAGE_SORT, nullptr);
161
162 gnc_ppr_check_for_empty_group (state_file, state_section);
163 }
164 else
165 g_key_file_set_string (state_file, state_section, KEY_PAGE_SORT, sort_order.c_str());
166
167 g_free (state_section);
168}
169
170static bool
171gnc_ppr_sort_get_reversed (GNCSplitReg *gsr)
172{
173 if (!gsr)
174 return false;
175
176 // get the sort_reversed from the .gcm file
177 GKeyFile* state_file = gnc_state_get_current();
178 auto state_section = gsr_get_register_state_section (gsr);
179 GError* error = nullptr;
180 gboolean sort_reversed = g_key_file_get_boolean (state_file, state_section,
181 KEY_PAGE_SORT_REV, &error);
182
183 if (error)
184 g_clear_error (&error);
185
186 g_free (state_section);
187 return gboolean_to_bool (sort_reversed);
188}
189
190static void
191gnc_ppr_sort_set_reversed (GNCSplitReg* gsr, bool reverse_order)
192{
193 if (!gsr)
194 return;
195
196 // save reverse_order to the .gcm file also
197 GKeyFile* state_file = gnc_state_get_current();
198 auto state_section = gsr_get_register_state_section (gsr);
199
200 if (!reverse_order)
201 {
202 if (g_key_file_has_key (state_file, state_section, KEY_PAGE_SORT_REV, nullptr))
203 g_key_file_remove_key (state_file, state_section, KEY_PAGE_SORT_REV, nullptr);
204
205 gnc_ppr_check_for_empty_group (state_file, state_section);
206 }
207 else
208 g_key_file_set_boolean (state_file, state_section, KEY_PAGE_SORT_REV,
209 bool_to_gboolean (reverse_order));
210
211 g_free (state_section);
212}
213
219void
221{
222 g_return_if_fail (GNC_IS_PLUGIN_PAGE_REGISTER(plugin_page));
223
224 auto sd = gnc_plugin_page_register_get_sort_data (plugin_page);
225 auto gsr = gnc_plugin_page_register_get_gsr (plugin_page);
226 GNCLedgerDisplayType ledger_type = gnc_ledger_display_type (gsr->ledger);
227
228 sd->save_order = false;
229
230 // Set the sort direction for the split register and status of save order button
231 sd->reverse_order = gnc_ppr_sort_get_reversed (gsr);
232
233 PINFO("Loaded Sort reversed order is %s", sd->reverse_order ? "true" : "false");
234
235 gnc_split_reg_set_sort_reversed (gsr, sd->reverse_order, no_refresh);
236 if (sd->reverse_order)
237 sd->save_order = true;
238
239 // Set the sort order for the split register and status of save order button
240 std::string sort_type = gnc_ppr_sort_get_order (gsr);
241
242 PINFO("Loaded Sort type is %s", sort_type.c_str());
243
244 SortType type = SortTypefromString (sort_type.c_str());
245
246 gnc_split_reg_sort (gsr, type, no_force, no_refresh);
247
248 if (sort_type.compare (DEFAULT_SORT_ORDER) != 0)
249 sd->save_order = true;
250
251 if (ledger_type == LD_GL)
252 {
253 auto reg = gnc_ledger_display_get_split_register (gsr->ledger);
254
255 if (reg->type != GENERAL_JOURNAL) // search ledger and the like
256 {
257 gnc_split_reg_sort (gsr, SortTypefromString (DEFAULT_SORT_ORDER.c_str()), no_force, no_refresh);
258 sd->reverse_order = false;
259 sd->save_order = false;
260 }
261 }
262}
263
273static void
274gnc_ppr_sort_book_option_changed (gpointer new_val,
275 gpointer user_data)
276{
277 RegisterSortDialog *rsd = (RegisterSortDialog*)user_data;
278 gboolean* new_data = (gboolean*)new_val;
279
280 g_return_if_fail (GNC_IS_PLUGIN_PAGE_REGISTER(rsd->plugin_page));
281
282 if (*new_data)
283 {
284 gtk_button_set_label (GTK_BUTTON(rsd->num_radio), _("Transaction Number"));
285 gtk_button_set_label (GTK_BUTTON(rsd->act_radio), _("Number/Action"));
286 }
287 else
288 {
289 gtk_button_set_label (GTK_BUTTON(rsd->num_radio), _("Number"));
290 gtk_button_set_label (GTK_BUTTON(rsd->act_radio), _("Action"));
291 }
292
293 auto gsr = gnc_plugin_page_register_get_gsr (rsd->plugin_page);
294
295 gnc_split_reg_sort (gsr, (SortType)gsr->sort_type, force, refresh);
296}
297
308void
309gnc_ppr_sort_response_cb (GtkDialog* dialog,
310 gint response,
312{
313 g_return_if_fail (GTK_IS_DIALOG(dialog));
314 g_return_if_fail (GNC_IS_PLUGIN_PAGE_REGISTER(rsd->plugin_page));
315
316 ENTER(" ");
317
318 auto sd = gnc_plugin_page_register_get_sort_data (rsd->plugin_page);
319 auto gsr = gnc_plugin_page_register_get_gsr (rsd->plugin_page);
320
321 if (response != GTK_RESPONSE_OK)
322 {
323 // Restore the original sort order
324 gnc_split_reg_set_sort_reversed (gsr, rsd->original_reverse_order, no_refresh);
325 sd->reverse_order = rsd->original_reverse_order;
326 // use force as sort_type may still be the same if only reverse_order changed
327 gnc_split_reg_sort (gsr, rsd->original_sort_type, force, refresh);
328 sd->sort_type = rsd->original_sort_type;
329 sd->save_order = rsd->original_save_order;
330 }
331 else
332 {
333 // clear the sort when unticking the save option
334 if ((!sd->save_order) && ((rsd->original_sort_type) ||
335 (rsd->original_reverse_order)))
336 {
337 gnc_ppr_sort_set_order (gsr, DEFAULT_SORT_ORDER);
338 gnc_ppr_sort_set_reversed (gsr, false);
339 }
340 rsd->original_sort_type = sd->sort_type;
341 rsd->original_reverse_order = sd->reverse_order;
342
343 if (sd->save_order)
344 {
345 SortType type = gnc_split_reg_get_sort_type (gsr);
346 std::string sort_type = (SortTypeasString (type));
347
348 gnc_ppr_sort_set_order (gsr, sort_type);
349 gnc_ppr_sort_set_reversed (gsr, sd->reverse_order);
350 }
351 }
352 gnc_book_option_remove_cb (OPTION_NAME_NUM_FIELD_SOURCE,
353 gnc_ppr_sort_book_option_changed,
354 (gpointer)rsd);
355 rsd->dialog = nullptr;
356 rsd->num_radio = nullptr;
357 rsd->act_radio = nullptr;
358 sd->dialog = nullptr;
359 g_free (rsd);
360 gtk_widget_destroy (GTK_WIDGET(dialog));
361 LEAVE (" ");
362}
363
371void
372gnc_ppr_sort_button_cb (GtkToggleButton* button,
374{
375 g_return_if_fail (GTK_IS_TOGGLE_BUTTON(button));
376 g_return_if_fail (GNC_IS_PLUGIN_PAGE_REGISTER(rsd->plugin_page));
377
378 auto name = gtk_buildable_get_name (GTK_BUILDABLE(button));
379
380 ENTER("button %s(%p), page %p", name, button, rsd->plugin_page);
381
382 if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(button)))
383 {
384 LEAVE("1st callback of pair. Defer to 2nd callback.");
385 return;
386 }
387
388 auto gsr = gnc_plugin_page_register_get_gsr (rsd->plugin_page);
389
390 SortType type = SortTypefromString (name);
391 gnc_split_reg_sort (gsr, type, no_force, refresh);
392 LEAVE (" ");
393}
394
402void
403gnc_ppr_sort_order_save_cb (GtkToggleButton* button,
405{
406 g_return_if_fail (GTK_IS_CHECK_BUTTON(button));
407 g_return_if_fail (GNC_IS_PLUGIN_PAGE_REGISTER(rsd->plugin_page));
408
409 ENTER("Save toggle button (%p), page %p", button, rsd->plugin_page);
410
411 /* Compute the new save sort order */
412 auto sd = gnc_plugin_page_register_get_sort_data (rsd->plugin_page);
413
414 if (gtk_toggle_button_get_active (button))
415 sd->save_order = true;
416 else
417 sd->save_order = false;
418 LEAVE (" ");
419}
420
428void
429gnc_ppr_sort_order_reverse_cb (GtkToggleButton* button,
431{
432 g_return_if_fail (GTK_IS_CHECK_BUTTON(button));
433 g_return_if_fail (GNC_IS_PLUGIN_PAGE_REGISTER(rsd->plugin_page));
434
435 ENTER("Reverse toggle button (%p), page %p", button, rsd->plugin_page);
436
437 /* Compute the new save sort order */
438 auto sd = gnc_plugin_page_register_get_sort_data (rsd->plugin_page);
439 auto gsr = gnc_plugin_page_register_get_gsr (rsd->plugin_page);
440
441 sd->reverse_order = gtk_toggle_button_get_active (button);
442 gnc_split_reg_set_sort_reversed (gsr, sd->reverse_order, refresh);
443 LEAVE (" ");
444}
445
452static void
453gnc_ppr_sort_dialog_create (RegisterSortDialog *rsd, SortData *sd)
454{
455 /* Create the dialog */
456 auto builder = gtk_builder_new();
457 gnc_builder_add_from_file (builder, "gnc-plugin-page-register.glade", "sort_by_dialog");
458 auto dialog = GTK_WIDGET(gtk_builder_get_object (builder, "sort_by_dialog"));
459 rsd->dialog = dialog;
460 sd->dialog = rsd->dialog;
461
462 gtk_window_set_transient_for (GTK_WINDOW(dialog),
463 gnc_window_get_gtk_window (GNC_WINDOW(
464 GNC_PLUGIN_PAGE(rsd->plugin_page)->window)));
465 /* Translators: The %s is the name of the plugin page */
466 auto title = g_strdup_printf (_("Sort %s by…"),
467 gnc_plugin_page_get_page_name (rsd->plugin_page));
468 gtk_window_set_title (GTK_WINDOW(dialog), title);
469 g_free (title);
470
471 auto gsr = gnc_plugin_page_register_get_gsr (rsd->plugin_page);
472
473 /* Set the button for the current sort order */
474 SortType sort = gnc_split_reg_get_sort_type (gsr);
475 auto name = SortTypeasString (sort);
476 auto button = GTK_WIDGET(gtk_builder_get_object (builder, name));
477 DEBUG("current sort %d, button %s(%p)", sort, name, button);
478 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(button), TRUE);
479 rsd->original_sort_type = sort;
480
481 button = GTK_WIDGET(gtk_builder_get_object (builder, "sort_save"));
482 if (sd->save_order)
483 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(button), TRUE);
484
485 rsd->original_save_order = sd->save_order;
486
487 // hide the save button if appropriate
488 gtk_widget_set_visible (GTK_WIDGET(button), bool_to_gboolean (rsd->show_save_button));
489
490 /* Set the button for the current reverse_order order */
491 button = GTK_WIDGET(gtk_builder_get_object (builder, "sort_reverse"));
492 if (sd->reverse_order)
493 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(button), TRUE);
494 rsd->original_reverse_order = sd->reverse_order;
495
496 rsd->num_radio = GTK_WIDGET(gtk_builder_get_object (builder, "BY_NUM"));
497 rsd->act_radio = GTK_WIDGET(gtk_builder_get_object (builder, "BY_ACTION"));
498 /* Adjust labels related to Num/Action radio buttons based on book option */
499 if (rsd->reg && !rsd->reg->use_tran_num_for_num_field)
500 {
501 gtk_button_set_label (GTK_BUTTON(rsd->num_radio), _ ("Transaction Number"));
502 gtk_button_set_label (GTK_BUTTON(rsd->act_radio), _ ("Number/Action"));
503 }
504 gnc_book_option_register_cb (OPTION_NAME_NUM_FIELD_SOURCE,
505 (GncBOCb)gnc_ppr_sort_book_option_changed,
506 (gpointer)rsd);
507
508 /* Wire it up */
509 gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func,
510 rsd);
511
512 /* Show it */
513 gtk_widget_show (dialog);
514 g_object_unref (G_OBJECT(builder));
515 LEAVE (" ");
516}
517
529void
530gnc_ppr_sort_dialog (GncPluginPage *plugin_page, SplitRegister* reg,
531 SortData *sd, bool show_save_button)
532{
534
535 ENTER(" ");
536
537 rsd = g_new0 (RegisterSortDialog, 1);
538
539 rsd->plugin_page = plugin_page;
540 rsd->reg = reg;
541 rsd->show_save_button = show_save_button;
542
543 gnc_ppr_sort_dialog_create (rsd, sd);
544
545 LEAVE(" ");
546}
Functions for adding content to a window.
Functions providing a register page for the GnuCash UI.
Generic api to store and retrieve preferences.
Functions to load, save and get gui state.
utility functions for the GnuCash UI
Functions that are supported by all types of windows.
const gchar * gnc_plugin_page_get_page_name(GncPluginPage *page)
Retrieve the name of this 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
GKeyFile * gnc_state_get_current(void)
Returns a pointer to the most recently loaded state.
Definition gnc-state.c:248
SplitRegister * gnc_ledger_display_get_split_register(GNCLedgerDisplay *ld)
return the split register associated with a ledger display
#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 ENTER(format, args...)
Print a function entry debugging message.
Definition qoflog.h:272
SortData * gnc_plugin_page_register_get_sort_data(GncPluginPage *plugin_page)
Get the SortData data structure associated with this register page.
void gnc_ppr_sort_order_reverse_cb(GtkToggleButton *button, RegisterSortDialog *rsd)
This function is called whenever the reverse sort order is checked or unchecked which allows reversin...
void gnc_ppr_sort_response_cb(GtkDialog *dialog, gint response, RegisterSortDialog *rsd)
This function is called when the "Sort By…" dialog is closed.
void gnc_ppr_sort_dialog(GncPluginPage *plugin_page, SplitRegister *reg, SortData *sd, bool show_save_button)
This function is called for the sort dialog.
void gnc_ppr_sort_button_cb(GtkToggleButton *button, RegisterSortDialog *rsd)
This function is called when a radio button in the "Sort By…" dialog is clicked.
void gnc_ppr_sort_order_save_cb(GtkToggleButton *button, RegisterSortDialog *rsd)
This function is called whenever the save sort order is checked or unchecked which allows saving of t...
void gnc_ppr_sort_update_register(GncPluginPage *plugin_page)
This function is called to update the register.
GNCSplitReg * gnc_plugin_page_register_get_gsr(GncPluginPage *plugin_page)
Get the GNCSplitReg data structure associated with this register page.
The instance data structure for a content plugin.