Properly update the combo box selected text when the current item is changed, and...
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / Application.cs
index c362f6e0314fe6f8c8559a3d3ea90180ed3ed0bf..2c2d5bf931d3f08343f4a39947239bc0adacce93 100644 (file)
@@ -39,10 +39,8 @@ using System.IO;
 using System.Reflection;
 using System.Runtime.InteropServices;
 using System.Threading;
-#if NET_2_0
 using System.Text;
 using System.Windows.Forms.VisualStyles;
-#endif
 
 namespace System.Windows.Forms
 {
@@ -148,18 +146,63 @@ namespace System.Windows.Forms
                private static readonly ArrayList message_filters = new ArrayList();
                private static readonly FormCollection forms = new FormCollection ();
 
-#if NET_2_0
                private static bool use_wait_cursor;
                private static ToolStrip keyboard_capture;
                private static VisualStyleState visual_style_state = VisualStyleState.ClientAndNonClientAreasEnabled;
-#endif
+               static bool visual_styles_enabled;
 
                private Application ()
                {
+                       browser_embedded = false;
+               }
+
+               static Application ()
+               {
+                       // Attempt to load UIA support for winforms
+                       // UIA support requires .NET 2.0
+                       InitializeUIAutomation ();
                }
 
                #region Private Methods
 
+               private static void InitializeUIAutomation ()
+               {
+                       // Initialize the UIAutomationWinforms Global class,
+                       // which create some listeners which subscribe to internal
+                       // MWF events so that it can provide a11y support for MWF
+                       const string UIA_WINFORMS_ASSEMBLY = 
+                         "UIAutomationWinforms, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f4ceacb585d99812";
+                       MethodInfo init_method;
+                       Assembly mwf_providers = null;
+                       try {
+                               mwf_providers = Assembly.Load (UIA_WINFORMS_ASSEMBLY);
+                       } catch { }
+                       
+                       if (mwf_providers == null)
+                               return;
+
+                       const string UIA_WINFORMS_TYPE     = "Mono.UIAutomation.Winforms.Global";
+                       const string UIA_WINFORMS_METHOD   = "Initialize";
+                       try {
+                               Type global_type = mwf_providers.GetType (UIA_WINFORMS_TYPE, false);
+                               if (global_type != null) {
+                                       init_method = global_type.GetMethod (UIA_WINFORMS_METHOD, 
+                                                                            BindingFlags.Static | 
+                                                                            BindingFlags.Public);
+                                       if (init_method != null)
+                                               init_method.Invoke (null, new object [] {});
+                                       else
+                                               throw new Exception (String.Format ("Method {0} not found in type {1}.",
+                                                                                   UIA_WINFORMS_METHOD, UIA_WINFORMS_TYPE));
+                               }
+                               else
+                                       throw new Exception (String.Format ("Type {0} not found in assembly {1}.",
+                                                                           UIA_WINFORMS_TYPE, UIA_WINFORMS_ASSEMBLY));
+                       } catch (Exception ex) {
+                               Console.Error.WriteLine ("Error setting up UIA: " + ex);
+                       }
+               }
+               
                internal static void CloseForms (Thread thread)
                {
                        #if DebugRunLoop
@@ -195,30 +238,51 @@ namespace System.Windows.Forms
 
                public static string CommonAppDataPath {
                        get {
-                               return Environment.GetFolderPath (Environment.SpecialFolder.CommonApplicationData);
+                               return CreateDataPath (Environment.GetFolderPath (Environment.SpecialFolder.CommonApplicationData));
                        }
                }
 
                public static RegistryKey CommonAppDataRegistry {
                        get {
-                               return Registry.LocalMachine.OpenSubKey ("Software\\" +
-                                       Application.CompanyName + "\\" + Application.ProductName +
-                                       "\\" + Application.ProductVersion, true);
+                               string key = string.Format ("Software\\{0}\\{1}\\{2}", CompanyName, ProductName, ProductVersion);
+
+                               return Registry.LocalMachine.CreateSubKey (key);
                        }
                }
 
                public static string CompanyName {
                        get {
+                               string company = string.Empty;
+
                                Assembly assembly = Assembly.GetEntryAssembly ();
+                               
                                if (assembly == null)
                                        assembly = Assembly.GetCallingAssembly ();
 
                                AssemblyCompanyAttribute[] attrs = (AssemblyCompanyAttribute[])
                                        assembly.GetCustomAttributes (typeof(AssemblyCompanyAttribute), true);
                                if (attrs != null && attrs.Length > 0)
-                                       return attrs [0].Company;
+                                       company = attrs [0].Company;
+
+                               // If there is no [AssemblyCompany], return the outermost namespace
+                               // on Main ()
+                               if (company == null || company.Length == 0)
+                                       if (assembly.EntryPoint != null) {
+                                               company = assembly.EntryPoint.DeclaringType.Namespace;
+
+                                               if (company != null) {
+                                                       int firstDot = company.IndexOf ('.');
+                                                       if (firstDot >= 0)
+                                                               company = company.Substring (0, firstDot);
+                                               }
+                                       }
 
-                               return assembly.GetName().Name;
+                               // If that doesn't work, return the name of class containing Main ()
+                               if (company == null || company.Length == 0)
+                                       if (assembly.EntryPoint != null)
+                                               company = assembly.EntryPoint.DeclaringType.FullName;
+                               
+                               return company;
                        }
                }
 
@@ -242,15 +306,13 @@ namespace System.Windows.Forms
 
                public static string ExecutablePath {
                        get {
-                               return Assembly.GetEntryAssembly().Location;
+                               return Path.GetFullPath (Environment.GetCommandLineArgs ()[0]);
                        }
                }
 
                public static string LocalUserAppDataPath {
                        get {
-                               return Path.Combine (Path.Combine (Path.Combine (
-                                       Environment.GetFolderPath (Environment.SpecialFolder.LocalApplicationData),
-                                       CompanyName), ProductName), ProductVersion);
+                               return CreateDataPath (Environment.GetFolderPath (Environment.SpecialFolder.LocalApplicationData));
                        }
                }
 
@@ -262,25 +324,46 @@ namespace System.Windows.Forms
 
                public static string ProductName {
                        get {
+                               string name = string.Empty;
+                               
                                Assembly assembly = Assembly.GetEntryAssembly ();
+                               
                                if (assembly == null)
                                        assembly = Assembly.GetCallingAssembly ();
 
                                AssemblyProductAttribute[] attrs = (AssemblyProductAttribute[])
-                                       assembly.GetCustomAttributes(typeof(AssemblyProductAttribute), true);
+                                       assembly.GetCustomAttributes (typeof(AssemblyProductAttribute), true);
 
                                if (attrs != null && attrs.Length > 0)
-                                       return attrs [0].Product;
+                                       name = attrs [0].Product;
+
+                               // If there is no [AssemblyProduct], .NET returns the name of 
+                               // the innermost namespace and if that fails, resorts to the 
+                               // name of the class containing Main ()
+                               if (name == null || name.Length == 0)
+                                       if (assembly.EntryPoint != null) {
+                                               name = assembly.EntryPoint.DeclaringType.Namespace;
+
+                                               if (name != null) {
+                                                       int lastDot = name.LastIndexOf ('.');
+                                                       if (lastDot >= 0 && lastDot < name.Length - 1)
+                                                               name = name.Substring (lastDot + 1);
+                                               }
+
+                                               if (name == null || name.Length == 0)
+                                                       name = assembly.EntryPoint.DeclaringType.FullName;
+                                       }
 
-                               return assembly.GetName ().Name;
+                               return name;
                        }
                }
 
                public static string ProductVersion {
                        get {
-                               String version;
+                               String version = string.Empty;
 
                                Assembly assembly = Assembly.GetEntryAssembly ();
+                               
                                if (assembly == null)
                                        assembly = Assembly.GetCallingAssembly ();
 
@@ -288,10 +371,27 @@ namespace System.Windows.Forms
                                        Attribute.GetCustomAttribute (assembly,
                                        typeof (AssemblyInformationalVersionAttribute))
                                        as AssemblyInformationalVersionAttribute;
+                                       
                                if (infoVersion != null)
                                        version = infoVersion.InformationalVersion;
-                               else
+
+                               // If [AssemblyFileVersion] is present it is used
+                               // before resorting to assembly version
+                               if (version == null || version.Length == 0) {
+                                       AssemblyFileVersionAttribute fileVersion =
+                                               Attribute.GetCustomAttribute (assembly,
+                                               typeof (AssemblyFileVersionAttribute))
+                                               as AssemblyFileVersionAttribute;
+                                       if (fileVersion != null)
+                                               version = fileVersion.Version;
+                               }
+
+                               // If neither [AssemblyInformationalVersionAttribute]
+                               // nor [AssemblyFileVersion] are present, then use
+                               // the assembly version
+                               if (version == null || version.Length == 0)
                                        version = assembly.GetName ().Version.ToString ();
+
                                return version;
                        }
                }
@@ -313,21 +413,18 @@ namespace System.Windows.Forms
 
                public static string UserAppDataPath {
                        get {
-                               return Path.Combine (Path.Combine (Path.Combine (
-                                       Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData),
-                                       CompanyName), ProductName), ProductVersion);
+                               return CreateDataPath (Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData));
                        }
                }
 
                public static RegistryKey UserAppDataRegistry {
                        get {
-                               return Registry.CurrentUser.OpenSubKey ("Software\\" +
-                                       Application.CompanyName + "\\" + Application.ProductName +
-                                       "\\" + Application.ProductVersion, true);
+                               string key = string.Format ("Software\\{0}\\{1}\\{2}", CompanyName, ProductName, ProductVersion);
+                               
+                               return Registry.CurrentUser.CreateSubKey (key);
                        }
                }
 
