[MWF] Implement multi-display support on Linux (Fixes #325669)
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / XplatUIWin32.cs
index dad9f5f7bd9d311fb78393cf63d7e5e9a605d150..e541b1fdb97e8ac428e21922b014da5a404fed6f 100644 (file)
@@ -53,7 +53,6 @@ namespace System.Windows.Forms {
                internal static Rectangle       grab_area;
                internal static WndProc         wnd_proc;
                internal static IntPtr          prev_mouse_hwnd;
-               internal static IntPtr          override_cursor;
                internal static bool            caret_visible;
 
                internal static bool            themes_enabled;
@@ -653,11 +652,7 @@ namespace System.Windows.Forms {
                        internal int                            uTimeoutOrVersion;
                        [MarshalAs(UnmanagedType.ByValTStr, SizeConst=64)]
                        internal string                         szInfoTitle;
-#if NET_2_0
                        internal ToolTipIcon            dwInfoFlags;
-#else
-                       internal int                            dwInfoFlags;
-#endif
                }
 
                [Flags]
@@ -843,6 +838,9 @@ namespace System.Windows.Forms {
                        mouse_state = MouseButtons.None;
                        mouse_position = Point.Empty;
 
+                       grab_confined = false;
+                       grab_area = Rectangle.Empty;
+
                        message_queue = new Queue();
 
                        themes_enabled = false;
@@ -1209,7 +1207,6 @@ namespace System.Windows.Forms {
                        get { return GetSystemParametersInfoBool (SPIAction.SPI_GETMENUDROPALIGNMENT) == true ? LeftRightAlignment.Left : LeftRightAlignment.Right; }
                }
 
-#if NET_2_0
                internal override PowerStatus PowerStatus {
                        get {
                                SYSTEMPOWERSTATUS p = new SYSTEMPOWERSTATUS ();
@@ -1221,7 +1218,6 @@ namespace System.Windows.Forms {
                                return ps;
                        }
                }
-#endif
 
                internal override int SizingBorderWidth {
                        get { return Win32GetSystemMetrics (SystemMetrics.SM_CXSIZEFRAME); }
@@ -1509,6 +1505,14 @@ namespace System.Windows.Forms {
                        }
                }
 
+               [MonoTODO]
+               internal override Screen[] AllScreens {
+                       get {
+                               // To support multiples, we need to use GetMonitorInfo API on Win32
+                               return null;
+                       }
+               }
+
                internal override bool ThemesEnabled {
                        get {
                                return XplatUIWin32.themes_enabled;
@@ -1566,8 +1570,27 @@ namespace System.Windows.Forms {
                        Console.WriteLine("Xplat version $revision: $");
                }
 
-               internal override void AudibleAlert() {
-                       Win32PlaySound("Default", IntPtr.Zero, SndFlags.SND_ALIAS | SndFlags.SND_ASYNC | SndFlags.SND_NOSTOP | SndFlags.SND_NOWAIT);
+               string GetSoundAlias (AlertType alert)
+               {
+                       switch (alert) {
+                               case AlertType.Error:
+                                       return "SystemHand";
+                               case AlertType.Question:
+                                       return "SystemQuestion";
+                               case AlertType.Warning:
+                                       return "SystemExclamation";
+                               case AlertType.Information:
+                                       return "SystemAsterisk";
+                               default:
+                                       return "SystemDefault";
+                       }
+               }
+
+               internal override void AudibleAlert(AlertType alert) {
+                       Win32PlaySound(GetSoundAlias (alert), IntPtr.Zero, SndFlags.SND_ALIAS_ID | SndFlags.SND_ASYNC | SndFlags.SND_NOSTOP | SndFlags.SND_NOWAIT);
+               }
+
+               internal override void BeginMoveResize (IntPtr handle) {
                }
 
                internal override void GetDisplaySize(out Size size) {
@@ -1612,6 +1635,11 @@ namespace System.Windows.Forms {
                        string class_name = RegisterWindowClass (cp.ClassStyle);
                        HwndCreating = hwnd;
 
+                       // We cannot actually send the WS_EX_MDICHILD flag to Windows because we
+                       // are faking MDI, not uses Windows' version.
+                       if ((cp.WindowExStyle & WindowExStyles.WS_EX_MDICHILD) == WindowExStyles.WS_EX_MDICHILD)
+                               cp.WindowExStyle ^= WindowExStyles.WS_EX_MDICHILD;
+                               
                        WindowHandle = Win32CreateWindow (cp.WindowExStyle, class_name, cp.Caption, cp.WindowStyle, location.X, location.Y, cp.Width, cp.Height, ParentHandle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
 
                        HwndCreating = null;
@@ -1638,7 +1666,7 @@ namespace System.Windows.Forms {
                        create_params.Width = Width;
                        create_params.Height = Height;
 
-                       create_params.ClassName=XplatUI.DefaultClassName;
+                       create_params.ClassName=XplatUI.GetDefaultClassName (GetType ());
                        create_params.ClassStyle = 0;
                        create_params.ExStyle=0;
                        create_params.Parent=IntPtr.Zero;
@@ -1937,11 +1965,12 @@ namespace System.Windows.Forms {
                internal override void DoEvents() {
                        MSG msg = new MSG();
 
-                       if (override_cursor != IntPtr.Zero) {
-                               Cursor.Current = null;
-                       }
-
                        while (GetMessage(ref msg, IntPtr.Zero, 0, 0, false)) {
+                               Message m = Message.Create (msg.hwnd, (int)msg.message, msg.wParam, msg.lParam);
+
+                               if (Application.FilterMessage (ref m))
+                                       continue;
+
                                XplatUI.TranslateMessage(ref msg);
                                XplatUI.DispatchMessage(ref msg);
                        }
@@ -2056,6 +2085,8 @@ namespace System.Windows.Forms {
                                        if (msg.hwnd != prev_mouse_hwnd) {
                                                TRACKMOUSEEVENT tme;
 
+                                               mouse_state = Control.FromParamToMouseButtons ((int)msg.lParam.ToInt32());
+
                                                // The current message will be sent out next time around
                                                StoreMessage(ref msg);
 
@@ -2077,7 +2108,9 @@ namespace System.Windows.Forms {
                                case Msg.WM_NCMOUSEMOVE: {
                                        if (wm_nc_registered == null || !wm_nc_registered.Contains (msg.hwnd))
                                                break;
-                                               
+
+                                       mouse_state = Control.FromParamToMouseButtons ((int)msg.lParam.ToInt32 ());
+
                                        TRACKMOUSEEVENT tme;
 
                                        tme = new TRACKMOUSEEVENT ();
@@ -2542,7 +2575,7 @@ namespace System.Windows.Forms {
                                timer_list[index]=timer;
                        }
 
-                       if (Win32SetTimer(FosterParent, index, (uint)timer.Interval, IntPtr.Zero) == IntPtr.Zero)
+                       if (Win32SetTimer(FosterParent, index, (uint)timer.Interval, IntPtr.Zero) != IntPtr.Zero)
                                timer.window = FosterParent;
                        else
                                timer.window = IntPtr.Zero;
@@ -2709,7 +2742,6 @@ namespace System.Windows.Forms {
                        Win32Shell_NotifyIcon(NotifyIconMessage.NIM_DELETE, ref nid);
                }
 
-#if NET_2_0
                internal override void SystrayBalloon(IntPtr hwnd, int timeout, string title, string text, ToolTipIcon icon)
                {
                        NOTIFYICONDATA  nid;
@@ -2727,7 +2759,6 @@ namespace System.Windows.Forms {
                        
                        Win32Shell_NotifyIcon(NotifyIconMessage.NIM_MODIFY, ref nid);
                }
-#endif
 
                internal override void SetBorderStyle(IntPtr handle, FormBorderStyle border_style) {
                        // Nothing to do on Win32
@@ -2885,10 +2916,9 @@ namespace System.Windows.Forms {
 
                }
 
-               internal override void ClipboardStore(IntPtr handle, object obj, int type, XplatUI.ObjectToClipboard converter) {
-                       byte[]  data;
-                       IntPtr  hmem;
-                       IntPtr  hmem_ptr;
+               internal override void ClipboardStore(IntPtr handle, object obj, int type, XplatUI.ObjectToClipboard converter, bool copy)
+               {
+                       byte[]  data = null;
 
                        if (handle != clip_magic) {
                                throw new ArgumentException("handle is not a valid clipboard handle");
@@ -2910,52 +2940,106 @@ namespace System.Windows.Forms {
                        }
 
                        if (type == DataFormats.GetFormat(DataFormats.Rtf).Id) {
-                               hmem = Marshal.StringToHGlobalAnsi((string)obj);
-                               if (Win32SetClipboardData((uint)type, hmem) == IntPtr.Zero )
-                                       throw new ExternalException("Win32SetClipboardData");
-                               return;
+                               data = StringToAnsi ((string)obj);
                        } else switch((ClipboardFormats)type) {
                                case ClipboardFormats.CF_UNICODETEXT: {
-                                       hmem = Marshal.StringToHGlobalUni((string)obj);
-                                       if (Win32SetClipboardData((uint)type, hmem) == IntPtr.Zero)
-                                               throw new ExternalException("Win32SetClipboardData");
-                                       return;
+                                       data = StringToUnicode ((string)obj);
+                                       break;
                                }
 
                                case ClipboardFormats.CF_TEXT: {
-                                       hmem = Marshal.StringToHGlobalAnsi((string)obj);
-                                       if (Win32SetClipboardData((uint)type, hmem) == IntPtr.Zero)
-                                               throw new ExternalException("Win32SetClipboardData");
-                                       return;
+                                       data = StringToAnsi ((string)obj);
+                                       break;
                                }
 
                                case ClipboardFormats.CF_BITMAP:
                                case ClipboardFormats.CF_DIB: {
-                                       data = ImageToDIB((Image)obj);
-
-                                       hmem = Win32GlobalAlloc(GAllocFlags.GMEM_MOVEABLE | GAllocFlags.GMEM_DDESHARE, data.Length);
-                                       hmem_ptr = Win32GlobalLock(hmem);
-                                       Marshal.Copy(data, 0, hmem_ptr, data.Length);
-                                       Win32GlobalUnlock(hmem);
-                                       if (Win32SetClipboardData((uint)ClipboardFormats.CF_DIB, hmem) == IntPtr.Zero)
-                                               throw new ExternalException("Win32SetClipboardData");
-                                       return;
+                                       data = ImageToDIB ((Image)obj);
+                                       type = (int)ClipboardFormats.CF_DIB;
+                                       break;
                                }
 
                                default: {
-                                       if (converter != null && converter(ref type, obj, out data)) {
-                                               hmem = Win32GlobalAlloc(GAllocFlags.GMEM_MOVEABLE | GAllocFlags.GMEM_DDESHARE, data.Length);
-                                               hmem_ptr = Win32GlobalLock(hmem);
-                                               Marshal.Copy(data, 0, hmem_ptr, data.Length);
-                                               Win32GlobalUnlock(hmem);
-                                               if (Win32SetClipboardData((uint)type, hmem) == IntPtr.Zero)
-                                                       throw new ExternalException("Win32SetClipboardData");
+                                       if (converter != null && !converter(ref type, obj, out data)) {
+                                               data = null; // ensure that a failed conversion leaves null.
                                        }
-                                       return;
+                                       break;
                                }
                        }
+                       if (data != null) {
+                               SetClipboardData ((uint)type, data);
+                       }
+               }
+
+               internal static byte[] StringToUnicode (string text)
+               {
+                       return Encoding.Unicode.GetBytes (text + "\0");
                }
 
+               internal static byte[] StringToAnsi (string text)
+               {
+                       // FIXME, follow the behaviour of the previous code using UTF-8,
+                       // but this should be 'ANSI' on Windows, i.e. the current code page.
+                       // Does Encoding.Default work on Windows?
+                       return Encoding.UTF8.GetBytes (text + "\0");
+               }
+
+               private void SetClipboardData (uint type, byte[] data)
+               {
+                       if (data.Length == 0)
+                               // Shouldn't call Win32SetClipboard with NULL, as, from MSDN:
+                               // "This parameter can be NULL, indicating that the window provides data 
+                               //  in the specified clipboard format (renders the format) upon request."
+                               // and I don't think we support that...
+                               // Note this is unrelated to the fact that passing a null obj to 
+                               // ClipboardStore is actually a request to empty the clipboard!
+                               return;
+                       IntPtr hmem = CopyToMoveableMemory (data);
+                       if (hmem == IntPtr.Zero)
+                               // As above, should not call with null.
+                               // (Not that CopyToMoveableMemory should ever return null!)
+                               throw new ExternalException ("CopyToMoveableMemory failed.");
+                       if (Win32SetClipboardData (type, hmem) == IntPtr.Zero)
+                               throw new ExternalException ("Win32SetClipboardData");
+               }
+
+               /// <summary>
+               /// Creates a memory block with GlobalAlloc(GMEM_MOVEABLE), copies the data 
+               /// into it, and returns the handle to the memory.
+               /// </summary>
+               /// -
+               /// <param name="data">The data.  Must not be null or zero-length &#x2014; 
+               /// see the exception notes.</param>
+               /// -
+               /// <returns>The *handle* to the allocated GMEM_MOVEABLE block.</returns>
+               /// -
+               /// <exception cref="T:System.ArgumentException">The data was null or zero 
+               /// length.  This is disallowed since a zero length allocation can't be made
+               /// </exception>
+               /// <exception cref="T:System.ComponentModel.Win32Exception">The allocation, 
+               /// or locking (handle->pointer) failed.
+               /// Either out of memory or the handle table is full (256 max currently).
+               /// Note Win32Exception is a subclass of ExternalException so this is OK in 
+               /// the documented Clipboard interface.
+               /// </exception>
+               internal static IntPtr CopyToMoveableMemory (byte[] data)
+               {
+                       if (data == null || data.Length == 0)
+                               // detect this before GlobalAlloc does.
+                               throw new ArgumentException ("Can't create a zero length memory block.");
+
+                       IntPtr hmem = Win32GlobalAlloc (GAllocFlags.GMEM_MOVEABLE | GAllocFlags.GMEM_DDESHARE, data.Length);
+                       if (hmem == IntPtr.Zero)
+                               throw new Win32Exception ();
+                       IntPtr hmem_ptr = Win32GlobalLock (hmem);
+                       if (hmem_ptr == IntPtr.Zero) // If the allocation was valid this shouldn't occur.
+                               throw new Win32Exception ();
+                       Marshal.Copy (data, 0, hmem_ptr, data.Length);
+                       Win32GlobalUnlock (hmem);
+                       return hmem;
+               }
+
+
                internal override void SetAllowDrop(IntPtr hwnd, bool allowed) {
                        if (allowed) {
                                Win32DnD.RegisterDropTarget(hwnd);