2006-05-25 Peter Dennis Bartok <pbartok@novell.com>
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / Application.cs
index e40f83545657d74880174c5230ddc5dc549906f7..2d39e14c41737862d63b0485e357f1de6f4aa739 100644 (file)
 // 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 Novell, Inc.
+// Copyright (c) 2004 - 2006 Novell, Inc.
 //
 // Authors:
 //     Peter Bartok    pbartok@novell.com
 //
 
-
 // COMPLETE
 
+#undef DebugRunLoop
+
 using Microsoft.Win32;
 using System;
 using System.Drawing;
@@ -40,79 +41,143 @@ using System.Threading;
 
 namespace System.Windows.Forms {
        public sealed class Application {
-               private static bool                     browser_embedded        = false;
-               private static bool                     exiting                 = false;
-               private static InputLanguage            input_language          = InputLanguage.CurrentInputLanguage;
-               private static bool                     messageloop_started     = false;
-               private static string                   safe_caption_format     = "{1} - {0} - {2}";
-               private static ArrayList                message_filters         = new ArrayList();
-               private static ApplicationContext       app_context             = null;
+               private class MWFThread {
+                       #region Fields
+                       private ApplicationContext      context;
+                       private bool                    messageloop_started;
+                       private int                     thread_id;
 
-               private Application () {
-               }
+                       private static Hashtable        threads = new Hashtable();
+                       #endregion      // Fields
 
-               #region Private and Internal Methods
-               internal static void ModalRun(Form form) {
-                       MSG     msg = new MSG();
-                       Queue   toplevels = new Queue();
-                       IEnumerator control = Control.controls.GetEnumerator();
+                       #region Constructors
+                       private MWFThread() {
+                       }
+                       #endregion      // Constructors
 
-                       if (form == null) {
-                               return;
+                       #region Properties
+                       public ApplicationContext Context {
+                               get {
+                                       return context;
+                               }
+
+                               set {
+                                       context = value;
+                               }
                        }
 
-                       // Both calls are needed, one is for the WM, the other for our focus logic
-                       XplatUI.Activate(form.window.Handle);
-                       form.Activate();
+                       public bool MessageLoop {
+                               get {
+                                       return messageloop_started;
+                               }
 
-                       while (control.MoveNext()) {
-                               if ((((Control)control.Current).parent == null) && (((Control)control.Current).is_visible) && (((Control)control.Current).is_enabled)) {
-                                       if ((control.Current is Form)  && (((Form)control.Current)!=form)) {
-                                               XplatUI.EnableWindow(((Control)control.Current).window.Handle, false);
-                                               toplevels.Enqueue((Control)control.Current);
-                                       }
+                               set {
+                                       messageloop_started = value;
                                }
                        }
 
-                       form.CreateControl();
 
-                       while (!exiting && !form.end_modal && XplatUI.GetMessage(ref msg, IntPtr.Zero, 0, 0)) {
-                               if ((message_filters != null) && (message_filters.Count > 0)) {
-                                       Message m;
-                                       bool    drop;
+                       public static int LoopCount {
+                               get {
+                                       IEnumerator     e;
+                                       int             loops;
+                                       MWFThread       thread;
 
-                                       drop = false;
-                                       m = new Message();
-                                       m.Msg = (int)msg.message;
-                                       m.HWnd = msg.hwnd;
-                                       m.LParam = msg.lParam;
-                                       m.WParam = msg.wParam;
-                                       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;
+                                       e = threads.Values.GetEnumerator();
+                                       loops = 0;
+
+                                       while (e.MoveNext()) {
+                                               thread = (MWFThread)e.Current;
+                                               if (thread != null && thread.messageloop_started) {
+                                                       loops++;
                                                }
                                        }
-                                       if (drop) {
-                                               continue;
+
+                                       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
 
-                               XplatUI.TranslateMessage(ref msg);
-                               XplatUI.DispatchMessage(ref msg);
+                       #region Methods
+                       public void Exit() {
+                               if (context != null) {
+                                       context.ExitThread();
+                               }
+                               context = null;
 
-                               // Handle exit, Form might have received WM_CLOSE and set 'closing' in response
-                               if (form.closing) {
-                                       form.end_modal = true;
+                               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
+               }
 
-                       while (toplevels.Count>0) {
-                               XplatUI.EnableWindow(((Control)toplevels.Dequeue()).window.Handle, true);
+               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 and Internal Methods
+               #endregion      // Private methods
 
                #region Public Static Properties
                public static bool AllowQuit {
@@ -184,7 +249,7 @@ namespace System.Windows.Forms {
 
                public static bool MessageLoop {
                        get {
-                               return messageloop_started;
+                               return MWFThread.Current.MessageLoop;
                        }
                }
 
@@ -260,21 +325,31 @@ namespace System.Windows.Forms {
                }
 
 #if NET_2_0
-               public static void EnableRTLMirroring () 
+               //
+               // 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() {
-                       XplatUI.Exit();
-               }
+                       CloseForms(null);
 
-               public static void ExitThread() {
-                       exiting=true;
+                       // 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);
+                       }
                }
 
-               private static void InternalExit(object sender, EventArgs e) {
-                       Application.Exit();
+               public static void ExitThread() {
+                       CloseForms(Thread.CurrentThread);
+                       MWFThread.Current.Exit();
                }
 
                public static ApartmentState OleRequired() {
@@ -287,20 +362,13 @@ namespace System.Windows.Forms {
                                Application.ThreadException(null, new ThreadExceptionEventArgs(t));
                                return;
                        }
-#if !later
-                        else {
-                               XplatUI.HandleException(t);
-                       }
-#else
-                       // TODO: Missing implementation
-                       //if (SystemInformation.UserInteractive)
-                       {
+
+                       if (SystemInformation.UserInteractive) {
                                Form form = new ThreadExceptionDialog (t);
                                form.ShowDialog ();
-                       }
-                       //else
+                       } else {
                                Console.WriteLine (t.ToString ());
-#endif
+                       }
                }
 
                public static void RemoveMessageFilter(IMessageFilter filter) {
@@ -308,32 +376,85 @@ namespace System.Windows.Forms {
                }
 
                public static void Run() {
-                       MSG     msg = new MSG();
-                       Form    form = null;
+                       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;
 
-                       if (app_context != null) {
-                               form = app_context.MainForm;
+                       msg = new MSG();
+
+                       if (context == null) {
+                               context = new ApplicationContext();
                        }
 
-                       if (form != null) {
-                               // Both calls are needed, one is for the WM, the other for our focus logic
-                               XplatUI.Activate(form.window.Handle);
-                               form.Activate();
+                       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
+                               // FIXME - do we need this?
+                               //context.MainForm.PerformLayout();
+                               context.MainForm.Activate();
                        }
 
-                       messageloop_started = true;
+                       #if DebugRunLoop
+                               Console.WriteLine("Entering RunLoop(Modal={0}, Form={1})", Modal, context.MainForm != null ? context.MainForm.ToString() : "NULL");
+                       #endif
+
+                       if (Modal) {
+                               Control c;
 
-                       while (!exiting && XplatUI.GetMessage(ref msg, IntPtr.Zero, 0, 0)) {
+                               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 = new Message();
-                                       m.Msg = (int)msg.message;
-                                       m.HWnd = msg.hwnd;
-                                       m.LParam = msg.lParam;
-                                       m.WParam = msg.wParam;
+                                       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
@@ -346,40 +467,84 @@ namespace System.Windows.Forms {
                                        }
                                }
 
-                               XplatUI.TranslateMessage(ref msg);
-                               XplatUI.DispatchMessage(ref msg);
+                               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 ((form != null) && form.closing) {
-                                       exiting = true;
+                               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
 
-                       messageloop_started = false;
+                       thread.MessageLoop = false;
+                       XplatUI.EndLoop(Thread.CurrentThread);
 
-                       if (ThreadExit != null) {
-                               ThreadExit(null, EventArgs.Empty);
-                       }
+                       if (Modal) {
+                               Control c;
 
-                       if (ApplicationExit != null) {
-                               ApplicationExit(null, EventArgs.Empty);
+                               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
                        }
-               }
 
-               public static void Run(Form mainForm) {
-                       mainForm.CreateControl();
-                       Run(new ApplicationContext(mainForm));
-               }
+                       #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;
+                       }
 
-               public static void Run(ApplicationContext context) {
-                       app_context=context;
-                       if (app_context.MainForm!=null) {
-                               app_context.MainForm.Show();
-                               app_context.MainForm.PerformLayout();
-                               app_context.ThreadExit += new EventHandler(InternalExit);
+                       if (!Modal) {
+                               thread.Exit();
                        }
-                       Run();
                }
+
                #endregion      // Public Static Methods
 
                #region Events