/* * Copyright (C) 2007 OpenedHand Ltd * * 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., 51 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* TODO: add undo/redo context menu items */ #include #include #include #include "koto-undo-manager.h" #include "koto-entry.h" static void koto_entry_editable_init (GtkEditableClass *iface); G_DEFINE_TYPE_WITH_CODE (KotoEntry, koto_entry, GTK_TYPE_ENTRY, G_IMPLEMENT_INTERFACE (GTK_TYPE_EDITABLE, koto_entry_editable_init)); static GtkEditableClass *parent_editable_interface = NULL; #define ENTRY_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), KOTO_TYPE_ENTRY, KotoEntryPrivate)) typedef struct { KotoUndoManager *manager; gboolean user_edit; } KotoEntryPrivate; typedef struct { GtkEditable *editable; char *text; int length; int position; } InsertState; static void insert_undo (gpointer closure) { InsertState *state = closure; KotoEntryPrivate *priv = ENTRY_PRIVATE (state->editable); priv->user_edit = FALSE; gtk_editable_delete_text (state->editable, state->position, state->position + state->length); priv->user_edit = TRUE; } static void insert_redo (gpointer closure) { InsertState *state = closure; KotoEntryPrivate *priv = ENTRY_PRIVATE (state->editable); int pos = state->position; priv->user_edit = FALSE; gtk_editable_insert_text (state->editable, state->text, state->length, &pos); priv->user_edit = TRUE; } static void insert_destroy (gpointer closure) { InsertState *state = closure; g_free (state->text); g_slice_free (InsertState, state); } static void koto_entry_insert_text (GtkEditable *editable, const gchar *text, int length, int *position) { KotoEntry *entry = KOTO_ENTRY (editable); KotoEntryPrivate *priv = ENTRY_PRIVATE (entry); if (priv->user_edit) { KotoUndoContext *context; InsertState *state; context = koto_undo_manager_context_begin (priv->manager, _("Insert")); state = g_slice_new0 (InsertState); state->editable = editable; state->text = g_strdup (text); state->length = strlen (text); state->position = *position; koto_undo_context_add (context, koto_undoable_new (insert_undo, insert_redo, insert_destroy, state)); koto_undo_manager_context_end (priv->manager, context); } parent_editable_interface->insert_text (editable, text, length, position); } static void koto_entry_delete_text (GtkEditable *editable, int start_pos, int end_pos) { KotoEntry *entry = KOTO_ENTRY (editable); KotoEntryPrivate *priv = ENTRY_PRIVATE (entry); if (priv->user_edit) { KotoUndoContext *context; InsertState *state; context = koto_undo_manager_context_begin (priv->manager, _("Delete")); state = g_slice_new0 (InsertState); state->editable = editable; state->text = gtk_editable_get_chars (editable, start_pos, end_pos); state->length = strlen (state->text); state->position = start_pos; koto_undo_context_add (context, koto_undoable_new (insert_redo, insert_undo, insert_destroy, state)); koto_undo_manager_context_end (priv->manager, context); } parent_editable_interface->delete_text (editable, start_pos, end_pos); } static void koto_entry_dispose (GObject *object) { KotoEntryPrivate *priv = ENTRY_PRIVATE (object); if (priv->manager) { g_object_unref (priv->manager); priv->manager = NULL; } G_OBJECT_CLASS (koto_entry_parent_class)->dispose (object); } static void koto_entry_editable_init (GtkEditableClass *iface) { parent_editable_interface = g_type_interface_peek_parent (iface); iface->insert_text = koto_entry_insert_text; iface->delete_text = koto_entry_delete_text; } static void koto_entry_class_init (KotoEntryClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (klass, sizeof (KotoEntryPrivate)); object_class->dispose = koto_entry_dispose; } static gboolean on_key_press_event (GtkWidget *widget, GdkEventKey *event) { gboolean control; control = event->state & GDK_CONTROL_MASK; if (control && event->keyval == 'z') { KotoEntryPrivate *priv = ENTRY_PRIVATE (widget); koto_undo_manager_undo (priv->manager); return TRUE; } else if (control && event->keyval == 'Z') { KotoEntryPrivate *priv = ENTRY_PRIVATE (widget); koto_undo_manager_redo (priv->manager); return TRUE; } return FALSE; } static void koto_entry_init (KotoEntry *self) { KotoEntryPrivate *priv = ENTRY_PRIVATE (self); priv->manager = koto_undo_manager_new (); priv->user_edit = TRUE; g_signal_connect (self, "key-press-event", G_CALLBACK (on_key_press_event), NULL); } GtkWidget * koto_entry_new (void) { return g_object_new (KOTO_TYPE_ENTRY, NULL); } void koto_entry_set_text (KotoEntry *entry, const char *text) { KotoEntryPrivate *priv; g_return_if_fail (KOTO_IS_ENTRY (entry)); priv = ENTRY_PRIVATE (entry); priv->user_edit = FALSE; gtk_entry_set_text (GTK_ENTRY (entry), text); priv->user_edit = TRUE; }