-#if NET_2_0
                public static bool UseWaitCursor {
                        get {
                                return use_wait_cursor;
@@ -341,7 +438,7 @@ namespace System.Windows.Forms
                                }
                        }
                }
-               
+
                public static bool RenderWithVisualStyles {
                        get {
                                if (VisualStyleInformation.IsSupportedByOS) {
@@ -362,7 +459,6 @@ namespace System.Windows.Forms
                        get { return Application.visual_style_state; }
                        set { Application.visual_style_state = value; }
                }
-#endif
 
                #endregion
 
@@ -375,6 +471,11 @@ namespace System.Windows.Forms
                        }
                }
 
+               internal static void AddKeyFilter (IKeyFilter value)
+               {
+                       XplatUI.AddKeyFilter (value);
+               }
+
                public static void DoEvents ()
                {
                        XplatUI.DoEvents ();
@@ -382,24 +483,23 @@ namespace System.Windows.Forms
 
                public static void EnableVisualStyles ()
                {
+                       visual_styles_enabled = true;
                        XplatUI.EnableThemes ();
                }
 
-#if NET_2_0
                [EditorBrowsable (EditorBrowsableState.Advanced)]
-               public
-#endif
-               static bool FilterMessage (ref Message message)
+               public static bool FilterMessage (ref Message message)
                {
-                       lock (message_filters)
-                               foreach (IMessageFilter filter in message_filters)
+                       lock (message_filters) {
+                               for (int i = 0; i < message_filters.Count; i++) {
+                                       IMessageFilter filter = (IMessageFilter) message_filters[i];
                                        if (filter.PreFilterMessage (ref message))
                                                return true;
-
+                               }
+                       }
                        return false;
                }
                
-#if NET_2_0
                //
                // If true, it uses GDI+, performance reasons were quoted
                //
@@ -514,15 +614,12 @@ namespace System.Windows.Forms
                        Application.Exit ();
                        Process.Start (procInfo);
                }
