2
0
Эх сурвалжийг харах

Add undo and redo support for cells attribute.

BDisp 9 сар өмнө
parent
commit
e58c15f2e6

+ 23 - 2
Terminal.Gui/Drawing/Cell.cs

@@ -72,8 +72,8 @@ public record struct Cell (Attribute? Attribute = null, bool IsDirty = false, Ru
     public static List<List<Cell>> StringToLinesOfCells (string content, Attribute? attribute = null)
     {
         List<Cell> cells = content.EnumerateRunes ()
-                                      .Select (x => new Cell { Rune = x, Attribute = attribute })
-                                      .ToList ();
+                                  .Select (x => new Cell { Rune = x, Attribute = attribute })
+                                  .ToList ();
 
         return SplitNewLines (cells);
     }
@@ -93,6 +93,27 @@ public record struct Cell (Attribute? Attribute = null, bool IsDirty = false, Ru
         return str;
     }
 
+    /// <summary>Converts a <see cref="List{Cell}"/> generic collection into a string.</summary>
+    /// <param name="cellsList">The enumerable cell to convert.</param>
+    /// <returns></returns>
+    public static string ToString (List<List<Cell>> cellsList)
+    {
+        var str = string.Empty;
+
+        for (var i = 0; i < cellsList.Count; i++)
+        {
+            IEnumerable<Cell> cellList = cellsList [i];
+            str += ToString (cellList);
+
+            if (i + 1 < cellsList.Count)
+            {
+                str += Environment.NewLine;
+            }
+        }
+
+        return str;
+    }
+
     // Turns the string into cells, this does not split the contents on a newline if it is present.
 
     internal static List<Cell> StringToCells (string str, Attribute? attribute = null)

+ 119 - 0
Terminal.Gui/Drawing/ColorScheme.Colors.cs

@@ -173,4 +173,123 @@ public sealed class Colors : INotifyCollectionChanged, IDictionary<string, Color
 
         return ColorSchemes;
     }
+
+    /// <summary>
+    ///     Open a <see cref="Dialog"/> with two <see cref="ColorPicker"/> or <see cref="ColorPicker16"/>, based on the
+    ///     <see cref="ConsoleDriver.Force16Colors"/> is false or true, respectively, for <see cref="Attribute.Foreground"/>
+    ///     and <see cref="Attribute.Background"/> colors.
+    /// </summary>
+    /// <param name="title">The title to show in the dialog.</param>
+    /// <param name="currentAttribute">The current attribute used.</param>
+    /// <param name="newAttribute">The new attribute.</param>
+    /// <returns><see langword="true"/> if a new color was accepted, otherwise <see langword="false"/>.</returns>
+    public static bool PromptForColors (string title, Attribute? currentAttribute, out Attribute newAttribute)
+    {
+        var accept = false;
+
+        var d = new Dialog
+        {
+            Title = title,
+            Width = Application.Force16Colors ? 37 : Dim.Auto (DimAutoStyle.Auto, Dim.Percent (80), Dim.Percent (90)),
+            Height = 20
+        };
+
+        var btnOk = new Button
+        {
+            X = Pos.Center () - 5,
+            Y = Application.Force16Colors ? 6 : 4,
+            Text = "Ok",
+            Width = Dim.Auto (),
+            IsDefault = true
+        };
+
+        btnOk.Accept += (s, e) =>
+                        {
+                            accept = true;
+                            e.Handled = true;
+                            Application.RequestStop ();
+                        };
+
+        var btnCancel = new Button
+        {
+            X = Pos.Center () + 5,
+            Y = 4,
+            Text = "Cancel",
+            Width = Dim.Auto ()
+        };
+
+        btnCancel.Accept += (s, e) =>
+                            {
+                                e.Handled = true;
+                                Application.RequestStop ();
+                            };
+
+        d.Add (btnOk);
+        d.Add (btnCancel);
+
+        d.AddButton (btnOk);
+        d.AddButton (btnCancel);
+
+        View cpForeground;
+
+        if (Application.Force16Colors)
+        {
+            cpForeground = new ColorPicker16
+            {
+                SelectedColor = currentAttribute!.Value.Foreground.GetClosestNamedColor16 (),
+                Width = Dim.Fill (),
+                BorderStyle = LineStyle.Single,
+                Title = "Foreground"
+            };
+        }
+        else
+        {
+            cpForeground = new ColorPicker
+            {
+                SelectedColor = currentAttribute!.Value.Foreground,
+                Width = Dim.Fill (),
+                Style = new () { ShowColorName = true, ShowTextFields = true },
+                BorderStyle = LineStyle.Single,
+                Title = "Foreground"
+            };
+            ((ColorPicker)cpForeground).ApplyStyleChanges ();
+        }
+
+        View cpBackground;
+
+        if (Application.Force16Colors)
+        {
+            cpBackground = new ColorPicker16
+            {
+                SelectedColor = currentAttribute!.Value.Background.GetClosestNamedColor16 (),
+                Y = Pos.Bottom (cpForeground) + 1,
+                Width = Dim.Fill (),
+                BorderStyle = LineStyle.Single,
+                Title = "Background"
+            };
+        }
+        else
+        {
+            cpBackground = new ColorPicker
+            {
+                SelectedColor = currentAttribute!.Value.Background,
+                Width = Dim.Fill (),
+                Y = Pos.Bottom (cpForeground) + 1,
+                Style = new () { ShowColorName = true, ShowTextFields = true },
+                BorderStyle = LineStyle.Single,
+                Title = "Background"
+            };
+            ((ColorPicker)cpBackground).ApplyStyleChanges ();
+        }
+
+        d.Add (cpForeground, cpBackground);
+
+        Application.Run (d);
+        d.Dispose ();
+        Color newForeColor = Application.Force16Colors ? ((ColorPicker16)cpForeground).SelectedColor : ((ColorPicker)cpForeground).SelectedColor;
+        Color newBackColor = Application.Force16Colors ? ((ColorPicker16)cpBackground).SelectedColor : ((ColorPicker)cpBackground).SelectedColor;
+        newAttribute = new (newForeColor, newBackColor);
+
+        return accept;
+    }
 }

+ 9 - 0
Terminal.Gui/Resources/Strings.Designer.cs

