From 8c65b775fa5bd68b476ed2dbbea02ac1ee160b5a Mon Sep 17 00:00:00 2001 From: Yevgen Muntyan <17531749+muntyan@users.noreply.github.com> Date: Tue, 6 May 2008 23:21:11 -0500 Subject: [PATCH] Generic Cut/Copy/Paste/Delete/Select All/Undo/Redo actions --- moo/mooedit/mooedit-accels.h | 6 - moo/mooedit/mooeditwindow.c | 111 --------- moo/moofileview/moofileview.c | 62 ++++- moo/mooterm/mooterm-text.c | 20 ++ moo/mooterm/mooterm.c | 102 ++++++-- moo/mooutils/Makefile.am | 2 + moo/mooutils/mooeditops.c | 449 ++++++++++++++++++++++++++++++++++ moo/mooutils/mooeditops.h | 92 +++++++ moo/mooutils/moowindow.c | 395 +++++++++++++++++++++++++++++- 9 files changed, 1103 insertions(+), 136 deletions(-) create mode 100644 moo/mooutils/mooeditops.c create mode 100644 moo/mooutils/mooeditops.h diff --git a/moo/mooedit/mooedit-accels.h b/moo/mooedit/mooedit-accels.h index d0d828d6..de983b5c 100644 --- a/moo/mooedit/mooedit-accels.h +++ b/moo/mooedit/mooedit-accels.h @@ -8,12 +8,6 @@ #define MOO_EDIT_ACCEL_SAVE MOO_ACCEL_SAVE #define MOO_EDIT_ACCEL_SAVE_AS MOO_ACCEL_SAVE_AS #define MOO_EDIT_ACCEL_CLOSE MOO_ACCEL_CLOSE -#define MOO_EDIT_ACCEL_UNDO MOO_ACCEL_UNDO -#define MOO_EDIT_ACCEL_REDO MOO_ACCEL_REDO -#define MOO_EDIT_ACCEL_CUT MOO_ACCEL_CUT -#define MOO_EDIT_ACCEL_COPY MOO_ACCEL_COPY -#define MOO_EDIT_ACCEL_PASTE MOO_ACCEL_PASTE -#define MOO_EDIT_ACCEL_SELECT_ALL MOO_ACCEL_SELECT_ALL #define MOO_EDIT_ACCEL_PAGE_SETUP MOO_ACCEL_PAGE_SETUP #define MOO_EDIT_ACCEL_PRINT MOO_ACCEL_PRINT diff --git a/moo/mooedit/mooeditwindow.c b/moo/mooedit/mooeditwindow.c index 99a6358a..07a66ae7 100644 --- a/moo/mooedit/mooeditwindow.c +++ b/moo/mooedit/mooeditwindow.c @@ -265,11 +265,7 @@ enum { /* aux properties */ PROP_CAN_RELOAD, PROP_HAS_OPEN_DOCUMENT, - PROP_CAN_UNDO, - PROP_CAN_REDO, - PROP_HAS_SELECTION, PROP_HAS_COMMENTS, - PROP_HAS_TEXT, PROP_HAS_JOBS_RUNNING, PROP_HAS_STOP_CLIENTS }; @@ -358,11 +354,7 @@ moo_edit_window_class_init (MooEditWindowClass *klass) INSTALL_PROP (PROP_CAN_RELOAD, "can-reload"); INSTALL_PROP (PROP_HAS_OPEN_DOCUMENT, "has-open-document"); - INSTALL_PROP (PROP_CAN_UNDO, "can-undo"); - INSTALL_PROP (PROP_CAN_REDO, "can-redo"); - INSTALL_PROP (PROP_HAS_SELECTION, "has-selection"); INSTALL_PROP (PROP_HAS_COMMENTS, "has-comments"); - INSTALL_PROP (PROP_HAS_TEXT, "has-text"); INSTALL_PROP (PROP_HAS_JOBS_RUNNING, "has-jobs-running"); INSTALL_PROP (PROP_HAS_STOP_CLIENTS, "has-stop-clients"); @@ -443,81 +435,6 @@ moo_edit_window_class_init (MooEditWindowClass *klass) "condition::sensitive", "has-open-document", NULL); - moo_window_class_new_action (window_class, "Undo", NULL, - "display-name", GTK_STOCK_UNDO, - "label", GTK_STOCK_UNDO, - "tooltip", GTK_STOCK_UNDO, - "stock-id", GTK_STOCK_UNDO, - "accel", MOO_EDIT_ACCEL_UNDO, - "closure-signal", "undo", - "closure-proxy-func", moo_edit_window_get_active_doc, - "condition::sensitive", "can-undo", - NULL); - - moo_window_class_new_action (window_class, "Redo", NULL, - "display-name", GTK_STOCK_REDO, - "label", GTK_STOCK_REDO, - "tooltip", GTK_STOCK_REDO, - "stock-id", GTK_STOCK_REDO, - "accel", MOO_EDIT_ACCEL_REDO, - "closure-signal", "redo", - "closure-proxy-func", moo_edit_window_get_active_doc, - "condition::sensitive", "can-redo", - NULL); - - moo_window_class_new_action (window_class, "Cut", NULL, - "display-name", GTK_STOCK_CUT, - "label", GTK_STOCK_CUT, - "tooltip", GTK_STOCK_CUT, - "stock-id", GTK_STOCK_CUT, - "accel", MOO_EDIT_ACCEL_CUT, - "closure-signal", "cut-clipboard", - "closure-proxy-func", moo_edit_window_get_active_doc, - "condition::sensitive", "has-selection", - NULL); - - moo_window_class_new_action (window_class, "Copy", NULL, - "display-name", GTK_STOCK_COPY, - "label", GTK_STOCK_COPY, - "tooltip", GTK_STOCK_COPY, - "stock-id", GTK_STOCK_COPY, - "accel", MOO_EDIT_ACCEL_COPY, - "closure-signal", "copy-clipboard", - "closure-proxy-func", moo_edit_window_get_active_doc, - "condition::sensitive", "has-selection", - NULL); - - moo_window_class_new_action (window_class, "Paste", NULL, - "display-name", GTK_STOCK_PASTE, - "label", GTK_STOCK_PASTE, - "tooltip", GTK_STOCK_PASTE, - "stock-id", GTK_STOCK_PASTE, - "accel", MOO_EDIT_ACCEL_PASTE, - "closure-signal", "paste-clipboard", - "closure-proxy-func", moo_edit_window_get_active_doc, - "condition::sensitive", "has-open-document", - NULL); - - moo_window_class_new_action (window_class, "Delete", NULL, - "display-name", GTK_STOCK_DELETE, - "label", GTK_STOCK_DELETE, - "tooltip", GTK_STOCK_DELETE, - "stock-id", GTK_STOCK_DELETE, - "closure-signal", "delete-selection", - "closure-proxy-func", moo_edit_window_get_active_doc, - "condition::sensitive", "has-selection", - NULL); - - moo_window_class_new_action (window_class, "SelectAll", NULL, - "display-name", GTK_STOCK_SELECT_ALL, - "label", GTK_STOCK_SELECT_ALL, - "tooltip", GTK_STOCK_SELECT_ALL, - "accel", MOO_EDIT_ACCEL_SELECT_ALL, - "closure-callback", moo_text_view_select_all, - "closure-proxy-func", moo_edit_window_get_active_doc, - "condition::sensitive", "has-text", - NULL); - moo_window_class_new_action (window_class, "PreviousTab", NULL, "display-name", _("Previous Tab"), "label", _("_Previous Tab"), @@ -963,26 +880,10 @@ static void moo_edit_window_get_property(GObject *object, case PROP_HAS_OPEN_DOCUMENT: g_value_set_boolean (value, ACTIVE_DOC (window) != NULL); break; - case PROP_CAN_UNDO: - doc = ACTIVE_DOC (window); - g_value_set_boolean (value, doc && moo_text_view_can_undo (MOO_TEXT_VIEW (doc))); - break; - case PROP_CAN_REDO: - doc = ACTIVE_DOC (window); - g_value_set_boolean (value, doc && moo_text_view_can_redo (MOO_TEXT_VIEW (doc))); - break; - case PROP_HAS_SELECTION: - doc = ACTIVE_DOC (window); - g_value_set_boolean (value, doc && moo_text_view_has_selection (MOO_TEXT_VIEW (doc))); - break; case PROP_HAS_COMMENTS: doc = ACTIVE_DOC (window); g_value_set_boolean (value, doc && _moo_edit_has_comments (doc, NULL, NULL)); break; - case PROP_HAS_TEXT: - doc = ACTIVE_DOC (window); - g_value_set_boolean (value, doc && moo_text_view_has_text (MOO_TEXT_VIEW (doc))); - break; case PROP_HAS_JOBS_RUNNING: g_value_set_boolean (value, window->priv->jobs != NULL); break; @@ -2133,11 +2034,7 @@ edit_changed (MooEditWindow *window, g_object_freeze_notify (G_OBJECT (window)); g_object_notify (G_OBJECT (window), "can-reload"); g_object_notify (G_OBJECT (window), "has-open-document"); - g_object_notify (G_OBJECT (window), "can-undo"); - g_object_notify (G_OBJECT (window), "can-redo"); - g_object_notify (G_OBJECT (window), "has-selection"); g_object_notify (G_OBJECT (window), "has-comments"); - g_object_notify (G_OBJECT (window), "has-text"); g_object_thaw_notify (G_OBJECT (window)); update_window_title (window); @@ -2400,16 +2297,8 @@ _moo_edit_window_insert_doc (MooEditWindow *window, G_CALLBACK (edit_show_line_numbers_changed), window); g_signal_connect_swapped (edit, "filename_changed", G_CALLBACK (edit_filename_changed), window); - g_signal_connect_swapped (edit, "notify::can-undo", - G_CALLBACK (proxy_boolean_property), window); - g_signal_connect_swapped (edit, "notify::can-redo", - G_CALLBACK (proxy_boolean_property), window); - g_signal_connect_swapped (edit, "notify::has-selection", - G_CALLBACK (proxy_boolean_property), window); g_signal_connect_swapped (edit, "notify::has-comments", G_CALLBACK (proxy_boolean_property), window); - g_signal_connect_swapped (edit, "notify::has-text", - G_CALLBACK (proxy_boolean_property), window); g_signal_connect_swapped (edit, "config-notify::lang", G_CALLBACK (edit_lang_changed), window); g_signal_connect_swapped (edit, "cursor-moved", diff --git a/moo/moofileview/moofileview.c b/moo/moofileview/moofileview.c index d9795396..6629c7b1 100644 --- a/moo/moofileview/moofileview.c +++ b/moo/moofileview/moofileview.c @@ -42,6 +42,7 @@ #include "mooutils/moostock.h" #include "mooutils/mooactionfactory.h" #include "mooutils/mooaction-private.h" +#include "mooutils/mooeditops.h" #include "marshals.h" #include "mooutils/mooi18n.h" #include @@ -425,9 +426,12 @@ static void file_view_cut_clipboard (MooFileView *fileview); static void file_view_copy_clipboard (MooFileView *fileview); static void file_view_paste_clipboard (MooFileView *fileview); +static void edit_ops_iface_init (MooEditOpsIface *iface); /* MOO_TYPE_FILE_VIEW */ -G_DEFINE_TYPE (MooFileView, moo_file_view, GTK_TYPE_VBOX) +G_DEFINE_TYPE_WITH_CODE (MooFileView, moo_file_view, GTK_TYPE_VBOX, + G_IMPLEMENT_INTERFACE (MOO_TYPE_EDIT_OPS, + edit_ops_iface_init)) enum { PROP_0, @@ -2420,6 +2424,62 @@ moo_file_view_get_home_dir (MooFileView *fileview) /* Clipboard */ +static void +edit_ops_do_op (MooEditOps *obj, + MooEditOpType type) +{ + MooFileView *fileview = MOO_FILE_VIEW (obj); + + switch (type) + { + case MOO_EDIT_OP_CUT: + g_signal_emit_by_name (fileview, "cut-clipboard"); + break; + case MOO_EDIT_OP_COPY: + g_signal_emit_by_name (fileview, "copy-clipboard"); + break; + case MOO_EDIT_OP_PASTE: + g_signal_emit_by_name (fileview, "paste-clipboard"); + break; + case MOO_EDIT_OP_DELETE: + g_signal_emit_by_name (fileview, "delete-selected"); + break; + case MOO_EDIT_OP_SELECT_ALL: + _moo_icon_view_select_all (fileview->priv->iconview); + break; + } +} + +static gboolean +edit_ops_can_do_op (MooEditOps *obj, + MooEditOpType type) +{ + MooFileView *fileview = MOO_FILE_VIEW (obj); + + switch (type) + { + case MOO_EDIT_OP_CUT: + return !_moo_tree_view_selection_is_empty (fileview->priv->view); + case MOO_EDIT_OP_COPY: + return !_moo_tree_view_selection_is_empty (fileview->priv->view); + case MOO_EDIT_OP_PASTE: + return TRUE; + case MOO_EDIT_OP_DELETE: + return !_moo_tree_view_selection_is_empty (fileview->priv->view); + case MOO_EDIT_OP_SELECT_ALL: + return TRUE; + } + + g_return_val_if_reached (FALSE); +} + +static void +edit_ops_iface_init (MooEditOpsIface *iface) +{ + iface->do_op = edit_ops_do_op; + iface->can_do_op = edit_ops_can_do_op; +} + enum { CB_TARGET_CLIPBOARD = 1, CB_TARGET_URI_LIST = 2, diff --git a/moo/mooterm/mooterm-text.c b/moo/mooterm/mooterm-text.c index 96d97647..aeb84583 100644 --- a/moo/mooterm/mooterm-text.c +++ b/moo/mooterm/mooterm-text.c @@ -17,6 +17,7 @@ #include "mooterm/mooterm-selection.h" #include "mooterm/mootermline-private.h" #include "mooutils/mooutils-misc.h" +#include "mooutils/mooeditops.h" typedef struct { @@ -290,6 +291,13 @@ invalidate_segment (Segment *segm, guint num) } +static void +notify_has_selection (MooTerm *term) +{ + g_object_notify (G_OBJECT (term), "has-selection"); + moo_edit_ops_can_do_op_changed (G_OBJECT (term), MOO_EDIT_OP_COPY); +} + static void _moo_term_select_range (MooTerm *term, const MooTermIter *start, @@ -299,12 +307,14 @@ _moo_term_select_range (MooTerm *term, Segment new_sel; Segment old_selection; gboolean inv = FALSE; + gboolean had_selection; CHECK_ITER (start); CHECK_ITER (end); old_selection = *GET_SELECTION (ITER_TERM (start)); CHECK_SEGMENT (&old_selection); + had_selection = !segment_empty (&old_selection); new_sel.start = *start; new_sel.end = *end; @@ -352,9 +362,19 @@ _moo_term_select_range (MooTerm *term, diff)); if (_moo_term_selection_empty (term)) + { _moo_term_release_selection (term); + + if (had_selection) + notify_has_selection (term); + } else + { _moo_term_grab_selection (term); + + if (!had_selection) + notify_has_selection (term); + } } diff --git a/moo/mooterm/mooterm.c b/moo/mooterm/mooterm.c index f3f3de54..07b9e88c 100644 --- a/moo/mooterm/mooterm.c +++ b/moo/mooterm/mooterm.c @@ -20,9 +20,11 @@ #include "marshals.h" #include "mooutils/mooutils-misc.h" #include "mooutils/mooutils-debug.h" +#include "mooutils/mooeditops.h" #include +static void moo_term_edit_ops_init (MooEditOpsIface *iface); static void moo_term_class_init (MooTermClass *klass); static void moo_term_init (MooTerm *term); static void moo_term_finalize (GObject *object); @@ -110,12 +112,15 @@ enum { enum { PROP_0, PROP_CURSOR_BLINKS, - PROP_FONT_NAME + PROP_FONT_NAME, + PROP_HAS_SELECTION }; /* MOO_TYPE_TERM */ -G_DEFINE_TYPE (MooTerm, moo_term, GTK_TYPE_WIDGET) +G_DEFINE_TYPE_WITH_CODE (MooTerm, moo_term, GTK_TYPE_WIDGET, + G_IMPLEMENT_INTERFACE (MOO_TYPE_EDIT_OPS, + moo_term_edit_ops_init)) static guint signals[LAST_SIGNAL]; @@ -151,21 +156,17 @@ static void moo_term_class_init (MooTermClass *klass) klass->reset = moo_term_reset_real; klass->bell = moo_term_bell_real; - g_object_class_install_property (gobject_class, - PROP_CURSOR_BLINKS, - g_param_spec_boolean ("cursor-blinks", - "cursor-blinks", - "cursor-blinks", - FALSE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + g_object_class_install_property (gobject_class, PROP_CURSOR_BLINKS, + g_param_spec_boolean ("cursor-blinks", "cursor-blinks", "cursor-blinks", + FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); - g_object_class_install_property (gobject_class, - PROP_FONT_NAME, - g_param_spec_string ("font-name", - "font-name", - "font-name", - DEFAULT_MONOSPACE_FONT, - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT)); + g_object_class_install_property (gobject_class, PROP_FONT_NAME, + g_param_spec_string ("font-name", "font-name", "font-name", + DEFAULT_MONOSPACE_FONT, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT)); + + g_object_class_install_property (gobject_class, PROP_HAS_SELECTION, + g_param_spec_boolean ("has-selection", "has-selection", "has-selection", + FALSE, G_PARAM_READABLE)); signals[SET_SCROLL_ADJUSTMENTS] = g_signal_new ("set-scroll-adjustments", @@ -455,11 +456,16 @@ static void moo_term_get_property (GObject *object, { MooTerm *term = MOO_TERM (object); - switch (prop_id) { + switch (prop_id) + { case PROP_CURSOR_BLINKS: g_value_set_boolean (value, term->priv->cursor_blinks); break; + case PROP_HAS_SELECTION: + g_value_set_boolean (value, moo_term_get_selection_bounds (term, NULL, NULL)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1073,6 +1079,68 @@ moo_term_paste_clipboard (MooTerm *term, } +static void +moo_term_do_edit_op (MooEditOps *obj, + MooEditOpType type) +{ + MooTerm *term; + + g_return_if_fail (MOO_IS_TERM (obj)); + term = MOO_TERM (obj); + + switch (type) + { + case MOO_EDIT_OP_COPY: + moo_term_copy_clipboard (term, GDK_SELECTION_CLIPBOARD); + break; + case MOO_EDIT_OP_PASTE: + moo_term_paste_clipboard (term, GDK_SELECTION_CLIPBOARD); + break; + case MOO_EDIT_OP_SELECT_ALL: + moo_term_select_all (term); + break; + default: + g_return_if_reached (); + break; + } +} + +static gboolean +moo_term_can_do_edit_op (MooEditOps *obj, + MooEditOpType type) +{ + MooTerm *term; + + g_return_val_if_fail (MOO_IS_TERM (obj), FALSE); + term = MOO_TERM (obj); + + switch (type) + { + case MOO_EDIT_OP_COPY: + return moo_term_get_selection_bounds (term, NULL, NULL); + + case MOO_EDIT_OP_PASTE: + case MOO_EDIT_OP_SELECT_ALL: + return TRUE; + + case MOO_EDIT_OP_CUT: + case MOO_EDIT_OP_DELETE: + return FALSE; + + default: + g_return_val_if_reached (FALSE); + break; + } +} + +static void +moo_term_edit_ops_init (MooEditOpsIface *iface) +{ + iface->do_op = moo_term_do_edit_op; + iface->can_do_op = moo_term_can_do_edit_op; +} + + static gboolean process_incoming (MooTerm *term) { diff --git a/moo/mooutils/Makefile.am b/moo/mooutils/Makefile.am index 440f571a..a5f8c4e7 100644 --- a/moo/mooutils/Makefile.am +++ b/moo/mooutils/Makefile.am @@ -29,6 +29,7 @@ mooutils_include_headers = \ moobigpaned.h \ mooclosure.h \ moocombo.h \ + mooeditops.h \ mooentry.h \ moofiledialog.h \ moofiltermgr.h \ @@ -87,6 +88,7 @@ mooutils_sources = \ moocompat.h \ moodialogs.c \ moodialogs.h \ + mooeditops.c \ mooencodings.c \ mooencodings.h \ mooencodings-data.h \ diff --git a/moo/mooutils/mooeditops.c b/moo/mooutils/mooeditops.c new file mode 100644 index 00000000..e349e3c3 --- /dev/null +++ b/moo/mooutils/mooeditops.c @@ -0,0 +1,449 @@ +/* + * mooeditops.c + * + * Copyright (C) 2004-2008 by Yevgen Muntyan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation. + * + * See COPYING file that comes with this distribution. + */ + +#include "mooeditops.h" +#include "marshals.h" +#include + +static void +moo_edit_ops_class_init (G_GNUC_UNUSED MooEditOpsIface *iface) +{ + g_signal_new ("moo-edit-ops-can-do-op-changed", + MOO_TYPE_EDIT_OPS, + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, + _moo_marshal_VOID__UINT, + G_TYPE_NONE, 1, + G_TYPE_UINT); +} + +GType +moo_edit_ops_get_type (void) +{ + static GType type; + + if (G_UNLIKELY (!type)) + { + GTypeInfo type_info = { + sizeof (MooEditOpsIface), /* class_size */ + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) moo_edit_ops_class_init, /* class_init */ + NULL + }; + + type = g_type_register_static (G_TYPE_INTERFACE, "MooEditOps", + &type_info, 0); + + g_type_interface_add_prerequisite (type, G_TYPE_OBJECT); + } + + return type; +} + + +void +moo_edit_ops_can_do_op_changed (GObject *obj, + MooEditOpType type) +{ + g_return_if_fail (type < MOO_N_EDIT_OPS); + g_signal_emit_by_name (obj, "moo-edit-ops-can-do-op-changed", type); +} + + +static void +emit_can_do_op_changed (GObject *obj) +{ + int i; + for (i = 0; i < MOO_N_EDIT_OPS; i++) + moo_edit_ops_can_do_op_changed (obj, i); +} + + +static void +editable_do_op (GtkEditable *obj, + MooEditOpType type) +{ + switch (type) + { + case MOO_EDIT_OP_CUT: + gtk_editable_cut_clipboard (obj); + break; + case MOO_EDIT_OP_COPY: + gtk_editable_copy_clipboard (obj); + break; + case MOO_EDIT_OP_PASTE: + gtk_editable_paste_clipboard (obj); + break; + case MOO_EDIT_OP_DELETE: + gtk_editable_delete_selection (obj); + break; + case MOO_EDIT_OP_SELECT_ALL: + gtk_editable_select_region (obj, 0, -1); + break; + default: + g_return_if_reached (); + } +} + +static gboolean +entry_can_do_op (GtkEntry *obj, + MooEditOpType type) +{ + switch (type) + { + case MOO_EDIT_OP_CUT: + return gtk_editable_get_editable (GTK_EDITABLE (obj)) && + gtk_editable_get_selection_bounds (GTK_EDITABLE (obj), NULL, NULL); + case MOO_EDIT_OP_COPY: + return gtk_editable_get_selection_bounds (GTK_EDITABLE (obj), NULL, NULL); + case MOO_EDIT_OP_PASTE: + return gtk_editable_get_editable (GTK_EDITABLE (obj)); + case MOO_EDIT_OP_DELETE: + return gtk_editable_get_selection_bounds (GTK_EDITABLE (obj), NULL, NULL); + + case MOO_EDIT_OP_SELECT_ALL: + { + const char *text = gtk_entry_get_text (obj); + return text && text[0]; + } + break; + + default: + g_return_val_if_reached (FALSE); + } +} + +static void +entry_connect (GtkEntry *obj) +{ + g_signal_connect (obj, "notify::selection-bound", + G_CALLBACK (emit_can_do_op_changed), NULL); + g_signal_connect (obj, "notify::text", + G_CALLBACK (emit_can_do_op_changed), NULL); +} + +static void +entry_disconnect (GtkEntry *obj) +{ + g_signal_handlers_disconnect_by_func (obj, (gpointer) emit_can_do_op_changed, NULL); +} + + +static void +textview_do_op (GtkTextView *obj, + MooEditOpType type) +{ + switch (type) + { + case MOO_EDIT_OP_CUT: + g_signal_emit_by_name (obj, "cut-clipboard"); + break; + case MOO_EDIT_OP_COPY: + g_signal_emit_by_name (obj, "copy-clipboard"); + break; + case MOO_EDIT_OP_PASTE: + g_signal_emit_by_name (obj, "paste-clipboard"); + break; + case MOO_EDIT_OP_DELETE: + g_signal_emit_by_name (obj, "delete-from-cursor", GTK_DELETE_CHARS, 1); + break; + case MOO_EDIT_OP_SELECT_ALL: + g_signal_emit_by_name (obj, "select-all"); + break; + default: + g_return_if_reached (); + } +} + +static gboolean +textview_can_do_op (GtkTextView *obj, + MooEditOpType type) +{ + GtkTextBuffer *buffer = obj->buffer; + + switch (type) + { + case MOO_EDIT_OP_CUT: + return gtk_text_view_get_editable (obj) && + gtk_text_buffer_get_selection_bounds (buffer, NULL, NULL); + case MOO_EDIT_OP_COPY: + return gtk_text_buffer_get_selection_bounds (buffer, NULL, NULL); + case MOO_EDIT_OP_PASTE: + return gtk_text_view_get_editable (obj); + case MOO_EDIT_OP_DELETE: + return gtk_text_buffer_get_selection_bounds (buffer, NULL, NULL); + + case MOO_EDIT_OP_SELECT_ALL: + return gtk_text_buffer_get_char_count (buffer) != 0; + + default: + g_return_val_if_reached (FALSE); + } +} + +static void +textview_connect (GtkTextView *obj) +{ + /* XXX */ + GtkTextBuffer *buffer = obj->buffer; + + if (buffer) + { + g_signal_connect_swapped (buffer, "notify::has-selection", + G_CALLBACK (emit_can_do_op_changed), obj); + g_signal_connect_swapped (buffer, "changed", + G_CALLBACK (emit_can_do_op_changed), obj); + } +} + +static void +textview_disconnect (GtkTextView *obj) +{ + /* XXX */ + GtkTextBuffer *buffer = obj->buffer; + + if (buffer) + g_signal_handlers_disconnect_by_func (buffer, (gpointer) emit_can_do_op_changed, obj); +} + + +void +_moo_edit_ops_do_op (GObject *obj, + MooEditOpType type) +{ + g_return_if_fail (type < MOO_N_EDIT_OPS); + + if (MOO_IS_EDIT_OPS (obj)) + { + g_return_if_fail (MOO_EDIT_OPS_GET_IFACE (obj)->do_op != NULL); + MOO_EDIT_OPS_GET_IFACE (obj)->do_op (MOO_EDIT_OPS (obj), type); + } + else if (GTK_IS_ENTRY (obj)) + { + editable_do_op (GTK_EDITABLE (obj), type); + } + else if (GTK_IS_TEXT_VIEW (obj)) + { + textview_do_op (GTK_TEXT_VIEW (obj), type); + } + else + { + g_return_if_reached (); + } +} + +gboolean +_moo_edit_ops_can_do_op (GObject *obj, + MooEditOpType type) +{ + g_return_val_if_fail (type < MOO_N_EDIT_OPS, FALSE); + + if (MOO_IS_EDIT_OPS (obj)) + { + g_return_val_if_fail (MOO_EDIT_OPS_GET_IFACE (obj)->can_do_op != NULL, FALSE); + return MOO_EDIT_OPS_GET_IFACE (obj)->can_do_op (MOO_EDIT_OPS (obj), type); + } + else if (GTK_IS_TEXT_VIEW (obj)) + { + return textview_can_do_op (GTK_TEXT_VIEW (obj), type); + } + else if (GTK_IS_ENTRY (obj)) + { + return entry_can_do_op (GTK_ENTRY (obj), type); + } + else + { + g_return_val_if_reached (FALSE); + } +} + +void +_moo_edit_ops_connect (GObject *obj) +{ + if (!MOO_IS_EDIT_OPS (obj)) + { + if (GTK_IS_TEXT_VIEW (obj)) + textview_connect (GTK_TEXT_VIEW (obj)); + else if (GTK_IS_ENTRY (obj)) + entry_connect (GTK_ENTRY (obj)); + else + g_return_if_reached (); + } +} + +void +_moo_edit_ops_disconnect (GObject *obj) +{ + if (!MOO_IS_EDIT_OPS (obj)) + { + if (GTK_IS_TEXT_VIEW (obj)) + textview_disconnect (GTK_TEXT_VIEW (obj)); + else if (GTK_IS_ENTRY (obj)) + entry_disconnect (GTK_ENTRY (obj)); + else + g_return_if_reached (); + } +} + +gboolean +_moo_edit_ops_check (GObject *obj) +{ + return MOO_IS_EDIT_OPS (obj) || + GTK_IS_TEXT_VIEW (obj) || + GTK_IS_ENTRY (obj); +} + +void +_moo_edit_ops_iface_install (void) +{ + static gboolean been_here; + + if (!been_here) + { + g_signal_new ("moo-edit-ops-can-do-op-changed", + GTK_TYPE_TEXT_VIEW, + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, + _moo_marshal_VOID__UINT, + G_TYPE_NONE, 1, + G_TYPE_UINT); + g_signal_new ("moo-edit-ops-can-do-op-changed", + GTK_TYPE_ENTRY, + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, + _moo_marshal_VOID__UINT, + G_TYPE_NONE, 1, + G_TYPE_UINT); + been_here = TRUE; + } +} + + +/************************************************************************/ +/* MooUndoOps + */ + +static void +moo_undo_ops_class_init (G_GNUC_UNUSED MooUndoOpsIface *iface) +{ + g_signal_new ("moo-undo-ops-can-undo-changed", + MOO_TYPE_EDIT_OPS, + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, + _moo_marshal_VOID__VOID, + G_TYPE_NONE, 0); + g_signal_new ("moo-undo-ops-can-redo-changed", + MOO_TYPE_EDIT_OPS, + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, + _moo_marshal_VOID__VOID, + G_TYPE_NONE, 0); +} + +GType +moo_undo_ops_get_type (void) +{ + static GType type; + + if (G_UNLIKELY (!type)) + { + GTypeInfo type_info = { + sizeof (MooEditOpsIface), NULL, NULL, + (GClassInitFunc) moo_undo_ops_class_init, + NULL + }; + + type = g_type_register_static (G_TYPE_INTERFACE, "MooUndoOps", + &type_info, 0); + + g_type_interface_add_prerequisite (type, G_TYPE_OBJECT); + } + + return type; +} + + +void +moo_undo_ops_can_undo_changed (GObject *obj) +{ + g_signal_emit_by_name (obj, "moo-edit-ops-can-undo-changed"); +} + +void +moo_undo_ops_can_redo_changed (GObject *obj) +{ + g_signal_emit_by_name (obj, "moo-edit-ops-can-redo-changed"); +} + + +void +_moo_undo_ops_undo (GObject *obj) +{ + if (MOO_IS_UNDO_OPS (obj)) + { + g_return_if_fail (MOO_UNDO_OPS_GET_IFACE (obj)->undo != NULL); + MOO_UNDO_OPS_GET_IFACE (obj)->undo (MOO_UNDO_OPS (obj)); + } + else + { + g_return_if_reached (); + } +} + +void +_moo_undo_ops_redo (GObject *obj) +{ + if (MOO_IS_UNDO_OPS (obj)) + { + g_return_if_fail (MOO_UNDO_OPS_GET_IFACE (obj)->redo != NULL); + MOO_UNDO_OPS_GET_IFACE (obj)->redo (MOO_UNDO_OPS (obj)); + } + else + { + g_return_if_reached (); + } +} + +gboolean +_moo_undo_ops_can_undo (GObject *obj) +{ + if (MOO_IS_UNDO_OPS (obj)) + { + g_return_val_if_fail (MOO_UNDO_OPS_GET_IFACE (obj)->can_undo != NULL, FALSE); + return MOO_UNDO_OPS_GET_IFACE (obj)->can_undo (MOO_UNDO_OPS (obj)); + } + else + { + g_return_val_if_reached (FALSE); + } +} + +gboolean +_moo_undo_ops_can_redo (GObject *obj) +{ + if (MOO_IS_UNDO_OPS (obj)) + { + g_return_val_if_fail (MOO_UNDO_OPS_GET_IFACE (obj)->can_redo != NULL, FALSE); + return MOO_UNDO_OPS_GET_IFACE (obj)->can_redo (MOO_UNDO_OPS (obj)); + } + else + { + g_return_val_if_reached (FALSE); + } +} + +gboolean +_moo_undo_ops_check (GObject *obj) +{ + return MOO_IS_UNDO_OPS (obj); +} diff --git a/moo/mooutils/mooeditops.h b/moo/mooutils/mooeditops.h new file mode 100644 index 00000000..931ab044 --- /dev/null +++ b/moo/mooutils/mooeditops.h @@ -0,0 +1,92 @@ +/* + * mooeditops.h + * + * Copyright (C) 2004-2008 by Yevgen Muntyan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation. + * + * See COPYING file that comes with this distribution. + */ + +#ifndef MOO_EDIT_OPS_H +#define MOO_EDIT_OPS_H + +#include + +G_BEGIN_DECLS + + +#define MOO_TYPE_EDIT_OPS (moo_edit_ops_get_type ()) +#define MOO_EDIT_OPS(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), MOO_TYPE_EDIT_OPS, MooEditOps)) +#define MOO_IS_EDIT_OPS(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), MOO_TYPE_EDIT_OPS)) +#define MOO_EDIT_OPS_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MOO_TYPE_EDIT_OPS, MooEditOpsIface)) + +#define MOO_TYPE_UNDO_OPS (moo_undo_ops_get_type ()) +#define MOO_UNDO_OPS(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), MOO_TYPE_UNDO_OPS, MooUndoOps)) +#define MOO_IS_UNDO_OPS(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), MOO_TYPE_UNDO_OPS)) +#define MOO_UNDO_OPS_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MOO_TYPE_UNDO_OPS, MooUndoOpsIface)) + +typedef struct MooEditOps MooEditOps; +typedef struct MooEditOpsIface MooEditOpsIface; +typedef struct MooUndoOps MooUndoOps; +typedef struct MooUndoOpsIface MooUndoOpsIface; + +typedef enum { + MOO_EDIT_OP_CUT, + MOO_EDIT_OP_COPY, + MOO_EDIT_OP_PASTE, + MOO_EDIT_OP_DELETE, + MOO_EDIT_OP_SELECT_ALL +} MooEditOpType; + +#define MOO_N_EDIT_OPS (MOO_EDIT_OP_SELECT_ALL+1) + +struct MooEditOpsIface +{ + GTypeInterface g_iface; + + void (*do_op) (MooEditOps *obj, + MooEditOpType type); + gboolean (*can_do_op) (MooEditOps *obj, + MooEditOpType type); +}; + +struct MooUndoOpsIface +{ + GTypeInterface g_iface; + + void (*undo) (MooUndoOps *obj); + void (*redo) (MooUndoOps *obj); + gboolean (*can_undo) (MooUndoOps *obj); + gboolean (*can_redo) (MooUndoOps *obj); +}; + +GType moo_edit_ops_get_type (void) G_GNUC_CONST; +GType moo_undo_ops_get_type (void) G_GNUC_CONST; + +void _moo_edit_ops_do_op (GObject *obj, + MooEditOpType type); +gboolean _moo_edit_ops_can_do_op (GObject *obj, + MooEditOpType type); +void moo_edit_ops_can_do_op_changed (GObject *obj, + MooEditOpType type); + +gboolean _moo_edit_ops_check (GObject *obj); +void _moo_edit_ops_iface_install (void); +void _moo_edit_ops_connect (GObject *obj); +void _moo_edit_ops_disconnect (GObject *obj); + +void _moo_undo_ops_undo (GObject *obj); +void _moo_undo_ops_redo (GObject *obj); +gboolean _moo_undo_ops_can_undo (GObject *obj); +gboolean _moo_undo_ops_can_redo (GObject *obj); +void moo_undo_ops_can_undo_changed (GObject *obj); +void moo_undo_ops_can_redo_changed (GObject *obj); +gboolean _moo_undo_ops_check (GObject *obj); + + +G_END_DECLS + +#endif /* MOO_EDIT_OPS_H */ diff --git a/moo/mooutils/moowindow.c b/moo/mooutils/moowindow.c index 85a99b48..85076d07 100644 --- a/moo/mooutils/moowindow.c +++ b/moo/mooutils/moowindow.c @@ -23,6 +23,7 @@ #include "mooutils/mooi18n.h" #include "mooutils/mooutils-misc.h" #include "mooutils/mooutils-mem.h" +#include "mooutils/mooeditops.h" #include #include @@ -56,6 +57,11 @@ struct _MooWindowPrivate { char *name; char *id; + GtkWidget *eo_widget; + GtkWidget *default_eo_widget; + GtkWidget *uo_widget; + GtkWidget *default_uo_widget; + guint global_accels : 1; }; @@ -125,6 +131,18 @@ static void moo_window_set_toolbar_visible (MooWindow *window, static GtkAction *create_toolbar_style_action (MooWindow *window, gpointer dummy); +static void moo_window_set_focus (GtkWindow *window, + GtkWidget *widget); +static void moo_window_disconnect_eo_widget (MooWindow *window); +static void moo_window_disconnect_uo_widget (MooWindow *window); +static void moo_window_action_cut (MooWindow *window); +static void moo_window_action_copy (MooWindow *window); +static void moo_window_action_paste (MooWindow *window); +static void moo_window_action_delete (MooWindow *window); +static void moo_window_action_select_all (MooWindow *window); +static void moo_window_action_undo (MooWindow *window); +static void moo_window_action_redo (MooWindow *window); + enum { PROP_0, @@ -135,7 +153,16 @@ enum { PROP_UI_XML, PROP_ACTIONS, PROP_TOOLBAR_VISIBLE, - PROP_MENUBAR_VISIBLE + PROP_MENUBAR_VISIBLE, + + PROP_MEO_CAN_CUT, + PROP_MEO_CAN_COPY, + PROP_MEO_CAN_PASTE, + PROP_MEO_CAN_SELECT_ALL, + PROP_MEO_CAN_DELETE, + + PROP_MUO_CAN_UNDO, + PROP_MUO_CAN_REDO }; enum { @@ -152,11 +179,16 @@ G_DEFINE_TYPE (MooWindow, moo_window, GTK_TYPE_WINDOW) static gpointer moo_window_grand_parent_class; +#define INSTALL_PROP(prop_id,name) \ + g_object_class_install_property (gobject_class, prop_id, \ + g_param_spec_boolean (name, name, name, FALSE, G_PARAM_READABLE)) + static void moo_window_class_init (MooWindowClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + GtkWindowClass *window_class = GTK_WINDOW_CLASS (klass); moo_window_grand_parent_class = g_type_class_peek_parent (moo_window_parent_class); @@ -167,9 +199,12 @@ moo_window_class_init (MooWindowClass *klass) widget_class->delete_event = moo_window_delete_event; widget_class->key_press_event = moo_window_key_press_event; + window_class->set_focus = moo_window_set_focus; moo_window_class_set_id (klass, "MooWindow", "Window"); + _moo_edit_ops_iface_install (); + moo_window_class_new_action (klass, "ConfigureShortcuts", NULL, "label", _("Configure _Shortcuts..."), "no-accel", TRUE, @@ -195,6 +230,75 @@ moo_window_class_init (MooWindowClass *klass) create_toolbar_style_action, NULL, NULL); + moo_window_class_new_action (klass, "Cut", NULL, + "display-name", GTK_STOCK_CUT, + "label", GTK_STOCK_CUT, + "tooltip", GTK_STOCK_CUT, + "stock-id", GTK_STOCK_CUT, + "accel", MOO_ACCEL_CUT, + "closure-callback", moo_window_action_cut, + "condition::sensitive", "can-cut", + NULL); + + moo_window_class_new_action (klass, "Copy", NULL, + "display-name", GTK_STOCK_COPY, + "label", GTK_STOCK_COPY, + "tooltip", GTK_STOCK_COPY, + "stock-id", GTK_STOCK_COPY, + "accel", MOO_ACCEL_COPY, + "closure-callback", moo_window_action_copy, + "condition::sensitive", "can-copy", + NULL); + + moo_window_class_new_action (klass, "Paste", NULL, + "display-name", GTK_STOCK_PASTE, + "label", GTK_STOCK_PASTE, + "tooltip", GTK_STOCK_PASTE, + "stock-id", GTK_STOCK_PASTE, + "accel", MOO_ACCEL_PASTE, + "closure-callback", moo_window_action_paste, + "condition::sensitive", "can-paste", + NULL); + + moo_window_class_new_action (klass, "Delete", NULL, + "display-name", GTK_STOCK_DELETE, + "label", GTK_STOCK_DELETE, + "tooltip", GTK_STOCK_DELETE, + "stock-id", GTK_STOCK_DELETE, + "closure-callback", moo_window_action_delete, + "condition::sensitive", "can-delete", + NULL); + + moo_window_class_new_action (klass, "SelectAll", NULL, + "display-name", GTK_STOCK_SELECT_ALL, + "label", GTK_STOCK_SELECT_ALL, + "tooltip", GTK_STOCK_SELECT_ALL, + "stock-id", GTK_STOCK_SELECT_ALL, + "accel", MOO_ACCEL_SELECT_ALL, + "closure-callback", moo_window_action_select_all, + "condition::sensitive", "can-select-all", + NULL); + + moo_window_class_new_action (klass, "Undo", NULL, + "display-name", GTK_STOCK_UNDO, + "label", GTK_STOCK_UNDO, + "tooltip", GTK_STOCK_UNDO, + "stock-id", GTK_STOCK_UNDO, + "accel", MOO_ACCEL_UNDO, + "closure-callback", moo_window_action_undo, + "condition::sensitive", "can-undo", + NULL); + + moo_window_class_new_action (klass, "Redo", NULL, + "display-name", GTK_STOCK_REDO, + "label", GTK_STOCK_REDO, + "tooltip", GTK_STOCK_REDO, + "stock-id", GTK_STOCK_REDO, + "accel", MOO_ACCEL_REDO, + "closure-callback", moo_window_action_redo, + "condition::sensitive", "can-redo", + NULL); + g_object_class_install_property (gobject_class, PROP_ACCEL_GROUP, g_param_spec_object ("accel-group", @@ -259,6 +363,15 @@ moo_window_class_init (MooWindowClass *klass) TRUE, G_PARAM_READWRITE)); + INSTALL_PROP (PROP_MEO_CAN_COPY, "can-copy"); + INSTALL_PROP (PROP_MEO_CAN_CUT, "can-cut"); + INSTALL_PROP (PROP_MEO_CAN_PASTE, "can-paste"); + INSTALL_PROP (PROP_MEO_CAN_DELETE, "can-delete"); + INSTALL_PROP (PROP_MEO_CAN_SELECT_ALL, "can-select-all"); + + INSTALL_PROP (PROP_MUO_CAN_UNDO, "can-undo"); + INSTALL_PROP (PROP_MUO_CAN_REDO, "can-redo"); + signals[CLOSE] = g_signal_new ("close", G_OBJECT_CLASS_TYPE (klass), @@ -375,6 +488,12 @@ moo_window_dispose (GObject *object) if (window->priv) { + window->priv->default_eo_widget = NULL; + moo_window_disconnect_eo_widget (window); + + window->priv->default_uo_widget = NULL; + moo_window_disconnect_uo_widget (window); + if (window->priv->ui_xml) g_object_unref (window->priv->ui_xml); @@ -579,6 +698,36 @@ moo_window_get_property (GObject *object, g_value_set_boolean (value, window->priv->menubar_visible); break; + case PROP_MEO_CAN_COPY: + g_value_set_boolean (value, window->priv->eo_widget && + _moo_edit_ops_can_do_op (G_OBJECT (window->priv->eo_widget), MOO_EDIT_OP_COPY)); + break; + case PROP_MEO_CAN_CUT: + g_value_set_boolean (value, window->priv->eo_widget && + _moo_edit_ops_can_do_op (G_OBJECT (window->priv->eo_widget), MOO_EDIT_OP_CUT)); + break; + case PROP_MEO_CAN_PASTE: + g_value_set_boolean (value, window->priv->eo_widget && + _moo_edit_ops_can_do_op (G_OBJECT (window->priv->eo_widget), MOO_EDIT_OP_PASTE)); + break; + case PROP_MEO_CAN_DELETE: + g_value_set_boolean (value, window->priv->eo_widget && + _moo_edit_ops_can_do_op (G_OBJECT (window->priv->eo_widget), MOO_EDIT_OP_DELETE)); + break; + case PROP_MEO_CAN_SELECT_ALL: + g_value_set_boolean (value, window->priv->eo_widget && + _moo_edit_ops_can_do_op (G_OBJECT (window->priv->eo_widget), MOO_EDIT_OP_SELECT_ALL)); + break; + + case PROP_MUO_CAN_UNDO: + g_value_set_boolean (value, window->priv->uo_widget && + _moo_undo_ops_can_undo (G_OBJECT (window->priv->uo_widget))); + break; + case PROP_MUO_CAN_REDO: + g_value_set_boolean (value, window->priv->uo_widget && + _moo_undo_ops_can_redo (G_OBJECT (window->priv->uo_widget))); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1792,3 +1941,247 @@ _moo_window_class_new_action_callback (MooWindowClass *klass, g_object_unref (action_factory); g_strfreev (conditions); } + + +/*************************************************************************/ +/* MooEditOps + */ + +static void +moo_window_action_cut (MooWindow *window) +{ + g_return_if_fail (window->priv->eo_widget != NULL); + _moo_edit_ops_do_op (G_OBJECT (window->priv->eo_widget), + MOO_EDIT_OP_CUT); +} + +static void +moo_window_action_copy (MooWindow *window) +{ + g_return_if_fail (window->priv->eo_widget != NULL); + _moo_edit_ops_do_op (G_OBJECT (window->priv->eo_widget), + MOO_EDIT_OP_COPY); +} + +static void +moo_window_action_paste (MooWindow *window) +{ + g_return_if_fail (window->priv->eo_widget != NULL); + _moo_edit_ops_do_op (G_OBJECT (window->priv->eo_widget), + MOO_EDIT_OP_PASTE); +} + +static void +moo_window_action_delete (MooWindow *window) +{ + g_return_if_fail (window->priv->eo_widget != NULL); + _moo_edit_ops_do_op (G_OBJECT (window->priv->eo_widget), + MOO_EDIT_OP_DELETE); +} + +static void +moo_window_action_select_all (MooWindow *window) +{ + g_return_if_fail (window->priv->eo_widget != NULL); + _moo_edit_ops_do_op (G_OBJECT (window->priv->eo_widget), + MOO_EDIT_OP_SELECT_ALL); +} + +static void +moo_window_action_undo (MooWindow *window) +{ + g_return_if_fail (window->priv->uo_widget != NULL); + _moo_undo_ops_undo (G_OBJECT (window->priv->uo_widget)); +} + +static void +moo_window_action_redo (MooWindow *window) +{ + g_return_if_fail (window->priv->uo_widget != NULL); + _moo_undo_ops_redo (G_OBJECT (window->priv->uo_widget)); +} + + +static void +emit_can_do_op_changed (MooWindow *window, + MooEditOpType type) +{ + switch (type) + { + case MOO_EDIT_OP_CUT: + g_object_notify (G_OBJECT (window), "can-cut"); + break; + case MOO_EDIT_OP_COPY: + g_object_notify (G_OBJECT (window), "can-copy"); + break; + case MOO_EDIT_OP_PASTE: + g_object_notify (G_OBJECT (window), "can-paste"); + break; + case MOO_EDIT_OP_DELETE: + g_object_notify (G_OBJECT (window), "can-delete"); + break; + case MOO_EDIT_OP_SELECT_ALL: + g_object_notify (G_OBJECT (window), "can-select-all"); + break; + default: + g_return_if_reached (); + } +} + +static void +moo_window_connect_eo_widget (MooWindow *window, + GtkWidget *widget) +{ + g_return_if_fail (_moo_edit_ops_check (G_OBJECT (widget))); + + window->priv->eo_widget = g_object_ref (widget); + + _moo_edit_ops_connect (G_OBJECT (widget)); + g_signal_connect_swapped (widget, "moo-edit-ops-can-do-op-changed", + G_CALLBACK (emit_can_do_op_changed), window); +} + +static void +moo_window_disconnect_eo_widget (MooWindow *window) +{ + GtkWidget *widget; + + widget = window->priv->eo_widget; + window->priv->eo_widget = NULL; + + if (widget) + { + _moo_edit_ops_disconnect (G_OBJECT (widget)); + g_signal_handlers_disconnect_by_func (widget, + (gpointer) emit_can_do_op_changed, + window); + g_object_unref (widget); + } +} + +static GtkWidget * +find_widget_for_edit_ops (MooWindow *window) +{ + GtkWidget *widget = gtk_window_get_focus (GTK_WINDOW (window)); + + while (widget) + { + if (_moo_edit_ops_check (G_OBJECT (widget))) + return widget; + widget = widget->parent; + } + + return window->priv ? window->priv->default_eo_widget : NULL; +} + +static void +check_edit_ops_widget (MooWindow *window) +{ + GtkWidget *widget; + + widget = find_widget_for_edit_ops (window); + + if (window->priv && widget != window->priv->eo_widget) + { + int i; + + moo_window_disconnect_eo_widget (window); + + if (widget) + moo_window_connect_eo_widget (window, widget); + + for (i = 0; i < MOO_N_EDIT_OPS; i++) + emit_can_do_op_changed (window, i); + } +} + + +static void +emit_can_undo_changed (MooWindow *window) +{ + g_object_notify (G_OBJECT (window), "can-undo"); +} + +static void +emit_can_redo_changed (MooWindow *window) +{ + g_object_notify (G_OBJECT (window), "can-redo"); +} + +static void +moo_window_connect_uo_widget (MooWindow *window, + GtkWidget *widget) +{ + g_return_if_fail (_moo_undo_ops_check (G_OBJECT (widget))); + + window->priv->uo_widget = g_object_ref (widget); + + g_signal_connect_swapped (widget, "moo-undo-ops-can-undo-changed", + G_CALLBACK (emit_can_undo_changed), window); + g_signal_connect_swapped (widget, "moo-undo-ops-can-redo-changed", + G_CALLBACK (emit_can_redo_changed), window); +} + +static void +moo_window_disconnect_uo_widget (MooWindow *window) +{ + GtkWidget *widget; + + widget = window->priv->uo_widget; + window->priv->uo_widget = NULL; + + if (widget) + { + g_signal_handlers_disconnect_by_func (widget, + (gpointer) emit_can_undo_changed, + window); + g_signal_handlers_disconnect_by_func (widget, + (gpointer) emit_can_redo_changed, + window); + g_object_unref (widget); + } +} + +static GtkWidget * +find_widget_for_undo_ops (MooWindow *window) +{ + GtkWidget *widget = gtk_window_get_focus (GTK_WINDOW (window)); + + while (widget) + { + if (_moo_undo_ops_check (G_OBJECT (widget))) + return widget; + + widget = widget->parent; + } + + return window->priv ? window->priv->default_uo_widget : NULL; +} + +static void +check_undo_ops_widget (MooWindow *window) +{ + GtkWidget *widget; + + widget = find_widget_for_undo_ops (window); + + if (window->priv && widget != window->priv->uo_widget) + { + moo_window_disconnect_uo_widget (window); + + if (widget) + moo_window_connect_uo_widget (window, widget); + + emit_can_undo_changed (window); + emit_can_redo_changed (window); + } +} + +static void +moo_window_set_focus (GtkWindow *window, + GtkWidget *widget) +{ + GTK_WINDOW_CLASS (moo_window_parent_class)->set_focus (window, widget); + check_edit_ops_widget (MOO_WINDOW (window)); + check_undo_ops_widget (MOO_WINDOW (window)); +}