-#endif
 
                public static void Exit ()
                {
-                       XplatUI.PostQuitMessage (0);
-                       CloseForms (null);
+                       Exit (new CancelEventArgs ());
                }
 
-#if NET_2_0
                [EditorBrowsable (EditorBrowsableState.Advanced)]
                public static void Exit (CancelEventArgs e)
                {
@@ -533,11 +630,12 @@ namespace System.Windows.Forms
 
                                foreach (Form f in forms_to_close) {
                                        // Give each form a chance to cancel the Application.Exit
-                                       e.Cancel = f.FireClosingEvents (CloseReason.ApplicationExitCall);
+                                       e.Cancel = f.FireClosingEvents (CloseReason.ApplicationExitCall, false);
 
                                        if (e.Cancel)
                                                return;
 
+                                       f.suppress_closing_events = true;
                                        f.Close ();
                                        f.Dispose ();
                                }
@@ -545,7 +643,6 @@ namespace System.Windows.Forms
 
                        XplatUI.PostQuitMessage (0);
                }
-#endif
 
                public static void ExitThread()
                {
@@ -593,26 +690,42 @@ namespace System.Windows.Forms
                        }
                }
 
-               public static void RemoveMessageFilter (IMessageFilter filter)
+               public static void RemoveMessageFilter (IMessageFilter value)
                {
                        lock (message_filters) {
-                               message_filters.Remove (filter);
+                               message_filters.Remove (value);
                        }
                }
 
                public static void Run ()
                {
-                       RunLoop (false, new ApplicationContext ());
+                       Run (new ApplicationContext ());
                }
 
                public static void Run (Form mainForm)
                {
-                       RunLoop (false, new ApplicationContext (mainForm));
+                       Run (new ApplicationContext (mainForm));
+               }
+
+               internal static void FirePreRun ()
+               {
+                       EventHandler handler = PreRun;
+                       if (handler != null)
+                               handler (null, EventArgs.Empty);
                }
 
                public static void Run (ApplicationContext context)
                {
+                       // If a sync context hasn't been created by now, create
+                       // a default one
+                       if (SynchronizationContext.Current == null)
+                               SynchronizationContext.SetSynchronizationContext (new SynchronizationContext ());
+                               
                        RunLoop (false, context);
+                       
+                       // Reset the sync context back to the default
+                       if (SynchronizationContext.Current is WindowsFormsSynchronizationContext)
+                               WindowsFormsSynchronizationContext.Uninstall ();
                }
 
                private static void DisableFormsForModalLoop (Queue toplevels, ApplicationContext context)
