// 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 // // 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; namespace System.Windows.Forms { public sealed class Application { private class MWFThread { #region Fields private ApplicationContext context; private bool messageloop_started; private int thread_id; private static 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 static int LoopCount { get { IEnumerator e; int loops; MWFThread thread; e = threads.Values.GetEnumerator(); loops = 0; while (e.MoveNext()) { thread = (MWFThread)e.Current; if (thread != null && thread.messageloop_started) { loops++; } } return loops; } } public static MWFThread Current { get { MWFThread thread; 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); } } threads[thread_id] = null; } #endregion // Methods } private static bool browser_embedded = false; private static InputLanguage input_language = InputLanguage.CurrentInputLanguage; private static string safe_caption_format = "{1} - {0} - {2}"; private static ArrayList message_filters = new ArrayList(); private Application () { } #region Private Methods private static void CloseForms(Thread thread) { Control c; IEnumerator control; bool all; #if DebugRunLoop Console.WriteLine(" CloseForms({0}) called", thread); #endif if (thread == null) { all = true; } else { all = false; } control = Control.controls.GetEnumerator(); while (control.MoveNext()) { c = (Control)control.Current; if (c is Form) { if (all || (thread == c.creator_thread)) { if (c.IsHandleCreated) { XplatUI.PostMessage(c.Handle, Msg.WM_CLOSE_INTERNAL, IntPtr.Zero, IntPtr.Zero); } #if DebugRunLoop Console.WriteLine(" Closing form {0}", c); #endif } } } } #endregion // Private methods #region Public Static Properties public static bool AllowQuit { get { return browser_embedded; } } public static string CommonAppDataPath { get { return Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData); } } public static RegistryKey CommonAppDataRegistry { get { RegistryKey key; key = Registry.LocalMachine.OpenSubKey("Software\\" + Application.CompanyName + "\\" + Application.ProductName + "\\" + Application.ProductVersion, true); return key; } } public static string CompanyName { get { AssemblyCompanyAttribute[] attrs = (AssemblyCompanyAttribute[]) Assembly.GetEntryAssembly().GetCustomAttributes(typeof(AssemblyCompanyAttribute), true); if ((attrs != null) && attrs.Length>0) { return attrs[0].Company; } return Assembly.GetEntryAssembly().GetName().Name; } } 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 Assembly.GetEntryAssembly().Location; } } public static string LocalUserAppDataPath { get { return Path.Combine(Path.Combine(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), CompanyName), ProductName), ProductVersion); } } public static bool MessageLoop { get { return MWFThread.Current.MessageLoop; } } public static string ProductName { get { AssemblyProductAttribute[] attrs = (AssemblyProductAttribute[]) Assembly.GetEntryAssembly().GetCustomAttributes(typeof(AssemblyProductAttribute), true); if ((attrs != null) && attrs.Length>0) { return attrs[0].Product; } return Assembly.GetEntryAssembly().GetName().Name; } } public static string ProductVersion { get { String version; version = Assembly.GetEntryAssembly().GetName().Version.ToString(); if (version.StartsWith("0.")) { version="1." + version.Substring(2); } 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 Path.Combine(Path.Combine(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), CompanyName), ProductName), ProductVersion); } } public static RegistryKey UserAppDataRegistry { get { RegistryKey key; key = Registry.CurrentUser.OpenSubKey("Software\\" + Application.CompanyName + "\\" + Application.ProductName + "\\" + Application.ProductVersion, true); return key; } } #endregion #region Public Static Methods public static void AddMessageFilter(IMessageFilter value) { message_filters.Add(value); } public static void DoEvents() { XplatUI.DoEvents(); } public static void EnableVisualStyles() { XplatUI.EnableThemes(); } #if NET_2_0 // // 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; } #endif public static void Exit() { CloseForms(null); // FIXME - this needs to be fired when they're all closed // But CloseForms uses PostMessage, so it gets fired before // We need to wait on something... if (ApplicationExit != null) { ApplicationExit(null, EventArgs.Empty); } } public static void ExitThread() { CloseForms(Thread.CurrentThread); MWFThread.Current.Exit(); } 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 (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 ()); } } public static void RemoveMessageFilter(IMessageFilter filter) { message_filters.Remove(filter); } public static void Run() { RunLoop(false, new ApplicationContext()); } public static void Run(Form mainForm) { RunLoop(false, new ApplicationContext(mainForm)); } public static void Run(ApplicationContext context) { RunLoop(false, context); } internal static void RunLoop(bool Modal, ApplicationContext context) { Queue toplevels; IEnumerator control; MSG msg; Object queue_id; MWFThread thread; thread = MWFThread.Current; msg = new MSG(); if (context == null) { context = new ApplicationContext(); } thread.Context = context; if (context.MainForm != null) { context.MainForm.Visible = true; // Cannot use Show() or scaling gets confused by menus // FIXME - do we need this? //context.MainForm.PerformLayout(); context.MainForm.context = context; context.MainForm.Activate(); context.MainForm.closing = false; } #if DebugRunLoop Console.WriteLine("Entering RunLoop(Modal={0}, Form={1})", Modal, context.MainForm != null ? context.MainForm.ToString() : "NULL"); #endif if (Modal) { Control c; if (context.MainForm.Modal) { throw new Exception("fixme"); } context.MainForm.is_modal = true; toplevels = new Queue(); control = Control.controls.GetEnumerator(); while (control.MoveNext()) { c = (Control)control.Current; if (c is Form && (c != context.MainForm)) { if (c.IsHandleCreated && XplatUI.IsEnabled(c.Handle)) { #if DebugRunLoop Console.WriteLine(" Disabling form {0}", c); #endif XplatUI.EnableWindow(c.Handle, false); toplevels.Enqueue(c); } } } // FIXME - need activate? XplatUI.SetModal(context.MainForm.Handle, true); } else { toplevels = null; } queue_id = XplatUI.StartLoop(Thread.CurrentThread); thread.MessageLoop = true; while (XplatUI.GetMessage(queue_id, ref msg, IntPtr.Zero, 0, 0)) { if ((message_filters != null) && (message_filters.Count > 0)) { Message m; bool drop; drop = false; m = Message.Create(msg.hwnd, (int)msg.message, msg.wParam, msg.lParam); for (int i = 0; i < message_filters.Count; i++) { if (((IMessageFilter)message_filters[i]).PreFilterMessage(ref m)) { // we're dropping the message drop = true; break; } } if (drop) { 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: { Message m; Control c; m = Message.Create(msg.hwnd, (int)msg.message, msg.wParam, msg.lParam); c = Control.FromHandle(msg.hwnd); if ((c != null) && !c.PreProcessMessage(ref m)) { goto default; } break; } default: { XplatUI.TranslateMessage(ref msg); XplatUI.DispatchMessage(ref msg); break; } } // 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 { context.MainForm.DialogResult = DialogResult.Cancel; break; } } } #if DebugRunLoop Console.WriteLine(" RunLoop loop left"); #endif thread.MessageLoop = false; XplatUI.EndLoop(Thread.CurrentThread); if (Modal) { Control c; context.MainForm.Hide(); context.MainForm.is_modal = false; while (toplevels.Count>0) { #if DebugRunLoop Console.WriteLine(" Re-Enabling form form {0}", toplevels.Peek()); #endif c = (Control)toplevels.Dequeue(); if (c.IsHandleCreated) { XplatUI.EnableWindow(c.window.Handle, true); } } #if DebugRunLoop Console.WriteLine(" Done with the re-enable"); #endif if (context.MainForm.IsHandleCreated) { XplatUI.SetModal(context.MainForm.Handle, false); } #if DebugRunLoop Console.WriteLine(" Done with the SetModal"); #endif } #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; } 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; #endregion // Events } }