diff options
-rw-r--r-- | widgets/misc/ChangeLog | 7 | ||||
-rw-r--r-- | widgets/misc/Makefile.am | 2 | ||||
-rw-r--r-- | widgets/misc/e-cell-date-edit.c | 440 | ||||
-rw-r--r-- | widgets/misc/e-cell-date-edit.h | 64 |
4 files changed, 513 insertions, 0 deletions
diff --git a/widgets/misc/ChangeLog b/widgets/misc/ChangeLog index 207ac511e7..7724bcffcb 100644 --- a/widgets/misc/ChangeLog +++ b/widgets/misc/ChangeLog @@ -1,3 +1,10 @@ +2001-02-25 Damon Chaplin <damon@ximian.com> + + * e-cell-date-edit.[hc]: new files to implement an ECell for showing + and editing dates & times. (Unfinished) + + * Makefile.am (libemiscwidgets_a_SOURCES): added e-cell-date-edit.[hc] + 2001-02-24 Not Zed <NotZed@Ximian.com> * e-search-bar.c (add_dropdown): Move the event box into the diff --git a/widgets/misc/Makefile.am b/widgets/misc/Makefile.am index 915799e6a0..bdd397c5d4 100644 --- a/widgets/misc/Makefile.am +++ b/widgets/misc/Makefile.am @@ -15,6 +15,8 @@ libemiscwidgets_a_SOURCES = \ e-calendar.h \ e-calendar-item.c \ e-calendar-item.h \ + e-cell-date-edit.c \ + e-cell-date-edit.h \ e-clipped-label.c \ e-clipped-label.h \ e-dateedit.c \ diff --git a/widgets/misc/e-cell-date-edit.c b/widgets/misc/e-cell-date-edit.c new file mode 100644 index 0000000000..3318590baf --- /dev/null +++ b/widgets/misc/e-cell-date-edit.c @@ -0,0 +1,440 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * Author : + * Damon Chaplin <damon@ximian.com> + * + * Copyright 2001, Ximian, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +/* + * ECellDateEdit - a subclass of ECellPopup used to show a date with a popup + * window to edit it. + */ + +#include <config.h> +#include <gal/util/e-i18n.h> +#include <gdk/gdkkeysyms.h> +#include "gal/util/e-util.h" +#include <gal/e-table/e-table-item.h> +#include "e-cell-date-edit.h" + +/* This depends on ECalendar which is why I didn't put it in gal. */ +#include "e-calendar.h" + +static void e_cell_date_edit_class_init (GtkObjectClass *object_class); +static void e_cell_date_edit_init (ECellDateEdit *ecde); +static void e_cell_date_edit_destroy (GtkObject *object); + +static gint e_cell_date_edit_do_popup (ECellPopup *ecp, + GdkEvent *event); +static void e_cell_date_edit_show_popup (ECellDateEdit *ecde); +static void e_cell_date_edit_get_popup_pos (ECellDateEdit *ecde, + gint *x, + gint *y, + gint *height, + gint *width); + +static void e_cell_date_edit_rebuild_time_list (ECellDateEdit *ecde); + +static int e_cell_date_edit_key_press (GtkWidget *popup_window, + GdkEventKey *event, + ECellDateEdit *ecde); + +static ECellPopupClass *parent_class; + + +E_MAKE_TYPE (e_cell_date_edit, "ECellDateEdit", ECellDateEdit, + e_cell_date_edit_class_init, e_cell_date_edit_init, + e_cell_popup_get_type()); + + +static void +e_cell_date_edit_class_init (GtkObjectClass *object_class) +{ + ECellPopupClass *ecpc = (ECellPopupClass *) object_class; + + object_class->destroy = e_cell_date_edit_destroy; + + ecpc->popup = e_cell_date_edit_do_popup; + + parent_class = gtk_type_class (e_cell_popup_get_type ()); +} + + +static void +e_cell_date_edit_init (ECellDateEdit *ecde) +{ + GtkWidget *frame, *vbox, *table, *label, *entry; + GtkWidget *calendar, *scrolled_window, *list, *bbox; + GtkWidget *now_button, *today_button, *none_button; + + ecde->use_24_hour_format = TRUE; + + ecde->lower_hour = 0; + ecde->upper_hour = 24; + + /* We create one popup window for the ECell, since there will only + ever be one popup in use at a time. */ + ecde->popup_window = gtk_window_new (GTK_WINDOW_POPUP); + + gtk_window_set_policy (GTK_WINDOW (ecde->popup_window), + TRUE, TRUE, FALSE); + + frame = gtk_frame_new (NULL); + gtk_container_add (GTK_CONTAINER (ecde->popup_window), frame); + gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT); + gtk_widget_show (frame); + + vbox = gtk_vbox_new (FALSE, 0); + gtk_container_add (GTK_CONTAINER (frame), vbox); + gtk_widget_show (vbox); + + table = gtk_table_new (3, 2, FALSE); + gtk_box_pack_start (GTK_BOX (vbox), table, TRUE, TRUE, 0); + gtk_table_set_col_spacings (GTK_TABLE (table), 4); + gtk_widget_show (table); + +#if 0 + label = gtk_label_new (_("Date:")); + gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5); + gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1, + GTK_FILL, 0, 0, 0); + gtk_widget_show (label); + + entry = gtk_entry_new (); + gtk_table_attach (GTK_TABLE (table), entry, 0, 1, 1, 2, + GTK_FILL, 0, 0, 0); + gtk_widget_show (entry); +#endif + + calendar = e_calendar_new (); + gnome_canvas_item_set (GNOME_CANVAS_ITEM (E_CALENDAR (calendar)->calitem), + "move_selection_when_moving", FALSE, + NULL); + gtk_table_attach (GTK_TABLE (table), calendar, 0, 1, 0, 3, + GTK_FILL, GTK_FILL, 0, 0); + gtk_widget_show (calendar); + + +#if 0 + label = gtk_label_new (_("Time:")); + gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5); + gtk_table_attach (GTK_TABLE (table), label, 1, 2, 0, 1, + GTK_FILL, 0, 0, 0); + gtk_widget_show (label); + + entry = gtk_entry_new (); + gtk_table_attach (GTK_TABLE (table), entry, 1, 2, 0, 1, + GTK_FILL, 0, 0, 0); + gtk_widget_show (entry); +#endif + + scrolled_window = gtk_scrolled_window_new (NULL, NULL); + gtk_table_attach (GTK_TABLE (table), scrolled_window, 1, 2, 0, 3, + GTK_FILL, GTK_FILL, 0, 0); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), + GTK_POLICY_NEVER, + GTK_POLICY_ALWAYS); + gtk_widget_show (scrolled_window); + + list = gtk_list_new (); + gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrolled_window), list); + gtk_container_set_focus_vadjustment (GTK_CONTAINER (list), + gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (scrolled_window))); + gtk_widget_show (list); + ecde->time_list = list; + e_cell_date_edit_rebuild_time_list (ecde); + + bbox = gtk_hbutton_box_new (); + gtk_container_set_border_width (GTK_CONTAINER (bbox), 4); + gtk_button_box_set_spacing (GTK_BUTTON_BOX (bbox), 2); + gtk_button_box_set_child_ipadding (GTK_BUTTON_BOX (bbox), 2, 0); + gtk_button_box_set_child_size (GTK_BUTTON_BOX (bbox), 0, 0); + gtk_box_pack_start (GTK_BOX (vbox), bbox, FALSE, FALSE, 0); + gtk_widget_show (bbox); + + now_button = gtk_button_new_with_label (_("Now")); + gtk_container_add (GTK_CONTAINER (bbox), now_button); + gtk_widget_show (now_button); +#if 0 + gtk_signal_connect (GTK_OBJECT (now_button), "clicked", + GTK_SIGNAL_FUNC (on_date_popup_now_button_clicked), dedit); +#endif + + today_button = gtk_button_new_with_label (_("Today")); + gtk_container_add (GTK_CONTAINER (bbox), today_button); + gtk_widget_show (today_button); +#if 0 + gtk_signal_connect (GTK_OBJECT (today_button), "clicked", + GTK_SIGNAL_FUNC (on_date_popup_today_button_clicked), dedit); +#endif + + /* Note that we don't show this here, since by default a 'None' date + is not permitted. */ + none_button = gtk_button_new_with_label (_("None")); + gtk_container_add (GTK_CONTAINER (bbox), none_button); +#if 0 + gtk_signal_connect (GTK_OBJECT (none_button), "clicked", + GTK_SIGNAL_FUNC (on_date_popup_none_button_clicked), dedit); +#endif + + + + gtk_signal_connect (GTK_OBJECT (ecde->popup_window), + "key_press_event", + GTK_SIGNAL_FUNC (e_cell_date_edit_key_press), + ecde); +} + + +/** + * e_cell_date_edit_new: + * + * Creates a new ECellDateEdit renderer. + * + * Returns: an ECellDateEdit object. + */ +ECell * +e_cell_date_edit_new (void) +{ + ECellDateEdit *ecde = gtk_type_new (e_cell_date_edit_get_type ()); + + return (ECell*) ecde; +} + + +/* + * GtkObject::destroy method + */ +static void +e_cell_date_edit_destroy (GtkObject *object) +{ + ECellDateEdit *ecde = E_CELL_DATE_EDIT (object); + + gtk_widget_unref (ecde->popup_window); + + GTK_OBJECT_CLASS (parent_class)->destroy (object); +} + + +static gint +e_cell_date_edit_do_popup (ECellPopup *ecp, + GdkEvent *event) +{ + ECellDateEdit *ecde = E_CELL_DATE_EDIT (ecp); + guint32 time; + gint error_code; + + e_cell_date_edit_show_popup (ecde); + + if (event->type == GDK_BUTTON_PRESS) { + time = event->button.time; + } else { + time = event->key.time; + } + + error_code = gdk_pointer_grab (ecde->popup_window->window, TRUE, + GDK_ENTER_NOTIFY_MASK | + GDK_BUTTON_PRESS_MASK | + GDK_BUTTON_RELEASE_MASK | + GDK_POINTER_MOTION_HINT_MASK | + GDK_BUTTON1_MOTION_MASK, + NULL, NULL, time); + if (error_code != 0) + g_warning ("Failed to get pointer grab"); + + gtk_grab_add (ecde->popup_window); + + return TRUE; +} + + +static void +e_cell_date_edit_show_popup (ECellDateEdit *ecde) +{ + gint x, y, width, height, old_width, old_height; + + g_print ("In e_cell_popup_popup_list\n"); + + /* This code is practically copied from GtkCombo. */ + old_width = ecde->popup_window->allocation.width; + old_height = ecde->popup_window->allocation.height; + + e_cell_date_edit_get_popup_pos (ecde, &x, &y, &height, &width); + + gtk_widget_set_uposition (ecde->popup_window, x, y); + gtk_widget_set_usize (ecde->popup_window, width, height); + gtk_widget_realize (ecde->popup_window); + gdk_window_resize (ecde->popup_window->window, width, height); + gtk_widget_show (ecde->popup_window); + + /* FIXME: Set the focus to the first widget. */ + gtk_widget_grab_focus (ecde->popup_window); + + E_CELL_POPUP (ecde)->popup_shown = TRUE; +} + + +/* Calculates the size and position of the popup window (like GtkCombo). */ +static void +e_cell_date_edit_get_popup_pos (ECellDateEdit *ecde, + gint *x, + gint *y, + gint *height, + gint *width) +{ + ECellPopup *ecp = E_CELL_POPUP (ecde); + ETableItem *eti = E_TABLE_ITEM (ecp->popup_cell_view->cell_view.e_table_item_view); + GtkWidget *canvas = GTK_WIDGET (GNOME_CANVAS_ITEM (eti)->canvas); + GtkRequisition popup_requisition; + gint avail_height, screen_width, column_width, row_height; + double x1, y1; + + gdk_window_get_origin (canvas->window, x, y); + + x1 = e_table_header_col_diff (eti->header, 0, eti->editing_col + 1); + y1 = eti_row_diff (eti, 0, eti->editing_row + 1); + column_width = e_table_header_col_diff (eti->header, eti->editing_col, + eti->editing_col + 1); + row_height = eti_row_diff (eti, eti->editing_row, + eti->editing_row + 1); + gnome_canvas_item_i2w (GNOME_CANVAS_ITEM (eti), &x1, &y1); + + *x += x1; + /* The ETable positions don't include the grid lines, I think, so we + add 1. */ + *y += y1 + 1; + + avail_height = gdk_screen_height () - *y; + + /* We'll use the entire screen width if needed, but we save space for + the vertical scrollbar in case we need to show that. */ + screen_width = gdk_screen_width (); + + gtk_widget_size_request (ecde->popup_window, &popup_requisition); + + /* Calculate the desired width. */ + *width = popup_requisition.width; + + /* Use at least the same width as the column. */ + if (*width < column_width) + *width = column_width; + + /* Check if it fits in the available height. */ + if (popup_requisition.height > avail_height) { + /* It doesn't fit, so we see if we have the minimum space + needed. */ + if (*y - row_height > avail_height) { + /* We don't, so we show the popup above the cell + instead of below it. */ + avail_height = *y - row_height; + *y -= (popup_requisition.height + row_height); + if (*y < 0) + *y = 0; + } + } + + /* We try to line it up with the right edge of the column, but we don't + want it to go off the edges of the screen. */ + if (*x > screen_width) + *x = screen_width; + *x -= *width; + if (*x < 0) + *x = 0; + + *height = popup_requisition.height; +} + + +/* This handles key press events in the popup window. If the Escape key is + pressed we hide the popup, and do not change the cell contents. */ +static int +e_cell_date_edit_key_press (GtkWidget *popup_window, + GdkEventKey *event, + ECellDateEdit *ecde) +{ + g_print ("In e_cell_date_edit_key_press\n"); + + /* If the Escape key is pressed we hide the popup. */ + if (event->keyval != GDK_Escape) + return FALSE; + + gtk_grab_remove (ecde->popup_window); + gdk_pointer_ungrab (event->time); + gtk_widget_hide (ecde->popup_window); + + E_CELL_POPUP (ecde)->popup_shown = FALSE; + + return TRUE; +} + + +/* Clears the time list and rebuilds it using the lower_hour, upper_hour + and use_24_hour_format settings. */ +static void +e_cell_date_edit_rebuild_time_list (ECellDateEdit *ecde) +{ + GtkList *list; + GtkWidget *listitem; + char buffer[40], *format; + struct tm tmp_tm; + gint hour, min; + + list = GTK_LIST (ecde->time_list); + + gtk_list_clear_items (list, 0, -1); + + /* Fill the struct tm with some sane values. */ + tmp_tm.tm_year = 2000; + tmp_tm.tm_mon = 0; + tmp_tm.tm_mday = 1; + tmp_tm.tm_sec = 0; + tmp_tm.tm_isdst = 0; + + for (hour = ecde->lower_hour; hour <= ecde->upper_hour; hour++) { + + /* We don't want to display midnight at the end, since that is + really in the next day. */ + if (hour == 24) + break; + + /* We want to finish on upper_hour, with min == 0. */ + for (min = 0; + min == 0 || (min < 60 && hour != ecde->upper_hour); + min += 30) { + tmp_tm.tm_hour = hour; + tmp_tm.tm_min = min; + + if (ecde->use_24_hour_format) + /* This is a strftime() format. %H = hour (0-23), %M = minute. */ + format = _("%H:%M"); + else + /* This is a strftime() format. %I = hour (1-12), %M = minute, %p = am/pm string. */ + format = _("%I:%M %p"); + + strftime (buffer, sizeof (buffer), format, &tmp_tm); + + listitem = gtk_list_item_new_with_label (buffer); + gtk_widget_show (listitem); + gtk_container_add (GTK_CONTAINER (list), listitem); + } + } +} + + diff --git a/widgets/misc/e-cell-date-edit.h b/widgets/misc/e-cell-date-edit.h new file mode 100644 index 0000000000..a852b95361 --- /dev/null +++ b/widgets/misc/e-cell-date-edit.h @@ -0,0 +1,64 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * Author : + * Damon Chaplin <damon@ximian.com> + * + * Copyright 2001, Ximian, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +/* + * ECellDateEdit - a subclass of ECellPopup used to show a date with a popup + * window to edit it. + */ + +#ifndef _E_CELL_DATE_EDIT_H_ +#define _E_CELL_DATE_EDIT_H_ + +#include <gal/e-table/e-cell-popup.h> + +#define E_CELL_DATE_EDIT_TYPE (e_cell_date_edit_get_type ()) +#define E_CELL_DATE_EDIT(o) (GTK_CHECK_CAST ((o), E_CELL_DATE_EDIT_TYPE, ECellDateEdit)) +#define E_CELL_DATE_EDIT_CLASS(k) (GTK_CHECK_CLASS_CAST((k), E_CELL_DATE_EDIT_TYPE, ECellDateEditClass)) +#define E_IS_CELL_DATE_EDIT(o) (GTK_CHECK_TYPE ((o), E_CELL_DATE_EDIT_TYPE)) +#define E_IS_CELL_DATE_EDIT_CLASS(k) (GTK_CHECK_CLASS_TYPE ((k), E_CELL_DATE_EDIT_TYPE)) + + +typedef struct { + ECellPopup parent; + + GtkWidget *popup_window; + GtkWidget *time_list; + + /* This is the range of hours we show in the time popup. */ + gint lower_hour; + gint upper_hour; + + gboolean use_24_hour_format; +} ECellDateEdit; + +typedef struct { + ECellPopupClass parent_class; +} ECellDateEditClass; + + +GtkType e_cell_date_edit_get_type (void); +ECell *e_cell_date_edit_new (void); + + +#endif /* _E_CELL_DATE_EDIT_H_ */ |