]> granicus.if.org Git - handbrake/commitdiff
WinGui: Adding Subtitle Burn-in Behaviour Support.
authorsr55 <sr55.hb@outlook.com>
Sat, 4 Apr 2015 21:09:12 +0000 (21:09 +0000)
committersr55 <sr55.hb@outlook.com>
Sat, 4 Apr 2015 21:09:12 +0000 (21:09 +0000)
git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@7052 b64f7644-9d1e-0410-96f1-a4d463321fa5

win/CS/HandBrake.ApplicationServices/Services/Encode/Factories/EncodeFactory.cs
win/CS/HandBrakeWPF/Converters/Subtitles/SubtitleBurnInBehaviourConverter.cs [new file with mode: 0644]
win/CS/HandBrakeWPF/HandBrakeWPF.csproj
win/CS/HandBrakeWPF/Model/Subtitles/SubtitleBehaviours.cs
win/CS/HandBrakeWPF/Model/Subtitles/SubtitleBurnInBehaviourModes.cs [new file with mode: 0644]
win/CS/HandBrakeWPF/Properties/Resources.Designer.cs
win/CS/HandBrakeWPF/Properties/Resources.resx
win/CS/HandBrakeWPF/ViewModels/SubtitlesViewModel.cs
win/CS/HandBrakeWPF/Views/SubtitlesView.xaml

index bebfeca1803acd440571600e5cf4e4855b50d32f..2ec14a1e20a8be87ff3b0bc17e8b5381dbef1dca 100644 (file)
@@ -257,10 +257,10 @@ namespace HandBrake.ApplicationServices.Services.Encode.Factories
             }\r
             else\r
             {\r
-                video.Level = job.VideoLevel.ShortName;\r
+                video.Level = job.VideoLevel != null ? job.VideoLevel.ShortName : null;\r
                 video.Options = job.ExtraAdvancedArguments;\r
-                video.Preset = job.VideoPreset.ShortName;\r
-                video.Profile = job.VideoProfile.ShortName;\r
+                video.Preset = job.VideoPreset != null ? job.VideoPreset.ShortName : null;\r
+                video.Profile = job.VideoProfile != null ? job.VideoProfile.ShortName : null;\r
             }\r
 \r
             if (job.VideoEncodeRateType == VideoEncodeRateType.ConstantQuality) video.Quality = job.Quality;\r
