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