@@ -1437,6 +1437,15 @@ namespace Terminal.Gui.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to Co_lors.
+        /// </summary>
+        internal static string ctxColors {
+            get {
+                return ResourceManager.GetString("ctxColors", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to _Copy.
         /// </summary>

+ 15 - 12
Terminal.Gui/Resources/Strings.fr-FR.resx

@@ -117,27 +117,27 @@
   <resheader name="writer">
     <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
   </resheader>
+  <data name="ctxSelectAll" xml:space="preserve">
+    <value>Tout _sélectionner</value>
+  </data>
+  <data name="ctxDeleteAll" xml:space="preserve">
+    <value>_Tout supprimer</value>
+  </data>
   <data name="ctxCopy" xml:space="preserve">
     <value>_Copier</value>
   </data>
   <data name="ctxCut" xml:space="preserve">
     <value>Co_uper</value>
   </data>
-  <data name="ctxDeleteAll" xml:space="preserve">
-    <value>_Tout supprimer</value>
-  </data>
   <data name="ctxPaste" xml:space="preserve">
     <value>C_oller</value>
   </data>
-  <data name="ctxRedo" xml:space="preserve">
-    <value>_Rétablir</value>
-  </data>
-  <data name="ctxSelectAll" xml:space="preserve">
-    <value>Tout _sélectionner</value>
-  </data>
   <data name="ctxUndo" xml:space="preserve">
     <value>_Annuler</value>
   </data>
+  <data name="ctxRedo" xml:space="preserve">
+    <value>_Rétablir</value>
+  </data>
   <data name="fdDirectory" xml:space="preserve">
     <value>_Dossier</value>
   </data>
@@ -168,16 +168,19 @@
   <data name="wzNext" xml:space="preserve">
     <value>Prochai_n...</value>
   </data>
+  <data name="btnOpen" xml:space="preserve">
+    <value>Ouvrir</value>
+  </data>
   <data name="btnSave" xml:space="preserve">
     <value>Enregistrer</value>
   </data>
   <data name="btnSaveAs" xml:space="preserve">
     <value>E_nregistrer sous</value>
   </data>
-  <data name="btnOpen" xml:space="preserve">
-    <value>Ouvrir</value>
-  </data>
   <data name="dpTitle" xml:space="preserve">
     <value>Sélecteur de Date</value>
   </data>
+  <data name="ctxColors" xml:space="preserve">
+    <value>Cou_leurs</value>
+  </data>
 </root>

+ 58 - 55
Terminal.Gui/Resources/Strings.ja-JP.resx

@@ -117,27 +117,27 @@
   <resheader name="writer">
     <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
   </resheader>
+  <data name="ctxSelectAll" xml:space="preserve">
+    <value>全て選択 (_S)</value>
+  </data>
+  <data name="ctxDeleteAll" xml:space="preserve">
+    <value>全て削除 (_D)</value>
+  </data>
   <data name="ctxCopy" xml:space="preserve">
     <value>コピー (_C)</value>
   </data>
   <data name="ctxCut" xml:space="preserve">
     <value>切り取り (_T)</value>
   </data>
-  <data name="ctxDeleteAll" xml:space="preserve">
-    <value>全て削除 (_D)</value>
-  </data>
   <data name="ctxPaste" xml:space="preserve">
     <value>貼り付け (_P)</value>
   </data>
-  <data name="ctxRedo" xml:space="preserve">
-    <value>やり直し (_R)</value>
-  </data>
-  <data name="ctxSelectAll" xml:space="preserve">
-    <value>全て選択 (_S)</value>
-  </data>
   <data name="ctxUndo" xml:space="preserve">
     <value>元に戻す (_U)</value>
   </data>
+  <data name="ctxRedo" xml:space="preserve">
+    <value>やり直し (_R)</value>
+  </data>
   <data name="fdDirectory" xml:space="preserve">
     <value>ディレクトリ</value>
   </data>
@@ -171,44 +171,47 @@
   <data name="fdDirectoryAlreadyExistsFeedback" xml:space="preserve">
     <value>同じ名前のディレクトリはすでに存在しました</value>
   </data>
-  <data name="fdDeleteBody" xml:space="preserve">
-    <value>“{0}”を削除もよろしいですか?この操作は元に戻りません</value>
-  </data>
-  <data name="fdType" xml:space="preserve">
-    <value>タイプ</value>
+  <data name="fdDirectoryMustExistFeedback" xml:space="preserve">
+    <value>すでに存在したディレクトリを選択してください</value>
   </data>
-  <data name="fdSize" xml:space="preserve">
-    <value>サイズ</value>
+  <data name="fdFileAlreadyExistsFeedback" xml:space="preserve">
+    <value>同じ名前のファイルはすでに存在しました</value>
   </data>
-  <data name="fdPathCaption" xml:space="preserve">
-    <value>パスを入力</value>
+  <data name="fdFileMustExistFeedback" xml:space="preserve">
+    <value>すでに存在したファイルを選択してください</value>
   </data>
   <data name="fdFilename" xml:space="preserve">
     <value>ファイル名</value>
   </data>
-  <data name="fdNewTitle" xml:space="preserve">
-    <value>新規ディレクトリ</value>
-  </data>
-  <data name="btnNo" xml:space="preserve">
-    <value>いいえ (_N)</value>
-  </data>
-  <data name="btnYes" xml:space="preserve">
-    <value>はい (_Y)</value>
+  <data name="fdFileOrDirectoryMustExistFeedback" xml:space="preserve">
+    <value>すでに存在したファイルまたはディレクトリを選択してください</value>
   </data>
   <data name="fdModified" xml:space="preserve">
     <value>変更日時</value>
   </data>
-  <data name="fdFileOrDirectoryMustExistFeedback" xml:space="preserve">
-    <value>すでに存在したファイルまたはディレクトリを選択してください</value>
+  <data name="fdPathCaption" xml:space="preserve">
+    <value>パスを入力</value>
   </data>
-  <data name="fdDirectoryMustExistFeedback" xml:space="preserve">
-    <value>すでに存在したディレクトリを選択してください</value>
+  <data name="fdSearchCaption" xml:space="preserve">
+    <value>検索を入力</value>
   </data>
-  <data name="fdFileMustExistFeedback" xml:space="preserve">
-    <value>すでに存在したファイルを選択してください</value>
+  <data name="fdSize" xml:space="preserve">
+    <value>サイズ</value>
   </data>
-  <data name="fdRenamePrompt" xml:space="preserve">
-    <value>名前:</value>
+  <data name="fdType" xml:space="preserve">
+    <value>タイプ</value>
+  </data>
+  <data name="fdWrongFileTypeFeedback" xml:space="preserve">
+    <value>ファイルタイプが間違っでいます</value>
+  </data>
+  <data name="fdAnyFiles" xml:space="preserve">
+    <value>任意ファイル</value>
+  </data>
+  <data name="fdDeleteBody" xml:space="preserve">
+    <value>“{0}”を削除もよろしいですか?この操作は元に戻りません</value>
+  </data>
+  <data name="fdDeleteFailedTitle" xml:space="preserve">
+    <value>削除失敗</value>
   </data>
   <data name="fdDeleteTitle" xml:space="preserve">
     <value>{0} を削除</value>
@@ -216,35 +219,26 @@
   <data name="fdNewFailed" xml:space="preserve">
     <value>新規失敗</value>
   </data>
-  <data name="fdExisting" xml:space="preserve">
-    <value>既存</value>
+  <data name="fdNewTitle" xml:space="preserve">
+    <value>新規ディレクトリ</value>
   </data>
-  <data name="fdRenameTitle" xml:space="preserve">
-    <value>名前を変更</value>
+  <data name="btnNo" xml:space="preserve">
+    <value>いいえ (_N)</value>
   </data>
   <data name="fdRenameFailedTitle" xml:space="preserve">
     <value>変更失敗</value>
   </data>
-  <data name="fdDeleteFailedTitle" xml:space="preserve">
-    <value>削除失敗</value>
-  </data>
-  <data name="fdFileAlreadyExistsFeedback" xml:space="preserve">
-    <value>同じ名前のファイルはすでに存在しました</value>
-  </data>
-  <data name="fdSearchCaption" xml:space="preserve">
-    <value>検索を入力</value>
-  </data>
-  <data name="fdWrongFileTypeFeedback" xml:space="preserve">
-    <value>ファイルタイプが間違っでいます</value>
+  <data name="fdRenamePrompt" xml:space="preserve">
+    <value>名前:</value>
   </data>
-  <data name="fdAnyFiles" xml:space="preserve">
-    <value>任意ファイル</value>
+  <data name="fdRenameTitle" xml:space="preserve">
+    <value>名前を変更</value>
   </data>
-  <data name="btnCancel" xml:space="preserve">
-    <value>キャンセル (_C)</value>
+  <data name="btnYes" xml:space="preserve">
+    <value>はい (_Y)</value>
   </data>
-  <data name="btnOk" xml:space="preserve">
-    <value>OK (_O)</value>
+  <data name="fdExisting" xml:space="preserve">
+    <value>既存</value>
   </data>
   <data name="btnOpen" xml:space="preserve">
     <value>開く (_O)</value>
@@ -255,6 +249,12 @@
   <data name="btnSaveAs" xml:space="preserve">
     <value>名前を付けて保存 (_S)</value>
   </data>
+  <data name="btnOk" xml:space="preserve">
+    <value>OK (_O)</value>
+  </data>
+  <data name="btnCancel" xml:space="preserve">
+    <value>キャンセル (_C)</value>
+  </data>
   <data name="fdCtxDelete" xml:space="preserve">
     <value>削除 (_D)</value>
   </data>
@@ -276,4 +276,7 @@
   <data name="dpTitle" xml:space="preserve">
     <value>日付ピッカー</value>
   </data>
+  <data name="ctxColors" xml:space="preserve">
+    <value>絵の具 (_L)</value>
+  </data>
 </root>

+ 16 - 13
Terminal.Gui/Resources/Strings.pt-PT.resx

@@ -117,27 +117,27 @@
   <resheader name="writer">
     <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
   </resheader>
+  <data name="ctxSelectAll" xml:space="preserve">
+    <value>_Selecionar Tudo</value>
+  </data>
+  <data name="ctxDeleteAll" xml:space="preserve">
+    <value>_Apagar Tudo</value>
+  </data>
   <data name="ctxCopy" xml:space="preserve">
     <value>_Copiar</value>
   </data>
   <data name="ctxCut" xml:space="preserve">
     <value>Cor_tar</value>
   </data>
-  <data name="ctxDeleteAll" xml:space="preserve">
-    <value>_Apagar Tudo</value>
-  </data>
   <data name="ctxPaste" xml:space="preserve">
     <value>Co_lar</value>
   </data>
-  <data name="ctxRedo" xml:space="preserve">
-    <value>_Refazer</value>
-  </data>
-  <data name="ctxSelectAll" xml:space="preserve">
-    <value>_Selecionar Tudo</value>
-  </data>
   <data name="ctxUndo" xml:space="preserve">
     <value>_Desfazer</value>
   </data>
+  <data name="ctxRedo" xml:space="preserve">
+    <value>_Refazer</value>
+  </data>
   <data name="fdDirectory" xml:space="preserve">
     <value>Diretório</value>
   </data>
@@ -168,16 +168,19 @@
   <data name="wzNext" xml:space="preserve">
     <value>S_eguir...</value>
   </data>
-  <data name="btnSaveAs" xml:space="preserve">
-    <value>Guardar como</value>
+  <data name="btnOpen" xml:space="preserve">
+    <value>Abrir</value>
   </data>
   <data name="btnSave" xml:space="preserve">
     <value>Guardar</value>
   </data>
-  <data name="btnOpen" xml:space="preserve">
-    <value>Abrir</value>
+  <data name="btnSaveAs" xml:space="preserve">
+    <value>Guardar como</value>
   </data>
   <data name="dpTitle" xml:space="preserve">
     <value>Seletor de Data</value>
   </data>
+  <data name="ctxColors" xml:space="preserve">
+    <value>Co_res</value>
+  </data>
 </root>

+ 264 - 261
Terminal.Gui/Resources/Strings.resx

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <root>
-    <!-- 
+  <!-- 
     Microsoft ResX Schema 
     
     Version 2.0
@@ -59,663 +59,666 @@
             : using a System.ComponentModel.TypeConverter
             : and then encoded with base64 encoding.
     -->
-    <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
-        <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
-        <xsd:element name="root" msdata:IsDataSet="true">
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
             <xsd:complexType>
-                <xsd:choice maxOccurs="unbounded">
-                    <xsd:element name="metadata">
-                        <xsd:complexType>
-                            <xsd:sequence>
-                                <xsd:element name="value" type="xsd:string" minOccurs="0" />
-                            </xsd:sequence>
-                            <xsd:attribute name="name" use="required" type="xsd:string" />
-                            <xsd:attribute name="type" type="xsd:string" />
-                            <xsd:attribute name="mimetype" type="xsd:string" />
-                            <xsd:attribute ref="xml:space" />
-                        </xsd:complexType>
-                    </xsd:element>
-                    <xsd:element name="assembly">
-                        <xsd:complexType>
-                            <xsd:attribute name="alias" type="xsd:string" />
-                            <xsd:attribute name="name" type="xsd:string" />
-                        </xsd:complexType>
-                    </xsd:element>
-                    <xsd:element name="data">
-                        <xsd:complexType>
-                            <xsd:sequence>
-                                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
-                                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
-                            </xsd:sequence>
-                            <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
-                            <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
-                            <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
-                            <xsd:attribute ref="xml:space" />
-                        </xsd:complexType>
-                    </xsd:element>
-                    <xsd:element name="resheader">
-                        <xsd:complexType>
-                            <xsd:sequence>
-                                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
-                            </xsd:sequence>
-                            <xsd:attribute name="name" type="xsd:string" use="required" />
-                        </xsd:complexType>
-                    </xsd:element>
-                </xsd:choice>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
             </xsd:complexType>
-        </xsd:element>
-    </xsd:schema>
-    <resheader name="resmimetype">
-        <value>text/microsoft-resx</value>
-    </resheader>
-    <resheader name="version">
-        <value>2.0</value>
-    </resheader>
-    <resheader name="reader">
-        <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
-    </resheader>
-    <resheader name="writer">
-        <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
-    </resheader>
-    <data name="ctxSelectAll" xml:space="preserve">
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <data name="ctxSelectAll" xml:space="preserve">
     <value>_Select All</value>
   </data>
-    <data name="ctxDeleteAll" xml:space="preserve">
+  <data name="ctxDeleteAll" xml:space="preserve">
     <value>_Delete All</value>
   </data>
-    <data name="ctxCopy" xml:space="preserve">
+  <data name="ctxCopy" xml:space="preserve">
     <value>_Copy</value>
   </data>
-    <data name="ctxCut" xml:space="preserve">
+  <data name="ctxCut" xml:space="preserve">
     <value>Cu_t</value>
   </data>
-    <data name="ctxPaste" xml:space="preserve">
+  <data name="ctxPaste" xml:space="preserve">
     <value>_Paste</value>
   </data>
-    <data name="ctxUndo" xml:space="preserve">
+  <data name="ctxUndo" xml:space="preserve">
     <value>_Undo</value>
   </data>
-    <data name="ctxRedo" xml:space="preserve">
+  <data name="ctxRedo" xml:space="preserve">
     <value>_Redo</value>
   </data>
-    <data name="fdDirectory" xml:space="preserve">
+  <data name="fdDirectory" xml:space="preserve">
     <value>Directory</value>
   </data>
-    <data name="fdFile" xml:space="preserve">
+  <data name="fdFile" xml:space="preserve">
     <value>File</value>
   </data>
-    <data name="fdSave" xml:space="preserve">
+  <data name="fdSave" xml:space="preserve">
     <value>Save</value>
   </data>
-    <data name="fdSaveAs" xml:space="preserve">
+  <data name="fdSaveAs" xml:space="preserve">
     <value>Save as</value>
   </data>
-    <data name="fdOpen" xml:space="preserve">
+  <data name="fdOpen" xml:space="preserve">
     <value>Open</value>
   </data>
-    <data name="fdSelectFolder" xml:space="preserve">
+  <data name="fdSelectFolder" xml:space="preserve">
     <value>Select folder</value>
   </data>
-    <data name="fdSelectMixed" xml:space="preserve">
+  <data name="fdSelectMixed" xml:space="preserve">
     <value>Select Mixed</value>
   </data>
-    <data name="wzBack" xml:space="preserve">
+  <data name="wzBack" xml:space="preserve">
     <value>_Back</value>
   </data>
-    <data name="wzFinish" xml:space="preserve">
+  <data name="wzFinish" xml:space="preserve">
     <value>Fi_nish</value>
   </data>
-    <data name="wzNext" xml:space="preserve">
+  <data name="wzNext" xml:space="preserve">
     <value>_Next...</value>
   </data>
-    <data name="fdDirectoryAlreadyExistsFeedback" xml:space="preserve">
+  <data name="fdDirectoryAlreadyExistsFeedback" xml:space="preserve">
     <value>Directory already exists with that name</value>
     <comment>When trying to save a file with a name already taken by a directory</comment>
   </data>
-    <data name="fdDirectoryMustExistFeedback" xml:space="preserve">
+  <data name="fdDirectoryMustExistFeedback" xml:space="preserve">
     <value>Must select an existing directory</value>
   </data>
-    <data name="fdFileAlreadyExistsFeedback" xml:space="preserve">
+  <data name="fdFileAlreadyExistsFeedback" xml:space="preserve">
     <value>File already exists with that name</value>
   </data>
-    <data name="fdFileMustExistFeedback" xml:space="preserve">
+  <data name="fdFileMustExistFeedback" xml:space="preserve">
     <value>Must select an existing file</value>
     <comment>When trying to save a directory with a name already used by a file</comment>
   </data>
-    <data name="fdFilename" xml:space="preserve">
+  <data name="fdFilename" xml:space="preserve">
     <value>Filename</value>
   </data>
-    <data name="fdFileOrDirectoryMustExistFeedback" xml:space="preserve">
+  <data name="fdFileOrDirectoryMustExistFeedback" xml:space="preserve">
     <value>Must select an existing file or directory</value>
   </data>
-    <data name="fdModified" xml:space="preserve">
+  <data name="fdModified" xml:space="preserve">
     <value>Modified</value>
   </data>
-    <data name="fdPathCaption" xml:space="preserve">
+  <data name="fdPathCaption" xml:space="preserve">
     <value>Enter Path</value>
   </data>
-    <data name="fdSearchCaption" xml:space="preserve">
+  <data name="fdSearchCaption" xml:space="preserve">
     <value>Enter Search</value>
   </data>
-    <data name="fdSize" xml:space="preserve">
+  <data name="fdSize" xml:space="preserve">
     <value>Size</value>
   </data>
-    <data name="fdType" xml:space="preserve">
+  <data name="fdType" xml:space="preserve">
     <value>Type</value>
   </data>
-    <data name="fdWrongFileTypeFeedback" xml:space="preserve">
+  <data name="fdWrongFileTypeFeedback" xml:space="preserve">
     <value>Wrong file type</value>
     <comment>When trying to open/save a file that does not match the provided filter (e.g. csv)</comment>
   </data>
-    <data name="fdAnyFiles" xml:space="preserve">
+  <data name="fdAnyFiles" xml:space="preserve">
     <value>Any Files</value>
     <comment>Describes an AllowedType that matches anything</comment>
   </data>
-    <data name="fdDeleteBody" xml:space="preserve">
+  <data name="fdDeleteBody" xml:space="preserve">
     <value>Are you sure you want to delete '{0}'? This operation is permanent</value>
   </data>
-    <data name="fdDeleteFailedTitle" xml:space="preserve">
+  <data name="fdDeleteFailedTitle" xml:space="preserve">
     <value>Delete Failed</value>
   </data>
-    <data name="fdDeleteTitle" xml:space="preserve">
+  <data name="fdDeleteTitle" xml:space="preserve">
     <value>Delete {0}</value>
   </data>
-    <data name="fdNewFailed" xml:space="preserve">
+  <data name="fdNewFailed" xml:space="preserve">
     <value>New Failed</value>
   </data>
-    <data name="fdNewTitle" xml:space="preserve">
+  <data name="fdNewTitle" xml:space="preserve">
     <value>New Folder</value>
   </data>
-    <data name="btnNo" xml:space="preserve">
+  <data name="btnNo" xml:space="preserve">
     <value>_No</value>
   </data>
-    <data name="fdRenameFailedTitle" xml:space="preserve">
+  <data name="fdRenameFailedTitle" xml:space="preserve">
     <value>Rename Failed</value>
   </data>
-    <data name="fdRenamePrompt" xml:space="preserve">
+  <data name="fdRenamePrompt" xml:space="preserve">
     <value>Name:</value>
   </data>
-    <data name="fdRenameTitle" xml:space="preserve">
+  <data name="fdRenameTitle" xml:space="preserve">
     <value>Rename</value>
   </data>
-    <data name="btnYes" xml:space="preserve">
+  <data name="btnYes" xml:space="preserve">
     <value>_Yes</value>
   </data>
-    <data name="fdExisting" xml:space="preserve">
+  <data name="fdExisting" xml:space="preserve">
     <value>Existing</value>
   </data>
-    <data name="btnOpen" xml:space="preserve">
+  <data name="btnOpen" xml:space="preserve">
     <value>O_pen</value>
   </data>
-    <data name="btnSave" xml:space="preserve">
+  <data name="btnSave" xml:space="preserve">
     <value>_Save</value>
   </data>
-    <data name="btnSaveAs" xml:space="preserve">
+  <data name="btnSaveAs" xml:space="preserve">
     <value>Save _as</value>
   </data>
-    <data name="btnOk" xml:space="preserve">
+  <data name="btnOk" xml:space="preserve">
     <value>_OK</value>
   </data>
-    <data name="btnCancel" xml:space="preserve">
+  <data name="btnCancel" xml:space="preserve">
     <value>_Cancel</value>
   </data>
-    <data name="fdCtxDelete" xml:space="preserve">
+  <data name="fdCtxDelete" xml:space="preserve">
     <value>_Delete</value>
   </data>
-    <data name="fdCtxHide" xml:space="preserve">
+  <data name="fdCtxHide" xml:space="preserve">
     <value>_Hide {0}</value>
   </data>
-    <data name="fdCtxNew" xml:space="preserve">
+  <data name="fdCtxNew" xml:space="preserve">
     <value>_New</value>
   </data>
-    <data name="fdCtxRename" xml:space="preserve">
+  <data name="fdCtxRename" xml:space="preserve">
     <value>_Rename</value>
   </data>
-    <data name="fdCtxSortAsc" xml:space="preserve">
+  <data name="fdCtxSortAsc" xml:space="preserve">
     <value>_Sort {0} ASC</value>
   </data>
-    <data name="fdCtxSortDesc" xml:space="preserve">
+  <data name="fdCtxSortDesc" xml:space="preserve">
     <value>_Sort {0} DESC</value>
   </data>
-    <data name="dpTitle" xml:space="preserve">
+  <data name="dpTitle" xml:space="preserve">
     <value>Date Picker</value>
   </data>
-    <data name="#F0F8FF" xml:space="preserve">
+  <data name="#F0F8FF" xml:space="preserve">
     <value>AliceBlue</value>
   </data>
-    <data name="#FAEBD7" xml:space="preserve">
+  <data name="#FAEBD7" xml:space="preserve">
     <value>AntiqueWhite</value>
   </data>
-    <data name="#7FFFD4" xml:space="preserve">
+  <data name="#7FFFD4" xml:space="preserve">
     <value>Aquamarine</value>
   </data>
-    <data name="#F0FFFF" xml:space="preserve">
+  <data name="#F0FFFF" xml:space="preserve">
     <value>Azure</value>
   </data>
-    <data name="#F5F5DC" xml:space="preserve">
+  <data name="#F5F5DC" xml:space="preserve">
     <value>Beige</value>
   </data>
-    <data name="#FFE4C4" xml:space="preserve">
+  <data name="#FFE4C4" xml:space="preserve">
     <value>Bisque</value>
   </data>
-    <data name="#000000" xml:space="preserve">
+  <data name="#000000" xml:space="preserve">
     <value>Black</value>
   </data>
-    <data name="#FFEBCD" xml:space="preserve">
+  <data name="#FFEBCD" xml:space="preserve">
     <value>BlanchedAlmond</value>
   </data>
-    <data name="#0000FF" xml:space="preserve">
+  <data name="#0000FF" xml:space="preserve">
     <value>Blue</value>
   </data>
-    <data name="#8A2BE2" xml:space="preserve">
+  <data name="#8A2BE2" xml:space="preserve">
     <value>BlueViolet</value>
   </data>
-    <data name="#A52A2A" xml:space="preserve">
+  <data name="#A52A2A" xml:space="preserve">
     <value>Brown</value>
   </data>
-    <data name="#DEB887" xml:space="preserve">
+  <data name="#DEB887" xml:space="preserve">
     <value>BurlyWood</value>
   </data>
-    <data name="#5F9EA0" xml:space="preserve">
+  <data name="#5F9EA0" xml:space="preserve">
     <value>CadetBlue</value>
   </data>
-    <data name="#7FFF00" xml:space="preserve">
+  <data name="#7FFF00" xml:space="preserve">
     <value>Chartreuse</value>
   </data>
-    <data name="#D2691E" xml:space="preserve">
+  <data name="#D2691E" xml:space="preserve">
     <value>Chocolate</value>
   </data>
-    <data name="#FF7F50" xml:space="preserve">
+  <data name="#FF7F50" xml:space="preserve">
     <value>Coral</value>
   </data>
-    <data name="#6495ED" xml:space="preserve">
+  <data name="#6495ED" xml:space="preserve">
     <value>CornflowerBlue</value>
   </data>
-    <data name="#FFF8DC" xml:space="preserve">
+  <data name="#FFF8DC" xml:space="preserve">
     <value>Cornsilk</value>
   </data>
-    <data name="#DC143C" xml:space="preserve">
+  <data name="#DC143C" xml:space="preserve">
     <value>Crimson</value>
   </data>
-    <data name="#00FFFF" xml:space="preserve">
+  <data name="#00FFFF" xml:space="preserve">
     <value>Cyan</value>
   </data>
-    <data name="#00008B" xml:space="preserve">
+  <data name="#00008B" xml:space="preserve">
     <value>DarkBlue</value>
   </data>
-    <data name="#008B8B" xml:space="preserve">
+  <data name="#008B8B" xml:space="preserve">
     <value>DarkCyan</value>
   </data>
-    <data name="#B8860B" xml:space="preserve">
+  <data name="#B8860B" xml:space="preserve">
     <value>DarkGoldenRod</value>
   </data>
-    <data name="#A9A9A9" xml:space="preserve">
+  <data name="#A9A9A9" xml:space="preserve">
     <value>DarkGrey</value>
   </data>
-    <data name="#006400" xml:space="preserve">
+  <data name="#006400" xml:space="preserve">
     <value>DarkGreen</value>
   </data>
-    <data name="#BDB76B" xml:space="preserve">
+  <data name="#BDB76B" xml:space="preserve">
     <value>DarkKhaki</value>
   </data>
-    <data name="#8B008B" xml:space="preserve">
+  <data name="#8B008B" xml:space="preserve">
     <value>DarkMagenta</value>
   </data>
-    <data name="#556B2F" xml:space="preserve">
+  <data name="#556B2F" xml:space="preserve">
     <value>DarkOliveGreen</value>
   </data>
-    <data name="#FF8C00" xml:space="preserve">
+  <data name="#FF8C00" xml:space="preserve">
     <value>DarkOrange</value>
   </data>
-    <data name="#9932CC" xml:space="preserve">
+  <data name="#9932CC" xml:space="preserve">
     <value>DarkOrchid</value>
   </data>
-    <data name="#8B0000" xml:space="preserve">
+  <data name="#8B0000" xml:space="preserve">
     <value>DarkRed</value>
   </data>
-    <data name="#E9967A" xml:space="preserve">
+  <data name="#E9967A" xml:space="preserve">
     <value>DarkSalmon</value>
   </data>
-    <data name="#8FBC8F" xml:space="preserve">
+  <data name="#8FBC8F" xml:space="preserve">
     <value>DarkSeaGreen</value>
   </data>
-    <data name="#483D8B" xml:space="preserve">
+  <data name="#483D8B" xml:space="preserve">
     <value>DarkSlateBlue</value>
   </data>
-    <data name="#2F4F4F" xml:space="preserve">
+  <data name="#2F4F4F" xml:space="preserve">
     <value>DarkSlateGrey</value>
   </data>
-    <data name="#00CED1" xml:space="preserve">
+  <data name="#00CED1" xml:space="preserve">
     <value>DarkTurquoise</value>
   </data>
-    <data name="#9400D3" xml:space="preserve">
+  <data name="#9400D3" xml:space="preserve">
     <value>DarkViolet</value>
   </data>
-    <data name="#FF1493" xml:space="preserve">
+  <data name="#FF1493" xml:space="preserve">
     <value>DeepPink</value>
   </data>
-    <data name="#00BFFF" xml:space="preserve">
+  <data name="#00BFFF" xml:space="preserve">
     <value>DeepSkyBlue</value>
   </data>
-    <data name="#696969" xml:space="preserve">
+  <data name="#696969" xml:space="preserve">
     <value>DimGray</value>
   </data>
-    <data name="#1E90FF" xml:space="preserve">
+  <data name="#1E90FF" xml:space="preserve">
     <value>DodgerBlue</value>
   </data>
-    <data name="#B22222" xml:space="preserve">
+  <data name="#B22222" xml:space="preserve">
     <value>FireBrick</value>
   </data>
-    <data name="#FFFAF0" xml:space="preserve">
+  <data name="#FFFAF0" xml:space="preserve">
     <value>FloralWhite</value>
   </data>
-    <data name="#228B22" xml:space="preserve">
+  <data name="#228B22" xml:space="preserve">
     <value>ForestGreen</value>
   </data>
-    <data name="#DCDCDC" xml:space="preserve">
+  <data name="#DCDCDC" xml:space="preserve">
     <value>Gainsboro</value>
   </data>
-    <data name="#F8F8FF" xml:space="preserve">
+  <data name="#F8F8FF" xml:space="preserve">
     <value>GhostWhite</value>
   </data>
-    <data name="#FFD700" xml:space="preserve">
+  <data name="#FFD700" xml:space="preserve">
     <value>Gold</value>
   </data>
-    <data name="#DAA520" xml:space="preserve">
+  <data name="#DAA520" xml:space="preserve">
     <value>GoldenRod</value>
   </data>
-    <data name="#808080" xml:space="preserve">
+  <data name="#808080" xml:space="preserve">
     <value>Gray</value>
   </data>
-    <data name="#008000" xml:space="preserve">
+  <data name="#008000" xml:space="preserve">
     <value>Green</value>
   </data>
-    <data name="#ADFF2F" xml:space="preserve">
+  <data name="#ADFF2F" xml:space="preserve">
     <value>GreenYellow</value>
   </data>
-    <data name="#F0FFF0" xml:space="preserve">
+  <data name="#F0FFF0" xml:space="preserve">
     <value>HoneyDew</value>
   </data>
-    <data name="#FF69B4" xml:space="preserve">
+  <data name="#FF69B4" xml:space="preserve">
     <value>HotPink</value>
   </data>
-    <data name="#CD5C5C" xml:space="preserve">
+  <data name="#CD5C5C" xml:space="preserve">
     <value>IndianRed</value>
   </data>
-    <data name="#4B0082" xml:space="preserve">
+  <data name="#4B0082" xml:space="preserve">
     <value>Indigo</value>
   </data>
-    <data name="#FFFFF0" xml:space="preserve">
+  <data name="#FFFFF0" xml:space="preserve">
     <value>Ivory</value>
   </data>
-    <data name="#F0E68C" xml:space="preserve">
+  <data name="#F0E68C" xml:space="preserve">
     <value>Khaki</value>
   </data>
-    <data name="#E6E6FA" xml:space="preserve">
+  <data name="#E6E6FA" xml:space="preserve">
     <value>Lavender</value>
   </data>
-    <data name="#FFF0F5" xml:space="preserve">
+  <data name="#FFF0F5" xml:space="preserve">
     <value>LavenderBlush</value>
   </data>
-    <data name="#7CFC00" xml:space="preserve">
+  <data name="#7CFC00" xml:space="preserve">
     <value>LawnGreen</value>
   </data>
-    <data name="#FFFACD" xml:space="preserve">
+  <data name="#FFFACD" xml:space="preserve">
     <value>LemonChiffon</value>
   </data>
-    <data name="#ADD8E6" xml:space="preserve">
+  <data name="#ADD8E6" xml:space="preserve">
     <value>LightBlue</value>
   </data>
-    <data name="#F08080" xml:space="preserve">
+  <data name="#F08080" xml:space="preserve">
     <value>LightCoral</value>
   </data>
-    <data name="#E0FFFF" xml:space="preserve">
+  <data name="#E0FFFF" xml:space="preserve">
     <value>LightCyan</value>
   </data>
-    <data name="#FAFAD2" xml:space="preserve">
+  <data name="#FAFAD2" xml:space="preserve">
     <value>LightGoldenRodYellow</value>
   </data>
-    <data name="#D3D3D3" xml:space="preserve">
+  <data name="#D3D3D3" xml:space="preserve">
     <value>LightGray</value>
   </data>
-    <data name="#90EE90" xml:space="preserve">
+  <data name="#90EE90" xml:space="preserve">
     <value>LightGreen</value>
   </data>
-    <data name="#FFB6C1" xml:space="preserve">
+  <data name="#FFB6C1" xml:space="preserve">
     <value>LightPink</value>
   </data>
-    <data name="#FFA07A" xml:space="preserve">
+  <data name="#FFA07A" xml:space="preserve">
     <value>LightSalmon</value>
   </data>
-    <data name="#20B2AA" xml:space="preserve">
+  <data name="#20B2AA" xml:space="preserve">
     <value>LightSeaGreen</value>
   </data>
-    <data name="#87CEFA" xml:space="preserve">
+  <data name="#87CEFA" xml:space="preserve">
     <value>LightSkyBlue</value>
   </data>
-    <data name="#778899" xml:space="preserve">
+  <data name="#778899" xml:space="preserve">
     <value>LightSlateGrey</value>
   </data>
-    <data name="#B0C4DE" xml:space="preserve">
+  <data name="#B0C4DE" xml:space="preserve">
     <value>LightSteelBlue</value>
   </data>
-    <data name="#FFFFE0" xml:space="preserve">
+  <data name="#FFFFE0" xml:space="preserve">
     <value>LightYellow</value>
   </data>
-    <data name="#00FF00" xml:space="preserve">
+  <data name="#00FF00" xml:space="preserve">
     <value>Lime</value>
   </data>
-    <data name="#32CD32" xml:space="preserve">
+  <data name="#32CD32" xml:space="preserve">
     <value>LimeGreen</value>
   </data>
-    <data name="#FAF0E6" xml:space="preserve">
+  <data name="#FAF0E6" xml:space="preserve">
     <value>Linen</value>
   </data>
-    <data name="#FF00FF" xml:space="preserve">
+  <data name="#FF00FF" xml:space="preserve">
     <value>Magenta</value>
   </data>
-    <data name="#800000" xml:space="preserve">
+  <data name="#800000" xml:space="preserve">
     <value>Maroon</value>
   </data>
-    <data name="#66CDAA" xml:space="preserve">
+  <data name="#66CDAA" xml:space="preserve">
     <value>MediumAquaMarine</value>
   </data>
-    <data name="#0000CD" xml:space="preserve">
+  <data name="#0000CD" xml:space="preserve">
     <value>MediumBlue</value>
   </data>
-    <data name="#BA55D3" xml:space="preserve">
+  <data name="#BA55D3" xml:space="preserve">
     <value>MediumOrchid</value>
   </data>
-    <data name="#9370DB" xml:space="preserve">
+  <data name="#9370DB" xml:space="preserve">
     <value>MediumPurple</value>
   </data>
-    <data name="#3CB371" xml:space="preserve">
+  <data name="#3CB371" xml:space="preserve">
     <value>MediumSeaGreen</value>
   </data>
-    <data name="#7B68EE" xml:space="preserve">
+  <data name="#7B68EE" xml:space="preserve">
     <value>MediumSlateBlue</value>
   </data>
-    <data name="#00FA9A" xml:space="preserve">
+  <data name="#00FA9A" xml:space="preserve">
     <value>MediumSpringGreen</value>
   </data>
-    <data name="#48D1CC" xml:space="preserve">
+  <data name="#48D1CC" xml:space="preserve">
     <value>MediumTurquoise</value>
   </data>
-    <data name="#C71585" xml:space="preserve">
+  <data name="#C71585" xml:space="preserve">
     <value>MediumVioletRed</value>
   </data>
-    <data name="#191970" xml:space="preserve">
+  <data name="#191970" xml:space="preserve">
     <value>MidnightBlue</value>
   </data>
-    <data name="#F5FFFA" xml:space="preserve">
+  <data name="#F5FFFA" xml:space="preserve">
     <value>MintCream</value>
   </data>
-    <data name="#FFE4E1" xml:space="preserve">
+  <data name="#FFE4E1" xml:space="preserve">
     <value>MistyRose</value>
   </data>
-    <data name="#FFE4B5" xml:space="preserve">
+  <data name="#FFE4B5" xml:space="preserve">
     <value>Moccasin</value>
   </data>
-    <data name="#FFDEAD" xml:space="preserve">
+  <data name="#FFDEAD" xml:space="preserve">
     <value>NavajoWhite</value>
   </data>
-    <data name="#000080" xml:space="preserve">
+  <data name="#000080" xml:space="preserve">
     <value>Navy</value>
   </data>
-    <data name="#FDF5E6" xml:space="preserve">
+  <data name="#FDF5E6" xml:space="preserve">
     <value>OldLace</value>
   </data>
-    <data name="#808000" xml:space="preserve">
+  <data name="#808000" xml:space="preserve">
     <value>Olive</value>
   </data>
-    <data name="#6B8E23" xml:space="preserve">
+  <data name="#6B8E23" xml:space="preserve">
     <value>OliveDrab</value>
   </data>
-    <data name="#FFA500" xml:space="preserve">
+  <data name="#FFA500" xml:space="preserve">
     <value>Orange</value>
   </data>
-    <data name="#FF4500" xml:space="preserve">
+  <data name="#FF4500" xml:space="preserve">
     <value>OrangeRed</value>
   </data>
-    <data name="#DA70D6" xml:space="preserve">
+  <data name="#DA70D6" xml:space="preserve">
     <value>Orchid</value>
   </data>
-    <data name="#EEE8AA" xml:space="preserve">
+  <data name="#EEE8AA" xml:space="preserve">
     <value>PaleGoldenRod</value>
   </data>
-    <data name="#98FB98" xml:space="preserve">
+  <data name="#98FB98" xml:space="preserve">
     <value>PaleGreen</value>
   </data>
-    <data name="#AFEEEE" xml:space="preserve">
+  <data name="#AFEEEE" xml:space="preserve">
     <value>PaleTurquoise</value>
   </data>
-    <data name="#DB7093" xml:space="preserve">
+  <data name="#DB7093" xml:space="preserve">
     <value>PaleVioletRed</value>
   </data>
-    <data name="#FFEFD5" xml:space="preserve">
+  <data name="#FFEFD5" xml:space="preserve">
     <value>PapayaWhip</value>
   </data>
-    <data name="#FFDAB9" xml:space="preserve">
+  <data name="#FFDAB9" xml:space="preserve">
     <value>PeachPuff</value>
   </data>
-    <data name="#CD853F" xml:space="preserve">
+  <data name="#CD853F" xml:space="preserve">
     <value>Peru</value>
   </data>
-    <data name="#FFC0CB" xml:space="preserve">
+  <data name="#FFC0CB" xml:space="preserve">
     <value>Pink</value>
   </data>
-    <data name="#DDA0DD" xml:space="preserve">
+  <data name="#DDA0DD" xml:space="preserve">
     <value>Plum</value>
   </data>
-    <data name="#B0E0E6" xml:space="preserve">
+  <data name="#B0E0E6" xml:space="preserve">
     <value>PowderBlue</value>
   </data>
-    <data name="#800080" xml:space="preserve">
+  <data name="#800080" xml:space="preserve">
     <value>Purple</value>
   </data>
-    <data name="#663399" xml:space="preserve">
+  <data name="#663399" xml:space="preserve">
     <value>RebeccaPurple</value>
   </data>
-    <data name="#FF0000" xml:space="preserve">
+  <data name="#FF0000" xml:space="preserve">
     <value>Red</value>
   </data>
-    <data name="#BC8F8F" xml:space="preserve">
+  <data name="#BC8F8F" xml:space="preserve">
     <value>RosyBrown</value>
   </data>
-    <data name="#4169E1" xml:space="preserve">
+  <data name="#4169E1" xml:space="preserve">
     <value>RoyalBlue</value>
   </data>
-    <data name="#8B4513" xml:space="preserve">
+  <data name="#8B4513" xml:space="preserve">
     <value>SaddleBrown</value>
   </data>
-    <data name="#FA8072" xml:space="preserve">
+  <data name="#FA8072" xml:space="preserve">
     <value>Salmon</value>
   </data>
-    <data name="#F4A460" xml:space="preserve">
+  <data name="#F4A460" xml:space="preserve">
     <value>SandyBrown</value>
   </data>
-    <data name="#2E8B57" xml:space="preserve">
+  <data name="#2E8B57" xml:space="preserve">
     <value>SeaGreen</value>
   </data>
-    <data name="#FFF5EE" xml:space="preserve">
+  <data name="#FFF5EE" xml:space="preserve">
     <value>SeaShell</value>
   </data>
-    <data name="#A0522D" xml:space="preserve">
+  <data name="#A0522D" xml:space="preserve">
     <value>Sienna</value>
   </data>
-    <data name="#C0C0C0" xml:space="preserve">
+  <data name="#C0C0C0" xml:space="preserve">
     <value>Silver</value>
   </data>
-    <data name="#87CEEB" xml:space="preserve">
+  <data name="#87CEEB" xml:space="preserve">
     <value>SkyBlue</value>
   </data>
-    <data name="#6A5ACD" xml:space="preserve">
+  <data name="#6A5ACD" xml:space="preserve">
     <value>SlateBlue</value>
   </data>
-    <data name="#708090" xml:space="preserve">
+  <data name="#708090" xml:space="preserve">
     <value>SlateGray</value>
   </data>
-    <data name="#FFFAFA" xml:space="preserve">
+  <data name="#FFFAFA" xml:space="preserve">
     <value>Snow</value>
   </data>
-    <data name="#00FF7F" xml:space="preserve">
+  <data name="#00FF7F" xml:space="preserve">
     <value>SpringGreen</value>
   </data>
-    <data name="#4682B4" xml:space="preserve">
+  <data name="#4682B4" xml:space="preserve">
     <value>SteelBlue</value>
   </data>
-    <data name="#D2B48C" xml:space="preserve">
+  <data name="#D2B48C" xml:space="preserve">
     <value>Tan</value>
   </data>
-    <data name="#008080" xml:space="preserve">
+  <data name="#008080" xml:space="preserve">
     <value>Teal</value>
   </data>
-    <data name="#D8BFD8" xml:space="preserve">
+  <data name="#D8BFD8" xml:space="preserve">
     <value>Thistle</value>
   </data>
-    <data name="#FF6347" xml:space="preserve">
+  <data name="#FF6347" xml:space="preserve">
     <value>Tomato</value>
   </data>
-    <data name="#40E0D0" xml:space="preserve">
+  <data name="#40E0D0" xml:space="preserve">
     <value>Turquoise</value>
   </data>
-    <data name="#EE82EE" xml:space="preserve">
+  <data name="#EE82EE" xml:space="preserve">
     <value>Violet</value>
   </data>
-    <data name="#F5DEB3" xml:space="preserve">
+  <data name="#F5DEB3" xml:space="preserve">
     <value>Wheat</value>
   </data>
-    <data name="#FFFFFF" xml:space="preserve">
+  <data name="#FFFFFF" xml:space="preserve">
     <value>White</value>
   </data>
-    <data name="#F5F5F5" xml:space="preserve">
+  <data name="#F5F5F5" xml:space="preserve">
     <value>WhiteSmoke</value>
   </data>
-    <data name="#FFFF00" xml:space="preserve">
+  <data name="#FFFF00" xml:space="preserve">
     <value>Yellow</value>
   </data>
-    <data name="#9ACD32" xml:space="preserve">
+  <data name="#9ACD32" xml:space="preserve">
     <value>YellowGreen</value>
   </data>
-    <data name="#3B78FF" xml:space="preserve">
+  <data name="#3B78FF" xml:space="preserve">
     <value>BrightBlue</value>
   </data>
-    <data name="#61D6D6" xml:space="preserve">
+  <data name="#61D6D6" xml:space="preserve">
     <value>BrightCyan</value>
   </data>
-    <data name="#E74856" xml:space="preserve">
+  <data name="#E74856" xml:space="preserve">
     <value>BrightRed</value>
-</data>
-    <data name="#16C60C" xml:space="preserve">
+  </data>
+  <data name="#16C60C" xml:space="preserve">
     <value>BrightGreen</value>
-</data>
-    <data name="#B4009E" xml:space="preserve">
+  </data>
+  <data name="#B4009E" xml:space="preserve">
     <value>BrightMagenta</value>
-</data>
-    <data name="#F9F1A5" xml:space="preserve">
+  </data>
+  <data name="#F9F1A5" xml:space="preserve">
     <value>BrightYellow</value>
-</data>
-    <data name="#767676" xml:space="preserve">
+  </data>
+  <data name="#767676" xml:space="preserve">
     <value>DarkGray</value>
-</data>
+  </data>
+  <data name="ctxColors" xml:space="preserve">
+    <value>Co_lors</value>
+  </data>
 </root>

+ 9 - 6
Terminal.Gui/Resources/Strings.zh-Hans.resx

@@ -153,9 +153,6 @@
   <data name="fdOpen" xml:space="preserve">
     <value>打开</value>
   </data>
-  <data name="wzNext" xml:space="preserve">
-    <value>下一步 (_N)...</value>
-  </data>
   <data name="fdSelectFolder" xml:space="preserve">
     <value>选择文件夹 (_S)</value>
   </data>
@@ -168,6 +165,9 @@
   <data name="wzFinish" xml:space="preserve">
     <value>结束 (_N)</value>
   </data>
+  <data name="wzNext" xml:space="preserve">
+    <value>下一步 (_N)...</value>
+  </data>
   <data name="fdDirectoryAlreadyExistsFeedback" xml:space="preserve">
     <value>已存在相同名称的目录</value>
   </data>
@@ -240,9 +240,6 @@
   <data name="fdExisting" xml:space="preserve">
     <value>已有</value>
   </data>
-  <data name="btnOk" xml:space="preserve">
-    <value>确定 (_O)</value>
-  </data>
   <data name="btnOpen" xml:space="preserve">
     <value>打开 (_O)</value>
   </data>
@@ -252,6 +249,9 @@
   <data name="btnSaveAs" xml:space="preserve">
     <value>另存为 (_S)</value>
   </data>
+  <data name="btnOk" xml:space="preserve">
+    <value>确定 (_O)</value>
+  </data>
   <data name="btnCancel" xml:space="preserve">
     <value>取消 (_C)</value>
   </data>
@@ -276,4 +276,7 @@
   <data name="dpTitle" xml:space="preserve">
     <value>日期选择器</value>
   </data>
+  <data name="ctxColors" xml:space="preserve">
+    <value>旗帜 (_L)</value>
+  </data>
 </root>

+ 2 - 2
Terminal.Gui/Views/TextField.cs

@@ -464,7 +464,7 @@ public class TextField : View
     ///     Indicates whatever the text was changed or not. <see langword="true"/> if the text was changed
     ///     <see langword="false"/> otherwise.
     /// </summary>
-    public bool IsDirty => _historyText.IsDirty (Text);
+    public bool IsDirty => _historyText.IsDirty ([Cell.StringToCells (Text)]);
 
     /// <summary>If set to true its not allow any changes in the text.</summary>
     public bool ReadOnly { get; set; }
@@ -594,7 +594,7 @@ public class TextField : View
     }
 
     /// <summary>Allows clearing the <see cref="HistoryText.HistoryTextItemEventArgs"/> items updating the original text.</summary>
-    public void ClearHistoryChanges () { _historyText.Clear (Text); }
+    public void ClearHistoryChanges () { _historyText.Clear ([Cell.StringToCells (Text)]); }
 
     /// <summary>Copy the selected text to the clipboard.</summary>
     public virtual void Copy ()

+ 192 - 36
Terminal.Gui/Views/TextView.cs

@@ -180,7 +180,7 @@ internal class TextModel
     {
         if (_lines.Count > 0 && pos < _lines.Count)
         {
-            _lines [pos] = new (runes);
+            _lines [pos] = [..runes];
         }
         else if (_lines.Count == 0 || (_lines.Count > 0 && pos >= _lines.Count))
         {
@@ -1125,12 +1125,13 @@ internal partial class HistoryText
         Original,
         Replaced,
         Removed,
-        Added
+        Added,
+        Attribute
     }
 
-    private readonly List<HistoryTextItemEventArgs> _historyTextItems = new ();
+    private readonly List<HistoryTextItemEventArgs> _historyTextItems = [];
     private int _idxHistoryText = -1;
-    private string? _originalText;
+    private List<List<Cell>> _originalCellsList = [];
     public bool HasHistoryChanges => _idxHistoryText > -1;
     public bool IsFromHistory { get; private set; }
 
@@ -1165,15 +1166,51 @@ internal partial class HistoryText
 
     public event EventHandler<HistoryTextItemEventArgs>? ChangeText;
 
-    public void Clear (string text)
+    public void Clear (List<List<Cell>> cellsList)
     {
         _historyTextItems.Clear ();
         _idxHistoryText = -1;
-        _originalText = text;
+        _originalCellsList.Clear ();
+
+        foreach (List<Cell> cells in cellsList)
+        {
+            _originalCellsList.Add ([..cells]);
+        }
+
         OnChangeText (null);
     }
 
-    public bool IsDirty (string text) { return _originalText != text; }
+    public bool IsDirty (List<List<Cell>> cellsList)
+    {
+        if (cellsList.Count != _originalCellsList.Count)
+        {
+            return true;
+        }
+
+        for (var r = 0; r < cellsList.Count; r++)
+        {
+            List<Cell> cells = cellsList [r];
+            List<Cell> originalCells = _originalCellsList [r];
+
+            if (cells.Count != originalCells.Count)
+            {
+                return true;
+            }
+
+            for (var c = 0; c < cells.Count; c++)
+            {
+                Cell cell = cells [c];
+                Cell originalCell = originalCells [c];
+
+                if (!cell.Equals (originalCell))
+                {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
 
     public void Redo ()
     {
@@ -1227,7 +1264,8 @@ internal partial class HistoryText
             if (_idxHistoryText - 1 > -1
                 && (_historyTextItems [_idxHistoryText - 1].LineStatus == LineStatus.Added
                     || _historyTextItems [_idxHistoryText - 1].LineStatus == LineStatus.Removed
-                    || (historyTextItem.LineStatus == LineStatus.Replaced && _historyTextItems [_idxHistoryText - 1].LineStatus == LineStatus.Original)))
+                    || (historyTextItem.LineStatus == LineStatus.Replaced && _historyTextItems [_idxHistoryText - 1].LineStatus == LineStatus.Original)
+                    || (historyTextItem.LineStatus == LineStatus.Attribute && _historyTextItems [_idxHistoryText - 1].LineStatus == LineStatus.Original)))
             {
                 _idxHistoryText--;
 
@@ -1858,7 +1896,7 @@ public class TextView : View
         CursorVisibility = CursorVisibility.Default;
         Used = true;
 
-        // By default, disable hotkeys (in case someome sets Title)
+        // By default, disable hotkeys (in case someone sets Title)
         HotKeySpecifier = new ('\xffff');
 
         _model.LinesLoaded += Model_LinesLoaded!;
@@ -2264,6 +2302,15 @@ public class TextView : View
                     }
                    );
 
+        AddCommand (
+                    Command.Open,
+                    () =>
+                    {
+                        PromptForColors ();
+
+                        return true;
+                    });
+
         // Default keybindings for this view
         KeyBindings.Add (Key.PageDown, Command.PageDown);
         KeyBindings.Add (Key.V.WithCtrl, Command.PageDown);
@@ -2357,6 +2404,8 @@ public class TextView : View
         KeyBindings.Add (Key.G.WithCtrl, Command.DeleteAll);
         KeyBindings.Add (Key.D.WithCtrl.WithShift, Command.DeleteAll);
 
+        KeyBindings.Add (Key.L.WithCtrl, Command.Open);
+
         _currentCulture = Thread.CurrentThread.CurrentUICulture;
 
         ContextMenu = new ();
@@ -2495,8 +2544,8 @@ public class TextView : View
     /// </summary>
     public bool IsDirty
     {
-        get => _historyText.IsDirty (Text);
-        set => _historyText.Clear (Text);
+        get => _historyText.IsDirty (_model.GetAllLines ());
+        set => _historyText.Clear (_model.GetAllLines ());
     }
 
     /// <summary>Gets or sets the left column.</summary>
@@ -2585,15 +2634,21 @@ public class TextView : View
     /// <summary>Length of the selected text.</summary>
     public int SelectedLength => GetSelectedLength ();
 
-    private List<List<Cell>> _selectedCellsList = [];
-
     /// <summary>
     ///     Gets the selected text as
     ///     <see>
     ///         <cref>List{List{Cell}}</cref>
     ///     </see>
     /// </summary>
-    public List<List<Cell>> SelectedCellsList => _selectedCellsList;
+    public List<List<Cell>> SelectedCellsList
+    {
+        get
+        {
+            GetRegion (out List<List<Cell>> selectedCellsList);
+
+            return selectedCellsList;
+        }
+    }
 
     /// <summary>The selected text.</summary>
     public string SelectedText
@@ -2689,7 +2744,7 @@ public class TextView : View
             OnTextChanged ();
             SetNeedsDisplay ();
 
-            _historyText.Clear (Text);
+            _historyText.Clear (_model.GetAllLines ());
         }
     }
 
@@ -2741,7 +2796,7 @@ public class TextView : View
 
 
     /// <summary>Allows clearing the <see cref="HistoryText.HistoryTextItemEventArgs"/> items updating the original text.</summary>
-    public void ClearHistoryChanges () { _historyText?.Clear (Text); }
+    public void ClearHistoryChanges () { _historyText?.Clear (_model.GetAllLines ()); }
 
     /// <summary>Closes the contents of the stream into the <see cref="TextView"/>.</summary>
     /// <returns><c>true</c>, if stream was closed, <c>false</c> otherwise.</returns>
@@ -2763,7 +2818,100 @@ public class TextView : View
     /// </remarks>
     public event EventHandler<ContentsChangedEventArgs>? ContentsChanged;
 
-    private string _copiedText;
+    internal void ApplyCellsAttribute (Attribute attribute)
+    {
+        if (!ReadOnly && SelectedLength > 0)
+        {
+            int startRow = Math.Min (SelectionStartRow, CurrentRow);
+            int endRow = Math.Max (CurrentRow, SelectionStartRow);
+            int startCol = SelectionStartRow <= CurrentRow ? SelectionStartColumn : CurrentColumn;
+            int endCol = CurrentRow >= SelectionStartRow ? CurrentColumn : SelectionStartColumn;
+            List<List<Cell>> selectedCellsOriginal = [];
+            List<List<Cell>> selectedCellsChanged = [];
+
+            for (int r = startRow; r <= endRow; r++)
+            {
+                List<Cell> line = GetLine (r);
+
+                selectedCellsOriginal.Add ([.. line]);
+
+                for (int c = r == startRow ? startCol : 0;
+                     c < (r == endRow ? endCol : line.Count);
+                     c++)
+                {
+                    Cell cell = line [c]; // Copy value to a new variable
+                    cell.Attribute = attribute; // Modify the copy
+                    line [c] = cell; // Assign the modified copy back
+                }
+
+                selectedCellsChanged.Add ([..GetLine (r)]);
+            }
+
+            GetSelectedRegion ();
+            Selecting = false;
+
+            _historyText.Add (
+                              [.. selectedCellsOriginal],
+                              new (startCol, startRow)
+                             );
+
+            _historyText.Add (
+                              [.. selectedCellsChanged],
+                              new (startCol, startRow),
+                              HistoryText.LineStatus.Attribute
+                             );
+        }
+    }
+
+    private Attribute? GetSelectedCellAttribute ()
+    {
+        List<Cell> line;
+
+        if (SelectedLength > 0)
+        {
+            line = GetLine (SelectionStartRow);
+
+            if (line [Math.Min (SelectionStartColumn, line.Count - 1)].Attribute is { } attributeSel)
+            {
+                return new (attributeSel);
+            }
+
+            return new (ColorScheme!.Focus);
+        }
+
+        line = GetCurrentLine ();
+
+        if (line [Math.Min (CurrentColumn, line.Count - 1)].Attribute is { } attribute)
+        {
+            return new (attribute);
+        }
+
+        return new (ColorScheme!.Focus);
+    }
+
+    /// <summary>
+    ///     Open a dialog to set the foreground and background colors.
+    /// </summary>
+    public void PromptForColors ()
+    {
+        if (!Colors.PromptForColors (
+                                     "Colors",
+                                     GetSelectedCellAttribute (),
+                                     out Attribute newAttribute
+                                    ))
+        {
+            return;
+        }
+
+        var attribute = new Attribute (
+                                       newAttribute.Foreground,
+                                       newAttribute.Background
+                                      );
+
+        ApplyCellsAttribute (attribute);
+    }
+
+    private string? _copiedText;
     private List<List<Cell>> _copiedCellsList = [];
 
     /// <summary>Copy the selected text to the clipboard contents.</summary>
@@ -3070,7 +3218,7 @@ public class TextView : View
         {
             SetWrapModel ();
             res = _model.LoadFile (path);
-            _historyText.Clear (Text);
+            _historyText.Clear (_model.GetAllLines ());
             ResetPosition ();
         }
         finally
@@ -3092,7 +3240,7 @@ public class TextView : View
     {
         SetWrapModel ();
         _model.LoadStream (stream);
-        _historyText.Clear (Text);
+        _historyText.Clear (_model.GetAllLines ());
         ResetPosition ();
         SetNeedsDisplay ();
         UpdateWrapModel ();
@@ -3104,7 +3252,7 @@ public class TextView : View
     {
         SetWrapModel ();
         _model.LoadCells (cells, ColorScheme?.Focus);
-        _historyText.Clear (Text);
+        _historyText.Clear (_model.GetAllLines ());
         ResetPosition ();
         SetNeedsDisplay ();
         UpdateWrapModel ();
@@ -3118,7 +3266,7 @@ public class TextView : View
         SetWrapModel ();
         InheritsPreviousAttribute = true;
         _model.LoadListCells (cellsList, ColorScheme?.Focus);
-        _historyText.Clear (Text);
+        _historyText.Clear (_model.GetAllLines ());
         ResetPosition ();
         SetNeedsDisplay ();
         UpdateWrapModel ();
@@ -4069,6 +4217,14 @@ public class TextView : View
                              null,
                              null,
                              (KeyCode)KeyBindings.GetKeyFromCommands (Command.Redo)
+                            ),
+                        new (
+                             Strings.ctxColors,
+                             "",
+                             () => PromptForColors (),
+                             null,
+                             null,
+                             (KeyCode)KeyBindings.GetKeyFromCommands (Command.Open)
                             )
                     }
                    );
@@ -4460,7 +4616,7 @@ public class TextView : View
     // Returns a string with the text in the selected 
     // region.
     //
-    private string GetRegion (
+    internal string GetRegion (
         out List<List<Cell>> cellsList,
         int? sRow = null,
         int? sCol = null,
@@ -4536,7 +4692,7 @@ public class TextView : View
 
         OnUnwrappedCursorPosition (cRow, cCol);
 
-        return GetRegion (out _selectedCellsList, sRow: startRow, sCol: startCol, cRow: cRow, cCol: cCol, model: model);
+        return GetRegion (out _, sRow: startRow, sCol: startCol, cRow: cRow, cCol: cCol, model: model);
     }
 
     private (int Row, int Col) GetUnwrappedPosition (int line, int col)
@@ -4588,12 +4744,12 @@ public class TextView : View
 
             for (var i = 0; i < obj.Lines.Count; i++)
             {
-                if (i == 0)
+                if (i == 0 || obj.LineStatus == HistoryText.LineStatus.Original || obj.LineStatus == HistoryText.LineStatus.Attribute)
                 {
                     _model.ReplaceLine (startLine, obj.Lines [i]);
                 }
-                else if ((obj.IsUndoing && obj.LineStatus == HistoryText.LineStatus.Removed)
-                         || (!obj.IsUndoing && obj.LineStatus == HistoryText.LineStatus.Added))
+                else if (obj is { IsUndoing: true, LineStatus: HistoryText.LineStatus.Removed }
+                                or { IsUndoing: false, LineStatus: HistoryText.LineStatus.Added })
                 {
                     _model.AddLine (startLine, obj.Lines [i]);
                 }
@@ -4671,7 +4827,7 @@ public class TextView : View
 
         List<Cell> line = GetCurrentLine ();
 
-        _historyText.Add (new () { new (line) }, CursorPosition);
+        _historyText.Add ([new (line)], CursorPosition);
 
         // Optimize single line
         if (lines.Count == 1)
@@ -4680,7 +4836,7 @@ public class TextView : View
             CurrentColumn += lines [0].Count;
 
             _historyText.Add (
-                              new () { new (line) },
+                              [new (line)],
                               CursorPosition,
                               HistoryText.LineStatus.Replaced
                              );
@@ -4709,7 +4865,7 @@ public class TextView : View
         }
 
         List<Cell>? rest = null;
-        var lastp = 0;
+        var lastPosition = 0;
 
         if (_model.Count > 0 && line.Count > 0 && !_copyWithoutSelection)
         {
@@ -4724,19 +4880,19 @@ public class TextView : View
 
         //model.AddLine (currentRow, lines [0]);
 
-        List<List<Cell>> addedLines = new () { new (line) };
+        List<List<Cell>> addedLines = [new (line)];
 
         for (var i = 1; i < lines.Count; i++)
         {
             _model.AddLine (CurrentRow + i, lines [i]);
 
-            addedLines.Add (new (lines [i]));
+            addedLines.Add ([..lines [i]]);
         }
 
         if (rest is { })
         {
             List<Cell> last = _model.GetLine (CurrentRow + lines.Count - 1);
-            lastp = last.Count;
+            lastPosition = last.Count;
             last.InsertRange (last.Count, rest);
 
             addedLines.Last ().InsertRange (addedLines.Last ().Count, rest);
@@ -4746,11 +4902,11 @@ public class TextView : View
 
         // Now adjust column and row positions
         CurrentRow += lines.Count - 1;
-        CurrentColumn = rest is { } ? lastp : lines [lines.Count - 1].Count;
+        CurrentColumn = rest is { } ? lastPosition : lines [^1].Count;
         Adjust ();
 
         _historyText.Add (
-                          new () { new (line) },
+                          [new (line)],
                           CursorPosition,
                           HistoryText.LineStatus.Replaced
                          );
@@ -4769,7 +4925,7 @@ public class TextView : View
 
         SetWrapModel ();
 
-        _historyText.Add (new () { new (GetCurrentLine ()) }, CursorPosition);
+        _historyText.Add ([new (GetCurrentLine ())], CursorPosition);
 
         if (Selecting)
         {
@@ -4807,7 +4963,7 @@ public class TextView : View
         }
 
         _historyText.Add (
-                          new () { new (GetCurrentLine ()) },
+                          [new (GetCurrentLine ())],
                           CursorPosition,
                           HistoryText.LineStatus.Replaced
                          );

+ 5 - 176
UICatalog/Scenarios/Editor.cs

@@ -206,23 +206,11 @@ public class Editor : Scenario
                          new MenuItem (
                                        "Colors",
                                        "",
-                                       () =>
-                                       {
-                                           if (!PromptForColor (
-                                                                "Colors",
-                                                                GetSelectedCellAttribute (),
-                                                                out Attribute newAttribute
-                                                               ))
-                                           {
-                                               return;
-                                           }
-
-                                           var attribute = new Attribute (newAttribute.Foreground,
-                                                                          newAttribute.Background
-                                                                         );
-
-                                           ApplyCellAttribute (attribute);
-                                       })
+                                       () => _textView.PromptForColors (),
+                                       null,
+                                       null,
+                                       KeyCode.CtrlMask | KeyCode.L
+                                      )
                      }
                     ),
                 new (
@@ -337,165 +325,6 @@ public class Editor : Scenario
 
     }
 
-    private void ApplyCellAttribute (Attribute attribute)
-    {
-        if (!_textView.ReadOnly && _textView.SelectedLength > 0)
-        {
-            var startRow = Math.Min (_textView.SelectionStartRow, _textView.CurrentRow);
-            var endRow = Math.Max (_textView.CurrentRow, _textView.SelectionStartRow);
-            var startCol = _textView.SelectionStartRow <= _textView.CurrentRow ? _textView.SelectionStartColumn : _textView.CurrentColumn;
-            var endCol = _textView.CurrentRow >= _textView.SelectionStartRow ? _textView.CurrentColumn : _textView.SelectionStartColumn;
-
-            for (int r = startRow; r <= endRow; r++)
-            {
-                List<Cell> line = _textView.GetLine (r);
-
-                for (int c = r == startRow ? startCol : 0;
-                     c < (r == endRow ? endCol : line.Count);
-                     c++)
-                {
-                    Cell cell = line [c]; // Copy value to a new variable
-                    cell.Attribute = attribute; // Modify the copy
-                    line [c] = cell; // Assign the modified copy back
-                }
-            }
-        }
-    }
-
-    private Attribute? GetSelectedCellAttribute ()
-    {
-        List<Cell> line;
-
-        if (_textView.SelectedLength > 0)
-        {
-            line = _textView.GetLine (_textView.SelectionStartRow);
-
-            if (line [Math.Min (_textView.SelectionStartColumn, line.Count - 1)].Attribute is { } attributeSel)
-            {
-                return new (attributeSel);
-            }
-
-            return new (_textView.ColorScheme!.Focus);
-        }
-
-        line = _textView.GetCurrentLine ();
-
-        if (line [Math.Min (_textView.CurrentColumn, line.Count - 1)].Attribute is { } attribute)
-        {
-            return new (attribute);
-        }
-
-        return new (_textView.ColorScheme!.Focus);
-    }
-
-    public static bool PromptForColor (string title, Attribute? current, out Attribute newAttribute)
-    {
-        var accept = false;
-
-        var d = new Dialog
-        {
-            Title = title,
-            Width = Application.Force16Colors ? 37 : Dim.Auto (DimAutoStyle.Auto, Dim.Percent (80), Dim.Percent (90)),
-            Height = 20
-        };
-
-        var btnOk = new Button
-        {
-            X = Pos.Center () - 5,
-            Y = Application.Force16Colors ? 6 : 4,
-            Text = "Ok",
-            Width = Dim.Auto (),
-            IsDefault = true
-        };
-
-        btnOk.Accept += (s, e) =>
-        {
-            accept = true;
-            e.Handled = true;
-            Application.RequestStop ();
-        };
-
-        var btnCancel = new Button
-        {
-            X = Pos.Center () + 5,
-            Y = 4,
-            Text = "Cancel",
-            Width = Dim.Auto ()
-        };
-
-        btnCancel.Accept += (s, e) =>
-        {
-            e.Handled = true;
-            Application.RequestStop ();
-        };
-
-        d.Add (btnOk);
-        d.Add (btnCancel);
-
-        d.AddButton (btnOk);
-        d.AddButton (btnCancel);
-
-        View cpForeground;
-        if (Application.Force16Colors)
-        {
-            cpForeground = new ColorPicker16
-            {
-                SelectedColor = current!.Value.Foreground.GetClosestNamedColor16 (),
-                Width = Dim.Fill (),
-                BorderStyle = LineStyle.Single,
-                Title = "Foreground"
-            };
-        }
-        else
-        {
-            cpForeground = new ColorPicker
-            {
-                SelectedColor = current!.Value.Foreground,
-                Width = Dim.Fill (),
-                Style = new () { ShowColorName = true, ShowTextFields = true },
-                BorderStyle = LineStyle.Single,
-                Title = "Foreground"
-            };
-            ((ColorPicker)cpForeground).ApplyStyleChanges ();
-        }
-
-        View cpBackground;
-        if (Application.Force16Colors)
-        {
-            cpBackground = new ColorPicker16
-            {
-                SelectedColor = current!.Value.Background.GetClosestNamedColor16 (),
-                Y = Pos.Bottom (cpForeground) + 1,
-                Width = Dim.Fill (),
-                BorderStyle = LineStyle.Single,
-                Title = "Background"
-            };
-        }
-        else
-        {
-            cpBackground = new ColorPicker
-            {
-                SelectedColor = current!.Value.Background,
-                Width = Dim.Fill (),
-                Y = Pos.Bottom (cpForeground) + 1,
-                Style = new () { ShowColorName = true, ShowTextFields = true },
-                BorderStyle = LineStyle.Single,
-                Title = "Background"
-            };
-            ((ColorPicker)cpBackground).ApplyStyleChanges ();
-        }
-
-        d.Add (cpForeground, cpBackground);
-
-        Application.Run (d);
-        d.Dispose ();
-        var newForeColor = Application.Force16Colors ? ((ColorPicker16)cpForeground).SelectedColor : ((ColorPicker)cpForeground).SelectedColor;
-        var newBackColor = Application.Force16Colors ? ((ColorPicker16)cpBackground).SelectedColor : ((ColorPicker)cpBackground).SelectedColor;
-        newAttribute = new (newForeColor, newBackColor);
-
-        return accept;
-    }
-
     private bool CanCloseFile ()
     {
         if (_textView.Text == Encoding.Unicode.GetString (_originalText))

+ 80 - 0
UnitTests/Views/TextViewTests.cs

@@ -4713,6 +4713,86 @@ This is the second line.
         Assert.Equal (new Point (0, 1), tv.CursorPosition);
     }
 
+    [Fact]
+    public void HistoryText_Undo_Redo_ApplyCellsAttribute ()
+    {
+        var text = "This is the first line.\nThis is the second line.\nThis is the third line.";
+        var tv = new TextView { Text = text };
+
+        tv.SelectionStartColumn = 12;
+        tv.CursorPosition = new Point (18, 1);
+
+        Assert.Equal (31, tv.SelectedLength);
+        Assert.Equal ($"first line.{Environment.NewLine}This is the second", tv.SelectedText);
+        Assert.Equal ($"first line.{Environment.NewLine}This is the second", Cell.ToString (tv.SelectedCellsList));
+        Assert.Equal (new Point (18, 1), tv.CursorPosition);
+        Assert.False (tv.IsDirty);
+
+        AssertNullAttribute ();
+
+        tv.ApplyCellsAttribute (new (Color.Red, Color.Green));
+
+        AssertRedGreenAttribute ();
+
+        Assert.Equal (0, tv.SelectedLength);
+        Assert.Equal ("", tv.SelectedText);
+        Assert.Equal ($"first line.{Environment.NewLine}This is the second", Cell.ToString (tv.SelectedCellsList));
+        Assert.Equal (new Point (18, 1), tv.CursorPosition);
+        Assert.True (tv.IsDirty);
+
+        // Undo
+        Assert.True (tv.NewKeyDownEvent (Key.Z.WithCtrl));
+
+        AssertNullAttribute ();
+
+        Assert.Equal (12, tv.SelectionStartColumn);
+        Assert.Equal (0, tv.SelectionStartRow);
+        Assert.Equal (0, tv.SelectedLength);
+        Assert.Equal ("", tv.SelectedText);
+        Assert.Empty (tv.SelectedCellsList);
+        Assert.Equal (new Point (12, 0), tv.CursorPosition);
+        Assert.False (tv.IsDirty);
+
+        // Redo
+        Assert.True (tv.NewKeyDownEvent (Key.R.WithCtrl));
+
+        AssertRedGreenAttribute ();
+
+        Assert.Equal (12, tv.SelectionStartColumn);
+        Assert.Equal (0, tv.SelectionStartRow);
+        Assert.Equal (0, tv.SelectedLength);
+        Assert.Equal ("", tv.SelectedText);
+        Assert.Empty (tv.SelectedCellsList);
+        Assert.Equal (new Point (12, 0), tv.CursorPosition);
+        Assert.True (tv.IsDirty);
+
+        void AssertNullAttribute ()
+        {
+            tv.GetRegion (out List<List<Cell>> region, 0, 12, 1, 18);
+
+            foreach (List<Cell> cells in region)
+            {
+                foreach (Cell cell in cells)
+                {
+                    Assert.Null (cell.Attribute);
+                }
+            }
+        }
+
+        void AssertRedGreenAttribute ()
+        {
+            tv.GetRegion (out List<List<Cell>> region, 0, 12, 1, 18);
+
+            foreach (List<Cell> cells in region)
+            {
+                foreach (Cell cell in cells)
+                {
+                    Assert.Equal ("[Red,Green]", cell.Attribute.ToString ());
+                }
+            }
+        }
+    }
+
     [Fact]
     public void Internal_Tests ()
     {