diff --git a/win/CS/HandBrakeWPF/Converters/Subtitles/SubtitleBurnInBehaviourConverter.cs b/win/CS/HandBrakeWPF/Converters/Subtitles/SubtitleBurnInBehaviourConverter.cs
new file mode 100644 (file)
index 0000000..76be941
--- /dev/null
@@ -0,0 +1,91 @@
+// --------------------------------------------------------------------------------------------------------------------\r
+// <copyright file="SubtitleBurnInBehaviourConverter.cs" company="HandBrake Project (http://handbrake.fr)">\r
+//   This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License.\r
+// </copyright>\r
+// <summary>\r
+//   Subtitle Behaviour Converter\r
+// </summary>\r
+// --------------------------------------------------------------------------------------------------------------------\r
+\r
+namespace HandBrakeWPF.Converters.Subtitles\r
+{\r
+    using System;\r
+    using System.ComponentModel;\r
+    using System.Globalization;\r
+    using System.Linq;\r
+    using System.Windows.Data;\r
+\r
+    using HandBrake.ApplicationServices.Utilities;\r
+\r
+    using HandBrakeWPF.Model.Subtitles;\r
+\r
+    /// <summary>\r
+    /// Subtitle Behaviour Converter\r
+    /// </summary>\r
+    public class SubtitleBurnInBehaviourConverter : IValueConverter\r
+    {\r
+        /// <summary>\r
+        /// The convert.\r
+        /// </summary>\r
+        /// <param name="value">\r
+        /// The value.\r
+        /// </param>\r
+        /// <param name="targetType">\r
+        /// The target type.\r
+        /// </param>\r
+        /// <param name="parameter">\r
+        /// The parameter.\r
+        /// </param>\r
+        /// <param name="culture">\r
+        /// The culture.\r
+        /// </param>\r
+        /// <returns>\r
+        /// The <see cref="object"/>.\r
+        /// </returns>\r
+        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)\r
+        {\r
+            if (value != null && value.GetType() == typeof(BindingList<SubtitleBurnInBehaviourModes>))\r
+            {\r
+                return\r
+                    new BindingList<string>(\r
+                        EnumHelper<SubtitleBurnInBehaviourModes>.GetEnumDisplayValues(typeof(SubtitleBurnInBehaviourModes)).ToList());\r
+            }\r
+\r
+            if (value != null && value.GetType() == typeof(SubtitleBehaviourModes))\r
+            {\r
+                return EnumHelper<SubtitleBurnInBehaviourModes>.GetDisplay((SubtitleBurnInBehaviourModes)value);\r
+            }\r
+\r
+            return null;\r
+        }\r
+\r
+        /// <summary>\r
+        /// The convert back.\r
+        /// </summary>\r
+        /// <param name="value">\r
+        /// The value.\r
+        /// </param>\r
+        /// <param name="targetType">\r
+        /// The target type.\r
+        /// </param>\r
+        /// <param name="parameter">\r
+        /// The parameter.\r
+        /// </param>\r
+        /// <param name="culture">\r
+        /// The culture.\r
+        /// </param>\r
+        /// <returns>\r
+        /// The <see cref="object"/>.\r
+        /// </returns>\r
+        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)\r
+        {\r
+            string name = value as string;\r
+            if (!string.IsNullOrEmpty(name))\r
+            {\r
+                return EnumHelper<SubtitleBurnInBehaviourModes>.GetValue(name);\r
+            }\r
+\r
+            return null;\r
+        }\r
+    }\r
+}\r
index dd5ee90eda9ae9425565e4e0834bffc712b5e35d..f8e7546636cd927af7c4638363af71f069fed680 100644 (file)
     <Compile Include="Controls\SplitButton\SplitMenuButton.cs" />\r
     <Compile Include="Converters\Audio\AudioBehaviourConverter.cs" />\r
     <Compile Include="Converters\Filters\DenoisePresetConverter.cs" />\r
+    <Compile Include="Converters\Subtitles\SubtitleBurnInBehaviourConverter.cs" />\r
     <Compile Include="Converters\Subtitles\SubtitleBehaviourConverter.cs" />\r
     <Compile Include="Converters\Video\VideoOptionsTooltipConverter.cs" />\r
     <Compile Include="Converters\Video\ScalingConverter.cs" />\r
     <Compile Include="Model\Audio\AudioBehaviours.cs" />\r
     <Compile Include="Model\DriveInformation.cs" />\r
     <Compile Include="Model\Picture\PresetPictureSettingsMode.cs" />\r
+    <Compile Include="Model\Subtitles\SubtitleBurnInBehaviourModes.cs" />\r
     <Compile Include="Model\Subtitles\SubtitleBehaviourModes.cs" />\r
     <Compile Include="Model\Subtitles\SubtitleBehaviours.cs" />\r
     <Compile Include="Services\Interfaces\IQueueProcessor.cs" />\r
index 8570f00e73a03ef8e55183dc57b17de44ea690bc..db5b7eb3969592d42b6260f9b4ac91b9f87a1188 100644 (file)
@@ -19,25 +19,11 @@ namespace HandBrakeWPF.Model.Subtitles
     /// </summary>\r
     public class SubtitleBehaviours : PropertyChangedBase\r
     {\r
-        /// <summary>\r
-        /// The selected behaviour.\r
-        /// </summary>\r
         private SubtitleBehaviourModes selectedBehaviour;\r
-\r
-        /// <summary>\r
-        /// The selected langauges.\r
-        /// </summary>\r
         private BindingList<string> selectedLangauges;\r
-\r
-        /// <summary>\r
-        /// The add foreign audio scan track.\r
-        /// </summary>\r
         private bool addForeignAudioScanTrack;\r
-\r
-        /// <summary>\r
-        /// The add closed captions.\r
-        /// </summary>\r
         private bool addClosedCaptions;\r
+        private SubtitleBurnInBehaviourModes selectedBurnInBehaviour;\r
 \r
         /// <summary>\r
         /// Initializes a new instance of the <see cref="SubtitleBehaviours"/> class.\r
@@ -45,6 +31,7 @@ namespace HandBrakeWPF.Model.Subtitles
         public SubtitleBehaviours()\r
         {\r
             this.SelectedBehaviour = SubtitleBehaviourModes.None;\r
+            this.SelectedBurnInBehaviour = SubtitleBurnInBehaviourModes.None;\r
             this.SelectedLangauges = new BindingList<string>();\r
         }\r
 \r
@@ -57,6 +44,7 @@ namespace HandBrakeWPF.Model.Subtitles
         public SubtitleBehaviours(SubtitleBehaviours behaviours)\r
         {\r
             this.SelectedBehaviour = behaviours.selectedBehaviour;\r
+            this.SelectedBurnInBehaviour = behaviours.selectedBurnInBehaviour;\r
             this.SelectedLangauges = new BindingList<string>(behaviours.SelectedLangauges.ToList());\r
         }\r
 \r
@@ -80,6 +68,26 @@ namespace HandBrakeWPF.Model.Subtitles
             }\r
         }\r
 \r
