Browse Source

Introjucer: ability to drag files from the project tree into external apps. Added more drag-and-drop functionality for linux.

tags/2021-05-28
jules 13 years ago
parent
commit
565cb189ea
6 changed files with 439 additions and 56 deletions
  1. +30
    -0
      extras/Introjucer/Source/Application/jucer_MainWindow.cpp
  2. +2
    -0
      extras/Introjucer/Source/Application/jucer_MainWindow.h
  3. +2
    -0
      extras/Introjucer/Source/Project/jucer_ProjectTreeViewBase.h
  4. +2
    -1
      extras/Introjucer/Source/Utility/jucer_JucerTreeViewBase.h
  5. +38
    -24
      modules/juce_gui_basics/mouse/juce_DragAndDropContainer.cpp
  6. +365
    -31
      modules/juce_gui_basics/native/juce_linux_Windowing.cpp

+ 30
- 0
extras/Introjucer/Source/Application/jucer_MainWindow.cpp View File

@@ -29,6 +29,7 @@
#include "jucer_OpenDocumentManager.h" #include "jucer_OpenDocumentManager.h"
#include "../Code Editor/jucer_SourceCodeEditor.h" #include "../Code Editor/jucer_SourceCodeEditor.h"
#include "../Project/jucer_NewProjectWizard.h" #include "../Project/jucer_NewProjectWizard.h"
#include "../Utility/jucer_JucerTreeViewBase.h"
ScopedPointer<ApplicationCommandManager> commandManager; ScopedPointer<ApplicationCommandManager> 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 <TreeView*> (sourceDetails.sourceComponent.get()))
{
Array<JucerTreeViewBase*> selected;
for (int i = tv->getNumSelectedItems(); --i >= 0;)
if (JucerTreeViewBase* b = dynamic_cast <JucerTreeViewBase*> (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() void MainWindow::activeWindowStatusChanged()
{ {
DocumentWindow::activeWindowStatusChanged(); DocumentWindow::activeWindowStatusChanged();


+ 2
- 0
extras/Introjucer/Source/Application/jucer_MainWindow.h View File

@@ -74,6 +74,8 @@ public:
void getCommandInfo (CommandID commandID, ApplicationCommandInfo& result); void getCommandInfo (CommandID commandID, ApplicationCommandInfo& result);
bool perform (const InvocationInfo& info); bool perform (const InvocationInfo& info);
bool shouldDropFilesWhenDraggedExternally (const DragAndDropTarget::SourceDetails& sourceDetails,
StringArray& files, bool& canMoveFiles);
private: private:
ScopedPointer <Project> currentProject; ScopedPointer <Project> currentProject;


+ 2
- 0
extras/Introjucer/Source/Project/jucer_ProjectTreeViewBase.h View File

@@ -92,6 +92,8 @@ public:
static void getAllSelectedNodesInTree (Component* componentInTree, OwnedArray <Project::Item>& selectedNodes); static void getAllSelectedNodesInTree (Component* componentInTree, OwnedArray <Project::Item>& selectedNodes);
File getDraggableFile() const { return getFile(); }
//============================================================================== //==============================================================================
Project::Item item; Project::Item item;


+ 2
- 1
extras/Introjucer/Source/Utility/jucer_JucerTreeViewBase.h View File

@@ -58,7 +58,8 @@ public:
virtual Icon getIcon() const = 0; virtual Icon getIcon() const = 0;
virtual float getIconSize() const; virtual float getIconSize() const;
virtual void paintContent (Graphics& g, const Rectangle<int>& area); virtual void paintContent (Graphics& g, const Rectangle<int>& area);
virtual int getMillisecsAllowedForDragGesture() { return 120; };
virtual int getMillisecsAllowedForDragGesture() { return 120; };
virtual File getDraggableFile() const { return File::nonexistent; }
void refreshSubItems(); void refreshSubItems();
virtual void deleteItem(); virtual void deleteItem();


+ 38
- 24
modules/juce_gui_basics/mouse/juce_DragAndDropContainer.cpp View File

@@ -43,8 +43,7 @@ public:
owner (owner_), owner (owner_),
mouseDragSource (mouseDragSource_), mouseDragSource (mouseDragSource_),
imageOffset (imageOffset_), imageOffset (imageOffset_),
hasCheckedForExternalDrag (false),
isDoingExternalDrag (false)
hasCheckedForExternalDrag (false)
{ {
setSize (im.getWidth(), im.getHeight()); setSize (im.getWidth(), im.getHeight());
@@ -96,16 +95,13 @@ public:
DragAndDropTarget::SourceDetails details (sourceDetails); DragAndDropTarget::SourceDetails details (sourceDetails);
DragAndDropTarget* finalTarget = nullptr; 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) if (getParentComponent() != nullptr)
getParentComponent()->removeChildComponent (this); getParentComponent()->removeChildComponent (this);
@@ -154,8 +150,15 @@ public:
sendDragMove (details); 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() void timerCallback()
@@ -179,7 +182,8 @@ private:
DragAndDropContainer& owner; DragAndDropContainer& owner;
WeakReference<Component> mouseDragSource, currentlyOverComp; WeakReference<Component> mouseDragSource, currentlyOverComp;
const Point<int> imageOffset; const Point<int> imageOffset;
bool hasCheckedForExternalDrag, isDoingExternalDrag;
bool hasCheckedForExternalDrag;
Time lastTimeOverTarget;
DragAndDropTarget* getCurrentlyOver() const noexcept DragAndDropTarget* getCurrentlyOver() const noexcept
{ {
@@ -236,6 +240,22 @@ private:
target->itemDragMove (details); 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<int>& screenPos) void checkForExternalDrag (DragAndDropTarget::SourceDetails& details, const Point<int>& screenPos)
{ {
if (! hasCheckedForExternalDrag) if (! hasCheckedForExternalDrag)
@@ -247,17 +267,11 @@ private:
bool canMoveFiles = false; bool canMoveFiles = false;
if (owner.shouldDropFilesWhenDraggedExternally (details, files, canMoveFiles) if (owner.shouldDropFilesWhenDraggedExternally (details, files, canMoveFiles)
&& files.size() > 0)
&& files.size() > 0
&& ModifierKeys::getCurrentModifiersRealtime().isAnyMouseButtonDown())
{ {
WeakReference<Component> 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;
} }
} }
} }


+ 365
- 31
modules/juce_gui_basics/native/juce_linux_Windowing.cpp View File

@@ -58,6 +58,7 @@ struct Atoms
XdndTypeList = getCreating ("XdndTypeList"); XdndTypeList = getCreating ("XdndTypeList");
XdndActionList = getCreating ("XdndActionList"); XdndActionList = getCreating ("XdndActionList");
XdndActionCopy = getCreating ("XdndActionCopy"); XdndActionCopy = getCreating ("XdndActionCopy");
XdndActionPrivate = getCreating ("XdndActionPrivate");
XdndActionDescription = getCreating ("XdndActionDescription"); XdndActionDescription = getCreating ("XdndActionDescription");
allowedMimeTypes[0] = getCreating ("UTF8_STRING"); allowedMimeTypes[0] = getCreating ("UTF8_STRING");
@@ -65,11 +66,14 @@ struct Atoms
allowedMimeTypes[2] = getCreating ("text/plain"); allowedMimeTypes[2] = getCreating ("text/plain");
allowedMimeTypes[3] = getCreating ("text/uri-list"); allowedMimeTypes[3] = getCreating ("text/uri-list");
externalAllowedFileMimeTypes[0] = getCreating ("text/uri-list");
externalAllowedTextMimeTypes[0] = getCreating ("text/plain");
allowedActions[0] = getCreating ("XdndActionMove"); allowedActions[0] = getCreating ("XdndActionMove");
allowedActions[1] = XdndActionCopy; allowedActions[1] = XdndActionCopy;
allowedActions[2] = getCreating ("XdndActionLink"); allowedActions[2] = getCreating ("XdndActionLink");
allowedActions[3] = getCreating ("XdndActionAsk"); allowedActions[3] = getCreating ("XdndActionAsk");
allowedActions[4] = getCreating ("XdndActionPrivate");
allowedActions[4] = XdndActionPrivate;
} }
static const Atoms& get() static const Atoms& get()
@@ -89,9 +93,11 @@ struct Atoms
ActiveWin, Pid, WindowType, WindowState, ActiveWin, Pid, WindowType, WindowState,
XdndAware, XdndEnter, XdndLeave, XdndPosition, XdndStatus, XdndAware, XdndEnter, XdndLeave, XdndPosition, XdndStatus,
XdndDrop, XdndFinished, XdndSelection, XdndTypeList, XdndActionList, XdndDrop, XdndFinished, XdndSelection, XdndTypeList, XdndActionList,
XdndActionDescription, XdndActionCopy,
XdndActionDescription, XdndActionCopy, XdndActionPrivate,
allowedActions[5], allowedActions[5],
allowedMimeTypes[4];
allowedMimeTypes[4],
externalAllowedFileMimeTypes[1],
externalAllowedTextMimeTypes[1];
static const unsigned long DndVersion; 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 class LinuxComponentPeer : public ComponentPeer
@@ -1270,6 +1285,8 @@ public:
case ConfigureNotify: handleConfigureNotifyEvent (event.xconfigure); break; case ConfigureNotify: handleConfigureNotifyEvent (event.xconfigure); break;
case ReparentNotify: handleReparentNotifyEvent(); break; case ReparentNotify: handleReparentNotifyEvent(); break;
case GravityNotify: handleGravityNotify(); break; case GravityNotify: handleGravityNotify(); break;
case SelectionClear: handleExternalSelectionClear(); break;
case SelectionRequest: handleExternalSelectionRequest (event); break;
case CirculateNotify: case CirculateNotify:
case CreateNotify: case CreateNotify:
@@ -1286,10 +1303,6 @@ public:
mapped = false; mapped = false;
break; break;
case SelectionClear:
case SelectionRequest:
break;
default: default:
#if JUCE_USE_XSHM #if JUCE_USE_XSHM
{ {
@@ -1493,6 +1506,9 @@ public:
default: break; default: break;
} }
if (dragState.dragging)
handleExternalDragButtonReleaseEvent (buttonRelEvent);
handleMouseEvent (0, getMousePos (buttonRelEvent), currentModifiers, getEventTime (buttonRelEvent)); handleMouseEvent (0, getMousePos (buttonRelEvent), currentModifiers, getEventTime (buttonRelEvent));
clearLastMousePos(); clearLastMousePos();
@@ -1531,6 +1547,9 @@ public:
} }
} }
if (dragState.dragging)
handleExternalDragMotionNotify();
handleMouseEvent (0, mousePos - getScreenPosition(), currentModifiers, getEventTime (movedEvent)); handleMouseEvent (0, mousePos - getScreenPosition(), currentModifiers, getEventTime (movedEvent));
} }
} }
@@ -1718,14 +1737,42 @@ public:
} }
else if (clientMsg.message_type == atoms.XdndStatus) else if (clientMsg.message_type == atoms.XdndStatus)
{ {
handleDragAndDropStatus (clientMsg);
handleExternalDragAndDropStatus (clientMsg);
} }
else if (clientMsg.message_type == atoms.XdndFinished) 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 void showMouseCursor (Cursor cursor) noexcept
{ {
@@ -2128,6 +2175,7 @@ private:
{ {
ScopedXLock xlock; ScopedXLock xlock;
resetDragAndDrop(); resetDragAndDrop();
resetExternalDragState();
// Get defaults for various properties // Get defaults for various properties
const int screen = DefaultScreen (display); 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<int> 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<int>();
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() void resetDragAndDrop()
{ {
@@ -2318,6 +2406,11 @@ private:
finishAfterDropDataReceived = false; finishAfterDropDataReceived = false;
} }
void resetExternalDragState()
{
dragState.reset();
}
void sendDragAndDropMessage (XClientMessageEvent& msg) void sendDragAndDropMessage (XClientMessageEvent& msg)
{ {
msg.type = ClientMessage; msg.type = ClientMessage;
@@ -2330,6 +2423,63 @@ private:
XSendEvent (display, dragAndDropSourceWindow, False, 0, (XEvent*) &msg); 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<int> 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) void sendDragAndDropStatus (const bool acceptDrop, Atom dropAction)
{ {
XClientMessageEvent msg = { 0 }; XClientMessageEvent msg = { 0 };
@@ -2340,11 +2490,11 @@ private:
sendDragAndDropMessage (msg); sendDragAndDropMessage (msg);
} }
void sendDragAndDropLeave()
void sendExternalDragAndDropLeave (const Window targetWindow)
{ {
XClientMessageEvent msg = { 0 }; XClientMessageEvent msg = { 0 };
msg.message_type = Atoms::get().XdndLeave; msg.message_type = Atoms::get().XdndLeave;
sendDragAndDropMessage (msg);
sendExternalDragAndDropMessage (msg, targetWindow);
} }
void sendDragAndDropFinish() void sendDragAndDropFinish()
@@ -2354,19 +2504,116 @@ private:
sendDragAndDropMessage (msg); 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<int>();
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<int>();
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) void handleDragAndDropPosition (const XClientMessageEvent& clientMsg)
{ {
if (dragAndDropSourceWindow == 0) if (dragAndDropSourceWindow == 0)
@@ -2425,7 +2672,7 @@ private:
sendDragAndDropFinish(); sendDragAndDropFinish();
resetDragAndDrop(); resetDragAndDrop();
if (dragInfoCopy.files.size() > 0 || dragInfoCopy.text.isNotEmpty())
if (! dragInfoCopy.isEmpty())
handleDragDrop (dragInfoCopy); handleDragDrop (dragInfoCopy);
} }
@@ -2499,7 +2746,7 @@ private:
for (;;) for (;;)
{ {
GetXProperty prop (evt.xany.window, evt.xselection.property, GetXProperty prop (evt.xany.window, evt.xselection.property,
dropData.getSize() / 4, 65536, true, AnyPropertyType);
dropData.getSize() / 4, 65536, false, AnyPropertyType);
if (! prop.success) if (! prop.success)
break; 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; DragInfo dragInfo;
Atom dragAndDropCurrentMimeType; Atom dragAndDropCurrentMimeType;
Window dragAndDropSourceWindow; Window dragAndDropSourceWindow;
@@ -2981,16 +3306,7 @@ void* MouseCursor::createStandardMouseCursor (MouseCursor::StandardCursorType ty
case BottomLeftCornerResizeCursor: shape = XC_bottom_left_corner; break; case BottomLeftCornerResizeCursor: shape = XC_bottom_left_corner; break;
case BottomRightCornerResizeCursor: shape = XC_bottom_right_corner; break; case BottomRightCornerResizeCursor: shape = XC_bottom_right_corner; break;
case CrosshairCursor: shape = XC_crosshair; 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: case CopyingCursor:
{ {
@@ -3033,13 +3349,31 @@ Image juce_createIconForFile (const File& file)
//============================================================================== //==============================================================================
bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& files, const bool canMoveFiles) 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 <LinuxComponentPeer*> (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; return false;
} }
bool DragAndDropContainer::performExternalDragDropOfText (const String& text) 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 <LinuxComponentPeer*> (sourceComp->getPeer()))
return lp->externalDragTextInit (text);
// This method must be called in response to a component's mouseDown or mouseDrag event!
jassertfalse;
return false; return false;
} }


Loading…
Cancel
Save