@@ -752,52 +865,74 @@ namespace System.Windows.Forms
                                        Control c;
                                        c = Control.FromHandle(msg.hwnd);
 
-#if NET_2_0
                                        // If we have a control with keyboard capture (usually a *Strip)
                                        // give it the message, and then drop the message
                                        if (keyboard_capture != null) {
                                                // WM_SYSKEYUP does not make it into ProcessCmdKey, so do it here
-                                               if ((Msg)m.Msg == Msg.WM_SYSKEYUP)
+                                               if ((Msg)m.Msg == Msg.WM_SYSKEYDOWN)
                                                        if (m.WParam.ToInt32() == (int)Keys.Menu) {
                                                                keyboard_capture.GetTopLevelToolStrip ().Dismiss (ToolStripDropDownCloseReason.Keyboard);
                                                                continue;
                                                        }
 
                                                m.HWnd = keyboard_capture.Handle;
-                                               
-                                               if (keyboard_capture.PreProcessControlMessageInternal (ref m) != PreProcessControlState.MessageProcessed && (m.Msg == (int)Msg.WM_KEYDOWN && !keyboard_capture.ProcessControlMnemonic ((char)m.WParam))) {
-                                                       if (c == null || c.Parent == null || !(c.Parent is ToolStrip))
+
+                                               switch (keyboard_capture.PreProcessControlMessageInternal (ref m)) {
+                                                       case PreProcessControlState.MessageProcessed:
                                                                continue;
-                                                       else
-                                                               m.HWnd = msg.hwnd;
-                                               } else
-                                                       continue;
+                                                       case PreProcessControlState.MessageNeeded:
+                                                       case PreProcessControlState.MessageNotNeeded:
+                                                               if (((m.Msg == (int)Msg.WM_KEYDOWN || m.Msg == (int)Msg.WM_CHAR) && !keyboard_capture.ProcessControlMnemonic ((char)m.WParam))) {
+                                                                       if (c == null || !ControlOnToolStrip (c))
+                                                                               continue;
+                                                                       else
+                                                                               m.HWnd = msg.hwnd;
+                                                               } else
+                                                                       continue;
+                                                               
+                                                               break;
+                                               }
                                        }
-#endif
 
-                                       if ((c != null) && c.PreProcessControlMessageInternal (ref m) != PreProcessControlState.MessageProcessed) {
+                                       if (((c != null) && c.PreProcessControlMessageInternal (ref m) != PreProcessControlState.MessageProcessed) ||
+                                               (c == null)) {
                                                goto default;
-                                       }
+                                       } 
                                        break;
-#if NET_2_0
+
                                case Msg.WM_LBUTTONDOWN:
                                case Msg.WM_MBUTTONDOWN:
                                case Msg.WM_RBUTTONDOWN:
                                        if (keyboard_capture != null) {
                                                Control c2 = Control.FromHandle (msg.hwnd);
-                                               
+
+                                               // the target is not a winforms control (an embedded control, perhaps), so
+                                               // release everything
+                                               if (c2 == null) {
+                                                       ToolStripManager.FireAppClicked ();
+                                                       goto default;
+                                               }
+
                                                // If we clicked a ToolStrip, we have to make sure it isn't
                                                // the one we are on, or any of its parents or children
                                                // If we clicked off the dropped down menu, release everything
                                                if (c2 is ToolStrip) {
                                                        if ((c2 as ToolStrip).GetTopLevelToolStrip () != keyboard_capture.GetTopLevelToolStrip ())
                                                                ToolStripManager.FireAppClicked ();
-                                               } else
+                                               } else {
+                                                       if (c2.Parent != null)
+                                                               if (c2.Parent is ToolStripDropDownMenu)
+                                                                       if ((c2.Parent as ToolStripDropDownMenu).GetTopLevelToolStrip () == keyboard_capture.GetTopLevelToolStrip ())
+                                                                               goto default;
+                                                       if (c2.TopLevelControl == null)
+                                                               goto default;
+                                                               
                                                        ToolStripManager.FireAppClicked ();
+                                               }
                                        }
                                        
                                        goto default;