+        /// <summary>\r
+        /// Gets or sets the selected burn in behaviour.\r
+        /// </summary>\r
+        public SubtitleBurnInBehaviourModes SelectedBurnInBehaviour\r
+        {\r
+            get\r
+            {\r
+                return this.selectedBurnInBehaviour;\r
+            }\r
+            set\r
+            {\r
+                if (value == this.selectedBurnInBehaviour)\r
+                {\r
+                    return;\r
+                }\r
+                this.selectedBurnInBehaviour = value;\r
+                this.NotifyOfPropertyChange(() => this.SelectedBurnInBehaviour);\r
+            }\r
+        }\r
+\r
         /// <summary>\r
         /// Gets or sets the selected langages.\r
         /// </summary>\r
@@ -151,6 +159,7 @@ namespace HandBrakeWPF.Model.Subtitles
             SubtitleBehaviours cloned = new SubtitleBehaviours\r
             {\r
                 SelectedBehaviour = this.selectedBehaviour,\r
+                SelectedBurnInBehaviour = this.selectedBurnInBehaviour,\r
                 SelectedLangauges = new BindingList<string>(),\r
                 AddClosedCaptions = this.addClosedCaptions,\r
                 AddForeignAudioScanTrack = this.addForeignAudioScanTrack,\r
diff --git a/win/CS/HandBrakeWPF/Model/Subtitles/SubtitleBurnInBehaviourModes.cs b/win/CS/HandBrakeWPF/Model/Subtitles/SubtitleBurnInBehaviourModes.cs
new file mode 100644 (file)
index 0000000..174b4a5
--- /dev/null
@@ -0,0 +1,31 @@
+// --------------------------------------------------------------------------------------------------------------------\r
+// <copyright file="SubtitleBurnInBehaviourModes.cs" company="HandBrake Project (http://handbrake.fr)">\r
+//   This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License.\r
+// </copyright>\r
+// <summary>\r
+//   The subtitle behaviours modes.\r
+// </summary>\r
+// --------------------------------------------------------------------------------------------------------------------\r
+\r
+namespace HandBrakeWPF.Model.Subtitles\r
+{\r
+    using System.ComponentModel.DataAnnotations;\r
+\r
+    /// <summary>\r
+    /// The subtitle behaviours modes.\r
+    /// </summary>\r
+    public enum SubtitleBurnInBehaviourModes\r
+    {\r
+        [Display(Name = "None")]\r
+        None = 0,\r
+\r
+        [Display(Name = "Foreign Audio Track")]\r
+        ForeignAudio,\r
+\r
+        [Display(Name = "First Track")]\r
+        FirstTrack,\r
+\r
+        [Display(Name = "Foreign Audio Preferred, else First")]\r
+        ForeignAudioPreferred,\r
+    }\r
+}\r
index 5c4097933506db0f78a23ac587a8fa459c052d75..3f26afe6ea4ce425c087d617c43ddcd444db5df0 100644 (file)
@@ -900,6 +900,18 @@ namespace HandBrakeWPF.Properties {
             }\r
         }\r
         \r
