]> Dogcows Code - chaz/homebank/blob - src/gtk-dateentry.c
import homebank-5.1.7
[chaz/homebank] / src / gtk-dateentry.c
1 /* HomeBank -- Free, easy, personal accounting for everyone.
2 * Copyright (C) 1995-2018 Maxime DOYEN
3 *
4 * This file is part of HomeBank.
5 *
6 * HomeBank is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * HomeBank 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, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <stdlib.h>
21 #include <string.h>
22 #include <time.h>
23
24 #include <gtk/gtk.h>
25 #include <gdk/gdkkeysyms.h>
26
27 #include "gtk-dateentry.h"
28
29 #define MYDEBUG 0
30
31 #if MYDEBUG
32 #define DB(x) (x);
33 #else
34 #define DB(x);
35 #endif
36
37
38 enum {
39 CHANGED,
40 LAST_SIGNAL
41 };
42
43
44 enum {
45 PROPERTY_DATE = 5,
46 };
47
48
49 static guint dateentry_signals[LAST_SIGNAL] = {0,};
50
51
52 G_DEFINE_TYPE(GtkDateEntry, gtk_date_entry, GTK_TYPE_BOX)
53
54
55
56
57 static void
58 update_text(GtkDateEntry *self)
59 {
60 GtkDateEntryPrivate *priv = self->priv;
61 gchar label[24];
62
63 DB( g_print("\n[dateentry] update text\n") );
64
65 g_date_strftime (label, 17 - 1, "%x", priv->date);
66 gtk_entry_set_text (GTK_ENTRY (priv->entry), label);
67 DB( g_print(" = %s\n", label) );
68 }
69
70
71 static void
72 eval_date(GtkDateEntry *self)
73 {
74 GtkDateEntryPrivate *priv = self->priv;
75
76 g_date_clamp(priv->date, &priv->mindate, &priv->maxdate);
77
78 update_text(self);
79
80 if(priv->lastdate != g_date_get_julian(priv->date))
81 {
82 DB( g_print(" **emit 'changed' signal**\n") );
83 g_signal_emit_by_name (self, "changed", NULL, NULL);
84 }
85
86 priv->lastdate = g_date_get_julian(priv->date);
87 }
88
89 static void
90 parse_date(GtkDateEntry *self)
91 {
92 GtkDateEntryPrivate *priv = self->priv;
93
94 DB( g_print("\n[dateentry] parse date\n") );
95
96 g_date_set_parse (priv->date, gtk_entry_get_text (GTK_ENTRY (priv->entry)));
97 if(!g_date_valid(priv->date))
98 {
99 g_date_set_time_t(priv->date, time(NULL));
100 }
101 eval_date(self);
102 }
103
104
105 static void
106 gtk_date_entry_cb_calendar_day_selected(GtkWidget * calendar, GtkDateEntry * dateentry)
107 {
108 GtkDateEntryPrivate *priv = dateentry->priv;
109 guint year, month, day;
110
111 DB( g_print("\n[dateentry] calendar_day_selected\n") );
112
113 gtk_calendar_get_date (GTK_CALENDAR (priv->calendar), &year, &month, &day);
114 g_date_set_dmy (priv->date, day, month + 1, year);
115 eval_date(dateentry);
116 }
117
118
119 static gint
120 gtk_date_entry_cb_calendar_day_select_double_click(GtkWidget * calendar, gpointer user_data)
121 {
122 GtkDateEntry *dateentry = user_data;
123 GtkDateEntryPrivate *priv = dateentry->priv;
124
125 DB( g_print("\n[dateentry] calendar_day_select_double_click\n") );
126
127 gtk_widget_hide (priv->popover);
128
129 return FALSE;
130 }
131
132
133 static void
134 gtk_date_entry_cb_calendar_monthyear(GtkWidget *calendar, GtkDateEntry *dateentry)
135 {
136 GtkDateEntryPrivate *priv = dateentry->priv;
137 guint year, month, day;
138
139 DB( g_print("\n[dateentry] cb_calendar_monthyear\n") );
140
141 gtk_calendar_get_date (GTK_CALENDAR (priv->calendar), &year, &month, &day);
142 if( year < 1900)
143 g_object_set(calendar, "year", 1900, NULL);
144
145 if( year > 2200)
146 g_object_set(calendar, "year", 2200, NULL);
147
148 }
149
150
151 static gint
152 gtk_date_entry_cb_entry_key_pressed (GtkWidget *widget, GdkEventKey *event, gpointer user_data)
153 {
154 GtkDateEntry *dateentry = user_data;
155 GtkDateEntryPrivate *priv = dateentry->priv;
156
157 DB( g_print("\n[dateentry] entry key pressed: state=%04x, keyval=%04x\n", event->state, event->keyval) );
158
159 if( event->keyval == GDK_KEY_Up )
160 {
161 if( !(event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK)) )
162 {
163 g_date_add_days (priv->date, 1);
164 eval_date(dateentry);
165 }
166 else
167 if( event->state & GDK_SHIFT_MASK )
168 {
169 g_date_add_months (priv->date, 1);
170 eval_date(dateentry);
171 }
172 else
173 if( event->state & GDK_CONTROL_MASK )
174 {
175 g_date_add_years (priv->date, 1);
176 eval_date(dateentry);
177 }
178 return TRUE;
179 }
180 else
181 if( event->keyval == GDK_KEY_Down )
182 {
183 if( !(event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK)) )
184 {
185 g_date_subtract_days (priv->date, 1);
186 eval_date(dateentry);
187 }
188 else
189 if( event->state & GDK_SHIFT_MASK )
190 {
191 g_date_subtract_months (priv->date, 1);
192 eval_date(dateentry);
193 }
194 else
195 if( event->state & GDK_CONTROL_MASK )
196 {
197 g_date_subtract_years (priv->date, 1);
198 eval_date(dateentry);
199 }
200 return TRUE;
201 }
202
203 return FALSE;
204 }
205
206
207 static void
208 gtk_date_entry_cb_entry_activate(GtkWidget *gtkentry, gpointer user_data)
209 {
210 GtkDateEntry *dateentry = user_data;
211
212 DB( g_print("\n[dateentry] entry_activate\n") );
213
214 parse_date(dateentry);
215 eval_date(dateentry);
216 }
217
218
219 static gboolean
220 gtk_date_entry_cb_entry_focus_out(GtkWidget *widget, GdkEventFocus *event, gpointer user_data)
221 {
222 GtkDateEntry *dateentry = user_data;
223
224 DB( g_print("\n[dateentry] entry focus-out-event %d\n", gtk_widget_is_focus(GTK_WIDGET(dateentry))) );
225
226 parse_date(dateentry);
227 eval_date(dateentry);
228 return FALSE;
229 }
230
231
232 static void
233 gtk_date_entry_cb_button_clicked (GtkWidget * widget, GtkDateEntry * dateentry)
234 {
235 GtkDateEntryPrivate *priv = dateentry->priv;
236 //GdkRectangle rect;
237 int month;
238
239 DB( g_print("\n[dateentry] button_clicked\n") );
240
241 /* GtkCalendar expects month to be in 0-11 range (inclusive) */
242 month = g_date_get_month (priv->date) - 1;
243
244 g_signal_handler_block(priv->calendar, priv->hid_dayselect);
245
246 gtk_calendar_select_month (GTK_CALENDAR (priv->calendar),
247 CLAMP (month, 0, 11),
248 g_date_get_year (priv->date));
249 gtk_calendar_select_day (GTK_CALENDAR (priv->calendar),
250 g_date_get_day (priv->date));
251
252 g_signal_handler_unblock(priv->calendar, priv->hid_dayselect);
253
254 gtk_popover_set_relative_to (GTK_POPOVER (priv->popover), GTK_WIDGET (priv->entry));
255 //gtk_widget_get_clip(priv->arrow, &rect);
256 //gtk_popover_set_pointing_to (GTK_POPOVER (priv->popover), &rect);
257
258 gtk_widget_show_all (priv->popover);
259 }
260
261
262 static void
263 gtk_date_entry_destroy (GtkWidget *object)
264 {
265 GtkDateEntry *dateentry = GTK_DATE_ENTRY (object);
266 GtkDateEntryPrivate *priv = dateentry->priv;
267
268 g_return_if_fail(object != NULL);
269 g_return_if_fail(GTK_IS_DATE_ENTRY(object));
270
271 DB( g_print("\n[dateentry] destroy\n") );
272
273 DB( g_print(" free gtkentry: %p\n", priv->entry) );
274 DB( g_print(" free arrow: %p\n", priv->button) );
275
276 DB( g_print(" free dateentry: %p\n", dateentry) );
277
278 if(priv->date)
279 g_date_free(priv->date);
280 priv->date = NULL;
281
282 GTK_WIDGET_CLASS (gtk_date_entry_parent_class)->destroy (object);
283 }
284
285
286
287 static void
288 gtk_date_entry_dispose (GObject *gobject)
289 {
290 //GtkDateEntry *self = GTK_DATE_ENTRY (gobject);
291
292 DB( g_print("\n[dateentry] dispose\n") );
293
294
295 //g_clear_object (&self->priv->an_object);
296
297 G_OBJECT_CLASS (gtk_date_entry_parent_class)->dispose (gobject);
298 }
299
300
301
302
303 static void
304 gtk_date_entry_finalize (GObject *gobject)
305 {
306 //GtkDateEntry *self = GTK_DATE_ENTRY (gobject);
307
308 DB( g_print("\n[dateentry] finalize\n") );
309
310
311 //g_date_free(self->date);
312 //g_free (self->priv->a_string);
313
314 /* Always chain up to the parent class; as with dispose(), finalize()
315 * is guaranteed to exist on the parent's class virtual function table
316 */
317 G_OBJECT_CLASS(gtk_date_entry_parent_class)->finalize (gobject);
318 }
319
320
321
322 static void
323 gtk_date_entry_class_init (GtkDateEntryClass *class)
324 {
325 GObjectClass *object_class;
326 GtkWidgetClass *widget_class;
327
328 object_class = G_OBJECT_CLASS (class);
329 widget_class = GTK_WIDGET_CLASS (class);
330
331 DB( g_print("\n[dateentry] class_init\n") );
332
333 //object_class->constructor = gtk_date_entry_constructor;
334 //object_class->set_property = gtk_date_entry_set_property;
335 //object_class->get_property = gtk_date_entry_get_property;
336 object_class->dispose = gtk_date_entry_dispose;
337 object_class->finalize = gtk_date_entry_finalize;
338
339 widget_class->destroy = gtk_date_entry_destroy;
340
341 dateentry_signals[CHANGED] =
342 g_signal_new ("changed",
343 G_TYPE_FROM_CLASS (class),
344 G_SIGNAL_RUN_LAST,
345 G_STRUCT_OFFSET (GtkDateEntryClass, changed),
346 NULL, NULL,
347 g_cclosure_marshal_VOID__VOID,
348 G_TYPE_NONE, 0);
349
350 g_type_class_add_private (object_class, sizeof (GtkDateEntryPrivate));
351
352 }
353
354 static void
355 gtk_date_entry_init (GtkDateEntry *dateentry)
356 {
357 GtkDateEntryPrivate *priv;
358
359 DB( g_print("\n[dateentry] init\n") );
360
361 /* yes, also priv, need to keep the code readable */
362 dateentry->priv = G_TYPE_INSTANCE_GET_PRIVATE (dateentry,
363 GTK_TYPE_DATE_ENTRY,
364 GtkDateEntryPrivate);
365 priv = dateentry->priv;
366
367 gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET(dateentry)), GTK_STYLE_CLASS_LINKED);
368
369 priv->entry = gtk_entry_new ();
370 gtk_entry_set_width_chars(GTK_ENTRY(priv->entry), 16);
371 gtk_entry_set_max_width_chars(GTK_ENTRY(priv->entry), 16);
372 gtk_box_pack_start (GTK_BOX (dateentry), priv->entry, TRUE, TRUE, 0);
373
374 priv->button = gtk_button_new ();
375 priv->arrow = gtk_image_new_from_icon_name ("pan-down-symbolic", GTK_ICON_SIZE_BUTTON);
376 gtk_container_add (GTK_CONTAINER (priv->button), priv->arrow);
377 gtk_box_pack_end (GTK_BOX (dateentry), priv->button, FALSE, FALSE, 0);
378
379 priv->popover = gtk_popover_new (priv->button);
380 gtk_popover_set_position(GTK_POPOVER(priv->popover), GTK_POS_BOTTOM);
381 gtk_container_set_border_width (GTK_CONTAINER (priv->popover), 6);
382 priv->calendar = gtk_calendar_new ();
383 gtk_container_add (GTK_CONTAINER (priv->popover), priv->calendar);
384
385 gtk_widget_show_all (GTK_WIDGET(dateentry));
386
387 /* initialize datas */
388 priv->date = g_date_new();
389 g_date_set_time_t(priv->date, time(NULL));
390 g_date_set_dmy(&priv->mindate, 1, 1, 1900); //693596
391 g_date_set_dmy(&priv->maxdate, 31, 12, 2200); //803533
392 update_text(dateentry);
393
394
395 g_signal_connect (priv->entry, "key-press-event",
396 G_CALLBACK (gtk_date_entry_cb_entry_key_pressed), dateentry);
397
398 g_signal_connect_after (priv->entry, "focus-out-event",
399 G_CALLBACK (gtk_date_entry_cb_entry_focus_out), dateentry);
400
401 g_signal_connect (priv->entry, "activate",
402 G_CALLBACK (gtk_date_entry_cb_entry_activate), dateentry);
403
404
405 g_signal_connect (priv->button, "clicked",
406 G_CALLBACK (gtk_date_entry_cb_button_clicked), dateentry);
407
408
409 g_signal_connect (priv->calendar, "prev-year",
410 G_CALLBACK (gtk_date_entry_cb_calendar_monthyear), dateentry);
411 g_signal_connect (priv->calendar, "next-year",
412 G_CALLBACK (gtk_date_entry_cb_calendar_monthyear), dateentry);
413 g_signal_connect (priv->calendar, "prev-month",
414 G_CALLBACK (gtk_date_entry_cb_calendar_monthyear), dateentry);
415 g_signal_connect (priv->calendar, "next-month",
416 G_CALLBACK (gtk_date_entry_cb_calendar_monthyear), dateentry);
417
418 priv->hid_dayselect = g_signal_connect (priv->calendar, "day-selected",
419 G_CALLBACK (gtk_date_entry_cb_calendar_day_selected), dateentry);
420
421 g_signal_connect (priv->calendar, "day-selected-double-click",
422 G_CALLBACK (gtk_date_entry_cb_calendar_day_select_double_click), dateentry);
423
424 }
425
426
427 GtkWidget *
428 gtk_date_entry_new ()
429 {
430 GtkDateEntry *dateentry;
431
432 DB( g_print("\n[dateentry] new\n") );
433
434 dateentry = g_object_new (GTK_TYPE_DATE_ENTRY, NULL);
435
436 return GTK_WIDGET(dateentry);
437 }
438
439
440 /*
441 **
442 */
443 void
444 gtk_date_entry_set_mindate(GtkDateEntry *dateentry, guint32 julian_days)
445 {
446 GtkDateEntryPrivate *priv = dateentry->priv;
447
448 DB( g_print("\n[dateentry] set mindate\n") );
449
450 g_return_if_fail (GTK_IS_DATE_ENTRY (dateentry));
451
452 if(g_date_valid_julian(julian_days))
453 {
454 g_date_set_julian (&priv->mindate, julian_days);
455 }
456 }
457
458
459 /*
460 **
461 */
462 void
463 gtk_date_entry_set_maxdate(GtkDateEntry *dateentry, guint32 julian_days)
464 {
465 GtkDateEntryPrivate *priv = dateentry->priv;
466
467 DB( g_print("\n[dateentry] set maxdate\n") );
468
469 g_return_if_fail (GTK_IS_DATE_ENTRY (dateentry));
470
471 if(g_date_valid_julian(julian_days))
472 {
473 g_date_set_julian (&priv->maxdate, julian_days);
474 }
475 }
476
477
478 /*
479 **
480 */
481 void
482 gtk_date_entry_set_date(GtkDateEntry *dateentry, guint32 julian_days)
483 {
484 GtkDateEntryPrivate *priv = dateentry->priv;
485
486 DB( g_print("\n[dateentry] set date\n") );
487
488 g_return_if_fail (GTK_IS_DATE_ENTRY (dateentry));
489
490 if(g_date_valid_julian(julian_days))
491 {
492 g_date_set_julian (priv->date, julian_days);
493 }
494 else
495 {
496 g_date_set_time_t(priv->date, time(NULL));
497 }
498 eval_date(dateentry);
499 }
500
501
502 /*
503 **
504 */
505 guint32
506 gtk_date_entry_get_date(GtkDateEntry *dateentry)
507 {
508 GtkDateEntryPrivate *priv = dateentry->priv;
509
510 DB( g_print("\n[dateentry] get date\n") );
511
512 g_return_val_if_fail (GTK_IS_DATE_ENTRY (dateentry), 0);
513
514 return(g_date_get_julian(priv->date));
515 }
516
This page took 0.054182 seconds and 5 git commands to generate.