1 /* 2 * Copyright (C) 2009 - 2012 Vivien Malerba <malerba@gnome-db.org> 3 * Copyright (C) 2010 David King <davidk@openismus.com> 4 * Copyright (C) 2011 Murray Cumming <murrayc@murrayc.com> 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2 of the License, or (at your option) any later version. 10 * 11 * This library 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 GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, write to the 18 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 19 * Boston, MA 02110-1301, USA. 20 */ 21 22 #include <glib/gi18n-lib.h> 23 #include "gdaui-entry-common-time.h" 24 #include <libgda/gda-data-handler.h> 25 #include <gdk/gdkkeysyms.h> 26 #include <string.h> 27 #include "gdaui-formatted-entry.h" 28 #include <libgda/gda-debug-macros.h> 29 30 /* 31 * Main static functions 32 */ 33 static void gdaui_entry_common_time_class_init (GdauiEntryCommonTimeClass * class); 34 static void gdaui_entry_common_time_init (GdauiEntryCommonTime * srv); 35 static void gdaui_entry_common_time_dispose (GObject *object); 36 static void gdaui_entry_common_time_finalize (GObject *object); 37 38 static void gdaui_entry_common_time_set_property (GObject *object, 39 guint param_id, 40 const GValue *value, 41 GParamSpec *pspec); 42 static void gdaui_entry_common_time_get_property (GObject *object, 43 guint param_id, 44 GValue *value, 45 GParamSpec *pspec); 46 47 /* properties */ 48 enum 49 { 50 PROP_0, 51 PROP_EDITING_CANCELED, 52 PROP_TYPE 53 }; 54 55 /* GtkCellEditable interface */ 56 static void gdaui_entry_common_time_cell_editable_init (GtkCellEditableIface *iface); 57 static void gdaui_entry_common_time_start_editing (GtkCellEditable *iface, GdkEvent *event); 58 59 /* virtual functions */ 60 static GtkWidget *create_entry (GdauiEntryWrapper *mgwrap); 61 static void real_set_value (GdauiEntryWrapper *mgwrap, const GValue *value); 62 static GValue *real_get_value (GdauiEntryWrapper *mgwrap); 63 static void connect_signals(GdauiEntryWrapper *mgwrap, GCallback modify_cb, GCallback activate_cb); 64 static void set_editable (GdauiEntryWrapper *mgwrap, gboolean editable); 65 static void grab_focus (GdauiEntryWrapper *mgwrap); 66 67 /* get a pointer to the parents to be able to call their destructor */ 68 static GObjectClass *parent_class = NULL; 69 70 71 /* private structure */ 72 struct _GdauiEntryCommonTimePrivate 73 { 74 /* for date */ 75 GtkWidget *entry_date; 76 GtkWidget *date; 77 GtkWidget *window; 78 GtkWidget *date_button; 79 gboolean editing_canceled; 80 81 /* for time */ 82 GtkWidget *entry_time; 83 84 /* for timestamp */ 85 GtkWidget *hbox; 86 87 /* Last value set */ 88 GValue *last_value_set; 89 }; 90 91 static void 92 gdaui_entry_common_time_cell_editable_init (GtkCellEditableIface *iface) 93 { 94 iface->start_editing = gdaui_entry_common_time_start_editing; 95 } 96 97 GType 98 gdaui_entry_common_time_get_type (void) 99 { 100 static GType type = 0; 101 102 if (G_UNLIKELY (type == 0)) { 103 static const GTypeInfo info = { 104 sizeof (GdauiEntryCommonTimeClass), 105 (GBaseInitFunc) NULL, 106 (GBaseFinalizeFunc) NULL, 107 (GClassInitFunc) gdaui_entry_common_time_class_init, 108 NULL, 109 NULL, 110 sizeof (GdauiEntryCommonTime), 111 0, 112 (GInstanceInitFunc) gdaui_entry_common_time_init, 113 0 114 }; 115 116 static const GInterfaceInfo cell_editable_info = { 117 (GInterfaceInitFunc) gdaui_entry_common_time_cell_editable_init, /* interface_init */ 118 NULL, /* interface_finalize */ 119 NULL /* interface_data */ 120 }; 121 122 type = g_type_register_static (GDAUI_TYPE_ENTRY_WRAPPER, "GdauiEntryCommonTime", &info, 0); 123 g_type_add_interface_static (type, GTK_TYPE_CELL_EDITABLE, &cell_editable_info); 124 } 125 return type; 126 } 127 128 static void 129 gdaui_entry_common_time_class_init (GdauiEntryCommonTimeClass * class) 130 { 131 GObjectClass *object_class = G_OBJECT_CLASS (class); 132 133 parent_class = g_type_class_peek_parent (class); 134 135 object_class->dispose = gdaui_entry_common_time_dispose; 136 object_class->finalize = gdaui_entry_common_time_finalize; 137 138 GDAUI_ENTRY_WRAPPER_CLASS (class)->create_entry = create_entry; 139 GDAUI_ENTRY_WRAPPER_CLASS (class)->real_set_value = real_set_value; 140 GDAUI_ENTRY_WRAPPER_CLASS (class)->real_get_value = real_get_value; 141 GDAUI_ENTRY_WRAPPER_CLASS (class)->connect_signals = connect_signals; 142 GDAUI_ENTRY_WRAPPER_CLASS (class)->set_editable = set_editable; 143 GDAUI_ENTRY_WRAPPER_CLASS (class)->grab_focus = grab_focus; 144 145 /* Properties */ 146 object_class->set_property = gdaui_entry_common_time_set_property; 147 object_class->get_property = gdaui_entry_common_time_get_property; 148 149 g_object_class_install_property (object_class, PROP_EDITING_CANCELED, 150 g_param_spec_boolean ("editing-canceled", NULL, NULL, FALSE, 151 G_PARAM_READABLE | G_PARAM_WRITABLE)); 152 g_object_class_install_property (object_class, PROP_TYPE, 153 g_param_spec_uint ("type", NULL, NULL, 0, G_MAXUINT, GDA_TYPE_TIME, 154 G_PARAM_WRITABLE | G_PARAM_READABLE)); 155 } 156 157 static gboolean 158 key_press_event_cb (GdauiEntryCommonTime *mgtim, GdkEventKey *key_event, G_GNUC_UNUSED gpointer data) 159 { 160 if (key_event->keyval == GDK_KEY_Escape) 161 mgtim->priv->editing_canceled = TRUE; 162 return FALSE; 163 } 164 165 static void 166 gdaui_entry_common_time_init (GdauiEntryCommonTime *gdaui_entry_common_time) 167 { 168 gdaui_entry_common_time->priv = g_new0 (GdauiEntryCommonTimePrivate, 1); 169 gdaui_entry_common_time->priv->entry_date = NULL; 170 gdaui_entry_common_time->priv->entry_time = NULL; 171 gdaui_entry_common_time->priv->date = NULL; 172 gdaui_entry_common_time->priv->window = NULL; 173 gdaui_entry_common_time->priv->date_button = NULL; 174 gdaui_entry_common_time->priv->hbox = NULL; 175 gdaui_entry_common_time->priv->last_value_set = NULL; 176 gdaui_entry_common_time->priv->editing_canceled = FALSE; 177 g_signal_connect (gdaui_entry_common_time, "key-press-event", 178 G_CALLBACK (key_press_event_cb), NULL); 179 } 180 181 /** 182 * gdaui_entry_common_time_new: 183 * @dh: the data handler to be used by the new widget 184 * @type: the requested data type (compatible with @dh) 185 * 186 * Creates a new data entry widget 187 * 188 * Returns: (transfer full): the new widget 189 */ 190 GtkWidget * 191 gdaui_entry_common_time_new (GdaDataHandler *dh, GType type) 192 { 193 GObject *obj; 194 GdauiEntryCommonTime *mgtim; 195 196 g_return_val_if_fail (dh && GDA_IS_DATA_HANDLER (dh), NULL); 197 g_return_val_if_fail (gda_data_handler_accepts_g_type (dh, type), NULL); 198 199 obj = g_object_new (GDAUI_TYPE_ENTRY_COMMON_TIME, "handler", dh, NULL); 200 mgtim = GDAUI_ENTRY_COMMON_TIME (obj); 201 gdaui_data_entry_set_value_type (GDAUI_DATA_ENTRY (mgtim), type); 202 203 return GTK_WIDGET (obj); 204 } 205 206 207 static void 208 gdaui_entry_common_time_dispose (GObject * object) 209 { 210 GdauiEntryCommonTime *gdaui_entry_common_time; 211 212 g_return_if_fail (object != NULL); 213 g_return_if_fail (GDAUI_IS_ENTRY_COMMON_TIME (object)); 214 215 gdaui_entry_common_time = GDAUI_ENTRY_COMMON_TIME (object); 216 if (gdaui_entry_common_time->priv) { 217 if (gdaui_entry_common_time->priv->window) { 218 gtk_widget_destroy (gdaui_entry_common_time->priv->window); 219 gdaui_entry_common_time->priv->window = NULL; 220 } 221 } 222 223 /* parent class */ 224 parent_class->dispose (object); 225 } 226 227 static void 228 gdaui_entry_common_time_finalize (GObject * object) 229 { 230 GdauiEntryCommonTime *gdaui_entry_common_time; 231 232 g_return_if_fail (object != NULL); 233 g_return_if_fail (GDAUI_IS_ENTRY_COMMON_TIME (object)); 234 235 gdaui_entry_common_time = GDAUI_ENTRY_COMMON_TIME (object); 236 if (gdaui_entry_common_time->priv) { 237 if (gdaui_entry_common_time->priv->last_value_set) 238 gda_value_free (gdaui_entry_common_time->priv->last_value_set); 239 240 g_free (gdaui_entry_common_time->priv); 241 gdaui_entry_common_time->priv = NULL; 242 } 243 244 /* parent class */ 245 parent_class->finalize (object); 246 } 247 248 static void 249 gdaui_entry_common_time_set_property (GObject *object, 250 guint param_id, 251 const GValue *value, 252 GParamSpec *pspec) 253 { 254 GdauiEntryCommonTime *mgtim; 255 256 mgtim = GDAUI_ENTRY_COMMON_TIME (object); 257 if (mgtim->priv) { 258 switch (param_id) { 259 case PROP_TYPE: 260 gdaui_data_entry_set_value_type (GDAUI_DATA_ENTRY (object), g_value_get_uint (value)); 261 break; 262 case PROP_EDITING_CANCELED: 263 TO_IMPLEMENT; 264 break; 265 default: 266 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); 267 break; 268 } 269 } 270 } 271 272 static void 273 gdaui_entry_common_time_get_property (GObject *object, 274 guint param_id, 275 GValue *value, 276 GParamSpec *pspec) 277 { 278 GdauiEntryCommonTime *mgtim; 279 280 mgtim = GDAUI_ENTRY_COMMON_TIME (object); 281 if (mgtim->priv) { 282 switch (param_id) { 283 case PROP_EDITING_CANCELED: 284 g_value_set_boolean (value, mgtim->priv->editing_canceled); 285 break; 286 case PROP_TYPE: 287 g_value_set_uint (value, gdaui_data_entry_get_value_type (GDAUI_DATA_ENTRY (object))); 288 break; 289 default: 290 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); 291 break; 292 } 293 } 294 } 295 296 static GtkWidget *create_entry_date (GdauiEntryCommonTime *mgtim); 297 static GtkWidget *create_entry_time (GdauiEntryCommonTime *mgtim); 298 static GtkWidget *create_entry_ts (GdauiEntryCommonTime *mgtim); 299 static GtkWidget * 300 create_entry (GdauiEntryWrapper *mgwrap) 301 { 302 GdauiEntryCommonTime *mgtim; 303 GtkWidget *entry = NULL; 304 GType type; 305 306 g_return_val_if_fail (mgwrap && GDAUI_IS_ENTRY_COMMON_TIME (mgwrap), NULL); 307 mgtim = GDAUI_ENTRY_COMMON_TIME (mgwrap); 308 g_return_val_if_fail (mgtim->priv, NULL); 309 310 type = gdaui_data_entry_get_value_type (GDAUI_DATA_ENTRY (mgtim)); 311 if (type == G_TYPE_DATE) 312 entry = create_entry_date (mgtim); 313 else if (type == GDA_TYPE_TIME) 314 entry = create_entry_time (mgtim); 315 else if (type == GDA_TYPE_TIMESTAMP) 316 entry = create_entry_ts (mgtim); 317 else 318 g_assert_not_reached (); 319 320 return entry; 321 } 322 323 static void 324 real_set_value (GdauiEntryWrapper *mgwrap, const GValue *value) 325 { 326 GdauiEntryCommonTime *mgtim; 327 GType type; 328 GdaDataHandler *dh; 329 330 g_return_if_fail (mgwrap && GDAUI_IS_ENTRY_COMMON_TIME (mgwrap)); 331 mgtim = GDAUI_ENTRY_COMMON_TIME (mgwrap); 332 g_return_if_fail (mgtim->priv); 333 334 dh = gdaui_data_entry_get_handler (GDAUI_DATA_ENTRY (mgwrap)); 335 type = gdaui_data_entry_get_value_type (GDAUI_DATA_ENTRY (mgtim)); 336 337 if (type == G_TYPE_DATE) { 338 if (value) { 339 if (gda_value_is_null ((GValue *) value)) 340 gdaui_entry_set_text (GDAUI_ENTRY (mgtim->priv->entry_date), NULL); 341 else { 342 gchar *str; 343 344 str = gda_data_handler_get_str_from_value (dh, value); 345 gdaui_entry_set_text (GDAUI_ENTRY (mgtim->priv->entry_date), str); 346 g_free (str); 347 } 348 } 349 else 350 gdaui_entry_set_text (GDAUI_ENTRY (mgtim->priv->entry_date), NULL); 351 } 352 else if (type == GDA_TYPE_TIME) { 353 if (value) { 354 if (gda_value_is_null ((GValue *) value)) 355 gdaui_entry_set_text (GDAUI_ENTRY (mgtim->priv->entry_time), NULL); 356 else { 357 gchar *str; 358 359 str = gda_data_handler_get_str_from_value (dh, value); 360 gdaui_entry_set_text (GDAUI_ENTRY (mgtim->priv->entry_time), str); 361 g_free (str); 362 } 363 } 364 else 365 gdaui_entry_set_text (GDAUI_ENTRY (mgtim->priv->entry_time), NULL); 366 } 367 else if (type == GDA_TYPE_TIMESTAMP) { 368 if (value) { 369 if (gda_value_is_null ((GValue *) value)) { 370 gdaui_entry_set_text (GDAUI_ENTRY (mgtim->priv->entry_date), NULL); 371 gdaui_entry_set_text (GDAUI_ENTRY (mgtim->priv->entry_time), NULL); 372 } 373 else { 374 gchar *str, *ptr; 375 376 str = gda_data_handler_get_str_from_value (dh, value); 377 ptr = strtok (str, " "); 378 gdaui_entry_set_text (GDAUI_ENTRY (mgtim->priv->entry_date), ptr); 379 ptr = strtok (NULL, " "); 380 gdaui_entry_set_text (GDAUI_ENTRY (mgtim->priv->entry_time), ptr); 381 g_free (str); 382 } 383 } 384 else { 385 gdaui_entry_set_text (GDAUI_ENTRY (mgtim->priv->entry_date), NULL); 386 gdaui_entry_set_text (GDAUI_ENTRY (mgtim->priv->entry_time), NULL); 387 } 388 } 389 else 390 g_assert_not_reached (); 391 392 /* keep track of the last value set */ 393 if (mgtim->priv->last_value_set) { 394 gda_value_free (mgtim->priv->last_value_set); 395 mgtim->priv->last_value_set = NULL; 396 } 397 if (value) 398 mgtim->priv->last_value_set = gda_value_copy ((GValue *) value); 399 } 400 401 static GValue * 402 real_get_value (GdauiEntryWrapper *mgwrap) 403 { 404 GValue *value = NULL; 405 GdauiEntryCommonTime *mgtim; 406 GdaDataHandler *dh; 407 gchar *str2; 408 GType type; 409 410 g_return_val_if_fail (mgwrap && GDAUI_IS_ENTRY_COMMON_TIME (mgwrap), NULL); 411 mgtim = GDAUI_ENTRY_COMMON_TIME (mgwrap); 412 g_return_val_if_fail (mgtim->priv, NULL); 413 414 type = gdaui_data_entry_get_value_type (GDAUI_DATA_ENTRY (mgtim)); 415 dh = gdaui_data_entry_get_handler (GDAUI_DATA_ENTRY (mgwrap)); 416 417 if (type == G_TYPE_DATE) { 418 str2 = gdaui_formatted_entry_get_text (GDAUI_FORMATTED_ENTRY (mgtim->priv->entry_date)); 419 if (str2) { 420 value = gda_data_handler_get_value_from_str (dh, str2, type); 421 g_free (str2); 422 } 423 } 424 else if (type == GDA_TYPE_TIME) { 425 str2 = gdaui_formatted_entry_get_text (GDAUI_FORMATTED_ENTRY (mgtim->priv->entry_time)); 426 if (str2) { 427 value = gda_data_handler_get_value_from_str (dh, str2, type); 428 g_free (str2); 429 } 430 431 if (value && (G_VALUE_TYPE (value) != GDA_TYPE_NULL) && 432 mgtim->priv->last_value_set && 433 gda_value_isa (mgtim->priv->last_value_set, GDA_TYPE_TIME)) { 434 /* copy the 'timezone' part, we we have not modified */ 435 const GdaTime *dgatime_set = gda_value_get_time (mgtim->priv->last_value_set); 436 GdaTime *gdatime = g_new (GdaTime, 1); 437 *gdatime = *(gda_value_get_time (value)); 438 gdatime->timezone = dgatime_set->timezone; 439 gda_value_set_time (value, gdatime); 440 g_free (gdatime); 441 } 442 } 443 else if (type == GDA_TYPE_TIMESTAMP) { 444 gchar *tmpstr, *tmpstr2; 445 446 tmpstr = gdaui_formatted_entry_get_text (GDAUI_FORMATTED_ENTRY (mgtim->priv->entry_time)); 447 tmpstr2 = gdaui_formatted_entry_get_text (GDAUI_FORMATTED_ENTRY (mgtim->priv->entry_date)); 448 if (tmpstr && tmpstr2) { 449 str2 = g_strdup_printf ("%s %s", tmpstr2, tmpstr); 450 value = gda_data_handler_get_value_from_str (dh, str2, type); 451 g_free (str2); 452 } 453 g_free (tmpstr); 454 g_free (tmpstr2); 455 if (value && (G_VALUE_TYPE (value) != GDA_TYPE_NULL) && 456 mgtim->priv->last_value_set && 457 gda_value_isa (mgtim->priv->last_value_set, GDA_TYPE_TIMESTAMP)) { 458 /* copy the 'fraction' and 'timezone' parts, we have not modified */ 459 const GdaTimestamp *dgatime_set = gda_value_get_timestamp (mgtim->priv->last_value_set); 460 GdaTimestamp *gdatime = g_new (GdaTimestamp, 1); 461 *gdatime = *(gda_value_get_timestamp (value)); 462 gdatime->fraction = dgatime_set->fraction; 463 gdatime->timezone = dgatime_set->timezone; 464 gda_value_set_timestamp (value, gdatime); 465 g_free (gdatime); 466 } 467 } 468 else 469 g_assert_not_reached (); 470 471 if (!value) { 472 /* in case the gda_data_handler_get_value_from_str() returned an error because 473 the contents of the GtkEntry cannot be interpreted as a GValue */ 474 value = gda_value_new_null (); 475 } 476 477 return value; 478 } 479 480 static void 481 connect_signals (GdauiEntryWrapper *mgwrap, GCallback modify_cb, GCallback activate_cb) 482 { 483 GdauiEntryCommonTime *mgtim; 484 GType type; 485 486 g_return_if_fail (mgwrap && GDAUI_IS_ENTRY_COMMON_TIME (mgwrap)); 487 mgtim = GDAUI_ENTRY_COMMON_TIME (mgwrap); 488 g_return_if_fail (mgtim->priv); 489 490 type = gdaui_data_entry_get_value_type (GDAUI_DATA_ENTRY (mgtim)); 491 492 if ((type == G_TYPE_DATE) || (type == GDA_TYPE_TIMESTAMP)) { 493 g_signal_connect (G_OBJECT (mgtim->priv->entry_date), "changed", 494 modify_cb, mgwrap); 495 g_signal_connect (G_OBJECT (mgtim->priv->entry_date), "activate", 496 activate_cb, mgwrap); 497 } 498 499 if ((type == GDA_TYPE_TIME) || (type == GDA_TYPE_TIMESTAMP)) { 500 g_signal_connect (G_OBJECT (mgtim->priv->entry_time), "changed", 501 modify_cb, mgwrap); 502 g_signal_connect (G_OBJECT (mgtim->priv->entry_time), "activate", 503 activate_cb, mgwrap); 504 } 505 } 506 507 static void 508 set_editable (GdauiEntryWrapper *mgwrap, gboolean editable) 509 { 510 GdauiEntryCommonTime *mgtim; 511 512 g_return_if_fail (mgwrap && GDAUI_IS_ENTRY_COMMON_TIME (mgwrap)); 513 mgtim = GDAUI_ENTRY_COMMON_TIME (mgwrap); 514 g_return_if_fail (mgtim->priv); 515 516 if (mgtim->priv->entry_date) 517 gtk_editable_set_editable (GTK_EDITABLE (mgtim->priv->entry_date), editable); 518 if (mgtim->priv->entry_time) 519 gtk_editable_set_editable (GTK_EDITABLE (mgtim->priv->entry_time), editable); 520 if (mgtim->priv->date_button) 521 gtk_widget_set_sensitive (mgtim->priv->date_button, editable); 522 } 523 524 static void 525 grab_focus (GdauiEntryWrapper *mgwrap) 526 { 527 GdauiEntryCommonTime *mgtim; 528 529 g_return_if_fail (mgwrap && GDAUI_IS_ENTRY_COMMON_TIME (mgwrap)); 530 mgtim = GDAUI_ENTRY_COMMON_TIME (mgwrap); 531 g_return_if_fail (mgtim->priv); 532 533 if (mgtim->priv->entry_date) 534 gtk_widget_grab_focus (mgtim->priv->entry_date); 535 if (mgtim->priv->entry_time) 536 gtk_widget_grab_focus (mgtim->priv->entry_time); 537 } 538 539 540 541 /* 542 * callbacks for the date 543 */ 544 static gint date_delete_popup (GtkWidget *widget, GdauiEntryCommonTime *mgtim); 545 static gint date_key_press_popup (GtkWidget *widget, GdkEventKey *event, GdauiEntryCommonTime *mgtim); 546 static gint date_button_press_popup (GtkWidget *widget, GdkEventButton *event, GdauiEntryCommonTime *mgtim); 547 static void date_day_selected (GtkCalendar *calendar, GdauiEntryCommonTime *mgtim); 548 static void date_day_selected_double_click (GtkCalendar *calendar, GdauiEntryCommonTime *mgtim); 549 static void date_calendar_choose_cb (GtkWidget *button, GdauiEntryCommonTime *mgtim); 550 551 static void entry_date_insert_func (GdauiFormattedEntry *fentry, gunichar insert_char, gint virt_pos, gpointer data); 552 553 static GtkWidget * 554 create_entry_date (GdauiEntryCommonTime *mgtim) 555 { 556 GtkWidget *wid, *hb, *window, *arrow; 557 GdaDataHandler *dh; 558 559 /* top widget */ 560 hb = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 3); 561 562 /* text entry */ 563 dh = gdaui_data_entry_get_handler (GDAUI_DATA_ENTRY (mgtim)); 564 if (GDA_IS_HANDLER_TIME (dh)) { 565 gchar *str, *mask, *ptr; 566 str = gda_handler_time_get_format (GDA_HANDLER_TIME (dh), G_TYPE_DATE); 567 mask = g_strdup (str); 568 for (ptr = mask; *ptr; ptr++) { 569 if (*ptr == '0') 570 *ptr = '-'; 571 } 572 wid = gdaui_formatted_entry_new (str, mask); 573 g_free (str); 574 g_free (mask); 575 576 gdaui_formatted_entry_set_insert_func (GDAUI_FORMATTED_ENTRY (wid), entry_date_insert_func, 577 mgtim); 578 } 579 else 580 wid = gdaui_entry_new (NULL, NULL); 581 gtk_box_pack_start (GTK_BOX (hb), wid, FALSE, FALSE, 0); 582 gtk_widget_show (wid); 583 mgtim->priv->entry_date = wid; 584 585 /* window to hold the calendar popup */ 586 window = gtk_window_new (GTK_WINDOW_POPUP); 587 gtk_widget_set_events (window, gtk_widget_get_events (window) | GDK_KEY_PRESS_MASK); 588 gtk_window_set_resizable (GTK_WINDOW (window), FALSE); 589 g_signal_connect (G_OBJECT (window), "delete-event", 590 G_CALLBACK (date_delete_popup), mgtim); 591 g_signal_connect (G_OBJECT (window), "key-press-event", 592 G_CALLBACK (date_key_press_popup), mgtim); 593 g_signal_connect (G_OBJECT (window), "button-press-event", 594 G_CALLBACK (date_button_press_popup), mgtim); 595 mgtim->priv->window = window; 596 597 /* calendar */ 598 wid = gtk_calendar_new (); 599 mgtim->priv->date = wid; 600 gtk_container_add (GTK_CONTAINER (window), wid); 601 gtk_widget_show (wid); 602 g_signal_connect (G_OBJECT (wid), "day-selected", 603 G_CALLBACK (date_day_selected), mgtim); 604 g_signal_connect (G_OBJECT (wid), "day-selected-double-click", 605 G_CALLBACK (date_day_selected_double_click), mgtim); 606 607 /* button to pop up the calendar */ 608 wid = gtk_button_new (); 609 arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE); 610 gtk_container_add (GTK_CONTAINER (wid), arrow); 611 gtk_box_pack_start (GTK_BOX (hb), wid, FALSE, FALSE, 0); 612 gtk_widget_show_all (wid); 613 g_signal_connect (G_OBJECT (wid), "clicked", 614 G_CALLBACK (date_calendar_choose_cb), mgtim); 615 mgtim->priv->date_button = wid; 616 617 /* padding */ 618 wid = gtk_label_new (""); 619 gtk_box_pack_start (GTK_BOX (hb), wid, TRUE, TRUE, 0); 620 gtk_widget_show (wid); 621 622 return hb; 623 } 624 625 static void 626 entry_date_insert_func (G_GNUC_UNUSED GdauiFormattedEntry *fentry, gunichar insert_char, 627 G_GNUC_UNUSED gint virt_pos, gpointer data) 628 { 629 GValue *value; 630 GType type; 631 632 type = gdaui_data_entry_get_value_type (GDAUI_DATA_ENTRY (data)); 633 value = real_get_value (GDAUI_ENTRY_WRAPPER (data)); 634 if (!value) 635 return; 636 637 if (G_VALUE_TYPE (value) == GDA_TYPE_NULL) { 638 if (type == G_TYPE_DATE) { 639 /* set current date, whatever @insert_char is */ 640 GDate *ndate; 641 ndate = g_new0 (GDate, 1); 642 g_date_set_time_t (ndate, time (NULL)); 643 644 gda_value_reset_with_type (value, type); 645 g_value_take_boxed (value, ndate); 646 real_set_value (GDAUI_ENTRY_WRAPPER (data), value); 647 } 648 else if (type == GDA_TYPE_TIMESTAMP) { 649 GValue *tsvalue; 650 gchar *str; 651 GdauiEntryCommonTime *mgtim = GDAUI_ENTRY_COMMON_TIME (data); 652 str = gdaui_formatted_entry_get_text (GDAUI_FORMATTED_ENTRY (mgtim->priv->entry_time)); 653 tsvalue = gda_value_new_timestamp_from_timet (time (NULL)); 654 real_set_value (GDAUI_ENTRY_WRAPPER (data), tsvalue); 655 gda_value_free (tsvalue); 656 if (str && g_ascii_isdigit (*str)) 657 gdaui_entry_set_text (GDAUI_ENTRY (mgtim->priv->entry_time), str); 658 g_free (str); 659 } 660 } 661 else { 662 GDate *date = NULL; 663 if (type == G_TYPE_DATE) { 664 date = (GDate*) g_value_get_boxed (value); 665 } 666 else if (type == GDA_TYPE_TIMESTAMP) { 667 const GdaTimestamp *ts; 668 ts = gda_value_get_timestamp (value); 669 date = g_date_new_dmy (ts->day, ts->month, ts->year); 670 } 671 672 if (date) { 673 GDate *ndate; 674 gboolean changed = FALSE; 675 676 ndate = g_new (GDate, 1); 677 *ndate = *date; 678 if ((insert_char == g_utf8_get_char ("+")) || 679 (insert_char == g_utf8_get_char ("="))) { 680 g_date_add_days (ndate, 1); 681 changed = TRUE; 682 } 683 else if ((insert_char == g_utf8_get_char ("-")) || 684 (insert_char == g_utf8_get_char ("6"))) { 685 g_date_subtract_days (ndate, 1); 686 changed = TRUE; 687 } 688 689 if (changed) { 690 if (type == G_TYPE_DATE) { 691 g_value_take_boxed (value, ndate); 692 } 693 else if (type == GDA_TYPE_TIMESTAMP) { 694 GdaTimestamp *ts; 695 ts = (GdaTimestamp*) gda_timestamp_copy ((gpointer) gda_value_get_timestamp (value)); 696 ts->day = g_date_get_day (ndate); 697 ts->month = g_date_get_month (ndate); 698 ts->year = g_date_get_year (ndate); 699 g_date_free (date); 700 g_date_free (ndate); 701 gda_value_set_timestamp (value, ts); 702 gda_timestamp_free (ts); 703 } 704 real_set_value (GDAUI_ENTRY_WRAPPER (data), value); 705 } 706 } 707 } 708 gda_value_free (value); 709 } 710 711 static void 712 hide_popup (GdauiEntryCommonTime *mgtim) 713 { 714 gtk_widget_hide (mgtim->priv->window); 715 gtk_grab_remove (mgtim->priv->window); 716 } 717 718 static gint 719 date_delete_popup (G_GNUC_UNUSED GtkWidget *widget, GdauiEntryCommonTime *mgtim) 720 { 721 hide_popup (mgtim); 722 return TRUE; 723 } 724 725 static gint 726 date_key_press_popup (GtkWidget *widget, GdkEventKey *event, GdauiEntryCommonTime *mgtim) 727 { 728 if (event->keyval != GDK_KEY_Escape) 729 return FALSE; 730 731 g_signal_stop_emission_by_name (widget, "key-press-event"); 732 hide_popup (mgtim); 733 734 return TRUE; 735 } 736 737 static gint 738 date_button_press_popup (GtkWidget *widget, GdkEventButton *event, GdauiEntryCommonTime *mgtim) 739 { 740 GtkWidget *child; 741 742 child = gtk_get_event_widget ((GdkEvent *) event); 743 744 /* We don't ask for button press events on the grab widget, so 745 * if an event is reported directly to the grab widget, it must 746 * be on a window outside the application (and thus we remove 747 * the popup window). Otherwise, we check if the widget is a child 748 * of the grab widget, and only remove the popup window if it 749 * is not. 750 */ 751 if (child != widget) { 752 while (child) { 753 if (child == widget) 754 return FALSE; 755 child = gtk_widget_get_parent (child); 756 } 757 } 758 759 hide_popup (mgtim); 760 761 return TRUE; 762 } 763 764 static void 765 date_day_selected (GtkCalendar *calendar, GdauiEntryCommonTime *mgtim) 766 { 767 char buffer [256]; 768 guint year, month, day; 769 struct tm mtm = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 770 char *str_utf8; 771 772 gtk_calendar_get_date (calendar, &year, &month, &day); 773 774 mtm.tm_mday = day; 775 mtm.tm_mon = month; 776 if (year > 1900) 777 mtm.tm_year = year - 1900; 778 else 779 mtm.tm_year = year; 780 781 if (strftime (buffer, sizeof (buffer), "%x", &mtm) == 0) 782 strcpy (buffer, "???"); 783 buffer[sizeof(buffer)-1] = '\0'; 784 785 str_utf8 = g_locale_to_utf8 (buffer, -1, NULL, NULL, NULL); 786 gdaui_entry_set_text (GDAUI_ENTRY (mgtim->priv->entry_date), str_utf8); 787 g_free (str_utf8); 788 } 789 790 static void 791 date_day_selected_double_click (G_GNUC_UNUSED GtkCalendar *calendar, GdauiEntryCommonTime *mgtim) 792 { 793 hide_popup (mgtim); 794 } 795 796 797 static gboolean popup_grab_on_window (GtkWidget *widget, guint32 activate_time); 798 static void position_popup (GdauiEntryCommonTime *mgtim); 799 static void 800 date_calendar_choose_cb (GtkWidget *button, GdauiEntryCommonTime *mgtim) 801 { 802 GValue *value; 803 guint year=0, month=0, day=0; 804 gboolean unset = TRUE; 805 806 /* setting the calendar to the latest displayed date */ 807 value = gdaui_data_entry_get_value (GDAUI_DATA_ENTRY (mgtim)); 808 809 if (value && !gda_value_is_null (value)) { 810 const GDate *date; 811 const GdaTimestamp *ts; 812 GType type; 813 814 type = gdaui_data_entry_get_value_type (GDAUI_DATA_ENTRY (mgtim)); 815 if (type == G_TYPE_DATE) { 816 date = (GDate*) g_value_get_boxed (value); 817 if (date) { 818 month = g_date_get_month (date); 819 year = g_date_get_year (date); 820 day = g_date_get_day (date); 821 if ((month != G_DATE_BAD_MONTH) && 822 (day != G_DATE_BAD_DAY) && 823 (year != G_DATE_BAD_YEAR)) { 824 month -= 1; 825 unset = FALSE; 826 } 827 } 828 } 829 else if (type == GDA_TYPE_TIMESTAMP) { 830 ts = gda_value_get_timestamp (value); 831 if (ts) { 832 year = ts->year; 833 month = ts->month - 1; 834 day = ts->day; 835 unset = FALSE; 836 } 837 } 838 else 839 g_assert_not_reached (); 840 } 841 842 if (unset) { 843 time_t now; 844 struct tm *stm; 845 846 now = time (NULL); 847 #ifdef HAVE_LOCALTIME_R 848 struct tm tmpstm; 849 stm = localtime_r (&now, &tmpstm); 850 #elif HAVE_LOCALTIME_S 851 struct tm tmpstm; 852 g_assert (localtime_s (&tmpstm, &now) == 0); 853 stm = &tmpstm; 854 #else 855 stm = localtime (&now); 856 #endif 857 year = stm->tm_year + 1900; 858 month = stm->tm_mon; 859 day = stm->tm_mday; 860 } 861 862 gtk_calendar_select_month (GTK_CALENDAR (mgtim->priv->date), month, year); 863 gtk_calendar_select_day (GTK_CALENDAR (mgtim->priv->date), day); 864 865 /* popup window */ 866 /* Temporarily grab pointer and keyboard, copied from GnomeDateEdit */ 867 if (!popup_grab_on_window (button, gtk_get_current_event_time ())) 868 return; 869 870 position_popup (mgtim); 871 gtk_widget_show (mgtim->priv->window); 872 gtk_grab_add (mgtim->priv->window); 873 874 GdkScreen *screen; 875 gint swidth, sheight; 876 gint root_x, root_y; 877 gint wwidth, wheight; 878 gboolean do_move = FALSE; 879 screen = gtk_window_get_screen (GTK_WINDOW (mgtim->priv->window)); 880 if (screen) { 881 swidth = gdk_screen_get_width (screen); 882 sheight = gdk_screen_get_height (screen); 883 } 884 else { 885 swidth = gdk_screen_width (); 886 sheight = gdk_screen_height (); 887 } 888 gtk_window_get_position (GTK_WINDOW (mgtim->priv->window), &root_x, &root_y); 889 gtk_window_get_size (GTK_WINDOW (mgtim->priv->window), &wwidth, &wheight); 890 if (root_x + wwidth > swidth) { 891 do_move = TRUE; 892 root_x = swidth - wwidth; 893 } 894 else if (root_x < 0) { 895 do_move = TRUE; 896 root_x = 0; 897 } 898 if (root_y + wheight > sheight) { 899 do_move = TRUE; 900 root_y = sheight - wheight; 901 } 902 else if (root_y < 0) { 903 do_move = TRUE; 904 root_y = 0; 905 } 906 if (do_move) 907 gtk_window_move (GTK_WINDOW (mgtim->priv->window), root_x, root_y); 908 909 gtk_widget_grab_focus (mgtim->priv->date); 910 popup_grab_on_window (mgtim->priv->window, 911 gtk_get_current_event_time ()); 912 } 913 914 static gboolean 915 popup_grab_on_window (GtkWidget *widget, guint32 activate_time) 916 { 917 GdkDeviceManager *manager; 918 GdkDevice *pointer; 919 GdkWindow *window; 920 window = gtk_widget_get_window (widget); 921 manager = gdk_display_get_device_manager (gtk_widget_get_display (widget)); 922 pointer = gdk_device_manager_get_client_pointer (manager); 923 if (gdk_device_grab (pointer, window, GDK_OWNERSHIP_WINDOW, TRUE, 924 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | 925 GDK_POINTER_MOTION_MASK, 926 NULL, activate_time) == GDK_GRAB_SUCCESS) { 927 GdkDevice *keyb; 928 keyb = gdk_device_get_associated_device (pointer); 929 if (gdk_device_grab (keyb, window, GDK_OWNERSHIP_WINDOW, TRUE, 930 GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK, NULL, activate_time) == 931 GDK_GRAB_SUCCESS) 932 return TRUE; 933 else { 934 gdk_device_ungrab (pointer, activate_time); 935 return FALSE; 936 } 937 } 938 return FALSE; 939 } 940 941 static void 942 position_popup (GdauiEntryCommonTime *mgtim) 943 { 944 gint x, y; 945 gint bwidth, bheight; 946 GtkRequisition req_minimum, req_natural; 947 948 gtk_widget_get_preferred_size (mgtim->priv->window, &req_minimum, 949 &req_natural); 950 951 gdk_window_get_origin (gtk_widget_get_window (mgtim->priv->date_button), &x, &y); 952 GtkAllocation alloc; 953 gtk_widget_get_allocation (mgtim->priv->date_button, &alloc); 954 x += alloc.x; 955 y += alloc.y; 956 bwidth = alloc.width; 957 bheight = alloc.height; 958 959 x += bwidth - req_natural.width; 960 y += bheight; 961 962 if (x < 0) 963 x = 0; 964 965 if (y < 0) 966 y = 0; 967 968 gtk_window_move (GTK_WINDOW (mgtim->priv->window), x, y); 969 } 970 971 972 973 974 /* 975 * callbacks for the time 976 */ 977 static void entry_time_insert_func (GdauiFormattedEntry *fentry, gunichar insert_char, gint virt_pos, gpointer data); 978 979 static GtkWidget * 980 create_entry_time (GdauiEntryCommonTime *mgtim) 981 { 982 GtkWidget *wid; 983 GdaDataHandler *dh; 984 985 /* text entry */ 986 dh = gdaui_data_entry_get_handler (GDAUI_DATA_ENTRY (mgtim)); 987 if (GDA_IS_HANDLER_TIME (dh)) { 988 gchar *str, *mask, *ptr; 989 str = gda_handler_time_get_format (GDA_HANDLER_TIME (dh), GDA_TYPE_TIME); 990 mask = g_strdup (str); 991 for (ptr = mask; *ptr; ptr++) { 992 if (*ptr == '0') 993 *ptr = '-'; 994 } 995 wid = gdaui_formatted_entry_new (str, mask); 996 g_free (str); 997 g_free (mask); 998 999 gdaui_formatted_entry_set_insert_func (GDAUI_FORMATTED_ENTRY (wid), entry_time_insert_func, 1000 mgtim); 1001 } 1002 else 1003 wid = gdaui_entry_new (NULL, NULL); 1004 mgtim->priv->entry_time = wid; 1005 1006 /* format tooltip */ 1007 gtk_widget_set_tooltip_text (wid, _("Format is hh:mm:ss")); 1008 1009 return wid; 1010 } 1011 1012 static void 1013 entry_time_insert_func (G_GNUC_UNUSED GdauiFormattedEntry *fentry, gunichar insert_char, 1014 G_GNUC_UNUSED gint virt_pos, gpointer data) 1015 { 1016 GValue *value; 1017 GType type; 1018 1019 type = gdaui_data_entry_get_value_type (GDAUI_DATA_ENTRY (data)); 1020 value = real_get_value (GDAUI_ENTRY_WRAPPER (data)); 1021 if (!value) 1022 return; 1023 1024 if (insert_char != g_utf8_get_char (" ")) 1025 return; 1026 1027 if (type == GDA_TYPE_TIME) { 1028 /* set current time */ 1029 gda_value_reset_with_type (value, type); 1030 struct tm *ltm; 1031 time_t val; 1032 1033 val = time (NULL); 1034 ltm = localtime ((const time_t *) &val); 1035 if (ltm) { 1036 GdaTime tim; 1037 memset (&tim, 0, sizeof (GdaTime)); 1038 tim.hour = ltm->tm_hour; 1039 tim.minute = ltm->tm_min; 1040 tim.second = ltm->tm_sec; 1041 tim.fraction = 0; 1042 tim.timezone = GDA_TIMEZONE_INVALID; 1043 gda_value_set_time (value, (const GdaTime *) &tim); 1044 real_set_value (GDAUI_ENTRY_WRAPPER (data), value); 1045 } 1046 } 1047 else if (type == GDA_TYPE_TIMESTAMP && (G_VALUE_TYPE (value) == GDA_TYPE_TIMESTAMP)) { 1048 const GdaTimestamp *ts; 1049 ts = gda_value_get_timestamp (value); 1050 if (ts) { 1051 struct tm *ltm; 1052 time_t val; 1053 1054 val = time (NULL); 1055 ltm = localtime ((const time_t *) &val); 1056 if (ltm) { 1057 GdaTimestamp tim; 1058 tim = *ts; 1059 tim.hour = ltm->tm_hour; 1060 tim.minute = ltm->tm_min; 1061 tim.second = ltm->tm_sec; 1062 tim.fraction = 0; 1063 tim.timezone = GDA_TIMEZONE_INVALID; 1064 gda_value_set_timestamp (value, (const GdaTimestamp *) &tim); 1065 real_set_value (GDAUI_ENTRY_WRAPPER (data), value); 1066 } 1067 } 1068 } 1069 else if (type == GDA_TYPE_TIMESTAMP) { 1070 /* value is GDA_TYPE_NULL */ 1071 entry_date_insert_func (NULL, insert_char, 0, data); 1072 } 1073 1074 gda_value_free (value); 1075 } 1076 1077 /* 1078 * callbacks for the timestamp 1079 */ 1080 static GtkWidget * 1081 create_entry_ts (GdauiEntryCommonTime *mgtim) 1082 { 1083 GtkWidget *hb, *wid; 1084 1085 hb = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); 1086 1087 /* date part */ 1088 wid = create_entry_date (mgtim); 1089 gtk_box_pack_start (GTK_BOX (hb), wid, FALSE, FALSE, 0); 1090 gtk_widget_show (wid); 1091 1092 /* time part */ 1093 wid = create_entry_time (mgtim); 1094 gtk_box_pack_start (GTK_BOX (hb), wid, FALSE, FALSE, 0); 1095 gtk_widget_show (wid); 1096 1097 mgtim->priv->hbox = hb; 1098 1099 return hb; 1100 } 1101 1102 1103 1104 /* 1105 * GtkCellEditable interface 1106 */ 1107 static void 1108 gtk_cell_editable_entry_editing_done_cb (G_GNUC_UNUSED GtkEntry *entry, GdauiEntryCommonTime *mgtim) 1109 { 1110 gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (mgtim)); 1111 } 1112 1113 static void 1114 gtk_cell_editable_entry_remove_widget_cb (G_GNUC_UNUSED GtkEntry *entry, GdauiEntryCommonTime *mgtim) 1115 { 1116 gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (mgtim)); 1117 } 1118 1119 static void 1120 gdaui_entry_common_time_start_editing (GtkCellEditable *iface, GdkEvent *event) 1121 { 1122 GdauiEntryCommonTime *mgtim; 1123 1124 g_return_if_fail (GDAUI_IS_ENTRY_COMMON_TIME (iface)); 1125 mgtim = GDAUI_ENTRY_COMMON_TIME (iface); 1126 g_return_if_fail (mgtim->priv); 1127 1128 mgtim->priv->editing_canceled = FALSE; 1129 if (mgtim->priv->date_button) { 1130 gtk_widget_destroy (mgtim->priv->date_button); 1131 mgtim->priv->date_button = NULL; 1132 } 1133 1134 if (mgtim->priv->hbox) { 1135 gtk_box_set_spacing (GTK_BOX (mgtim->priv->hbox), 0); 1136 gtk_container_set_border_width (GTK_CONTAINER (mgtim->priv->hbox), 0); 1137 } 1138 1139 if (mgtim->priv->entry_date) { 1140 g_object_set (G_OBJECT (mgtim->priv->entry_date), "has-frame", FALSE, NULL); 1141 gtk_cell_editable_start_editing (GTK_CELL_EDITABLE (mgtim->priv->entry_date), event); 1142 g_signal_connect (G_OBJECT (mgtim->priv->entry_date), "editing-done", 1143 G_CALLBACK (gtk_cell_editable_entry_editing_done_cb), mgtim); 1144 g_signal_connect (G_OBJECT (mgtim->priv->entry_date), "remove-widget", 1145 G_CALLBACK (gtk_cell_editable_entry_remove_widget_cb), mgtim); 1146 } 1147 1148 if (mgtim->priv->entry_time) { 1149 g_object_set (G_OBJECT (mgtim->priv->entry_time), "has-frame", FALSE, NULL); 1150 gtk_cell_editable_start_editing (GTK_CELL_EDITABLE (mgtim->priv->entry_time), event); 1151 g_signal_connect (G_OBJECT (mgtim->priv->entry_time), "editing-done", 1152 G_CALLBACK (gtk_cell_editable_entry_editing_done_cb), mgtim); 1153 g_signal_connect (G_OBJECT (mgtim->priv->entry_time), "remove-widget", 1154 G_CALLBACK (gtk_cell_editable_entry_remove_widget_cb), mgtim); 1155 } 1156 1157 gdaui_entry_shell_refresh (GDAUI_ENTRY_SHELL (mgtim)); 1158 1159 if (mgtim->priv->entry_date) 1160 gtk_widget_grab_focus (mgtim->priv->entry_date); 1161 else 1162 gtk_widget_grab_focus (mgtim->priv->entry_time); 1163 gtk_widget_queue_draw (GTK_WIDGET (mgtim)); 1164 } 1165