@@ -6,7 +6,29 @@ Develop | |||||
Change | Change | ||||
------ | ------ | ||||
AudioProcessor::getTailLengthSeconds can now return infinity for VST/VST3/AU/AUv3 | |||||
DragAndDropContainer::performExternalDragDropOfFiles() and ::performExternalDragDropOfText() | |||||
are now asynchronous on Windows. | |||||
Possible Issues | |||||
--------------- | |||||
Code that previously relied on these operations being synchronous and blocking until | |||||
completion will no longer work as the methods will return immediately and run | |||||
asynchronously. | |||||
Workaround | |||||
---------- | |||||
Use the callback argument that has been added to these methods to register a lambda | |||||
that will be called when the operation has been completed. | |||||
Rationale | |||||
--------- | |||||
The behaviour of these methods is now consistent across all platforms and the method | |||||
no longer blocks the message thread on Windows. | |||||
Change | |||||
------ | |||||
AudioProcessor::getTailLengthSeconds can now return infinity for VST/VST3/AU/AUv3. | |||||
Possible Issues | Possible Issues | ||||
--------------- | --------------- | ||||
@@ -20,7 +42,6 @@ a buffer. | |||||
Rationale | Rationale | ||||
--------- | --------- | ||||
Before this change there was no way for a JUCE plug-in to report an infinite tail time. | Before this change there was no way for a JUCE plug-in to report an infinite tail time. | ||||
@@ -153,42 +153,46 @@ public: | |||||
//============================================================================== | //============================================================================== | ||||
/** This performs a synchronous drag-and-drop of a set of files to some external | |||||
/** This performs an asynchronous drag-and-drop of a set of files to some external | |||||
application. | application. | ||||
You can call this function in response to a mouseDrag callback, and it will | You can call this function in response to a mouseDrag callback, and it will | ||||
block, running its own internal message loop and tracking the mouse, while it | |||||
uses a native operating system drag-and-drop operation to move or copy some | |||||
use a native operating system drag-and-drop operation to move or copy some | |||||
files to another application. | files to another application. | ||||
@param files a list of filenames to drag | @param files a list of filenames to drag | ||||
@param canMoveFiles if true, the app that receives the files is allowed to move the files to a new location | @param canMoveFiles if true, the app that receives the files is allowed to move the files to a new location | ||||
(if this is appropriate). If false, the receiver is expected to make a copy of them. | (if this is appropriate). If false, the receiver is expected to make a copy of them. | ||||
@param sourceComponent Normally, JUCE will assume that the component under the mouse is the source component | |||||
@param sourceComponent normally, JUCE will assume that the component under the mouse is the source component | |||||
of the drag, but you can use this parameter to override this. | of the drag, but you can use this parameter to override this. | ||||
@returns true if the files were successfully dropped somewhere, or false if it | |||||
was interrupted | |||||
@param callback an optional completion callback that will be called when the operation has ended. | |||||
@returns true if the drag operation was successfully started, or false if it failed for some reason | |||||
@see performExternalDragDropOfText | @see performExternalDragDropOfText | ||||
*/ | */ | ||||
static bool performExternalDragDropOfFiles (const StringArray& files, bool canMoveFiles, | static bool performExternalDragDropOfFiles (const StringArray& files, bool canMoveFiles, | ||||
Component* sourceComponent = nullptr); | |||||
Component* sourceComponent = nullptr, | |||||
std::function<void()> callback = nullptr); | |||||
/** This performs a synchronous drag-and-drop of a block of text to some external | |||||
/** This performs an asynchronous drag-and-drop of a block of text to some external | |||||
application. | application. | ||||
You can call this function in response to a mouseDrag callback, and it will | You can call this function in response to a mouseDrag callback, and it will | ||||
block, running its own internal message loop and tracking the mouse, while it | |||||
uses a native operating system drag-and-drop operation to move or copy some | |||||
use a native operating system drag-and-drop operation to move or copy some | |||||
text to another application. | text to another application. | ||||
@param text the text to copy | @param text the text to copy | ||||
@param sourceComponent Normally, JUCE will assume that the component under the mouse is the source component | @param sourceComponent Normally, JUCE will assume that the component under the mouse is the source component | ||||
of the drag, but you can use this parameter to override this. | of the drag, but you can use this parameter to override this. | ||||
@returns true if the text was successfully dropped somewhere, or false if it | |||||
was interrupted | |||||
@param callback an optional completion callback that will be called when the operation has ended. | |||||
@returns true if the drag operation was successfully started, or false if it failed for some reason | |||||
@see performExternalDragDropOfFiles | @see performExternalDragDropOfFiles | ||||
*/ | */ | ||||
static bool performExternalDragDropOfText (const String& text, Component* sourceComponent = nullptr); | |||||
static bool performExternalDragDropOfText (const String& text, Component* sourceComponent = nullptr, | |||||
std::function<void()> callback = nullptr); | |||||
protected: | protected: | ||||
/** Override this if you want to be able to perform an external drag of a set of files | /** Override this if you want to be able to perform an external drag of a set of files | ||||
@@ -50,9 +50,7 @@ public: | |||||
virtual ~DragAndDropTarget() {} | virtual ~DragAndDropTarget() {} | ||||
//============================================================================== | //============================================================================== | ||||
/** Contains details about the source of a drag-and-drop operation. | |||||
The contents of this | |||||
*/ | |||||
/** Contains details about the source of a drag-and-drop operation. */ | |||||
class JUCE_API SourceDetails | class JUCE_API SourceDetails | ||||
{ | { | ||||
public: | public: | ||||
@@ -1077,13 +1077,16 @@ void MouseCursor::showInAllWindows() const {} | |||||
//============================================================================== | //============================================================================== | ||||
bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& /*files*/, const bool /*canMove*/, | bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& /*files*/, const bool /*canMove*/, | ||||
Component* /*srcComp*/) | |||||
Component* /*srcComp*/, std::function<void()> /*callback*/) | |||||
{ | { | ||||
jassertfalse; // no such thing on Android! | |||||
return false; | return false; | ||||
} | } | ||||
bool DragAndDropContainer::performExternalDragDropOfText (const String& /*text*/, Component* /*srcComp*/) | |||||
bool DragAndDropContainer::performExternalDragDropOfText (const String& /*text*/, Component* /*srcComp*/, | |||||
std::function<void()> /*callback*/) | |||||
{ | { | ||||
jassertfalse; // no such thing on Android! | |||||
return false; | return false; | ||||
} | } | ||||
@@ -640,13 +640,13 @@ int JUCE_CALLTYPE NativeMessageBox::showYesNoBox (AlertWindow::AlertIconType /*i | |||||
} | } | ||||
//============================================================================== | //============================================================================== | ||||
bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray&, bool, Component*) | |||||
bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray&, bool, Component*, std::function<void()>) | |||||
{ | { | ||||
jassertfalse; // no such thing on iOS! | jassertfalse; // no such thing on iOS! | ||||
return false; | return false; | ||||
} | } | ||||
bool DragAndDropContainer::performExternalDragDropOfText (const String&, Component*) | |||||
bool DragAndDropContainer::performExternalDragDropOfText (const String&, Component*, std::function<void()>) | |||||
{ | { | ||||
jassertfalse; // no such thing on iOS! | jassertfalse; // no such thing on iOS! | ||||
return false; | return false; | ||||
@@ -2471,15 +2471,15 @@ public: | |||||
} | } | ||||
} | } | ||||
bool externalDragTextInit (const String& text) | |||||
bool externalDragTextInit (const String& text, std::function<void()> cb) | |||||
{ | { | ||||
if (dragState->dragging) | if (dragState->dragging) | ||||
return false; | return false; | ||||
return externalDragInit (true, text); | |||||
return externalDragInit (true, text, cb); | |||||
} | } | ||||
bool externalDragFileInit (const StringArray& files, bool /*canMoveFiles*/) | |||||
bool externalDragFileInit (const StringArray& files, bool /*canMoveFiles*/, std::function<void()> cb) | |||||
{ | { | ||||
if (dragState->dragging) | if (dragState->dragging) | ||||
return false; | return false; | ||||
@@ -2494,7 +2494,7 @@ public: | |||||
uriList.add ("file://" + f); | uriList.add ("file://" + f); | ||||
} | } | ||||
return externalDragInit (false, uriList.joinIntoString ("\r\n")); | |||||
return externalDragInit (false, uriList.joinIntoString ("\r\n"), cb); | |||||
} | } | ||||
//============================================================================== | //============================================================================== | ||||
@@ -3183,6 +3183,7 @@ private: | |||||
Rectangle<int> silentRect; | Rectangle<int> silentRect; | ||||
String textOrFiles; | String textOrFiles; | ||||
Array<Atom> allowedTypes; | Array<Atom> allowedTypes; | ||||
std::function<void()> completionCallback; | |||||
}; | }; | ||||
//============================================================================== | //============================================================================== | ||||
@@ -3624,7 +3625,7 @@ private: | |||||
return externalFindDragTargetWindow (child); | return externalFindDragTargetWindow (child); | ||||
} | } | ||||
bool externalDragInit (bool isText, const String& textOrFiles) | |||||
bool externalDragInit (bool isText, const String& textOrFiles, std::function<void()> cb) | |||||
{ | { | ||||
ScopedXLock xlock (display); | ScopedXLock xlock (display); | ||||
@@ -3632,6 +3633,7 @@ private: | |||||
dragState->isText = isText; | dragState->isText = isText; | ||||
dragState->textOrFiles = textOrFiles; | dragState->textOrFiles = textOrFiles; | ||||
dragState->targetWindow = windowH; | dragState->targetWindow = windowH; | ||||
dragState->completionCallback = cb; | |||||
const int pointerGrabMask = Button1MotionMask | ButtonReleaseMask; | const int pointerGrabMask = Button1MotionMask | ButtonReleaseMask; | ||||
@@ -3664,6 +3666,9 @@ private: | |||||
XUngrabPointer (display, CurrentTime); | XUngrabPointer (display, CurrentTime); | ||||
} | } | ||||
if (dragState->completionCallback != nullptr) | |||||
dragState->completionCallback(); | |||||
resetExternalDragState(); | resetExternalDragState(); | ||||
} | } | ||||
@@ -4262,26 +4267,27 @@ static LinuxComponentPeer* getPeerForDragEvent (Component* sourceComp) | |||||
} | } | ||||
bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& files, bool canMoveFiles, | bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& files, bool canMoveFiles, | ||||
Component* sourceComp) | |||||
Component* sourceComp, std::function<void()> callback) | |||||
{ | { | ||||
if (files.isEmpty()) | if (files.isEmpty()) | ||||
return false; | return false; | ||||
if (auto* lp = getPeerForDragEvent (sourceComp)) | if (auto* lp = getPeerForDragEvent (sourceComp)) | ||||
return lp->externalDragFileInit (files, canMoveFiles); | |||||
return lp->externalDragFileInit (files, canMoveFiles, callback); | |||||
// This method must be called in response to a component's mouseDown or mouseDrag event! | // This method must be called in response to a component's mouseDown or mouseDrag event! | ||||
jassertfalse; | jassertfalse; | ||||
return false; | return false; | ||||
} | } | ||||
bool DragAndDropContainer::performExternalDragDropOfText (const String& text, Component* sourceComp) | |||||
bool DragAndDropContainer::performExternalDragDropOfText (const String& text, Component* sourceComp, | |||||
std::function<void()> callback) | |||||
{ | { | ||||
if (text.isEmpty()) | if (text.isEmpty()) | ||||
return false; | return false; | ||||
if (auto* lp = getPeerForDragEvent (sourceComp)) | if (auto* lp = getPeerForDragEvent (sourceComp)) | ||||
return lp->externalDragTextInit (text); | |||||
return lp->externalDragTextInit (text, callback); | |||||
// This method must be called in response to a component's mouseDown or mouseDrag event! | // This method must be called in response to a component's mouseDown or mouseDrag event! | ||||
jassertfalse; | jassertfalse; | ||||
@@ -177,14 +177,22 @@ static NSView* getNSViewForDragEvent (Component* sourceComp) | |||||
return nil; | return nil; | ||||
} | } | ||||
struct TextDragDataProviderClass : public ObjCClass<NSObject> | |||||
struct NSDraggingSourceHelper : public ObjCClass<NSObject<NSDraggingSource>> | |||||
{ | { | ||||
TextDragDataProviderClass() : ObjCClass<NSObject> ("JUCE_NSTextDragDataProvider_") | |||||
NSDraggingSourceHelper() : ObjCClass<NSObject<NSDraggingSource>> ("JUCENSDraggingSourceHelper_") | |||||
{ | { | ||||
addIvar<std::function<void()>*> ("callback"); | |||||
addIvar<String*> ("text"); | addIvar<String*> ("text"); | ||||
addIvar<NSDragOperation*> ("operation"); | |||||
addMethod (@selector (dealloc), dealloc, "v@:"); | addMethod (@selector (dealloc), dealloc, "v@:"); | ||||
addMethod (@selector (pasteboard:item:provideDataForType:), provideDataForType, "v@:@@@"); | addMethod (@selector (pasteboard:item:provideDataForType:), provideDataForType, "v@:@@@"); | ||||
addMethod (@selector (draggingSession:sourceOperationMaskForDraggingContext:), sourceOperationMaskForDraggingContext, "c@:@@"); | |||||
addMethod (@selector (draggingSession:endedAtPoint:operation:), draggingSessionEnded, "v@:@@@"); | |||||
addProtocol (@protocol (NSPasteboardItemDataProvider)); | addProtocol (@protocol (NSPasteboardItemDataProvider)); | ||||
registerClass(); | registerClass(); | ||||
} | } | ||||
@@ -193,10 +201,23 @@ struct TextDragDataProviderClass : public ObjCClass<NSObject> | |||||
object_setInstanceVariable (self, "text", new String (text)); | object_setInstanceVariable (self, "text", new String (text)); | ||||
} | } | ||||
static void setCompletionCallback (id self, std::function<void()> cb) | |||||
{ | |||||
object_setInstanceVariable (self, "callback", new std::function<void()> (cb)); | |||||
} | |||||
static void setDragOperation (id self, NSDragOperation op) | |||||
{ | |||||
object_setInstanceVariable (self, "operation", new NSDragOperation (op)); | |||||
} | |||||
private: | private: | ||||
static void dealloc (id self, SEL) | static void dealloc (id self, SEL) | ||||
{ | { | ||||
delete getIvar<String*> (self, "text"); | delete getIvar<String*> (self, "text"); | ||||
delete getIvar<std::function<void()>*> (self, "callback"); | |||||
delete getIvar<NSDragOperation*> (self, "operation"); | |||||
sendSuperclassMessage (self, @selector (dealloc)); | sendSuperclassMessage (self, @selector (dealloc)); | ||||
} | } | ||||
@@ -207,9 +228,23 @@ private: | |||||
[sender setData: [juceStringToNS (*text) dataUsingEncoding: NSUTF8StringEncoding] | [sender setData: [juceStringToNS (*text) dataUsingEncoding: NSUTF8StringEncoding] | ||||
forType: NSPasteboardTypeString]; | forType: NSPasteboardTypeString]; | ||||
} | } | ||||
static NSDragOperation sourceOperationMaskForDraggingContext (id self, SEL, NSDraggingSession*, NSDraggingContext) | |||||
{ | |||||
return *getIvar<NSDragOperation*> (self, "operation"); | |||||
} | |||||
static void draggingSessionEnded (id self, SEL, NSDraggingSession*, NSPoint, NSDragOperation) | |||||
{ | |||||
if (auto* cb = getIvar<std::function<void()>*> (self, "callback")) | |||||
cb->operator()(); | |||||
} | |||||
}; | }; | ||||
bool DragAndDropContainer::performExternalDragDropOfText (const String& text, Component* sourceComponent) | |||||
static NSDraggingSourceHelper draggingSourceHelper; | |||||
bool DragAndDropContainer::performExternalDragDropOfText (const String& text, Component* sourceComponent, | |||||
std::function<void()> callback) | |||||
{ | { | ||||
if (text.isEmpty()) | if (text.isEmpty()) | ||||
return false; | return false; | ||||
@@ -220,12 +255,15 @@ bool DragAndDropContainer::performExternalDragDropOfText (const String& text, Co | |||||
{ | { | ||||
if (auto* event = [[view window] currentEvent]) | if (auto* event = [[view window] currentEvent]) | ||||
{ | { | ||||
static TextDragDataProviderClass dataProviderClass; | |||||
id delegate = [dataProviderClass.createInstance() init]; | |||||
TextDragDataProviderClass::setText (delegate, text); | |||||
id helper = [draggingSourceHelper.createInstance() init]; | |||||
NSDraggingSourceHelper::setText (helper, text); | |||||
NSDraggingSourceHelper::setDragOperation (helper, NSDragOperationCopy); | |||||
if (callback != nullptr) | |||||
NSDraggingSourceHelper::setCompletionCallback (helper, callback); | |||||
auto* pasteboardItem = [[NSPasteboardItem new] autorelease]; | auto* pasteboardItem = [[NSPasteboardItem new] autorelease]; | ||||
[pasteboardItem setDataProvider: delegate | |||||
[pasteboardItem setDataProvider: helper | |||||
forTypes: [NSArray arrayWithObjects: NSPasteboardTypeString, nil]]; | forTypes: [NSArray arrayWithObjects: NSPasteboardTypeString, nil]]; | ||||
auto* dragItem = [[[NSDraggingItem alloc] initWithPasteboardWriter: pasteboardItem] autorelease]; | auto* dragItem = [[[NSDraggingItem alloc] initWithPasteboardWriter: pasteboardItem] autorelease]; | ||||
@@ -233,13 +271,15 @@ bool DragAndDropContainer::performExternalDragDropOfText (const String& text, Co | |||||
NSImage* image = [[NSWorkspace sharedWorkspace] iconForFile: nsEmptyString()]; | NSImage* image = [[NSWorkspace sharedWorkspace] iconForFile: nsEmptyString()]; | ||||
[dragItem setDraggingFrame: getDragRect (view, event) contents: image]; | [dragItem setDraggingFrame: getDragRect (view, event) contents: image]; | ||||
auto* draggingSession = [view beginDraggingSessionWithItems: [NSArray arrayWithObject: dragItem] | |||||
event: event | |||||
source: delegate]; | |||||
if (auto* session = [view beginDraggingSessionWithItems: [NSArray arrayWithObject: dragItem] | |||||
event: event | |||||
source: helper]) | |||||
{ | |||||
session.animatesToStartingPositionsOnCancelOrFail = YES; | |||||
session.draggingFormation = NSDraggingFormationNone; | |||||
draggingSession.animatesToStartingPositionsOnCancelOrFail = YES; | |||||
draggingSession.draggingFormation = NSDraggingFormationNone; | |||||
return true; | |||||
return true; | |||||
} | |||||
} | } | ||||
} | } | ||||
} | } | ||||
@@ -247,24 +287,8 @@ bool DragAndDropContainer::performExternalDragDropOfText (const String& text, Co | |||||
return false; | return false; | ||||
} | } | ||||
struct NSDraggingSourceHelper : public ObjCClass<NSObject<NSDraggingSource>> | |||||
{ | |||||
NSDraggingSourceHelper() : ObjCClass<NSObject<NSDraggingSource>> ("JUCENSDraggingSourceHelper_") | |||||
{ | |||||
addMethod (@selector (draggingSession:sourceOperationMaskForDraggingContext:), sourceOperationMaskForDraggingContext, "c@:@@"); | |||||
registerClass(); | |||||
} | |||||
static NSDragOperation sourceOperationMaskForDraggingContext (id, SEL, NSDraggingSession*, NSDraggingContext) | |||||
{ | |||||
return NSDragOperationCopy; | |||||
} | |||||
}; | |||||
static NSDraggingSourceHelper draggingSourceHelper; | |||||
bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& files, bool /*canMoveFiles*/, | |||||
Component* sourceComponent) | |||||
bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& files, bool canMoveFiles, | |||||
Component* sourceComponent, std::function<void()> callback) | |||||
{ | { | ||||
if (files.isEmpty()) | if (files.isEmpty()) | ||||
return false; | return false; | ||||
@@ -296,9 +320,16 @@ bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& fi | |||||
auto* helper = [draggingSourceHelper.createInstance() autorelease]; | auto* helper = [draggingSourceHelper.createInstance() autorelease]; | ||||
return [view beginDraggingSessionWithItems: dragItems | |||||
event: event | |||||
source: helper]; | |||||
if (callback != nullptr) | |||||
NSDraggingSourceHelper::setCompletionCallback (helper, callback); | |||||
NSDraggingSourceHelper::setDragOperation (helper, canMoveFiles ? NSDragOperationMove | |||||
: NSDragOperationCopy); | |||||
if (auto* session = [view beginDraggingSessionWithItems: dragItems | |||||
event: event | |||||
source: helper]) | |||||
return true; | |||||
} | } | ||||
} | } | ||||
} | } | ||||
@@ -241,49 +241,112 @@ namespace DragAndDropHelpers | |||||
return hDrop; | return hDrop; | ||||
} | } | ||||
bool performDragDrop (FORMATETC* const format, STGMEDIUM* const medium, const DWORD whatToDo) | |||||
struct DragAndDropJob : public ThreadPoolJob | |||||
{ | { | ||||
auto source = new JuceDropSource(); | |||||
auto data = new JuceDataObject (source, format, medium); | |||||
DragAndDropJob (FORMATETC f, STGMEDIUM m, DWORD d, std::function<void()> cb) | |||||
: ThreadPoolJob ("DragAndDrop"), | |||||
format (f), medium (m), whatToDo (d), | |||||
completionCallback (cb) | |||||
{ | |||||
} | |||||
DWORD effect; | |||||
auto res = DoDragDrop (data, source, whatToDo, &effect); | |||||
JobStatus runJob() override | |||||
{ | |||||
OleInitialize (0); | |||||
data->Release(); | |||||
source->Release(); | |||||
auto source = new JuceDropSource(); | |||||
auto data = new JuceDataObject (source, &format, &medium); | |||||
return res == DRAGDROP_S_DROP; | |||||
} | |||||
DWORD effect; | |||||
DoDragDrop (data, source, whatToDo, &effect); | |||||
data->Release(); | |||||
source->Release(); | |||||
if (completionCallback != nullptr) | |||||
MessageManager::callAsync (completionCallback); | |||||
return jobHasFinished; | |||||
} | |||||
FORMATETC format; | |||||
STGMEDIUM medium; | |||||
DWORD whatToDo; | |||||
std::function<void()> completionCallback; | |||||
}; | |||||
class ThreadPoolHolder : private DeletedAtShutdown | |||||
{ | |||||
public: | |||||
ThreadPoolHolder() = default; | |||||
~ThreadPoolHolder() | |||||
{ | |||||
// Wait forever if there's a job running. The user needs to cancel the transfer | |||||
// in the GUI. | |||||
pool.removeAllJobs (true, -1); | |||||
clearSingletonInstance(); | |||||
} | |||||
juce_DeclareSingleton_SingleThreaded (ThreadPoolHolder, true) | |||||
// We need to make sure we don't do simultaneous text and file drag and drops, | |||||
// so use a pool that can only run a single job. | |||||
ThreadPool pool { 1 }; | |||||
}; | |||||
juce_ImplementSingleton_SingleThreaded (ThreadPoolHolder) | |||||
} | } | ||||
//============================================================================== | //============================================================================== | ||||
bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& files, const bool canMove, Component*) | |||||
bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& files, const bool canMove, | |||||
Component*, std::function<void()> callback) | |||||
{ | { | ||||
if (files.isEmpty()) | |||||
return false; | |||||
FORMATETC format = { CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; | FORMATETC format = { CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; | ||||
STGMEDIUM medium = { TYMED_HGLOBAL, { 0 }, 0 }; | STGMEDIUM medium = { TYMED_HGLOBAL, { 0 }, 0 }; | ||||
medium.hGlobal = DragAndDropHelpers::createHDrop (files); | medium.hGlobal = DragAndDropHelpers::createHDrop (files); | ||||
return DragAndDropHelpers::performDragDrop (&format, &medium, canMove ? (DWORD) (DROPEFFECT_COPY | DROPEFFECT_MOVE) | |||||
: (DWORD) DROPEFFECT_COPY); | |||||
auto& pool = DragAndDropHelpers::ThreadPoolHolder::getInstance()->pool; | |||||
pool.addJob (new DragAndDropHelpers::DragAndDropJob (format, medium, | |||||
canMove ? (DROPEFFECT_COPY | DROPEFFECT_MOVE) : DROPEFFECT_COPY, | |||||
callback), | |||||
true); | |||||
return true; | |||||
} | } | ||||
bool DragAndDropContainer::performExternalDragDropOfText (const String& text, Component*) | |||||
bool DragAndDropContainer::performExternalDragDropOfText (const String& text, Component*, std::function<void()> callback) | |||||
{ | { | ||||
if (text.isEmpty()) | |||||
return false; | |||||
FORMATETC format = { CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; | FORMATETC format = { CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; | ||||
STGMEDIUM medium = { TYMED_HGLOBAL, { 0 }, 0 }; | STGMEDIUM medium = { TYMED_HGLOBAL, { 0 }, 0 }; | ||||
auto numBytes = CharPointer_UTF16::getBytesRequiredFor (text.getCharPointer()); | auto numBytes = CharPointer_UTF16::getBytesRequiredFor (text.getCharPointer()); | ||||
medium.hGlobal = GlobalAlloc (GMEM_MOVEABLE | GMEM_ZEROINIT, numBytes + 2); | medium.hGlobal = GlobalAlloc (GMEM_MOVEABLE | GMEM_ZEROINIT, numBytes + 2); | ||||
WCHAR* const data = static_cast<WCHAR*> (GlobalLock (medium.hGlobal)); | |||||
auto* data = static_cast<WCHAR*> (GlobalLock (medium.hGlobal)); | |||||
text.copyToUTF16 (data, numBytes); | |||||
text.copyToUTF16 (data, numBytes + 2); | |||||
format.cfFormat = CF_UNICODETEXT; | format.cfFormat = CF_UNICODETEXT; | ||||
GlobalUnlock (medium.hGlobal); | GlobalUnlock (medium.hGlobal); | ||||
return DragAndDropHelpers::performDragDrop (&format, &medium, DROPEFFECT_COPY | DROPEFFECT_MOVE); | |||||
auto& pool = DragAndDropHelpers::ThreadPoolHolder::getInstance()->pool; | |||||
pool.addJob (new DragAndDropHelpers::DragAndDropJob (format, | |||||
medium, | |||||
DROPEFFECT_COPY | DROPEFFECT_MOVE, | |||||
callback), | |||||
true); | |||||
return true; | |||||
} | } | ||||
} // namespace juce | } // namespace juce |