From 60e1be176ebd2e93357b6c77c3a975d793d5b73a Mon Sep 17 00:00:00 2001 From: jules Date: Wed, 26 Sep 2007 14:43:50 +0000 Subject: [PATCH] added FileDragAndDropTarget class and rewrote all the drag handling code --- .../juce_linux_Threads.cpp | 2 +- .../juce_linux_Windowing.cpp | 162 ++-- build/macosx/Juce.xcodeproj/project.pbxproj | 4 + .../juce_mac_Threads.cpp | 772 +++++++++--------- .../juce_mac_Windowing.cpp | 292 ++++--- .../juce_win32_DirectSound.cpp | 2 +- .../juce_win32_Messaging.cpp | 2 +- .../juce_win32_Windowing.cpp | 315 ++++--- build/win32/vc8/JUCE.vcproj | 4 + docs/JUCE changelist.txt | 1 + .../src/host/MainHostWindow.cpp | 45 +- .../src/host/MainHostWindow.h | 10 +- .../src/plugins/juce_KnownPluginList.cpp | 12 +- .../src/plugins/juce_PluginListComponent.cpp | 9 +- .../src/plugins/juce_PluginListComponent.h | 4 +- .../plugins/vst/juce_VSTPluginInstance.cpp | 8 +- .../audio plugins/demo/src/DemoJuceFilter.cpp | 2 +- extras/juce demo/src/BinaryData.cpp | 1 - extras/juce demo/src/binarydata/AudioDemo.cpp | 2 +- extras/juce demo/src/demos/AudioDemo.cpp | 2 +- extras/the jucer/src/BinaryData.cpp | 1 - .../src/ui/jucer_ComponentLayoutEditor.cpp | 14 +- .../src/ui/jucer_ComponentLayoutEditor.h | 5 +- extras/the jucer/src/ui/jucer_MainWindow.cpp | 16 +- extras/the jucer/src/ui/jucer_MainWindow.h | 7 +- .../src/ui/jucer_PaintRoutineEditor.cpp | 19 +- .../src/ui/jucer_PaintRoutineEditor.h | 6 +- src/juce_app_includes.h | 3 + .../audio/devices/juce_AudioDeviceManager.h | 4 +- .../audio/processors/juce_AudioProcessor.cpp | 6 +- .../gui/components/controls/juce_Slider.cpp | 2 +- .../juce_FileSearchPathListComponent.cpp | 10 +- .../juce_FileSearchPathListComponent.h | 6 +- .../filebrowser/juce_FilenameComponent.cpp | 32 +- .../filebrowser/juce_FilenameComponent.h | 14 +- .../gui/components/juce_Component.cpp | 32 - .../gui/components/juce_Component.h | 24 +- .../mouse/juce_DragAndDropContainer.cpp | 16 + .../components/mouse/juce_DragAndDropTarget.h | 9 +- .../components/special/juce_OpenGLComponent.h | 12 +- .../components/windows/juce_AlertWindow.cpp | 2 +- .../components/windows/juce_ComponentPeer.cpp | 109 ++- .../components/windows/juce_ComponentPeer.h | 9 +- src/juce_core/io/files/juce_File.cpp | 2 +- 44 files changed, 1182 insertions(+), 829 deletions(-) diff --git a/build/linux/platform_specific_code/juce_linux_Threads.cpp b/build/linux/platform_specific_code/juce_linux_Threads.cpp index e4c3bc9911..3366a62b12 100644 --- a/build/linux/platform_specific_code/juce_linux_Threads.cpp +++ b/build/linux/platform_specific_code/juce_linux_Threads.cpp @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include "../../../src/juce_core/basics/juce_StandardHeader.h" BEGIN_JUCE_NAMESPACE diff --git a/build/linux/platform_specific_code/juce_linux_Windowing.cpp b/build/linux/platform_specific_code/juce_linux_Windowing.cpp index b22ffd1b95..f78ef6fac5 100644 --- a/build/linux/platform_specific_code/juce_linux_Windowing.cpp +++ b/build/linux/platform_specific_code/juce_linux_Windowing.cpp @@ -1108,8 +1108,8 @@ public: data[index++] = newIcon.getPixelAt (x, y).getARGB(); XChangeProperty (display, windowH, - XInternAtom (display, "_NET_WM_ICON", False), - XA_CARDINAL, 32, PropModeReplace, + XInternAtom (display, "_NET_WM_ICON", False), + XA_CARDINAL, 32, PropModeReplace, (unsigned char*) data, dataSize); XSync (display, False); @@ -1542,7 +1542,7 @@ public: case ClientMessage: { - XClientMessageEvent* clientMsg = (XClientMessageEvent*) &event->xclient; + const XClientMessageEvent* const clientMsg = (const XClientMessageEvent*) &event->xclient; if (clientMsg->message_type == wm_Protocols && clientMsg->format == 32) { @@ -1592,14 +1592,14 @@ public: break; } - case SelectionClear: - case SelectionRequest: - break; - case SelectionNotify: handleDragAndDropSelection (event); break; + case SelectionClear: + case SelectionRequest: + break; + default: break; } @@ -2219,6 +2219,7 @@ private: //============================================================================== void resetDragAndDrop() { + dragAndDropFiles.clear(); lastDropX = lastDropY = -1; dragAndDropCurrentMimeType = 0; dragAndDropSourceWindow = 0; @@ -2242,8 +2243,6 @@ private: zerostruct (msg); msg.message_type = XA_XdndStatus; msg.data.l[1] = (acceptDrop ? 1 : 0) | 2; // 2 indicates that we want to receive position messages - //msg.data.l[2] = (0 << 16) + 0; - //msg.data.l[3] = (0 << 16) + 0; msg.data.l[4] = dropAction; sendDragAndDropMessage (msg); @@ -2270,7 +2269,11 @@ private: if ((clientMsg->data.l[1] & 1) == 0) { sendDragAndDropLeave(); - return; + + if (dragAndDropFiles.size() > 0) + handleFileDragExit (dragAndDropFiles); + + dragAndDropFiles.clear(); } } @@ -2303,83 +2306,33 @@ private: } sendDragAndDropStatus (true, targetAction); + + if (dragAndDropFiles.size() == 0) + updateDraggedFileList (clientMsg); + + if (dragAndDropFiles.size() > 0) + handleFileDragMove (dragAndDropFiles, dropX, dropY); } } void handleDragAndDropDrop (const XClientMessageEvent* const clientMsg) { - if (dragAndDropSourceWindow != None - && dragAndDropCurrentMimeType != 0) - { - dragAndDropTimestamp = clientMsg->data.l[2]; - - XConvertSelection (display, - XA_XdndSelection, - dragAndDropCurrentMimeType, - XA_JXSelectionWindowProperty, - windowH, - dragAndDropTimestamp); - } - } - - void handleDragAndDropSelection (const XEvent* const evt) - { - StringArray files; - - if (evt->xselection.property != 0) - { - StringArray lines; - - { - MemoryBlock dropData; - - for (;;) - { - Atom actual; - uint8* data = 0; - unsigned long count = 0, remaining = 0; - int format = 0; - - if (XGetWindowProperty (display, evt->xany.window, evt->xselection.property, - dropData.getSize() / 4, 65536, 1, AnyPropertyType, &actual, - &format, &count, &remaining, &data) == Success) - { - dropData.append (data, count * format / 8); - XFree (data); - - if (remaining == 0) - break; - } - else - { - XFree (data); - break; - } - } - - lines.addLines (dropData.toString()); - } - - for (int i = 0; i < lines.size(); ++i) - { - const String filename (URL::removeEscapeChars (lines[i].fromFirstOccurrenceOf (T("file://"), false, true))); - - if (filename.isNotEmpty()) - files.add (filename); - } - } + if (dragAndDropFiles.size() == 0) + updateDraggedFileList (clientMsg); + const StringArray files (dragAndDropFiles); const int lastX = lastDropX, lastY = lastDropY; sendDragAndDropFinish(); resetDragAndDrop(); if (files.size() > 0) - handleFilesDropped (lastX, lastY, files); + handleFileDragDrop (files, lastX, lastY); } void handleDragAndDropEnter (const XClientMessageEvent* const clientMsg) { + dragAndDropFiles.clear(); srcMimeTypeAtomList.clear(); dragAndDropCurrentMimeType = 0; @@ -2432,8 +2385,75 @@ private: for (int j = 0; j < numElementsInArray (allowedMimeTypeAtoms); ++j) if (srcMimeTypeAtomList[i] == allowedMimeTypeAtoms[j]) dragAndDropCurrentMimeType = allowedMimeTypeAtoms[j]; + + handleDragAndDropPosition (clientMsg); } + void handleDragAndDropSelection (const XEvent* const evt) + { + dragAndDropFiles.clear(); + + if (evt->xselection.property != 0) + { + StringArray lines; + + { + MemoryBlock dropData; + + for (;;) + { + Atom actual; + uint8* data = 0; + unsigned long count = 0, remaining = 0; + int format = 0; + + if (XGetWindowProperty (display, evt->xany.window, evt->xselection.property, + dropData.getSize() / 4, 65536, 1, AnyPropertyType, &actual, + &format, &count, &remaining, &data) == Success) + { + dropData.append (data, count * format / 8); + XFree (data); + + if (remaining == 0) + break; + } + else + { + XFree (data); + break; + } + } + + lines.addLines (dropData.toString()); + } + + for (int i = 0; i < lines.size(); ++i) + dragAndDropFiles.add (URL::removeEscapeChars (lines[i].fromFirstOccurrenceOf (T("file://"), false, true))); + + dragAndDropFiles.trim(); + dragAndDropFiles.removeEmptyStrings(); + } + } + + void updateDraggedFileList (const XClientMessageEvent* const clientMsg) + { + dragAndDropFiles.clear(); + + if (dragAndDropSourceWindow != None + && dragAndDropCurrentMimeType != 0) + { + dragAndDropTimestamp = clientMsg->data.l[2]; + + XConvertSelection (display, + XA_XdndSelection, + dragAndDropCurrentMimeType, + XA_JXSelectionWindowProperty, + windowH, + dragAndDropTimestamp); + } + } + + StringArray dragAndDropFiles; int dragAndDropTimestamp, lastDropX, lastDropY; Atom XA_OtherMime, dragAndDropCurrentMimeType; diff --git a/build/macosx/Juce.xcodeproj/project.pbxproj b/build/macosx/Juce.xcodeproj/project.pbxproj index 137b07c608..e28fcfd084 100644 --- a/build/macosx/Juce.xcodeproj/project.pbxproj +++ b/build/macosx/Juce.xcodeproj/project.pbxproj @@ -654,6 +654,7 @@ 84FC31BB09B74A5C00B75141 /* juce_WildcardFileFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 84FC31A909B74A5C00B75141 /* juce_WildcardFileFilter.h */; }; 84FC31BE09B74A7700B75141 /* juce_BorderSize.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84FC31BC09B74A7700B75141 /* juce_BorderSize.cpp */; }; 84FC31BF09B74A7700B75141 /* juce_BorderSize.h in Headers */ = {isa = PBXBuildFile; fileRef = 84FC31BD09B74A7700B75141 /* juce_BorderSize.h */; }; + 84FED3C90CAA96DA00003997 /* juce_FileDragAndDropTarget.h in Headers */ = {isa = PBXBuildFile; fileRef = 84FED3C80CAA96DA00003997 /* juce_FileDragAndDropTarget.h */; }; 84FFAF2B0C6C8F2B009F6E72 /* juce_FileSearchPathListComponent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84FFAF290C6C8F2B009F6E72 /* juce_FileSearchPathListComponent.cpp */; }; 84FFAF2C0C6C8F2B009F6E72 /* juce_FileSearchPathListComponent.h in Headers */ = {isa = PBXBuildFile; fileRef = 84FFAF2A0C6C8F2B009F6E72 /* juce_FileSearchPathListComponent.h */; }; /* End PBXBuildFile section */ @@ -1309,6 +1310,7 @@ 84FC31A909B74A5C00B75141 /* juce_WildcardFileFilter.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = juce_WildcardFileFilter.h; path = filebrowser/juce_WildcardFileFilter.h; sourceTree = ""; }; 84FC31BC09B74A7700B75141 /* juce_BorderSize.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = juce_BorderSize.cpp; sourceTree = ""; }; 84FC31BD09B74A7700B75141 /* juce_BorderSize.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = juce_BorderSize.h; sourceTree = ""; }; + 84FED3C80CAA96DA00003997 /* juce_FileDragAndDropTarget.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = juce_FileDragAndDropTarget.h; sourceTree = ""; }; 84FFAF290C6C8F2B009F6E72 /* juce_FileSearchPathListComponent.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = juce_FileSearchPathListComponent.cpp; path = filebrowser/juce_FileSearchPathListComponent.cpp; sourceTree = ""; }; 84FFAF2A0C6C8F2B009F6E72 /* juce_FileSearchPathListComponent.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = juce_FileSearchPathListComponent.h; path = filebrowser/juce_FileSearchPathListComponent.h; sourceTree = ""; }; D2AAC046055464E500DB518D /* libjucedebug.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libjucedebug.a; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -1785,6 +1787,7 @@ 84A488D408A22E4900752A2B /* juce_DragAndDropContainer.cpp */, 84A488D508A22E4900752A2B /* juce_DragAndDropContainer.h */, 84A488D608A22E4900752A2B /* juce_DragAndDropTarget.h */, + 84FED3C80CAA96DA00003997 /* juce_FileDragAndDropTarget.h */, 84F593B009855693008153BA /* juce_LassoComponent.h */, 84A488D708A22E4900752A2B /* juce_MouseCursor.cpp */, 84A488D808A22E4900752A2B /* juce_MouseCursor.h */, @@ -2766,6 +2769,7 @@ 84BC4E2D0C8DD38C00FA249B /* juce_AudioProcessorEditor.h in Headers */, 84BC4E2E0C8DD38C00FA249B /* juce_AudioProcessorListener.h in Headers */, 84BC4E300C8DD38C00FA249B /* juce_GenericAudioProcessorEditor.h in Headers */, + 84FED3C90CAA96DA00003997 /* juce_FileDragAndDropTarget.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/build/macosx/platform_specific_code/juce_mac_Threads.cpp b/build/macosx/platform_specific_code/juce_mac_Threads.cpp index 9a589372b0..e6508daaf8 100644 --- a/build/macosx/platform_specific_code/juce_mac_Threads.cpp +++ b/build/macosx/platform_specific_code/juce_mac_Threads.cpp @@ -1,386 +1,386 @@ -/* - ============================================================================== - - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-7 by Raw Material Software ltd. - - ------------------------------------------------------------------------------ - - JUCE can be redistributed and/or modified under the terms of the - GNU General Public License, as published by the Free Software Foundation; - either version 2 of the License, or (at your option) any later version. - - JUCE is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with JUCE; if not, visit www.gnu.org/licenses or write to the - Free Software Foundation, Inc., 59 Temple Place, Suite 330, - Boston, MA 02111-1307 USA - - ------------------------------------------------------------------------------ - - If you'd like to release a closed-source product which uses JUCE, commercial - licenses are also available: visit www.rawmaterialsoftware.com/juce for - more information. - - ============================================================================== -*/ - -#include "../../../src/juce_core/basics/juce_StandardHeader.h" -#include -#include -#include -#include -#include -#include - -BEGIN_JUCE_NAMESPACE - -#include "../../../src/juce_core/threads/juce_CriticalSection.h" -#include "../../../src/juce_core/threads/juce_WaitableEvent.h" -#include "../../../src/juce_core/threads/juce_Thread.h" -#include "../../../src/juce_core/threads/juce_Process.h" -#include "../../../src/juce_core/threads/juce_InterProcessLock.h" -#include "../../../src/juce_core/misc/juce_PlatformUtilities.h" -#include "../../../src/juce_core/io/files/juce_File.h" - - -//============================================================================== -CriticalSection::CriticalSection() throw() -{ - pthread_mutexattr_t atts; - pthread_mutexattr_init (&atts); - pthread_mutexattr_settype (&atts, PTHREAD_MUTEX_RECURSIVE); - pthread_mutex_init (&internal, &atts); -} - -CriticalSection::~CriticalSection() throw() -{ - pthread_mutex_destroy (&internal); -} - -void CriticalSection::enter() const throw() -{ - pthread_mutex_lock (&internal); -} - -bool CriticalSection::tryEnter() const throw() -{ - return pthread_mutex_trylock (&internal) == 0; -} - -void CriticalSection::exit() const throw() -{ - pthread_mutex_unlock (&internal); -} - -//============================================================================== -struct EventStruct -{ - pthread_cond_t condition; - pthread_mutex_t mutex; - bool triggered; -}; - -WaitableEvent::WaitableEvent() throw() -{ - EventStruct* const es = new EventStruct(); - es->triggered = false; - - pthread_cond_init (&es->condition, 0); - pthread_mutex_init (&es->mutex, 0); - - internal = es; -} - -WaitableEvent::~WaitableEvent() throw() -{ - EventStruct* const es = (EventStruct*) internal; - - pthread_cond_destroy (&es->condition); - pthread_mutex_destroy (&es->mutex); - - delete es; -} - -bool WaitableEvent::wait (const int timeOutMillisecs) const throw() -{ - EventStruct* const es = (EventStruct*) internal; - - bool ok = true; - pthread_mutex_lock (&es->mutex); - - if (! es->triggered) - { - if (timeOutMillisecs < 0) - { - pthread_cond_wait (&es->condition, &es->mutex); - } - else - { - struct timespec time; - time.tv_sec = timeOutMillisecs / 1000; - time.tv_nsec = (timeOutMillisecs % 1000) * 1000000; - pthread_cond_timedwait_relative_np (&es->condition, &es->mutex, &time); - } - - ok = es->triggered; - } - - es->triggered = false; - - pthread_mutex_unlock (&es->mutex); - return ok; -} - -void WaitableEvent::signal() const throw() -{ - EventStruct* const es = (EventStruct*) internal; - - pthread_mutex_lock (&es->mutex); - es->triggered = true; - pthread_cond_signal (&es->condition); - pthread_mutex_unlock (&es->mutex); -} - -void WaitableEvent::reset() const throw() -{ - EventStruct* const es = (EventStruct*) internal; - - pthread_mutex_lock (&es->mutex); - es->triggered = false; - pthread_mutex_unlock (&es->mutex); -} - -//============================================================================== -void JUCE_API juce_threadEntryPoint (void*); - -void* threadEntryProc (void* userData) throw() -{ - juce_threadEntryPoint (userData); - return 0; -} - -void* juce_createThread (void* userData) throw() -{ - pthread_t handle = 0; - - if (pthread_create (&handle, 0, threadEntryProc, userData) == 0) - { - pthread_detach (handle); - return (void*) handle; - } - - return 0; -} - -void juce_killThread (void* handle) throw() -{ - if (handle != 0) - pthread_cancel ((pthread_t) handle); -} - -void juce_setCurrentThreadName (const String& /*name*/) throw() -{ -} - -int Thread::getCurrentThreadId() throw() -{ - return (int) pthread_self(); -} - -void juce_setThreadPriority (void* handle, int priority) throw() -{ - if (handle == 0) - handle = (void*) pthread_self(); - - struct sched_param param; - int policy; - pthread_getschedparam ((pthread_t) handle, &policy, ¶m); - param.sched_priority = jlimit (1, 127, 1 + (priority * 126) / 11); - pthread_setschedparam ((pthread_t) handle, policy, ¶m); -} - -void Thread::yield() throw() -{ - sched_yield(); -} - -void Thread::setCurrentThreadAffinityMask (const uint32 affinityMask) throw() -{ - // xxx - jassertfalse -} - -void JUCE_CALLTYPE Thread::sleep (int millisecs) throw() -{ - struct timespec time; - time.tv_sec = millisecs / 1000; - time.tv_nsec = (millisecs % 1000) * 1000000; - nanosleep (&time, 0); -} - - -//============================================================================== -bool JUCE_CALLTYPE juce_isRunningUnderDebugger() throw() -{ - static char testResult = 0; - - if (testResult == 0) - { - testResult = (ptrace (PT_TRACE_ME, 0, 0, 0) < 0) ? 1 : -1; - - if (testResult < 0) - ptrace (PT_DETACH, 0, (caddr_t) 1, 0); - } - - return testResult > 0; -} - -bool JUCE_CALLTYPE Process::isRunningUnderDebugger() throw() -{ - return juce_isRunningUnderDebugger(); -} - -void Process::raisePrivilege() -{ - jassertfalse -} - -void Process::lowerPrivilege() -{ - jassertfalse -} - -void Process::terminate() -{ - ExitToShell(); -} - -void Process::setPriority (ProcessPriority p) -{ - // xxx -} - -void* Process::loadDynamicLibrary (const String& name) -{ - // xxx needs to use bundles - - FSSpec fs; - if (PlatformUtilities::makeFSSpecFromPath (&fs, name)) - { - CFragConnectionID connID; - Ptr mainPtr; - Str255 errorMessage; - Str63 nm; - PlatformUtilities::copyToStr63 (nm, name); - - const OSErr err = GetDiskFragment (&fs, 0, kCFragGoesToEOF, nm, kReferenceCFrag, &connID, &mainPtr, errorMessage); - if (err == noErr) - return (void*)connID; - } - - return 0; -} - -void Process::freeDynamicLibrary (void* handle) -{ - if (handle != 0) - CloseConnection ((CFragConnectionID*)&handle); -} - -void* Process::getProcedureEntryPoint (void* h, const String& procedureName) -{ - if (h != 0) - { - CFragSymbolClass cl; - Ptr ptr; - Str255 name; - PlatformUtilities::copyToStr255 (name, procedureName); - - if (FindSymbol ((CFragConnectionID) h, name, &ptr, &cl) == noErr) - { - return ptr; - } - } - - return 0; -} - -//============================================================================== -InterProcessLock::InterProcessLock (const String& name_) throw() - : internal (0), - name (name_), - reentrancyLevel (0) -{ - const File tempDir (File::getSpecialLocation (File::tempDirectory)); - const File temp (tempDir.getChildFile (name)); - temp.create(); - - internal = (void*) open (temp.getFullPathName().toUTF8(), O_NONBLOCK | O_RDONLY); -} - -InterProcessLock::~InterProcessLock() throw() -{ - while (reentrancyLevel > 0) - this->exit(); - - close ((int) internal); -} - -bool InterProcessLock::enter (const int timeOutMillisecs) throw() -{ - if (internal == 0) - return false; - - if (reentrancyLevel != 0) - return true; - - if (timeOutMillisecs <= 0) - { - if (flock ((int) internal, - timeOutMillisecs < 0 ? LOCK_EX - : (LOCK_EX | LOCK_NB)) == 0) - { - ++reentrancyLevel; - return true; - } - } - else - { - const int64 endTime = Time::currentTimeMillis() + timeOutMillisecs; - - for (;;) - { - if (flock ((int) internal, LOCK_EX | LOCK_NB) == 0) - { - ++reentrancyLevel; - return true; - } - - if (Time::currentTimeMillis() >= endTime) - break; - - Thread::sleep (10); - } - } - - return false; -} - -void InterProcessLock::exit() throw() -{ - if (reentrancyLevel > 0 && internal != 0) - { - --reentrancyLevel; - - const int result = flock ((int) internal, LOCK_UN); - (void) result; - jassert (result == 0); - } -} - -END_JUCE_NAMESPACE +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-7 by Raw Material Software ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the + GNU General Public License, as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + JUCE is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with JUCE; if not, visit www.gnu.org/licenses or write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------------ + + If you'd like to release a closed-source product which uses JUCE, commercial + licenses are also available: visit www.rawmaterialsoftware.com/juce for + more information. + + ============================================================================== +*/ + +#include "../../../src/juce_core/basics/juce_StandardHeader.h" +#include +#include +#include +#include +#include +#include + +BEGIN_JUCE_NAMESPACE + +#include "../../../src/juce_core/threads/juce_CriticalSection.h" +#include "../../../src/juce_core/threads/juce_WaitableEvent.h" +#include "../../../src/juce_core/threads/juce_Thread.h" +#include "../../../src/juce_core/threads/juce_Process.h" +#include "../../../src/juce_core/threads/juce_InterProcessLock.h" +#include "../../../src/juce_core/misc/juce_PlatformUtilities.h" +#include "../../../src/juce_core/io/files/juce_File.h" + + +//============================================================================== +CriticalSection::CriticalSection() throw() +{ + pthread_mutexattr_t atts; + pthread_mutexattr_init (&atts); + pthread_mutexattr_settype (&atts, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init (&internal, &atts); +} + +CriticalSection::~CriticalSection() throw() +{ + pthread_mutex_destroy (&internal); +} + +void CriticalSection::enter() const throw() +{ + pthread_mutex_lock (&internal); +} + +bool CriticalSection::tryEnter() const throw() +{ + return pthread_mutex_trylock (&internal) == 0; +} + +void CriticalSection::exit() const throw() +{ + pthread_mutex_unlock (&internal); +} + +//============================================================================== +struct EventStruct +{ + pthread_cond_t condition; + pthread_mutex_t mutex; + bool triggered; +}; + +WaitableEvent::WaitableEvent() throw() +{ + EventStruct* const es = new EventStruct(); + es->triggered = false; + + pthread_cond_init (&es->condition, 0); + pthread_mutex_init (&es->mutex, 0); + + internal = es; +} + +WaitableEvent::~WaitableEvent() throw() +{ + EventStruct* const es = (EventStruct*) internal; + + pthread_cond_destroy (&es->condition); + pthread_mutex_destroy (&es->mutex); + + delete es; +} + +bool WaitableEvent::wait (const int timeOutMillisecs) const throw() +{ + EventStruct* const es = (EventStruct*) internal; + + bool ok = true; + pthread_mutex_lock (&es->mutex); + + if (! es->triggered) + { + if (timeOutMillisecs < 0) + { + pthread_cond_wait (&es->condition, &es->mutex); + } + else + { + struct timespec time; + time.tv_sec = timeOutMillisecs / 1000; + time.tv_nsec = (timeOutMillisecs % 1000) * 1000000; + pthread_cond_timedwait_relative_np (&es->condition, &es->mutex, &time); + } + + ok = es->triggered; + } + + es->triggered = false; + + pthread_mutex_unlock (&es->mutex); + return ok; +} + +void WaitableEvent::signal() const throw() +{ + EventStruct* const es = (EventStruct*) internal; + + pthread_mutex_lock (&es->mutex); + es->triggered = true; + pthread_cond_signal (&es->condition); + pthread_mutex_unlock (&es->mutex); +} + +void WaitableEvent::reset() const throw() +{ + EventStruct* const es = (EventStruct*) internal; + + pthread_mutex_lock (&es->mutex); + es->triggered = false; + pthread_mutex_unlock (&es->mutex); +} + +//============================================================================== +void JUCE_API juce_threadEntryPoint (void*); + +void* threadEntryProc (void* userData) throw() +{ + juce_threadEntryPoint (userData); + return 0; +} + +void* juce_createThread (void* userData) throw() +{ + pthread_t handle = 0; + + if (pthread_create (&handle, 0, threadEntryProc, userData) == 0) + { + pthread_detach (handle); + return (void*) handle; + } + + return 0; +} + +void juce_killThread (void* handle) throw() +{ + if (handle != 0) + pthread_cancel ((pthread_t) handle); +} + +void juce_setCurrentThreadName (const String& /*name*/) throw() +{ +} + +int Thread::getCurrentThreadId() throw() +{ + return (int) pthread_self(); +} + +void juce_setThreadPriority (void* handle, int priority) throw() +{ + if (handle == 0) + handle = (void*) pthread_self(); + + struct sched_param param; + int policy; + pthread_getschedparam ((pthread_t) handle, &policy, ¶m); + param.sched_priority = jlimit (1, 127, 1 + (priority * 126) / 11); + pthread_setschedparam ((pthread_t) handle, policy, ¶m); +} + +void Thread::yield() throw() +{ + sched_yield(); +} + +void Thread::setCurrentThreadAffinityMask (const uint32 affinityMask) throw() +{ + // xxx + jassertfalse +} + +void JUCE_CALLTYPE Thread::sleep (int millisecs) throw() +{ + struct timespec time; + time.tv_sec = millisecs / 1000; + time.tv_nsec = (millisecs % 1000) * 1000000; + nanosleep (&time, 0); +} + + +//============================================================================== +bool JUCE_CALLTYPE juce_isRunningUnderDebugger() throw() +{ + static char testResult = 0; + + if (testResult == 0) + { + testResult = (ptrace (PT_TRACE_ME, 0, 0, 0) < 0) ? 1 : -1; + + if (testResult < 0) + ptrace (PT_DETACH, 0, (caddr_t) 1, 0); + } + + return testResult > 0; +} + +bool JUCE_CALLTYPE Process::isRunningUnderDebugger() throw() +{ + return juce_isRunningUnderDebugger(); +} + +void Process::raisePrivilege() +{ + jassertfalse +} + +void Process::lowerPrivilege() +{ + jassertfalse +} + +void Process::terminate() +{ + ExitToShell(); +} + +void Process::setPriority (ProcessPriority p) +{ + // xxx +} + +void* Process::loadDynamicLibrary (const String& name) +{ + // xxx needs to use bundles + + FSSpec fs; + if (PlatformUtilities::makeFSSpecFromPath (&fs, name)) + { + CFragConnectionID connID; + Ptr mainPtr; + Str255 errorMessage; + Str63 nm; + PlatformUtilities::copyToStr63 (nm, name); + + const OSErr err = GetDiskFragment (&fs, 0, kCFragGoesToEOF, nm, kReferenceCFrag, &connID, &mainPtr, errorMessage); + if (err == noErr) + return (void*)connID; + } + + return 0; +} + +void Process::freeDynamicLibrary (void* handle) +{ + if (handle != 0) + CloseConnection ((CFragConnectionID*)&handle); +} + +void* Process::getProcedureEntryPoint (void* h, const String& procedureName) +{ + if (h != 0) + { + CFragSymbolClass cl; + Ptr ptr; + Str255 name; + PlatformUtilities::copyToStr255 (name, procedureName); + + if (FindSymbol ((CFragConnectionID) h, name, &ptr, &cl) == noErr) + { + return ptr; + } + } + + return 0; +} + +//============================================================================== +InterProcessLock::InterProcessLock (const String& name_) throw() + : internal (0), + name (name_), + reentrancyLevel (0) +{ + const File tempDir (File::getSpecialLocation (File::tempDirectory)); + const File temp (tempDir.getChildFile (name)); + temp.create(); + + internal = (void*) open (temp.getFullPathName().toUTF8(), O_NONBLOCK | O_RDONLY); +} + +InterProcessLock::~InterProcessLock() throw() +{ + while (reentrancyLevel > 0) + this->exit(); + + close ((int) internal); +} + +bool InterProcessLock::enter (const int timeOutMillisecs) throw() +{ + if (internal == 0) + return false; + + if (reentrancyLevel != 0) + return true; + + if (timeOutMillisecs <= 0) + { + if (flock ((int) internal, + timeOutMillisecs < 0 ? LOCK_EX + : (LOCK_EX | LOCK_NB)) == 0) + { + ++reentrancyLevel; + return true; + } + } + else + { + const int64 endTime = Time::currentTimeMillis() + timeOutMillisecs; + + for (;;) + { + if (flock ((int) internal, LOCK_EX | LOCK_NB) == 0) + { + ++reentrancyLevel; + return true; + } + + if (Time::currentTimeMillis() >= endTime) + break; + + Thread::sleep (10); + } + } + + return false; +} + +void InterProcessLock::exit() throw() +{ + if (reentrancyLevel > 0 && internal != 0) + { + --reentrancyLevel; + + const int result = flock ((int) internal, LOCK_UN); + (void) result; + jassert (result == 0); + } +} + +END_JUCE_NAMESPACE diff --git a/build/macosx/platform_specific_code/juce_mac_Windowing.cpp b/build/macosx/platform_specific_code/juce_mac_Windowing.cpp index d54cbb6351..212bf5457c 100644 --- a/build/macosx/platform_specific_code/juce_mac_Windowing.cpp +++ b/build/macosx/platform_specific_code/juce_mac_Windowing.cpp @@ -835,6 +835,7 @@ public: private: EventHandlerRef eventHandlerRef; bool fullScreen, isSharedWindow, isCompositingWindow; + StringArray dragAndDropFiles; //============================================================================== class RepaintManager : public Timer @@ -996,106 +997,6 @@ public: } //============================================================================== - OSStatus handleWindowClassEvent (EventRef theEvent) - { - switch (GetEventKind (theEvent)) - { - case kEventWindowBoundsChanged: - resizeViewToFitWindow(); - break; // allow other handlers in the event chain to also get a look at the events - - case kEventWindowBoundsChanging: - if ((styleFlags & (windowIsResizable | windowHasTitleBar)) == (windowIsResizable | windowHasTitleBar)) - { - UInt32 atts = 0; - GetEventParameter (theEvent, kEventParamAttributes, typeUInt32, - 0, sizeof (UInt32), 0, &atts); - - if ((atts & (kWindowBoundsChangeUserDrag | kWindowBoundsChangeUserResize)) != 0) - { - if (component->isCurrentlyBlockedByAnotherModalComponent()) - { - Component* const modal = Component::getCurrentlyModalComponent(); - if (modal != 0) - modal->inputAttemptWhenModal(); - } - - if ((atts & kWindowBoundsChangeUserResize) != 0 - && constrainer != 0 && ! isSharedWindow) - { - Rect current; - GetEventParameter (theEvent, kEventParamCurrentBounds, typeQDRectangle, - 0, sizeof (Rect), 0, ¤t); - - int x = current.left; - int y = current.top; - int w = current.right - current.left; - int h = current.bottom - current.top; - - const Rectangle currentRect (getComponent()->getBounds()); - - constrainer->checkBounds (x, y, w, h, currentRect, - Desktop::getInstance().getAllMonitorDisplayAreas().getBounds(), - y != currentRect.getY() && y + h == currentRect.getBottom(), - x != currentRect.getX() && x + w == currentRect.getRight(), - y == currentRect.getY() && y + h != currentRect.getBottom(), - x == currentRect.getX() && x + w != currentRect.getRight()); - - current.left = x; - current.top = y; - current.right = x + w; - current.bottom = y + h; - - SetEventParameter (theEvent, kEventParamCurrentBounds, typeQDRectangle, - sizeof (Rect), ¤t); - - return noErr; - } - } - } - break; - - case kEventWindowFocusAcquired: - keysCurrentlyDown.clear(); - - if ((! isSharedWindow) || HIViewSubtreeContainsFocus (viewRef)) - viewFocusGain(); - - break; // allow other handlers in the event chain to also get a look at the events - - case kEventWindowFocusRelinquish: - keysCurrentlyDown.clear(); - viewFocusLoss(); - - break; // allow other handlers in the event chain to also get a look at the events - - case kEventWindowCollapsed: - minimisedWindows.addIfNotAlreadyThere (windowRef); - handleMovedOrResized(); - break; // allow other handlers in the event chain to also get a look at the events - - case kEventWindowExpanded: - minimisedWindows.removeValue (windowRef); - handleMovedOrResized(); - break; // allow other handlers in the event chain to also get a look at the events - - case kEventWindowShown: - break; // allow other handlers in the event chain to also get a look at the events - - case kEventWindowClose: - if (isSharedWindow) - break; // break to let the OS delete the window - - handleUserClosingWindow(); - return noErr; // avoids letting the OS to delete the window, we'll do that ourselves. - - default: - break; - } - - return eventNotHandledErr; - } - OSStatus handleKeyEvent (EventRef theEvent, juce_wchar textCharacter) { updateModifiers (theEvent); @@ -1200,6 +1101,7 @@ public: return handleKeyEvent (originalEvent, (juce_wchar) uc); } + //============================================================================== OSStatus handleMouseEvent (EventHandlerCallRef callRef, EventRef theEvent) { MouseCheckTimer::getInstance()->moved (this); @@ -1318,19 +1220,57 @@ public: return noErr; } - OSStatus handleDragAndDrop (EventRef theEvent) + //============================================================================== + void doDragDropEnter (EventRef theEvent) { + updateDragAndDropFileList (theEvent); + + if (dragAndDropFiles.size() > 0) + { + int x, y; + component->getMouseXYRelative (x, y); + handleFileDragMove (dragAndDropFiles, x, y); + } + } + + void doDragDropMove (EventRef theEvent) + { + if (dragAndDropFiles.size() > 0) + { + int x, y; + component->getMouseXYRelative (x, y); + handleFileDragMove (dragAndDropFiles, x, y); + } + } + + void doDragDropExit (EventRef theEvent) + { + if (dragAndDropFiles.size() > 0) + handleFileDragExit (dragAndDropFiles); + } + + void doDragDrop (EventRef theEvent) + { + updateDragAndDropFileList (theEvent); + + if (dragAndDropFiles.size() > 0) + { + int x, y; + component->getMouseXYRelative (x, y); + handleFileDragDrop (dragAndDropFiles, x, y); + } + } + + void updateDragAndDropFileList (EventRef theEvent) + { + dragAndDropFiles.clear(); + DragRef dragRef; if (GetEventParameter (theEvent, kEventParamDragRef, typeDragRef, 0, sizeof (dragRef), 0, &dragRef) == noErr) { - int mx, my; - component->getMouseXYRelative (mx, my); - UInt16 numItems = 0; if (CountDragItems (dragRef, &numItems) == noErr) { - StringArray filenames; - for (int i = 0; i < (int) numItems; ++i) { DragItemRef ref; @@ -1354,7 +1294,7 @@ public: const String path (PlatformUtilities::makePathFromFSRef (&fsref)); if (path.isNotEmpty()) - filenames.add (path); + dragAndDropFiles.add (path); } } @@ -1363,17 +1303,13 @@ public: } } - filenames.trim(); - filenames.removeEmptyStrings(); - - if (filenames.size() > 0) - handleFilesDropped (mx, my, filenames); + dragAndDropFiles.trim(); + dragAndDropFiles.removeEmptyStrings(); } } - - return noErr; } + //============================================================================== void resizeViewToFitWindow() { HIRect r; @@ -1403,6 +1339,7 @@ public: #endif } + //============================================================================== OSStatus hiViewDraw (EventRef theEvent) { CGContextRef context = 0; @@ -1500,16 +1437,103 @@ public: return noErr; } - static pascal OSStatus handleWindowEvent (EventHandlerCallRef callRef, EventRef theEvent, void* userData) + //============================================================================== + OSStatus handleWindowClassEvent (EventRef theEvent) { - MessageManager::delayWaitCursor(); + switch (GetEventKind (theEvent)) + { + case kEventWindowBoundsChanged: + resizeViewToFitWindow(); + break; // allow other handlers in the event chain to also get a look at the events - HIViewComponentPeer* const peer = (HIViewComponentPeer*) userData; + case kEventWindowBoundsChanging: + if ((styleFlags & (windowIsResizable | windowHasTitleBar)) == (windowIsResizable | windowHasTitleBar)) + { + UInt32 atts = 0; + GetEventParameter (theEvent, kEventParamAttributes, typeUInt32, + 0, sizeof (UInt32), 0, &atts); - const MessageManagerLock messLock; + if ((atts & (kWindowBoundsChangeUserDrag | kWindowBoundsChangeUserResize)) != 0) + { + if (component->isCurrentlyBlockedByAnotherModalComponent()) + { + Component* const modal = Component::getCurrentlyModalComponent(); + if (modal != 0) + modal->inputAttemptWhenModal(); + } - if (ComponentPeer::isValidPeer (peer)) - return peer->handleWindowEventForPeer (callRef, theEvent); + if ((atts & kWindowBoundsChangeUserResize) != 0 + && constrainer != 0 && ! isSharedWindow) + { + Rect current; + GetEventParameter (theEvent, kEventParamCurrentBounds, typeQDRectangle, + 0, sizeof (Rect), 0, ¤t); + + int x = current.left; + int y = current.top; + int w = current.right - current.left; + int h = current.bottom - current.top; + + const Rectangle currentRect (getComponent()->getBounds()); + + constrainer->checkBounds (x, y, w, h, currentRect, + Desktop::getInstance().getAllMonitorDisplayAreas().getBounds(), + y != currentRect.getY() && y + h == currentRect.getBottom(), + x != currentRect.getX() && x + w == currentRect.getRight(), + y == currentRect.getY() && y + h != currentRect.getBottom(), + x == currentRect.getX() && x + w != currentRect.getRight()); + + current.left = x; + current.top = y; + current.right = x + w; + current.bottom = y + h; + + SetEventParameter (theEvent, kEventParamCurrentBounds, typeQDRectangle, + sizeof (Rect), ¤t); + + return noErr; + } + } + } + break; + + case kEventWindowFocusAcquired: + keysCurrentlyDown.clear(); + + if ((! isSharedWindow) || HIViewSubtreeContainsFocus (viewRef)) + viewFocusGain(); + + break; // allow other handlers in the event chain to also get a look at the events + + case kEventWindowFocusRelinquish: + keysCurrentlyDown.clear(); + viewFocusLoss(); + + break; // allow other handlers in the event chain to also get a look at the events + + case kEventWindowCollapsed: + minimisedWindows.addIfNotAlreadyThere (windowRef); + handleMovedOrResized(); + break; // allow other handlers in the event chain to also get a look at the events + + case kEventWindowExpanded: + minimisedWindows.removeValue (windowRef); + handleMovedOrResized(); + break; // allow other handlers in the event chain to also get a look at the events + + case kEventWindowShown: + break; // allow other handlers in the event chain to also get a look at the events + + case kEventWindowClose: + if (isSharedWindow) + break; // break to let the OS delete the window + + handleUserClosingWindow(); + return noErr; // avoids letting the OS to delete the window, we'll do that ourselves. + + default: + break; + } return eventNotHandledErr; } @@ -1579,6 +1603,21 @@ public: return eventNotHandledErr; } + static pascal OSStatus handleWindowEvent (EventHandlerCallRef callRef, EventRef theEvent, void* userData) + { + MessageManager::delayWaitCursor(); + + HIViewComponentPeer* const peer = (HIViewComponentPeer*) userData; + + const MessageManagerLock messLock; + + if (ComponentPeer::isValidPeer (peer)) + return peer->handleWindowEventForPeer (callRef, theEvent); + + return eventNotHandledErr; + } + + //============================================================================== static pascal OSStatus hiViewEventHandler (EventHandlerCallRef myHandler, EventRef theEvent, void* userData) { MessageManager::delayWaitCursor(); @@ -1674,14 +1713,22 @@ public: #endif Boolean accept = true; SetEventParameter (theEvent, kEventParamControlWouldAcceptDrop, typeBoolean, sizeof (accept), &accept); + + peer->doDragDropEnter (theEvent); return noErr; } case kEventControlDragWithin: + peer->doDragDropMove (theEvent); + return noErr; + + case kEventControlDragLeave: + peer->doDragDropExit (theEvent); return noErr; case kEventControlDragReceive: - return peer->handleDragAndDrop (theEvent); + peer->doDragDrop (theEvent); + return noErr; case kEventControlOwningWindowChanged: return peer->ownerWindowChanged (theEvent); @@ -1718,6 +1765,7 @@ public: return eventNotHandledErr; } + //============================================================================== WindowRef createNewWindow (const int windowStyleFlags) { jassert (windowRef == 0); @@ -1894,6 +1942,7 @@ public: { kEventClassControl, kEventControlSetFocusPart }, { kEventClassControl, kEventControlHitTest }, { kEventClassControl, kEventControlDragEnter }, + { kEventClassControl, kEventControlDragLeave }, { kEventClassControl, kEventControlDragWithin }, { kEventClassControl, kEventControlDragReceive }, { kEventClassControl, kEventControlOwningWindowChanged } @@ -1931,6 +1980,7 @@ public: } }; +//============================================================================== bool juce_isHIViewCreatedByJuce (HIViewRef view) { return juceHiViewClassNameCFString != 0 diff --git a/build/win32/platform_specific_code/juce_win32_DirectSound.cpp b/build/win32/platform_specific_code/juce_win32_DirectSound.cpp index 0f0123303d..00a7b5f86b 100644 --- a/build/win32/platform_specific_code/juce_win32_DirectSound.cpp +++ b/build/win32/platform_specific_code/juce_win32_DirectSound.cpp @@ -147,7 +147,7 @@ BEGIN_JUCE_NAMESPACE #include "../../../src/juce_core/basics/juce_Time.h" #include "../../../src/juce_core/containers/juce_OwnedArray.h" #include "../../../src/juce_core/text/juce_LocalisedStrings.h" -#include "../../../src/juce_appframework/application/juce_DeletedAtShutdown.h" + static const String getDSErrorMessage (HRESULT hr) { diff --git a/build/win32/platform_specific_code/juce_win32_Messaging.cpp b/build/win32/platform_specific_code/juce_win32_Messaging.cpp index faeff8c90a..c953ed5088 100644 --- a/build/win32/platform_specific_code/juce_win32_Messaging.cpp +++ b/build/win32/platform_specific_code/juce_win32_Messaging.cpp @@ -151,7 +151,7 @@ void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* call else { // If a thread has a MessageManagerLock and then tries to call this method, it'll - // deadlock because the message manager is blocked from running, and can't + // deadlock because the message manager is blocked from running, and can't // call your function.. jassert (! MessageManager::getInstance()->currentThreadHasLockedMessageManager()); diff --git a/build/win32/platform_specific_code/juce_win32_Windowing.cpp b/build/win32/platform_specific_code/juce_win32_Windowing.cpp index e637f968fc..53f4660825 100644 --- a/build/win32/platform_specific_code/juce_win32_Windowing.cpp +++ b/build/win32/platform_specific_code/juce_win32_Windowing.cpp @@ -54,7 +54,7 @@ // these are in the windows SDK, but need to be repeated here for GCC.. #ifndef GET_APPCOMMAND_LPARAM #define FAPPCOMMAND_MASK 0xF000 - #define GET_APPCOMMAND_LPARAM(lParam) ((short) (HIWORD(lParam) & ~FAPPCOMMAND_MASK)) + #define GET_APPCOMMAND_LPARAM(lParam) ((short) (HIWORD (lParam) & ~FAPPCOMMAND_MASK)) #define APPCOMMAND_MEDIA_NEXTTRACK 11 #define APPCOMMAND_MEDIA_PREVIOUSTRACK 12 #define APPCOMMAND_MEDIA_STOP 13 @@ -520,8 +520,9 @@ public: fullScreen (false), isDragging (false), isMouseOver (false), + currentWindowIcon (0), taskBarIcon (0), - currentWindowIcon (0) + dropTarget (0) { #if JUCE_ENABLE_WIN98_COMPATIBILITY juce_initialiseUnicodeWindowFunctions(); @@ -560,6 +561,12 @@ public: if (currentWindowIcon != 0) DestroyIcon (currentWindowIcon); + + if (dropTarget != 0) + { + dropTarget->Release(); + dropTarget = 0; + } } //============================================================================== @@ -945,6 +952,7 @@ private: BorderSize windowBorder; HICON currentWindowIcon; NOTIFYICONDATA* taskBarIcon; + IDropTarget* dropTarget; //============================================================================== class TemporaryImage : public Timer @@ -1153,7 +1161,10 @@ private: SetWindowLongPtr (hwnd, 8, (LONG_PTR) this); SetWindowLongPtr (hwnd, GWLP_USERDATA, improbableWindowNumber); - DragAcceptFiles (hwnd, TRUE); + if (dropTarget == 0) + dropTarget = new JuceDropTarget (this); + + RegisterDragDrop (hwnd, dropTarget); updateBorderSize(); @@ -1169,6 +1180,7 @@ private: static void* destroyWindowCallback (void* handle) { + RevokeDragDrop ((HWND) handle); DestroyWindow ((HWND) handle); return 0; } @@ -1765,41 +1777,131 @@ private: } //============================================================================== - void doDroppedFiles (HDROP hdrop) + class JuceDropTarget : public IDropTarget { - POINT p; - DragQueryPoint (hdrop, &p); - - const int numFiles = DragQueryFile (hdrop, 0xffffffff, 0, 0); - StringArray files; - - const int size = sizeof (WCHAR) * MAX_PATH * 2 + 8; - char* const name = (char*) juce_calloc (size); - - for (int i = 0; i < numFiles; ++i) + public: + JuceDropTarget (Win32ComponentPeer* const owner_) + : owner (owner_), + refCount (1) { -#if JUCE_ENABLE_WIN98_COMPATIBILITY - if (wDragQueryFileW != 0) - { - wDragQueryFileW (hdrop, i, (LPWSTR) name, MAX_PATH); - files.add ((LPWSTR) name); - } - else - { - DragQueryFile (hdrop, i, (LPSTR) name, MAX_PATH); - files.add ((LPSTR) name); - } -#else - DragQueryFileW (hdrop, i, (LPWSTR) name, MAX_PATH); - files.add ((LPWSTR) name); -#endif } - juce_free (name); - DragFinish (hdrop); + virtual ~JuceDropTarget() + { + jassert (refCount == 0); + } - handleFilesDropped (p.x, p.y, files); - } + HRESULT __stdcall QueryInterface (REFIID id, void __RPC_FAR* __RPC_FAR* result) + { + if (id == IID_IUnknown || id == IID_IDropTarget) + { + AddRef(); + *result = this; + return S_OK; + } + + *result = 0; + return E_NOINTERFACE; + } + + ULONG __stdcall AddRef() { return ++refCount; } + ULONG __stdcall Release() { jassert (refCount > 0); const int r = --refCount; if (r == 0) delete this; return r; } + + HRESULT __stdcall DragEnter (IDataObject* pDataObject, DWORD /*grfKeyState*/, POINTL mousePos, DWORD* pdwEffect) + { + updateFileList (pDataObject); + int x = mousePos.x, y = mousePos.y; + owner->globalPositionToRelative (x, y); + owner->handleFileDragMove (files, x, y); + *pdwEffect = DROPEFFECT_COPY; + return S_OK; + } + + HRESULT __stdcall DragLeave() + { + owner->handleFileDragExit (files); + return S_OK; + } + + HRESULT __stdcall DragOver (DWORD /*grfKeyState*/, POINTL mousePos, DWORD* pdwEffect) + { + int x = mousePos.x, y = mousePos.y; + owner->globalPositionToRelative (x, y); + owner->handleFileDragMove (files, x, y); + *pdwEffect = DROPEFFECT_COPY; + return S_OK; + } + + HRESULT __stdcall Drop (IDataObject* pDataObject, DWORD /*grfKeyState*/, POINTL mousePos, DWORD* pdwEffect) + { + updateFileList (pDataObject); + int x = mousePos.x, y = mousePos.y; + owner->globalPositionToRelative (x, y); + owner->handleFileDragDrop (files, x, y); + *pdwEffect = DROPEFFECT_COPY; + return S_OK; + } + + private: + Win32ComponentPeer* const owner; + int refCount; + StringArray files; + + void updateFileList (IDataObject* const pDataObject) + { + files.clear(); + + FORMATETC format = { CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; + STGMEDIUM medium = { TYMED_HGLOBAL, { 0 }, 0 }; + + if (pDataObject->GetData (&format, &medium) == S_OK) + { + const SIZE_T totalLen = GlobalSize (medium.hGlobal); + const LPDROPFILES pDropFiles = (const LPDROPFILES) GlobalLock (medium.hGlobal); + unsigned int i = 0; + + if (pDropFiles->fWide) + { + const WCHAR* const fname = (WCHAR*) (((const char*) pDropFiles) + sizeof (DROPFILES)); + + for (;;) + { + unsigned int len = 0; + while (i + len < totalLen && fname [i + len] != 0) + ++len; + + if (len == 0) + break; + + files.add (String (fname + i, len)); + i += len + 1; + } + } + else + { + const char* const fname = ((const char*) pDropFiles) + sizeof (DROPFILES); + + for (;;) + { + unsigned int len = 0; + while (i + len < totalLen && fname [i + len] != 0) + ++len; + + if (len == 0) + break; + + files.add (String (fname + i, len)); + i += len + 1; + } + } + + GlobalUnlock (medium.hGlobal); + } + } + + JuceDropTarget (const JuceDropTarget&); + const JuceDropTarget& operator= (const JuceDropTarget&); + }; void doSettingChange() { @@ -2045,10 +2147,6 @@ private: return 0; //============================================================================== - case WM_DROPFILES: - doDroppedFiles ((HDROP) wParam); - break; - case WM_TRAYNOTIFY: if (component->isCurrentlyBlockedByAnotherModalComponent()) { @@ -2693,28 +2791,10 @@ public: class JuceEnumFormatEtc : public IEnumFORMATETC { -private: - int refCount; - FORMATETC* formats; - int numFormats, index; - - static void copyFormatEtc (FORMATETC& dest, FORMATETC& source) - { - dest = source; - - if (source.ptd != 0) - { - dest.ptd = (DVTARGETDEVICE*) CoTaskMemAlloc (sizeof (DVTARGETDEVICE)); - *(dest.ptd) = *(source.ptd); - } - } - public: - JuceEnumFormatEtc (FORMATETC* const formats_, - const int numFormats_) + JuceEnumFormatEtc (const FORMATETC* const format_) : refCount (1), - formats (formats_), - numFormats (numFormats_), + format (format_), index (0) { } @@ -2745,7 +2825,7 @@ public: if (result == 0) return E_POINTER; - JuceEnumFormatEtc* const newOne = new JuceEnumFormatEtc (formats, numFormats); + JuceEnumFormatEtc* const newOne = new JuceEnumFormatEtc (format); newOne->index = index; *result = newOne; @@ -2756,25 +2836,26 @@ public: { if (pceltFetched != 0) *pceltFetched = 0; - - if (celt <= 0 || lpFormatEtc == 0 || index >= numFormats - || (pceltFetched == 0 && celt != 1)) + else if (celt != 1) return S_FALSE; - int numDone = 0; + if (index == 0 && celt > 0 && lpFormatEtc != 0) + { + copyFormatEtc (lpFormatEtc [0], *format); + ++index; - while (index < numFormats && numDone < (int) celt) - copyFormatEtc (lpFormatEtc [numDone++], formats [index++]); + if (pceltFetched != 0) + *pceltFetched = 1; - if (pceltFetched != 0) - *pceltFetched = numDone; + return S_OK; + } - return (numDone != 0) ? S_OK : S_FALSE; + return S_FALSE; } HRESULT __stdcall Skip (ULONG celt) { - if (index + (int) celt >= numFormats) + if (index + (int) celt >= 1) return S_FALSE; index += celt; @@ -2786,42 +2867,45 @@ public: index = 0; return S_OK; } + +private: + int refCount; + const FORMATETC* const format; + int index; + + static void copyFormatEtc (FORMATETC& dest, const FORMATETC& source) + { + dest = source; + + if (source.ptd != 0) + { + dest.ptd = (DVTARGETDEVICE*) CoTaskMemAlloc (sizeof (DVTARGETDEVICE)); + *(dest.ptd) = *(source.ptd); + } + } + + JuceEnumFormatEtc (const JuceEnumFormatEtc&); + const JuceEnumFormatEtc& operator= (const JuceEnumFormatEtc&); }; class JuceDataObject : public IDataObject { + JuceDropSource* const dropSource; + const FORMATETC* const format; + const STGMEDIUM* const medium; int refCount; - JuceDropSource* dropSource; - FORMATETC* formats; - STGMEDIUM* mediums; - int numFormats; - - int indexOfFormat (const FORMATETC* const f) const - { - for (int i = 0; i < numFormats; ++i) - { - if (f->tymed == formats[i].tymed - && f->cfFormat == formats[i].cfFormat - && f->dwAspect == formats[i].dwAspect) - { - return i; - } - } - - return -1; - } + JuceDataObject (const JuceDataObject&); + const JuceDataObject& operator= (const JuceDataObject&); public: JuceDataObject (JuceDropSource* const dropSource_, - FORMATETC* const formats_, - STGMEDIUM* const mediums_, - const int numFormats_) - : refCount (1), - dropSource (dropSource_), - formats (formats_), - mediums (mediums_), - numFormats (numFormats_) + const FORMATETC* const format_, + const STGMEDIUM* const medium_) + : dropSource (dropSource_), + format (format_), + medium (medium_), + refCount (1) { } @@ -2848,22 +2932,22 @@ public: HRESULT __stdcall GetData (FORMATETC __RPC_FAR* pFormatEtc, STGMEDIUM __RPC_FAR* pMedium) { - const int i = indexOfFormat (pFormatEtc); - - if (i >= 0) + if (pFormatEtc->tymed == format->tymed + && pFormatEtc->cfFormat == format->cfFormat + && pFormatEtc->dwAspect == format->dwAspect) { - pMedium->tymed = formats[i].tymed; + pMedium->tymed = format->tymed; pMedium->pUnkForRelease = 0; - if (formats[i].tymed == TYMED_HGLOBAL) + if (format->tymed == TYMED_HGLOBAL) { - const SIZE_T len = GlobalSize (mediums[i].hGlobal); - void* const src = GlobalLock (mediums[i].hGlobal); + const SIZE_T len = GlobalSize (medium->hGlobal); + void* const src = GlobalLock (medium->hGlobal); void* const dst = GlobalAlloc (GMEM_FIXED, len); memcpy (dst, src, len); - GlobalUnlock (mediums[i].hGlobal); + GlobalUnlock (medium->hGlobal); pMedium->hGlobal = dst; return S_OK; @@ -2873,12 +2957,17 @@ public: return DV_E_FORMATETC; } - HRESULT __stdcall QueryGetData (FORMATETC __RPC_FAR* result) + HRESULT __stdcall QueryGetData (FORMATETC __RPC_FAR* f) { - if (result == 0) + if (f == 0) return E_INVALIDARG; - return (indexOfFormat (result) >= 0) ? S_OK : DV_E_FORMATETC; + if (f->tymed == format->tymed + && f->cfFormat == format->cfFormat + && f->dwAspect == format->dwAspect) + return S_OK; + + return DV_E_FORMATETC; } HRESULT __stdcall GetCanonicalFormatEtc (FORMATETC __RPC_FAR*, FORMATETC __RPC_FAR* pFormatEtcOut) @@ -2894,7 +2983,7 @@ public: if (direction == DATADIR_GET) { - *result = new JuceEnumFormatEtc (formats, numFormats); + *result = new JuceEnumFormatEtc (format); return S_OK; } @@ -2974,7 +3063,7 @@ static HDROP createHDrop (const StringArray& fileNames) throw() static bool performDragDrop (FORMATETC* const format, STGMEDIUM* const medium, const DWORD whatToDo) throw() { JuceDropSource* const source = new JuceDropSource(); - JuceDataObject* const data = new JuceDataObject (source, format, medium, 1); + JuceDataObject* const data = new JuceDataObject (source, format, medium); DWORD effect; const HRESULT res = DoDragDrop (data, source, whatToDo, &effect); @@ -3043,11 +3132,11 @@ typedef int (WINAPI * PFNWGLGETSWAPINTERVALEXTPROC) (void); #define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000 #define WGL_DRAW_TO_WINDOW_ARB 0x2001 #define WGL_ACCELERATION_ARB 0x2003 -#define WGL_SWAP_METHOD_ARB 0x2007 +#define WGL_SWAP_METHOD_ARB 0x2007 #define WGL_SUPPORT_OPENGL_ARB 0x2010 -#define WGL_PIXEL_TYPE_ARB 0x2013 +#define WGL_PIXEL_TYPE_ARB 0x2013 #define WGL_DOUBLE_BUFFER_ARB 0x2011 -#define WGL_COLOR_BITS_ARB 0x2014 +#define WGL_COLOR_BITS_ARB 0x2014 #define WGL_RED_BITS_ARB 0x2015 #define WGL_GREEN_BITS_ARB 0x2017 #define WGL_BLUE_BITS_ARB 0x2019 @@ -3079,7 +3168,7 @@ static void getWglExtensions (HDC dc, StringArray& result) throw() class WindowedGLContext : public OpenGLContext { public: - WindowedGLContext (Component* const component_, + WindowedGLContext (Component* const component_, HGLRC contextToShareWith, const OpenGLPixelFormat& pixelFormat) : renderContext (0), @@ -3323,7 +3412,7 @@ public: PFNWGLGETSWAPINTERVALEXTPROC wglGetSwapIntervalEXT = 0; - if (availableExtensions.contains ("WGL_EXT_swap_control") + if (availableExtensions.contains ("WGL_EXT_swap_control") && WGL_EXT_FUNCTION_INIT (PFNWGLGETSWAPINTERVALEXTPROC, wglGetSwapIntervalEXT)) return wglGetSwapIntervalEXT(); diff --git a/build/win32/vc8/JUCE.vcproj b/build/win32/vc8/JUCE.vcproj index 34e0db01ec..5615729acd 100644 --- a/build/win32/vc8/JUCE.vcproj +++ b/build/win32/vc8/JUCE.vcproj @@ -3893,6 +3893,10 @@ RelativePath="..\..\..\src\juce_appframework\gui\components\mouse\juce_DragAndDropTarget.h" > + + diff --git a/docs/JUCE changelist.txt b/docs/JUCE changelist.txt index 633d974bee..3d1a75c89f 100644 --- a/docs/JUCE changelist.txt +++ b/docs/JUCE changelist.txt @@ -13,6 +13,7 @@ Changelist for version 1.45 - audio plugins: new methods AudioProcessor::beginParameterChangeGesture() and endParameterChangeGesture() let you tell the host when a parameter-change action starts and finishes. - audio plugins: new method AudioProcessor::updateHostDisplay() to tell the host that something about your plugin has changed and that it should refresh its display. - new class: FileSearchPathListComponent, for letting the user edit a FileSearchPath. +- new class: FileDragAndDropTarget, which replaces the old method Component::filesDropped. To use it, just make your component inherit from FileDragAndDropTarget, and it'll receive external file drops. This provides more functionality than the old method, allowing you to track the drag enter/exit/movements as well as just reacting to the drop itself. - added a critical section option to ReferenceCountedArray - refactored and added features to the Socket class, replacing it with StreamableSocket (basically the same as the original class), and DatagramSocket. - refactored the OpenGLComponent, adding new classes OpenGLPixelFormat and OpenGLContext diff --git a/extras/audio plugin host/src/host/MainHostWindow.cpp b/extras/audio plugin host/src/host/MainHostWindow.cpp index a23bf5fd52..b681c2182d 100644 --- a/extras/audio plugin host/src/host/MainHostWindow.cpp +++ b/extras/audio plugin host/src/host/MainHostWindow.cpp @@ -463,26 +463,47 @@ void MainHostWindow::showAudioSettings() graphEditor->graph.removeIllegalConnections(); } -bool MainHostWindow::filesDropped (const StringArray& files, int x, int y) +bool MainHostWindow::isInterestedInFileDrag (const StringArray&) +{ + return true; +} + +void MainHostWindow::fileDragEnter (const StringArray&, int, int) +{ +} + +void MainHostWindow::fileDragMove (const StringArray&, int, int) +{ +} + +void MainHostWindow::fileDragExit (const StringArray&) +{ +} + +void MainHostWindow::filesDropped (const StringArray& files, int x, int y) { if (files.size() == 1 && File (files[0]).hasFileExtension (filenameSuffix)) { GraphDocumentComponent* const graphEditor = getGraphEditor(); - if (graphEditor == 0) - return false; - - return graphEditor->graph.saveIfNeededAndUserAgrees() == FileBasedDocument::savedOk - && graphEditor->graph.loadFrom (File (files[0]), true); + if (graphEditor != 0 + && graphEditor->graph.saveIfNeededAndUserAgrees() == FileBasedDocument::savedOk) + { + graphEditor->graph.loadFrom (File (files[0]), true); + } } + else + { + OwnedArray typesFound; + knownPluginList.scanAndAddDragAndDroppedFiles (files, typesFound); - OwnedArray typesFound; - knownPluginList.scanAndAddDragAndDroppedFiles (files, typesFound); + GraphDocumentComponent* const graphEditor = getGraphEditor(); + if (graphEditor != 0) + relativePositionToOtherComponent (graphEditor, x, y); - for (int i = 0; i < jmin (5, typesFound.size()); ++i) - createPlugin (typesFound.getUnchecked(i), x, y); - - return typesFound.size() > 0; + for (int i = 0; i < jmin (5, typesFound.size()); ++i) + createPlugin (typesFound.getUnchecked(i), x, y); + } } GraphDocumentComponent* MainHostWindow::getGraphEditor() const diff --git a/extras/audio plugin host/src/host/MainHostWindow.h b/extras/audio plugin host/src/host/MainHostWindow.h index c17166c7a3..7f1cf39497 100644 --- a/extras/audio plugin host/src/host/MainHostWindow.h +++ b/extras/audio plugin host/src/host/MainHostWindow.h @@ -57,7 +57,8 @@ extern ApplicationCommandManager* commandManager; class MainHostWindow : public DocumentWindow, public MenuBarModel, public ApplicationCommandTarget, - public ChangeListener + public ChangeListener, + public FileDragAndDropTarget { public: //============================================================================== @@ -67,7 +68,12 @@ public: //============================================================================== void closeButtonPressed(); void changeListenerCallback (void*); - bool filesDropped (const StringArray& filenames, int x, int y); + + bool isInterestedInFileDrag (const StringArray& files); + void fileDragEnter (const StringArray& files, int, int); + void fileDragMove (const StringArray& files, int, int); + void fileDragExit (const StringArray& files); + void filesDropped (const StringArray& files, int, int); const StringArray getMenuBarNames(); const PopupMenu getMenuForIndex (int topLevelMenuIndex, const String& menuName); diff --git a/extras/audio plugin host/src/plugins/juce_KnownPluginList.cpp b/extras/audio plugin host/src/plugins/juce_KnownPluginList.cpp index b374d9e291..37c9e9dadf 100644 --- a/extras/audio plugin host/src/plugins/juce_KnownPluginList.cpp +++ b/extras/audio plugin host/src/plugins/juce_KnownPluginList.cpp @@ -120,14 +120,12 @@ bool KnownPluginList::scanAndAddFile (const File& possiblePluginFile, { const PluginDescription* const d = types.getUnchecked(i); - if (d->file == possiblePluginFile - && d->lastFileModTime != possiblePluginFile.getLastModificationTime()) + if (d->file == possiblePluginFile) { - needsRescanning = true; - } - else - { - typesFound.add (new PluginDescription (*d)); + if (d->lastFileModTime != possiblePluginFile.getLastModificationTime()) + needsRescanning = true; + else + typesFound.add (new PluginDescription (*d)); } } diff --git a/extras/audio plugin host/src/plugins/juce_PluginListComponent.cpp b/extras/audio plugin host/src/plugins/juce_PluginListComponent.cpp index 55301c774f..e188e630de 100644 --- a/extras/audio plugin host/src/plugins/juce_PluginListComponent.cpp +++ b/extras/audio plugin host/src/plugins/juce_PluginListComponent.cpp @@ -201,12 +201,15 @@ void PluginListComponent::buttonClicked (Button* b) } } -bool PluginListComponent::filesDropped (const StringArray& files, int, int) +bool PluginListComponent::isInterestedInFileDrag (const StringArray& files) +{ + return true; +} + +void PluginListComponent::filesDropped (const StringArray& files, int, int) { OwnedArray typesFound; list.scanAndAddDragAndDroppedFiles (files, typesFound); - - return typesFound.size() > 0; } void PluginListComponent::scanFor (AudioPluginFormat* format) diff --git a/extras/audio plugin host/src/plugins/juce_PluginListComponent.h b/extras/audio plugin host/src/plugins/juce_PluginListComponent.h index b6a810075d..9a952ae914 100644 --- a/extras/audio plugin host/src/plugins/juce_PluginListComponent.h +++ b/extras/audio plugin host/src/plugins/juce_PluginListComponent.h @@ -66,7 +66,9 @@ public: /** @internal */ void resized(); /** @internal */ - bool filesDropped (const StringArray& files, int x, int y); + bool isInterestedInFileDrag (const StringArray& files); + /** @internal */ + void filesDropped (const StringArray& files, int, int); /** @internal */ int getNumRows(); /** @internal */ diff --git a/extras/audio plugin host/src/plugins/vst/juce_VSTPluginInstance.cpp b/extras/audio plugin host/src/plugins/vst/juce_VSTPluginInstance.cpp index c9773f9cf4..af4f0ce8bc 100644 --- a/extras/audio plugin host/src/plugins/vst/juce_VSTPluginInstance.cpp +++ b/extras/audio plugin host/src/plugins/vst/juce_VSTPluginInstance.cpp @@ -53,7 +53,7 @@ #include "../../../../../juce.h" #include "juce_VSTPluginInstance.h" -#if JUCE_LINUX +#if JUCE_LINUX #define Font JUCE_NAMESPACE::Font #define KeyPress JUCE_NAMESPACE::KeyPress #define Drawable JUCE_NAMESPACE::Drawable @@ -225,7 +225,7 @@ static int getPropertyFromXWindow (Window handle, Atom atom) XSetErrorHandler (oldErrorHandler); - return (userCount == 1 && ! xErrorTriggered) ? *(int*) data + return (userCount == 1 && ! xErrorTriggered) ? *(int*) data : 0; } @@ -1452,7 +1452,7 @@ private: h = 150; } } - + if (pluginWindow != 0) XMapRaised (display, pluginWindow); #endif @@ -1611,7 +1611,7 @@ private: } else if (pluginWindow != 0) { - // if the plugin has a window, then send the event to the window so that + // if the plugin has a window, then send the event to the window so that // its message thread will pick it up.. XSendEvent (display, pluginWindow, False, 0L, event); XFlush (display); diff --git a/extras/audio plugins/demo/src/DemoJuceFilter.cpp b/extras/audio plugins/demo/src/DemoJuceFilter.cpp index 2d108be33f..c7bc07de0a 100644 --- a/extras/audio plugins/demo/src/DemoJuceFilter.cpp +++ b/extras/audio plugins/demo/src/DemoJuceFilter.cpp @@ -36,7 +36,7 @@ //============================================================================== /** - This function must be implemented to create a new instance of your + This function must be implemented to create a new instance of your plugin object. */ AudioProcessor* JUCE_CALLTYPE createPluginFilter() diff --git a/extras/juce demo/src/BinaryData.cpp b/extras/juce demo/src/BinaryData.cpp index aada0452c0..2f58e54033 100644 --- a/extras/juce demo/src/BinaryData.cpp +++ b/extras/juce demo/src/BinaryData.cpp @@ -7961,4 +7961,3 @@ static const unsigned char temp17[] = {47,42,13,10,32,32,61,61,61,61,61,61,61,61 110,100,77,97,110,97,103,101,114,42,32,99,111,109,109,97,110,100,77,97,110,97,103,101,114,41,13,10,123,13,10,32,32,32,32,114,101,116,117,114, 110,32,110,101,119,32,87,105,100,103,101,116,115,68,101,109,111,32,40,99,111,109,109,97,110,100,77,97,110,97,103,101,114,41,59,13,10,125,13,10,0,0}; const char* BinaryData::widgetsdemo_cpp = (const char*) temp17; - diff --git a/extras/juce demo/src/binarydata/AudioDemo.cpp b/extras/juce demo/src/binarydata/AudioDemo.cpp index 23b2d28e98..799452309d 100644 --- a/extras/juce demo/src/binarydata/AudioDemo.cpp +++ b/extras/juce demo/src/binarydata/AudioDemo.cpp @@ -577,7 +577,7 @@ public: AudioDeviceSelectorComponent audioSettingsComp (audioDeviceManager, 0, 1, 2, 2, - true, + true, false); // ...and show it in a DialogWindow... diff --git a/extras/juce demo/src/demos/AudioDemo.cpp b/extras/juce demo/src/demos/AudioDemo.cpp index 23b2d28e98..799452309d 100644 --- a/extras/juce demo/src/demos/AudioDemo.cpp +++ b/extras/juce demo/src/demos/AudioDemo.cpp @@ -577,7 +577,7 @@ public: AudioDeviceSelectorComponent audioSettingsComp (audioDeviceManager, 0, 1, 2, 2, - true, + true, false); // ...and show it in a DialogWindow... diff --git a/extras/the jucer/src/BinaryData.cpp b/extras/the jucer/src/BinaryData.cpp index c08fddd03e..1279f531a8 100644 --- a/extras/the jucer/src/BinaryData.cpp +++ b/extras/the jucer/src/BinaryData.cpp @@ -911,4 +911,3 @@ static const unsigned char temp4[] = {137,80,78,71,13,10,26,10,0,0,0,13,73,72,68 0,98,28,9,155,95,0,2,104,68,236,11,1,8,160,17,225,73,128,0,3,0,120,52,172,151,198,78,252,63,0,0,0,0,73,69,78,68,174,66, 96,130,0,0}; const char* BinaryData::prefs_misc_png = (const char*) temp4; - diff --git a/extras/the jucer/src/ui/jucer_ComponentLayoutEditor.cpp b/extras/the jucer/src/ui/jucer_ComponentLayoutEditor.cpp index 1ae6183754..b373337ec3 100644 --- a/extras/the jucer/src/ui/jucer_ComponentLayoutEditor.cpp +++ b/extras/the jucer/src/ui/jucer_ComponentLayoutEditor.cpp @@ -357,9 +357,15 @@ bool ComponentLayoutEditor::keyPressed (const KeyPress& key) return true; } -bool ComponentLayoutEditor::filesDropped (const StringArray& filenames, int x, int y) +bool ComponentLayoutEditor::isInterestedInFileDrag (const StringArray& filenames) { - File f (filenames [0]); + const File f (filenames [0]); + return f.hasFileExtension (T(".cpp")); +} + +void ComponentLayoutEditor::filesDropped (const StringArray& filenames, int x, int y) +{ + const File f (filenames [0]); if (f.hasFileExtension (T(".cpp"))) { @@ -385,12 +391,8 @@ bool ComponentLayoutEditor::filesDropped (const StringArray& filenames, int x, i } layout.getDocument()->getUndoManager().beginNewTransaction(); - - return true; } } - - return false; } ComponentOverlayComponent* ComponentLayoutEditor::getOverlayCompFor (Component* compToFind) const diff --git a/extras/the jucer/src/ui/jucer_ComponentLayoutEditor.h b/extras/the jucer/src/ui/jucer_ComponentLayoutEditor.h index 5cc15c8529..f17a78ad5b 100644 --- a/extras/the jucer/src/ui/jucer_ComponentLayoutEditor.h +++ b/extras/the jucer/src/ui/jucer_ComponentLayoutEditor.h @@ -42,6 +42,7 @@ */ class ComponentLayoutEditor : public Component, public ChangeListener, + public FileDragAndDropTarget, public LassoSource { public: @@ -59,7 +60,9 @@ public: void mouseDrag (const MouseEvent& e); void mouseUp (const MouseEvent& e); bool keyPressed (const KeyPress& key); - bool filesDropped (const StringArray& filenames, int x, int y); + + bool isInterestedInFileDrag (const StringArray& files); + void filesDropped (const StringArray& filenames, int x, int y); ComponentLayout& getLayout() const throw() { return layout; } diff --git a/extras/the jucer/src/ui/jucer_MainWindow.cpp b/extras/the jucer/src/ui/jucer_MainWindow.cpp index dcd8724726..f25f771d9a 100644 --- a/extras/the jucer/src/ui/jucer_MainWindow.cpp +++ b/extras/the jucer/src/ui/jucer_MainWindow.cpp @@ -201,20 +201,30 @@ bool MainWindow::openFile (const File& file) return newDoc != 0; } -bool MainWindow::filesDropped (const StringArray& filenames, int mouseX, int mouseY) +bool MainWindow::isInterestedInFileDrag (const StringArray& filenames) { for (int i = filenames.size(); --i >= 0;) { const File f (filenames[i]); if (f.hasFileExtension (T(".cpp"))) - if (openFile (f)) - return true; + return true; } return false; } +void MainWindow::filesDropped (const StringArray& filenames, int mouseX, int mouseY) +{ + for (int i = filenames.size(); --i >= 0;) + { + const File f (filenames[i]); + + if (f.hasFileExtension (T(".cpp")) && openFile (f)) + break; + } +} + void MainWindow::activeWindowStatusChanged() { DocumentWindow::activeWindowStatusChanged(); diff --git a/extras/the jucer/src/ui/jucer_MainWindow.h b/extras/the jucer/src/ui/jucer_MainWindow.h index 639900e326..f2ed5808ab 100644 --- a/extras/the jucer/src/ui/jucer_MainWindow.h +++ b/extras/the jucer/src/ui/jucer_MainWindow.h @@ -42,7 +42,8 @@ class MultiDocHolder; */ class MainWindow : public DocumentWindow, public MenuBarModel, - public ApplicationCommandTarget + public ApplicationCommandTarget, + public FileDragAndDropTarget { public: //============================================================================== @@ -59,7 +60,9 @@ public: bool closeDocument (JucerDocumentHolder* designHolder); bool closeAllDocuments(); - bool filesDropped (const StringArray& filenames, int mouseX, int mouseY); + bool isInterestedInFileDrag (const StringArray& files); + void filesDropped (const StringArray& filenames, int mouseX, int mouseY); + void activeWindowStatusChanged(); //============================================================================== diff --git a/extras/the jucer/src/ui/jucer_PaintRoutineEditor.cpp b/extras/the jucer/src/ui/jucer_PaintRoutineEditor.cpp index 730fe595f1..e3fa13b810 100644 --- a/extras/the jucer/src/ui/jucer_PaintRoutineEditor.cpp +++ b/extras/the jucer/src/ui/jucer_PaintRoutineEditor.cpp @@ -286,9 +286,20 @@ SelectedItemSet & PaintRoutineEditor::getLassoSelection() return graphics.getSelectedElements(); } -bool PaintRoutineEditor::filesDropped (const StringArray& filenames, int x, int y) +bool PaintRoutineEditor::isInterestedInFileDrag (const StringArray& files) { - File f (filenames [0]); + const File f (files [0]); + + return f.hasFileExtension ("jpg") + || f.hasFileExtension ("jpeg") + || f.hasFileExtension ("png") + || f.hasFileExtension ("gif") + || f.hasFileExtension ("svg"); +} + +void PaintRoutineEditor::filesDropped (const StringArray& filenames, int x, int y) +{ + const File f (filenames [0]); if (f.existsAsFile()) { @@ -305,10 +316,6 @@ bool PaintRoutineEditor::filesDropped (const StringArray& filenames, int x, int jlimit (10, getHeight() - 10, y)); document.getUndoManager().beginNewTransaction(); - - return true; } } - - return false; } diff --git a/extras/the jucer/src/ui/jucer_PaintRoutineEditor.h b/extras/the jucer/src/ui/jucer_PaintRoutineEditor.h index 07f9deeb98..b09cd0f43a 100644 --- a/extras/the jucer/src/ui/jucer_PaintRoutineEditor.h +++ b/extras/the jucer/src/ui/jucer_PaintRoutineEditor.h @@ -43,7 +43,8 @@ class JucerDocumentHolder; */ class PaintRoutineEditor : public Component, public ChangeListener, - public LassoSource + public LassoSource , + public FileDragAndDropTarget { public: //============================================================================== @@ -68,7 +69,8 @@ public: SelectedItemSet & getLassoSelection(); - bool filesDropped (const StringArray& filenames, int x, int y); + bool isInterestedInFileDrag (const StringArray& files); + void filesDropped (const StringArray& filenames, int x, int y); const Rectangle getComponentArea() const; diff --git a/src/juce_app_includes.h b/src/juce_app_includes.h index 2c0da4bf02..e06a787d2a 100644 --- a/src/juce_app_includes.h +++ b/src/juce_app_includes.h @@ -431,6 +431,9 @@ #ifndef __JUCE_DRAGANDDROPTARGET_JUCEHEADER__ #include "juce_appframework/gui/components/mouse/juce_DragAndDropTarget.h" #endif +#ifndef __JUCE_FILEDRAGANDDROPTARGET_JUCEHEADER__ + #include "juce_appframework/gui/components/mouse/juce_FileDragAndDropTarget.h" +#endif #ifndef __JUCE_LASSOCOMPONENT_JUCEHEADER__ #include "juce_appframework/gui/components/mouse/juce_LassoComponent.h" #endif diff --git a/src/juce_appframework/audio/devices/juce_AudioDeviceManager.h b/src/juce_appframework/audio/devices/juce_AudioDeviceManager.h index d3196e124e..c8f702c9f9 100644 --- a/src/juce_appframework/audio/devices/juce_AudioDeviceManager.h +++ b/src/juce_appframework/audio/devices/juce_AudioDeviceManager.h @@ -309,7 +309,7 @@ public: The specified device will be opened automatically and can be retrieved with the getDefaultMidiOutput() method. - Pass in an empty string to deselect all devices. For the default device, you + Pass in an empty string to deselect all devices. For the default device, you can use MidiOutput::getDevices() [MidiOutput::getDefaultDeviceIndex()]. @see getDefaultMidiOutput, getDefaultMidiOutputName @@ -324,7 +324,7 @@ public: /** Returns the current default midi output device. - If no device has been selected, or the device can't be opened, this will + If no device has been selected, or the device can't be opened, this will return 0. @see getDefaultMidiOutputName diff --git a/src/juce_appframework/audio/processors/juce_AudioProcessor.cpp b/src/juce_appframework/audio/processors/juce_AudioProcessor.cpp index 6b5d40ee89..e72d14f397 100644 --- a/src/juce_appframework/audio/processors/juce_AudioProcessor.cpp +++ b/src/juce_appframework/audio/processors/juce_AudioProcessor.cpp @@ -57,7 +57,7 @@ AudioProcessor::~AudioProcessor() // that it refers to is deleted.. jassert (activeEditor == 0); -#ifdef JUCE_DEBUG +#ifdef JUCE_DEBUG // This will fail if you've called beginParameterChangeGesture() for one // or more parameters without having made a corresponding call to endParameterChangeGesture... jassert (changingParams.countNumberOfSetBits() == 0); @@ -127,7 +127,7 @@ void AudioProcessor::beginParameterChangeGesture (int parameterIndex) { jassert (parameterIndex >= 0 && parameterIndex < getNumParameters()); -#ifdef JUCE_DEBUG +#ifdef JUCE_DEBUG // This means you've called beginParameterChangeGesture twice in succession without a matching // call to endParameterChangeGesture. That might be fine in most hosts, but better to avoid doing it. jassert (! changingParams [parameterIndex]); @@ -148,7 +148,7 @@ void AudioProcessor::endParameterChangeGesture (int parameterIndex) { jassert (parameterIndex >= 0 && parameterIndex < getNumParameters()); -#ifdef JUCE_DEBUG +#ifdef JUCE_DEBUG // This means you've called endParameterChangeGesture without having previously called // endParameterChangeGesture. That might be fine in most hosts, but better to keep the // calls matched correctly. diff --git a/src/juce_appframework/gui/components/controls/juce_Slider.cpp b/src/juce_appframework/gui/components/controls/juce_Slider.cpp index 611b827669..e616de3a86 100644 --- a/src/juce_appframework/gui/components/controls/juce_Slider.cpp +++ b/src/juce_appframework/gui/components/controls/juce_Slider.cpp @@ -1089,7 +1089,7 @@ void Slider::restoreMouseIfHidden() c->enableUnboundedMouseMovement (false); const double pos = (sliderBeingDragged == 2) ? getMaxValue() - : ((sliderBeingDragged == 1) ? getMinValue() + : ((sliderBeingDragged == 1) ? getMinValue() : currentValue); const int pixelPos = (int) getLinearSliderPos (pos); diff --git a/src/juce_appframework/gui/components/filebrowser/juce_FileSearchPathListComponent.cpp b/src/juce_appframework/gui/components/filebrowser/juce_FileSearchPathListComponent.cpp index 79ea51d714..597dce365c 100644 --- a/src/juce_appframework/gui/components/filebrowser/juce_FileSearchPathListComponent.cpp +++ b/src/juce_appframework/gui/components/filebrowser/juce_FileSearchPathListComponent.cpp @@ -202,10 +202,13 @@ void FileSearchPathListComponent::resized() changeButton->setTopRightPosition (upButton->getX() - 8, buttonY); } -bool FileSearchPathListComponent::filesDropped (const StringArray& filenames, int /*mouseX*/, int mouseY) +bool FileSearchPathListComponent::isInterestedInFileDrag (const StringArray&) { - bool usedAny = false; + return true; +} +void FileSearchPathListComponent::filesDropped (const StringArray& filenames, int, int mouseY) +{ for (int i = filenames.size(); --i >= 0;) { const File f (filenames[i]); @@ -215,11 +218,8 @@ bool FileSearchPathListComponent::filesDropped (const StringArray& filenames, in const int row = listBox->getRowContainingPosition (0, mouseY - listBox->getY()); path.add (f, row); changed(); - usedAny = true; } } - - return usedAny; } void FileSearchPathListComponent::buttonClicked (Button* button) diff --git a/src/juce_appframework/gui/components/filebrowser/juce_FileSearchPathListComponent.h b/src/juce_appframework/gui/components/filebrowser/juce_FileSearchPathListComponent.h index 17861d9994..fdaf8c9dbf 100644 --- a/src/juce_appframework/gui/components/filebrowser/juce_FileSearchPathListComponent.h +++ b/src/juce_appframework/gui/components/filebrowser/juce_FileSearchPathListComponent.h @@ -34,6 +34,7 @@ #include "../controls/juce_ListBox.h" #include "../buttons/juce_Button.h" +#include "../mouse/juce_FileDragAndDropTarget.h" #include "../../../../juce_core/io/files/juce_FileSearchPath.h" @@ -46,6 +47,7 @@ */ class JUCE_API FileSearchPathListComponent : public Component, public SettableTooltipClient, + public FileDragAndDropTarget, private ButtonListener, private ListBoxModel { @@ -103,7 +105,9 @@ public: /** @internal */ void paint (Graphics& g); /** @internal */ - bool filesDropped (const StringArray& filenames, int mouseX, int mouseY); + bool isInterestedInFileDrag (const StringArray& files); + /** @internal */ + void filesDropped (const StringArray& files, int, int); /** @internal */ void buttonClicked (Button* button); diff --git a/src/juce_appframework/gui/components/filebrowser/juce_FilenameComponent.cpp b/src/juce_appframework/gui/components/filebrowser/juce_FilenameComponent.cpp index 3c11ab266d..93bbf08c31 100644 --- a/src/juce_appframework/gui/components/filebrowser/juce_FilenameComponent.cpp +++ b/src/juce_appframework/gui/components/filebrowser/juce_FilenameComponent.cpp @@ -52,6 +52,7 @@ FilenameComponent::FilenameComponent (const String& name, maxRecentFiles (30), isDir (isDirectory), isSaving (isForSaving), + isFileDragOver (false), wildcard (fileBrowserWildcard), enforcedSuffix (enforcedSuffix_) { @@ -73,6 +74,15 @@ FilenameComponent::~FilenameComponent() } //============================================================================== +void FilenameComponent::paintOverChildren (Graphics& g) +{ + if (isFileDragOver) + { + g.setColour (Colours::red.withAlpha (0.2f)); + g.drawRect (0, 0, getWidth(), getHeight(), 3); + } +} + void FilenameComponent::resized() { getLookAndFeel().layoutFilenameComponent (*this, filenameBox, browseButton); @@ -126,14 +136,32 @@ void FilenameComponent::comboBoxChanged (ComboBox*) setCurrentFile (getCurrentFile(), true); } -bool FilenameComponent::filesDropped (const StringArray& filenames, int, int) +bool FilenameComponent::isInterestedInFileDrag (const StringArray&) { + return true; +} + +void FilenameComponent::filesDropped (const StringArray& filenames, int, int) +{ + isFileDragOver = false; + repaint(); + const File f (filenames[0]); if (f.exists() && (f.isDirectory() == isDir)) setCurrentFile (f, true); +} - return true; +void FilenameComponent::fileDragEnter (const StringArray&, int, int) +{ + isFileDragOver = true; + repaint(); +} + +void FilenameComponent::fileDragExit (const StringArray&) +{ + isFileDragOver = false; + repaint(); } //============================================================================== diff --git a/src/juce_appframework/gui/components/filebrowser/juce_FilenameComponent.h b/src/juce_appframework/gui/components/filebrowser/juce_FilenameComponent.h index 9273c17b3b..ff6108a5d5 100644 --- a/src/juce_appframework/gui/components/filebrowser/juce_FilenameComponent.h +++ b/src/juce_appframework/gui/components/filebrowser/juce_FilenameComponent.h @@ -36,6 +36,7 @@ #include "../../../events/juce_AsyncUpdater.h" #include "../buttons/juce_TextButton.h" #include "../../../../juce_core/io/files/juce_File.h" +#include "../mouse/juce_FileDragAndDropTarget.h" class FilenameComponent; @@ -75,6 +76,7 @@ public: */ class JUCE_API FilenameComponent : public Component, public SettableTooltipClient, + public FileDragAndDropTarget, private AsyncUpdater, private ButtonListener, private ComboBoxListener @@ -188,11 +190,19 @@ public: //============================================================================== /** @internal */ + void paintOverChildren (Graphics& g); + /** @internal */ void resized(); /** @internal */ void lookAndFeelChanged(); /** @internal */ - bool filesDropped (const StringArray& filenames, int mouseX, int mouseY); + bool isInterestedInFileDrag (const StringArray& files); + /** @internal */ + void filesDropped (const StringArray& files, int, int); + /** @internal */ + void fileDragEnter (const StringArray& files, int, int); + /** @internal */ + void fileDragExit (const StringArray& files); //============================================================================== juce_UseDebuggingNewOperator @@ -203,7 +213,7 @@ private: String lastFilename; Button* browseButton; int maxRecentFiles; - bool isDir, isSaving; + bool isDir, isSaving, isFileDragOver; String wildcard, enforcedSuffix, browseButtonText; SortedSet listeners; File defaultBrowseFile; diff --git a/src/juce_appframework/gui/components/juce_Component.cpp b/src/juce_appframework/gui/components/juce_Component.cpp index 9e9b313745..b14f4950c9 100644 --- a/src/juce_appframework/gui/components/juce_Component.cpp +++ b/src/juce_appframework/gui/components/juce_Component.cpp @@ -3502,38 +3502,6 @@ void Component::internalModifierKeysChanged() } //============================================================================== -bool Component::filesDropped (const StringArray&, int, int) -{ - // base class returns false to let the message get passed up to its parent - return false; -} - -void Component::internalFilesDropped (const int x, - const int y, - const StringArray& files) -{ - if (isCurrentlyBlockedByAnotherModalComponent()) - { - internalModalInputAttempt(); - - if (isCurrentlyBlockedByAnotherModalComponent()) - return; - } - - Component* c = getComponentAt (x, y); - - while (c->isValidComponent()) - { - int rx = x, ry = y; - relativePositionToOtherComponent (c, rx, ry); - - if (c->filesDropped (files, rx, ry)) - break; - - c = c->getParentComponent(); - } -} - ComponentPeer* Component::getPeer() const throw() { if (flags.hasHeavyweightPeerFlag) diff --git a/src/juce_appframework/gui/components/juce_Component.h b/src/juce_appframework/gui/components/juce_Component.h index b01aa3d5e9..8f9a752661 100644 --- a/src/juce_appframework/gui/components/juce_Component.h +++ b/src/juce_appframework/gui/components/juce_Component.h @@ -1688,22 +1688,6 @@ public: */ void removeComponentListener (ComponentListener* const listenerToRemove) throw(); - //============================================================================== - /** Called when files are dragged-and-dropped onto this component. - - If the component isn't interested in the files, it should return false, to indicate - that its parent can be offered the files instead. - - @param filenames a list of the filenames of the files that were dropped - @param mouseX x co-ordinate of the mouse when they were dropped, (relative to this - component's top-left) - @param mouseY y co-ordinate of the mouse when they were dropped, (relative to this - component's top-left) - */ - virtual bool filesDropped (const StringArray& filenames, - int mouseX, - int mouseY); - //============================================================================== /** Dispatches a numbered message to this component. @@ -2078,7 +2062,6 @@ private: void internalModifierKeysChanged(); void internalChildrenChanged(); void internalHierarchyChanged(); - void internalFilesDropped (const int x, const int y, const StringArray& files); void internalUpdateMouseCursor (const bool forcedUpdate) throw(); void sendMovedResizedMessages (const bool wasMoved, const bool wasResized); void repaintParent() throw(); @@ -2099,6 +2082,13 @@ private: const Rectangle getUnclippedArea() const; void sendVisibilityChangeMessage(); + //============================================================================== + // This is included here just to cause a compile error if your code is still handling + // drag-and-drop with this method. If so, just update it to use the new FileDragAndDropTarget + // class, which is easy (just make your class inherit from FileDragAndDropTarget, and + // implement its methods instead of this Component method). + virtual void filesDropped (const StringArray&, int, int) {} + // components aren't allowed to have copy constructors, as this would mess up parent // hierarchies. You might need to give your subclasses a private dummy constructor like // this one to avoid compiler warnings. diff --git a/src/juce_appframework/gui/components/mouse/juce_DragAndDropContainer.cpp b/src/juce_appframework/gui/components/mouse/juce_DragAndDropContainer.cpp index 111cb48cc7..585edebb72 100644 --- a/src/juce_appframework/gui/components/mouse/juce_DragAndDropContainer.cpp +++ b/src/juce_appframework/gui/components/mouse/juce_DragAndDropContainer.cpp @@ -39,6 +39,7 @@ BEGIN_JUCE_NAMESPACE #include "../../../events/juce_Timer.h" #include "../../../../juce_core/basics/juce_Random.h" #include "../../graphics/imaging/juce_Image.h" +#include "juce_FileDragAndDropTarget.h" bool juce_performDragDropFiles (const StringArray& files, const bool copyFiles, bool& shouldStop); bool juce_performDragDropText (const String& text, bool& shouldStop); @@ -477,4 +478,19 @@ bool DragAndDropTarget::shouldDrawDragImageWhenOver() return true; } + +//============================================================================== +void FileDragAndDropTarget::fileDragEnter (const StringArray&, int, int) +{ +} + +void FileDragAndDropTarget::fileDragMove (const StringArray&, int, int) +{ +} + +void FileDragAndDropTarget::fileDragExit (const StringArray&) +{ +} + + END_JUCE_NAMESPACE diff --git a/src/juce_appframework/gui/components/mouse/juce_DragAndDropTarget.h b/src/juce_appframework/gui/components/mouse/juce_DragAndDropTarget.h index 0415b1d71a..aa02c0f49d 100644 --- a/src/juce_appframework/gui/components/mouse/juce_DragAndDropTarget.h +++ b/src/juce_appframework/gui/components/mouse/juce_DragAndDropTarget.h @@ -44,10 +44,10 @@ DragAndDropContainer component. Note: If all that you need to do is to respond to files being drag-and-dropped from - the operating system onto your component, you don't need any of these classes: you can do this - simply by overriding Component::filesDropped(). + the operating system onto your component, you don't need any of these classes: instead + see the FileDragAndDropTarget class. - @see DragAndDropContainer + @see DragAndDropContainer, FileDragAndDropTarget */ class JUCE_API DragAndDropTarget { @@ -119,6 +119,9 @@ public: When the user drops an item this get called, and you can use the description to work out whether your object wants to deal with it or not. + Note that after this is called, the itemDragExit method may not be called, so you should + clean up in here if there's anything you need to do when the drag finishes. + @param sourceDescription the description string passed into DragAndDropContainer::startDragging() @param sourceComponent the component passed into DragAndDropContainer::startDragging() @param x the mouse x position, relative to this component diff --git a/src/juce_appframework/gui/components/special/juce_OpenGLComponent.h b/src/juce_appframework/gui/components/special/juce_OpenGLComponent.h index 7fa00502fc..f021b926bb 100644 --- a/src/juce_appframework/gui/components/special/juce_OpenGLComponent.h +++ b/src/juce_appframework/gui/components/special/juce_OpenGLComponent.h @@ -116,7 +116,7 @@ public: /** Sets whether the context checks the vertical sync before swapping. - The value is the number of frames to allow between buffer-swapping. This is + The value is the number of frames to allow between buffer-swapping. This is fairly system-dependent, but 0 turns off syncing, 1 makes it swap on frame-boundaries, and greater numbers indicate that it should swap less often. @@ -235,10 +235,10 @@ public: that your context needs. New contexts are created on-demand by the makeCurrentContextActive() method - so - if the context is deleted, e.g. by changing the pixel format or window, no context + if the context is deleted, e.g. by changing the pixel format or window, no context will be created until the next call to makeCurrentContextActive(), which will synchronously create one and call this method. This means that if you're using - a non-GUI thread for rendering, you can make sure this method is be called by + a non-GUI thread for rendering, you can make sure this method is be called by your renderer thread. When this callback happens, the context will already have been made current @@ -252,7 +252,7 @@ public: /** Returns the context that will draw into this component. This may return 0 if the component is currently invisible or hasn't currently - got a context. The context object can be deleted and a new one created during + got a context. The context object can be deleted and a new one created during the lifetime of this component, and there may be times when it doesn't have one. @see newOpenGLContextCreated() @@ -267,8 +267,8 @@ public: If this returns false, then the context isn't active, so you should avoid making any calls. - This call may actually create a context if one isn't currently initialised. If - it does this, it will also synchronously call the newOpenGLContextCreated() + This call may actually create a context if one isn't currently initialised. If + it does this, it will also synchronously call the newOpenGLContextCreated() method to let you initialise it as necessary. @see OpenGLContext::makeActive diff --git a/src/juce_appframework/gui/components/windows/juce_AlertWindow.cpp b/src/juce_appframework/gui/components/windows/juce_AlertWindow.cpp index c0e9c15db6..6933718573 100644 --- a/src/juce_appframework/gui/components/windows/juce_AlertWindow.cpp +++ b/src/juce_appframework/gui/components/windows/juce_AlertWindow.cpp @@ -595,7 +595,7 @@ struct AlertWindowInfo int run() const { - return (int) (pointer_sized_int) + return (int) (pointer_sized_int) MessageManager::getInstance()->callFunctionOnMessageThread (showCallback, (void*) this); } diff --git a/src/juce_appframework/gui/components/windows/juce_ComponentPeer.cpp b/src/juce_appframework/gui/components/windows/juce_ComponentPeer.cpp index bedc83bcd3..9c0d1f431f 100644 --- a/src/juce_appframework/gui/components/windows/juce_ComponentPeer.cpp +++ b/src/juce_appframework/gui/components/windows/juce_ComponentPeer.cpp @@ -41,7 +41,7 @@ BEGIN_JUCE_NAMESPACE #include "../../../../juce_core/basics/juce_Time.h" #include "../../../../juce_core/basics/juce_Random.h" #include "../layout/juce_ComponentBoundsConstrainer.h" - +#include "../mouse/juce_FileDragAndDropTarget.h" //#define JUCE_ENABLE_REPAINT_DEBUGGING 1 @@ -70,6 +70,8 @@ ComponentPeer::ComponentPeer (Component* const component_, lastPaintTime (0), constrainer (0), lastFocusedComponent (0), + dragAndDropTargetComponent (0), + lastDragAndDropCompUnderMouse (0), fakeMouseMessageSent (false), isWindowMinimised (false) { @@ -79,6 +81,7 @@ ComponentPeer::ComponentPeer (Component* const component_, ComponentPeer::~ComponentPeer() { heavyweightPeers.removeValue (this); + delete dragAndDropTargetComponent; } //============================================================================== @@ -451,7 +454,7 @@ bool ComponentPeer::handleKeyPress (const int keyCode, if (keyWasUsed || deletionChecker.hasBeenDeleted()) break; - + if (keyInfo.isKeyCode (KeyPress::tabKey) && Component::getCurrentlyFocusedComponent() != 0) { Component::getCurrentlyFocusedComponent() @@ -654,13 +657,111 @@ const Rectangle& ComponentPeer::getNonFullScreenBounds() const throw() } //============================================================================== -void ComponentPeer::handleFilesDropped (int x, int y, const StringArray& files) +static FileDragAndDropTarget* findDragAndDropTarget (Component* c, + const StringArray& files, + FileDragAndDropTarget* const lastOne) +{ + while (c != 0) + { + FileDragAndDropTarget* const t = dynamic_cast (c); + + if (t != 0 && (t == lastOne || t->isInterestedInFileDrag (files))) + return t; + + c = c->getParentComponent(); + } + + return 0; +} + +void ComponentPeer::handleFileDragMove (const StringArray& files, int x, int y) { updateCurrentModifiers(); - component->internalFilesDropped (x, y, files); + FileDragAndDropTarget* lastTarget = 0; + + if (dragAndDropTargetComponent != 0 && ! dragAndDropTargetComponent->hasBeenDeleted()) + lastTarget = const_cast (dynamic_cast (dragAndDropTargetComponent->getComponent())); + + FileDragAndDropTarget* newTarget = 0; + + Component* const compUnderMouse = component->getComponentAt (x, y); + + if (compUnderMouse != lastDragAndDropCompUnderMouse) + { + lastDragAndDropCompUnderMouse = compUnderMouse; + newTarget = findDragAndDropTarget (compUnderMouse, files, lastTarget); + + if (newTarget != lastTarget) + { + if (lastTarget != 0) + lastTarget->fileDragExit (files); + + deleteAndZero (dragAndDropTargetComponent); + + if (newTarget != 0) + { + Component* const targetComp = dynamic_cast (newTarget); + int mx = x, my = y; + component->relativePositionToOtherComponent (targetComp, mx, my); + + dragAndDropTargetComponent = new ComponentDeletionWatcher (dynamic_cast (newTarget)); + newTarget->fileDragEnter (files, mx, my); + } + } + } + else + { + newTarget = lastTarget; + } + + if (newTarget != 0) + { + Component* const targetComp = dynamic_cast (newTarget); + component->relativePositionToOtherComponent (targetComp, x, y); + + newTarget->fileDragMove (files, x, y); + } } +void ComponentPeer::handleFileDragExit (const StringArray& files) +{ + handleFileDragMove (files, -1, -1); + + jassert (dragAndDropTargetComponent == 0); + lastDragAndDropCompUnderMouse = 0; +} + +void ComponentPeer::handleFileDragDrop (const StringArray& files, int x, int y) +{ + handleFileDragMove (files, x, y); + + if (dragAndDropTargetComponent != 0 && ! dragAndDropTargetComponent->hasBeenDeleted()) + { + FileDragAndDropTarget* const target = const_cast (dynamic_cast (dragAndDropTargetComponent->getComponent())); + + deleteAndZero (dragAndDropTargetComponent); + lastDragAndDropCompUnderMouse = 0; + + if (target != 0) + { + Component* const targetComp = dynamic_cast (target); + + if (targetComp->isCurrentlyBlockedByAnotherModalComponent()) + { + targetComp->internalModalInputAttempt(); + + if (targetComp->isCurrentlyBlockedByAnotherModalComponent()) + return; + } + + component->relativePositionToOtherComponent (targetComp, x, y); + target->filesDropped (files, x, y); + } + } +} + +//============================================================================== void ComponentPeer::handleUserClosingWindow() { updateCurrentModifiers(); diff --git a/src/juce_appframework/gui/components/windows/juce_ComponentPeer.h b/src/juce_appframework/gui/components/windows/juce_ComponentPeer.h index e2fd7e5ff9..71387c6631 100644 --- a/src/juce_appframework/gui/components/windows/juce_ComponentPeer.h +++ b/src/juce_appframework/gui/components/windows/juce_ComponentPeer.h @@ -39,6 +39,7 @@ class Graphics; #include "../../../../juce_core/text/juce_StringArray.h" #include "../../graphics/geometry/juce_RectangleList.h" class ComponentBoundsConstrainer; +class ComponentDeletionWatcher; //============================================================================== @@ -300,7 +301,9 @@ public: void handleUserClosingWindow(); - void handleFilesDropped (int x, int y, const StringArray& files); + void handleFileDragMove (const StringArray& files, int x, int y); + void handleFileDragExit (const StringArray& files); + void handleFileDragDrop (const StringArray& files, int x, int y); //============================================================================== /** Resets the masking region. @@ -361,11 +364,15 @@ protected: private: //============================================================================== Component* lastFocusedComponent; + ComponentDeletionWatcher* dragAndDropTargetComponent; + Component* lastDragAndDropCompUnderMouse; bool fakeMouseMessageSent : 1, isWindowMinimised : 1; friend class Component; static ComponentPeer* getPeerFor (const Component* const component) throw(); + void setLastDragDropTarget (Component* comp); + ComponentPeer (const ComponentPeer&); const ComponentPeer& operator= (const ComponentPeer&); }; diff --git a/src/juce_core/io/files/juce_File.cpp b/src/juce_core/io/files/juce_File.cpp index b8eedf3c99..ea1eae82b1 100644 --- a/src/juce_core/io/files/juce_File.cpp +++ b/src/juce_core/io/files/juce_File.cpp @@ -1071,7 +1071,7 @@ const String File::getRelativePathFrom (const File& dir) const throw() || (commonBitLength == 1 && thisPath [1] == File::separator) || (commonBitLength <= 3 && thisPath [1] == T(':'))) #else - if (commonBitLength <= 0 + if (commonBitLength <= 0 || (commonBitLength == 1 && thisPath [1] == File::separator)) #endif return fullPath;