Added gtk_cell_area_activate_cell() and some cell editing management

Now:
  - The current edit cell and editable widget in use can be fetched
    with properties and accessors
  - gtk_cell_area_activate_cell() handles bookkeeping of the currently
    edited cell, starting the editing of a cell, activating a cell etc
  - Exported signals are available on GtkCellArea: "editing-started",
    "editing-canceled", "editing-done", "remove-editable".
  - Upon receiving GDK_KEY_Escape current editing gets canceled.
This commit is contained in:
Tristan Van Berkom 2010-11-06 15:29:27 +09:00
parent 017f637a76
commit cbe1154e51
2 changed files with 303 additions and 74 deletions

View File

@ -117,6 +117,20 @@ static void cell_attribute_free (CellAttribute *attribute);
static gint cell_attribute_find (CellAttribute *cell_attribute,
const gchar *attribute);
/* Internal signal emissions */
static void gtk_cell_area_editing_started (GtkCellArea *area,
GtkCellRenderer *renderer,
GtkCellEditable *editable);
static void gtk_cell_area_editing_canceled (GtkCellArea *area,
GtkCellRenderer *renderer);
static void gtk_cell_area_editing_done (GtkCellArea *area,
GtkCellRenderer *renderer,
GtkCellEditable *editable);
static void gtk_cell_area_remove_editable (GtkCellArea *area,
GtkCellRenderer *renderer,
GtkCellEditable *editable);
/* Struct to pass data along while looping over
* cell renderers to apply attributes
*/
@ -144,7 +158,15 @@ struct _GtkCellAreaPrivate
* of gtk_cell_area_apply_attributes() */
gchar *current_path;
/* Current cell being edited and editable widget used */
GtkCellEditable *edit_widget;
GtkCellRenderer *edited_cell;
/* Signal connections to the editable widget */
gulong editing_done_id;
gulong remove_widget_id;
/* Currently focused cell */
GtkCellRenderer *focus_cell;
guint can_focus : 1;
@ -157,12 +179,16 @@ enum {
PROP_CELL_MARGIN_TOP,
PROP_CELL_MARGIN_BOTTOM,
PROP_FOCUS_CELL,
PROP_EDITED_CELL
PROP_EDITED_CELL,
PROP_EDIT_WIDGET
};
enum {
SIGNAL_FOCUS_LEAVE,
SIGNAL_EDITING_STARTED,
SIGNAL_EDITING_CANCELED,
SIGNAL_EDITING_DONE,
SIGNAL_REMOVE_EDITABLE,
LAST_SIGNAL
};
@ -200,6 +226,12 @@ gtk_cell_area_init (GtkCellArea *area)
priv->cell_border.bottom = 0;
priv->focus_cell = NULL;
priv->edited_cell = NULL;
priv->edit_widget = NULL;
priv->can_focus = FALSE;
priv->editing_done_id = 0;
priv->remove_widget_id = 0;
}
static void
@ -255,6 +287,38 @@ gtk_cell_area_class_init (GtkCellAreaClass *class)
GTK_TYPE_CELL_EDITABLE,
G_TYPE_STRING);
cell_area_signals[SIGNAL_EDITING_CANCELED] =
g_signal_new (I_("editing-canceled"),
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_FIRST,
0, /* No class closure here */
NULL, NULL,
_gtk_marshal_VOID__OBJECT,
G_TYPE_NONE, 1,
GTK_TYPE_CELL_RENDERER);
cell_area_signals[SIGNAL_EDITING_DONE] =
g_signal_new (I_("editing-done"),
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_FIRST,
0, /* No class closure here */
NULL, NULL,
_gtk_marshal_VOID__OBJECT_OBJECT,
G_TYPE_NONE, 2,
GTK_TYPE_CELL_RENDERER,
GTK_TYPE_CELL_EDITABLE);
cell_area_signals[SIGNAL_REMOVE_EDITABLE] =
g_signal_new (I_("remove-editable"),
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_FIRST,
0, /* No class closure here */
NULL, NULL,
_gtk_marshal_VOID__OBJECT_OBJECT,
G_TYPE_NONE, 2,
GTK_TYPE_CELL_RENDERER,
GTK_TYPE_CELL_EDITABLE);
/* Properties */
g_object_class_install_property (object_class,
PROP_CELL_MARGIN_LEFT,
@ -318,6 +382,15 @@ gtk_cell_area_class_init (GtkCellAreaClass *class)
GTK_TYPE_CELL_RENDERER,
GTK_PARAM_READWRITE));
g_object_class_install_property (object_class,
PROP_EDIT_WIDGET,
g_param_spec_object
("edit-widget",
P_("Edit Widget"),
P_("The widget currently editing the edited cell"),
GTK_TYPE_CELL_RENDERER,
GTK_PARAM_READWRITE));
/* Pool for Cell Properties */
if (!cell_property_pool)
cell_property_pool = g_param_spec_pool_new (FALSE);
@ -427,6 +500,7 @@ gtk_cell_area_dispose (GObject *object)
/* Remove any ref to a focused/edited cell */
gtk_cell_area_set_focus_cell (GTK_CELL_AREA (object), NULL);
gtk_cell_area_set_edited_cell (GTK_CELL_AREA (object), NULL);
gtk_cell_area_set_edit_widget (GTK_CELL_AREA (object), NULL);
G_OBJECT_CLASS (gtk_cell_area_parent_class)->dispose (object);
}
@ -456,6 +530,12 @@ gtk_cell_area_set_property (GObject *object,
case PROP_FOCUS_CELL:
gtk_cell_area_set_focus_cell (area, (GtkCellRenderer *)g_value_get_object (value));
break;
case PROP_EDITED_CELL:
gtk_cell_area_set_edited_cell (area, (GtkCellRenderer *)g_value_get_object (value));
break;
case PROP_EDIT_WIDGET:
gtk_cell_area_set_edit_widget (area, (GtkCellEditable *)g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -488,6 +568,12 @@ gtk_cell_area_get_property (GObject *object,
case PROP_FOCUS_CELL:
g_value_set_object (value, priv->focus_cell);
break;
case PROP_EDITED_CELL:
g_value_set_object (value, priv->edited_cell);
break;
case PROP_EDIT_WIDGET:
g_value_set_object (value, priv->edit_widget);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -517,61 +603,30 @@ gtk_cell_area_real_event (GtkCellArea *area,
key_event->keyval == GDK_KEY_ISO_Enter ||
key_event->keyval == GDK_KEY_KP_Enter))
{
/* Activate or Edit the currently focused cell */
GtkCellRendererMode mode;
GdkRectangle background_area;
GdkRectangle inner_area;
GdkRectangle background_area;
/* Get the allocation of the focused cell.
*/
gtk_cell_area_get_cell_allocation (area, iter, widget, priv->focus_cell,
cell_area, &background_area);
/* Remove margins from the background area to produce the cell area.
*/
gtk_cell_area_inner_cell_area (area, &background_area, &inner_area);
/* Activate or Edit the currently focused cell */
if (gtk_cell_area_activate_cell (area, widget, priv->focus_cell, event,
&background_area, flags))
return TRUE;
}
else if (priv->edited_cell &&
(key_event->keyval == GDK_KEY_Escape))
{
/* Cancel editing of the cell renderer */
gtk_cell_renderer_stop_editing (priv->edited_cell, TRUE);
/* XXX Need to do some extra right-to-left casing either here
* or inside the above called apis.
*/
/* Signal that editing has been canceled */
gtk_cell_area_editing_canceled (area, priv->edited_cell);
g_object_get (priv->focus_cell, "mode", &mode, NULL);
if (mode == GTK_CELL_RENDERER_MODE_ACTIVATABLE)
{
if (gtk_cell_renderer_activate (priv->focus_cell,
event, widget,
priv->current_path,
&background_area,
&inner_area,
flags))
return TRUE;
}
else if (mode == GTK_CELL_RENDERER_MODE_EDITABLE)
{
GtkCellEditable *editable_widget;
editable_widget =
gtk_cell_renderer_start_editing (priv->focus_cell,
event, widget,
priv->current_path,
&background_area,
&inner_area,
flags);
if (editable_widget != NULL)
{
g_return_val_if_fail (GTK_IS_CELL_EDITABLE (editable_widget), FALSE);
gtk_cell_area_set_edited_cell (area, priv->focus_cell);
/* Signal that editing started so that callers can get
* a handle on the editable_widget */
gtk_cell_area_editing_started (area, priv->focus_cell, editable_widget);
return TRUE;
}
}
/* Remove any references to the editable widget */
gtk_cell_area_set_edited_cell (area, NULL);
gtk_cell_area_set_edit_widget (area, NULL);
}
}
@ -1825,6 +1880,71 @@ gtk_cell_area_get_focus_cell (GtkCellArea *area)
return priv->focus_cell;
}
/*************************************************************
* API: Cell Activation/Editing *
*************************************************************/
static void
gtk_cell_area_editing_started (GtkCellArea *area,
GtkCellRenderer *renderer,
GtkCellEditable *editable)
{
g_signal_emit (area, cell_area_signals[SIGNAL_EDITING_STARTED], 0,
renderer, editable, area->priv->current_path);
}
static void
gtk_cell_area_editing_canceled (GtkCellArea *area,
GtkCellRenderer *renderer)
{
g_signal_emit (area, cell_area_signals[SIGNAL_EDITING_CANCELED], 0, renderer);
}
static void
gtk_cell_area_editing_done (GtkCellArea *area,
GtkCellRenderer *renderer,
GtkCellEditable *editable)
{
g_signal_emit (area, cell_area_signals[SIGNAL_EDITING_DONE], 0, renderer, editable);
}
static void
gtk_cell_area_remove_editable (GtkCellArea *area,
GtkCellRenderer *renderer,
GtkCellEditable *editable)
{
g_signal_emit (area, cell_area_signals[SIGNAL_REMOVE_EDITABLE], 0, renderer, editable);
}
static void
cell_area_editing_done_cb (GtkCellEditable *editable,
GtkCellArea *area)
{
GtkCellAreaPrivate *priv = area->priv;
g_assert (priv->edit_widget == editable);
g_assert (priv->edited_cell != NULL);
gtk_cell_area_editing_done (area, priv->edited_cell, priv->edit_widget);
}
static void
cell_area_remove_widget_cb (GtkCellEditable *editable,
GtkCellArea *area)
{
GtkCellAreaPrivate *priv = area->priv;
g_assert (priv->edit_widget == editable);
g_assert (priv->edited_cell != NULL);
gtk_cell_area_remove_editable (area, priv->edited_cell, priv->edit_widget);
/* Now that we're done with editing the widget and it can be removed,
* remove our references to the widget and disconnect handlers */
gtk_cell_area_set_edited_cell (area, NULL);
gtk_cell_area_set_edit_widget (area, NULL);
}
void
gtk_cell_area_set_edited_cell (GtkCellArea *area,
GtkCellRenderer *renderer)
@ -1862,6 +1982,126 @@ gtk_cell_area_get_edited_cell (GtkCellArea *area)
return priv->edited_cell;
}
void
gtk_cell_area_set_edit_widget (GtkCellArea *area,
GtkCellEditable *editable)
{
GtkCellAreaPrivate *priv;
g_return_if_fail (GTK_IS_CELL_AREA (area));
g_return_if_fail (editable == NULL || GTK_IS_CELL_EDITABLE (editable));
priv = area->priv;
if (priv->edit_widget != editable)
{
if (priv->edit_widget)
{
g_signal_handler_disconnect (priv->edit_widget, priv->editing_done_id);
g_signal_handler_disconnect (priv->edit_widget, priv->remove_widget_id);
g_object_unref (priv->edit_widget);
}
priv->edit_widget = editable;
if (priv->edit_widget)
{
priv->editing_done_id =
g_signal_connect (priv->edit_widget, "editing-done",
G_CALLBACK (cell_area_editing_done_cb), area);
priv->remove_widget_id =
g_signal_connect (priv->edit_widget, "remove-widget",
G_CALLBACK (cell_area_remove_widget_cb), area);
g_object_ref (priv->edit_widget);
}
g_object_notify (G_OBJECT (area), "edit-widget");
}
}
GtkCellEditable *
gtk_cell_area_get_edit_widget (GtkCellArea *area)
{
GtkCellAreaPrivate *priv;
g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL);
priv = area->priv;
return priv->edit_widget;
}
gboolean
gtk_cell_area_activate_cell (GtkCellArea *area,
GtkWidget *widget,
GtkCellRenderer *renderer,
GdkEvent *event,
const GdkRectangle *cell_area,
GtkCellRendererState flags)
{
GtkCellRendererMode mode;
GdkRectangle inner_area;
GtkCellAreaPrivate *priv;
g_return_val_if_fail (GTK_IS_CELL_AREA (area), FALSE);
g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
g_return_val_if_fail (GTK_IS_CELL_RENDERER (renderer), FALSE);
g_return_val_if_fail (event != NULL, FALSE);
g_return_val_if_fail (cell_area != NULL, FALSE);
priv = area->priv;
/* Remove margins from the background area to produce the cell area.
*
* XXX Maybe have to do some rtl mode treatment here...
*/
gtk_cell_area_inner_cell_area (area, cell_area, &inner_area);
g_object_get (renderer, "mode", &mode, NULL);
if (mode == GTK_CELL_RENDERER_MODE_ACTIVATABLE)
{
if (gtk_cell_renderer_activate (renderer,
event, widget,
priv->current_path,
cell_area,
&inner_area,
flags))
return TRUE;
}
else if (mode == GTK_CELL_RENDERER_MODE_EDITABLE)
{
GtkCellEditable *editable_widget;
editable_widget =
gtk_cell_renderer_start_editing (renderer,
event, widget,
priv->current_path,
cell_area,
&inner_area,
flags);
if (editable_widget != NULL)
{
g_return_val_if_fail (GTK_IS_CELL_EDITABLE (editable_widget), FALSE);
gtk_cell_area_set_edited_cell (area, renderer);
gtk_cell_area_set_edit_widget (area, editable_widget);
/* Signal that editing started so that callers can get
* a handle on the editable_widget */
gtk_cell_area_editing_started (area, priv->focus_cell, editable_widget);
return TRUE;
}
}
return FALSE;
}
/*************************************************************
* API: Margins *
*************************************************************/
@ -1969,25 +2209,9 @@ gtk_cell_area_set_cell_margin_bottom (GtkCellArea *area,
}
}
/* For convenience in area implementations */
void
gtk_cell_area_editing_started (GtkCellArea *area,
GtkCellRenderer *renderer,
GtkCellEditable *editable)
{
GtkCellAreaPrivate *priv;
g_return_if_fail (GTK_IS_CELL_AREA (area));
priv = area->priv;
g_signal_emit (area, cell_area_signals[SIGNAL_EDITING_STARTED], 0,
renderer, editable, priv->current_path);
}
void
gtk_cell_area_inner_cell_area (GtkCellArea *area,
GdkRectangle *background_area,
const GdkRectangle *background_area,
GdkRectangle *cell_area)
{
GtkCellAreaPrivate *priv;

View File

@ -277,11 +277,21 @@ gboolean gtk_cell_area_get_can_focus (GtkCellArea
void gtk_cell_area_set_focus_cell (GtkCellArea *area,
GtkCellRenderer *renderer);
GtkCellRenderer *gtk_cell_area_get_focus_cell (GtkCellArea *area);
void gtk_cell_area_set_edited_cell (GtkCellArea *area,
GtkCellRenderer *renderer);
GtkCellRenderer *gtk_cell_area_get_edited_cell (GtkCellArea *area);
/* Cell Activation/Editing */
void gtk_cell_area_set_edited_cell (GtkCellArea *area,
GtkCellRenderer *renderer);
GtkCellRenderer *gtk_cell_area_get_edited_cell (GtkCellArea *area);
void gtk_cell_area_set_edit_widget (GtkCellArea *area,
GtkCellEditable *editable);
GtkCellEditable *gtk_cell_area_get_edit_widget (GtkCellArea *area);
gboolean gtk_cell_area_activate_cell (GtkCellArea *area,
GtkWidget *widget,
GtkCellRenderer *renderer,
GdkEvent *event,
const GdkRectangle *cell_area,
GtkCellRendererState flags);
/* Margins */
gint gtk_cell_area_get_cell_margin_left (GtkCellArea *area);
@ -299,14 +309,9 @@ void gtk_cell_area_set_cell_margin_bottom (GtkCellArea
/* Functions for area implementations */
/* Signal that editing started on the area (fires the "editing-started" signal) */
void gtk_cell_area_editing_started (GtkCellArea *area,
GtkCellRenderer *renderer,
GtkCellEditable *editable);
/* Distinguish the inner cell area from the whole requested area including margins */
void gtk_cell_area_inner_cell_area (GtkCellArea *area,
GdkRectangle *cell_area,
const GdkRectangle *cell_area,
GdkRectangle *inner_cell_area);
/* Request the size of a cell while respecting the cell margins (requests are margin inclusive) */