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_copy (fy_end);
274  }
275  else
276  {
277  period->fy_end = NULL;
278  }
279 
280  if (fy_end)
281  {
282  for (i = GNC_ACCOUNTING_PERIOD_CYEAR_LAST; i < GNC_ACCOUNTING_PERIOD_FYEAR_LAST; i++)
283  {
284  label = period->start ? _(start_strings[i]) : _(end_strings[i]);
285  gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(period->selector), label);
286  }
287  }
288  else
289  {
290  for (i = GNC_ACCOUNTING_PERIOD_FYEAR_LAST - 1; i >= GNC_ACCOUNTING_PERIOD_FYEAR_LAST; i--)
291  {
292  gtk_combo_box_text_remove(GTK_COMBO_BOX_TEXT(period->selector), i);
293  }
294  }
295 }
296 
297 
298 static void
299 gnc_period_select_set_date_common (GncPeriodSelect *period, const GDate *date)
300 {
301  if (date)
302  {
303  if (period->date_base)
304  g_date_free(period->date_base);
305  period->date_base = g_date_new_dmy(g_date_get_day(date),
306  g_date_get_month(date),
307  g_date_get_year(date));
308  if (period->date_label == NULL)
309  {
310  period->date_label = gtk_label_new("");
311  gtk_widget_set_margin_start (GTK_WIDGET(period->date_label), 6);
312  gtk_box_pack_start(GTK_BOX(period), period->date_label, TRUE, TRUE, 0);
313  gtk_widget_show_all(period->date_label);
314  }
315  gnc_period_sample_update_date_label(period);
316  return;
317  }
318 
319  if (period->date_base)
320  {
321  g_date_free(period->date_base);
322  period->date_base = NULL;
323  gtk_widget_destroy(period->date_label);
324  period->date_label = NULL;
325  }
326 }
327 
328 
329 /* Get the current value of the "show date" setting from a
330  * GncPeriodSelect widget.
331  */
332 gboolean
333 gnc_period_select_get_show_date (GncPeriodSelect *period)
334 {
335  g_return_val_if_fail(period != NULL, FALSE);
336  g_return_val_if_fail(GNC_IS_PERIOD_SELECT(period), FALSE);
337 
338  return (period->date_base != NULL);
339 }
340 
341 /* Set the "show date" setting on a GncPeriodSelect widget. If set
342  * to TRUE then a GtkLabel will be used to show the date
343  * corresponding to the selected time period.
344  */
345 void
346 gnc_period_select_set_show_date (GncPeriodSelect *period, const gboolean show_date)
347 {
348  GDate date;
349 
350  g_return_if_fail(period != NULL);
351  g_return_if_fail(GNC_IS_PERIOD_SELECT(period));
352 
353  if (show_date)
354  {
355  g_date_clear(&date, 1);
356  gnc_gdate_set_time64(&date, gnc_time (NULL));
357  gnc_period_select_set_date_common(period, &date);
358  }
359  else
360  {
361  gnc_period_select_set_date_common(period, NULL);
362  }
363 }
364 
365 
366 GDate *
367 gnc_period_select_get_date_base (GncPeriodSelect *period)
368 {
369  g_return_val_if_fail(period != NULL, NULL);
370  g_return_val_if_fail(GNC_IS_PERIOD_SELECT(period), NULL);
371 
372  if (!period->date_base)
373  return NULL;
374  return g_date_new_dmy(g_date_get_day(period->date_base),
375  g_date_get_month(period->date_base),
376  g_date_get_year(period->date_base));
377 }
378 
379 
380 /* Set the base date used by a GncPeriodSelect widget. All example
381  * dates presented by the widget will be computed from this date.
382  */
383 void
384 gnc_period_select_set_date_base (GncPeriodSelect *period, const GDate *date_base)
385 {
386  g_return_if_fail(period != NULL);
387  g_return_if_fail(GNC_IS_PERIOD_SELECT(period));
388 
389  gnc_period_select_set_date_common(period, date_base);
390 }
391 
392 
400 static void
401 gnc_period_select_get_property (GObject *object,
402  guint prop_id,
403  GValue *value,
404  GParamSpec *pspec)
405 {
406  GncPeriodSelect *period = GNC_PERIOD_SELECT(object);
407 
408  switch (prop_id)
409  {
410  case PROP_FY_END:
411  g_value_set_pointer(value, gnc_period_select_get_fy_end(period));
412  break;
413  case PROP_SHOW_DATE:
414  g_value_set_boolean(value, gnc_period_select_get_show_date(period));
415  break;
416  case PROP_DATE_BASE:
417  g_value_set_pointer(value, gnc_period_select_get_date_base(period));
418  break;
419  case PROP_PS_ACTIVE:
420  g_value_set_int(value, gnc_period_select_get_active(period));
421  break;
422  default:
423  G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
424  break;
425  }
426 }
427 
428 
437 static void
438 gnc_period_select_set_property (GObject *object,
439  guint prop_id,
440  const GValue *value,
441  GParamSpec *pspec)
442 {
443  GncPeriodSelect *period = GNC_PERIOD_SELECT(object);
444 
445  switch (prop_id)
446  {
447  case PROP_FY_END:
448  gnc_period_select_set_fy_end(period, g_value_get_pointer(value));
449  break;
450  case PROP_SHOW_DATE:
451  gnc_period_select_set_show_date(period, g_value_get_boolean(value));
452  break;
453  case PROP_DATE_BASE:
454  gnc_period_select_set_date_base(period, g_value_get_pointer(value));
455  break;
456  case PROP_PS_ACTIVE:
457  gnc_period_select_set_active_internal(period, g_value_get_int(value));
458  break;
459  default:
460  G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
461  break;
462  }
463 }
464 
467 /************************************************************/
468 /* Core Implementation */
469 /************************************************************/
470 
483 static void
484 gnc_period_select_class_init (GncPeriodSelectClass *klass)
485 {
486  GObjectClass *gobject_class;
487 
488  gobject_class = G_OBJECT_CLASS(klass);
489  gobject_class->set_property = gnc_period_select_set_property;
490  gobject_class->get_property = gnc_period_select_get_property;
491  gobject_class->finalize = gnc_period_select_finalize;
492 
493 
494  signals[CHANGED] = g_signal_new("changed",
495  G_OBJECT_CLASS_TYPE (klass),
496  G_SIGNAL_RUN_FIRST,
497  0,
498  NULL, NULL,
499  g_cclosure_marshal_VOID__VOID,
500  G_TYPE_NONE,
501  0);
502 
503 
504  g_object_class_install_property(gobject_class,
505  PROP_FY_END,
506  g_param_spec_pointer("fy-end",
507  "Fiscal Year End",
508  "The fiscal year to use for this widget",
509  G_PARAM_READWRITE));
510  g_object_class_install_property(gobject_class,
511  PROP_SHOW_DATE,
512  g_param_spec_boolean("show-date",
513  "Show Date",
514  "Show the start/end date of the accounting period in this widget",
515  FALSE,
516  G_PARAM_READWRITE));
517  g_object_class_install_property(gobject_class,
518  PROP_DATE_BASE,
519  g_param_spec_pointer("date-base",
520  "Date Base",
521  "The starting date to use for display calculations",
522  G_PARAM_READWRITE));
523  g_object_class_install_property(gobject_class,
524  PROP_PS_ACTIVE,
525  g_param_spec_int("active",
526  "Active period",
527  "The currently selected period in the list of periods",
528  -1,
529  G_MAXINT,
530  0,
531  G_PARAM_READWRITE));
532 }
533 
542 static void
543 gnc_period_select_init (GncPeriodSelect *period)
544 {
545  gtk_orientable_set_orientation (GTK_ORIENTABLE(period), GTK_ORIENTATION_HORIZONTAL);
546 
547  // Set the name for this widget so it can be easily manipulated with css
548  gtk_widget_set_name (GTK_WIDGET(period), "gnc-id-period-select");
549 
550  period->start = TRUE;
551 }
552 
553 
564 static void
565 gnc_period_select_finalize (GObject *object)
566 {
567  GncPeriodSelect *period;
568 
569  g_return_if_fail (object != NULL);
570  g_return_if_fail (GNC_IS_PERIOD_SELECT (object));
571 
572  period = GNC_PERIOD_SELECT(object);
573 
574  /* Stop tracking changes to date formatting */
575  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL, GNC_PREF_DATE_FORMAT,
576  gnc_period_sample_new_date_format, period);
577 
578  /* The selector and date_label were added to the hbox. They will be
579  * freed automatically. */
580  if (period->fy_end)
581  g_date_free(period->fy_end);
582  if (period->date_base)
583  g_date_free(period->date_base);
584 
585  /* Do not free the private data structure. It is part of a larger
586  * memory block allocated by the type system. */
587 
588  G_OBJECT_CLASS(gnc_period_select_parent_class)->finalize (object);
589 }
590 
591 
592 /* Create a new GncPeriodSelect widget which is used to select a
593  * accounting period like "previous month" or "this year".
594  *
595  * @param starting_labels If set to TRUE then all the labels will
596  * refer to the "Start of…". If FALSE, labels will refer to "End
597  * of...".
598  *
599  * @return A GncPeriodSelect widget.
600  */
601 GtkWidget *
602 gnc_period_select_new (gboolean starting_labels)
603 {
604  GncPeriodSelect *period;
605  const gchar *label;
606  gint i;
607 
608  period = g_object_new(GNC_TYPE_PERIOD_SELECT, NULL);
609 
610  /* Set up private data structures */
611  period->selector = gtk_combo_box_text_new();
612  period->start = starting_labels;
613 
614  /* Add the internal widgets to the hbox */
615  gtk_box_pack_start(GTK_BOX(period), period->selector, TRUE, TRUE, 0);
616  gtk_widget_show(period->selector);
617 
618  /* Find out when the combo box changes */
619  g_signal_connect(G_OBJECT(period->selector), "changed",
620  G_CALLBACK(gnc_period_sample_combobox_changed), period);
621 
622  /* Build all the labels except the fiscal year labels */
623  for (i = 0; i < GNC_ACCOUNTING_PERIOD_CYEAR_LAST; i++)
624  {
625  label = starting_labels ? _(start_strings[i]) : _(end_strings[i]);
626  gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(period->selector), label);
627  }
628 
629  /* Track changes to date formatting */
630  gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL, GNC_PREF_DATE_FORMAT,
631  gnc_period_sample_new_date_format, period);
632 
633  return GTK_WIDGET (period);
634 }
635 
636 
637 /* Create a new GncPeriodSelect widget from a glade file. The int1
638  * argument passed from glade is used to determine whether the widget
639  * uses labels for start times or end times. A non-zero int2
640  * argument indicates that an example date should be shown.
641  */
642 GtkWidget *
643 gnc_period_select_new_glade (gchar *widget_name,
644  gchar *string1, gchar *string2,
645  gint int1, gint int2)
646 {
647  GtkWidget *widget;
648  widget = gnc_period_select_new(int1 != 0);
649  if (int2)
650  gnc_period_select_set_show_date(GNC_PERIOD_SELECT(widget), TRUE);
651  gtk_widget_show(widget);
652  return widget;
653 }
654 
657 /************************************************************/
658 /* Auxiliary Functions */
659 /************************************************************/
660 
661 
662 /* Set which item in the GncPeriodSelect is initially selected. This
663  * is used to set the initial selection before the widget is shown to
664  * the user.
665  */
666 void
667 gnc_period_select_set_active (GncPeriodSelect *period,
668  GncAccountingPeriod which)
669 {
670  g_return_if_fail(period != NULL);
671  g_return_if_fail(GNC_IS_PERIOD_SELECT(period));
672  g_return_if_fail(which >= 0);
673  g_return_if_fail(which < GNC_ACCOUNTING_PERIOD_LAST);
674 
675  g_object_set (G_OBJECT (period), "active", which, NULL);
676 }
677 
678 
679 /* Get the currently selected accounting period from a
680  * GncPeriodSelect widget. This is used to retrieve the user's
681  * selection in the form of an enum.
682  */
684 gnc_period_select_get_active (GncPeriodSelect *period)
685 {
686  g_return_val_if_fail(period != NULL, -1);
687  g_return_val_if_fail(GNC_IS_PERIOD_SELECT(period), -1);
688 
689  return gtk_combo_box_get_active(GTK_COMBO_BOX(period->selector));
690 }
691 
692 
693 /* Get the currently selected accounting period choice from a
694  * GncPeriodSelect widget. This is used to retrieve the user's
695  * selection in the form of a GDate.
696  */
697 GDate *
698 gnc_period_select_get_date (GncPeriodSelect *period)
699 {
700  GncAccountingPeriod which;
701 
702  g_return_val_if_fail(period != NULL, 0);
703  g_return_val_if_fail(GNC_IS_PERIOD_SELECT(period), 0);
704 
705  which = gtk_combo_box_get_active(GTK_COMBO_BOX(period->selector));
706  if (which == -1)
707  return NULL;
708 
709  if (period->start)
710  return gnc_accounting_period_start_gdate(which, period->fy_end,
711  period->date_base);
712  return gnc_accounting_period_end_gdate(which, period->fy_end,
713  period->date_base);
714 }
715 
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.cpp:127
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:598
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:1314
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: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"...
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.cpp:142