GnuCash  5.6-150-g038405b370+
gnc-period-select.c
Go to the documentation of this file.
1 /*
2  * gnc-period-select.c -- Accounting Period selection widget
3  *
4  * Copyright (c) 2005 David Hampton <hampton@employees.org>
5  * All rights reserved.
6  *
7  * Gnucash is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public License
9  * as published by the Free Software Foundation; either version 2 of the
10  * License, or (at your option) any later version.
11  *
12  * Gnucash is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, contact:
19  *
20  * Free Software Foundation Voice: +1-617-542-5942
21  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652
22  * Boston, MA 02110-1301, USA gnu@gnu.org
23  */
24 
32 #include <config.h>
33 
34 #include <gtk/gtk.h>
35 #include <glib/gi18n.h>
36 
37 #include "gnc-date.h"
38 #include "gnc-period-select.h"
39 #include "gnc-prefs.h"
40 #include "dialog-utils.h"
41 
42 enum
43 {
44  PROP_0,
45  PROP_FY_END,
46  PROP_SHOW_DATE,
47  PROP_DATE_BASE,
48  PROP_PS_ACTIVE,
49 };
50 
51 enum
52 {
53  CHANGED,
54  LAST_SIGNAL
55 };
56 
57 static guint signals[LAST_SIGNAL] = { 0 };
58 
60 static void gnc_period_select_finalize (GObject *object);
61 
62 const gchar *start_strings[GNC_ACCOUNTING_PERIOD_LAST] =
63 {
64  /* CY (current year) Strings */
65  N_("Today"),
66  N_("Start of this month"),
67  N_("Start of previous month"),
68  N_("Start of this quarter"),
69  N_("Start of previous quarter"),
70  N_("Start of this year"),
71  N_("Start of previous year"),
72 
73  /* FY (fiscal year) Strings */
74  N_("Start of this accounting period"),
75  N_("Start of previous accounting period"),
76 };
77 
78 const gchar *end_strings[GNC_ACCOUNTING_PERIOD_LAST] =
79 {
80  /* CY (current year) Strings */
81  N_("Today"),
82  N_("End of this month"),
83  N_("End of previous month"),
84  N_("End of this quarter"),
85  N_("End of previous quarter"),
86  N_("End of this year"),
87  N_("End of previous year"),
88 
89  /* FY (fiscal year) Strings */
90  N_("End of this accounting period"),
91  N_("End of previous accounting period"),
92 };
93 
94 
98 {
99  GtkBox hbox;
100 
101  GtkWidget *selector;
102 
103  gboolean start;
104  GDate *fy_end;
105 
106  GDate *date_base;
107  GtkWidget *date_label;
108 };
109 
110 G_DEFINE_TYPE(GncPeriodSelect, gnc_period_select, GTK_TYPE_BOX)
111 
112 
113 /************************************************************/
114 /* Signal Functions */
115 /************************************************************/
116 
117 /* Tells a GncPeriodSelect object to emit a "changed" signal.
118  */
119 static void
120 gnc_period_select_changed (GncPeriodSelect *period)
121 {
122  g_return_if_fail(GNC_IS_PERIOD_SELECT(period));
123 
124  g_signal_emit(G_OBJECT(period), signals[CHANGED], 0);
125 }
126 
127 
133 static void
134 gnc_period_sample_update_date_label (GncPeriodSelect *period)
135 {
136  gchar time_string[MAX_DATE_LENGTH + 1];
137  GDate *date;
138  GncAccountingPeriod which;
139 
140  g_return_if_fail(GNC_IS_PERIOD_SELECT(period));
141  if (!period->date_label)
142  return;
143  which = gtk_combo_box_get_active (GTK_COMBO_BOX (period->selector));
144  if (which == -1)
145  date = g_date_new_dmy (31, 7, 2013);
146 
147  else if (period->start)
148  date = gnc_accounting_period_start_gdate (which, period->fy_end,
149  period->date_base);
150  else
151  date = gnc_accounting_period_end_gdate (which, period->fy_end,
152  period->date_base);
153  qof_print_gdate (time_string, MAX_DATE_LENGTH, date);
154  gtk_label_set_label (GTK_LABEL(period->date_label), time_string);
155  g_date_free (date);
156 }
157 
158 
168 static void
169 gnc_period_sample_combobox_changed (GtkComboBox *box, GncPeriodSelect *period)
170 {
171  g_return_if_fail(GNC_IS_PERIOD_SELECT(period));
172 
173  g_object_set (G_OBJECT (period),
174  "active",
175  gtk_combo_box_get_active (box),
176  NULL);
177 }
178 
179 
192 static void
193 gnc_period_sample_new_date_format (gpointer prefs, gchar *pref,
194  GncPeriodSelect *period)
195 {
196  gnc_period_sample_update_date_label(period);
197 }
198 
199 
200 /************************************************************/
201 /* Property Functions */
202 /************************************************************/
203 
204 /* Set an item in the GncPeriodSelect to be the active one.
205  * This will first update the internal GtkCombobox (blocking
206  * its "changed" callback to prevent an infinite loop).
207  * Then it will update the sample label and finally it will
208  * emit a "changed" signal of it's own for other objects
209  * listening for this signal.
210  */
211 static void
212 gnc_period_select_set_active_internal (GncPeriodSelect *period,
213  GncAccountingPeriod which)
214 {
215  g_return_if_fail(period != NULL);
216  g_return_if_fail(GNC_IS_PERIOD_SELECT(period));
217  g_return_if_fail(which >= 0);
218  g_return_if_fail(which < GNC_ACCOUNTING_PERIOD_LAST);
219 
220  g_signal_handlers_block_by_func(G_OBJECT(period),
221  G_CALLBACK(gnc_period_sample_combobox_changed), period);
222  gtk_combo_box_set_active(GTK_COMBO_BOX(period->selector), which);
223  g_signal_handlers_unblock_by_func(G_OBJECT(period),
224  G_CALLBACK(gnc_period_sample_combobox_changed), period);
225 
226  /* Update this widget */
227  gnc_period_sample_update_date_label(period);
228 
229  /* Pass it on... */
230  gnc_period_select_changed(period);
231 }
232 
233 
237 /* Get the current value of the fiscal year end setting from a
238  * GncPeriodSelect widget. If the result is NULL then fiscal years
239  * are not currently supported.
240  */
241 GDate *
242 gnc_period_select_get_fy_end (GncPeriodSelect *period)
243 {
244  g_return_val_if_fail(period != NULL, NULL);
245  g_return_val_if_fail(GNC_IS_PERIOD_SELECT(period), NULL);
246 
247  if (!period->fy_end)
248  return NULL;
249  return g_date_new_dmy(g_date_get_day(period->fy_end),
250  g_date_get_month(period->fy_end),
251  G_DATE_BAD_YEAR);
252 }
253 
254 
255 /* Set the fiscal year end on a GncPeriodSelect widget. If set to a
256  * value other than NULL then widget will include fiscal accounting
257  * period like "this fiscal year".
258  */
259 void
260 gnc_period_select_set_fy_end (GncPeriodSelect *period, const GDate *fy_end)
261 {
262  const gchar *label;
263  gint i;
264 
265  g_return_if_fail(period != NULL);
266  g_return_if_fail(GNC_IS_PERIOD_SELECT(period));
267 
268  if (period->fy_end)
269  g_date_free(period->fy_end);
270 
271  if (fy_end)
272  {
273  period->fy_end = g_date_new_dmy(g_date_get_day(fy_end),
274  g_date_get_month(fy_end),
275  G_DATE_BAD_YEAR);
276  }
277  else
278  {
279  period->fy_end = NULL;
280  }
281 
282  if (fy_end)
283  {
284  for (i = GNC_ACCOUNTING_PERIOD_CYEAR_LAST; i < GNC_ACCOUNTING_PERIOD_FYEAR_LAST; i++)
285  {
286  label = period->start ? _(start_strings[i]) : _(end_strings[i]);
287  gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(period->selector), label);
288  }
289  }
290  else
291  {
292  for (i = GNC_ACCOUNTING_PERIOD_FYEAR_LAST - 1; i >= GNC_ACCOUNTING_PERIOD_FYEAR_LAST; i--)
293  {
294  gtk_combo_box_text_remove(GTK_COMBO_BOX_TEXT(period->selector), i);
295  }
296  }
297 }
298 
299 
300 static void
301 gnc_period_select_set_date_common (GncPeriodSelect *period, const GDate *date)
302 {
303  if (date)
304  {
305  if (period->date_base)
306  g_date_free(period->date_base);
307  period->date_base = g_date_new_dmy(g_date_get_day(date),
308  g_date_get_month(date),
309  g_date_get_year(date));
310  if (period->date_label == NULL)
311  {
312  period->date_label = gtk_label_new("");
313  gtk_widget_set_margin_start (GTK_WIDGET(period->date_label), 6);
314  gtk_box_pack_start(GTK_BOX(period), period->date_label, TRUE, TRUE, 0);
315  gtk_widget_show_all(period->date_label);
316  }
317  gnc_period_sample_update_date_label(period);
318  return;
319  }
320 
321  if (period->date_base)
322  {
323  g_date_free(period->date_base);
324  period->date_base = NULL;
325  gtk_widget_destroy(period->date_label);
326  period->date_label = NULL;
327  }
328 }
329 
330 
331 /* Get the current value of the "show date" setting from a
332  * GncPeriodSelect widget.
333  */
334 gboolean
335 gnc_period_select_get_show_date (GncPeriodSelect *period)
336 {
337  g_return_val_if_fail(period != NULL, FALSE);
338  g_return_val_if_fail(GNC_IS_PERIOD_SELECT(period), FALSE);
339 
340  return (period->date_base != NULL);
341 }
342 
343 /* Set the "show date" setting on a GncPeriodSelect widget. If set
344  * to TRUE then a GtkLabel will be used to show the date
345  * corresponding to the selected time period.
346  */
347 void
348 gnc_period_select_set_show_date (GncPeriodSelect *period, const gboolean show_date)
349 {
350  GDate date;
351 
352  g_return_if_fail(period != NULL);
353  g_return_if_fail(GNC_IS_PERIOD_SELECT(period));
354 
355  if (show_date)
356  {
357  g_date_clear(&date, 1);
358  gnc_gdate_set_time64(&date, gnc_time (NULL));
359  gnc_period_select_set_date_common(period, &date);
360  }
361  else
362  {
363  gnc_period_select_set_date_common(period, NULL);
364  }
365 }
366 
367 
368 GDate *
369 gnc_period_select_get_date_base (GncPeriodSelect *period)
370 {
371  g_return_val_if_fail(period != NULL, NULL);
372  g_return_val_if_fail(GNC_IS_PERIOD_SELECT(period), NULL);
373 
374  if (!period->date_base)
375  return NULL;
376  return g_date_new_dmy(g_date_get_day(period->date_base),
377  g_date_get_month(period->date_base),
378  g_date_get_year(period->date_base));
379 }
380 
381 
382 /* Set the base date used by a GncPeriodSelect widget. All example
383  * dates presented by the widget will be computed from this date.
384  */
385 void
386 gnc_period_select_set_date_base (GncPeriodSelect *period, const GDate *date_base)
387 {
388  g_return_if_fail(period != NULL);
389  g_return_if_fail(GNC_IS_PERIOD_SELECT(period));
390 
391  gnc_period_select_set_date_common(period, date_base);
392 }
393 
394 
402 static void
403 gnc_period_select_get_property (GObject *object,
404  guint prop_id,
405  GValue *value,
406  GParamSpec *pspec)
407 {
408  GncPeriodSelect *period = GNC_PERIOD_SELECT(object);
409 
410  switch (prop_id)
411  {
412  case PROP_FY_END:
413  g_value_set_pointer(value, gnc_period_select_get_fy_end(period));
414  break;
415  case PROP_SHOW_DATE:
416  g_value_set_boolean(value, gnc_period_select_get_show_date(period));
417  break;
418  case PROP_DATE_BASE:
419  g_value_set_pointer(value, gnc_period_select_get_date_base(period));
420  break;
421  case PROP_PS_ACTIVE:
422  g_value_set_int(value, gnc_period_select_get_active(period));
423  break;
424  default:
425  G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
426  break;
427  }
428 }
429 
430 
439 static void
440 gnc_period_select_set_property (GObject *object,
441  guint prop_id,
442  const GValue *value,
443  GParamSpec *pspec)
444 {
445  GncPeriodSelect *period = GNC_PERIOD_SELECT(object);
446 
447  switch (prop_id)
448  {
449  case PROP_FY_END:
450  gnc_period_select_set_fy_end(period, g_value_get_pointer(value));
451  break;
452  case PROP_SHOW_DATE:
453  gnc_period_select_set_show_date(period, g_value_get_boolean(value));
454  break;
455  case PROP_DATE_BASE:
456  gnc_period_select_set_date_base(period, g_value_get_pointer(value));
457  break;
458  case PROP_PS_ACTIVE:
459  gnc_period_select_set_active_internal(period, g_value_get_int(value));
460  break;
461  default:
462  G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
463  break;
464  }
465 }
466 
469 /************************************************************/
470 /* Core Implementation */
471 /************************************************************/
472 
485 static void
486 gnc_period_select_class_init (GncPeriodSelectClass *klass)
487 {
488  GObjectClass *gobject_class;
489 
490  gobject_class = G_OBJECT_CLASS(klass);
491  gobject_class->set_property = gnc_period_select_set_property;
492  gobject_class->get_property = gnc_period_select_get_property;
493  gobject_class->finalize = gnc_period_select_finalize;
494 
495 
496  signals[CHANGED] = g_signal_new("changed",
497  G_OBJECT_CLASS_TYPE (klass),
498  G_SIGNAL_RUN_FIRST,
499  0,
500  NULL, NULL,
501  g_cclosure_marshal_VOID__VOID,
502  G_TYPE_NONE,
503  0);
504 
505 
506  g_object_class_install_property(gobject_class,
507  PROP_FY_END,
508  g_param_spec_pointer("fy-end",
509  "Fiscal Year End",
510  "The fiscal year to use for this widget",
511  G_PARAM_READWRITE));
512  g_object_class_install_property(gobject_class,
513  PROP_SHOW_DATE,
514  g_param_spec_boolean("show-date",
515  "Show Date",
516  "Show the start/end date of the accounting period in this widget",
517  FALSE,
518  G_PARAM_READWRITE));
519  g_object_class_install_property(gobject_class,
520  PROP_DATE_BASE,
521  g_param_spec_pointer("date-base",
522  "Date Base",
523  "The starting date to use for display calculations",
524  G_PARAM_READWRITE));
525  g_object_class_install_property(gobject_class,
526  PROP_PS_ACTIVE,
527  g_param_spec_int("active",
528  "Active period",
529  "The currently selected period in the list of periods",
530  -1,
531  G_MAXINT,
532  0,
533  G_PARAM_READWRITE));
534 }
535 
544 static void
545 gnc_period_select_init (GncPeriodSelect *period)
546 {
547  gtk_orientable_set_orientation (GTK_ORIENTABLE(period), GTK_ORIENTATION_HORIZONTAL);
548 
549  // Set the name for this widget so it can be easily manipulated with css
550  gtk_widget_set_name (GTK_WIDGET(period), "gnc-id-period-select");
551 
552  period->start = TRUE;
553 }
554 
555 
566 static void
567 gnc_period_select_finalize (GObject *object)
568 {
569  GncPeriodSelect *period;
570 
571  g_return_if_fail (object != NULL);
572  g_return_if_fail (GNC_IS_PERIOD_SELECT (object));
573 
574  period = GNC_PERIOD_SELECT(object);
575 
576  /* Stop tracking changes to date formatting */
577  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL, GNC_PREF_DATE_FORMAT,
578  gnc_period_sample_new_date_format, period);
579 
580  /* The selector and date_label were added to the hbox. They will be
581  * freed automatically. */
582  if (period->fy_end)
583  g_date_free(period->fy_end);
584  if (period->date_base)
585  g_date_free(period->date_base);
586 
587  /* Do not free the private data structure. It is part of a larger
588  * memory block allocated by the type system. */
589 
590  G_OBJECT_CLASS(gnc_period_select_parent_class)->finalize (object);
591 }
592 
593 
594 /* Create a new GncPeriodSelect widget which is used to select a
595  * accounting period like "previous month" or "this year".
596  *
597  * @param starting_labels If set to TRUE then all the labels will
598  * refer to the "Start of…". If FALSE, labels will refer to "End
599  * of...".
600  *
601  * @return A GncPeriodSelect widget.
602  */
603 GtkWidget *
604 gnc_period_select_new (gboolean starting_labels)
605 {
606  GncPeriodSelect *period;
607  const gchar *label;
608  gint i;
609 
610  period = g_object_new(GNC_TYPE_PERIOD_SELECT, NULL);
611 
612  /* Set up private data structures */
613  period->selector = gtk_combo_box_text_new();
614  period->start = starting_labels;
615 
616  /* Add the internal widgets to the hbox */
617  gtk_box_pack_start(GTK_BOX(period), period->selector, TRUE, TRUE, 0);
618  gtk_widget_show(period->selector);
619 
620  /* Find out when the combo box changes */
621  g_signal_connect(G_OBJECT(period->selector), "changed",
622  G_CALLBACK(gnc_period_sample_combobox_changed), period);
623 
624  /* Build all the labels except the fiscal year labels */
625  for (i = 0; i < GNC_ACCOUNTING_PERIOD_CYEAR_LAST; i++)
626  {
627  label = starting_labels ? _(start_strings[i]) : _(end_strings[i]);
628  gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(period->selector), label);
629  }
630 
631  /* Track changes to date formatting */
632  gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL, GNC_PREF_DATE_FORMAT,
633  gnc_period_sample_new_date_format, period);
634 
635  return GTK_WIDGET (period);
636 }
637 
638 
639 /* Create a new GncPeriodSelect widget from a glade file. The int1
640  * argument passed from glade is used to determine whether the widget
641  * uses labels for start times or end times. A non-zero int2
642  * argument indicates that an example date should be shown.
643  */
644 GtkWidget *
645 gnc_period_select_new_glade (gchar *widget_name,
646  gchar *string1, gchar *string2,
647  gint int1, gint int2)
648 {
649  GtkWidget *widget;
650  widget = gnc_period_select_new(int1 != 0);
651  if (int2)
652  gnc_period_select_set_show_date(GNC_PERIOD_SELECT(widget), TRUE);
653  gtk_widget_show(widget);
654  return widget;
655 }
656 
659 /************************************************************/
660 /* Auxiliary Functions */
661 /************************************************************/
662 
663 
664 /* Set which item in the GncPeriodSelect is initially selected. This
665  * is used to set the initial selection before the widget is shown to
666  * the user.
667  */
668 void
669 gnc_period_select_set_active (GncPeriodSelect *period,
670  GncAccountingPeriod which)
671 {
672  g_return_if_fail(period != NULL);
673  g_return_if_fail(GNC_IS_PERIOD_SELECT(period));
674  g_return_if_fail(which >= 0);
675  g_return_if_fail(which < GNC_ACCOUNTING_PERIOD_LAST);
676 
677  g_object_set (G_OBJECT (period), "active", which, NULL);
678 }
679 
680 
681 /* Get the currently selected accounting period from a
682  * GncPeriodSelect widget. This is used to retrieve the user's
683  * selection in the form of an enum.
684  */
686 gnc_period_select_get_active (GncPeriodSelect *period)
687 {
688  g_return_val_if_fail(period != NULL, -1);
689  g_return_val_if_fail(GNC_IS_PERIOD_SELECT(period), -1);
690 
691  return gtk_combo_box_get_active(GTK_COMBO_BOX(period->selector));
692 }
693 
694 
695 /* Get the currently selected accounting period choice from a
696  * GncPeriodSelect widget. This is used to retrieve the user's
697  * selection in the form of a GDate.
698  */
699 GDate *
700 gnc_period_select_get_date (GncPeriodSelect *period)
701 {
702  GncAccountingPeriod which;
703 
704  g_return_val_if_fail(period != NULL, 0);
705  g_return_val_if_fail(GNC_IS_PERIOD_SELECT(period), 0);
706 
707  which = gtk_combo_box_get_active(GTK_COMBO_BOX(period->selector));
708  if (which == -1)
709  return NULL;
710 
711  if (period->start)
712  return gnc_accounting_period_start_gdate(which, period->fy_end,
713  period->date_base);
714  return gnc_accounting_period_end_gdate(which, period->fy_end,
715  period->date_base);
716 }
717 
Date and Time handling routines.
gulong gnc_prefs_register_cb(const char *group, const gchar *pref_name, gpointer func, gpointer user_data)
Register a callback that gets triggered when the given preference changes.
Definition: gnc-prefs.c:128
size_t qof_print_gdate(char *buf, size_t bufflen, const GDate *gd)
Convenience; calls through to qof_print_date_dmy_buff().
Definition: gnc-date.cpp:597
GncAccountingPeriod gnc_period_select_get_active(GncPeriodSelect *period)
Get the currently selected accounting period from a GncPeriodSelect widget.
void gnc_period_select_set_show_date(GncPeriodSelect *period, const gboolean show_date)
Set the "show sample" setting on a GncPeriodSelect widget.
GDate * gnc_period_select_get_date(GncPeriodSelect *period)
Get the currently selected accounting period choice from a GncPeriodSelect widget.
GncAccountingPeriod
This specifies a time interval.
GDate * gnc_period_select_get_fy_end(GncPeriodSelect *period)
Get the current value of the fiscal year end setting from a GncPeriodSelect widget.
void gnc_period_select_set_fy_end(GncPeriodSelect *period, const GDate *fy_end)
Set the fiscal year end on a GncPeriodSelect widget.
#define MAX_DATE_LENGTH
The maximum length of a string created by the date printers.
Definition: gnc-date.h:108
Generic api to store and retrieve preferences.
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.
gboolean gnc_period_select_get_show_date(GncPeriodSelect *period)
Get the current value of the "show sample" setting from a GncPeriodSelect widget. ...
void gnc_gdate_set_time64(GDate *gd, time64 time)
Set a GDate to a time64.
Definition: gnc-date.cpp:1244
GtkWidget * gnc_period_select_new_glade(gchar *widget_name, gchar *string1, gchar *string2, gint int1, gint int2)
Create a new GncPeriodSelect widget from a glade file.
Private Data Structure.
time64 gnc_time(time64 *tbuf)
get the current time
Definition: gnc-date.cpp:261
GtkWidget * gnc_period_select_new(gboolean starting_labels)
Create a new GncPeriodSelect widget which is used to select a accounting period like "previous month"...
void gnc_period_select_set_active(GncPeriodSelect *period, GncAccountingPeriod which)
Set which item in the GncPeriodSelect is initially selected.
A custom widget for selecting accounting periods.
void gnc_prefs_remove_cb_by_func(const gchar *group, const gchar *pref_name, gpointer func, gpointer user_data)
Remove a function that was registered for a callback when the given preference changed.
Definition: gnc-prefs.c:143