-#endif
+
                                case Msg.WM_QUIT:
                                        quit = true; // make sure we exit
                                        break;
@@ -807,7 +942,11 @@ namespace System.Windows.Forms
                                        break;
                                }
 
-                               // Handle exit, Form might have received WM_CLOSE and set 'closing' in response
+                               // If our Form doesn't have a handle anymore, it means it was destroyed and we need to *wait* for WM_QUIT.
+                               if ((context.MainForm != null) && (!context.MainForm.IsHandleCreated))
+                                       continue;
+
+                               // Handle exit, Form might have received WM_CLOSE and set 'closing' in response.
                                if ((context.MainForm != null) && (context.MainForm.closing || (Modal && !context.MainForm.Visible))) {
                                        if (!Modal) {
                                                XplatUI.PostQuitMessage (0);
@@ -836,7 +975,7 @@ namespace System.Windows.Forms
                                #if DebugRunLoop
                                        Console.WriteLine ("   Done with the SetModal");
                                #endif
-                               old.RaiseCloseEvents (true);
+                               old.RaiseCloseEvents (true, false);
                                old.is_modal = false;
                        }
 
@@ -872,33 +1011,39 @@ namespace System.Windows.Forms
 
                public static event EventHandler ThreadExit;
                public static event ThreadExceptionEventHandler ThreadException;
+               
+               // These are used externally by the UIA framework
+               internal static event EventHandler FormAdded;
+               internal static event EventHandler PreRun;
 
-#if NET_2_0
+#pragma warning disable 0067
+               [MonoTODO]
                [EditorBrowsable (EditorBrowsableState.Advanced)]
                public static event EventHandler EnterThreadModal;
 
+               [MonoTODO]
                [EditorBrowsable (EditorBrowsableState.Advanced)]
                public static event EventHandler LeaveThreadModal;
-#endif
+#pragma warning restore 0067
 
                #endregion      // Events
 
                #region Public Delegates
 
-#if NET_2_0
                [EditorBrowsable (EditorBrowsableState.Advanced)]
                public delegate bool MessageLoopCallback ();
-#endif
 
                #endregion
                
                #region Internal Properties
-#if NET_2_0
                internal static ToolStrip KeyboardCapture {
                        get { return keyboard_capture; }
                        set { keyboard_capture = value; }
                }
-#endif
+
+               internal static bool VisualStylesEnabled {
+                       get { return visual_styles_enabled; }
+               }
                #endregion
                
                #region Internal Methods
@@ -907,6 +1052,13 @@ namespace System.Windows.Forms
                {
                        lock (forms)
                                forms.Add (f);
+                       // Signal that a Form has been added to this
+                       // Application. Used by UIA to detect new Forms that
+                       // need a11y support. This event may be fired even if
+                       // the form has already been added, so clients should
+                       // account for that when handling this signal.
+                       if (FormAdded != null)
+                               FormAdded (f, null);
                }
                
                internal static void RemoveForm (Form f)
@@ -915,6 +1067,35 @@ namespace System.Windows.Forms
                                forms.Remove (f);
                }
 
+               private static bool ControlOnToolStrip (Control c)
+               {
+                       Control p = c.Parent;
+                       
+                       while (p != null) {
+                               if (p is ToolStrip)
+                                       return true;
+                                       
+                               p = p.Parent;
+                       }
+                       
+                       return false;
+               }
+
+               // Takes a starting path, appends company name, product name, and
+               // product version.  If the directory doesn't exist, create it
+               private static string CreateDataPath (string basePath)
+               {
+                       string path;
+
+                       path = Path.Combine (basePath, CompanyName);
+                       path = Path.Combine (path, ProductName);
+                       path = Path.Combine (path, ProductVersion);
+
+                       if (!Directory.Exists (path))
+                               Directory.CreateDirectory (path);
+                       
+                       return path;
+               }
                #endregion
        }
 }