+        /// <summary>\r
+        ///   Looks up a localized string similar to None - Only tracks where the container does not support the format will be burned in.\r
+        ///Foreign Audio Track - The Foreign Audio track will be burned in if available. \r
+        ///First Track - The first track will be burned in.\r
+        ///Foreign Audio Preferred, else First - If the foreign audio track exists, it will be burned in, otherwise the first track will be chosen..\r
+        /// </summary>\r
+        public static string Subtitles_BurnInBehaviourModes {\r
+            get {\r
+                return ResourceManager.GetString("Subtitles_BurnInBehaviourModes", resourceCulture);\r
+            }\r
+        }\r
+        \r
         /// <summary>\r
         ///   Looks up a localized string similar to Updated.\r
         /// </summary>\r
index 2a3222d1db83086b76c1ef2d8c66f7849713a677..2bc82bd716ac4ec206146679488f32a84aa54c5a 100644 (file)
@@ -556,4 +556,10 @@ Please use the 'Extra Options' box on the 'Video' tab to input any additional en
     <value>You cannot overwrite the source file you want to convert.\r
 Please choose a different filename.</value>\r
   </data>\r
+  <data name="Subtitles_BurnInBehaviourModes" xml:space="preserve">\r
+    <value>None - Only tracks where the container does not support the format will be burned in.\r
+Foreign Audio Track - The Foreign Audio track will be burned in if available. \r
+First Track - The first track will be burned in.\r
+Foreign Audio Preferred, else First - If the foreign audio track exists, it will be burned in, otherwise the first track will be chosen.</value>\r
+  </data>\r
 </root>
\ No newline at end of file
index 425417cf1e4ff3c0f5f005ef88a1823c9691a549..33aa86be14bea1dfe0acccb09d1c997874499cf9 100644 (file)
@@ -192,6 +192,17 @@ namespace HandBrakeWPF.ViewModels
             }\r
         }\r
 \r
+        /// <summary>\r
+        /// Gets the subtitle burn in behaviour mode list.\r
+        /// </summary>\r
+        public BindingList<SubtitleBurnInBehaviourModes> SubtitleBurnInBehaviourModeList\r
+        {\r
+            get\r
+            {\r
+                return new BindingList<SubtitleBurnInBehaviourModes>(EnumHelper<SubtitleBurnInBehaviourModes>.GetEnumList().ToList());\r
+            }\r
+        }\r
+\r
         /// <summary>\r
         /// Gets or sets AvailableLanguages.\r
         /// </summary>\r
@@ -418,6 +429,62 @@ namespace HandBrakeWPF.ViewModels
                     break;\r
             }\r
 \r
+            // Burn In Behaviour\r
+            if (this.Task.SubtitleTracks.Count >= 1)\r
+            {\r
+                bool burnInSet = false;\r
+                switch (this.SubtitleBehaviours.SelectedBurnInBehaviour)\r
+                {\r
+                    case SubtitleBurnInBehaviourModes.None:\r
+                        // Do Nothing. Only tracks where the container requires it will be burned in.\r
+                        break;\r
+                    case SubtitleBurnInBehaviourModes.ForeignAudio:\r
+                        foreach (var track in this.Task.SubtitleTracks)\r
+                        {\r
+                            // Set the Foreign Audio Track to burned-in\r
+                            if (track.SourceTrack.SubtitleType == SubtitleType.ForeignAudioSearch)\r
+                            {\r
+                                track.Burned = true;\r
+                                this.SetBurnedToFalseForAllExcept(track);\r
+                                break;\r
+                            }\r
+                        }\r
+                        break;\r
+                    case SubtitleBurnInBehaviourModes.FirstTrack:                    \r
+                        foreach (var track in this.Task.SubtitleTracks)\r
+                        {\r
+                            // Set the first track.\r
+                            if (!burnInSet && track.SourceTrack.SubtitleType != SubtitleType.ForeignAudioSearch)\r
+                            {\r
+                                burnInSet = true;\r
+                                track.Burned = true;\r
+                                this.SetBurnedToFalseForAllExcept(track);\r
+                            }\r
+                        }                  \r
+                        break;\r
+                    case SubtitleBurnInBehaviourModes.ForeignAudioPreferred:\r
+                        foreach (var track in this.Task.SubtitleTracks)\r
+                        {\r
+                            // Set the first track.\r
+                            if (!burnInSet)\r
+                            {\r
+                                burnInSet = true;\r
+                                track.Burned = true;\r
+                                this.SetBurnedToFalseForAllExcept(track);\r
+                            }\r
+\r
+                            // But if there is a foreign audio track, prefer this to the first.\r
+                            if (track.SourceTrack.SubtitleType == SubtitleType.ForeignAudioSearch)\r
+                            {\r
+                                track.Burned = true;\r
+                                this.SetBurnedToFalseForAllExcept(track);\r
+                                break;\r
+                            }\r
+                        }            \r
+                        break;\r
+                }\r
+            }\r
+\r
             // Add all closed captions if enabled.\r
             if (this.SubtitleBehaviours.AddClosedCaptions)\r
             {\r
@@ -491,6 +558,14 @@ namespace HandBrakeWPF.ViewModels
             this.SubtitleBehaviours.SelectedLangauges.Clear();\r
         }\r
 \r
