/* * A label that can be used to edit its text on demand. * * Author: * Miguel de Icaza (miguel@kernel.org) * */ #include #include #include #include "widget-editable-label.h" #define MARGIN 1 /* * Inside this file we define a number of aliases for the * EditableClass object, a short hand for it is EL. * * I do want to avoid typing. */ #define EL(x) EDITABLE_LABEL(x) typedef EditableLabel El; typedef EditableLabelClass ElClass; /* Signals we emit */ enum { TEXT_CHANGED, LAST_SIGNAL }; static guint el_signals [LAST_SIGNAL] = { 0 }; static GnomeCanvasClass *el_parent_class; static void el_stop_editing (El *el); static void el_change_text (El *el, const char *text); static void el_realize (GtkWidget *widget); static void el_entry_activate (GtkWidget *entry, El *el) { gboolean accept = TRUE; char *text = gtk_entry_get_text (GTK_ENTRY (el->entry)); gtk_signal_emit (GTK_OBJECT (el), el_signals [TEXT_CHANGED], text, &accept); if (accept) editable_label_set_text (el, text); else editable_label_set_text (el, el->text); el_stop_editing (el); } static void el_edit_sync (El *el) { GnomeCanvasGroup *root_group = GNOME_CANVAS_GROUP (GNOME_CANVAS (el)->root); GtkEntry *entry = GTK_ENTRY (el->entry); GtkWidget *widget = GTK_WIDGET (el); GnomeCanvasPoints *points; GdkFont *font; int cursor_pos; char *text; text = gtk_entry_get_text (entry); font = widget->style->font; el_change_text (el, text); cursor_pos = GTK_EDITABLE (entry)->current_pos; points = gnome_canvas_points_new (2); points->coords [0] = gdk_text_width (font, text, cursor_pos) + MARGIN; points->coords [1] = 1 + MARGIN; points->coords [2] = points->coords [0]; points->coords [3] = font->ascent + font->descent - 1 - MARGIN; /* Draw the cursor */ if (!el->cursor) el->cursor = gnome_canvas_item_new ( root_group, gnome_canvas_line_get_type (), "points", points, "fill_color_gdk", &widget->style->fg [GTK_STATE_NORMAL], NULL); else gnome_canvas_item_set ( el->cursor, "points", points, NULL); gnome_canvas_points_free (points); if (!el->background){ el->background = gnome_canvas_item_new ( root_group, gnome_canvas_rect_get_type (), "x1", (double) 0, "y1", (double) 0, "x2", (double) gdk_string_measure (font, text) + MARGIN * 2, "y2", (double) font->ascent + font->descent + MARGIN * 2, "fill_color_gdk", &widget->style->base [GTK_STATE_NORMAL], "outline_color_gdk", &widget->style->fg [GTK_STATE_NORMAL], NULL); /* Just a large enough number, to make sure it goes to the very back */ gnome_canvas_item_lower (el->background, 10); } else gnome_canvas_item_set ( el->background, "x1", (double) 0, "y1", (double) 0, "x2", (double) gdk_string_measure (font, text) + MARGIN * 2, "y2", (double) font->ascent + font->descent + MARGIN * 2, "fill_color_gdk", &widget->style->base [GTK_STATE_NORMAL], NULL); } static void el_start_editing (El *el, const char *text) { GtkWidget *toplevel; /* * Temporarly be focusable */ GTK_WIDGET_SET_FLAGS (GTK_WIDGET (el), GTK_CAN_FOCUS); gtk_widget_grab_focus (GTK_WIDGET (el)); /* * Create a GtkEntry to actually do the editing. */ el->entry = gtk_entry_new (); gtk_entry_set_text (GTK_ENTRY (el->entry), text); gtk_signal_connect (GTK_OBJECT (el->entry), "activate", GTK_SIGNAL_FUNC (el_entry_activate), el); toplevel = gtk_window_new (GTK_WINDOW_POPUP); gtk_container_add (GTK_CONTAINER (toplevel), el->entry); gtk_widget_set_uposition (toplevel, 20000, 20000); gtk_widget_show_all (toplevel); gtk_grab_add (GTK_WIDGET (el)); /* gtk_editable_select_region (GTK_EDITABLE (el->entry, 0, -1)); */ /* * Syncronize the GtkEntry with the label */ el_edit_sync (el); } static void el_stop_editing (El *el) { if (el->entry){ GtkWidget *toplevel = gtk_widget_get_toplevel (el->entry); gtk_object_destroy (GTK_OBJECT (toplevel)); el->entry = NULL; } if (el->cursor){ gtk_object_destroy (GTK_OBJECT (el->cursor)); el->cursor = NULL; } if (el->background){ gtk_object_destroy (GTK_OBJECT (el->background)); el->background = NULL; } gtk_grab_remove (GTK_WIDGET (el)); GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (el), GTK_CAN_FOCUS); } static void el_change_text (El *el, const char *text) { char *item_text; item_text = GNOME_CANVAS_TEXT (el->text_item)->text; if (strcmp (text, item_text) == 0) return; gnome_canvas_item_set ( GNOME_CANVAS_ITEM (el->text_item), "text", text, NULL); gtk_widget_queue_resize (GTK_WIDGET (el)); } /* * GtkObject destroy method override */ static void el_destroy (GtkObject *object) { El *el = EL (object); el_stop_editing (el); if (el->text) g_free (el->text); ((GtkObjectClass *)el_parent_class)->destroy (object); } /* * GtkWidget size_request method override */ static void el_size_request (GtkWidget *widget, GtkRequisition *requisition) { El *el = EL (widget); GdkFont *font; char *text; if (!el->text_item || !GTK_WIDGET_REALIZED (widget)){ requisition->width = 1; requisition->height = 1; } /* The widget is realized, and we have a text item inside */ font = widget->style->font; text = GNOME_CANVAS_TEXT (el->text_item)->text; requisition->width = gdk_string_measure (font, text) + MARGIN * 2; requisition->height = font->ascent + font->descent + MARGIN * 2; } /* * GtkWidget button_press_event method override */ static gint el_button_press_event (GtkWidget *widget, GdkEventButton *button) { El *el = EL (widget); if (!el->text) return FALSE; if (el->entry && button->window != widget->window){ el_stop_editing (el); return FALSE; } if (button->type == GDK_2BUTTON_PRESS){ char *text = GNOME_CANVAS_TEXT (el->text_item)->text; el_start_editing (el, text); return FALSE; } gtk_widget_event (GTK_WIDGET (el)->parent, button); return FALSE; } /* * GtkWidget key_press method override * * If the label is being edited, we forward the event to the GtkEntry widget. */ static gint el_key_press_event (GtkWidget *widget, GdkEventKey *event) { El *el = EL (widget); if (!el->entry) return FALSE; if (event->keyval == GDK_Escape){ el_stop_editing (el); el_change_text (el, el->text); return TRUE; } gtk_widget_event (GTK_WIDGET (el->entry), (GdkEvent *) event); /* The previous call could have killed the edition */ if (el->entry) el_edit_sync (el); return TRUE; } static void el_realize(GtkWidget *widget) { if (GTK_WIDGET_CLASS (el_parent_class)->realize) (*GTK_WIDGET_CLASS (el_parent_class)->realize)(widget); gnome_canvas_item_set(GNOME_CANVAS_ITEM (EL (widget)->text_item), "font_gdk", widget->style->font, NULL); } static void el_class_init (ElClass *class) { GtkObjectClass *object_class; GtkWidgetClass *widget_class; GnomeCanvasClass *canvas_class; object_class = (GtkObjectClass *) class; widget_class = (GtkWidgetClass *) class; canvas_class = (GnomeCanvasClass *) class; el_parent_class = gtk_type_class (gnome_canvas_get_type ()); object_class->destroy = el_destroy; widget_class->size_request = el_size_request; widget_class->button_press_event = el_button_press_event; widget_class->key_press_event = el_key_press_event; widget_class->realize = el_realize; /* The signals */ el_signals [TEXT_CHANGED] = gtk_signal_new ( "text_changed", GTK_RUN_LAST, object_class->type, GTK_SIGNAL_OFFSET (EditableLabelClass, text_changed), gtk_marshal_BOOL__POINTER, GTK_TYPE_BOOL, 1, GTK_TYPE_POINTER); gtk_object_class_add_signals (object_class, el_signals, LAST_SIGNAL); } static void el_init (El *el) { gnome_canvas_set_scroll_region (GNOME_CANVAS (el), 0, 0, 32000, 32000); } GtkType editable_label_get_type (void) { static GtkType el_type = 0; if (!el_type){ GtkTypeInfo el_info = { "EditableLabel", sizeof (EditableLabel), sizeof (EditableLabelClass), (GtkClassInitFunc) el_class_init, (GtkObjectInitFunc) el_init, NULL, /* reserved 1 */ NULL, /* reserved 2 */ (GtkClassInitFunc) NULL }; el_type = gtk_type_unique (gnome_canvas_get_type (), &el_info); } return el_type; } void editable_label_set_text (EditableLabel *el, const char *text) { g_return_if_fail (el != NULL); g_return_if_fail (text != NULL); g_return_if_fail (IS_EDITABLE_LABEL (el)); /* This code is usually invoked with el->text as the name */ if (text != el->text){ if (el->text) g_free (el->text); el->text = g_strdup (text); } if (!el->text_item){ GnomeCanvasGroup *root_group; root_group = GNOME_CANVAS_GROUP (GNOME_CANVAS (el)->root); el->text_item = gnome_canvas_item_new ( root_group, gnome_canvas_text_get_type (), "anchor", GTK_ANCHOR_NORTH_WEST, "text", text, "x", (double) 1, "y", (double) 1, NULL); } else { char *item_text; el_change_text (el, text); } } GtkWidget * editable_label_new (const char *text) { GtkWidget *el; el = gtk_type_new (editable_label_get_type ()); if (text) editable_label_set_text (EDITABLE_LABEL (el), text); return el; }