GnuCash c935c2f+
Loading...
Searching...
No Matches
gnc-recurrence.c
1/********************************************************************
2 * gnc-recurrence.c -- GncRecurrence is a minimal GUI for *
3 * specifying a Recurrence. *
4 * *
5 * You see, small is _nice_. :) *
6 * Copyright (C) 2005, Chris Shoemaker <c.shoemaker@cox.net> *
7 * Copyright (C) 2011, Robert Fewell *
8 * *
9 * This program is free software; you can redistribute it and/or *
10 * modify it under the terms of the GNU General Public License as *
11 * published by the Free Software Foundation; either version 2 of *
12 * the License, or (at your option) any later version. *
13 * *
14 * This program is distributed in the hope that it will be useful, *
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
17 * GNU General Public License for more details. *
18 * *
19 * You should have received a copy of the GNU General Public License*
20 * along with this program; if not, contact: *
21 * *
22 * Free Software Foundation Voice: +1-617-542-5942 *
23 * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
24 * Boston, MA 02110-1301, USA gnu@gnu.org *
25 *******************************************************************/
26#include <config.h>
27
28#include <gtk/gtk.h>
29#include <glib/gi18n.h>
30
31#include "dialog-utils.h"
32#include "gnc-date.h"
33#include "gnc-recurrence.h"
34#include "gnc-date-edit.h"
35#include "Recurrence.h"
36#include "gnc-engine.h"
37
38static QofLogModule log_module = GNC_MOD_GUI;
39
41{
42 GtkBox widget;
43
44 GtkWidget *gde_start;
45 GtkComboBox *gcb_period;
46 GtkCheckButton *gcb_eom;
47 GtkSpinButton *gsb_mult;
48 GtkCheckButton *nth_weekday;
49
50 Recurrence recurrence;
51};
52
53typedef enum
54{
55 GNCRECURRENCE_CHANGED,
56 LAST_SIGNAL
57} GNCR_Signals;
58
59typedef enum
60{
61 GNCR_DAY,
62 GNCR_WEEK,
63 GNCR_MONTH,
64 GNCR_YEAR,
65} UIPeriodType;
66
67G_DEFINE_TYPE (GncRecurrence, gnc_recurrence, GTK_TYPE_BOX)
68
69static UIPeriodType get_pt_ui(GncRecurrence *gr)
70{
71 return (gtk_combo_box_get_active(gr->gcb_period));
72}
73
74
75static void set_pt_ui(GncRecurrence *gr, PeriodType pt)
76{
77 UIPeriodType idx;
78 switch (pt)
79 {
80 case PERIOD_DAY:
81 idx = 0;
82 break;
83 case PERIOD_WEEK:
84 idx = 1;
85 break;
86 case PERIOD_MONTH:
87 case PERIOD_END_OF_MONTH:
88 case PERIOD_NTH_WEEKDAY:
89 case PERIOD_LAST_WEEKDAY:
90 idx = 2;
91 break;
92 case PERIOD_YEAR:
93 idx = 3;
94 break;
95 default:
96 return;
97 }
98 gtk_combo_box_set_active(gr->gcb_period, idx);
99
100 gtk_toggle_button_set_active(
101 GTK_TOGGLE_BUTTON(gr->nth_weekday),
102 (pt == PERIOD_NTH_WEEKDAY || pt == PERIOD_LAST_WEEKDAY));
103
104 gtk_toggle_button_set_active(
105 GTK_TOGGLE_BUTTON(gr->gcb_eom),
106 (pt == PERIOD_END_OF_MONTH || pt == PERIOD_LAST_WEEKDAY));
107}
108
109
110static gboolean
111is_ambiguous_relative(const GDate *date)
112{
113 GDateDay d;
114 guint8 dim;
115
116 d = g_date_get_day(date);
117 dim = g_date_get_days_in_month(
118 g_date_get_month(date), g_date_get_year(date));
119 return ((d - 1) / 7 == 3) && (dim - d < 7);
120}
121
122
123static gboolean
124is_ambiguous_absolute(const GDate *date)
125{
126 return (g_date_is_last_of_month(date) &&
127 (g_date_get_day(date) < 31));
128}
129
130
131static void
132something_changed( GtkWidget *wid, gpointer d )
133{
134 UIPeriodType pt;
135 GDate start;
136 gboolean show_last, use_wd;
137 GncRecurrence *gr = GNC_RECURRENCE(d);
138
139
140 pt = get_pt_ui(gr);
141 gnc_date_edit_get_gdate(GNC_DATE_EDIT(gr->gde_start), &start);
142
143 if (pt == GNCR_MONTH)
144 g_object_set(G_OBJECT(gr->nth_weekday), "visible", TRUE, NULL);
145 else
146 {
147 g_object_set(G_OBJECT(gr->nth_weekday), "visible", FALSE, NULL);
148 gtk_toggle_button_set_active(
149 GTK_TOGGLE_BUTTON(gr->nth_weekday), FALSE);
150 }
151 use_wd = gtk_toggle_button_get_active(
152 GTK_TOGGLE_BUTTON(gr->nth_weekday));
153 //TODO: change label
154
155 /* The case under which we show the "end of month" flag is very
156 narrow, because we can almost always DTRT without it. */
157 if (pt == GNCR_MONTH)
158 {
159 if (use_wd)
160 show_last = is_ambiguous_relative(&start);
161 else
162 show_last = is_ambiguous_absolute(&start);
163 }
164 else
165 {
166 show_last = FALSE;
167 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gr->gcb_eom), FALSE);
168 }
169 g_object_set(G_OBJECT(gr->gcb_eom), "visible", show_last, NULL);
170
171 g_signal_emit_by_name(d, "changed");
172}
173
174
175static void
176gnc_recurrence_init( GncRecurrence *gr )
177{
178 GtkBox *vb;
179 GtkBox *hb;
180 GtkWidget *w;
181 GtkBuilder *builder;
182
183 recurrenceSet(&gr->recurrence, 1, PERIOD_MONTH, NULL, WEEKEND_ADJ_NONE);
184
185 // Set the name for this widget so it can be easily manipulated with css
186 gtk_widget_set_name (GTK_WIDGET(gr), "gnc-id-recurrence");
187
188 /* Open up the builder file */
189 builder = gtk_builder_new();
190 gnc_builder_add_from_file (builder, "gnc-recurrence.glade", "GCB_PeriodType_liststore");
191 gnc_builder_add_from_file (builder, "gnc-recurrence.glade", "GSB_Mult_Adj");
192 gnc_builder_add_from_file (builder, "gnc-recurrence.glade", "RecurrenceEntryVBox");
193
194 vb = GTK_BOX(gtk_builder_get_object (builder, "RecurrenceEntryVBox"));
195 hb = GTK_BOX(gtk_builder_get_object (builder, "Startdate_hbox"));
196 w = gnc_date_edit_new (gnc_time (NULL), FALSE, FALSE);
197 gr->gde_start = w;
198 gtk_box_pack_start (GTK_BOX (hb), w, TRUE, TRUE, 0);
199 gtk_widget_show (w);
200
201 gtk_widget_set_no_show_all(GTK_WIDGET(gr->gde_start), TRUE);
202 gr->gcb_period = GTK_COMBO_BOX(gtk_builder_get_object (builder, "GCB_PeriodType"));
203 gr->gsb_mult = GTK_SPIN_BUTTON(gtk_builder_get_object (builder, "GSB_Mult"));
204 gr->gcb_eom = GTK_CHECK_BUTTON(gtk_builder_get_object (builder, "GCB_EndOfMonth"));
205 gr->nth_weekday = GTK_CHECK_BUTTON(gtk_builder_get_object (builder, "GCB_NthWeekday"));
206 gtk_widget_set_no_show_all(GTK_WIDGET(gr->gcb_eom), TRUE);
207 gtk_widget_set_no_show_all(GTK_WIDGET(gr->nth_weekday), TRUE);
208
209 gtk_container_add( GTK_CONTAINER(&gr->widget), GTK_WIDGET(vb) );
210
211 gnc_recurrence_set(gr, &gr->recurrence);
212 something_changed( GTK_WIDGET(gr), gr);
213
214 /* Setup the signals */
215 g_signal_connect( G_OBJECT(gr->gde_start), "date_changed",
216 G_CALLBACK(something_changed), gr );
217 g_signal_connect( G_OBJECT(gr->gcb_period), "changed",
218 G_CALLBACK(something_changed), gr );
219 g_signal_connect( G_OBJECT(gr->gsb_mult), "value-changed",
220 G_CALLBACK(something_changed), gr );
221 g_signal_connect( G_OBJECT(gr->gcb_eom), "toggled",
222 G_CALLBACK(something_changed), gr );
223 g_signal_connect( G_OBJECT(gr->nth_weekday), "toggled",
224 G_CALLBACK(something_changed), gr );
225
226 gtk_widget_show_all( GTK_WIDGET(&gr->widget) );
227
228 gtk_builder_connect_signals(builder, gr);
229 g_object_unref(G_OBJECT(builder));
230}
231
232
233void
234gnc_recurrence_set(GncRecurrence *gr, const Recurrence *r)
235{
236 PeriodType pt;
237 guint mult;
238 GDate start;
239
240 g_return_if_fail(gr && r);
241 pt = recurrenceGetPeriodType(r);
242 mult = recurrenceGetMultiplier(r);
243 start = recurrenceGetDate(r);
244
245 gtk_spin_button_set_value(gr->gsb_mult, (gdouble) mult);
246
247 // is there some better way?
248 {
249 time64 t;
251 gnc_date_edit_set_time (GNC_DATE_EDIT(gr->gde_start), t);
252 }
253
254 set_pt_ui(gr, pt);
255}
256
257
258const Recurrence *
259gnc_recurrence_get(GncRecurrence *gr)
260{
261 guint mult;
262 UIPeriodType period;
263 PeriodType pt;
264 GDate start;
265 gboolean use_eom = FALSE, rel;
266
267 mult = (guint) gtk_spin_button_get_value_as_int(gr->gsb_mult);
268 gnc_date_edit_get_gdate(GNC_DATE_EDIT(gr->gde_start), &start);
269 period = get_pt_ui(gr);
270
271 switch (period)
272 {
273 case GNCR_DAY:
274 pt = PERIOD_DAY;
275 break;
276 case GNCR_WEEK:
277 pt = PERIOD_WEEK;
278 break;
279 case GNCR_MONTH:
280 rel = gtk_toggle_button_get_active(
281 GTK_TOGGLE_BUTTON(gr->nth_weekday));
282 if (rel)
283 {
284 if (is_ambiguous_relative(&start))
285 {
286 use_eom = gtk_toggle_button_get_active(
287 GTK_TOGGLE_BUTTON(gr->gcb_eom));
288 }
289 else
290 {
291 GDateDay d;
292 d = g_date_get_day(&start);
293
294 use_eom = ((d - 1) / 7 == 4);
295 }
296 if (use_eom)
297 pt = PERIOD_LAST_WEEKDAY;
298 else pt = PERIOD_NTH_WEEKDAY;
299 }
300 else
301 {
302 if (g_date_is_last_of_month(&start) &&
303 (g_date_get_day(&start) < 31))
304 {
305 // ambiguous, need to examine the checkbox
306 use_eom = gtk_toggle_button_get_active(
307 GTK_TOGGLE_BUTTON(gr->gcb_eom));
308 }
309 else
310 {
311 // if it's the last dom, use eom anyway because it's the 31st.
312 use_eom = g_date_is_last_of_month(&start);
313 }
314 if (use_eom)
315 pt = PERIOD_END_OF_MONTH;
316 else pt = PERIOD_MONTH;
317 }
318 break;
319 case GNCR_YEAR:
320 pt = PERIOD_YEAR;
321 break;
322 default:
323 pt = PERIOD_INVALID;
324 }
325
326 recurrenceSet(&gr->recurrence, mult, pt, &start, WEEKEND_ADJ_NONE);
327 return &gr->recurrence;
328}
329
330
331static void
332gnc_recurrence_finalize(GObject *o)
333{
334 GncRecurrence *gr = GNC_RECURRENCE(o);
335
336 if (gr)
337 G_OBJECT_CLASS (gnc_recurrence_parent_class)->finalize (o);
338}
339
340
341static void
342gnc_recurrence_class_init( GncRecurrenceClass *klass )
343{
344 GObjectClass *object_class;
345
346 object_class = G_OBJECT_CLASS (klass);
347 g_signal_new ("changed",
348 G_OBJECT_CLASS_TYPE (object_class),
349 G_SIGNAL_RUN_FIRST,
350 0,
351 NULL,
352 NULL,
353 g_cclosure_marshal_VOID__VOID,
354 G_TYPE_NONE,
355 0);
356
357 object_class->finalize = gnc_recurrence_finalize;
358}
359
360GtkWidget *
361gnc_recurrence_new()
362{
363 GncRecurrence *gr;
364
365 ENTER(" ");
366 gr = g_object_new(gnc_recurrence_get_type(), NULL);
367 LEAVE(" ");
368 return GTK_WIDGET(gr);
369}
370
371/* ========================= END OF FILE =========================== */
Date and Time handling routines.
All type declarations for the whole Gnucash engine.
gint64 time64
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
Definition gnc-date.h:87
time64 gnc_time(time64 *tbuf)
get the current time
Definition gnc-date.cpp:262
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...
#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