[gnome-subtitles: 3/6] #126 Undo/Redo in commands with text+translations
- From: Pedro Castro <pcastro src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-subtitles: 3/6] #126 Undo/Redo in commands with text+translations
- Date: Tue, 14 May 2019 07:44:30 +0000 (UTC)
commit 60f95698953d7f02781ba8becea7189ce8f0822f
Author: Pedro Castro <pedro gnomesubtitles org>
Date: Sat May 11 11:27:06 2019 +0100
#126 Undo/Redo in commands with text+translations
Undo/Redo in commands with text and translations must consider if the
translation was reloaded
src/GnomeSubtitles/Core/Command/BaseCommand.cs | 28 +++-
src/GnomeSubtitles/Core/Command/CommandManager.cs | 27 +++-
.../Core/Command/DeleteSubtitlesCommand.cs | 31 +++--
.../Core/Command/ReplaceAllCommand.cs | 20 ++-
.../Core/Command/SplitSubtitlesCommand.cs | 151 +++++++++++++++------
src/SubLib/Core/Timing/SplitOperator.cs | 2 +-
6 files changed, 197 insertions(+), 62 deletions(-)
---
diff --git a/src/GnomeSubtitles/Core/Command/BaseCommand.cs b/src/GnomeSubtitles/Core/Command/BaseCommand.cs
index 64e7497..cc26ec2 100644
--- a/src/GnomeSubtitles/Core/Command/BaseCommand.cs
+++ b/src/GnomeSubtitles/Core/Command/BaseCommand.cs
@@ -1,6 +1,6 @@
/*
* This file is part of Gnome Subtitles.
- * Copyright (C) 2006-2008 Pedro Castro
+ * Copyright (C) 2006-2019 Pedro Castro
*
* Gnome Subtitles is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -17,8 +17,22 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
+using System;
+
namespace GnomeSubtitles.Core.Command {
+/*
+ * Note: commands affecting text *and* translation need to take into account that:
+ * - A translation may not be present when the command is executed, but may have been loaded (Translation >
Open) afterwards.
+ * When undoing, that translation needs to be taken into account. Example: Subtitles (with text only) were
split. Then translation
+ * was opened (which does not generate undo/redo because it's a file open). Undoing the split command will
revert the original
+ * text (which means the subtitles that were split will now be merged). In this case, the translation
needs to be merged for those
+ * subtitles, and not just set to their previous values (because there were none). Additionally, Redo
should always apply the Split
+ * command again in order to do a new split that takes into account the existing translation text.
+ * - A translation may be present when the command is executed, but may have been changed / loaded with a
new file afterwards. This
+ * means a ClearTarget event will be triggered in order to clear the translation data inside this command.
However, this needs to be
+ * taken into account later when Undoing / Redoing.
+ */
public abstract class Command {
private string description;
private bool canGroup = false; //Whether this command can possibly be grouped with the previous
command, if similar
@@ -65,6 +79,18 @@ public abstract class Command {
public virtual Command MergeWith (Command command) {
return command;
}
+
+ /// <summary>Clears a target within an existing command. When a command only has one target (normal
or translation), this is
+ /// not necessary as the whole command will be deleted if that target (whole document or translation,
respectively) becomes
+ /// unavailable. This is used when clearing a translation target in a command that includes
NormalAndTranslation, which
+ /// means that only its translation part needs to be cleared (and that needs to be done by the
command itself).</summary>
+ public virtual void ClearTarget (CommandTarget target) {
+ //If this command has NormalAndTranslation target, make sure it overrides this method
+ if (this.target == CommandTarget.NormalAndTranslation) {
+ throw new NotImplementedException("ClearTarget is required for commands with
NormalAndTranslation target");
+ }
+ }
+
/* Protected members */
diff --git a/src/GnomeSubtitles/Core/Command/CommandManager.cs
b/src/GnomeSubtitles/Core/Command/CommandManager.cs
index 84d373e..15b930d 100644
--- a/src/GnomeSubtitles/Core/Command/CommandManager.cs
+++ b/src/GnomeSubtitles/Core/Command/CommandManager.cs
@@ -1,6 +1,6 @@
/*
* This file is part of Gnome Subtitles.
- * Copyright (C) 2006-2009 Pedro Castro
+ * Copyright (C) 2006-2019 Pedro Castro
*
* Gnome Subtitles is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -248,13 +248,21 @@ public class CommandManager {
/* Go through the undo commands */
if (undoCount > 0) {
int lastUndoIter = iterator - undoCount;
- if (lastUndoIter < 0)
+ if (lastUndoIter < 0) {
lastUndoIter = limit + lastUndoIter;
+ }
int undoIter = lastUndoIter;
while (undoIter != iterator) {
Command undoCommand = commands[undoIter];
+
+ //We only keep the command if its target is not the one we're clearing
if (undoCommand.Target != target) {
+ //If the command target is NormalAndTranslation, it means at least
part of it may need to be cleared
+ if (undoCommand.Target == CommandTarget.NormalAndTranslation) {
+ undoCommand.ClearTarget(target);
+ }
+
newCommands[newIterator] = undoCommand;
newIterator++;
newUndoCount++;
@@ -269,7 +277,14 @@ public class CommandManager {
int newRedoIterator = newIterator; //Because newIterator cannot be changed now
for (int redoNum = 0 ; redoNum < redoCount ; redoNum++) {
Command redoCommand = commands[redoIter];
+
+ //We only keep the command if its target is not the one we're clearing
if (redoCommand.Target != target) {
+ //If the command target is NormalAndTranslation, it means at least
part of it may need to be cleared
+ if (redoCommand.Target == CommandTarget.NormalAndTranslation) {
+ redoCommand.ClearTarget(target);
+ }
+
newCommands[newRedoIterator] = redoCommand;
newRedoIterator++;
newRedoCount++;
@@ -289,12 +304,16 @@ public class CommandManager {
iterator = newIterator;
/* Issue possible events */
- if (toToggleUndo)
+ if (toToggleUndo) {
EmitUndoToggled();
- if (toToggleRedo)
+ }
+
+ if (toToggleRedo) {
EmitRedoToggled();
+ }
}
+
/* Event members */
private void OnBaseInitFinished () {
diff --git a/src/GnomeSubtitles/Core/Command/DeleteSubtitlesCommand.cs
b/src/GnomeSubtitles/Core/Command/DeleteSubtitlesCommand.cs
index 054e70a..79894ca 100644
--- a/src/GnomeSubtitles/Core/Command/DeleteSubtitlesCommand.cs
+++ b/src/GnomeSubtitles/Core/Command/DeleteSubtitlesCommand.cs
@@ -1,6 +1,6 @@
/*
* This file is part of Gnome Subtitles.
- * Copyright (C) 2006-2009,2011 Pedro Castro
+ * Copyright (C) 2006-2019 Pedro Castro
*
* Gnome Subtitles is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -29,11 +29,21 @@ public class DeleteSubtitlesCommand : MultipleSelectionCommand {
private Subtitle[] subtitles = null;
public DeleteSubtitlesCommand () : base(description, false, SelectionIntended.Simple, null) {
- StoreSubtitles(); //TODO move to Execute
}
public override bool Execute () {
+ //Store subtitles to be deleted
+ int count = Paths.Length;
+ subtitles = new Subtitle[count];
+ for (int index = 0 ; index < count ; index++) {
+ TreePath path = Paths[index];
+ subtitles[index] = Base.Document.Subtitles[path];
+ }
+
+ //If translations are loaded, our command affects both the normal document and translations
+ SetCommandTarget(Base.Document.IsTranslationLoaded ? CommandTarget.NormalAndTranslation :
CommandTarget.Normal);
+
Base.Ui.View.Remove(Paths);
return true;
}
@@ -45,15 +55,14 @@ public class DeleteSubtitlesCommand : MultipleSelectionCommand {
public override void Redo () {
Execute();
}
-
- /* Private members */
-
- private void StoreSubtitles () {
- int count = Paths.Length;
- subtitles = new Subtitle[count];
- for (int index = 0 ; index < count ; index++) {
- TreePath path = Paths[index];
- subtitles[index] = Base.Document.Subtitles[path];
+
+ public override void ClearTarget (CommandTarget target) {
+ if (target == CommandTarget.Translation) {
+ foreach (Subtitle subtitle in subtitles) {
+ if (subtitle.HasTranslation) {
+ subtitle.Translation.Clear();
+ }
+ }
}
}
diff --git a/src/GnomeSubtitles/Core/Command/ReplaceAllCommand.cs
b/src/GnomeSubtitles/Core/Command/ReplaceAllCommand.cs
index 09f0fec..167036f 100644
--- a/src/GnomeSubtitles/Core/Command/ReplaceAllCommand.cs
+++ b/src/GnomeSubtitles/Core/Command/ReplaceAllCommand.cs
@@ -1,6 +1,6 @@
/*
* This file is part of Gnome Subtitles.
- * Copyright (C) 2006-2009 Pedro Castro
+ * Copyright (C) 2006-2019 Pedro Castro
*
* Gnome Subtitles is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -90,9 +90,23 @@ public class ReplaceAllCommand : MultipleSelectionCommand {
public override void Redo () {
Undo();
}
+
+ public override void ClearTarget (CommandTarget target) {
+ if (target == CommandTarget.Translation) {
+ ArrayList newReplacedSubtitles = new ArrayList();
+
+ foreach (SubtitleReplaceResult replacedSubtitle in replacedSubtitles) {
+ if (replacedSubtitle.Text != null) {
+ newReplacedSubtitles.Add(new
SubtitleReplaceResult(replacedSubtitle.Number, replacedSubtitle.Text, null));
+ }
+ }
+
+ replacedSubtitles = newReplacedSubtitles;
+ }
+ }
- /* Private members */
+ /* Private members */
private void GetCommandData (out TreePath[] paths, out CommandTarget target) {
ArrayList foundPaths = new ArrayList();
@@ -120,4 +134,4 @@ public class ReplaceAllCommand : MultipleSelectionCommand {
}
-}
+}
\ No newline at end of file
diff --git a/src/GnomeSubtitles/Core/Command/SplitSubtitlesCommand.cs
b/src/GnomeSubtitles/Core/Command/SplitSubtitlesCommand.cs
index 0ac685f..0106815 100644
--- a/src/GnomeSubtitles/Core/Command/SplitSubtitlesCommand.cs
+++ b/src/GnomeSubtitles/Core/Command/SplitSubtitlesCommand.cs
@@ -22,30 +22,125 @@ using Gtk;
using Mono.Unix;
using SubLib.Core.Domain;
using SubLib.Core.Timing;
+using System;
using System.Collections;
+using System.Collections.Generic;
namespace GnomeSubtitles.Core.Command {
public class SplitSubtitlesCommand : MultipleSelectionCommand {
private static string description = Catalog.GetString("Splitting subtitles");
+
private Subtitle[] subtitlesBefore = null;
+ private Dictionary<int, int[]> mapping = new Dictionary<int, int[]>(); //Maps the indices of the
subtitles that were split <originalIndex, resultingIndices[]>
private TreePath[] pathsAfter = null;
- private Subtitle[] subtitlesAfter = null;
+ bool translationCleared = false; //Translation was cleared, so if we are undoing, we need to use the
new translation text if present
public SplitSubtitlesCommand () : base(description, false, SelectionIntended.Simple, null) {
}
-
+
public override bool Execute () {
+ SetCommandTarget(Base.Document.IsTranslationLoaded ? CommandTarget.NormalAndTranslation :
CommandTarget.Normal);
+
+ if (!Split()) {
+ return false;
+ }
+
+ PostProcess();
+ return true;
+ }
+
+ public override void Undo () {
+ /*
+ * If the command originally applied to translations and they have changed, we need to use
the new translations because we didn't have them when this command was first executed.
+ * The same goes if the command didn't originally have translations but it now has.
+ */
+ bool hadTranslation = (this.Target == CommandTarget.NormalAndTranslation);
+ bool updateTranslation = Base.Document.IsTranslationLoaded
+ && ((hadTranslation && translationCleared) || !hadTranslation);
+
+ if (updateTranslation) {
+ for (int i = 0; i < subtitlesBefore.Length; i++) {
+ Subtitle subtitle = subtitlesBefore[i];
+ int subtitleIndex = Util.PathToInt(this.Paths[i]);
+
+ ArrayList translations = new ArrayList();
+ foreach (int subtitleIndexAfter in this.mapping[subtitleIndex]) {
+ string translation =
Base.Document.Subtitles[subtitleIndexAfter].Translation.Get();
+ if (!String.IsNullOrEmpty(translation)) {
+ translations.Add(translation);
+ }
+ }
+
+ string mergedTranslation = String.Join("\n",
(string[])translations.ToArray(typeof(string)));
+ subtitle.Translation.Set(mergedTranslation);
+ }
+ translationCleared = false;
+ }
+
+ SetCommandTarget(Base.Document.IsTranslationLoaded ? CommandTarget.NormalAndTranslation :
CommandTarget.Normal);
+
+
+ Base.Document.Subtitles.Remove(this.pathsAfter);
+ Base.Ui.View.Insert(this.subtitlesBefore, this.Paths, this.FirstPath);
+ PostProcess();
+ }
+
+
+ /*
+ * In Redo, we just run the Split command again, so we don't reuse existing subtitles. Because of
that, if a new translation
+ * has been loaded there's no problem, its current text will be used.
+ */
+ public override void Redo () {
+ SetCommandTarget(Base.Document.IsTranslationLoaded ? CommandTarget.NormalAndTranslation :
CommandTarget.Normal);
+
+ if (!Split()) {
+ return;
+ }
+
+ PostProcess();
+
+ Split();
+ }
+
+ public override void ClearTarget (CommandTarget target) {
+ if (target == CommandTarget.Translation) {
+ if (subtitlesBefore != null) {
+ foreach (Subtitle subtitle in subtitlesBefore) {
+ if (subtitle.HasTranslation) {
+ subtitle.Translation.Clear();
+ }
+ }
+ }
+
+ translationCleared = true;
+ }
+ }
+
+
+ /* Protected members */
+
+ protected void PostProcess () {
+ Base.Ui.Video.SeekToSelection(true);
+ }
+
+
+ /* Private members */
+
+ private bool Split () {
Ui.View.Subtitles subtitles = Base.Document.Subtitles;
ArrayList pathsBefore = new ArrayList();
ArrayList subtitlesBefore = new ArrayList();
ArrayList pathsAfter = new ArrayList();
+ Dictionary<int, int[]> mapping = new Dictionary<int, int[]>();
SplitOperator splitOperator = new SplitOperator(subtitles,
Base.Config.TimingsTimeBetweenSubtitles);
foreach (TreePath path in Paths) {
+ int originalSubtitleIndex = Util.PathToInt(path);
+
int subtitlesThatHaveBeenAdded = pathsAfter.Count - pathsBefore.Count; //number of
subtitles that have been added ever since, in this loop
- int subtitleIndex = Util.PathToInt(path) + subtitlesThatHaveBeenAdded;
+ int subtitleIndex = originalSubtitleIndex + subtitlesThatHaveBeenAdded;
Subtitle subtitle = subtitles[subtitleIndex];
Subtitle[] newSubtitles = splitOperator.Split(subtitle);
@@ -54,10 +149,15 @@ public class SplitSubtitlesCommand : MultipleSelectionCommand {
subtitlesBefore.Add(subtitle);
subtitles.Remove(subtitleIndex);
+ int[] newSubtitleIndices = new int[newSubtitles.Length];
for (int i = 0 ; i < newSubtitles.Length ; i++) {
- pathsAfter.Add(Util.IntToPath(subtitleIndex + i));
- subtitles.Add(newSubtitles[i], subtitleIndex + i);
+ int newSubtitleIndex = subtitleIndex + i;
+ pathsAfter.Add(Util.IntToPath(newSubtitleIndex));
+ subtitles.Add(newSubtitles[i], newSubtitleIndex);
+ newSubtitleIndices[i] = newSubtitleIndex;
}
+
+ mapping.Add(originalSubtitleIndex, newSubtitleIndices);
}
}
@@ -68,46 +168,13 @@ public class SplitSubtitlesCommand : MultipleSelectionCommand {
this.subtitlesBefore = (Subtitle [])subtitlesBefore.ToArray(typeof(Subtitle));
this.Paths = (TreePath [])pathsBefore.ToArray(typeof(TreePath));
this.pathsAfter = (TreePath [])pathsAfter.ToArray(typeof(TreePath));
+ this.mapping = mapping;
+
Base.Ui.View.RedrawPaths(this.pathsAfter);
- Base.Ui.View.Selection.Select(this.pathsAfter, this.pathsAfter [0], true);
+ Base.Ui.View.Selection.Select(this.pathsAfter, this.pathsAfter[0], true);
PostProcess();
return true;
}
-
- public override void Undo () {
- if (this.subtitlesAfter == null) {
- this.subtitlesAfter = GetSubtitlesAfter(Base.Document.Subtitles, this.pathsAfter);
- }
- Base.Document.Subtitles.Remove(this.pathsAfter);
- Base.Ui.View.Insert(this.subtitlesBefore, this.Paths, this.FirstPath);
- PostProcess();
- }
-
- public override void Redo () {
- Base.Document.Subtitles.Remove(this.Paths);
- Base.Ui.View.Insert(this.subtitlesAfter, this.pathsAfter, this.pathsAfter[0]);
- PostProcess();
- }
-
- /* Protected members */
-
- protected void PostProcess () {
- Base.Ui.Video.SeekToSelection(true);
- }
-
-
- /* Private members */
-
- private Subtitle[] GetSubtitlesAfter (GnomeSubtitles.Ui.View.Subtitles subtitles, TreePath[]
pathsAfter) {
- Subtitle[] subtitlesAfter = new Subtitle[pathsAfter.Length];
- for (int index = 0 ; index < pathsAfter.Length ; index++) {
- TreePath path = pathsAfter[index];
- int subtitleIndex = Util.PathToInt(path);
- subtitlesAfter[index] = subtitles[subtitleIndex];
- }
- return subtitlesAfter;
- }
-
}
-}
+}
\ No newline at end of file
diff --git a/src/SubLib/Core/Timing/SplitOperator.cs b/src/SubLib/Core/Timing/SplitOperator.cs
index d588924..f41eaa3 100644
--- a/src/SubLib/Core/Timing/SplitOperator.cs
+++ b/src/SubLib/Core/Timing/SplitOperator.cs
@@ -131,7 +131,7 @@ public class SplitOperator {
ArrayList translationLines = GetNonEmptyLines(originalLines);
if (translationLines.Count > textLines.Count) {
int lastTextIndex = textLines.Count - 1;
- String lastLine = String.Join("\n",
(string[])translationLines.ToArray(typeof(string)), lastTextIndex, translationLines.Count - textLines.Count +
1);
+ string lastLine = String.Join("\n",
(string[])translationLines.ToArray(typeof(string)), lastTextIndex, translationLines.Count - textLines.Count +
1);
translationLines[lastTextIndex] = lastLine;
translationLines.RemoveRange(lastTextIndex + 1, translationLines.Count -
textLines.Count);
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]