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