Allegro.cc - Online Community

Allegro.cc Forums » Allegro Development » Drag/Drop Part 2

This thread is locked; no one can reply to it. rss feed Print
Drag/Drop Part 2
DanielH
Member #934
January 2001
avatar

As a continuation from this thread, I been working on adding drag and drop functionality to allegro. Once again, windows only as I can only test on my personal setup.

MSDN has information about the functions. There are few online examples. I found this website with a link to an all 'C' source to add drop capability to a Win32 app.

An edit box is created that you can drag text from one application to the box.

I even cleaned up the code to remove all extraneous functions that are not needed as a target only app. I also condensed the code into two files (main.c and dropdata.c). I also changed some function names.

#SelectExpand
1// internally defined as an IDropTarget object 2typedef struct ALLEGRO_DROP ALLEGRO_DROP; 3 4// internally defined as an IDataObject object 5typedef struct ALLEGRO_DROP_DATA ALLEGRO_DROP_DATA; 6 7// calls the original function RegisterDropWindow(HWND hWnd, IDropTarget **target) 8ALLEGRO_DROP *al_create_drop(HWND hWnd); 9 10// call the original function UnRegisterDropWindow(HWND hWnd, IDropTarget *target) 11void al_destroy_drop(ALLEGRO_DROP *drop); 12 13/* 14a callback function to verify that a specific point on the window 15can accept a drop. If not set, any point will be accepted 16*/ 17void al_set_drop_validate_position_func(bool(*func)(HWND hWnd, int x, int y)); 18 19/* 20a callback function to accept the dropped data. This will eventually be changed to an ALLEGRO_DROP_EVENT under allegro. 21*/ 22void al_set_drop_data_func(void(*func)(ALLEGRO_DROP *drop, ALLEGRO_DROP_DATA *data));

The example app, with the changes, compiles, runs, and behaves as expected. It will accept text from a secondary source.

I added this file my allegro project compiles and runs. However, the window does not accept the drop. It won't accept any of the drag/drop functions (drag_enter, drag_over, drop).

Any thoughts?

The example source actually loads a dialog with a simple editbox. Is it the type of window that allegro creates that won't accept drops?

Pho75_
Member #12,377
November 2010


I think this could only be a very rudimentary implimentation.
Not even sure this belongs as an addon, 
because once you get into DnD, very quickly you want all the bells and whistles of a full GUI library.

But it would be great example code to donate to the community.

-sorry I couldn't find the contents of ALLEGRO_DROP or ALLEGRO_DROP_DATA.
 ALLEGRO_DROP_DATA can be anything. why not just have a "void *data;" in 
 your ALLEGRO_DROP struct.

-since you don't know what is inside of ALLEGRO_DROP_DATA you need a generic way of asking "what am I dragging or what kind of object am I dragging?"
 like a "const char *drag_id_string;" or a simple "int drag_id;" in the DnD struct.

-u should probably keep a copy of the original ALLEGRO_EVENT
 that began the drag in your DnD struct or else copy all the relavant fields
 so they are available to the programmer,

 So when processing your mouse/touch events, you can know if they
 are related to your current DnD operation and compare to origin (position, time, etc.) and update the screen accordingly.

-you need to pass the DnD struct as a parameter in all your callback funcs,
 otherwise at most you can only perform 1 DnD operation at a time which
 would be a very severe limitation to impose.
 worst case: the mouse and each touchscreen finger can all be dragging something simultaneously.
 
-usually drag-and-drop has a configurable movement threshold before kicking in, 
 otherwise it would be too sensitive (especially on high-resolution displays)

-HWND is Windows specific. It needs to be generic across all platforms.
 At worst, pass ALLEGRO_DISPLAY instead 

good luck

SiegeLord
Member #7,827
October 2006
avatar

Pho75_ said:

Not even sure this belongs as an addon, because once you get into DnD, very quickly you want all the bells and whistles of a full GUI library.

Only if by bells and whistles you mean clipboard support :P. I think DnD might be useful in some situations.

