undo: Turn describe() vfunc into "title" property

This way, applications can override the title to make it more useful.
This commit is contained in:
Benjamin Otte
2015-08-19 19:27:50 +02:00
parent 3b11dcf442
commit 716878cc90
5 changed files with 155 additions and 90 deletions

View File

@@ -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

View File

@@ -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]);
}
/*

View File

@@ -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

View File

@@ -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));
}
}

View File

@@ -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);
}