datecell-gnome.c

00001 /********************************************************************\
00002  * datecell-gnome.c -- implement date cell handler in gnome         *
00003  *                                                                  *  
00004  * This program is free software; you can redistribute it and/or    *
00005  * modify it under the terms of the GNU General Public License as   *
00006  * published by the Free Software Foundation; either version 2 of   *
00007  * the License, or (at your option) any later version.              *
00008  *                                                                  *
00009  * This program is distributed in the hope that it will be useful,  *
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
00012  * GNU General Public License for more details.                     *
00013  *                                                                  *
00014  * You should have received a copy of the GNU General Public License*
00015  * along with this program; if not, contact:                        *
00016  *                                                                  *
00017  * Free Software Foundation           Voice:  +1-617-542-5942       *
00018  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
00019  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
00020  *                                                                  *
00021 \********************************************************************/
00022 
00023 /*
00024  * FILE: datecell-gnome.c
00025  *
00026  * FUNCTION: Implement gnome portion of datecell widget
00027  *           embedded in a table cell.
00028  *
00029  * HISTORY:
00030  * Copyright (c) 2000 Dave Peticolas <dave@krondo.com>
00031  */
00032 
00033 #include "config.h"
00034 
00035 #include <gnome.h>
00036 #include <stdio.h>
00037 #include <stdlib.h>
00038 #include <string.h>
00039 #include <time.h>
00040 
00041 #include "datecell.h"
00042 #include "dialog-utils.h"
00043 #include "gnc-ui-util.h"
00044 #include "gnucash-date-picker.h"
00045 #include "gnucash-item-edit.h"
00046 #include "gnucash-sheet.h"
00047 
00048 
00049 #define DATE_BUF (MAX_DATE_LENGTH+1)
00050 
00051 typedef struct _PopBox
00052 {
00053   GnucashSheet  *sheet;
00054   GncItemEdit      *item_edit;
00055   GNCDatePicker *date_picker;
00056 
00057   gboolean signals_connected; /* date picker signals connected? */
00058   gboolean calendar_popped;   /* calendar is popped up? */
00059   gboolean in_date_select;
00060 
00061   struct tm date;
00062 } PopBox;
00063 
00064 
00065 static void block_picker_signals (DateCell *cell);
00066 static void unblock_picker_signals (DateCell *cell);
00067 static void gnc_date_cell_realize (BasicCell *bcell, gpointer w);
00068 static void gnc_date_cell_set_value_internal (BasicCell *bcell,
00069                                               const char *value);
00070 static void gnc_date_cell_move (BasicCell *bcell);
00071 static void gnc_date_cell_gui_destroy (BasicCell *bcell);
00072 static void gnc_date_cell_destroy (BasicCell *bcell);
00073 static void gnc_date_cell_modify_verify (BasicCell *_cell,
00074                                          const char *change,
00075                                          int change_len,
00076                                          const char *newval,
00077                                          int newval_len,
00078                                          int *cursor_position,
00079                                          int *start_selection,
00080                                          int *end_selection);
00081 static gboolean gnc_date_cell_direct_update (BasicCell *bcell,
00082                                              int *cursor_position,
00083                                              int *start_selection,
00084                                              int *end_selection,
00085                                              void *gui_data);
00086 static gboolean gnc_date_cell_enter (BasicCell *bcell,
00087                                      int *cursor_position,
00088                                      int *start_selection,
00089                                      int *end_selection);
00090 static void gnc_date_cell_leave (BasicCell *bcell);
00091 
00092 
00093 static void
00094 gnc_parse_date (struct tm *parsed, const char * datestr)
00095 {
00096   int day, month, year;
00097 
00098   if (!parsed) return;
00099   if (!datestr) return;
00100 
00101   qof_scan_date (datestr, &day, &month, &year);
00102 
00103   parsed->tm_mday = day;
00104   parsed->tm_mon  = month - 1;
00105   parsed->tm_year = year - 1900;
00106 
00107   gnc_tm_set_day_start(parsed);
00108   if (mktime (parsed) == -1)
00109     gnc_tm_get_today_start (parsed);
00110   mktime (parsed);
00111 }
00112 
00113 static void
00114 gnc_date_cell_print_date (DateCell *cell, char *buff)
00115 {
00116   PopBox *box = cell->cell.gui_private;
00117 
00118   qof_print_date_dmy_buff (buff, MAX_DATE_LENGTH,
00119              box->date.tm_mday,
00120              box->date.tm_mon + 1,
00121              box->date.tm_year+1900);
00122 }
00123 
00124 static void
00125 gnc_date_cell_init (DateCell *cell)
00126 {
00127   PopBox *box;
00128   time_t secs;
00129   char buff[DATE_BUF];
00130 
00131   gnc_basic_cell_init (&(cell->cell));
00132 
00133   cell->cell.is_popup = TRUE;
00134 
00135   cell->cell.destroy = gnc_date_cell_destroy;
00136 
00137   cell->cell.gui_realize = gnc_date_cell_realize;
00138   cell->cell.gui_destroy = gnc_date_cell_gui_destroy;
00139   cell->cell.modify_verify = gnc_date_cell_modify_verify;
00140   cell->cell.direct_update = gnc_date_cell_direct_update;
00141   cell->cell.set_value = gnc_date_cell_set_value_internal;
00142 
00143   box = g_new0 (PopBox, 1);
00144 
00145   box->sheet = NULL;
00146   box->item_edit = NULL;
00147   box->date_picker = NULL;
00148 
00149   box->signals_connected = FALSE;
00150   box->calendar_popped = FALSE;
00151   box->in_date_select = FALSE;
00152 
00153   cell->cell.gui_private = box;
00154 
00155   /* default value is today's date */
00156   time (&secs);
00157   box->date = *localtime (&secs);
00158   gnc_date_cell_print_date (cell, buff);
00159 
00160   gnc_basic_cell_set_value_internal (&cell->cell, buff);
00161 }
00162 
00163 BasicCell *
00164 gnc_date_cell_new (void)
00165 {
00166    DateCell *cell;
00167 
00168    cell = g_new0 (DateCell, 1);
00169 
00170    gnc_date_cell_init (cell);
00171 
00172    return &cell->cell;
00173 }
00174 
00175 static void
00176 date_picked_cb (GNCDatePicker *gdp, gpointer data)
00177 {
00178   DateCell *cell = data;
00179   PopBox *box = cell->cell.gui_private;
00180   guint day, month, year;
00181   char buffer[DATE_BUF];
00182 
00183   gtk_calendar_get_date (gdp->calendar, &year, &month, &day);
00184 
00185   qof_print_date_dmy_buff (buffer, MAX_DATE_LENGTH, day, month + 1, year);
00186 
00187   box->in_date_select = TRUE;
00188   gnucash_sheet_modify_current_cell (box->sheet, buffer);
00189   box->in_date_select = FALSE;
00190 
00191   gnc_item_edit_hide_popup (box->item_edit);
00192   box->calendar_popped = FALSE;
00193 }
00194 
00195 static void
00196 date_selected_cb (GNCDatePicker *gdp, gpointer data)
00197 {
00198   DateCell *cell = data;
00199   PopBox *box = cell->cell.gui_private;
00200   guint day, month, year;
00201   char buffer[DATE_BUF];
00202 
00203   gtk_calendar_get_date (gdp->calendar, &year, &month, &day);
00204 
00205   qof_print_date_dmy_buff (buffer, MAX_DATE_LENGTH, day, month + 1, year);
00206 
00207   box->in_date_select = TRUE;
00208   gnucash_sheet_modify_current_cell (box->sheet, buffer);
00209   box->in_date_select = FALSE;
00210 }
00211 
00212 static void
00213 key_press_item_cb (GNCDatePicker *gdp, GdkEventKey *event, gpointer data)
00214 {
00215   DateCell *cell = data;
00216   PopBox *box = cell->cell.gui_private;
00217 
00218   switch(event->keyval)
00219   {
00220     case GDK_Escape:
00221       gnc_item_edit_hide_popup (box->item_edit);
00222       box->calendar_popped = FALSE;
00223       break;
00224 
00225     default:
00226       gtk_widget_event(GTK_WIDGET (box->sheet), (GdkEvent *) event);
00227       break;
00228   }
00229 }
00230 
00231 static void
00232 date_picker_disconnect_signals (DateCell *cell)
00233 {
00234   PopBox *box = cell->cell.gui_private;
00235 
00236   if (!box->signals_connected)
00237     return;
00238 
00239   g_signal_handlers_disconnect_matched (box->date_picker, G_SIGNAL_MATCH_DATA,
00240                                         0, 0, NULL, NULL, cell);
00241 
00242   box->signals_connected = FALSE;
00243 }
00244 
00245 static void
00246 date_picker_connect_signals (DateCell *cell)
00247 {
00248   PopBox *box = cell->cell.gui_private;
00249 
00250   if (box->signals_connected)
00251     return;
00252 
00253   g_signal_connect (box->date_picker, "date_selected",
00254                     G_CALLBACK(date_selected_cb), cell);
00255 
00256   g_signal_connect(box->date_picker, "date_picked",
00257                    G_CALLBACK(date_picked_cb), cell);
00258 
00259   g_signal_connect(box->date_picker, "key_press_event",
00260                    G_CALLBACK(key_press_item_cb), cell);
00261 
00262   box->signals_connected = TRUE;
00263 }
00264 
00265 static void
00266 block_picker_signals (DateCell *cell)
00267 {
00268   PopBox *box = cell->cell.gui_private;
00269 
00270   if (!box->signals_connected)
00271     return;
00272 
00273   g_signal_handlers_block_matched (box->date_picker, G_SIGNAL_MATCH_DATA,
00274                                    0, 0, NULL, NULL, cell);
00275 }
00276 
00277 static void
00278 unblock_picker_signals (DateCell *cell)
00279 {
00280   PopBox *box = cell->cell.gui_private;
00281 
00282   if (!box->signals_connected)
00283     return;
00284 
00285   g_signal_handlers_unblock_matched (box->date_picker, G_SIGNAL_MATCH_DATA,
00286                                      0, 0, NULL, NULL, cell);
00287 }
00288 
00289 static void
00290 gnc_date_cell_gui_destroy (BasicCell *bcell)
00291 {
00292   PopBox *box = bcell->gui_private;
00293   DateCell *cell = (DateCell *) bcell;
00294 
00295   if (cell->cell.gui_realize == NULL)
00296   {
00297     if (box != NULL && box->date_picker != NULL)
00298     {
00299       date_picker_disconnect_signals (cell);
00300       g_object_unref (box->date_picker);
00301       box->date_picker = NULL;
00302     }
00303 
00304     /* allow the widget to be shown again */
00305     cell->cell.gui_realize = gnc_date_cell_realize;
00306     cell->cell.gui_move = NULL;
00307     cell->cell.enter_cell = NULL;
00308     cell->cell.leave_cell = NULL;
00309     cell->cell.gui_destroy = NULL;
00310   }
00311 }
00312 
00313 static void
00314 gnc_date_cell_destroy (BasicCell *bcell)
00315 {
00316   DateCell *cell = (DateCell *) bcell;
00317   PopBox *box = cell->cell.gui_private;
00318 
00319   gnc_date_cell_gui_destroy (&(cell->cell));
00320 
00321   g_free (box);
00322 
00323   cell->cell.gui_private = NULL;
00324   cell->cell.gui_realize = NULL;
00325 }
00326 
00327 void 
00328 gnc_date_cell_set_value (DateCell *cell, int day, int mon, int year)
00329 {
00330   PopBox *box = cell->cell.gui_private;
00331   struct tm dada;
00332   char buff[DATE_BUF];
00333 
00334   dada.tm_mday = day;
00335   dada.tm_mon  = mon - 1;
00336   dada.tm_year = year - 1900;
00337 
00338   gnc_tm_set_day_start(&dada);
00339   mktime (&dada);
00340 
00341   box->date.tm_mday = dada.tm_mday;
00342   box->date.tm_mon  = dada.tm_mon;
00343   box->date.tm_year = dada.tm_year;
00344 
00345   qof_print_date_dmy_buff (buff, MAX_DATE_LENGTH, dada.tm_mday, dada.tm_mon + 1, dada.tm_year + 1900);
00346 
00347   gnc_basic_cell_set_value_internal (&cell->cell, buff);
00348 
00349   if (!box->date_picker)
00350     return;
00351 
00352   block_picker_signals (cell);
00353   gnc_date_picker_set_date (box->date_picker, day, mon - 1, year);
00354   unblock_picker_signals (cell);
00355 }
00356 
00357 void 
00358 gnc_date_cell_set_value_secs (DateCell *cell, time_t secs)
00359 {
00360   PopBox *box = cell->cell.gui_private;
00361   char buff[DATE_BUF];
00362   struct tm * stm;
00363 
00364   stm = localtime (&secs);
00365   box->date = *stm;
00366 
00367   qof_print_date_dmy_buff (buff, MAX_DATE_LENGTH,
00368              box->date.tm_mday, 
00369              box->date.tm_mon + 1, 
00370              box->date.tm_year + 1900);
00371 
00372   gnc_basic_cell_set_value_internal (&cell->cell, buff);
00373 
00374   if (!box->date_picker)
00375     return;
00376 
00377   block_picker_signals (cell);
00378   gnc_date_picker_set_date (box->date_picker,
00379                             box->date.tm_mday,
00380                             box->date.tm_mon,
00381                             box->date.tm_year + 1900);
00382   unblock_picker_signals (cell);
00383 }
00384 
00385 void
00386 gnc_date_cell_commit (DateCell *cell)
00387 {
00388   PopBox *box = cell->cell.gui_private;
00389   char buff[DATE_BUF];
00390 
00391   if (!cell)
00392     return;
00393 
00394   gnc_parse_date (&(box->date), cell->cell.value);
00395 
00396   qof_print_date_dmy_buff (buff, MAX_DATE_LENGTH,
00397              box->date.tm_mday, 
00398              box->date.tm_mon + 1,
00399              box->date.tm_year + 1900);
00400 
00401   gnc_basic_cell_set_value_internal (&cell->cell, buff);
00402 
00403   if (!box->date_picker)
00404     return;
00405 
00406   block_picker_signals (cell);
00407   gnc_date_picker_set_date (box->date_picker,
00408                             box->date.tm_mday,
00409                             box->date.tm_mon,
00410                             box->date.tm_year + 1900);
00411   unblock_picker_signals (cell);
00412 }
00413 
00414 static gboolean
00415 gnc_date_cell_direct_update (BasicCell *bcell,
00416                              int *cursor_position,
00417                              int *start_selection,
00418                              int *end_selection,
00419                              void *gui_data)
00420 {
00421   DateCell *cell = (DateCell *) bcell;
00422   PopBox *box = cell->cell.gui_private;
00423   GdkEventKey *event = gui_data;
00424   char buff[DATE_BUF];
00425 
00426   if (!gnc_handle_date_accelerator (event, &(box->date), bcell->value))
00427     return FALSE;
00428 
00429   qof_print_date_dmy_buff (buff, MAX_DATE_LENGTH,
00430              box->date.tm_mday,
00431              box->date.tm_mon + 1,
00432              box->date.tm_year + 1900);
00433 
00434   gnc_basic_cell_set_value_internal (&cell->cell, buff);
00435 
00436   *start_selection = 0;
00437   *end_selection = -1;
00438 
00439   if (!box->date_picker)
00440     return TRUE;
00441 
00442   block_picker_signals (cell);
00443   gnc_date_picker_set_date (box->date_picker,
00444                             box->date.tm_mday,
00445                             box->date.tm_mon,
00446                             box->date.tm_year + 1900);
00447   unblock_picker_signals (cell);
00448 
00449   return TRUE;
00450 }
00451 
00452 static void
00453 gnc_date_cell_modify_verify (BasicCell *_cell,
00454                              const char *change,
00455                              int change_len,
00456                              const char *newval,
00457                              int newval_len,
00458                              int *cursor_position,
00459                              int *start_selection,
00460                              int *end_selection)
00461 {
00462   DateCell *cell = (DateCell *) _cell;
00463   PopBox *box = cell->cell.gui_private;
00464   gboolean accept = FALSE;
00465 
00466   if (box->in_date_select)
00467   {
00468     gnc_basic_cell_set_value (_cell, newval);
00469     return;
00470   }
00471 
00472   /* if user hit backspace, accept the change */
00473   if (change == NULL)
00474     accept = TRUE;
00475   else if (change_len == 0)
00476     accept = TRUE;
00477   else
00478   {
00479     int count = 0;
00480     unsigned char separator = dateSeparator ();
00481     gboolean ok = TRUE;
00482     const gchar *c;
00483     gunichar uc;
00484     
00485     /* accept only numbers or a date separator. Note that the
00486      * separator of '-' (for DATE_FORMAT_ISO) takes precedence
00487      * over the accelerator below! */      
00488     c = change;
00489     while (*c)
00490     {
00491       uc = g_utf8_get_char (c);
00492         
00493       if (!g_unichar_isdigit (uc) && (separator != uc))
00494         ok = FALSE;
00495 
00496       if (separator == uc)
00497         count++;
00498       
00499       c = g_utf8_next_char (c);
00500     }      
00501     
00502     c = _cell->value;
00503     while (*c)
00504     {
00505       uc = g_utf8_get_char (c);
00506         
00507       if (separator == uc)
00508         count++;
00509 
00510       c = g_utf8_next_char (c);
00511     }
00512      
00513     if (2 < count)
00514       ok = FALSE;
00515 
00516     if (ok)
00517       accept = TRUE;
00518   }
00519 
00520   /* keep a copy of the new value */
00521   if (accept)
00522   {
00523 
00524     gnc_basic_cell_set_value_internal (&cell->cell, newval);
00525     gnc_parse_date (&(box->date), newval);
00526 
00527     if (!box->date_picker)
00528       return;
00529 
00530     block_picker_signals (cell);
00531     gnc_date_picker_set_date (box->date_picker,
00532                               box->date.tm_mday,
00533                               box->date.tm_mon,
00534                               box->date.tm_year + 1900);
00535     unblock_picker_signals (cell);
00536   }
00537 }
00538 
00539 static void
00540 gnc_date_cell_realize (BasicCell *bcell, gpointer data)
00541 {
00542   GnucashSheet *sheet = data;
00543   GnomeCanvasItem *item = sheet->item_editor;
00544   GncItemEdit *item_edit = GNC_ITEM_EDIT (item);
00545   DateCell *cell = (DateCell *) bcell;
00546   PopBox *box = cell->cell.gui_private;
00547 
00548   /* initialize gui-specific, private data */
00549   box->sheet = sheet;
00550   box->item_edit = item_edit;
00551   box->date_picker = gnc_item_edit_new_date_picker (box->item_edit);
00552 #ifdef HAVE_GTK_2_10
00553   g_object_ref_sink(box->date_picker);
00554 #else
00555   g_object_ref (box->date_picker);
00556   gtk_object_sink (GTK_OBJECT(box->date_picker));
00557 #endif
00558 
00559   /* to mark cell as realized, remove the realize method */
00560   cell->cell.gui_realize = NULL;
00561   cell->cell.gui_move = gnc_date_cell_move;
00562   cell->cell.enter_cell = gnc_date_cell_enter;
00563   cell->cell.leave_cell = gnc_date_cell_leave;
00564 }
00565 
00566 static void
00567 gnc_date_cell_move (BasicCell *bcell)
00568 {
00569   PopBox *box = bcell->gui_private;
00570 
00571   date_picker_disconnect_signals ((DateCell *) bcell);
00572 
00573   gnc_item_edit_set_popup (box->item_edit, NULL, NULL,
00574                        NULL, NULL, NULL, NULL, NULL);
00575 
00576   box->calendar_popped = FALSE;
00577 }
00578 
00579 static int
00580 get_popup_height (GnomeCanvasItem *item,
00581                   int space_available,
00582                   int row_height,
00583                   gpointer user_data)
00584 {
00585   GtkWidget *cal = GTK_WIDGET (GNC_DATE_PICKER (item)->calendar);
00586   GtkRequisition req;
00587 
00588   req.height = 0;
00589   req.width = 0;
00590 
00591   gtk_widget_size_request (cal, &req);
00592 
00593   return req.height;
00594 }
00595 
00596 static void
00597 popup_set_focus (GnomeCanvasItem *item,
00598                  gpointer user_data)
00599 {
00600   gtk_widget_grab_focus (GTK_WIDGET (GNC_DATE_PICKER (item)->calendar));
00601 }
00602 
00603 static gboolean
00604 gnc_date_cell_enter (BasicCell *bcell,
00605                      int *cursor_position,
00606                      int *start_selection,
00607                      int *end_selection)
00608 {
00609   DateCell *cell = (DateCell *) bcell;
00610   PopBox *box = bcell->gui_private;
00611 
00612   gnc_item_edit_set_popup (box->item_edit, GNOME_CANVAS_ITEM (box->date_picker),
00613                        get_popup_height, NULL, popup_set_focus,
00614                        NULL, NULL, NULL);
00615 
00616   block_picker_signals (cell);
00617   gnc_date_picker_set_date (box->date_picker,
00618                             box->date.tm_mday,
00619                             box->date.tm_mon,
00620                             box->date.tm_year + 1900);
00621   unblock_picker_signals (cell);
00622 
00623   date_picker_connect_signals ((DateCell *) bcell);
00624 
00625   *start_selection = 0;
00626   *end_selection = -1;
00627 
00628   return TRUE;
00629 }
00630 
00631 static void
00632 gnc_date_cell_leave (BasicCell *bcell)
00633 {
00634   Timespec ts;
00635   PopBox *box = bcell->gui_private;
00636 
00637   date_picker_disconnect_signals ((DateCell *) bcell);
00638 
00639   gnc_item_edit_set_popup (box->item_edit, NULL, NULL,
00640                        NULL, NULL, NULL, NULL, NULL);
00641 
00642   box->calendar_popped = FALSE;
00643 
00644   /* Refresh the date to expand any shortcuts. */
00645   gnc_date_cell_get_date ((DateCell *)bcell, &ts);
00646   gnc_date_cell_set_value_secs ((DateCell *)bcell, ts.tv_sec);
00647 }
00648 
00649 void
00650 gnc_date_cell_get_date (DateCell *cell, Timespec *ts)
00651 {
00652   PopBox *box = cell->cell.gui_private;
00653 
00654   if (!cell || !ts)
00655     return;
00656 
00657   gnc_parse_date (&(box->date), cell->cell.value);
00658 
00659   ts->tv_sec = mktime (&box->date);
00660   ts->tv_nsec = 0;
00661 }
00662 
00663 static void 
00664 gnc_date_cell_set_value_internal (BasicCell *_cell, const char *str)
00665 {
00666   DateCell *cell = (DateCell *) _cell;
00667   PopBox *box = cell->cell.gui_private;
00668   char buff[DATE_BUF];
00669 
00670   gnc_parse_date (&(box->date), str);
00671 
00672   qof_print_date_dmy_buff (buff, MAX_DATE_LENGTH,
00673              box->date.tm_mday, 
00674              box->date.tm_mon + 1, 
00675              box->date.tm_year + 1900);
00676 
00677   gnc_basic_cell_set_value_internal (_cell, buff);
00678 
00679   if (!box->date_picker)
00680     return;
00681 
00682   block_picker_signals (cell);
00683   gnc_date_picker_set_date (box->date_picker,
00684                             box->date.tm_mday,
00685                             box->date.tm_mon,
00686                             box->date.tm_year + 1900);
00687   unblock_picker_signals (cell);
00688 }

Generated on Mon Sep 8 05:03:45 2008 for GnuCash by  doxygen 1.5.2