Incidentally, just so this doesn't get lost, I suggest, at your convenience, to make a pull request at the github mirror (https://github.com/liballeg/allegro5). It might continue to get ignored (I'm a bit busy and this still seems like it needs some work), but at least it won't get buried in the forums.

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

DanielH
Member #934
January 2001
avatar

Pho75_, I think you're misunderstanding what I'm trying to do. I want to drag data from one application into my application not dragging objects around my screen.

And, I know it needs to be platform independent. These are generic objects and functions. Each platform will need it's own implementation of those objects. I'm working on a windows version.

The callbacks are for testing at the moment. Eventually, when it gets closer to finalization, I plan on instead pushing drag/drop events into a queue.

At the moment, I'm just trying to get a rudimentary text dragging example running. Here is the code I have.

#SelectExpand
1// al_drop.h 2#ifndef __al_included_allegro5_aldrop_h 3#define __al_included_allegro5_aldrop_h 4 5#include <allegro5\allegro.h> 6 7#ifdef __cplusplus 8extern "C" { 9#endif 10 11typedef struct ALLEGRO_DROP_DATA ALLEGRO_DROP_DATA; 12typedef struct ALLEGRO_DROP_TARGET ALLEGRO_DROP_TARGET; 13 14ALLEGRO_DROP_TARGET *al_create_drop_target(ALLEGRO_DISPLAY *display); 15void al_destroy_drop_target(ALLEGRO_DROP_TARGET *pDropTarget); 16 17void al_set_drop_position_function(bool(*func)(ALLEGRO_DISPLAY *display, int x, int y)); 18void al_set_drop_target_function(void(*func)(ALLEGRO_DISPLAY *display, int x, int y, ALLEGRO_DROP_DATA *pDropData)); 19 20#ifdef __cplusplus 21} 22#endif 23 24#endif // !__al_included_allegro5_aldrop_h

