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