1 #include "win32\win32guts.h"
2 #include <ole2.h>
3 #ifndef _APRICOT_H_
4 #include "apricot.h"
5 #endif
6 #include "guts.h"
7 #include "Image.h"
8 #include "Widget.h"
9
10 #ifdef __cplusplus
11 extern "C" {
12 #endif
13
14 #define sys (( PDrawableData)(( PComponent) self)-> sysData)->
15 #define dsys( view) (( PDrawableData)(( PComponent) view)-> sysData)->
16 #define var (( PWidget) self)->
17 #define DHANDLE(x) dsys(x) handle
18
19 #define DEFCC IDataObject* data = (IDataObject*) ( guts.dndInsideEvent ? guts.dndDataReceiver : guts.dndDataSender );
20
21 static PList formats = NULL;
22
23 /*
24 Classes
25 */
26
27 /* IUnknown */
28 typedef struct {
29 IUnknownVtbl* lpVtbl;
30 const GUID *guid;
31 ULONG refcnt;
32 } OLEUnknown, *POLEUnknown;
33
34 static void*
create_ole_object(int size,void * vmt,const GUID * guid)35 create_ole_object(int size, void * vmt, const GUID * guid)
36 {
37 void * object;
38 if ( !( object = malloc(size)))
39 return false;
40 bzero(object,size);
41 ((POLEUnknown)object)->refcnt = 1;
42 ((POLEUnknown)object)->guid = guid;
43 ((POLEUnknown)object)->lpVtbl = (void*)vmt;
44 return object;
45 }
46
47 #define CreateObject(type) create_ole_object(sizeof(type),&type ## VMT, &IID_I ## type)
48
49 static ULONG __stdcall
OLEUnknown__AddRef(POLEUnknown self)50 OLEUnknown__AddRef(POLEUnknown self)
51 {
52 return self->refcnt++;
53 }
54
55 static ULONG __stdcall
OLEUnknown__Release(POLEUnknown self)56 OLEUnknown__Release(POLEUnknown self)
57 {
58 if ( --(self->refcnt) == 0 ) {
59 free(self);
60 return 0;
61 } else
62 return self->refcnt;
63 }
64
65 static HRESULT __stdcall
OLEUnknown__QueryInterface(POLEUnknown self,REFIID riid,void ** object)66 OLEUnknown__QueryInterface(POLEUnknown self, REFIID riid, void **object)
67 {
68 if (
69 IsEqualGUID(riid, &IID_IUnknown) ||
70 IsEqualGUID(riid, self->guid)
71 ) {
72 self->lpVtbl->AddRef((IUnknown*) self);
73 *object = self;
74 return S_OK;
75 } else {
76 *object = NULL;
77 return E_NOINTERFACE;
78 }
79 }
80
81 /* IDropTarget */
82 typedef struct {
83 IDropTargetVtbl* lpVtbl;
84 GUID * guid;
85 ULONG refcnt;
86 Handle widget;
87 Box pad;
88 int first_action;
89 int last_action;
90 } DropTarget, *PDropTarget;
91
92 int
convert_modmap(int modmap)93 convert_modmap(int modmap)
94 {
95 return 0
96 | ((modmap & MK_CONTROL ) ? kmCtrl : 0)
97 | ((modmap & MK_SHIFT ) ? kmShift : 0)
98 | ((modmap & MK_ALT ) ? kmAlt : 0)
99 | ((modmap & MK_LBUTTON ) ? mb1 : 0)
100 | ((modmap & MK_RBUTTON ) ? mb2 : 0)
101 | ((modmap & MK_MBUTTON ) ? mb3 : 0)
102 | ((modmap & MK_XBUTTON1 ) ? mb4 : 0)
103 | ((modmap & MK_XBUTTON2 ) ? mb5 : 0)
104 ;
105 }
106
107 static Point
convert_position(Handle self,POINTL _pt)108 convert_position( Handle self, POINTL _pt)
109 {
110 RECT r;
111 POINT pt = {_pt.x, _pt.y}, size;
112 Point ret;
113 GetWindowRect((HWND) PWidget(self)->handle, &r);
114 size.x = r.right - r.left;
115 size.y = r.bottom - r.top;
116 MapWindowPoints((HWND) NULL, ( HWND) PWidget(self)->handle, &pt, 1); /* XXX */
117 if ( pt.x < 0 || pt.y < 0 || pt.x > size.x - 1 || pt.y > size.y - 1) {
118 ret.x = ret.y = -1;
119 } else {
120 ret.x = pt.x;
121 ret.y = size.y - pt.y - 1;
122 }
123 return ret;
124 }
125
126 static int
convert_effect(DWORD effect)127 convert_effect( DWORD effect)
128 {
129 /* DROPEFFECT_XXX and dndXXX constants are directly mapped to each other, except DROPEFFECT_SCROLL */
130 return effect & dndMask;
131 }
132
133 static HRESULT __stdcall
DropTarget__DragEnter(PDropTarget self,IDataObject * data,DWORD modmap,POINTL pt,DWORD * effect)134 DropTarget__DragEnter(PDropTarget self, IDataObject *data, DWORD modmap, POINTL pt, DWORD *effect)
135 {
136 int stage;
137 Handle w;
138 Event ev = { cmDragBegin };
139
140 if (
141 ((ev.dnd.clipboard = guts.clipboards[CLIPBOARD_DND]) == NULL_HANDLE) ||
142 !dsys(self->widget)options.aptEnabled
143 ) {
144 *effect = DROPEFFECT_NONE;
145 return S_OK;
146 }
147
148 guts.dndDataReceiver = data;
149 bzero(&(self->pad), sizeof(self->pad));
150 self->last_action = DROPEFFECT_NONE;
151 self->first_action = *effect & dndMask;
152
153 w = self->widget;
154 ev.dnd.clipboard = guts.clipboards[CLIPBOARD_DND];
155 ev.dnd.action = convert_effect(*effect);
156 ev.dnd.modmap = convert_modmap(modmap);
157 ev.dnd.where = convert_position(w, pt);
158 ev.dnd.counterpart = guts.dragSourceWidget;
159 if ( ev.dnd.where.x < 0 ) {
160 *effect = DROPEFFECT_NONE;
161 return S_OK;
162 }
163
164 protect_object(w);
165 guts.dndInsideEvent = true;
166 CWidget(w)->message(w, &ev);
167 guts.dndInsideEvent = false;
168 stage = PObject(w)-> stage;
169 unprotect_object(w);
170 if ( stage == csDead ) {
171 self->widget = NULL_HANDLE;
172 guts. dndDataReceiver = NULL;
173 *effect = DROPEFFECT_NONE;
174 return S_OK;
175 }
176
177 return S_OK;
178 }
179
180 static HRESULT __stdcall
DropTarget__DragOver(PDropTarget self,DWORD modmap,POINTL pt,DWORD * effect)181 DropTarget__DragOver(PDropTarget self, DWORD modmap, POINTL pt, DWORD *effect)
182 {
183 int stage;
184 Handle w;
185 Event ev = { cmDragOver };
186
187 if (
188 self->widget == NULL_HANDLE ||
189 guts.clipboards[CLIPBOARD_DND] == NULL_HANDLE ||
190 !dsys(self->widget)options.aptEnabled
191 ) {
192 *effect = DROPEFFECT_NONE;
193 return S_OK;
194 }
195
196 w = self->widget;
197 ev.dnd.clipboard = guts.clipboards[CLIPBOARD_DND];
198 ev.dnd.action = convert_effect(*effect);
199 ev.dnd.modmap = convert_modmap(modmap);
200 ev.dnd.where = convert_position(w, pt);
201 ev.dnd.counterpart = guts.dragSourceWidget;
202 if ( ev.dnd.where.x < 0 ) {
203 *effect = self-> last_action;
204 return S_OK;
205 }
206
207 if (
208 self->pad.width > 0 && self->pad.height > 0 &&
209 (
210 ev.dnd.where.x < self->pad.x ||
211 ev.dnd.where.x > self->pad.width + self->pad.x ||
212 ev.dnd.where.y < self->pad.y ||
213 ev.dnd.where.y > self->pad.height + self->pad.y
214 )
215 ) {
216 *effect = self-> last_action;
217 return S_OK;
218 }
219
220 protect_object(w);
221 guts.dndInsideEvent = true;
222 CWidget(w)->message(w, &ev);
223 guts.dndInsideEvent = false;
224 stage = PObject(w)-> stage;
225 unprotect_object(w);
226 if ( stage == csDead ) {
227 self->widget = NULL_HANDLE;
228 guts. dndDataReceiver = NULL;
229 *effect = DROPEFFECT_NONE;
230 return S_OK;
231 }
232
233 ev.dnd.action &= self->first_action;
234 self->last_action = *effect = ev.dnd.allow ? ev.dnd.action : DROPEFFECT_NONE;
235 self->pad = ev.dnd.pad;
236
237 return S_OK;
238 }
239
240 static HRESULT __stdcall
DropTarget__DragLeave(PDropTarget self)241 DropTarget__DragLeave(PDropTarget self)
242 {
243 Handle w;
244 Event ev = { cmDragEnd };
245
246 guts. dndDataReceiver = NULL;
247 if (
248 self->widget == NULL_HANDLE ||
249 guts.clipboards[CLIPBOARD_DND] == NULL_HANDLE ||
250 !dsys(self->widget)options.aptEnabled
251 )
252 return S_OK;
253
254 w = self->widget;
255 ev.dnd.allow = 0;
256 ev.dnd.clipboard = NULL_HANDLE;
257 ev.dnd.modmap = apc_kbd_get_state(self->widget) | apc_pointer_get_state(self->widget);
258 ev.dnd.where = apc_pointer_get_pos(self->widget);
259 ev.dnd.action = dndNone;
260 ev.dnd.counterpart = guts.dragSourceWidget;
261 guts.dndInsideEvent = true;
262 CWidget(w)->message(w, &ev);
263 guts.dndInsideEvent = false;
264 return S_OK;
265 }
266
267 static HRESULT __stdcall
DropTarget__Drop(PDropTarget self,IDataObject * data,DWORD modmap,POINTL pt,DWORD * effect)268 DropTarget__Drop(PDropTarget self, IDataObject *data, DWORD modmap, POINTL pt, DWORD *effect)
269 {
270 Handle w;
271 Event ev = { cmDragEnd };
272
273 if (
274 self->widget == NULL_HANDLE ||
275 guts.clipboards[CLIPBOARD_DND] == NULL_HANDLE ||
276 !dsys(self->widget)options.aptEnabled
277 ) {
278 *effect = DROPEFFECT_NONE;
279 guts. dndDataReceiver = NULL;
280 return S_OK;
281 }
282
283 w = self->widget;
284 ev.dnd.allow = 1;
285 ev.dnd.clipboard = guts.clipboards[CLIPBOARD_DND];
286 ev.dnd.action = convert_effect(*effect);
287 ev.dnd.modmap = convert_modmap(modmap);
288 ev.dnd.where = convert_position(w, pt);
289 ev.dnd.counterpart = guts.dragSourceWidget;
290 if ( ev.dnd.where.x < 0 ) {
291 *effect = DROPEFFECT_NONE;
292 return S_OK;
293 }
294
295 /* DROPEFFECT_XXX and dndXXX constants are directly mapped to each other, except DROPEFFECT_SCROLL */
296 guts.dndInsideEvent = true;
297 CWidget(w)->message(w, &ev);
298
299 guts.dndInsideEvent = false;
300 *effect = ev.dnd.allow ? (ev.dnd.action & self->first_action) : DROPEFFECT_NONE;
301 if ( *effect & dndCopy ) *effect = dndCopy;
302 else if ( *effect & dndMove ) *effect = dndMove;
303 else if ( *effect & dndLink ) *effect = dndLink;
304
305 guts.dragTarget = w;
306
307 return S_OK;
308 }
309
310 static IDropTargetVtbl DropTargetVMT = {
311 (void*) OLEUnknown__QueryInterface,
312 (void*) OLEUnknown__AddRef,
313 (void*) OLEUnknown__Release,
314 (void*) DropTarget__DragEnter,
315 (void*) DropTarget__DragOver,
316 (void*) DropTarget__DragLeave,
317 (void*) DropTarget__Drop
318 };
319
320 /* IDropSource */
321 typedef struct {
322 IDropSourceVtbl* lpVtbl;
323 GUID * guid;
324 ULONG refcnt;
325 Handle widget;
326 int first_modmap;
327 int last_action;
328 } DropSource, *PDropSource;
329
330 static HRESULT __stdcall
DropSource__QueryContinueDrag(PDropSource self,BOOL escape,DWORD modmap)331 DropSource__QueryContinueDrag(PDropSource self, BOOL escape, DWORD modmap)
332 {
333 Event ev = { cmDragQuery };
334 ev.dnd.modmap =
335 convert_modmap(modmap) |
336 (escape ? kmEscape : 0)
337 ;
338 if ( escape )
339 ev.dnd.allow = -1;
340 else if ( self->first_modmap != (ev.dnd.modmap & self->first_modmap ))
341 ev.dnd.allow = 1;
342 else
343 ev.dnd.allow = 0;
344 CWidget(self->widget)->message(self->widget, &ev);
345
346 if ( ev.dnd.allow < 0 )
347 return DRAGDROP_S_CANCEL;
348 else if ( ev.dnd.allow > 0)
349 return DRAGDROP_S_DROP;
350 else
351 return S_OK;
352 }
353
354 static HRESULT __stdcall
DropSource__GiveFeedback(PDropSource self,DWORD effect)355 DropSource__GiveFeedback(PDropSource self, DWORD effect)
356 {
357 Event ev = { cmDragResponse };
358 if ( effect & dndCopy ) effect = dndCopy;
359 else if ( effect & dndMove ) effect = dndMove;
360 else if ( effect & dndLink ) effect = dndLink;
361 else effect = dndNone;
362 ev.dnd.action = effect & dndMask;
363 ev.dnd.allow = ev.dnd.action != dndNone;
364 CWidget(self->widget)->message(self->widget, &ev);
365 /* Force our cursor again, otherwise a standart IDC_NO will be slapped on */
366 if ( !guts.dndDefaultCursors && ev.dnd.action == dndNone)
367 PostMessage( DHANDLE(self->widget), WM_DRAG_RESPONSE, 0, 0);
368 return guts.dndDefaultCursors ? DRAGDROP_S_USEDEFAULTCURSORS : S_OK;
369 }
370
371 static IDropSourceVtbl DropSourceVMT = {
372 (void*) OLEUnknown__QueryInterface,
373 (void*) OLEUnknown__AddRef,
374 (void*) OLEUnknown__Release,
375 (void*) DropSource__QueryContinueDrag,
376 (void*) DropSource__GiveFeedback
377 };
378
379 /* IDataObject */
380 typedef struct {
381 IDataObjectVtbl* lpVtbl;
382 GUID * guid;
383 ULONG refcnt;
384 List list;
385 } DataObject, *PDataObject;
386
387 typedef struct {
388 int formats[3], n_formats, size;
389 void * data;
390 Handle image;
391 char payload[1]; /* data points here */
392 } DataObjectEntry, *PDataObjectEntry;
393
394 static TYMED
cf2tymed(int cf)395 cf2tymed(int cf)
396 {
397 return (cf == CF_BITMAP || cf == CF_PALETTE) ? TYMED_GDI : TYMED_HGLOBAL;
398 }
399
400 static PDataObjectEntry
dataobject_alloc_buffer(int format,int size,void * data)401 dataobject_alloc_buffer(int format, int size, void * data)
402 {
403 PDataObjectEntry entry;
404 if ( !( entry = malloc( size + sizeof(DataObjectEntry)))) {
405 warn("Not enough memory");
406 return NULL;
407 }
408 bzero( entry, sizeof(DataObjectEntry));
409 entry->formats[0] = format;
410 entry->formats[1] = -1;
411 entry->formats[2] = -1;
412 entry->n_formats = 1;
413 entry->size = size;
414 entry->data = &(entry->payload);
415 memcpy( entry->data, data, size );
416 return entry;
417 }
418
419 static PDataObjectEntry
dataobject_alloc_image(Handle image)420 dataobject_alloc_image(Handle image)
421 {
422 PDataObjectEntry entry;
423 if ( !( entry = malloc( sizeof(DataObjectEntry)))) {
424 warn("Not enough memory");
425 return NULL;
426 }
427 bzero( entry, sizeof(DataObjectEntry));
428 entry->image = image;
429 entry->formats[0] = CF_BITMAP;
430 entry->formats[1] = CF_PALETTE;
431 entry->formats[2] = CF_DIB;
432 entry->n_formats = 3;
433 protect_object(image);
434 CComponent(guts.clipboards[CLIPBOARD_DND])->attach(guts.clipboards[CLIPBOARD_DND], image);
435 return entry;
436 }
437
438 static void
dataobject_free_entry(PDataObjectEntry entry)439 dataobject_free_entry(PDataObjectEntry entry)
440 {
441 if ( entry->formats[0] == CF_BITMAP ) {
442 CComponent(guts.clipboards[CLIPBOARD_DND])->detach(guts.clipboards[CLIPBOARD_DND], entry->image, false);
443 unprotect_object(entry->image);
444 }
445 free( entry );
446 }
447
448 static int
dataobject_find_entry(PDataObject data,int format,PDataObjectEntry * entry_ref)449 dataobject_find_entry(PDataObject data, int format, PDataObjectEntry * entry_ref)
450 {
451 int i, j;
452
453 if ( entry_ref ) *entry_ref = NULL;
454 for ( i = 0; i < data->list.count; i++) {
455 PDataObjectEntry entry = (void*) data->list.items[i];
456 for ( j = 0; j < entry->n_formats; j++) {
457 if ( entry->formats[j] == format ) {
458 if ( entry_ref ) *entry_ref = entry;
459 return i;
460 }
461 }
462 }
463 return -1;
464 }
465
466 static void
dataobject_clear()467 dataobject_clear()
468 {
469 int i;
470 PDataObject data = (PDataObject) guts.dndDataSender;
471 if ( !data ) return;
472 for ( i = 0; i < data->list.count; i++)
473 dataobject_free_entry((PDataObjectEntry) data->list.items[i]);
474 data->list.count = 0;
475 }
476
477 static HRESULT
dataobject_convert(PDataObjectEntry entry,int format,LPSTGMEDIUM medium)478 dataobject_convert( PDataObjectEntry entry, int format, LPSTGMEDIUM medium)
479 {
480 switch (format) {
481 case CF_BITMAP: {
482 HPALETTE p;
483 if ( PImage(entry->image)->stage != csNormal)
484 return E_UNEXPECTED;
485 p = ( HGLOBAL) palette_create( entry-> image);
486 medium->hBitmap = image_create_bitmap_by_type( entry-> image, p, NULL, BM_AUTO);
487 DeleteObject(p);
488 break;
489 }
490 case CF_PALETTE:
491 if ( PImage(entry->image)->stage != csNormal)
492 return E_UNEXPECTED;
493 medium->hGlobal = ( HGLOBAL) palette_create( entry-> image);
494 break;
495 case CF_DIB: {
496 if ( PImage(entry->image)->stage != csNormal)
497 return E_UNEXPECTED;
498 if ((medium->hGlobal = image_create_dib(entry->image, true)) == NULL)
499 return E_OUTOFMEMORY;
500 break;
501 }
502 case CF_UNICODETEXT: {
503 int ulen;
504 void *ptr = NULL;
505
506 ulen = MultiByteToWideChar(CP_UTF8, 0, (char*) entry->data, entry->size, NULL, 0) + 1;
507 if (( medium->hGlobal = GlobalAlloc( GMEM_DDESHARE, ( ulen + 0) * sizeof( WCHAR))) == NULL)
508 return E_OUTOFMEMORY;
509
510 if (( ptr = GlobalLock( medium->hGlobal)) == NULL) {
511 GlobalFree( medium->hGlobal);
512 medium->hGlobal = NULL;
513 return E_UNEXPECTED;
514 }
515
516 MultiByteToWideChar(CP_UTF8, 0, (LPSTR)entry-> data, entry->size, ptr, ulen);
517 GlobalUnlock( medium->hGlobal);
518 break;
519 }
520 case CF_TEXT: {
521 int i, cr = 0;
522 void *ptr = NULL;
523 char *src = (char*)entry->data, *dst;
524
525 for ( i = 0; i < entry-> size; i++)
526 if (src[i] == '\n' && ( i == 0 || src[i-1] != '\r'))
527 cr++;
528
529 if (( medium->hGlobal = GlobalAlloc( GMEM_DDESHARE, entry->size + cr + 1)) == NULL)
530 return E_OUTOFMEMORY;
531 if (( ptr = GlobalLock( medium->hGlobal)) == NULL ) {
532 GlobalFree( medium->hGlobal);
533 medium->hGlobal = NULL;
534 return E_UNEXPECTED;
535 }
536
537 dst = ( char *) ptr;
538 for ( i = 0; i < entry->size; i++) {
539 if ( src[i] == '\n' && ( i == 0 || src[i-1] != '\r'))
540 *(dst++) = '\r';
541 *(dst++) = src[i];
542 }
543 *dst = 0;
544 GlobalUnlock( ptr);
545 break;
546 }
547 default: {
548 char* ptr;
549 if (!( medium->hGlobal = GlobalAlloc( GMEM_DDESHARE, entry-> size)))
550 return E_OUTOFMEMORY;
551 if ( !( ptr = ( char *) GlobalLock( medium->hGlobal))) {
552 GlobalFree( medium->hGlobal );
553 medium->hGlobal = NULL;
554 return E_UNEXPECTED;
555 }
556 memcpy( ptr, entry-> data, entry-> size);
557 GlobalUnlock( medium->hGlobal );
558 break;
559 }}
560
561 return S_OK;
562 }
563
564 static HRESULT __stdcall
DataObject__GetData(PDataObject self,LPFORMATETC format,LPSTGMEDIUM medium)565 DataObject__GetData(PDataObject self, LPFORMATETC format, LPSTGMEDIUM medium)
566 {
567 PDataObjectEntry entry;
568 int found = -1;
569
570 if ((found = dataobject_find_entry(self, format->cfFormat, &entry)) < 0)
571 return DV_E_FORMATETC;
572 if ( format->tymed != cf2tymed(format->cfFormat))
573 return DV_E_TYMED;
574
575 medium->tymed = format->tymed;
576 medium->pUnkForRelease = 0;
577 return dataobject_convert(entry, format->cfFormat, medium);
578 }
579
580 static HRESULT __stdcall
DataObject__GetDataHere(PDataObject self,LPFORMATETC format,LPSTGMEDIUM medium)581 DataObject__GetDataHere(PDataObject self, LPFORMATETC format, LPSTGMEDIUM medium)
582 {
583 return DATA_E_FORMATETC;
584 }
585
586 static HRESULT __stdcall
DataObject__QueryGetData(PDataObject self,LPFORMATETC format)587 DataObject__QueryGetData(PDataObject self, LPFORMATETC format)
588 {
589 PDataObjectEntry entry;
590 int found = -1;
591
592 if ((found = dataobject_find_entry(self, format->cfFormat, &entry)) < 0)
593 return S_FALSE;
594 if ( format->tymed != cf2tymed(format->cfFormat))
595 return DV_E_TYMED;
596
597 return S_OK;
598 }
599
600 static HRESULT __stdcall
DataObject__GetCanonicalFormatEtc(PDataObject self,LPFORMATETC format_in,LPFORMATETC format_out)601 DataObject__GetCanonicalFormatEtc(PDataObject self, LPFORMATETC format_in, LPFORMATETC format_out)
602 {
603 format_out->ptd = NULL;
604 return E_NOTIMPL;
605 }
606
607 static HRESULT __stdcall
DataObject__SetData(PDataObject self,LPFORMATETC format,LPSTGMEDIUM medium,BOOL release)608 DataObject__SetData(PDataObject self, LPFORMATETC format, LPSTGMEDIUM medium, BOOL release)
609 {
610 return E_UNEXPECTED;
611 }
612
613 STDAPI __stdcall SHCreateStdEnumFmtEtc(UINT cfmt, const FORMATETC* afmt, LPENUMFORMATETC *ppenumFormatEtc);
614
615 static HRESULT __stdcall
DataObject__EnumFormatEtc(PDataObject self,DWORD direction,LPENUMFORMATETC * enum_formats)616 DataObject__EnumFormatEtc(PDataObject self, DWORD direction, LPENUMFORMATETC *enum_formats)
617 {
618 int i, j, n_formats = 0;
619 FORMATETC *formats, *f;
620
621 if (direction != DATADIR_GET)
622 return E_NOTIMPL;
623
624 for ( i = 0; i < self->list.count; i++)
625 n_formats += ((PDataObjectEntry)self->list.items[i])->n_formats;
626
627 if ((formats = malloc(sizeof(FORMATETC) * n_formats)) == NULL)
628 return E_OUTOFMEMORY;
629
630 for ( i = 0, f = formats; i < self->list.count; i++) {
631 PDataObjectEntry entry = (PDataObjectEntry)self->list.items[i];
632 for ( j = 0; j < entry->n_formats; j++, f++) {
633 f->cfFormat = entry->formats[j];
634 f->ptd = NULL;
635 f->dwAspect = DVASPECT_CONTENT;
636 f->lindex = -1;
637 f->tymed = cf2tymed(entry->formats[j]);
638 }
639 }
640
641 SHCreateStdEnumFmtEtc(n_formats, formats, enum_formats);
642 free(formats);
643
644 return S_OK;
645 }
646
647 static HRESULT __stdcall
DataObject__DAdvise(PDataObject self,LPFORMATETC format,DWORD advf,LPADVISESINK sink,DWORD * connection)648 DataObject__DAdvise(PDataObject self, LPFORMATETC format, DWORD advf, LPADVISESINK sink, DWORD *connection)
649 {
650 return E_FAIL;
651 }
652
653 static HRESULT __stdcall
DataObject__Dunadvise(PDataObject self,DWORD connection)654 DataObject__Dunadvise(PDataObject self, DWORD connection)
655 {
656 return E_FAIL;
657 }
658
659 static HRESULT __stdcall
DataObject__EnumDAdvise(PDataObject self,LPENUMSTATDATA * enum_advise)660 DataObject__EnumDAdvise(PDataObject self, LPENUMSTATDATA *enum_advise)
661 {
662 return E_FAIL;
663 }
664
665 static IDataObjectVtbl DataObjectVMT = {
666 (void*) OLEUnknown__QueryInterface,
667 (void*) OLEUnknown__AddRef,
668 (void*) OLEUnknown__Release,
669 (void*) DataObject__GetData,
670 (void*) DataObject__GetDataHere,
671 (void*) DataObject__QueryGetData,
672 (void*) DataObject__GetCanonicalFormatEtc,
673 (void*) DataObject__SetData,
674 (void*) DataObject__EnumFormatEtc,
675 (void*) DataObject__DAdvise,
676 (void*) DataObject__Dunadvise,
677 (void*) DataObject__EnumDAdvise
678 };
679
680 /* clipboard */
681
682 static PList
get_formats(void)683 get_formats(void)
684 {
685 DEFCC;
686 FORMATETC etc;
687 IEnumFORMATETC *e = NULL;
688
689 if (data == NULL) return NULL;
690 if (formats != NULL) return formats;
691
692 if (data->lpVtbl->EnumFormatEtc(data, DATADIR_GET, &e) != S_OK) {
693 apiErr;
694 return NULL;
695 }
696 if (e == NULL)
697 return NULL;
698 formats = plist_create(8, 8);
699 while ( e->lpVtbl->Next(e, 1, &etc, NULL) == S_OK ) {
700 list_add( formats, (Handle)etc.cfFormat);
701 list_add( formats, (Handle)etc.tymed);
702 }
703 e->lpVtbl->Release(e);
704 return formats;
705 }
706
707 Bool
dnd_clipboard_create(void)708 dnd_clipboard_create( void)
709 {
710 if (guts.dndDataSender) return false;
711 if (!(guts.dndDataSender = CreateObject(DataObject)))
712 return false;
713 list_create(&((PDataObject) guts.dndDataSender)->list, 8, 8);
714 return true;
715 }
716
717 void
dnd_clipboard_destroy(void)718 dnd_clipboard_destroy( void)
719 {
720 if (!guts.dndDataSender) return;
721 dataobject_clear();
722 list_destroy(&((PDataObject) guts.dndDataSender)->list);
723 free(guts.dndDataSender);
724 guts.dndDataSender = NULL;
725 }
726
727 Bool
dnd_clipboard_open(void)728 dnd_clipboard_open( void)
729 {
730 DEFCC;
731 if (data == NULL) return false;
732 return true;
733 }
734
735 Bool
dnd_clipboard_close(void)736 dnd_clipboard_close( void)
737 {
738 DEFCC;
739 if ( formats ) {
740 plist_destroy(formats);
741 formats = NULL;
742 }
743 if (data == NULL) return false;
744 return true;
745 }
746
747 Bool
dnd_clipboard_clear(void)748 dnd_clipboard_clear( void)
749 {
750 DEFCC;
751 if ( guts.dndInsideEvent ) return false;
752 if (data == NULL) return false;
753 dataobject_clear();
754 return true;
755 }
756
757 PList
dnd_clipboard_get_formats(void)758 dnd_clipboard_get_formats( void)
759 {
760 int i;
761 PList cf_formats, ret;
762
763 if (( cf_formats = get_formats()) == NULL )
764 return NULL;
765 ret = plist_create(8, 8);
766 for ( i = 0; i < cf_formats->count; i+=2) {
767 char buf[256];
768 char * name = cf2name((UINT) cf_formats->items[i]);
769 if ( name )
770 snprintf(buf, 256, "%s", name);
771 else
772 snprintf(buf, 256, "0x%x", (unsigned int)cf_formats->items[i]);
773 switch (cf_formats->items[i+1]) {
774 case TYMED_HGLOBAL : break;
775 case TYMED_FILE : strncat(buf, ".FILE" ,255); break;
776 case TYMED_ISTREAM : strncat(buf, ".ISTREAM" ,255); break;
777 case TYMED_ISTORAGE : strncat(buf, ".ISTORAGE",255); break;
778 case TYMED_GDI : strncat(buf, ".GDI" ,255); break;
779 case TYMED_MFPICT : strncat(buf, ".MFPICT" ,255); break;
780 case TYMED_ENHMF : strncat(buf, ".ENHMF" ,255); break;
781 default : {
782 char num[16];
783 snprintf(num, 16, ".0x%x", (unsigned int) cf_formats->items[i+1]);
784 strncat(buf, num, 255);
785 }}
786 buf[255] = 0;
787 list_add( ret, (Handle) duplicate_string(buf));
788 }
789
790 return ret;
791 }
792
793 Bool
dnd_clipboard_get_data(Handle id,PClipboardDataRec c)794 dnd_clipboard_get_data( Handle id, PClipboardDataRec c)
795 {
796 DEFCC;
797 Bool ret;
798 HPALETTE palette = NULL;
799 STGMEDIUM medium, medium2;
800 FORMATETC etc = { id, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
801
802 if (data == NULL)
803 return false;
804
805 /* copy cached data directly */
806 if ( !guts. dndInsideEvent ) {
807 PDataObjectEntry entry;
808 if (( dataobject_find_entry((PDataObject) data, id, &entry)) < 0)
809 return false;
810
811 if ( id == CF_BITMAP ) {
812 c->image = CImage(entry->image)->dup((Handle)(entry->image));
813 } else {
814 c->length = 0;
815 if (( c->data = malloc(entry->size )) == NULL)
816 return false;
817 memcpy( c->data, entry->data, c->length = entry->size );
818 }
819 return true;
820 }
821
822 /* read from OLE */
823 if ( id == CF_BITMAP ) {
824 /* HBITMAP needs palette, which doesn't seem to be served through DND - check DIB first */
825 etc.cfFormat = CF_DIB;
826 if (
827 (data->lpVtbl->QueryGetData(data, &etc) == S_OK) &&
828 (data->lpVtbl->GetData(data, &etc, &medium) == S_OK)
829 ) {
830 id = CF_DIB;
831 } else {
832 etc.cfFormat = CF_BITMAP;
833 etc.tymed = TYMED_GDI;
834 if (
835 (data->lpVtbl->QueryGetData(data, &etc) == S_OK) &&
836 (data->lpVtbl->GetData(data, &etc, &medium) == S_OK)
837 ) {
838 id = CF_BITMAP;
839 etc.cfFormat = CF_PALETTE;
840 if (
841 (data->lpVtbl->QueryGetData(data, &etc) == S_OK) &&
842 (data->lpVtbl->GetData(data, &etc, &medium2) == S_OK)
843 ) {
844 palette = medium2.hGlobal;
845 }
846 } else
847 return false;
848 }
849 } else {
850 if (data->lpVtbl->QueryGetData(data, &etc) != S_OK)
851 return false;
852 if (( rc = data->lpVtbl->GetData(data, &etc, &medium)) != S_OK) {
853 apcWarn;
854 return false;
855 }
856 }
857
858 ret = clipboard_get_data(id, c, (void*) medium.hGlobal, (void*)palette);
859
860 if ( palette )
861 ReleaseStgMedium(&medium2);
862 ReleaseStgMedium(&medium);
863
864 return ret;
865 }
866
867 Bool
dnd_clipboard_has_format(Handle id)868 dnd_clipboard_has_format( Handle id)
869 {
870 DEFCC;
871 int i;
872 PList cf_formats;
873
874 if ( !guts. dndInsideEvent ) {
875 if ( !data )
876 return false;
877 if (( dataobject_find_entry((PDataObject)data, id, NULL)) < 0)
878 return false;
879 return true;
880 }
881
882 if (( cf_formats = get_formats()) == NULL )
883 return false;
884 for ( i = 0; i < cf_formats->count; i+=2) {
885 if ( id == cf_formats->items[i])
886 return true;
887 }
888
889 return false;
890 }
891
892 Bool
dnd_clipboard_set_data(Handle id,PClipboardDataRec c)893 dnd_clipboard_set_data( Handle id, PClipboardDataRec c)
894 {
895 int index;
896 PDataObject data = (PDataObject) guts.dndDataSender;
897 PDataObjectEntry entry;
898
899 if ( guts. dndInsideEvent )
900 return false;
901 if (data == NULL)
902 return false;
903
904 if ((index = dataobject_find_entry((PDataObject)data, id, &entry)) >= 0) {
905 dataobject_free_entry(entry);
906 data->list.items[index] = NULL_HANDLE;
907 }
908
909 entry = ( id == CF_BITMAP ) ?
910 dataobject_alloc_image(c->image) :
911 dataobject_alloc_buffer(id, c->length, c->data);
912 if ( entry == NULL ) {
913 warn("Not enough memory");
914 return false;
915 }
916
917 if ( index >= 0 )
918 data->list.items[index] = (Handle) entry;
919 else
920 list_add(&data->list, (Handle) entry);
921
922 return true;
923 }
924
925 /* Widgets */
926
927 Bool
apc_dnd_get_aware(Handle self)928 apc_dnd_get_aware( Handle self )
929 {
930 return sys dropTarget != NULL;
931 }
932
933 Bool
apc_dnd_set_aware(Handle self,Bool is_target)934 apc_dnd_set_aware( Handle self, Bool is_target )
935 {
936 if ( is_target == (sys dropTarget != NULL )) return true;
937
938 if ( is_target ) {
939 PDropTarget target;
940 if (!(target = CreateObject(DropTarget)))
941 return false;
942 target->widget = self;
943 if ( RegisterDragDrop( sys handle, (IDropTarget*) target) != S_OK) {
944 apiErr;
945 free(target);
946 return false;
947 }
948 sys dropTarget = target;
949 } else {
950 RevokeDragDrop(sys handle);
951 free(sys dropTarget);
952 sys dropTarget = NULL;
953 }
954
955 return true;
956 }
957
958 Handle
apc_dnd_get_clipboard(Handle self)959 apc_dnd_get_clipboard( Handle self )
960 {
961 return guts.clipboards[CLIPBOARD_DND];
962 }
963
964 int
apc_dnd_start(Handle self,int actions,Bool default_pointers,Handle * counterpart)965 apc_dnd_start( Handle self, int actions, Bool default_pointers, Handle * counterpart)
966 {
967 int ret = dndNone;
968 int old_pointer;
969 DWORD effect;
970 actions &= dndMask;
971
972 if ( actions == 0 )
973 return -1;
974 if ( guts.dragSource )
975 return -1;
976 if (!(guts.dragSource = CreateObject(DropSource)))
977 return -1;
978 guts.dragSourceWidget = self;
979 if ( counterpart ) *counterpart = NULL_HANDLE;
980 guts.dragTarget = NULL_HANDLE;
981 guts.dndDefaultCursors = default_pointers;
982 ((PDropSource)guts.dragSource)->widget = self;
983 ((PDropSource)guts.dragSource)->first_modmap =
984 apc_kbd_get_state(self) | apc_pointer_get_state(self);
985 ((PDropSource)guts.dragSource)->last_action = -1;
986
987 protect_object(self);
988 old_pointer = apc_pointer_get_shape(self);
989
990 rc = DoDragDrop(
991 (LPDATAOBJECT) guts.dndDataSender,
992 (LPDROPSOURCE) guts.dragSource,
993 actions,
994 &effect
995 );
996
997 if ( PObject(self)->stage == csNormal )
998 apc_pointer_set_shape(self, old_pointer);
999 unprotect_object(self);
1000
1001 switch (rc) {
1002 case DRAGDROP_S_DROP:
1003 ret = effect & dndMask;
1004 break;
1005 case DRAGDROP_S_CANCEL:
1006 ret = dndNone;
1007 break;
1008 default:
1009 ret = -1;
1010 apcWarn;
1011 }
1012
1013 free(guts.dragSource);
1014 guts.dragSource = NULL;
1015 guts.dragSourceWidget = NULL_HANDLE;
1016 if ( counterpart ) *counterpart = guts.dragTarget;
1017
1018 return ret;
1019 }
1020
1021 #ifdef __cplusplus
1022 }
1023 #endif
1024