#SelectExpand
1// al_drop.c 2#define STRICT 3 4#include <stdint.h> 5#include <allegro5\allegro.h> 6#include <allegro5\allegro_windows.h> 7#include <Ole2.h> 8#include "al_drop.h" 9 10static bool(*_al_drop_validate_func)(ALLEGRO_DISPLAY *display, int x, int y) = 0; 11static void(*_al_drop_function)(ALLEGRO_DISPLAY *display, int x, int y, ALLEGRO_DROP_DATA *pDropData) = 0; 12 13typedef struct ALLEGRO_DROP_DATA 14{ 15 IDataObject ido; 16 int ref_count; 17 FORMATETC *m_pFormatEtc; 18 STGMEDIUM *m_pStgMedium; 19 LONG m_nNumFormats; 20 LONG m_lRefCount; 21} ALLEGRO_DROP_DATA; 22 23typedef struct ALLEGRO_DROP_TARGET 24{ 25 IDropTarget idt; 26 LONG m_lRefCount; 27 ALLEGRO_DISPLAY *m_display; 28 BOOL m_fAllowDrop; 29 ALLEGRO_DROP_DATA *m_pDataObject; 30} ALLEGRO_DROP_TARGET; 31 32typedef struct _al_drop_table_t 33{ 34 BEGIN_INTERFACE 35 HRESULT(STDMETHODCALLTYPE *QueryInterface)(__RPC__in ALLEGRO_DROP_TARGET * pDropTarget, __RPC__in REFIID riid, _COM_Outptr_ void **ppvObject); 36 ULONG(STDMETHODCALLTYPE *AddRef)(__RPC__in ALLEGRO_DROP_TARGET * pDropTarget); 37 ULONG(STDMETHODCALLTYPE *Release)(__RPC__in ALLEGRO_DROP_TARGET * pDropTarget); 38 HRESULT(STDMETHODCALLTYPE *DragEnter)(__RPC__in ALLEGRO_DROP_TARGET * pDropTarget, __RPC__in_opt ALLEGRO_DROP_DATA *pDataObject, DWORD grfKeyState, POINTL pt, __RPC__inout DWORD *pdwEffect); 39 HRESULT(STDMETHODCALLTYPE *DragOver)(__RPC__in ALLEGRO_DROP_TARGET * pDropTarget, DWORD grfKeyState, POINTL pt, __RPC__inout DWORD *pdwEffect); 40 HRESULT(STDMETHODCALLTYPE *DragLeave)(__RPC__in ALLEGRO_DROP_TARGET * pDropTarget); 41 HRESULT(STDMETHODCALLTYPE *Drop)(__RPC__in ALLEGRO_DROP_TARGET * pDropTarget, __RPC__in_opt ALLEGRO_DROP_DATA *pDataObject, DWORD grfKeyState, POINTL pt, __RPC__inout DWORD *pdwEffect); 42 END_INTERFACE 43} _al_drop_table_t; 44 45 46static BOOL _al_query_data_object(ALLEGRO_DROP_DATA *pDataObject) 47{ 48 FORMATETC fmtetc = { CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; 49 50 // does the data object support CF_TEXT using a HGLOBAL? 51 return (pDataObject->ido.lpVtbl->QueryGetData((LPDATAOBJECT)pDataObject, &fmtetc) == S_OK ? TRUE : FALSE); 52} 53 54static DWORD _al_query_drop_effect(ALLEGRO_DROP_TARGET *pDropTarget, DWORD grfKeyState, POINTL pt, DWORD dwAllowed) 55{ 56 DWORD dwEffect = 0; 57 58 if (0 != _al_drop_validate_func) 59 { 60 if (!_al_drop_validate_func(pDropTarget->m_display, pt.x, pt.y)) 61 { 62 return dwEffect; 63 } 64 } 65 66 // 2. work out that the drop-effect should be based on grfKeyState 67 if (grfKeyState & MK_CONTROL) 68 { 69 dwEffect = dwAllowed & DROPEFFECT_COPY; 70 } 71 else 72 { 73 if (grfKeyState & MK_SHIFT) 74 { 75 dwEffect = dwAllowed & DROPEFFECT_MOVE; 76 } 77 } 78 79 // 3. no key-modifiers were specified (or drop effect not allowed), so 80 // base the effect on those allowed by the dropsource 81 if (dwEffect == 0) 82 { 83 if ((dwAllowed & DROPEFFECT_COPY) == DROPEFFECT_COPY) 84 { 85 dwEffect = DROPEFFECT_COPY; 86 } 87 88 if ((dwAllowed & DROPEFFECT_MOVE) == DROPEFFECT_MOVE) 89 { 90 dwEffect = DROPEFFECT_MOVE; 91 } 92 } 93 94 return dwEffect; 95} 96 97static ULONG STDMETHODCALLTYPE _al_drop_target_add_reference(__RPC__in ALLEGRO_DROP_TARGET *pDropTarget) 98{ 99 return InterlockedIncrement(&pDropTarget->m_lRefCount); 100} 101 102static HRESULT STDMETHODCALLTYPE _al_drop_target_query_interface(__RPC__in ALLEGRO_DROP_TARGET * pDropTarget, __RPC__in REFIID riid, _COM_Outptr_ void **ppvObject) 103{ 104 *ppvObject = NULL; 105 106 if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IDropTarget)) 107 { 108 _al_drop_target_add_reference(pDropTarget); 109 *ppvObject = pDropTarget; 110 return S_OK; 111 } 112 113 return E_NOINTERFACE; 114} 115 116static ULONG STDMETHODCALLTYPE _al_drop_target_release(__RPC__in ALLEGRO_DROP_TARGET * pDropTarget) 117{ 118 ULONG count = InterlockedDecrement(&pDropTarget->m_lRefCount); 119 120 if (count == 0) 121 { 122 free(pDropTarget); 123 } 124 125 return count; 126} 127 128static HRESULT STDMETHODCALLTYPE _al_drop_target_drag_enter(__RPC__in ALLEGRO_DROP_TARGET * pDropTarget, __RPC__in_opt ALLEGRO_DROP_DATA *pDataObject, DWORD grfKeyState, POINTL pt, __RPC__inout DWORD *pdwEffect) 129{ 130 pDropTarget->m_fAllowDrop = _al_query_data_object(pDataObject); 131 132 if (pDropTarget->m_fAllowDrop) 133 { 134 *pdwEffect = _al_query_drop_effect(pDropTarget, grfKeyState, pt, *pdwEffect); 135 } 136 else 137 { 138 *pdwEffect = DROPEFFECT_NONE; 139 } 140 141 return S_OK; 142} 143 144static HRESULT STDMETHODCALLTYPE _al_drop_target_drag_over(__RPC__in ALLEGRO_DROP_TARGET * pDropTarget, DWORD grfKeyState, POINTL pt, __RPC__inout DWORD *pdwEffect) 145{ 146 if (pDropTarget->m_fAllowDrop) 147 { 148 *pdwEffect = _al_query_drop_effect(pDropTarget, grfKeyState, pt, *pdwEffect); 149 } 150 else 151 { 152 *pdwEffect = DROPEFFECT_NONE; 153 } 154 155 return S_OK; 156} 157 158static HRESULT STDMETHODCALLTYPE _al_drop_target_drag_leave(__RPC__in ALLEGRO_DROP_TARGET * pDropTarget) 159{ 160 return S_OK; 161} 162 163static HRESULT STDMETHODCALLTYPE _al_drop_target_drop(__RPC__in ALLEGRO_DROP_TARGET * pDropTarget, __RPC__in_opt ALLEGRO_DROP_DATA *pDataObject, DWORD grfKeyState, POINTL pt, __RPC__inout DWORD *pdwEffect) 164{ 165 if (pDropTarget->m_fAllowDrop) 166 { 167 if (_al_drop_function) 168 { 169 _al_drop_function(pDropTarget->m_display, pt.x, pt.y, pDataObject); 170 } 171 172 *pdwEffect = _al_query_drop_effect(pDropTarget, grfKeyState, pt, *pdwEffect); 173 } 174 else 175 { 176 *pdwEffect = DROPEFFECT_NONE; 177 } 178 179 return S_OK; 180} 181 182static _al_drop_table_t idt_vtbl = 183{ 184 _al_drop_target_query_interface, 185 _al_drop_target_add_reference, 186 _al_drop_target_release, 187 _al_drop_target_drag_enter, 188 _al_drop_target_drag_over, 189 _al_drop_target_drag_leave, 190 _al_drop_target_drop 191}; 192 193ALLEGRO_DROP_TARGET *al_create_drop_target(ALLEGRO_DISPLAY *display) 194{ 195 ALLEGRO_DROP_TARGET *pDropTarget = (ALLEGRO_DROP_TARGET*)malloc(sizeof(ALLEGRO_DROP_TARGET)); 196 197 if (0 != pDropTarget) 198 { 199 pDropTarget->idt.lpVtbl = ((IDropTargetVtbl*)&idt_vtbl); 200 pDropTarget->m_lRefCount = 1; 201 pDropTarget->m_display = display; 202 pDropTarget->m_fAllowDrop = FALSE; 203 204 if (S_OK != CoLockObjectExternal((struct IUnknown*)pDropTarget, TRUE, FALSE) || 205 S_OK != RegisterDragDrop(al_get_win_window_handle(display), (LPDROPTARGET)pDropTarget)) 206 { 207 free(pDropTarget); 208 pDropTarget = NULL; 209 } 210 } 211 212 return pDropTarget; 213} 214 215void al_destroy_drop_target(ALLEGRO_DROP_TARGET *pDropTarget) 216{ 217 if (0 != pDropTarget) 218 { 219 // remove drag+drop 220 RevokeDragDrop(al_get_win_window_handle(pDropTarget->m_display)); 221 222 // remove the strong lock 223 CoLockObjectExternal((struct IUnknown*)pDropTarget, FALSE, TRUE); 224 225 // release our own reference 226 ((IDropTarget*)pDropTarget)->lpVtbl->Release((IDropTarget*)pDropTarget); 227 } 228} 229 230void al_set_drop_position_function(bool(*func)(ALLEGRO_DISPLAY *display, int x, int y)) 231{ 232 _al_drop_validate_func = func; 233} 234 235void al_set_drop_target_function(void(*func)(ALLEGRO_DISPLAY *display, int x, int y, ALLEGRO_DROP_DATA *pDropData)) 236{ 237 _al_drop_function = func; 238}

