1 /*
   2  * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 #include "common.h"
  27 
  28 #include "GlassDnD.h"
  29 #include "GlassClipboard.h"
  30 #include "GlassApplication.h"
  31 
  32 #include "com_sun_glass_events_MouseEvent.h"
  33 
  34 //Ctrl + Shift -> ACTION_LINK
  35 //Ctrl         -> ACTION_COPY
  36 //Shift        -> ACTION_MOVE
  37 GlassDropTarget::GlassDropTarget(ViewContainer *viewContainer, HWND hwnd)
  38 {
  39     OLE_TRY
  40 
  41     m_viewContainer = viewContainer;
  42     m_hwnd = hwnd;
  43 
  44     OLE_HRT(CoCreateInstance(
  45         CLSID_DragDropHelper,
  46         NULL,
  47         CLSCTX_ALL,
  48         IID_IDropTargetHelper,
  49         (LPVOID*)&m_spDropTargetHelper
  50     ))
  51     OLE_HRT(::RegisterDragDrop(m_hwnd, this))
  52     OLE_CATCH
  53     STRACE(_T("{GlassDropTarget"));
  54 }
  55 
  56 GlassDropTarget::~GlassDropTarget()
  57 {
  58     m_spDropTargetHelper = NULL;
  59     ::RevokeDragDrop(m_hwnd);
  60     STRACE(_T("}GlassDropTarget"));
  61 }
  62 
  63 HRESULT  GlassDropTarget::UpdateDnDClipboardData(
  64     IDataObject *pDataObj)
  65 {
  66     JNIEnv *env = GetEnv();
  67     //Get "DnD" clipboard
  68     JLClass jcWinDnDClipboard(env,
  69         GlassApplication::ClassForName(env, "com.sun.glass.ui.win.WinDnDClipboard"));
  70     ASSERT(jcWinDnDClipboard)
  71     static jmethodID midGetInstance = env->GetStaticMethodID(jcWinDnDClipboard, "getInstance",
  72         "()Lcom/sun/glass/ui/win/WinDnDClipboard;");
  73     ASSERT(midGetInstance)
  74     HRESULT result = checkJavaException(env);
  75     if (result != S_OK) {
  76         return result;
  77     }
  78     JLObject jDnDClipboard(env, env->CallStaticObjectMethod(jcWinDnDClipboard, midGetInstance));
  79     ASSERT(jDnDClipboard)
  80 
  81     IDataObject *pOldDataObj = getPtr(env, jDnDClipboard);
  82     if (pOldDataObj != pDataObj) {
  83         if (NULL != pDataObj) {
  84             //lock it till clipboard close
  85             pDataObj->AddRef();
  86         }
  87         setPtr(env, jDnDClipboard, pDataObj);
  88         if (NULL != pOldDataObj) {
  89             //unlock old data instance
  90             pOldDataObj->Release();
  91         }
  92     }
  93 
  94     return checkJavaException(env);
  95 }
  96 
  97 HRESULT  GlassDropTarget::CallbackToJava(
  98     /* [in] */ jmethodID method,
  99     /* [in] */ DWORD grfKeyState,
 100     /* [in] */ POINTL pt,
 101     /* [out][in] */ DWORD *pdwEffect)
 102 {
 103     if (!m_viewContainer->GetView()) {
 104         return S_OK;
 105     }
 106 
 107     JNIEnv *env = GetEnv();
 108     POINT ptClient = *(LPPOINT)&pt;
 109     ::ScreenToClient(m_hwnd, &ptClient);
 110 
 111     SetSourceSupportedActions(getACTION(*pdwEffect));
 112 
 113     //We want to be Explorer-like.
 114     //Found the action from keyboard state.
 115     DWORD like = DROPEFFECT_MOVE;
 116     grfKeyState &= MK_CONTROL | MK_SHIFT | MK_ALT;
 117     if ( (MK_CONTROL | MK_SHIFT)== grfKeyState || MK_ALT == grfKeyState) {
 118         like = DROPEFFECT_LINK;
 119     } else if (MK_CONTROL == grfKeyState) {
 120         like = DROPEFFECT_COPY;
 121     }
 122 
 123     static DROPEFFECT DesiredActions[] = {
 124         //Actions in order of priority (the same order is in Explorer)
 125         DROPEFFECT_COPY,
 126         DROPEFFECT_MOVE,
 127         DROPEFFECT_LINK
 128     };
 129 
 130     //Let's check the target ability for selected action.
 131     for( int iDesiredIndex = 0;
 132 
 133         (like & *pdwEffect) == 0 //action is not supported by target
 134         && iDesiredIndex < sizeof(DesiredActions)/sizeof(*DesiredActions);
 135 
 136         ++iDesiredIndex)
 137     {
 138         //target cannot do the action, let's try the next
 139         like = DesiredActions[iDesiredIndex];
 140     }
 141 
 142     *pdwEffect = getDROPEFFECT(DROPEFFECT(env->CallIntMethod(m_viewContainer->GetView(), method,
 143         jint(ptClient.x), jint(ptClient.y), jint(pt.x), jint(pt.y), getACTION(like))));
 144 
 145     return checkJavaException(env);
 146 }
 147 
 148 HRESULT GlassDropTarget::DragEnter(
 149         /* [unique][in] */ IDataObject *pDataObj,
 150         /* [in] */ DWORD grfKeyState,
 151         /* [in] */ POINTL pt,
 152         /* [out][in] */ DWORD *pdwEffect)
 153 {
 154     OLE_TRY
 155     OLE_HRT(UpdateDnDClipboardData(pDataObj))
 156     //dragAction = View.notifyDragEnter(...)
 157     OLE_HRT(CallbackToJava(javaIDs.View.notifyDragEnter, grfKeyState, pt, pdwEffect))
 158     //ignore HRESULT - just no image
 159     m_spDropTargetHelper->DragEnter(m_hwnd, pDataObj, (LPPOINT)&pt, *pdwEffect);
 160     OLE_CATCH
 161     OLE_RETURN_HR
 162 }
 163 
 164 HRESULT GlassDropTarget::DragOver(
 165     /* [in] */ DWORD grfKeyState,
 166     /* [in] */ POINTL pt,
 167     /* [out][in] */ DWORD *pdwEffect)
 168 {
 169     OLE_TRY
 170     //dragAction = View.notifyDragOver(...)
 171     OLE_HRT(OLE_HRT(CallbackToJava(javaIDs.View.notifyDragOver, grfKeyState, pt, pdwEffect)))
 172     //ignore HRESULT - just no image
 173     m_spDropTargetHelper->DragOver((LPPOINT)&pt, *pdwEffect);
 174     OLE_CATCH
 175     OLE_RETURN_HR
 176 }
 177 
 178 HRESULT GlassDropTarget::DragLeave()
 179 {
 180     if (!m_viewContainer->GetView()) {
 181         return S_OK;
 182     }
 183 
 184     OLE_TRY
 185     JNIEnv *env = GetEnv();
 186     //View.notifyDragLeave()
 187     env->CallIntMethod(m_viewContainer->GetView(), javaIDs.View.notifyDragLeave);
 188     OLE_HRT(checkJavaException(env))
 189     //ignore HRESULT - just no image
 190     m_spDropTargetHelper->DragLeave();
 191     OLE_CATCH
 192     OLE_RETURN_HR
 193 }
 194 
 195 HRESULT GlassDropTarget::Drop(
 196     /* [unique][in] */ IDataObject *pDataObj,
 197     /* [in] */ DWORD grfKeyState,
 198     /* [in] */ POINTL pt,
 199     /* [out][in] */ DWORD *pdwEffect)
 200 {
 201     OLE_TRY
 202     OLE_HRT(UpdateDnDClipboardData(pDataObj))
 203     //performedAction = View.notifyDragDrop(...)
 204     OLE_HRT(OLE_HRT(CallbackToJava(javaIDs.View.notifyDragDrop, grfKeyState, pt, pdwEffect)))
 205     //ignore HRESULT - just no image
 206     m_spDropTargetHelper->Drop(pDataObj, (LPPOINT)&pt, *pdwEffect);
 207     OLE_CATCH
 208     OLE_RETURN_HR
 209 }
 210 
 211 /*static*/
 212 HRESULT GlassDropTarget::SetSourceSupportedActions(/*in*/jint actions)
 213 {
 214     JNIEnv *env = GetEnv();
 215     //Get "DnD" clipboard
 216     JLClass jcWinDnDClipboard(env,
 217         GlassApplication::ClassForName(env, "com.sun.glass.ui.win.WinDnDClipboard"));
 218     ASSERT(jcWinDnDClipboard)
 219     static jmethodID midGetInstance = env->GetStaticMethodID(jcWinDnDClipboard, "getInstance",
 220         "()Lcom/sun/glass/ui/win/WinDnDClipboard;");
 221     ASSERT(midGetInstance)
 222     HRESULT result = checkJavaException(env);
 223     if (result != S_OK) {
 224         return result;
 225     }
 226 
 227     static jmethodID midSetSourceSupportedActions = env->GetMethodID(jcWinDnDClipboard, "setSourceSupportedActions",
 228         "(I)V");
 229     ASSERT(midSetSourceSupportedActions)
 230     result = checkJavaException(env);
 231     if (result != S_OK) {
 232         return result;
 233     }
 234 
 235     JLObject jDnDClipboard(env, env->CallStaticObjectMethod(jcWinDnDClipboard, midGetInstance));
 236     ASSERT(jDnDClipboard)
 237 
 238     env->CallVoidMethod(jDnDClipboard, midSetSourceSupportedActions, actions);
 239 
 240     return checkJavaException(env);
 241 }
 242 
 243 //////////////////////////////////////////////////////////////////////////
 244 // GlassDropSource
 245 //////////////////////////////////////////////////////////////////////////
 246 
 247 /*static*/
 248 HRESULT  GlassDropSource::SetDragButton(jint button)
 249 {
 250     JNIEnv *env = GetEnv();
 251     //Get "DnD" clipboard
 252     JLClass jcWinDnDClipboard(env,
 253         GlassApplication::ClassForName(env, "com.sun.glass.ui.win.WinDnDClipboard"));
 254     ASSERT(jcWinDnDClipboard)
 255     static jmethodID midGetInstance = env->GetStaticMethodID(jcWinDnDClipboard, "getInstance",
 256         "()Lcom/sun/glass/ui/win/WinDnDClipboard;");
 257     ASSERT(midGetInstance)
 258     HRESULT result = checkJavaException(env);
 259     if (result != S_OK) {
 260         return result;
 261     }
 262 
 263     static jmethodID midSetDragButton = env->GetMethodID(jcWinDnDClipboard, "setDragButton",
 264         "(I)V");
 265     ASSERT(midSetDragButton)
 266     result = checkJavaException(env);
 267     if (result != S_OK) {
 268         return result;
 269     }
 270 
 271     JLObject jDnDClipboard(env, env->CallStaticObjectMethod(jcWinDnDClipboard, midGetInstance));
 272     ASSERT(jDnDClipboard)
 273 
 274     env->CallVoidMethod(jDnDClipboard, midSetDragButton, button);
 275 
 276     return checkJavaException(env);
 277 }
 278 
 279 GlassDropSource::GlassDropSource(jobject jDnDClipboard)
 280 {
 281     JNIEnv *env = GetEnv();
 282     static jmethodID midGetDragButton = 0;
 283     if (0 == midGetDragButton) {
 284         JLClass jcWinDnDClipboard(env,
 285             GlassApplication::ClassForName(env, "com.sun.glass.ui.win.WinDnDClipboard"));
 286         ASSERT(jcWinDnDClipboard)
 287 
 288         midGetDragButton = env->GetMethodID(jcWinDnDClipboard, "getDragButton",
 289             "()I");
 290         ASSERT(midGetDragButton)
 291         HRESULT result = checkJavaException(env);
 292         if (result != S_OK) {
 293             return;
 294         }
 295     }
 296 
 297     jint jbutton = env->CallIntMethod(jDnDClipboard, midGetDragButton);
 298     switch (jbutton) {
 299     case com_sun_glass_events_MouseEvent_BUTTON_LEFT:
 300         m_button = MK_LBUTTON;
 301         break;
 302     case com_sun_glass_events_MouseEvent_BUTTON_RIGHT:
 303         m_button = MK_RBUTTON;
 304         break;
 305     case com_sun_glass_events_MouseEvent_BUTTON_OTHER:
 306         m_button = MK_MBUTTON;
 307         break;
 308     default:
 309         m_button = 0;
 310         break;
 311     }
 312     STRACE(_T("{GlassDropSource"));
 313 }
 314 
 315 GlassDropSource::~GlassDropSource()
 316 {
 317     STRACE(_T("}GlassDropSource"));
 318 }
 319 
 320 HRESULT GlassDropSource::QueryContinueDrag(
 321     /* [in] */ BOOL fEscapePressed,
 322     /* [in] */ DWORD grfKeyState)
 323 {
 324     return fEscapePressed
 325         ? DRAGDROP_S_CANCEL
 326         : 0 == (grfKeyState & m_button)
 327           ? DRAGDROP_S_DROP
 328           : S_OK;
 329 }
 330 
 331 HRESULT GlassDropSource::GiveFeedback(/* [in] */DWORD dwEffect)
 332 {
 333     return DRAGDROP_S_USEDEFAULTCURSORS;
 334 }