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