[winforms,fix] Prevent Clipboard APIs from running any Idle handlers
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms.CarbonInternal / MouseHandler.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) 2007 Novell, Inc.
21 //
22 // Authors:
23 //      Geoff Norton  <gnorton@novell.com>
24 //
25 //
26
27 using System;
28 using System.Runtime.InteropServices;
29
30 namespace System.Windows.Forms.CarbonInternal {
31         internal class MouseHandler : EventHandlerBase, IEventHandler {
32                 internal const uint kEventMouseDown = 1;
33                 internal const uint kEventMouseUp = 2;
34                 internal const uint kEventMouseMoved = 5;
35                 internal const uint kEventMouseDragged = 6;
36                 internal const uint kEventMouseEntered = 8;
37                 internal const uint kEventMouseExited = 9;
38                 internal const uint kEventMouseWheelMoved = 10;
39                 internal const uint kEventMouseScroll = 11;
40
41                 internal const uint kEventParamMouseLocation = 1835822947;
42                 internal const uint kEventParamMouseButton = 1835168878;
43                 internal const uint kEventParamMouseWheelAxis = 1836540280;
44                 internal const uint kEventParamMouseWheelDelta = 1836541036;
45                 internal const uint typeLongInteger = 1819242087;
46                 internal const uint typeMouseWheelAxis = 1836540280;
47                 internal const uint typeMouseButton = 1835168878;
48                 internal const uint typeQDPoint = 1363439732;
49
50                 internal const uint kEventMouseWheelAxisX = 0;
51                 internal const uint kEventMouseWheelAxisY = 1;
52
53                 internal const uint DoubleClickInterval = 7500000;
54                 internal static ClickStruct ClickPending;
55                 
56                 internal MouseHandler (XplatUICarbon driver) : base (driver) {}
57
58                 public bool ProcessEvent (IntPtr callref, IntPtr eventref, IntPtr handle, uint kind, ref MSG msg) {
59                         QDPoint qdpoint = new QDPoint ();
60                         CGPoint point = new CGPoint ();
61                         Rect window_bounds = new Rect ();
62                         IntPtr view_handle = IntPtr.Zero;
63                         IntPtr window_handle = IntPtr.Zero;
64                         bool client = true;
65                         ushort button = 0;
66                         Hwnd hwnd;
67
68                         GetEventParameter (eventref, kEventParamMouseLocation, typeQDPoint, IntPtr.Zero, (uint)Marshal.SizeOf (typeof (QDPoint)), IntPtr.Zero, ref qdpoint);
69                         GetEventParameter (eventref, kEventParamMouseButton, typeMouseButton, IntPtr.Zero, (uint)Marshal.SizeOf (typeof (ushort)), IntPtr.Zero, ref button);
70                         
71                         if (button == 1 && ((Driver.ModifierKeys & Keys.Control) != 0))
72                                 button = 2;
73
74                         point.x = qdpoint.x;
75                         point.y = qdpoint.y;
76
77                         if (FindWindow (qdpoint, ref window_handle) == 5)
78                                 return true;
79
80                         GetWindowBounds (handle, 33, ref window_bounds);
81                         HIViewFindByID (HIViewGetRoot (handle), new HIViewID (EventHandler.kEventClassWindow, 1), ref window_handle);
82
83                         point.x -= window_bounds.left;
84                         point.y -= window_bounds.top;
85
86                         HIViewGetSubviewHit (window_handle, ref point, true, ref view_handle);
87                         HIViewConvertPoint (ref point, window_handle, view_handle);
88
89                         hwnd = Hwnd.ObjectFromHandle (view_handle);
90
91                         if (hwnd != null)
92                                 client = (hwnd.ClientWindow == view_handle ? true : false);
93
94                         if (XplatUICarbon.Grab.Hwnd != IntPtr.Zero) {
95                                 hwnd = Hwnd.ObjectFromHandle (XplatUICarbon.Grab.Hwnd); 
96                                 client = true;
97                         }
98                         if (hwnd == null)
99                                 return true;
100                         
101                         if (client) {
102                                 qdpoint.x = (short) point.x;
103                                 qdpoint.y = (short) point.y;
104
105                                 Driver.ScreenToClient (hwnd.Handle, ref qdpoint);
106                         } else {
107                                 point.x = qdpoint.x;
108                                 point.y = qdpoint.y;
109                         }
110
111                         msg.hwnd = hwnd.Handle;
112                         msg.lParam = (IntPtr) ((ushort) point.y << 16 | (ushort) point.x);
113
114                         switch (kind) {
115                                 case kEventMouseDown:
116                                         UpdateMouseState (button, true);
117                                         msg.message = (client ? Msg.WM_MOUSEMOVE : Msg.WM_NCMOUSEMOVE) + ((button - 1) * 3) + 1;
118                                         msg.wParam = Driver.GetMousewParam (0);
119                                         if (ClickPending.Pending && (((DateTime.Now.Ticks - ClickPending.Time) < DoubleClickInterval) && (msg.hwnd == ClickPending.Hwnd) && (msg.wParam == ClickPending.wParam) && (msg.lParam == ClickPending.lParam) && (msg.message == ClickPending.Message))) {
120                                                 msg.message = (client ? Msg.WM_MOUSEMOVE : Msg.WM_NCMOUSEMOVE) + ((button - 1) * 3) + 3;
121                                                 ClickPending.Pending = false;
122                                         } else {
123                                                 ClickPending.Pending = true;
124                                                 ClickPending.Hwnd = msg.hwnd;
125                                                 ClickPending.Message = msg.message;
126                                                 ClickPending.wParam = msg.wParam;
127                                                 ClickPending.lParam = msg.lParam;
128                                                 ClickPending.Time = DateTime.Now.Ticks;
129                                         }
130                                         break;
131                                 case kEventMouseUp:
132                                         UpdateMouseState (button, false);
133                                         msg.message = (client ? Msg.WM_MOUSEMOVE : Msg.WM_NCMOUSEMOVE) + ((button - 1) * 3) + 2;
134                                         msg.wParam = Driver.GetMousewParam (0);
135                                         break;
136                                 case kEventMouseDragged:
137                                 case kEventMouseMoved:
138                                         if (XplatUICarbon.Grab.Hwnd == IntPtr.Zero) {
139                                                 IntPtr ht = IntPtr.Zero;
140                                                 if (client) {
141                                                         ht = (IntPtr) HitTest.HTCLIENT;
142                                                         NativeWindow.WndProc(msg.hwnd, Msg.WM_SETCURSOR, msg.hwnd, (IntPtr)HitTest.HTCLIENT);
143                                                 } else {
144                                                         ht = (IntPtr) NativeWindow.WndProc (hwnd.client_window, Msg.WM_NCHITTEST, IntPtr.Zero, msg.lParam).ToInt32 ();
145                                                         NativeWindow.WndProc(hwnd.client_window, Msg.WM_SETCURSOR, msg.hwnd, ht);
146                                                 }
147                                         }
148                                         msg.message = (client ? Msg.WM_MOUSEMOVE : Msg.WM_NCMOUSEMOVE);
149                                         msg.wParam = Driver.GetMousewParam (0);
150                                         break;
151                                 case kEventMouseWheelMoved:
152                                 case kEventMouseScroll:
153                                         UInt16 axis = 0;
154                                         Int32 delta = 0;
155
156                                         GetEventParameter (eventref, kEventParamMouseWheelAxis, typeMouseWheelAxis, IntPtr.Zero, (uint)Marshal.SizeOf (typeof (UInt16)), IntPtr.Zero, ref axis);
157                                         GetEventParameter (eventref, kEventParamMouseWheelDelta, typeLongInteger, IntPtr.Zero, (uint)Marshal.SizeOf (typeof (Int32)), IntPtr.Zero, ref delta);
158
159                                         if (axis == kEventMouseWheelAxisY) {
160                                                 msg.hwnd = XplatUICarbon.FocusWindow;
161                                                 msg.message = Msg.WM_MOUSEWHEEL;
162                                                 msg.wParam = Driver.GetMousewParam (delta*40);
163                                                 return true;
164                                         }
165                                         break;
166                                 default:
167                                         return false;
168                         }
169                         Driver.mouse_position.X = (int) point.x;
170                         Driver.mouse_position.Y = (int) point.y;
171                         return true;
172                 }
173                 
174                 internal bool TranslateMessage (ref MSG msg) {
175                         if (msg.message == Msg.WM_MOUSEMOVE || msg.message == Msg.WM_NCMOUSEMOVE) {
176                                 Hwnd hwnd = Hwnd.ObjectFromHandle (msg.hwnd);
177                                 if (XplatUICarbon.MouseHwnd == null) { 
178                                         Driver.PostMessage (hwnd.Handle, Msg.WM_MOUSE_ENTER, IntPtr.Zero, IntPtr.Zero);
179                                         Cursor.SetCursor (hwnd.Cursor);
180                                 } else if (XplatUICarbon.MouseHwnd.Handle != hwnd.Handle) {
181                                         Driver.PostMessage (XplatUICarbon.MouseHwnd.Handle, Msg.WM_MOUSELEAVE, IntPtr.Zero, IntPtr.Zero);
182                                         Driver.PostMessage (hwnd.Handle, Msg.WM_MOUSE_ENTER, IntPtr.Zero, IntPtr.Zero);
183                                         Cursor.SetCursor (hwnd.Cursor);
184                                 }
185                                 XplatUICarbon.MouseHwnd = hwnd;
186                         }
187                         
188                         return false;
189                 }
190
191                 private void UpdateMouseState (int button, bool down) {
192                         switch (button) {
193                                 case 1:
194                                         if (down) XplatUICarbon.MouseState |= MouseButtons.Left; else XplatUICarbon.MouseState &= ~MouseButtons.Left;
195                                         break;
196                                 case 2:
197                                         if (down) XplatUICarbon.MouseState |= MouseButtons.Right; else XplatUICarbon.MouseState &= ~MouseButtons.Right;
198                                         break;
199                                 case 3:
200                                         if (down) XplatUICarbon.MouseState |= MouseButtons.Middle; else XplatUICarbon.MouseState &= ~MouseButtons.Middle;
201                                         break;
202                         }
203                 }
204
205                 [DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
206                 static extern int GetEventParameter (IntPtr eventref, uint name, uint type, IntPtr outtype, uint size, IntPtr outsize, ref QDPoint data);
207                 [DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
208                 static extern int GetEventParameter (IntPtr eventref, uint name, uint type, IntPtr outtype, uint size, IntPtr outsize, ref Int32 data);
209                 [DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
210                 static extern int GetEventParameter (IntPtr eventref, uint name, uint type, IntPtr outtype, uint size, IntPtr outsize, ref ushort data);
211
212                 [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
213                 internal static extern short FindWindow (QDPoint point, ref IntPtr handle);
214                 [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
215                 internal static extern int GetWindowBounds (IntPtr handle, uint region, ref Rect bounds);
216                 [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
217                 internal static extern int HIViewConvertPoint (ref CGPoint point, IntPtr source_view, IntPtr target_view);
218                 [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
219                 internal static extern IntPtr HIViewGetRoot (IntPtr handle);
220                 [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
221                 internal static extern int HIViewGetSubviewHit (IntPtr content_view, ref CGPoint point, bool tval, ref IntPtr hit_view);
222                 [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
223                 internal static extern int HIViewFindByID (IntPtr root_window, HIViewID id, ref IntPtr view_handle);
224                 
225                 [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
226                 internal static extern int GetCurrentEventButtonState ();
227         }
228 }