GnuCash  5.6-150-g038405b370+
gnc-report-combo.c
1 /********************************************************************\
2  * gnc-report-combo.c -- report select widget for GnuCash *
3  * *
4  * Copyright (C) 2022 Bob 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 \********************************************************************/
24 
25 #include <config.h>
26 #include <gtk/gtk.h>
27 #include <glib/gi18n.h>
28 
29 #include "gnc-report-combo.h"
30 #include "gnc-ui-util.h"
31 #include "gnc-engine.h"
32 #include "dialog-utils.h"
33 
35 __attribute__((unused)) static QofLogModule log_module = GNC_MOD_GUI;
36 
37 static void gnc_report_combo_dispose (GObject *object);
38 static void gnc_report_combo_finalize (GObject *object);
39 
40 #define GNC_REPORT_COMBO_PATH "gnc-report-combo-path"
41 
42 enum
43 {
44  RC_NAME,
45  RC_GUID,
46  RC_MISSING
47 };
48 
50 {
51  GtkBox box;
52 
53  GtkWidget *combo;
54  GtkWidget *warning_image;
55 
56  const gchar *rpt_guids;
57 
58  gboolean block_signal;
59  gboolean popup_shown;
60 
61  gchar *active_report_guid;
62  gchar *active_report_name;
63 
64 };
65 
66 G_DEFINE_TYPE(GncReportCombo, gnc_report_combo, GTK_TYPE_BOX)
67 
68 enum
69 {
70  SIGNAL_0,
71  CHANGED,
72  LAST_SIGNAL
73 };
74 
75 static guint report_combo_signals [LAST_SIGNAL] = {0};
76 
77 enum
78 {
79  PROP_0,
80  PROP_POPUP_SHOWN,
81  N_PROPERTIES
82 };
83 
84 static GParamSpec *report_combo_properties [N_PROPERTIES] = {NULL,};
85 
86 static void
87 gnc_report_combo_get_property (GObject *object,
88  guint property_id,
89  GValue *value,
90  GParamSpec *pspec)
91 {
92  GncReportCombo *grc = GNC_REPORT_COMBO(object);
93 
94  switch (property_id)
95  {
96  case PROP_POPUP_SHOWN:
97  g_value_set_boolean (value, grc->popup_shown);
98  break;
99 
100  default:
101  /* We don't have any other property... */
102  G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
103  break;
104  }
105 }
106 
113 static void
114 gnc_report_combo_class_init (GncReportComboClass *klass)
115 {
116  GObjectClass *object_class = G_OBJECT_CLASS(klass);
117 
118  object_class->get_property = gnc_report_combo_get_property;
119  object_class->dispose = gnc_report_combo_dispose;
120  object_class->finalize = gnc_report_combo_finalize;
121 
122  report_combo_signals [CHANGED] =
123  g_signal_new ("changed",
124  G_OBJECT_CLASS_TYPE(object_class),
125  G_SIGNAL_RUN_FIRST,
126  0,
127  NULL,
128  NULL,
129  g_cclosure_marshal_VOID__VOID,
130  G_TYPE_NONE,
131  0);
132 
133  report_combo_properties [PROP_POPUP_SHOWN] =
134  g_param_spec_boolean ("popup-shown",
135  "State of PopUp",
136  "State of PopUp",
137  FALSE /* default value */,
138  G_PARAM_READABLE);
139 
140  g_object_class_install_properties (object_class,
141  N_PROPERTIES,
142  report_combo_properties);
143 }
144 
151 static void
152 gnc_report_combo_init (GncReportCombo *grc)
153 {
154  g_return_if_fail (grc != NULL);
155  g_return_if_fail (GNC_IS_REPORT_COMBO(grc));
156 
157  // Set the name for this widget so it can be easily manipulated with css
158  gtk_widget_set_name (GTK_WIDGET(grc), "gnc-id-report-combo");
159 
160  grc->block_signal = FALSE;
161  grc->active_report_guid = NULL;
162  grc->active_report_name = NULL;
163  grc->popup_shown = FALSE;
164 }
165 
176 static void
177 gnc_report_combo_dispose (GObject *object)
178 {
179  /* Do not free the private data structure itself. It is part of
180  * a larger memory block allocated by the type system. */
181 
182  G_OBJECT_CLASS (gnc_report_combo_parent_class)->dispose (object);
183 }
184 
195 static void
196 gnc_report_combo_finalize (GObject *object)
197 {
198  GncReportCombo *grc;
199 
200  g_return_if_fail (object != NULL);
201  g_return_if_fail (GNC_IS_REPORT_COMBO(object));
202 
203  grc = GNC_REPORT_COMBO(object);
204 
205  g_free (grc->active_report_guid);
206  g_free (grc->active_report_name);
207 
208  G_OBJECT_CLASS (gnc_report_combo_parent_class)->finalize (object);
209 }
210 
221 static gboolean
222 select_active_and_check_exists (GncReportCombo *grc)
223 {
224  GtkTreeModel *model = gtk_combo_box_get_model (GTK_COMBO_BOX(grc->combo));
225  GtkTreeIter iter;
226  gboolean valid_iter = gtk_tree_model_get_iter_first (model, &iter);
227  gchar *tmp;
228 
229  while (valid_iter)
230  {
231  gchar *guid;
232  gtk_tree_model_get (model, &iter, RC_GUID, &guid, -1);
233 
234  if (g_strcmp0 (grc->active_report_guid, guid) == 0)
235  {
236  gtk_combo_box_set_active_iter (GTK_COMBO_BOX(grc->combo), &iter);
237  g_free (guid);
238  return TRUE;
239  }
240  g_free (guid);
241  valid_iter = gtk_tree_model_iter_next (model, &iter);
242  }
243 
244  if (grc->active_report_name)
245  tmp = g_strdup (grc->active_report_name);
246  else
247  tmp = g_strdup (_("Selected Report is Missing"));
248 
249  gtk_list_store_prepend (GTK_LIST_STORE(model), &iter);
250  gtk_list_store_set (GTK_LIST_STORE(model), &iter,
251  RC_NAME, tmp,
252  RC_GUID, grc->active_report_guid,
253  RC_MISSING, TRUE,
254  -1);
255 
256  g_free (tmp);
257  gtk_combo_box_set_active_iter (GTK_COMBO_BOX(grc->combo), &iter);
258  return FALSE;
259 }
260 
261 static void
262 update_report_list (GncReportCombo *grc, GSList *report_list)
263 {
264  GtkTreeModel *model = gtk_combo_box_get_model (GTK_COMBO_BOX(grc->combo));
265 
266  gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(model),
267  RC_NAME, GTK_SORT_ASCENDING);
268 
269  gtk_list_store_clear (GTK_LIST_STORE(model));
270 
271  if (report_list)
272  {
273  GtkTreeIter iter;
274 
275  for (GSList* node = report_list; node != NULL; node = g_slist_next (node))
276  {
277  ReportListEntry *rle = node->data;
278 
279  gtk_list_store_append (GTK_LIST_STORE(model), &iter);
280  gtk_list_store_set (GTK_LIST_STORE(model), &iter,
281  RC_NAME, rle->report_name,
282  RC_GUID, rle->report_guid,
283  RC_MISSING, FALSE,
284  -1);
285  g_free (rle->report_name);
286  g_free (rle->report_guid);
287  g_free (rle);
288  }
289  }
290  g_slist_free (report_list);
291 }
292 
293 static void
294 update_warning_tooltip (GncReportCombo *grc)
295 {
296  gchar *tool_tip;
297 
298  if (grc->active_report_name)
299  /* Translators: %s is the report name. */
300  tool_tip = g_strdup_printf (_("'%s' is missing"),
301  grc->active_report_name);
302  else
303  /* Translators: %s is the internal report guid. */
304  tool_tip = g_strdup_printf (_("Report with GUID '%s' is missing"),
305  grc->active_report_guid);
306 
307  gtk_widget_show (grc->warning_image);
308  gtk_widget_set_tooltip_text (grc->warning_image, tool_tip);
309  g_free (tool_tip);
310 }
311 
312 void
313 gnc_report_combo_set_active (GncReportCombo *grc,
314  const char* active_report_guid,
315  const char* active_report_name)
316 {
317  g_return_if_fail (grc != NULL);
318  g_return_if_fail (GNC_IS_REPORT_COMBO(grc));
319 
320  g_free (grc->active_report_guid);
321 
322  grc->active_report_guid = g_strdup (active_report_guid);
323 
324  g_free (grc->active_report_name);
325 
326  grc->active_report_name = g_strdup (active_report_name);
327 
328  grc->block_signal = TRUE;
329 
330  if (!select_active_and_check_exists (grc))
331  update_warning_tooltip (grc);
332 
333  grc->block_signal = FALSE;
334 }
335 
336 gchar *
337 gnc_report_combo_get_active_guid (GncReportCombo *grc)
338 {
339  gchar *guid = NULL;
340  GtkTreeIter iter;
341 
342  g_return_val_if_fail (grc != NULL, NULL);
343  g_return_val_if_fail (GNC_IS_REPORT_COMBO(grc), NULL);
344 
345  if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX(grc->combo), &iter))
346  {
347  GtkTreeModel *model = gtk_combo_box_get_model (GTK_COMBO_BOX(grc->combo));
348  gtk_tree_model_get (model, &iter, RC_GUID, &guid, -1);
349  }
350  return guid;
351 }
352 
353 gchar *
354 gnc_report_combo_get_active_name (GncReportCombo *grc)
355 {
356  gchar *name = NULL;
357  GtkTreeIter iter;
358 
359  g_return_val_if_fail (grc != NULL, NULL);
360  g_return_val_if_fail (GNC_IS_REPORT_COMBO(grc), NULL);
361 
362  if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX(grc->combo), &iter))
363  {
364  GtkTreeModel *model = gtk_combo_box_get_model (GTK_COMBO_BOX(grc->combo));
365  gtk_tree_model_get (model, &iter, RC_NAME, &name, -1);
366  }
367  return name;
368 }
369 
370 gchar*
371 gnc_report_combo_get_active_guid_name (GncReportCombo *grc)
372 {
373  gchar *report = NULL;
374  GtkTreeIter iter;
375 
376  g_return_val_if_fail (grc != NULL, NULL);
377  g_return_val_if_fail (GNC_IS_REPORT_COMBO(grc), NULL);
378 
379  if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX(grc->combo), &iter))
380  {
381  GtkTreeModel *model = gtk_combo_box_get_model (GTK_COMBO_BOX(grc->combo));
382  gchar *report_guid;
383  gchar *report_name;
384  gtk_tree_model_get (model, &iter, RC_NAME, &report_name,
385  RC_GUID, &report_guid,
386  -1);
387 
388  report = g_strconcat (report_guid, "/", report_name, NULL);
389  g_free (report_guid);
390  g_free (report_name);
391  }
392  return report;
393 }
394 
395 void
396 gnc_report_combo_set_active_guid_name (GncReportCombo *grc,
397  const gchar *guid_name)
398 {
399  g_return_if_fail (grc != NULL);
400  g_return_if_fail (GNC_IS_REPORT_COMBO(grc));
401 
402  if (guid_name && *guid_name)
403  {
404  gchar *guid = NULL;
405  gchar *name = g_strstr_len (guid_name, -1, "/");
406 
407  if (name)
408  {
409  guid = g_strndup (guid_name, (name - guid_name));
410  gnc_report_combo_set_active (grc, guid, name + 1);
411  }
412  g_free (guid);
413  }
414 }
415 
416 gboolean
417 gnc_report_combo_is_warning_visible_for_active (GncReportCombo *grc)
418 {
419  g_return_val_if_fail (grc != NULL, FALSE);
420  g_return_val_if_fail (GNC_IS_REPORT_COMBO(grc), FALSE);
421 
422  return gtk_widget_is_visible (GTK_WIDGET(grc->warning_image));
423 }
424 
425 static void
426 combo_changed_cb (GtkComboBox *widget, gpointer user_data)
427 {
428  GncReportCombo *grc = GNC_REPORT_COMBO(user_data);
429  GtkTreeIter iter;
430 
431  if (gtk_combo_box_get_active_iter (widget, &iter))
432  {
433  GtkTreeModel *model = gtk_combo_box_get_model (widget);
434  gboolean missing;
435  gtk_tree_model_get (model, &iter, RC_MISSING, &missing, -1);
436  // set visibility of the warning image
437  gtk_widget_set_visible (grc->warning_image, missing);
438 
439  if (!grc->block_signal)
440  g_signal_emit (grc, report_combo_signals [CHANGED], 0);
441 
442  gtk_widget_queue_resize (GTK_WIDGET(widget));
443  }
444 }
445 
446 static void
447 combo_popped_cb (GObject *gobject,
448  GParamSpec *pspec,
449  gpointer user_data)
450 {
451  GncReportCombo *grc = GNC_REPORT_COMBO(user_data);
452  gboolean popup_shown;
453 
454  g_object_get (G_OBJECT(gobject), "popup-shown", &popup_shown, NULL);
455 
456  grc->popup_shown = popup_shown;
457  g_object_notify (G_OBJECT(grc), "popup-shown");
458 }
459 
460 void
461 gnc_report_combo_refresh (GncReportCombo *grc, GSList *report_list)
462 {
463  g_return_if_fail (grc != NULL);
464  g_return_if_fail (GNC_IS_REPORT_COMBO(grc));
465  g_return_if_fail (report_list != NULL);
466 
467  grc->block_signal = TRUE;
468 
469  update_report_list (grc, report_list);
470 
471  if (!select_active_and_check_exists (grc))
472  update_warning_tooltip (grc);
473 
474  grc->block_signal = FALSE;
475 }
476 
477 /* Create a new GncReportCombo widget which can be used to select
478  * a report from a GtkComboBox.
479  *
480  * @return A GncReportCombo widget.
481  */
482 GtkWidget *
483 gnc_report_combo_new (GSList *report_list)
484 {
485  GncReportCombo *grc;
486  GtkListStore *store;
487  GtkCellRenderer *renderer;
488 
489  store = gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN);
490  grc = g_object_new (GNC_TYPE_REPORT_COMBO, NULL);
491 
492  grc->combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL(store));
493  g_object_unref (store);
494 
495  renderer = gtk_cell_renderer_text_new ();
496  gtk_cell_layout_pack_start (GTK_CELL_LAYOUT(grc->combo), renderer, TRUE);
497  gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT(grc->combo), renderer,
498  "text", RC_NAME, NULL);
499 
500  gtk_box_pack_start (GTK_BOX(grc), GTK_WIDGET(grc->combo), TRUE, TRUE, 0);
501  grc->warning_image = gtk_image_new_from_icon_name ("dialog-warning",
502  GTK_ICON_SIZE_SMALL_TOOLBAR);
503  gtk_box_pack_start (GTK_BOX(grc), GTK_WIDGET(grc->warning_image), FALSE, FALSE, 6);
504  gtk_widget_set_no_show_all (GTK_WIDGET(grc->warning_image), TRUE);
505  gtk_widget_hide (GTK_WIDGET(grc->warning_image));
506 
507  update_report_list (grc, report_list);
508 
509  g_signal_connect (G_OBJECT(grc->combo), "changed",
510  G_CALLBACK(combo_changed_cb), grc);
511 
512  g_signal_connect (G_OBJECT(grc->combo), "notify::popup-shown",
513  G_CALLBACK(combo_popped_cb), grc);
514 
515  gtk_widget_show_all (GTK_WIDGET(grc));
516 
517  return GTK_WIDGET(grc);
518 }
utility functions for the GnuCash UI
All type declarations for the whole Gnucash engine.