X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fclass%2FSystem.Windows.Forms%2FSystem.Windows.Forms%2FApplication.cs;h=2c2d5bf931d3f08343f4a39947239bc0adacce93;hb=34d0fdb5a32a979fdf347497cde43127a874dac1;hp=ba9ba78fb5fc9237232220f535f4b79f0b23c1a5;hpb=c48238d24e913ebb1a0b4951116005fa08daec8b;p=mono.git diff --git a/mcs/class/System.Windows.Forms/System.Windows.Forms/Application.cs b/mcs/class/System.Windows.Forms/System.Windows.Forms/Application.cs index ba9ba78fb5f..2c2d5bf931d 100644 --- a/mcs/class/System.Windows.Forms/System.Windows.Forms/Application.cs +++ b/mcs/class/System.Windows.Forms/System.Windows.Forms/Application.cs @@ -1,231 +1,1101 @@ -// -// System.Windows.Forms.Application -// -// Author: -// stubbed out by Jaak Simm (jaaksimm@firm.ee) -// Miguel de Icaza (miguel@ximian.com) -// Dennis hayes (dennish@raytek.com) -// WINELib implementation started by John Sohn (jsohn@columbus.rr.com) -// -// (C) Ximian, Inc 2002 -// - -using System; -using System.Drawing; -using Microsoft.Win32; -using System.ComponentModel; -using System.Threading; -using System.Globalization; -using System.Reflection; -using System.Collections; -using System.Runtime.CompilerServices; - -namespace System.Windows.Forms { - - /// - /// Provides static methods and properties to manage an application, - /// such as methods to start and stop an application, to process - /// Windows messages, and properties to get information about an - /// application. This class cannot be inherited. - /// - - [MonoTODO] - public sealed class Application { - static private ApplicationContext applicationContext = null; - static private bool messageLoopStarted = false; - static private bool messageLoopStopRequest = false; - private static ArrayList messageFilters = new ArrayList (); - - // --- (public) Properties --- - public static bool AllowQuit { - // according to docs return false if embbedded in a - // browser, not (yet?) embedded in a browser - get { return true; } - } - - [MonoTODO] - public static string CommonAppDataPath { - get { throw new NotImplementedException (); } - } - - [MonoTODO] - // Registry key not yet defined (this should be interesting) - //public static RegistryKey CommonAppDataRegistry { - // get { throw new NotImplementedException (); } - //} - - [MonoTODO] - public static string CompanyName { - get { throw new NotImplementedException (); } - } - - [MonoTODO] - public static CultureInfo CurrentCulture { - get { throw new NotImplementedException (); } - set { throw new NotImplementedException (); } - } - - [MonoTODO] - public static InputLanguage CurrentInputLanguage { - get { throw new NotImplementedException (); } - } - - [MonoTODO] - public static string ExecutablePath { - get { throw new NotImplementedException (); } - } - - [MonoTODO] - public static string LocalUserAppDataPath { - get { throw new NotImplementedException (); } - } - - public static bool MessageLoop { - get { return messageLoopStarted; } - } - - [MonoTODO] - public static string ProductName { - get { throw new NotImplementedException (); } - } - - [MonoTODO] - public static string ProductVersion { - get { throw new NotImplementedException (); } - } - - [MonoTODO] - public static string SafeTopLevelCaptionFormat { - get { throw new NotImplementedException (); } - set { throw new NotImplementedException (); } - } - - [MonoTODO] - public static string StartupPath { - get { throw new NotImplementedException (); } - } - - [MonoTODO] - public static string UserAppDataPath { - get { throw new NotImplementedException (); } - } - - [MonoTODO] - // Registry key not yet defined - //public static RegistryKey UserAppDataRegistry { - // get { throw new NotImplementedException (); } - //} - - // --- Methods --- - public static void AddMessageFilter (IMessageFilter value) - { - messageFilters.Add (value); - } - - public static void DoEvents () - { - Win32.MSG msg = new Win32.MSG(); - - while (Win32.PeekMessageA (ref msg, (IntPtr) 0, 0, 0, - Win32.PM_REMOVE) != 0); - } - - public static void Exit () - { - Win32.PostQuitMessage (0); - } - - public static void ExitThread () - { - messageLoopStopRequest = true; - } - - [MonoTODO] - public static ApartmentState OleRequired () - { - throw new NotImplementedException (); - } - - [MonoTODO] - public static void OnThreadException (Exception t) - { - throw new NotImplementedException (); - } - - public static void RemoveMessageFilter (IMessageFilter value) - { - messageFilters.Remove (value); - } - - static private void ApplicationFormClosed (object o, EventArgs args) - { - Win32.PostQuitMessage (0); - } - - static public void Run () - { - Win32.MSG msg = new Win32.MSG(); - - messageLoopStarted = true; - - while (!messageLoopStopRequest && - Win32.GetMessageA (ref msg, 0, 0, 0) != 0) { - - bool dispatchMessage = true; - - Message message = new Message (); - message.HWnd = msg.hwnd; - message.Msg = (int) msg.message; - message.WParam = msg.wParam; - message.LParam = msg.lParam; - - IEnumerator e = messageFilters.GetEnumerator(); - - while (e.MoveNext()) { - IMessageFilter filter = - (IMessageFilter) e.Current; - - // if PreFilterMessage returns true - // the message should not be dispatched - if (filter.PreFilterMessage (ref message)) - dispatchMessage = false; - } - - if (dispatchMessage) { - Win32.TranslateMessage (ref msg); - Win32.DispatchMessageA (ref msg); - } - - //if (Idle != null) - //Idle (null, new EventArgs()); - } - - //if (ApplicationExit != null) - //ApplicationExit (null, new EventArgs()); - } - - public static void Run (ApplicationContext context) - { - applicationContext = context; - applicationContext.MainForm.Show (); - applicationContext.MainForm.Closed += - new EventHandler (ApplicationFormClosed); - Run(); - } - - //[TypeAttributes.BeforeFieldInit] - public static void Run (Form form) - // Documents say this parameter name should be mainform, - // but the verifier says context. - { - ApplicationContext context = new ApplicationContext (form); - Run (context); - } - - // --- Events --- - public static event EventHandler ApplicationExit; - public static event EventHandler Idle; - public static event ThreadExceptionEventHandler ThreadException; - public static event EventHandler ThreadExit; - } -} +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// Copyright (c) 2004 - 2006 Novell, Inc. +// +// Authors: +// Peter Bartok pbartok@novell.com +// Daniel Nauck (dna(at)mono-project(dot)de) +// + +// COMPLETE + +#undef DebugRunLoop + +using Microsoft.Win32; +using System; +using System.Drawing; +using System.ComponentModel; +using System.Collections; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Threading; +using System.Text; +using System.Windows.Forms.VisualStyles; + +namespace System.Windows.Forms +{ + public sealed class Application + { + internal class MWFThread + { + #region Fields + + private ApplicationContext context; + private bool messageloop_started; + private bool handling_exception; + private int thread_id; + + private static readonly Hashtable threads = new Hashtable(); + + #endregion // Fields + + #region Constructors + + private MWFThread() + { + } + + #endregion // Constructors + + #region Properties + + public ApplicationContext Context { + get { return context; } + set { context = value; } + } + + public bool MessageLoop { + get { return messageloop_started; } + set { messageloop_started = value; } + } + + public bool HandlingException { + get { return handling_exception; } + set { handling_exception = value; } + } + + public static int LoopCount { + get { + lock (threads) { + int loops = 0; + + foreach (MWFThread thread in threads.Values) { + if (thread.messageloop_started) + loops++; + } + + return loops; + } + } + } + + public static MWFThread Current { + get { + MWFThread thread = null; + + lock (threads) { + thread = (MWFThread) threads [Thread.CurrentThread.GetHashCode ()]; + if (thread == null) { + thread = new MWFThread(); + thread.thread_id = Thread.CurrentThread.GetHashCode (); + threads [thread.thread_id] = thread; + } + } + + return thread; + } + } + + #endregion // Properties + + #region Methods + + public void Exit () + { + if (context != null) + context.ExitThread(); + context = null; + + if (Application.ThreadExit != null) + Application.ThreadExit(null, EventArgs.Empty); + + if (LoopCount == 0) { + if (Application.ApplicationExit != null) + Application.ApplicationExit (null, EventArgs.Empty); + } + + ((MWFThread) threads [thread_id]).MessageLoop = false; + } + + #endregion // Methods + } + + private static bool browser_embedded; + private static InputLanguage input_language = InputLanguage.CurrentInputLanguage; + private static string safe_caption_format = "{1} - {0} - {2}"; + private static readonly ArrayList message_filters = new ArrayList(); + private static readonly FormCollection forms = new FormCollection (); + + private static bool use_wait_cursor; + private static ToolStrip keyboard_capture; + private static VisualStyleState visual_style_state = VisualStyleState.ClientAndNonClientAreasEnabled; + static bool visual_styles_enabled; + + private Application () + { + browser_embedded = false; + } + + static Application () + { + // Attempt to load UIA support for winforms + // UIA support requires .NET 2.0 + InitializeUIAutomation (); + } + + #region Private Methods + + private static void InitializeUIAutomation () + { + // Initialize the UIAutomationWinforms Global class, + // which create some listeners which subscribe to internal + // MWF events so that it can provide a11y support for MWF + const string UIA_WINFORMS_ASSEMBLY = + "UIAutomationWinforms, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f4ceacb585d99812"; + MethodInfo init_method; + Assembly mwf_providers = null; + try { + mwf_providers = Assembly.Load (UIA_WINFORMS_ASSEMBLY); + } catch { } + + if (mwf_providers == null) + return; + + const string UIA_WINFORMS_TYPE = "Mono.UIAutomation.Winforms.Global"; + const string UIA_WINFORMS_METHOD = "Initialize"; + try { + Type global_type = mwf_providers.GetType (UIA_WINFORMS_TYPE, false); + if (global_type != null) { + init_method = global_type.GetMethod (UIA_WINFORMS_METHOD, + BindingFlags.Static | + BindingFlags.Public); + if (init_method != null) + init_method.Invoke (null, new object [] {}); + else + throw new Exception (String.Format ("Method {0} not found in type {1}.", + UIA_WINFORMS_METHOD, UIA_WINFORMS_TYPE)); + } + else + throw new Exception (String.Format ("Type {0} not found in assembly {1}.", + UIA_WINFORMS_TYPE, UIA_WINFORMS_ASSEMBLY)); + } catch (Exception ex) { + Console.Error.WriteLine ("Error setting up UIA: " + ex); + } + } + + internal static void CloseForms (Thread thread) + { + #if DebugRunLoop + Console.WriteLine(" CloseForms({0}) called", thread); + #endif + + ArrayList forms_to_close = new ArrayList (); + + lock (forms) { + foreach (Form f in forms) { + if (thread == null || thread == f.creator_thread) + forms_to_close.Add (f); + } + + foreach (Form f in forms_to_close) { + #if DebugRunLoop + Console.WriteLine(" Closing form {0}", f); + #endif + f.Dispose (); + } + } + } + + #endregion // Private methods + + #region Public Static Properties + + public static bool AllowQuit { + get { + return !browser_embedded; + } + } + + public static string CommonAppDataPath { + get { + return CreateDataPath (Environment.GetFolderPath (Environment.SpecialFolder.CommonApplicationData)); + } + } + + public static RegistryKey CommonAppDataRegistry { + get { + string key = string.Format ("Software\\{0}\\{1}\\{2}", CompanyName, ProductName, ProductVersion); + + return Registry.LocalMachine.CreateSubKey (key); + } + } + + public static string CompanyName { + get { + string company = string.Empty; + + Assembly assembly = Assembly.GetEntryAssembly (); + + if (assembly == null) + assembly = Assembly.GetCallingAssembly (); + + AssemblyCompanyAttribute[] attrs = (AssemblyCompanyAttribute[]) + assembly.GetCustomAttributes (typeof(AssemblyCompanyAttribute), true); + if (attrs != null && attrs.Length > 0) + company = attrs [0].Company; + + // If there is no [AssemblyCompany], return the outermost namespace + // on Main () + if (company == null || company.Length == 0) + if (assembly.EntryPoint != null) { + company = assembly.EntryPoint.DeclaringType.Namespace; + + if (company != null) { + int firstDot = company.IndexOf ('.'); + if (firstDot >= 0) + company = company.Substring (0, firstDot); + } + } + + // If that doesn't work, return the name of class containing Main () + if (company == null || company.Length == 0) + if (assembly.EntryPoint != null) + company = assembly.EntryPoint.DeclaringType.FullName; + + return company; + } + } + + public static CultureInfo CurrentCulture { + get { + return Thread.CurrentThread.CurrentUICulture; + } + set { + Thread.CurrentThread.CurrentUICulture = value; + } + } + + public static InputLanguage CurrentInputLanguage { + get { + return input_language; + } + set { + input_language=value; + } + } + + public static string ExecutablePath { + get { + return Path.GetFullPath (Environment.GetCommandLineArgs ()[0]); + } + } + + public static string LocalUserAppDataPath { + get { + return CreateDataPath (Environment.GetFolderPath (Environment.SpecialFolder.LocalApplicationData)); + } + } + + public static bool MessageLoop { + get { + return MWFThread.Current.MessageLoop; + } + } + + public static string ProductName { + get { + string name = string.Empty; + + Assembly assembly = Assembly.GetEntryAssembly (); + + if (assembly == null) + assembly = Assembly.GetCallingAssembly (); + + AssemblyProductAttribute[] attrs = (AssemblyProductAttribute[]) + assembly.GetCustomAttributes (typeof(AssemblyProductAttribute), true); + + if (attrs != null && attrs.Length > 0) + name = attrs [0].Product; + + // If there is no [AssemblyProduct], .NET returns the name of + // the innermost namespace and if that fails, resorts to the + // name of the class containing Main () + if (name == null || name.Length == 0) + if (assembly.EntryPoint != null) { + name = assembly.EntryPoint.DeclaringType.Namespace; + + if (name != null) { + int lastDot = name.LastIndexOf ('.'); + if (lastDot >= 0 && lastDot < name.Length - 1) + name = name.Substring (lastDot + 1); + } + + if (name == null || name.Length == 0) + name = assembly.EntryPoint.DeclaringType.FullName; + } + + return name; + } + } + + public static string ProductVersion { + get { + String version = string.Empty; + + Assembly assembly = Assembly.GetEntryAssembly (); + + if (assembly == null) + assembly = Assembly.GetCallingAssembly (); + + AssemblyInformationalVersionAttribute infoVersion = + Attribute.GetCustomAttribute (assembly, + typeof (AssemblyInformationalVersionAttribute)) + as AssemblyInformationalVersionAttribute; + + if (infoVersion != null) + version = infoVersion.InformationalVersion; + + // If [AssemblyFileVersion] is present it is used + // before resorting to assembly version + if (version == null || version.Length == 0) { + AssemblyFileVersionAttribute fileVersion = + Attribute.GetCustomAttribute (assembly, + typeof (AssemblyFileVersionAttribute)) + as AssemblyFileVersionAttribute; + if (fileVersion != null) + version = fileVersion.Version; + } + + // If neither [AssemblyInformationalVersionAttribute] + // nor [AssemblyFileVersion] are present, then use + // the assembly version + if (version == null || version.Length == 0) + version = assembly.GetName ().Version.ToString (); + + return version; + } + } + + public static string SafeTopLevelCaptionFormat { + get { + return safe_caption_format; + } + set { + safe_caption_format = value; + } + } + + public static string StartupPath { + get { + return Path.GetDirectoryName (Application.ExecutablePath); + } + } + + public static string UserAppDataPath { + get { + return CreateDataPath (Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData)); + } + } + + public static RegistryKey UserAppDataRegistry { + get { + string key = string.Format ("Software\\{0}\\{1}\\{2}", CompanyName, ProductName, ProductVersion); + + return Registry.CurrentUser.CreateSubKey (key); + } + } + + public static bool UseWaitCursor { + get { + return use_wait_cursor; + } + set { + use_wait_cursor = value; + if (use_wait_cursor) { + foreach (Form form in OpenForms) { + form.Cursor = Cursors.WaitCursor; + } + } + } + } + + public static bool RenderWithVisualStyles { + get { + if (VisualStyleInformation.IsSupportedByOS) { + if (!VisualStyleInformation.IsEnabledByUser) + return false; + if (!XplatUI.ThemesEnabled) + return false; + if (Application.VisualStyleState == VisualStyleState.ClientAndNonClientAreasEnabled) + return true; + if (Application.VisualStyleState == VisualStyleState.ClientAreaEnabled) + return true; + } + return false; + } + } + + public static VisualStyleState VisualStyleState { + get { return Application.visual_style_state; } + set { Application.visual_style_state = value; } + } + + #endregion + + #region Public Static Methods + + public static void AddMessageFilter (IMessageFilter value) + { + lock (message_filters) { + message_filters.Add (value); + } + } + + internal static void AddKeyFilter (IKeyFilter value) + { + XplatUI.AddKeyFilter (value); + } + + public static void DoEvents () + { + XplatUI.DoEvents (); + } + + public static void EnableVisualStyles () + { + visual_styles_enabled = true; + XplatUI.EnableThemes (); + } + + [EditorBrowsable (EditorBrowsableState.Advanced)] + public static bool FilterMessage (ref Message message) + { + lock (message_filters) { + for (int i = 0; i < message_filters.Count; i++) { + IMessageFilter filter = (IMessageFilter) message_filters[i]; + if (filter.PreFilterMessage (ref message)) + return true; + } + } + return false; + } + + // + // If true, it uses GDI+, performance reasons were quoted + // + static internal bool use_compatible_text_rendering = true; + + public static void SetCompatibleTextRenderingDefault (bool defaultValue) + { + use_compatible_text_rendering = defaultValue; + } + + public static FormCollection OpenForms { + get { + return forms; + } + } + + [MonoNotSupported ("Only applies when Winforms is being hosted by an unmanaged app.")] + [EditorBrowsable (EditorBrowsableState.Advanced)] + public static void RegisterMessageLoop (MessageLoopCallback callback) + { + } + + [MonoNotSupported ("Empty stub.")] + public static bool SetSuspendState (PowerState state, bool force, bool disableWakeEvent) + { + return false; + } + + [MonoNotSupported ("Empty stub.")] + public static void SetUnhandledExceptionMode (UnhandledExceptionMode mode) + { + //FIXME: a stub to fill + } + + [MonoNotSupported ("Empty stub.")] + public static void SetUnhandledExceptionMode (UnhandledExceptionMode mode, bool threadScope) + { + //FIXME: a stub to fill + } + + [MonoNotSupported ("Only applies when Winforms is being hosted by an unmanaged app.")] + [EditorBrowsable (EditorBrowsableState.Advanced)] + public static void UnregisterMessageLoop () + { + } + + [EditorBrowsable (EditorBrowsableState.Advanced)] + public static void RaiseIdle (EventArgs e) + { + XplatUI.RaiseIdle (e); + } + + public static void Restart () + { + //FIXME: ClickOnce stuff using the Update or UpdateAsync methods. + //FIXME: SecurityPermission: Restart () requires IsUnrestricted permission. + + if (Assembly.GetEntryAssembly () == null) + throw new NotSupportedException ("The method 'Restart' is not supported by this application type."); + + string mono_path = null; + + //Get mono path + PropertyInfo gac = typeof (Environment).GetProperty ("GacPath", BindingFlags.Static | BindingFlags.NonPublic); + MethodInfo get_gac = null; + if (gac != null) + get_gac = gac.GetGetMethod (true); + + if (get_gac != null) { + string gac_path = Path.GetDirectoryName ((string)get_gac.Invoke (null, null)); + string mono_prefix = Path.GetDirectoryName (Path.GetDirectoryName (gac_path)); + + if (XplatUI.RunningOnUnix) { + mono_path = Path.Combine (mono_prefix, "bin/mono"); + if (!File.Exists (mono_path)) + mono_path = "mono"; + } else { + mono_path = Path.Combine (mono_prefix, "bin\\mono.bat"); + + if (!File.Exists (mono_path)) + mono_path = Path.Combine (mono_prefix, "bin\\mono.exe"); + + if (!File.Exists (mono_path)) + mono_path = Path.Combine (mono_prefix, "mono\\mono\\mini\\mono.exe"); + + if (!File.Exists (mono_path)) + throw new FileNotFoundException (string.Format ("Windows mono path not found: '{0}'", mono_path)); + } + } + + //Get command line arguments + StringBuilder argsBuilder = new StringBuilder (); + string[] args = Environment.GetCommandLineArgs (); + for (int i = 0; i < args.Length; i++) + { + argsBuilder.Append (string.Format ("\"{0}\" ", args[i])); + } + string arguments = argsBuilder.ToString (); + ProcessStartInfo procInfo = Process.GetCurrentProcess ().StartInfo; + + if (mono_path == null) { //it is .NET on Windows + procInfo.FileName = args[0]; + procInfo.Arguments = arguments.Remove (0, args[0].Length + 3); //1 space and 2 quotes + } + else { + procInfo.Arguments = arguments; + procInfo.FileName = mono_path; + } + + procInfo.WorkingDirectory = Environment.CurrentDirectory; + + Application.Exit (); + Process.Start (procInfo); + } + + public static void Exit () + { + Exit (new CancelEventArgs ()); + } + + [EditorBrowsable (EditorBrowsableState.Advanced)] + public static void Exit (CancelEventArgs e) + { + ArrayList forms_to_close; + + lock (forms) { + forms_to_close = new ArrayList (forms); + + foreach (Form f in forms_to_close) { + // Give each form a chance to cancel the Application.Exit + e.Cancel = f.FireClosingEvents (CloseReason.ApplicationExitCall, false); + + if (e.Cancel) + return; + + f.suppress_closing_events = true; + f.Close (); + f.Dispose (); + } + } + + XplatUI.PostQuitMessage (0); + } + + public static void ExitThread() + { + CloseForms(Thread.CurrentThread); + // this might not be right - need to investigate (somehow) if a WM_QUIT message is generated here + XplatUI.PostQuitMessage(0); + } + + public static ApartmentState OleRequired () + { + //throw new NotImplementedException("OLE Not supported by this System.Windows.Forms implementation"); + return ApartmentState.Unknown; + } + + public static void OnThreadException (Exception t) + { + if (MWFThread.Current.HandlingException) { + /* we're already handling an exception and we got + another one? print it out and exit, this means + we've got a runtime/SWF bug. */ + Console.WriteLine (t); + // Don't use Application.Exit here, since it may cause a stack overflow + // in certain cases. It's however hard to reproduce since it seems to + // be depending on when the GC kicks in. + Environment.Exit(1); + } + + try { + MWFThread.Current.HandlingException = true; + + if (Application.ThreadException != null) { + Application.ThreadException(null, new ThreadExceptionEventArgs(t)); + return; + } + + if (SystemInformation.UserInteractive) { + Form form = new ThreadExceptionDialog (t); + form.ShowDialog (); + } else { + Console.WriteLine (t.ToString ()); + Application.Exit (); + } + } finally { + MWFThread.Current.HandlingException = false; + } + } + + public static void RemoveMessageFilter (IMessageFilter value) + { + lock (message_filters) { + message_filters.Remove (value); + } + } + + public static void Run () + { + Run (new ApplicationContext ()); + } + + public static void Run (Form mainForm) + { + Run (new ApplicationContext (mainForm)); + } + + internal static void FirePreRun () + { + EventHandler handler = PreRun; + if (handler != null) + handler (null, EventArgs.Empty); + } + + public static void Run (ApplicationContext context) + { + // If a sync context hasn't been created by now, create + // a default one + if (SynchronizationContext.Current == null) + SynchronizationContext.SetSynchronizationContext (new SynchronizationContext ()); + + RunLoop (false, context); + + // Reset the sync context back to the default + if (SynchronizationContext.Current is WindowsFormsSynchronizationContext) + WindowsFormsSynchronizationContext.Uninstall (); + } + + private static void DisableFormsForModalLoop (Queue toplevels, ApplicationContext context) + { + Form f; + + lock (forms) { + IEnumerator control = forms.GetEnumerator (); + + while (control.MoveNext ()) { + f = (Form)control.Current; + + // Don't disable the main form. + if (f == context.MainForm) { + continue; + } + + // Don't disable any children of the main form. + // These do not have to be MDI children. + Control current = f; + bool is_child_of_main = false; ; + + do { + if (current.Parent == context.MainForm) { + is_child_of_main = true; + break; + } + current = current.Parent; + } while (current != null); + + if (is_child_of_main) + continue; + + // Disable the rest + if (f.IsHandleCreated && XplatUI.IsEnabled (f.Handle)) { +#if DebugRunLoop + Console.WriteLine(" Disabling form {0}", f); +#endif + XplatUI.EnableWindow (f.Handle, false); + toplevels.Enqueue (f); + } + } + } + + } + + + private static void EnableFormsForModalLoop (Queue toplevels, ApplicationContext context) + { + while (toplevels.Count > 0) { +#if DebugRunLoop + Console.WriteLine(" Re-Enabling form form {0}", toplevels.Peek()); +#endif + Form c = (Form) toplevels.Dequeue (); + if (c.IsHandleCreated) { + XplatUI.EnableWindow (c.window.Handle, true); + context.MainForm = c; + } + } +#if DebugRunLoop + Console.WriteLine(" Done with the re-enable"); +#endif + } + + 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; + 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); + + // the target is not a winforms control (an embedded control, perhaps), so + // release everything + if (c2 == null) { + ToolStripManager.FireAppClicked (); + goto default; + } + + // If we clicked a ToolStrip, we have to make sure it isn't + // the one we are on, or any of its parents or children + // If we clicked off the dropped down menu, release everything + if (c2 is ToolStrip) { + if ((c2 as ToolStrip).GetTopLevelToolStrip () != keyboard_capture.GetTopLevelToolStrip ()) + ToolStripManager.FireAppClicked (); + } else { + if (c2.Parent != null) + if (c2.Parent is ToolStripDropDownMenu) + if ((c2.Parent as ToolStripDropDownMenu).GetTopLevelToolStrip () == keyboard_capture.GetTopLevelToolStrip ()) + goto default; + if (c2.TopLevelControl == null) + goto default; + + ToolStripManager.FireAppClicked (); + } + } + + 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 (context.MainForm != null && context.MainForm.IsHandleCreated) { + XplatUI.SetModal (context.MainForm.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(); + } + + #endregion // Public Static Methods + + #region Events + + public static event EventHandler ApplicationExit; + + public static event EventHandler Idle { + add { + XplatUI.Idle += value; + } + remove { + XplatUI.Idle -= value; + } + } + + public static event EventHandler ThreadExit; + public static event ThreadExceptionEventHandler ThreadException; + + // These are used externally by the UIA framework + internal static event EventHandler FormAdded; + internal static event EventHandler PreRun; + +#pragma warning disable 0067 + [MonoTODO] + [EditorBrowsable (EditorBrowsableState.Advanced)] + public static event EventHandler EnterThreadModal; + + [MonoTODO] + [EditorBrowsable (EditorBrowsableState.Advanced)] + public static event EventHandler LeaveThreadModal; +#pragma warning restore 0067 + + #endregion // Events + + #region Public Delegates + + [EditorBrowsable (EditorBrowsableState.Advanced)] + public delegate bool MessageLoopCallback (); + + #endregion + + #region Internal Properties + internal static ToolStrip KeyboardCapture { + get { return keyboard_capture; } + set { keyboard_capture = value; } + } + + internal static bool VisualStylesEnabled { + get { return visual_styles_enabled; } + } + #endregion + + #region Internal Methods + + internal static void AddForm (Form f) + { + lock (forms) + forms.Add (f); + // Signal that a Form has been added to this + // Application. Used by UIA to detect new Forms that + // need a11y support. This event may be fired even if + // the form has already been added, so clients should + // account for that when handling this signal. + if (FormAdded != null) + FormAdded (f, null); + } + + internal static void RemoveForm (Form f) + { + lock (forms) + forms.Remove (f); + } + + private static bool ControlOnToolStrip (Control c) + { + Control p = c.Parent; + + while (p != null) { + if (p is ToolStrip) + return true; + + p = p.Parent; + } + + return false; + } + + // Takes a starting path, appends company name, product name, and + // product version. If the directory doesn't exist, create it + private static string CreateDataPath (string basePath) + { + string path; + + path = Path.Combine (basePath, CompanyName); + path = Path.Combine (path, ProductName); + path = Path.Combine (path, ProductVersion); + + if (!Directory.Exists (path)) + Directory.CreateDirectory (path); + + return path; + } + #endregion + } +}