Window/Region selection: refactored menu creation.

This commit is contained in:
Lorenz Cuno Klopfenstein 2012-05-29 17:07:57 +02:00
parent 89f10cd894
commit 2ebd15fac2
10 changed files with 240 additions and 146 deletions

View file

@ -19,10 +19,8 @@ namespace OnTopReplica {
ThumbnailPanel _thumbnailPanel;
//Managers
BaseWindowSeeker _windowSeeker = new TaskWindowSeeker {
SkipNotVisibleWindows = true
};
MessagePumpManager _msgPumpManager = new MessagePumpManager();
WindowListMenuManager _windowListManager;
Options _startupOptions;
@ -63,9 +61,12 @@ namespace OnTopReplica {
GlassEnabled = true;
GlassMargins = new Margins(-1);
//Window handlers
_windowSeeker.OwnerHandle = this.Handle;
//Managers
_msgPumpManager.Initialize(this);
_windowListManager = new WindowListMenuManager(this, menuWindows);
_windowListManager.ParentMenus = new System.Windows.Forms.ContextMenuStrip[] {
menuContext, menuFullscreenContext
};
//Platform specific form initialization
Program.Platform.PostHandleFormInit(this);

View file

@ -26,14 +26,12 @@ namespace OnTopReplica {
chromeToolStripMenuItem.Enabled = showing;
clickThroughToolStripMenuItem.Enabled = showing;
clickForwardingToolStripMenuItem.Enabled = showing;
}
private void Menu_Windows_opening(object sender, CancelEventArgs e) {
_windowSeeker.Refresh();
var menu = (ToolStrip)sender;
menu.PopulateMenu(this, _windowSeeker,
CurrentThumbnailWindowHandle, new EventHandler(Menu_Windows_itemclick));
//_windowSeeker.Refresh();
//var menu = (ToolStrip)sender;
//menu.PopulateMenu(_windowSeeker, CurrentThumbnailWindowHandle, new EventHandler(Menu_Windows_itemclick));
}
void Menu_Windows_itemclick(object sender, EventArgs e) {

View file

@ -173,6 +173,8 @@
<DesignTime>True</DesignTime>
<DependentUpon>Strings.resx</DependentUpon>
</Compile>
<Compile Include="Tuples.cs" />
<Compile Include="WindowListMenuManager.cs" />
<Compile Include="WindowSeekers\BaseWindowSeeker.cs" />
<Compile Include="WindowSeekers\ByClassWindowSeeker.cs" />
<Compile Include="WindowSeekers\ByTitleWindowSeeker.cs" />

20
OnTopReplica/Tuples.cs Normal file
View file

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace OnTopReplica {
public class Tuple<T1, T2> {
public Tuple(T1 item1, T2 item2) {
Item1 = item1;
Item2 = item2;
}
public T1 Item1 { get; set; }
public T2 Item2 { get; set; }
}
}

View file

@ -8,7 +8,9 @@ using System.Windows.Forms;
namespace OnTopReplica {
public static class Win32Helper {
/// <summary>Inject a fake left mouse click on a target window, on a location expressed in client coordinates.</summary>
#region Injection
/// <summary>Inject a fake left mouse click on a target window, on a location expressed in client coordinates.</summary>
/// <param name="window">Target window to click on.</param>
/// <param name="clickLocation">Location of the mouse click expressed in client coordiantes of the target window.</param>
/// <param name="doubleClick">True if a double click should be injected.</param>
@ -82,7 +84,9 @@ namespace OnTopReplica {
#endif
}
/// <summary>Returns the child control of a window corresponding to a screen location.</summary>
#endregion
/// <summary>Returns the child control of a window corresponding to a screen location.</summary>
/// <param name="parent">Parent window to explore.</param>
/// <param name="scrClickLocation">Child control location in screen coordinates.</param>
private static IntPtr GetRealChildControlFromPoint(IntPtr parent, NPoint scrClickLocation) {
@ -115,7 +119,7 @@ namespace OnTopReplica {
if (handle == IntPtr.Zero)
return null;
return new WindowHandle(handle, null);
return new WindowHandle(handle);
}
}

View file

@ -24,6 +24,15 @@ namespace OnTopReplica {
_title = title;
}
/// <summary>
/// Creates a new WindowHandle instance. Additional features of the handle will be queried as needed.
/// </summary>
/// <param name="p"></param>
public WindowHandle(IntPtr p) {
_handle = p;
_title = null;
}
public string Title {
get {
if (_title == null) {

View file

@ -1,123 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using OnTopReplica.Properties;
using OnTopReplica.WindowSeekers;
namespace OnTopReplica {
/// <summary>
/// Extension methods used to apply a window list to a menu.
/// </summary>
static class WindowListHelper {
public class WindowSelectionData {
public WindowHandle Handle { get; set; }
public StoredRegion Region { get; set; }
}
const int MaxWindowTitleLength = 55;
/// <summary>
/// Populates the menu with a list of windows.
/// </summary>
/// <param name="menu">The menu to populate.</param>
/// <param name="ownerForm">The owning form.</param>
/// <param name="windowManager">The window manager that provides the windows list.</param>
/// <param name="currentHandle">The currently used window (will be checked in the list).</param>
/// <param name="clickHandler">Event handler for clicks on window items.</param>
public static void PopulateMenu(this ToolStrip menu, Form ownerForm, BaseWindowSeeker windowManager,
WindowHandle currentHandle, EventHandler clickHandler) {
var regions = GetRegions();
//Clear
menu.Items.Clear();
//"None" selection
var nullTsi = new ToolStripMenuItem(Strings.MenuWindowsNone);
nullTsi.Tag = null;
nullTsi.Click += clickHandler;
nullTsi.Checked = (currentHandle == null);
menu.Items.Add(nullTsi);
//Add an item for each window
foreach (WindowHandle h in windowManager.Windows) {
//Skip if in the same process
if (h.Handle.Equals(ownerForm.Handle))
continue;
var tsi = new ToolStripMenuItem();
//Window title
if (h.Title.Length > MaxWindowTitleLength) {
tsi.Text = h.Title.Substring(0, MaxWindowTitleLength) + "...";
tsi.ToolTipText = h.Title;
}
else
tsi.Text = h.Title;
//Icon
if (h.Icon != null) {
tsi.Image = h.Icon.ToBitmap();
}
//Check if this is the currently displayed window
tsi.Checked = h.Equals(currentHandle);
//Add direct click if no stored regions
tsi.Tag = new WindowSelectionData {
Handle = h,
Region = null
};
tsi.Click += clickHandler;
PopulateRegions(tsi, h, clickHandler, regions);
menu.Items.Add(tsi);
}
}
private static void PopulateRegions(ToolStripMenuItem tsi, WindowHandle handle,
EventHandler clickHandler, IEnumerable<StoredRegion> regions) {
if (regions != null) {
//Add subitem for no region
var nullRegionItem = new ToolStripMenuItem(Strings.MenuWindowsWholeRegion);
nullRegionItem.Tag = new WindowSelectionData {
Handle = handle,
Region = null
};
nullRegionItem.Image = Resources.regions;
nullRegionItem.Click += clickHandler;
tsi.DropDownItems.Add(nullRegionItem);
foreach (StoredRegion region in regions) {
var regionItem = new ToolStripMenuItem(region.Name);
regionItem.Tag = new WindowSelectionData {
Handle = handle,
Region = region
};
regionItem.Click += clickHandler;
tsi.DropDownItems.Add(regionItem);
}
}
}
private static IEnumerable<StoredRegion> GetRegions() {
if (Settings.Default.SavedRegions == null || Settings.Default.SavedRegions.Count == 0)
return null;
StoredRegion[] regions = new StoredRegion[Settings.Default.SavedRegions.Count];
Settings.Default.SavedRegions.CopyTo(regions);
Array.Sort<StoredRegion>(regions, new Comparison<StoredRegion>((a, b) => {
return a.Name.CompareTo(b.Name);
}));
return regions;
}
}
}

View file

@ -0,0 +1,182 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using OnTopReplica.WindowSeekers;
using OnTopReplica.Properties;
namespace OnTopReplica {
/// <summary>
/// Manages the window list displayed when allowing the user to select a window to clone.
/// </summary>
class WindowListMenuManager {
const int MaxWindowTitleLength = 55;
readonly MainForm _owner;
readonly ContextMenuStrip _windowsMenu;
public WindowListMenuManager(MainForm owner, ContextMenuStrip windowsMenu) {
_owner = owner;
_windowsMenu = windowsMenu;
WindowSeeker = new TaskWindowSeeker() {
OwnerHandle = owner.Handle,
SkipNotVisibleWindows = true
};
//Bind events
windowsMenu.Opening += new System.ComponentModel.CancelEventHandler(WindowsMenu_opening);
}
void WindowsMenu_opening(object sender, System.ComponentModel.CancelEventArgs e) {
WindowSeeker.Refresh();
PopulateMenu(_owner.CurrentThumbnailWindowHandle);
}
/// <summary>
/// Populates the menu with windows from the window seeker instance.
/// </summary>
/// <param name="currentSelection">Handle of the currently selected window or null if none selected.</param>
private void PopulateMenu(WindowHandle currentSelection) {
var regions = GetStoredRegions();
_windowsMenu.Items.Clear();
//"None" selection
var nullTsi = new ToolStripMenuItem(Strings.MenuWindowsNone);
nullTsi.Tag = null;
nullTsi.Click += MenuWindowClickHandler;
nullTsi.Checked = (currentSelection == null);
_windowsMenu.Items.Add(nullTsi);
//Add an item for each window
foreach (WindowHandle h in WindowSeeker.Windows) {
var tsi = new ToolStripMenuItem();
//Window title
if (h.Title.Length > MaxWindowTitleLength) {
tsi.Text = h.Title.Substring(0, MaxWindowTitleLength) + "...";
tsi.ToolTipText = h.Title;
}
else
tsi.Text = h.Title;
//Icon
if (h.Icon != null) {
tsi.Image = h.Icon.ToBitmap();
}
//Check if this is the currently displayed window
tsi.Checked = h.Equals(currentSelection);
//Click handler
tsi.Tag = h;
tsi.Click += MenuWindowClickHandler;
PopulateRegionsDropdown(tsi, h, regions);
_windowsMenu.Items.Add(tsi);
}
}
private void PopulateRegionsDropdown(ToolStripMenuItem parent, WindowHandle parentHandle, StoredRegion[] regions) {
parent.DropDownItems.Clear();
//No region
var nullRegionItem = new ToolStripMenuItem(Strings.MenuWindowsWholeRegion);
nullRegionItem.Tag = new Tuple<WindowHandle, StoredRegion>(parentHandle, null);
nullRegionItem.Image = Resources.regions;
nullRegionItem.Click += MenuRegionWindowClickHandler;
parent.DropDownItems.Add(nullRegionItem);
//Video detector
//Regions (if any)
if (regions == null || regions.Length == 0)
return;
parent.DropDownItems.Add(new ToolStripSeparator());
foreach (StoredRegion region in regions) {
var regionItem = new ToolStripMenuItem(region.Name);
regionItem.Tag = new Tuple<WindowHandle, StoredRegion>(parentHandle, region);
regionItem.Click += MenuRegionWindowClickHandler;
parent.DropDownItems.Add(regionItem);
}
}
private void MenuWindowClickHandler(object sender, EventArgs args) {
CommonClickHandler();
var tsi = (ToolStripMenuItem)sender;
if (tsi.Tag == null) {
_owner.UnsetThumbnail();
}
else {
var handle = (WindowHandle)tsi.Tag;
_owner.SetThumbnail(handle, null);
}
}
private void MenuRegionWindowClickHandler(object sender, EventArgs args) {
CommonClickHandler();
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);
}
private void MenuVideoCropperClickHandler(object sender, EventArgs args){
CommonClickHandler();
}
private void CommonClickHandler() {
_windowsMenu.Close();
foreach (ContextMenuStrip menu in _parentMenus)
menu.Close();
}
/// <summary>
/// Gets an array of stored regions.
/// </summary>
private StoredRegion[] GetStoredRegions() {
if (Settings.Default.SavedRegions == null || Settings.Default.SavedRegions.Count == 0)
return null;
StoredRegion[] ret = new StoredRegion[Settings.Default.SavedRegions.Count];
Settings.Default.SavedRegions.CopyTo(ret);
Array.Sort<StoredRegion>(ret, (a, b) => {
return a.Name.CompareTo(b.Name);
});
return ret;
}
/// <summary>
/// Gets or sets the window seeker instance used to list windows.
/// </summary>
public BaseWindowSeeker WindowSeeker { get; set; }
ContextMenuStrip[] _parentMenus = new ContextMenuStrip[0];
/// <summary>
/// Gets the parent menus which are bound to the context menu handled by this manager.
/// </summary>
public ContextMenuStrip[] ParentMenus {
get {
return (ContextMenuStrip[])_parentMenus.Clone();
}
set {
if(value == null)
_parentMenus = new ContextMenuStrip[0];
else
_parentMenus = (ContextMenuStrip[])value.Clone();
}
}
}
}

View file

@ -53,22 +53,25 @@ namespace OnTopReplica.WindowSeekers {
if (title.StartsWith(Title, StringComparison.InvariantCultureIgnoreCase)) {
points += 10;
}
if (title.Equals(Title, StringComparison.InvariantCultureIgnoreCase)) {
points += 5;
}
}
//Handle match (will probably not work, but anyhow)
if (Handle != IntPtr.Zero) {
if (Handle == hwnd) {
points += 5;
points += 10;
}
}
//Store handle if it matches
if (points > 0) {
_points.Add(hwnd.ToInt64(), points);
return true;
}
else
return false;
//Never store windows in base class list
return false;
}
public override IList<WindowHandle> Windows {

View file

@ -9,17 +9,15 @@ namespace OnTopReplica.WindowSeekers {
protected override bool InspectWindow(IntPtr hwnd, string title, ref bool terminate) {
//Code taken from: http://www.thescarms.com/VBasic/alttab.aspx
//Reject empty titles
if (string.IsNullOrEmpty(title))
return false;
//Accept windows that
// - are visible
// - do not have a parent
// - have no owner and are not Tool windows OR
// - have an owner and are App windows
//Reject empty titles
if (string.IsNullOrEmpty(title))
return false;
if ((long)WindowManagerMethods.GetParent(hwnd) == 0) {
bool hasOwner = (long)WindowManagerMethods.GetWindow(hwnd, WindowManagerMethods.GetWindowMode.GW_OWNER) != 0;
WindowMethods.WindowExStyles exStyle = (WindowMethods.WindowExStyles)WindowMethods.GetWindowLong(hwnd, WindowMethods.WindowLong.ExStyle);