From 565cb189ea38d506148b61da31ca1c726a5bc319 Mon Sep 17 00:00:00 2001 From: jules Date: Mon, 22 Oct 2012 12:28:46 +0100 Subject: [PATCH] Introjucer: ability to drag files from the project tree into external apps. Added more drag-and-drop functionality for linux. --- .../Source/Application/jucer_MainWindow.cpp | 30 ++ .../Source/Application/jucer_MainWindow.h | 2 + .../Project/jucer_ProjectTreeViewBase.h | 2 + .../Source/Utility/jucer_JucerTreeViewBase.h | 3 +- .../mouse/juce_DragAndDropContainer.cpp | 62 +-- .../native/juce_linux_Windowing.cpp | 396 ++++++++++++++++-- 6 files changed, 439 insertions(+), 56 deletions(-) diff --git a/extras/Introjucer/Source/Application/jucer_MainWindow.cpp b/extras/Introjucer/Source/Application/jucer_MainWindow.cpp index da1a559133..f3675fa682 100644 --- a/extras/Introjucer/Source/Application/jucer_MainWindow.cpp +++ b/extras/Introjucer/Source/Application/jucer_MainWindow.cpp @@ -29,6 +29,7 @@ #include "jucer_OpenDocumentManager.h" #include "../Code Editor/jucer_SourceCodeEditor.h" #include "../Project/jucer_NewProjectWizard.h" +#include "../Utility/jucer_JucerTreeViewBase.h" ScopedPointer commandManager; @@ -228,6 +229,35 @@ void MainWindow::filesDropped (const StringArray& filenames, int mouseX, int mou } } +bool MainWindow::shouldDropFilesWhenDraggedExternally (const DragAndDropTarget::SourceDetails& sourceDetails, + StringArray& files, bool& canMoveFiles) +{ + if (TreeView* tv = dynamic_cast (sourceDetails.sourceComponent.get())) + { + Array selected; + + for (int i = tv->getNumSelectedItems(); --i >= 0;) + if (JucerTreeViewBase* b = dynamic_cast (tv->getSelectedItem(i))) + selected.add (b); + + if (selected.size() > 0) + { + for (int i = selected.size(); --i >= 0;) + { + const File f (selected.getUnchecked(i)->getDraggableFile()); + + if (f.existsAsFile()) + files.add (f.getFullPathName()); + } + + canMoveFiles = false; + return files.size() > 0; + } + } + + return false; +} + void MainWindow::activeWindowStatusChanged() { DocumentWindow::activeWindowStatusChanged(); diff --git a/extras/Introjucer/Source/Application/jucer_MainWindow.h b/extras/Introjucer/Source/Application/jucer_MainWindow.h index 0ae0c1fb5b..143d2ac23b 100644 --- a/extras/Introjucer/Source/Application/jucer_MainWindow.h +++ b/extras/Introjucer/Source/Application/jucer_MainWindow.h @@ -74,6 +74,8 @@ public: void getCommandInfo (CommandID commandID, ApplicationCommandInfo& result); bool perform (const InvocationInfo& info); + bool shouldDropFilesWhenDraggedExternally (const DragAndDropTarget::SourceDetails& sourceDetails, + StringArray& files, bool& canMoveFiles); private: ScopedPointer currentProject; diff --git a/extras/Introjucer/Source/Project/jucer_ProjectTreeViewBase.h b/extras/Introjucer/Source/Project/jucer_ProjectTreeViewBase.h index 5294b37b80..1ddcb5e4d8 100644 --- a/extras/Introjucer/Source/Project/jucer_ProjectTreeViewBase.h +++ b/extras/Introjucer/Source/Project/jucer_ProjectTreeViewBase.h @@ -92,6 +92,8 @@ public: static void getAllSelectedNodesInTree (Component* componentInTree, OwnedArray & selectedNodes); + File getDraggableFile() const { return getFile(); } + //============================================================================== Project::Item item; diff --git a/extras/Introjucer/Source/Utility/jucer_JucerTreeViewBase.h b/extras/Introjucer/Source/Utility/jucer_JucerTreeViewBase.h index 675a2e55a7..8464176248 100644 --- a/extras/Introjucer/Source/Utility/jucer_JucerTreeViewBase.h +++ b/extras/Introjucer/Source/Utility/jucer_JucerTreeViewBase.h @@ -58,7 +58,8 @@ public: virtual Icon getIcon() const = 0; virtual float getIconSize() const; virtual void paintContent (Graphics& g, const Rectangle& area); - virtual int getMillisecsAllowedForDragGesture() { return 120; }; + virtual int getMillisecsAllowedForDragGesture() { return 120; }; + virtual File getDraggableFile() const { return File::nonexistent; } void refreshSubItems(); virtual void deleteItem(); diff --git a/modules/juce_gui_basics/mouse/juce_DragAndDropContainer.cpp b/modules/juce_gui_basics/mouse/juce_DragAndDropContainer.cpp index 95489bf099..13a7e240e2 100644 --- a/modules/juce_gui_basics/mouse/juce_DragAndDropContainer.cpp +++ b/modules/juce_gui_basics/mouse/juce_DragAndDropContainer.cpp @@ -43,8 +43,7 @@ public: owner (owner_), mouseDragSource (mouseDragSource_), imageOffset (imageOffset_), - hasCheckedForExternalDrag (false), - isDoingExternalDrag (false) + hasCheckedForExternalDrag (false) { setSize (im.getWidth(), im.getHeight()); @@ -96,16 +95,13 @@ public: DragAndDropTarget::SourceDetails details (sourceDetails); DragAndDropTarget* finalTarget = nullptr; - if (! isDoingExternalDrag) - { - const bool wasVisible = isVisible(); - setVisible (false); - Component* unused; - finalTarget = findTarget (e.getScreenPosition(), details.localPosition, unused); + const bool wasVisible = isVisible(); + setVisible (false); + Component* unused; + finalTarget = findTarget (e.getScreenPosition(), details.localPosition, unused); - if (wasVisible) // fade the component and remove it - it'll be deleted later by the timer callback - dismissWithAnimation (finalTarget == nullptr); - } + if (wasVisible) // fade the component and remove it - it'll be deleted later by the timer callback + dismissWithAnimation (finalTarget == nullptr); if (getParentComponent() != nullptr) getParentComponent()->removeChildComponent (this); @@ -154,8 +150,15 @@ public: sendDragMove (details); - if (canDoExternalDrag && getCurrentlyOver() == nullptr) - checkForExternalDrag (details, screenPos); + if (canDoExternalDrag) + { + const Time now (Time::getCurrentTime()); + + if (getCurrentlyOver() != nullptr) + lastTimeOverTarget = now; + else if (now > lastTimeOverTarget + RelativeTime::milliseconds (700)) + checkForExternalDrag (details, screenPos); + } } void timerCallback() @@ -179,7 +182,8 @@ private: DragAndDropContainer& owner; WeakReference mouseDragSource, currentlyOverComp; const Point imageOffset; - bool hasCheckedForExternalDrag, isDoingExternalDrag; + bool hasCheckedForExternalDrag; + Time lastTimeOverTarget; DragAndDropTarget* getCurrentlyOver() const noexcept { @@ -236,6 +240,22 @@ private: target->itemDragMove (details); } + struct ExternalDragAndDropMessage : public CallbackMessage + { + ExternalDragAndDropMessage (const StringArray& f, bool canMove) + : files (f), canMoveFiles (canMove) + {} + + void messageCallback() + { + DragAndDropContainer::performExternalDragDropOfFiles (files, canMoveFiles); + } + + private: + StringArray files; + bool canMoveFiles; + }; + void checkForExternalDrag (DragAndDropTarget::SourceDetails& details, const Point& screenPos) { if (! hasCheckedForExternalDrag) @@ -247,17 +267,11 @@ private: bool canMoveFiles = false; if (owner.shouldDropFilesWhenDraggedExternally (details, files, canMoveFiles) - && files.size() > 0) + && files.size() > 0 + && ModifierKeys::getCurrentModifiersRealtime().isAnyMouseButtonDown()) { - WeakReference thisWeakRef (this); - setVisible (false); - isDoingExternalDrag = true; - - if (ModifierKeys::getCurrentModifiersRealtime().isAnyMouseButtonDown()) - DragAndDropContainer::performExternalDragDropOfFiles (files, canMoveFiles); - - delete thisWeakRef.get(); - return; + (new ExternalDragAndDropMessage (files, canMoveFiles))->post(); + delete this; } } } diff --git a/modules/juce_gui_basics/native/juce_linux_Windowing.cpp b/modules/juce_gui_basics/native/juce_linux_Windowing.cpp index 4c189a6fba..1731d549c6 100644 --- a/modules/juce_gui_basics/native/juce_linux_Windowing.cpp +++ b/modules/juce_gui_basics/native/juce_linux_Windowing.cpp @@ -58,6 +58,7 @@ struct Atoms XdndTypeList = getCreating ("XdndTypeList"); XdndActionList = getCreating ("XdndActionList"); XdndActionCopy = getCreating ("XdndActionCopy"); + XdndActionPrivate = getCreating ("XdndActionPrivate"); XdndActionDescription = getCreating ("XdndActionDescription"); allowedMimeTypes[0] = getCreating ("UTF8_STRING"); @@ -65,11 +66,14 @@ struct Atoms allowedMimeTypes[2] = getCreating ("text/plain"); allowedMimeTypes[3] = getCreating ("text/uri-list"); + externalAllowedFileMimeTypes[0] = getCreating ("text/uri-list"); + externalAllowedTextMimeTypes[0] = getCreating ("text/plain"); + allowedActions[0] = getCreating ("XdndActionMove"); allowedActions[1] = XdndActionCopy; allowedActions[2] = getCreating ("XdndActionLink"); allowedActions[3] = getCreating ("XdndActionAsk"); - allowedActions[4] = getCreating ("XdndActionPrivate"); + allowedActions[4] = XdndActionPrivate; } static const Atoms& get() @@ -89,9 +93,11 @@ struct Atoms ActiveWin, Pid, WindowType, WindowState, XdndAware, XdndEnter, XdndLeave, XdndPosition, XdndStatus, XdndDrop, XdndFinished, XdndSelection, XdndTypeList, XdndActionList, - XdndActionDescription, XdndActionCopy, + XdndActionDescription, XdndActionCopy, XdndActionPrivate, allowedActions[5], - allowedMimeTypes[4]; + allowedMimeTypes[4], + externalAllowedFileMimeTypes[1], + externalAllowedTextMimeTypes[1]; static const unsigned long DndVersion; @@ -774,6 +780,15 @@ namespace PixmapHelpers } } +static void* createDraggingHandCursor() +{ + static unsigned char dragHandData[] = { 71,73,70,56,57,97,16,0,16,0,145,2,0,0,0,0,255,255,255,0, + 0,0,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0, 16,0,0,2,52,148,47,0,200,185,16,130,90,12,74,139,107,84,123,39, + 132,117,151,116,132,146,248,60,209,138,98,22,203,114,34,236,37,52,77,217, 247,154,191,119,110,240,193,128,193,95,163,56,60,234,98,135,2,0,59 }; + const int dragHandDataSize = 99; + + return CustomMouseCursorInfo (ImageFileFormat::loadFrom (dragHandData, dragHandDataSize), 8, 7).create(); +} //============================================================================== class LinuxComponentPeer : public ComponentPeer @@ -1270,6 +1285,8 @@ public: case ConfigureNotify: handleConfigureNotifyEvent (event.xconfigure); break; case ReparentNotify: handleReparentNotifyEvent(); break; case GravityNotify: handleGravityNotify(); break; + case SelectionClear: handleExternalSelectionClear(); break; + case SelectionRequest: handleExternalSelectionRequest (event); break; case CirculateNotify: case CreateNotify: @@ -1286,10 +1303,6 @@ public: mapped = false; break; - case SelectionClear: - case SelectionRequest: - break; - default: #if JUCE_USE_XSHM { @@ -1493,6 +1506,9 @@ public: default: break; } + if (dragState.dragging) + handleExternalDragButtonReleaseEvent (buttonRelEvent); + handleMouseEvent (0, getMousePos (buttonRelEvent), currentModifiers, getEventTime (buttonRelEvent)); clearLastMousePos(); @@ -1531,6 +1547,9 @@ public: } } + if (dragState.dragging) + handleExternalDragMotionNotify(); + handleMouseEvent (0, mousePos - getScreenPosition(), currentModifiers, getEventTime (movedEvent)); } } @@ -1718,14 +1737,42 @@ public: } else if (clientMsg.message_type == atoms.XdndStatus) { - handleDragAndDropStatus (clientMsg); + handleExternalDragAndDropStatus (clientMsg); } else if (clientMsg.message_type == atoms.XdndFinished) { - resetDragAndDrop(); + externalResetDragAndDrop(); } } + bool externalDragTextInit (const String& text) + { + if (dragState.dragging) + return false; + + return externalDragInit (true, text); + } + + bool externalDragFileInit (const StringArray& files, bool /*canMoveFiles*/) + { + if (dragState.dragging) + return false; + + StringArray uriList; + + for (int i = 0; i < files.size(); ++i) + { + const String& f = files[i]; + + if (f.matchesWildcard ("?*://*", false)) + uriList.add (f); + else + uriList.add ("file://" + f); + } + + return externalDragInit (false, uriList.joinIntoString ("\r\n")); + } + //============================================================================== void showMouseCursor (Cursor cursor) noexcept { @@ -2128,6 +2175,7 @@ private: { ScopedXLock xlock; resetDragAndDrop(); + resetExternalDragState(); // Get defaults for various properties const int screen = DefaultScreen (display); @@ -2307,6 +2355,46 @@ private: } } + //============================================================================== + struct DragState + { + bool isText; + bool dragging; // currently performing outgoing external dnd as Xdnd source, have grabbed mouse + bool expectingStatus; // XdndPosition sent, waiting for XdndStatus + Window targetWindow; // potential drop target + Rectangle silentRect; + bool canDrop; // target window signals it will accept the drop + int xdndVersion; // negotiated version with target + String textOrFiles; + + void reset() + { + isText = false; + dragging = false; + expectingStatus = false; + canDrop = false; + silentRect = Rectangle(); + targetWindow = None; + xdndVersion = -1; + textOrFiles = String::empty; + } + + const Atom* getMimeTypes() const noexcept { return isText ? Atoms::get().externalAllowedTextMimeTypes + : Atoms::get().externalAllowedFileMimeTypes; } + + int getNumMimeTypes() const noexcept { return isText ? numElementsInArray (Atoms::get().externalAllowedTextMimeTypes) + : numElementsInArray (Atoms::get().externalAllowedFileMimeTypes); } + + bool matchesTarget (Atom targetType) const + { + for (int i = getNumMimeTypes(); --i >= 0;) + if (getMimeTypes()[i] == targetType) + return true; + + return false; + } + }; + //============================================================================== void resetDragAndDrop() { @@ -2318,6 +2406,11 @@ private: finishAfterDropDataReceived = false; } + void resetExternalDragState() + { + dragState.reset(); + } + void sendDragAndDropMessage (XClientMessageEvent& msg) { msg.type = ClientMessage; @@ -2330,6 +2423,63 @@ private: XSendEvent (display, dragAndDropSourceWindow, False, 0, (XEvent*) &msg); } + bool sendExternalDragAndDropMessage (XClientMessageEvent& msg, const Window targetWindow) + { + msg.type = ClientMessage; + msg.display = display; + msg.window = targetWindow; + msg.format = 32; + msg.data.l[0] = windowH; + + ScopedXLock xlock; + return XSendEvent (display, targetWindow, False, 0, (XEvent*) &msg) != 0; + } + + void sendExternalDragAndDropDrop (const Window targetWindow) + { + XClientMessageEvent msg = { 0 }; + msg.message_type = Atoms::get().XdndDrop; + msg.data.l[2] = CurrentTime; + + sendExternalDragAndDropMessage (msg, targetWindow); + } + + void sendExternalDragAndDropEnter (const Window targetWindow) + { + XClientMessageEvent msg = { 0 }; + msg.message_type = Atoms::get().XdndEnter; + + const Atoms& atoms = Atoms::get(); + + const Atom* mimeTypes = dragState.getMimeTypes(); + const int numMimeTypes = dragState.getNumMimeTypes(); + + msg.data.l[1] = dragState.xdndVersion << 24 | numMimeTypes > 3; + msg.data.l[2] = numMimeTypes > 0 ? mimeTypes[0] : 0; + msg.data.l[3] = numMimeTypes > 1 ? mimeTypes[1] : 0; + msg.data.l[4] = numMimeTypes > 2 ? mimeTypes[2] : 0; + + sendExternalDragAndDropMessage (msg, targetWindow); + } + + void sendExternalDragAndDropPosition (const Window targetWindow) + { + XClientMessageEvent msg = { 0 }; + msg.message_type = Atoms::get().XdndPosition; + + const Point mousePos (Desktop::getInstance().getMousePosition()); + + if (dragState.silentRect.contains (mousePos)) // we've been asked to keep silent + return; + + msg.data.l[1] = 0; + msg.data.l[2] = (mousePos.x << 16) | mousePos.y; + msg.data.l[3] = CurrentTime; + msg.data.l[4] = Atoms::get().XdndActionCopy; // this is all JUCE currently supports + + dragState.expectingStatus = sendExternalDragAndDropMessage (msg, targetWindow); + } + void sendDragAndDropStatus (const bool acceptDrop, Atom dropAction) { XClientMessageEvent msg = { 0 }; @@ -2340,11 +2490,11 @@ private: sendDragAndDropMessage (msg); } - void sendDragAndDropLeave() + void sendExternalDragAndDropLeave (const Window targetWindow) { XClientMessageEvent msg = { 0 }; msg.message_type = Atoms::get().XdndLeave; - sendDragAndDropMessage (msg); + sendExternalDragAndDropMessage (msg, targetWindow); } void sendDragAndDropFinish() @@ -2354,19 +2504,116 @@ private: sendDragAndDropMessage (msg); } - void handleDragAndDropStatus (const XClientMessageEvent& clientMsg) + void handleExternalSelectionClear() { - if ((clientMsg.data.l[1] & 1) == 0) + if (dragState.dragging) + externalResetDragAndDrop(); + } + + void handleExternalSelectionRequest (const XEvent& evt) + { + Atom targetType = evt.xselectionrequest.target; + + XEvent s; + s.xselection.type = SelectionNotify; + s.xselection.requestor = evt.xselectionrequest.requestor; + s.xselection.selection = evt.xselectionrequest.selection; + s.xselection.target = targetType; + s.xselection.property = None; + s.xselection.time = evt.xselectionrequest.time; + + if (dragState.matchesTarget (targetType)) { - sendDragAndDropLeave(); + s.xselection.property = evt.xselectionrequest.property; - if (! dragInfo.isEmpty()) - handleDragExit (dragInfo); + xchangeProperty (evt.xselectionrequest.requestor, + evt.xselectionrequest.property, + targetType, 8, + dragState.textOrFiles.toUTF8().getAddress(), + dragState.textOrFiles.getNumBytesAsUTF8()); + } + + XSendEvent (display, evt.xselectionrequest.requestor, True, 0, &s); + } - dragInfo.clear(); + void handleExternalDragAndDropStatus (const XClientMessageEvent& clientMsg) + { + if (dragState.expectingStatus) + { + dragState.expectingStatus = false; + dragState.canDrop = false; + dragState.silentRect = Rectangle(); + + if ((clientMsg.data.l[1] & 1) != 0 + && (clientMsg.data.l[4] == Atoms::get().XdndActionCopy + || clientMsg.data.l[4] == Atoms::get().XdndActionPrivate)) + { + if ((clientMsg.data.l[1] & 2) == 0) // target requests silent rectangle + dragState.silentRect.setBounds (clientMsg.data.l[2] >> 16, + clientMsg.data.l[2] & 0xffff, + clientMsg.data.l[3] >> 16, + clientMsg.data.l[3] & 0xffff); + + dragState.canDrop = true; + } } } + void handleExternalDragButtonReleaseEvent (const XButtonReleasedEvent& buttonRelEvent) + { + if (dragState.dragging) + XUngrabPointer (display, CurrentTime); + + if (dragState.canDrop) + { + sendExternalDragAndDropDrop (dragState.targetWindow); + } + else + { + sendExternalDragAndDropLeave (dragState.targetWindow); + externalResetDragAndDrop(); + } + } + + void handleExternalDragMotionNotify() + { + Window targetWindow = externalFindDragTargetWindow (RootWindow (display, DefaultScreen (display))); + + if (dragState.targetWindow != targetWindow) + { + if (dragState.targetWindow != None) + sendExternalDragAndDropLeave (dragState.targetWindow); + + dragState.canDrop = false; + dragState.silentRect = Rectangle(); + + if (targetWindow == None) + return; + + GetXProperty prop (targetWindow, Atoms::get().XdndAware, + 0, 2, false, AnyPropertyType); + + if (prop.success + && prop.data != None + && prop.actualFormat == 32 + && prop.numItems == 1) + { + dragState.xdndVersion = jmin ((int) prop.data[0], (int) Atoms::DndVersion); + } + else + { + dragState.xdndVersion = -1; + return; + } + + sendExternalDragAndDropEnter (targetWindow); + dragState.targetWindow = targetWindow; + } + + if (! dragState.expectingStatus) + sendExternalDragAndDropPosition (targetWindow); + } + void handleDragAndDropPosition (const XClientMessageEvent& clientMsg) { if (dragAndDropSourceWindow == 0) @@ -2425,7 +2672,7 @@ private: sendDragAndDropFinish(); resetDragAndDrop(); - if (dragInfoCopy.files.size() > 0 || dragInfoCopy.text.isNotEmpty()) + if (! dragInfoCopy.isEmpty()) handleDragDrop (dragInfoCopy); } @@ -2499,7 +2746,7 @@ private: for (;;) { GetXProperty prop (evt.xany.window, evt.xselection.property, - dropData.getSize() / 4, 65536, true, AnyPropertyType); + dropData.getSize() / 4, 65536, false, AnyPropertyType); if (! prop.success) break; @@ -2548,6 +2795,84 @@ private: } } + static bool isWindowDnDAware (Window w) + { + int numProperties = 0; + Atom* const atoms = XListProperties (display, w, &numProperties); + + bool dndAwarePropFound = false; + for (int i = 0; i < numProperties; ++i) + if (atoms[i] == Atoms::get().XdndAware) + dndAwarePropFound = true; + + if (atoms != nullptr) + XFree (atoms); + + return dndAwarePropFound; + } + + Window externalFindDragTargetWindow (Window targetWindow) + { + if (targetWindow == None) + return None; + + if (isWindowDnDAware (targetWindow)) + return targetWindow; + + Window child, phonyWin; + int phony; + unsigned int uphony; + + XQueryPointer (display, targetWindow, &phonyWin, &child, + &phony, &phony, &phony, &phony, &uphony); + + return externalFindDragTargetWindow (child); + } + + bool externalDragInit (bool isText, const String& textOrFiles) + { + ScopedXLock xlock; + + resetExternalDragState(); + dragState.isText = isText; + dragState.textOrFiles = textOrFiles; + + const int pointerGrabMask = Button1MotionMask | ButtonReleaseMask; + + if (XGrabPointer (display, windowH, True, pointerGrabMask, + GrabModeAsync, GrabModeAsync, None, None, CurrentTime) == GrabSuccess) + { + // No other method of changing the pointer seems to work, this call is needed from this very context + XChangeActivePointerGrab (display, pointerGrabMask, (Cursor) createDraggingHandCursor(), CurrentTime); + + const Atoms& atoms = Atoms::get(); + XSetSelectionOwner (display, atoms.XdndSelection, windowH, CurrentTime); + + // save the available types to XdndTypeList + xchangeProperty (windowH, atoms.XdndTypeList, XA_ATOM, 32, + dragState.getMimeTypes(), + dragState.getNumMimeTypes()); + + dragState.dragging = true; + handleExternalDragMotionNotify(); + return true; + } + + return false; + } + + void externalResetDragAndDrop() + { + if (dragState.dragging) + { + ScopedXLock xlock; + XUngrabPointer (display, CurrentTime); + } + + resetExternalDragState(); + } + + DragState dragState; DragInfo dragInfo; Atom dragAndDropCurrentMimeType; Window dragAndDropSourceWindow; @@ -2981,16 +3306,7 @@ void* MouseCursor::createStandardMouseCursor (MouseCursor::StandardCursorType ty case BottomLeftCornerResizeCursor: shape = XC_bottom_left_corner; break; case BottomRightCornerResizeCursor: shape = XC_bottom_right_corner; break; case CrosshairCursor: shape = XC_crosshair; break; - - case DraggingHandCursor: - { - static unsigned char dragHandData[] = { 71,73,70,56,57,97,16,0,16,0,145,2,0,0,0,0,255,255,255,0, - 0,0,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0, 16,0,0,2,52,148,47,0,200,185,16,130,90,12,74,139,107,84,123,39, - 132,117,151,116,132,146,248,60,209,138,98,22,203,114,34,236,37,52,77,217, 247,154,191,119,110,240,193,128,193,95,163,56,60,234,98,135,2,0,59 }; - const int dragHandDataSize = 99; - - return CustomMouseCursorInfo (ImageFileFormat::loadFrom (dragHandData, dragHandDataSize), 8, 7).create(); - } + case DraggingHandCursor: return createDraggingHandCursor(); case CopyingCursor: { @@ -3033,13 +3349,31 @@ Image juce_createIconForFile (const File& file) //============================================================================== bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& files, const bool canMoveFiles) { - jassertfalse; // not implemented! + if (files.size() == 0) + return false; + + if (MouseInputSource* draggingSource = Desktop::getInstance().getDraggingMouseSource(0)) + if (Component* sourceComp = draggingSource->getComponentUnderMouse()) + if (LinuxComponentPeer* const lp = dynamic_cast (sourceComp->getPeer())) + return lp->externalDragFileInit (files, canMoveFiles); + + // This method must be called in response to a component's mouseDown or mouseDrag event! + jassertfalse; return false; } bool DragAndDropContainer::performExternalDragDropOfText (const String& text) { - jassertfalse; // not implemented! + if (text.isEmpty()) + return false; + + if (MouseInputSource* draggingSource = Desktop::getInstance().getDraggingMouseSource(0)) + if (Component* sourceComp = draggingSource->getComponentUnderMouse()) + if (LinuxComponentPeer* const lp = dynamic_cast (sourceComp->getPeer())) + return lp->externalDragTextInit (text); + + // This method must be called in response to a component's mouseDown or mouseDrag event! + jassertfalse; return false; }