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