In corlib/System.Runtime.InteropServices:
[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                 public static void Restart ()
381                 {
382                         //FIXME: ClickOnce stuff using the Update or UpdateAsync methods.
383                         //FIXME: SecurityPermission: Restart () requires IsUnrestricted permission.
384
385                         if (Assembly.GetEntryAssembly () == null)
386                                 throw new NotSupportedException ("The method 'Restart' is not supported by this application type.");
387
388                         string mono_path = null;
389
390                         //Get mono path
391                         PropertyInfo gac = typeof (Environment).GetProperty ("GacPath", BindingFlags.Static | BindingFlags.NonPublic);
392                         MethodInfo get_gac = null;
393                         if (gac != null)
394                                 get_gac = gac.GetGetMethod (true);
395
396                         if (get_gac != null) {
397                                 string gac_path = Path.GetDirectoryName ((string)get_gac.Invoke (null, null));
398                                 string mono_prefix = Path.GetDirectoryName (Path.GetDirectoryName (gac_path));
399
400                                 if (Environment.OSVersion.Platform == PlatformID.Unix) {
401                                         mono_path = Path.Combine (mono_prefix, "bin/mono");
402                                         if (!File.Exists (mono_path))
403                                                 mono_path = "mono";
404                                 }
405                                 else {
406                                         mono_path = Path.Combine (mono_prefix, "bin\\mono.bat");
407
408                                         if (!File.Exists (mono_path))
409                                                 mono_path = Path.Combine (mono_prefix, "bin\\mono.exe");
410
411                                         if (!File.Exists (mono_path))
412                                                 mono_path = Path.Combine (mono_prefix, "mono\\mono\\mini\\mono.exe");
413
414                                         if (!File.Exists (mono_path))
415                                                 throw new FileNotFoundException (string.Format ("Windows mono path not found: '{0}'", mono_path));
416                                 }
417                         }
418
419                         //Get command line arguments
420                         StringBuilder argsBuilder = new StringBuilder ();
421                         string[] args = Environment.GetCommandLineArgs ();
422                         for (int i = 0; i < args.Length; i++)
423                         {
424                                 argsBuilder.Append (string.Format ("\"{0}\" ", args[i]));
425                         }
426                         string arguments = argsBuilder.ToString ();
427                         ProcessStartInfo procInfo = Process.GetCurrentProcess ().StartInfo;
428
429                         if (mono_path == null) { //it is .NET on Windows
430                                 procInfo.FileName = args[0];
431                                 procInfo.Arguments = arguments.Remove (0, args[0].Length + 3); //1 space and 2 quotes
432                         }
433                         else {
434                                 procInfo.Arguments = arguments;
435                                 procInfo.FileName = mono_path;
436                         }
437
438                         procInfo.WorkingDirectory = Environment.CurrentDirectory;
439
440                         Application.Exit ();
441                         Process.Start (procInfo);
442                 }
443 #endif
444
445                 public static void Exit() {
446                         XplatUI.PostQuitMessage(0);
447                         CloseForms(null);
448                 }
449
450                 public static void ExitThread() {
451                         CloseForms(Thread.CurrentThread);
452                         // this might not be right - need to investigate (somehow) if a WM_QUIT message is generated here
453                         XplatUI.PostQuitMessage(0);
454                 }
455
456                 public static ApartmentState OleRequired() {
457                         //throw new NotImplementedException("OLE Not supported by this System.Windows.Forms implementation");
458                         return ApartmentState.Unknown;
459                 }
460
461                 public static void OnThreadException(Exception t) {
462                         if (MWFThread.Current.HandlingException) {
463                                 /* we're already handling an exception and we got
464                                    another one?  print it out and exit, this means
465                                    we've got a runtime/SWF bug. */
466                                 Console.WriteLine (t);
467                                 Application.Exit();
468                         }
469
470                         MWFThread.Current.HandlingException = true;
471
472                         if (Application.ThreadException != null) {
473                                 Application.ThreadException(null, new ThreadExceptionEventArgs(t));
474                                 return;
475                         }
476
477                         if (SystemInformation.UserInteractive) {
478                                 Form form = new ThreadExceptionDialog (t);
479                                 form.ShowDialog ();
480                         } else {
481                                 Console.WriteLine (t.ToString ());
482                                 Application.Exit ();
483                         }
484
485                         MWFThread.Current.HandlingException = false;
486                 }
487
488                 public static void RemoveMessageFilter(IMessageFilter filter) {
489                         lock (message_filters) {
490                                 message_filters.Remove(filter);
491                         }
492                 }
493
494                 public static void Run() {
495                         RunLoop(false, new ApplicationContext());
496                 }
497
498                 public static void Run(Form mainForm) {
499                         RunLoop(false, new ApplicationContext(mainForm));
500                 }
501
502                 public static void Run(ApplicationContext context) {
503                         RunLoop(false, context);
504                 }
505
506                 internal static void RunLoop(bool Modal, ApplicationContext context) {
507                         Queue           toplevels;
508                         IEnumerator     control;
509                         MSG             msg;
510                         Object          queue_id;
511                         MWFThread       thread;
512
513                         thread = MWFThread.Current;
514
515                         msg = new MSG();
516
517                         if (context == null) {
518                                 context = new ApplicationContext();
519                         }
520
521                         thread.Context = context;
522
523                         if (context.MainForm != null) {
524                                 context.MainForm.is_modal = Modal;
525                                 context.MainForm.context = context;
526                                 context.MainForm.closing = false;
527                                 context.MainForm.Visible = true;        // Cannot use Show() or scaling gets confused by menus
528                                 // XXX the above line can be used to close the form. another problem with our handling of Show/Activate.
529                                 if (context.MainForm != null)
530                                         context.MainForm.Activate();
531                         }
532
533                         #if DebugRunLoop
534                                 Console.WriteLine("Entering RunLoop(Modal={0}, Form={1})", Modal, context.MainForm != null ? context.MainForm.ToString() : "NULL");
535                         #endif
536
537                         if (Modal) {
538                                 Form f;
539
540                                 toplevels = new Queue();
541                                 
542                                 lock (forms) {
543                                         control = forms.GetEnumerator();
544
545                                         while (control.MoveNext()) {
546                                                 f = (Form)control.Current;
547                                                 
548                                                 // Don't disable the main form.
549                                                 if (f == context.MainForm) {
550                                                         continue;
551                                                 }
552                                                 
553                                                 // Don't disable any children of the main form.
554                                                 // These do not have to be MDI children.
555                                                 Control current = f;
556                                                 bool is_child_of_main = false;;
557
558                                                 do {
559                                                         if (current.Parent == context.MainForm) {
560                                                                 is_child_of_main = true;
561                                                                 break;
562                                                         }
563                                                         current = current.Parent;
564                                                 } while (current != null);
565
566                                                 if (is_child_of_main)
567                                                         continue;
568                                                 
569                                                 // Disable the rest
570                                                 if (f.IsHandleCreated && XplatUI.IsEnabled(f.Handle)) {
571                                                         #if DebugRunLoop
572                                                                 Console.WriteLine("      Disabling form {0}", f);
573                                                         #endif
574                                                         XplatUI.EnableWindow(f.Handle, false);
575                                                         toplevels.Enqueue(f);
576                                                 }
577                                         }
578                                 }
579                                 
580                                 // FIXME - need activate?
581                                 /* make sure the MainForm is enabled */
582                                 if (context.MainForm != null) {
583                                         XplatUI.EnableWindow (context.MainForm.Handle, true);
584                                         XplatUI.SetModal(context.MainForm.Handle, true);
585                                 }
586                         } else {
587                                 toplevels = null;
588                         }
589
590                         queue_id = XplatUI.StartLoop(Thread.CurrentThread);
591                         thread.MessageLoop = true;
592
593                         bool quit = false;
594
595                         while (!quit && 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                                 case Msg.WM_QUIT:
633                                         quit = true; // make sure we exit
634                                         break;
635                                 default:
636                                         XplatUI.TranslateMessage(ref msg);
637                                         XplatUI.DispatchMessage(ref msg);
638                                         break;
639                                 }
640
641                                 // Handle exit, Form might have received WM_CLOSE and set 'closing' in response
642                                 if ((context.MainForm != null) && (context.MainForm.closing || (Modal && !context.MainForm.Visible))) {
643                                         if (!Modal) {
644                                                 XplatUI.PostQuitMessage(0);
645                                         } else {
646                                                 break;
647                                         }
648                                 }
649                         }
650                         #if DebugRunLoop
651                                 Console.WriteLine("   RunLoop loop left");
652                         #endif
653
654                         thread.MessageLoop = false;
655                         XplatUI.EndLoop(Thread.CurrentThread);
656
657                         if (Modal) {
658                                 Form c;
659
660                                 Form old = context.MainForm;
661
662                                 context.MainForm = null;
663
664                                 while (toplevels.Count>0) {
665                                         #if DebugRunLoop
666                                                 Console.WriteLine("      Re-Enabling form form {0}", toplevels.Peek());
667                                         #endif
668                                         c = (Form)toplevels.Dequeue();
669                                         if (c.IsHandleCreated) {
670                                                 XplatUI.EnableWindow(c.window.Handle, true);
671                                                 context.MainForm = c;
672                                         }
673                                 }
674                                 #if DebugRunLoop
675                                         Console.WriteLine("   Done with the re-enable");
676                                 #endif
677                                 if (context.MainForm != null && context.MainForm.IsHandleCreated) {
678                                         XplatUI.SetModal(context.MainForm.Handle, false);
679                                 }
680                                 #if DebugRunLoop
681                                         Console.WriteLine("   Done with the SetModal");
682                                 #endif
683                                 old.Close();
684                                 old.is_modal = false;
685                         }
686
687                         #if DebugRunLoop
688                                 Console.WriteLine("Leaving RunLoop(Modal={0}, Form={1})", Modal, context.MainForm != null ? context.MainForm.ToString() : "NULL");
689                         #endif
690                         if (context.MainForm != null) {
691                                 context.MainForm.context = null;
692                                 context.MainForm = null;
693                         }
694
695                         if (!Modal) {
696                                 thread.Exit();
697                         }
698                 }
699
700                 #endregion      // Public Static Methods
701
702                 #region Events
703                 public static event EventHandler        ApplicationExit;
704
705                 public static event EventHandler        Idle {
706                         add {
707                                 XplatUI.Idle += value;
708                         }
709                         remove {
710                                 XplatUI.Idle -= value;
711                         }
712                 }
713
714                 public static event EventHandler        ThreadExit;
715                 public static event ThreadExceptionEventHandler ThreadException;
716
717 #if NET_2_0
718                 [EditorBrowsable (EditorBrowsableState.Advanced)]
719                 public static event EventHandler EnterThreadModal;
720
721                 [EditorBrowsable (EditorBrowsableState.Advanced)]
722                 public static event EventHandler LeaveThreadModal;
723 #endif
724                 #endregion      // Events
725
726                 #region Internal Methods
727                 internal static void AddForm (Form f)
728                 {
729                         lock (forms)
730                                 forms.Add (f);
731                 }
732                 
733                 internal static void RemoveForm (Form f)
734                 {
735                         lock (forms)
736                                 forms.Remove (f);
737                 }
738                 #endregion
739         }
740 }