521 lines
17 KiB
C
521 lines
17 KiB
C
/*
|
|
* mooentry.c
|
|
*
|
|
* Copyright (C) 2004-2005 by Yevgen Muntyan <muntyan@math.tamu.edu>
|
|
*
|
|
* 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 <gtk/gtkbindings.h>
|
|
#include <gtk/gtkimagemenuitem.h>
|
|
#include <gtk/gtkseparatormenuitem.h>
|
|
#include <gtk/gtkstock.h>
|
|
#include <gdk/gdkkeysyms.h>
|
|
|
|
|
|
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)));
|
|
}
|