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  * 59 Temple Place - Suite 330        Fax:    +1-617-542-2652       *
00019  * Boston, MA  02111-1307,  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 30
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   gtk_signal_disconnect_by_data (GTK_OBJECT (box->date_picker), cell);
00240 
00241   box->signals_connected = FALSE;
00242 }
00243 
00244 static void
00245 date_picker_connect_signals (DateCell *cell)
00246 {
00247   PopBox *box = cell->cell.gui_private;
00248 
00249   if (box->signals_connected)
00250     return;
00251 
00252   gtk_signal_connect (GTK_OBJECT(box->date_picker), "date_selected",
00253                       GTK_SIGNAL_FUNC(date_selected_cb), cell);
00254 
00255   gtk_signal_connect(GTK_OBJECT(box->date_picker), "date_picked",
00256                      GTK_SIGNAL_FUNC(date_picked_cb), cell);
00257 
00258   gtk_signal_connect(GTK_OBJECT(box->date_picker), "key_press_event",
00259                      GTK_SIGNAL_FUNC(key_press_item_cb), cell);
00260 
00261   box->signals_connected = TRUE;
00262 }
00263 
00264 static void
00265 block_picker_signals (DateCell *cell)
00266 {
00267   PopBox *box = cell->cell.gui_private;
00268 
00269   if (!box->signals_connected)
00270     return;
00271 
00272   gtk_signal_handler_block_by_data (GTK_OBJECT (box->date_picker), cell);
00273 }
00274 
00275 static void
00276 unblock_picker_signals (DateCell *cell)
00277 {
00278   PopBox *box = cell->cell.gui_private;
00279 
00280   if (!box->signals_connected)
00281     return;
00282 
00283   gtk_signal_handler_unblock_by_data (GTK_OBJECT (box->date_picker), cell);
00284 }
00285 
00286 static void
00287 gnc_date_cell_gui_destroy (BasicCell *bcell)
00288 {
00289   PopBox *box = bcell->gui_private;
00290   DateCell *cell = (DateCell *) bcell;
00291 
00292   if (cell->cell.gui_realize == NULL)
00293   {
00294     if (box != NULL && box->date_picker != NULL)
00295     {
00296       date_picker_disconnect_signals (cell);
00297       g_object_unref (box->date_picker);
00298       box->date_picker = NULL;
00299     }
00300 
00301     /* allow the widget to be shown again */
00302     cell->cell.gui_realize = gnc_date_cell_realize;
00303     cell->cell.gui_move = NULL;
00304     cell->cell.enter_cell = NULL;
00305     cell->cell.leave_cell = NULL;
00306     cell->cell.gui_destroy = NULL;
00307   }
00308 }
00309 
00310 static void
00311 gnc_date_cell_destroy (BasicCell *bcell)
00312 {
00313   DateCell *cell = (DateCell *) bcell;
00314   PopBox *box = cell->cell.gui_private;
00315 
00316   gnc_date_cell_gui_destroy (&(cell->cell));
00317 
00318   g_free (box);
00319 
00320   cell->cell.gui_private = NULL;
00321   cell->cell.gui_realize = NULL;
00322 }
00323 
00324 void 
00325 gnc_date_cell_set_value (DateCell *cell, int day, int mon, int year)
00326 {
00327   PopBox *box = cell->cell.gui_private;
00328   struct tm dada;
00329   char buff[DATE_BUF];
00330 
00331   dada.tm_mday = day;
00332   dada.tm_mon  = mon - 1;
00333   dada.tm_year = year - 1900;
00334 
00335   gnc_tm_set_day_start(&dada);
00336   mktime (&dada);
00337 
00338   box->date.tm_mday = dada.tm_mday;
00339   box->date.tm_mon  = dada.tm_mon;
00340   box->date.tm_year = dada.tm_year;
00341 
00342   qof_print_date_dmy_buff (buff, MAX_DATE_LENGTH, dada.tm_mday, dada.tm_mon + 1, dada.tm_year + 1900);
00343 
00344   gnc_basic_cell_set_value_internal (&cell->cell, buff);
00345 
00346   if (!box->date_picker)
00347     return;
00348 
00349   block_picker_signals (cell);
00350   gnc_date_picker_set_date (box->date_picker, day, mon - 1, year);
00351   unblock_picker_signals (cell);
00352 }
00353 
00354 void 
00355 gnc_date_cell_set_value_secs (DateCell *cell, time_t secs)
00356 {
00357   PopBox *box = cell->cell.gui_private;
00358   char buff[DATE_BUF];
00359   struct tm * stm;
00360 
00361   stm = localtime (&secs);
00362   box->date = *stm;
00363 
00364   qof_print_date_dmy_buff (buff, MAX_DATE_LENGTH,
00365              box->date.tm_mday, 
00366              box->date.tm_mon + 1, 
00367              box->date.tm_year + 1900);
00368 
00369   gnc_basic_cell_set_value_internal (&cell->cell, buff);
00370 
00371   if (!box->date_picker)
00372     return;
00373 
00374   block_picker_signals (cell);
00375   gnc_date_picker_set_date (box->date_picker,
00376                             box->date.tm_mday,
00377                             box->date.tm_mon,
00378                             box->date.tm_year + 1900);
00379   unblock_picker_signals (cell);
00380 }
00381 
00382 void
00383 gnc_date_cell_commit (DateCell *cell)
00384 {
00385   PopBox *box = cell->cell.gui_private;
00386   char buff[DATE_BUF];
00387 
00388   if (!cell)
00389     return;
00390 
00391   gnc_parse_date (&(box->date), cell->cell.value);
00392 
00393   qof_print_date_dmy_buff (buff, MAX_DATE_LENGTH,
00394              box->date.tm_mday, 
00395              box->date.tm_mon + 1,
00396              box->date.tm_year + 1900);
00397 
00398   gnc_basic_cell_set_value_internal (&cell->cell, buff);
00399 
00400   if (!box->date_picker)
00401     return;
00402 
00403   block_picker_signals (cell);
00404   gnc_date_picker_set_date (box->date_picker,
00405                             box->date.tm_mday,
00406                             box->date.tm_mon,
00407                             box->date.tm_year + 1900);
00408   unblock_picker_signals (cell);
00409 }
00410 
00411 static gboolean
00412 gnc_date_cell_direct_update (BasicCell *bcell,
00413                              int *cursor_position,
00414                              int *start_selection,
00415                              int *end_selection,
00416                              void *gui_data)
00417 {
00418   DateCell *cell = (DateCell *) bcell;
00419   PopBox *box = cell->cell.gui_private;
00420   GdkEventKey *event = gui_data;
00421   char buff[DATE_BUF];
00422 
00423   if (!gnc_handle_date_accelerator (event, &(box->date), bcell->value))
00424     return FALSE;
00425 
00426   qof_print_date_dmy_buff (buff, MAX_DATE_LENGTH,
00427              box->date.tm_mday,
00428              box->date.tm_mon + 1,
00429              box->date.tm_year + 1900);
00430 
00431   gnc_basic_cell_set_value_internal (&cell->cell, buff);
00432 
00433   *start_selection = 0;
00434   *end_selection = -1;
00435 
00436   if (!box->date_picker)
00437     return TRUE;
00438 
00439   block_picker_signals (cell);
00440   gnc_date_picker_set_date (box->date_picker,
00441                             box->date.tm_mday,
00442                             box->date.tm_mon,
00443                             box->date.tm_year + 1900);
00444   unblock_picker_signals (cell);
00445 
00446   return TRUE;
00447 }
00448 
00449 static void
00450 gnc_date_cell_modify_verify (BasicCell *_cell,
00451                              const char *change,
00452                              int change_len,
00453                              const char *newval,
00454                              int newval_len,
00455                              int *cursor_position,
00456                              int *start_selection,
00457                              int *end_selection)
00458 {
00459   DateCell *cell = (DateCell *) _cell;
00460   PopBox *box = cell->cell.gui_private;
00461   gboolean accept = FALSE;
00462 
00463   if (box->in_date_select)
00464   {
00465     gnc_basic_cell_set_value (_cell, newval);
00466     return;
00467   }
00468 
00469   /* if user hit backspace, accept the change */
00470   if (change == NULL)
00471     accept = TRUE;
00472   else if (change_len == 0)
00473     accept = TRUE;
00474   else
00475   {
00476     int count = 0;
00477     unsigned char separator = dateSeparator ();
00478     gboolean ok = TRUE;
00479     const gchar *c;
00480     gunichar uc;
00481     
00482     /* accept only numbers or a date separator. Note that the
00483      * separator of '-' (for DATE_FORMAT_ISO) takes precedence
00484      * over the accelerator below! */      
00485     c = change;
00486     while (*c)
00487     {
00488       uc = g_utf8_get_char (c);
00489         
00490       if (!g_unichar_isdigit (uc) && (separator != uc))
00491         ok = FALSE;
00492 
00493       if (separator == uc)
00494         count++;
00495       
00496       c = g_utf8_next_char (c);
00497     }      
00498     
00499     c = _cell->value;
00500     while (*c)
00501     {
00502       uc = g_utf8_get_char (c);
00503         
00504       if (separator == uc)
00505         count++;
00506 
00507       c = g_utf8_next_char (c);
00508     }
00509      
00510     if (2 < count)
00511       ok = FALSE;
00512 
00513     if (ok)
00514       accept = TRUE;
00515   }
00516 
00517   /* keep a copy of the new value */
00518   if (accept)
00519   {
00520 
00521     gnc_basic_cell_set_value_internal (&cell->cell, newval);
00522     gnc_parse_date (&(box->date), newval);
00523 
00524     if (!box->date_picker)
00525       return;
00526 
00527     block_picker_signals (cell);
00528     gnc_date_picker_set_date (box->date_picker,
00529                               box->date.tm_mday,
00530                               box->date.tm_mon,
00531                               box->date.tm_year + 1900);
00532     unblock_picker_signals (cell);
00533   }
00534 }
00535 
00536 static void
00537 gnc_date_cell_realize (BasicCell *bcell, gpointer data)
00538 {
00539   GnucashSheet *sheet = data;
00540   GnomeCanvasItem *item = sheet->item_editor;
00541   GncItemEdit *item_edit = GNC_ITEM_EDIT (item);
00542   DateCell *cell = (DateCell *) bcell;
00543   PopBox *box = cell->cell.gui_private;
00544 
00545   /* initialize gui-specific, private data */
00546   box->sheet = sheet;
00547   box->item_edit = item_edit;
00548   box->date_picker = gnc_item_edit_new_date_picker (box->item_edit);
00549   g_object_ref (box->date_picker);
00550   gtk_object_sink (GTK_OBJECT(box->date_picker));
00551 
00552   /* to mark cell as realized, remove the realize method */
00553   cell->cell.gui_realize = NULL;
00554   cell->cell.gui_move = gnc_date_cell_move;
00555   cell->cell.enter_cell = gnc_date_cell_enter;
00556   cell->cell.leave_cell = gnc_date_cell_leave;
00557 }
00558 
00559 static void
00560 gnc_date_cell_move (BasicCell *bcell)
00561 {
00562   PopBox *box = bcell->gui_private;
00563 
00564   date_picker_disconnect_signals ((DateCell *) bcell);
00565 
00566   gnc_item_edit_set_popup (box->item_edit, NULL, NULL,
00567                        NULL, NULL, NULL, NULL, NULL);
00568 
00569   box->calendar_popped = FALSE;
00570 }
00571 
00572 static int
00573 get_popup_height (GnomeCanvasItem *item,
00574                   int space_available,
00575                   int row_height,
00576                   gpointer user_data)
00577 {
00578   GtkWidget *cal = GTK_WIDGET (GNC_DATE_PICKER (item)->calendar);
00579   GtkRequisition req;
00580 
00581   req.height = 0;
00582   req.width = 0;
00583 
00584   gtk_widget_size_request (cal, &req);
00585 
00586   return req.height;
00587 }
00588 
00589 static void
00590 popup_set_focus (GnomeCanvasItem *item,
00591                  gpointer user_data)
00592 {
00593   gtk_widget_grab_focus (GTK_WIDGET (GNC_DATE_PICKER (item)->calendar));
00594 }
00595 
00596 static gboolean
00597 gnc_date_cell_enter (BasicCell *bcell,
00598                      int *cursor_position,
00599                      int *start_selection,
00600                      int *end_selection)
00601 {
00602   DateCell *cell = (DateCell *) bcell;
00603   PopBox *box = bcell->gui_private;
00604 
00605   gnc_item_edit_set_popup (box->item_edit, GNOME_CANVAS_ITEM (box->date_picker),
00606                        get_popup_height, NULL, popup_set_focus,
00607                        NULL, NULL, NULL);
00608 
00609   block_picker_signals (cell);
00610   gnc_date_picker_set_date (box->date_picker,
00611                             box->date.tm_mday,
00612                             box->date.tm_mon,
00613                             box->date.tm_year + 1900);
00614   unblock_picker_signals (cell);
00615 
00616   date_picker_connect_signals ((DateCell *) bcell);
00617 
00618   *start_selection = 0;
00619   *end_selection = -1;
00620 
00621   return TRUE;
00622 }
00623 
00624 static void
00625 gnc_date_cell_leave (BasicCell *bcell)
00626 {
00627   Timespec ts;
00628   PopBox *box = bcell->gui_private;
00629 
00630   date_picker_disconnect_signals ((DateCell *) bcell);
00631 
00632   gnc_item_edit_set_popup (box->item_edit, NULL, NULL,
00633                        NULL, NULL, NULL, NULL, NULL);
00634 
00635   box->calendar_popped = FALSE;
00636 
00637   /* Refresh the date to expand any shortcuts. */
00638   gnc_date_cell_get_date ((DateCell *)bcell, &ts);
00639   gnc_date_cell_set_value_secs ((DateCell *)bcell, ts.tv_sec);
00640 }
00641 
00642 void
00643 gnc_date_cell_get_date (DateCell *cell, Timespec *ts)
00644 {
00645   PopBox *box = cell->cell.gui_private;
00646 
00647   if (!cell || !ts)
00648     return;
00649 
00650   gnc_parse_date (&(box->date), cell->cell.value);
00651 
00652   ts->tv_sec = mktime (&box->date);
00653   ts->tv_nsec = 0;
00654 }
00655 
00656 static void 
00657 gnc_date_cell_set_value_internal (BasicCell *_cell, const char *str)
00658 {
00659   DateCell *cell = (DateCell *) _cell;
00660   PopBox *box = cell->cell.gui_private;
00661   char buff[DATE_BUF];
00662 
00663   gnc_parse_date (&(box->date), str);
00664 
00665   qof_print_date_dmy_buff (buff, MAX_DATE_LENGTH,
00666              box->date.tm_mday, 
00667              box->date.tm_mon + 1, 
00668              box->date.tm_year + 1900);
00669 
00670   gnc_basic_cell_set_value_internal (_cell, buff);
00671 
00672   if (!box->date_picker)
00673     return;
00674 
00675   block_picker_signals (cell);
00676   gnc_date_picker_set_date (box->date_picker,
00677                             box->date.tm_mday,
00678                             box->date.tm_mon,
00679                             box->date.tm_year + 1900);
00680   unblock_picker_signals (cell);
00681 }

Generated on Sun Sep 4 18:07:34 2005 for GnuCash by  doxygen 1.4.3-20050530