internal static void RunLoop(bool Modal, ApplicationContext context)
{
Queue toplevels;
MSG msg;
Object queue_id;
MWFThread thread;
ApplicationContext previous_thread_context;
thread = MWFThread.Current;
/*
* There is a NotWorking test for this, but since we are using this method both for Form.ShowDialog as for ApplicationContexts we'll
* fail on nested ShowDialogs, so disable the check for the moment.
*/
//if (thread.MessageLoop) {
// throw new InvalidOperationException ("Starting a second message loop on a single thread is not a valid operation. Use Form.ShowDialog instead.");
//}
msg = new MSG();
if (context == null)
{
context = new ApplicationContext();
}
previous_thread_context = thread.Context;
thread.Context = context;
if (context.MainForm != null)
{
context.MainForm.is_modal = Modal;
context.MainForm.context = context;
context.MainForm.closing = false;
context.MainForm.Visible = true; // Cannot use Show() or scaling gets confused by menus
// XXX the above line can be used to close the form. another problem with our handling of Show/Activate.
if (context.MainForm != null)
{
context.MainForm.Activate();
}
}
#if DebugRunLoop
Console.WriteLine("Entering RunLoop(Modal={0}, Form={1})", Modal, context.MainForm != null ? context.MainForm.ToString() : "NULL");
#endif
if (Modal)
{
toplevels = new Queue();
DisableFormsForModalLoop(toplevels, context);
// FIXME - need activate?
/* make sure the MainForm is enabled */
if (context.MainForm != null)
{
XplatUI.EnableWindow(context.MainForm.Handle, true);
XplatUI.SetModal(context.MainForm.Handle, true);
}
}
else
{
toplevels = null;
}
queue_id = XplatUI.StartLoop(Thread.CurrentThread);
thread.MessageLoop = true;
bool quit = false;
while (!quit && XplatUI.GetMessage(queue_id, ref msg, IntPtr.Zero, 0, 0))
{
Message m = Message.Create(msg.hwnd, (int)msg.message, msg.wParam, msg.lParam);
if (Application.FilterMessage(ref m))
{
continue;
}
switch ((Msg)msg.message)
{
case Msg.WM_KEYDOWN:
case Msg.WM_SYSKEYDOWN:
case Msg.WM_CHAR:
case Msg.WM_SYSCHAR:
case Msg.WM_KEYUP:
case Msg.WM_SYSKEYUP:
Control c = Control.FromHandle(msg.hwnd);
// If we have a control with keyboard capture (usually a *Strip)
// give it the message, and then drop the message
if (keyboard_capture != null)
{
// WM_SYSKEYUP does not make it into ProcessCmdKey, so do it here
if ((Msg)m.Msg == Msg.WM_SYSKEYDOWN)
{
if (m.WParam.ToInt32() == (int)Keys.Menu)
{
keyboard_capture.GetTopLevelToolStrip().Dismiss(ToolStripDropDownCloseReason.Keyboard);
continue;
}
}
m.HWnd = keyboard_capture.Handle;
switch (keyboard_capture.PreProcessControlMessageInternal(ref m))
{
case PreProcessControlState.MessageProcessed:
continue;
case PreProcessControlState.MessageNeeded:
case PreProcessControlState.MessageNotNeeded:
if (((m.Msg == (int)Msg.WM_KEYDOWN || m.Msg == (int)Msg.WM_CHAR) && !keyboard_capture.ProcessControlMnemonic((char)m.WParam)))
{
if (c == null || !ControlOnToolStrip(c))
{
continue;
}
else
{
m.HWnd = msg.hwnd;
}
}
else
{
continue;
}
break;
}
}
if (((c != null) && c.PreProcessControlMessageInternal(ref m) != PreProcessControlState.MessageProcessed) ||
(c == null))
{
goto default;
}
break;
case Msg.WM_LBUTTONDOWN:
case Msg.WM_MBUTTONDOWN:
case Msg.WM_RBUTTONDOWN:
if (keyboard_capture != null)
{
Control c2 = Control.FromHandle(msg.hwnd);
var contextMenuStrip = keyboard_capture.GetTopLevelToolStrip() as ContextMenuStrip;
// The target is not a winforms control (an embedded control, perhaps), so
// release everything
if (c2 == null)
{
ToolStripManager.FireAppClicked();
goto default;
}
// Skip clicks on owner windows, eg. expanded ComboBox
if (Control.IsChild(keyboard_capture.Handle, msg.hwnd))
{
goto default;
}
// Close any active toolstrips drop-downs if we click outside of them,
// but also don't close them all if we click outside of the top-most
// one, but into its owner.
Point c2_point = c2.PointToScreen(new Point(
(int)(short)(m.LParam.ToInt32() & 0xffff),
(int)(short)(m.LParam.ToInt32() >> 16)));
while (keyboard_capture != null && !keyboard_capture.ClientRectangle.Contains(keyboard_capture.PointToClient(c2_point)))
{
keyboard_capture.Dismiss();
}
var iter_OwnerItem = (c2 as ToolStripDropDown)?.OwnerItem;
while (iter_OwnerItem != null && iter_OwnerItem.Owner != contextMenuStrip)
{
iter_OwnerItem = iter_OwnerItem.OwnerItem;
}
var contextMenuStripIsOwnerOf_c2 = (iter_OwnerItem != null);
if (c2 != contextMenuStrip && !contextMenuStripIsOwnerOf_c2)
{
contextMenuStrip.Dismiss();
}
}
goto default;
case Msg.WM_QUIT:
quit = true; // make sure we exit
break;
default:
XplatUI.TranslateMessage(ref msg);
XplatUI.DispatchMessage(ref msg);
break;
}
// If our Form doesn't have a handle anymore, it means it was destroyed and we need to *wait* for WM_QUIT.
if ((context.MainForm != null) && (!context.MainForm.IsHandleCreated))
{
continue;
}
// Handle exit, Form might have received WM_CLOSE and set 'closing' in response.
if ((context.MainForm != null) && (context.MainForm.closing || (Modal && !context.MainForm.Visible)))
{
if (!Modal)
{
XplatUI.PostQuitMessage(0);
}
else
{
break;
}
}
}
#if DebugRunLoop
Console.WriteLine(" RunLoop loop left");
#endif
thread.MessageLoop = false;
XplatUI.EndLoop(Thread.CurrentThread);
if (Modal)
{
Form old = context.MainForm;
context.MainForm = null;
EnableFormsForModalLoop(toplevels, context);
if (old != null && old.IsHandleCreated)
{
XplatUI.SetModal(old.Handle, false);
}
#if DebugRunLoop
Console.WriteLine(" Done with the SetModal");
#endif
old.RaiseCloseEvents(true, false);
old.is_modal = false;
}
#if DebugRunLoop
Console.WriteLine("Leaving RunLoop(Modal={0}, Form={1})", Modal, context.MainForm != null ? context.MainForm.ToString() : "NULL");
#endif
if (context.MainForm != null)
{
context.MainForm.context = null;
context.MainForm = null;
}
thread.Context = previous_thread_context;
if (!Modal)
{
thread.Exit();
}
}