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#

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