Halve the stack depth for both the new single step tests
[mono.git] / mcs / class / System.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 using System.Text;
43 using System.Windows.Forms.VisualStyles;
44
45 namespace System.Windows.Forms
46 {
47         public sealed class Application
48         {
49                 internal class MWFThread
50                 {
51                         #region Fields
52
53                         private ApplicationContext context;
54                         private bool messageloop_started;
55                         private bool handling_exception;
56                         private int thread_id;
57
58                         private static readonly Hashtable threads = new Hashtable();
59
60                         #endregion      // Fields
61
62                         #region Constructors
63
64                         private MWFThread()
65                         {
66                         }
67
68                         #endregion      // Constructors
69
70                         #region Properties
71
72                         public ApplicationContext Context {
73                                 get { return context; }
74                                 set { context = value; }
75                         }
76
77                         public bool MessageLoop {
78                                 get { return messageloop_started; }
79                                 set { messageloop_started = value; }
80                         }
81
82                         public bool HandlingException {
83                                 get { return handling_exception; }
84                                 set { handling_exception = value; }
85                         }
86
87                         public static int LoopCount {
88                                 get {
89                                         lock (threads) {
90                                                 int loops = 0;
91
92                                                 foreach (MWFThread thread in threads.Values) {
93                                                         if (thread.messageloop_started)
94                                                                 loops++;
95                                                 }
96
97                                                 return loops;
98                                         }
99                                 }
100                         }
101
102                         public static MWFThread Current {
103                                 get {
104                                         MWFThread thread = null;
105
106                                         lock (threads) {
107                                                 thread = (MWFThread) threads [Thread.CurrentThread.GetHashCode ()];
108                                                 if (thread == null) {
109                                                         thread = new MWFThread();
110                                                         thread.thread_id = Thread.CurrentThread.GetHashCode ();
111                                                         threads [thread.thread_id] = thread;
112                                                 }
113                                         }
114
115                                         return thread;
116                                 }
117                         }
118
119                         #endregion      // Properties
120
121                         #region Methods
122
123                         public void Exit ()
124                         {
125                                 if (context != null)
126                                         context.ExitThread();
127                                 context = null;
128
129                                 if (Application.ThreadExit != null)
130                                         Application.ThreadExit(null, EventArgs.Empty);
131
132                                 if (LoopCount == 0) {
133                                         if (Application.ApplicationExit != null)
134                                                 Application.ApplicationExit (null, EventArgs.Empty);
135                                 }
136
137                                 ((MWFThread) threads [thread_id]).MessageLoop = false;
138                         }
139
140                         #endregion      // Methods
141                 }
142
143                 private static bool browser_embedded;
144                 private static InputLanguage input_language = InputLanguage.CurrentInputLanguage;
145                 private static string safe_caption_format = "{1} - {0} - {2}";
146                 private static readonly ArrayList message_filters = new ArrayList();
147                 private static readonly FormCollection forms = new FormCollection ();
148
149                 private static bool use_wait_cursor;
150                 private static ToolStrip keyboard_capture;
151                 private static VisualStyleState visual_style_state = VisualStyleState.ClientAndNonClientAreasEnabled;
152                 static bool visual_styles_enabled;
153
154                 private Application ()
155                 {
156                         browser_embedded = false;
157                 }
158
159                 static Application ()
160                 {
161                         // Attempt to load UIA support for winforms
162                         // UIA support requires .NET 2.0
163                         InitializeUIAutomation ();
164                 }
165
166                 #region Private Methods
167
168                 private static void InitializeUIAutomation ()
169                 {
170                         // Initialize the UIAutomationWinforms Global class,
171                         // which create some listeners which subscribe to internal
172                         // MWF events so that it can provide a11y support for MWF
173                         const string UIA_WINFORMS_ASSEMBLY = 
174                           "UIAutomationWinforms, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f4ceacb585d99812";
175                         MethodInfo init_method;
176                         Assembly mwf_providers = null;
177                         try {
178                                 mwf_providers = Assembly.Load (UIA_WINFORMS_ASSEMBLY);
179                         } catch { }
180                         
181                         if (mwf_providers == null)
182                                 return;
183
184                         const string UIA_WINFORMS_TYPE     = "Mono.UIAutomation.Winforms.Global";
185                         const string UIA_WINFORMS_METHOD   = "Initialize";
186                         try {
187                                 Type global_type = mwf_providers.GetType (UIA_WINFORMS_TYPE, false);
188                                 if (global_type != null) {
189                                         init_method = global_type.GetMethod (UIA_WINFORMS_METHOD, 
190                                                                              BindingFlags.Static | 
191                                                                              BindingFlags.Public);
192                                         if (init_method != null)
193                                                 init_method.Invoke (null, new object [] {});
194                                         else
195                                                 throw new Exception (String.Format ("Method {0} not found in type {1}.",
196                                                                                     UIA_WINFORMS_METHOD, UIA_WINFORMS_TYPE));
197                                 }
198                                 else
199                                         throw new Exception (String.Format ("Type {0} not found in assembly {1}.",
200                                                                             UIA_WINFORMS_TYPE, UIA_WINFORMS_ASSEMBLY));
201                         } catch (Exception ex) {
202                                 Console.Error.WriteLine ("Error setting up UIA: " + ex);
203                         }
204                 }
205                 
206                 internal static void CloseForms (Thread thread)
207                 {
208                         #if DebugRunLoop
209                                 Console.WriteLine("   CloseForms({0}) called", thread);
210                         #endif
211
212                         ArrayList forms_to_close = new ArrayList ();
213
214                         lock (forms) {
215                                 foreach (Form f in forms) {
216                                         if (thread == null || thread == f.creator_thread)
217                                                 forms_to_close.Add (f);
218                                 }
219
220                                 foreach (Form f in forms_to_close) {
221                                         #if DebugRunLoop
222                                                 Console.WriteLine("      Closing form {0}", f);
223                                         #endif
224                                         f.Dispose ();
225                                 }
226                         }
227                 }
228
229                 #endregion      // Private methods
230
231                 #region Public Static Properties
232
233                 public static bool AllowQuit {
234                         get {
235                                 return !browser_embedded;
236                         }
237                 }
238
239                 public static string CommonAppDataPath {
240                         get {
241                                 return CreateDataPath (Environment.GetFolderPath (Environment.SpecialFolder.CommonApplicationData));
242                         }
243                 }
244
245                 public static RegistryKey CommonAppDataRegistry {
246                         get {
247                                 string key = string.Format ("Software\\{0}\\{1}\\{2}", CompanyName, ProductName, ProductVersion);
248
249                                 return Registry.LocalMachine.CreateSubKey (key);
250                         }
251                 }
252
253                 public static string CompanyName {
254                         get {
255                                 string company = string.Empty;
256
257                                 Assembly assembly = Assembly.GetEntryAssembly ();
258                                 
259                                 if (assembly == null)
260                                         assembly = Assembly.GetCallingAssembly ();
261
262                                 AssemblyCompanyAttribute[] attrs = (AssemblyCompanyAttribute[])
263                                         assembly.GetCustomAttributes (typeof(AssemblyCompanyAttribute), true);
264                                 if (attrs != null && attrs.Length > 0)
265                                         company = attrs [0].Company;
266
267                                 // If there is no [AssemblyCompany], return the outermost namespace
268                                 // on Main ()
269                                 if (company == null || company.Length == 0)
270                                         if (assembly.EntryPoint != null) {
271                                                 company = assembly.EntryPoint.DeclaringType.Namespace;
272
273                                                 if (company != null) {
274                                                         int firstDot = company.IndexOf ('.');
275                                                         if (firstDot >= 0)
276                                                                 company = company.Substring (0, firstDot);
277                                                 }
278                                         }
279
280                                 // If that doesn't work, return the name of class containing Main ()
281                                 if (company == null || company.Length == 0)
282                                         if (assembly.EntryPoint != null)
283                                                 company = assembly.EntryPoint.DeclaringType.FullName;
284                                 
285                                 return company;
286                         }
287                 }
288
289                 public static CultureInfo CurrentCulture {
290                         get {
291                                 return Thread.CurrentThread.CurrentUICulture;
292                         }
293                         set {
294                                 Thread.CurrentThread.CurrentUICulture = value;
295                         }
296                 }
297
298                 public static InputLanguage CurrentInputLanguage {
299                         get {
300                                 return input_language;
301                         }
302                         set {
303                                 input_language=value;
304                         }
305                 }
306
307                 public static string ExecutablePath {
308                         get {
309                                 return Path.GetFullPath (Environment.GetCommandLineArgs ()[0]);
310                         }
311                 }
312
313                 public static string LocalUserAppDataPath {
314                         get {
315                                 return CreateDataPath (Environment.GetFolderPath (Environment.SpecialFolder.LocalApplicationData));
316                         }
317                 }
318
319                 public static bool MessageLoop {
320                         get {
321                                 return MWFThread.Current.MessageLoop;
322                         }
323                 }
324
325                 public static string ProductName {
326                         get {
327                                 string name = string.Empty;
328                                 
329                                 Assembly assembly = Assembly.GetEntryAssembly ();
330                                 
331                                 if (assembly == null)
332                                         assembly = Assembly.GetCallingAssembly ();
333
334                                 AssemblyProductAttribute[] attrs = (AssemblyProductAttribute[])
335                                         assembly.GetCustomAttributes (typeof(AssemblyProductAttribute), true);
336
337                                 if (attrs != null && attrs.Length > 0)
338                                         name = attrs [0].Product;
339
340                                 // If there is no [AssemblyProduct], .NET returns the name of 
341                                 // the innermost namespace and if that fails, resorts to the 
342                                 // name of the class containing Main ()
343                                 if (name == null || name.Length == 0)
344                                         if (assembly.EntryPoint != null) {
345                                                 name = assembly.EntryPoint.DeclaringType.Namespace;
346
347                                                 if (name != null) {
348                                                         int lastDot = name.LastIndexOf ('.');
349                                                         if (lastDot >= 0 && lastDot < name.Length - 1)
350                                                                 name = name.Substring (lastDot + 1);
351                                                 }
352
353                                                 if (name == null || name.Length == 0)
354                                                         name = assembly.EntryPoint.DeclaringType.FullName;
355                                         }
356
357                                 return name;
358                         }
359                 }
360
361                 public static string ProductVersion {
362                         get {
363                                 String version = string.Empty;
364
365                                 Assembly assembly = Assembly.GetEntryAssembly ();
366                                 
367                                 if (assembly == null)
368                                         assembly = Assembly.GetCallingAssembly ();
369
370                                 AssemblyInformationalVersionAttribute infoVersion =
371                                         Attribute.GetCustomAttribute (assembly,
372                                         typeof (AssemblyInformationalVersionAttribute))
373                                         as AssemblyInformationalVersionAttribute;
374                                         
375                                 if (infoVersion != null)
376                                         version = infoVersion.InformationalVersion;
377
378                                 // If [AssemblyFileVersion] is present it is used
379                                 // before resorting to assembly version
380                                 if (version == null || version.Length == 0) {
381                                         AssemblyFileVersionAttribute fileVersion =
382                                                 Attribute.GetCustomAttribute (assembly,
383                                                 typeof (AssemblyFileVersionAttribute))
384                                                 as AssemblyFileVersionAttribute;
385                                         if (fileVersion != null)
386                                                 version = fileVersion.Version;
387                                 }
388
389                                 // If neither [AssemblyInformationalVersionAttribute]
390                                 // nor [AssemblyFileVersion] are present, then use
391                                 // the assembly version
392                                 if (version == null || version.Length == 0)
393                                         version = assembly.GetName ().Version.ToString ();
394
395                                 return version;
396                         }
397                 }
398
399                 public static string SafeTopLevelCaptionFormat {
400                         get {
401                                 return safe_caption_format;
402                         }
403                         set {
404                                 safe_caption_format = value;
405                         }
406                 }
407
408                 public static string StartupPath {
409                         get {
410                                 return Path.GetDirectoryName (Application.ExecutablePath);
411                         }
412                 }
413
414                 public static string UserAppDataPath {
415                         get {
416                                 return CreateDataPath (Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData));
417                         }
418                 }
419
420                 public static RegistryKey UserAppDataRegistry {
421                         get {
422                                 string key = string.Format ("Software\\{0}\\{1}\\{2}", CompanyName, ProductName, ProductVersion);
423                                 
424                                 return Registry.CurrentUser.CreateSubKey (key);
425                         }
426                 }
427
428                 public static bool UseWaitCursor {
429                         get {
430                                 return use_wait_cursor;
431                         }
432                         set {
433                                 use_wait_cursor = value;
434                                 if (use_wait_cursor) {
435                                         foreach (Form form in OpenForms) {
436                                                 form.Cursor = Cursors.WaitCursor;
437                                         }
438                                 }
439                         }
440                 }
441
442                 public static bool RenderWithVisualStyles {
443                         get {
444                                 if (VisualStyleInformation.IsSupportedByOS) {
445                                         if (!VisualStyleInformation.IsEnabledByUser)
446                                                 return false;
447                                         if (!XplatUI.ThemesEnabled)
448                                                 return false;
449                                         if (Application.VisualStyleState == VisualStyleState.ClientAndNonClientAreasEnabled)
450                                                 return true;
451                                         if (Application.VisualStyleState == VisualStyleState.ClientAreaEnabled)
452                                                 return true;
453                                 }
454                                 return false;
455                         }
456                 }
457
458                 public static VisualStyleState VisualStyleState {
459                         get { return Application.visual_style_state; }
460                         set { Application.visual_style_state = value; }
461                 }
462
463                 #endregion
464
465                 #region Public Static Methods
466
467                 public static void AddMessageFilter (IMessageFilter value)
468                 {
469                         lock (message_filters) {
470                                 message_filters.Add (value);
471                         }
472                 }
473
474                 internal static void AddKeyFilter (IKeyFilter value)
475                 {
476                         XplatUI.AddKeyFilter (value);
477                 }
478
479                 public static void DoEvents ()
480                 {
481                         XplatUI.DoEvents ();
482                 }
483
484                 public static void EnableVisualStyles ()
485                 {
486                         visual_styles_enabled = true;
487                         XplatUI.EnableThemes ();
488                 }
489
490                 [EditorBrowsable (EditorBrowsableState.Advanced)]
491                 public static bool FilterMessage (ref Message message)
492                 {
493                         lock (message_filters) {
494                                 for (int i = 0; i < message_filters.Count; i++) {
495                                         IMessageFilter filter = (IMessageFilter) message_filters[i];
496                                         if (filter.PreFilterMessage (ref message))
497                                                 return true;
498                                 }
499                         }
500                         return false;
501                 }
502                 
503                 //
504                 // If true, it uses GDI+, performance reasons were quoted
505                 //
506                 static internal bool use_compatible_text_rendering = true;
507                 
508                 public static void SetCompatibleTextRenderingDefault (bool defaultValue)
509                 {
510                         use_compatible_text_rendering = defaultValue;
511                 }
512
513                 public static FormCollection OpenForms {
514                         get {
515                                 return forms;
516                         }
517                 }
518                 
519                 [MonoNotSupported ("Only applies when Winforms is being hosted by an unmanaged app.")]
520                 [EditorBrowsable (EditorBrowsableState.Advanced)]
521                 public static void RegisterMessageLoop (MessageLoopCallback callback)
522                 {
523                 }
524
525                 [MonoNotSupported ("Empty stub.")]
526                 public static bool SetSuspendState (PowerState state, bool force, bool disableWakeEvent)
527                 {
528                         return false;
529                 }
530
531                 [MonoNotSupported ("Empty stub.")]
532                 public static void SetUnhandledExceptionMode (UnhandledExceptionMode mode)
533                 {
534                         //FIXME: a stub to fill
535                 }
536
537                 [MonoNotSupported ("Empty stub.")]
538                 public static void SetUnhandledExceptionMode (UnhandledExceptionMode mode, bool threadScope)
539                 {
540                         //FIXME: a stub to fill
541                 }
542
543                 [MonoNotSupported ("Only applies when Winforms is being hosted by an unmanaged app.")]
544                 [EditorBrowsable (EditorBrowsableState.Advanced)]
545                 public static void UnregisterMessageLoop ()
546                 {
547                 }
548
549                 [EditorBrowsable (EditorBrowsableState.Advanced)]
550                 public static void RaiseIdle (EventArgs e)
551                 {
552                         XplatUI.RaiseIdle (e);
553                 }
554                 
555                 public static void Restart ()
556                 {
557                         //FIXME: ClickOnce stuff using the Update or UpdateAsync methods.
558                         //FIXME: SecurityPermission: Restart () requires IsUnrestricted permission.
559
560                         if (Assembly.GetEntryAssembly () == null)
561                                 throw new NotSupportedException ("The method 'Restart' is not supported by this application type.");
562
563                         string mono_path = null;
564
565                         //Get mono path
566                         PropertyInfo gac = typeof (Environment).GetProperty ("GacPath", BindingFlags.Static | BindingFlags.NonPublic);
567                         MethodInfo get_gac = null;
568                         if (gac != null)
569                                 get_gac = gac.GetGetMethod (true);
570
571                         if (get_gac != null) {
572                                 string gac_path = Path.GetDirectoryName ((string)get_gac.Invoke (null, null));
573                                 string mono_prefix = Path.GetDirectoryName (Path.GetDirectoryName (gac_path));
574
575                                 if (XplatUI.RunningOnUnix) {
576                                         mono_path = Path.Combine (mono_prefix, "bin/mono");
577                                         if (!File.Exists (mono_path))
578                                                 mono_path = "mono";
579                                 } else {
580                                         mono_path = Path.Combine (mono_prefix, "bin\\mono.bat");
581
582                                         if (!File.Exists (mono_path))
583                                                 mono_path = Path.Combine (mono_prefix, "bin\\mono.exe");
584
585                                         if (!File.Exists (mono_path))
586                                                 mono_path = Path.Combine (mono_prefix, "mono\\mono\\mini\\mono.exe");
587
588                                         if (!File.Exists (mono_path))
589                                                 throw new FileNotFoundException (string.Format ("Windows mono path not found: '{0}'", mono_path));
590                                 }
591                         }
592
593                         //Get command line arguments
594                         StringBuilder argsBuilder = new StringBuilder ();
595                         string[] args = Environment.GetCommandLineArgs ();
596                         for (int i = 0; i < args.Length; i++)
597                         {
598                                 argsBuilder.Append (string.Format ("\"{0}\" ", args[i]));
599                         }
600                         string arguments = argsBuilder.ToString ();
601                         ProcessStartInfo procInfo = Process.GetCurrentProcess ().StartInfo;
602
603                         if (mono_path == null) { //it is .NET on Windows
604                                 procInfo.FileName = args[0];
605                                 procInfo.Arguments = arguments.Remove (0, args[0].Length + 3); //1 space and 2 quotes
606                         }
607                         else {
608                                 procInfo.Arguments = arguments;
609                                 procInfo.FileName = mono_path;
610                         }
611
612                         procInfo.WorkingDirectory = Environment.CurrentDirectory;
613
614                         Application.Exit ();
615                         Process.Start (procInfo);
616                 }
617
618                 public static void Exit ()
619                 {
620                         Exit (new CancelEventArgs ());
621                 }
622
623                 [EditorBrowsable (EditorBrowsableState.Advanced)]
624                 public static void Exit (CancelEventArgs e)
625                 {
626                         ArrayList forms_to_close;
627                         
628                         lock (forms) {
629                                 forms_to_close = new ArrayList (forms);
630
631                                 foreach (Form f in forms_to_close) {
632                                         // Give each form a chance to cancel the Application.Exit
633                                         e.Cancel = f.FireClosingEvents (CloseReason.ApplicationExitCall, false);
634
635                                         if (e.Cancel)
636                                                 return;
637
638                                         f.suppress_closing_events = true;
639                                         f.Close ();
640                                         f.Dispose ();
641                                 }
642                         }
643
644                         XplatUI.PostQuitMessage (0);
645                 }
646
647                 public static void ExitThread()
648                 {
649                         CloseForms(Thread.CurrentThread);
650                         // this might not be right - need to investigate (somehow) if a WM_QUIT message is generated here
651                         XplatUI.PostQuitMessage(0);
652                 }
653
654                 public static ApartmentState OleRequired ()
655                 {
656                         //throw new NotImplementedException("OLE Not supported by this System.Windows.Forms implementation");
657                         return ApartmentState.Unknown;
658                 }
659
660                 public static void OnThreadException (Exception t)
661                 {
662                         if (MWFThread.Current.HandlingException) {
663                                 /* we're already handling an exception and we got
664                                    another one?  print it out and exit, this means
665                                    we've got a runtime/SWF bug. */
666                                 Console.WriteLine (t);
667                                 // Don't use Application.Exit here, since it may cause a stack overflow
668                                 // in certain cases. It's however hard to reproduce since it seems to 
669                                 // be depending on when the GC kicks in.
670                                 Environment.Exit(1);
671                         }
672
673                         try {
674                                 MWFThread.Current.HandlingException = true;
675
676                                 if (Application.ThreadException != null) {
677                                         Application.ThreadException(null, new ThreadExceptionEventArgs(t));
678                                         return;
679                                 }
680
681                                 if (SystemInformation.UserInteractive) {
682                                         Form form = new ThreadExceptionDialog (t);
683                                         form.ShowDialog ();
684                                 } else {
685                                         Console.WriteLine (t.ToString ());
686                                         Application.Exit ();
687                                 }
688                         } finally {
689                                 MWFThread.Current.HandlingException = false;
690                         }
691                 }
692
693                 public static void RemoveMessageFilter (IMessageFilter value)
694                 {
695                         lock (message_filters) {
696                                 message_filters.Remove (value);
697                         }
698                 }
699
700                 public static void Run ()
701                 {
702                         Run (new ApplicationContext ());
703                 }
704
705                 public static void Run (Form mainForm)
706                 {
707                         Run (new ApplicationContext (mainForm));
708                 }
709
710                 internal static void FirePreRun ()
711                 {
712                         EventHandler handler = PreRun;
713                         if (handler != null)
714                                 handler (null, EventArgs.Empty);
715                 }
716
717                 public static void Run (ApplicationContext context)
718                 {
719                         // If a sync context hasn't been created by now, create
720                         // a default one
721                         if (SynchronizationContext.Current == null)
722                                 SynchronizationContext.SetSynchronizationContext (new SynchronizationContext ());
723                                 
724                         RunLoop (false, context);
725                         
726                         // Reset the sync context back to the default
727                         if (SynchronizationContext.Current is WindowsFormsSynchronizationContext)
728                                 WindowsFormsSynchronizationContext.Uninstall ();
729                 }
730
731                 private static void DisableFormsForModalLoop (Queue toplevels, ApplicationContext context)
732                 {
733                         Form f;
734
735                         lock (forms) {
736                                 IEnumerator control = forms.GetEnumerator ();
737
738                                 while (control.MoveNext ()) {
739                                         f = (Form)control.Current;
740
741                                         // Don't disable the main form.
742                                         if (f == context.MainForm) {
743                                                 continue;
744                                         }
745
746                                         // Don't disable any children of the main form.
747                                         // These do not have to be MDI children.
748                                         Control current = f;
749                                         bool is_child_of_main = false; ;
750
751                                         do {
752                                                 if (current.Parent == context.MainForm) {
753                                                         is_child_of_main = true;
754                                                         break;
755                                                 }
756                                                 current = current.Parent;
757                                         } while (current != null);
758
759                                         if (is_child_of_main)
760                                                 continue;
761
762                                         // Disable the rest
763                                         if (f.IsHandleCreated && XplatUI.IsEnabled (f.Handle)) {
764 #if DebugRunLoop
765                                                 Console.WriteLine("      Disabling form {0}", f);
766 #endif
767                                                 XplatUI.EnableWindow (f.Handle, false);
768                                                 toplevels.Enqueue (f);
769                                         }
770                                 }
771                         }
772                                 
773                 }
774                 
775                 
776                 private static void EnableFormsForModalLoop (Queue toplevels, ApplicationContext context)
777                 {
778                         while (toplevels.Count > 0) {
779 #if DebugRunLoop
780                                 Console.WriteLine("      Re-Enabling form form {0}", toplevels.Peek());
781 #endif
782                                 Form c = (Form) toplevels.Dequeue ();
783                                 if (c.IsHandleCreated) {
784                                         XplatUI.EnableWindow (c.window.Handle, true);
785                                         context.MainForm = c;
786                                 }
787                         }
788 #if DebugRunLoop
789                         Console.WriteLine("   Done with the re-enable");
790 #endif
791                 }
792
793                 internal static void RunLoop (bool Modal, ApplicationContext context)
794                 {
795                         Queue           toplevels;
796                         MSG             msg;
797                         Object          queue_id;
798                         MWFThread       thread;
799                         ApplicationContext previous_thread_context;
800                         
801                         thread = MWFThread.Current;
802
803                         /*
804                          * There is a NotWorking test for this, but since we are using this method both for Form.ShowDialog as for ApplicationContexts we'll
805                          * fail on nested ShowDialogs, so disable the check for the moment.
806                          */
807                         //if (thread.MessageLoop) {
808                         //        throw new InvalidOperationException ("Starting a second message loop on a single thread is not a valid operation. Use Form.ShowDialog instead.");
809                         //}
810
811                         msg = new MSG();
812
813                         if (context == null)
814                                 context = new ApplicationContext();
815                 
816                         previous_thread_context = thread.Context;
817                         thread.Context = context;
818
819                         if (context.MainForm != null) {
820                                 context.MainForm.is_modal = Modal;
821                                 context.MainForm.context = context;
822                                 context.MainForm.closing = false;
823                                 context.MainForm.Visible = true;        // Cannot use Show() or scaling gets confused by menus
824                                 // XXX the above line can be used to close the form. another problem with our handling of Show/Activate.
825                                 if (context.MainForm != null)
826                                         context.MainForm.Activate();
827                         }
828
829                         #if DebugRunLoop
830                                 Console.WriteLine("Entering RunLoop(Modal={0}, Form={1})", Modal, context.MainForm != null ? context.MainForm.ToString() : "NULL");
831                         #endif
832
833                         if (Modal) {
834                                 toplevels = new Queue ();
835                                 DisableFormsForModalLoop (toplevels, context);
836                                 
837                                 // FIXME - need activate?
838                                 /* make sure the MainForm is enabled */
839                                 if (context.MainForm != null) {
840                                         XplatUI.EnableWindow (context.MainForm.Handle, true);
841                                         XplatUI.SetModal(context.MainForm.Handle, true);
842                                 }
843                         } else {
844                                 toplevels = null;
845                         }
846
847                         queue_id = XplatUI.StartLoop(Thread.CurrentThread);
848                         thread.MessageLoop = true;
849
850                         bool quit = false;
851
852                         while (!quit && XplatUI.GetMessage(queue_id, ref msg, IntPtr.Zero, 0, 0)) {
853                                 Message m = Message.Create(msg.hwnd, (int)msg.message, msg.wParam, msg.lParam);
854                                 
855                                 if (Application.FilterMessage (ref m))
856                                         continue;
857                                         
858                                 switch((Msg)msg.message) {
859                                 case Msg.WM_KEYDOWN:
860                                 case Msg.WM_SYSKEYDOWN:
861                                 case Msg.WM_CHAR:
862                                 case Msg.WM_SYSCHAR:
863                                 case Msg.WM_KEYUP:
864                                 case Msg.WM_SYSKEYUP:
865                                         Control c;
866                                         c = Control.FromHandle(msg.hwnd);
867
868                                         // If we have a control with keyboard capture (usually a *Strip)
869                                         // give it the message, and then drop the message
870                                         if (keyboard_capture != null) {
871                                                 // WM_SYSKEYUP does not make it into ProcessCmdKey, so do it here
872                                                 if ((Msg)m.Msg == Msg.WM_SYSKEYDOWN)
873                                                         if (m.WParam.ToInt32() == (int)Keys.Menu) {
874                                                                 keyboard_capture.GetTopLevelToolStrip ().Dismiss (ToolStripDropDownCloseReason.Keyboard);
875                                                                 continue;
876                                                         }
877
878                                                 m.HWnd = keyboard_capture.Handle;
879
880                                                 switch (keyboard_capture.PreProcessControlMessageInternal (ref m)) {
881                                                         case PreProcessControlState.MessageProcessed:
882                                                                 continue;
883                                                         case PreProcessControlState.MessageNeeded:
884                                                         case PreProcessControlState.MessageNotNeeded:
885                                                                 if (((m.Msg == (int)Msg.WM_KEYDOWN || m.Msg == (int)Msg.WM_CHAR) && !keyboard_capture.ProcessControlMnemonic ((char)m.WParam))) {
886                                                                         if (c == null || !ControlOnToolStrip (c))
887                                                                                 continue;
888                                                                         else
889                                                                                 m.HWnd = msg.hwnd;
890                                                                 } else
891                                                                         continue;
892                                                                 
893                                                                 break;
894                                                 }
895                                         }
896
897                                         if (((c != null) && c.PreProcessControlMessageInternal (ref m) != PreProcessControlState.MessageProcessed) ||
898                                                 (c == null)) {
899                                                 goto default;
900                                         } 
901                                         break;
902
903                                 case Msg.WM_LBUTTONDOWN:
904                                 case Msg.WM_MBUTTONDOWN:
905                                 case Msg.WM_RBUTTONDOWN:
906                                         if (keyboard_capture != null) {
907                                                 Control c2 = Control.FromHandle (msg.hwnd);
908
909                                                 // the target is not a winforms control (an embedded control, perhaps), so
910                                                 // release everything
911                                                 if (c2 == null) {
912                                                         ToolStripManager.FireAppClicked ();
913                                                         goto default;
914                                                 }
915
916                                                 // If we clicked a ToolStrip, we have to make sure it isn't
917                                                 // the one we are on, or any of its parents or children
918                                                 // If we clicked off the dropped down menu, release everything
919                                                 if (c2 is ToolStrip) {
920                                                         if ((c2 as ToolStrip).GetTopLevelToolStrip () != keyboard_capture.GetTopLevelToolStrip ())
921                                                                 ToolStripManager.FireAppClicked ();
922                                                 } else {
923                                                         if (c2.Parent != null)
924                                                                 if (c2.Parent is ToolStripDropDownMenu)
925                                                                         if ((c2.Parent as ToolStripDropDownMenu).GetTopLevelToolStrip () == keyboard_capture.GetTopLevelToolStrip ())
926                                                                                 goto default;
927                                                         if (c2.TopLevelControl == null)
928                                                                 goto default;
929                                                                 
930                                                         ToolStripManager.FireAppClicked ();
931                                                 }
932                                         }
933                                         
934                                         goto default;
935
936                                 case Msg.WM_QUIT:
937                                         quit = true; // make sure we exit
938                                         break;
939                                 default:
940                                         XplatUI.TranslateMessage (ref msg);
941                                         XplatUI.DispatchMessage (ref msg);
942                                         break;
943                                 }
944
945                                 // If our Form doesn't have a handle anymore, it means it was destroyed and we need to *wait* for WM_QUIT.
946                                 if ((context.MainForm != null) && (!context.MainForm.IsHandleCreated))
947                                         continue;
948
949                                 // Handle exit, Form might have received WM_CLOSE and set 'closing' in response.
950                                 if ((context.MainForm != null) && (context.MainForm.closing || (Modal && !context.MainForm.Visible))) {
951                                         if (!Modal) {
952                                                 XplatUI.PostQuitMessage (0);
953                                         } else {
954                                                 break;
955                                         }
956                                 }
957                         }
958                         #if DebugRunLoop
959                                 Console.WriteLine ("   RunLoop loop left");
960                         #endif
961
962                         thread.MessageLoop = false;
963                         XplatUI.EndLoop (Thread.CurrentThread);
964
965                         if (Modal) {
966                                 Form old = context.MainForm;
967
968                                 context.MainForm = null;
969
970                                 EnableFormsForModalLoop (toplevels, context);
971                                 
972                                 if (context.MainForm != null && context.MainForm.IsHandleCreated) {
973                                         XplatUI.SetModal (context.MainForm.Handle, false);
974                                 }
975                                 #if DebugRunLoop
976                                         Console.WriteLine ("   Done with the SetModal");
977                                 #endif
978                                 old.RaiseCloseEvents (true, false);
979                                 old.is_modal = false;
980                         }
981
982                         #if DebugRunLoop
983                                 Console.WriteLine ("Leaving RunLoop(Modal={0}, Form={1})", Modal, context.MainForm != null ? context.MainForm.ToString() : "NULL");
984                         #endif
985
986                         if (context.MainForm != null) {
987                                 context.MainForm.context = null;
988                                 context.MainForm = null;
989                         }
990
991                         thread.Context = previous_thread_context;
992
993                         if (!Modal)
994                                 thread.Exit();
995                 }
996
997                 #endregion      // Public Static Methods
998
999                 #region Events
1000
1001                 public static event EventHandler ApplicationExit;
1002
1003                 public static event EventHandler Idle {
1004                         add {
1005                                 XplatUI.Idle += value;
1006                         }
1007                         remove {
1008                                 XplatUI.Idle -= value;
1009                         }
1010                 }
1011
1012                 public static event EventHandler ThreadExit;
1013                 public static event ThreadExceptionEventHandler ThreadException;
1014                 
1015                 // These are used externally by the UIA framework
1016                 internal static event EventHandler FormAdded;
1017                 internal static event EventHandler PreRun;
1018
1019 #pragma warning disable 0067
1020                 [MonoTODO]
1021                 [EditorBrowsable (EditorBrowsableState.Advanced)]
1022                 public static event EventHandler EnterThreadModal;
1023
1024                 [MonoTODO]
1025                 [EditorBrowsable (EditorBrowsableState.Advanced)]
1026                 public static event EventHandler LeaveThreadModal;
1027 #pragma warning restore 0067
1028
1029                 #endregion      // Events
1030
1031                 #region Public Delegates
1032
1033                 [EditorBrowsable (EditorBrowsableState.Advanced)]
1034                 public delegate bool MessageLoopCallback ();
1035
1036                 #endregion
1037                 
1038                 #region Internal Properties
1039                 internal static ToolStrip KeyboardCapture {
1040                         get { return keyboard_capture; }
1041                         set { keyboard_capture = value; }
1042                 }
1043
1044                 internal static bool VisualStylesEnabled {
1045                         get { return visual_styles_enabled; }
1046                 }
1047                 #endregion
1048                 
1049                 #region Internal Methods
1050
1051                 internal static void AddForm (Form f)
1052                 {
1053                         lock (forms)
1054                                 forms.Add (f);
1055                         // Signal that a Form has been added to this
1056                         // Application. Used by UIA to detect new Forms that
1057                         // need a11y support. This event may be fired even if
1058                         // the form has already been added, so clients should
1059                         // account for that when handling this signal.
1060                         if (FormAdded != null)
1061                                 FormAdded (f, null);
1062                 }
1063                 
1064                 internal static void RemoveForm (Form f)
1065                 {
1066                         lock (forms)
1067                                 forms.Remove (f);
1068                 }
1069
1070                 private static bool ControlOnToolStrip (Control c)
1071                 {
1072                         Control p = c.Parent;
1073                         
1074                         while (p != null) {
1075                                 if (p is ToolStrip)
1076                                         return true;
1077                                         
1078                                 p = p.Parent;
1079                         }
1080                         
1081                         return false;
1082                 }
1083
1084                 // Takes a starting path, appends company name, product name, and
1085                 // product version.  If the directory doesn't exist, create it
1086                 private static string CreateDataPath (string basePath)
1087                 {
1088                         string path;
1089
1090                         path = Path.Combine (basePath, CompanyName);
1091                         path = Path.Combine (path, ProductName);
1092                         path = Path.Combine (path, ProductVersion);
1093
1094                         if (!Directory.Exists (path))
1095                                 Directory.CreateDirectory (path);
1096                         
1097                         return path;
1098                 }
1099                 #endregion
1100         }
1101 }