[gnome-subtitles] Show margin with character counts in subtitle edit (bug #625485)



commit b0cc60bf22d944c0c4247302f34bb786fc79cf7e
Author: Pedro Castro <pedro gnomesubtitles org>
Date:   Sat Jul 16 19:05:43 2011 +0100

    Show margin with character counts in subtitle edit (bug #625485)

 gnome-subtitles.mdp                                |    3 +
 src/Glade/MainWindow.glade                         |   24 ++-
 src/GnomeSubtitles/Ui/Edit/SubtitleEditTextView.cs |   37 ++--
 .../Ui/Edit/SubtitleEditTextViewMargin.cs          |  222 ++++++++++++++++++++
 4 files changed, 263 insertions(+), 23 deletions(-)
---
diff --git a/gnome-subtitles.mdp b/gnome-subtitles.mdp
index 7081637..710d0a8 100644
--- a/gnome-subtitles.mdp
+++ b/gnome-subtitles.mdp
@@ -260,6 +260,7 @@
     <File subtype="Code" buildaction="Compile" name="src/SubLib/Core/Timing/MergeOperator.cs" />
     <File subtype="Code" buildaction="Compile" name="src/GnomeSubtitles/Core/Command/MergeSubtitlesCommand.cs" />
     <File subtype="Code" buildaction="Compile" name="src/SubLib/Exceptions/FileTooLargeException.cs" />
+    <File subtype="Code" buildaction="Compile" name="src/GnomeSubtitles/Ui/Edit/SubtitleEditTextViewMargin.cs" />
   </Contents>
   <References>
     <ProjectReference type="Gac" localcopy="True" refto="Mono.Posix, Version=2.0.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756" />
@@ -271,6 +272,8 @@
     <ProjectReference type="Gac" localcopy="True" refto="gconf-sharp, Version=2.24.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
     <ProjectReference type="Gac" localcopy="True" refto="glib-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
     <ProjectReference type="Gac" localcopy="True" refto="System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
+    <ProjectReference type="Gac" localcopy="True" refto="gdk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
+    <ProjectReference type="Gac" localcopy="True" refto="pango-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
   </References>
   <LanguageParameters StartupObject="GnomeSubtitles.Execution.Executable" ApplicationIcon="." ctype="CSharpProjectParameters" />
   <DeploymentInformation strategy="File" />
diff --git a/src/Glade/MainWindow.glade b/src/Glade/MainWindow.glade
index 437d91a..a8cfc86 100644
--- a/src/Glade/MainWindow.glade
+++ b/src/Glade/MainWindow.glade
@@ -247,8 +247,8 @@
                         <property name="use_underline">True</property>
                         <property name="use_stock">True</property>
                         <signal name="activate" handler="OnEditRedo"/>
-                        <accelerator key="Z" signal="activate" modifiers="GDK_SHIFT_MASK | GDK_CONTROL_MASK"/>
                         <accelerator key="Y" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+                        <accelerator key="Z" signal="activate" modifiers="GDK_SHIFT_MASK | GDK_CONTROL_MASK"/>
                       </widget>
                     </child>
                     <child>
@@ -586,8 +586,8 @@
                         <property name="label" translatable="yes">Find Ne_xt</property>
                         <property name="use_underline">True</property>
                         <signal name="activate" handler="OnSearchFindNext"/>
-                        <accelerator key="g" signal="activate" modifiers="GDK_CONTROL_MASK"/>
                         <accelerator key="F3" signal="activate"/>
+                        <accelerator key="g" signal="activate" modifiers="GDK_CONTROL_MASK"/>
                       </widget>
                     </child>
                     <child>
@@ -597,8 +597,8 @@
                         <property name="label" translatable="yes">Find Pre_vious</property>
                         <property name="use_underline">True</property>
                         <signal name="activate" handler="OnSearchFindPrevious"/>
-                        <accelerator key="g" signal="activate" modifiers="GDK_SHIFT_MASK | GDK_CONTROL_MASK"/>
                         <accelerator key="F3" signal="activate" modifiers="GDK_SHIFT_MASK"/>
+                        <accelerator key="g" signal="activate" modifiers="GDK_SHIFT_MASK | GDK_CONTROL_MASK"/>
                       </widget>
                     </child>
                     <child>
@@ -842,8 +842,8 @@
                         <property name="use_underline">True</property>
                         <property name="use_stock">False</property>
                         <signal name="activate" handler="OnVideoPlayPause"/>
-                        <accelerator key="p" signal="activate" modifiers="GDK_CONTROL_MASK"/>
                         <accelerator key="F5" signal="activate"/>
+                        <accelerator key="p" signal="activate" modifiers="GDK_CONTROL_MASK"/>
                         <child internal-child="image">
                           <widget class="GtkImage" id="videoPlayPauseImage">
                             <property name="visible">True</property>
@@ -870,8 +870,8 @@
                         <property name="use_underline">True</property>
                         <property name="use_stock">False</property>
                         <signal name="activate" handler="OnVideoRewind"/>
-                        <accelerator key="k" signal="activate" modifiers="GDK_CONTROL_MASK"/>
                         <accelerator key="F6" signal="activate"/>
+                        <accelerator key="k" signal="activate" modifiers="GDK_CONTROL_MASK"/>
                         <child internal-child="image">
                           <widget class="GtkImage" id="videoRewindImage">
                             <property name="visible">True</property>
@@ -889,8 +889,8 @@
                         <property name="use_underline">True</property>
                         <property name="use_stock">False</property>
                         <signal name="activate" handler="OnVideoForward"/>
-                        <accelerator key="l" signal="activate" modifiers="GDK_CONTROL_MASK"/>
                         <accelerator key="F7" signal="activate"/>
+                        <accelerator key="l" signal="activate" modifiers="GDK_CONTROL_MASK"/>
                         <child internal-child="image">
                           <widget class="GtkImage" id="videoForwardImage">
                             <property name="visible">True</property>
@@ -930,8 +930,8 @@
                         <property name="label" translatable="yes">Seek _to Selection</property>
                         <property name="use_underline">True</property>
                         <signal name="activate" handler="OnVideoSeekToSelection"/>
-                        <accelerator key="r" signal="activate" modifiers="GDK_SHIFT_MASK | GDK_CONTROL_MASK"/>
                         <accelerator key="F4" signal="activate" modifiers="GDK_SHIFT_MASK | GDK_CONTROL_MASK"/>
+                        <accelerator key="r" signal="activate" modifiers="GDK_SHIFT_MASK | GDK_CONTROL_MASK"/>
                       </widget>
                     </child>
                     <child>
@@ -941,8 +941,8 @@
                         <property name="label" translatable="yes">Select Nearest Subtitle</property>
                         <property name="use_underline">True</property>
                         <signal name="activate" handler="OnVideoSelectNearestSubtitle"/>
-                        <accelerator key="F4" signal="activate" modifiers="GDK_CONTROL_MASK"/>
                         <accelerator key="r" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+                        <accelerator key="F4" signal="activate" modifiers="GDK_CONTROL_MASK"/>
                       </widget>
                     </child>
                     <child>
@@ -1919,8 +1919,10 @@ Shift+Plus/Minus (on the numpad) shifts timings.</property>
                           <widget class="GtkTextView" id="subtitleEditText">
                             <property name="visible">True</property>
                             <property name="can_focus">True</property>
-                            <property name="pixels_above_lines">10</property>
+                            <property name="pixels_above_lines">5</property>
                             <property name="justification">center</property>
+                            <property name="left_margin">15</property>
+                            <property name="right_margin">15</property>
                             <property name="accepts_tab">False</property>
                           </widget>
                         </child>
@@ -1939,8 +1941,10 @@ Shift+Plus/Minus (on the numpad) shifts timings.</property>
                           <widget class="GtkTextView" id="subtitleEditTranslation">
                             <property name="visible">True</property>
                             <property name="can_focus">True</property>
-                            <property name="pixels_above_lines">10</property>
+                            <property name="pixels_above_lines">5</property>
                             <property name="justification">center</property>
+                            <property name="left_margin">15</property>
+                            <property name="right_margin">15</property>
                             <property name="accepts_tab">False</property>
                           </widget>
                         </child>
diff --git a/src/GnomeSubtitles/Ui/Edit/SubtitleEditTextView.cs b/src/GnomeSubtitles/Ui/Edit/SubtitleEditTextView.cs
index dfee5e4..9a74ce9 100644
--- a/src/GnomeSubtitles/Ui/Edit/SubtitleEditTextView.cs
+++ b/src/GnomeSubtitles/Ui/Edit/SubtitleEditTextView.cs
@@ -1,6 +1,6 @@
 /*
  * This file is part of Gnome Subtitles.
- * Copyright (C) 2006-2009 Pedro Castro
+ * Copyright (C) 2006-2009,2011 Pedro Castro
  *
  * Gnome Subtitles is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -26,15 +26,16 @@ using System.Runtime.InteropServices;
 
 namespace GnomeSubtitles.Ui.Edit {
 
+//FIXME italics, bolds, underlines, change style insted of applying tags
 public abstract class SubtitleEditTextView {
 	private TextView textView = null;
+
 	private bool isBufferChangeSilent = false; //used to indicate whether a buffer change should set the subtitle text in the subtitle list
 	private bool isBufferInsertManual = false; //used to indicate whether there were manual (not by the user) inserts to the buffer
 	private bool isBufferDeleteManual = false; //used to indicate whether there were manual (not by the user) inserts to the buffer
 	private bool isToggleOverwriteSilent = false; //used to indicate whether an overwrite toggle was manual
 	
 	/* Text tags */
-	private TextTag scaleTag = new TextTag("scale");
 	private TextTag boldTag = new TextTag("bold");
 	private TextTag italicTag = new TextTag("italic");
 	private TextTag underlineTag = new TextTag("underline");
@@ -44,21 +45,22 @@ public abstract class SubtitleEditTextView {
 	private Subtitle subtitle = null;
 	private IntPtr spellTextView = IntPtr.Zero;
 
-
 	public SubtitleEditTextView (TextView textView) {
 		this.textView = textView;
 
 		/* Init tags */
-		scaleTag.Scale = Pango.Scale.XLarge;
     	boldTag.Weight = Pango.Weight.Bold;
     	italicTag.Style = Pango.Style.Italic;
     	underlineTag.Underline = Pango.Underline.Single;
 
 		/* Init text view */
-		textView.Buffer.TagTable.Add(scaleTag);
-    	textView.Buffer.TagTable.Add(boldTag);
-    	textView.Buffer.TagTable.Add(italicTag);
-    	textView.Buffer.TagTable.Add(underlineTag);
+    	this.textView.Buffer.TagTable.Add(boldTag);
+    	this.textView.Buffer.TagTable.Add(italicTag);
+    	this.textView.Buffer.TagTable.Add(underlineTag);
+   		this.textView.ModifyFont(Pango.FontDescription.FromString("sans 14"));
+
+		/* Init margin */
+		new SubtitleEditTextViewMargin(this.textView);
 
 		Base.InitFinished += OnBaseInitFinished;
 	}
@@ -249,11 +251,21 @@ public abstract class SubtitleEditTextView {
     
 	private void LoadTags (SubLib.Core.Domain.Style style) {
     	subtitleTags.Clear();
-    	if (style.Bold)
+    	/*if (style.Bold)
     		subtitleTags.Add(boldTag);
     	if (style.Italic)
     		subtitleTags.Add(italicTag);
     	if (style.Underline)
+    		subtitleTags.Add(underlineTag);*/
+    		
+    	if (style.Bold)
+    		subtitleTags.Add(boldTag);
+    	if (style.Italic) {
+    		Pango.FontDescription fd = textView.PangoContext.FontDescription.Copy();
+    		fd.Style = Pango.Style.Italic;
+    		textView.ModifyFont(fd);
+    	}
+    	if (style.Underline)
     		subtitleTags.Add(underlineTag);
     }
     
@@ -261,7 +273,6 @@ public abstract class SubtitleEditTextView {
     	TextBuffer buffer = textView.Buffer;
     	TextIter start = buffer.StartIter;
     	TextIter end = buffer.EndIter;
-    	buffer.ApplyTag(scaleTag, start, end);
     	foreach (TextTag tag in subtitleTags)
 			SetTag(tag, start, end, true);
     }
@@ -416,8 +427,9 @@ public abstract class SubtitleEditTextView {
     		}
     	}
     }
-
+    
     private void OnBaseInitFinished () {
+    
 		/* Buffer signals */
 		textView.Buffer.Changed += OnBufferChanged;
 		textView.Buffer.MarkSet += OnBufferMarkSet;
@@ -431,11 +443,11 @@ public abstract class SubtitleEditTextView {
 		textView.ToggleOverwrite += OnToggleOverwrite;
 		textView.Destroyed += OnDestroyed;
 		
-		
 		/* Spell signals */
 		Base.SpellLanguages.ToggleEnabled += OnSpellToggleEnabled;
 		ConnectLanguageChangedSignal();
 		
+		/* Selection signals */
 		Base.Ui.View.Selection.Changed += OnSubtitleSelectionChanged;
     }
     
@@ -468,7 +480,6 @@ public abstract class SubtitleEditTextView {
 		SetText(String.Empty);	
 	}
 
-
 }
 
 }
diff --git a/src/GnomeSubtitles/Ui/Edit/SubtitleEditTextViewMargin.cs b/src/GnomeSubtitles/Ui/Edit/SubtitleEditTextViewMargin.cs
new file mode 100644
index 0000000..9deab9a
--- /dev/null
+++ b/src/GnomeSubtitles/Ui/Edit/SubtitleEditTextViewMargin.cs
@@ -0,0 +1,222 @@
+/*
+ * This file is part of Gnome Subtitles.
+ * Copyright (C) 2011 Pedro Castro
+ *
+ * Gnome Subtitles is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Gnome Subtitles is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+using GnomeSubtitles.Core;
+using Gtk;
+using System;
+
+namespace GnomeSubtitles.Ui.Edit {
+
+public class SubtitleEditTextViewMargin {
+	private int marginCharWidth = -1; //pixels
+	private int marginSpace = 4; //pixels
+	private int marginMinDigits = 2; //the minimum number of digits for margin width (1 would make the margin adjust with more than 9 chars, so it's better to keep a minimum of 2 to avoid constant adjustment
+	private int marginDigitCount = 2;
+	
+	/* Cached GCs and Pango Layout */
+	private Gdk.GC bgGC = null;
+	private Gdk.GC lineGC = null;
+	private Gdk.GC textGC = null;
+	private Pango.Layout textLayout = null;
+
+	/* Widgets */
+	private TextView textView = null;
+	
+	public SubtitleEditTextViewMargin (TextView textView) {
+		this.textView = textView;
+		
+		Base.InitFinished += OnBaseInitFinished;
+	}
+	
+	/* Private methods */
+	
+	public void DrawMargin (TextView textView, Gdk.Window window) {
+    	/* Get char count info  */
+    	int[,] info;
+    	int maxCharCount;
+    	GetCharCountDrawInfo(textView, out info, out maxCharCount);
+
+    	/* Do some calculations */    	
+    	int marginNumbersWidth = marginDigitCount * this.marginCharWidth;
+    	int marginNumbersX = textView.Allocation.Width - this.marginSpace - marginNumbersWidth;
+    	
+    	/* Draw line */
+    	int marginWidth = (this.marginSpace * 2) + marginNumbersWidth;
+    	int marginLineX = textView.Allocation.Width - marginWidth;
+    	window.DrawLine(this.lineGC, marginLineX, 0, marginLineX, textView.Allocation.Height);
+    	
+    	/* Draw background area */
+    	window.DrawRectangle(this.bgGC, true, marginLineX+1, 0, marginWidth-1, textView.Allocation.Height);
+    	
+    	/* Draw text */
+    	int infoCount = info.GetLength(0);
+    	for (int i = 0 ; i < infoCount ; i++) {
+    		int charCount = info[i, 0];
+    		int y = info[i, 1];
+    		
+    		this.textLayout.SetText(charCount.ToString());
+    		Pango.Rectangle layoutRect = GetPangoLayoutRect(this.textLayout);
+    		window.DrawLayout(this.textGC, marginNumbersX, y - layoutRect.Height/2, this.textLayout);
+		}
+    }
+
+    private void GetCharCountDrawInfo (TextView textView, out int[,] info, out int maxCharCount) {
+    	if (textView.Buffer.LineCount == 0) {
+    		info = null;
+    		maxCharCount = 0;
+    		return; //shouldn't happen, but just to make sure
+    	}
+    	
+    	/* Get visible coordinates */	
+    	int minVisibleY = textView.VisibleRect.Top;
+   		int maxVisibleY = textView.VisibleRect.Bottom;
+   		
+   		/* Get visible start and end iters */
+   		TextIter startIter, endIter;
+   		int lineTop;
+    	textView.GetLineAtY(out startIter, minVisibleY, out lineTop);
+    	textView.GetLineAtY(out endIter, maxVisibleY, out lineTop);
+    	int lineCount = endIter.Line - startIter.Line + 1;
+    	int startLine = startIter.Line;
+    	int endLine = endIter.Line;
+
+		/* Initializations */
+		info = new int[lineCount, 2];
+    	maxCharCount = -1;
+    	
+    	/* Process start iter */
+    	int startLineCharCount = startIter.CharsInLine - (lineCount > 1 ? 1 : 0); //subtract 1 for newline if there are >1 lines
+    	info[0, 0] = startLineCharCount; //Char Count
+    	Gdk.Rectangle startIterLocation = textView.GetIterLocation(startIter);
+    	info[0, 1] = startIterLocation.Bottom - (startIterLocation.Height/2) - minVisibleY; //Y
+    	if (startLineCharCount > maxCharCount) {
+    		maxCharCount = startLineCharCount;
+    	}
+    	
+    	/* If only 1 line, return */
+    	if (lineCount == 1) {
+    		return;
+    	}
+
+		/* Process middle iters */
+    	for (int i = 1, line = startLine + 1 ; line < endLine ; i++, line++) {
+    		TextIter iter = textView.Buffer.GetIterAtLine(line);
+			int charCount = iter.CharsInLine - 1; //subtract 1 for newline
+			info[i, 0] = charCount;
+			Gdk.Rectangle iterLocation = textView.GetIterLocation(iter);
+    		info[i, 1] = iterLocation.Bottom - (iterLocation.Height/2) - minVisibleY; //Y
+    		if (charCount > maxCharCount) {
+    			maxCharCount = charCount;
+    		}
+    	}
+    	
+    	/* Process end iter */
+    	int endLineCharCount = endIter.CharsInLine; //don't subtract newline because it's the last line
+    	info[lineCount-1, 0] = endLineCharCount;
+    	Gdk.Rectangle endIterLocation = textView.GetIterLocation(endIter);
+    	info[lineCount-1, 1] = endIterLocation.Bottom - (endIterLocation.Height/2) - minVisibleY; //Y
+		if (endLineCharCount > maxCharCount) {
+			maxCharCount = endLineCharCount;
+		}
+    }
+	   
+	private Pango.Rectangle GetPangoLayoutRect (Pango.Layout layout) {
+		Pango.Rectangle inkRect, logicalRect;
+    	layout.GetPixelExtents(out inkRect, out logicalRect);
+    	return logicalRect;
+	}
+	
+		
+	private int CalcDigitCount (TextBuffer buffer, int marginMinDigits) {
+		int maxChars = -1;
+		int lineCount = buffer.LineCount;
+		for (int line = 0 ; line < lineCount; line++) {
+			TextIter iter = buffer.GetIterAtLine(line);
+			int chars = iter.CharsInLine - (line == lineCount - 1 ? 0 : 1); //Subtract 1 for newline (except for the last line)
+			if (chars > maxChars) {
+				maxChars = chars;
+			}
+		}
+		
+		int digitCount = CountDigitsInNumber(maxChars);
+		return Math.Max(digitCount, this.marginMinDigits);
+	}
+    
+    private int CountDigitsInNumber (int number) {
+    	return (number == 0 ? 1 : (int)Math.Floor(Math.Log10(number)) + 1); //assuming the number is positive, otherwise would need to use abs() too
+    }
+    
+    private void SetGCs () {
+    	this.bgGC = Base.Ui.Window.Style.BackgroundGC(StateType.Normal);
+		this.lineGC = Base.Ui.Window.Style.BackgroundGC(StateType.Active);
+		this.textGC = Base.Ui.Window.Style.TextGC(StateType.Active);
+    }
+    
+    private void Refresh () {
+    	textView.QueueDraw();
+    }
+   
+    	
+	/* Event members */
+	
+	private void OnBaseInitFinished () {
+	
+		/* GCs */
+		SetGCs();
+		
+		/* Layouts */
+		this.textLayout = new Pango.Layout(textView.PangoContext);
+		this.textLayout.FontDescription = Pango.FontDescription.FromString("sans 10");
+		
+		/* Margin char width */
+		this.textLayout.SetText("0");
+		Pango.Rectangle layoutRect = GetPangoLayoutRect(this.textLayout);
+		this.marginCharWidth = layoutRect.Width;
+			
+		/* Events */
+		textView.ExposeEvent += OnExposeEvent;
+		textView.Buffer.Changed += OnBufferChanged; //To calculate margin digit count (based on the largest line char count)
+		textView.StyleSet += OnStyleSet; //To update colors if the style is changed
+		textView.Parent.ExposeEvent += OnScrolledWindowExposeEvent;
+	}
+	
+	private void OnExposeEvent (object o, ExposeEventArgs args) {
+		TextView textView = o as TextView;
+		if (textView.State != StateType.Insensitive) {
+			DrawMargin(textView, args.Event.Window);
+		}
+	}
+	
+	private void OnScrolledWindowExposeEvent (object o, ExposeEventArgs args) {
+		Refresh(); //Necessary for artifacts not to appear when scrolling
+	}
+	
+	private void OnBufferChanged (object o, EventArgs args) {
+		this.marginDigitCount = CalcDigitCount(o as TextBuffer, this.marginMinDigits);
+	}
+	
+	private void OnStyleSet (object o, StyleSetArgs args) {
+		SetGCs();
+	}
+
+	
+}
+
+}
+



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]