* Control.cs: ControlCollection.Count must be public. Fixed build of
[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
149 #if NET_2_0
150                 private static VisualStyleState visual_style_state = VisualStyleState.ClientAndNonClientAreasEnabled;
151 #endif
152
153                 private Application () {
154                 }
155
156                 #region Private Methods
157                 private static void CloseForms(Thread thread) {
158                         Form            f;
159                         IEnumerator     control;
160                         bool            all;
161
162                         #if DebugRunLoop
163                                 Console.WriteLine("   CloseForms({0}) called", thread);
164                         #endif
165                         if (thread == null) {
166                                 all = true;
167                         } else {
168                                 all = false;
169                         }
170
171                         lock (forms) {
172                                 control = forms.GetEnumerator();
173
174                                 while (control.MoveNext()) {
175                                         f = (Form)control.Current;
176                                         
177                                         if (all || (thread == f.creator_thread)) {
178                                                 if (f.IsHandleCreated) {
179                                                         XplatUI.PostMessage(f.Handle, Msg.WM_CLOSE_INTERNAL, IntPtr.Zero, IntPtr.Zero);
180                                                 }
181                                                 #if DebugRunLoop
182                                                         Console.WriteLine("      Closing form {0}", c);
183                                                 #endif
184                                         }
185                                 }
186                         }
187                 }
188                 #endregion      // Private methods
189
190                 #region Public Static Properties
191                 public static bool AllowQuit {
192                         get {
193                                 return !browser_embedded;
194                         }
195                 }
196
197                 public static string CommonAppDataPath {
198                         get {
199                                 return Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
200                         }
201                 }
202
203                 public static RegistryKey CommonAppDataRegistry {
204                         get {
205                                 RegistryKey     key;
206
207                                 key = Registry.LocalMachine.OpenSubKey("Software\\" + Application.CompanyName + "\\" + Application.ProductName + "\\" + Application.ProductVersion, true);
208
209                                 return key;
210                         }
211                 }
212
213                 public static string CompanyName {
214                         get {
215                                 AssemblyCompanyAttribute[] attrs = (AssemblyCompanyAttribute[]) Assembly.GetEntryAssembly().GetCustomAttributes(typeof(AssemblyCompanyAttribute), true);
216                                 
217                                 if ((attrs != null) && attrs.Length>0) {
218                                         return attrs[0].Company;
219                                 }
220
221                                 return Assembly.GetEntryAssembly().GetName().Name;
222                         }
223                 }
224
225                 public static CultureInfo CurrentCulture {
226                         get {
227                                 return Thread.CurrentThread.CurrentUICulture;
228                         }
229
230                         set {
231                                 
232                                 Thread.CurrentThread.CurrentUICulture=value;
233                         }
234                 }
235
236                 public static InputLanguage CurrentInputLanguage {
237                         get {
238                                 return input_language;
239                         }
240
241                         set {
242                                 input_language=value;
243                         }
244                 }
245
246                 public static string ExecutablePath {
247                         get {
248                                 return Assembly.GetEntryAssembly().Location;
249                         }
250                 }
251
252                 public static string LocalUserAppDataPath {
253                         get {
254                                 return Path.Combine(Path.Combine(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), CompanyName), ProductName), ProductVersion);
255                         }
256                 }
257
258                 public static bool MessageLoop {
259                         get {
260                                 return MWFThread.Current.MessageLoop;
261                         }
262                 }
263
264                 public static string ProductName {
265                         get {
266                                 AssemblyProductAttribute[] attrs = (AssemblyProductAttribute[]) Assembly.GetEntryAssembly().GetCustomAttributes(typeof(AssemblyProductAttribute), true);
267                                 
268                                 if ((attrs != null) && attrs.Length>0) {
269                                         return attrs[0].Product;
270                                 }
271
272                                 return Assembly.GetEntryAssembly().GetName().Name;
273                         }
274                 }
275
276                 public static string ProductVersion {
277                         get {
278                                 String version;
279
280                                 version = Assembly.GetEntryAssembly().GetName().Version.ToString();
281
282                                 return version;
283                         }
284                 }
285
286                 public static string SafeTopLevelCaptionFormat {
287                         get {
288                                 return safe_caption_format;
289                         }
290
291                         set {
292                                 safe_caption_format=value;
293                         }
294                 }
295
296                 public static string StartupPath {
297                         get {
298                                 return Path.GetDirectoryName(Application.ExecutablePath);
299                         }
300                 }
301
302                 public static string UserAppDataPath {
303                         get {
304                                 return Path.Combine(Path.Combine(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), CompanyName), ProductName), ProductVersion);
305                         }
306                 }
307
308                 public static RegistryKey UserAppDataRegistry {
309                         get {
310                                 RegistryKey     key;
311
312                                 key = Registry.CurrentUser.OpenSubKey("Software\\" + Application.CompanyName + "\\" + Application.ProductName + "\\" + Application.ProductVersion, true);
313
314                                 return key;
315                         }
316                 }
317
318 #if NET_2_0
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                         CloseForms(null);
447
448                         // FIXME - this needs to be fired when they're all closed
449                         // But CloseForms uses PostMessage, so it gets fired before
450                         // We need to wait on something...
451                         if (ApplicationExit != null) {
452                                 ApplicationExit(null, EventArgs.Empty);
453                         }
454                 }
455
456                 public static void ExitThread() {
457                         CloseForms(Thread.CurrentThread);
458                         MWFThread.Current.Exit();
459                 }
460
461                 public static ApartmentState OleRequired() {
462                         //throw new NotImplementedException("OLE Not supported by this System.Windows.Forms implementation");
463                         return ApartmentState.Unknown;
464                 }
465
466                 public static void OnThreadException(Exception t) {
467                         if (MWFThread.Current.HandlingException) {
468                                 /* we're already handling an exception and we got
469                                    another one?  print it out and exit, this means
470                                    we've got a runtime/SWF bug. */
471                                 Console.WriteLine (t);
472                                 Application.Exit();
473                         }
474
475                         MWFThread.Current.HandlingException = true;
476
477                         if (Application.ThreadException != null) {
478                                 Application.ThreadException(null, new ThreadExceptionEventArgs(t));
479                                 return;
480                         }
481
482                         if (SystemInformation.UserInteractive) {
483                                 Form form = new ThreadExceptionDialog (t);
484                                 form.ShowDialog ();
485                         } else {
486                                 Console.WriteLine (t.ToString ());
487                                 Application.Exit ();
488                         }
489
490                         MWFThread.Current.HandlingException = false;
491                 }
492
493                 public static void RemoveMessageFilter(IMessageFilter filter) {
494                         lock (message_filters) {
495                                 message_filters.Remove(filter);
496                         }
497                 }
498
499                 public static void Run() {
500                         RunLoop(false, new ApplicationContext());
501                 }
502
503                 public static void Run(Form mainForm) {
504                         RunLoop(false, new ApplicationContext(mainForm));
505                 }
506
507                 public static void Run(ApplicationContext context) {
508                         RunLoop(false, context);
509                 }
510
511                 internal static void RunLoop(bool Modal, ApplicationContext context) {
512                         Queue           toplevels;
513                         IEnumerator     control;
514                         MSG             msg;
515                         Object          queue_id;
516                         MWFThread       thread;
517
518
519                         thread = MWFThread.Current;
520
521                         msg = new MSG();
522
523                         if (context == null) {
524                                 context = new ApplicationContext();
525                         }
526
527                         thread.Context = context;
528
529                         if (context.MainForm != null) {
530                                 context.MainForm.is_modal = Modal;
531                                 context.MainForm.context = context;
532                                 context.MainForm.closing = false;
533                                 context.MainForm.Visible = true;        // Cannot use Show() or scaling gets confused by menus
534                                 // FIXME - do we need this?
535                                 //context.MainForm.PerformLayout();
536                                 context.MainForm.Activate();
537                         }
538
539                         #if DebugRunLoop
540                                 Console.WriteLine("Entering RunLoop(Modal={0}, Form={1})", Modal, context.MainForm != null ? context.MainForm.ToString() : "NULL");
541                         #endif
542
543                         if (Modal) {
544                                 Form f;
545
546                                 toplevels = new Queue();
547                                 
548                                 lock (forms) {
549                                         control = forms.GetEnumerator();
550
551                                         while (control.MoveNext()) {
552                                                 f = (Form)control.Current;
553                                                 
554                                                 if (f != context.MainForm) {
555                                                         if (f.IsHandleCreated && XplatUI.IsEnabled(f.Handle)) {
556                                                                 #if DebugRunLoop
557                                                                         Console.WriteLine("      Disabling form {0}", c);
558                                                                 #endif
559                                                                 XplatUI.EnableWindow(f.Handle, false);
560                                                                 toplevels.Enqueue(f);
561                                                         }
562                                                 }
563                                         }
564                                 }
565                                 
566                                 // FIXME - need activate?
567                                 /* make sure the MainForm is enabled */
568                                 if (context.MainForm != null) {
569                                         XplatUI.EnableWindow (context.MainForm.Handle, true);
570                                         XplatUI.SetModal(context.MainForm.Handle, true);
571                                 }
572                         } else {
573                                 toplevels = null;
574                         }
575
576                         queue_id = XplatUI.StartLoop(Thread.CurrentThread);
577                         thread.MessageLoop = true;
578
579                         while (XplatUI.GetMessage(queue_id, ref msg, IntPtr.Zero, 0, 0)) {
580                                 lock (message_filters) {
581                                         if (message_filters.Count > 0) {
582                                                 Message m;
583                                                 bool    drop;
584
585                                                 drop = false;
586                                                 m = Message.Create(msg.hwnd, (int)msg.message, msg.wParam, msg.lParam);
587                                                 for (int i = 0; i < message_filters.Count; i++) {
588                                                         if (((IMessageFilter)message_filters[i]).PreFilterMessage(ref m)) {
589                                                                 // we're dropping the message
590                                                                 drop = true;
591                                                                 break;
592                                                         }
593                                                 }
594                                                 if (drop) {
595                                                         continue;
596                                                 }
597                                         }
598                                 }
599
600                                 switch((Msg)msg.message) {
601                                 case Msg.WM_KEYDOWN:
602                                 case Msg.WM_SYSKEYDOWN:
603                                 case Msg.WM_CHAR:
604                                 case Msg.WM_SYSCHAR:
605                                 case Msg.WM_KEYUP:
606                                 case Msg.WM_SYSKEYUP:
607                                         Message m;
608                                         Control c;
609
610                                         m = Message.Create(msg.hwnd, (int)msg.message, msg.wParam, msg.lParam);
611                                         c = Control.FromHandle(msg.hwnd);
612                                         if ((c != null) && !c.PreProcessMessage(ref m)) {
613                                                 goto default;
614                                         }
615                                         break;
616                                 default:
617                                         XplatUI.TranslateMessage(ref msg);
618                                         XplatUI.DispatchMessage(ref msg);
619                                         break;
620                                 }
621
622                                 // Handle exit, Form might have received WM_CLOSE and set 'closing' in response
623                                 if ((context.MainForm != null) && (context.MainForm.closing || (Modal && !context.MainForm.Visible))) {
624                                         if (!Modal) {
625                                                 XplatUI.PostQuitMessage(0);
626                                         } else {
627                                                 break;
628                                         }
629                                 }
630                         }
631                         #if DebugRunLoop
632                                 Console.WriteLine("   RunLoop loop left");
633                         #endif
634
635                         thread.MessageLoop = false;
636                         XplatUI.EndLoop(Thread.CurrentThread);
637
638                         if (Modal) {
639                                 Form c;
640
641                                 Form old = context.MainForm;
642
643                                 context.MainForm = null;
644
645                                 while (toplevels.Count>0) {
646                                         #if DebugRunLoop
647                                                 Console.WriteLine("      Re-Enabling form form {0}", toplevels.Peek());
648                                         #endif
649                                         c = (Form)toplevels.Dequeue();
650                                         if (c.IsHandleCreated) {
651                                                 XplatUI.EnableWindow(c.window.Handle, true);
652                                                 context.MainForm = c;
653                                         }
654                                 }
655                                 #if DebugRunLoop
656                                         Console.WriteLine("   Done with the re-enable");
657                                 #endif
658                                 if (context.MainForm != null && context.MainForm.IsHandleCreated) {
659                                         XplatUI.SetModal(context.MainForm.Handle, false);
660                                 }
661                                 #if DebugRunLoop
662                                         Console.WriteLine("   Done with the SetModal");
663                                 #endif
664                                 old.Close();
665                                 old.is_modal = false;
666                         }
667
668                         #if DebugRunLoop
669                                 Console.WriteLine("Leaving RunLoop(Modal={0}, Form={1})", Modal, context.MainForm != null ? context.MainForm.ToString() : "NULL");
670                         #endif
671                         if (context.MainForm != null) {
672                                 context.MainForm.context = null;
673                                 context.MainForm = null;
674                         }
675
676                         if (!Modal) {
677                                 thread.Exit();
678                         }
679                 }
680
681                 #endregion      // Public Static Methods
682
683                 #region Events
684                 public static event EventHandler        ApplicationExit;
685
686                 public static event EventHandler        Idle {
687                         add {
688                                 XplatUI.Idle += value;
689                         }
690                         remove {
691                                 XplatUI.Idle -= value;
692                         }
693                 }
694
695                 public static event EventHandler        ThreadExit;
696                 public static event ThreadExceptionEventHandler ThreadException;
697
698 #if NET_2_0
699                 [EditorBrowsable (EditorBrowsableState.Advanced)]
700                 public static event EventHandler EnterThreadModal;
701
702                 [EditorBrowsable (EditorBrowsableState.Advanced)]
703                 public static event EventHandler LeaveThreadModal;
704 #endif
705                 #endregion      // Events
706
707                 #region Internal Methods
708                 internal static void AddForm (Form f)
709                 {
710                         lock (forms)
711                                 forms.Add (f);
712                 }
713                 
714                 internal static void RemoveForm (Form f)
715                 {
716                         lock (forms)
717                                 forms.Remove (f);
718                 }
719                 #endregion
720         }
721 }