2 // "$Id: fl_dnd_win32.cxx 8028 2010-12-14 19:46:55Z AlbrechtS $"
4 // Drag & Drop code for the Fast Light Tool Kit (FLTK).
6 // Copyright 1998-2010 by Bill Spitzak and others.
8 // This library is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU Library General Public
10 // License as published by the Free Software Foundation; either
11 // version 2 of the License, or (at your option) any later version.
13 // This library is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 // Library General Public License for more details.
18 // You should have received a copy of the GNU Library General Public
19 // License along with this library; if not, write to the Free Software
20 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
23 // Please report all bugs and problems on the following page:
25 // http://www.fltk.org/str.php
27 // This file contains win32-specific code for fltk which is always linked
28 // in. Search other files for "WIN32" or filenames ending in _win32.cxx
29 // for other system-specific code.
33 #include <FL/Fl_Window.H>
34 #include <FL/fl_utf8.h>
38 #include <sys/types.h>
41 #if defined(__CYGWIN__)
46 extern char *fl_selection_buffer
[2];
47 extern int fl_selection_length
[2];
48 extern int fl_selection_buffer_length
[2];
49 extern char fl_i_own_selection
[2];
50 extern char *fl_locale2utf8(const char *s
, UINT codepage
= 0);
51 extern unsigned int fl_codepage
;
53 Fl_Window
*fl_dnd_target_window
= 0;
61 * subclass the IDropTarget to receive data from DnD operations
63 class FLDropTarget
: public IDropTarget
69 FLDropTarget() : m_cRefCount(0) { } // initialize
70 HRESULT STDMETHODCALLTYPE
QueryInterface( REFIID riid
, LPVOID
*ppvObject
) {
71 if (IID_IUnknown
==riid
|| IID_IDropTarget
==riid
)
74 ((LPUNKNOWN
)*ppvObject
)->AddRef();
80 ULONG STDMETHODCALLTYPE
AddRef() { return ++m_cRefCount
; }
81 ULONG STDMETHODCALLTYPE
Release() {
83 nTemp
= --m_cRefCount
;
88 HRESULT STDMETHODCALLTYPE
DragEnter( IDataObject
*pDataObj
, DWORD
/*grfKeyState*/, POINTL pt
, DWORD
*pdwEffect
) {
89 if( !pDataObj
) return E_INVALIDARG
;
90 // set e_modifiers here from grfKeyState, set e_x and e_root_x
91 // check if FLTK handles this drag and return if it can't (i.e. BMP drag without filename)
93 Fl::e_x_root
= ppt
.x
= pt
.x
;
94 Fl::e_y_root
= ppt
.y
= pt
.y
;
95 HWND hWnd
= WindowFromPoint( ppt
);
96 Fl_Window
*target
= fl_find( hWnd
);
98 Fl::e_x
= Fl::e_x_root
-target
->x();
99 Fl::e_y
= Fl::e_y_root
-target
->y();
101 fl_dnd_target_window
= target
;
102 px
= pt
.x
; py
= pt
.y
;
103 if (fillCurrentDragData(pDataObj
)) {
104 // FLTK has no mechanism yet for the different drop effects, so we allow move and copy
105 if ( target
&& Fl::handle( FL_DND_ENTER
, target
) )
106 *pdwEffect
= DROPEFFECT_MOVE
|DROPEFFECT_COPY
; //|DROPEFFECT_LINK;
108 *pdwEffect
= DROPEFFECT_NONE
;
110 *pdwEffect
= DROPEFFECT_NONE
;
112 lastEffect
= *pdwEffect
;
115 HRESULT STDMETHODCALLTYPE
DragOver( DWORD
/*grfKeyState*/, POINTL pt
, DWORD
*pdwEffect
) {
116 if ( px
==pt
.x
&& py
==pt
.y
)
118 *pdwEffect
= lastEffect
;
121 if ( !fl_dnd_target_window
)
123 *pdwEffect
= lastEffect
= DROPEFFECT_NONE
;
126 // set e_modifiers here from grfKeyState, set e_x and e_root_x
129 if (fl_dnd_target_window
) {
130 Fl::e_x
= Fl::e_x_root
-fl_dnd_target_window
->x();
131 Fl::e_y
= Fl::e_y_root
-fl_dnd_target_window
->y();
133 if (fillCurrentDragData(0)) {
134 // Fl_Group will change DND_DRAG into DND_ENTER and DND_LEAVE if needed
135 if ( Fl::handle( FL_DND_DRAG
, fl_dnd_target_window
) )
136 *pdwEffect
= DROPEFFECT_MOVE
|DROPEFFECT_COPY
; //|DROPEFFECT_LINK;
138 *pdwEffect
= DROPEFFECT_NONE
;
140 *pdwEffect
= DROPEFFECT_NONE
;
142 px
= pt
.x
; py
= pt
.y
;
143 lastEffect
= *pdwEffect
;
146 HRESULT STDMETHODCALLTYPE
DragLeave() {
147 if ( fl_dnd_target_window
&& fillCurrentDragData(0))
149 Fl::handle( FL_DND_LEAVE
, fl_dnd_target_window
);
150 fl_dnd_target_window
= 0;
151 clearCurrentDragData();
155 HRESULT STDMETHODCALLTYPE
Drop( IDataObject
*data
, DWORD
/*grfKeyState*/, POINTL pt
, DWORD
* /*pdwEffect*/) {
156 if ( !fl_dnd_target_window
)
158 Fl_Window
*target
= fl_dnd_target_window
;
159 fl_dnd_target_window
= 0;
163 Fl::e_x
= Fl::e_x_root
-target
->x();
164 Fl::e_y
= Fl::e_y_root
-target
->y();
166 // tell FLTK that the user released an object on this widget
167 if ( !Fl::handle( FL_DND_RELEASE
, target
) )
170 Fl_Widget
*w
= target
;
171 while (w
->parent()) w
= w
->window();
172 HWND hwnd
= fl_xid( (Fl_Window
*)w
);
173 if (fillCurrentDragData(data
)) {
174 int old_event
= Fl::e_number
;
176 a
= b
= currDragData
;
177 while (*a
) { // strip the CRLF pairs
178 if (*a
== '\r' && a
[1] == '\n') a
++;
182 Fl::e_text
= currDragData
;
183 Fl::e_length
= b
- currDragData
;
184 Fl::belowmouse()->handle(Fl::e_number
= FL_PASTE
); // e_text will be invalid after this call
185 Fl::e_number
= old_event
;
186 SetForegroundWindow( hwnd
);
187 clearCurrentDragData();
194 static IDataObject
*currDragRef
;
195 static char *currDragData
;
196 static int currDragSize
;
197 static char currDragResult
;
199 static void clearCurrentDragData() {
201 if (currDragData
) free(currDragData
);
206 static char fillCurrentDragData(IDataObject
*data
) {
207 // shortcut through this whole procedure if there is no fresh data
209 return currDragResult
;
210 // shortcut through this whole procedure if this is still the same drag event
211 // (* this is safe, because 'currDragRef' is cleared on Leave and Drop events)
212 if (data
==currDragRef
)
213 return currDragResult
;
215 // clear currDrag* for a new drag event
216 clearCurrentDragData();
219 // fill currDrag* with UTF-8 data, if available
220 FORMATETC fmt
= { 0 };
221 STGMEDIUM medium
= { 0 };
222 fmt
.tymed
= TYMED_HGLOBAL
;
223 fmt
.dwAspect
= DVASPECT_CONTENT
;
225 fmt
.cfFormat
= CF_UNICODETEXT
;
226 // if it is UNICODE text, return a UTF-8-converted copy of it
227 if ( data
->GetData( &fmt
, &medium
)==S_OK
)
229 void *stuff
= GlobalLock( medium
.hGlobal
);
231 const wchar_t *wstuff
= (const wchar_t *)stuff
;
232 while (*wstuff
++) srclen
++;
233 wstuff
= (const wchar_t *)stuff
;
234 unsigned utf8len
= fl_utf8fromwc(NULL
, 0, wstuff
, srclen
);
235 currDragSize
= utf8len
;
236 currDragData
= (char*)malloc(utf8len
+ 1);
237 fl_utf8fromwc(currDragData
, currDragSize
+1, wstuff
, srclen
+1); // include null-byte
238 GlobalUnlock( medium
.hGlobal
);
239 ReleaseStgMedium( &medium
);
241 return currDragResult
;
243 fmt
.cfFormat
= CF_TEXT
;
244 // if it is CP1252 text, return a UTF-8-converted copy of it
245 if ( data
->GetData( &fmt
, &medium
)==S_OK
)
250 void *stuff
= GlobalLock( medium
.hGlobal
);
251 currDragData
= (char*)malloc(3 * strlen((char*)stuff
) + 10);
253 last
= p
+ strlen(p
);
256 u
= fl_utf8decode(p
, last
, &len
);
258 len
= fl_utf8encode(u
, q
);
262 currDragSize
= q
- currDragData
;
263 currDragData
= (char*)realloc(currDragData
, currDragSize
+ 1);
264 GlobalUnlock( medium
.hGlobal
);
265 ReleaseStgMedium( &medium
);
267 return currDragResult
;
269 // else fill currDrag* with filenames, if possible
270 memset(&fmt
, 0, sizeof(fmt
));
271 fmt
.tymed
= TYMED_HGLOBAL
;
272 fmt
.dwAspect
= DVASPECT_CONTENT
;
274 fmt
.cfFormat
= CF_HDROP
;
275 // if it is a pathname list, send an FL_PASTE with a \n separated list of filepaths
276 if ( data
->GetData( &fmt
, &medium
)==S_OK
)
278 HDROP hdrop
= (HDROP
)medium
.hGlobal
;
279 int i
, n
, nn
= 0, nf
= DragQueryFileW( hdrop
, (UINT
)-1, 0, 0 );
280 for ( i
=0; i
<nf
; i
++ ) nn
+= DragQueryFileW( hdrop
, i
, 0, 0 );
282 xchar
*dst
= (xchar
*)malloc(nn
* sizeof(xchar
));
284 for ( i
=0; i
<nf
; i
++ ) {
285 n
= DragQueryFileW( hdrop
, i
, (WCHAR
*)dst
, nn
);
293 currDragData
= (char*) malloc(nn
* 5 + 1);
294 // Fl::e_length = fl_unicode2utf(bu, nn, Fl::e_text);
295 currDragSize
= fl_utf8fromwc(currDragData
, (nn
*5+1), bu
, nn
);
296 currDragData
[currDragSize
] = 0;
299 // Fl::belowmouse()->handle(FL_DROP);
300 // free( Fl::e_text );
301 ReleaseStgMedium( &medium
);
303 return currDragResult
;
306 return currDragResult
;
310 IDropTarget
*flIDropTarget
= &flDropTarget
;
312 IDataObject
*FLDropTarget::currDragRef
= 0;
313 char *FLDropTarget::currDragData
= 0;
314 int FLDropTarget::currDragSize
= 0;
315 char FLDropTarget::currDragResult
= 0;
318 * this class is needed to allow FLTK apps to be a DnD source
320 class FLDropSource
: public IDropSource
324 FLDropSource() { m_cRefCount
= 0; }
325 HRESULT STDMETHODCALLTYPE
QueryInterface( REFIID riid
, LPVOID
*ppvObject
) {
326 if (IID_IUnknown
==riid
|| IID_IDropSource
==riid
)
329 ((LPUNKNOWN
)*ppvObject
)->AddRef();
333 return E_NOINTERFACE
;
335 ULONG STDMETHODCALLTYPE
AddRef() { return ++m_cRefCount
; }
336 ULONG STDMETHODCALLTYPE
Release() {
338 nTemp
= --m_cRefCount
;
343 STDMETHODIMP
GiveFeedback( ulong
) { return DRAGDROP_S_USEDEFAULTCURSORS
; }
344 STDMETHODIMP
QueryContinueDrag( BOOL esc
, DWORD keyState
) {
346 return DRAGDROP_S_CANCEL
;
347 if ( !(keyState
& (MK_LBUTTON
|MK_MBUTTON
|MK_RBUTTON
)) )
348 return DRAGDROP_S_DROP
;
352 class FLEnum
: public IEnumFORMATETC
358 ULONG __stdcall
AddRef(void) {
359 return InterlockedIncrement(&m_lRefCount
);
362 ULONG __stdcall
Release(void) {
363 LONG count
= InterlockedDecrement(&m_lRefCount
);
373 HRESULT __stdcall
QueryInterface(REFIID iid
, void **ppvObject
) {
374 if(iid
== IID_IEnumFORMATETC
|| iid
== IID_IUnknown
) {
380 return E_NOINTERFACE
;
384 HRESULT __stdcall
Next(ULONG celt
, FORMATETC
* rgelt
, ULONG
*pceltFetched
) {
385 if (n
> 0) return S_FALSE
;
386 for (ULONG i
= 0; i
< celt
; i
++) {
388 rgelt
->cfFormat
= CF_HDROP
;
389 rgelt
->dwAspect
= DVASPECT_CONTENT
;
392 rgelt
->tymed
= TYMED_HGLOBAL
;
394 if (pceltFetched
) *pceltFetched
= celt
;
398 HRESULT __stdcall
Skip(ULONG celt
) {
400 return (n
== 0) ? S_OK
: S_FALSE
;
403 HRESULT __stdcall
Reset(void) {
408 HRESULT __stdcall
Clone(IEnumFORMATETC
**ppenum
){
409 *ppenum
= new FLEnum();
418 virtual ~FLEnum(void) {
425 This is the actual object that FLTK can drop somewhere.
427 The implementation is minimal, but it should work with all decent Win32 drop targets
429 class FLDataObject
: public IDataObject
434 FLDataObject() { m_cRefCount
= 1; }// m_EnumF = new FLEnum();}
435 HRESULT STDMETHODCALLTYPE
QueryInterface( REFIID riid
, LPVOID
*ppvObject
) {
436 if (IID_IUnknown
==riid
|| IID_IDataObject
==riid
)
439 ((LPUNKNOWN
)*ppvObject
)->AddRef();
443 return E_NOINTERFACE
;
445 ULONG STDMETHODCALLTYPE
AddRef() { return ++m_cRefCount
; }
446 ULONG STDMETHODCALLTYPE
Release() {
448 nTemp
= --m_cRefCount
;
453 // GetData currently allows UNICODE text through Global Memory only
454 HRESULT STDMETHODCALLTYPE
GetData( FORMATETC
*pformatetcIn
, STGMEDIUM
*pmedium
) {
455 if ((pformatetcIn
->dwAspect
& DVASPECT_CONTENT
) &&
456 (pformatetcIn
->tymed
& TYMED_HGLOBAL
) &&
457 (pformatetcIn
->cfFormat
== CF_UNICODETEXT
))
459 int utf16_len
= fl_utf8toUtf16(fl_selection_buffer
[0], fl_selection_length
[0], 0, 0);
460 HGLOBAL gh
= GlobalAlloc( GHND
, utf16_len
* 2 + 2 );
461 char *pMem
= (char*)GlobalLock( gh
);
462 fl_utf8toUtf16(fl_selection_buffer
[0], fl_selection_length
[0], (unsigned short*)pMem
, utf16_len
+ 1);
463 // HGLOBAL gh = GlobalAlloc( GHND| GMEM_SHARE,
464 // (fl_selection_length[0]+4) * sizeof(short)
465 // + sizeof(DROPFILES));
466 // unsigned char *pMem = (unsigned char*)GlobalLock( gh );
469 // return DV_E_FORMATETC;
471 // DROPFILES *df =(DROPFILES*) pMem;
473 // df->pFiles = sizeof(DROPFILES);
477 // for (int i = 0; i < fl_selection_length[0]; i++) {
478 // if (fl_selection_buffer[0][i] == '\n') {
479 // fl_selection_buffer[0][i] = '\0';
484 // l = fl_utf2unicode((unsigned char*)fl_selection_buffer[0],
485 // fl_selection_length[0], (xchar*)(((char*)pMem)
486 // + sizeof(DROPFILES)));
488 // pMem[l * sizeof(WCHAR) + sizeof(DROPFILES)] = 0;
489 // pMem[l * sizeof(WCHAR) + 1 + sizeof(DROPFILES)] = 0;
490 // pMem[l * sizeof(WCHAR) + 2 + sizeof(DROPFILES)] = 0;
491 // pMem[l * sizeof(WCHAR) + 3 + sizeof(DROPFILES)] = 0;
492 pmedium
->tymed
= TYMED_HGLOBAL
;
493 pmedium
->hGlobal
= gh
;
494 pmedium
->pUnkForRelease
= NULL
;
498 return DV_E_FORMATETC
;
500 HRESULT STDMETHODCALLTYPE
QueryGetData( FORMATETC
*pformatetc
)
502 if ((pformatetc
->dwAspect
& DVASPECT_CONTENT
) &&
503 (pformatetc
->tymed
& TYMED_HGLOBAL
) &&
504 (pformatetc
->cfFormat
== CF_UNICODETEXT
))
506 return DV_E_FORMATETC
;
508 // HRESULT STDMETHODCALLTYPE EnumFormatEtc( DWORD dir, IEnumFORMATETC** ppenumFormatEtc) {
509 // *ppenumFormatEtc = m_EnumF;
513 // all the following methods are not really needed for a DnD object
514 HRESULT STDMETHODCALLTYPE
GetDataHere( FORMATETC
* /*pformatetcIn*/, STGMEDIUM
* /*pmedium*/) { return E_NOTIMPL
; }
515 HRESULT STDMETHODCALLTYPE
GetCanonicalFormatEtc( FORMATETC
* /*in*/, FORMATETC
* /*out*/) { return E_NOTIMPL
; }
516 HRESULT STDMETHODCALLTYPE
SetData( FORMATETC
* /*pformatetc*/, STGMEDIUM
* /*pmedium*/, BOOL
/*fRelease*/) { return E_NOTIMPL
; }
517 HRESULT STDMETHODCALLTYPE
EnumFormatEtc( DWORD
/*dir*/, IEnumFORMATETC
** /*ppenumFormatEtc*/) { return E_NOTIMPL
; }
518 // HRESULT STDMETHODCALLTYPE EnumFormatEtc( DWORD dir, IEnumFORMATETC** ppenumFormatEtc) {*ppenumFormatEtc = m_EnumF; return S_OK;}
519 HRESULT STDMETHODCALLTYPE
DAdvise( FORMATETC
* /*pformatetc*/, DWORD
/*advf*/,
520 IAdviseSink
* /*pAdvSink*/, DWORD
* /*pdwConnection*/) { return E_NOTIMPL
; }
521 HRESULT STDMETHODCALLTYPE
DUnadvise( DWORD
/*dwConnection*/) { return E_NOTIMPL
; }
522 HRESULT STDMETHODCALLTYPE
EnumDAdvise( IEnumSTATDATA
** /*ppenumAdvise*/) { return E_NOTIMPL
; }
531 FLDataObject
*fdo
= new FLDataObject
;
533 FLDropSource
*fds
= new FLDropSource
;
536 HRESULT ret
= DoDragDrop( fdo
, fds
, DROPEFFECT_MOVE
|DROPEFFECT_LINK
|DROPEFFECT_COPY
, &dropEffect
);
541 Fl_Widget
*w
= Fl::pushed();
544 int old_event
= Fl::e_number
;
545 w
->handle(Fl::e_number
= FL_RELEASE
);
546 Fl::e_number
= old_event
;
549 if ( ret
==DRAGDROP_S_DROP
) return 1; // or DD_S_CANCEL
554 // End of "$Id: fl_dnd_win32.cxx 8028 2010-12-14 19:46:55Z AlbrechtS $".