2007-01-18 Rolf Bjarne Kvinge <RKvinge@novell.com>
[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                                         IEnumerator     e;
84                                         int             loops;
85                                         MWFThread       thread;
86
87                                         e = threads.Values.GetEnumerator();
88                                         loops = 0;
89
90                                         while (e.MoveNext()) {
91                                                 thread = (MWFThread)e.Current;
92                                                 if (thread != null && thread.messageloop_started) {
93                                                         loops++;
94                                                 }
95                                         }
96
97                                         return loops;
98                                 }
99                         }
100
101                         public static MWFThread Current {
102                                 get {
103                                         MWFThread       thread;
104
105                                         thread = null;
106                                         lock (threads) {
107                                                 thread = (MWFThread)threads[Thread.CurrentThread.GetHashCode()];
108                                                 if (thread == null) {
109                                                         thread = new MWFThread();
110                                                         thread.thread_id = Thread.CurrentThread.GetHashCode();
111                                                         threads[thread.thread_id] = thread;
112                                                 }
113                                         }
114
115                                         return thread;
116                                 }
117                         }
118                         #endregion      // Properties
119
120                         #region Methods
121                         public void Exit() {
122                                 if (context != null) {
123                                         context.ExitThread();
124                                 }
125                                 context = null;
126
127                                 if (Application.ThreadExit != null) {
128                                         Application.ThreadExit(null, EventArgs.Empty);
129                                 }
130
131                                 if (LoopCount == 0) {
132                                         if (Application.ApplicationExit != null) {
133                                                 Application.ApplicationExit(null, EventArgs.Empty);
134                                         }
135                                 }
136                                 lock (threads) {
137                                         threads[thread_id] = null;
138                                 }
139                         }
140                         #endregion      // Methods
141                 }
142
143                 private static bool                     browser_embedded        = false;
144                 private static InputLanguage            input_language          = InputLanguage.CurrentInputLanguage;
145                 private static string                   safe_caption_format     = "{1} - {0} - {2}";
146                 private static ArrayList                message_filters         = new ArrayList();
147                 private static FormCollection           forms                   = new FormCollection ();
148                 private static bool                     use_wait_cursor         = false;
149
150 #if NET_2_0
151                 private static VisualStyleState visual_style_state = VisualStyleState.ClientAndNonClientAreasEnabled;
152 #endif
153
154                 private Application () {
155                 }
156
157                 #region Private Methods
158                 private static void CloseForms(Thread thread) {
159                         Form            f;
160                         IEnumerator     control;
161                         bool            all;
162
163                         #if DebugRunLoop
164                                 Console.WriteLine("   CloseForms({0}) called", thread);
165                         #endif
166                         if (thread == null) {
167                                 all = true;
168                         } else {
169                                 all = false;
170                         }
171
172                         lock (forms) {
173                                 control = forms.GetEnumerator();
174
175                                 while (control.MoveNext()) {
176                                         f = (Form)control.Current;
177                                         
178                                         if (all || (thread == f.creator_thread)) {
179                                                 if (f.IsHandleCreated) {
180                                                         XplatUI.PostMessage(f.Handle, Msg.WM_CLOSE_INTERNAL, IntPtr.Zero, IntPtr.Zero);
181                                                 }
182                                                 #if DebugRunLoop
183                                                         Console.WriteLine("      Closing form {0}", f);
184                                                 #endif
185                                         }
186                                 }
187                         }
188                 }
189                 #endregion      // Private methods
190
191                 #region Public Static Properties
192                 public static bool AllowQuit {
193                         get {
194                                 return !browser_embedded;
195                         }
196                 }
197
198                 public static string CommonAppDataPath {
199                         get {
200                                 return Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
201                         }
202                 }
203
204                 public static RegistryKey CommonAppDataRegistry {
205                         get {
206                                 RegistryKey     key;
207
208                                 key = Registry.LocalMachine.OpenSubKey("Software\\" + Application.CompanyName + "\\" + Application.ProductName + "\\" + Application.ProductVersion, true);
209
210                                 return key;
211                         }
212                 }
213
214                 public static string CompanyName {
215                         get {
216                                 AssemblyCompanyAttribute[] attrs = (AssemblyCompanyAttribute[]) Assembly.GetEntryAssembly().GetCustomAttributes(typeof(AssemblyCompanyAttribute), true);
217                                 
218                                 if ((attrs != null) && attrs.Length>0) {
219                                         return attrs[0].Company;
220                                 }
221
222                                 return Assembly.GetEntryAssembly().GetName().Name;
223                         }
224                 }
225
226                 public static CultureInfo CurrentCulture {
227                         get {
228                                 return Thread.CurrentThread.CurrentUICulture;
229                         }
230
231                         set {
232                                 
233                                 Thread.CurrentThread.CurrentUICulture=value;
234                         }
235                 }
236
237                 public static InputLanguage CurrentInputLanguage {
238                         get {
239                                 return input_language;
240                         }
241
242                         set {
243                                 input_language=value;
244                         }
245                 }
246
247                 public static string ExecutablePath {
248                         get {
249                                 return Assembly.GetEntryAssembly().Location;
250                         }
251                 }
252
253                 public static string LocalUserAppDataPath {
254                         get {
255                                 return Path.Combine(Path.Combine(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), CompanyName), ProductName), ProductVersion);
256                         }
257                 }
258
259                 public static bool MessageLoop {
260                         get {
261                                 return MWFThread.Current.MessageLoop;
262                         }
263                 }
264
265                 public static string ProductName {
266                         get {
267                                 AssemblyProductAttribute[] attrs = (AssemblyProductAttribute[]) Assembly.GetEntryAssembly().GetCustomAttributes(typeof(AssemblyProductAttribute), true);
268                                 
269                                 if ((attrs != null) && attrs.Length>0) {
270                                         return attrs[0].Product;
271                                 }
272
273                                 return Assembly.GetEntryAssembly().GetName().Name;
274                         }
275                 }
276
277                 public static string ProductVersion {
278                         get {
279                                 String version;
280
281                                 version = Assembly.GetEntryAssembly().GetName().Version.ToString();
282
283                                 return version;
284                         }
285                 }
286
287                 public static string SafeTopLevelCaptionFormat {
288                         get {
289                                 return safe_caption_format;
290                         }
291
292                         set {
293                                 safe_caption_format=value;
294                         }
295                 }
296
297                 public static string StartupPath {
298                         get {
299                                 return Path.GetDirectoryName(Application.ExecutablePath);
300                         }
301                 }
302
303                 public static string UserAppDataPath {
304                         get {
305                                 return Path.Combine(Path.Combine(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), CompanyName), ProductName), ProductVersion);
306                         }
307                 }
308
309                 public static RegistryKey UserAppDataRegistry {
310                         get {
311                                 RegistryKey     key;
312
313                                 key = Registry.CurrentUser.OpenSubKey("Software\\" + Application.CompanyName + "\\" + Application.ProductName + "\\" + Application.ProductVersion, true);
314
315                                 return key;
316                         }
317                 }
318
319 #if NET_2_0
320
321                 public static bool UseWaitCursor {
322                         get {
323                                 return use_wait_cursor;
324                         }
325                         set {
326                                 use_wait_cursor = value;
327                                 if (use_wait_cursor) {
328                                         foreach (Form form in OpenForms) {
329                                                 form.Cursor = Cursors.WaitCursor;
330                                         }
331                                 }
332                         }
333                 }
334                 
335                 public static bool RenderWithVisualStyles {
336                       get {
337                                 if (VisualStyleInformation.IsSupportedByOS)
338                                 {
339                                         if (!VisualStyleInformation.IsEnabledByUser)
340                                                 return false;
341                                   
342                                         if (!XplatUI.ThemesEnabled)
343                                                 return false;
344                                   
345                                         if (Application.VisualStyleState == VisualStyleState.ClientAndNonClientAreasEnabled)
346                                                 return true;
347                                   
348                                         if (Application.VisualStyleState == VisualStyleState.ClientAreaEnabled)
349                                                 return true;
350                                 }
351                                 
352                                 return false;
353                       }
354                 }
355
356                 public static VisualStyleState VisualStyleState {
357                         get { return Application.visual_style_state; }
358                         set { Application.visual_style_state = value; }
359                 }
360 #endif
361
362                 #endregion
363
364                 #region Public Static Methods
365                 public static void AddMessageFilter(IMessageFilter value) {
366                         lock (message_filters) {
367                                 message_filters.Add(value);
368                         }
369                 }
370
371                 public static void DoEvents() {
372                         XplatUI.DoEvents();
373                 }
374
375                 public static void EnableVisualStyles() {
376                         XplatUI.EnableThemes();
377                 }
378
379 #if NET_2_0
380                 //
381                 // If true, it uses GDI+, performance reasons were quoted
382                 //
383                 static internal bool use_compatible_text_rendering = true;
384                 
385                 public static void SetCompatibleTextRenderingDefault (bool defaultValue)
386                 {
387                         use_compatible_text_rendering = defaultValue;
388                 }
389
390                 public static FormCollection OpenForms {
391                         get {
392                                 return forms;
393                         }
394                 }
395
396                 public static void Restart ()
397                 {
398                         //FIXME: ClickOnce stuff using the Update or UpdateAsync methods.
399                         //FIXME: SecurityPermission: Restart () requires IsUnrestricted permission.
400
401                         if (Assembly.GetEntryAssembly () == null)
402                                 throw new NotSupportedException ("The method 'Restart' is not supported by this application type.");
403
404                         string mono_path = null;
405
406                         //Get mono path
407                         PropertyInfo gac = typeof (Environment).GetProperty ("GacPath", BindingFlags.Static | BindingFlags.NonPublic);
408                         MethodInfo get_gac = null;
409                         if (gac != null)
410                                 get_gac = gac.GetGetMethod (true);
411
412                         if (get_gac != null) {
413                                 string gac_path = Path.GetDirectoryName ((string)get_gac.Invoke (null, null));
414                                 string mono_prefix = Path.GetDirectoryName (Path.GetDirectoryName (gac_path));
415
416                                 if (Environment.OSVersion.Platform == PlatformID.Unix) {
417                                         mono_path = Path.Combine (mono_prefix, "bin/mono");
418                                         if (!File.Exists (mono_path))
419                                                 mono_path = "mono";
420                                 }
421                                 else {
422                                         mono_path = Path.Combine (mono_prefix, "bin\\mono.bat");
423
424                                         if (!File.Exists (mono_path))
425                                                 mono_path = Path.Combine (mono_prefix, "bin\\mono.exe");
426
427                                         if (!File.Exists (mono_path))
428                                                 mono_path = Path.Combine (mono_prefix, "mono\\mono\\mini\\mono.exe");
429
430                                         if (!File.Exists (mono_path))
431                                                 throw new FileNotFoundException (string.Format ("Windows mono path not found: '{0}'", mono_path));
432                                 }
433                         }
434
435                         //Get command line arguments
436                         StringBuilder argsBuilder = new StringBuilder ();
437                         string[] args = Environment.GetCommandLineArgs ();
438                         for (int i = 0; i < args.Length; i++)
439                         {
440                                 argsBuilder.Append (string.Format ("\"{0}\" ", args[i]));
441                         }
442                         string arguments = argsBuilder.ToString ();
443                         ProcessStartInfo procInfo = Process.GetCurrentProcess ().StartInfo;
444
445                         if (mono_path == null) { //it is .NET on Windows
446                                 procInfo.FileName = args[0];
447                                 procInfo.Arguments = arguments.Remove (0, args[0].Length + 3); //1 space and 2 quotes
448                         }
449                         else {
450                                 procInfo.Arguments = arguments;
451                                 procInfo.FileName = mono_path;
452                         }
453
454                         procInfo.WorkingDirectory = Environment.CurrentDirectory;
455
456                         Application.Exit ();
457                         Process.Start (procInfo);
458                 }
459 #endif
460
461                 public static void Exit() {
462                         CloseForms(null);
463
464                         // FIXME - this needs to be fired when they're all closed
465                         // But CloseForms uses PostMessage, so it gets fired before
466                         // We need to wait on something...
467                         if (ApplicationExit != null) {
468                                 ApplicationExit(null, EventArgs.Empty);
469                         }
470                 }
471
472                 public static void ExitThread() {
473                         CloseForms(Thread.CurrentThread);
474                         MWFThread.Current.Exit();
475                 }
476
477                 public static ApartmentState OleRequired() {
478                         //throw new NotImplementedException("OLE Not supported by this System.Windows.Forms implementation");
479                         return ApartmentState.Unknown;
480                 }
481
482                 public static void OnThreadException(Exception t) {
483                         if (MWFThread.Current.HandlingException) {
484                                 /* we're already handling an exception and we got
485                                    another one?  print it out and exit, this means
486                                    we've got a runtime/SWF bug. */
487                                 Console.WriteLine (t);
488                                 Application.Exit();
489                         }
490
491                         MWFThread.Current.HandlingException = true;
492
493                         if (Application.ThreadException != null) {
494                                 Application.ThreadException(null, new ThreadExceptionEventArgs(t));
495                                 return;
496                         }
497
498                         if (SystemInformation.UserInteractive) {
499                                 Form form = new ThreadExceptionDialog (t);
500                                 form.ShowDialog ();
501                         } else {
502                                 Console.WriteLine (t.ToString ());
503                                 Application.Exit ();
504                         }
505
506                         MWFThread.Current.HandlingException = false;
507                 }
508
509                 public static void RemoveMessageFilter(IMessageFilter filter) {
510                         lock (message_filters) {
511                                 message_filters.Remove(filter);
512                         }
513                 }
514
515                 public static void Run() {
516                         RunLoop(false, new ApplicationContext());
517                 }
518
519                 public static void Run(Form mainForm) {
520                         RunLoop(false, new ApplicationContext(mainForm));
521                 }
522
523                 public static void Run(ApplicationContext context) {
524                         RunLoop(false, context);
525                 }
526
527                 internal static void RunLoop(bool Modal, ApplicationContext context) {
528                         Queue           toplevels;
529                         IEnumerator     control;
530                         MSG             msg;
531                         Object          queue_id;
532                         MWFThread       thread;
533
534
535                         thread = MWFThread.Current;
536
537                         msg = new MSG();
538
539                         if (context == null) {
540                                 context = new ApplicationContext();
541                         }
542
543                         thread.Context = context;
544
545                         if (context.MainForm != null) {
546                                 context.MainForm.is_modal = Modal;
547                                 context.MainForm.context = context;
548                                 context.MainForm.closing = false;
549                                 context.MainForm.Visible = true;        // Cannot use Show() or scaling gets confused by menus
550                                 // FIXME - do we need this?
551                                 //context.MainForm.PerformLayout();
552                                 context.MainForm.Activate();
553                         }
554
555                         #if DebugRunLoop
556                                 Console.WriteLine("Entering RunLoop(Modal={0}, Form={1})", Modal, context.MainForm != null ? context.MainForm.ToString() : "NULL");
557                         #endif
558
559                         if (Modal) {
560                                 Form f;
561
562                                 toplevels = new Queue();
563                                 
564                                 lock (forms) {
565                                         control = forms.GetEnumerator();
566
567                                         while (control.MoveNext()) {
568                                                 f = (Form)control.Current;
569                                                 
570                                                 if (f != context.MainForm) {
571                                                         if (f.IsHandleCreated && XplatUI.IsEnabled(f.Handle)) {
572                                                                 #if DebugRunLoop
573                                                                         Console.WriteLine("      Disabling form {0}", f);
574                                                                 #endif
575                                                                 XplatUI.EnableWindow(f.Handle, false);
576                                                                 toplevels.Enqueue(f);
577                                                         }
578                                                 }
579                                         }
580                                 }
581                                 
582                                 // FIXME - need activate?
583                                 /* make sure the MainForm is enabled */
584                                 if (context.MainForm != null) {
585                                         XplatUI.EnableWindow (context.MainForm.Handle, true);
586                                         XplatUI.SetModal(context.MainForm.Handle, true);
587                                 }
588                         } else {
589                                 toplevels = null;
590                         }
591
592                         queue_id = XplatUI.StartLoop(Thread.CurrentThread);
593                         thread.MessageLoop = true;
594
595                         while (XplatUI.GetMessage(queue_id, ref msg, IntPtr.Zero, 0, 0)) {
596                                 lock (message_filters) {
597                                         if (message_filters.Count > 0) {
598                                                 Message m;
599                                                 bool    drop;
600
601                                                 drop = false;
602                                                 m = Message.Create(msg.hwnd, (int)msg.message, msg.wParam, msg.lParam);
603                                                 for (int i = 0; i < message_filters.Count; i++) {
604                                                         if (((IMessageFilter)message_filters[i]).PreFilterMessage(ref m)) {
605                                                                 // we're dropping the message
606                                                                 drop = true;
607                                                                 break;
608                                                         }
609                                                 }
610                                                 if (drop) {
611                                                         continue;
612                                                 }
613                                         }
614                                 }
615
616                                 switch((Msg)msg.message) {
617                                 case Msg.WM_KEYDOWN:
618                                 case Msg.WM_SYSKEYDOWN:
619                                 case Msg.WM_CHAR:
620                                 case Msg.WM_SYSCHAR:
621                                 case Msg.WM_KEYUP:
622                                 case Msg.WM_SYSKEYUP:
623                                         Message m;
624                                         Control c;
625
626                                         m = Message.Create(msg.hwnd, (int)msg.message, msg.wParam, msg.lParam);
627                                         c = Control.FromHandle(msg.hwnd);
628                                         if ((c != null) && !c.PreProcessMessage(ref m)) {
629                                                 goto default;
630                                         }
631                                         break;
632                                 default:
633                                         XplatUI.TranslateMessage(ref msg);
634                                         XplatUI.DispatchMessage(ref msg);
635                                         break;
636                                 }
637
638                                 // Handle exit, Form might have received WM_CLOSE and set 'closing' in response
639                                 if ((context.MainForm != null) && (context.MainForm.closing || (Modal && !context.MainForm.Visible))) {
640                                         if (!Modal) {
641                                                 XplatUI.PostQuitMessage(0);
642                                         } else {
643                                                 break;
644                                         }
645                                 }
646                         }
647                         #if DebugRunLoop
648                                 Console.WriteLine("   RunLoop loop left");
649                         #endif
650
651                         thread.MessageLoop = false;
652                         XplatUI.EndLoop(Thread.CurrentThread);
653
654                         if (Modal) {
655                                 Form c;
656
657                                 Form old = context.MainForm;
658
659                                 context.MainForm = null;
660
661                                 while (toplevels.Count>0) {
662                                         #if DebugRunLoop
663                                                 Console.WriteLine("      Re-Enabling form form {0}", toplevels.Peek());
664                                         #endif
665                                         c = (Form)toplevels.Dequeue();
666                                         if (c.IsHandleCreated) {
667                                                 XplatUI.EnableWindow(c.window.Handle, true);
668                                                 context.MainForm = c;
669                                         }
670                                 }
671                                 #if DebugRunLoop
672                                         Console.WriteLine("   Done with the re-enable");
673                                 #endif
674                                 if (context.MainForm != null && context.MainForm.IsHandleCreated) {
675                                         XplatUI.SetModal(context.MainForm.Handle, false);
676                                 }
677                                 #if DebugRunLoop
678                                         Console.WriteLine("   Done with the SetModal");
679                                 #endif
680                                 old.Close();
681                                 old.is_modal = false;
682                         }
683
684                         #if DebugRunLoop
685                                 Console.WriteLine("Leaving RunLoop(Modal={0}, Form={1})", Modal, context.MainForm != null ? context.MainForm.ToString() : "NULL");
686                         #endif
687                         if (context.MainForm != null) {
688                                 context.MainForm.context = null;
689                                 context.MainForm = null;
690                         }
691
692                         if (!Modal) {
693                                 thread.Exit();
694                         }
695                 }
696
697                 #endregion      // Public Static Methods
698
699                 #region Events
700                 public static event EventHandler        ApplicationExit;
701
702                 public static event EventHandler        Idle {
703                         add {
704                                 XplatUI.Idle += value;
705                         }
706                         remove {
707                                 XplatUI.Idle -= value;
708                         }
709                 }
710
711                 public static event EventHandler        ThreadExit;
712                 public static event ThreadExceptionEventHandler ThreadException;
713
714 #if NET_2_0
715                 [EditorBrowsable (EditorBrowsableState.Advanced)]
716                 public static event EventHandler EnterThreadModal;
717
718                 [EditorBrowsable (EditorBrowsableState.Advanced)]
719                 public static event EventHandler LeaveThreadModal;
720 #endif
721                 #endregion      // Events
722
723                 #region Internal Methods
724                 internal static void AddForm (Form f)
725                 {
726                         lock (forms)
727                                 forms.Add (f);
728                 }
729                 
730                 internal static void RemoveForm (Form f)
731                 {
732                         lock (forms)
733                                 forms.Remove (f);
734                 }
735                 #endregion
736         }
737 }