You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
569 lines
20 KiB
C#
569 lines
20 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
using System.ComponentModel;
|
|
using System.Runtime.InteropServices;
|
|
using System.Windows.Forms;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
|
|
namespace Mesnac.Docking
|
|
{
|
|
internal interface IContentFocusManager
|
|
{
|
|
void Activate(IDockContent content);
|
|
void GiveUpFocus(IDockContent content);
|
|
void AddToList(IDockContent content);
|
|
void RemoveFromList(IDockContent content);
|
|
}
|
|
|
|
partial class DockPanel
|
|
{
|
|
private interface IFocusManager
|
|
{
|
|
void SuspendFocusTracking();
|
|
void ResumeFocusTracking();
|
|
bool IsFocusTrackingSuspended { get; }
|
|
IDockContent ActiveContent { get; }
|
|
DockPane ActivePane { get; }
|
|
IDockContent ActiveDocument { get; }
|
|
DockPane ActiveDocumentPane { get; }
|
|
}
|
|
|
|
private class FocusManagerImpl : Component, IContentFocusManager, IFocusManager
|
|
{
|
|
private class HookEventArgs : EventArgs
|
|
{
|
|
[SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
|
|
public int HookCode;
|
|
[SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
|
|
public IntPtr wParam;
|
|
public IntPtr lParam;
|
|
}
|
|
|
|
private class LocalWindowsHook : IDisposable
|
|
{
|
|
// Internal properties
|
|
private IntPtr m_hHook = IntPtr.Zero;
|
|
private NativeMethods.HookProc m_filterFunc = null;
|
|
private Win32.HookType m_hookType;
|
|
|
|
// Event delegate
|
|
public delegate void HookEventHandler(object sender, HookEventArgs e);
|
|
|
|
// Event: HookInvoked
|
|
public event HookEventHandler HookInvoked;
|
|
protected void OnHookInvoked(HookEventArgs e)
|
|
{
|
|
if (HookInvoked != null)
|
|
HookInvoked(this, e);
|
|
}
|
|
|
|
public LocalWindowsHook(Win32.HookType hook)
|
|
{
|
|
m_hookType = hook;
|
|
m_filterFunc = new NativeMethods.HookProc(this.CoreHookProc);
|
|
}
|
|
|
|
// Default filter function
|
|
public IntPtr CoreHookProc(int code, IntPtr wParam, IntPtr lParam)
|
|
{
|
|
if (code < 0)
|
|
return NativeMethods.CallNextHookEx(m_hHook, code, wParam, lParam);
|
|
|
|
// Let clients determine what to do
|
|
HookEventArgs e = new HookEventArgs();
|
|
e.HookCode = code;
|
|
e.wParam = wParam;
|
|
e.lParam = lParam;
|
|
OnHookInvoked(e);
|
|
|
|
// Yield to the next hook in the chain
|
|
return NativeMethods.CallNextHookEx(m_hHook, code, wParam, lParam);
|
|
}
|
|
|
|
// Install the hook
|
|
public void Install()
|
|
{
|
|
if (m_hHook != IntPtr.Zero)
|
|
Uninstall();
|
|
|
|
int threadId = NativeMethods.GetCurrentThreadId();
|
|
m_hHook = NativeMethods.SetWindowsHookEx(m_hookType, m_filterFunc, IntPtr.Zero, threadId);
|
|
}
|
|
|
|
// Uninstall the hook
|
|
public void Uninstall()
|
|
{
|
|
if (m_hHook != IntPtr.Zero)
|
|
{
|
|
NativeMethods.UnhookWindowsHookEx(m_hHook);
|
|
m_hHook = IntPtr.Zero;
|
|
}
|
|
}
|
|
|
|
~LocalWindowsHook()
|
|
{
|
|
Dispose(false);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
Dispose(true);
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
protected virtual void Dispose(bool disposing)
|
|
{
|
|
Uninstall();
|
|
}
|
|
}
|
|
|
|
private LocalWindowsHook m_localWindowsHook;
|
|
private LocalWindowsHook.HookEventHandler m_hookEventHandler;
|
|
|
|
public FocusManagerImpl(DockPanel dockPanel)
|
|
{
|
|
m_dockPanel = dockPanel;
|
|
m_localWindowsHook = new LocalWindowsHook(Win32.HookType.WH_CALLWNDPROCRET);
|
|
m_hookEventHandler = new LocalWindowsHook.HookEventHandler(HookEventHandler);
|
|
m_localWindowsHook.HookInvoked += m_hookEventHandler;
|
|
m_localWindowsHook.Install();
|
|
}
|
|
|
|
private DockPanel m_dockPanel;
|
|
public DockPanel DockPanel
|
|
{
|
|
get { return m_dockPanel; }
|
|
}
|
|
|
|
private bool m_disposed = false;
|
|
protected override void Dispose(bool disposing)
|
|
{
|
|
lock (this)
|
|
{
|
|
if (!m_disposed && disposing)
|
|
{
|
|
m_localWindowsHook.Dispose();
|
|
m_disposed = true;
|
|
}
|
|
|
|
base.Dispose(disposing);
|
|
}
|
|
}
|
|
|
|
private IDockContent m_contentActivating = null;
|
|
private IDockContent ContentActivating
|
|
{
|
|
get { return m_contentActivating; }
|
|
set { m_contentActivating = value; }
|
|
}
|
|
|
|
public void Activate(IDockContent content)
|
|
{
|
|
if (IsFocusTrackingSuspended)
|
|
{
|
|
ContentActivating = content;
|
|
return;
|
|
}
|
|
|
|
if (content == null)
|
|
return;
|
|
DockContentHandler handler = content.DockHandler;
|
|
if (handler.Form.IsDisposed)
|
|
return; // Should not reach here, but better than throwing an exception
|
|
if (ContentContains(content, handler.ActiveWindowHandle))
|
|
NativeMethods.SetFocus(handler.ActiveWindowHandle);
|
|
if (!handler.Form.ContainsFocus)
|
|
{
|
|
if (!handler.Form.SelectNextControl(handler.Form.ActiveControl, true, true, true, true))
|
|
// Since DockContent Form is not selectalbe, use Win32 SetFocus instead
|
|
NativeMethods.SetFocus(handler.Form.Handle);
|
|
}
|
|
}
|
|
|
|
private List<IDockContent> m_listContent = new List<IDockContent>();
|
|
private List<IDockContent> ListContent
|
|
{
|
|
get { return m_listContent; }
|
|
}
|
|
public void AddToList(IDockContent content)
|
|
{
|
|
if (ListContent.Contains(content) || IsInActiveList(content))
|
|
return;
|
|
|
|
ListContent.Add(content);
|
|
}
|
|
|
|
public void RemoveFromList(IDockContent content)
|
|
{
|
|
if (IsInActiveList(content))
|
|
RemoveFromActiveList(content);
|
|
if (ListContent.Contains(content))
|
|
ListContent.Remove(content);
|
|
}
|
|
|
|
private IDockContent m_lastActiveContent = null;
|
|
private IDockContent LastActiveContent
|
|
{
|
|
get { return m_lastActiveContent; }
|
|
set { m_lastActiveContent = value; }
|
|
}
|
|
|
|
private bool IsInActiveList(IDockContent content)
|
|
{
|
|
return !(content.DockHandler.NextActive == null && LastActiveContent != content);
|
|
}
|
|
|
|
private void AddLastToActiveList(IDockContent content)
|
|
{
|
|
IDockContent last = LastActiveContent;
|
|
if (last == content)
|
|
return;
|
|
|
|
DockContentHandler handler = content.DockHandler;
|
|
|
|
if (IsInActiveList(content))
|
|
RemoveFromActiveList(content);
|
|
|
|
handler.PreviousActive = last;
|
|
handler.NextActive = null;
|
|
LastActiveContent = content;
|
|
if (last != null)
|
|
last.DockHandler.NextActive = LastActiveContent;
|
|
}
|
|
|
|
private void RemoveFromActiveList(IDockContent content)
|
|
{
|
|
if (LastActiveContent == content)
|
|
LastActiveContent = content.DockHandler.PreviousActive;
|
|
|
|
IDockContent prev = content.DockHandler.PreviousActive;
|
|
IDockContent next = content.DockHandler.NextActive;
|
|
if (prev != null)
|
|
prev.DockHandler.NextActive = next;
|
|
if (next != null)
|
|
next.DockHandler.PreviousActive = prev;
|
|
|
|
content.DockHandler.PreviousActive = null;
|
|
content.DockHandler.NextActive = null;
|
|
}
|
|
|
|
public void GiveUpFocus(IDockContent content)
|
|
{
|
|
DockContentHandler handler = content.DockHandler;
|
|
if (!handler.Form.ContainsFocus)
|
|
return;
|
|
|
|
if (IsFocusTrackingSuspended)
|
|
DockPanel.DummyControl.Focus();
|
|
|
|
if (LastActiveContent == content)
|
|
{
|
|
IDockContent prev = handler.PreviousActive;
|
|
if (prev != null)
|
|
Activate(prev);
|
|
else if (ListContent.Count > 0)
|
|
Activate(ListContent[ListContent.Count - 1]);
|
|
}
|
|
else if (LastActiveContent != null)
|
|
Activate(LastActiveContent);
|
|
else if (ListContent.Count > 0)
|
|
Activate(ListContent[ListContent.Count - 1]);
|
|
}
|
|
|
|
private static bool ContentContains(IDockContent content, IntPtr hWnd)
|
|
{
|
|
Control control = Control.FromChildHandle(hWnd);
|
|
for (Control parent = control; parent != null; parent = parent.Parent)
|
|
if (parent == content.DockHandler.Form)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
private int m_countSuspendFocusTracking = 0;
|
|
public void SuspendFocusTracking()
|
|
{
|
|
m_countSuspendFocusTracking++;
|
|
m_localWindowsHook.HookInvoked -= m_hookEventHandler;
|
|
}
|
|
|
|
public void ResumeFocusTracking()
|
|
{
|
|
if (m_countSuspendFocusTracking > 0)
|
|
m_countSuspendFocusTracking--;
|
|
|
|
if (m_countSuspendFocusTracking == 0)
|
|
{
|
|
if (ContentActivating != null)
|
|
{
|
|
Activate(ContentActivating);
|
|
ContentActivating = null;
|
|
}
|
|
m_localWindowsHook.HookInvoked += m_hookEventHandler;
|
|
if (!InRefreshActiveWindow)
|
|
RefreshActiveWindow();
|
|
}
|
|
}
|
|
|
|
public bool IsFocusTrackingSuspended
|
|
{
|
|
get { return m_countSuspendFocusTracking != 0; }
|
|
}
|
|
|
|
// Windows hook event handler
|
|
private void HookEventHandler(object sender, HookEventArgs e)
|
|
{
|
|
Win32.Msgs msg = (Win32.Msgs)Marshal.ReadInt32(e.lParam, IntPtr.Size * 3);
|
|
|
|
if (msg == Win32.Msgs.WM_KILLFOCUS)
|
|
{
|
|
IntPtr wParam = Marshal.ReadIntPtr(e.lParam, IntPtr.Size * 2);
|
|
DockPane pane = GetPaneFromHandle(wParam);
|
|
if (pane == null)
|
|
RefreshActiveWindow();
|
|
}
|
|
else if (msg == Win32.Msgs.WM_SETFOCUS)
|
|
RefreshActiveWindow();
|
|
}
|
|
|
|
private DockPane GetPaneFromHandle(IntPtr hWnd)
|
|
{
|
|
Control control = Control.FromChildHandle(hWnd);
|
|
|
|
IDockContent content = null;
|
|
DockPane pane = null;
|
|
for (; control != null; control = control.Parent)
|
|
{
|
|
content = control as IDockContent;
|
|
if (content != null)
|
|
content.DockHandler.ActiveWindowHandle = hWnd;
|
|
|
|
if (content != null && content.DockHandler.DockPanel == DockPanel)
|
|
return content.DockHandler.Pane;
|
|
|
|
pane = control as DockPane;
|
|
if (pane != null && pane.DockPanel == DockPanel)
|
|
break;
|
|
}
|
|
|
|
return pane;
|
|
}
|
|
|
|
private bool m_inRefreshActiveWindow = false;
|
|
private bool InRefreshActiveWindow
|
|
{
|
|
get { return m_inRefreshActiveWindow; }
|
|
}
|
|
|
|
private void RefreshActiveWindow()
|
|
{
|
|
SuspendFocusTracking();
|
|
m_inRefreshActiveWindow = true;
|
|
|
|
DockPane oldActivePane = ActivePane;
|
|
IDockContent oldActiveContent = ActiveContent;
|
|
IDockContent oldActiveDocument = ActiveDocument;
|
|
|
|
SetActivePane();
|
|
SetActiveContent();
|
|
SetActiveDocumentPane();
|
|
SetActiveDocument();
|
|
DockPanel.AutoHideWindow.RefreshActivePane();
|
|
|
|
ResumeFocusTracking();
|
|
m_inRefreshActiveWindow = false;
|
|
|
|
if (oldActiveContent != ActiveContent)
|
|
DockPanel.OnActiveContentChanged(EventArgs.Empty);
|
|
if (oldActiveDocument != ActiveDocument)
|
|
DockPanel.OnActiveDocumentChanged(EventArgs.Empty);
|
|
if (oldActivePane != ActivePane)
|
|
DockPanel.OnActivePaneChanged(EventArgs.Empty);
|
|
}
|
|
|
|
private DockPane m_activePane = null;
|
|
public DockPane ActivePane
|
|
{
|
|
get { return m_activePane; }
|
|
}
|
|
|
|
private void SetActivePane()
|
|
{
|
|
DockPane value = GetPaneFromHandle(NativeMethods.GetFocus());
|
|
if (m_activePane == value)
|
|
return;
|
|
|
|
if (m_activePane != null)
|
|
m_activePane.SetIsActivated(false);
|
|
|
|
m_activePane = value;
|
|
|
|
if (m_activePane != null)
|
|
m_activePane.SetIsActivated(true);
|
|
}
|
|
|
|
private IDockContent m_activeContent = null;
|
|
public IDockContent ActiveContent
|
|
{
|
|
get { return m_activeContent; }
|
|
}
|
|
|
|
internal void SetActiveContent()
|
|
{
|
|
IDockContent value = ActivePane == null ? null : ActivePane.ActiveContent;
|
|
|
|
if (m_activeContent == value)
|
|
return;
|
|
|
|
if (m_activeContent != null)
|
|
m_activeContent.DockHandler.IsActivated = false;
|
|
|
|
m_activeContent = value;
|
|
|
|
if (m_activeContent != null)
|
|
{
|
|
m_activeContent.DockHandler.IsActivated = true;
|
|
if (!DockHelper.IsDockStateAutoHide((m_activeContent.DockHandler.DockState)))
|
|
AddLastToActiveList(m_activeContent);
|
|
}
|
|
}
|
|
|
|
private DockPane m_activeDocumentPane = null;
|
|
public DockPane ActiveDocumentPane
|
|
{
|
|
get { return m_activeDocumentPane; }
|
|
}
|
|
|
|
private void SetActiveDocumentPane()
|
|
{
|
|
DockPane value = null;
|
|
|
|
if (ActivePane != null && ActivePane.DockState == DockState.Document)
|
|
value = ActivePane;
|
|
|
|
if (value == null && DockPanel.DockWindows != null)
|
|
{
|
|
if (ActiveDocumentPane == null)
|
|
value = DockPanel.DockWindows[DockState.Document].DefaultPane;
|
|
else if (ActiveDocumentPane.DockPanel != DockPanel || ActiveDocumentPane.DockState != DockState.Document)
|
|
value = DockPanel.DockWindows[DockState.Document].DefaultPane;
|
|
else
|
|
value = ActiveDocumentPane;
|
|
}
|
|
|
|
if (m_activeDocumentPane == value)
|
|
return;
|
|
|
|
if (m_activeDocumentPane != null)
|
|
m_activeDocumentPane.SetIsActiveDocumentPane(false);
|
|
|
|
m_activeDocumentPane = value;
|
|
|
|
if (m_activeDocumentPane != null)
|
|
m_activeDocumentPane.SetIsActiveDocumentPane(true);
|
|
}
|
|
|
|
private IDockContent m_activeDocument = null;
|
|
public IDockContent ActiveDocument
|
|
{
|
|
get { return m_activeDocument; }
|
|
}
|
|
|
|
private void SetActiveDocument()
|
|
{
|
|
IDockContent value = ActiveDocumentPane == null ? null : ActiveDocumentPane.ActiveContent;
|
|
|
|
if (m_activeDocument == value)
|
|
return;
|
|
|
|
m_activeDocument = value;
|
|
}
|
|
}
|
|
|
|
private IFocusManager FocusManager
|
|
{
|
|
get { return m_focusManager; }
|
|
}
|
|
|
|
internal IContentFocusManager ContentFocusManager
|
|
{
|
|
get { return m_focusManager; }
|
|
}
|
|
|
|
internal void SaveFocus()
|
|
{
|
|
DummyControl.Focus();
|
|
}
|
|
|
|
[Browsable(false)]
|
|
public IDockContent ActiveContent
|
|
{
|
|
get { return FocusManager.ActiveContent; }
|
|
}
|
|
|
|
[Browsable(false)]
|
|
public DockPane ActivePane
|
|
{
|
|
get { return FocusManager.ActivePane; }
|
|
}
|
|
|
|
[Browsable(false)]
|
|
public IDockContent ActiveDocument
|
|
{
|
|
get { return FocusManager.ActiveDocument; }
|
|
}
|
|
|
|
[Browsable(false)]
|
|
public DockPane ActiveDocumentPane
|
|
{
|
|
get { return FocusManager.ActiveDocumentPane; }
|
|
}
|
|
|
|
private static readonly object ActiveDocumentChangedEvent = new object();
|
|
[LocalizedCategory("Category_PropertyChanged")]
|
|
[LocalizedDescription("DockPanel_ActiveDocumentChanged_Description")]
|
|
public event EventHandler ActiveDocumentChanged
|
|
{
|
|
add { Events.AddHandler(ActiveDocumentChangedEvent, value); }
|
|
remove { Events.RemoveHandler(ActiveDocumentChangedEvent, value); }
|
|
}
|
|
protected virtual void OnActiveDocumentChanged(EventArgs e)
|
|
{
|
|
EventHandler handler = (EventHandler)Events[ActiveDocumentChangedEvent];
|
|
if (handler != null)
|
|
handler(this, e);
|
|
}
|
|
|
|
private static readonly object ActiveContentChangedEvent = new object();
|
|
[LocalizedCategory("Category_PropertyChanged")]
|
|
[LocalizedDescription("DockPanel_ActiveContentChanged_Description")]
|
|
public event EventHandler ActiveContentChanged
|
|
{
|
|
add { Events.AddHandler(ActiveContentChangedEvent, value); }
|
|
remove { Events.RemoveHandler(ActiveContentChangedEvent, value); }
|
|
}
|
|
protected void OnActiveContentChanged(EventArgs e)
|
|
{
|
|
EventHandler handler = (EventHandler)Events[ActiveContentChangedEvent];
|
|
if (handler != null)
|
|
handler(this, e);
|
|
}
|
|
|
|
private static readonly object ActivePaneChangedEvent = new object();
|
|
[LocalizedCategory("Category_PropertyChanged")]
|
|
[LocalizedDescription("DockPanel_ActivePaneChanged_Description")]
|
|
public event EventHandler ActivePaneChanged
|
|
{
|
|
add { Events.AddHandler(ActivePaneChangedEvent, value); }
|
|
remove { Events.RemoveHandler(ActivePaneChangedEvent, value); }
|
|
}
|
|
protected virtual void OnActivePaneChanged(EventArgs e)
|
|
{
|
|
EventHandler handler = (EventHandler)Events[ActivePaneChangedEvent];
|
|
if (handler != null)
|
|
handler(this, e);
|
|
}
|
|
}
|
|
}
|