+        /// <summary>\r
+        /// Reload the audio tracks based on the defaults.\r
+        /// </summary>\r
+        public void ReloadDefaults()\r
+        {\r
+            this.AutomaticSubtitleSelection();\r
+        }\r
+\r
         #endregion\r
 \r
         #region Implemented Interfaces\r
@@ -592,9 +667,17 @@ namespace HandBrakeWPF.ViewModels
                                           SourceTrack = source,\r
                                       };\r
 \r
-            if ((source.SubtitleType == SubtitleType.PGS || source.SubtitleType == SubtitleType.ForeignAudioSearch)\r
-                && this.Task != null\r
-                && this.Task.OutputFormat == OutputFormat.Mp4)\r
+\r
+            // Burn-in Behaviours\r
+            if (this.SubtitleBehaviours.SelectedBurnInBehaviour == SubtitleBurnInBehaviourModes.ForeignAudio\r
+                  || this.SubtitleBehaviours.SelectedBurnInBehaviour == SubtitleBurnInBehaviourModes.ForeignAudioPreferred)\r
+            {\r
+                track.Burned = true;\r
+                this.SetBurnedToFalseForAllExcept(track);\r
+            }\r
+\r
+            // For MP4, PGS Subtitles must be burned in.\r
+            if (!track.Burned && (source.SubtitleType == SubtitleType.PGS) && this.Task != null && this.Task.OutputFormat == OutputFormat.Mp4)\r
             {\r
                 if (track.CanBeBurned)\r
                 {\r
@@ -670,6 +753,7 @@ namespace HandBrakeWPF.ViewModels
         {\r
             // Step 1, Set the behaviour mode\r
             this.SubtitleBehaviours.SelectedBehaviour = SubtitleBehaviourModes.None;\r
+            this.SubtitleBehaviours.SelectedBurnInBehaviour = SubtitleBurnInBehaviourModes.None;\r
             this.SubtitleBehaviours.AddClosedCaptions = false;\r
             this.SubtitleBehaviours.AddForeignAudioScanTrack = false;\r
             this.SubtitleBehaviours.SelectedLangauges.Clear();\r
@@ -689,6 +773,7 @@ namespace HandBrakeWPF.ViewModels
             if (preset != null && preset.SubtitleTrackBehaviours != null)\r
             {\r
                 this.SubtitleBehaviours.SelectedBehaviour = preset.SubtitleTrackBehaviours.SelectedBehaviour;\r
+                this.SubtitleBehaviours.SelectedBurnInBehaviour = preset.SubtitleTrackBehaviours.SelectedBurnInBehaviour;\r
                 this.SubtitleBehaviours.AddClosedCaptions = preset.SubtitleTrackBehaviours.AddClosedCaptions;\r
                 this.SubtitleBehaviours.AddForeignAudioScanTrack = preset.SubtitleTrackBehaviours.AddForeignAudioScanTrack;\r
 \r
index de2418ed7c7d90c5f5297f388ac83ee2a2c63fb7..cccbaf79070e99b497ab39d7748a3487bb6148e4 100644 (file)
@@ -11,6 +11,7 @@
              xmlns:splitButton="clr-namespace:HandBrakeWPF.Controls.SplitButton"\r
              xmlns:helpers="clr-namespace:HandBrakeWPF.Helpers"\r
              xmlns:subtitles="clr-namespace:HandBrakeWPF.Converters.Subtitles"\r
+             xmlns:Properties="clr-namespace:HandBrakeWPF.Properties"\r
              d:DesignHeight="350"\r
              d:DesignWidth="500"\r
              mc:Ignorable="d"\r
     <UserControl.Resources>\r
         <Converters:BooleanToVisibilityConverter x:Key="booleanToVisConverter" />\r
         <subtitles:SubtitleBehaviourConverter x:Key="subtitleBehaviourConverter" />\r
+        <subtitles:SubtitleBurnInBehaviourConverter x:Key="subtitleBurnInBehaviourConverter" />\r
+\r
+        <Style x:Key="LongToolTip" TargetType="TextBlock">\r
+            <Setter Property="Width" Value="400" />\r
+            <Setter Property="TextWrapping" Value="Wrap" />\r
+            <Setter Property="ToolTipService.ShowDuration" Value="20000" />\r
+            <Setter Property="Margin" Value="0,2,0,2" />\r
+        </Style>\r
+\r
     </UserControl.Resources>\r
 \r
     <Grid>\r
                     Margin="0,0,10,0"\r
                     cal:Message.Attach="[Event Click] = [Action ShowSubtitleDefaultsPanel]"\r
                     Content="{Binding SwitchDisplayTitle}" />\r
+\r
+            <Button MinWidth="65"\r
+                    Margin="0,0,10,0"\r
+                    cal:Message.Attach="[Event Click] = [Action ReloadDefaults]"\r
+                    Content="Reload Defaults" />\r
+\r
+\r
         </StackPanel>\r
 \r
 \r
                     <RowDefinition Height="Auto" />\r
                     <RowDefinition Height="Auto" />\r
                     <RowDefinition Height="Auto" />\r
+                    <RowDefinition Height="Auto" />\r
                 </Grid.RowDefinitions>\r
 \r
                 <Grid.ColumnDefinitions>\r
                 <ComboBox Name="autoSubtitleMode"  Grid.Column="3" Grid.Row="0" HorizontalAlignment="Left"\r
                           ItemsSource="{Binding SubtitleBehaviourModeList, Converter={StaticResource subtitleBehaviourConverter}}" \r
                           SelectedItem="{Binding SubtitleBehaviours.SelectedBehaviour, Converter={StaticResource subtitleBehaviourConverter}}" Width="210" Margin="0,0,5,0" />\r
-                <CheckBox Content="Add Closed Captions when available"  Grid.Column="3" Grid.Row="1" Margin="0,5,0,0" \r
+                <TextBlock Text="Burn-In Behaviour:"  Grid.Column="2" Grid.Row="1" Margin="0,5,5,0" HorizontalAlignment="Left" VerticalAlignment="Center" />\r
+                <ComboBox Name="burninBehaviour"  Grid.Column="3" Grid.Row="1" HorizontalAlignment="Left"\r
+                          ItemsSource="{Binding SubtitleBurnInBehaviourModeList, Converter={StaticResource subtitleBurnInBehaviourConverter}}" \r
+                          SelectedItem="{Binding SubtitleBehaviours.SelectedBurnInBehaviour, Converter={StaticResource subtitleBurnInBehaviourConverter}}" Width="210" Margin="0,5,5,0">\r
+                    <ComboBox.ToolTip>\r
+                        <TextBlock Style="{StaticResource LongToolTip}" Text="{x:Static Properties:Resources.Subtitles_BurnInBehaviourModes}" />\r
+                    </ComboBox.ToolTip>\r
+                </ComboBox> \r
+                <CheckBox Content="Add Closed Captions when available"  Grid.Column="3" Grid.Row="2" Margin="0,5,0,0" \r
                                   HorizontalAlignment="Left" IsChecked="{Binding SubtitleBehaviours.AddClosedCaptions}"/>\r
-                <CheckBox Content="Add 'Foreign Audio Scan'"  Grid.Column="3" Grid.Row="2" Margin="0,5,0,0" \r
+                <CheckBox Content="Add 'Foreign Audio Scan'"  Grid.Column="3" Grid.Row="3" Margin="0,5,0,0" \r
                                   HorizontalAlignment="Left" IsChecked="{Binding SubtitleBehaviours.AddForeignAudioScanTrack}"/>\r
             </Grid>\r
 \r