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.
This commit is contained in:
Lorenz Cuno Klopfenstein 2012-11-14 14:50:22 +01:00
parent 726879dfdf
commit 6dfa6615d3
28 changed files with 1333 additions and 777 deletions

View file

@ -111,6 +111,10 @@ namespace OnTopReplica {
/// <param name="forceRefresh">True if the size of the form should be refreshed to match the new aspect ratio.</param>
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;

View file

@ -50,6 +50,25 @@ namespace OnTopReplica {
ctrl.MinimumSize = minimumClientSize.Expand(offset);
}
/// <summary>
/// Attempts to fit a size structure to another fixed destination size, by maintaining
/// the original aspect ratio.
/// </summary>
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;
}
}
}

View file

@ -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.
/// </summary>
/// <param name="handle">Handle to the window to clone.</param>
/// <param name="region">Region of the window to clone.</param>
public void SetThumbnail(WindowHandle handle, Rectangle? region) {
/// <param name="region">Region of the window to clone or null.</param>
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 {
/// <summary>
/// Gets or sets the region displayed of the current thumbnail.
/// </summary>
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 {
/// <param name="p">Scale of the thumbnail to consider.</param>
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;
}

View file

@ -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()));
}
/// <summary>
@ -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
}
/// <summary>
@ -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) {

View file

@ -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);
}

View file

@ -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;

View file

@ -72,7 +72,8 @@
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<DefineConstants>
</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>
@ -91,11 +92,10 @@
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Deployment" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
<Reference Include="System.Xml.Linq" />
<Reference Include="WindowsFormsAero">
<HintPath>..\Lib\WindowsFormsAero.dll</HintPath>
</Reference>
@ -184,6 +184,7 @@
<DesignTime>True</DesignTime>
<DependentUpon>Strings.resx</DependentUpon>
</Compile>
<Compile Include="ThumbnailRegion.cs" />
<Compile Include="WindowListMenuManager.cs" />
<Compile Include="WindowSeekers\BaseWindowSeeker.cs" />
<Compile Include="WindowSeekers\ByClassWindowSeeker.cs" />

View file

@ -9,8 +9,12 @@
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows Vista -->
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />
<!-- Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
<!-- Windows 8 -->
<!--<supportedOS ID="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />-->
</application>
</compatibility>
<description>Lightweight clone of a window.</description>

View file

@ -12,10 +12,18 @@ namespace OnTopReplica {
static PluginRegionLocator() {
_pluginClassNames = new HashSet<string>() {
"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

View file

@ -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);

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;
}
}

View file

@ -22,18 +22,18 @@ namespace OnTopReplica.SidePanels {
_regionDrawnHandler = new ThumbnailPanel.RegionDrawnHandler(ThumbnailPanel_RegionDrawn);
}
/// <summary>
/// Localizes the dialog's labels.
/// </summary>
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();
}
/// <summary>
/// Updates the labels for the region value selectors and the relative mode checkbox.
/// </summary>
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();
}
/// <summary>
/// Updates the labels of region selectors based on the dialog's state.
/// </summary>
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.
/// </summary>
/// <param name="region">The region boundaries.</param>
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
/// <summary>
/// Constructs a ThumbnailRegion from the dialog's current state.
/// </summary>
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;
}
/// <summary>
/// Adds a new stored region.
/// </summary>
/// <param name="rectangle">Region bounds.</param>
/// <param name="regionName">Name of the region.</param>
private void AddRegion(Rectangle rectangle, string regionName) {
var region = new StoredRegion(rectangle, regionName);
/// <param name="isRelative">Whether the region is relative to the border.</param>
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);
}
/// <summary>
/// Internal event raised when a change occurs in the selected region.
/// </summary>
/// <param name="regionBounds">Region bounds.</param>
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
}

View file

@ -112,12 +112,12 @@
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="toolTip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<metadata name="toolTip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
</root>

View file

@ -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;

View file

