Add a more functional (i.e. fewer-stubs) implementation of System.Data.Linq.
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms.CarbonInternal / Dnd.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
28 using System;
29 using System.IO;
30 using System.Text;
31 using System.Drawing;
32 using System.Threading;
33 using System.Collections;
34 using System.Windows.Forms;
35 using System.Runtime.Serialization;
36 using System.Runtime.InteropServices;
37 using System.Runtime.Serialization.Formatters.Binary;
38
39 namespace System.Windows.Forms.CarbonInternal {
40         internal delegate void DragTrackingDelegate (short message, IntPtr window, IntPtr data, IntPtr dragref);
41
42         internal class Dnd {
43                 internal const uint kEventParamDragRef = 1685217639; 
44                 internal const uint typeDragRef = 1685217639;
45
46                 internal const uint typeMono = 1836019311;
47                 internal const uint typeMonoSerializedObject = 1836279154;
48
49                 private static DragDropEffects effects = DragDropEffects.None;
50                 private static DragTrackingDelegate DragTrackingHandler = new DragTrackingDelegate (TrackingCallback);
51
52                 static Dnd () {
53                         InstallTrackingHandler (DragTrackingHandler, IntPtr.Zero, IntPtr.Zero);
54                 }
55
56                 internal Dnd () {
57                 }
58
59                 internal static void TrackingCallback (short message, IntPtr window, IntPtr data, IntPtr dragref) {
60                         XplatUICarbon.GetInstance ().FlushQueue ();
61                 }
62         
63                 internal static DragDropEffects DragActionsToEffects (UInt32 actions) {
64                         DragDropEffects effects = DragDropEffects.None;
65                         if ((actions & 1) != 0)
66                                 effects |= DragDropEffects.Copy;
67                         if ((actions & (1 << 4)) != 0)
68                                 effects |= DragDropEffects.Move;
69                         if ((actions & 0xFFFFFFFF) != 0)
70                                 effects |= DragDropEffects.All;
71
72                         return effects;
73                 }
74
75                 internal static DataObject DragToDataObject (IntPtr dragref) {
76                         UInt32 items = 0;
77                         ArrayList flavorlist = new ArrayList ();
78
79                         CountDragItems (dragref, ref items);
80                         
81                         for (uint item_counter = 1; item_counter <= items; item_counter++) {
82                                 IntPtr itemref = IntPtr.Zero;
83                                 UInt32 flavors = 0;
84                                 
85                                 GetDragItemReferenceNumber (dragref, item_counter, ref itemref);
86                                 CountDragItemFlavors (dragref, itemref, ref flavors);
87                                 for (uint flavor_counter = 1; flavor_counter <= flavors; flavor_counter++) {
88                                         FlavorHandler flavor = new FlavorHandler (dragref, itemref, flavor_counter);
89                                         if (flavor.Supported)
90                                                 flavorlist.Add (flavor);
91                                 }
92                         }
93
94                         if (flavorlist.Count > 0) {
95                                 return ((FlavorHandler) flavorlist [0]).Convert (flavorlist);
96                         } 
97
98                         return new DataObject ();
99                 }
100
101                 internal static bool HandleEvent (IntPtr callref, IntPtr eventref, IntPtr handle, uint kind, ref MSG msg) {
102                         Control control;
103                         DataObject data;
104                         DragEventArgs drag_event;
105                         DragDropEffects allowed;
106                         QDPoint point = new QDPoint ();
107                         UInt32 actions = 0;
108                         IntPtr dragref = IntPtr.Zero;
109                         Hwnd hwnd = Hwnd.ObjectFromHandle (handle);
110
111                         if (hwnd == null || hwnd.Handle != handle)
112                                 return false;
113
114                         GetEventParameter (eventref, kEventParamDragRef, typeDragRef, IntPtr.Zero, (uint) Marshal.SizeOf (typeof (IntPtr)), IntPtr.Zero, ref dragref);
115                         XplatUICarbon.GetGlobalMouse (ref point);
116                         GetDragAllowableActions (dragref, ref actions);
117                         control = Control.FromHandle (hwnd.Handle);
118                         allowed = DragActionsToEffects (actions);
119                         data = DragToDataObject (dragref);
120                         drag_event = new DragEventArgs (data, 0, point.x, point.y, allowed, DragDropEffects.None);
121                                 
122                         switch (kind) {
123                                 case ControlHandler.kEventControlDragEnter: {
124                                         bool accept = control.AllowDrop;
125                                         SetEventParameter (eventref, ControlHandler.kEventParamControlLikesDrag, ControlHandler.typeBoolean, (uint)Marshal.SizeOf (typeof (bool)), ref accept);
126
127                                         control.DndEnter (drag_event);
128                                         effects = drag_event.Effect;
129                                         return false;
130                                 }
131                                 case ControlHandler.kEventControlDragWithin:
132                                         control.DndOver (drag_event);
133                                         effects = drag_event.Effect;
134                                         break;
135                                 case ControlHandler.kEventControlDragLeave:
136                                         control.DndLeave (drag_event);
137                                         break;
138                                 case ControlHandler.kEventControlDragReceive:
139                                         control.DndDrop (drag_event);
140                                         break;
141                         }
142                         return true;
143                 }
144
145                 public void SetAllowDrop (Hwnd hwnd, bool allow) {
146                         if (hwnd.allow_drop == allow)
147                                 return;
148
149                         hwnd.allow_drop = allow;
150                         SetControlDragTrackingEnabled (hwnd.whole_window, true);
151                         SetControlDragTrackingEnabled (hwnd.client_window, true);
152                 }
153
154                 public void SendDrop (IntPtr handle, IntPtr from, IntPtr time) {
155                 }
156
157                 public DragDropEffects StartDrag (IntPtr handle, object data, DragDropEffects allowed_effects) {
158                         IntPtr dragref = IntPtr.Zero;
159                         EventRecord eventrecord = new EventRecord ();
160
161                         effects = DragDropEffects.None;
162
163                         NewDrag (ref dragref);
164                         XplatUICarbon.GetGlobalMouse (ref eventrecord.mouse);
165                         StoreObjectInDrag (handle, dragref, data);
166
167                         TrackDrag (dragref, ref eventrecord, IntPtr.Zero);
168
169                         DisposeDrag (dragref);
170
171                         return effects;
172                 }
173
174                 public void StoreObjectInDrag (IntPtr handle, IntPtr dragref, object data) {
175                         IntPtr type = IntPtr.Zero;
176                         IntPtr dataptr = IntPtr.Zero;
177                         Int32 size = 0;
178
179                         if (data is string) {
180                                 // implement me
181                                 throw new NotSupportedException ("Implement me.");
182                         } else if (data is ISerializable) {
183                                 MemoryStream stream = new MemoryStream ();
184                                 BinaryFormatter bf = new BinaryFormatter ();
185
186                                 bf.Serialize (stream, data);
187
188                                 dataptr = Marshal.AllocHGlobal ((int) stream.Length);
189                                 stream.Seek (0, 0);
190
191                                 for (int i = 0; i < stream.Length; i++) {
192                                         Marshal.WriteByte (dataptr, i, (byte) stream.ReadByte ());
193                                 }
194                                 
195                                 type = (IntPtr) typeMonoSerializedObject;
196                                 size = (int) stream.Length;
197                         } else {
198                                 dataptr = (IntPtr) GCHandle.Alloc (data);
199
200                                 type = (IntPtr) typeMono;
201                                 size = Marshal.SizeOf (typeof (IntPtr));
202                         }
203
204                         AddDragItemFlavor (dragref, handle, type, ref dataptr, size, 1 << 0);
205                 }
206
207                 [DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
208                 static extern int InstallTrackingHandler (DragTrackingDelegate callback, IntPtr window, IntPtr data);
209
210                 [DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
211                 static extern int GetEventParameter (IntPtr eventref, uint name, uint type, IntPtr outtype, uint size, IntPtr outsize, ref IntPtr data);
212                 [DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
213                 static extern int SetEventParameter (IntPtr eventref, uint name, uint type, uint size, ref bool data);
214
215                 [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
216                 extern static int SetControlDragTrackingEnabled (IntPtr view, bool enabled);
217                 [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
218                 extern static int AddDragItemFlavor (IntPtr dragref, IntPtr itemref, IntPtr flavortype, ref IntPtr data, Int32 size, UInt32 flags);
219                 [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
220                 extern static int CountDragItems (IntPtr dragref, ref UInt32 count);
221                 [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
222                 extern static int CountDragItemFlavors (IntPtr dragref, IntPtr itemref, ref UInt32 count);
223                 [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
224                 extern static int GetDragItemReferenceNumber (IntPtr dragref, UInt32 index, ref IntPtr itemref);
225                 [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
226                 extern static int NewDrag (ref IntPtr dragref);
227                 [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
228                 extern static int TrackDrag (IntPtr dragref, ref EventRecord eventrecord, IntPtr region);
229                 [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
230                 extern static int DisposeDrag (IntPtr dragref);
231                 [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
232                 extern static int GetDragAllowableActions (IntPtr dragref, ref UInt32 actions);
233         }
234
235         internal struct EventRecord {
236                 internal UInt16 what;
237                 internal UInt32 message;
238                 internal UInt32 when;
239                 internal QDPoint mouse;
240                 internal UInt16 modifiers;
241         }
242
243         internal class FlavorHandler {
244                 internal IntPtr flavorref;
245                 internal IntPtr dragref;
246                 internal IntPtr itemref;
247                 internal Int32 size;
248                 internal UInt32 flags;
249                 internal byte [] data;
250                 internal string fourcc;
251
252                 internal FlavorHandler (IntPtr dragref, IntPtr itemref, uint counter) {
253                         GetFlavorType (dragref, itemref, counter, ref flavorref);
254                         GetFlavorFlags (dragref, itemref, flavorref, ref flags);
255                         byte [] fourcc_b = BitConverter.GetBytes ((Int32)flavorref);
256                         this.fourcc = String.Format ("{0}{1}{2}{3}", (char)fourcc_b [3], (char)fourcc_b [2], (char)fourcc_b [1], (char)fourcc_b [0]);
257                         this.dragref = dragref;
258                         this.itemref = itemref;
259
260                         this.GetData ();
261                 }
262
263                 internal void GetData () {
264                         GetFlavorDataSize (dragref, itemref, flavorref, ref size);
265                         data = new byte [size];
266                         GetFlavorData (dragref, itemref, flavorref, data, ref size, 0);
267                 }
268
269                 internal string DataString {
270                         get { return Encoding.Default.GetString (this.data); }
271                 }
272                 
273                 internal byte[] DataArray {
274                         get { return this.data; }
275                 }
276
277                 internal IntPtr DataPtr {
278                         get { return (IntPtr) BitConverter.ToInt32 (this.data, 0); }
279                 }
280
281                 internal bool Supported {
282                         get {
283                                 switch (fourcc) {
284                                         case "furl":
285                                                 return true;
286                                         case "mono":
287                                                 return true;
288                                         case "mser":
289                                                 return true;
290                                 }
291                                 return false;
292                         }
293                 }
294
295                 internal DataObject Convert (ArrayList flavorlist) {
296                         switch (fourcc) {
297                                 case "furl":
298                                         return ConvertToFileDrop (flavorlist);
299                                 case "mono":
300                                         return ConvertToObject (flavorlist);
301                                 case "mser":
302                                         return DeserializeObject (flavorlist);
303                         }
304
305                         return new DataObject ();
306                 }
307
308                 internal DataObject DeserializeObject (ArrayList flavorlist) {
309                         DataObject data = new DataObject ();
310                         MemoryStream stream = new MemoryStream (this.DataArray);
311                         BinaryFormatter bf = new BinaryFormatter ();
312
313                         if (stream.Length == 0)
314                                 return data;
315
316                         stream.Seek (0, 0);
317                         data.SetData (bf.Deserialize (stream));
318
319                         return data;
320                 }
321
322                 internal DataObject ConvertToObject (ArrayList flavorlist) {
323                         DataObject data = new DataObject ();
324                         
325                         foreach (FlavorHandler flavor in flavorlist) {
326                                 GCHandle handle = (GCHandle) flavor.DataPtr;
327
328                                 data.SetData (handle.Target);
329                         }
330                         
331                         return data;
332                 }
333
334                 internal DataObject ConvertToFileDrop (ArrayList flavorlist) {
335                         DataObject data = new DataObject ();
336                         ArrayList uri_list = new ArrayList ();
337
338                         foreach (FlavorHandler flavor in flavorlist) {
339                                 try {
340                                         uri_list.Add (new Uri (flavor.DataString).LocalPath);                   
341                                 } catch { }
342                         }
343
344                         string [] l = (string []) uri_list.ToArray (typeof (string));
345                         if (l.Length < 1)
346                                 return data;
347                         data.SetData (DataFormats.FileDrop, l);
348                         data.SetData ("FileName", l [0]);
349                         data.SetData ("FileNameW", l [0]);
350                         
351                         return data;
352                 }
353
354                 public override string ToString () {
355                         return fourcc;
356                 }
357                 
358                 [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
359                 extern static int GetFlavorDataSize (IntPtr dragref, IntPtr itemref, IntPtr flavorref, ref Int32 size);
360                 [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
361                 extern static int GetFlavorData (IntPtr dragref, IntPtr itemref, IntPtr flavorref, [In, Out] byte[] data, ref Int32 size, UInt32 offset);
362                 [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
363                 extern static int GetFlavorFlags (IntPtr dragref, IntPtr itemref, IntPtr flavorref, ref UInt32 flags);
364                 [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")]
365                 extern static int GetFlavorType (IntPtr dragref, IntPtr itemref, UInt32 index, ref IntPtr flavor);
366         }
367 }