Trent Gamblin
Member #261
April 2000
avatar

You have functions to create and destroy ALLEGRO_DROP_TARGET but I don't see any public functions that use those.

Pho75_
Member #12,377
November 2010

DanielH,

Apologies, thanks for clarifying. Cool feature to add.

DanielH
Member #934
January 2001
avatar

Trent - I originally had this:

void al_set_drop_position_function(bool(*func)(ALLEGRO_DROP_TARGET *pDropTarget, y, int x, int y));
void al_set_drop_target_function(void(*func)(ALLEGRO_DROP_TARGET *pDropTarget, int x, int y, ALLEGRO_DROP_DATA *pDropData));

with a separate function to retrieve the display for that target

ALLEGRO_DISPLAY *al_get_display_from_drop_target(ALLEGRO_DROP_TARGET *pDropTarget);

At the moment, I am simplifying the whole process until I get it working.

UPDATE: I finally have a working prototype. The problem was that the drop target had to be created in the same thread that processes windows messages.

The new code is set up that all you have to do is or another flag when you create a new display (ALLEGRO_ACCEPT_DROP). This will setup the display to generate drag/drop events. Default: any type any where on the screen. There are functions to change this.

#SelectExpand
1// events.h 2enum 3{ 4 ... 5 ALLEGRO_EVENT_DRAG_ENTER = 48, 6 ALLEGRO_EVENT_DRAG_OVER = 49, 7 ALLEGRO_EVENT_DRAG_LEAVE = 50, 8 ALLEGRO_EVENT_DROP = 51 9} 10 11typedef struct ALLEGRO_DROP_EVENT 12{ 13 _AL_EVENT_HEADER(struct ALLEGRO_DISPLAY) 14 int32_t x; 15 int32_t y; 16 int32_t data_type; // text and bitmaps. 17 struct ALLEGRO_DROP_DATA *data; 18} ALLEGRO_DROP_EVENT;