@ -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<Rectangle>("r|region=", "Region {BOUNDS} of the original window.", region => {
options.Region = region;
.Add<Rectangle>("r|region=", "Region {BOUNDS} of the cloned window.", region => {
options.Region = new ThumbnailRegion(region);
})
.Add<System.Windows.Forms.Padding>("p|padding=", "Padding {BOUNDS} of the clone.", padding => {
options.Region = new ThumbnailRegion(padding);
})
.Add<byte>("o|opacity=", "Opacity of the window (0-255).", opacity => {
options.Opacity = opacity;

View file

@ -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; }

View file

@ -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
}
}

View file

@ -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 {
/// <summary>
/// Strongly styped array of StoredRegion elements.
/// </summary>
/// <remarks>
/// Handles XML serialization.
/// </remarks>
public class StoredRegionArray : List<StoredRegion>, 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
}

File diff suppressed because it is too large Load diff

View file

@ -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;
/// <summary>
/// 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.
/// </summary>
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 {
}
/// <summary>
/// Gets the thumbnail's original size.
/// Gets the thumbnail's size (in effectively thumbnailed pixels).
/// </summary>
/// <remarks>
/// This size varies if the thumbnail has been cropped to a region.
/// </remarks>
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
}
}
}
/// <summary>
/// Gets the thumbnailed window's original size.
/// </summary>
/// <remarks>
/// This size is not influenced by the region cropping applied to the thumbnail.
/// </remarks>
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.
/// </summary>
/// <param name="handle">Handle of the window to clone.</param>
public void SetThumbnailHandle(WindowHandle handle) {
if (_thumbnail != null && !_thumbnail.IsInvalid)
_thumbnail.Close();
/// <param name="region">Optional region.</param>
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();
}
/// <summary>
/// Disposes current thumbnail and enters stand-by mode.
/// </summary>
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 {
}
}
/// <summary>
/// Computes ideal thumbnail size given an original size and a target to fit.
/// </summary>
/// <param name="sourceSize">Size of the original thumbnail.</param>
/// <param name="clientSize">Size of the client area to fit.</param>
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));
}
/// <summary>
@ -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),

View file

@ -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 {
/// <summary>
/// Represents a thumbnail region.
/// </summary>
/// <remarks>
/// 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
/// <see cref="Bounds"/> property.
/// In relative mode, the region is expressed in padding pixels from the borders of the source. Internally this
/// is still represented by the <see cref="Bounds"/> 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
/// </remarks>
public class ThumbnailRegion {
/// <summary>
/// Creates a ThumbnailRegion from a padding value relative to the thumbnail borders.
/// </summary>
public ThumbnailRegion(Padding padding) {
_bounds = new Rectangle {
X = padding.Left,
Y = padding.Top,
Width = padding.Right,
Height = padding.Bottom
};
Relative = true;
}
/// <summary>
/// Creates a ThumbnailRegion from a bounds rectangle (in absolute terms).
/// </summary>
public ThumbnailRegion(Rectangle rectangle) {
_bounds = rectangle;
Relative = false;
}
/// <summary>
/// Creates a ThumbnailRegion from a rectangle, either expressing values in relative or in absolute terms.
/// </summary>
public ThumbnailRegion(Rectangle paddingOrBounds, bool relative) {
_bounds = paddingOrBounds;
Relative = relative;
}
private Rectangle _bounds;
/// <summary>
/// Gets or sets the bounds of the thumbnail region.
/// </summary>
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;
}
}
/// <summary>
/// Gets or sets whether the bounds are expressed relative to the thumbnail borders.
/// </summary>
public bool Relative {
get;
set;
}
/// <summary>
/// Sets the relative bounds of the region. Switches to relative mode.
/// </summary>
/// <param name="padding">Padding in relative terms from the borders.</param>
public void SetRelativeBounds(Padding padding) {
Bounds = new Rectangle {
X = padding.Left,
Y = padding.Top,
Width = padding.Right,
Height = padding.Bottom
};
Relative = true;
}
/// <summary>
/// Gets the bounds of the thumbnail region as relative padding from the thumbnail borders.
/// </summary>
/// <remarks>Makes sense only in relative mode.</remarks>
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
};
}
}
/// <summary>
/// Gets the offset of the region.
/// </summary>
/// <remarks>
/// The offset is expressed as a point of displacement from the up-right corner (0,0) of the original source.
/// </remarks>
public Point Offset {
get {
//This is equal in both absolute and relative mode
return _bounds.Location;
}
}
const int MinimumRegionSize = 8;
/// <summary>
/// Computes the effective region representing the bounds inside a source thumbnail of a certain size.
/// </summary>
/// <param name="sourceSize">Size of the full thumbnail source.</param>
/// <returns>Bounds inside the thumbnail.</returns>
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;
}
/// <summary>
/// Computes a rectangle representing the bounds of the region inside a source thumbnail of a certain size.
/// </summary>
/// <param name="sourceSize">Size of the full thumbnail source.</param>
/// <returns>Bounds inside the thumbnail.</returns>
public Rectangle ComputeRegionRectangle(Size sourceSize) {
return ComputeRegion(sourceSize);
}
/// <summary>
/// Computes a value representing the size of the region inside a source thumbnail of a certain size.
/// </summary>
/// <param name="sourceSize">Size of the full thumbnail source.</param>
/// <returns>Size of the bounds inside the thumbnail.</returns>
public Size ComputeRegionSize(Size sourceSize) {
return ComputeRegion(sourceSize).Size;
}
/// <summary>
/// Switches the region to relative mode, according to a source thumbnail of a given size.
/// </summary>
/// <param name="sourceSize">Size of the full thumbnail source.</param>
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);
}
/// <summary>
/// Switches the region to absolute mode, according to a source thumbnail of a given size.
/// </summary>
/// <param name="sourceSize">Size of the full thumbnail source.</param>
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");
}
}
}

View file

@ -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
}

View file

@ -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) {

View file

@ -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<WindowHandle, StoredRegion>)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();