/* * mooentry.c * * Copyright (C) 2004-2005 by Yevgen Muntyan * * 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. * * See COPYING file that comes with this distribution. */ #include MOO_MARSHALS_H #ifndef __MOO__ #include "mooentry.h" #include "gtksourceundomanager.h" #else #include "mooutils/mooentry.h" #include "mooutils/mooutils-gobject.h" #include "mooutils/gtksourceundomanager.h" #endif #include #include #include #include #include struct _MooEntryPrivate { GtkSourceUndoManager *undo_mgr; gboolean enable_undo; gboolean enable_undo_menu; guint user_action_stack; guint use_ctrl_u : 1; }; static void moo_entry_class_init (MooEntryClass *klass); static void moo_entry_editable_init (GtkEditableClass *klass); static void moo_entry_init (MooEntry *entry); static void moo_entry_finalize (GObject *object); static void moo_entry_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); static void moo_entry_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); static void moo_entry_delete_to_start (MooEntry *entry); static void moo_entry_populate_popup (GtkEntry *entry, GtkMenu *menu); static void moo_entry_insert_at_cursor (GtkEntry *entry, const gchar *str); static void moo_entry_delete_from_cursor(GtkEntry *entry, GtkDeleteType type, gint count); static void moo_entry_backspace (GtkEntry *entry); static void moo_entry_cut_clipboard (GtkEntry *entry); static void moo_entry_paste_clipboard (GtkEntry *entry); static void moo_entry_do_insert_text (GtkEditable *editable, const gchar *text, gint length, gint *position); static void moo_entry_do_delete_text (GtkEditable *editable, gint start_pos, gint end_pos); static void moo_entry_insert_text (GtkEditable *editable, const gchar *text, gint length, gint *position); static void moo_entry_delete_text (GtkEditable *editable, gint start_pos, gint end_pos); GType moo_entry_get_type (void) { static GType type = 0; if (!type) { static const GTypeInfo info = { sizeof (MooEntryClass), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) moo_entry_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (MooEntry), 0, (GInstanceInitFunc) moo_entry_init, NULL }; static const GInterfaceInfo editable_info = { (GInterfaceInitFunc) moo_entry_editable_init, NULL, NULL }; type = g_type_register_static (GTK_TYPE_ENTRY, "MooEntry", &info, 0); g_type_add_interface_static (type, GTK_TYPE_EDITABLE, &editable_info); } return type; } enum { PROP_0, PROP_UNDO_MANAGER, PROP_ENABLE_UNDO, PROP_ENABLE_UNDO_MENU }; enum { UNDO, REDO, BEGIN_USER_ACTION, END_USER_ACTION, DELETE_TO_START, NUM_SIGNALS }; static guint signals[NUM_SIGNALS]; static GtkEditableClass *parent_editable_iface; static gpointer moo_entry_parent_class; static void moo_entry_class_init (MooEntryClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GtkEntryClass *entry_class = GTK_ENTRY_CLASS (klass); GtkBindingSet *binding_set; gobject_class->finalize = moo_entry_finalize; gobject_class->set_property = moo_entry_set_property; gobject_class->get_property = moo_entry_get_property; entry_class->populate_popup = moo_entry_populate_popup; entry_class->insert_at_cursor = moo_entry_insert_at_cursor; entry_class->delete_from_cursor = moo_entry_delete_from_cursor; entry_class->backspace = moo_entry_backspace; entry_class->cut_clipboard = moo_entry_cut_clipboard; entry_class->paste_clipboard = moo_entry_paste_clipboard; klass->undo = moo_entry_undo; klass->redo = moo_entry_redo; moo_entry_parent_class = g_type_class_peek_parent (klass); parent_editable_iface = g_type_interface_peek (moo_entry_parent_class, GTK_TYPE_EDITABLE); g_object_class_install_property (gobject_class, PROP_UNDO_MANAGER, g_param_spec_object ("undo-manager", "undo-manager", "undo-manager", GTK_SOURCE_TYPE_UNDO_MANAGER, G_PARAM_READABLE)); g_object_class_install_property (gobject_class, PROP_ENABLE_UNDO, g_param_spec_boolean ("enable-undo", "enable-undo", "enable-undo", TRUE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); g_object_class_install_property (gobject_class, PROP_ENABLE_UNDO_MENU, g_param_spec_boolean ("enable-undo-menu", "enable-undo-menu", "enable-undo-menu", TRUE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); signals[UNDO] = g_signal_new ("undo", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (MooEntryClass, undo), NULL, NULL, _moo_marshal_VOID__VOID, G_TYPE_NONE, 0); signals[REDO] = g_signal_new ("redo", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (MooEntryClass, redo), NULL, NULL, _moo_marshal_VOID__VOID, G_TYPE_NONE, 0); signals[BEGIN_USER_ACTION] = g_signal_new ("begin-user-action", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (MooEntryClass, begin_user_action), NULL, NULL, _moo_marshal_VOID__VOID, G_TYPE_NONE, 0); signals[END_USER_ACTION] = g_signal_new ("end-user-action", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (MooEntryClass, end_user_action), NULL, NULL, _moo_marshal_VOID__VOID, G_TYPE_NONE, 0); signals[DELETE_TO_START] = moo_signal_new_cb ("delete-to-start", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_CALLBACK (moo_entry_delete_to_start), NULL, NULL, _moo_marshal_VOID__VOID, G_TYPE_NONE, 0); binding_set = gtk_binding_set_by_class (klass); gtk_binding_entry_add_signal (binding_set, GDK_z, GDK_CONTROL_MASK, "undo", 0); gtk_binding_entry_add_signal (binding_set, GDK_z, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "redo", 0); gtk_binding_entry_add_signal (binding_set, GDK_u, GDK_CONTROL_MASK, "delete-to-start", 0); } static void moo_entry_editable_init (GtkEditableClass *klass) { klass->do_insert_text = moo_entry_do_insert_text; klass->do_delete_text = moo_entry_do_delete_text; klass->insert_text = moo_entry_insert_text; klass->delete_text = moo_entry_delete_text; } static void moo_entry_init (MooEntry *entry) { entry->priv = g_new0 (MooEntryPrivate, 1); entry->priv->undo_mgr = gtk_source_undo_manager_new (entry); entry->priv->use_ctrl_u = TRUE; } static void moo_entry_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MooEntry *entry = MOO_ENTRY (object); switch (prop_id) { case PROP_ENABLE_UNDO: entry->priv->enable_undo = g_value_get_boolean (value); g_object_notify (object, "enable-undo"); break; case PROP_ENABLE_UNDO_MENU: entry->priv->enable_undo_menu = g_value_get_boolean (value); g_object_notify (object, "enable-undo-menu"); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void moo_entry_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MooEntry *entry = MOO_ENTRY (object); switch (prop_id) { case PROP_ENABLE_UNDO: g_value_set_boolean (value, entry->priv->enable_undo); break; case PROP_ENABLE_UNDO_MENU: g_value_set_boolean (value, entry->priv->enable_undo_menu); break; case PROP_UNDO_MANAGER: g_value_set_object (value, entry->priv->undo_mgr); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void moo_entry_finalize (GObject *object) { MooEntry *entry = MOO_ENTRY (object); g_object_unref (entry->priv->undo_mgr); g_free (entry->priv); G_OBJECT_CLASS (moo_entry_parent_class)->finalize (object); } void moo_entry_undo (MooEntry *entry) { g_return_if_fail (MOO_IS_ENTRY (entry)); if (entry->priv->enable_undo && gtk_source_undo_manager_can_undo (entry->priv->undo_mgr)) gtk_source_undo_manager_undo (entry->priv->undo_mgr); } void moo_entry_redo (MooEntry *entry) { g_return_if_fail (MOO_IS_ENTRY (entry)); if (entry->priv->enable_undo && gtk_source_undo_manager_can_redo (entry->priv->undo_mgr)) gtk_source_undo_manager_redo (entry->priv->undo_mgr); } GtkWidget* moo_entry_new (void) { return g_object_new (MOO_TYPE_ENTRY, NULL); } static void moo_entry_populate_popup (GtkEntry *gtkentry, GtkMenu *menu) { GtkWidget *item; MooEntry *entry = MOO_ENTRY (gtkentry); if (!entry->priv->enable_undo_menu) return; item = gtk_separator_menu_item_new (); gtk_widget_show (item); gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item); item = gtk_image_menu_item_new_from_stock (GTK_STOCK_REDO, NULL); gtk_widget_show (item); gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item); gtk_widget_set_sensitive (item, entry->priv->enable_undo && gtk_source_undo_manager_can_redo (entry->priv->undo_mgr)); g_signal_connect_swapped (item, "activate", G_CALLBACK (moo_entry_redo), entry); item = gtk_image_menu_item_new_from_stock (GTK_STOCK_UNDO, NULL); gtk_widget_show (item); gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item); gtk_widget_set_sensitive (item, entry->priv->enable_undo && gtk_source_undo_manager_can_undo (entry->priv->undo_mgr)); g_signal_connect_swapped (item, "activate", G_CALLBACK (moo_entry_undo), entry); } void moo_entry_begin_user_action (MooEntry *entry) { g_return_if_fail (MOO_IS_ENTRY (entry)); entry->priv->user_action_stack++; if (entry->priv->user_action_stack == 1) g_signal_emit (entry, signals[BEGIN_USER_ACTION], 0); } void moo_entry_end_user_action (MooEntry *entry) { g_return_if_fail (MOO_IS_ENTRY (entry)); g_return_if_fail (entry->priv->user_action_stack > 0); entry->priv->user_action_stack--; if (!entry->priv->user_action_stack) g_signal_emit (entry, signals[END_USER_ACTION], 0); } void moo_entry_begin_not_undoable_action (MooEntry *entry) { g_return_if_fail (MOO_IS_ENTRY (entry)); gtk_source_undo_manager_begin_not_undoable_action (entry->priv->undo_mgr); } void moo_entry_end_not_undoable_action (MooEntry *entry) { g_return_if_fail (MOO_IS_ENTRY (entry)); gtk_source_undo_manager_end_not_undoable_action (entry->priv->undo_mgr); } static void moo_entry_insert_at_cursor (GtkEntry *entry, const gchar *str) { moo_entry_begin_user_action (MOO_ENTRY (entry)); GTK_ENTRY_CLASS(moo_entry_parent_class)->insert_at_cursor (entry, str); moo_entry_end_user_action (MOO_ENTRY (entry)); } static void moo_entry_delete_from_cursor (GtkEntry *entry, GtkDeleteType type, gint count) { moo_entry_begin_user_action (MOO_ENTRY (entry)); GTK_ENTRY_CLASS(moo_entry_parent_class)->delete_from_cursor (entry, type, count); moo_entry_end_user_action (MOO_ENTRY (entry)); } static void moo_entry_backspace (GtkEntry *entry) { moo_entry_begin_user_action (MOO_ENTRY (entry)); GTK_ENTRY_CLASS(moo_entry_parent_class)->backspace (entry); moo_entry_end_user_action (MOO_ENTRY (entry)); } static void moo_entry_cut_clipboard (GtkEntry *entry) { moo_entry_begin_user_action (MOO_ENTRY (entry)); GTK_ENTRY_CLASS(moo_entry_parent_class)->cut_clipboard (entry); moo_entry_end_user_action (MOO_ENTRY (entry)); } static void moo_entry_paste_clipboard (GtkEntry *entry) { moo_entry_begin_user_action (MOO_ENTRY (entry)); GTK_ENTRY_CLASS(moo_entry_parent_class)->paste_clipboard (entry); moo_entry_end_user_action (MOO_ENTRY (entry)); } static void moo_entry_do_insert_text (GtkEditable *editable, const gchar *text, gint length, gint *position) { moo_entry_begin_user_action (MOO_ENTRY (editable)); parent_editable_iface->do_insert_text (editable, text, length, position); moo_entry_end_user_action (MOO_ENTRY (editable)); } static void moo_entry_do_delete_text (GtkEditable *editable, gint start_pos, gint end_pos) { moo_entry_begin_user_action (MOO_ENTRY (editable)); parent_editable_iface->do_delete_text (editable, start_pos, end_pos); moo_entry_end_user_action (MOO_ENTRY (editable)); } static void moo_entry_insert_text (GtkEditable *editable, const gchar *text, gint length, gint *position) { moo_entry_begin_user_action (MOO_ENTRY (editable)); parent_editable_iface->insert_text (editable, text, length, position); moo_entry_end_user_action (MOO_ENTRY (editable)); } static void moo_entry_delete_text (GtkEditable *editable, gint start_pos, gint end_pos) { moo_entry_begin_user_action (MOO_ENTRY (editable)); parent_editable_iface->delete_text (editable, start_pos, end_pos); moo_entry_end_user_action (MOO_ENTRY (editable)); } static void moo_entry_delete_to_start (MooEntry *entry) { if (entry->priv->use_ctrl_u) gtk_editable_delete_text (GTK_EDITABLE (entry), 0, gtk_editable_get_position (GTK_EDITABLE (entry))); }