Commit f436ff97 authored by Jody Goldberg's avatar Jody Goldberg Committed by Jody Goldberg

rewrite.

2002-03-12  Jody Goldberg <jody@gnome.org>

	* widget-editable-label.c : rewrite.

2002-03-12  Jody Goldberg <jody@gnome.org>

	* src/workbook-control-gui.c (cb_sheet_label_edit_finished) : renamed
	  from cb_sheet_label_changed and handle NULL text as cancel.
	(cb_sheet_label_edit_stopped) : deleted.
parent 0e816f89
...@@ -17,7 +17,6 @@ Release Critical ...@@ -17,7 +17,6 @@ Release Critical
- Summary dialog is non-functional. - Summary dialog is non-functional.
- Function selection dialog is broken, and it sucks. - Function selection dialog is broken, and it sucks.
- Go-to cell dialog is shot, make it a guru. - Go-to cell dialog is shot, make it a guru.
- rewrite editable-lable to be utf8 clean
1.2 Targets 1.2 Targets
----------- -----------
......
2002-03-12 Jody Goldberg <jody@gnome.org> 2002-03-12 Jody Goldberg <jody@gnome.org>
* src/workbook-control-gui.c (cb_sheet_label_edit_finished) : renamed
from cb_sheet_label_changed and handle NULL text as cancel.
(cb_sheet_label_edit_stopped) : deleted.
* configure.in : Generate GNOME_Gnumeric.server.in * configure.in : Generate GNOME_Gnumeric.server.in
* Generate GNOME_Gnumeric.server : start to think about this. * Generate GNOME_Gnumeric.server : start to think about this.
......
...@@ -8,6 +8,7 @@ Andreas: ...@@ -8,6 +8,7 @@ Andreas:
Jody: Jody:
* Fix XL import of external function names. * Fix XL import of external function names.
* plug item-bar leak. * plug item-bar leak.
* Rewrite editable-label to use gtkentry and to be utf8 clean.
Jukka: Jukka:
* More solver improvements. * More solver improvements.
......
2002-03-12 Jody Goldberg <jody@gnome.org> 2002-03-12 Jody Goldberg <jody@gnome.org>
* src/workbook-control-gui.c (cb_sheet_label_edit_finished) : renamed
from cb_sheet_label_changed and handle NULL text as cancel.
(cb_sheet_label_edit_stopped) : deleted.
* configure.in : Generate GNOME_Gnumeric.server.in * configure.in : Generate GNOME_Gnumeric.server.in
* Generate GNOME_Gnumeric.server : start to think about this. * Generate GNOME_Gnumeric.server : start to think about this.
......
2002-03-12 Jody Goldberg <jody@gnome.org> 2002-03-12 Jody Goldberg <jody@gnome.org>
* src/workbook-control-gui.c (cb_sheet_label_edit_finished) : renamed
from cb_sheet_label_changed and handle NULL text as cancel.
(cb_sheet_label_edit_stopped) : deleted.
* configure.in : Generate GNOME_Gnumeric.server.in * configure.in : Generate GNOME_Gnumeric.server.in
* Generate GNOME_Gnumeric.server : start to think about this. * Generate GNOME_Gnumeric.server : start to think about this.
......
2002-03-12 Jody Goldberg <jody@gnome.org>
* widget-editable-label.c : rewrite.
2002-03-10 Jody Goldberg <jody@gnome.org> 2002-03-10 Jody Goldberg <jody@gnome.org>
* Release 1.1.1 * Release 1.1.1
......
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/* /*
* A label that can be used to edit its text on demand. * widget-editable-label.c: A label that can be used to edit its text on demand
* and provides control over its colour.
*
* Copyright (C) 2002 Jody Goldberg (jody@gnome.org)
* *
* Author: * This program is free software; you can redistribute it and/or modify it
* Miguel de Icaza (miguel@kernel.org) * under the terms of version 2 of the GNU General Public License as published
* by the Free Software Foundation.
* *
* FIXME: add support for drawing the selection. * 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
*/ */
#include <gnumeric-config.h> #include <gnumeric-config.h>
#include <gnumeric.h> #include <gnumeric.h>
...@@ -12,279 +26,143 @@ ...@@ -12,279 +26,143 @@
#include <style-color.h> #include <style-color.h>
#include <gnm-marshalers.h> #include <gnm-marshalers.h>
#include <gdk/gdkkeysyms.h>
#include <gtk/gtkbutton.h>
#include <gtk/gtkentry.h> #include <gtk/gtkentry.h>
#include <gtk/gtkmain.h> #include <gtk/gtkmain.h>
#include <gtk/gtkwindow.h> #include <gdk/gdkkeysyms.h>
#include <libgnomecanvas/gnome-canvas.h>
#include <libgnomecanvas/gnome-canvas-util.h>
#include <libgnomecanvas/gnome-canvas-line.h>
#include <libgnomecanvas/gnome-canvas-text.h>
#include <libgnomecanvas/gnome-canvas-rect-ellipse.h>
#include <gal/util/e-util.h> #include <gal/util/e-util.h>
#define EDITABLE_LABEL_CLASS(k) (G_TYPE_CHECK_CLASS_CAST (k), EDITABLE_LABEL_TYPE) #define EDITABLE_LABEL_CLASS(k) (G_TYPE_CHECK_CLASS_CAST (k), EDITABLE_LABEL_TYPE)
struct _EditableLabel { struct _EditableLabel {
GnomeCanvas canvas; GtkEntry entry;
GnomeCanvasItem *text_item;
StyleColor *color; GdkColor base, text;
char *unedited_text;
char *text;
GtkWidget *toplevel;
GtkWidget *entry;
GnomeCanvasItem *cursor;
GnomeCanvasItem *background;
GtkWidget *style_button;
}; };
typedef struct { typedef struct {
GnomeCanvasClass parent_class; GtkEntryClass entry;
gboolean (* text_changed) (EditableLabel *el, char const *newtext); gboolean (* edit_finished) (EditableLabel *el, char const *newtext);
void (* editing_stopped) (EditableLabel *el);
} EditableLabelClass; } EditableLabelClass;
#define MARGIN 1 #define MARGIN 1
/* Signals we emit */ /* Signals we emit */
enum { enum {
TEXT_CHANGED, EDIT_FINISHED,
EDITING_STOPPED,
LAST_SIGNAL LAST_SIGNAL
}; };
static guint el_signals [LAST_SIGNAL] = { 0 }; #define BASE_TYPE GTK_TYPE_ENTRY
static GnomeCanvasClass *el_parent_class;
static void el_stop_editing (EditableLabel *el);
static void el_change_text (EditableLabel *el, const char *text);
static void
el_entry_activate (GtkWidget *entry, EditableLabel *el)
{
gboolean accept = TRUE;
char const *text = gtk_entry_get_text (GTK_ENTRY (el->entry));
g_signal_emit (G_OBJECT (el), el_signals [TEXT_CHANGED], 0,
text, &accept);
if (accept) static guint el_signals [LAST_SIGNAL] = { 0 };
editable_label_set_text (el, text);
else
editable_label_set_text (el, el->text);
el_stop_editing (el);
}
static void static void
el_edit_sync (EditableLabel *el) el_set_color_gdk (EditableLabel *el, GdkColor *base, GdkColor *text)
{ {
GnomeCanvasGroup *root_group = GNOME_CANVAS_GROUP (GNOME_CANVAS (el)->root); GtkStyle *s = gtk_style_copy (gtk_widget_get_style (GTK_WIDGET (el)));
GtkEntry *entry = GTK_ENTRY (el->entry);
GtkWidget *widget = GTK_WIDGET (el); GdkColor tmp = s->base [GTK_STATE_NORMAL];
GnomeCanvasPoints *points; s->base [GTK_STATE_NORMAL] = *base;
GdkFont *font; *base = tmp;
int cursor_pos;
char const *text; tmp = s->text [GTK_STATE_NORMAL];
s->text [GTK_STATE_NORMAL] = *text;
text = gtk_entry_get_text (entry); *text = tmp;
font = gtk_style_get_font (widget->style);
gtk_widget_set_style (GTK_WIDGET (el), s);
el_change_text (el, text); gtk_style_unref (s);
cursor_pos = gtk_editable_get_position (GTK_EDITABLE (entry));
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_TYPE_CANVAS_LINE,
"points", points,
"fill_color_gdk", &widget->style->fg [GTK_STATE_NORMAL],
NULL);
gnome_canvas_item_set (GNOME_CANVAS_ITEM (el->text_item),
"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_TYPE_CANVAS_RECT,
"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 static void
el_start_editing (EditableLabel *el, char const *text, gboolean select_text) el_stop_editing (EditableLabel *el)
{ {
gtk_widget_grab_focus (GTK_WIDGET (el)); if (el->unedited_text == NULL)
return;
/* Create a GtkEntry to actually do the editing */
el->entry = gtk_entry_new ();
gtk_entry_set_text (GTK_ENTRY (el->entry), text);
g_signal_connect (G_OBJECT (el->entry), "activate",
GTK_SIGNAL_FUNC (el_entry_activate), el);
el->toplevel = gtk_window_new (GTK_WINDOW_POPUP);
gtk_container_add (GTK_CONTAINER (el->toplevel), el->entry);
gtk_widget_set_uposition (el->toplevel, 20000, 20000);
gtk_widget_show_all (el->toplevel);
gtk_grab_add (GTK_WIDGET (el));
if (select_text) g_free (el->unedited_text);
gtk_editable_select_region (GTK_EDITABLE (el->entry), 0, -1); el->unedited_text = NULL;
el_edit_sync (el); el_set_color_gdk (el, &el->base, &el->text);
gtk_editable_set_editable (GTK_EDITABLE (el), FALSE);
gtk_editable_select_region (GTK_EDITABLE (el), 0, 0);
gtk_grab_remove (GTK_WIDGET (el));
} }
static void static void
el_stop_editing (EditableLabel *el) el_entry_activate (GtkEntry *entry, gpointer ignored)
{ {
if (el->toplevel) { EditableLabel *el = EDITABLE_LABEL (entry);
gtk_object_destroy (GTK_OBJECT (el->toplevel)); gboolean reject = FALSE;
el->toplevel = NULL; char const *text = gtk_entry_get_text (entry);
el->entry = NULL;
}
if (el->cursor) { if (el->unedited_text == NULL)
gtk_object_destroy (GTK_OBJECT (el->cursor)); return;
el->cursor = NULL;
}
if (el->background) { if (strcmp (el->unedited_text, text))
gtk_object_destroy (GTK_OBJECT (el->background)); g_signal_emit (G_OBJECT (entry), el_signals [EDIT_FINISHED], 0,
el->background = NULL; text, &reject);
} else
reject = FALSE;
editable_label_set_color (el, el->color); if (reject)
gtk_grab_remove (GTK_WIDGET (el)); editable_label_set_text (el, el->unedited_text);
el_stop_editing (el);
} }
static void static void
el_change_text (EditableLabel *el, const char *text) el_start_editing (EditableLabel *el)
{ {
const char *item_text; if (el->unedited_text != NULL)
item_text = GNOME_CANVAS_TEXT (el->text_item)->text;
if (strcmp (text, item_text) == 0)
return; return;
gnome_canvas_item_set ( el->unedited_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (el)));
GNOME_CANVAS_ITEM (el->text_item), g_signal_connect (G_OBJECT (el),
"text", text, "activate",
NULL); G_CALLBACK (el_entry_activate), NULL);
gtk_widget_queue_resize (GTK_WIDGET (el)); gtk_editable_select_region (GTK_EDITABLE (el), 0, -1);
gtk_editable_set_editable (GTK_EDITABLE (el), TRUE);
el_set_color_gdk (el, &el->base, &el->text);
gtk_widget_grab_focus (GTK_WIDGET (el));
gtk_grab_add (GTK_WIDGET (el));
} }
/*
* GtkObject destroy method override
*/
static void static void
el_destroy (GtkObject *object) el_destroy (GtkObject *object)
{ {
EditableLabel *el = EDITABLE_LABEL (object); EditableLabel *el = EDITABLE_LABEL (object);
GtkObjectClass *base;
el_stop_editing (el); el_stop_editing (el);
if (el->text != NULL) {
g_free (el->text);
el->text = NULL;
}
editable_label_set_color (el, NULL);
if (el->style_button) {
gtk_widget_unref (el->style_button);
el->style_button = NULL;
}
((GtkObjectClass *)el_parent_class)->destroy (object); base = g_type_class_peek (BASE_TYPE);
base->destroy (object);
} }
/*
* GtkWidget size_request method override
*/
static void
el_size_request (GtkWidget *widget, GtkRequisition *requisition)
{
EditableLabel *el = EDITABLE_LABEL (widget);
double width, height;
if (!el->text_item || !GTK_WIDGET_REALIZED (widget)){
requisition->width = 1;
requisition->height = 1;
}
g_object_get (G_OBJECT (el->text_item),
"text_width", &width,
"text_height", &height,
NULL);
requisition->width = width + MARGIN * 2;
requisition->height = height + MARGIN * 2;
}
/*
* GtkWidget button_press_event method override
*/
static gint static gint
el_button_press_event (GtkWidget *widget, GdkEventButton *button) el_button_press_event (GtkWidget *widget, GdkEventButton *button)
{ {
GtkWidgetClass *widget_class; GtkWidgetClass *base;
EditableLabel *el = EDITABLE_LABEL (widget); EditableLabel *el = EDITABLE_LABEL (widget);
if (!el->text) if (button->window != widget->window &&
return FALSE; button->window != el->entry.text_area) {
if (el->entry && button->window != widget->window){
/* Accept the name change */ /* Accept the name change */
el_entry_activate (el->entry, el); el_entry_activate (GTK_ENTRY (el), NULL);
gdk_event_put ((GdkEvent *)button); gdk_event_put ((GdkEvent *)button);
return TRUE; return TRUE;
} }
if (button->type == GDK_2BUTTON_PRESS) { if (button->type == GDK_2BUTTON_PRESS) {
el_start_editing (el, el_start_editing (el);
GNOME_CANVAS_TEXT (el->text_item)->text, TRUE);
return FALSE; return FALSE;
} }
widget_class = g_type_class_peek (GNOME_TYPE_CANVAS); if (el->unedited_text == NULL)
if (widget_class && widget_class->button_press_event) return FALSE;
return widget_class->button_press_event (widget, button);
return FALSE; base = g_type_class_peek (BASE_TYPE);
return base->button_press_event (widget, button);
} }
/* /*
...@@ -293,166 +171,134 @@ el_button_press_event (GtkWidget *widget, GdkEventButton *button) ...@@ -293,166 +171,134 @@ el_button_press_event (GtkWidget *widget, GdkEventButton *button)
* If the label is being edited, we forward the event to the GtkEntry widget. * If the label is being edited, we forward the event to the GtkEntry widget.
*/ */
static gint static gint
el_key_press_event (GtkWidget *widget, GdkEventKey *event) el_key_press_event (GtkWidget *w, GdkEventKey *event)
{ {
EditableLabel *el = EDITABLE_LABEL (widget); GtkWidgetClass *base;
EditableLabel *el = EDITABLE_LABEL (w);
if (!el->entry) if (el->unedited_text == NULL)
return FALSE; return FALSE;
if (event->keyval == GDK_Escape) { if (event->keyval == GDK_Escape) {
gboolean dummy;
el_stop_editing (el); el_stop_editing (el);
el_change_text (el, el->text); g_signal_emit (G_OBJECT (el), el_signals [EDIT_FINISHED], 0,
g_signal_emit (G_OBJECT (el), el_signals [EDITING_STOPPED], 0); NULL, &dummy);
return TRUE; return TRUE;
} }
base = g_type_class_peek (BASE_TYPE);
gtk_widget_event (GTK_WIDGET (el->entry), (GdkEvent *) event); return base->key_press_event (w, event);
/* The previous call could have killed the edition */
if (el->entry)
el_edit_sync (el);
return TRUE;
} }
static void static void
el_realize (GtkWidget *widget) el_size_request (GtkWidget *el, GtkRequisition *req)
{ {
EditableLabel *el = EDITABLE_LABEL (widget); PangoRectangle logical_rect;
PangoLayoutLine *line;
PangoLayout *layout;
GtkWidgetClass *base = g_type_class_peek (BASE_TYPE);
if (GTK_WIDGET_CLASS (el_parent_class)->realize) base->size_request (el, req);
(*GTK_WIDGET_CLASS (el_parent_class)->realize) (widget); layout = gtk_entry_get_layout (GTK_ENTRY (el));
line = pango_layout_get_lines (layout)->data;
pango_layout_line_get_extents (line, NULL, &logical_rect);
gnome_canvas_set_scroll_region (GNOME_CANVAS (el), 0, 0, 32000, 32000); req->width = logical_rect.width / PANGO_SCALE + 2*2;
editable_label_set_color (el, el->color); }
gnome_canvas_item_set (GNOME_CANVAS_ITEM (EDITABLE_LABEL (widget)->text_item),
"font_desc", widget->style->font_desc, static gint
NULL); el_expose_event (GtkWidget *widget, GdkEventExpose *event)
{
GtkWidgetClass *base = g_type_class_peek (BASE_TYPE);
base = g_type_class_peek (BASE_TYPE);
return base->expose_event (widget, event);
} }
static void static void
el_class_init (EditableLabelClass *klass) el_class_init (GtkObjectClass *object_class)
{ {
GtkObjectClass *object_class;
GtkWidgetClass *widget_class; GtkWidgetClass *widget_class;
GnomeCanvasClass *canvas_class;
object_class = (GtkObjectClass *) klass;
widget_class = (GtkWidgetClass *) klass;
canvas_class = (GnomeCanvasClass *) klass;
el_parent_class = gtk_type_class (gnome_canvas_get_type ());
object_class->destroy = el_destroy; object_class->destroy = el_destroy;
widget_class->size_request = el_size_request;
widget_class = (GtkWidgetClass *) object_class;
widget_class->button_press_event = el_button_press_event; widget_class->button_press_event = el_button_press_event;
widget_class->key_press_event = el_key_press_event; widget_class->key_press_event = el_key_press_event;
widget_class->realize = el_realize; widget_class->size_request = el_size_request;
widget_class->expose_event = el_expose_event;
el_signals [TEXT_CHANGED] = g_signal_new ("text_changed", el_signals [EDIT_FINISHED] = g_signal_new ("edit_finished",
EDITABLE_LABEL_TYPE, EDITABLE_LABEL_TYPE,
G_SIGNAL_RUN_LAST, G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (EditableLabelClass, text_changed), G_STRUCT_OFFSET (EditableLabelClass, edit_finished),
(GSignalAccumulator) NULL, NULL, (GSignalAccumulator) NULL, NULL,
gnm__BOOLEAN__POINTER, gnm__BOOLEAN__POINTER,
G_TYPE_BOOLEAN, 1, G_TYPE_POINTER); G_TYPE_BOOLEAN, 1, G_TYPE_POINTER);
}
el_signals [EDITING_STOPPED] = g_signal_new ( "editing_stopped", static void
EDITABLE_LABEL_TYPE, cb_el_changed (GtkWidget *w, gpointer ignored)
G_SIGNAL_RUN_LAST, {
G_STRUCT_OFFSET (EditableLabelClass, editing_stopped), gtk_widget_queue_resize (w);
(GSignalAccumulator) NULL, NULL, }
gnm__VOID__VOID,
GTK_TYPE_NONE, 0); static void
el_init (GObject *obj)
{
g_signal_connect (obj, "changed", G_CALLBACK (cb_el_changed), NULL);
} }
E_MAKE_TYPE (editable_label, "EditableLabel", EditableLabel, E_MAKE_TYPE (editable_label, "EditableLabel", EditableLabel,
el_class_init, NULL, GNOME_TYPE_CANVAS) el_class_init, el_init, BASE_TYPE)
void void
editable_label_set_text (EditableLabel *el, char const *text) editable_label_set_text (EditableLabel *el, char const *text)
{ {
g_return_if_fail (el != NULL); gtk_entry_set_text (GTK_ENTRY (el), text);
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 == NULL) {
GnomeCanvasGroup *root_group;
GtkWidget* text_color_widget;
el->style_button = text_color_widget = gtk_button_new ();
gtk_widget_ref (text_color_widget);
gtk_object_sink (GTK_OBJECT (text_color_widget));
gtk_widget_ensure_style (text_color_widget);
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) MARGIN,
"y", (double) MARGIN,
"fill_color_gdk",
&text_color_widget->style->text[GTK_STATE_NORMAL],
NULL);
gtk_widget_destroy (text_color_widget);
} else
el_change_text (el, text);
} }
char const * char const *
editable_label_get_text (EditableLabel const *el) editable_label_get_text (EditableLabel const *el)
{ {
g_return_val_if_fail (IS_EDITABLE_LABEL (el), ""); g_return_val_if_fail (IS_EDITABLE_LABEL (el), "");
return el->text; return (el->unedited_text != NULL)
? el->unedited_text
: gtk_entry_get_text (GTK_ENTRY (el));
} }
void void
editable_label_set_color (EditableLabel *el, StyleColor *color) editable_label_set_color (EditableLabel *el, GdkColor *color)
{ {
int contrast = color->red + color->green + color->blue;
GdkColor base = *color;
GdkColor text = (contrast >= 0x18000) ? gs_black : gs_white;
g_return_if_fail (IS_EDITABLE_LABEL (el)); g_return_if_fail (IS_EDITABLE_LABEL (el));
g_return_if_fail (el->unedited_text == NULL