1 /********************************************************************
2 * gnc-date-format.c -- Date formator widget *
3 * (GnuCash) *
4 * Copyright (C) 2003 Derek Atkins <derek@ihtfp.com> *
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 @NOTATION@
26 */
27
28 /*
29 * Date format widget
30 *
31 * Authors: Derek Atkins <derek@ihtfp.com>
32 */
33
34 #include <config.h>
35
36 #include <gtk/gtk.h>
37 #include <string.h>
38 #include <stdio.h>
39
40 #include "gnc-date-format.h"
41 #include "dialog-utils.h"
42 #include "gnc-engine.h"
43
44 /* Perhaps it's better just to use MAX_DATE_LENGTH defined in gnc-date.h */
45 #define MAX_DATE_LEN 80
46
47 /* This static indicates the debugging module that this .o belongs to. */
48 G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_GUI;
49
50 enum
51 {
52 FORMAT_CHANGED,
53 LAST_SIGNAL
54 };
55
56 typedef struct _GNCDateFormatPrivate GNCDateFormatPrivate;
57
58 struct _GNCDateFormatPrivate
59 {
60 GtkWidget* format_combobox;
61
62 GtkWidget* label;
63 GtkWidget* table;
64
65 GtkWidget* months_label;
66 GtkWidget* months_number;
67 GtkWidget* months_abbrev;
68 GtkWidget* months_name;
69
70 GtkWidget* years_label;
71 GtkWidget* years_button;
72
73 GtkWidget* custom_label;
74 GtkWidget* custom_entry;
75
76 GtkWidget* sample_label;
77 };
78
79 #define GNC_DATE_FORMAT_GET_PRIVATE(o) \
80 ((GNCDateFormatPrivate*)g_type_instance_get_private((GTypeInstance*)o, GNC_TYPE_DATE_FORMAT))
81
82 static guint date_format_signals [LAST_SIGNAL] = { 0 };
83
84 static void gnc_date_format_init (GNCDateFormat *gdf);
85 static void gnc_date_format_class_init (GNCDateFormatClass *klass);
86 static void gnc_date_format_finalize (GObject *object);
87 static void gnc_date_format_compute_format(GNCDateFormat *gdf);
88
89 void gnc_ui_date_format_changed_cb(GtkWidget *unused, gpointer user_data);
90
91 static GtkBoxClass *parent_class;
92
G_DEFINE_TYPE_WITH_PRIVATE(GNCDateFormat,gnc_date_format,GTK_TYPE_BOX)93 G_DEFINE_TYPE_WITH_PRIVATE(GNCDateFormat, gnc_date_format, GTK_TYPE_BOX)
94
95 static void
96 gnc_date_format_class_init (GNCDateFormatClass *klass)
97 {
98 GObjectClass *gobject_class = (GObjectClass *) klass;
99
100 parent_class = g_type_class_peek_parent(klass);
101
102 gobject_class->finalize = gnc_date_format_finalize;
103
104 date_format_signals [FORMAT_CHANGED] =
105 g_signal_new ("format_changed",
106 G_OBJECT_CLASS_TYPE (gobject_class),
107 G_SIGNAL_RUN_FIRST,
108 G_STRUCT_OFFSET (GNCDateFormatClass, format_changed),
109 NULL,
110 NULL,
111 g_cclosure_marshal_VOID__VOID,
112 G_TYPE_NONE,
113 0);
114 }
115
116
117 static void
gnc_date_format_init(GNCDateFormat * gdf)118 gnc_date_format_init (GNCDateFormat *gdf)
119 {
120 GNCDateFormatPrivate *priv;
121 GtkBuilder *builder;
122 GtkWidget *dialog;
123
124 g_return_if_fail(gdf);
125 g_return_if_fail(GNC_IS_DATE_FORMAT(gdf));
126
127 gtk_orientable_set_orientation (GTK_ORIENTABLE(gdf), GTK_ORIENTATION_HORIZONTAL);
128
129 // Set the name for this widget so it can be easily manipulated with css
130 gtk_widget_set_name (GTK_WIDGET(gdf), "gnc-id-date-format");
131
132 /* Open up the Glade and set the signals */
133 builder = gtk_builder_new();
134 gnc_builder_add_from_file (builder, "gnc-date-format.glade", "format-liststore");
135 gnc_builder_add_from_file (builder, "gnc-date-format.glade", "gnc_date_format_window");
136
137 gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, gdf);
138
139 /* pull in all the child widgets */
140 priv = GNC_DATE_FORMAT_GET_PRIVATE(gdf);
141 priv->label = GTK_WIDGET(gtk_builder_get_object (builder, "widget_label"));
142 priv->format_combobox = GTK_WIDGET(gtk_builder_get_object (builder, "format_combobox"));
143
144 priv->months_label = GTK_WIDGET(gtk_builder_get_object (builder, "months_label"));
145 priv->months_number = GTK_WIDGET(gtk_builder_get_object (builder, "month_number_button"));
146 priv->months_abbrev = GTK_WIDGET(gtk_builder_get_object (builder, "month_abbrev_button"));
147 priv->months_name = GTK_WIDGET(gtk_builder_get_object (builder, "month_name_button"));
148
149 priv->years_label = GTK_WIDGET(gtk_builder_get_object (builder, "years_label"));
150 priv->years_button = GTK_WIDGET(gtk_builder_get_object (builder, "years_button"));
151
152 priv->custom_label = GTK_WIDGET(gtk_builder_get_object (builder, "format_label"));
153 priv->custom_entry = GTK_WIDGET(gtk_builder_get_object (builder, "format_entry"));
154
155 priv->sample_label = GTK_WIDGET(gtk_builder_get_object (builder, "sample_label"));
156
157 /* Set initial format to gnucash default */
158 gnc_date_format_set_format(gdf, QOF_DATE_FORMAT_UNSET);
159
160 /* pull in the dialog and table widgets and play the reconnect game */
161 dialog = GTK_WIDGET(gtk_builder_get_object (builder, "gnc_date_format_window"));
162
163 priv->table = GTK_WIDGET(gtk_builder_get_object (builder, "date_format_table"));
164 g_object_ref (G_OBJECT(priv->table));
165 gtk_container_remove (GTK_CONTAINER(dialog), priv->table);
166 gtk_container_add (GTK_CONTAINER(gdf), priv->table);
167 g_object_unref (G_OBJECT(priv->table));
168
169 g_object_unref(G_OBJECT(builder));
170
171 /* Destroy the now empty window */
172 gtk_widget_destroy(dialog);
173 }
174
175
176 static void
gnc_date_format_finalize(GObject * object)177 gnc_date_format_finalize (GObject *object)
178 {
179 g_return_if_fail(object != NULL);
180 g_return_if_fail(GNC_IS_DATE_FORMAT(object));
181
182 if (G_OBJECT_CLASS(parent_class)->finalize)
183 (* G_OBJECT_CLASS(parent_class)->finalize) (object);
184 }
185
186
187 /**
188 * gnc_date_format_new:
189 *
190 * Creates a new GNCDateFormat widget which can be used to provide
191 * an easy to use way for entering date formats and seeing the sample.
192 *
193 * Returns a GNCDateFormat widget.
194 */
195 GtkWidget *
gnc_date_format_new(void)196 gnc_date_format_new (void)
197 {
198 return gnc_date_format_new_with_label (NULL);
199 }
200
201
202 GtkWidget *
gnc_date_format_new_without_label(void)203 gnc_date_format_new_without_label (void)
204 {
205 GtkWidget *widget = gnc_date_format_new_with_label(NULL);
206 GNCDateFormat *gdf = GNC_DATE_FORMAT(widget);
207 GNCDateFormatPrivate *priv = GNC_DATE_FORMAT_GET_PRIVATE(gdf);
208
209 // remove the first column which has the label
210 gtk_grid_remove_column (GTK_GRID(priv->table), 0);
211 priv->label = NULL;
212 return widget;
213 }
214
215
216 /**
217 * gnc_date_format_new_with_label:
218 * @label: the label to use to define the widget.
219 *
220 * Creates a new GNCDateFormat widget which can be used to provide
221 * an easy to use way for entering date formats and seeing the sample.
222 *
223 * Returns a GNCDateFormat widget.
224 */
225 GtkWidget *
gnc_date_format_new_with_label(const char * label)226 gnc_date_format_new_with_label (const char *label)
227 {
228 GNCDateFormat *gdf;
229 GNCDateFormatPrivate *priv;
230
231 gdf = g_object_new(GNC_TYPE_DATE_FORMAT, NULL);
232 priv = GNC_DATE_FORMAT_GET_PRIVATE(gdf);
233
234 if (label)
235 gtk_label_set_text(GTK_LABEL(priv->label), label);
236
237 gnc_date_format_compute_format(gdf);
238 return GTK_WIDGET(gdf);
239 }
240
241
242 void
gnc_date_format_set_format(GNCDateFormat * gdf,QofDateFormat format)243 gnc_date_format_set_format (GNCDateFormat *gdf, QofDateFormat format)
244 {
245 GNCDateFormatPrivate *priv;
246
247 g_return_if_fail(gdf);
248 g_return_if_fail(GNC_IS_DATE_FORMAT(gdf));
249
250 priv = GNC_DATE_FORMAT_GET_PRIVATE(gdf);
251 gtk_combo_box_set_active(GTK_COMBO_BOX(priv->format_combobox), format);
252 gnc_date_format_compute_format(gdf);
253 }
254
255
256 QofDateFormat
gnc_date_format_get_format(GNCDateFormat * gdf)257 gnc_date_format_get_format (GNCDateFormat *gdf)
258 {
259 GNCDateFormatPrivate *priv;
260
261 g_return_val_if_fail (gdf, QOF_DATE_FORMAT_LOCALE);
262 g_return_val_if_fail (GNC_IS_DATE_FORMAT(gdf), QOF_DATE_FORMAT_LOCALE);
263
264 priv = GNC_DATE_FORMAT_GET_PRIVATE(gdf);
265 return gtk_combo_box_get_active(GTK_COMBO_BOX(priv->format_combobox));
266 }
267
268
269 void
gnc_date_format_set_months(GNCDateFormat * gdf,GNCDateMonthFormat months)270 gnc_date_format_set_months (GNCDateFormat *gdf, GNCDateMonthFormat months)
271 {
272 GNCDateFormatPrivate *priv;
273 GtkWidget *button = NULL;
274
275 g_return_if_fail(gdf);
276 g_return_if_fail(GNC_IS_DATE_FORMAT(gdf));
277
278 priv = GNC_DATE_FORMAT_GET_PRIVATE(gdf);
279 switch (months)
280 {
281 case GNCDATE_MONTH_NUMBER:
282 button = priv->months_number;
283 break;
284 case GNCDATE_MONTH_ABBREV:
285 button = priv->months_abbrev;
286 break;
287 case GNCDATE_MONTH_NAME:
288 button = priv->months_name;
289 break;
290 default:
291 break;
292 }
293
294 g_return_if_fail(button);
295
296 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
297 gnc_date_format_compute_format(gdf);
298 }
299
300
301 GNCDateMonthFormat
gnc_date_format_get_months(GNCDateFormat * gdf)302 gnc_date_format_get_months (GNCDateFormat *gdf)
303 {
304 GNCDateFormatPrivate *priv;
305
306 g_return_val_if_fail(gdf, GNCDATE_MONTH_NUMBER);
307 g_return_val_if_fail(GNC_IS_DATE_FORMAT(gdf), GNCDATE_MONTH_NUMBER);
308
309 priv = GNC_DATE_FORMAT_GET_PRIVATE(gdf);
310 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->months_number)))
311 return GNCDATE_MONTH_NUMBER;
312 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->months_abbrev)))
313 return GNCDATE_MONTH_ABBREV;
314 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->months_name)))
315 return GNCDATE_MONTH_NAME;
316
317 /* We should never reach this point */
318 g_assert(FALSE);
319 return GNCDATE_MONTH_NUMBER;
320 }
321
322
323 void
gnc_date_format_set_years(GNCDateFormat * gdf,gboolean include_century)324 gnc_date_format_set_years (GNCDateFormat *gdf, gboolean include_century)
325 {
326 GNCDateFormatPrivate *priv;
327
328 g_return_if_fail(gdf);
329 g_return_if_fail(GNC_IS_DATE_FORMAT(gdf));
330
331 priv = GNC_DATE_FORMAT_GET_PRIVATE(gdf);
332 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->years_button),
333 include_century);
334 gnc_date_format_compute_format(gdf);
335 }
336
337
338 gboolean
gnc_date_format_get_years(GNCDateFormat * gdf)339 gnc_date_format_get_years (GNCDateFormat *gdf)
340 {
341 GNCDateFormatPrivate *priv;
342
343 g_return_val_if_fail(gdf, FALSE);
344 g_return_val_if_fail(GNC_IS_DATE_FORMAT(gdf), FALSE);
345
346 priv = GNC_DATE_FORMAT_GET_PRIVATE(gdf);
347 return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->years_button));
348 }
349
350
351 void
gnc_date_format_set_custom(GNCDateFormat * gdf,const char * format)352 gnc_date_format_set_custom (GNCDateFormat *gdf, const char *format)
353 {
354 GNCDateFormatPrivate *priv;
355
356 g_return_if_fail(gdf);
357 g_return_if_fail(GNC_IS_DATE_FORMAT(gdf));
358
359 if (format == NULL || *format == '\0')
360 return;
361
362 priv = GNC_DATE_FORMAT_GET_PRIVATE(gdf);
363 gtk_entry_set_text(GTK_ENTRY(priv->custom_entry), format);
364 gnc_date_format_compute_format(gdf);
365 }
366
367
368 const char *
gnc_date_format_get_custom(GNCDateFormat * gdf)369 gnc_date_format_get_custom (GNCDateFormat *gdf)
370 {
371 GNCDateFormatPrivate *priv;
372
373 g_return_val_if_fail(gdf, "");
374 g_return_val_if_fail(GNC_IS_DATE_FORMAT(gdf), "");
375
376 priv = GNC_DATE_FORMAT_GET_PRIVATE(gdf);
377 return gtk_entry_get_text(GTK_ENTRY(priv->custom_entry));
378 }
379
380
381 void
gnc_ui_date_format_changed_cb(GtkWidget * unused,gpointer user_data)382 gnc_ui_date_format_changed_cb(GtkWidget *unused, gpointer user_data)
383 {
384 GNCDateFormat * gdf = user_data;
385
386 gnc_date_format_compute_format(gdf);
387 }
388
389
390 static void
gnc_date_format_enable_month(GNCDateFormat * gdf,gboolean sensitive)391 gnc_date_format_enable_month (GNCDateFormat *gdf, gboolean sensitive)
392 {
393 GNCDateFormatPrivate *priv;
394
395 priv = GNC_DATE_FORMAT_GET_PRIVATE(gdf);
396 gtk_widget_set_sensitive(priv->months_label, sensitive);
397 gtk_widget_set_sensitive(priv->months_number, sensitive);
398 gtk_widget_set_sensitive(priv->months_abbrev, sensitive);
399 gtk_widget_set_sensitive(priv->months_name, sensitive);
400 }
401
402
403 static void
gnc_date_format_enable_year(GNCDateFormat * gdf,gboolean sensitive)404 gnc_date_format_enable_year (GNCDateFormat *gdf, gboolean sensitive)
405 {
406 GNCDateFormatPrivate *priv;
407
408 priv = GNC_DATE_FORMAT_GET_PRIVATE(gdf);
409 gtk_widget_set_sensitive(priv->years_label, sensitive);
410 gtk_widget_set_sensitive(priv->years_button, sensitive);
411 }
412
413
414 static void
gnc_date_format_enable_format(GNCDateFormat * gdf,gboolean sensitive)415 gnc_date_format_enable_format (GNCDateFormat *gdf, gboolean sensitive)
416 {
417 GNCDateFormatPrivate *priv;
418
419 priv = GNC_DATE_FORMAT_GET_PRIVATE(gdf);
420 gtk_widget_set_sensitive(priv->custom_label, sensitive);
421 gtk_widget_set_sensitive(priv->custom_entry, sensitive);
422 }
423
424
425 void
gnc_date_format_refresh(GNCDateFormat * gdf)426 gnc_date_format_refresh (GNCDateFormat *gdf)
427 {
428 GNCDateFormatPrivate *priv;
429 int sel_option;
430 gboolean enable_year, enable_month, enable_custom, check_modifiers;
431 static gchar *format, *c;
432 gchar date_string[MAX_DATE_LEN];
433 time64 secs_now;
434 struct tm today;
435
436 g_return_if_fail(gdf);
437 g_return_if_fail(GNC_IS_DATE_FORMAT(gdf));
438
439 priv = GNC_DATE_FORMAT_GET_PRIVATE(gdf);
440 sel_option =
441 gtk_combo_box_get_active(GTK_COMBO_BOX(priv->format_combobox));
442
443 switch (sel_option)
444 {
445 case QOF_DATE_FORMAT_CUSTOM:
446 format = g_strdup(gtk_entry_get_text(GTK_ENTRY(priv->custom_entry)));
447 enable_year = enable_month = check_modifiers = FALSE;
448 enable_custom = TRUE;
449 break;
450
451 case QOF_DATE_FORMAT_UNSET:
452 case QOF_DATE_FORMAT_LOCALE:
453 case QOF_DATE_FORMAT_UTC:
454 format = g_strdup(qof_date_format_get_string(sel_option));
455 enable_year = enable_month = check_modifiers = enable_custom = FALSE;
456 break;
457
458 case QOF_DATE_FORMAT_ISO:
459 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->months_number), TRUE);
460 enable_year = check_modifiers = TRUE;
461 enable_month = enable_custom = FALSE;
462 break;
463
464 default:
465 enable_year = enable_month = check_modifiers = TRUE;
466 enable_custom = FALSE;
467 break;
468 }
469
470 /* Tweak widget sensitivities, as appropriate. */
471 gnc_date_format_enable_year(gdf, enable_year);
472 gnc_date_format_enable_month(gdf, enable_month);
473 gnc_date_format_enable_format(gdf, enable_custom);
474
475 /* Update the format string based upon the user's preferences */
476 if (check_modifiers)
477 {
478 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->months_number)))
479 {
480 format = g_strdup(qof_date_format_get_string(sel_option));
481 }
482 else
483 {
484 format = g_strdup(qof_date_text_format_get_string(sel_option));
485 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->months_name)))
486 {
487 c = strchr(format, 'b');
488 if (c)
489 *c = 'B';
490 }
491 }
492 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->years_button)))
493 {
494 c = strchr(format, 'y');
495 if (c)
496 *c = 'Y';
497 }
498 }
499
500 /*
501 * Give feedback on the format string so users can see how it works
502 * without having to read the strftime man page. Prevent recursive
503 * signals.
504 */
505 g_signal_handlers_block_matched(priv->custom_entry, G_SIGNAL_MATCH_DATA,
506 0, 0, NULL, NULL, gdf);
507 gtk_entry_set_text(GTK_ENTRY(priv->custom_entry), format);
508 g_signal_handlers_unblock_matched(priv->custom_entry, G_SIGNAL_MATCH_DATA,
509 0, 0, NULL, NULL, gdf);
510
511 /* Visual feedback on what the date will look like. */
512 secs_now = gnc_time (NULL);
513 gnc_localtime_r (&secs_now, &today);
514 qof_strftime(date_string, MAX_DATE_LEN, format, &today);
515 gtk_label_set_text(GTK_LABEL(priv->sample_label), date_string);
516 g_free(format);
517 }
518
519
520 static void
gnc_date_format_compute_format(GNCDateFormat * gdf)521 gnc_date_format_compute_format(GNCDateFormat *gdf)
522 {
523 g_return_if_fail(gdf);
524 g_return_if_fail(GNC_IS_DATE_FORMAT(gdf));
525
526 /* refresh the widget */
527 gnc_date_format_refresh(gdf);
528
529 /* Emit a signal that we've changed */
530 g_signal_emit(G_OBJECT(gdf), date_format_signals[FORMAT_CHANGED], 0);
531 }
532