2006-05-16 Peter Dennis Bartok <pbartok@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 //
25
26 // COMPLETE
27
28 #undef DebugRunLoop
29
30 using Microsoft.Win32;
31 using System;
32 using System.Drawing;
33 using System.ComponentModel;
34 using System.Collections;
35 using System.Diagnostics;
36 using System.Globalization;
37 using System.IO;
38 using System.Reflection;
39 using System.Runtime.InteropServices;
40 using System.Threading;
41
42 namespace System.Windows.Forms {
43         public sealed class Application {
44                 private class MWFThread {
45                         #region Fields
46                         private ApplicationContext      context;
47                         private bool                    messageloop_started;
48                         private int                     thread_id;
49
50                         private static Hashtable        threads = new Hashtable();
51                         #endregion      // Fields
52
53                         #region Constructors
54                         private MWFThread() {
55                         }
56                         #endregion      // Constructors
57
58                         #region Properties
59                         public ApplicationContext Context {
60                                 get {
61                                         return context;
62                                 }
63
64                                 set {
65                                         context = value;
66                                 }
67                         }
68
69                         public bool MessageLoop {
70                                 get {
71                                         return messageloop_started;
72                                 }
73
74                                 set {
75                                         messageloop_started = value;
76                                 }
77                         }
78
79
80                         public static int LoopCount {
81                                 get {
82                                         IEnumerator     e;
83                                         int             loops;
84                                         MWFThread       thread;
85
86                                         e = threads.Values.GetEnumerator();
87                                         loops = 0;
88
89                                         while (e.MoveNext()) {
90                                                 thread = (MWFThread)e.Current;
91                                                 if (thread != null && thread.messageloop_started) {
92                                                         loops++;
93                                                 }
94                                         }
95
96                                         return loops;
97                                 }
98                         }
99
100                         public static MWFThread Current {
101                                 get {
102                                         MWFThread       thread;
103
104                                         thread = null;
105                                         lock (threads) {
106                                                 thread = (MWFThread)threads[Thread.CurrentThread.GetHashCode()];
107                                                 if (thread == null) {
108                                                         thread = new MWFThread();
109                                                         thread.thread_id = Thread.CurrentThread.GetHashCode();
110                                                         threads[thread.thread_id] = thread;
111                                                 }
112                                         }
113
114                                         return thread;
115                                 }
116                         }
117                         #endregion      // Properties
118
119                         #region Methods
120                         public void Exit() {
121                                 if (context != null) {
122                                         context.ExitThread();
123                                 }
124                                 context = null;
125
126                                 if (Application.ThreadExit != null) {
127                                         Application.ThreadExit(null, EventArgs.Empty);
128                                 }
129
130                                 if (LoopCount == 0) {
131                                         if (Application.ApplicationExit != null) {
132                                                 Application.ApplicationExit(null, EventArgs.Empty);
133                                         }
134                                 }
135                                 threads[thread_id] = null;
136                         }
137                         #endregion      // Methods
138                 }
139
140                 private static bool                     browser_embedded        = false;
141                 private static InputLanguage            input_language          = InputLanguage.CurrentInputLanguage;
142                 private static string                   safe_caption_format     = "{1} - {0} - {2}";
143                 private static ArrayList                message_filters         = new ArrayList();
144
145                 private Application () {
146                 }
147
148                 #region Private Methods
149                 private static void CloseForms(Thread thread) {
150                         Control         c;
151                         IEnumerator     control;
152                         bool            all;
153
154                         #if DebugRunLoop
155                                 Console.WriteLine("   CloseForms({0}) called", thread);
156                         #endif
157                         if (thread == null) {
158                                 all = true;
159                         } else {
160                                 all = false;
161                         }
162
163                         control = Control.controls.GetEnumerator();
164
165                         while (control.MoveNext()) {
166                                 c = (Control)control.Current;
167                                 if (c is Form) {
168                                         if (all || (thread == c.creator_thread)) {
169                                                 if (c.IsHandleCreated) {
170                                                         XplatUI.PostMessage(c.Handle, Msg.WM_CLOSE_INTERNAL, IntPtr.Zero, IntPtr.Zero);
171                                                 }
172                                                 #if DebugRunLoop
173                                                         Console.WriteLine("      Closing form {0}", c);
174                                                 #endif
175                                         }
176                                 }
177                         }
178
179                 }
180                 #endregion      // Private methods
181
182                 #region Public Static Properties
183                 public static bool AllowQuit {
184                         get {
185                                 return browser_embedded;
186                         }
187                 }
188
189                 public static string CommonAppDataPath {
190                         get {
191                                 return Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
192                         }
193                 }
194
195                 public static RegistryKey CommonAppDataRegistry {
196                         get {
197                                 RegistryKey     key;
198
199                                 key = Registry.LocalMachine.OpenSubKey("Software\\" + Application.CompanyName + "\\" + Application.ProductName + "\\" + Application.ProductVersion, true);
200
201                                 return key;
202                         }
203                 }
204
205                 public static string CompanyName {
206                         get {
207                                 AssemblyCompanyAttribute[] attrs = (AssemblyCompanyAttribute[]) Assembly.GetEntryAssembly().GetCustomAttributes(typeof(AssemblyCompanyAttribute), true);
208                                 
209                                 if ((attrs != null) && attrs.Length>0) {
210                                         return attrs[0].Company;
211                                 }
212
213                                 return Assembly.GetEntryAssembly().GetName().Name;
214                         }
215                 }
216
217                 public static CultureInfo CurrentCulture {
218                         get {
219                                 return Thread.CurrentThread.CurrentUICulture;
220                         }
221
222                         set {
223                                 
224                                 Thread.CurrentThread.CurrentUICulture=value;
225                         }
226                 }
227
228                 public static InputLanguage CurrentInputLanguage {
229                         get {
230                                 return input_language;
231                         }
232
233                         set {
234                                 input_language=value;
235                         }
236                 }
237
238                 public static string ExecutablePath {
239                         get {
240                                 return Assembly.GetEntryAssembly().Location;
241                         }
242                 }
243
244                 public static string LocalUserAppDataPath {
245                         get {
246                                 return Path.Combine(Path.Combine(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), CompanyName), ProductName), ProductVersion);
247                         }
248                 }
249
250                 public static bool MessageLoop {
251                         get {
252                                 return MWFThread.Current.MessageLoop;
253                         }
254                 }
255
256                 public static string ProductName {
257                         get {
258                                 AssemblyProductAttribute[] attrs = (AssemblyProductAttribute[]) Assembly.GetEntryAssembly().GetCustomAttributes(typeof(AssemblyProductAttribute), true);
259                                 
260                                 if ((attrs != null) && attrs.Length>0) {
261                                         return attrs[0].Product;
262                                 }
263
264                                 return Assembly.GetEntryAssembly().GetName().Name;
265                         }
266                 }
267
268                 public static string ProductVersion {
269                         get {
270                                 String version;
271
272                                 version = Assembly.GetEntryAssembly().GetName().Version.ToString();
273
274                                 if (version.StartsWith("0.")) {
275                                         version="1." + version.Substring(2);
276                                 }
277                                 return version;
278                         }
279                 }
280
281                 public static string SafeTopLevelCaptionFormat {
282                         get {
283                                 return safe_caption_format;
284                         }
285
286                         set {
287                                 safe_caption_format=value;
288                         }
289                 }
290
291                 public static string StartupPath {
292                         get {
293                                 return Path.GetDirectoryName(Application.ExecutablePath);
294                         }
295                 }
296
297                 public static string UserAppDataPath {
298                         get {
299                                 return Path.Combine(Path.Combine(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), CompanyName), ProductName), ProductVersion);
300                         }
301                 }
302
303                 public static RegistryKey UserAppDataRegistry {
304                         get {
305                                 RegistryKey     key;
306
307                                 key = Registry.CurrentUser.OpenSubKey("Software\\" + Application.CompanyName + "\\" + Application.ProductName + "\\" + Application.ProductVersion, true);
308
309                                 return key;
310                         }
311                 }
312                 #endregion
313
314                 #region Public Static Methods
315                 public static void AddMessageFilter(IMessageFilter value) {
316                         message_filters.Add(value);
317                 }
318
319                 public static void DoEvents() {
320                         XplatUI.DoEvents();
321                 }
322
323                 public static void EnableVisualStyles() {
324                         XplatUI.EnableThemes();
325                 }
326
327 #if NET_2_0
328                 //
329                 // If true, it uses GDI+, performance reasons were quoted
330                 //
331                 static internal bool use_compatible_text_rendering = true;
332                 
333                 public static void SetCompatibleTextRenderingDefault (bool defaultValue)
334                 {
335                         use_compatible_text_rendering = defaultValue;
336                 }
337 #endif
338
339                 public static void Exit() {
340                         CloseForms(null);
341
342                         // FIXME - this needs to be fired when they're all closed
343                         // But CloseForms uses PostMessage, so it gets fired before
344                         // We need to wait on something...
345                         if (ApplicationExit != null) {
346                                 ApplicationExit(null, EventArgs.Empty);
347                         }
348                 }
349
350                 public static void ExitThread() {
351                         CloseForms(Thread.CurrentThread);
352                         MWFThread.Current.Exit();
353                 }
354
355                 public static ApartmentState OleRequired() {
356                         //throw new NotImplementedException("OLE Not supported by this System.Windows.Forms implementation");
357                         return ApartmentState.Unknown;
358                 }
359
360                 public static void OnThreadException(Exception t) {
361                         if (Application.ThreadException != null) {
362                                 Application.ThreadException(null, new ThreadExceptionEventArgs(t));
363                                 return;
364                         }
365
366                         if (SystemInformation.UserInteractive) {
367                                 Form form = new ThreadExceptionDialog (t);
368                                 form.ShowDialog ();
369                         } else {
370                                 Console.WriteLine (t.ToString ());
371                         }
372                 }
373
374                 public static void RemoveMessageFilter(IMessageFilter filter) {
375                         message_filters.Remove(filter);
376                 }
377
378                 public static void Run() {
379                         RunLoop(false, new ApplicationContext());
380                 }
381
382                 public static void Run(Form mainForm) {
383                         RunLoop(false, new ApplicationContext(mainForm));
384                 }
385
386                 public static void Run(ApplicationContext context) {
387                         RunLoop(false, context);
388                 }
389
390                 internal static void RunLoop(bool Modal, ApplicationContext context) {
391                         Queue           toplevels;
392                         IEnumerator     control;
393                         MSG             msg;
394                         Object          queue_id;
395                         MWFThread       thread;
396
397
398                         thread = MWFThread.Current;
399
400                         msg = new MSG();
401
402                         if (context == null) {
403                                 context = new ApplicationContext();
404                         }
405
406                         thread.Context = context;
407
408                         if (context.MainForm != null) {
409                                 context.MainForm.Visible = true;        // Cannot use Show() or scaling gets confused by menus
410                                 // FIXME - do we need this?
411                                 //context.MainForm.PerformLayout();
412                                 context.MainForm.context = context;
413                                 context.MainForm.Activate();
414                                 context.MainForm.closing = false;
415                         }
416
417                         #if DebugRunLoop
418                                 Console.WriteLine("Entering RunLoop(Modal={0}, Form={1})", Modal, context.MainForm != null ? context.MainForm.ToString() : "NULL");
419                         #endif
420
421                         if (Modal) {
422                                 Control c;
423
424                                 if (context.MainForm.Modal) {
425                                         throw new Exception("fixme");
426                                 }
427                                 context.MainForm.is_modal = true;
428
429                                 toplevels = new Queue();
430                                 control = Control.controls.GetEnumerator();
431
432                                 while (control.MoveNext()) {
433
434                                         c = (Control)control.Current;
435                                         if (c is Form && (c != context.MainForm)) {
436                                                 if (c.IsHandleCreated && XplatUI.IsEnabled(c.Handle)) {
437                                                         #if DebugRunLoop
438                                                                 Console.WriteLine("      Disabling form {0}", c);
439                                                         #endif
440                                                         XplatUI.EnableWindow(c.Handle, false);
441                                                         toplevels.Enqueue(c);
442                                                 }
443                                         }
444                                 }
445                                 // FIXME - need activate?
446
447                                 XplatUI.SetModal(context.MainForm.Handle, true);
448                         } else {
449                                 toplevels = null;
450                         }
451
452                         queue_id = XplatUI.StartLoop(Thread.CurrentThread);
453                         thread.MessageLoop = true;
454
455                         while (XplatUI.GetMessage(queue_id, ref msg, IntPtr.Zero, 0, 0)) {
456                                 if ((message_filters != null) && (message_filters.Count > 0)) {
457                                         Message m;
458                                         bool    drop;
459
460                                         drop = false;
461                                         m = Message.Create(msg.hwnd, (int)msg.message, msg.wParam, msg.lParam);
462                                         for (int i = 0; i < message_filters.Count; i++) {
463                                                 if (((IMessageFilter)message_filters[i]).PreFilterMessage(ref m)) {
464                                                         // we're dropping the message
465                                                         drop = true;
466                                                         break;
467                                                 }
468                                         }
469                                         if (drop) {
470                                                 continue;
471                                         }
472                                 }
473
474                                 switch((Msg)msg.message) {
475                                         case Msg.WM_KEYDOWN:
476                                         case Msg.WM_SYSKEYDOWN:
477                                         case Msg.WM_CHAR:
478                                         case Msg.WM_SYSCHAR:
479                                         case Msg.WM_KEYUP:
480                                         case Msg.WM_SYSKEYUP: {
481                                                 Message m;
482                                                 Control c;
483
484                                                 m = Message.Create(msg.hwnd, (int)msg.message, msg.wParam, msg.lParam);
485                                                 c = Control.FromHandle(msg.hwnd);
486                                                 if ((c != null) && !c.PreProcessMessage(ref m)) {
487                                                         goto default;
488                                                 }
489                                                 break;
490                                         }
491                                         default: {
492                                                 XplatUI.TranslateMessage(ref msg);
493                                                 XplatUI.DispatchMessage(ref msg);
494                                                 break;
495                                         }
496                                 }
497
498                                 // Handle exit, Form might have received WM_CLOSE and set 'closing' in response
499                                 if ((context.MainForm != null) && context.MainForm.closing) {
500                                         if (!Modal) {
501                                                 XplatUI.PostQuitMessage(0);
502                                         } else {
503                                                 break;
504                                         }
505                                 }
506                         }
507                         #if DebugRunLoop
508                                 Console.WriteLine("   RunLoop loop left");
509                         #endif
510
511                         thread.MessageLoop = false;
512                         XplatUI.EndLoop(Thread.CurrentThread);
513
514                         if (Modal) {
515                                 Control c;
516
517                                 context.MainForm.Hide();
518                                 context.MainForm.is_modal = false;
519
520                                 while (toplevels.Count>0) {
521                                         #if DebugRunLoop
522                                                 Console.WriteLine("      Re-Enabling form form {0}", toplevels.Peek());
523                                         #endif
524                                         c = (Control)toplevels.Dequeue();
525                                         if (c.IsHandleCreated) {
526                                                 XplatUI.EnableWindow(c.window.Handle, true);
527                                         }
528                                 }
529                                 #if DebugRunLoop
530                                         Console.WriteLine("   Done with the re-enable");
531                                 #endif
532                                 if (context.MainForm.IsHandleCreated) {
533                                         XplatUI.SetModal(context.MainForm.Handle, false);
534                                 }
535                                 #if DebugRunLoop
536                                         Console.WriteLine("   Done with the SetModal");
537                                 #endif
538                         }
539
540                         #if DebugRunLoop
541                                 Console.WriteLine("Leaving RunLoop(Modal={0}, Form={1})", Modal, context.MainForm != null ? context.MainForm.ToString() : "NULL");
542                         #endif
543                         if (context.MainForm != null) {
544                                 context.MainForm.context = null;
545                         }
546
547                         if (!Modal) {
548                                 thread.Exit();
549                         }
550                 }
551
552                 #endregion      // Public Static Methods
553
554                 #region Events
555                 public static event EventHandler        ApplicationExit;
556
557                 public static event EventHandler        Idle {
558                         add {
559                                 XplatUI.Idle += value;
560                         }
561                         remove {
562                                 XplatUI.Idle -= value;
563                         }
564                 }
565
566                 public static event EventHandler        ThreadExit;
567                 public static event ThreadExceptionEventHandler ThreadException;
568                 #endregion      // Events
569         }
570 }