From 6dfa6615d37201b626aa0e4a41df0d2266669b91 Mon Sep 17 00:00:00 2001 From: Lorenz Cuno Klopfenstein Date: Wed, 14 Nov 2012 14:50:22 +0100 Subject: [PATCH] Added ThumbnailRegion class to support relative regions. Refactored XML serialization for stored regions. Refactored several thumbnail cloning (and region) methods. Removed PluginRegionLocator. Moved to System.Diagnostics.Trace for debug messaging. --- OnTopReplica/AspectRatioForm.cs | 4 + OnTopReplica/GeometryExtensions.cs | 19 + OnTopReplica/MainForm.cs | 43 +- OnTopReplica/MessagePumpManager.cs | 15 +- .../GroupSwitchManager.cs | 15 +- .../ShellInterceptProcessor.cs | 2 +- OnTopReplica/OnTopReplica.csproj | 7 +- OnTopReplica/OnTopReplica.exe.manifest | 4 + OnTopReplica/PluginRegionLocator.cs | 21 +- OnTopReplica/ScreenPosition.cs | 4 +- OnTopReplica/SidePanelContainer.cs | 1 - OnTopReplica/SidePanels/AboutPanel.cs | 1 - OnTopReplica/SidePanels/AboutPanelContents.cs | 1 - OnTopReplica/SidePanels/OptionsPanel.cs | 1 - .../SidePanels/RegionPanel.Designer.cs | 57 +- OnTopReplica/SidePanels/RegionPanel.cs | 131 +- OnTopReplica/SidePanels/RegionPanel.resx | 6 +- .../StartupOptions/CommandLineReportForm.cs | 1 - OnTopReplica/StartupOptions/Factory.cs | 11 +- OnTopReplica/StartupOptions/Options.cs | 2 +- OnTopReplica/StoredRegion.cs | 49 +- OnTopReplica/StoredRegionArray.cs | 130 +- OnTopReplica/Strings.resx | 1183 +++++++++-------- OnTopReplica/ThumbnailPanel.cs | 155 ++- OnTopReplica/ThumbnailRegion.cs | 222 ++++ OnTopReplica/Win32Helper.cs | 8 +- OnTopReplica/WindowHandle.cs | 7 +- OnTopReplica/WindowListMenuManager.cs | 10 +- 28 files changed, 1333 insertions(+), 777 deletions(-) create mode 100644 OnTopReplica/ThumbnailRegion.cs diff --git a/OnTopReplica/AspectRatioForm.cs b/OnTopReplica/AspectRatioForm.cs index 8d32192..4fe4f68 100644 --- a/OnTopReplica/AspectRatioForm.cs +++ b/OnTopReplica/AspectRatioForm.cs @@ -111,6 +111,10 @@ namespace OnTopReplica { /// True if the size of the form should be refreshed to match the new aspect ratio. public void SetAspectRatio(Size aspectRatioSource, bool forceRefresh) { AspectRatio = ((double)aspectRatioSource.Width / (double)aspectRatioSource.Height); + +#if DEBUG + System.Diagnostics.Trace.WriteLine(string.Format("Setting aspect ratio of {0} (for {1}).", AspectRatio, aspectRatioSource)); +#endif if (forceRefresh) { KeepAspectRatio = true; diff --git a/OnTopReplica/GeometryExtensions.cs b/OnTopReplica/GeometryExtensions.cs index d11a61a..5f61feb 100644 --- a/OnTopReplica/GeometryExtensions.cs +++ b/OnTopReplica/GeometryExtensions.cs @@ -50,6 +50,25 @@ namespace OnTopReplica { ctrl.MinimumSize = minimumClientSize.Expand(offset); } + /// + /// Attempts to fit a size structure to another fixed destination size, by maintaining + /// the original aspect ratio. + /// + public static Size Fit(this Size sourceSize, Size destinationSize) { + double sourceRatio = (double)sourceSize.Width / (double)sourceSize.Height; + double clientRatio = (double)destinationSize.Width / (double)destinationSize.Height; + + Size ret; + if (sourceRatio >= clientRatio) { + ret = new Size(destinationSize.Width, (int)((double)destinationSize.Width / sourceRatio)); + } + else { + ret = new Size((int)((double)destinationSize.Height * sourceRatio), destinationSize.Height); + } + + return ret; + } + } } diff --git a/OnTopReplica/MainForm.cs b/OnTopReplica/MainForm.cs index 9e8e1d3..4a2c9ce 100644 --- a/OnTopReplica/MainForm.cs +++ b/OnTopReplica/MainForm.cs @@ -229,11 +229,6 @@ namespace OnTopReplica { //ESCAPE else if (e.KeyCode == Keys.Escape) { - -#if DEBUG - Console.WriteLine("Received ESCAPE"); -#endif - //Disable click-through if (ClickThroughEnabled) { ClickThroughEnabled = false; @@ -307,27 +302,22 @@ namespace OnTopReplica { /// Sets a new thumbnail. /// /// Handle to the window to clone. - /// Region of the window to clone. - public void SetThumbnail(WindowHandle handle, Rectangle? region) { + /// Region of the window to clone or null. + public void SetThumbnail(WindowHandle handle, ThumbnailRegion region) { try { + System.Diagnostics.Trace.WriteLine(string.Format("Cloning window HWND {0} of class {1}.", handle.Handle, handle.Class)); + CurrentThumbnailWindowHandle = handle; - _thumbnailPanel.SetThumbnailHandle(handle); - -#if DEBUG - string windowClass = WindowMethods.GetWindowClass(handle.Handle); - Console.WriteLine("Cloning window HWND {0} of class {1}.", handle.Handle, windowClass); -#endif - - if (region.HasValue) - _thumbnailPanel.SelectedRegion = region.Value; - else - _thumbnailPanel.ConstrainToRegion = false; + _thumbnailPanel.SetThumbnailHandle(handle, region); //Set aspect ratio (this will resize the form), do not refresh if in fullscreen - SetAspectRatio(_thumbnailPanel.ThumbnailOriginalSize, !IsFullscreen); + SetAspectRatio(_thumbnailPanel.ThumbnailPixelSize, !IsFullscreen); } catch (Exception ex) { + System.Diagnostics.Trace.Fail("Unable to set thumbnail.", ex.ToString()); + ThumbnailError(ex, false, Strings.ErrorUnableToCreateThumbnail); + _thumbnailPanel.UnsetThumbnail(); } } @@ -365,7 +355,7 @@ namespace OnTopReplica { /// /// Gets or sets the region displayed of the current thumbnail. /// - public Rectangle? SelectedThumbnailRegion { + public ThumbnailRegion SelectedThumbnailRegion { get { if (!_thumbnailPanel.IsShowingThumbnail || !_thumbnailPanel.ConstrainToRegion) return null; @@ -376,14 +366,9 @@ namespace OnTopReplica { if (!_thumbnailPanel.IsShowingThumbnail) return; - if (value.HasValue) { - _thumbnailPanel.SelectedRegion = value.Value; - SetAspectRatio(value.Value.Size, true); - } - else { - _thumbnailPanel.ConstrainToRegion = false; - SetAspectRatio(_thumbnailPanel.ThumbnailOriginalSize, true); - } + _thumbnailPanel.SelectedRegion = value; + + SetAspectRatio(_thumbnailPanel.ThumbnailPixelSize, true); FixPositionAndSize(); } @@ -423,7 +408,7 @@ namespace OnTopReplica { /// Scale of the thumbnail to consider. private void FitToThumbnail(double p) { try { - Size originalSize = _thumbnailPanel.ThumbnailOriginalSize; + Size originalSize = _thumbnailPanel.ThumbnailPixelSize; Size fittedSize = new Size((int)(originalSize.Width * p), (int)(originalSize.Height * p)); ClientSize = fittedSize; } diff --git a/OnTopReplica/MessagePumpManager.cs b/OnTopReplica/MessagePumpManager.cs index 20993d7..ad2b48d 100644 --- a/OnTopReplica/MessagePumpManager.cs +++ b/OnTopReplica/MessagePumpManager.cs @@ -15,9 +15,7 @@ namespace OnTopReplica { _processors[processor.GetType()] = processor; processor.Initialize(form); -#if DEBUG - Console.WriteLine("Registered message pump processor: {0}", processor.GetType()); -#endif + System.Diagnostics.Trace.WriteLine(string.Format("Registered message pump processor: {0}", processor.GetType())); } /// @@ -32,18 +30,13 @@ namespace OnTopReplica { Console.Error.WriteLine("Failed to register shell hook window."); } else { -#if DEBUG - Console.WriteLine("Shell hook window registered successfully."); -#endif + System.Diagnostics.Trace.WriteLine("Shell hook window registered successfully."); } //Register message pump processors Register(new WindowKeeper(), form); Register(new HotKeyManager(), form); Register(new GroupSwitchManager(), form); -#if DEBUG - //Register(new ShellInterceptProcessor(), form); -#endif } /// @@ -75,9 +68,7 @@ namespace OnTopReplica { Console.Error.WriteLine("Failed to deregister shell hook window."); } else { -#if DEBUG - Console.WriteLine("Deregistered shell hook window successfully."); -#endif + System.Diagnostics.Trace.WriteLine("Deregistered shell hook window successfully."); } foreach (var processor in _processors.Values) { diff --git a/OnTopReplica/MessagePumpProcessors/GroupSwitchManager.cs b/OnTopReplica/MessagePumpProcessors/GroupSwitchManager.cs index 2bfd3e9..e665ef9 100644 --- a/OnTopReplica/MessagePumpProcessors/GroupSwitchManager.cs +++ b/OnTopReplica/MessagePumpProcessors/GroupSwitchManager.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices; -using System.Diagnostics; using System.Reflection; using System.IO; using System.Windows.Forms; @@ -72,9 +71,7 @@ namespace OnTopReplica.MessagePumpProcessors { } private void HandleForegroundWindowChange(IntPtr activeWindow) { -#if DEBUG - Console.Write("New active window (h {0}). ", activeWindow); -#endif + System.Diagnostics.Trace.WriteLine(string.Format("New active window (h {0}). ", activeWindow)); //Seek window in tracked handles WindowHandleWrapper activated = null; @@ -84,10 +81,8 @@ namespace OnTopReplica.MessagePumpProcessors { } if (activated == null) { -#if DEBUG - //new foreground window is not tracked - Console.WriteLine("Active window is not tracked."); -#endif + //New foreground window is not tracked + System.Diagnostics.Trace.WriteLine("Active window is not tracked."); return; } @@ -98,9 +93,7 @@ namespace OnTopReplica.MessagePumpProcessors { //Get least recently used var next = _lruHandles[0]; -#if DEBUG - Console.WriteLine("Tracked. Switching to {0} (last use: {1}).", next.WindowHandle.Title, next.LastTimeUsed); -#endif + System.Diagnostics.Trace.WriteLine(string.Format("Tracked. Switching to {0} (last use: {1}).", next.WindowHandle.Title, next.LastTimeUsed)); Form.SetThumbnail(next.WindowHandle, null); } diff --git a/OnTopReplica/MessagePumpProcessors/ShellInterceptProcessor.cs b/OnTopReplica/MessagePumpProcessors/ShellInterceptProcessor.cs index dc4f899..ca95066 100644 --- a/OnTopReplica/MessagePumpProcessors/ShellInterceptProcessor.cs +++ b/OnTopReplica/MessagePumpProcessors/ShellInterceptProcessor.cs @@ -17,7 +17,7 @@ namespace OnTopReplica.MessagePumpProcessors { if (msg.Msg == HookMethods.WM_SHELLHOOKMESSAGE) { int hookCode = msg.WParam.ToInt32(); - Console.WriteLine("Hook msg #{0}: {1}", hookCode, msg.LParam); + System.Diagnostics.Trace.WriteLine(string.Format("Hook msg #{0}: {1}", hookCode, msg.LParam)); } return false; diff --git a/OnTopReplica/OnTopReplica.csproj b/OnTopReplica/OnTopReplica.csproj index 86f742f..851ded4 100644 --- a/OnTopReplica/OnTopReplica.csproj +++ b/OnTopReplica/OnTopReplica.csproj @@ -72,7 +72,8 @@ pdbonly true bin\Release\ - TRACE + + prompt 4 @@ -91,11 +92,10 @@ - - + ..\Lib\WindowsFormsAero.dll @@ -184,6 +184,7 @@ True Strings.resx + diff --git a/OnTopReplica/OnTopReplica.exe.manifest b/OnTopReplica/OnTopReplica.exe.manifest index b9febc2..0928f9f 100644 --- a/OnTopReplica/OnTopReplica.exe.manifest +++ b/OnTopReplica/OnTopReplica.exe.manifest @@ -9,8 +9,12 @@ + + + + Lightweight clone of a window. diff --git a/OnTopReplica/PluginRegionLocator.cs b/OnTopReplica/PluginRegionLocator.cs index ad0b419..eaebb70 100644 --- a/OnTopReplica/PluginRegionLocator.cs +++ b/OnTopReplica/PluginRegionLocator.cs @@ -12,10 +12,18 @@ namespace OnTopReplica { static PluginRegionLocator() { _pluginClassNames = new HashSet() { - "aPluginWinClass", //Opera 11 Flash plugin - "MacromediaFlashPlayerActiveX", //IE 9 Flash plugin - "NativeWindowClass", //Google Chrome Flash plugin - "GeckoPluginWindow", //Firefox 9 Flash plugin + //Opera 11 Flash plugin + "aPluginWinClass", + + //IE 9 Flash plugin + "MacromediaFlashPlayerActiveX", + + //Google Chrome + "NativeWindowClass", //Flash plugin + "Chrome_RenderWidgetHostHWND", //Tab content + + //Firefox 9 Flash plugin + "GeckoPluginWindow", }; } @@ -79,10 +87,7 @@ namespace OnTopReplica { //Class name check string cl = WindowMethods.GetWindowClass(handle); - -#if DEBUG - Console.Out.WriteLine("Child window, class {0}", cl); -#endif + System.Diagnostics.Trace.WriteLine(string.Format("Child window, class {0}", cl)); if (_pluginClassNames.Contains(cl)) { //Found plugin window, stop now diff --git a/OnTopReplica/ScreenPosition.cs b/OnTopReplica/ScreenPosition.cs index 6808b9d..0a99d6c 100644 --- a/OnTopReplica/ScreenPosition.cs +++ b/OnTopReplica/ScreenPosition.cs @@ -76,9 +76,7 @@ namespace OnTopReplica { var move = end.Difference(start); -#if DEBUG - Console.WriteLine("From {0} to {1} => {2}.", start, end, move); -#endif + System.Diagnostics.Trace.WriteLine(string.Format("From {0} to {1} => {2}.", start, end, move)); var original = form.Location; form.Location = new Point(original.X + move.X, original.Y + move.Y); diff --git a/OnTopReplica/SidePanelContainer.cs b/OnTopReplica/SidePanelContainer.cs index f124b24..9a195a6 100644 --- a/OnTopReplica/SidePanelContainer.cs +++ b/OnTopReplica/SidePanelContainer.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.ComponentModel; -using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; diff --git a/OnTopReplica/SidePanels/AboutPanel.cs b/OnTopReplica/SidePanels/AboutPanel.cs index 949d2bc..87da71a 100644 --- a/OnTopReplica/SidePanels/AboutPanel.cs +++ b/OnTopReplica/SidePanels/AboutPanel.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.ComponentModel; using System.Drawing; -using System.Data; using System.Text; using System.Windows.Forms; using WindowsFormsAero.Dwm; diff --git a/OnTopReplica/SidePanels/AboutPanelContents.cs b/OnTopReplica/SidePanels/AboutPanelContents.cs index b6a6bad..84738b2 100644 --- a/OnTopReplica/SidePanels/AboutPanelContents.cs +++ b/OnTopReplica/SidePanels/AboutPanelContents.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.ComponentModel; using System.Drawing; -using System.Data; using System.Text; using System.Windows.Forms; using OnTopReplica.Update; diff --git a/OnTopReplica/SidePanels/OptionsPanel.cs b/OnTopReplica/SidePanels/OptionsPanel.cs index 1669c37..d09cdaa 100644 --- a/OnTopReplica/SidePanels/OptionsPanel.cs +++ b/OnTopReplica/SidePanels/OptionsPanel.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.ComponentModel; using System.Drawing; -using System.Data; using System.Text; using System.Windows.Forms; using OnTopReplica.Properties; diff --git a/OnTopReplica/SidePanels/RegionPanel.Designer.cs b/OnTopReplica/SidePanels/RegionPanel.Designer.cs index 616d3fb..f716e1c 100644 --- a/OnTopReplica/SidePanels/RegionPanel.Designer.cs +++ b/OnTopReplica/SidePanels/RegionPanel.Designer.cs @@ -25,6 +25,7 @@ private void InitializeComponent() { this.components = new System.ComponentModel.Container(); this.groupRegions = new System.Windows.Forms.GroupBox(); + this.checkRelative = new System.Windows.Forms.CheckBox(); this.textRegionName = new OnTopReplica.FocusedTextBox(); this.numH = new System.Windows.Forms.NumericUpDown(); this.numW = new System.Windows.Forms.NumericUpDown(); @@ -50,6 +51,7 @@ // // groupRegions // + this.groupRegions.Controls.Add(this.checkRelative); this.groupRegions.Controls.Add(this.textRegionName); this.groupRegions.Controls.Add(this.numH); this.groupRegions.Controls.Add(this.numW); @@ -68,11 +70,25 @@ this.groupRegions.Dock = System.Windows.Forms.DockStyle.Fill; this.groupRegions.Location = new System.Drawing.Point(6, 6); this.groupRegions.Name = "groupRegions"; - this.groupRegions.Size = new System.Drawing.Size(218, 158); + this.groupRegions.Size = new System.Drawing.Size(218, 180); this.groupRegions.TabIndex = 0; this.groupRegions.TabStop = false; this.groupRegions.Text = "Regions:"; // + // checkRelative + // + this.checkRelative.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.checkRelative.CheckAlign = System.Drawing.ContentAlignment.MiddleRight; + this.checkRelative.Location = new System.Drawing.Point(6, 119); + this.checkRelative.Name = "checkRelative"; + this.checkRelative.Size = new System.Drawing.Size(206, 18); + this.checkRelative.TabIndex = 12; + this.checkRelative.Text = "Relative to border"; + this.checkRelative.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + this.checkRelative.UseVisualStyleBackColor = true; + this.checkRelative.CheckedChanged += new System.EventHandler(this.CheckRelative_checked); + // // textRegionName // this.textRegionName.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) @@ -89,8 +105,9 @@ // // numH // + this.numH.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.numH.Enabled = false; - this.numH.Location = new System.Drawing.Point(150, 93); + this.numH.Location = new System.Drawing.Point(169, 93); this.numH.Maximum = new decimal(new int[] { 100000, 0, @@ -108,8 +125,9 @@ // // numW // + this.numW.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.numW.Enabled = false; - this.numW.Location = new System.Drawing.Point(150, 67); + this.numW.Location = new System.Drawing.Point(169, 67); this.numW.Maximum = new decimal(new int[] { 100000, 0, @@ -128,7 +146,7 @@ // numY // this.numY.Enabled = false; - this.numY.Location = new System.Drawing.Point(29, 93); + this.numY.Location = new System.Drawing.Point(55, 93); this.numY.Maximum = new decimal(new int[] { 100000, 0, @@ -147,7 +165,7 @@ // numX // this.numX.Enabled = false; - this.numX.Location = new System.Drawing.Point(29, 67); + this.numX.Location = new System.Drawing.Point(55, 67); this.numX.Maximum = new decimal(new int[] { 100000, 0, @@ -165,10 +183,10 @@ // // buttonDone // - this.buttonDone.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.buttonDone.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.buttonDone.Image = global::OnTopReplica.Properties.Resources.xiao_ok; this.buttonDone.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft; - this.buttonDone.Location = new System.Drawing.Point(142, 129); + this.buttonDone.Location = new System.Drawing.Point(142, 151); this.buttonDone.Name = "buttonDone"; this.buttonDone.Size = new System.Drawing.Size(70, 23); this.buttonDone.TabIndex = 9; @@ -179,8 +197,8 @@ // // buttonReset // - this.buttonReset.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.buttonReset.Location = new System.Drawing.Point(66, 129); + this.buttonReset.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.buttonReset.Location = new System.Drawing.Point(66, 151); this.buttonReset.Name = "buttonReset"; this.buttonReset.Size = new System.Drawing.Size(70, 23); this.buttonReset.TabIndex = 8; @@ -190,20 +208,22 @@ // // labelHeight // + this.labelHeight.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.labelHeight.ForeColor = System.Drawing.SystemColors.ControlDark; - this.labelHeight.Location = new System.Drawing.Point(81, 96); + this.labelHeight.Location = new System.Drawing.Point(104, 95); this.labelHeight.Name = "labelHeight"; - this.labelHeight.Size = new System.Drawing.Size(63, 17); + this.labelHeight.Size = new System.Drawing.Size(60, 18); this.labelHeight.TabIndex = 9; this.labelHeight.Text = "Height"; this.labelHeight.TextAlign = System.Drawing.ContentAlignment.TopRight; // // labelWidth // + this.labelWidth.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.labelWidth.ForeColor = System.Drawing.SystemColors.ControlDark; - this.labelWidth.Location = new System.Drawing.Point(81, 70); + this.labelWidth.Location = new System.Drawing.Point(107, 69); this.labelWidth.Name = "labelWidth"; - this.labelWidth.Size = new System.Drawing.Size(63, 17); + this.labelWidth.Size = new System.Drawing.Size(57, 18); this.labelWidth.TabIndex = 8; this.labelWidth.Text = "Width"; this.labelWidth.TextAlign = System.Drawing.ContentAlignment.TopRight; @@ -213,7 +233,7 @@ this.labelY.ForeColor = System.Drawing.SystemColors.ControlDark; this.labelY.Location = new System.Drawing.Point(6, 96); this.labelY.Name = "labelY"; - this.labelY.Size = new System.Drawing.Size(17, 17); + this.labelY.Size = new System.Drawing.Size(43, 17); this.labelY.TabIndex = 5; this.labelY.Text = "Y"; this.labelY.TextAlign = System.Drawing.ContentAlignment.TopRight; @@ -223,7 +243,7 @@ this.labelX.ForeColor = System.Drawing.SystemColors.ControlDark; this.labelX.Location = new System.Drawing.Point(6, 70); this.labelX.Name = "labelX"; - this.labelX.Size = new System.Drawing.Size(17, 17); + this.labelX.Size = new System.Drawing.Size(43, 17); this.labelX.TabIndex = 4; this.labelX.Text = "X"; this.labelX.TextAlign = System.Drawing.ContentAlignment.TopRight; @@ -259,7 +279,7 @@ this.buttonSave.Name = "buttonSave"; this.buttonSave.Size = new System.Drawing.Size(23, 23); this.buttonSave.TabIndex = 1; - this.buttonSave.UseVisualStyleBackColor = true; + this.buttonSave.UseVisualStyleBackColor = false; this.buttonSave.Click += new System.EventHandler(this.Save_click); // // comboRegions @@ -281,10 +301,10 @@ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.Controls.Add(this.groupRegions); - this.MinimumSize = new System.Drawing.Size(230, 170); + this.MinimumSize = new System.Drawing.Size(230, 185); this.Name = "RegionPanel"; this.Padding = new System.Windows.Forms.Padding(6); - this.Size = new System.Drawing.Size(230, 170); + this.Size = new System.Drawing.Size(230, 192); this.groupRegions.ResumeLayout(false); this.groupRegions.PerformLayout(); ((System.ComponentModel.ISupportInitialize)(this.numH)).EndInit(); @@ -314,5 +334,6 @@ private System.Windows.Forms.NumericUpDown numX; private FocusedTextBox textRegionName; private System.Windows.Forms.ToolTip toolTip; + private System.Windows.Forms.CheckBox checkRelative; } } diff --git a/OnTopReplica/SidePanels/RegionPanel.cs b/OnTopReplica/SidePanels/RegionPanel.cs index 29ce999..521cbda 100644 --- a/OnTopReplica/SidePanels/RegionPanel.cs +++ b/OnTopReplica/SidePanels/RegionPanel.cs @@ -22,18 +22,18 @@ namespace OnTopReplica.SidePanels { _regionDrawnHandler = new ThumbnailPanel.RegionDrawnHandler(ThumbnailPanel_RegionDrawn); } + /// + /// Localizes the dialog's labels. + /// private void Localize() { this.SuspendLayout(); groupRegions.Text = Strings.RegionsTitle; comboRegions.CueBannerText = Strings.RegionsStoredRegions; labelCurrentRegion.Text = Strings.RegionsCurrentRegion; - //labelX - //labelY - labelWidth.Text = Strings.RegionsWidth; - labelHeight.Text = Strings.RegionsHeight; buttonReset.Text = Strings.RegionsResetButton; buttonDone.Text = Strings.RegionsDoneButton; + UpdateRegionLabels(); toolTip.SetToolTip(buttonSave, Strings.RegionsSaveButton); toolTip.SetToolTip(buttonDelete, Strings.RegionsDeleteButton); @@ -41,6 +41,48 @@ namespace OnTopReplica.SidePanels { this.ResumeLayout(); } + /// + /// Updates the labels for the region value selectors and the relative mode checkbox. + /// + private void UpdateRegionControls(ThumbnailRegion region) { + checkRelative.Checked = region.Relative; + + if (region.Relative) { + Padding p = region.BoundsAsPadding; + numX.Value = p.Left; + numY.Value = p.Top; + numW.Value = p.Right; + numH.Value = p.Bottom; + } + else { + Rectangle r = region.Bounds; + numX.Value = r.X; + numY.Value = r.Y; + numW.Value = r.Width; + numH.Value = r.Height; + } + + UpdateRegionLabels(); + } + + /// + /// Updates the labels of region selectors based on the dialog's state. + /// + private void UpdateRegionLabels() { + if (checkRelative.Checked) { + labelX.Text = Strings.RegionsLeft; + labelY.Text = Strings.RegionsTop; + labelWidth.Text = Strings.RegionsRight; + labelHeight.Text = Strings.RegionsBottom; + } + else { + labelX.Text = Strings.RegionsX; + labelY.Text = Strings.RegionsY; + labelWidth.Text = Strings.RegionsWidth; + labelHeight.Text = Strings.RegionsHeight; + } + } + public override string Title { get { return Strings.MenuRegion; @@ -52,22 +94,25 @@ namespace OnTopReplica.SidePanels { public override void OnFirstShown(MainForm form) { base.OnFirstShown(form); - //Init shown region if needed - if (form.SelectedThumbnailRegion.HasValue) - SetRegion(form.SelectedThumbnailRegion.Value); + //Init shown region if current thumbnail is clipped to region + if (form.SelectedThumbnailRegion != null) { + SetRegion(form.SelectedThumbnailRegion); + } + //Enable region drawing form.ThumbnailPanel.DrawMouseRegions = true; form.ThumbnailPanel.RegionDrawn += _regionDrawnHandler; } public override void OnClosing(MainForm form) { base.OnClosing(form); - + + //Reset region drawing form.ThumbnailPanel.DrawMouseRegions = false; form.ThumbnailPanel.RegionDrawn -= _regionDrawnHandler; } - void ThumbnailPanel_RegionDrawn(object sender, Rectangle region) { + void ThumbnailPanel_RegionDrawn(object sender, ThumbnailRegion region) { SetRegion(region); } @@ -83,7 +128,7 @@ namespace OnTopReplica.SidePanels { return; } - SetRegion(region.Bounds); + SetRegion(region.Region); //Select right combobox if (comboRegions.Items.Contains(region)) { @@ -95,16 +140,13 @@ namespace OnTopReplica.SidePanels { /// Sets the current selected region to a specific region rectangle. /// /// The region boundaries. - public void SetRegion(Rectangle region) { + public void SetRegion(ThumbnailRegion region) { try { _ignoreValueChanges = true; - numX.Enabled = numY.Enabled = numW.Enabled = numH.Enabled = true; + UpdateRegionControls(region); - numX.Value = region.Left; - numY.Value = region.Top; - numW.Value = region.Width; - numH.Value = region.Height; + numX.Enabled = numY.Enabled = numW.Enabled = numH.Enabled = true; } finally { _ignoreValueChanges = false; @@ -122,6 +164,8 @@ namespace OnTopReplica.SidePanels { numX.Value = numY.Value = numW.Value = numH.Value = 0; numX.Enabled = numY.Enabled = numW.Enabled = numH.Enabled = false; + checkRelative.Checked = false; + UpdateRegionLabels(); buttonSave.Enabled = false; @@ -134,30 +178,48 @@ namespace OnTopReplica.SidePanels { #endregion + /// + /// Constructs a ThumbnailRegion from the dialog's current state. + /// + protected ThumbnailRegion ConstructCurrentRegion() { + Rectangle bounds = new Rectangle { + X = (int)numX.Value, + Y = (int)numY.Value, + Width = (int)numW.Value, + Height = (int)numH.Value + }; + + ThumbnailRegion newRegion = new ThumbnailRegion(bounds, checkRelative.Checked); + + return newRegion; + } + /// /// Adds a new stored region. /// /// Region bounds. /// Name of the region. - private void AddRegion(Rectangle rectangle, string regionName) { - var region = new StoredRegion(rectangle, regionName); + /// Whether the region is relative to the border. + private void StoreCurrentRegion(string regionName) { + StoredRegion storedRegion = new StoredRegion(this.ConstructCurrentRegion(), regionName); - int index = comboRegions.Items.Add(region); + int index = comboRegions.Items.Add(storedRegion); comboRegions.SelectedIndex = index; if (Settings.Default.SavedRegions == null) Settings.Default.SavedRegions = new StoredRegionArray(); - Settings.Default.SavedRegions.Add(region); + Settings.Default.SavedRegions.Add(storedRegion); } /// /// Internal event raised when a change occurs in the selected region. /// /// Region bounds. - protected virtual void OnRegionSet(Rectangle regionBounds) { + protected virtual void OnRegionSet(ThumbnailRegion region) { //Forward region to thumbnail - ParentForm.SelectedThumbnailRegion = regionBounds; + ParentForm.SelectedThumbnailRegion = region; + //Have region, allowed to save buttonSave.Enabled = true; } @@ -193,10 +255,7 @@ namespace OnTopReplica.SidePanels { private void Save_confirm(object sender, EventArgs e) { if (!string.IsNullOrEmpty(textRegionName.Text)) { - AddRegion( - new Rectangle((int)numX.Value, (int)numY.Value, (int)numW.Value, (int)numH.Value), - textRegionName.Text - ); + StoreCurrentRegion(textRegionName.Text); } //Hide textbox and show button again @@ -222,7 +281,7 @@ namespace OnTopReplica.SidePanels { if (_ignoreValueChanges) return; - OnRegionSet(new Rectangle((int)numX.Value, (int)numY.Value, (int)numW.Value, (int)numH.Value)); + OnRegionSet(ConstructCurrentRegion()); } private void RegionCombo_index_change(object sender, EventArgs e) { @@ -235,10 +294,26 @@ namespace OnTopReplica.SidePanels { if (region == null) return; - SetRegion(region.Bounds); + SetRegion(region.Region); } } + private void CheckRelative_checked(object sender, EventArgs e) { + if (_ignoreValueChanges) + return; + + //Get current region and switch mode + var region = ConstructCurrentRegion(); + region.Relative = !region.Relative; //this must be reversed because the GUI has already switched state when calling ConstructCurrentRegion() + if (checkRelative.Checked) + region.SwitchToRelative(ParentForm.ThumbnailPanel.ThumbnailOriginalSize); + else + region.SwitchToAbsolute(ParentForm.ThumbnailPanel.ThumbnailOriginalSize); + + //Update GUI + SetRegion(region); + } + #endregion } diff --git a/OnTopReplica/SidePanels/RegionPanel.resx b/OnTopReplica/SidePanels/RegionPanel.resx index a5979aa..026c576 100644 --- a/OnTopReplica/SidePanels/RegionPanel.resx +++ b/OnTopReplica/SidePanels/RegionPanel.resx @@ -112,12 +112,12 @@ 2.0 - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + 17, 17 \ No newline at end of file diff --git a/OnTopReplica/StartupOptions/CommandLineReportForm.cs b/OnTopReplica/StartupOptions/CommandLineReportForm.cs index 90eea63..e3315ca 100644 --- a/OnTopReplica/StartupOptions/CommandLineReportForm.cs +++ b/OnTopReplica/StartupOptions/CommandLineReportForm.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.ComponentModel; -using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; diff --git a/OnTopReplica/StartupOptions/Factory.cs b/OnTopReplica/StartupOptions/Factory.cs index 8602911..dbc0a5e 100644 --- a/OnTopReplica/StartupOptions/Factory.cs +++ b/OnTopReplica/StartupOptions/Factory.cs @@ -47,11 +47,9 @@ namespace OnTopReplica.StartupOptions { //Load window options.WindowId = resultHandle.Handle; } -#if DEBUG else { - Console.WriteLine("Couldn't find window to restore."); + System.Diagnostics.Trace.WriteLine("Couldn't find window to restore."); } -#endif } } @@ -80,8 +78,11 @@ namespace OnTopReplica.StartupOptions { options.StartLocation = null; options.StartScreenPosition = pos; }) - .Add("r|region=", "Region {BOUNDS} of the original window.", region => { - options.Region = region; + .Add("r|region=", "Region {BOUNDS} of the cloned window.", region => { + options.Region = new ThumbnailRegion(region); + }) + .Add("p|padding=", "Padding {BOUNDS} of the clone.", padding => { + options.Region = new ThumbnailRegion(padding); }) .Add("o|opacity=", "Opacity of the window (0-255).", opacity => { options.Opacity = opacity; diff --git a/OnTopReplica/StartupOptions/Options.cs b/OnTopReplica/StartupOptions/Options.cs index 1980a42..0e3cb94 100644 --- a/OnTopReplica/StartupOptions/Options.cs +++ b/OnTopReplica/StartupOptions/Options.cs @@ -39,7 +39,7 @@ namespace OnTopReplica.StartupOptions { public string WindowClass { get; set; } - public Rectangle? Region { get; set; } + public ThumbnailRegion Region { get; set; } public bool MustBeVisible { get; set; } diff --git a/OnTopReplica/StoredRegion.cs b/OnTopReplica/StoredRegion.cs index f8337a1..d2d6894 100644 --- a/OnTopReplica/StoredRegion.cs +++ b/OnTopReplica/StoredRegion.cs @@ -6,21 +6,17 @@ using System.Drawing; namespace OnTopReplica { - [Serializable] - public class StoredRegion : IXmlSerializable { + public class StoredRegion { - public StoredRegion() { - } + public StoredRegion(ThumbnailRegion r, string name) { + Region = r; + Name = name; + } - public StoredRegion(Rectangle r, string n) { - Bounds = r; - Name = n; - } - - public Rectangle Bounds { - get; - set; - } + public ThumbnailRegion Region { + get; + set; + } public string Name { get; @@ -31,33 +27,6 @@ namespace OnTopReplica { return Name; } - - #region IXmlSerializable Members - - public System.Xml.Schema.XmlSchema GetSchema() { - return null; - } - - public void ReadXml(System.Xml.XmlReader reader) { - if (reader.MoveToAttribute("name")) - Name = reader.Value; - else - throw new Exception(); - - reader.Read(); - - XmlSerializer x = new XmlSerializer(typeof(Rectangle)); - Bounds = (Rectangle)x.Deserialize(reader); - } - - public void WriteXml(System.Xml.XmlWriter writer) { - writer.WriteAttributeString("name", Name); - - XmlSerializer x = new XmlSerializer(typeof(Rectangle)); - x.Serialize(writer, Bounds); - } - - #endregion } } diff --git a/OnTopReplica/StoredRegionArray.cs b/OnTopReplica/StoredRegionArray.cs index 1a948c6..f140178 100644 --- a/OnTopReplica/StoredRegionArray.cs +++ b/OnTopReplica/StoredRegionArray.cs @@ -3,10 +3,18 @@ using System.Collections.Generic; using System.Text; using System.Collections; using System.Xml.Serialization; +using System.Xml; +using System.Xml.Linq; namespace OnTopReplica { - public class StoredRegionArray : ArrayList, IXmlSerializable { + /// + /// Strongly styped array of StoredRegion elements. + /// + /// + /// Handles XML serialization. + /// + public class StoredRegionArray : List, IXmlSerializable { #region IXmlSerializable Members @@ -16,22 +24,122 @@ namespace OnTopReplica { public void ReadXml(System.Xml.XmlReader reader) { this.Clear(); - XmlSerializer x = new XmlSerializer(typeof(StoredRegion)); - while (reader.ReadToFollowing("StoredRegion")) { - object o = x.Deserialize(reader); - if (o is StoredRegion) - this.Add(o); - } + var doc = XDocument.Load(reader); + foreach (var xmlRegion in doc.Descendants("StoredRegion")) { + System.Diagnostics.Debug.WriteLine(string.Format("Found region '{0}'.", xmlRegion.Attribute("name"))); + + StoredRegion parsedRegion = ParseStoredRegion(xmlRegion); + if (parsedRegion != null) { + this.Add(parsedRegion); + } + } } + private StoredRegion ParseStoredRegion(XElement xmlRegion) { + var xName = xmlRegion.Attribute("name"); + if (xName == null || string.IsNullOrWhiteSpace(xName.Value)) { + System.Diagnostics.Debug.Fail("Parsed stored region has no name attribute."); + return null; + } + + ThumbnailRegion region = ParseRegion(xmlRegion); + if (region == null) { + System.Diagnostics.Debug.Fail("Parsed stored region has no valid region."); + return null; + } + + return new StoredRegion(region, xName.Value); + } + + private ThumbnailRegion ParseRegion(XElement xmlRegion) { + var xRectangle = xmlRegion.Element("Rectangle"); + if (xRectangle != null) { + System.Drawing.Rectangle rectangle = ParseRectangle(xRectangle); + return new ThumbnailRegion(rectangle); + } + + var xPadding = xmlRegion.Element("Padding"); + if (xPadding != null) { + System.Windows.Forms.Padding padding = ParsePadding(xPadding); + return new ThumbnailRegion(padding); + } + + return null; + } + + private System.Windows.Forms.Padding ParsePadding(XElement xPadding) { + var p = new System.Windows.Forms.Padding(); + try { + p.Left = Int32.Parse(xPadding.Element("Left").Value); + p.Top = Int32.Parse(xPadding.Element("Top").Value); + p.Right = Int32.Parse(xPadding.Element("Right").Value); + p.Bottom = Int32.Parse(xPadding.Element("Bottom").Value); + } + catch (Exception ex) { + System.Diagnostics.Debug.Fail("Failure while parsing padding data.", ex.ToString()); + } + return p; + } + + private System.Drawing.Rectangle ParseRectangle(XElement xRectangle) { + var r = new System.Drawing.Rectangle(); + try { + r.X = Int32.Parse(xRectangle.Element("X").Value); + r.Y = Int32.Parse(xRectangle.Element("Y").Value); + r.Width = Int32.Parse(xRectangle.Element("Width").Value); + r.Height = Int32.Parse(xRectangle.Element("Height").Value); + } + catch (Exception ex) { + System.Diagnostics.Debug.Fail("Failure while parsing rectangle data.", ex.ToString()); + } + return r; + } + public void WriteXml(System.Xml.XmlWriter writer) { - XmlSerializer x = new XmlSerializer(typeof(StoredRegion)); - foreach (StoredRegion sr in this) { - x.Serialize(writer, sr); - } + foreach (var region in this) { + WriteRegion(writer, region); + } } + private void WriteRegion(XmlWriter writer, StoredRegion region) { + writer.WriteStartElement("StoredRegion"); + writer.WriteAttributeString("name", region.Name); + + if (region.Region.Relative) { + WriteRelativeRegion(writer, region); + } + else { + WriteAbsoluteRegion(writer, region); + } + + writer.WriteEndElement(); + } + + private void WriteAbsoluteRegion(XmlWriter writer, StoredRegion region) { + writer.WriteStartElement("Rectangle"); + + var bounds = region.Region.Bounds; + writer.WriteElementString("X", bounds.X.ToString()); + writer.WriteElementString("Y", bounds.Y.ToString()); + writer.WriteElementString("Width", bounds.Width.ToString()); + writer.WriteElementString("Height", bounds.Height.ToString()); + + writer.WriteEndElement(); + } + + private void WriteRelativeRegion(XmlWriter writer, StoredRegion region) { + writer.WriteStartElement("Padding"); + + var padding = region.Region.BoundsAsPadding; + writer.WriteElementString("Left", padding.Left.ToString()); + writer.WriteElementString("Top", padding.Top.ToString()); + writer.WriteElementString("Right", padding.Right.ToString()); + writer.WriteElementString("Bottom", padding.Bottom.ToString()); + + writer.WriteEndElement(); + } + #endregion } diff --git a/OnTopReplica/Strings.resx b/OnTopReplica/Strings.resx index 735ef67..d778409 100644 --- a/OnTopReplica/Strings.resx +++ b/OnTopReplica/Strings.resx @@ -1,550 +1,673 @@ - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Created by: %. - Link % is replaced by string AboutAuthorContent. - - - Lorenz Cuno Klopfenstein - Must not be localized. Leave blank in non-default languages. - - - Abort update process. - - - Show details about OnTopReplica. - - - Reset all OnTopReplica settings. - - - Update OnTopReplica now. - - - Care to contribute to the project? You are welcome to improve available translations, start a new one for your native language or to check out the % from CodePlex. - - - source code - - - OnTopReplica is based upon the WindowsFormsAero library and some other libraries and code sources. %. - Link % is replaced by string AboutCreditsSourcesContent. - - - Read the full credits - - - Contribute - - - Credits - - - License - - - Updates - - - The application is licensed under the % license, which meets the terms of the "open-source" definition specified by OSI. - - - Microsoft Reciprocal (MS-RL) - Can be left blank. - - - A lightweight, real-time, always on top thumbnail of a window of your choice. - - - About OnTopReplica - - - Translators: {0} - {0} translators (do not end with period) - - - Christian Olaechea M., Daniel Zeus.EX, Federico Lorenzo, Goran Brecelj, Jan Romanczyk, Marco Kraxner, Patrik (batupata), Raúl Morillo, René Mihula, Roberto Leiro, Rodrigo Lourenço, Thomas Amundsen, Eric Hoffmann. - Must not be localized. Leave blank in non-default languages. - - - Check now! - - - OnTopReplica automatically checks for updates at every start up. - - - Version {0} - - - OnTopReplica - Probably doesn't need localization. :) - - - Reset window completely? - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Created by: %. + Link % is replaced by string AboutAuthorContent. + + + Lorenz Cuno Klopfenstein + Must not be localized. Leave blank in non-default languages. + + + Abort update process. + + + Show details about OnTopReplica. + + + Reset all OnTopReplica settings. + + + Update OnTopReplica now. + + + Care to contribute to the project? You are welcome to improve available translations, start a new one for your native language or to check out the % from CodePlex. + + + source code + + + OnTopReplica is based upon the WindowsFormsAero library and some other libraries and code sources. %. + Link % is replaced by string AboutCreditsSourcesContent. + + + Read the full credits + + + Contribute + + + Credits + + + License + + + Updates + + + The application is licensed under the % license, which meets the terms of the "open-source" definition specified by OSI. + + + Microsoft Reciprocal (MS-RL) + Can be left blank. + + + A lightweight, real-time, always on top thumbnail of a window of your choice. + + + About OnTopReplica + + + Translators: {0} + {0} translators (do not end with period) + + + Christian Olaechea M., Daniel Zeus.EX, Federico Lorenzo, Goran Brecelj, Jan Romanczyk, Marco Kraxner, Patrik (batupata), Raúl Morillo, René Mihula, Roberto Leiro, Rodrigo Lourenço, Thomas Amundsen, Eric Hoffmann. + Must not be localized. Leave blank in non-default languages. + + + Check now! + + + OnTopReplica automatically checks for updates at every start up. + + + Version {0} + + + OnTopReplica + Probably doesn't need localization. :) + + + Reset window completely? + + &Reset -All settings will be lost. - - - Can be used to reset all settings if you lost control over the window or moved it beyond the screen boundary. - - - Reset - - - Reset settings? - - - This will erase all settings of OnTopReplica, returning it to the original state right after installation (all saved information, like stored regions, will be lost). - - - Reset settings - - - &Cancel - & marks the ALT+[] shortcut - - - Draw regions using mouse. - - - Details - - - Details on Windows Aero - - - Windows Aero is only available on Windows Vista Home Premium or higher. - - - 'Desktop Composition' is not enabled. - - +All settings will be lost. + + + Can be used to reset all settings if you lost control over the window or moved it beyond the screen boundary. + + + Reset + + + Reset settings? + + + This will erase all settings of OnTopReplica, returning it to the original state right after installation (all saved information, like stored regions, will be lost). + + + Reset settings + + + &Cancel + & marks the ALT+[] shortcut + + + Draw regions using mouse. + + + Details + + + Details on Windows Aero + + + Windows Aero is only available on Windows Vista Home Premium or higher. + + + 'Desktop Composition' is not enabled. + + You must enable desktop composition, by selecting 'Windows Aero' as the theme used by Windows. -To do so, right-click on the desktop and click on Personalize. - - - Error details - - - Error: - - - It appears that the selected window has been closed or is not valid anymore. - - - Error - - +To do so, right-click on the desktop and click on Personalize. + + + Error details + + + Error: + + + It appears that the selected window has been closed or is not valid anymore. + + + Error + + Desktop Composition' is not supported on your Operating System. -You must run this application on Windows Vista Home Premium or better. - - - Desktop Composition unsupported - - - No thumbnail loaded. - - - Unable to create thumbnail - - - Unable to fit window. - - - Unable to check for updates. - - - It appears that OnTopReplica wasn't installed using 'ClickOnce'. You'll have to update manually (visit <a href="http://ontopreplica.codeplex.com">OnTopReplica's homepage</a>). - - - OnTopReplica was unable to check whether an updated version is available. Make sure you are connected to the Internet. If you are, the website may be temporarily down. - - - Mode - - - Always on top - - - Forces OnTopReplica to stay always on top. - - - Click through - - - OnTopReplica will behave like a transparent overlay that lets your clicks through to the windows below. - - - Standard - - - Behaves like an ordinary window. You can bring other windows above OnTopReplica. - - - OnTopReplica fullscreen - - - Disable - - - Enable Group mode - - - Select multiple windows to enable. - - - Group switch mode is enabled. - - - Group switch mode: - - - Windows - Column Header of list, simply refers to available windows to be cloned - - - Homepage: www.codeplex.com/ontopreplica. - - - Do you want to enable "click forwarding"? - - +You must run this application on Windows Vista Home Premium or better. + + + Desktop Composition unsupported + + + No thumbnail loaded. + + + Unable to create thumbnail + + + Unable to fit window. + + + Unable to check for updates. + + + It appears that OnTopReplica wasn't installed using 'ClickOnce'. You'll have to update manually (visit <a href="http://ontopreplica.codeplex.com">OnTopReplica's homepage</a>). + + + OnTopReplica was unable to check whether an updated version is available. Make sure you are connected to the Internet. If you are, the website may be temporarily down. + + + Mode + + + Always on top + + + Forces OnTopReplica to stay always on top. + + + Click through + + + OnTopReplica will behave like a transparent overlay that lets your clicks through to the windows below. + + + Standard + + + Behaves like an ordinary window. You can bring other windows above OnTopReplica. + + + OnTopReplica fullscreen + + + Disable + + + Enable Group mode + + + Select multiple windows to enable. + + + Group switch mode is enabled. + + + Group switch mode: + + + Windows + Column Header of list, simply refers to available windows to be cloned + + + Homepage: www.codeplex.com/ontopreplica. + + + Do you want to enable "click forwarding"? + + If this mode is enabled, OnTopReplica will forward all left mouse clicks to the window that is being cloned (this will allow you to do basic mouse operations on the cloned window without having to activate it). -To exit this mode, push ESC. - - - Click forwarding - - - Enable Click-Through mode? - - - Click-through only works if it has been enabled in the Resize submenu and if the window is semi-transparent. - - +To exit this mode, push ESC. + + + Click forwarding + + + Enable Click-Through mode? + + + Click-through only works if it has been enabled in the Resize submenu and if the window is semi-transparent. + + In this mode the fullscreen window will behave as a partially transparent overlay, allowing you to click on the other windows behind it. -To return to normal mode anytime, activate OnTopReplica by clicking on the task bar (or the tray icon). - - +To return to normal mode anytime, activate OnTopReplica by clicking on the task bar (or the tray icon). + + No, thank you. -You can enable click-through later - - - Use Click-Through - - - Click-Through mode - - - OnTopReplica has been updated. - - - In order to use the updated version of OnTopReplica you'll have to restart the application. - - - Update successful - - - OnTopReplica is up to date. - - - No update available - - - Language - - - About... - - - Hides the main window and displays the "about" box. - - - Advanced - - - Show window border - - - Toggles the display of the window border. - - - Enable Click forwarding - - - Enable "click forwarding" to the cloned window. - - - Enable Click-Through - - - OnTopReplica will behave like a transparent overlay that lets your clicks through to the windows below. - - - Close - - - Closes OnTopReplica. - - - 2:1 Double - - - Fullscreen - - - 1:2 Half - - - 1:1 Fit to window - - - 1:4 Quarter - - - Group Switch mode - - - OnTopReplica will automatically clone a window from an user defined group, switching to the one least recently activated to the foreground. - - - 100% (opaque) - - - Sets OnTopReplica to be completely opaque. - - - 25% - - - Sets OnTopReplica to 25% opacity. - - - 50% - - - Sets OnTopReplica to 50% opacity. - - - 75% - - - Sets OnTopReplica to 75% opacity. - - - Opacity - - - Open - - - Displays OnTopReplica. - - - Bottom Left - - - Bottom Right - - - Center - - - Disabled - - - Position lock - - - Automatically position OnTopReplica on the current screen. - - - Top Left - - - Top Right - - - Quit fullscreen mode - - - Recall last position and size - - - Toggles whether OnTopReplica should store its last position and size and use them when it is restarted. - - - Minimize - - - Minimizes OnTopReplica to the task bar (or the tray). - - - Select region... - - - Switches to "region mode", that allows you to select a limited region of the source's window as thumbnail. - - - Reset window - - - Resets OnTopReplica settings and its main window. - - - Resize - - - Restore last cloned window - - - When enabled, OnTopReplica will attempt to restore the last cloned window on start up. - - - Settings... - - - Displays the settings panel. - - - Switch to window - - - Switches to the source window and hides OnTopReplica. - - - Select window - - - - none - - - - Displays a list of window you can select as thumbnail source. - - - - whole - - - - Current region: - - - Delete - - - Done - - - Height - - - Reset - - - Save - - - Stored regions - - - Regions: - - - Width - - - Right-click here to start... - - - Clone current window - - - These system-wide shortcuts can also be used when OnTopReplica is not in focus. - - - Show/Hide - - - Hot keys: - - - Language: - - - Requires a restart. - - - Settings - - +You can enable click-through later + + + Use Click-Through + + + Click-Through mode + + + OnTopReplica has been updated. + + + In order to use the updated version of OnTopReplica you'll have to restart the application. + + + Update successful + + + OnTopReplica is up to date. + + + No update available + + + Language + + + About... + + + Hides the main window and displays the "about" box. + + + Advanced + + + Show window border + + + Toggles the display of the window border. + + + Enable Click forwarding + + + Enable "click forwarding" to the cloned window. + + + Enable Click-Through + + + OnTopReplica will behave like a transparent overlay that lets your clicks through to the windows below. + + + Close + + + Closes OnTopReplica. + + + 2:1 Double + + + Fullscreen + + + 1:2 Half + + + 1:1 Fit to window + + + 1:4 Quarter + + + Group Switch mode + + + OnTopReplica will automatically clone a window from an user defined group, switching to the one least recently activated to the foreground. + + + 100% (opaque) + + + Sets OnTopReplica to be completely opaque. + + + 25% + + + Sets OnTopReplica to 25% opacity. + + + 50% + + + Sets OnTopReplica to 50% opacity. + + + 75% + + + Sets OnTopReplica to 75% opacity. + + + Opacity + + + Open + + + Displays OnTopReplica. + + + Bottom Left + + + Bottom Right + + + Center + + + Disabled + + + Position lock + + + Automatically position OnTopReplica on the current screen. + + + Top Left + + + Top Right + + + Quit fullscreen mode + + + Recall last position and size + + + Toggles whether OnTopReplica should store its last position and size and use them when it is restarted. + + + Minimize + + + Minimizes OnTopReplica to the task bar (or the tray). + + + Select region... + + + Switches to "region mode", that allows you to select a limited region of the source's window as thumbnail. + + + Reset window + + + Resets OnTopReplica settings and its main window. + + + Resize + + + Restore last cloned window + + + When enabled, OnTopReplica will attempt to restore the last cloned window on start up. + + + Settings... + + + Displays the settings panel. + + + Switch to window + + + Switches to the source window and hides OnTopReplica. + + + Select window + + + - none - + + + Displays a list of window you can select as thumbnail source. + + + - whole - + + + Current region: + + + Delete + + + Done + + + Height + + + Reset + + + Save + + + Stored regions + + + Regions: + + + Width + + + Right-click here to start... + + + Clone current window + + + These system-wide shortcuts can also be used when OnTopReplica is not in focus. + + + Show/Hide + + + Hot keys: + + + Language: + + + Requires a restart. + + + Settings + + Cancel update -OnTopReplica will prompt you the next time it is started. - - +OnTopReplica will prompt you the next time it is started. + + Download -Install OnTopReplica {0}. - - - The new version can be downloaded and installed from the official website. - - +Install OnTopReplica {0}. + + + The new version can be downloaded and installed from the official website. + + Installed version: {0} -Available version: {1} - - - Version {0} available - - - {0}/{1} bytes downloaded. +Available version: {1} + + + Version {0} available + + + {0}/{1} bytes downloaded. {0} downloaded bytes {1} total bytes {0} bytes geladen von {1} bytes gesamt {0} downloaded bytes {1} total bytes {0} downloaded bytes {1} total bytes -{0} downloaded bytes {1} total bytes - - - Downloading... - - +{0} downloaded bytes {1} total bytes + + + Downloading... + + The latest version of OnTopReplica is already installed. The program automatically checks for updates at every start. -You can keep up to date about OnTopReplica's development, suggest improvements and new features by <a href="website">visiting the official website</a>. - The website link should be enclosed in <a href="website"></a> tags. - - - Latest stable version released on {0}. - {0} last version release date - - - OnTopReplica is up to date - - +You can keep up to date about OnTopReplica's development, suggest improvements and new features by <a href="website">visiting the official website</a>. + The website link should be enclosed in <a href="website"></a> tags. + + + Latest stable version released on {0}. + {0} last version release date + + + OnTopReplica is up to date + + Install -OnTopReplica will be closed and the update installed. - - - OnTopReplica version {0} is ready to be installed on your computer. - - - Update ready - - - OnTopReplica updates - +OnTopReplica will be closed and the update installed. + + + OnTopReplica version {0} is ready to be installed on your computer. + + + Update ready + + + OnTopReplica updates + + + Bottom + + + Left + + + Right + + + Top + + + X + + + Y + \ No newline at end of file diff --git a/OnTopReplica/ThumbnailPanel.cs b/OnTopReplica/ThumbnailPanel.cs index 10e04bc..bd0b81d 100644 --- a/OnTopReplica/ThumbnailPanel.cs +++ b/OnTopReplica/ThumbnailPanel.cs @@ -14,7 +14,6 @@ namespace OnTopReplica { //DWM Thumbnail stuff Thumbnail _thumbnail = null; - Rectangle _regionCurrent; //Labels ThemedLabel _labelGlass; @@ -42,16 +41,19 @@ namespace OnTopReplica { #region Properties and settings + ThumbnailRegion _currentRegion; + /// - /// Gets or sets the region that is currently shown on the thumbnail. When set, also enabled region constrain. + /// Gets or sets the region that is currently shown on the thumbnail. When set, also enables region constrain. /// - public Rectangle SelectedRegion { + public ThumbnailRegion SelectedRegion { get { - return _regionCurrent; + return _currentRegion; } set { - _regionCurrent = value; - ConstrainToRegion = true; + _currentRegion = value; + _regionEnabled = (value != null); + UpdateThubmnail(); } } @@ -65,8 +67,10 @@ namespace OnTopReplica { return _regionEnabled; } set { - _regionEnabled = value; - UpdateThubmnail(); + if (_regionEnabled != value) { + _regionEnabled = value; + UpdateThubmnail(); + } } } @@ -112,18 +116,50 @@ namespace OnTopReplica { } /// - /// Gets the thumbnail's original size. + /// Gets the thumbnail's size (in effectively thumbnailed pixels). /// + /// + /// This size varies if the thumbnail has been cropped to a region. + /// + public Size ThumbnailPixelSize { + get { + if (_thumbnail != null && !_thumbnail.IsInvalid) { + if (_regionEnabled) { + return _currentRegion.ComputeRegionSize(_thumbnail.SourceSize); + } + else { + //Thumbnail is not cropped, return full thumbnail source size + return _thumbnail.SourceSize; + } + } + else { +#if DEBUG + throw new InvalidOperationException(Strings.ErrorNoThumbnail); +#else + return Size.Empty; +#endif + } + } + } + + /// + /// Gets the thumbnailed window's original size. + /// + /// + /// This size is not influenced by the region cropping applied to the thumbnail. + /// public Size ThumbnailOriginalSize { get { if (_thumbnail != null && !_thumbnail.IsInvalid) { - if (_regionEnabled) - return _regionCurrent.Size; - return _thumbnail.SourceSize; } - else - throw new Exception(Strings.ErrorNoThumbnail); + else { +#if DEBUG + throw new InvalidOperationException(Strings.ErrorNoThumbnail); +#else + return Size.Empty; +#endif + } } } @@ -156,9 +192,14 @@ namespace OnTopReplica { /// Creates a new thumbnail of a certain window. /// /// Handle of the window to clone. - public void SetThumbnailHandle(WindowHandle handle) { - if (_thumbnail != null && !_thumbnail.IsInvalid) - _thumbnail.Close(); + /// Optional region. + public void SetThumbnailHandle(WindowHandle handle, ThumbnailRegion region) { + System.Diagnostics.Trace.WriteLine(string.Format("Setting thumbnail to handle {0}, with region {1}.", handle, region), "ThumbnailPanel"); + + if (_thumbnail != null && !_thumbnail.IsInvalid) { + _thumbnail.Close(); + _thumbnail = null; + } //Get form and register thumbnail on it Form owner = this.TopLevelControl as Form; @@ -167,16 +208,22 @@ namespace OnTopReplica { _labelGlass.Visible = false; + //Register new thumbnail, disable regioning directly and refresh _thumbnail = DwmManager.Register(owner, handle.Handle); - ConstrainToRegion = false; //this also invokes a thumbnail update + _currentRegion = region; + _regionEnabled = (region != null); + UpdateThubmnail(); } /// /// Disposes current thumbnail and enters stand-by mode. /// public void UnsetThumbnail() { - if (_thumbnail != null && !_thumbnail.IsInvalid) - _thumbnail.Close(); + System.Diagnostics.Trace.WriteLine("Unsetting thumbnail."); + + if (_thumbnail != null && !_thumbnail.IsInvalid) { + _thumbnail.Close(); + } _thumbnail = null; _labelGlass.Visible = true; @@ -201,14 +248,16 @@ namespace OnTopReplica { private void UpdateThubmnail() { if (_thumbnail != null && !_thumbnail.IsInvalid){ try { - Size sourceSize = ThumbnailOriginalSize; - _thumbnailSize = ComputeIdealSize(sourceSize, Size); - + //Get thumbnail size and attempt to fit to control, with padding + Size sourceSize = ThumbnailPixelSize; + _thumbnailSize = sourceSize.Fit(Size); _padWidth = (Size.Width - _thumbnailSize.Width) / 2; _padHeight = (Size.Height - _thumbnailSize.Height) / 2; + System.Diagnostics.Debug.WriteLine("Fitting {0} inside {1} as {2}. Padding {3},{4}.", sourceSize, Size, _thumbnailSize, _padWidth, _padHeight); + var target = new Rectangle(_padWidth, _padHeight, _thumbnailSize.Width, _thumbnailSize.Height); - Rectangle source = (_regionEnabled) ? _regionCurrent : new Rectangle(Point.Empty, _thumbnail.SourceSize); + Rectangle source = (_regionEnabled) ? _currentRegion.ComputeRegionRectangle(_thumbnail.SourceSize) : new Rectangle(Point.Empty, _thumbnail.SourceSize); _thumbnail.Update(target, source, ThumbnailOpacity, true, true); } @@ -220,30 +269,12 @@ namespace OnTopReplica { } } - /// - /// Computes ideal thumbnail size given an original size and a target to fit. - /// - /// Size of the original thumbnail. - /// Size of the client area to fit. - private Size ComputeIdealSize(Size sourceSize, Size clientSize) { - double sourceRatio = (double)sourceSize.Width / (double)sourceSize.Height; - double clientRatio = (double)clientSize.Width / (double)clientSize.Height; - - Size ret; - if (sourceRatio >= clientRatio) { - ret = new Size(clientSize.Width, (int)((double)clientSize.Width / sourceRatio)); - } - else { - ret = new Size((int)((double)clientSize.Height * sourceRatio), clientSize.Height); - } - - return ret; - } - #endregion #region Region drawing + const int MinimumRegionSize = 1; + //Set if currently drawing a window (first click/drag was initiated) bool _drawingRegion = false; //Set if drawing was suspended because the mouse left the control @@ -251,18 +282,20 @@ namespace OnTopReplica { Point _regionStartPoint; Point _regionLastPoint; - public delegate void RegionDrawnHandler(object sender, Rectangle region); + public delegate void RegionDrawnHandler(object sender, ThumbnailRegion region); public event RegionDrawnHandler RegionDrawn; protected virtual void OnRegionDrawn(Rectangle region) { //Fix region if necessary (bug report by Gunter, via comment) - if (region.Width < 1) region.Width = 1; - if (region.Height < 1) region.Height = 1; + if (region.Width < MinimumRegionSize) + region.Width = MinimumRegionSize; + if (region.Height < MinimumRegionSize) + region.Height = MinimumRegionSize; var evt = RegionDrawn; if (evt != null) - evt(this, region); + evt(this, new ThumbnailRegion(region)); } /// @@ -272,7 +305,7 @@ namespace OnTopReplica { if (_thumbnailSize.Width < 1 || _thumbnailSize.Height < 1) //causes DivBy0 return; - //Compute bounds + //Compute bounds and clip to boundaries int left = Math.Min(start.X, end.X); int right = Math.Max(start.X, end.X); int top = Math.Min(start.Y, end.Y); @@ -288,12 +321,15 @@ namespace OnTopReplica { var startPoint = ClientToThumbnail(new Point(left, top)); var endPoint = ClientToThumbnail(new Point(right, bottom)); var final = new Rectangle( - startPoint, - new Size(endPoint.X - startPoint.X, endPoint.Y - startPoint.Y) + startPoint.X, + startPoint.Y, + endPoint.X - startPoint.X, + endPoint.Y - startPoint.Y ); - //Update region - SelectedRegion = final; + System.Diagnostics.Trace.WriteLine(string.Format("Drawn from {0} to {1}, as region {2}.", start, end, final)); + + //Signal OnRegionDrawn(final); } @@ -356,7 +392,7 @@ namespace OnTopReplica { base.OnMouseMove(e); } - Pen penRed = new Pen(Color.FromArgb(255, Color.Red), 1.0f); + readonly static Pen RedPen = new Pen(Color.FromArgb(255, Color.Red), 1.5f); //TODO: check width protected override void OnPaint(PaintEventArgs e) { if (_drawingRegion) { @@ -366,12 +402,12 @@ namespace OnTopReplica { int top = Math.Min(_regionStartPoint.Y, _regionLastPoint.Y); int bottom = Math.Max(_regionStartPoint.Y, _regionLastPoint.Y); - e.Graphics.DrawRectangle(penRed, left, top, right - left, bottom - top); + e.Graphics.DrawRectangle(RedPen, left, top, right - left, bottom - top); } else if (DrawMouseRegions && ! _drawingSuspended) { //Show cursor coordinates - e.Graphics.DrawLine(penRed, new Point(0, _regionLastPoint.Y), new Point(ClientSize.Width, _regionLastPoint.Y)); - e.Graphics.DrawLine(penRed, new Point(_regionLastPoint.X, 0), new Point(_regionLastPoint.X, ClientSize.Height)); + e.Graphics.DrawLine(RedPen, new Point(0, _regionLastPoint.Y), new Point(ClientSize.Width, _regionLastPoint.Y)); + e.Graphics.DrawLine(RedPen, new Point(_regionLastPoint.X, 0), new Point(_regionLastPoint.X, ClientSize.Height)); } base.OnPaint(e); @@ -427,14 +463,15 @@ namespace OnTopReplica { position.X -= _padWidth; position.Y -= _padHeight; + //Determine position in fractional terms (on the size of the thumbnail control) PointF proportionalPosition = new PointF( (float)position.X / _thumbnailSize.Width, (float)position.Y / _thumbnailSize.Height ); //Get real pixel region info - Size source = (_regionEnabled) ? _regionCurrent.Size : _thumbnail.SourceSize; - Point offset = (_regionEnabled) ? _regionCurrent.Location : Point.Empty; + Size source = ThumbnailPixelSize; + Point offset = (_regionEnabled) ? SelectedRegion.Offset : Point.Empty; return new Point( (int)((proportionalPosition.X * source.Width) + offset.X), diff --git a/OnTopReplica/ThumbnailRegion.cs b/OnTopReplica/ThumbnailRegion.cs new file mode 100644 index 0000000..20cfcf9 --- /dev/null +++ b/OnTopReplica/ThumbnailRegion.cs @@ -0,0 +1,222 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Drawing; +using System.Windows.Forms; + +namespace OnTopReplica { + + /// + /// Represents a thumbnail region. + /// + /// + /// A ThumbnailRegion can work in absolute or in relative mode. + /// In absolute mode, the region is expressed in absolute pixel values, as expressed by the value of the + /// property. + /// In relative mode, the region is expressed in padding pixels from the borders of the source. Internally this + /// is still represented by the property. Properties of the Rectangle value are mapped as follows: + /// Rectangle.X = Padding.Left + /// Rectangle.Y = Padding.Top + /// Rectangle.Width = Padding.Right + /// Rectangle.Height = Padding.Bottom + /// + public class ThumbnailRegion { + + /// + /// Creates a ThumbnailRegion from a padding value relative to the thumbnail borders. + /// + public ThumbnailRegion(Padding padding) { + _bounds = new Rectangle { + X = padding.Left, + Y = padding.Top, + Width = padding.Right, + Height = padding.Bottom + }; + Relative = true; + } + + /// + /// Creates a ThumbnailRegion from a bounds rectangle (in absolute terms). + /// + public ThumbnailRegion(Rectangle rectangle) { + _bounds = rectangle; + Relative = false; + } + + /// + /// Creates a ThumbnailRegion from a rectangle, either expressing values in relative or in absolute terms. + /// + public ThumbnailRegion(Rectangle paddingOrBounds, bool relative) { + _bounds = paddingOrBounds; + Relative = relative; + } + + private Rectangle _bounds; + + /// + /// Gets or sets the bounds of the thumbnail region. + /// + public Rectangle Bounds { + get { +#if DEBUG + if (Relative) + throw new InvalidOperationException("Not allowed to use ThumbnailRegion Bounds as Rectangle value (in relative mode)."); +#endif + + return _bounds; + } + set { + _bounds = value; + Relative = false; + } + } + + /// + /// Gets or sets whether the bounds are expressed relative to the thumbnail borders. + /// + public bool Relative { + get; + set; + } + + /// + /// Sets the relative bounds of the region. Switches to relative mode. + /// + /// Padding in relative terms from the borders. + public void SetRelativeBounds(Padding padding) { + Bounds = new Rectangle { + X = padding.Left, + Y = padding.Top, + Width = padding.Right, + Height = padding.Bottom + }; + Relative = true; + } + + /// + /// Gets the bounds of the thumbnail region as relative padding from the thumbnail borders. + /// + /// Makes sense only in relative mode. + public Padding BoundsAsPadding { + get { +#if DEBUG + if (!Relative) + throw new InvalidOperationException("Not allowed to use ThumbnailRegion Bounds as Padding value (not in relative mode)."); +#endif + + return new Padding { + Left = _bounds.X, + Top = _bounds.Y, + Right = _bounds.Width, + Bottom = _bounds.Height + }; + } + } + + /// + /// Gets the offset of the region. + /// + /// + /// The offset is expressed as a point of displacement from the up-right corner (0,0) of the original source. + /// + public Point Offset { + get { + //This is equal in both absolute and relative mode + return _bounds.Location; + } + } + + const int MinimumRegionSize = 8; + + /// + /// Computes the effective region representing the bounds inside a source thumbnail of a certain size. + /// + /// Size of the full thumbnail source. + /// Bounds inside the thumbnail. + protected Rectangle ComputeRegion(Size sourceSize) { + Rectangle ret; + + //Compute + if (Relative) { + ret = new Rectangle { + X = _bounds.X, + Y = _bounds.Y, + Width = sourceSize.Width - _bounds.X - _bounds.Width, + Height = sourceSize.Height - _bounds.Y - _bounds.Height + }; + } + else { + ret = _bounds; + } + + //Constrain to bounds + if (ret.X + ret.Width > sourceSize.Width) + ret.Width = sourceSize.Width - ret.X; + if (ret.Y + ret.Height > sourceSize.Height) + ret.Height = sourceSize.Height - ret.Y; + + return ret; + } + + /// + /// Computes a rectangle representing the bounds of the region inside a source thumbnail of a certain size. + /// + /// Size of the full thumbnail source. + /// Bounds inside the thumbnail. + public Rectangle ComputeRegionRectangle(Size sourceSize) { + return ComputeRegion(sourceSize); + } + + /// + /// Computes a value representing the size of the region inside a source thumbnail of a certain size. + /// + /// Size of the full thumbnail source. + /// Size of the bounds inside the thumbnail. + public Size ComputeRegionSize(Size sourceSize) { + return ComputeRegion(sourceSize).Size; + } + + /// + /// Switches the region to relative mode, according to a source thumbnail of a given size. + /// + /// Size of the full thumbnail source. + public void SwitchToRelative(Size sourceSize) { + if (Relative) + return; + + var relativeBounds = new Padding { + Left = _bounds.X, + Top = _bounds.Y, + Right = sourceSize.Width - (_bounds.X + _bounds.Width), + Bottom = sourceSize.Height - (_bounds.Y + _bounds.Height) + }; + + this.SetRelativeBounds(relativeBounds); + } + + /// + /// Switches the region to absolute mode, according to a source thumbnail of a given size. + /// + /// Size of the full thumbnail source. + public void SwitchToAbsolute(Size sourceSize) { + if (!Relative) + return; + + var absoluteBounds = new Rectangle { + X = _bounds.X, + Y = _bounds.Y, + Width = (sourceSize.Width - _bounds.Width) - _bounds.X, + Height = (sourceSize.Height - _bounds.Height) - _bounds.Y + }; + + Bounds = absoluteBounds; + } + + public override string ToString() { + return string.Format("({0}, {1})", _bounds, (Relative) ? "relative" : "absolute"); + } + + } + +} diff --git a/OnTopReplica/Win32Helper.cs b/OnTopReplica/Win32Helper.cs index f14493d..618c69e 100644 --- a/OnTopReplica/Win32Helper.cs +++ b/OnTopReplica/Win32Helper.cs @@ -49,7 +49,7 @@ namespace OnTopReplica { MessagingMethods.PostMessage(child, WM.LBUTTONUP, new IntPtr(MK.LBUTTON), lParamClickLocation); #if DEBUG - Console.WriteLine("Left click on window #" + child.ToString() + " at " + clientLocation.ToString()); + System.Diagnostics.Debug.WriteLine("Left click on window #" + child.ToString() + " at " + clientLocation.ToString()); #endif } @@ -60,7 +60,7 @@ namespace OnTopReplica { MessagingMethods.PostMessage(child, WM.RBUTTONUP, new IntPtr(MK.RBUTTON), lParamClickLocation); #if DEBUG - Console.WriteLine("Right click on window #" + child.ToString() + " at " + clientLocation.ToString()); + System.Diagnostics.Debug.WriteLine("Right click on window #" + child.ToString() + " at " + clientLocation.ToString()); #endif } @@ -70,7 +70,7 @@ namespace OnTopReplica { MessagingMethods.PostMessage(child, WM.LBUTTONDBLCLK, new IntPtr(MK.LBUTTON), lParamClickLocation); #if DEBUG - Console.WriteLine("Double left click on window #" + child.ToString() + " at " + clientLocation.ToString()); + System.Diagnostics.Debug.WriteLine("Double left click on window #" + child.ToString() + " at " + clientLocation.ToString()); #endif } @@ -80,7 +80,7 @@ namespace OnTopReplica { MessagingMethods.PostMessage(child, WM.RBUTTONDBLCLK, new IntPtr(MK.RBUTTON), lParamClickLocation); #if DEBUG - Console.WriteLine("Double right click on window #" + child.ToString() + " at " + clientLocation.ToString()); + System.Diagnostics.Debug.WriteLine("Double right click on window #" + child.ToString() + " at " + clientLocation.ToString()); #endif } diff --git a/OnTopReplica/WindowHandle.cs b/OnTopReplica/WindowHandle.cs index 0dfff94..e2d4851 100644 --- a/OnTopReplica/WindowHandle.cs +++ b/OnTopReplica/WindowHandle.cs @@ -95,7 +95,12 @@ namespace OnTopReplica { #region Object override public override string ToString() { - return _title; + if (string.IsNullOrWhiteSpace(_title)) { + return string.Format("#{0}", _handle.ToInt64()); + } + else { + return string.Format("#{0} ({1})", _handle.ToInt64(), _title); + } } public override bool Equals(object obj) { diff --git a/OnTopReplica/WindowListMenuManager.cs b/OnTopReplica/WindowListMenuManager.cs index 7c4f3d3..a79fa74 100644 --- a/OnTopReplica/WindowListMenuManager.cs +++ b/OnTopReplica/WindowListMenuManager.cs @@ -96,10 +96,10 @@ namespace OnTopReplica { parent.DropDownItems.Add(nullRegionItem); //Video detector - var detectorItem = new ToolStripMenuItem("Autodetect plugin"); + /*var detectorItem = new ToolStripMenuItem("Autodetect plugin"); detectorItem.Tag = parentHandle; detectorItem.Click += MenuVideoCropperClickHandler; - parent.DropDownItems.Add(detectorItem); + parent.DropDownItems.Add(detectorItem);*/ //Regions (if any) if (regions == null || regions.Length == 0) @@ -135,10 +135,10 @@ namespace OnTopReplica { var tsi = (ToolStripMenuItem)sender; var tuple = (Tuple)tsi.Tag; _owner.SetThumbnail(tuple.Item1, - (tuple.Item2 != null) ? (System.Drawing.Rectangle?)tuple.Item2.Bounds : null); + (tuple.Item2 != null) ? (ThumbnailRegion)tuple.Item2.Region : null); } - PluginRegionLocator _pluginRegionLocator = null; + /*PluginRegionLocator _pluginRegionLocator = null; private void MenuVideoCropperClickHandler(object sender, EventArgs args){ CommonClickHandler(); @@ -151,7 +151,7 @@ namespace OnTopReplica { var detectedRegion = _pluginRegionLocator.LocatePluginRegion(handle); _owner.SetThumbnail(handle, detectedRegion); - } + }*/ private void CommonClickHandler() { _windowsMenu.Close();