diff options
Diffstat (limited to 'widgets/table/e-cell-combo.c')
-rw-r--r-- | widgets/table/e-cell-combo.c | 642 |
1 files changed, 0 insertions, 642 deletions
diff --git a/widgets/table/e-cell-combo.c b/widgets/table/e-cell-combo.c deleted file mode 100644 index 0eb3eb914b..0000000000 --- a/widgets/table/e-cell-combo.c +++ /dev/null @@ -1,642 +0,0 @@ -/* -*- 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 - */ - -/* - * ECellCombo - a subclass of ECellPopup used to support popup lists like a - * GtkCombo widget. It only supports a basic popup list of strings at present, - * with no auto-completion. - */ - -/* - * Notes: (handling pointer grabs and GTK+ grabs is a nightmare!) - * - * o We must grab the pointer when we show the popup, so that if any buttons - * are pressed outside the application we hide the popup. - * - * o We have to be careful when popping up any widgets which also grab the - * pointer at some point, since we will lose our own pointer grab. - * When we pop up a list it will grab the pointer itself when an item is - * selected, and release the grab when the button is released. - * Fortunately we hide the popup at this point, so it isn't a problem. - * But for other types of widgets in the popup it could cause trouble. - * - I think GTK+ should provide help for this (nested pointer grabs?). - * - * o We must set the 'owner_events' flag of the pointer grab to TRUE so that - * pointer events get reported to all the application windows as normal. - * If we don't do this then the widgets in the popup may not work properly. - * - * o We must do a gtk_grab_add() so that we only allow events to go to the - * widgets within the popup (though some special events still get reported - * to the widget owning the window). Doing th gtk_grab_add() on the toplevel - * popup window should be fine. We can then check for any events that should - * close the popup, like the Escape key, or a button press outside the popup. - */ - -#include <config.h> -#include <string.h> /* strcmp() */ -#include <gdk/gdkkeysyms.h> -#include "gal/util/e-util.h" -#include "e-table-item.h" -#include "e-cell-combo.h" -#include "e-cell-text.h" - - -/* The height to make the popup list if there aren't any items in it. */ -#define E_CELL_COMBO_LIST_EMPTY_HEIGHT 15 - -/* The object data key used to store the UTF-8 text of the popup list items. */ -#define E_CELL_COMBO_UTF8_KEY "UTF-8-TEXT" - - -static void e_cell_combo_class_init (GtkObjectClass *object_class); -static void e_cell_combo_init (ECellCombo *ecc); -static void e_cell_combo_destroy (GtkObject *object); - -static gint e_cell_combo_do_popup (ECellPopup *ecp, - GdkEvent *event); -static void e_cell_combo_select_matching_item (ECellCombo *ecc); -static void e_cell_combo_show_popup (ECellCombo *ecc); -static void e_cell_combo_get_popup_pos (ECellCombo *ecc, - gint *x, - gint *y, - gint *height, - gint *width); - -static gint e_cell_combo_button_press (GtkWidget *popup_window, - GdkEvent *event, - ECellCombo *ecc); -static gint e_cell_combo_button_release (GtkWidget *popup_window, - GdkEventButton *event, - ECellCombo *ecc); -static int e_cell_combo_key_press (GtkWidget *popup_window, - GdkEventKey *event, - ECellCombo *ecc); - -static void e_cell_combo_update_cell (ECellCombo *ecc); -static void e_cell_combo_restart_edit (ECellCombo *ecc); - - -static ECellPopupClass *parent_class; - - -E_MAKE_TYPE (e_cell_combo, "ECellCombo", ECellCombo, - e_cell_combo_class_init, e_cell_combo_init, - e_cell_popup_get_type()); - - -static void -e_cell_combo_class_init (GtkObjectClass *object_class) -{ - ECellPopupClass *ecpc = (ECellPopupClass *) object_class; - - object_class->destroy = e_cell_combo_destroy; - - ecpc->popup = e_cell_combo_do_popup; - - parent_class = gtk_type_class (e_cell_popup_get_type ()); -} - - -static void -e_cell_combo_init (ECellCombo *ecc) -{ - GtkWidget *frame; - - /* We create one popup window for the ECell, since there will only - ever be one popup in use at a time. */ - ecc->popup_window = gtk_window_new (GTK_WINDOW_POPUP); - - gtk_window_set_policy (GTK_WINDOW (ecc->popup_window), - TRUE, TRUE, FALSE); - - frame = gtk_frame_new (NULL); - gtk_container_add (GTK_CONTAINER (ecc->popup_window), frame); - gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT); - gtk_widget_show (frame); - - ecc->popup_scrolled_window = gtk_scrolled_window_new (NULL, NULL); - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (ecc->popup_scrolled_window), - GTK_POLICY_AUTOMATIC, - GTK_POLICY_AUTOMATIC); - GTK_WIDGET_UNSET_FLAGS (GTK_SCROLLED_WINDOW (ecc->popup_scrolled_window)->hscrollbar, GTK_CAN_FOCUS); - GTK_WIDGET_UNSET_FLAGS (GTK_SCROLLED_WINDOW (ecc->popup_scrolled_window)->vscrollbar, GTK_CAN_FOCUS); - gtk_container_add (GTK_CONTAINER (frame), ecc->popup_scrolled_window); - gtk_widget_show (ecc->popup_scrolled_window); - - ecc->popup_list = gtk_list_new (); - gtk_list_set_selection_mode (GTK_LIST (ecc->popup_list), - GTK_SELECTION_BROWSE); - gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (ecc->popup_scrolled_window), ecc->popup_list); - gtk_container_set_focus_vadjustment (GTK_CONTAINER (ecc->popup_list), - gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (ecc->popup_scrolled_window))); - gtk_container_set_focus_hadjustment (GTK_CONTAINER (ecc->popup_list), - gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (ecc->popup_scrolled_window))); - gtk_widget_show (ecc->popup_list); - - gtk_signal_connect (GTK_OBJECT (ecc->popup_window), - "button_press_event", - GTK_SIGNAL_FUNC (e_cell_combo_button_press), - ecc); - /* We use connect_after here so the list updates the selection before - we hide the popup and update the cell. */ - gtk_signal_connect_after (GTK_OBJECT (ecc->popup_window), - "button_release_event", - GTK_SIGNAL_FUNC (e_cell_combo_button_release), - ecc); - gtk_signal_connect (GTK_OBJECT (ecc->popup_window), - "key_press_event", - GTK_SIGNAL_FUNC (e_cell_combo_key_press), ecc); -} - - -/** - * e_cell_combo_new: - * - * Creates a new ECellCombo renderer. - * - * Returns: an ECellCombo object. - */ -ECell * -e_cell_combo_new (void) -{ - ECellCombo *ecc = gtk_type_new (e_cell_combo_get_type ()); - - return (ECell*) ecc; -} - - -/* - * GtkObject::destroy method - */ -static void -e_cell_combo_destroy (GtkObject *object) -{ - ECellCombo *ecc = E_CELL_COMBO (object); - - gtk_widget_destroy (ecc->popup_window); - ecc->popup_window = NULL; - - GTK_OBJECT_CLASS (parent_class)->destroy (object); -} - - - -void -e_cell_combo_set_popdown_strings (ECellCombo *ecc, - GList *strings) -{ - GList *elem; - GtkWidget *listitem; - - g_return_if_fail (E_IS_CELL_COMBO (ecc)); - g_return_if_fail (strings != NULL); - - gtk_list_clear_items (GTK_LIST (ecc->popup_list), 0, -1); - elem = strings; - while (elem) { - char *utf8_text = elem->data; - - /* We store a copy of the UTF-8 text as data inside the - listitem, but convert it to the current locale to go in - the listitem widget. */ - char *locale_text = e_utf8_to_locale_string (utf8_text); - - listitem = gtk_list_item_new_with_label (locale_text); - g_free (locale_text); - - gtk_widget_show (listitem); - gtk_container_add (GTK_CONTAINER (ecc->popup_list), listitem); - - gtk_object_set_data_full (GTK_OBJECT (listitem), - E_CELL_COMBO_UTF8_KEY, - g_strdup (utf8_text), g_free); - - elem = elem->next; - } -} - - -static gint -e_cell_combo_do_popup (ECellPopup *ecp, - GdkEvent *event) -{ - ECellCombo *ecc = E_CELL_COMBO (ecp); - guint32 time; - gint error_code; - - e_cell_combo_show_popup (ecc); - e_cell_combo_select_matching_item (ecc); - - if (event->type == GDK_BUTTON_PRESS) { - GTK_LIST (ecc->popup_list)->drag_selection = TRUE; - time = event->button.time; - } else { - time = event->key.time; - } - - error_code = gdk_pointer_grab (ecc->popup_list->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 (%i)", error_code); - gtk_grab_add (ecc->popup_window); - - return TRUE; -} - - -static void -e_cell_combo_select_matching_item (ECellCombo *ecc) -{ - ECellPopup *ecp = E_CELL_POPUP (ecc); - ECellView *ecv = (ECellView*) ecp->popup_cell_view; - ECellText *ecell_text = E_CELL_TEXT (ecp->child); - ETableItem *eti = E_TABLE_ITEM (ecp->popup_cell_view->cell_view.e_table_item_view); - ETableCol *ecol; - GtkList *list; - GtkWidget *listitem; - GList *elem; - gboolean found = FALSE; - char *cell_text, *list_item_text; - - ecol = e_table_header_get_column (eti->header, ecp->popup_view_col); - cell_text = e_cell_text_get_text (ecell_text, ecv->e_table_model, - ecol->col_idx, ecp->popup_row); - - list = GTK_LIST (ecc->popup_list); - elem = list->children; - while (elem) { - listitem = GTK_WIDGET (elem->data); - - /* We need to compare against the UTF-8 text. */ - list_item_text = gtk_object_get_data (GTK_OBJECT (listitem), - E_CELL_COMBO_UTF8_KEY); - - if (list_item_text && !strcmp (list_item_text, cell_text)) { - found = TRUE; - gtk_list_select_child (list, listitem); - gtk_widget_grab_focus (listitem); - break; - } - - elem = elem->next; - } - - if (!found) { - gtk_list_unselect_all (list); - if (list->children) - gtk_widget_grab_focus (GTK_WIDGET (list->children->data)); - } - - e_cell_text_free_text (ecell_text, cell_text); -} - - -static void -e_cell_combo_show_popup (ECellCombo *ecc) -{ - gint x, y, width, height, old_width, old_height; - - /* This code is practically copied from GtkCombo. */ - old_width = ecc->popup_window->allocation.width; - old_height = ecc->popup_window->allocation.height; - - e_cell_combo_get_popup_pos (ecc, &x, &y, &height, &width); - - /* workaround for gtk_scrolled_window_size_allocate bug */ - if (old_width != width || old_height != height) { - gtk_widget_hide (GTK_SCROLLED_WINDOW (ecc->popup_scrolled_window)->hscrollbar); - gtk_widget_hide (GTK_SCROLLED_WINDOW (ecc->popup_scrolled_window)->vscrollbar); - } - - gtk_widget_set_uposition (ecc->popup_window, x, y); - gtk_widget_set_usize (ecc->popup_window, width, height); - gtk_widget_realize (ecc->popup_window); - gdk_window_resize (ecc->popup_window->window, width, height); - gtk_widget_show (ecc->popup_window); - - E_CELL_POPUP (ecc)->popup_shown = TRUE; -} - - -/* Calculates the size and position of the popup window (like GtkCombo). */ -static void -e_cell_combo_get_popup_pos (ECellCombo *ecc, - gint *x, - gint *y, - gint *height, - gint *width) -{ - ECellPopup *ecp = E_CELL_POPUP (ecc); - ETableItem *eti = E_TABLE_ITEM (ecp->popup_cell_view->cell_view.e_table_item_view); - GtkWidget *canvas = GTK_WIDGET (GNOME_CANVAS_ITEM (eti)->canvas); - GtkBin *popwin; - GtkScrolledWindow *popup; - GtkRequisition list_requisition; - gboolean show_vscroll = FALSE, show_hscroll = FALSE; - gint avail_height, avail_width, min_height, work_height, screen_width; - gint column_width, row_height, scrollbar_width; - double x1, y1; - double wx, wy; - - /* This code is practically copied from GtkCombo. */ - popup = GTK_SCROLLED_WINDOW (ecc->popup_scrolled_window); - popwin = GTK_BIN (ecc->popup_window); - - gdk_window_get_origin (canvas->window, x, y); - - x1 = e_table_header_col_diff (eti->header, 0, eti->editing_col + 1); - y1 = e_table_item_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 = e_table_item_row_diff (eti, eti->editing_row, - eti->editing_row + 1); - gnome_canvas_item_i2w (GNOME_CANVAS_ITEM (eti), &x1, &y1); - - gnome_canvas_world_to_window (GNOME_CANVAS (canvas), - x1, - y1, - &wx, - &wy); - x1 = wx; - y1 = wy; - - *x += x1; - /* The ETable positions don't include the grid lines, I think, so we - add 1. */ - *y += y1 + 1; - - scrollbar_width = popup->vscrollbar->requisition.width - + GTK_SCROLLED_WINDOW_CLASS (GTK_OBJECT (popup)->klass)->scrollbar_spacing; - - 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 (); - avail_width = screen_width - scrollbar_width; - - gtk_widget_size_request (ecc->popup_list, &list_requisition); - min_height = MIN (list_requisition.height, - popup->vscrollbar->requisition.height); - if (!GTK_LIST (ecc->popup_list)->children) - list_requisition.height += E_CELL_COMBO_LIST_EMPTY_HEIGHT; - - /* Calculate the desired width. */ - *width = list_requisition.width - + 2 * popwin->child->style->klass->xthickness - + 2 * GTK_CONTAINER (popwin->child)->border_width - + 2 * GTK_CONTAINER (popup)->border_width - + 2 * GTK_CONTAINER (GTK_BIN (popup)->child)->border_width - + 2 * GTK_BIN (popup)->child->style->klass->xthickness; - - /* Use at least the same width as the column. */ - if (*width < column_width) - *width = column_width; - - /* If it is larger than the available width, use that instead and show - the horizontal scrollbar. */ - if (*width > avail_width) { - *width = avail_width; - show_hscroll = TRUE; - } - - /* Calculate all the borders etc. that we need to add to the height. */ - work_height = (2 * popwin->child->style->klass->ythickness - + 2 * GTK_CONTAINER (popwin->child)->border_width - + 2 * GTK_CONTAINER (popup)->border_width - + 2 * GTK_CONTAINER (GTK_BIN (popup)->child)->border_width - + 2 * GTK_BIN (popup)->child->style->klass->xthickness); - - /* Add on the height of the horizontal scrollbar if we need it. */ - if (show_hscroll) - work_height += popup->hscrollbar->requisition.height + - GTK_SCROLLED_WINDOW_CLASS (GTK_OBJECT (popup)->klass)->scrollbar_spacing; - - /* Check if it fits in the available height. */ - if (work_height + list_requisition.height > avail_height) { - /* It doesn't fit, so we see if we have the minimum space - needed. */ - if (work_height + min_height > avail_height - && *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 -= (work_height + list_requisition.height - + row_height); - if (*y < 0) - *y = 0; - } - } - - /* Check if we still need the vertical scrollbar. */ - if (work_height + list_requisition.height > avail_height) { - *width += scrollbar_width; - show_vscroll = TRUE; - } - - /* 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; - - if (show_vscroll) - *height = avail_height; - else - *height = work_height + list_requisition.height; -} - - - - - - -/* This handles button press events in the popup window. - Note that since we have a pointer grab on this window, we also get button - press events for windows outside the application here, so we hide the popup - window if that happens. We also get propagated events from child widgets - which we ignore. */ -static gint -e_cell_combo_button_press (GtkWidget *popup_window, - GdkEvent *event, - ECellCombo *ecc) -{ - GtkWidget *event_widget; - - event_widget = gtk_get_event_widget (event); - - /* If the button press was for a widget inside the popup list, but - not the popup window itself, then we ignore the event and return - FALSE. Otherwise we will hide the popup. - Note that since we have a pointer grab on the popup list, button - presses outside the application will be reported to this window, - which is why we hide the popup in this case. */ - while (event_widget) { - event_widget = event_widget->parent; - if (event_widget == ecc->popup_list) - return FALSE; - } - - gtk_grab_remove (ecc->popup_window); - gdk_pointer_ungrab (event->button.time); - gtk_widget_hide (ecc->popup_window); - - E_CELL_POPUP (ecc)->popup_shown = FALSE; - - /* We don't want to update the cell here. Since the list is in browse - mode there will always be one item selected, so when we popup the - list one item is selected even if it doesn't match the current text - in the cell. So if you click outside the popup (which is what has - happened here) it is better to not update the cell. */ - /*e_cell_combo_update_cell (ecc);*/ - e_cell_combo_restart_edit (ecc); - - return TRUE; -} - - -/* This handles button release events in the popup window. If the button is - released inside the list, we want to hide the popup window and update the - cell with the new selection. */ -static gint -e_cell_combo_button_release (GtkWidget *popup_window, - GdkEventButton *event, - ECellCombo *ecc) -{ - GtkWidget *event_widget; - - event_widget = gtk_get_event_widget ((GdkEvent*) event); - - /* See if the button was released in the list (or its children). */ - while (event_widget && event_widget != ecc->popup_list) - event_widget = event_widget->parent; - - /* If it wasn't, then we just ignore the event. */ - if (event_widget != ecc->popup_list) - return FALSE; - - /* The button was released inside the list, so we hide the popup and - update the cell to reflect the new selection. */ - gtk_grab_remove (ecc->popup_window); - gdk_pointer_ungrab (event->time); - gtk_widget_hide (ecc->popup_window); - - E_CELL_POPUP (ecc)->popup_shown = FALSE; - - e_cell_combo_update_cell (ecc); - e_cell_combo_restart_edit (ecc); - - return TRUE; -} - - -/* 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_combo_key_press (GtkWidget *popup_window, - GdkEventKey *event, - ECellCombo *ecc) -{ - /* If the Escape key is pressed we hide the popup. */ - if (event->keyval != GDK_Escape - && event->keyval != GDK_Return - && event->keyval != GDK_KP_Enter - && event->keyval != GDK_ISO_Enter - && event->keyval != GDK_3270_Enter) - return FALSE; - - gtk_grab_remove (ecc->popup_window); - gdk_pointer_ungrab (event->time); - gtk_widget_hide (ecc->popup_window); - - E_CELL_POPUP (ecc)->popup_shown = FALSE; - - if (event->keyval != GDK_Escape) - e_cell_combo_update_cell (ecc); - - e_cell_combo_restart_edit (ecc); - - return TRUE; -} - - -static void -e_cell_combo_update_cell (ECellCombo *ecc) -{ - ECellPopup *ecp = E_CELL_POPUP (ecc); - ECellView *ecv = (ECellView*) ecp->popup_cell_view; - ECellText *ecell_text = E_CELL_TEXT (ecp->child); - ETableItem *eti = E_TABLE_ITEM (ecv->e_table_item_view); - ETableCol *ecol; - GtkList *list = GTK_LIST (ecc->popup_list); - GtkListItem *listitem; - gchar *text, *old_text; - - /* Return if no item is selected. */ - if (list->selection == NULL) - return; - - /* Get the text of the selected item. */ - listitem = list->selection->data; - text = gtk_object_get_data (GTK_OBJECT (listitem), - E_CELL_COMBO_UTF8_KEY); - g_return_if_fail (text != NULL); - - /* Compare it with the existing cell contents. */ - ecol = e_table_header_get_column (eti->header, ecp->popup_view_col); - - old_text = e_cell_text_get_text (ecell_text, ecv->e_table_model, - ecol->col_idx, ecp->popup_row); - - /* If they are different, update the cell contents. */ - if (old_text && strcmp (old_text, text)) { - e_cell_text_set_value (ecell_text, ecv->e_table_model, - ecol->col_idx, ecp->popup_row, text); - } - - e_cell_text_free_text (ecell_text, old_text); -} - - -static void -e_cell_combo_restart_edit (ECellCombo *ecc) -{ - /* This doesn't work. ETable stops the edit straight-away again. */ -#if 0 - ECellView *ecv = (ECellView*) ecc->popup_cell_view; - ETableItem *eti = E_TABLE_ITEM (ecv->e_table_item_view); - - e_table_item_enter_edit (eti, ecc->popup_view_col, ecc->popup_row); -#endif -} - - - |