From 716878cc90c65129fedf590e6ea75ccf70ea3e31 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Wed, 19 Aug 2015 19:27:50 +0200 Subject: [PATCH] undo: Turn describe() vfunc into "title" property This way, applications can override the title to make it more useful. --- gtk/gtkentryundocommand.c | 122 +++++++++++++++++++----------------- gtk/gtkundocommand.c | 99 ++++++++++++++++++++++++----- gtk/gtkundocommandprivate.h | 6 +- gtk/gtkundostack.c | 4 +- gtk/gtkundoundocommand.c | 14 ++--- 5 files changed, 155 insertions(+), 90 deletions(-) diff --git a/gtk/gtkentryundocommand.c b/gtk/gtkentryundocommand.c index eefc78ce22..c88570caa9 100644 --- a/gtk/gtkentryundocommand.c +++ b/gtk/gtkentryundocommand.c @@ -103,6 +103,64 @@ gtk_entry_undo_command_redo (GtkUndoCommand *command) return gtk_entry_undo_command_run (priv->entry, &priv->after); } +static guint +get_prefix_len (const char *str1, + const char *str2) +{ + guint i; + + for (i = 0; str1[i] == str2[i] && str1[i] != 0; i++) + { + /* nothing to do here */ + } + + return i; +} + +static guint +get_suffix_len (const char *str1, + guint len1, + const char *str2, + guint len2) +{ + const char *cur1, *cur2; + guint i, max_len; + + cur1 = str1 + len1 - 1; + cur2 = str2 + len2 - 1; + max_len = MIN (len1, len2); + + for (i = 0; *cur1 == *cur2 && i < max_len; i++) + { + cur1--; + cur2--; + } + + return i; +} + +char * +generate_title (const GtkEntrySnapshot *before, + const GtkEntrySnapshot *after) +{ + guint before_len, after_len, prefix_len, suffix_len; + + before_len = strlen (before->text); + after_len = strlen (after->text); + prefix_len = get_prefix_len (before->text, after->text); + suffix_len = get_suffix_len (before->text, before_len, + after->text, after_len); + + if (before_len == after_len && before_len == prefix_len) + return g_strdup (_("No changes")); /* huh? */ + else if (prefix_len + suffix_len == before_len) + return g_strdup_printf (_("Entered `%.*s'"), after_len - prefix_len - suffix_len, after->text + prefix_len); + else if (prefix_len + suffix_len == after_len) + return g_strdup_printf (_("Deleted `%.*s'"), before_len - prefix_len - suffix_len, before->text + prefix_len); + else + return g_strdup (_("Text changed")); +} + static GtkUndoCommand * gtk_entry_undo_command_new_from_snapshots (GtkEntry *entry, gint64 timestamp, @@ -111,11 +169,16 @@ gtk_entry_undo_command_new_from_snapshots (GtkEntry *entry, { GtkEntryUndoCommand *command; GtkEntryUndoCommandPrivate *priv; + char *title; + + title = generate_title (before, after); command = g_object_new (GTK_TYPE_ENTRY_UNDO_COMMAND, "timestamp", timestamp, + "title", title, NULL); priv = gtk_entry_undo_command_get_instance_private (command); + g_free (title); priv->entry = entry; gtk_entry_snapshot_copy (&priv->before, before); @@ -167,64 +230,6 @@ gtk_entry_undo_command_should_merge (GtkUndoCommand *command, return TRUE; } -static guint -get_prefix_len (const char *str1, - const char *str2) -{ - guint i; - - for (i = 0; str1[i] == str2[i] && str1[i] != 0; i++) - { - /* nothing to do here */ - } - - return i; -} - -static guint -get_suffix_len (const char *str1, - guint len1, - const char *str2, - guint len2) -{ - const char *cur1, *cur2; - guint i, max_len; - - cur1 = str1 + len1 - 1; - cur2 = str2 + len2 - 1; - max_len = MIN (len1, len2); - - for (i = 0; *cur1 == *cur2 && i < max_len; i++) - { - cur1--; - cur2--; - } - - return i; -} - -char * -gtk_entry_undo_command_describe (GtkUndoCommand *command) -{ - GtkEntryUndoCommandPrivate *priv = gtk_entry_undo_command_get_instance_private (GTK_ENTRY_UNDO_COMMAND (command)); - guint before_len, after_len, prefix_len, suffix_len; - - before_len = strlen (priv->before.text); - after_len = strlen (priv->after.text); - prefix_len = get_prefix_len (priv->before.text, priv->after.text); - suffix_len = get_suffix_len (priv->before.text, before_len, - priv->after.text, after_len); - - if (before_len == after_len && before_len == prefix_len) - return g_strdup (_("No changes")); /* huh? */ - else if (prefix_len + suffix_len == before_len) - return g_strdup_printf (_("Entered `%.*s'"), after_len - prefix_len - suffix_len, priv->after.text + prefix_len); - else if (prefix_len + suffix_len == after_len) - return g_strdup_printf (_("Deleted `%.*s'"), before_len - prefix_len - suffix_len, priv->before.text + prefix_len); - else - return g_strdup (_("Text changed")); -} - static void gtk_entry_undo_command_finalize (GObject *object) { @@ -248,7 +253,6 @@ gtk_entry_undo_command_class_init (GtkEntryUndoCommandClass *klass) undo_class->redo = gtk_entry_undo_command_redo; undo_class->merge = gtk_entry_undo_command_merge; undo_class->should_merge = gtk_entry_undo_command_should_merge; - undo_class->describe = gtk_entry_undo_command_describe; } static void diff --git a/gtk/gtkundocommand.c b/gtk/gtkundocommand.c index bbc60efabf..b7fc36683d 100644 --- a/gtk/gtkundocommand.c +++ b/gtk/gtkundocommand.c @@ -26,11 +26,13 @@ typedef struct _GtkUndoCommandPrivate GtkUndoCommandPrivate; struct _GtkUndoCommandPrivate { gint64 timestamp; + char *title; }; enum { PROP_0, PROP_TIMESTAMP, + PROP_TITLE, /* add more */ NUM_PROPERTIES }; @@ -54,6 +56,10 @@ gtk_undo_command_get_property (GObject *object, g_value_set_int64 (value, priv->timestamp); break; + case PROP_TITLE: + g_value_set_string (value, priv->title); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } @@ -65,7 +71,8 @@ gtk_undo_command_set_property (GObject *object, const GValue *value, GParamSpec *pspec) { - GtkUndoCommandPrivate *priv = gtk_undo_command_get_instance_private (GTK_UNDO_COMMAND (object)); + GtkUndoCommand *command = GTK_UNDO_COMMAND (object); + GtkUndoCommandPrivate *priv = gtk_undo_command_get_instance_private (command); switch (property_id) { @@ -75,11 +82,25 @@ gtk_undo_command_set_property (GObject *object, priv->timestamp = g_get_real_time (); break; + case PROP_TITLE: + gtk_undo_command_set_title (command, g_value_get_string (value)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } +static void +gtk_undo_command_finalize (GObject *object) +{ + GtkUndoCommandPrivate *priv = gtk_undo_command_get_instance_private (GTK_UNDO_COMMAND (object)); + + g_free (priv->title); + + G_OBJECT_CLASS (gtk_undo_command_parent_class)->finalize (object); +} + static gboolean gtk_undo_command_real_undo (GtkUndoCommand *command) { @@ -116,17 +137,6 @@ gtk_undo_command_real_should_merge (GtkUndoCommand *command, return TRUE; } -char * -gtk_undo_command_real_describe (GtkUndoCommand *command) -{ - g_warning ("%s class failed to implement undo", G_OBJECT_TYPE_NAME (command)); - - /* translators: This string is the fallback message that gets used - * when undo commands are improperly implemented and don't have a - * proper description. */ - return g_strdup (_("unknown undo command")); -} - static void gtk_undo_command_class_init (GtkUndoCommandClass *klass) { @@ -134,12 +144,12 @@ gtk_undo_command_class_init (GtkUndoCommandClass *klass) object_class->set_property = gtk_undo_command_set_property; object_class->get_property = gtk_undo_command_get_property; + object_class->finalize = gtk_undo_command_finalize; klass->undo = gtk_undo_command_real_undo; klass->redo = gtk_undo_command_real_redo; klass->merge = gtk_undo_command_real_merge; klass->should_merge = gtk_undo_command_real_should_merge; - klass->describe = gtk_undo_command_real_describe; /* * GtkUndoCommand:timestamp: @@ -153,6 +163,18 @@ gtk_undo_command_class_init (GtkUndoCommandClass *klass) G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); + /* + * GtkUndoCommand:title: + * + * Title of this command. See + * gtk_undo_command_get_title() for details. + */ + properties[PROP_TITLE] = g_param_spec_string ("title", "Title", + "Title of this command", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | + G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); + g_object_class_install_properties (object_class, NUM_PROPERTIES, properties); } @@ -209,12 +231,57 @@ gtk_undo_command_should_merge (GtkUndoCommand *command, return GTK_UNDO_COMMAND_GET_CLASS (command)->should_merge (command, followup_command); } -char * -gtk_undo_command_describe (GtkUndoCommand *command) +/* + * gtk_undo_command_get_title: + * @command: The command + * + * Gets the title of the @command. The title is a translated string + * for presentation in a user interface, for example a list of actions + * to undo. + * + * Undo commands always have a title set, but that title is often generic. + * Applications may want to update titles via gtk_undo_command_set_title() + * + * Returns: The title for the command + */ +const char * +gtk_undo_command_get_title (GtkUndoCommand *command) { + GtkUndoCommandPrivate *priv = gtk_undo_command_get_instance_private (command); + g_return_val_if_fail (GTK_IS_UNDO_COMMAND (command), NULL); - return GTK_UNDO_COMMAND_GET_CLASS (command)->describe (command); + return priv->title; +} + +/* + * gtk_undo_command_set_title: + * @command: The command + * @title: The new title + * + * Updates the title for the given @command. This function should be called + * when applications can provide a better title for an action than the generic + * title provided upon creation of commands. + */ +void +gtk_undo_command_set_title (GtkUndoCommand *command, + const char *title) +{ + GtkUndoCommandPrivate *priv = gtk_undo_command_get_instance_private (command); + + g_return_if_fail (GTK_IS_UNDO_COMMAND (command)); + + if (title == NULL) + /* translators: This is the (hopefully never used) fallback string for undo commands without a name */ + title = _("Unknown command"); + + if (g_strcmp0 (priv->title, title) == 0) + return; + + g_free (priv->title); + priv->title = g_strdup (title); + + g_object_notify_by_pspec (G_OBJECT (command), properties[PROP_TITLE]); } /* diff --git a/gtk/gtkundocommandprivate.h b/gtk/gtkundocommandprivate.h index bfe982e47a..f5d00002d2 100644 --- a/gtk/gtkundocommandprivate.h +++ b/gtk/gtkundocommandprivate.h @@ -53,13 +53,14 @@ struct _GtkUndoCommandClass /* should the two commands be merged for user undo purposes? */ gboolean (* should_merge) (GtkUndoCommand *command, GtkUndoCommand *followup_command); - /* get a translated string describing the command */ - char * (* describe) (GtkUndoCommand *command); }; GType gtk_undo_command_get_type (void) G_GNUC_CONST; gint64 gtk_undo_command_get_timestamp (GtkUndoCommand *command); +const char * gtk_undo_command_get_title (GtkUndoCommand *command); +void gtk_undo_command_set_title (GtkUndoCommand *command, + const char *title); gboolean gtk_undo_command_undo (GtkUndoCommand *command); gboolean gtk_undo_command_redo (GtkUndoCommand *command); @@ -67,7 +68,6 @@ GtkUndoCommand * gtk_undo_command_merge (GtkUndoCommand GtkUndoCommand *followup_command); gboolean gtk_undo_command_should_merge (GtkUndoCommand *command, GtkUndoCommand *followup_command); -char * gtk_undo_command_describe (GtkUndoCommand *command); G_END_DECLS diff --git a/gtk/gtkundostack.c b/gtk/gtkundostack.c index d977b1799d..5dea553ab9 100644 --- a/gtk/gtkundostack.c +++ b/gtk/gtkundostack.c @@ -147,10 +147,8 @@ gtk_undo_stack_dump (GtkUndoStack *stack) iter = g_sequence_iter_next (iter)) { GtkUndoCommand *command = g_sequence_get (iter); - char *desc; - desc = gtk_undo_command_describe (command); - g_print (" %s\n", desc); + g_print (" %s\n", gtk_undo_command_get_title (command)); } } diff --git a/gtk/gtkundoundocommand.c b/gtk/gtkundoundocommand.c index 0c119c9d0b..17aa3907e6 100644 --- a/gtk/gtkundoundocommand.c +++ b/gtk/gtkundoundocommand.c @@ -77,14 +77,6 @@ gtk_undo_undo_command_merge (GtkUndoCommand *command, return NULL; } -char * -gtk_undo_undo_command_describe (GtkUndoCommand *command) -{ - GtkUndoUndoCommandPrivate *priv = gtk_undo_undo_command_get_instance_private (GTK_UNDO_UNDO_COMMAND (command)); - - return g_strdup_printf (_("Undo last %u commands"), g_sequence_get_length (priv->commands)); -} - static void gtk_undo_undo_command_finalize (GObject *object) { @@ -106,7 +98,6 @@ gtk_undo_undo_command_class_init (GtkUndoUndoCommandClass *klass) undo_class->undo = gtk_undo_undo_command_undo; undo_class->redo = gtk_undo_undo_command_redo; undo_class->merge = gtk_undo_undo_command_merge; - undo_class->describe = gtk_undo_undo_command_describe; } static void @@ -124,6 +115,7 @@ gtk_undo_undo_command_new (GSequenceIter *begin_iter, GtkUndoUndoCommand *result; GtkUndoUndoCommandPrivate *priv; GSequenceIter *iter; + char *title; g_return_val_if_fail (begin_iter != NULL, NULL); g_return_val_if_fail (end_iter != NULL, NULL); @@ -136,6 +128,10 @@ gtk_undo_undo_command_new (GSequenceIter *begin_iter, g_sequence_prepend (priv->commands, g_object_ref (g_sequence_get (iter))); } + title = g_strdup_printf (_("Undo last %u commands"), g_sequence_get_length (priv->commands)); + gtk_undo_command_set_title (GTK_UNDO_COMMAND (result), title); + g_free (title); + return GTK_UNDO_COMMAND (result); }