* Application.cs: Try to avoid NRE when Assembly.GetEntryAssembly is
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / Application.cs
1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
8 // 
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
11 // 
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19 //
20 // Copyright (c) 2004 - 2006 Novell, Inc.
21 //
22 // Authors:
23 //      Peter Bartok    pbartok@novell.com
24 //      Daniel Nauck    (dna(at)mono-project(dot)de)
25 //
26
27 // COMPLETE
28
29 #undef DebugRunLoop
30
31 using Microsoft.Win32;
32 using System;
33 using System.Drawing;
34 using System.ComponentModel;
35 using System.Collections;
36 using System.Diagnostics;
37 using System.Globalization;
38 using System.IO;
39 using System.Reflection;
40 using System.Runtime.InteropServices;
41 using System.Threading;
42 #if NET_2_0
43 using System.Text;
44 using System.Windows.Forms.VisualStyles;
45 #endif
46
47 namespace System.Windows.Forms {
48         public sealed class Application {
49                 internal class MWFThread {
50                         #region Fields
51                         private ApplicationContext      context;
52                         private bool                    messageloop_started;
53                         private bool                    handling_exception;
54                         private int                     thread_id;
55
56                         private static Hashtable        threads = new Hashtable();
57                         #endregion      // Fields
58
59                         #region Constructors
60                         private MWFThread() {
61                         }
62                         #endregion      // Constructors
63
64                         #region Properties
65                         public ApplicationContext Context {
66                                 get { return context; }
67                                 set { context = value; }
68                         }
69
70                         public bool MessageLoop {
71                                 get { return messageloop_started; }
72                                 set { messageloop_started = value; }
73                         }
74
75                         public bool HandlingException {
76                                 get { return handling_exception; }
77                                 set { handling_exception = value; }
78
79                         }
80
81                         public static int LoopCount {
82                                 get {
83                                         lock (threads) {
84                                                 int loops = 0;
85
86                                                 foreach (MWFThread thread in threads.Values) {
87                                                         if (thread.messageloop_started)
88                                                                 loops++;
89                                                 }
90
91                                                 return loops;
92                                         }
93                                 }
94                         }
95
96                         public static MWFThread Current {
97                                 get {
98                                         MWFThread       thread;
99
100                                         thread = null;
101                                         lock (threads) {
102                                                 thread = (MWFThread)threads[Thread.CurrentThread.GetHashCode()];
103                                                 if (thread == null) {
104                                                         thread = new MWFThread();
105                                                         thread.thread_id = Thread.CurrentThread.GetHashCode();
106                                                         threads[thread.thread_id] = thread;
107                                                 }
108                                         }
109
110                                         return thread;
111                                 }
112                         }
113                         #endregion      // Properties
114
115                         #region Methods
116                         public void Exit() {
117                                 if (context != null) {
118                                         context.ExitThread();
119                                 }
120                                 context = null;
121
122                                 if (Application.ThreadExit != null) {
123                                         Application.ThreadExit(null, EventArgs.Empty);
124                                 }
125
126                                 if (LoopCount == 0) {
127                                         if (Application.ApplicationExit != null) {
128                                                 Application.ApplicationExit(null, EventArgs.Empty);
129                                         }
130                                 }
131
132                                 ((MWFThread)threads[thread_id]).MessageLoop = false;
133                         }
134                         #endregion      // Methods
135                 }
136
137                 private static bool                     browser_embedded        = false;
138                 private static InputLanguage            input_language          = InputLanguage.CurrentInputLanguage;
139                 private static string                   safe_caption_format     = "{1} - {0} - {2}";
140                 private static ArrayList                message_filters         = new ArrayList();
141                 private static FormCollection           forms                   = new FormCollection ();
142                 private static bool                     use_wait_cursor         = false;
143
144 #if NET_2_0
145                 private static ToolStrip keyboard_capture;
146                 private static VisualStyleState visual_style_state = VisualStyleState.ClientAndNonClientAreasEnabled;
147 #endif
148
149                 private Application () {
150                 }
151
152                 #region Private Methods
153                 internal static void CloseForms(Thread thread) {
154                         #if DebugRunLoop
155                                 Console.WriteLine("   CloseForms({0}) called", thread);
156                         #endif
157
158                         ArrayList forms_to_close = new ArrayList ();
159
160                         lock (forms) {
161                                 foreach (Form f in forms) {
162                                         if (thread == null || thread == f.creator_thread)
163                                                 forms_to_close.Add (f);
164                                 }
165
166                                 foreach (Form f in forms_to_close) {
167                                         #if DebugRunLoop
168                                                 Console.WriteLine("      Closing form {0}", f);
169                                         #endif
170                                         f.Dispose ();
171                                 }
172                         }
173                 }
174                 #endregion      // Private methods
175
176                 #region Public Static Properties
177                 public static bool AllowQuit {
178                         get {
179                                 return !browser_embedded;
180                         }
181                 }
182
183                 public static string CommonAppDataPath {
184                         get {
185                                 return Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
186                         }
187                 }
188
189                 public static RegistryKey CommonAppDataRegistry {
190                         get {
191                                 RegistryKey     key;
192
193                                 key = Registry.LocalMachine.OpenSubKey("Software\\" + Application.CompanyName + "\\" + Application.ProductName + "\\" + Application.ProductVersion, true);
194
195                                 return key;
196                         }
197                 }
198
199                 public static string CompanyName {
200                         get {
201                                 Assembly assembly = Assembly.GetEntryAssembly ();
202                                 if (assembly == null)
203                                         assembly = Assembly.GetCallingAssembly ();
204                                 if (assembly == null)
205                                         return string.Empty;
206                                         
207                                 AssemblyCompanyAttribute[] attrs = (AssemblyCompanyAttribute[]) assembly.GetCustomAttributes(typeof(AssemblyCompanyAttribute), true);
208                                 
209                                 if ((attrs != null) && attrs.Length>0) {
210                                         return attrs[0].Company;
211                                 }
212
213                                 return assembly.GetName().Name;
214                         }
215                 }
216
217                 public static CultureInfo CurrentCulture {
218                         get {
219                                 return Thread.CurrentThread.CurrentUICulture;
220                         }
221
222                         set {
223                                 
224                                 Thread.CurrentThread.CurrentUICulture=value;
225                         }
226                 }
227
228                 public static InputLanguage CurrentInputLanguage {
229                         get {
230                                 return input_language;
231                         }
232
233                         set {
234                                 input_language=value;
235                         }
236                 }
237
238                 public static string ExecutablePath {
239                         get {
240                                 return Assembly.GetEntryAssembly().Location;
241                         }
242                 }
243
244                 public static string LocalUserAppDataPath {
245                         get {
246                                 return Path.Combine(Path.Combine(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), CompanyName), ProductName), ProductVersion);
247                         }
248                 }
249
250                 public static bool MessageLoop {
251                         get {
252                                 return MWFThread.Current.MessageLoop;
253                         }
254                 }
255
256                 public static string ProductName {
257                         get {
258                                 Assembly assembly = Assembly.GetEntryAssembly ();
259                                 if (assembly == null)
260                                         assembly = Assembly.GetCallingAssembly ();
261                                 if (assembly == null)
262                                         return string.Empty;
263                                         
264                                 AssemblyProductAttribute[] attrs = (AssemblyProductAttribute[]) assembly.GetCustomAttributes(typeof(AssemblyProductAttribute), true);
265                                 
266                                 if ((attrs != null) && attrs.Length>0) {
267                                         return attrs[0].Product;
268                                 }
269
270                                 return assembly.GetName ().Name;
271                         }
272                 }
273
274                 public static string ProductVersion {
275                         get {
276                                 String version;
277
278                                 Assembly assembly = Assembly.GetEntryAssembly ();
279                                 if (assembly == null)
280                                         assembly = Assembly.GetCallingAssembly ();
281                                 if (assembly == null)
282                                         return string.Empty;
283
284                                 version = assembly.GetName ().Version.ToString ();
285
286                                 return version;
287                         }
288                 }
289
290                 public static string SafeTopLevelCaptionFormat {
291                         get {
292                                 return safe_caption_format;
293                         }
294
295                         set {
296                                 safe_caption_format=value;
297                         }
298                 }
299
300                 public static string StartupPath {
301                         get {
302                                 return Path.GetDirectoryName(Application.ExecutablePath);
303                         }
304                 }
305
306                 public static string UserAppDataPath {
307                         get {
308                                 return Path.Combine(Path.Combine(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), CompanyName), ProductName), ProductVersion);
309                         }
310                 }
311
312                 public static RegistryKey UserAppDataRegistry {
313                         get {
314                                 RegistryKey     key;
315
316                                 key = Registry.CurrentUser.OpenSubKey("Software\\" + Application.CompanyName + "\\" + Application.ProductName + "\\" + Application.ProductVersion, true);
317
318                                 return key;
319                         }
320                 }
321
322 #if NET_2_0
323
324                 public static bool UseWaitCursor {
325                         get {
326                                 return use_wait_cursor;
327                         }
328                         set {
329                                 use_wait_cursor = value;
330                                 if (use_wait_cursor) {
331                                         foreach (Form form in OpenForms) {
332                                                 form.Cursor = Cursors.WaitCursor;
333                                         }
334                                 }
335                         }
336                 }
337                 
338                 public static bool RenderWithVisualStyles {
339                       get {
340                                 if (VisualStyleInformation.IsSupportedByOS)
341                                 {
342                                         if (!VisualStyleInformation.IsEnabledByUser)
343                                                 return false;
344                                   
345                                         if (!XplatUI.ThemesEnabled)
346                                                 return false;
347                                   
348                                         if (Application.VisualStyleState == VisualStyleState.ClientAndNonClientAreasEnabled)
349                                                 return true;
350                                   
351                                         if (Application.VisualStyleState == VisualStyleState.ClientAreaEnabled)
352                                                 return true;
353                                 }
354                                 
355                                 return false;
356                       }
357                 }
358
359                 public static VisualStyleState VisualStyleState {
360                         get { return Application.visual_style_state; }
361                         set { Application.visual_style_state = value; }
362                 }
363 #endif
364
365                 #endregion
366
367                 #region Public Static Methods
368                 public static void AddMessageFilter(IMessageFilter value) {
369                         lock (message_filters) {
370                                 message_filters.Add(value);
371                         }
372                 }
373
374                 public static void DoEvents() {
375                         XplatUI.DoEvents();
376                 }
377
378                 public static void EnableVisualStyles() {
379                         XplatUI.EnableThemes();
380                 }
381
382 #if NET_2_0
383                 //
384                 // If true, it uses GDI+, performance reasons were quoted
385                 //
386                 static internal bool use_compatible_text_rendering = true;
387                 
388                 public static void SetCompatibleTextRenderingDefault (bool defaultValue)
389                 {
390                         use_compatible_text_rendering = defaultValue;
391                 }
392
393                 public static FormCollection OpenForms {
394                         get {
395                                 return forms;
396                         }
397                 }
398
399                 [MonoNotSupported ("Empty stub.")]
400                 public static void SetUnhandledExceptionMode (UnhandledExceptionMode mode)
401                 {
402                         //FIXME: a stub to fill
403                 }
404
405                 public static void RaiseIdle (EventArgs e)
406                 {
407                         XplatUI.RaiseIdle (e);
408                 }
409                 
410                 public static void Restart ()
411                 {
412                         //FIXME: ClickOnce stuff using the Update or UpdateAsync methods.
413                         //FIXME: SecurityPermission: Restart () requires IsUnrestricted permission.
414
415                         if (Assembly.GetEntryAssembly () == null)
416                                 throw new NotSupportedException ("The method 'Restart' is not supported by this application type.");
417
418                         string mono_path = null;
419
420                         //Get mono path
421                         PropertyInfo gac = typeof (Environment).GetProperty ("GacPath", BindingFlags.Static | BindingFlags.NonPublic);
422                         MethodInfo get_gac = null;
423                         if (gac != null)
424                                 get_gac = gac.GetGetMethod (true);
425
426                         if (get_gac != null) {
427                                 string gac_path = Path.GetDirectoryName ((string)get_gac.Invoke (null, null));
428                                 string mono_prefix = Path.GetDirectoryName (Path.GetDirectoryName (gac_path));
429
430                                 if (Environment.OSVersion.Platform == PlatformID.Unix) {
431                                         mono_path = Path.Combine (mono_prefix, "bin/mono");
432                                         if (!File.Exists (mono_path))
433                                                 mono_path = "mono";
434                                 }
435                                 else {
436                                         mono_path = Path.Combine (mono_prefix, "bin\\mono.bat");
437
438                                         if (!File.Exists (mono_path))
439                                                 mono_path = Path.Combine (mono_prefix, "bin\\mono.exe");
440
441                                         if (!File.Exists (mono_path))
442                                                 mono_path = Path.Combine (mono_prefix, "mono\\mono\\mini\\mono.exe");
443
444                                         if (!File.Exists (mono_path))
445                                                 throw new FileNotFoundException (string.Format ("Windows mono path not found: '{0}'", mono_path));
446                                 }
447                         }
448
449                         //Get command line arguments
450                         StringBuilder argsBuilder = new StringBuilder ();
451                         string[] args = Environment.GetCommandLineArgs ();
452                         for (int i = 0; i < args.Length; i++)
453                         {
454                                 argsBuilder.Append (string.Format ("\"{0}\" ", args[i]));
455                         }
456                         string arguments = argsBuilder.ToString ();
457                         ProcessStartInfo procInfo = Process.GetCurrentProcess ().StartInfo;
458
459                         if (mono_path == null) { //it is .NET on Windows
460                                 procInfo.FileName = args[0];
461                                 procInfo.Arguments = arguments.Remove (0, args[0].Length + 3); //1 space and 2 quotes
462                         }
463                         else {
464                                 procInfo.Arguments = arguments;
465                                 procInfo.FileName = mono_path;
466                         }
467
468                         procInfo.WorkingDirectory = Environment.CurrentDirectory;
469
470                         Application.Exit ();
471                         Process.Start (procInfo);
472                 }
473 #endif
474
475                 public static void Exit() {
476                         XplatUI.PostQuitMessage(0);
477                         CloseForms(null);
478                 }
479
480                 public static void ExitThread() {
481                         CloseForms(Thread.CurrentThread);
482                         // this might not be right - need to investigate (somehow) if a WM_QUIT message is generated here
483                         XplatUI.PostQuitMessage(0);
484                 }
485
486                 public static ApartmentState OleRequired() {
487                         //throw new NotImplementedException("OLE Not supported by this System.Windows.Forms implementation");
488                         return ApartmentState.Unknown;
489                 }
490
491                 public static void OnThreadException(Exception t) {
492                         if (MWFThread.Current.HandlingException) {
493                                 /* we're already handling an exception and we got
494                                    another one?  print it out and exit, this means
495                                    we've got a runtime/SWF bug. */
496                                 Console.WriteLine (t);
497                                 // Don't use Application.Exit here, since it may cause a stack overflow
498                                 // in certain cases. It's however hard to reproduce since it seems to 
499                                 // be depending on when the GC kicks in.
500                                 Environment.Exit(1);
501                         }
502
503                         try {
504                                 MWFThread.Current.HandlingException = true;
505
506                                 if (Application.ThreadException != null) {
507                                         Application.ThreadException(null, new ThreadExceptionEventArgs(t));
508                                         return;
509                                 }
510
511                                 if (SystemInformation.UserInteractive) {
512                                         Form form = new ThreadExceptionDialog (t);
513                                         form.ShowDialog ();
514                                 } else {
515                                         Console.WriteLine (t.ToString ());
516                                         Application.Exit ();
517                                 }
518                         } finally {
519                                 MWFThread.Current.HandlingException = false;
520                         }
521                 }
522
523                 public static void RemoveMessageFilter(IMessageFilter filter) {
524                         lock (message_filters) {
525                                 message_filters.Remove(filter);
526                         }
527                 }
528
529                 public static void Run() {
530                         RunLoop(false, new ApplicationContext());
531                 }
532
533                 public static void Run(Form mainForm) {
534                         RunLoop(false, new ApplicationContext(mainForm));
535                 }
536
537                 public static void Run(ApplicationContext context) {
538                         RunLoop(false, context);
539                 }
540
541                 private static void DisableFormsForModalLoop (Queue toplevels, ApplicationContext context)
542                 {
543                         Form f;
544
545                         lock (forms) {
546                                 IEnumerator control = forms.GetEnumerator ();
547
548                                 while (control.MoveNext ()) {
549                                         f = (Form)control.Current;
550
551                                         // Don't disable the main form.
552                                         if (f == context.MainForm) {
553                                                 continue;
554                                         }
555
556                                         // Don't disable any children of the main form.
557                                         // These do not have to be MDI children.
558                                         Control current = f;
559                                         bool is_child_of_main = false; ;
560
561                                         do {
562                                                 if (current.Parent == context.MainForm) {
563                                                         is_child_of_main = true;
564                                                         break;
565                                                 }
566                                                 current = current.Parent;
567                                         } while (current != null);
568
569                                         if (is_child_of_main)
570                                                 continue;
571
572                                         // Disable the rest
573                                         if (f.IsHandleCreated && XplatUI.IsEnabled (f.Handle)) {
574 #if DebugRunLoop
575                                                                 Console.WriteLine("      Disabling form {0}", f);
576 #endif
577                                                 XplatUI.EnableWindow (f.Handle, false);
578                                                 toplevels.Enqueue (f);
579                                         }
580                                 }
581                         }
582                                 
583                 }
584                 
585                 
586                 private static void EnableFormsForModalLoop (Queue toplevels, ApplicationContext context)
587                 {
588                         while (toplevels.Count > 0) {
589 #if DebugRunLoop
590                                                 Console.WriteLine("      Re-Enabling form form {0}", toplevels.Peek());
591 #endif
592                                 Form c = (Form)toplevels.Dequeue ();
593                                 if (c.IsHandleCreated) {
594                                         XplatUI.EnableWindow (c.window.Handle, true);
595                                         context.MainForm = c;
596                                 }
597                         }
598 #if DebugRunLoop
599                                         Console.WriteLine("   Done with the re-enable");
600 #endif
601                 }
602
603                 internal static void RunLoop(bool Modal, ApplicationContext context) {
604                         Queue           toplevels;
605                         MSG             msg;
606                         Object          queue_id;
607                         MWFThread       thread;
608                         ApplicationContext previous_thread_context;
609                         
610                         thread = MWFThread.Current;
611
612                         /*
613                          * There is a NotWorking test for this, but since we are using this method both for Form.ShowDialog as for ApplicationContexts we'll
614                          * fail on nested ShowDialogs, so disable the check for the moment.
615                          */
616                         //if (thread.MessageLoop) {
617                         //        throw new InvalidOperationException ("Starting a second message loop on a single thread is not a valid operation. Use Form.ShowDialog instead.");
618                         //}
619
620                         msg = new MSG();
621
622                         if (context == null) {
623                                 context = new ApplicationContext();
624                         }
625                 
626                         previous_thread_context = thread.Context;
627                         thread.Context = context;
628
629                         if (context.MainForm != null) {
630                                 context.MainForm.is_modal = Modal;
631                                 context.MainForm.context = context;
632                                 context.MainForm.closing = false;
633                                 context.MainForm.Visible = true;        // Cannot use Show() or scaling gets confused by menus
634                                 // XXX the above line can be used to close the form. another problem with our handling of Show/Activate.
635                                 if (context.MainForm != null)
636                                         context.MainForm.Activate();
637                         }
638
639                         #if DebugRunLoop
640                                 Console.WriteLine("Entering RunLoop(Modal={0}, Form={1})", Modal, context.MainForm != null ? context.MainForm.ToString() : "NULL");
641                         #endif
642
643                         if (Modal) {
644                                 toplevels = new Queue ();
645                                 DisableFormsForModalLoop (toplevels, context);
646                                 
647                                 // FIXME - need activate?
648                                 /* make sure the MainForm is enabled */
649                                 if (context.MainForm != null) {
650                                         XplatUI.EnableWindow (context.MainForm.Handle, true);
651                                         XplatUI.SetModal(context.MainForm.Handle, true);
652                                 }
653                         } else {
654                                 toplevels = null;
655                         }
656
657                         queue_id = XplatUI.StartLoop(Thread.CurrentThread);
658                         thread.MessageLoop = true;
659
660                         bool quit = false;
661
662                         while (!quit && XplatUI.GetMessage(queue_id, ref msg, IntPtr.Zero, 0, 0)) {
663                                 lock (message_filters) {
664                                         if (message_filters.Count > 0) {
665                                                 Message m;
666                                                 bool    drop;
667
668                                                 drop = false;
669                                                 m = Message.Create(msg.hwnd, (int)msg.message, msg.wParam, msg.lParam);
670                                                 for (int i = 0; i < message_filters.Count; i++) {
671                                                         if (((IMessageFilter)message_filters[i]).PreFilterMessage(ref m)) {
672                                                                 // we're dropping the message
673                                                                 drop = true;
674                                                                 break;
675                                                         }
676                                                 }
677                                                 if (drop) {
678                                                         continue;
679                                                 }
680                                         }
681                                 }
682
683                                 switch((Msg)msg.message) {
684                                 case Msg.WM_KEYDOWN:
685                                 case Msg.WM_SYSKEYDOWN:
686                                 case Msg.WM_CHAR:
687                                 case Msg.WM_SYSCHAR:
688                                 case Msg.WM_KEYUP:
689                                 case Msg.WM_SYSKEYUP:
690                                         Message m;
691                                         m = Message.Create(msg.hwnd, (int)msg.message, msg.wParam, msg.lParam);
692
693                                         Control c;
694                                         c = Control.FromHandle(msg.hwnd);
695
696 #if NET_2_0
697                                         // If we have a control with keyboard capture (usually a *Strip)
698                                         // give it the message, and then drop the message
699                                         if (keyboard_capture != null) {
700                                                 // WM_SYSKEYUP does not make it into ProcessCmdKey, so do it here
701                                                 if ((Msg)m.Msg == Msg.WM_SYSKEYUP)
702                                                         if (((Keys)m.WParam.ToInt32 () & Keys.Menu) == Keys.Menu) {
703                                                                 keyboard_capture.GetTopLevelToolStrip ().Dismiss (ToolStripDropDownCloseReason.Keyboard);
704                                                                 continue;
705                                                 }
706
707                                                 m.HWnd = keyboard_capture.Handle;
708                                                 
709                                                 if (keyboard_capture.PreProcessControlMessageInternal (ref m) != PreProcessControlState.MessageProcessed && (m.Msg == (int)Msg.WM_KEYDOWN && !keyboard_capture.ProcessControlMnemonic ((char)m.WParam))) {
710                                                         if (c == null || c.Parent == null || !(c.Parent is ToolStrip))
711                                                                 continue;
712                                                         else
713                                                                 m.HWnd = msg.hwnd;
714                                                 } else
715                                                         continue;
716                                         }
717 #endif
718
719                                         if ((c != null) && c.PreProcessControlMessageInternal (ref m) != PreProcessControlState.MessageProcessed) {
720                                                 goto default;
721                                         }
722                                         break;
723 #if NET_2_0
724                                 case Msg.WM_LBUTTONDOWN:
725                                 case Msg.WM_MBUTTONDOWN:
726                                 case Msg.WM_RBUTTONDOWN:
727                                         if (keyboard_capture != null) {
728                                                 Control c2 = Control.FromHandle (msg.hwnd);
729                                                 
730                                                 // If we clicked a ToolStrip, we have to make sure it isn't
731                                                 // the one we are on, or any of its parents or children
732                                                 // If we clicked off the dropped down menu, release everything
733                                                 if (c2 is ToolStrip) {
734                                                         if ((c2 as ToolStrip).GetTopLevelToolStrip () != keyboard_capture.GetTopLevelToolStrip ())
735                                                                 ToolStripManager.FireAppClicked ();
736                                                 } else
737                                                         ToolStripManager.FireAppClicked ();
738                                         }
739                                         
740                                         goto default;
741 #endif
742                                 case Msg.WM_QUIT:
743                                         quit = true; // make sure we exit
744                                         break;
745                                 default:
746                                         XplatUI.TranslateMessage(ref msg);
747                                         XplatUI.DispatchMessage(ref msg);
748                                         break;
749                                 }
750
751                                 // Handle exit, Form might have received WM_CLOSE and set 'closing' in response
752                                 if ((context.MainForm != null) && (context.MainForm.closing || (Modal && !context.MainForm.Visible))) {
753                                         if (!Modal) {
754                                                 XplatUI.PostQuitMessage(0);
755                                         } else {
756                                                 break;
757                                         }
758                                 }
759                         }
760                         #if DebugRunLoop
761                                 Console.WriteLine("   RunLoop loop left");
762                         #endif
763
764                         thread.MessageLoop = false;
765                         XplatUI.EndLoop(Thread.CurrentThread);
766
767                         if (Modal) {
768                                 Form old = context.MainForm;
769
770                                 context.MainForm = null;
771
772                                 EnableFormsForModalLoop (toplevels, context);
773                                 
774                                 if (context.MainForm != null && context.MainForm.IsHandleCreated) {
775                                         XplatUI.SetModal(context.MainForm.Handle, false);
776                                 }
777                                 #if DebugRunLoop
778                                         Console.WriteLine("   Done with the SetModal");
779                                 #endif
780                                 old.RaiseCloseEvents (true);
781                                 old.is_modal = false;
782                         }
783
784                         #if DebugRunLoop
785                                 Console.WriteLine("Leaving RunLoop(Modal={0}, Form={1})", Modal, context.MainForm != null ? context.MainForm.ToString() : "NULL");
786                         #endif
787                         if (context.MainForm != null) {
788                                 context.MainForm.context = null;
789                                 context.MainForm = null;
790                         }
791
792                         thread.Context = previous_thread_context;
793
794                         if (!Modal) {
795                                 thread.Exit();
796                         }
797                 }
798
799                 #endregion      // Public Static Methods
800
801                 #region Events
802                 public static event EventHandler        ApplicationExit;
803
804                 public static event EventHandler        Idle {
805                         add {
806                                 XplatUI.Idle += value;
807                         }
808                         remove {
809                                 XplatUI.Idle -= value;
810                         }
811                 }
812
813                 public static event EventHandler        ThreadExit;
814                 public static event ThreadExceptionEventHandler ThreadException;
815
816 #if NET_2_0
817                 [EditorBrowsable (EditorBrowsableState.Advanced)]
818                 public static event EventHandler EnterThreadModal;
819
820                 [EditorBrowsable (EditorBrowsableState.Advanced)]
821                 public static event EventHandler LeaveThreadModal;
822 #endif
823                 #endregion      // Events
824
825                 #region Internal Properties
826 #if NET_2_0
827                 internal static ToolStrip KeyboardCapture {
828                         get { return keyboard_capture; }
829                         set { keyboard_capture = value; }
830                 }
831 #endif
832                 #endregion
833                 
834                 #region Internal Methods
835                 internal static void AddForm (Form f)
836                 {
837                         lock (forms)
838                                 forms.Add (f);
839                 }
840                 
841                 internal static void RemoveForm (Form f)
842                 {
843                         lock (forms)
844                                 forms.Remove (f);
845                 }
846                 #endregion
847         }
848 }