GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
gnc-cell-renderer-popup-entry.c
1 /*************************************************************************
2  * The following code has come from Planner. This code implements a
3  * GtkCalendar in a custom GtkCellEditable popup from GtkCellRenderer.
4  *
5  * These files have been renamed and changed to remove code not required
6  * and to remove a dependency on libplanner.
7  *
8  * Copyright (C) 2012 Robert Fewell
9  *
10  * Copyright (C) 2001-2002 CodeFactory AB
11  * Copyright (C) 2001-2002 Richard Hult <[email protected]>
12  * Copyright (C) 2001-2002 Mikael Hallendal <[email protected]>
13  *
14  * This program is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU General Public License as
16  * published by the Free Software Foundation; either version 2 of the
17  * License, or (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22  * General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public
25  * License along with this program; if not, write to the
26  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
27  * Boston, MA 02111-1307, USA.
28  *************************************************************************/
29 #include "config.h"
30 
31 #include <gtk/gtk.h>
32 #include <glib/gi18n.h>
33 #include <gdk/gdkkeysyms.h>
34 #include <string.h>
35 
36 #include "gnc-cell-renderer-popup-entry.h"
37 #include "dialog-utils.h"
38 #include "gnc-date.h"
39 
40 static void gnc_popup_entry_init (GncPopupEntry *entry);
41 static void gnc_popup_entry_class_init (GncPopupEntryClass *klass);
42 static void gpw_cell_editable_init (GtkCellEditableIface *iface);
43 static gboolean gpw_key_press_event (GtkWidget *box,
44  GdkEventKey *key_event);
45 
46 enum
47 {
48  ARROW_CLICKED,
49  LAST_SIGNAL
50 };
51 
52 enum
53 {
54  ARG_0,
55  ARG_EDITING_CANCELED
56 };
57 
58 static GtkEventBoxClass *parent_class;
59 static guint signals[LAST_SIGNAL];
60 
61 GtkType
62 gnc_popup_entry_get_type (void)
63 {
64  static GtkType widget_type = 0;
65 
66  if (!widget_type)
67  {
68  static const GTypeInfo widget_info =
69  {
70  sizeof (GncPopupEntryClass),
71  NULL, /* base_init */
72  NULL, /* base_finalize */
73  (GClassInitFunc) gnc_popup_entry_class_init,
74  NULL, /* class_finalize */
75  NULL, /* class_data */
76  sizeof (GncPopupEntry),
77  0, /* n_preallocs */
78  (GInstanceInitFunc) gnc_popup_entry_init,
79  };
80 
81  static const GInterfaceInfo cell_editable_info =
82  {
83  (GInterfaceInitFunc) gpw_cell_editable_init, /* interface_init */
84  NULL, /* interface_finalize */
85  NULL /* interface_data */
86  };
87 
88  widget_type = g_type_register_static (GTK_TYPE_EVENT_BOX,
89  "GncPopupEntry",
90  &widget_info,
91  0);
92 
93  g_type_add_interface_static (widget_type,
94  GTK_TYPE_CELL_EDITABLE,
95  &cell_editable_info);
96  }
97 
98  return widget_type;
99 }
100 
101 static void
102 gnc_popup_entry_init (GncPopupEntry *widget)
103 {
104  GtkWidget *arrow;
105 
106  widget->hbox = gtk_hbox_new (FALSE, 0);
107  gtk_widget_show (widget->hbox);
108 
109  widget->entry = g_object_new (GTK_TYPE_ENTRY, "has_frame", FALSE, NULL);
110  GTK_ENTRY (widget->entry)->is_cell_renderer = TRUE;
111  gtk_widget_show (widget->entry);
112 
113  widget->button = gtk_button_new ();
114  gtk_widget_show (widget->button);
115 
116  arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_OUT);
117  gtk_widget_show (arrow);
118 
119  gtk_container_add (GTK_CONTAINER (widget->button), arrow);
120 
121  gtk_box_pack_start (GTK_BOX (widget->hbox), widget->entry, TRUE, TRUE, 0);
122  gtk_box_pack_start (GTK_BOX (widget->hbox), widget->button, FALSE, TRUE, 0);
123 
124  gtk_container_add (GTK_CONTAINER (widget), widget->hbox);
125 
126  GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS);
127  gtk_widget_add_events (GTK_WIDGET (widget), GDK_KEY_PRESS_MASK);
128  gtk_widget_add_events (GTK_WIDGET (widget), GDK_KEY_RELEASE_MASK);
129 }
130 
131 static void
132 gnc_popup_entry_class_init (GncPopupEntryClass *klass)
133 {
134  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
135 
136  widget_class->key_press_event = gpw_key_press_event;
137 
138  gtk_object_add_arg_type ("GncPopupEntry::editing-canceled", GTK_TYPE_BOOL, GTK_ARG_READWRITE, ARG_EDITING_CANCELED);
139 
140  parent_class = GTK_EVENT_BOX_CLASS (g_type_class_peek_parent (klass));
141 
142  signals[ARROW_CLICKED] = g_signal_new
143  ("arrow-clicked",
144  G_TYPE_FROM_CLASS (klass),
145  G_SIGNAL_RUN_LAST,
146  0,
147  NULL, NULL,
148  g_cclosure_marshal_VOID__VOID,
149  G_TYPE_NONE, 0);
150 
151 }
152 
153 static void
154 gpw_arrow_clicked (GtkWidget *button, GncPopupEntry *widget)
155 {
156  g_signal_emit (widget, signals[ARROW_CLICKED], 0);
157 }
158 
159 /* GtkCellEditable method implementations
160  */
161 static void
162 gtk_cell_editable_entry_activated (GtkEntry *entry, GncPopupEntry *widget)
163 {
164  gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (widget));
165  gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (widget));
166 }
167 
168 static gboolean
169 gtk_cell_editable_key_press_event (GtkEntry *entry,
170  GdkEventKey *key_event,
171  GncPopupEntry *widget)
172 {
173  const char *date_string;
174  gint year = 0, month = 0, day = 0;
175  struct tm when;
176 
177  if (key_event->keyval == GDK_Escape)
178  {
179  widget->editing_canceled = TRUE;
180 
181  gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (widget));
182  gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (widget));
183 
184  return TRUE;
185  }
186 
187  date_string = gtk_entry_get_text (entry);
188 
189  memset (&when, 0, sizeof (when));
190 
191  if (qof_scan_date (date_string, &day, &month, &year))
192  {
193  when.tm_year = year - 1900;
194  when.tm_mon = month - 1 ;
195  when.tm_mday = day;
196 
197  if (!gnc_handle_date_accelerator (key_event, &when, date_string))
198  return FALSE;
199 
200  gtk_entry_set_text (entry, qof_print_date (gnc_mktime (&when)));
201  gtk_widget_grab_focus (GTK_WIDGET (entry));
202  return TRUE;
203  }
204  return FALSE;
205 }
206 
207 static gboolean
208 gpw_key_press_event (GtkWidget *box,
209  GdkEventKey *key_event)
210 {
211  GncPopupEntry *widget = GNC_POPUP_ENTRY (box);
212  GdkEvent tmp_event;
213 
214  gtk_widget_grab_focus (widget->entry);
215 
216  if (key_event->keyval == GDK_Escape)
217  {
218  widget->editing_canceled = TRUE;
219 
220  gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (widget));
221  gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (widget));
222 
223  return TRUE;
224  }
225 
226  if (key_event->keyval == GDK_Left)
227  {
228  gtk_editable_set_position (GTK_EDITABLE (widget->entry), 0);
229  return TRUE;
230  }
231 
232  if (key_event->keyval == GDK_Right)
233  {
234  gtk_editable_set_position (GTK_EDITABLE (widget->entry), -1);
235  return TRUE;
236  }
237 
238  /* Hackish :/ Synthesize a key press event for the entry. */
239  memcpy (&tmp_event, key_event, sizeof (GdkEventKey));
240 
241  tmp_event.key.window = widget->entry->window;
242  tmp_event.key.send_event = TRUE;
243 
244  gtk_widget_event (widget->entry, &tmp_event);
245 
246  return GTK_WIDGET_CLASS (parent_class)->key_press_event (GTK_WIDGET (widget),
247  key_event);
248 }
249 
250 static void
251 gpw_start_editing (GtkCellEditable *cell_editable,
252  GdkEvent *event)
253 {
254  GncPopupEntry *widget = GNC_POPUP_ENTRY (cell_editable);
255 
256  gtk_editable_select_region (GTK_EDITABLE (widget->entry), 0, -1);
257 
258  g_signal_connect (G_OBJECT (widget->entry),
259  "activate",
260  G_CALLBACK (gtk_cell_editable_entry_activated),
261  widget);
262  g_signal_connect (G_OBJECT (widget->entry),
263  "key_press_event",
264  G_CALLBACK (gtk_cell_editable_key_press_event),
265  widget);
266  g_signal_connect (G_OBJECT (widget->button),
267  "clicked",
268  (GCallback) gpw_arrow_clicked,
269  widget);
270 }
271 
272 static void
273 gpw_cell_editable_init (GtkCellEditableIface *iface)
274 {
275  iface->start_editing = gpw_start_editing;
276 }
277 
278 void
279 gnc_popup_entry_set_text (GncPopupEntry *popup, const gchar *text)
280 {
281  g_return_if_fail (GNC_IS_POPUP_ENTRY (popup));
282 
283  gtk_entry_set_text (GTK_ENTRY (popup->entry), text ? text : "");
284 }
285 
286 const gchar *
287 gnc_popup_entry_get_text (GncPopupEntry *popup)
288 {
289  g_return_val_if_fail (GNC_IS_POPUP_ENTRY (popup), NULL);
290 
291  return gtk_entry_get_text (GTK_ENTRY (popup->entry));
292 }
293 
294 gint
295 gnc_popup_get_button_width (void)
296 {
297  GtkWidget *window, *button, *arrow;
298  gint width;
299 
300  GtkRequisition req;
301 
302  window = gtk_window_new (GTK_WINDOW_POPUP);
303 
304  button = gtk_button_new ();
305  gtk_widget_show (button);
306  gtk_container_add (GTK_CONTAINER (window), button);
307 
308  arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_OUT);
309  gtk_widget_show (arrow);
310  gtk_container_add (GTK_CONTAINER (button), arrow);
311 
312  gtk_window_move (GTK_WINDOW (window), -500, -500);
313  gtk_widget_show (window);
314 
315  gtk_widget_size_request (window, &req);
316 
317  width = req.width;
318 
319  gtk_widget_destroy (window);
320 
321  return width;
322 }
323 
Date and Time handling routines.
char * qof_print_date(time64 secs)
time64 gnc_mktime(struct tm *time)
calculate seconds from the epoch given a time struct
gboolean qof_scan_date(const char *buff, int *day, int *month, int *year)