1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

added FileDragAndDropTarget class and rewrote all the drag handling code

This commit is contained in:
jules 2007-09-26 14:43:50 +00:00
parent 6d2d246768
commit 60e1be176e
44 changed files with 1182 additions and 829 deletions

View file

@ -33,7 +33,7 @@
#include <dlfcn.h>
#include <sys/file.h>
#include <sys/types.h>
#include <sys/ptrace.h>
#include <sys/ptrace.h>
#include "../../../src/juce_core/basics/juce_StandardHeader.h"
BEGIN_JUCE_NAMESPACE

View file

@ -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;

View file

@ -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 = "<group>"; };
84FC31BC09B74A7700B75141 /* juce_BorderSize.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = juce_BorderSize.cpp; sourceTree = "<group>"; };
84FC31BD09B74A7700B75141 /* juce_BorderSize.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = juce_BorderSize.h; sourceTree = "<group>"; };
84FED3C80CAA96DA00003997 /* juce_FileDragAndDropTarget.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = juce_FileDragAndDropTarget.h; sourceTree = "<group>"; };
84FFAF290C6C8F2B009F6E72 /* juce_FileSearchPathListComponent.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = juce_FileSearchPathListComponent.cpp; path = filebrowser/juce_FileSearchPathListComponent.cpp; sourceTree = "<group>"; };
84FFAF2A0C6C8F2B009F6E72 /* juce_FileSearchPathListComponent.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = juce_FileSearchPathListComponent.h; path = filebrowser/juce_FileSearchPathListComponent.h; sourceTree = "<group>"; };
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;
};

View file

@ -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 <pthread.h>
#include <sched.h>
#include <sys/file.h>
#include <sys/types.h>
#include <sys/ptrace.h>
#include <Carbon/Carbon.h>
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, &param);
param.sched_priority = jlimit (1, 127, 1 + (priority * 126) / 11);
pthread_setschedparam ((pthread_t) handle, policy, &param);
}
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 <pthread.h>
#include <sched.h>
#include <sys/file.h>
#include <sys/types.h>
#include <sys/ptrace.h>
#include <Carbon/Carbon.h>
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, &param);
param.sched_priority = jlimit (1, 127, 1 + (priority * 126) / 11);
pthread_setschedparam ((pthread_t) handle, policy, &param);
}
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

View file

@ -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, &current);
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), &current);
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, &current);
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), &current);
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

View file

@ -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)
{

View file

@ -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());

View file

@ -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();

View file

@ -3893,6 +3893,10 @@
RelativePath="..\..\..\src\juce_appframework\gui\components\mouse\juce_DragAndDropTarget.h"
>
</File>
<File
RelativePath="..\..\..\src\juce_appframework\gui\components\mouse\juce_FileDragAndDropTarget.h"
>
</File>
<File
RelativePath="..\..\..\src\juce_appframework\gui\components\mouse\juce_LassoComponent.h"
>

View file

@ -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

View file

@ -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 <PluginDescription> typesFound;
knownPluginList.scanAndAddDragAndDroppedFiles (files, typesFound);
OwnedArray <PluginDescription> 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

View file

@ -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);

View file

@ -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));
}
}

View file

@ -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 <PluginDescription> typesFound;
list.scanAndAddDragAndDroppedFiles (files, typesFound);
return typesFound.size() > 0;
}
void PluginListComponent::scanFor (AudioPluginFormat* format)

View file

@ -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 */

View file

@ -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);

View file

@ -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()

View file

@ -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;

View file

@ -577,7 +577,7 @@ public:
AudioDeviceSelectorComponent audioSettingsComp (audioDeviceManager,
0, 1,
2, 2,
true,
true,
false);
// ...and show it in a DialogWindow...

View file

@ -577,7 +577,7 @@ public:
AudioDeviceSelectorComponent audioSettingsComp (audioDeviceManager,
0, 1,
2, 2,
true,
true,
false);
// ...and show it in a DialogWindow...

View file

@ -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;

View file

@ -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

View file

@ -42,6 +42,7 @@
*/
class ComponentLayoutEditor : public Component,
public ChangeListener,
public FileDragAndDropTarget,
public LassoSource <Component*>
{
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; }

View file

@ -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();

View file

@ -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();
//==============================================================================

View file

@ -286,9 +286,20 @@ SelectedItemSet <PaintElement*>& 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;
}

View file

@ -43,7 +43,8 @@ class JucerDocumentHolder;
*/
class PaintRoutineEditor : public Component,
public ChangeListener,
public LassoSource <PaintElement*>
public LassoSource <PaintElement*>,
public FileDragAndDropTarget
{
public:
//==============================================================================
@ -68,7 +69,8 @@ public:
SelectedItemSet <PaintElement*>& 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;

View file

@ -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

View file

@ -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

View file

@ -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.

View file

@ -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);

View file

@ -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)

View file

@ -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);

View file

@ -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();
}
//==============================================================================

View file

@ -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 <void*> listeners;
File defaultBrowseFile;

View file

@ -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)

View file

@ -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.

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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);
}

View file

@ -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 <FileDragAndDropTarget*> (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 <FileDragAndDropTarget*> (dynamic_cast <const FileDragAndDropTarget*> (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 <Component*> (newTarget);
int mx = x, my = y;
component->relativePositionToOtherComponent (targetComp, mx, my);
dragAndDropTargetComponent = new ComponentDeletionWatcher (dynamic_cast <Component*> (newTarget));
newTarget->fileDragEnter (files, mx, my);
}
}
}
else
{
newTarget = lastTarget;
}
if (newTarget != 0)
{
Component* const targetComp = dynamic_cast <Component*> (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 <FileDragAndDropTarget*> (dynamic_cast <const FileDragAndDropTarget*> (dragAndDropTargetComponent->getComponent()));
deleteAndZero (dragAndDropTargetComponent);
lastDragAndDropCompUnderMouse = 0;
if (target != 0)
{
Component* const targetComp = dynamic_cast <Component*> (target);
if (targetComp->isCurrentlyBlockedByAnotherModalComponent())
{
targetComp->internalModalInputAttempt();
if (targetComp->isCurrentlyBlockedByAnotherModalComponent())
return;
}
component->relativePositionToOtherComponent (targetComp, x, y);
target->filesDropped (files, x, y);
}
}
}
//==============================================================================
void ComponentPeer::handleUserClosingWindow()
{
updateCurrentModifiers();

View file

@ -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&);
};

View file

@ -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;