summaryrefslogtreecommitdiff
path: root/src/lib/ecore_win32/ecore_win32_dnd_drop_target.cpp
blob: 2f2da1af65460ea0f4f46fcb8bcf75f4fbe49dc3 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
/*
 * vim:ts=8:sw=3:sts=8:noexpandtab:cino=>5n-3f0^-2{2
 */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include "ecore_win32_dnd_drop_target.h"

#include "ecore_win32_private.h"


// structors

DropTarget::DropTarget(HWND window, Ecore_Win32_Dnd_DropTarget_Callback callback, void *window_obj_ptr)
  : ref_count_(1)
  , window_(window)
  , allow_drop_(false)
  , drop_callback_(callback)
  ,drop_callback_ptr_(window_obj_ptr)
{ }


// IUnknown

HRESULT DropTarget::QueryInterface(REFIID iid, void **ppvObject)
{
   // check to see what interface has been requested
   if (iid == IID_IDropTarget || iid == IID_IUnknown)
   {
      AddRef();
      *ppvObject = this;
      return S_OK;
   }
   *ppvObject = 0;

   return E_NOINTERFACE;
}

ULONG DropTarget::AddRef()
{
   return InterlockedIncrement(&ref_count_);
}

ULONG DropTarget::Release()
{
   LONG count = InterlockedDecrement(&ref_count_);
   if (count == 0)
   {
      delete this;
      return 0;
   }

   return count;
}


// IDropTarget

HRESULT DropTarget::DragEnter(IDataObject *pDataObject, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
{
   // does the dataobject contain data we want?
   allow_drop_ = QueryDataObject(pDataObject) &&
      (drop_callback_ == NULL ||
      (drop_callback_(drop_callback_ptr_, ECORE_WIN32_DND_EVENT_DRAG_ENTER, pt.x, pt.y, NULL, 0) != 0));

   if (allow_drop_)
   {
      // get the dropeffect based on keyboard state
      *pdwEffect = DropEffect(grfKeyState, pt, *pdwEffect);
      SetFocus(window_);
      //PositionCursor(_hwnd, pt);
   }
   else
      *pdwEffect = DROPEFFECT_NONE;
   return S_OK;
}

HRESULT DropTarget::DragOver(DWORD grfKeyState, POINTL pt, DWORD * pdwEffect)
{
   allow_drop_ =
     (drop_callback_ == NULL) ||
     (drop_callback_(drop_callback_ptr_, ECORE_WIN32_DND_EVENT_DRAG_OVER, pt.x, pt.y, NULL, 0) != 0);

   if (allow_drop_)
   {
      *pdwEffect = DropEffect(grfKeyState, pt, *pdwEffect);
      //PositionCursor(m_hWnd, pt);
   }
   else
   {
      *pdwEffect = DROPEFFECT_NONE;
   }

   return S_OK;
}

HRESULT DropTarget::DragLeave()
{
   POINT pt;

   GetCursorPos(&pt);
   if (drop_callback_ != NULL)
     drop_callback_(drop_callback_ptr_, ECORE_WIN32_DND_EVENT_DRAG_LEAVE, pt.x, pt.y, NULL, 0);

   return S_OK;
}

HRESULT DropTarget::Drop(IDataObject *pDataObject, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
{
   if (allow_drop_)
   {
      // construct a FORMATETC object
      FORMATETC fmtetc = { CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
      STGMEDIUM stgmed;

      // See if the dataobject contains any TEXT stored as a HGLOBAL
      if (pDataObject->QueryGetData(&fmtetc) == S_OK)
      {
         // Yippie! the data is there, so go get it!
         if (pDataObject->GetData(&fmtetc, &stgmed) == S_OK)
         {
            // we asked for the data as a HGLOBAL, so access it appropriately
            PVOID data = GlobalLock(stgmed.hGlobal);
            UINT size = GlobalSize(stgmed.hGlobal);

            if (drop_callback_ != NULL)
            {
               drop_callback_(drop_callback_ptr_,
                              ECORE_WIN32_DND_EVENT_DROP,
                              pt.x, pt.y,
                              data, size);
            }

            GlobalUnlock(stgmed.hGlobal);

            // release the data using the COM API
            ReleaseStgMedium(&stgmed);
         }
      }
      *pdwEffect = DropEffect(grfKeyState, pt, *pdwEffect);
   }
   else
   {
      *pdwEffect = DROPEFFECT_NONE;
   }

   return S_OK;
}


// internal helper function

DWORD DropTarget::DropEffect(DWORD grfKeyState, POINTL pt EINA_UNUSED, DWORD dwAllowed)
{
   DWORD dwEffect = 0;

   // 1. check "pt" -> do we allow a drop at the specified coordinates?

   // 2. work out that the drop-effect should be based on grfKeyState
   if (grfKeyState & MK_CONTROL)
   {
	   dwEffect = dwAllowed & DROPEFFECT_COPY;
   }
   else if (grfKeyState & MK_SHIFT)
   {
	   dwEffect = dwAllowed & DROPEFFECT_MOVE;
   }

   // 3. no key-modifiers were specified (or drop effect not allowed), so
   //    base the effect on those allowed by the dropsource
   if (dwEffect == 0)
   {
	   if (dwAllowed & DROPEFFECT_COPY) dwEffect = DROPEFFECT_COPY;
	   if (dwAllowed & DROPEFFECT_MOVE) dwEffect = DROPEFFECT_MOVE;
   }

   return dwEffect;
}

bool DropTarget::QueryDataObject(IDataObject *pDataObject)
{
    FORMATETC fmtetc = { CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };

    // does the data object support CF_TEXT using a HGLOBAL?
    return pDataObject->QueryGetData(&fmtetc) == S_OK;
}


// ecore_win32 private functions

void *_ecore_win32_dnd_register_drop_window(HWND hwnd,  Ecore_Win32_Dnd_DropTarget_Callback callback, void *ptr)
{
   DropTarget *pDropTarget = new DropTarget(hwnd, callback, ptr);

   if (pDropTarget == NULL)
     return NULL;

   // acquire a strong lock
   if (FAILED(CoLockObjectExternal(pDropTarget, TRUE, FALSE)))
     {
        delete pDropTarget;
        return NULL;
     }

   // tell OLE that the window is a drop target
   if (FAILED(RegisterDragDrop(hwnd, pDropTarget)))
     {
        delete pDropTarget;
        return NULL;
     }

   return pDropTarget;
}

void _ecore_win32_dnd_unregister_drop_window(HWND hwnd, void *drop_target)
{
   IDropTarget *pDropTarget = (IDropTarget *)drop_target;

   if (drop_target == NULL)
     return;

   // remove drag+drop
   RevokeDragDrop(hwnd);

   // remove the strong lock
   CoLockObjectExternal(pDropTarget, FALSE, TRUE);

   // release our own reference
   pDropTarget->Release();
}