#SelectExpand
1// drop.h 2#ifndef __al_included_allegro5_drop_h 3#define __al_included_allegro5_drop_h 4 5#ifdef __cplusplus 6extern "C" { 7#endif 8 9 enum { 10 ALLEGRO_DROP_TEXT = 1 << 0, 11 ALLEGRO_DROP_BITMAP = 1 << 1, 12 13 ALLEGRO_DROP_DEFAULT = 0xffff 14 }; 15 16 typedef struct ALLEGRO_DROP_DATA ALLEGRO_DROP_DATA; 17 18 // creates a corresponding allegro object (ALLEGRO_USTR,ALLEGRO_BITMAP,...) 19 AL_FUNC(void*, al_retrieve_drop_data, (ALLEGRO_DROP_DATA *pDropData)); 20 21 // set/get where on the display an item can be droppped (-1 on width/height for full display) 22 AL_FUNC(void, al_set_allow_drop_coordinates, (ALLEGRO_DISPLAY *display, int x, int y, int w, int h)); 23 AL_FUNC(void, al_get_allow_drop_coordinates, (ALLEGRO_DISPLAY *display, int *x, int *y, int *w, int *h)); 24 25 // set/get which objects can be dropped 26 AL_FUNC(void, al_set_allow_drop_type, (ALLEGRO_DISPLAY *display, int type)); 27 AL_FUNC(int, al_get_allow_drop_type, (ALLEGRO_DISPLAY *display)); 28 29#ifdef __cplusplus 30} 31#endif 32 33#endif // !__al_included_allegro5_drop_h

The function, al_retrieve_drop_data, needs work. Retrieving text is easy. Converting a HBITMAP to an ALLEGRO_BITMAP is needed.

Go to: