mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
First check-in of the "jucequake": a major re-organisation of the library to break it up into modules. For more details about this, see the website forum..
This commit is contained in:
parent
1a21c89755
commit
b70e0a28d2
1527 changed files with 90380 additions and 396643 deletions
260
modules/juce_gui_basics/application/juce_Application.cpp
Normal file
260
modules/juce_gui_basics/application/juce_Application.cpp
Normal file
|
|
@ -0,0 +1,260 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
#if JUCE_MAC
|
||||
extern void juce_initialiseMacMainMenu();
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
class AppBroadcastCallback : public ActionListener
|
||||
{
|
||||
public:
|
||||
AppBroadcastCallback() { MessageManager::getInstance()->registerBroadcastListener (this); }
|
||||
~AppBroadcastCallback() { MessageManager::getInstance()->deregisterBroadcastListener (this); }
|
||||
|
||||
void actionListenerCallback (const String& message)
|
||||
{
|
||||
JUCEApplication* const app = JUCEApplication::getInstance();
|
||||
|
||||
if (app != 0 && message.startsWith (app->getApplicationName() + "/"))
|
||||
app->anotherInstanceStarted (message.substring (app->getApplicationName().length() + 1));
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
JUCEApplication::JUCEApplication()
|
||||
: appReturnValue (0),
|
||||
stillInitialising (true)
|
||||
{
|
||||
}
|
||||
|
||||
JUCEApplication::~JUCEApplication()
|
||||
{
|
||||
if (appLock != nullptr)
|
||||
{
|
||||
appLock->exit();
|
||||
appLock = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool JUCEApplication::moreThanOneInstanceAllowed()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void JUCEApplication::anotherInstanceStarted (const String&)
|
||||
{
|
||||
}
|
||||
|
||||
void JUCEApplication::systemRequestedQuit()
|
||||
{
|
||||
quit();
|
||||
}
|
||||
|
||||
void JUCEApplication::quit()
|
||||
{
|
||||
MessageManager::getInstance()->stopDispatchLoop();
|
||||
}
|
||||
|
||||
void JUCEApplication::setApplicationReturnValue (const int newReturnValue) noexcept
|
||||
{
|
||||
appReturnValue = newReturnValue;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void JUCEApplication::unhandledException (const std::exception*,
|
||||
const String&,
|
||||
const int)
|
||||
{
|
||||
jassertfalse;
|
||||
}
|
||||
|
||||
void JUCEApplication::sendUnhandledException (const std::exception* const e,
|
||||
const char* const sourceFile,
|
||||
const int lineNumber)
|
||||
{
|
||||
if (JUCEApplicationBase::getInstance() != nullptr)
|
||||
JUCEApplicationBase::getInstance()->unhandledException (e, sourceFile, lineNumber);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
ApplicationCommandTarget* JUCEApplication::getNextCommandTarget()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void JUCEApplication::getAllCommands (Array <CommandID>& commands)
|
||||
{
|
||||
commands.add (StandardApplicationCommandIDs::quit);
|
||||
}
|
||||
|
||||
void JUCEApplication::getCommandInfo (const CommandID commandID, ApplicationCommandInfo& result)
|
||||
{
|
||||
if (commandID == StandardApplicationCommandIDs::quit)
|
||||
{
|
||||
result.setInfo (TRANS("Quit"),
|
||||
TRANS("Quits the application"),
|
||||
"Application",
|
||||
0);
|
||||
|
||||
result.defaultKeypresses.add (KeyPress ('q', ModifierKeys::commandModifier, 0));
|
||||
}
|
||||
}
|
||||
|
||||
bool JUCEApplication::perform (const InvocationInfo& info)
|
||||
{
|
||||
if (info.commandID == StandardApplicationCommandIDs::quit)
|
||||
{
|
||||
systemRequestedQuit();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool JUCEApplication::initialiseApp (const String& commandLine)
|
||||
{
|
||||
commandLineParameters = commandLine.trim();
|
||||
|
||||
#if ! JUCE_IOS
|
||||
jassert (appLock == nullptr); // initialiseApp must only be called once!
|
||||
|
||||
if (! moreThanOneInstanceAllowed())
|
||||
{
|
||||
appLock = new InterProcessLock ("juceAppLock_" + getApplicationName());
|
||||
|
||||
if (! appLock->enter(0))
|
||||
{
|
||||
appLock = nullptr;
|
||||
MessageManager::broadcastMessage (getApplicationName() + "/" + commandLineParameters);
|
||||
|
||||
DBG ("Another instance is running - quitting...");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// let the app do its setting-up..
|
||||
initialise (commandLineParameters);
|
||||
|
||||
#if JUCE_MAC
|
||||
juce_initialiseMacMainMenu(); // needs to be called after the app object has created, to get its name
|
||||
#endif
|
||||
|
||||
#if ! JUCE_IOS
|
||||
broadcastCallback = new AppBroadcastCallback();
|
||||
#endif
|
||||
|
||||
stillInitialising = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
int JUCEApplication::shutdownApp()
|
||||
{
|
||||
jassert (JUCEApplicationBase::getInstance() == this);
|
||||
|
||||
broadcastCallback = nullptr;
|
||||
|
||||
JUCE_TRY
|
||||
{
|
||||
// give the app a chance to clean up..
|
||||
shutdown();
|
||||
}
|
||||
JUCE_CATCH_EXCEPTION
|
||||
|
||||
return getApplicationReturnValue();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
#if ! JUCE_ANDROID
|
||||
int JUCEApplication::main (const String& commandLine)
|
||||
{
|
||||
ScopedJuceInitialiser_GUI libraryInitialiser;
|
||||
jassert (createInstance != nullptr);
|
||||
int returnCode = 0;
|
||||
|
||||
{
|
||||
const ScopedPointer<JUCEApplication> app (dynamic_cast <JUCEApplication*> (createInstance()));
|
||||
|
||||
jassert (app != nullptr);
|
||||
|
||||
if (! app->initialiseApp (commandLine))
|
||||
return 0;
|
||||
|
||||
JUCE_TRY
|
||||
{
|
||||
// loop until a quit message is received..
|
||||
MessageManager::getInstance()->runDispatchLoop();
|
||||
}
|
||||
JUCE_CATCH_EXCEPTION
|
||||
|
||||
returnCode = app->shutdownApp();
|
||||
}
|
||||
|
||||
return returnCode;
|
||||
}
|
||||
|
||||
#if JUCE_IOS
|
||||
extern int juce_iOSMain (int argc, const char* argv[]);
|
||||
#endif
|
||||
|
||||
#if ! JUCE_WINDOWS
|
||||
extern const char* juce_Argv0;
|
||||
#endif
|
||||
|
||||
#if JUCE_MAC
|
||||
extern void initialiseNSApplication();
|
||||
#endif
|
||||
|
||||
int JUCEApplication::main (int argc, const char* argv[])
|
||||
{
|
||||
JUCE_AUTORELEASEPOOL
|
||||
|
||||
#if JUCE_MAC
|
||||
initialiseNSApplication();
|
||||
#endif
|
||||
|
||||
#if ! JUCE_WINDOWS
|
||||
jassert (createInstance != nullptr);
|
||||
juce_Argv0 = argv[0];
|
||||
#endif
|
||||
|
||||
#if JUCE_IOS
|
||||
return juce_iOSMain (argc, argv);
|
||||
#else
|
||||
String cmd;
|
||||
for (int i = 1; i < argc; ++i)
|
||||
cmd << argv[i] << ' ';
|
||||
|
||||
return JUCEApplication::main (cmd);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
245
modules/juce_gui_basics/application/juce_Application.h
Normal file
245
modules/juce_gui_basics/application/juce_Application.h
Normal file
|
|
@ -0,0 +1,245 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_APPLICATION_JUCEHEADER__
|
||||
#define __JUCE_APPLICATION_JUCEHEADER__
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
An instance of this class is used to specify initialisation and shutdown
|
||||
code for the application.
|
||||
|
||||
An application that wants to run in the JUCE framework needs to declare a
|
||||
subclass of JUCEApplication and implement its various pure virtual methods.
|
||||
|
||||
It then needs to use the START_JUCE_APPLICATION macro somewhere in a cpp file
|
||||
to declare an instance of this class and generate a suitable platform-specific
|
||||
main() function.
|
||||
|
||||
e.g. @code
|
||||
class MyJUCEApp : public JUCEApplication
|
||||
{
|
||||
public:
|
||||
MyJUCEApp()
|
||||
{
|
||||
}
|
||||
|
||||
~MyJUCEApp()
|
||||
{
|
||||
}
|
||||
|
||||
void initialise (const String& commandLine)
|
||||
{
|
||||
myMainWindow = new MyApplicationWindow();
|
||||
myMainWindow->setBounds (100, 100, 400, 500);
|
||||
myMainWindow->setVisible (true);
|
||||
}
|
||||
|
||||
void shutdown()
|
||||
{
|
||||
myMainWindow = 0;
|
||||
}
|
||||
|
||||
const String getApplicationName()
|
||||
{
|
||||
return "Super JUCE-o-matic";
|
||||
}
|
||||
|
||||
const String getApplicationVersion()
|
||||
{
|
||||
return "1.0";
|
||||
}
|
||||
|
||||
private:
|
||||
ScopedPointer <MyApplicationWindow> myMainWindow;
|
||||
};
|
||||
|
||||
// this creates wrapper code to actually launch the app properly.
|
||||
START_JUCE_APPLICATION (MyJUCEApp)
|
||||
@endcode
|
||||
|
||||
@see MessageManager
|
||||
*/
|
||||
class JUCE_API JUCEApplication : public JUCEApplicationBase,
|
||||
public ApplicationCommandTarget
|
||||
{
|
||||
protected:
|
||||
//==============================================================================
|
||||
/** Constructs a JUCE app object.
|
||||
|
||||
If subclasses implement a constructor or destructor, they shouldn't call any
|
||||
JUCE code in there - put your startup/shutdown code in initialise() and
|
||||
shutdown() instead.
|
||||
*/
|
||||
JUCEApplication();
|
||||
|
||||
public:
|
||||
/** Destructor.
|
||||
|
||||
If subclasses implement a constructor or destructor, they shouldn't call any
|
||||
JUCE code in there - put your startup/shutdown code in initialise() and
|
||||
shutdown() instead.
|
||||
*/
|
||||
virtual ~JUCEApplication();
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the global instance of the application object being run. */
|
||||
static JUCEApplication* getInstance() noexcept { return dynamic_cast <JUCEApplication*> (JUCEApplicationBase::getInstance()); }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns true if the application hasn't yet completed its initialise() method
|
||||
and entered the main event loop.
|
||||
|
||||
This is handy for things like splash screens to know when the app's up-and-running
|
||||
properly.
|
||||
*/
|
||||
bool isInitialising() const noexcept { return stillInitialising; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the application's name.
|
||||
|
||||
An application must implement this to name itself.
|
||||
*/
|
||||
virtual const String getApplicationName() = 0;
|
||||
|
||||
/** Returns the application's version number.
|
||||
*/
|
||||
virtual const String getApplicationVersion() = 0;
|
||||
|
||||
/** Checks whether multiple instances of the app are allowed.
|
||||
|
||||
If you application class returns true for this, more than one instance is
|
||||
permitted to run (except on the Mac where this isn't possible).
|
||||
|
||||
If it's false, the second instance won't start, but it you will still get a
|
||||
callback to anotherInstanceStarted() to tell you about this - which
|
||||
gives you a chance to react to what the user was trying to do.
|
||||
*/
|
||||
virtual bool moreThanOneInstanceAllowed();
|
||||
|
||||
/** Indicates that the user has tried to start up another instance of the app.
|
||||
|
||||
This will get called even if moreThanOneInstanceAllowed() is false.
|
||||
*/
|
||||
virtual void anotherInstanceStarted (const String& commandLine);
|
||||
|
||||
/** Called when the operating system is trying to close the application.
|
||||
|
||||
The default implementation of this method is to call quit(), but it may
|
||||
be overloaded to ignore the request or do some other special behaviour
|
||||
instead. For example, you might want to offer the user the chance to save
|
||||
their changes before quitting, and give them the chance to cancel.
|
||||
|
||||
If you want to send a quit signal to your app, this is the correct method
|
||||
to call, because it means that requests that come from the system get handled
|
||||
in the same way as those from your own application code. So e.g. you'd
|
||||
call this method from a "quit" item on a menu bar.
|
||||
*/
|
||||
virtual void systemRequestedQuit();
|
||||
|
||||
/** If any unhandled exceptions make it through to the message dispatch loop, this
|
||||
callback will be triggered, in case you want to log them or do some other
|
||||
type of error-handling.
|
||||
|
||||
If the type of exception is derived from the std::exception class, the pointer
|
||||
passed-in will be valid. If the exception is of unknown type, this pointer
|
||||
will be null.
|
||||
*/
|
||||
virtual void unhandledException (const std::exception* e,
|
||||
const String& sourceFilename,
|
||||
int lineNumber);
|
||||
|
||||
//==============================================================================
|
||||
/** Signals that the main message loop should stop and the application should terminate.
|
||||
|
||||
This isn't synchronous, it just posts a quit message to the main queue, and
|
||||
when this message arrives, the message loop will stop, the shutdown() method
|
||||
will be called, and the app will exit.
|
||||
|
||||
Note that this will cause an unconditional quit to happen, so if you need an
|
||||
extra level before this, e.g. to give the user the chance to save their work
|
||||
and maybe cancel the quit, you'll need to handle this in the systemRequestedQuit()
|
||||
method - see that method's help for more info.
|
||||
|
||||
@see MessageManager
|
||||
*/
|
||||
static void quit();
|
||||
|
||||
/** Sets the value that should be returned as the application's exit code when the
|
||||
app quits.
|
||||
|
||||
This is the value that's returned by the main() function. Normally you'd leave this
|
||||
as 0 unless you want to indicate an error code.
|
||||
|
||||
@see getApplicationReturnValue
|
||||
*/
|
||||
void setApplicationReturnValue (int newReturnValue) noexcept;
|
||||
|
||||
/** Returns the value that has been set as the application's exit code.
|
||||
@see setApplicationReturnValue
|
||||
*/
|
||||
int getApplicationReturnValue() const noexcept { return appReturnValue; }
|
||||
|
||||
/** Returns the application's command line parameters. */
|
||||
const String& getCommandLineParameters() const noexcept { return commandLineParameters; }
|
||||
|
||||
/** Returns true if this executable is running as an app (as opposed to being a plugin
|
||||
or other kind of shared library. */
|
||||
static inline bool isStandaloneApp() noexcept { return createInstance != 0; }
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
ApplicationCommandTarget* getNextCommandTarget();
|
||||
/** @internal */
|
||||
void getCommandInfo (CommandID commandID, ApplicationCommandInfo& result);
|
||||
/** @internal */
|
||||
void getAllCommands (Array <CommandID>& commands);
|
||||
/** @internal */
|
||||
bool perform (const InvocationInfo& info);
|
||||
|
||||
//==============================================================================
|
||||
#ifndef DOXYGEN
|
||||
// The following methods are internal calls - not for public use.
|
||||
static int main (const String& commandLine);
|
||||
static int main (int argc, const char* argv[]);
|
||||
static void sendUnhandledException (const std::exception* e, const char* sourceFile, int lineNumber);
|
||||
bool initialiseApp (const String& commandLine);
|
||||
int shutdownApp();
|
||||
#endif
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
String commandLineParameters;
|
||||
ScopedPointer<InterProcessLock> appLock;
|
||||
ScopedPointer<ActionListener> broadcastCallback;
|
||||
int appReturnValue;
|
||||
bool stillInitialising;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (JUCEApplication);
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCE_APPLICATION_JUCEHEADER__
|
||||
135
modules/juce_gui_basics/application/juce_Initialisation.h
Normal file
135
modules/juce_gui_basics/application/juce_Initialisation.h
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_INITIALISATION_JUCEHEADER__
|
||||
#define __JUCE_INITIALISATION_JUCEHEADER__
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** Initialises Juce's GUI classes.
|
||||
|
||||
If you're embedding Juce into an application that uses its own event-loop rather
|
||||
than using the START_JUCE_APPLICATION macro, call this function before making any
|
||||
Juce calls, to make sure things are initialised correctly.
|
||||
|
||||
Note that if you're creating a Juce DLL for Windows, you may also need to call the
|
||||
Process::setCurrentModuleInstanceHandle() method.
|
||||
|
||||
@see shutdownJuce_GUI()
|
||||
*/
|
||||
JUCE_API void JUCE_CALLTYPE initialiseJuce_GUI();
|
||||
|
||||
/** Clears up any static data being used by Juce's GUI classes.
|
||||
|
||||
If you're embedding Juce into an application that uses its own event-loop rather
|
||||
than using the START_JUCE_APPLICATION macro, call this function in your shutdown
|
||||
code to clean up any juce objects that might be lying around.
|
||||
|
||||
@see initialiseJuce_GUI()
|
||||
*/
|
||||
JUCE_API void JUCE_CALLTYPE shutdownJuce_GUI();
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** A utility object that helps you initialise and shutdown Juce correctly
|
||||
using an RAII pattern.
|
||||
|
||||
When an instance of this class is created, it calls initialiseJuce_GUI(),
|
||||
and when it's deleted, it calls shutdownJuce_GUI(), which lets you easily
|
||||
make sure that these functions are matched correctly.
|
||||
|
||||
This class is particularly handy to use at the beginning of a console app's
|
||||
main() function, because it'll take care of shutting down whenever you return
|
||||
from the main() call.
|
||||
|
||||
@see ScopedJuceInitialiser_NonGUI
|
||||
*/
|
||||
class ScopedJuceInitialiser_GUI
|
||||
{
|
||||
public:
|
||||
/** The constructor simply calls initialiseJuce_GUI(). */
|
||||
ScopedJuceInitialiser_GUI() { initialiseJuce_GUI(); }
|
||||
|
||||
/** The destructor simply calls shutdownJuce_GUI(). */
|
||||
~ScopedJuceInitialiser_GUI() { shutdownJuce_GUI(); }
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/*
|
||||
To start a JUCE app, use this macro: START_JUCE_APPLICATION (AppSubClass) where
|
||||
AppSubClass is the name of a class derived from JUCEApplication.
|
||||
|
||||
See the JUCEApplication class documentation (juce_Application.h) for more details.
|
||||
|
||||
*/
|
||||
#if JUCE_ANDROID
|
||||
#define START_JUCE_APPLICATION(AppClass) \
|
||||
JUCE_NAMESPACE::JUCEApplication* juce_CreateApplication() { return new AppClass(); }
|
||||
|
||||
#elif defined (JUCE_GCC) || defined (__MWERKS__)
|
||||
|
||||
#define START_JUCE_APPLICATION(AppClass) \
|
||||
static JUCE_NAMESPACE::JUCEApplicationBase* juce_CreateApplication() { return new AppClass(); } \
|
||||
int main (int argc, char* argv[]) \
|
||||
{ \
|
||||
JUCE_NAMESPACE::JUCEApplication::createInstance = &juce_CreateApplication; \
|
||||
return JUCE_NAMESPACE::JUCEApplication::main (argc, (const char**) argv); \
|
||||
}
|
||||
|
||||
#elif JUCE_WINDOWS
|
||||
|
||||
#ifdef _CONSOLE
|
||||
#define START_JUCE_APPLICATION(AppClass) \
|
||||
static JUCE_NAMESPACE::JUCEApplicationBase* juce_CreateApplication() { return new AppClass(); } \
|
||||
int main (int, char* argv[]) \
|
||||
{ \
|
||||
JUCE_NAMESPACE::JUCEApplication::createInstance = &juce_CreateApplication; \
|
||||
return JUCE_NAMESPACE::JUCEApplication::main (JUCE_NAMESPACE::Process::getCurrentCommandLineParams()); \
|
||||
}
|
||||
#elif ! defined (_AFXDLL)
|
||||
#ifdef _WINDOWS_
|
||||
#define START_JUCE_APPLICATION(AppClass) \
|
||||
static JUCE_NAMESPACE::JUCEApplicationBase* juce_CreateApplication() { return new AppClass(); } \
|
||||
int WINAPI WinMain (HINSTANCE, HINSTANCE, LPSTR, int) \
|
||||
{ \
|
||||
JUCE_NAMESPACE::JUCEApplication::createInstance = &juce_CreateApplication; \
|
||||
return JUCE_NAMESPACE::JUCEApplication::main (JUCE_NAMESPACE::Process::getCurrentCommandLineParams()); \
|
||||
}
|
||||
#else
|
||||
#define START_JUCE_APPLICATION(AppClass) \
|
||||
static JUCE_NAMESPACE::JUCEApplicationBase* juce_CreateApplication() { return new AppClass(); } \
|
||||
int __stdcall WinMain (void*, void*, const char*, int) \
|
||||
{ \
|
||||
JUCE_NAMESPACE::JUCEApplication::createInstance = &juce_CreateApplication; \
|
||||
return JUCE_NAMESPACE::JUCEApplication::main (JUCE_NAMESPACE::Process::getCurrentCommandLineParams()); \
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif // __JUCE_INITIALISATION_JUCEHEADER__
|
||||
76
modules/juce_gui_basics/buttons/juce_ArrowButton.cpp
Normal file
76
modules/juce_gui_basics/buttons/juce_ArrowButton.cpp
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
//==============================================================================
|
||||
ArrowButton::ArrowButton (const String& name,
|
||||
float arrowDirectionInRadians,
|
||||
const Colour& arrowColour)
|
||||
: Button (name),
|
||||
colour (arrowColour)
|
||||
{
|
||||
path.lineTo (0.0f, 1.0f);
|
||||
path.lineTo (1.0f, 0.5f);
|
||||
path.closeSubPath();
|
||||
|
||||
path.applyTransform (AffineTransform::rotation (float_Pi * 2.0f * arrowDirectionInRadians,
|
||||
0.5f, 0.5f));
|
||||
|
||||
setComponentEffect (&shadow);
|
||||
updateShadowAndOffset();
|
||||
}
|
||||
|
||||
ArrowButton::~ArrowButton()
|
||||
{
|
||||
}
|
||||
|
||||
void ArrowButton::paintButton (Graphics& g,
|
||||
bool /*isMouseOverButton*/,
|
||||
bool /*isButtonDown*/)
|
||||
{
|
||||
g.setColour (colour);
|
||||
|
||||
g.fillPath (path, path.getTransformToScaleToFit ((float) offset,
|
||||
(float) offset,
|
||||
(float) (getWidth() - 3),
|
||||
(float) (getHeight() - 3),
|
||||
false));
|
||||
}
|
||||
|
||||
void ArrowButton::buttonStateChanged()
|
||||
{
|
||||
updateShadowAndOffset();
|
||||
}
|
||||
|
||||
void ArrowButton::updateShadowAndOffset()
|
||||
{
|
||||
offset = (isDown()) ? 1 : 0;
|
||||
|
||||
shadow.setShadowProperties ((isDown()) ? 1.2f : 3.0f,
|
||||
0.3f, -1, 0);
|
||||
}
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
80
modules/juce_gui_basics/buttons/juce_ArrowButton.h
Normal file
80
modules/juce_gui_basics/buttons/juce_ArrowButton.h
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_ARROWBUTTON_JUCEHEADER__
|
||||
#define __JUCE_ARROWBUTTON_JUCEHEADER__
|
||||
|
||||
#include "juce_Button.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A button with an arrow in it.
|
||||
|
||||
@see Button
|
||||
*/
|
||||
class JUCE_API ArrowButton : public Button
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an ArrowButton.
|
||||
|
||||
@param buttonName the name to give the button
|
||||
@param arrowDirection the direction the arrow should point in, where 0.0 is
|
||||
pointing right, 0.25 is down, 0.5 is left, 0.75 is up
|
||||
@param arrowColour the colour to use for the arrow
|
||||
*/
|
||||
ArrowButton (const String& buttonName,
|
||||
float arrowDirection,
|
||||
const Colour& arrowColour);
|
||||
|
||||
/** Destructor. */
|
||||
~ArrowButton();
|
||||
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void paintButton (Graphics& g,
|
||||
bool isMouseOverButton,
|
||||
bool isButtonDown);
|
||||
|
||||
/** @internal */
|
||||
void buttonStateChanged();
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
Colour colour;
|
||||
DropShadowEffect shadow;
|
||||
Path path;
|
||||
int offset;
|
||||
|
||||
void updateShadowAndOffset();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ArrowButton);
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCE_ARROWBUTTON_JUCEHEADER__
|
||||
670
modules/juce_gui_basics/buttons/juce_Button.cpp
Normal file
670
modules/juce_gui_basics/buttons/juce_Button.cpp
Normal file
|
|
@ -0,0 +1,670 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
//==============================================================================
|
||||
class Button::RepeatTimer : public Timer
|
||||
{
|
||||
public:
|
||||
RepeatTimer (Button& owner_) : owner (owner_) {}
|
||||
void timerCallback() { owner.repeatTimerCallback(); }
|
||||
|
||||
private:
|
||||
Button& owner;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RepeatTimer);
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
Button::Button (const String& name)
|
||||
: Component (name),
|
||||
text (name),
|
||||
buttonPressTime (0),
|
||||
lastRepeatTime (0),
|
||||
commandManagerToUse (nullptr),
|
||||
autoRepeatDelay (-1),
|
||||
autoRepeatSpeed (0),
|
||||
autoRepeatMinimumDelay (-1),
|
||||
radioGroupId (0),
|
||||
commandID (0),
|
||||
connectedEdgeFlags (0),
|
||||
buttonState (buttonNormal),
|
||||
lastToggleState (false),
|
||||
clickTogglesState (false),
|
||||
needsToRelease (false),
|
||||
needsRepainting (false),
|
||||
isKeyDown (false),
|
||||
triggerOnMouseDown (false),
|
||||
generateTooltip (false)
|
||||
{
|
||||
setWantsKeyboardFocus (true);
|
||||
isOn.addListener (this);
|
||||
}
|
||||
|
||||
Button::~Button()
|
||||
{
|
||||
isOn.removeListener (this);
|
||||
|
||||
if (commandManagerToUse != nullptr)
|
||||
commandManagerToUse->removeListener (this);
|
||||
|
||||
repeatTimer = nullptr;
|
||||
clearShortcuts();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Button::setButtonText (const String& newText)
|
||||
{
|
||||
if (text != newText)
|
||||
{
|
||||
text = newText;
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
void Button::setTooltip (const String& newTooltip)
|
||||
{
|
||||
SettableTooltipClient::setTooltip (newTooltip);
|
||||
generateTooltip = false;
|
||||
}
|
||||
|
||||
const String Button::getTooltip()
|
||||
{
|
||||
if (generateTooltip && commandManagerToUse != nullptr && commandID != 0)
|
||||
{
|
||||
String tt (commandManagerToUse->getDescriptionOfCommand (commandID));
|
||||
|
||||
Array <KeyPress> keyPresses (commandManagerToUse->getKeyMappings()->getKeyPressesAssignedToCommand (commandID));
|
||||
|
||||
for (int i = 0; i < keyPresses.size(); ++i)
|
||||
{
|
||||
const String key (keyPresses.getReference(i).getTextDescription());
|
||||
|
||||
tt << " [";
|
||||
|
||||
if (key.length() == 1)
|
||||
tt << TRANS("shortcut") << ": '" << key << "']";
|
||||
else
|
||||
tt << key << ']';
|
||||
}
|
||||
|
||||
return tt;
|
||||
}
|
||||
|
||||
return SettableTooltipClient::getTooltip();
|
||||
}
|
||||
|
||||
void Button::setConnectedEdges (const int connectedEdgeFlags_)
|
||||
{
|
||||
if (connectedEdgeFlags != connectedEdgeFlags_)
|
||||
{
|
||||
connectedEdgeFlags = connectedEdgeFlags_;
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Button::setToggleState (const bool shouldBeOn,
|
||||
const bool sendChangeNotification)
|
||||
{
|
||||
if (shouldBeOn != lastToggleState)
|
||||
{
|
||||
if (isOn != shouldBeOn) // this test means that if the value is void rather than explicitly set to
|
||||
isOn = shouldBeOn; // false, it won't be changed unless the required value is true.
|
||||
|
||||
lastToggleState = shouldBeOn;
|
||||
repaint();
|
||||
|
||||
WeakReference<Component> deletionWatcher (this);
|
||||
|
||||
if (sendChangeNotification)
|
||||
{
|
||||
sendClickMessage (ModifierKeys());
|
||||
|
||||
if (deletionWatcher == nullptr)
|
||||
return;
|
||||
}
|
||||
|
||||
if (lastToggleState)
|
||||
{
|
||||
turnOffOtherButtonsInGroup (sendChangeNotification);
|
||||
|
||||
if (deletionWatcher == nullptr)
|
||||
return;
|
||||
}
|
||||
|
||||
sendStateMessage();
|
||||
}
|
||||
}
|
||||
|
||||
void Button::setClickingTogglesState (const bool shouldToggle) noexcept
|
||||
{
|
||||
clickTogglesState = shouldToggle;
|
||||
|
||||
// if you've got clickTogglesState turned on, you shouldn't also connect the button
|
||||
// up to be a command invoker. Instead, your command handler must flip the state of whatever
|
||||
// it is that this button represents, and the button will update its state to reflect this
|
||||
// in the applicationCommandListChanged() method.
|
||||
jassert (commandManagerToUse == nullptr || ! clickTogglesState);
|
||||
}
|
||||
|
||||
bool Button::getClickingTogglesState() const noexcept
|
||||
{
|
||||
return clickTogglesState;
|
||||
}
|
||||
|
||||
void Button::valueChanged (Value& value)
|
||||
{
|
||||
if (value.refersToSameSourceAs (isOn))
|
||||
setToggleState (isOn.getValue(), true);
|
||||
}
|
||||
|
||||
void Button::setRadioGroupId (const int newGroupId)
|
||||
{
|
||||
if (radioGroupId != newGroupId)
|
||||
{
|
||||
radioGroupId = newGroupId;
|
||||
|
||||
if (lastToggleState)
|
||||
turnOffOtherButtonsInGroup (true);
|
||||
}
|
||||
}
|
||||
|
||||
void Button::turnOffOtherButtonsInGroup (const bool sendChangeNotification)
|
||||
{
|
||||
Component* const p = getParentComponent();
|
||||
|
||||
if (p != nullptr && radioGroupId != 0)
|
||||
{
|
||||
WeakReference<Component> deletionWatcher (this);
|
||||
|
||||
for (int i = p->getNumChildComponents(); --i >= 0;)
|
||||
{
|
||||
Component* const c = p->getChildComponent (i);
|
||||
|
||||
if (c != this)
|
||||
{
|
||||
Button* const b = dynamic_cast <Button*> (c);
|
||||
|
||||
if (b != nullptr && b->getRadioGroupId() == radioGroupId)
|
||||
{
|
||||
b->setToggleState (false, sendChangeNotification);
|
||||
|
||||
if (deletionWatcher == nullptr)
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Button::enablementChanged()
|
||||
{
|
||||
updateState();
|
||||
repaint();
|
||||
}
|
||||
|
||||
Button::ButtonState Button::updateState()
|
||||
{
|
||||
return updateState (isMouseOver (true), isMouseButtonDown());
|
||||
}
|
||||
|
||||
Button::ButtonState Button::updateState (const bool over, const bool down)
|
||||
{
|
||||
ButtonState newState = buttonNormal;
|
||||
|
||||
if (isEnabled() && isVisible() && ! isCurrentlyBlockedByAnotherModalComponent())
|
||||
{
|
||||
if ((down && (over || (triggerOnMouseDown && buttonState == buttonDown))) || isKeyDown)
|
||||
newState = buttonDown;
|
||||
else if (over)
|
||||
newState = buttonOver;
|
||||
}
|
||||
|
||||
setState (newState);
|
||||
return newState;
|
||||
}
|
||||
|
||||
void Button::setState (const ButtonState newState)
|
||||
{
|
||||
if (buttonState != newState)
|
||||
{
|
||||
buttonState = newState;
|
||||
repaint();
|
||||
|
||||
if (buttonState == buttonDown)
|
||||
{
|
||||
buttonPressTime = Time::getApproximateMillisecondCounter();
|
||||
lastRepeatTime = 0;
|
||||
}
|
||||
|
||||
sendStateMessage();
|
||||
}
|
||||
}
|
||||
|
||||
bool Button::isDown() const noexcept
|
||||
{
|
||||
return buttonState == buttonDown;
|
||||
}
|
||||
|
||||
bool Button::isOver() const noexcept
|
||||
{
|
||||
return buttonState != buttonNormal;
|
||||
}
|
||||
|
||||
void Button::buttonStateChanged()
|
||||
{
|
||||
}
|
||||
|
||||
uint32 Button::getMillisecondsSinceButtonDown() const noexcept
|
||||
{
|
||||
const uint32 now = Time::getApproximateMillisecondCounter();
|
||||
return now > buttonPressTime ? now - buttonPressTime : 0;
|
||||
}
|
||||
|
||||
void Button::setTriggeredOnMouseDown (const bool isTriggeredOnMouseDown) noexcept
|
||||
{
|
||||
triggerOnMouseDown = isTriggeredOnMouseDown;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Button::clicked()
|
||||
{
|
||||
}
|
||||
|
||||
void Button::clicked (const ModifierKeys& /*modifiers*/)
|
||||
{
|
||||
clicked();
|
||||
}
|
||||
|
||||
static const int clickMessageId = 0x2f3f4f99;
|
||||
|
||||
void Button::triggerClick()
|
||||
{
|
||||
postCommandMessage (clickMessageId);
|
||||
}
|
||||
|
||||
void Button::internalClickCallback (const ModifierKeys& modifiers)
|
||||
{
|
||||
if (clickTogglesState)
|
||||
setToggleState ((radioGroupId != 0) || ! lastToggleState, false);
|
||||
|
||||
sendClickMessage (modifiers);
|
||||
}
|
||||
|
||||
void Button::flashButtonState()
|
||||
{
|
||||
if (isEnabled())
|
||||
{
|
||||
needsToRelease = true;
|
||||
setState (buttonDown);
|
||||
getRepeatTimer().startTimer (100);
|
||||
}
|
||||
}
|
||||
|
||||
void Button::handleCommandMessage (int commandId)
|
||||
{
|
||||
if (commandId == clickMessageId)
|
||||
{
|
||||
if (isEnabled())
|
||||
{
|
||||
flashButtonState();
|
||||
internalClickCallback (ModifierKeys::getCurrentModifiers());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Component::handleCommandMessage (commandId);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Button::addListener (ButtonListener* const newListener)
|
||||
{
|
||||
buttonListeners.add (newListener);
|
||||
}
|
||||
|
||||
void Button::removeListener (ButtonListener* const listener)
|
||||
{
|
||||
buttonListeners.remove (listener);
|
||||
}
|
||||
|
||||
void Button::addButtonListener (ButtonListener* l) { addListener (l); }
|
||||
void Button::removeButtonListener (ButtonListener* l) { removeListener (l); }
|
||||
|
||||
void Button::sendClickMessage (const ModifierKeys& modifiers)
|
||||
{
|
||||
Component::BailOutChecker checker (this);
|
||||
|
||||
if (commandManagerToUse != nullptr && commandID != 0)
|
||||
{
|
||||
ApplicationCommandTarget::InvocationInfo info (commandID);
|
||||
info.invocationMethod = ApplicationCommandTarget::InvocationInfo::fromButton;
|
||||
info.originatingComponent = this;
|
||||
|
||||
commandManagerToUse->invoke (info, true);
|
||||
}
|
||||
|
||||
clicked (modifiers);
|
||||
|
||||
if (! checker.shouldBailOut())
|
||||
buttonListeners.callChecked (checker, &ButtonListener::buttonClicked, this); // (can't use Button::Listener due to idiotic VC2005 bug)
|
||||
}
|
||||
|
||||
void Button::sendStateMessage()
|
||||
{
|
||||
Component::BailOutChecker checker (this);
|
||||
|
||||
buttonStateChanged();
|
||||
|
||||
if (! checker.shouldBailOut())
|
||||
buttonListeners.callChecked (checker, &ButtonListener::buttonStateChanged, this);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Button::paint (Graphics& g)
|
||||
{
|
||||
if (needsToRelease && isEnabled())
|
||||
{
|
||||
needsToRelease = false;
|
||||
needsRepainting = true;
|
||||
}
|
||||
|
||||
paintButton (g, isOver(), isDown());
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Button::mouseEnter (const MouseEvent&)
|
||||
{
|
||||
updateState (true, false);
|
||||
}
|
||||
|
||||
void Button::mouseExit (const MouseEvent&)
|
||||
{
|
||||
updateState (false, false);
|
||||
}
|
||||
|
||||
void Button::mouseDown (const MouseEvent& e)
|
||||
{
|
||||
updateState (true, true);
|
||||
|
||||
if (isDown())
|
||||
{
|
||||
if (autoRepeatDelay >= 0)
|
||||
getRepeatTimer().startTimer (autoRepeatDelay);
|
||||
|
||||
if (triggerOnMouseDown)
|
||||
internalClickCallback (e.mods);
|
||||
}
|
||||
}
|
||||
|
||||
void Button::mouseUp (const MouseEvent& e)
|
||||
{
|
||||
const bool wasDown = isDown();
|
||||
updateState (isMouseOver(), false);
|
||||
|
||||
if (wasDown && isOver() && ! triggerOnMouseDown)
|
||||
internalClickCallback (e.mods);
|
||||
}
|
||||
|
||||
void Button::mouseDrag (const MouseEvent&)
|
||||
{
|
||||
const ButtonState oldState = buttonState;
|
||||
updateState (isMouseOver(), true);
|
||||
|
||||
if (autoRepeatDelay >= 0 && buttonState != oldState && isDown())
|
||||
getRepeatTimer().startTimer (autoRepeatSpeed);
|
||||
}
|
||||
|
||||
void Button::focusGained (FocusChangeType)
|
||||
{
|
||||
updateState();
|
||||
repaint();
|
||||
}
|
||||
|
||||
void Button::focusLost (FocusChangeType)
|
||||
{
|
||||
updateState();
|
||||
repaint();
|
||||
}
|
||||
|
||||
void Button::visibilityChanged()
|
||||
{
|
||||
needsToRelease = false;
|
||||
updateState();
|
||||
}
|
||||
|
||||
void Button::parentHierarchyChanged()
|
||||
{
|
||||
Component* const newKeySource = (shortcuts.size() == 0) ? nullptr : getTopLevelComponent();
|
||||
|
||||
if (newKeySource != keySource.get())
|
||||
{
|
||||
if (keySource != nullptr)
|
||||
keySource->removeKeyListener (this);
|
||||
|
||||
keySource = newKeySource;
|
||||
|
||||
if (keySource != nullptr)
|
||||
keySource->addKeyListener (this);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Button::setCommandToTrigger (ApplicationCommandManager* const commandManagerToUse_,
|
||||
const int commandID_,
|
||||
const bool generateTooltip_)
|
||||
{
|
||||
commandID = commandID_;
|
||||
generateTooltip = generateTooltip_;
|
||||
|
||||
if (commandManagerToUse != commandManagerToUse_)
|
||||
{
|
||||
if (commandManagerToUse != nullptr)
|
||||
commandManagerToUse->removeListener (this);
|
||||
|
||||
commandManagerToUse = commandManagerToUse_;
|
||||
|
||||
if (commandManagerToUse != nullptr)
|
||||
commandManagerToUse->addListener (this);
|
||||
|
||||
// if you've got clickTogglesState turned on, you shouldn't also connect the button
|
||||
// up to be a command invoker. Instead, your command handler must flip the state of whatever
|
||||
// it is that this button represents, and the button will update its state to reflect this
|
||||
// in the applicationCommandListChanged() method.
|
||||
jassert (commandManagerToUse == nullptr || ! clickTogglesState);
|
||||
}
|
||||
|
||||
if (commandManagerToUse != nullptr)
|
||||
applicationCommandListChanged();
|
||||
else
|
||||
setEnabled (true);
|
||||
}
|
||||
|
||||
void Button::applicationCommandInvoked (const ApplicationCommandTarget::InvocationInfo& info)
|
||||
{
|
||||
if (info.commandID == commandID
|
||||
&& (info.commandFlags & ApplicationCommandInfo::dontTriggerVisualFeedback) == 0)
|
||||
{
|
||||
flashButtonState();
|
||||
}
|
||||
}
|
||||
|
||||
void Button::applicationCommandListChanged()
|
||||
{
|
||||
if (commandManagerToUse != nullptr)
|
||||
{
|
||||
ApplicationCommandInfo info (0);
|
||||
|
||||
ApplicationCommandTarget* const target = commandManagerToUse->getTargetForCommand (commandID, info);
|
||||
|
||||
setEnabled (target != nullptr && (info.flags & ApplicationCommandInfo::isDisabled) == 0);
|
||||
|
||||
if (target != nullptr)
|
||||
setToggleState ((info.flags & ApplicationCommandInfo::isTicked) != 0, false);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Button::addShortcut (const KeyPress& key)
|
||||
{
|
||||
if (key.isValid())
|
||||
{
|
||||
jassert (! isRegisteredForShortcut (key)); // already registered!
|
||||
|
||||
shortcuts.add (key);
|
||||
parentHierarchyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void Button::clearShortcuts()
|
||||
{
|
||||
shortcuts.clear();
|
||||
|
||||
parentHierarchyChanged();
|
||||
}
|
||||
|
||||
bool Button::isShortcutPressed() const
|
||||
{
|
||||
if (! isCurrentlyBlockedByAnotherModalComponent())
|
||||
{
|
||||
for (int i = shortcuts.size(); --i >= 0;)
|
||||
if (shortcuts.getReference(i).isCurrentlyDown())
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Button::isRegisteredForShortcut (const KeyPress& key) const
|
||||
{
|
||||
for (int i = shortcuts.size(); --i >= 0;)
|
||||
if (key == shortcuts.getReference(i))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Button::keyStateChanged (const bool, Component*)
|
||||
{
|
||||
if (! isEnabled())
|
||||
return false;
|
||||
|
||||
const bool wasDown = isKeyDown;
|
||||
isKeyDown = isShortcutPressed();
|
||||
|
||||
if (autoRepeatDelay >= 0 && (isKeyDown && ! wasDown))
|
||||
getRepeatTimer().startTimer (autoRepeatDelay);
|
||||
|
||||
updateState();
|
||||
|
||||
if (isEnabled() && wasDown && ! isKeyDown)
|
||||
{
|
||||
internalClickCallback (ModifierKeys::getCurrentModifiers());
|
||||
|
||||
// (return immediately - this button may now have been deleted)
|
||||
return true;
|
||||
}
|
||||
|
||||
return wasDown || isKeyDown;
|
||||
}
|
||||
|
||||
bool Button::keyPressed (const KeyPress&, Component*)
|
||||
{
|
||||
// returning true will avoid forwarding events for keys that we're using as shortcuts
|
||||
return isShortcutPressed();
|
||||
}
|
||||
|
||||
bool Button::keyPressed (const KeyPress& key)
|
||||
{
|
||||
if (isEnabled() && key.isKeyCode (KeyPress::returnKey))
|
||||
{
|
||||
triggerClick();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Button::setRepeatSpeed (const int initialDelayMillisecs,
|
||||
const int repeatMillisecs,
|
||||
const int minimumDelayInMillisecs) noexcept
|
||||
{
|
||||
autoRepeatDelay = initialDelayMillisecs;
|
||||
autoRepeatSpeed = repeatMillisecs;
|
||||
autoRepeatMinimumDelay = jmin (autoRepeatSpeed, minimumDelayInMillisecs);
|
||||
}
|
||||
|
||||
void Button::repeatTimerCallback()
|
||||
{
|
||||
if (needsRepainting)
|
||||
{
|
||||
getRepeatTimer().stopTimer();
|
||||
updateState();
|
||||
needsRepainting = false;
|
||||
}
|
||||
else if (autoRepeatSpeed > 0 && (isKeyDown || (updateState() == buttonDown)))
|
||||
{
|
||||
int repeatSpeed = autoRepeatSpeed;
|
||||
|
||||
if (autoRepeatMinimumDelay >= 0)
|
||||
{
|
||||
double timeHeldDown = jmin (1.0, getMillisecondsSinceButtonDown() / 4000.0);
|
||||
timeHeldDown *= timeHeldDown;
|
||||
|
||||
repeatSpeed = repeatSpeed + (int) (timeHeldDown * (autoRepeatMinimumDelay - repeatSpeed));
|
||||
}
|
||||
|
||||
repeatSpeed = jmax (1, repeatSpeed);
|
||||
|
||||
const uint32 now = Time::getMillisecondCounter();
|
||||
|
||||
// if we've been blocked from repeating often enough, speed up the repeat timer to compensate..
|
||||
if (lastRepeatTime != 0 && (int) (now - lastRepeatTime) > repeatSpeed * 2)
|
||||
repeatSpeed = jmax (1, repeatSpeed / 2);
|
||||
|
||||
lastRepeatTime = now;
|
||||
getRepeatTimer().startTimer (repeatSpeed);
|
||||
|
||||
internalClickCallback (ModifierKeys::getCurrentModifiers());
|
||||
}
|
||||
else if (! needsToRelease)
|
||||
{
|
||||
getRepeatTimer().stopTimer();
|
||||
}
|
||||
}
|
||||
|
||||
Button::RepeatTimer& Button::getRepeatTimer()
|
||||
{
|
||||
if (repeatTimer == nullptr)
|
||||
repeatTimer = new RepeatTimer (*this);
|
||||
|
||||
return *repeatTimer;
|
||||
}
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
515
modules/juce_gui_basics/buttons/juce_Button.h
Normal file
515
modules/juce_gui_basics/buttons/juce_Button.h
Normal file
|
|
@ -0,0 +1,515 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_BUTTON_JUCEHEADER__
|
||||
#define __JUCE_BUTTON_JUCEHEADER__
|
||||
|
||||
#include "../components/juce_Component.h"
|
||||
#include "../keyboard/juce_KeyListener.h"
|
||||
#include "../commands/juce_ApplicationCommandManager.h"
|
||||
#include "../windows/juce_TooltipWindow.h"
|
||||
|
||||
#if JUCE_VC6
|
||||
#define Listener ButtonListener
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A base class for buttons.
|
||||
|
||||
This contains all the logic for button behaviours such as enabling/disabling,
|
||||
responding to shortcut keystrokes, auto-repeating when held down, toggle-buttons
|
||||
and radio groups, etc.
|
||||
|
||||
@see TextButton, DrawableButton, ToggleButton
|
||||
*/
|
||||
class JUCE_API Button : public Component,
|
||||
public SettableTooltipClient,
|
||||
public ApplicationCommandManagerListener,
|
||||
public ValueListener,
|
||||
private KeyListener
|
||||
{
|
||||
protected:
|
||||
//==============================================================================
|
||||
/** Creates a button.
|
||||
|
||||
@param buttonName the text to put in the button (the component's name is also
|
||||
initially set to this string, but these can be changed later
|
||||
using the setName() and setButtonText() methods)
|
||||
*/
|
||||
explicit Button (const String& buttonName);
|
||||
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~Button();
|
||||
|
||||
//==============================================================================
|
||||
/** Changes the button's text.
|
||||
|
||||
@see getButtonText
|
||||
*/
|
||||
void setButtonText (const String& newText);
|
||||
|
||||
/** Returns the text displayed in the button.
|
||||
|
||||
@see setButtonText
|
||||
*/
|
||||
const String& getButtonText() const { return text; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns true if the button is currently being held down by the mouse.
|
||||
|
||||
@see isOver
|
||||
*/
|
||||
bool isDown() const noexcept;
|
||||
|
||||
/** Returns true if the mouse is currently over the button.
|
||||
|
||||
This will be also be true if the mouse is being held down.
|
||||
|
||||
@see isDown
|
||||
*/
|
||||
bool isOver() const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** A button has an on/off state associated with it, and this changes that.
|
||||
|
||||
By default buttons are 'off' and for simple buttons that you click to perform
|
||||
an action you won't change this. Toggle buttons, however will want to
|
||||
change their state when turned on or off.
|
||||
|
||||
@param shouldBeOn whether to set the button's toggle state to be on or
|
||||
off. If it's a member of a button group, this will
|
||||
always try to turn it on, and to turn off any other
|
||||
buttons in the group
|
||||
@param sendChangeNotification if true, a callback will be made to clicked(); if false
|
||||
the button will be repainted but no notification will
|
||||
be sent
|
||||
@see getToggleState, setRadioGroupId
|
||||
*/
|
||||
void setToggleState (bool shouldBeOn,
|
||||
bool sendChangeNotification);
|
||||
|
||||
/** Returns true if the button in 'on'.
|
||||
|
||||
By default buttons are 'off' and for simple buttons that you click to perform
|
||||
an action you won't change this. Toggle buttons, however will want to
|
||||
change their state when turned on or off.
|
||||
|
||||
@see setToggleState
|
||||
*/
|
||||
bool getToggleState() const noexcept { return isOn.getValue(); }
|
||||
|
||||
/** Returns the Value object that represents the botton's toggle state.
|
||||
You can use this Value object to connect the button's state to external values or setters,
|
||||
either by taking a copy of the Value, or by using Value::referTo() to make it point to
|
||||
your own Value object.
|
||||
@see getToggleState, Value
|
||||
*/
|
||||
Value& getToggleStateValue() { return isOn; }
|
||||
|
||||
/** This tells the button to automatically flip the toggle state when
|
||||
the button is clicked.
|
||||
|
||||
If set to true, then before the clicked() callback occurs, the toggle-state
|
||||
of the button is flipped.
|
||||
*/
|
||||
void setClickingTogglesState (bool shouldToggle) noexcept;
|
||||
|
||||
/** Returns true if this button is set to be an automatic toggle-button.
|
||||
|
||||
This returns the last value that was passed to setClickingTogglesState().
|
||||
*/
|
||||
bool getClickingTogglesState() const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Enables the button to act as a member of a mutually-exclusive group
|
||||
of 'radio buttons'.
|
||||
|
||||
If the group ID is set to a non-zero number, then this button will
|
||||
act as part of a group of buttons with the same ID, only one of
|
||||
which can be 'on' at the same time. Note that when it's part of
|
||||
a group, clicking a toggle-button that's 'on' won't turn it off.
|
||||
|
||||
To find other buttons with the same ID, this button will search through
|
||||
its sibling components for ToggleButtons, so all the buttons for a
|
||||
particular group must be placed inside the same parent component.
|
||||
|
||||
Set the group ID back to zero if you want it to act as a normal toggle
|
||||
button again.
|
||||
|
||||
@see getRadioGroupId
|
||||
*/
|
||||
void setRadioGroupId (int newGroupId);
|
||||
|
||||
/** Returns the ID of the group to which this button belongs.
|
||||
|
||||
(See setRadioGroupId() for an explanation of this).
|
||||
*/
|
||||
int getRadioGroupId() const noexcept { return radioGroupId; }
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Used to receive callbacks when a button is clicked.
|
||||
|
||||
@see Button::addListener, Button::removeListener
|
||||
*/
|
||||
class JUCE_API Listener
|
||||
{
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~Listener() {}
|
||||
|
||||
/** Called when the button is clicked. */
|
||||
virtual void buttonClicked (Button* button) = 0;
|
||||
|
||||
/** Called when the button's state changes. */
|
||||
virtual void buttonStateChanged (Button*) {}
|
||||
};
|
||||
|
||||
/** Registers a listener to receive events when this button's state changes.
|
||||
If the listener is already registered, this will not register it again.
|
||||
@see removeListener
|
||||
*/
|
||||
void addListener (Listener* newListener);
|
||||
|
||||
/** Removes a previously-registered button listener
|
||||
@see addListener
|
||||
*/
|
||||
void removeListener (Listener* listener);
|
||||
|
||||
//==============================================================================
|
||||
/** Causes the button to act as if it's been clicked.
|
||||
|
||||
This will asynchronously make the button draw itself going down and up, and
|
||||
will then call back the clicked() method as if mouse was clicked on it.
|
||||
|
||||
@see clicked
|
||||
*/
|
||||
virtual void triggerClick();
|
||||
|
||||
//==============================================================================
|
||||
/** Sets a command ID for this button to automatically invoke when it's clicked.
|
||||
|
||||
When the button is pressed, it will use the given manager to trigger the
|
||||
command ID.
|
||||
|
||||
Obviously be careful that the ApplicationCommandManager doesn't get deleted
|
||||
before this button is. To disable the command triggering, call this method and
|
||||
pass 0 for the parameters.
|
||||
|
||||
If generateTooltip is true, then the button's tooltip will be automatically
|
||||
generated based on the name of this command and its current shortcut key.
|
||||
|
||||
@see addShortcut, getCommandID
|
||||
*/
|
||||
void setCommandToTrigger (ApplicationCommandManager* commandManagerToUse,
|
||||
int commandID,
|
||||
bool generateTooltip);
|
||||
|
||||
/** Returns the command ID that was set by setCommandToTrigger().
|
||||
*/
|
||||
int getCommandID() const noexcept { return commandID; }
|
||||
|
||||
//==============================================================================
|
||||
/** Assigns a shortcut key to trigger the button.
|
||||
|
||||
The button registers itself with its top-level parent component for keypresses.
|
||||
|
||||
Note that a different way of linking buttons to keypresses is by using the
|
||||
setCommandToTrigger() method to invoke a command.
|
||||
|
||||
@see clearShortcuts
|
||||
*/
|
||||
void addShortcut (const KeyPress& key);
|
||||
|
||||
/** Removes all key shortcuts that had been set for this button.
|
||||
|
||||
@see addShortcut
|
||||
*/
|
||||
void clearShortcuts();
|
||||
|
||||
/** Returns true if the given keypress is a shortcut for this button.
|
||||
|
||||
@see addShortcut
|
||||
*/
|
||||
bool isRegisteredForShortcut (const KeyPress& key) const;
|
||||
|
||||
//==============================================================================
|
||||
/** Sets an auto-repeat speed for the button when it is held down.
|
||||
|
||||
(Auto-repeat is disabled by default).
|
||||
|
||||
@param initialDelayInMillisecs how long to wait after the mouse is pressed before
|
||||
triggering the next click. If this is zero, auto-repeat
|
||||
is disabled
|
||||
@param repeatDelayInMillisecs the frequently subsequent repeated clicks should be
|
||||
triggered
|
||||
@param minimumDelayInMillisecs if this is greater than 0, the auto-repeat speed will
|
||||
get faster, the longer the button is held down, up to the
|
||||
minimum interval specified here
|
||||
*/
|
||||
void setRepeatSpeed (int initialDelayInMillisecs,
|
||||
int repeatDelayInMillisecs,
|
||||
int minimumDelayInMillisecs = -1) noexcept;
|
||||
|
||||
/** Sets whether the button click should happen when the mouse is pressed or released.
|
||||
|
||||
By default the button is only considered to have been clicked when the mouse is
|
||||
released, but setting this to true will make it call the clicked() method as soon
|
||||
as the button is pressed.
|
||||
|
||||
This is useful if the button is being used to show a pop-up menu, as it allows
|
||||
the click to be used as a drag onto the menu.
|
||||
*/
|
||||
void setTriggeredOnMouseDown (bool isTriggeredOnMouseDown) noexcept;
|
||||
|
||||
/** Returns the number of milliseconds since the last time the button
|
||||
went into the 'down' state.
|
||||
*/
|
||||
uint32 getMillisecondsSinceButtonDown() const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the tooltip for this button.
|
||||
|
||||
@see TooltipClient, TooltipWindow
|
||||
*/
|
||||
void setTooltip (const String& newTooltip);
|
||||
|
||||
// (implementation of the TooltipClient method)
|
||||
const String getTooltip();
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** A combination of these flags are used by setConnectedEdges().
|
||||
*/
|
||||
enum ConnectedEdgeFlags
|
||||
{
|
||||
ConnectedOnLeft = 1,
|
||||
ConnectedOnRight = 2,
|
||||
ConnectedOnTop = 4,
|
||||
ConnectedOnBottom = 8
|
||||
};
|
||||
|
||||
/** Hints about which edges of the button might be connected to adjoining buttons.
|
||||
|
||||
The value passed in is a bitwise combination of any of the values in the
|
||||
ConnectedEdgeFlags enum.
|
||||
|
||||
E.g. if you are placing two buttons adjacent to each other, you could use this to
|
||||
indicate which edges are touching, and the LookAndFeel might choose to drawn them
|
||||
without rounded corners on the edges that connect. It's only a hint, so the
|
||||
LookAndFeel can choose to ignore it if it's not relevent for this type of
|
||||
button.
|
||||
*/
|
||||
void setConnectedEdges (int connectedEdgeFlags);
|
||||
|
||||
/** Returns the set of flags passed into setConnectedEdges(). */
|
||||
int getConnectedEdgeFlags() const noexcept { return connectedEdgeFlags; }
|
||||
|
||||
/** Indicates whether the button adjoins another one on its left edge.
|
||||
@see setConnectedEdges
|
||||
*/
|
||||
bool isConnectedOnLeft() const noexcept { return (connectedEdgeFlags & ConnectedOnLeft) != 0; }
|
||||
|
||||
/** Indicates whether the button adjoins another one on its right edge.
|
||||
@see setConnectedEdges
|
||||
*/
|
||||
bool isConnectedOnRight() const noexcept { return (connectedEdgeFlags & ConnectedOnRight) != 0; }
|
||||
|
||||
/** Indicates whether the button adjoins another one on its top edge.
|
||||
@see setConnectedEdges
|
||||
*/
|
||||
bool isConnectedOnTop() const noexcept { return (connectedEdgeFlags & ConnectedOnTop) != 0; }
|
||||
|
||||
/** Indicates whether the button adjoins another one on its bottom edge.
|
||||
@see setConnectedEdges
|
||||
*/
|
||||
bool isConnectedOnBottom() const noexcept { return (connectedEdgeFlags & ConnectedOnBottom) != 0; }
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** Used by setState(). */
|
||||
enum ButtonState
|
||||
{
|
||||
buttonNormal,
|
||||
buttonOver,
|
||||
buttonDown
|
||||
};
|
||||
|
||||
/** Can be used to force the button into a particular state.
|
||||
|
||||
This only changes the button's appearance, it won't trigger a click, or stop any mouse-clicks
|
||||
from happening.
|
||||
|
||||
The state that you set here will only last until it is automatically changed when the mouse
|
||||
enters or exits the button, or the mouse-button is pressed or released.
|
||||
*/
|
||||
void setState (const ButtonState newState);
|
||||
|
||||
|
||||
//==============================================================================
|
||||
// These are deprecated - please use addListener() and removeListener() instead!
|
||||
JUCE_DEPRECATED (void addButtonListener (Listener*));
|
||||
JUCE_DEPRECATED (void removeButtonListener (Listener*));
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
/** This method is called when the button has been clicked.
|
||||
|
||||
Subclasses can override this to perform whatever they actions they need
|
||||
to do.
|
||||
|
||||
Alternatively, a ButtonListener can be added to the button, and these listeners
|
||||
will be called when the click occurs.
|
||||
|
||||
@see triggerClick
|
||||
*/
|
||||
virtual void clicked();
|
||||
|
||||
/** This method is called when the button has been clicked.
|
||||
|
||||
By default it just calls clicked(), but you might want to override it to handle
|
||||
things like clicking when a modifier key is pressed, etc.
|
||||
|
||||
@see ModifierKeys
|
||||
*/
|
||||
virtual void clicked (const ModifierKeys& modifiers);
|
||||
|
||||
/** Subclasses should override this to actually paint the button's contents.
|
||||
|
||||
It's better to use this than the paint method, because it gives you information
|
||||
about the over/down state of the button.
|
||||
|
||||
@param g the graphics context to use
|
||||
@param isMouseOverButton true if the button is either in the 'over' or
|
||||
'down' state
|
||||
@param isButtonDown true if the button should be drawn in the 'down' position
|
||||
*/
|
||||
virtual void paintButton (Graphics& g,
|
||||
bool isMouseOverButton,
|
||||
bool isButtonDown) = 0;
|
||||
|
||||
/** Called when the button's up/down/over state changes.
|
||||
|
||||
Subclasses can override this if they need to do something special when the button
|
||||
goes up or down.
|
||||
|
||||
@see isDown, isOver
|
||||
*/
|
||||
virtual void buttonStateChanged();
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
virtual void internalClickCallback (const ModifierKeys& modifiers);
|
||||
/** @internal */
|
||||
void handleCommandMessage (int commandId);
|
||||
/** @internal */
|
||||
void mouseEnter (const MouseEvent& e);
|
||||
/** @internal */
|
||||
void mouseExit (const MouseEvent& e);
|
||||
/** @internal */
|
||||
void mouseDown (const MouseEvent& e);
|
||||
/** @internal */
|
||||
void mouseDrag (const MouseEvent& e);
|
||||
/** @internal */
|
||||
void mouseUp (const MouseEvent& e);
|
||||
/** @internal */
|
||||
bool keyPressed (const KeyPress& key);
|
||||
/** @internal */
|
||||
bool keyPressed (const KeyPress& key, Component* originatingComponent);
|
||||
/** @internal */
|
||||
bool keyStateChanged (bool isKeyDown, Component* originatingComponent);
|
||||
/** @internal */
|
||||
void paint (Graphics& g);
|
||||
/** @internal */
|
||||
void parentHierarchyChanged();
|
||||
/** @internal */
|
||||
void visibilityChanged();
|
||||
/** @internal */
|
||||
void focusGained (FocusChangeType cause);
|
||||
/** @internal */
|
||||
void focusLost (FocusChangeType cause);
|
||||
/** @internal */
|
||||
void enablementChanged();
|
||||
/** @internal */
|
||||
void applicationCommandInvoked (const ApplicationCommandTarget::InvocationInfo&);
|
||||
/** @internal */
|
||||
void applicationCommandListChanged();
|
||||
/** @internal */
|
||||
void valueChanged (Value& value);
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
Array <KeyPress> shortcuts;
|
||||
WeakReference<Component> keySource;
|
||||
String text;
|
||||
ListenerList <Listener> buttonListeners;
|
||||
|
||||
class RepeatTimer;
|
||||
friend class RepeatTimer;
|
||||
friend class ScopedPointer <RepeatTimer>;
|
||||
ScopedPointer <RepeatTimer> repeatTimer;
|
||||
uint32 buttonPressTime, lastRepeatTime;
|
||||
ApplicationCommandManager* commandManagerToUse;
|
||||
int autoRepeatDelay, autoRepeatSpeed, autoRepeatMinimumDelay;
|
||||
int radioGroupId, commandID, connectedEdgeFlags;
|
||||
ButtonState buttonState;
|
||||
|
||||
Value isOn;
|
||||
bool lastToggleState : 1;
|
||||
bool clickTogglesState : 1;
|
||||
bool needsToRelease : 1;
|
||||
bool needsRepainting : 1;
|
||||
bool isKeyDown : 1;
|
||||
bool triggerOnMouseDown : 1;
|
||||
bool generateTooltip : 1;
|
||||
|
||||
void repeatTimerCallback();
|
||||
RepeatTimer& getRepeatTimer();
|
||||
|
||||
ButtonState updateState();
|
||||
ButtonState updateState (bool isOver, bool isDown);
|
||||
bool isShortcutPressed() const;
|
||||
void turnOffOtherButtonsInGroup (bool sendChangeNotification);
|
||||
|
||||
void flashButtonState();
|
||||
void sendClickMessage (const ModifierKeys& modifiers);
|
||||
void sendStateMessage();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Button);
|
||||
};
|
||||
|
||||
#ifndef DOXYGEN
|
||||
/** This typedef is just for compatibility with old code and VC6 - newer code should use Button::Listener instead. */
|
||||
typedef Button::Listener ButtonListener;
|
||||
#endif
|
||||
|
||||
#if JUCE_VC6
|
||||
#undef Listener
|
||||
#endif
|
||||
|
||||
#endif // __JUCE_BUTTON_JUCEHEADER__
|
||||
289
modules/juce_gui_basics/buttons/juce_DrawableButton.cpp
Normal file
289
modules/juce_gui_basics/buttons/juce_DrawableButton.cpp
Normal file
|
|
@ -0,0 +1,289 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
//==============================================================================
|
||||
DrawableButton::DrawableButton (const String& name,
|
||||
const DrawableButton::ButtonStyle buttonStyle)
|
||||
: Button (name),
|
||||
style (buttonStyle),
|
||||
currentImage (nullptr),
|
||||
edgeIndent (3)
|
||||
{
|
||||
if (buttonStyle == ImageOnButtonBackground)
|
||||
{
|
||||
backgroundOff = Colour (0xffbbbbff);
|
||||
backgroundOn = Colour (0xff3333ff);
|
||||
}
|
||||
else
|
||||
{
|
||||
backgroundOff = Colours::transparentBlack;
|
||||
backgroundOn = Colour (0xaabbbbff);
|
||||
}
|
||||
}
|
||||
|
||||
DrawableButton::~DrawableButton()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void DrawableButton::setImages (const Drawable* normal,
|
||||
const Drawable* over,
|
||||
const Drawable* down,
|
||||
const Drawable* disabled,
|
||||
const Drawable* normalOn,
|
||||
const Drawable* overOn,
|
||||
const Drawable* downOn,
|
||||
const Drawable* disabledOn)
|
||||
{
|
||||
jassert (normal != nullptr); // you really need to give it at least a normal image..
|
||||
|
||||
if (normal != nullptr) normalImage = normal->createCopy();
|
||||
if (over != nullptr) overImage = over->createCopy();
|
||||
if (down != nullptr) downImage = down->createCopy();
|
||||
if (disabled != nullptr) disabledImage = disabled->createCopy();
|
||||
if (normalOn != nullptr) normalImageOn = normalOn->createCopy();
|
||||
if (overOn != nullptr) overImageOn = overOn->createCopy();
|
||||
if (downOn != nullptr) downImageOn = downOn->createCopy();
|
||||
if (disabledOn != nullptr) disabledImageOn = disabledOn->createCopy();
|
||||
|
||||
buttonStateChanged();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void DrawableButton::setButtonStyle (const DrawableButton::ButtonStyle newStyle)
|
||||
{
|
||||
if (style != newStyle)
|
||||
{
|
||||
style = newStyle;
|
||||
buttonStateChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawableButton::setBackgroundColours (const Colour& toggledOffColour,
|
||||
const Colour& toggledOnColour)
|
||||
{
|
||||
if (backgroundOff != toggledOffColour
|
||||
|| backgroundOn != toggledOnColour)
|
||||
{
|
||||
backgroundOff = toggledOffColour;
|
||||
backgroundOn = toggledOnColour;
|
||||
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
const Colour& DrawableButton::getBackgroundColour() const noexcept
|
||||
{
|
||||
return getToggleState() ? backgroundOn
|
||||
: backgroundOff;
|
||||
}
|
||||
|
||||
void DrawableButton::setEdgeIndent (const int numPixelsIndent)
|
||||
{
|
||||
edgeIndent = numPixelsIndent;
|
||||
repaint();
|
||||
resized();
|
||||
}
|
||||
|
||||
void DrawableButton::resized()
|
||||
{
|
||||
Button::resized();
|
||||
|
||||
if (currentImage != nullptr)
|
||||
{
|
||||
if (style == ImageRaw)
|
||||
{
|
||||
currentImage->setOriginWithOriginalSize (Point<float>());
|
||||
}
|
||||
else
|
||||
{
|
||||
Rectangle<int> imageSpace;
|
||||
|
||||
if (style == ImageOnButtonBackground)
|
||||
{
|
||||
imageSpace = getLocalBounds().reduced (getWidth() / 4, getHeight() / 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
const int textH = (style == ImageAboveTextLabel) ? jmin (16, proportionOfHeight (0.25f)) : 0;
|
||||
|
||||
const int indentX = jmin (edgeIndent, proportionOfWidth (0.3f));
|
||||
const int indentY = jmin (edgeIndent, proportionOfHeight (0.3f));
|
||||
|
||||
imageSpace.setBounds (indentX, indentY,
|
||||
getWidth() - indentX * 2,
|
||||
getHeight() - indentY * 2 - textH);
|
||||
}
|
||||
|
||||
currentImage->setTransformToFit (imageSpace.toFloat(), RectanglePlacement::centred);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DrawableButton::buttonStateChanged()
|
||||
{
|
||||
repaint();
|
||||
|
||||
Drawable* imageToDraw = nullptr;
|
||||
float opacity = 1.0f;
|
||||
|
||||
if (isEnabled())
|
||||
{
|
||||
imageToDraw = getCurrentImage();
|
||||
}
|
||||
else
|
||||
{
|
||||
imageToDraw = getToggleState() ? disabledImageOn
|
||||
: disabledImage;
|
||||
|
||||
if (imageToDraw == nullptr)
|
||||
{
|
||||
opacity = 0.4f;
|
||||
imageToDraw = getNormalImage();
|
||||
}
|
||||
}
|
||||
|
||||
if (imageToDraw != currentImage)
|
||||
{
|
||||
removeChildComponent (currentImage);
|
||||
currentImage = imageToDraw;
|
||||
|
||||
if (currentImage != nullptr)
|
||||
{
|
||||
currentImage->setInterceptsMouseClicks (false, false);
|
||||
addAndMakeVisible (currentImage);
|
||||
DrawableButton::resized();
|
||||
}
|
||||
}
|
||||
|
||||
if (currentImage != nullptr)
|
||||
currentImage->setAlpha (opacity);
|
||||
}
|
||||
|
||||
void DrawableButton::paintButton (Graphics& g,
|
||||
bool isMouseOverButton,
|
||||
bool isButtonDown)
|
||||
{
|
||||
if (style == ImageOnButtonBackground)
|
||||
{
|
||||
getLookAndFeel().drawButtonBackground (g, *this,
|
||||
getBackgroundColour(),
|
||||
isMouseOverButton,
|
||||
isButtonDown);
|
||||
}
|
||||
else
|
||||
{
|
||||
g.fillAll (getBackgroundColour());
|
||||
|
||||
const int textH = (style == ImageAboveTextLabel)
|
||||
? jmin (16, proportionOfHeight (0.25f))
|
||||
: 0;
|
||||
|
||||
if (textH > 0)
|
||||
{
|
||||
g.setFont ((float) textH);
|
||||
|
||||
g.setColour (findColour (DrawableButton::textColourId)
|
||||
.withMultipliedAlpha (isEnabled() ? 1.0f : 0.4f));
|
||||
|
||||
g.drawFittedText (getButtonText(),
|
||||
2, getHeight() - textH - 1,
|
||||
getWidth() - 4, textH,
|
||||
Justification::centred, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
Drawable* DrawableButton::getCurrentImage() const noexcept
|
||||
{
|
||||
if (isDown())
|
||||
return getDownImage();
|
||||
|
||||
if (isOver())
|
||||
return getOverImage();
|
||||
|
||||
return getNormalImage();
|
||||
}
|
||||
|
||||
Drawable* DrawableButton::getNormalImage() const noexcept
|
||||
{
|
||||
return (getToggleState() && normalImageOn != nullptr) ? normalImageOn
|
||||
: normalImage;
|
||||
}
|
||||
|
||||
Drawable* DrawableButton::getOverImage() const noexcept
|
||||
{
|
||||
Drawable* d = normalImage;
|
||||
|
||||
if (getToggleState())
|
||||
{
|
||||
if (overImageOn != nullptr)
|
||||
d = overImageOn;
|
||||
else if (normalImageOn != nullptr)
|
||||
d = normalImageOn;
|
||||
else if (overImage != nullptr)
|
||||
d = overImage;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (overImage != nullptr)
|
||||
d = overImage;
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
Drawable* DrawableButton::getDownImage() const noexcept
|
||||
{
|
||||
Drawable* d = normalImage;
|
||||
|
||||
if (getToggleState())
|
||||
{
|
||||
if (downImageOn != nullptr)
|
||||
d = downImageOn;
|
||||
else if (overImageOn != nullptr)
|
||||
d = overImageOn;
|
||||
else if (normalImageOn != nullptr)
|
||||
d = normalImageOn;
|
||||
else if (downImage != nullptr)
|
||||
d = downImage;
|
||||
else
|
||||
d = getOverImage();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (downImage != nullptr)
|
||||
d = downImage;
|
||||
else
|
||||
d = getOverImage();
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
193
modules/juce_gui_basics/buttons/juce_DrawableButton.h
Normal file
193
modules/juce_gui_basics/buttons/juce_DrawableButton.h
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_DRAWABLEBUTTON_JUCEHEADER__
|
||||
#define __JUCE_DRAWABLEBUTTON_JUCEHEADER__
|
||||
|
||||
#include "juce_Button.h"
|
||||
#include "../drawables/juce_Drawable.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A button that displays a Drawable.
|
||||
|
||||
Up to three Drawable objects can be given to this button, to represent the
|
||||
'normal', 'over' and 'down' states.
|
||||
|
||||
@see Button
|
||||
*/
|
||||
class JUCE_API DrawableButton : public Button
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
enum ButtonStyle
|
||||
{
|
||||
ImageFitted, /**< The button will just display the images, but will resize and centre them to fit inside it. */
|
||||
ImageRaw, /**< The button will just display the images in their normal size and position.
|
||||
This leaves it up to the caller to make sure the images are the correct size and position for the button. */
|
||||
ImageAboveTextLabel, /**< Draws the button as a text label across the bottom with the image resized and scaled to fit above it. */
|
||||
ImageOnButtonBackground /**< Draws the button as a standard rounded-rectangle button with the image on top. */
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Creates a DrawableButton.
|
||||
|
||||
After creating one of these, use setImages() to specify the drawables to use.
|
||||
|
||||
@param buttonName the name to give the component
|
||||
@param buttonStyle the layout to use
|
||||
|
||||
@see ButtonStyle, setButtonStyle, setImages
|
||||
*/
|
||||
DrawableButton (const String& buttonName,
|
||||
ButtonStyle buttonStyle);
|
||||
|
||||
/** Destructor. */
|
||||
~DrawableButton();
|
||||
|
||||
//==============================================================================
|
||||
/** Sets up the images to draw for the various button states.
|
||||
|
||||
The button will keep its own internal copies of these drawables.
|
||||
|
||||
@param normalImage the thing to draw for the button's 'normal' state. An internal copy
|
||||
will be made of the object passed-in if it is non-zero.
|
||||
@param overImage the thing to draw for the button's 'over' state - if this is
|
||||
zero, the button's normal image will be used when the mouse is
|
||||
over it. An internal copy will be made of the object passed-in
|
||||
if it is non-zero.
|
||||
@param downImage the thing to draw for the button's 'down' state - if this is
|
||||
zero, the 'over' image will be used instead (or the normal image
|
||||
as a last resort). An internal copy will be made of the object
|
||||
passed-in if it is non-zero.
|
||||
@param disabledImage an image to draw when the button is disabled. If this is zero,
|
||||
the normal image will be drawn with a reduced opacity instead.
|
||||
An internal copy will be made of the object passed-in if it is
|
||||
non-zero.
|
||||
@param normalImageOn same as the normalImage, but this is used when the button's toggle
|
||||
state is 'on'. If this is 0, the normal image is used instead
|
||||
@param overImageOn same as the overImage, but this is used when the button's toggle
|
||||
state is 'on'. If this is 0, the normalImageOn is drawn instead
|
||||
@param downImageOn same as the downImage, but this is used when the button's toggle
|
||||
state is 'on'. If this is 0, the overImageOn is drawn instead
|
||||
@param disabledImageOn same as the disabledImage, but this is used when the button's toggle
|
||||
state is 'on'. If this is 0, the normal image will be drawn instead
|
||||
with a reduced opacity
|
||||
*/
|
||||
void setImages (const Drawable* normalImage,
|
||||
const Drawable* overImage = nullptr,
|
||||
const Drawable* downImage = nullptr,
|
||||
const Drawable* disabledImage = nullptr,
|
||||
const Drawable* normalImageOn = nullptr,
|
||||
const Drawable* overImageOn = nullptr,
|
||||
const Drawable* downImageOn = nullptr,
|
||||
const Drawable* disabledImageOn = nullptr);
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** Changes the button's style.
|
||||
|
||||
@see ButtonStyle
|
||||
*/
|
||||
void setButtonStyle (ButtonStyle newStyle);
|
||||
|
||||
//==============================================================================
|
||||
/** Changes the button's background colours.
|
||||
|
||||
The toggledOffColour is the colour to use when the button's toggle state
|
||||
is off, and toggledOnColour when it's on.
|
||||
|
||||
For an ImageOnly or ImageAboveTextLabel style, the background colour is
|
||||
used to fill the background of the component.
|
||||
|
||||
For an ImageOnButtonBackground style, the colour is used to draw the
|
||||
button's lozenge shape and exactly how the colour's used will depend
|
||||
on the LookAndFeel.
|
||||
*/
|
||||
void setBackgroundColours (const Colour& toggledOffColour,
|
||||
const Colour& toggledOnColour);
|
||||
|
||||
/** Returns the current background colour being used.
|
||||
|
||||
@see setBackgroundColour
|
||||
*/
|
||||
const Colour& getBackgroundColour() const noexcept;
|
||||
|
||||
/** Gives the button an optional amount of space around the edge of the drawable.
|
||||
|
||||
This will only apply to ImageFitted or ImageRaw styles, it won't affect the
|
||||
ones on a button background. If the button is too small for the given gap, a
|
||||
smaller gap will be used.
|
||||
|
||||
By default there's a gap of about 3 pixels.
|
||||
*/
|
||||
void setEdgeIndent (int numPixelsIndent);
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the image that the button is currently displaying. */
|
||||
Drawable* getCurrentImage() const noexcept;
|
||||
Drawable* getNormalImage() const noexcept;
|
||||
Drawable* getOverImage() const noexcept;
|
||||
Drawable* getDownImage() const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** A set of colour IDs to use to change the colour of various aspects of the link.
|
||||
|
||||
These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
|
||||
methods.
|
||||
|
||||
@see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
|
||||
*/
|
||||
enum ColourIds
|
||||
{
|
||||
textColourId = 0x1004010, /**< The colour to use for the URL text. */
|
||||
};
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void paintButton (Graphics& g,
|
||||
bool isMouseOverButton,
|
||||
bool isButtonDown);
|
||||
/** @internal */
|
||||
void buttonStateChanged();
|
||||
/** @internal */
|
||||
void resized();
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
ButtonStyle style;
|
||||
ScopedPointer <Drawable> normalImage, overImage, downImage, disabledImage;
|
||||
ScopedPointer <Drawable> normalImageOn, overImageOn, downImageOn, disabledImageOn;
|
||||
Drawable* currentImage;
|
||||
Colour backgroundOff, backgroundOn;
|
||||
int edgeIndent;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DrawableButton);
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCE_DRAWABLEBUTTON_JUCEHEADER__
|
||||
110
modules/juce_gui_basics/buttons/juce_HyperlinkButton.cpp
Normal file
110
modules/juce_gui_basics/buttons/juce_HyperlinkButton.cpp
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
//==============================================================================
|
||||
HyperlinkButton::HyperlinkButton (const String& linkText,
|
||||
const URL& linkURL)
|
||||
: Button (linkText),
|
||||
url (linkURL),
|
||||
font (14.0f, Font::underlined),
|
||||
resizeFont (true),
|
||||
justification (Justification::centred)
|
||||
{
|
||||
setMouseCursor (MouseCursor::PointingHandCursor);
|
||||
setTooltip (linkURL.toString (false));
|
||||
}
|
||||
|
||||
HyperlinkButton::~HyperlinkButton()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void HyperlinkButton::setFont (const Font& newFont,
|
||||
const bool resizeToMatchComponentHeight,
|
||||
const Justification& justificationType)
|
||||
{
|
||||
font = newFont;
|
||||
resizeFont = resizeToMatchComponentHeight;
|
||||
justification = justificationType;
|
||||
repaint();
|
||||
}
|
||||
|
||||
void HyperlinkButton::setURL (const URL& newURL) noexcept
|
||||
{
|
||||
url = newURL;
|
||||
setTooltip (newURL.toString (false));
|
||||
}
|
||||
|
||||
Font HyperlinkButton::getFontToUse() const
|
||||
{
|
||||
Font f (font);
|
||||
|
||||
if (resizeFont)
|
||||
f.setHeight (getHeight() * 0.7f);
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
void HyperlinkButton::changeWidthToFitText()
|
||||
{
|
||||
setSize (getFontToUse().getStringWidth (getName()) + 6, getHeight());
|
||||
}
|
||||
|
||||
void HyperlinkButton::colourChanged()
|
||||
{
|
||||
repaint();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void HyperlinkButton::clicked()
|
||||
{
|
||||
if (url.isWellFormed())
|
||||
url.launchInDefaultBrowser();
|
||||
}
|
||||
|
||||
void HyperlinkButton::paintButton (Graphics& g,
|
||||
bool isMouseOverButton,
|
||||
bool isButtonDown)
|
||||
{
|
||||
const Colour textColour (findColour (textColourId));
|
||||
|
||||
if (isEnabled())
|
||||
g.setColour ((isMouseOverButton) ? textColour.darker ((isButtonDown) ? 1.3f : 0.4f)
|
||||
: textColour);
|
||||
else
|
||||
g.setColour (textColour.withMultipliedAlpha (0.4f));
|
||||
|
||||
g.setFont (getFontToUse());
|
||||
|
||||
g.drawText (getButtonText(),
|
||||
2, 0, getWidth() - 2, getHeight(),
|
||||
justification.getOnlyHorizontalFlags() | Justification::verticallyCentred,
|
||||
true);
|
||||
}
|
||||
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
117
modules/juce_gui_basics/buttons/juce_HyperlinkButton.h
Normal file
117
modules/juce_gui_basics/buttons/juce_HyperlinkButton.h
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_HYPERLINKBUTTON_JUCEHEADER__
|
||||
#define __JUCE_HYPERLINKBUTTON_JUCEHEADER__
|
||||
|
||||
#include "juce_Button.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A button showing an underlined weblink, that will launch the link
|
||||
when it's clicked.
|
||||
|
||||
@see Button
|
||||
*/
|
||||
class JUCE_API HyperlinkButton : public Button
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a HyperlinkButton.
|
||||
|
||||
@param linkText the text that will be displayed in the button - this is
|
||||
also set as the Component's name, but the text can be
|
||||
changed later with the Button::getButtonText() method
|
||||
@param linkURL the URL to launch when the user clicks the button
|
||||
*/
|
||||
HyperlinkButton (const String& linkText,
|
||||
const URL& linkURL);
|
||||
|
||||
/** Destructor. */
|
||||
~HyperlinkButton();
|
||||
|
||||
//==============================================================================
|
||||
/** Changes the font to use for the text.
|
||||
|
||||
If resizeToMatchComponentHeight is true, the font's height will be adjusted
|
||||
to match the size of the component.
|
||||
*/
|
||||
void setFont (const Font& newFont,
|
||||
bool resizeToMatchComponentHeight,
|
||||
const Justification& justificationType = Justification::horizontallyCentred);
|
||||
|
||||
//==============================================================================
|
||||
/** A set of colour IDs to use to change the colour of various aspects of the link.
|
||||
|
||||
These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
|
||||
methods.
|
||||
|
||||
@see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
|
||||
*/
|
||||
enum ColourIds
|
||||
{
|
||||
textColourId = 0x1001f00, /**< The colour to use for the URL text. */
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Changes the URL that the button will trigger. */
|
||||
void setURL (const URL& newURL) noexcept;
|
||||
|
||||
/** Returns the URL that the button will trigger. */
|
||||
const URL& getURL() const noexcept { return url; }
|
||||
|
||||
//==============================================================================
|
||||
/** Resizes the button horizontally to fit snugly around the text.
|
||||
|
||||
This won't affect the button's height.
|
||||
*/
|
||||
void changeWidthToFitText();
|
||||
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void clicked();
|
||||
/** @internal */
|
||||
void colourChanged();
|
||||
/** @internal */
|
||||
void paintButton (Graphics& g,
|
||||
bool isMouseOverButton,
|
||||
bool isButtonDown);
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
URL url;
|
||||
Font font;
|
||||
bool resizeFont;
|
||||
Justification justification;
|
||||
|
||||
Font getFontToUse() const;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HyperlinkButton);
|
||||
};
|
||||
|
||||
#endif // __JUCE_HYPERLINKBUTTON_JUCEHEADER__
|
||||
195
modules/juce_gui_basics/buttons/juce_ImageButton.cpp
Normal file
195
modules/juce_gui_basics/buttons/juce_ImageButton.cpp
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
//==============================================================================
|
||||
ImageButton::ImageButton (const String& text_)
|
||||
: Button (text_),
|
||||
scaleImageToFit (true),
|
||||
preserveProportions (true),
|
||||
alphaThreshold (0),
|
||||
imageX (0),
|
||||
imageY (0),
|
||||
imageW (0),
|
||||
imageH (0)
|
||||
{
|
||||
}
|
||||
|
||||
ImageButton::~ImageButton()
|
||||
{
|
||||
}
|
||||
|
||||
void ImageButton::setImages (const bool resizeButtonNowToFitThisImage,
|
||||
const bool rescaleImagesWhenButtonSizeChanges,
|
||||
const bool preserveImageProportions,
|
||||
const Image& normalImage_,
|
||||
const float imageOpacityWhenNormal,
|
||||
const Colour& overlayColourWhenNormal,
|
||||
const Image& overImage_,
|
||||
const float imageOpacityWhenOver,
|
||||
const Colour& overlayColourWhenOver,
|
||||
const Image& downImage_,
|
||||
const float imageOpacityWhenDown,
|
||||
const Colour& overlayColourWhenDown,
|
||||
const float hitTestAlphaThreshold)
|
||||
{
|
||||
normalImage = normalImage_;
|
||||
overImage = overImage_;
|
||||
downImage = downImage_;
|
||||
|
||||
if (resizeButtonNowToFitThisImage && normalImage.isValid())
|
||||
{
|
||||
imageW = normalImage.getWidth();
|
||||
imageH = normalImage.getHeight();
|
||||
|
||||
setSize (imageW, imageH);
|
||||
}
|
||||
|
||||
scaleImageToFit = rescaleImagesWhenButtonSizeChanges;
|
||||
preserveProportions = preserveImageProportions;
|
||||
|
||||
normalOpacity = imageOpacityWhenNormal;
|
||||
normalOverlay = overlayColourWhenNormal;
|
||||
overOpacity = imageOpacityWhenOver;
|
||||
overOverlay = overlayColourWhenOver;
|
||||
downOpacity = imageOpacityWhenDown;
|
||||
downOverlay = overlayColourWhenDown;
|
||||
|
||||
alphaThreshold = (unsigned char) jlimit (0, 0xff, roundToInt (255.0f * hitTestAlphaThreshold));
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
Image ImageButton::getCurrentImage() const
|
||||
{
|
||||
if (isDown() || getToggleState())
|
||||
return getDownImage();
|
||||
|
||||
if (isOver())
|
||||
return getOverImage();
|
||||
|
||||
return getNormalImage();
|
||||
}
|
||||
|
||||
Image ImageButton::getNormalImage() const
|
||||
{
|
||||
return normalImage;
|
||||
}
|
||||
|
||||
Image ImageButton::getOverImage() const
|
||||
{
|
||||
return overImage.isValid() ? overImage
|
||||
: normalImage;
|
||||
}
|
||||
|
||||
Image ImageButton::getDownImage() const
|
||||
{
|
||||
return downImage.isValid() ? downImage
|
||||
: getOverImage();
|
||||
}
|
||||
|
||||
void ImageButton::paintButton (Graphics& g,
|
||||
bool isMouseOverButton,
|
||||
bool isButtonDown)
|
||||
{
|
||||
if (! isEnabled())
|
||||
{
|
||||
isMouseOverButton = false;
|
||||
isButtonDown = false;
|
||||
}
|
||||
|
||||
Image im (getCurrentImage());
|
||||
|
||||
if (im.isValid())
|
||||
{
|
||||
const int iw = im.getWidth();
|
||||
const int ih = im.getHeight();
|
||||
imageW = getWidth();
|
||||
imageH = getHeight();
|
||||
imageX = (imageW - iw) >> 1;
|
||||
imageY = (imageH - ih) >> 1;
|
||||
|
||||
if (scaleImageToFit)
|
||||
{
|
||||
if (preserveProportions)
|
||||
{
|
||||
int newW, newH;
|
||||
const float imRatio = ih / (float)iw;
|
||||
const float destRatio = imageH / (float)imageW;
|
||||
|
||||
if (imRatio > destRatio)
|
||||
{
|
||||
newW = roundToInt (imageH / imRatio);
|
||||
newH = imageH;
|
||||
}
|
||||
else
|
||||
{
|
||||
newW = imageW;
|
||||
newH = roundToInt (imageW * imRatio);
|
||||
}
|
||||
|
||||
imageX = (imageW - newW) / 2;
|
||||
imageY = (imageH - newH) / 2;
|
||||
imageW = newW;
|
||||
imageH = newH;
|
||||
}
|
||||
else
|
||||
{
|
||||
imageX = 0;
|
||||
imageY = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (! scaleImageToFit)
|
||||
{
|
||||
imageW = iw;
|
||||
imageH = ih;
|
||||
}
|
||||
|
||||
getLookAndFeel().drawImageButton (g, &im, imageX, imageY, imageW, imageH,
|
||||
isButtonDown ? downOverlay
|
||||
: (isMouseOverButton ? overOverlay
|
||||
: normalOverlay),
|
||||
isButtonDown ? downOpacity
|
||||
: (isMouseOverButton ? overOpacity
|
||||
: normalOpacity),
|
||||
*this);
|
||||
}
|
||||
}
|
||||
|
||||
bool ImageButton::hitTest (int x, int y)
|
||||
{
|
||||
if (alphaThreshold == 0)
|
||||
return true;
|
||||
|
||||
Image im (getCurrentImage());
|
||||
|
||||
return im.isNull() || (imageW > 0 && imageH > 0
|
||||
&& alphaThreshold < im.getPixelAt (((x - imageX) * im.getWidth()) / imageW,
|
||||
((y - imageY) * im.getHeight()) / imageH).getAlpha());
|
||||
}
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
154
modules/juce_gui_basics/buttons/juce_ImageButton.h
Normal file
154
modules/juce_gui_basics/buttons/juce_ImageButton.h
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_IMAGEBUTTON_JUCEHEADER__
|
||||
#define __JUCE_IMAGEBUTTON_JUCEHEADER__
|
||||
|
||||
#include "juce_Button.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
As the title suggests, this is a button containing an image.
|
||||
|
||||
The colour and transparency of the image can be set to vary when the
|
||||
button state changes.
|
||||
|
||||
@see Button, ShapeButton, TextButton
|
||||
*/
|
||||
class JUCE_API ImageButton : public Button
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an ImageButton.
|
||||
|
||||
Use setImage() to specify the image to use. The colours and opacities that
|
||||
are specified here can be changed later using setDrawingOptions().
|
||||
|
||||
@param name the name to give the component
|
||||
*/
|
||||
explicit ImageButton (const String& name);
|
||||
|
||||
/** Destructor. */
|
||||
~ImageButton();
|
||||
|
||||
//==============================================================================
|
||||
/** Sets up the images to draw in various states.
|
||||
|
||||
@param resizeButtonNowToFitThisImage if true, the button will be immediately
|
||||
resized to the same dimensions as the normal image
|
||||
@param rescaleImagesWhenButtonSizeChanges if true, the image will be rescaled to fit the
|
||||
button when the button's size changes
|
||||
@param preserveImageProportions if true then any rescaling of the image to fit
|
||||
the button will keep the image's x and y proportions
|
||||
correct - i.e. it won't distort its shape, although
|
||||
this might create gaps around the edges
|
||||
@param normalImage the image to use when the button is in its normal state.
|
||||
button no longer needs it.
|
||||
@param imageOpacityWhenNormal the opacity to use when drawing the normal image.
|
||||
@param overlayColourWhenNormal an overlay colour to use to fill the alpha channel of the
|
||||
normal image - if this colour is transparent, no overlay
|
||||
will be drawn. The overlay will be drawn over the top of the
|
||||
image, so you can basically add a solid or semi-transparent
|
||||
colour to the image to brighten or darken it
|
||||
@param overImage the image to use when the mouse is over the button. If
|
||||
you want to use the same image as was set in the normalImage
|
||||
parameter, this value can be a null image.
|
||||
@param imageOpacityWhenOver the opacity to use when drawing the image when the mouse
|
||||
is over the button
|
||||
@param overlayColourWhenOver an overlay colour to use to fill the alpha channel of the
|
||||
image when the mouse is over - if this colour is transparent,
|
||||
no overlay will be drawn
|
||||
@param downImage an image to use when the button is pressed down. If set
|
||||
to a null image, the 'over' image will be drawn instead (or the
|
||||
normal image if there isn't an 'over' image either).
|
||||
@param imageOpacityWhenDown the opacity to use when drawing the image when the button
|
||||
is pressed
|
||||
@param overlayColourWhenDown an overlay colour to use to fill the alpha channel of the
|
||||
image when the button is pressed down - if this colour is
|
||||
transparent, no overlay will be drawn
|
||||
@param hitTestAlphaThreshold if set to zero, the mouse is considered to be over the button
|
||||
whenever it's inside the button's bounding rectangle. If
|
||||
set to values higher than 0, the mouse will only be
|
||||
considered to be over the image when the value of the
|
||||
image's alpha channel at that position is greater than
|
||||
this level.
|
||||
*/
|
||||
void setImages (bool resizeButtonNowToFitThisImage,
|
||||
bool rescaleImagesWhenButtonSizeChanges,
|
||||
bool preserveImageProportions,
|
||||
const Image& normalImage,
|
||||
float imageOpacityWhenNormal,
|
||||
const Colour& overlayColourWhenNormal,
|
||||
const Image& overImage,
|
||||
float imageOpacityWhenOver,
|
||||
const Colour& overlayColourWhenOver,
|
||||
const Image& downImage,
|
||||
float imageOpacityWhenDown,
|
||||
const Colour& overlayColourWhenDown,
|
||||
float hitTestAlphaThreshold = 0.0f);
|
||||
|
||||
/** Returns the currently set 'normal' image. */
|
||||
Image getNormalImage() const;
|
||||
|
||||
/** Returns the image that's drawn when the mouse is over the button.
|
||||
|
||||
If a valid 'over' image has been set, this will return it; otherwise it'll
|
||||
just return the normal image.
|
||||
*/
|
||||
Image getOverImage() const;
|
||||
|
||||
/** Returns the image that's drawn when the button is held down.
|
||||
|
||||
If a valid 'down' image has been set, this will return it; otherwise it'll
|
||||
return the 'over' image or normal image, depending on what's available.
|
||||
*/
|
||||
Image getDownImage() const;
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
bool hitTest (int x, int y);
|
||||
/** @internal */
|
||||
void paintButton (Graphics& g,
|
||||
bool isMouseOverButton,
|
||||
bool isButtonDown);
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
bool scaleImageToFit, preserveProportions;
|
||||
unsigned char alphaThreshold;
|
||||
int imageX, imageY, imageW, imageH;
|
||||
Image normalImage, overImage, downImage;
|
||||
float normalOpacity, overOpacity, downOpacity;
|
||||
Colour normalOverlay, overOverlay, downOverlay;
|
||||
|
||||
Image getCurrentImage() const;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ImageButton);
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCE_IMAGEBUTTON_JUCEHEADER__
|
||||
123
modules/juce_gui_basics/buttons/juce_ShapeButton.cpp
Normal file
123
modules/juce_gui_basics/buttons/juce_ShapeButton.cpp
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
//==============================================================================
|
||||
ShapeButton::ShapeButton (const String& text_,
|
||||
const Colour& normalColour_,
|
||||
const Colour& overColour_,
|
||||
const Colour& downColour_)
|
||||
: Button (text_),
|
||||
normalColour (normalColour_),
|
||||
overColour (overColour_),
|
||||
downColour (downColour_),
|
||||
maintainShapeProportions (false),
|
||||
outlineWidth (0.0f)
|
||||
{
|
||||
}
|
||||
|
||||
ShapeButton::~ShapeButton()
|
||||
{
|
||||
}
|
||||
|
||||
void ShapeButton::setColours (const Colour& newNormalColour,
|
||||
const Colour& newOverColour,
|
||||
const Colour& newDownColour)
|
||||
{
|
||||
normalColour = newNormalColour;
|
||||
overColour = newOverColour;
|
||||
downColour = newDownColour;
|
||||
}
|
||||
|
||||
void ShapeButton::setOutline (const Colour& newOutlineColour,
|
||||
const float newOutlineWidth)
|
||||
{
|
||||
outlineColour = newOutlineColour;
|
||||
outlineWidth = newOutlineWidth;
|
||||
}
|
||||
|
||||
void ShapeButton::setShape (const Path& newShape,
|
||||
const bool resizeNowToFitThisShape,
|
||||
const bool maintainShapeProportions_,
|
||||
const bool hasShadow)
|
||||
{
|
||||
shape = newShape;
|
||||
maintainShapeProportions = maintainShapeProportions_;
|
||||
|
||||
shadow.setShadowProperties (3.0f, 0.5f, 0, 0);
|
||||
setComponentEffect ((hasShadow) ? &shadow : 0);
|
||||
|
||||
if (resizeNowToFitThisShape)
|
||||
{
|
||||
Rectangle<float> newBounds (shape.getBounds());
|
||||
|
||||
if (hasShadow)
|
||||
newBounds.expand (4.0f, 4.0f);
|
||||
|
||||
shape.applyTransform (AffineTransform::translation (-newBounds.getX(), -newBounds.getY()));
|
||||
|
||||
setSize (1 + (int) (newBounds.getWidth() + outlineWidth),
|
||||
1 + (int) (newBounds.getHeight() + outlineWidth));
|
||||
}
|
||||
}
|
||||
|
||||
void ShapeButton::paintButton (Graphics& g, bool isMouseOverButton, bool isButtonDown)
|
||||
{
|
||||
if (! isEnabled())
|
||||
{
|
||||
isMouseOverButton = false;
|
||||
isButtonDown = false;
|
||||
}
|
||||
|
||||
g.setColour ((isButtonDown) ? downColour
|
||||
: (isMouseOverButton) ? overColour
|
||||
: normalColour);
|
||||
|
||||
int w = getWidth();
|
||||
int h = getHeight();
|
||||
|
||||
if (getComponentEffect() != nullptr)
|
||||
{
|
||||
w -= 4;
|
||||
h -= 4;
|
||||
}
|
||||
|
||||
const float offset = (outlineWidth * 0.5f) + (isButtonDown ? 1.5f : 0.0f);
|
||||
|
||||
const AffineTransform trans (shape.getTransformToScaleToFit (offset, offset,
|
||||
w - offset - outlineWidth,
|
||||
h - offset - outlineWidth,
|
||||
maintainShapeProportions));
|
||||
g.fillPath (shape, trans);
|
||||
|
||||
if (outlineWidth > 0.0f)
|
||||
{
|
||||
g.setColour (outlineColour);
|
||||
g.strokePath (shape, PathStrokeType (outlineWidth), trans);
|
||||
}
|
||||
}
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
108
modules/juce_gui_basics/buttons/juce_ShapeButton.h
Normal file
108
modules/juce_gui_basics/buttons/juce_ShapeButton.h
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_SHAPEBUTTON_JUCEHEADER__
|
||||
#define __JUCE_SHAPEBUTTON_JUCEHEADER__
|
||||
|
||||
#include "juce_Button.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A button that contains a filled shape.
|
||||
|
||||
@see Button, ImageButton, TextButton, ArrowButton
|
||||
*/
|
||||
class JUCE_API ShapeButton : public Button
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a ShapeButton.
|
||||
|
||||
@param name a name to give the component - see Component::setName()
|
||||
@param normalColour the colour to fill the shape with when the mouse isn't over
|
||||
@param overColour the colour to use when the mouse is over the shape
|
||||
@param downColour the colour to use when the button is in the pressed-down state
|
||||
*/
|
||||
ShapeButton (const String& name,
|
||||
const Colour& normalColour,
|
||||
const Colour& overColour,
|
||||
const Colour& downColour);
|
||||
|
||||
/** Destructor. */
|
||||
~ShapeButton();
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the shape to use.
|
||||
|
||||
@param newShape the shape to use
|
||||
@param resizeNowToFitThisShape if true, the button will be resized to fit the shape's bounds
|
||||
@param maintainShapeProportions if true, the shape's proportions will be kept fixed when
|
||||
the button is resized
|
||||
@param hasDropShadow if true, the button will be given a drop-shadow effect
|
||||
*/
|
||||
void setShape (const Path& newShape,
|
||||
bool resizeNowToFitThisShape,
|
||||
bool maintainShapeProportions,
|
||||
bool hasDropShadow);
|
||||
|
||||
/** Set the colours to use for drawing the shape.
|
||||
|
||||
@param normalColour the colour to fill the shape with when the mouse isn't over
|
||||
@param overColour the colour to use when the mouse is over the shape
|
||||
@param downColour the colour to use when the button is in the pressed-down state
|
||||
*/
|
||||
void setColours (const Colour& normalColour,
|
||||
const Colour& overColour,
|
||||
const Colour& downColour);
|
||||
|
||||
/** Sets up an outline to draw around the shape.
|
||||
|
||||
@param outlineColour the colour to use
|
||||
@param outlineStrokeWidth the thickness of line to draw
|
||||
*/
|
||||
void setOutline (const Colour& outlineColour,
|
||||
float outlineStrokeWidth);
|
||||
|
||||
|
||||
protected:
|
||||
/** @internal */
|
||||
void paintButton (Graphics& g,
|
||||
bool isMouseOverButton,
|
||||
bool isButtonDown);
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
Colour normalColour, overColour, downColour, outlineColour;
|
||||
DropShadowEffect shadow;
|
||||
Path shape;
|
||||
bool maintainShapeProportions;
|
||||
float outlineWidth;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ShapeButton);
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCE_SHAPEBUTTON_JUCEHEADER__
|
||||
75
modules/juce_gui_basics/buttons/juce_TextButton.cpp
Normal file
75
modules/juce_gui_basics/buttons/juce_TextButton.cpp
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
//==============================================================================
|
||||
TextButton::TextButton (const String& name,
|
||||
const String& toolTip)
|
||||
: Button (name)
|
||||
{
|
||||
setTooltip (toolTip);
|
||||
}
|
||||
|
||||
TextButton::~TextButton()
|
||||
{
|
||||
}
|
||||
|
||||
void TextButton::paintButton (Graphics& g,
|
||||
bool isMouseOverButton,
|
||||
bool isButtonDown)
|
||||
{
|
||||
getLookAndFeel().drawButtonBackground (g, *this,
|
||||
findColour (getToggleState() ? buttonOnColourId
|
||||
: buttonColourId),
|
||||
isMouseOverButton,
|
||||
isButtonDown);
|
||||
|
||||
getLookAndFeel().drawButtonText (g, *this,
|
||||
isMouseOverButton,
|
||||
isButtonDown);
|
||||
}
|
||||
|
||||
void TextButton::colourChanged()
|
||||
{
|
||||
repaint();
|
||||
}
|
||||
|
||||
const Font TextButton::getFont()
|
||||
{
|
||||
return Font (jmin (15.0f, getHeight() * 0.6f));
|
||||
}
|
||||
|
||||
void TextButton::changeWidthToFitText (const int newHeight)
|
||||
{
|
||||
if (newHeight >= 0)
|
||||
setSize (jmax (1, getWidth()), newHeight);
|
||||
|
||||
setSize (getFont().getStringWidth (getButtonText()) + getHeight(),
|
||||
getHeight());
|
||||
}
|
||||
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
105
modules/juce_gui_basics/buttons/juce_TextButton.h
Normal file
105
modules/juce_gui_basics/buttons/juce_TextButton.h
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_TEXTBUTTON_JUCEHEADER__
|
||||
#define __JUCE_TEXTBUTTON_JUCEHEADER__
|
||||
|
||||
#include "juce_Button.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A button that uses the standard lozenge-shaped background with a line of
|
||||
text on it.
|
||||
|
||||
@see Button, DrawableButton
|
||||
*/
|
||||
class JUCE_API TextButton : public Button
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a TextButton.
|
||||
|
||||
@param buttonName the text to put in the button (the component's name is also
|
||||
initially set to this string, but these can be changed later
|
||||
using the setName() and setButtonText() methods)
|
||||
@param toolTip an optional string to use as a toolip
|
||||
|
||||
@see Button
|
||||
*/
|
||||
TextButton (const String& buttonName = String::empty,
|
||||
const String& toolTip = String::empty);
|
||||
|
||||
/** Destructor. */
|
||||
~TextButton();
|
||||
|
||||
//==============================================================================
|
||||
/** A set of colour IDs to use to change the colour of various aspects of the button.
|
||||
|
||||
These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
|
||||
methods.
|
||||
|
||||
@see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
|
||||
*/
|
||||
enum ColourIds
|
||||
{
|
||||
buttonColourId = 0x1000100, /**< The colour used to fill the button shape (when the button is toggled
|
||||
'off'). The look-and-feel class might re-interpret this to add
|
||||
effects, etc. */
|
||||
buttonOnColourId = 0x1000101, /**< The colour used to fill the button shape (when the button is toggled
|
||||
'on'). The look-and-feel class might re-interpret this to add
|
||||
effects, etc. */
|
||||
textColourOffId = 0x1000102, /**< The colour to use for the button's text when the button's toggle state is "off". */
|
||||
textColourOnId = 0x1000103 /**< The colour to use for the button's text.when the button's toggle state is "on". */
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Resizes the button to fit neatly around its current text.
|
||||
|
||||
If newHeight is >= 0, the button's height will be changed to this
|
||||
value. If it's less than zero, its height will be unaffected.
|
||||
*/
|
||||
void changeWidthToFitText (int newHeight = -1);
|
||||
|
||||
/** This can be overridden to use different fonts than the default one.
|
||||
|
||||
Note that you'll need to set the font's size appropriately, too.
|
||||
*/
|
||||
virtual const Font getFont();
|
||||
|
||||
|
||||
protected:
|
||||
/** @internal */
|
||||
void paintButton (Graphics& g, bool isMouseOverButton, bool isButtonDown);
|
||||
/** @internal */
|
||||
void colourChanged();
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TextButton);
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCE_TEXTBUTTON_JUCEHEADER__
|
||||
58
modules/juce_gui_basics/buttons/juce_ToggleButton.cpp
Normal file
58
modules/juce_gui_basics/buttons/juce_ToggleButton.cpp
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
//==============================================================================
|
||||
ToggleButton::ToggleButton (const String& buttonText)
|
||||
: Button (buttonText)
|
||||
{
|
||||
setClickingTogglesState (true);
|
||||
}
|
||||
|
||||
ToggleButton::~ToggleButton()
|
||||
{
|
||||
}
|
||||
|
||||
void ToggleButton::paintButton (Graphics& g,
|
||||
bool isMouseOverButton,
|
||||
bool isButtonDown)
|
||||
{
|
||||
getLookAndFeel().drawToggleButton (g, *this,
|
||||
isMouseOverButton,
|
||||
isButtonDown);
|
||||
}
|
||||
|
||||
void ToggleButton::changeWidthToFitText()
|
||||
{
|
||||
getLookAndFeel().changeToggleButtonWidthToFitText (*this);
|
||||
}
|
||||
|
||||
void ToggleButton::colourChanged()
|
||||
{
|
||||
repaint();
|
||||
}
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
91
modules/juce_gui_basics/buttons/juce_ToggleButton.h
Normal file
91
modules/juce_gui_basics/buttons/juce_ToggleButton.h
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_TOGGLEBUTTON_JUCEHEADER__
|
||||
#define __JUCE_TOGGLEBUTTON_JUCEHEADER__
|
||||
|
||||
#include "juce_Button.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A button that can be toggled on/off.
|
||||
|
||||
All buttons can be toggle buttons, but this lets you create one of the
|
||||
standard ones which has a tick-box and a text label next to it.
|
||||
|
||||
@see Button, DrawableButton, TextButton
|
||||
*/
|
||||
class JUCE_API ToggleButton : public Button
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a ToggleButton.
|
||||
|
||||
@param buttonText the text to put in the button (the component's name is also
|
||||
initially set to this string, but these can be changed later
|
||||
using the setName() and setButtonText() methods)
|
||||
*/
|
||||
explicit ToggleButton (const String& buttonText = String::empty);
|
||||
|
||||
/** Destructor. */
|
||||
~ToggleButton();
|
||||
|
||||
//==============================================================================
|
||||
/** Resizes the button to fit neatly around its current text.
|
||||
|
||||
The button's height won't be affected, only its width.
|
||||
*/
|
||||
void changeWidthToFitText();
|
||||
|
||||
//==============================================================================
|
||||
/** A set of colour IDs to use to change the colour of various aspects of the button.
|
||||
|
||||
These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
|
||||
methods.
|
||||
|
||||
@see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
|
||||
*/
|
||||
enum ColourIds
|
||||
{
|
||||
textColourId = 0x1006501 /**< The colour to use for the button's text. */
|
||||
};
|
||||
|
||||
protected:
|
||||
/** @internal */
|
||||
void paintButton (Graphics& g,
|
||||
bool isMouseOverButton,
|
||||
bool isButtonDown);
|
||||
|
||||
/** @internal */
|
||||
void colourChanged();
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ToggleButton);
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCE_TOGGLEBUTTON_JUCEHEADER__
|
||||
102
modules/juce_gui_basics/buttons/juce_ToolbarButton.cpp
Normal file
102
modules/juce_gui_basics/buttons/juce_ToolbarButton.cpp
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
//==============================================================================
|
||||
ToolbarButton::ToolbarButton (const int itemId_, const String& buttonText,
|
||||
Drawable* const normalImage_, Drawable* const toggledOnImage_)
|
||||
: ToolbarItemComponent (itemId_, buttonText, true),
|
||||
normalImage (normalImage_),
|
||||
toggledOnImage (toggledOnImage_),
|
||||
currentImage (nullptr)
|
||||
{
|
||||
jassert (normalImage_ != nullptr);
|
||||
}
|
||||
|
||||
ToolbarButton::~ToolbarButton()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool ToolbarButton::getToolbarItemSizes (int toolbarDepth, bool /*isToolbarVertical*/, int& preferredSize, int& minSize, int& maxSize)
|
||||
{
|
||||
preferredSize = minSize = maxSize = toolbarDepth;
|
||||
return true;
|
||||
}
|
||||
|
||||
void ToolbarButton::paintButtonArea (Graphics&, int /*width*/, int /*height*/, bool /*isMouseOver*/, bool /*isMouseDown*/)
|
||||
{
|
||||
}
|
||||
|
||||
void ToolbarButton::contentAreaChanged (const Rectangle<int>&)
|
||||
{
|
||||
buttonStateChanged();
|
||||
}
|
||||
|
||||
void ToolbarButton::updateDrawable()
|
||||
{
|
||||
if (currentImage != nullptr)
|
||||
{
|
||||
currentImage->setTransformToFit (getContentArea().toFloat(), RectanglePlacement::centred);
|
||||
currentImage->setAlpha (isEnabled() ? 1.0f : 0.5f);
|
||||
}
|
||||
}
|
||||
|
||||
void ToolbarButton::resized()
|
||||
{
|
||||
ToolbarItemComponent::resized();
|
||||
updateDrawable();
|
||||
}
|
||||
|
||||
void ToolbarButton::enablementChanged()
|
||||
{
|
||||
ToolbarItemComponent::enablementChanged();
|
||||
updateDrawable();
|
||||
}
|
||||
|
||||
void ToolbarButton::buttonStateChanged()
|
||||
{
|
||||
Drawable* d = normalImage;
|
||||
|
||||
if (getToggleState() && toggledOnImage != nullptr)
|
||||
d = toggledOnImage;
|
||||
|
||||
if (d != currentImage)
|
||||
{
|
||||
removeChildComponent (currentImage);
|
||||
currentImage = d;
|
||||
|
||||
if (d != nullptr)
|
||||
{
|
||||
enablementChanged();
|
||||
addAndMakeVisible (d);
|
||||
updateDrawable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
98
modules/juce_gui_basics/buttons/juce_ToolbarButton.h
Normal file
98
modules/juce_gui_basics/buttons/juce_ToolbarButton.h
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_TOOLBARBUTTON_JUCEHEADER__
|
||||
#define __JUCE_TOOLBARBUTTON_JUCEHEADER__
|
||||
|
||||
#include "../widgets/juce_ToolbarItemComponent.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A type of button designed to go on a toolbar.
|
||||
|
||||
This simple button can have two Drawable objects specified - one for normal
|
||||
use and another one (optionally) for the button's "on" state if it's a
|
||||
toggle button.
|
||||
|
||||
@see Toolbar, ToolbarItemFactory, ToolbarItemComponent, Drawable, Button
|
||||
*/
|
||||
class JUCE_API ToolbarButton : public ToolbarItemComponent
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a ToolbarButton.
|
||||
|
||||
@param itemId the ID for this toolbar item type. This is passed through to the
|
||||
ToolbarItemComponent constructor
|
||||
@param labelText the text to display on the button (if the toolbar is using a style
|
||||
that shows text labels). This is passed through to the
|
||||
ToolbarItemComponent constructor
|
||||
@param normalImage a drawable object that the button should use as its icon. The object
|
||||
that is passed-in here will be kept by this object and will be
|
||||
deleted when no longer needed or when this button is deleted.
|
||||
@param toggledOnImage a drawable object that the button can use as its icon if the button
|
||||
is in a toggled-on state (see the Button::getToggleState() method). If
|
||||
0 is passed-in here, then the normal image will be used instead, regardless
|
||||
of the toggle state. The object that is passed-in here will be kept by
|
||||
this object and will be deleted when no longer needed or when this button
|
||||
is deleted.
|
||||
*/
|
||||
ToolbarButton (int itemId,
|
||||
const String& labelText,
|
||||
Drawable* normalImage,
|
||||
Drawable* toggledOnImage);
|
||||
|
||||
/** Destructor. */
|
||||
~ToolbarButton();
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
bool getToolbarItemSizes (int toolbarDepth, bool isToolbarVertical, int& preferredSize,
|
||||
int& minSize, int& maxSize);
|
||||
/** @internal */
|
||||
void paintButtonArea (Graphics& g, int width, int height, bool isMouseOver, bool isMouseDown);
|
||||
/** @internal */
|
||||
void contentAreaChanged (const Rectangle<int>& newBounds);
|
||||
/** @internal */
|
||||
void buttonStateChanged();
|
||||
/** @internal */
|
||||
void resized();
|
||||
/** @internal */
|
||||
void enablementChanged();
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
ScopedPointer<Drawable> normalImage, toggledOnImage;
|
||||
Drawable* currentImage;
|
||||
|
||||
void updateDrawable();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ToolbarButton);
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCE_TOOLBARBUTTON_JUCEHEADER__
|
||||
83
modules/juce_gui_basics/commands/juce_ApplicationCommandID.h
Normal file
83
modules/juce_gui_basics/commands/juce_ApplicationCommandID.h
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_APPLICATIONCOMMANDID_JUCEHEADER__
|
||||
#define __JUCE_APPLICATIONCOMMANDID_JUCEHEADER__
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** A type used to hold the unique ID for an application command.
|
||||
|
||||
This is a numeric type, so it can be stored as an integer.
|
||||
|
||||
@see ApplicationCommandInfo, ApplicationCommandManager,
|
||||
ApplicationCommandTarget, KeyPressMappingSet
|
||||
*/
|
||||
typedef int CommandID;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** A set of general-purpose application command IDs.
|
||||
|
||||
Because these commands are likely to be used in most apps, they're defined
|
||||
here to help different apps to use the same numeric values for them.
|
||||
|
||||
Of course you don't have to use these, but some of them are used internally by
|
||||
Juce - e.g. the quit ID is recognised as a command by the JUCEApplication class.
|
||||
|
||||
@see ApplicationCommandInfo, ApplicationCommandManager,
|
||||
ApplicationCommandTarget, KeyPressMappingSet
|
||||
*/
|
||||
namespace StandardApplicationCommandIDs
|
||||
{
|
||||
/** This command ID should be used to send a "Quit the App" command.
|
||||
|
||||
This command is recognised by the JUCEApplication class, so if it is invoked
|
||||
and no other ApplicationCommandTarget handles the event first, the JUCEApplication
|
||||
object will catch it and call JUCEApplication::systemRequestedQuit().
|
||||
*/
|
||||
static const CommandID quit = 0x1001;
|
||||
|
||||
/** The command ID that should be used to send a "Delete" command. */
|
||||
static const CommandID del = 0x1002;
|
||||
|
||||
/** The command ID that should be used to send a "Cut" command. */
|
||||
static const CommandID cut = 0x1003;
|
||||
|
||||
/** The command ID that should be used to send a "Copy to clipboard" command. */
|
||||
static const CommandID copy = 0x1004;
|
||||
|
||||
/** The command ID that should be used to send a "Paste from clipboard" command. */
|
||||
static const CommandID paste = 0x1005;
|
||||
|
||||
/** The command ID that should be used to send a "Select all" command. */
|
||||
static const CommandID selectAll = 0x1006;
|
||||
|
||||
/** The command ID that should be used to send a "Deselect all" command. */
|
||||
static const CommandID deselectAll = 0x1007;
|
||||
}
|
||||
|
||||
|
||||
#endif // __JUCE_APPLICATIONCOMMANDID_JUCEHEADER__
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
//==============================================================================
|
||||
ApplicationCommandInfo::ApplicationCommandInfo (const CommandID commandID_) noexcept
|
||||
: commandID (commandID_),
|
||||
flags (0)
|
||||
{
|
||||
}
|
||||
|
||||
void ApplicationCommandInfo::setInfo (const String& shortName_,
|
||||
const String& description_,
|
||||
const String& categoryName_,
|
||||
const int flags_) noexcept
|
||||
{
|
||||
shortName = shortName_;
|
||||
description = description_;
|
||||
categoryName = categoryName_;
|
||||
flags = flags_;
|
||||
}
|
||||
|
||||
void ApplicationCommandInfo::setActive (const bool b) noexcept
|
||||
{
|
||||
if (b)
|
||||
flags &= ~isDisabled;
|
||||
else
|
||||
flags |= isDisabled;
|
||||
}
|
||||
|
||||
void ApplicationCommandInfo::setTicked (const bool b) noexcept
|
||||
{
|
||||
if (b)
|
||||
flags |= isTicked;
|
||||
else
|
||||
flags &= ~isTicked;
|
||||
}
|
||||
|
||||
void ApplicationCommandInfo::addDefaultKeypress (const int keyCode, const ModifierKeys& modifiers) noexcept
|
||||
{
|
||||
defaultKeypresses.add (KeyPress (keyCode, modifiers, 0));
|
||||
}
|
||||
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
194
modules/juce_gui_basics/commands/juce_ApplicationCommandInfo.h
Normal file
194
modules/juce_gui_basics/commands/juce_ApplicationCommandInfo.h
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_APPLICATIONCOMMANDINFO_JUCEHEADER__
|
||||
#define __JUCE_APPLICATIONCOMMANDINFO_JUCEHEADER__
|
||||
|
||||
#include "../keyboard/juce_KeyPress.h"
|
||||
#include "juce_ApplicationCommandID.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Holds information describing an application command.
|
||||
|
||||
This object is used to pass information about a particular command, such as its
|
||||
name, description and other usage flags.
|
||||
|
||||
When an ApplicationCommandTarget is asked to provide information about the commands
|
||||
it can perform, this is the structure gets filled-in to describe each one.
|
||||
|
||||
@see ApplicationCommandTarget, ApplicationCommandTarget::getCommandInfo(),
|
||||
ApplicationCommandManager
|
||||
*/
|
||||
struct JUCE_API ApplicationCommandInfo
|
||||
{
|
||||
//==============================================================================
|
||||
explicit ApplicationCommandInfo (CommandID commandID) noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Sets a number of the structures values at once.
|
||||
|
||||
The meanings of each of the parameters is described below, in the appropriate
|
||||
member variable's description.
|
||||
*/
|
||||
void setInfo (const String& shortName,
|
||||
const String& description,
|
||||
const String& categoryName,
|
||||
int flags) noexcept;
|
||||
|
||||
/** An easy way to set or remove the isDisabled bit in the structure's flags field.
|
||||
|
||||
If isActive is true, the flags member has the isDisabled bit cleared; if isActive
|
||||
is false, the bit is set.
|
||||
*/
|
||||
void setActive (bool isActive) noexcept;
|
||||
|
||||
/** An easy way to set or remove the isTicked bit in the structure's flags field.
|
||||
*/
|
||||
void setTicked (bool isTicked) noexcept;
|
||||
|
||||
/** Handy method for adding a keypress to the defaultKeypresses array.
|
||||
|
||||
This is just so you can write things like:
|
||||
@code
|
||||
myinfo.addDefaultKeypress ('s', ModifierKeys::commandModifier);
|
||||
@endcode
|
||||
instead of
|
||||
@code
|
||||
myinfo.defaultKeypresses.add (KeyPress ('s', ModifierKeys::commandModifier));
|
||||
@endcode
|
||||
*/
|
||||
void addDefaultKeypress (int keyCode,
|
||||
const ModifierKeys& modifiers) noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** The command's unique ID number.
|
||||
*/
|
||||
CommandID commandID;
|
||||
|
||||
/** A short name to describe the command.
|
||||
|
||||
This should be suitable for use in menus, on buttons that trigger the command, etc.
|
||||
|
||||
You can use the setInfo() method to quickly set this and some of the command's
|
||||
other properties.
|
||||
*/
|
||||
String shortName;
|
||||
|
||||
/** A longer description of the command.
|
||||
|
||||
This should be suitable for use in contexts such as a KeyMappingEditorComponent or
|
||||
pop-up tooltip describing what the command does.
|
||||
|
||||
You can use the setInfo() method to quickly set this and some of the command's
|
||||
other properties.
|
||||
*/
|
||||
String description;
|
||||
|
||||
/** A named category that the command fits into.
|
||||
|
||||
You can give your commands any category you like, and these will be displayed in
|
||||
contexts such as the KeyMappingEditorComponent, where the category is used to group
|
||||
commands together.
|
||||
|
||||
You can use the setInfo() method to quickly set this and some of the command's
|
||||
other properties.
|
||||
*/
|
||||
String categoryName;
|
||||
|
||||
/** A list of zero or more keypresses that should be used as the default keys for
|
||||
this command.
|
||||
|
||||
Methods such as KeyPressMappingSet::resetToDefaultMappings() will use the keypresses in
|
||||
this list to initialise the default set of key-to-command mappings.
|
||||
|
||||
@see addDefaultKeypress
|
||||
*/
|
||||
Array <KeyPress> defaultKeypresses;
|
||||
|
||||
//==============================================================================
|
||||
/** Flags describing the ways in which this command should be used.
|
||||
|
||||
A bitwise-OR of these values is stored in the ApplicationCommandInfo::flags
|
||||
variable.
|
||||
*/
|
||||
enum CommandFlags
|
||||
{
|
||||
/** Indicates that the command can't currently be performed.
|
||||
|
||||
The ApplicationCommandTarget::getCommandInfo() method must set this flag if it's
|
||||
not currently permissable to perform the command. If the flag is set, then
|
||||
components that trigger the command, e.g. PopupMenu, may choose to grey-out the
|
||||
command or show themselves as not being enabled.
|
||||
|
||||
@see ApplicationCommandInfo::setActive
|
||||
*/
|
||||
isDisabled = 1 << 0,
|
||||
|
||||
/** Indicates that the command should have a tick next to it on a menu.
|
||||
|
||||
If your command is shown on a menu and this is set, it'll show a tick next to
|
||||
it. Other components such as buttons may also use this flag to indicate that it
|
||||
is a value that can be toggled, and is currently in the 'on' state.
|
||||
|
||||
@see ApplicationCommandInfo::setTicked
|
||||
*/
|
||||
isTicked = 1 << 1,
|
||||
|
||||
/** If this flag is present, then when a KeyPressMappingSet invokes the command,
|
||||
it will call the command twice, once on key-down and again on key-up.
|
||||
|
||||
@see ApplicationCommandTarget::InvocationInfo
|
||||
*/
|
||||
wantsKeyUpDownCallbacks = 1 << 2,
|
||||
|
||||
/** If this flag is present, then a KeyMappingEditorComponent will not display the
|
||||
command in its list.
|
||||
*/
|
||||
hiddenFromKeyEditor = 1 << 3,
|
||||
|
||||
/** If this flag is present, then a KeyMappingEditorComponent will display the
|
||||
command in its list, but won't allow the assigned keypress to be changed.
|
||||
*/
|
||||
readOnlyInKeyEditor = 1 << 4,
|
||||
|
||||
/** If this flag is present and the command is invoked from a keypress, then any
|
||||
buttons or menus that are also connected to the command will not flash to
|
||||
indicate that they've been triggered.
|
||||
*/
|
||||
dontTriggerVisualFeedback = 1 << 5
|
||||
};
|
||||
|
||||
/** A bitwise-OR of the values specified in the CommandFlags enum.
|
||||
|
||||
You can use the setInfo() method to quickly set this and some of the command's
|
||||
other properties.
|
||||
*/
|
||||
int flags;
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCE_APPLICATIONCOMMANDINFO_JUCEHEADER__
|
||||
|
|
@ -0,0 +1,318 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
//==============================================================================
|
||||
ApplicationCommandManager::ApplicationCommandManager()
|
||||
: firstTarget (nullptr)
|
||||
{
|
||||
keyMappings = new KeyPressMappingSet (this);
|
||||
|
||||
Desktop::getInstance().addFocusChangeListener (this);
|
||||
}
|
||||
|
||||
ApplicationCommandManager::~ApplicationCommandManager()
|
||||
{
|
||||
Desktop::getInstance().removeFocusChangeListener (this);
|
||||
keyMappings = nullptr;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void ApplicationCommandManager::clearCommands()
|
||||
{
|
||||
commands.clear();
|
||||
keyMappings->clearAllKeyPresses();
|
||||
triggerAsyncUpdate();
|
||||
}
|
||||
|
||||
void ApplicationCommandManager::registerCommand (const ApplicationCommandInfo& newCommand)
|
||||
{
|
||||
// zero isn't a valid command ID!
|
||||
jassert (newCommand.commandID != 0);
|
||||
|
||||
// the name isn't optional!
|
||||
jassert (newCommand.shortName.isNotEmpty());
|
||||
|
||||
if (getCommandForID (newCommand.commandID) == 0)
|
||||
{
|
||||
ApplicationCommandInfo* const newInfo = new ApplicationCommandInfo (newCommand);
|
||||
newInfo->flags &= ~ApplicationCommandInfo::isTicked;
|
||||
commands.add (newInfo);
|
||||
|
||||
keyMappings->resetToDefaultMapping (newCommand.commandID);
|
||||
|
||||
triggerAsyncUpdate();
|
||||
}
|
||||
else
|
||||
{
|
||||
// trying to re-register the same command with different parameters?
|
||||
jassert (newCommand.shortName == getCommandForID (newCommand.commandID)->shortName
|
||||
&& (newCommand.description == getCommandForID (newCommand.commandID)->description || newCommand.description.isEmpty())
|
||||
&& newCommand.categoryName == getCommandForID (newCommand.commandID)->categoryName
|
||||
&& newCommand.defaultKeypresses == getCommandForID (newCommand.commandID)->defaultKeypresses
|
||||
&& (newCommand.flags & (ApplicationCommandInfo::wantsKeyUpDownCallbacks | ApplicationCommandInfo::hiddenFromKeyEditor | ApplicationCommandInfo::readOnlyInKeyEditor))
|
||||
== (getCommandForID (newCommand.commandID)->flags & (ApplicationCommandInfo::wantsKeyUpDownCallbacks | ApplicationCommandInfo::hiddenFromKeyEditor | ApplicationCommandInfo::readOnlyInKeyEditor)));
|
||||
}
|
||||
}
|
||||
|
||||
void ApplicationCommandManager::registerAllCommandsForTarget (ApplicationCommandTarget* target)
|
||||
{
|
||||
if (target != nullptr)
|
||||
{
|
||||
Array <CommandID> commandIDs;
|
||||
target->getAllCommands (commandIDs);
|
||||
|
||||
for (int i = 0; i < commandIDs.size(); ++i)
|
||||
{
|
||||
ApplicationCommandInfo info (commandIDs.getUnchecked(i));
|
||||
target->getCommandInfo (info.commandID, info);
|
||||
|
||||
registerCommand (info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ApplicationCommandManager::removeCommand (const CommandID commandID)
|
||||
{
|
||||
for (int i = commands.size(); --i >= 0;)
|
||||
{
|
||||
if (commands.getUnchecked (i)->commandID == commandID)
|
||||
{
|
||||
commands.remove (i);
|
||||
triggerAsyncUpdate();
|
||||
|
||||
const Array <KeyPress> keys (keyMappings->getKeyPressesAssignedToCommand (commandID));
|
||||
|
||||
for (int j = keys.size(); --j >= 0;)
|
||||
keyMappings->removeKeyPress (keys.getReference (j));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ApplicationCommandManager::commandStatusChanged()
|
||||
{
|
||||
triggerAsyncUpdate();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
const ApplicationCommandInfo* ApplicationCommandManager::getCommandForID (const CommandID commandID) const noexcept
|
||||
{
|
||||
for (int i = commands.size(); --i >= 0;)
|
||||
if (commands.getUnchecked(i)->commandID == commandID)
|
||||
return commands.getUnchecked(i);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
String ApplicationCommandManager::getNameOfCommand (const CommandID commandID) const noexcept
|
||||
{
|
||||
const ApplicationCommandInfo* const ci = getCommandForID (commandID);
|
||||
|
||||
return ci != nullptr ? ci->shortName : String::empty;
|
||||
}
|
||||
|
||||
String ApplicationCommandManager::getDescriptionOfCommand (const CommandID commandID) const noexcept
|
||||
{
|
||||
const ApplicationCommandInfo* const ci = getCommandForID (commandID);
|
||||
|
||||
return ci != nullptr ? (ci->description.isNotEmpty() ? ci->description : ci->shortName)
|
||||
: String::empty;
|
||||
}
|
||||
|
||||
StringArray ApplicationCommandManager::getCommandCategories() const
|
||||
{
|
||||
StringArray s;
|
||||
|
||||
for (int i = 0; i < commands.size(); ++i)
|
||||
s.addIfNotAlreadyThere (commands.getUnchecked(i)->categoryName, false);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
Array<CommandID> ApplicationCommandManager::getCommandsInCategory (const String& categoryName) const
|
||||
{
|
||||
Array <CommandID> results;
|
||||
|
||||
for (int i = 0; i < commands.size(); ++i)
|
||||
if (commands.getUnchecked(i)->categoryName == categoryName)
|
||||
results.add (commands.getUnchecked(i)->commandID);
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool ApplicationCommandManager::invokeDirectly (const CommandID commandID, const bool asynchronously)
|
||||
{
|
||||
ApplicationCommandTarget::InvocationInfo info (commandID);
|
||||
info.invocationMethod = ApplicationCommandTarget::InvocationInfo::direct;
|
||||
|
||||
return invoke (info, asynchronously);
|
||||
}
|
||||
|
||||
bool ApplicationCommandManager::invoke (const ApplicationCommandTarget::InvocationInfo& info_, const bool asynchronously)
|
||||
{
|
||||
// This call isn't thread-safe for use from a non-UI thread without locking the message
|
||||
// manager first..
|
||||
jassert (MessageManager::getInstance()->currentThreadHasLockedMessageManager());
|
||||
|
||||
ApplicationCommandInfo commandInfo (0);
|
||||
ApplicationCommandTarget* const target = getTargetForCommand (info_.commandID, commandInfo);
|
||||
|
||||
if (target == nullptr)
|
||||
return false;
|
||||
|
||||
ApplicationCommandTarget::InvocationInfo info (info_);
|
||||
info.commandFlags = commandInfo.flags;
|
||||
|
||||
sendListenerInvokeCallback (info);
|
||||
|
||||
const bool ok = target->invoke (info, asynchronously);
|
||||
|
||||
commandStatusChanged();
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
ApplicationCommandTarget* ApplicationCommandManager::getFirstCommandTarget (const CommandID)
|
||||
{
|
||||
return firstTarget != nullptr ? firstTarget
|
||||
: findDefaultComponentTarget();
|
||||
}
|
||||
|
||||
void ApplicationCommandManager::setFirstCommandTarget (ApplicationCommandTarget* const newTarget) noexcept
|
||||
{
|
||||
firstTarget = newTarget;
|
||||
}
|
||||
|
||||
ApplicationCommandTarget* ApplicationCommandManager::getTargetForCommand (const CommandID commandID,
|
||||
ApplicationCommandInfo& upToDateInfo)
|
||||
{
|
||||
ApplicationCommandTarget* target = getFirstCommandTarget (commandID);
|
||||
|
||||
if (target == nullptr)
|
||||
target = JUCEApplication::getInstance();
|
||||
|
||||
if (target != nullptr)
|
||||
target = target->getTargetForCommand (commandID);
|
||||
|
||||
if (target != nullptr)
|
||||
target->getCommandInfo (commandID, upToDateInfo);
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
ApplicationCommandTarget* ApplicationCommandManager::findTargetForComponent (Component* c)
|
||||
{
|
||||
ApplicationCommandTarget* target = dynamic_cast <ApplicationCommandTarget*> (c);
|
||||
|
||||
if (target == nullptr && c != nullptr)
|
||||
// (unable to use the syntax findParentComponentOfClass <ApplicationCommandTarget> () because of a VC6 compiler bug)
|
||||
target = c->findParentComponentOfClass ((ApplicationCommandTarget*) nullptr);
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
ApplicationCommandTarget* ApplicationCommandManager::findDefaultComponentTarget()
|
||||
{
|
||||
Component* c = Component::getCurrentlyFocusedComponent();
|
||||
|
||||
if (c == nullptr)
|
||||
{
|
||||
TopLevelWindow* const activeWindow = TopLevelWindow::getActiveTopLevelWindow();
|
||||
|
||||
if (activeWindow != nullptr)
|
||||
{
|
||||
c = activeWindow->getPeer()->getLastFocusedSubcomponent();
|
||||
|
||||
if (c == nullptr)
|
||||
c = activeWindow;
|
||||
}
|
||||
}
|
||||
|
||||
if (c == nullptr && Process::isForegroundProcess())
|
||||
{
|
||||
// getting a bit desperate now - try all desktop comps..
|
||||
for (int i = Desktop::getInstance().getNumComponents(); --i >= 0;)
|
||||
{
|
||||
ApplicationCommandTarget* const target
|
||||
= findTargetForComponent (Desktop::getInstance().getComponent (i)
|
||||
->getPeer()->getLastFocusedSubcomponent());
|
||||
|
||||
if (target != nullptr)
|
||||
return target;
|
||||
}
|
||||
}
|
||||
|
||||
if (c != nullptr)
|
||||
{
|
||||
ResizableWindow* const resizableWindow = dynamic_cast <ResizableWindow*> (c);
|
||||
|
||||
// if we're focused on a ResizableWindow, chances are that it's the content
|
||||
// component that really should get the event. And if not, the event will
|
||||
// still be passed up to the top level window anyway, so let's send it to the
|
||||
// content comp.
|
||||
if (resizableWindow != nullptr && resizableWindow->getContentComponent() != nullptr)
|
||||
c = resizableWindow->getContentComponent();
|
||||
|
||||
ApplicationCommandTarget* const target = findTargetForComponent (c);
|
||||
|
||||
if (target != nullptr)
|
||||
return target;
|
||||
}
|
||||
|
||||
return JUCEApplication::getInstance();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void ApplicationCommandManager::addListener (ApplicationCommandManagerListener* const listener)
|
||||
{
|
||||
listeners.add (listener);
|
||||
}
|
||||
|
||||
void ApplicationCommandManager::removeListener (ApplicationCommandManagerListener* const listener)
|
||||
{
|
||||
listeners.remove (listener);
|
||||
}
|
||||
|
||||
void ApplicationCommandManager::sendListenerInvokeCallback (const ApplicationCommandTarget::InvocationInfo& info)
|
||||
{
|
||||
listeners.call (&ApplicationCommandManagerListener::applicationCommandInvoked, info);
|
||||
}
|
||||
|
||||
void ApplicationCommandManager::handleAsyncUpdate()
|
||||
{
|
||||
listeners.call (&ApplicationCommandManagerListener::applicationCommandListChanged);
|
||||
}
|
||||
|
||||
void ApplicationCommandManager::globalFocusChanged (Component*)
|
||||
{
|
||||
commandStatusChanged();
|
||||
}
|
||||
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
|
|
@ -0,0 +1,365 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_APPLICATIONCOMMANDMANAGER_JUCEHEADER__
|
||||
#define __JUCE_APPLICATIONCOMMANDMANAGER_JUCEHEADER__
|
||||
|
||||
#include "juce_ApplicationCommandTarget.h"
|
||||
class KeyPressMappingSet;
|
||||
class ApplicationCommandManagerListener;
|
||||
class Desktop;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
One of these objects holds a list of all the commands your app can perform,
|
||||
and despatches these commands when needed.
|
||||
|
||||
Application commands are a good way to trigger actions in your app, e.g. "Quit",
|
||||
"Copy", "Paste", etc. Menus, buttons and keypresses can all be given commands
|
||||
to invoke automatically, which means you don't have to handle the result of a menu
|
||||
or button click manually. Commands are despatched to ApplicationCommandTarget objects
|
||||
which can choose which events they want to handle.
|
||||
|
||||
This architecture also allows for nested ApplicationCommandTargets, so that for example
|
||||
you could have two different objects, one inside the other, both of which can respond to
|
||||
a "delete" command. Depending on which one has focus, the command will be sent to the
|
||||
appropriate place, regardless of whether it was triggered by a menu, keypress or some other
|
||||
method.
|
||||
|
||||
To set up your app to use commands, you'll need to do the following:
|
||||
|
||||
- Create a global ApplicationCommandManager to hold the list of all possible
|
||||
commands. (This will also manage a set of key-mappings for them).
|
||||
|
||||
- Make some of your UI components (or other objects) inherit from ApplicationCommandTarget.
|
||||
This allows the object to provide a list of commands that it can perform, and
|
||||
to handle them.
|
||||
|
||||
- Register each type of command using ApplicationCommandManager::registerAllCommandsForTarget(),
|
||||
or ApplicationCommandManager::registerCommand().
|
||||
|
||||
- If you want key-presses to trigger your commands, use the ApplicationCommandManager::getKeyMappings()
|
||||
method to access the key-mapper object, which you will need to register as a key-listener
|
||||
in whatever top-level component you're using. See the KeyPressMappingSet class for more help
|
||||
about setting this up.
|
||||
|
||||
- Use methods such as PopupMenu::addCommandItem() or Button::setCommandToTrigger() to
|
||||
cause these commands to be invoked automatically.
|
||||
|
||||
- Commands can be invoked directly by your code using ApplicationCommandManager::invokeDirectly().
|
||||
|
||||
When a command is invoked, the ApplicationCommandManager will try to choose the best
|
||||
ApplicationCommandTarget to receive the specified command. To do this it will use the
|
||||
current keyboard focus to see which component might be interested, and will search the
|
||||
component hierarchy for those that also implement the ApplicationCommandTarget interface.
|
||||
If an ApplicationCommandTarget isn't interested in the command that is being invoked, then
|
||||
the next one in line will be tried (see the ApplicationCommandTarget::getNextCommandTarget()
|
||||
method), and so on until ApplicationCommandTarget::getNextCommandTarget() returns 0. At this
|
||||
point if the command still hasn't been performed, it will be passed to the current
|
||||
JUCEApplication object (which is itself an ApplicationCommandTarget).
|
||||
|
||||
To exert some custom control over which ApplicationCommandTarget is chosen to invoke a command,
|
||||
you can override the ApplicationCommandManager::getFirstCommandTarget() method and choose
|
||||
the object yourself.
|
||||
|
||||
@see ApplicationCommandTarget, ApplicationCommandInfo
|
||||
*/
|
||||
class JUCE_API ApplicationCommandManager : private AsyncUpdater,
|
||||
private FocusChangeListener
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an ApplicationCommandManager.
|
||||
|
||||
Once created, you'll need to register all your app's commands with it, using
|
||||
ApplicationCommandManager::registerAllCommandsForTarget() or
|
||||
ApplicationCommandManager::registerCommand().
|
||||
*/
|
||||
ApplicationCommandManager();
|
||||
|
||||
/** Destructor.
|
||||
|
||||
Make sure that you don't delete this if pointers to it are still being used by
|
||||
objects such as PopupMenus or Buttons.
|
||||
*/
|
||||
virtual ~ApplicationCommandManager();
|
||||
|
||||
//==============================================================================
|
||||
/** Clears the current list of all commands.
|
||||
|
||||
Note that this will also clear the contents of the KeyPressMappingSet.
|
||||
*/
|
||||
void clearCommands();
|
||||
|
||||
/** Adds a command to the list of registered commands.
|
||||
|
||||
@see registerAllCommandsForTarget
|
||||
*/
|
||||
void registerCommand (const ApplicationCommandInfo& newCommand);
|
||||
|
||||
/** Adds all the commands that this target publishes to the manager's list.
|
||||
|
||||
This will use ApplicationCommandTarget::getAllCommands() and ApplicationCommandTarget::getCommandInfo()
|
||||
to get details about all the commands that this target can do, and will call
|
||||
registerCommand() to add each one to the manger's list.
|
||||
|
||||
@see registerCommand
|
||||
*/
|
||||
void registerAllCommandsForTarget (ApplicationCommandTarget* target);
|
||||
|
||||
/** Removes the command with a specified ID.
|
||||
|
||||
Note that this will also remove any key mappings that are mapped to the command.
|
||||
*/
|
||||
void removeCommand (CommandID commandID);
|
||||
|
||||
/** This should be called to tell the manager that one of its registered commands may have changed
|
||||
its active status.
|
||||
|
||||
Because the command manager only finds out whether a command is active or inactive by querying
|
||||
the current ApplicationCommandTarget, this is used to tell it that things may have changed. It
|
||||
allows things like buttons to update their enablement, etc.
|
||||
|
||||
This method will cause an asynchronous call to ApplicationCommandManagerListener::applicationCommandListChanged()
|
||||
for any registered listeners.
|
||||
*/
|
||||
void commandStatusChanged();
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the number of commands that have been registered.
|
||||
|
||||
@see registerCommand
|
||||
*/
|
||||
int getNumCommands() const noexcept { return commands.size(); }
|
||||
|
||||
/** Returns the details about one of the registered commands.
|
||||
|
||||
The index is between 0 and (getNumCommands() - 1).
|
||||
*/
|
||||
const ApplicationCommandInfo* getCommandForIndex (int index) const noexcept { return commands [index]; }
|
||||
|
||||
/** Returns the details about a given command ID.
|
||||
|
||||
This will search the list of registered commands for one with the given command
|
||||
ID number, and return its associated info. If no matching command is found, this
|
||||
will return 0.
|
||||
*/
|
||||
const ApplicationCommandInfo* getCommandForID (CommandID commandID) const noexcept;
|
||||
|
||||
/** Returns the name field for a command.
|
||||
|
||||
An empty string is returned if no command with this ID has been registered.
|
||||
@see getDescriptionOfCommand
|
||||
*/
|
||||
String getNameOfCommand (CommandID commandID) const noexcept;
|
||||
|
||||
/** Returns the description field for a command.
|
||||
|
||||
An empty string is returned if no command with this ID has been registered. If the
|
||||
command has no description, this will return its short name field instead.
|
||||
|
||||
@see getNameOfCommand
|
||||
*/
|
||||
String getDescriptionOfCommand (CommandID commandID) const noexcept;
|
||||
|
||||
/** Returns the list of categories.
|
||||
|
||||
This will go through all registered commands, and return a list of all the distict
|
||||
categoryName values from their ApplicationCommandInfo structure.
|
||||
|
||||
@see getCommandsInCategory()
|
||||
*/
|
||||
StringArray getCommandCategories() const;
|
||||
|
||||
/** Returns a list of all the command UIDs in a particular category.
|
||||
|
||||
@see getCommandCategories()
|
||||
*/
|
||||
Array<CommandID> getCommandsInCategory (const String& categoryName) const;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the manager's internal set of key mappings.
|
||||
|
||||
This object can be used to edit the keypresses. To actually link this object up
|
||||
to invoke commands when a key is pressed, see the comments for the KeyPressMappingSet
|
||||
class.
|
||||
|
||||
@see KeyPressMappingSet
|
||||
*/
|
||||
KeyPressMappingSet* getKeyMappings() const noexcept { return keyMappings; }
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** Invokes the given command directly, sending it to the default target.
|
||||
|
||||
This is just an easy way to call invoke() without having to fill out the InvocationInfo
|
||||
structure.
|
||||
*/
|
||||
bool invokeDirectly (CommandID commandID, bool asynchronously);
|
||||
|
||||
/** Sends a command to the default target.
|
||||
|
||||
This will choose a target using getFirstCommandTarget(), and send the specified command
|
||||
to it using the ApplicationCommandTarget::invoke() method. This means that if the
|
||||
first target can't handle the command, it will be passed on to targets further down the
|
||||
chain (see ApplicationCommandTarget::invoke() for more info).
|
||||
|
||||
@param invocationInfo this must be correctly filled-in, describing the context for
|
||||
the invocation.
|
||||
@param asynchronously if false, the command will be performed before this method returns.
|
||||
If true, a message will be posted so that the command will be performed
|
||||
later on the message thread, and this method will return immediately.
|
||||
|
||||
@see ApplicationCommandTarget::invoke
|
||||
*/
|
||||
bool invoke (const ApplicationCommandTarget::InvocationInfo& invocationInfo,
|
||||
bool asynchronously);
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** Chooses the ApplicationCommandTarget to which a command should be sent.
|
||||
|
||||
Whenever the manager needs to know which target a command should be sent to, it calls
|
||||
this method to determine the first one to try.
|
||||
|
||||
By default, this method will return the target that was set by calling setFirstCommandTarget().
|
||||
If no target is set, it will return the result of findDefaultComponentTarget().
|
||||
|
||||
If you need to make sure all commands go via your own custom target, then you can
|
||||
either use setFirstCommandTarget() to specify a single target, or override this method
|
||||
if you need more complex logic to choose one.
|
||||
|
||||
It may return 0 if no targets are available.
|
||||
|
||||
@see getTargetForCommand, invoke, invokeDirectly
|
||||
*/
|
||||
virtual ApplicationCommandTarget* getFirstCommandTarget (CommandID commandID);
|
||||
|
||||
/** Sets a target to be returned by getFirstCommandTarget().
|
||||
|
||||
If this is set to 0, then getFirstCommandTarget() will by default return the
|
||||
result of findDefaultComponentTarget().
|
||||
|
||||
If you use this to set a target, make sure you call setFirstCommandTarget (0) before
|
||||
deleting the target object.
|
||||
*/
|
||||
void setFirstCommandTarget (ApplicationCommandTarget* newTarget) noexcept;
|
||||
|
||||
/** Tries to find the best target to use to perform a given command.
|
||||
|
||||
This will call getFirstCommandTarget() to find the preferred target, and will
|
||||
check whether that target can handle the given command. If it can't, then it'll use
|
||||
ApplicationCommandTarget::getNextCommandTarget() to find the next one to try, and
|
||||
so on until no more are available.
|
||||
|
||||
If no targets are found that can perform the command, this method will return 0.
|
||||
|
||||
If a target is found, then it will get the target to fill-in the upToDateInfo
|
||||
structure with the latest info about that command, so that the caller can see
|
||||
whether the command is disabled, ticked, etc.
|
||||
*/
|
||||
ApplicationCommandTarget* getTargetForCommand (CommandID commandID,
|
||||
ApplicationCommandInfo& upToDateInfo);
|
||||
|
||||
//==============================================================================
|
||||
/** Registers a listener that will be called when various events occur. */
|
||||
void addListener (ApplicationCommandManagerListener* listener);
|
||||
|
||||
/** Deregisters a previously-added listener. */
|
||||
void removeListener (ApplicationCommandManagerListener* listener);
|
||||
|
||||
//==============================================================================
|
||||
/** Looks for a suitable command target based on which Components have the keyboard focus.
|
||||
|
||||
This is used by the default implementation of ApplicationCommandTarget::getFirstCommandTarget(),
|
||||
but is exposed here in case it's useful.
|
||||
|
||||
It tries to pick the best ApplicationCommandTarget by looking at focused components, top level
|
||||
windows, etc., and using the findTargetForComponent() method.
|
||||
*/
|
||||
static ApplicationCommandTarget* findDefaultComponentTarget();
|
||||
|
||||
/** Examines this component and all its parents in turn, looking for the first one
|
||||
which is a ApplicationCommandTarget.
|
||||
|
||||
Returns the first ApplicationCommandTarget that it finds, or 0 if none of them implement
|
||||
that class.
|
||||
*/
|
||||
static ApplicationCommandTarget* findTargetForComponent (Component* component);
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
OwnedArray <ApplicationCommandInfo> commands;
|
||||
ListenerList <ApplicationCommandManagerListener> listeners;
|
||||
ScopedPointer <KeyPressMappingSet> keyMappings;
|
||||
ApplicationCommandTarget* firstTarget;
|
||||
|
||||
void sendListenerInvokeCallback (const ApplicationCommandTarget::InvocationInfo& info);
|
||||
void handleAsyncUpdate();
|
||||
void globalFocusChanged (Component*);
|
||||
|
||||
#if JUCE_CATCH_DEPRECATED_CODE_MISUSE
|
||||
// This is just here to cause a compile error in old code that hasn't been changed to use the new
|
||||
// version of this method.
|
||||
virtual short getFirstCommandTarget() { return 0; }
|
||||
#endif
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ApplicationCommandManager);
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A listener that receives callbacks from an ApplicationCommandManager when
|
||||
commands are invoked or the command list is changed.
|
||||
|
||||
@see ApplicationCommandManager::addListener, ApplicationCommandManager::removeListener
|
||||
|
||||
*/
|
||||
class JUCE_API ApplicationCommandManagerListener
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Destructor. */
|
||||
virtual ~ApplicationCommandManagerListener() {}
|
||||
|
||||
/** Called when an app command is about to be invoked. */
|
||||
virtual void applicationCommandInvoked (const ApplicationCommandTarget::InvocationInfo& info) = 0;
|
||||
|
||||
/** Called when commands are registered or deregistered from the
|
||||
command manager, or when commands are made active or inactive.
|
||||
|
||||
Note that if you're using this to watch for changes to whether a command is disabled,
|
||||
you'll need to make sure that ApplicationCommandManager::commandStatusChanged() is called
|
||||
whenever the status of your command might have changed.
|
||||
*/
|
||||
virtual void applicationCommandListChanged() = 0;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif // __JUCE_APPLICATIONCOMMANDMANAGER_JUCEHEADER__
|
||||
|
|
@ -0,0 +1,194 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
//==============================================================================
|
||||
ApplicationCommandTarget::ApplicationCommandTarget()
|
||||
{
|
||||
}
|
||||
|
||||
ApplicationCommandTarget::~ApplicationCommandTarget()
|
||||
{
|
||||
messageInvoker = nullptr;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool ApplicationCommandTarget::tryToInvoke (const InvocationInfo& info, const bool async)
|
||||
{
|
||||
if (isCommandActive (info.commandID))
|
||||
{
|
||||
if (async)
|
||||
{
|
||||
if (messageInvoker == nullptr)
|
||||
messageInvoker = new CommandTargetMessageInvoker (this);
|
||||
|
||||
messageInvoker->postMessage (new Message (0, 0, 0, new ApplicationCommandTarget::InvocationInfo (info)));
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
const bool success = perform (info);
|
||||
|
||||
jassert (success); // hmm - your target should have been able to perform this command. If it can't
|
||||
// do it at the moment for some reason, it should clear the 'isActive' flag when it
|
||||
// returns the command's info.
|
||||
return success;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ApplicationCommandTarget* ApplicationCommandTarget::findFirstTargetParentComponent()
|
||||
{
|
||||
Component* c = dynamic_cast <Component*> (this);
|
||||
|
||||
if (c != nullptr)
|
||||
// (unable to use the syntax findParentComponentOfClass <ApplicationCommandTarget> () because of a VC6 compiler bug)
|
||||
return c->findParentComponentOfClass ((ApplicationCommandTarget*) nullptr);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ApplicationCommandTarget* ApplicationCommandTarget::getTargetForCommand (const CommandID commandID)
|
||||
{
|
||||
ApplicationCommandTarget* target = this;
|
||||
int depth = 0;
|
||||
|
||||
while (target != nullptr)
|
||||
{
|
||||
Array <CommandID> commandIDs;
|
||||
target->getAllCommands (commandIDs);
|
||||
|
||||
if (commandIDs.contains (commandID))
|
||||
return target;
|
||||
|
||||
target = target->getNextCommandTarget();
|
||||
|
||||
++depth;
|
||||
jassert (depth < 100); // could be a recursive command chain??
|
||||
jassert (target != this); // definitely a recursive command chain!
|
||||
|
||||
if (depth > 100 || target == this)
|
||||
break;
|
||||
}
|
||||
|
||||
if (target == nullptr)
|
||||
{
|
||||
target = JUCEApplication::getInstance();
|
||||
|
||||
if (target != nullptr)
|
||||
{
|
||||
Array <CommandID> commandIDs;
|
||||
target->getAllCommands (commandIDs);
|
||||
|
||||
if (commandIDs.contains (commandID))
|
||||
return target;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool ApplicationCommandTarget::isCommandActive (const CommandID commandID)
|
||||
{
|
||||
ApplicationCommandInfo info (commandID);
|
||||
info.flags = ApplicationCommandInfo::isDisabled;
|
||||
|
||||
getCommandInfo (commandID, info);
|
||||
|
||||
return (info.flags & ApplicationCommandInfo::isDisabled) == 0;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool ApplicationCommandTarget::invoke (const InvocationInfo& info, const bool async)
|
||||
{
|
||||
ApplicationCommandTarget* target = this;
|
||||
int depth = 0;
|
||||
|
||||
while (target != nullptr)
|
||||
{
|
||||
if (target->tryToInvoke (info, async))
|
||||
return true;
|
||||
|
||||
target = target->getNextCommandTarget();
|
||||
|
||||
++depth;
|
||||
jassert (depth < 100); // could be a recursive command chain??
|
||||
jassert (target != this); // definitely a recursive command chain!
|
||||
|
||||
if (depth > 100 || target == this)
|
||||
break;
|
||||
}
|
||||
|
||||
if (target == nullptr)
|
||||
{
|
||||
target = JUCEApplication::getInstance();
|
||||
|
||||
if (target != nullptr)
|
||||
return target->tryToInvoke (info, async);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ApplicationCommandTarget::invokeDirectly (const CommandID commandID, const bool asynchronously)
|
||||
{
|
||||
ApplicationCommandTarget::InvocationInfo info (commandID);
|
||||
info.invocationMethod = ApplicationCommandTarget::InvocationInfo::direct;
|
||||
|
||||
return invoke (info, asynchronously);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
ApplicationCommandTarget::InvocationInfo::InvocationInfo (const CommandID commandID_)
|
||||
: commandID (commandID_),
|
||||
commandFlags (0),
|
||||
invocationMethod (direct),
|
||||
originatingComponent (nullptr),
|
||||
isKeyDown (false),
|
||||
millisecsSinceKeyPressed (0)
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
ApplicationCommandTarget::CommandTargetMessageInvoker::CommandTargetMessageInvoker (ApplicationCommandTarget* const owner_)
|
||||
: owner (owner_)
|
||||
{
|
||||
}
|
||||
|
||||
ApplicationCommandTarget::CommandTargetMessageInvoker::~CommandTargetMessageInvoker()
|
||||
{
|
||||
}
|
||||
|
||||
void ApplicationCommandTarget::CommandTargetMessageInvoker::handleMessage (const Message& message)
|
||||
{
|
||||
const ScopedPointer <InvocationInfo> info (static_cast <InvocationInfo*> (message.pointerParameter));
|
||||
owner->tryToInvoke (*info, false);
|
||||
}
|
||||
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
261
modules/juce_gui_basics/commands/juce_ApplicationCommandTarget.h
Normal file
261
modules/juce_gui_basics/commands/juce_ApplicationCommandTarget.h
Normal file
|
|
@ -0,0 +1,261 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_APPLICATIONCOMMANDTARGET_JUCEHEADER__
|
||||
#define __JUCE_APPLICATIONCOMMANDTARGET_JUCEHEADER__
|
||||
|
||||
#include "juce_ApplicationCommandInfo.h"
|
||||
class Component;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A command target publishes a list of command IDs that it can perform.
|
||||
|
||||
An ApplicationCommandManager despatches commands to targets, which must be
|
||||
able to provide information about what commands they can handle.
|
||||
|
||||
To create a target, you'll need to inherit from this class, implementing all of
|
||||
its pure virtual methods.
|
||||
|
||||
For info about how a target is chosen to receive a command, see
|
||||
ApplicationCommandManager::getFirstCommandTarget().
|
||||
|
||||
@see ApplicationCommandManager, ApplicationCommandInfo
|
||||
*/
|
||||
class JUCE_API ApplicationCommandTarget
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a command target. */
|
||||
ApplicationCommandTarget();
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~ApplicationCommandTarget();
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
*/
|
||||
struct JUCE_API InvocationInfo
|
||||
{
|
||||
//==============================================================================
|
||||
InvocationInfo (const CommandID commandID);
|
||||
|
||||
//==============================================================================
|
||||
/** The UID of the command that should be performed. */
|
||||
CommandID commandID;
|
||||
|
||||
/** The command's flags.
|
||||
|
||||
See ApplicationCommandInfo for a description of these flag values.
|
||||
*/
|
||||
int commandFlags;
|
||||
|
||||
//==============================================================================
|
||||
/** The types of context in which the command might be called. */
|
||||
enum InvocationMethod
|
||||
{
|
||||
direct = 0, /**< The command is being invoked directly by a piece of code. */
|
||||
fromKeyPress, /**< The command is being invoked by a key-press. */
|
||||
fromMenu, /**< The command is being invoked by a menu selection. */
|
||||
fromButton /**< The command is being invoked by a button click. */
|
||||
};
|
||||
|
||||
/** The type of event that triggered this command. */
|
||||
InvocationMethod invocationMethod;
|
||||
|
||||
//==============================================================================
|
||||
/** If triggered by a keypress or menu, this will be the component that had the
|
||||
keyboard focus at the time.
|
||||
|
||||
If triggered by a button, it may be set to that component, or it may be null.
|
||||
*/
|
||||
Component* originatingComponent;
|
||||
|
||||
//==============================================================================
|
||||
/** The keypress that was used to invoke it.
|
||||
|
||||
Note that this will be an invalid keypress if the command was invoked
|
||||
by some other means than a keyboard shortcut.
|
||||
*/
|
||||
KeyPress keyPress;
|
||||
|
||||
/** True if the callback is being invoked when the key is pressed,
|
||||
false if the key is being released.
|
||||
|
||||
@see KeyPressMappingSet::addCommand()
|
||||
*/
|
||||
bool isKeyDown;
|
||||
|
||||
/** If the key is being released, this indicates how long it had been held
|
||||
down for.
|
||||
|
||||
(Only relevant if isKeyDown is false.)
|
||||
*/
|
||||
int millisecsSinceKeyPressed;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** This must return the next target to try after this one.
|
||||
|
||||
When a command is being sent, and the first target can't handle
|
||||
that command, this method is used to determine the next target that should
|
||||
be tried.
|
||||
|
||||
It may return 0 if it doesn't know of another target.
|
||||
|
||||
If your target is a Component, you would usually use the findFirstTargetParentComponent()
|
||||
method to return a parent component that might want to handle it.
|
||||
|
||||
@see invoke
|
||||
*/
|
||||
virtual ApplicationCommandTarget* getNextCommandTarget() = 0;
|
||||
|
||||
/** This must return a complete list of commands that this target can handle.
|
||||
|
||||
Your target should add all the command IDs that it handles to the array that is
|
||||
passed-in.
|
||||
*/
|
||||
virtual void getAllCommands (Array <CommandID>& commands) = 0;
|
||||
|
||||
/** This must provide details about one of the commands that this target can perform.
|
||||
|
||||
This will be called with one of the command IDs that the target provided in its
|
||||
getAllCommands() methods.
|
||||
|
||||
It should fill-in all appropriate fields of the ApplicationCommandInfo structure with
|
||||
suitable information about the command. (The commandID field will already have been filled-in
|
||||
by the caller).
|
||||
|
||||
The easiest way to set the info is using the ApplicationCommandInfo::setInfo() method to
|
||||
set all the fields at once.
|
||||
|
||||
If the command is currently inactive for some reason, this method must use
|
||||
ApplicationCommandInfo::setActive() to make that clear, (or it should set the isDisabled
|
||||
bit of the ApplicationCommandInfo::flags field).
|
||||
|
||||
Any default key-presses for the command should be appended to the
|
||||
ApplicationCommandInfo::defaultKeypresses field.
|
||||
|
||||
Note that if you change something that affects the status of the commands
|
||||
that would be returned by this method (e.g. something that makes some commands
|
||||
active or inactive), you should call ApplicationCommandManager::commandStatusChanged()
|
||||
to cause the manager to refresh its status.
|
||||
*/
|
||||
virtual void getCommandInfo (CommandID commandID, ApplicationCommandInfo& result) = 0;
|
||||
|
||||
/** This must actually perform the specified command.
|
||||
|
||||
If this target is able to perform the command specified by the commandID field of the
|
||||
InvocationInfo structure, then it should do so, and must return true.
|
||||
|
||||
If it can't handle this command, it should return false, which tells the caller to pass
|
||||
the command on to the next target in line.
|
||||
|
||||
@see invoke, ApplicationCommandManager::invoke
|
||||
*/
|
||||
virtual bool perform (const InvocationInfo& info) = 0;
|
||||
|
||||
//==============================================================================
|
||||
/** Makes this target invoke a command.
|
||||
|
||||
Your code can call this method to invoke a command on this target, but normally
|
||||
you'd call it indirectly via ApplicationCommandManager::invoke() or
|
||||
ApplicationCommandManager::invokeDirectly().
|
||||
|
||||
If this target can perform the given command, it will call its perform() method to
|
||||
do so. If not, then getNextCommandTarget() will be used to determine the next target
|
||||
to try, and the command will be passed along to it.
|
||||
|
||||
@param invocationInfo this must be correctly filled-in, describing the context for
|
||||
the invocation.
|
||||
@param asynchronously if false, the command will be performed before this method returns.
|
||||
If true, a message will be posted so that the command will be performed
|
||||
later on the message thread, and this method will return immediately.
|
||||
@see perform, ApplicationCommandManager::invoke
|
||||
*/
|
||||
bool invoke (const InvocationInfo& invocationInfo,
|
||||
const bool asynchronously);
|
||||
|
||||
/** Invokes a given command directly on this target.
|
||||
|
||||
This is just an easy way to call invoke() without having to fill out the InvocationInfo
|
||||
structure.
|
||||
*/
|
||||
bool invokeDirectly (const CommandID commandID,
|
||||
const bool asynchronously);
|
||||
|
||||
//==============================================================================
|
||||
/** Searches this target and all subsequent ones for the first one that can handle
|
||||
the specified command.
|
||||
|
||||
This will use getNextCommandTarget() to determine the chain of targets to try
|
||||
after this one.
|
||||
*/
|
||||
ApplicationCommandTarget* getTargetForCommand (const CommandID commandID);
|
||||
|
||||
/** Checks whether this command can currently be performed by this target.
|
||||
|
||||
This will return true only if a call to getCommandInfo() doesn't set the
|
||||
isDisabled flag to indicate that the command is inactive.
|
||||
*/
|
||||
bool isCommandActive (const CommandID commandID);
|
||||
|
||||
/** If this object is a Component, this method will seach upwards in its current
|
||||
UI hierarchy for the next parent component that implements the
|
||||
ApplicationCommandTarget class.
|
||||
|
||||
If your target is a Component, this is a very handy method to use in your
|
||||
getNextCommandTarget() implementation.
|
||||
*/
|
||||
ApplicationCommandTarget* findFirstTargetParentComponent();
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
// (for async invocation of commands)
|
||||
class CommandTargetMessageInvoker : public MessageListener
|
||||
{
|
||||
public:
|
||||
CommandTargetMessageInvoker (ApplicationCommandTarget* owner);
|
||||
~CommandTargetMessageInvoker();
|
||||
|
||||
void handleMessage (const Message& message);
|
||||
|
||||
private:
|
||||
ApplicationCommandTarget* const owner;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (CommandTargetMessageInvoker);
|
||||
};
|
||||
|
||||
ScopedPointer <CommandTargetMessageInvoker> messageInvoker;
|
||||
|
||||
friend class CommandTargetMessageInvoker;
|
||||
bool tryToInvoke (const InvocationInfo& info, bool async);
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ApplicationCommandTarget);
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCE_APPLICATIONCOMMANDTARGET_JUCEHEADER__
|
||||
431
modules/juce_gui_basics/commands/juce_KeyPressMappingSet.cpp
Normal file
431
modules/juce_gui_basics/commands/juce_KeyPressMappingSet.cpp
Normal file
|
|
@ -0,0 +1,431 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
//==============================================================================
|
||||
KeyPressMappingSet::KeyPressMappingSet (ApplicationCommandManager* const commandManager_)
|
||||
: commandManager (commandManager_)
|
||||
{
|
||||
// A manager is needed to get the descriptions of commands, and will be called when
|
||||
// a command is invoked. So you can't leave this null..
|
||||
jassert (commandManager_ != nullptr);
|
||||
|
||||
Desktop::getInstance().addFocusChangeListener (this);
|
||||
}
|
||||
|
||||
KeyPressMappingSet::KeyPressMappingSet (const KeyPressMappingSet& other)
|
||||
: commandManager (other.commandManager)
|
||||
{
|
||||
Desktop::getInstance().addFocusChangeListener (this);
|
||||
}
|
||||
|
||||
KeyPressMappingSet::~KeyPressMappingSet()
|
||||
{
|
||||
Desktop::getInstance().removeFocusChangeListener (this);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
Array<KeyPress> KeyPressMappingSet::getKeyPressesAssignedToCommand (const CommandID commandID) const
|
||||
{
|
||||
for (int i = 0; i < mappings.size(); ++i)
|
||||
if (mappings.getUnchecked(i)->commandID == commandID)
|
||||
return mappings.getUnchecked (i)->keypresses;
|
||||
|
||||
return Array<KeyPress>();
|
||||
}
|
||||
|
||||
void KeyPressMappingSet::addKeyPress (const CommandID commandID,
|
||||
const KeyPress& newKeyPress,
|
||||
int insertIndex)
|
||||
{
|
||||
// If you specify an upper-case letter but no shift key, how is the user supposed to press it!?
|
||||
// Stick to lower-case letters when defining a keypress, to avoid ambiguity.
|
||||
jassert (! (CharacterFunctions::isUpperCase (newKeyPress.getTextCharacter())
|
||||
&& ! newKeyPress.getModifiers().isShiftDown()));
|
||||
|
||||
if (findCommandForKeyPress (newKeyPress) != commandID)
|
||||
{
|
||||
if (newKeyPress.isValid())
|
||||
{
|
||||
for (int i = mappings.size(); --i >= 0;)
|
||||
{
|
||||
if (mappings.getUnchecked(i)->commandID == commandID)
|
||||
{
|
||||
mappings.getUnchecked(i)->keypresses.insert (insertIndex, newKeyPress);
|
||||
|
||||
sendChangeMessage();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const ApplicationCommandInfo* const ci = commandManager->getCommandForID (commandID);
|
||||
|
||||
if (ci != nullptr)
|
||||
{
|
||||
CommandMapping* const cm = new CommandMapping();
|
||||
cm->commandID = commandID;
|
||||
cm->keypresses.add (newKeyPress);
|
||||
cm->wantsKeyUpDownCallbacks = (ci->flags & ApplicationCommandInfo::wantsKeyUpDownCallbacks) != 0;
|
||||
|
||||
mappings.add (cm);
|
||||
sendChangeMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KeyPressMappingSet::resetToDefaultMappings()
|
||||
{
|
||||
mappings.clear();
|
||||
|
||||
for (int i = 0; i < commandManager->getNumCommands(); ++i)
|
||||
{
|
||||
const ApplicationCommandInfo* const ci = commandManager->getCommandForIndex (i);
|
||||
|
||||
for (int j = 0; j < ci->defaultKeypresses.size(); ++j)
|
||||
{
|
||||
addKeyPress (ci->commandID,
|
||||
ci->defaultKeypresses.getReference (j));
|
||||
}
|
||||
}
|
||||
|
||||
sendChangeMessage();
|
||||
}
|
||||
|
||||
void KeyPressMappingSet::resetToDefaultMapping (const CommandID commandID)
|
||||
{
|
||||
clearAllKeyPresses (commandID);
|
||||
|
||||
const ApplicationCommandInfo* const ci = commandManager->getCommandForID (commandID);
|
||||
|
||||
for (int j = 0; j < ci->defaultKeypresses.size(); ++j)
|
||||
{
|
||||
addKeyPress (ci->commandID,
|
||||
ci->defaultKeypresses.getReference (j));
|
||||
}
|
||||
}
|
||||
|
||||
void KeyPressMappingSet::clearAllKeyPresses()
|
||||
{
|
||||
if (mappings.size() > 0)
|
||||
{
|
||||
sendChangeMessage();
|
||||
mappings.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void KeyPressMappingSet::clearAllKeyPresses (const CommandID commandID)
|
||||
{
|
||||
for (int i = mappings.size(); --i >= 0;)
|
||||
{
|
||||
if (mappings.getUnchecked(i)->commandID == commandID)
|
||||
{
|
||||
mappings.remove (i);
|
||||
sendChangeMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KeyPressMappingSet::removeKeyPress (const KeyPress& keypress)
|
||||
{
|
||||
if (keypress.isValid())
|
||||
{
|
||||
for (int i = mappings.size(); --i >= 0;)
|
||||
{
|
||||
CommandMapping* const cm = mappings.getUnchecked(i);
|
||||
|
||||
for (int j = cm->keypresses.size(); --j >= 0;)
|
||||
{
|
||||
if (keypress == cm->keypresses [j])
|
||||
{
|
||||
cm->keypresses.remove (j);
|
||||
sendChangeMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KeyPressMappingSet::removeKeyPress (const CommandID commandID, const int keyPressIndex)
|
||||
{
|
||||
for (int i = mappings.size(); --i >= 0;)
|
||||
{
|
||||
if (mappings.getUnchecked(i)->commandID == commandID)
|
||||
{
|
||||
mappings.getUnchecked(i)->keypresses.remove (keyPressIndex);
|
||||
sendChangeMessage();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
CommandID KeyPressMappingSet::findCommandForKeyPress (const KeyPress& keyPress) const noexcept
|
||||
{
|
||||
for (int i = 0; i < mappings.size(); ++i)
|
||||
if (mappings.getUnchecked(i)->keypresses.contains (keyPress))
|
||||
return mappings.getUnchecked(i)->commandID;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool KeyPressMappingSet::containsMapping (const CommandID commandID, const KeyPress& keyPress) const noexcept
|
||||
{
|
||||
for (int i = mappings.size(); --i >= 0;)
|
||||
if (mappings.getUnchecked(i)->commandID == commandID)
|
||||
return mappings.getUnchecked(i)->keypresses.contains (keyPress);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void KeyPressMappingSet::invokeCommand (const CommandID commandID,
|
||||
const KeyPress& key,
|
||||
const bool isKeyDown,
|
||||
const int millisecsSinceKeyPressed,
|
||||
Component* const originatingComponent) const
|
||||
{
|
||||
ApplicationCommandTarget::InvocationInfo info (commandID);
|
||||
|
||||
info.invocationMethod = ApplicationCommandTarget::InvocationInfo::fromKeyPress;
|
||||
info.isKeyDown = isKeyDown;
|
||||
info.keyPress = key;
|
||||
info.millisecsSinceKeyPressed = millisecsSinceKeyPressed;
|
||||
info.originatingComponent = originatingComponent;
|
||||
|
||||
commandManager->invoke (info, false);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool KeyPressMappingSet::restoreFromXml (const XmlElement& xmlVersion)
|
||||
{
|
||||
if (xmlVersion.hasTagName ("KEYMAPPINGS"))
|
||||
{
|
||||
if (xmlVersion.getBoolAttribute ("basedOnDefaults", true))
|
||||
{
|
||||
// if the XML was created as a set of differences from the default mappings,
|
||||
// (i.e. by calling createXml (true)), then we need to first restore the defaults.
|
||||
resetToDefaultMappings();
|
||||
}
|
||||
else
|
||||
{
|
||||
// if the XML was created calling createXml (false), then we need to clear all
|
||||
// the keys and treat the xml as describing the entire set of mappings.
|
||||
clearAllKeyPresses();
|
||||
}
|
||||
|
||||
forEachXmlChildElement (xmlVersion, map)
|
||||
{
|
||||
const CommandID commandId = map->getStringAttribute ("commandId").getHexValue32();
|
||||
|
||||
if (commandId != 0)
|
||||
{
|
||||
const KeyPress key (KeyPress::createFromDescription (map->getStringAttribute ("key")));
|
||||
|
||||
if (map->hasTagName ("MAPPING"))
|
||||
{
|
||||
addKeyPress (commandId, key);
|
||||
}
|
||||
else if (map->hasTagName ("UNMAPPING"))
|
||||
{
|
||||
if (containsMapping (commandId, key))
|
||||
removeKeyPress (key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
XmlElement* KeyPressMappingSet::createXml (const bool saveDifferencesFromDefaultSet) const
|
||||
{
|
||||
ScopedPointer <KeyPressMappingSet> defaultSet;
|
||||
|
||||
if (saveDifferencesFromDefaultSet)
|
||||
{
|
||||
defaultSet = new KeyPressMappingSet (commandManager);
|
||||
defaultSet->resetToDefaultMappings();
|
||||
}
|
||||
|
||||
XmlElement* const doc = new XmlElement ("KEYMAPPINGS");
|
||||
|
||||
doc->setAttribute ("basedOnDefaults", saveDifferencesFromDefaultSet);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < mappings.size(); ++i)
|
||||
{
|
||||
const CommandMapping* const cm = mappings.getUnchecked(i);
|
||||
|
||||
for (int j = 0; j < cm->keypresses.size(); ++j)
|
||||
{
|
||||
if (defaultSet == nullptr
|
||||
|| ! defaultSet->containsMapping (cm->commandID, cm->keypresses.getReference (j)))
|
||||
{
|
||||
XmlElement* const map = doc->createNewChildElement ("MAPPING");
|
||||
|
||||
map->setAttribute ("commandId", String::toHexString ((int) cm->commandID));
|
||||
map->setAttribute ("description", commandManager->getDescriptionOfCommand (cm->commandID));
|
||||
map->setAttribute ("key", cm->keypresses.getReference (j).getTextDescription());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (defaultSet != nullptr)
|
||||
{
|
||||
for (i = 0; i < defaultSet->mappings.size(); ++i)
|
||||
{
|
||||
const CommandMapping* const cm = defaultSet->mappings.getUnchecked(i);
|
||||
|
||||
for (int j = 0; j < cm->keypresses.size(); ++j)
|
||||
{
|
||||
if (! containsMapping (cm->commandID, cm->keypresses.getReference (j)))
|
||||
{
|
||||
XmlElement* const map = doc->createNewChildElement ("UNMAPPING");
|
||||
|
||||
map->setAttribute ("commandId", String::toHexString ((int) cm->commandID));
|
||||
map->setAttribute ("description", commandManager->getDescriptionOfCommand (cm->commandID));
|
||||
map->setAttribute ("key", cm->keypresses.getReference (j).getTextDescription());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool KeyPressMappingSet::keyPressed (const KeyPress& key,
|
||||
Component* originatingComponent)
|
||||
{
|
||||
bool commandWasDisabled = false;
|
||||
|
||||
for (int i = 0; i < mappings.size(); ++i)
|
||||
{
|
||||
CommandMapping* const cm = mappings.getUnchecked(i);
|
||||
|
||||
if (cm->keypresses.contains (key))
|
||||
{
|
||||
const ApplicationCommandInfo* const ci = commandManager->getCommandForID (cm->commandID);
|
||||
|
||||
if (ci != nullptr
|
||||
&& (ci->flags & ApplicationCommandInfo::wantsKeyUpDownCallbacks) == 0)
|
||||
{
|
||||
ApplicationCommandInfo info (0);
|
||||
|
||||
if (commandManager->getTargetForCommand (cm->commandID, info) != 0)
|
||||
{
|
||||
if ((info.flags & ApplicationCommandInfo::isDisabled) == 0)
|
||||
{
|
||||
invokeCommand (cm->commandID, key, true, 0, originatingComponent);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
commandWasDisabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (originatingComponent != nullptr && commandWasDisabled)
|
||||
originatingComponent->getLookAndFeel().playAlertSound();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool KeyPressMappingSet::keyStateChanged (const bool /*isKeyDown*/, Component* originatingComponent)
|
||||
{
|
||||
bool used = false;
|
||||
const uint32 now = Time::getMillisecondCounter();
|
||||
|
||||
for (int i = mappings.size(); --i >= 0;)
|
||||
{
|
||||
CommandMapping* const cm = mappings.getUnchecked(i);
|
||||
|
||||
if (cm->wantsKeyUpDownCallbacks)
|
||||
{
|
||||
for (int j = cm->keypresses.size(); --j >= 0;)
|
||||
{
|
||||
const KeyPress key (cm->keypresses.getReference (j));
|
||||
const bool isDown = key.isCurrentlyDown();
|
||||
|
||||
int keyPressEntryIndex = 0;
|
||||
bool wasDown = false;
|
||||
|
||||
for (int k = keysDown.size(); --k >= 0;)
|
||||
{
|
||||
if (key == keysDown.getUnchecked(k)->key)
|
||||
{
|
||||
keyPressEntryIndex = k;
|
||||
wasDown = true;
|
||||
used = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isDown != wasDown)
|
||||
{
|
||||
int millisecs = 0;
|
||||
|
||||
if (isDown)
|
||||
{
|
||||
KeyPressTime* const k = new KeyPressTime();
|
||||
k->key = key;
|
||||
k->timeWhenPressed = now;
|
||||
|
||||
keysDown.add (k);
|
||||
}
|
||||
else
|
||||
{
|
||||
const uint32 pressTime = keysDown.getUnchecked (keyPressEntryIndex)->timeWhenPressed;
|
||||
|
||||
if (now > pressTime)
|
||||
millisecs = (int) (now - pressTime);
|
||||
|
||||
keysDown.remove (keyPressEntryIndex);
|
||||
}
|
||||
|
||||
invokeCommand (cm->commandID, key, isDown, millisecs, originatingComponent);
|
||||
used = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return used;
|
||||
}
|
||||
|
||||
void KeyPressMappingSet::globalFocusChanged (Component* focusedComponent)
|
||||
{
|
||||
if (focusedComponent != nullptr)
|
||||
focusedComponent->keyStateChanged (false);
|
||||
}
|
||||
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
259
modules/juce_gui_basics/commands/juce_KeyPressMappingSet.h
Normal file
259
modules/juce_gui_basics/commands/juce_KeyPressMappingSet.h
Normal file
|
|
@ -0,0 +1,259 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_KEYPRESSMAPPINGSET_JUCEHEADER__
|
||||
#define __JUCE_KEYPRESSMAPPINGSET_JUCEHEADER__
|
||||
|
||||
#include "../keyboard/juce_KeyListener.h"
|
||||
#include "../commands/juce_ApplicationCommandManager.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Manages and edits a list of keypresses, which it uses to invoke the appropriate
|
||||
command in a ApplicationCommandManager.
|
||||
|
||||
Normally, you won't actually create a KeyPressMappingSet directly, because
|
||||
each ApplicationCommandManager contains its own KeyPressMappingSet, so typically
|
||||
you'd create yourself an ApplicationCommandManager, and call its
|
||||
ApplicationCommandManager::getKeyMappings() method to get a pointer to its
|
||||
KeyPressMappingSet.
|
||||
|
||||
For one of these to actually use keypresses, you'll need to add it as a KeyListener
|
||||
to the top-level component for which you want to handle keystrokes. So for example:
|
||||
|
||||
@code
|
||||
class MyMainWindow : public Component
|
||||
{
|
||||
ApplicationCommandManager* myCommandManager;
|
||||
|
||||
public:
|
||||
MyMainWindow()
|
||||
{
|
||||
myCommandManager = new ApplicationCommandManager();
|
||||
|
||||
// first, make sure the command manager has registered all the commands that its
|
||||
// targets can perform..
|
||||
myCommandManager->registerAllCommandsForTarget (myCommandTarget1);
|
||||
myCommandManager->registerAllCommandsForTarget (myCommandTarget2);
|
||||
|
||||
// this will use the command manager to initialise the KeyPressMappingSet with
|
||||
// the default keypresses that were specified when the targets added their commands
|
||||
// to the manager.
|
||||
myCommandManager->getKeyMappings()->resetToDefaultMappings();
|
||||
|
||||
// having set up the default key-mappings, you might now want to load the last set
|
||||
// of mappings that the user configured.
|
||||
myCommandManager->getKeyMappings()->restoreFromXml (lastSavedKeyMappingsXML);
|
||||
|
||||
// Now tell our top-level window to send any keypresses that arrive to the
|
||||
// KeyPressMappingSet, which will use them to invoke the appropriate commands.
|
||||
addKeyListener (myCommandManager->getKeyMappings());
|
||||
}
|
||||
|
||||
...
|
||||
}
|
||||
@endcode
|
||||
|
||||
KeyPressMappingSet derives from ChangeBroadcaster so that interested parties can
|
||||
register to be told when a command or mapping is added, removed, etc.
|
||||
|
||||
There's also a UI component called KeyMappingEditorComponent that can be used
|
||||
to easily edit the key mappings.
|
||||
|
||||
@see Component::addKeyListener(), KeyMappingEditorComponent, ApplicationCommandManager
|
||||
*/
|
||||
class JUCE_API KeyPressMappingSet : public KeyListener,
|
||||
public ChangeBroadcaster,
|
||||
public FocusChangeListener
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a KeyPressMappingSet for a given command manager.
|
||||
|
||||
Normally, you won't actually create a KeyPressMappingSet directly, because
|
||||
each ApplicationCommandManager contains its own KeyPressMappingSet, so the
|
||||
best thing to do is to create your ApplicationCommandManager, and use the
|
||||
ApplicationCommandManager::getKeyMappings() method to access its mappings.
|
||||
|
||||
When a suitable keypress happens, the manager's invoke() method will be
|
||||
used to invoke the appropriate command.
|
||||
|
||||
@see ApplicationCommandManager
|
||||
*/
|
||||
explicit KeyPressMappingSet (ApplicationCommandManager* commandManager);
|
||||
|
||||
/** Creates an copy of a KeyPressMappingSet. */
|
||||
KeyPressMappingSet (const KeyPressMappingSet& other);
|
||||
|
||||
/** Destructor. */
|
||||
~KeyPressMappingSet();
|
||||
|
||||
//==============================================================================
|
||||
ApplicationCommandManager* getCommandManager() const noexcept { return commandManager; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a list of keypresses that are assigned to a particular command.
|
||||
|
||||
@param commandID the command's ID
|
||||
*/
|
||||
Array<KeyPress> getKeyPressesAssignedToCommand (CommandID commandID) const;
|
||||
|
||||
/** Assigns a keypress to a command.
|
||||
|
||||
If the keypress is already assigned to a different command, it will first be
|
||||
removed from that command, to avoid it triggering multiple functions.
|
||||
|
||||
@param commandID the ID of the command that you want to add a keypress to. If
|
||||
this is 0, the keypress will be removed from anything that it
|
||||
was previously assigned to, but not re-assigned
|
||||
@param newKeyPress the new key-press
|
||||
@param insertIndex if this is less than zero, the key will be appended to the
|
||||
end of the list of keypresses; otherwise the new keypress will
|
||||
be inserted into the existing list at this index
|
||||
*/
|
||||
void addKeyPress (CommandID commandID,
|
||||
const KeyPress& newKeyPress,
|
||||
int insertIndex = -1);
|
||||
|
||||
/** Reset all mappings to the defaults, as dictated by the ApplicationCommandManager.
|
||||
|
||||
@see resetToDefaultMapping
|
||||
*/
|
||||
void resetToDefaultMappings();
|
||||
|
||||
/** Resets all key-mappings to the defaults for a particular command.
|
||||
|
||||
@see resetToDefaultMappings
|
||||
*/
|
||||
void resetToDefaultMapping (CommandID commandID);
|
||||
|
||||
/** Removes all keypresses that are assigned to any commands. */
|
||||
void clearAllKeyPresses();
|
||||
|
||||
/** Removes all keypresses that are assigned to a particular command. */
|
||||
void clearAllKeyPresses (CommandID commandID);
|
||||
|
||||
/** Removes one of the keypresses that are assigned to a command.
|
||||
|
||||
See the getKeyPressesAssignedToCommand() for the list of keypresses to
|
||||
which the keyPressIndex refers.
|
||||
*/
|
||||
void removeKeyPress (CommandID commandID, int keyPressIndex);
|
||||
|
||||
/** Removes a keypress from any command that it may be assigned to.
|
||||
*/
|
||||
void removeKeyPress (const KeyPress& keypress);
|
||||
|
||||
/** Returns true if the given command is linked to this key. */
|
||||
bool containsMapping (CommandID commandID, const KeyPress& keyPress) const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Looks for a command that corresponds to a keypress.
|
||||
|
||||
@returns the UID of the command or 0 if none was found
|
||||
*/
|
||||
CommandID findCommandForKeyPress (const KeyPress& keyPress) const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Tries to recreate the mappings from a previously stored state.
|
||||
|
||||
The XML passed in must have been created by the createXml() method.
|
||||
|
||||
If the stored state makes any reference to commands that aren't
|
||||
currently available, these will be ignored.
|
||||
|
||||
If the set of mappings being loaded was a set of differences (using createXml (true)),
|
||||
then this will call resetToDefaultMappings() and then merge the saved mappings
|
||||
on top. If the saved set was created with createXml (false), then this method
|
||||
will first clear all existing mappings and load the saved ones as a complete set.
|
||||
|
||||
@returns true if it manages to load the XML correctly
|
||||
@see createXml
|
||||
*/
|
||||
bool restoreFromXml (const XmlElement& xmlVersion);
|
||||
|
||||
/** Creates an XML representation of the current mappings.
|
||||
|
||||
This will produce a lump of XML that can be later reloaded using
|
||||
restoreFromXml() to recreate the current mapping state.
|
||||
|
||||
The object that is returned must be deleted by the caller.
|
||||
|
||||
@param saveDifferencesFromDefaultSet if this is false, then all keypresses
|
||||
will be saved into the XML. If it's true, then the XML will
|
||||
only store the differences between the current mappings and
|
||||
the default mappings you'd get from calling resetToDefaultMappings().
|
||||
The advantage of saving a set of differences from the default is that
|
||||
if you change the default mappings (in a new version of your app, for
|
||||
example), then these will be merged into a user's saved preferences.
|
||||
|
||||
@see restoreFromXml
|
||||
*/
|
||||
XmlElement* createXml (bool saveDifferencesFromDefaultSet) const;
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
bool keyPressed (const KeyPress& key, Component* originatingComponent);
|
||||
/** @internal */
|
||||
bool keyStateChanged (bool isKeyDown, Component* originatingComponent);
|
||||
/** @internal */
|
||||
void globalFocusChanged (Component* focusedComponent);
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
ApplicationCommandManager* commandManager;
|
||||
|
||||
struct CommandMapping
|
||||
{
|
||||
CommandID commandID;
|
||||
Array <KeyPress> keypresses;
|
||||
bool wantsKeyUpDownCallbacks;
|
||||
};
|
||||
|
||||
OwnedArray <CommandMapping> mappings;
|
||||
|
||||
struct KeyPressTime
|
||||
{
|
||||
KeyPress key;
|
||||
uint32 timeWhenPressed;
|
||||
};
|
||||
|
||||
OwnedArray <KeyPressTime> keysDown;
|
||||
|
||||
void handleMessage (const Message& message);
|
||||
|
||||
void invokeCommand (const CommandID commandID,
|
||||
const KeyPress& keyPress,
|
||||
const bool isKeyDown,
|
||||
const int millisecsSinceKeyPressed,
|
||||
Component* const originatingComponent) const;
|
||||
|
||||
KeyPressMappingSet& operator= (const KeyPressMappingSet&);
|
||||
JUCE_LEAK_DETECTOR (KeyPressMappingSet);
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCE_KEYPRESSMAPPINGSET_JUCEHEADER__
|
||||
2995
modules/juce_gui_basics/components/juce_Component.cpp
Normal file
2995
modules/juce_gui_basics/components/juce_Component.cpp
Normal file
File diff suppressed because it is too large
Load diff
2343
modules/juce_gui_basics/components/juce_Component.h
Normal file
2343
modules/juce_gui_basics/components/juce_Component.h
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
void ComponentListener::componentMovedOrResized (Component&, bool, bool) {}
|
||||
void ComponentListener::componentBroughtToFront (Component&) {}
|
||||
void ComponentListener::componentVisibilityChanged (Component&) {}
|
||||
void ComponentListener::componentChildrenChanged (Component&) {}
|
||||
void ComponentListener::componentParentHierarchyChanged (Component&) {}
|
||||
void ComponentListener::componentNameChanged (Component&) {}
|
||||
void ComponentListener::componentBeingDeleted (Component&) {}
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
113
modules/juce_gui_basics/components/juce_ComponentListener.h
Normal file
113
modules/juce_gui_basics/components/juce_ComponentListener.h
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_COMPONENTLISTENER_JUCEHEADER__
|
||||
#define __JUCE_COMPONENTLISTENER_JUCEHEADER__
|
||||
|
||||
class Component;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Gets informed about changes to a component's hierarchy or position.
|
||||
|
||||
To monitor a component for changes, register a subclass of ComponentListener
|
||||
with the component using Component::addComponentListener().
|
||||
|
||||
Be sure to deregister listeners before you delete them!
|
||||
|
||||
@see Component::addComponentListener, Component::removeComponentListener
|
||||
*/
|
||||
class JUCE_API ComponentListener
|
||||
{
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~ComponentListener() {}
|
||||
|
||||
/** Called when the component's position or size changes.
|
||||
|
||||
@param component the component that was moved or resized
|
||||
@param wasMoved true if the component's top-left corner has just moved
|
||||
@param wasResized true if the component's width or height has just changed
|
||||
@see Component::setBounds, Component::resized, Component::moved
|
||||
*/
|
||||
virtual void componentMovedOrResized (Component& component,
|
||||
bool wasMoved,
|
||||
bool wasResized);
|
||||
|
||||
/** Called when the component is brought to the top of the z-order.
|
||||
|
||||
@param component the component that was moved
|
||||
@see Component::toFront, Component::broughtToFront
|
||||
*/
|
||||
virtual void componentBroughtToFront (Component& component);
|
||||
|
||||
/** Called when the component is made visible or invisible.
|
||||
|
||||
@param component the component that changed
|
||||
@see Component::setVisible
|
||||
*/
|
||||
virtual void componentVisibilityChanged (Component& component);
|
||||
|
||||
/** Called when the component has children added or removed.
|
||||
|
||||
@param component the component whose children were changed
|
||||
@see Component::childrenChanged, Component::addChildComponent,
|
||||
Component::removeChildComponent
|
||||
*/
|
||||
virtual void componentChildrenChanged (Component& component);
|
||||
|
||||
/** Called to indicate that the component's parents have changed.
|
||||
|
||||
When a component is added or removed from its parent, all of its children
|
||||
will produce this notification (recursively - so all children of its
|
||||
children will also be called as well).
|
||||
|
||||
@param component the component that this listener is registered with
|
||||
@see Component::parentHierarchyChanged
|
||||
*/
|
||||
virtual void componentParentHierarchyChanged (Component& component);
|
||||
|
||||
/** Called when the component's name is changed.
|
||||
|
||||
@see Component::setName, Component::getName
|
||||
*/
|
||||
virtual void componentNameChanged (Component& component);
|
||||
|
||||
/** Called when the component is in the process of being deleted.
|
||||
|
||||
This callback is made from inside the destructor, so be very, very cautious
|
||||
about what you do in here.
|
||||
|
||||
In particular, bear in mind that it's the Component base class's destructor that calls
|
||||
this - so if the object that's being deleted is a subclass of Component, then the
|
||||
subclass layers of the object will already have been destructed when it gets to this
|
||||
point!
|
||||
*/
|
||||
virtual void componentBeingDeleted (Component& component);
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCE_COMPONENTLISTENER_JUCEHEADER__
|
||||
453
modules/juce_gui_basics/components/juce_Desktop.cpp
Normal file
453
modules/juce_gui_basics/components/juce_Desktop.cpp
Normal file
|
|
@ -0,0 +1,453 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
//==============================================================================
|
||||
Desktop::Desktop()
|
||||
: mouseClickCounter (0),
|
||||
kioskModeComponent (nullptr),
|
||||
allowedOrientations (allOrientations)
|
||||
{
|
||||
createMouseInputSources();
|
||||
refreshMonitorSizes();
|
||||
}
|
||||
|
||||
Desktop::~Desktop()
|
||||
{
|
||||
setScreenSaverEnabled (true);
|
||||
|
||||
jassert (instance == this);
|
||||
instance = nullptr;
|
||||
|
||||
// doh! If you don't delete all your windows before exiting, you're going to
|
||||
// be leaking memory!
|
||||
jassert (desktopComponents.size() == 0);
|
||||
}
|
||||
|
||||
Desktop& JUCE_CALLTYPE Desktop::getInstance()
|
||||
{
|
||||
if (instance == nullptr)
|
||||
instance = new Desktop();
|
||||
|
||||
return *instance;
|
||||
}
|
||||
|
||||
Desktop* Desktop::instance = nullptr;
|
||||
|
||||
//==============================================================================
|
||||
void Desktop::refreshMonitorSizes()
|
||||
{
|
||||
Array <Rectangle<int> > oldClipped, oldUnclipped;
|
||||
oldClipped.swapWithArray (monitorCoordsClipped);
|
||||
oldUnclipped.swapWithArray (monitorCoordsUnclipped);
|
||||
|
||||
getCurrentMonitorPositions (monitorCoordsClipped, true);
|
||||
getCurrentMonitorPositions (monitorCoordsUnclipped, false);
|
||||
jassert (monitorCoordsClipped.size() == monitorCoordsUnclipped.size());
|
||||
|
||||
if (oldClipped != monitorCoordsClipped
|
||||
|| oldUnclipped != monitorCoordsUnclipped)
|
||||
{
|
||||
for (int i = ComponentPeer::getNumPeers(); --i >= 0;)
|
||||
{
|
||||
ComponentPeer* const p = ComponentPeer::getPeer (i);
|
||||
if (p != nullptr)
|
||||
p->handleScreenSizeChange();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int Desktop::getNumDisplayMonitors() const noexcept
|
||||
{
|
||||
return monitorCoordsClipped.size();
|
||||
}
|
||||
|
||||
const Rectangle<int> Desktop::getDisplayMonitorCoordinates (const int index, const bool clippedToWorkArea) const noexcept
|
||||
{
|
||||
return clippedToWorkArea ? monitorCoordsClipped [index]
|
||||
: monitorCoordsUnclipped [index];
|
||||
}
|
||||
|
||||
const RectangleList Desktop::getAllMonitorDisplayAreas (const bool clippedToWorkArea) const
|
||||
{
|
||||
RectangleList rl;
|
||||
|
||||
for (int i = 0; i < getNumDisplayMonitors(); ++i)
|
||||
rl.addWithoutMerging (getDisplayMonitorCoordinates (i, clippedToWorkArea));
|
||||
|
||||
return rl;
|
||||
}
|
||||
|
||||
const Rectangle<int> Desktop::getMainMonitorArea (const bool clippedToWorkArea) const noexcept
|
||||
{
|
||||
return getDisplayMonitorCoordinates (0, clippedToWorkArea);
|
||||
}
|
||||
|
||||
const Rectangle<int> Desktop::getMonitorAreaContaining (const Point<int>& position, const bool clippedToWorkArea) const
|
||||
{
|
||||
Rectangle<int> best (getMainMonitorArea (clippedToWorkArea));
|
||||
double bestDistance = 1.0e10;
|
||||
|
||||
for (int i = getNumDisplayMonitors(); --i >= 0;)
|
||||
{
|
||||
const Rectangle<int> rect (getDisplayMonitorCoordinates (i, clippedToWorkArea));
|
||||
|
||||
if (rect.contains (position))
|
||||
return rect;
|
||||
|
||||
const double distance = rect.getCentre().getDistanceFrom (position);
|
||||
|
||||
if (distance < bestDistance)
|
||||
{
|
||||
bestDistance = distance;
|
||||
best = rect;
|
||||
}
|
||||
}
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
int Desktop::getNumComponents() const noexcept
|
||||
{
|
||||
return desktopComponents.size();
|
||||
}
|
||||
|
||||
Component* Desktop::getComponent (const int index) const noexcept
|
||||
{
|
||||
return desktopComponents [index];
|
||||
}
|
||||
|
||||
Component* Desktop::findComponentAt (const Point<int>& screenPosition) const
|
||||
{
|
||||
for (int i = desktopComponents.size(); --i >= 0;)
|
||||
{
|
||||
Component* const c = desktopComponents.getUnchecked(i);
|
||||
|
||||
if (c->isVisible())
|
||||
{
|
||||
const Point<int> relative (c->getLocalPoint (nullptr, screenPosition));
|
||||
|
||||
if (c->contains (relative))
|
||||
return c->getComponentAt (relative);
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
LookAndFeel& Desktop::getDefaultLookAndFeel() noexcept
|
||||
{
|
||||
if (currentLookAndFeel == nullptr)
|
||||
{
|
||||
if (defaultLookAndFeel == nullptr)
|
||||
defaultLookAndFeel = new LookAndFeel();
|
||||
|
||||
currentLookAndFeel = defaultLookAndFeel;
|
||||
}
|
||||
|
||||
return *currentLookAndFeel;
|
||||
}
|
||||
|
||||
void Desktop::setDefaultLookAndFeel (LookAndFeel* newDefaultLookAndFeel)
|
||||
{
|
||||
currentLookAndFeel = newDefaultLookAndFeel;
|
||||
|
||||
for (int i = getNumComponents(); --i >= 0;)
|
||||
{
|
||||
Component* const c = getComponent (i);
|
||||
|
||||
if (c != nullptr)
|
||||
c->sendLookAndFeelChange();
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Desktop::addDesktopComponent (Component* const c)
|
||||
{
|
||||
jassert (c != nullptr);
|
||||
jassert (! desktopComponents.contains (c));
|
||||
desktopComponents.addIfNotAlreadyThere (c);
|
||||
}
|
||||
|
||||
void Desktop::removeDesktopComponent (Component* const c)
|
||||
{
|
||||
desktopComponents.removeValue (c);
|
||||
}
|
||||
|
||||
void Desktop::componentBroughtToFront (Component* const c)
|
||||
{
|
||||
const int index = desktopComponents.indexOf (c);
|
||||
jassert (index >= 0);
|
||||
|
||||
if (index >= 0)
|
||||
{
|
||||
int newIndex = -1;
|
||||
|
||||
if (! c->isAlwaysOnTop())
|
||||
{
|
||||
newIndex = desktopComponents.size();
|
||||
|
||||
while (newIndex > 0 && desktopComponents.getUnchecked (newIndex - 1)->isAlwaysOnTop())
|
||||
--newIndex;
|
||||
|
||||
--newIndex;
|
||||
}
|
||||
|
||||
desktopComponents.move (index, newIndex);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
const Point<int> Desktop::getMousePosition()
|
||||
{
|
||||
return getInstance().getMainMouseSource().getScreenPosition();
|
||||
}
|
||||
|
||||
const Point<int> Desktop::getLastMouseDownPosition()
|
||||
{
|
||||
return getInstance().getMainMouseSource().getLastMouseDownPosition();
|
||||
}
|
||||
|
||||
int Desktop::getMouseButtonClickCounter()
|
||||
{
|
||||
return getInstance().mouseClickCounter;
|
||||
}
|
||||
|
||||
void Desktop::incrementMouseClickCounter() noexcept
|
||||
{
|
||||
++mouseClickCounter;
|
||||
}
|
||||
|
||||
int Desktop::getNumDraggingMouseSources() const noexcept
|
||||
{
|
||||
int num = 0;
|
||||
for (int i = mouseSources.size(); --i >= 0;)
|
||||
if (mouseSources.getUnchecked(i)->isDragging())
|
||||
++num;
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
MouseInputSource* Desktop::getDraggingMouseSource (int index) const noexcept
|
||||
{
|
||||
int num = 0;
|
||||
for (int i = mouseSources.size(); --i >= 0;)
|
||||
{
|
||||
MouseInputSource* const mi = mouseSources.getUnchecked(i);
|
||||
|
||||
if (mi->isDragging())
|
||||
{
|
||||
if (index == num)
|
||||
return mi;
|
||||
|
||||
++num;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class MouseDragAutoRepeater : public Timer
|
||||
{
|
||||
public:
|
||||
MouseDragAutoRepeater() {}
|
||||
|
||||
void timerCallback()
|
||||
{
|
||||
Desktop& desktop = Desktop::getInstance();
|
||||
int numMiceDown = 0;
|
||||
|
||||
for (int i = desktop.getNumMouseSources(); --i >= 0;)
|
||||
{
|
||||
MouseInputSource* const source = desktop.getMouseSource(i);
|
||||
if (source->isDragging())
|
||||
{
|
||||
source->triggerFakeMove();
|
||||
++numMiceDown;
|
||||
}
|
||||
}
|
||||
|
||||
if (numMiceDown == 0)
|
||||
desktop.beginDragAutoRepeat (0);
|
||||
}
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE (MouseDragAutoRepeater);
|
||||
};
|
||||
|
||||
void Desktop::beginDragAutoRepeat (const int interval)
|
||||
{
|
||||
if (interval > 0)
|
||||
{
|
||||
if (dragRepeater == nullptr)
|
||||
dragRepeater = new MouseDragAutoRepeater();
|
||||
|
||||
if (dragRepeater->getTimerInterval() != interval)
|
||||
dragRepeater->startTimer (interval);
|
||||
}
|
||||
else
|
||||
{
|
||||
dragRepeater = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Desktop::addFocusChangeListener (FocusChangeListener* const listener)
|
||||
{
|
||||
focusListeners.add (listener);
|
||||
}
|
||||
|
||||
void Desktop::removeFocusChangeListener (FocusChangeListener* const listener)
|
||||
{
|
||||
focusListeners.remove (listener);
|
||||
}
|
||||
|
||||
void Desktop::triggerFocusCallback()
|
||||
{
|
||||
triggerAsyncUpdate();
|
||||
}
|
||||
|
||||
void Desktop::handleAsyncUpdate()
|
||||
{
|
||||
// The component may be deleted during this operation, but we'll use a SafePointer rather than a
|
||||
// BailOutChecker so that any remaining listeners will still get a callback (with a null pointer).
|
||||
WeakReference<Component> currentFocus (Component::getCurrentlyFocusedComponent());
|
||||
focusListeners.call (&FocusChangeListener::globalFocusChanged, currentFocus);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Desktop::resetTimer()
|
||||
{
|
||||
if (mouseListeners.size() == 0)
|
||||
stopTimer();
|
||||
else
|
||||
startTimer (100);
|
||||
|
||||
lastFakeMouseMove = getMousePosition();
|
||||
}
|
||||
|
||||
ListenerList <MouseListener>& Desktop::getMouseListeners()
|
||||
{
|
||||
resetTimer();
|
||||
return mouseListeners;
|
||||
}
|
||||
|
||||
void Desktop::addGlobalMouseListener (MouseListener* const listener)
|
||||
{
|
||||
mouseListeners.add (listener);
|
||||
resetTimer();
|
||||
}
|
||||
|
||||
void Desktop::removeGlobalMouseListener (MouseListener* const listener)
|
||||
{
|
||||
mouseListeners.remove (listener);
|
||||
resetTimer();
|
||||
}
|
||||
|
||||
void Desktop::timerCallback()
|
||||
{
|
||||
if (lastFakeMouseMove != getMousePosition())
|
||||
sendMouseMove();
|
||||
}
|
||||
|
||||
void Desktop::sendMouseMove()
|
||||
{
|
||||
if (! mouseListeners.isEmpty())
|
||||
{
|
||||
startTimer (20);
|
||||
|
||||
lastFakeMouseMove = getMousePosition();
|
||||
|
||||
Component* const target = findComponentAt (lastFakeMouseMove);
|
||||
|
||||
if (target != nullptr)
|
||||
{
|
||||
Component::BailOutChecker checker (target);
|
||||
const Point<int> pos (target->getLocalPoint (nullptr, lastFakeMouseMove));
|
||||
const Time now (Time::getCurrentTime());
|
||||
|
||||
const MouseEvent me (getMainMouseSource(), pos, ModifierKeys::getCurrentModifiers(),
|
||||
target, target, now, pos, now, 0, false);
|
||||
|
||||
if (me.mods.isAnyMouseButtonDown())
|
||||
mouseListeners.callChecked (checker, &MouseListener::mouseDrag, me);
|
||||
else
|
||||
mouseListeners.callChecked (checker, &MouseListener::mouseMove, me);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Desktop::setKioskModeComponent (Component* componentToUse, const bool allowMenusAndBars)
|
||||
{
|
||||
if (kioskModeComponent != componentToUse)
|
||||
{
|
||||
// agh! Don't delete or remove a component from the desktop while it's still the kiosk component!
|
||||
jassert (kioskModeComponent == nullptr || ComponentPeer::getPeerFor (kioskModeComponent) != nullptr);
|
||||
|
||||
if (kioskModeComponent != nullptr)
|
||||
{
|
||||
setKioskComponent (kioskModeComponent, false, allowMenusAndBars);
|
||||
|
||||
kioskModeComponent->setBounds (kioskComponentOriginalBounds);
|
||||
}
|
||||
|
||||
kioskModeComponent = componentToUse;
|
||||
|
||||
if (kioskModeComponent != nullptr)
|
||||
{
|
||||
// Only components that are already on the desktop can be put into kiosk mode!
|
||||
jassert (ComponentPeer::getPeerFor (kioskModeComponent) != nullptr);
|
||||
|
||||
kioskComponentOriginalBounds = kioskModeComponent->getBounds();
|
||||
|
||||
setKioskComponent (kioskModeComponent, true, allowMenusAndBars);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Desktop::setOrientationsEnabled (const int newOrientations)
|
||||
{
|
||||
// Dodgy set of flags being passed here! Make sure you specify at least one permitted orientation.
|
||||
jassert (newOrientations != 0 && (newOrientations & ~allOrientations) == 0);
|
||||
|
||||
allowedOrientations = newOrientations;
|
||||
}
|
||||
|
||||
bool Desktop::isOrientationEnabled (const DisplayOrientation orientation) const noexcept
|
||||
{
|
||||
// Make sure you only pass one valid flag in here...
|
||||
jassert (orientation == upright || orientation == upsideDown || orientation == rotatedClockwise || orientation == rotatedAntiClockwise);
|
||||
|
||||
return (allowedOrientations & orientation) != 0;
|
||||
}
|
||||
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
412
modules/juce_gui_basics/components/juce_Desktop.h
Normal file
412
modules/juce_gui_basics/components/juce_Desktop.h
Normal file
|
|
@ -0,0 +1,412 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_DESKTOP_JUCEHEADER__
|
||||
#define __JUCE_DESKTOP_JUCEHEADER__
|
||||
|
||||
#include "juce_Component.h"
|
||||
#include "../layout/juce_ComponentAnimator.h"
|
||||
class MouseInputSource;
|
||||
class MouseInputSourceInternal;
|
||||
class MouseListener;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Classes can implement this interface and register themselves with the Desktop class
|
||||
to receive callbacks when the currently focused component changes.
|
||||
|
||||
@see Desktop::addFocusChangeListener, Desktop::removeFocusChangeListener
|
||||
*/
|
||||
class JUCE_API FocusChangeListener
|
||||
{
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~FocusChangeListener() {}
|
||||
|
||||
/** Callback to indicate that the currently focused component has changed. */
|
||||
virtual void globalFocusChanged (Component* focusedComponent) = 0;
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Describes and controls aspects of the computer's desktop.
|
||||
|
||||
*/
|
||||
class JUCE_API Desktop : private DeletedAtShutdown,
|
||||
private Timer,
|
||||
private AsyncUpdater
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** There's only one dektop object, and this method will return it.
|
||||
*/
|
||||
static Desktop& JUCE_CALLTYPE getInstance();
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a list of the positions of all the monitors available.
|
||||
|
||||
The first rectangle in the list will be the main monitor area.
|
||||
|
||||
If clippedToWorkArea is true, it will exclude any areas like the taskbar on Windows,
|
||||
or the menu bar on Mac. If clippedToWorkArea is false, the entire monitor area is returned.
|
||||
*/
|
||||
const RectangleList getAllMonitorDisplayAreas (bool clippedToWorkArea = true) const;
|
||||
|
||||
/** Returns the position and size of the main monitor.
|
||||
|
||||
If clippedToWorkArea is true, it will exclude any areas like the taskbar on Windows,
|
||||
or the menu bar on Mac. If clippedToWorkArea is false, the entire monitor area is returned.
|
||||
*/
|
||||
const Rectangle<int> getMainMonitorArea (bool clippedToWorkArea = true) const noexcept;
|
||||
|
||||
/** Returns the position and size of the monitor which contains this co-ordinate.
|
||||
|
||||
If none of the monitors contains the point, this will just return the
|
||||
main monitor.
|
||||
|
||||
If clippedToWorkArea is true, it will exclude any areas like the taskbar on Windows,
|
||||
or the menu bar on Mac. If clippedToWorkArea is false, the entire monitor area is returned.
|
||||
*/
|
||||
const Rectangle<int> getMonitorAreaContaining (const Point<int>& position, bool clippedToWorkArea = true) const;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the mouse position.
|
||||
|
||||
The co-ordinates are relative to the top-left of the main monitor.
|
||||
|
||||
Note that this is just a shortcut for calling getMainMouseSource().getScreenPosition(), and
|
||||
you should only resort to grabbing the global mouse position if there's really no
|
||||
way to get the coordinates via a mouse event callback instead.
|
||||
*/
|
||||
static const Point<int> getMousePosition();
|
||||
|
||||
/** Makes the mouse pointer jump to a given location.
|
||||
|
||||
The co-ordinates are relative to the top-left of the main monitor.
|
||||
*/
|
||||
static void setMousePosition (const Point<int>& newPosition);
|
||||
|
||||
/** Returns the last position at which a mouse button was pressed.
|
||||
|
||||
Note that this is just a shortcut for calling getMainMouseSource().getLastMouseDownPosition(),
|
||||
and in a multi-touch environment, it doesn't make much sense. ALWAYS prefer to
|
||||
get this information via other means, such as MouseEvent::getMouseDownScreenPosition()
|
||||
if possible, and only ever call this as a last resort.
|
||||
*/
|
||||
static const Point<int> getLastMouseDownPosition();
|
||||
|
||||
/** Returns the number of times the mouse button has been clicked since the
|
||||
app started.
|
||||
|
||||
Each mouse-down event increments this number by 1.
|
||||
*/
|
||||
static int getMouseButtonClickCounter();
|
||||
|
||||
//==============================================================================
|
||||
/** This lets you prevent the screensaver from becoming active.
|
||||
|
||||
Handy if you're running some sort of presentation app where having a screensaver
|
||||
appear would be annoying.
|
||||
|
||||
Pass false to disable the screensaver, and true to re-enable it. (Note that this
|
||||
won't enable a screensaver unless the user has actually set one up).
|
||||
|
||||
The disablement will only happen while the Juce application is the foreground
|
||||
process - if another task is running in front of it, then the screensaver will
|
||||
be unaffected.
|
||||
|
||||
@see isScreenSaverEnabled
|
||||
*/
|
||||
static void setScreenSaverEnabled (bool isEnabled);
|
||||
|
||||
/** Returns true if the screensaver has not been turned off.
|
||||
|
||||
This will return the last value passed into setScreenSaverEnabled(). Note that
|
||||
it won't tell you whether the user is actually using a screen saver, just
|
||||
whether this app is deliberately preventing one from running.
|
||||
|
||||
@see setScreenSaverEnabled
|
||||
*/
|
||||
static bool isScreenSaverEnabled();
|
||||
|
||||
//==============================================================================
|
||||
/** Registers a MouseListener that will receive all mouse events that occur on
|
||||
any component.
|
||||
|
||||
@see removeGlobalMouseListener
|
||||
*/
|
||||
void addGlobalMouseListener (MouseListener* listener);
|
||||
|
||||
/** Unregisters a MouseListener that was added with the addGlobalMouseListener()
|
||||
method.
|
||||
|
||||
@see addGlobalMouseListener
|
||||
*/
|
||||
void removeGlobalMouseListener (MouseListener* listener);
|
||||
|
||||
//==============================================================================
|
||||
/** Registers a MouseListener that will receive a callback whenever the focused
|
||||
component changes.
|
||||
*/
|
||||
void addFocusChangeListener (FocusChangeListener* listener);
|
||||
|
||||
/** Unregisters a listener that was added with addFocusChangeListener(). */
|
||||
void removeFocusChangeListener (FocusChangeListener* listener);
|
||||
|
||||
//==============================================================================
|
||||
/** Takes a component and makes it full-screen, removing the taskbar, dock, etc.
|
||||
|
||||
The component must already be on the desktop for this method to work. It will
|
||||
be resized to completely fill the screen and any extraneous taskbars, menu bars,
|
||||
etc will be hidden.
|
||||
|
||||
To exit kiosk mode, just call setKioskModeComponent (nullptr). When this is called,
|
||||
the component that's currently being used will be resized back to the size
|
||||
and position it was in before being put into this mode.
|
||||
|
||||
If allowMenusAndBars is true, things like the menu and dock (on mac) are still
|
||||
allowed to pop up when the mouse moves onto them. If this is false, it'll try
|
||||
to hide as much on-screen paraphenalia as possible.
|
||||
*/
|
||||
void setKioskModeComponent (Component* componentToUse,
|
||||
bool allowMenusAndBars = true);
|
||||
|
||||
/** Returns the component that is currently being used in kiosk-mode.
|
||||
|
||||
This is the component that was last set by setKioskModeComponent(). If none
|
||||
has been set, this returns 0.
|
||||
*/
|
||||
Component* getKioskModeComponent() const noexcept { return kioskModeComponent; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the number of components that are currently active as top-level
|
||||
desktop windows.
|
||||
|
||||
@see getComponent, Component::addToDesktop
|
||||
*/
|
||||
int getNumComponents() const noexcept;
|
||||
|
||||
/** Returns one of the top-level desktop window components.
|
||||
|
||||
The index is from 0 to getNumComponents() - 1. This could return 0 if the
|
||||
index is out-of-range.
|
||||
|
||||
@see getNumComponents, Component::addToDesktop
|
||||
*/
|
||||
Component* getComponent (int index) const noexcept;
|
||||
|
||||
/** Finds the component at a given screen location.
|
||||
|
||||
This will drill down into top-level windows to find the child component at
|
||||
the given position.
|
||||
|
||||
Returns 0 if the co-ordinates are inside a non-Juce window.
|
||||
*/
|
||||
Component* findComponentAt (const Point<int>& screenPosition) const;
|
||||
|
||||
/** The Desktop object has a ComponentAnimator instance which can be used for performing
|
||||
your animations.
|
||||
|
||||
Having a single shared ComponentAnimator object makes it more efficient when multiple
|
||||
components are being moved around simultaneously. It's also more convenient than having
|
||||
to manage your own instance of one.
|
||||
|
||||
@see ComponentAnimator
|
||||
*/
|
||||
ComponentAnimator& getAnimator() noexcept { return animator; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the current default look-and-feel for components which don't have one
|
||||
explicitly set.
|
||||
@see setDefaultLookAndFeel
|
||||
*/
|
||||
LookAndFeel& getDefaultLookAndFeel() noexcept;
|
||||
|
||||
/** Changes the default look-and-feel.
|
||||
@param newDefaultLookAndFeel the new look-and-feel object to use - if this is
|
||||
set to nullptr, it will revert to using the system's
|
||||
default one. The object passed-in must be deleted by the
|
||||
caller when it's no longer needed.
|
||||
@see getDefaultLookAndFeel
|
||||
*/
|
||||
void setDefaultLookAndFeel (LookAndFeel* newDefaultLookAndFeel);
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the number of MouseInputSource objects the system has at its disposal.
|
||||
In a traditional single-mouse system, there might be only one object. On a multi-touch
|
||||
system, there could be one input source per potential finger.
|
||||
To find out how many mouse events are currently happening, use getNumDraggingMouseSources().
|
||||
@see getMouseSource
|
||||
*/
|
||||
int getNumMouseSources() const noexcept { return mouseSources.size(); }
|
||||
|
||||
/** Returns one of the system's MouseInputSource objects.
|
||||
The index should be from 0 to getNumMouseSources() - 1. Out-of-range indexes will return
|
||||
a null pointer.
|
||||
In a traditional single-mouse system, there might be only one object. On a multi-touch
|
||||
system, there could be one input source per potential finger.
|
||||
*/
|
||||
MouseInputSource* getMouseSource (int index) const noexcept { return mouseSources [index]; }
|
||||
|
||||
/** Returns the main mouse input device that the system is using.
|
||||
@see getNumMouseSources()
|
||||
*/
|
||||
MouseInputSource& getMainMouseSource() const noexcept { return *mouseSources.getUnchecked(0); }
|
||||
|
||||
/** Returns the number of mouse-sources that are currently being dragged.
|
||||
In a traditional single-mouse system, this will be 0 or 1, depending on whether a
|
||||
juce component has the button down on it. In a multi-touch system, this could
|
||||
be any number from 0 to the number of simultaneous touches that can be detected.
|
||||
*/
|
||||
int getNumDraggingMouseSources() const noexcept;
|
||||
|
||||
/** Returns one of the mouse sources that's currently being dragged.
|
||||
The index should be between 0 and getNumDraggingMouseSources() - 1. If the index is
|
||||
out of range, or if no mice or fingers are down, this will return a null pointer.
|
||||
*/
|
||||
MouseInputSource* getDraggingMouseSource (int index) const noexcept;
|
||||
|
||||
/** Ensures that a non-stop stream of mouse-drag events will be sent during the
|
||||
current mouse-drag operation.
|
||||
|
||||
This allows you to make sure that mouseDrag() events are sent continuously, even
|
||||
when the mouse isn't moving. This can be useful for things like auto-scrolling
|
||||
components when the mouse is near an edge.
|
||||
|
||||
Call this method during a mouseDown() or mouseDrag() callback, specifying the
|
||||
minimum interval between consecutive mouse drag callbacks. The callbacks
|
||||
will continue until the mouse is released, and then the interval will be reset,
|
||||
so you need to make sure it's called every time you begin a drag event.
|
||||
Passing an interval of 0 or less will cancel the auto-repeat.
|
||||
|
||||
@see mouseDrag
|
||||
*/
|
||||
void beginDragAutoRepeat (int millisecondsBetweenCallbacks);
|
||||
|
||||
//==============================================================================
|
||||
/** In a tablet device which can be turned around, this is used to inidicate the orientation. */
|
||||
enum DisplayOrientation
|
||||
{
|
||||
upright = 1, /**< Indicates that the display is the normal way up. */
|
||||
upsideDown = 2, /**< Indicates that the display is upside-down. */
|
||||
rotatedClockwise = 4, /**< Indicates that the display is turned 90 degrees clockwise from its upright position. */
|
||||
rotatedAntiClockwise = 8, /**< Indicates that the display is turned 90 degrees anti-clockwise from its upright position. */
|
||||
|
||||
allOrientations = 1 + 2 + 4 + 8 /**< A combination of all the orientation values */
|
||||
};
|
||||
|
||||
/** In a tablet device which can be turned around, this returns the current orientation. */
|
||||
DisplayOrientation getCurrentOrientation() const;
|
||||
|
||||
/** Sets which orientations the display is allowed to auto-rotate to.
|
||||
|
||||
For devices that support rotating desktops, this lets you specify which of the orientations your app can use.
|
||||
|
||||
The parameter is a bitwise or-ed combination of the values in DisplayOrientation, and must contain at least one
|
||||
set bit.
|
||||
*/
|
||||
void setOrientationsEnabled (int allowedOrientations);
|
||||
|
||||
/** Returns whether the display is allowed to auto-rotate to the given orientation.
|
||||
Each orientation can be enabled using setOrientationEnabled(). By default, all orientations are allowed.
|
||||
*/
|
||||
bool isOrientationEnabled (DisplayOrientation orientation) const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Tells this object to refresh its idea of what the screen resolution is.
|
||||
|
||||
(Called internally by the native code).
|
||||
*/
|
||||
void refreshMonitorSizes();
|
||||
|
||||
/** True if the OS supports semitransparent windows */
|
||||
static bool canUseSemiTransparentWindows() noexcept;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
static Desktop* instance;
|
||||
|
||||
friend class Component;
|
||||
friend class ComponentPeer;
|
||||
friend class MouseInputSource;
|
||||
friend class MouseInputSourceInternal;
|
||||
friend class DeletedAtShutdown;
|
||||
friend class TopLevelWindowManager;
|
||||
|
||||
OwnedArray <MouseInputSource> mouseSources;
|
||||
void createMouseInputSources();
|
||||
|
||||
ListenerList <MouseListener> mouseListeners;
|
||||
ListenerList <FocusChangeListener> focusListeners;
|
||||
|
||||
Array <Component*> desktopComponents;
|
||||
Array <Rectangle<int> > monitorCoordsClipped, monitorCoordsUnclipped;
|
||||
|
||||
Point<int> lastFakeMouseMove;
|
||||
void sendMouseMove();
|
||||
|
||||
int mouseClickCounter;
|
||||
void incrementMouseClickCounter() noexcept;
|
||||
|
||||
ScopedPointer<Timer> dragRepeater;
|
||||
|
||||
ScopedPointer<LookAndFeel> defaultLookAndFeel;
|
||||
WeakReference<LookAndFeel> currentLookAndFeel;
|
||||
|
||||
Component* kioskModeComponent;
|
||||
Rectangle<int> kioskComponentOriginalBounds;
|
||||
|
||||
int allowedOrientations;
|
||||
|
||||
ComponentAnimator animator;
|
||||
|
||||
void timerCallback();
|
||||
void resetTimer();
|
||||
ListenerList <MouseListener>& getMouseListeners();
|
||||
|
||||
int getNumDisplayMonitors() const noexcept;
|
||||
const Rectangle<int> getDisplayMonitorCoordinates (int index, bool clippedToWorkArea) const noexcept;
|
||||
static void getCurrentMonitorPositions (Array <Rectangle<int> >& monitorCoords, const bool clipToWorkArea);
|
||||
|
||||
void addDesktopComponent (Component* c);
|
||||
void removeDesktopComponent (Component* c);
|
||||
void componentBroughtToFront (Component* c);
|
||||
|
||||
static void setKioskComponent (Component* kioskModeComponent, bool enableOrDisable, bool allowMenusAndBars);
|
||||
|
||||
void triggerFocusCallback();
|
||||
void handleAsyncUpdate();
|
||||
|
||||
Desktop();
|
||||
~Desktop();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Desktop);
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCE_DESKTOP_JUCEHEADER__
|
||||
|
|
@ -0,0 +1,283 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
//==============================================================================
|
||||
class ModalComponentManager::ModalItem : public ComponentMovementWatcher
|
||||
{
|
||||
public:
|
||||
ModalItem (Component* const comp)
|
||||
: ComponentMovementWatcher (comp),
|
||||
component (comp), returnValue (0), isActive (true)
|
||||
{
|
||||
jassert (comp != nullptr);
|
||||
}
|
||||
|
||||
void componentMovedOrResized (bool, bool) {}
|
||||
|
||||
void componentPeerChanged()
|
||||
{
|
||||
if (! component->isShowing())
|
||||
cancel();
|
||||
}
|
||||
|
||||
void componentVisibilityChanged()
|
||||
{
|
||||
if (! component->isShowing())
|
||||
cancel();
|
||||
}
|
||||
|
||||
void componentBeingDeleted (Component& comp)
|
||||
{
|
||||
ComponentMovementWatcher::componentBeingDeleted (comp);
|
||||
|
||||
if (component == &comp || comp.isParentOf (component))
|
||||
cancel();
|
||||
}
|
||||
|
||||
void cancel()
|
||||
{
|
||||
if (isActive)
|
||||
{
|
||||
isActive = false;
|
||||
ModalComponentManager::getInstance()->triggerAsyncUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
Component* component;
|
||||
OwnedArray<Callback> callbacks;
|
||||
int returnValue;
|
||||
bool isActive;
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE (ModalItem);
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
ModalComponentManager::ModalComponentManager()
|
||||
{
|
||||
}
|
||||
|
||||
ModalComponentManager::~ModalComponentManager()
|
||||
{
|
||||
clearSingletonInstance();
|
||||
}
|
||||
|
||||
juce_ImplementSingleton_SingleThreaded (ModalComponentManager);
|
||||
|
||||
|
||||
//==============================================================================
|
||||
void ModalComponentManager::startModal (Component* component)
|
||||
{
|
||||
if (component != nullptr)
|
||||
stack.add (new ModalItem (component));
|
||||
}
|
||||
|
||||
void ModalComponentManager::attachCallback (Component* component, Callback* callback)
|
||||
{
|
||||
if (callback != nullptr)
|
||||
{
|
||||
ScopedPointer<Callback> callbackDeleter (callback);
|
||||
|
||||
for (int i = stack.size(); --i >= 0;)
|
||||
{
|
||||
ModalItem* const item = stack.getUnchecked(i);
|
||||
|
||||
if (item->component == component)
|
||||
{
|
||||
item->callbacks.add (callback);
|
||||
callbackDeleter.release();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ModalComponentManager::endModal (Component* component)
|
||||
{
|
||||
for (int i = stack.size(); --i >= 0;)
|
||||
{
|
||||
ModalItem* const item = stack.getUnchecked(i);
|
||||
|
||||
if (item->component == component)
|
||||
item->cancel();
|
||||
}
|
||||
}
|
||||
|
||||
void ModalComponentManager::endModal (Component* component, int returnValue)
|
||||
{
|
||||
for (int i = stack.size(); --i >= 0;)
|
||||
{
|
||||
ModalItem* const item = stack.getUnchecked(i);
|
||||
|
||||
if (item->component == component)
|
||||
{
|
||||
item->returnValue = returnValue;
|
||||
item->cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int ModalComponentManager::getNumModalComponents() const
|
||||
{
|
||||
int n = 0;
|
||||
for (int i = 0; i < stack.size(); ++i)
|
||||
if (stack.getUnchecked(i)->isActive)
|
||||
++n;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
Component* ModalComponentManager::getModalComponent (const int index) const
|
||||
{
|
||||
int n = 0;
|
||||
for (int i = stack.size(); --i >= 0;)
|
||||
{
|
||||
const ModalItem* const item = stack.getUnchecked(i);
|
||||
if (item->isActive)
|
||||
if (n++ == index)
|
||||
return item->component;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool ModalComponentManager::isModal (Component* const comp) const
|
||||
{
|
||||
for (int i = stack.size(); --i >= 0;)
|
||||
{
|
||||
const ModalItem* const item = stack.getUnchecked(i);
|
||||
if (item->isActive && item->component == comp)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ModalComponentManager::isFrontModalComponent (Component* const comp) const
|
||||
{
|
||||
return comp == getModalComponent (0);
|
||||
}
|
||||
|
||||
void ModalComponentManager::handleAsyncUpdate()
|
||||
{
|
||||
for (int i = stack.size(); --i >= 0;)
|
||||
{
|
||||
const ModalItem* const item = stack.getUnchecked(i);
|
||||
|
||||
if (! item->isActive)
|
||||
{
|
||||
ScopedPointer<ModalItem> deleter (stack.removeAndReturn (i));
|
||||
|
||||
for (int j = item->callbacks.size(); --j >= 0;)
|
||||
item->callbacks.getUnchecked(j)->modalStateFinished (item->returnValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ModalComponentManager::bringModalComponentsToFront (bool topOneShouldGrabFocus)
|
||||
{
|
||||
ComponentPeer* lastOne = nullptr;
|
||||
|
||||
for (int i = 0; i < getNumModalComponents(); ++i)
|
||||
{
|
||||
Component* const c = getModalComponent (i);
|
||||
|
||||
if (c == nullptr)
|
||||
break;
|
||||
|
||||
ComponentPeer* peer = c->getPeer();
|
||||
|
||||
if (peer != nullptr && peer != lastOne)
|
||||
{
|
||||
if (lastOne == nullptr)
|
||||
{
|
||||
peer->toFront (topOneShouldGrabFocus);
|
||||
|
||||
if (topOneShouldGrabFocus)
|
||||
peer->grabFocus();
|
||||
}
|
||||
else
|
||||
peer->toBehind (lastOne);
|
||||
|
||||
lastOne = peer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
class ModalComponentManager::ReturnValueRetriever : public ModalComponentManager::Callback
|
||||
{
|
||||
public:
|
||||
ReturnValueRetriever (int& value_, bool& finished_) : value (value_), finished (finished_) {}
|
||||
|
||||
void modalStateFinished (int returnValue)
|
||||
{
|
||||
finished = true;
|
||||
value = returnValue;
|
||||
}
|
||||
|
||||
private:
|
||||
int& value;
|
||||
bool& finished;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (ReturnValueRetriever);
|
||||
};
|
||||
|
||||
int ModalComponentManager::runEventLoopForCurrentComponent()
|
||||
{
|
||||
// This can only be run from the message thread!
|
||||
jassert (MessageManager::getInstance()->isThisTheMessageThread());
|
||||
|
||||
Component* currentlyModal = getModalComponent (0);
|
||||
|
||||
if (currentlyModal == nullptr)
|
||||
return 0;
|
||||
|
||||
WeakReference<Component> prevFocused (Component::getCurrentlyFocusedComponent());
|
||||
|
||||
int returnValue = 0;
|
||||
bool finished = false;
|
||||
attachCallback (currentlyModal, new ReturnValueRetriever (returnValue, finished));
|
||||
|
||||
JUCE_TRY
|
||||
{
|
||||
while (! finished)
|
||||
{
|
||||
if (! MessageManager::getInstance()->runDispatchLoopUntil (20))
|
||||
break;
|
||||
}
|
||||
}
|
||||
JUCE_CATCH_EXCEPTION
|
||||
|
||||
if (prevFocused != nullptr)
|
||||
prevFocused->grabKeyboardFocus();
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
#endif
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
365
modules/juce_gui_basics/components/juce_ModalComponentManager.h
Normal file
365
modules/juce_gui_basics/components/juce_ModalComponentManager.h
Normal file
|
|
@ -0,0 +1,365 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_MODALCOMPONENTMANAGER_JUCEHEADER__
|
||||
#define __JUCE_MODALCOMPONENTMANAGER_JUCEHEADER__
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Manages the system's stack of modal components.
|
||||
|
||||
Normally you'll just use the Component methods to invoke modal states in components,
|
||||
and won't have to deal with this class directly, but this is the singleton object that's
|
||||
used internally to manage the stack.
|
||||
|
||||
@see Component::enterModalState, Component::exitModalState, Component::isCurrentlyModal,
|
||||
Component::getCurrentlyModalComponent, Component::isCurrentlyBlockedByAnotherModalComponent
|
||||
*/
|
||||
class JUCE_API ModalComponentManager : public AsyncUpdater,
|
||||
public DeletedAtShutdown
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Receives callbacks when a modal component is dismissed.
|
||||
|
||||
You can register a callback using Component::enterModalState() or
|
||||
ModalComponentManager::attachCallback().
|
||||
|
||||
For some quick ways of creating callback objects, see the ModalCallbackFunction class.
|
||||
@see ModalCallbackFunction
|
||||
*/
|
||||
class Callback
|
||||
{
|
||||
public:
|
||||
/** */
|
||||
Callback() {}
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~Callback() {}
|
||||
|
||||
/** Called to indicate that a modal component has been dismissed.
|
||||
|
||||
You can register a callback using Component::enterModalState() or
|
||||
ModalComponentManager::attachCallback().
|
||||
|
||||
The returnValue parameter is the value that was passed to Component::exitModalState()
|
||||
when the component was dismissed.
|
||||
|
||||
The callback object will be deleted shortly after this method is called.
|
||||
*/
|
||||
virtual void modalStateFinished (int returnValue) = 0;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the number of components currently being shown modally.
|
||||
@see getModalComponent
|
||||
*/
|
||||
int getNumModalComponents() const;
|
||||
|
||||
/** Returns one of the components being shown modally.
|
||||
An index of 0 is the most recently-shown, topmost component.
|
||||
*/
|
||||
Component* getModalComponent (int index) const;
|
||||
|
||||
/** Returns true if the specified component is in a modal state. */
|
||||
bool isModal (Component* component) const;
|
||||
|
||||
/** Returns true if the specified component is currently the topmost modal component. */
|
||||
bool isFrontModalComponent (Component* component) const;
|
||||
|
||||
/** Adds a new callback that will be called when the specified modal component is dismissed.
|
||||
|
||||
If the component is modal, then when it is dismissed, either by being hidden, or by calling
|
||||
Component::exitModalState(), then the Callback::modalStateFinished() method will be
|
||||
called.
|
||||
|
||||
Each component can have any number of callbacks associated with it, and this one is added
|
||||
to that list.
|
||||
|
||||
The object that is passed in will be deleted by the manager when it's no longer needed. If
|
||||
the given component is not currently modal, the callback object is deleted immediately and
|
||||
no action is taken.
|
||||
*/
|
||||
void attachCallback (Component* component, Callback* callback);
|
||||
|
||||
/** Brings any modal components to the front. */
|
||||
void bringModalComponentsToFront (bool topOneShouldGrabFocus = true);
|
||||
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
/** Runs the event loop until the currently topmost modal component is dismissed, and
|
||||
returns the exit code for that component.
|
||||
*/
|
||||
int runEventLoopForCurrentComponent();
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
juce_DeclareSingleton_SingleThreaded_Minimal (ModalComponentManager);
|
||||
|
||||
protected:
|
||||
/** Creates a ModalComponentManager.
|
||||
You shouldn't ever call the constructor - it's a singleton, so use ModalComponentManager::getInstance()
|
||||
*/
|
||||
ModalComponentManager();
|
||||
|
||||
/** Destructor. */
|
||||
~ModalComponentManager();
|
||||
|
||||
/** @internal */
|
||||
void handleAsyncUpdate();
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
class ModalItem;
|
||||
class ReturnValueRetriever;
|
||||
|
||||
friend class Component;
|
||||
friend class OwnedArray <ModalItem>;
|
||||
OwnedArray <ModalItem> stack;
|
||||
|
||||
void startModal (Component* component);
|
||||
void endModal (Component* component, int returnValue);
|
||||
void endModal (Component* component);
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (ModalComponentManager);
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
This class provides some handy utility methods for creating ModalComponentManager::Callback
|
||||
objects that will invoke a static function with some parameters when a modal component is dismissed.
|
||||
*/
|
||||
class ModalCallbackFunction
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** This is a utility function to create a ModalComponentManager::Callback that will
|
||||
call a static function with a parameter.
|
||||
|
||||
The function that you supply must take two parameters - the first being an int, which is
|
||||
the result code that was used when the modal component was dismissed, and the second
|
||||
can be a custom type. Note that this custom value will be copied and stored, so it must
|
||||
be a primitive type or a class that provides copy-by-value semantics.
|
||||
|
||||
E.g. @code
|
||||
static void myCallbackFunction (int modalResult, double customValue)
|
||||
{
|
||||
if (modalResult == 1)
|
||||
doSomethingWith (customValue);
|
||||
}
|
||||
|
||||
Component* someKindOfComp;
|
||||
...
|
||||
someKindOfComp->enterModalState (ModalCallbackFunction::create (myCallbackFunction, 3.0));
|
||||
@endcode
|
||||
@see ModalComponentManager::Callback
|
||||
*/
|
||||
template <typename ParamType>
|
||||
static ModalComponentManager::Callback* create (void (*functionToCall) (int, ParamType),
|
||||
ParamType parameterValue)
|
||||
{
|
||||
return new FunctionCaller1 <ParamType> (functionToCall, parameterValue);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** This is a utility function to create a ModalComponentManager::Callback that will
|
||||
call a static function with two custom parameters.
|
||||
|
||||
The function that you supply must take three parameters - the first being an int, which is
|
||||
the result code that was used when the modal component was dismissed, and the next two are
|
||||
your custom types. Note that these custom values will be copied and stored, so they must
|
||||
be primitive types or classes that provide copy-by-value semantics.
|
||||
|
||||
E.g. @code
|
||||
static void myCallbackFunction (int modalResult, double customValue1, String customValue2)
|
||||
{
|
||||
if (modalResult == 1)
|
||||
doSomethingWith (customValue1, customValue2);
|
||||
}
|
||||
|
||||
Component* someKindOfComp;
|
||||
...
|
||||
someKindOfComp->enterModalState (ModalCallbackFunction::create (myCallbackFunction, 3.0, String ("xyz")));
|
||||
@endcode
|
||||
@see ModalComponentManager::Callback
|
||||
*/
|
||||
template <typename ParamType1, typename ParamType2>
|
||||
static ModalComponentManager::Callback* withParam (void (*functionToCall) (int, ParamType1, ParamType2),
|
||||
ParamType1 parameterValue1,
|
||||
ParamType2 parameterValue2)
|
||||
{
|
||||
return new FunctionCaller2 <ParamType1, ParamType2> (functionToCall, parameterValue1, parameterValue2);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** This is a utility function to create a ModalComponentManager::Callback that will
|
||||
call a static function with a component.
|
||||
|
||||
The function that you supply must take two parameters - the first being an int, which is
|
||||
the result code that was used when the modal component was dismissed, and the second
|
||||
can be a Component class. The component will be stored as a WeakReference, so that if it gets
|
||||
deleted before this callback is invoked, the pointer that is passed to the function will be null.
|
||||
|
||||
E.g. @code
|
||||
static void myCallbackFunction (int modalResult, Slider* mySlider)
|
||||
{
|
||||
if (modalResult == 1 && mySlider != nullptr) // (must check that mySlider isn't null in case it was deleted..)
|
||||
mySlider->setValue (0.0);
|
||||
}
|
||||
|
||||
Component* someKindOfComp;
|
||||
Slider* mySlider;
|
||||
...
|
||||
someKindOfComp->enterModalState (ModalCallbackFunction::forComponent (myCallbackFunction, mySlider));
|
||||
@endcode
|
||||
@see ModalComponentManager::Callback
|
||||
*/
|
||||
template <class ComponentType>
|
||||
static ModalComponentManager::Callback* forComponent (void (*functionToCall) (int, ComponentType*),
|
||||
ComponentType* component)
|
||||
{
|
||||
return new ComponentCaller1 <ComponentType> (functionToCall, component);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Creates a ModalComponentManager::Callback that will call a static function with a component.
|
||||
|
||||
The function that you supply must take three parameters - the first being an int, which is
|
||||
the result code that was used when the modal component was dismissed, the second being a Component
|
||||
class, and the third being a custom type (which must be a primitive type or have copy-by-value semantics).
|
||||
The component will be stored as a WeakReference, so that if it gets deleted before this callback is
|
||||
invoked, the pointer that is passed into the function will be null.
|
||||
|
||||
E.g. @code
|
||||
static void myCallbackFunction (int modalResult, Slider* mySlider, String customParam)
|
||||
{
|
||||
if (modalResult == 1 && mySlider != nullptr) // (must check that mySlider isn't null in case it was deleted..)
|
||||
mySlider->setName (customParam);
|
||||
}
|
||||
|
||||
Component* someKindOfComp;
|
||||
Slider* mySlider;
|
||||
...
|
||||
someKindOfComp->enterModalState (ModalCallbackFunction::forComponent (myCallbackFunction, mySlider, String ("hello")));
|
||||
@endcode
|
||||
@see ModalComponentManager::Callback
|
||||
*/
|
||||
template <class ComponentType, typename ParamType>
|
||||
static ModalComponentManager::Callback* forComponent (void (*functionToCall) (int, ComponentType*, ParamType),
|
||||
ComponentType* component,
|
||||
ParamType param)
|
||||
{
|
||||
return new ComponentCaller2 <ComponentType, ParamType> (functionToCall, component, param);
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
template <typename ParamType>
|
||||
class FunctionCaller1 : public ModalComponentManager::Callback
|
||||
{
|
||||
public:
|
||||
typedef void (*FunctionType) (int, ParamType);
|
||||
|
||||
FunctionCaller1 (FunctionType& function_, ParamType& param_)
|
||||
: function (function_), param (param_) {}
|
||||
|
||||
void modalStateFinished (int returnValue) { function (returnValue, param); }
|
||||
|
||||
private:
|
||||
const FunctionType function;
|
||||
ParamType param;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FunctionCaller1);
|
||||
};
|
||||
|
||||
template <typename ParamType1, typename ParamType2>
|
||||
class FunctionCaller2 : public ModalComponentManager::Callback
|
||||
{
|
||||
public:
|
||||
typedef void (*FunctionType) (int, ParamType1, ParamType2);
|
||||
|
||||
FunctionCaller2 (FunctionType& function_, ParamType1& param1_, ParamType2& param2_)
|
||||
: function (function_), param1 (param1_), param2 (param2_) {}
|
||||
|
||||
void modalStateFinished (int returnValue) { function (returnValue, param1, param2); }
|
||||
|
||||
private:
|
||||
const FunctionType function;
|
||||
ParamType1 param1;
|
||||
ParamType2 param2;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FunctionCaller2);
|
||||
};
|
||||
|
||||
template <typename ComponentType>
|
||||
class ComponentCaller1 : public ModalComponentManager::Callback
|
||||
{
|
||||
public:
|
||||
typedef void (*FunctionType) (int, ComponentType*);
|
||||
|
||||
ComponentCaller1 (FunctionType& function_, ComponentType* comp_)
|
||||
: function (function_), comp (comp_) {}
|
||||
|
||||
void modalStateFinished (int returnValue)
|
||||
{
|
||||
function (returnValue, static_cast <ComponentType*> (comp.get()));
|
||||
}
|
||||
|
||||
private:
|
||||
const FunctionType function;
|
||||
WeakReference<Component> comp;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComponentCaller1);
|
||||
};
|
||||
|
||||
template <typename ComponentType, typename ParamType1>
|
||||
class ComponentCaller2 : public ModalComponentManager::Callback
|
||||
{
|
||||
public:
|
||||
typedef void (*FunctionType) (int, ComponentType*, ParamType1);
|
||||
|
||||
ComponentCaller2 (FunctionType& function_, ComponentType* comp_, ParamType1 param1_)
|
||||
: function (function_), comp (comp_), param1 (param1_) {}
|
||||
|
||||
void modalStateFinished (int returnValue)
|
||||
{
|
||||
function (returnValue, static_cast <ComponentType*> (comp.get()), param1);
|
||||
}
|
||||
|
||||
private:
|
||||
const FunctionType function;
|
||||
WeakReference<Component> comp;
|
||||
ParamType1 param1;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComponentCaller2);
|
||||
};
|
||||
|
||||
ModalCallbackFunction();
|
||||
~ModalCallbackFunction();
|
||||
JUCE_DECLARE_NON_COPYABLE (ModalCallbackFunction);
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCE_MODALCOMPONENTMANAGER_JUCEHEADER__
|
||||
233
modules/juce_gui_basics/drawables/juce_Drawable.cpp
Normal file
233
modules/juce_gui_basics/drawables/juce_Drawable.cpp
Normal file
|
|
@ -0,0 +1,233 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
//==============================================================================
|
||||
Drawable::Drawable()
|
||||
{
|
||||
setInterceptsMouseClicks (false, false);
|
||||
setPaintingIsUnclipped (true);
|
||||
}
|
||||
|
||||
Drawable::~Drawable()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Drawable::draw (Graphics& g, float opacity, const AffineTransform& transform) const
|
||||
{
|
||||
const_cast <Drawable*> (this)->nonConstDraw (g, opacity, transform);
|
||||
}
|
||||
|
||||
void Drawable::nonConstDraw (Graphics& g, float opacity, const AffineTransform& transform)
|
||||
{
|
||||
Graphics::ScopedSaveState ss (g);
|
||||
|
||||
const float oldOpacity = getAlpha();
|
||||
setAlpha (opacity);
|
||||
g.addTransform (AffineTransform::translation ((float) -originRelativeToComponent.getX(),
|
||||
(float) -originRelativeToComponent.getY())
|
||||
.followedBy (getTransform())
|
||||
.followedBy (transform));
|
||||
|
||||
if (! g.isClipEmpty())
|
||||
paintEntireComponent (g, false);
|
||||
|
||||
setAlpha (oldOpacity);
|
||||
}
|
||||
|
||||
void Drawable::drawAt (Graphics& g, float x, float y, float opacity) const
|
||||
{
|
||||
draw (g, opacity, AffineTransform::translation (x, y));
|
||||
}
|
||||
|
||||
void Drawable::drawWithin (Graphics& g, const Rectangle<float>& destArea, const RectanglePlacement& placement, float opacity) const
|
||||
{
|
||||
draw (g, opacity, placement.getTransformToFit (getDrawableBounds(), destArea));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
DrawableComposite* Drawable::getParent() const
|
||||
{
|
||||
return dynamic_cast <DrawableComposite*> (getParentComponent());
|
||||
}
|
||||
|
||||
void Drawable::transformContextToCorrectOrigin (Graphics& g)
|
||||
{
|
||||
g.setOrigin (originRelativeToComponent.getX(),
|
||||
originRelativeToComponent.getY());
|
||||
}
|
||||
|
||||
void Drawable::parentHierarchyChanged()
|
||||
{
|
||||
setBoundsToEnclose (getDrawableBounds());
|
||||
}
|
||||
|
||||
void Drawable::setBoundsToEnclose (const Rectangle<float>& area)
|
||||
{
|
||||
Drawable* const parent = getParent();
|
||||
Point<int> parentOrigin;
|
||||
if (parent != nullptr)
|
||||
parentOrigin = parent->originRelativeToComponent;
|
||||
|
||||
const Rectangle<int> newBounds (area.getSmallestIntegerContainer() + parentOrigin);
|
||||
originRelativeToComponent = parentOrigin - newBounds.getPosition();
|
||||
setBounds (newBounds);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Drawable::setOriginWithOriginalSize (const Point<float>& originWithinParent)
|
||||
{
|
||||
setTransform (AffineTransform::translation (originWithinParent.getX(), originWithinParent.getY()));
|
||||
}
|
||||
|
||||
void Drawable::setTransformToFit (const Rectangle<float>& area, const RectanglePlacement& placement)
|
||||
{
|
||||
if (! area.isEmpty())
|
||||
setTransform (placement.getTransformToFit (getDrawableBounds(), area));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
Drawable* Drawable::createFromImageData (const void* data, const size_t numBytes)
|
||||
{
|
||||
Drawable* result = nullptr;
|
||||
|
||||
Image image (ImageFileFormat::loadFrom (data, numBytes));
|
||||
|
||||
if (image.isValid())
|
||||
{
|
||||
DrawableImage* const di = new DrawableImage();
|
||||
di->setImage (image);
|
||||
result = di;
|
||||
}
|
||||
else
|
||||
{
|
||||
const String asString (String::createStringFromData (data, (int) numBytes));
|
||||
|
||||
XmlDocument doc (asString);
|
||||
ScopedPointer <XmlElement> outer (doc.getDocumentElement (true));
|
||||
|
||||
if (outer != nullptr && outer->hasTagName ("svg"))
|
||||
{
|
||||
ScopedPointer <XmlElement> svg (doc.getDocumentElement());
|
||||
|
||||
if (svg != nullptr)
|
||||
result = Drawable::createFromSVG (*svg);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Drawable* Drawable::createFromImageDataStream (InputStream& dataSource)
|
||||
{
|
||||
MemoryOutputStream mo;
|
||||
mo.writeFromInputStream (dataSource, -1);
|
||||
|
||||
return createFromImageData (mo.getData(), mo.getDataSize());
|
||||
}
|
||||
|
||||
Drawable* Drawable::createFromImageFile (const File& file)
|
||||
{
|
||||
const ScopedPointer <FileInputStream> fin (file.createInputStream());
|
||||
|
||||
return fin != nullptr ? createFromImageDataStream (*fin) : nullptr;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <class DrawableClass>
|
||||
class DrawableTypeHandler : public ComponentBuilder::TypeHandler
|
||||
{
|
||||
public:
|
||||
DrawableTypeHandler()
|
||||
: ComponentBuilder::TypeHandler (DrawableClass::valueTreeType)
|
||||
{
|
||||
}
|
||||
|
||||
Component* addNewComponentFromState (const ValueTree& state, Component* parent)
|
||||
{
|
||||
DrawableClass* const d = new DrawableClass();
|
||||
|
||||
if (parent != nullptr)
|
||||
parent->addAndMakeVisible (d);
|
||||
|
||||
updateComponentFromState (d, state);
|
||||
return d;
|
||||
}
|
||||
|
||||
void updateComponentFromState (Component* component, const ValueTree& state)
|
||||
{
|
||||
DrawableClass* const d = dynamic_cast <DrawableClass*> (component);
|
||||
jassert (d != nullptr);
|
||||
d->refreshFromValueTree (state, *this->getBuilder());
|
||||
}
|
||||
};
|
||||
|
||||
void Drawable::registerDrawableTypeHandlers (ComponentBuilder& builder)
|
||||
{
|
||||
builder.registerTypeHandler (new DrawableTypeHandler <DrawablePath>());
|
||||
builder.registerTypeHandler (new DrawableTypeHandler <DrawableComposite>());
|
||||
builder.registerTypeHandler (new DrawableTypeHandler <DrawableRectangle>());
|
||||
builder.registerTypeHandler (new DrawableTypeHandler <DrawableImage>());
|
||||
builder.registerTypeHandler (new DrawableTypeHandler <DrawableText>());
|
||||
}
|
||||
|
||||
Drawable* Drawable::createFromValueTree (const ValueTree& tree, ComponentBuilder::ImageProvider* imageProvider)
|
||||
{
|
||||
ComponentBuilder builder (tree);
|
||||
builder.setImageProvider (imageProvider);
|
||||
registerDrawableTypeHandlers (builder);
|
||||
|
||||
ScopedPointer<Component> comp (builder.createComponent());
|
||||
Drawable* const d = dynamic_cast<Drawable*> (static_cast <Component*> (comp));
|
||||
|
||||
if (d != nullptr)
|
||||
comp.release();
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
Drawable::ValueTreeWrapperBase::ValueTreeWrapperBase (const ValueTree& state_)
|
||||
: state (state_)
|
||||
{
|
||||
}
|
||||
|
||||
String Drawable::ValueTreeWrapperBase::getID() const
|
||||
{
|
||||
return state [ComponentBuilder::idProperty];
|
||||
}
|
||||
|
||||
void Drawable::ValueTreeWrapperBase::setID (const String& newID)
|
||||
{
|
||||
if (newID.isEmpty())
|
||||
state.removeProperty (ComponentBuilder::idProperty, nullptr);
|
||||
else
|
||||
state.setProperty (ComponentBuilder::idProperty, newID, nullptr);
|
||||
}
|
||||
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
255
modules/juce_gui_basics/drawables/juce_Drawable.h
Normal file
255
modules/juce_gui_basics/drawables/juce_Drawable.h
Normal file
|
|
@ -0,0 +1,255 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_DRAWABLE_JUCEHEADER__
|
||||
#define __JUCE_DRAWABLE_JUCEHEADER__
|
||||
|
||||
#include "../components/juce_Component.h"
|
||||
#include "../positioning/juce_RelativeCoordinate.h"
|
||||
#include "../positioning/juce_RelativeCoordinatePositioner.h"
|
||||
#include "../layout/juce_ComponentBuilder.h"
|
||||
class DrawableComposite;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
The base class for objects which can draw themselves, e.g. polygons, images, etc.
|
||||
|
||||
@see DrawableComposite, DrawableImage, DrawablePath, DrawableText
|
||||
*/
|
||||
class JUCE_API Drawable : public Component
|
||||
{
|
||||
protected:
|
||||
//==============================================================================
|
||||
/** The base class can't be instantiated directly.
|
||||
|
||||
@see DrawableComposite, DrawableImage, DrawablePath, DrawableText
|
||||
*/
|
||||
Drawable();
|
||||
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~Drawable();
|
||||
|
||||
//==============================================================================
|
||||
/** Creates a deep copy of this Drawable object.
|
||||
|
||||
Use this to create a new copy of this and any sub-objects in the tree.
|
||||
*/
|
||||
virtual Drawable* createCopy() const = 0;
|
||||
|
||||
//==============================================================================
|
||||
/** Renders this Drawable object.
|
||||
|
||||
Note that the preferred way to render a drawable in future is by using it
|
||||
as a component and adding it to a parent, so you might want to consider that
|
||||
before using this method.
|
||||
|
||||
@see drawWithin
|
||||
*/
|
||||
void draw (Graphics& g, float opacity,
|
||||
const AffineTransform& transform = AffineTransform::identity) const;
|
||||
|
||||
/** Renders the Drawable at a given offset within the Graphics context.
|
||||
|
||||
The co-ordinates passed-in are used to translate the object relative to its own
|
||||
origin before drawing it - this is basically a quick way of saying:
|
||||
|
||||
@code
|
||||
draw (g, AffineTransform::translation (x, y)).
|
||||
@endcode
|
||||
|
||||
Note that the preferred way to render a drawable in future is by using it
|
||||
as a component and adding it to a parent, so you might want to consider that
|
||||
before using this method.
|
||||
*/
|
||||
void drawAt (Graphics& g, float x, float y, float opacity) const;
|
||||
|
||||
/** Renders the Drawable within a rectangle, scaling it to fit neatly inside without
|
||||
changing its aspect-ratio.
|
||||
|
||||
The object can placed arbitrarily within the rectangle based on a Justification type,
|
||||
and can either be made as big as possible, or just reduced to fit.
|
||||
|
||||
Note that the preferred way to render a drawable in future is by using it
|
||||
as a component and adding it to a parent, so you might want to consider that
|
||||
before using this method.
|
||||
|
||||
@param g the graphics context to render onto
|
||||
@param destArea the target rectangle to fit the drawable into
|
||||
@param placement defines the alignment and rescaling to use to fit
|
||||
this object within the target rectangle.
|
||||
@param opacity the opacity to use, in the range 0 to 1.0
|
||||
*/
|
||||
void drawWithin (Graphics& g,
|
||||
const Rectangle<float>& destArea,
|
||||
const RectanglePlacement& placement,
|
||||
float opacity) const;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** Resets any transformations on this drawable, and positions its origin within
|
||||
its parent component.
|
||||
*/
|
||||
void setOriginWithOriginalSize (const Point<float>& originWithinParent);
|
||||
|
||||
/** Sets a transform for this drawable that will position it within the specified
|
||||
area of its parent component.
|
||||
*/
|
||||
void setTransformToFit (const Rectangle<float>& areaInParent, const RectanglePlacement& placement);
|
||||
|
||||
/** Returns the DrawableComposite that contains this object, if there is one. */
|
||||
DrawableComposite* getParent() const;
|
||||
|
||||
//==============================================================================
|
||||
/** Tries to turn some kind of image file into a drawable.
|
||||
|
||||
The data could be an image that the ImageFileFormat class understands, or it
|
||||
could be SVG.
|
||||
*/
|
||||
static Drawable* createFromImageData (const void* data, size_t numBytes);
|
||||
|
||||
/** Tries to turn a stream containing some kind of image data into a drawable.
|
||||
|
||||
The data could be an image that the ImageFileFormat class understands, or it
|
||||
could be SVG.
|
||||
*/
|
||||
static Drawable* createFromImageDataStream (InputStream& dataSource);
|
||||
|
||||
/** Tries to turn a file containing some kind of image data into a drawable.
|
||||
|
||||
The data could be an image that the ImageFileFormat class understands, or it
|
||||
could be SVG.
|
||||
*/
|
||||
static Drawable* createFromImageFile (const File& file);
|
||||
|
||||
/** Attempts to parse an SVG (Scalable Vector Graphics) document, and to turn this
|
||||
into a Drawable tree.
|
||||
|
||||
The object returned must be deleted by the caller. If something goes wrong
|
||||
while parsing, it may return 0.
|
||||
|
||||
SVG is a pretty large and complex spec, and this doesn't aim to be a full
|
||||
implementation, but it can return the basic vector objects.
|
||||
*/
|
||||
static Drawable* createFromSVG (const XmlElement& svgDocument);
|
||||
|
||||
//==============================================================================
|
||||
/** Tries to create a Drawable from a previously-saved ValueTree.
|
||||
The ValueTree must have been created by the createValueTree() method.
|
||||
If there are any images used within the drawable, you'll need to provide a valid
|
||||
ImageProvider object that can be used to retrieve these images from whatever type
|
||||
of identifier is used to represent them.
|
||||
Internally, this uses a ComponentBuilder, and registerDrawableTypeHandlers().
|
||||
*/
|
||||
static Drawable* createFromValueTree (const ValueTree& tree, ComponentBuilder::ImageProvider* imageProvider);
|
||||
|
||||
/** Creates a ValueTree to represent this Drawable.
|
||||
The ValueTree that is returned can be turned back into a Drawable with createFromValueTree().
|
||||
If there are any images used in this drawable, you'll need to provide a valid ImageProvider
|
||||
object that can be used to create storable representations of them.
|
||||
*/
|
||||
virtual ValueTree createValueTree (ComponentBuilder::ImageProvider* imageProvider) const = 0;
|
||||
|
||||
/** Returns the area that this drawble covers.
|
||||
The result is expressed in this drawable's own coordinate space, and does not take
|
||||
into account any transforms that may be applied to the component.
|
||||
*/
|
||||
virtual Rectangle<float> getDrawableBounds() const = 0;
|
||||
|
||||
//==============================================================================
|
||||
/** Internal class used to manage ValueTrees that represent Drawables. */
|
||||
class ValueTreeWrapperBase
|
||||
{
|
||||
public:
|
||||
ValueTreeWrapperBase (const ValueTree& state);
|
||||
|
||||
ValueTree& getState() noexcept { return state; }
|
||||
|
||||
String getID() const;
|
||||
void setID (const String& newID);
|
||||
|
||||
ValueTree state;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Registers a set of ComponentBuilder::TypeHandler objects that can be used to
|
||||
load all the different Drawable types from a saved state.
|
||||
@see ComponentBuilder::registerTypeHandler()
|
||||
*/
|
||||
static void registerDrawableTypeHandlers (ComponentBuilder& componentBuilder);
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
friend class DrawableComposite;
|
||||
friend class DrawableShape;
|
||||
|
||||
/** @internal */
|
||||
void transformContextToCorrectOrigin (Graphics& g);
|
||||
/** @internal */
|
||||
void parentHierarchyChanged();
|
||||
/** @internal */
|
||||
void setBoundsToEnclose (const Rectangle<float>& area);
|
||||
|
||||
Point<int> originRelativeToComponent;
|
||||
|
||||
#ifndef DOXYGEN
|
||||
/** Internal utility class used by Drawables. */
|
||||
template <class DrawableType>
|
||||
class Positioner : public RelativeCoordinatePositionerBase
|
||||
{
|
||||
public:
|
||||
Positioner (DrawableType& component_)
|
||||
: RelativeCoordinatePositionerBase (component_),
|
||||
owner (component_)
|
||||
{}
|
||||
|
||||
bool registerCoordinates() { return owner.registerCoordinates (*this); }
|
||||
void applyToComponentBounds()
|
||||
{
|
||||
ComponentScope scope (getComponent());
|
||||
owner.recalculateCoordinates (&scope);
|
||||
}
|
||||
|
||||
void applyNewBounds (const Rectangle<int>&)
|
||||
{
|
||||
jassertfalse; // drawables can't be resized directly!
|
||||
}
|
||||
|
||||
private:
|
||||
DrawableType& owner;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Positioner);
|
||||
};
|
||||
#endif
|
||||
|
||||
private:
|
||||
void nonConstDraw (Graphics& g, float opacity, const AffineTransform& transform);
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Drawable);
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCE_DRAWABLE_JUCEHEADER__
|
||||
345
modules/juce_gui_basics/drawables/juce_DrawableComposite.cpp
Normal file
345
modules/juce_gui_basics/drawables/juce_DrawableComposite.cpp
Normal file
|
|
@ -0,0 +1,345 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
//==============================================================================
|
||||
DrawableComposite::DrawableComposite()
|
||||
: bounds (Point<float>(), Point<float> (100.0f, 0.0f), Point<float> (0.0f, 100.0f)),
|
||||
updateBoundsReentrant (false)
|
||||
{
|
||||
setContentArea (RelativeRectangle (RelativeCoordinate (0.0),
|
||||
RelativeCoordinate (100.0),
|
||||
RelativeCoordinate (0.0),
|
||||
RelativeCoordinate (100.0)));
|
||||
}
|
||||
|
||||
DrawableComposite::DrawableComposite (const DrawableComposite& other)
|
||||
: bounds (other.bounds),
|
||||
markersX (other.markersX),
|
||||
markersY (other.markersY),
|
||||
updateBoundsReentrant (false)
|
||||
{
|
||||
for (int i = 0; i < other.getNumChildComponents(); ++i)
|
||||
{
|
||||
const Drawable* const d = dynamic_cast <const Drawable*> (other.getChildComponent(i));
|
||||
|
||||
if (d != nullptr)
|
||||
addAndMakeVisible (d->createCopy());
|
||||
}
|
||||
}
|
||||
|
||||
DrawableComposite::~DrawableComposite()
|
||||
{
|
||||
deleteAllChildren();
|
||||
}
|
||||
|
||||
Drawable* DrawableComposite::createCopy() const
|
||||
{
|
||||
return new DrawableComposite (*this);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
Rectangle<float> DrawableComposite::getDrawableBounds() const
|
||||
{
|
||||
Rectangle<float> r;
|
||||
|
||||
for (int i = getNumChildComponents(); --i >= 0;)
|
||||
{
|
||||
const Drawable* const d = dynamic_cast <const Drawable*> (getChildComponent(i));
|
||||
|
||||
if (d != nullptr)
|
||||
r = r.getUnion (d->isTransformed() ? d->getDrawableBounds().transformed (d->getTransform())
|
||||
: d->getDrawableBounds());
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
MarkerList* DrawableComposite::getMarkers (bool xAxis)
|
||||
{
|
||||
return xAxis ? &markersX : &markersY;
|
||||
}
|
||||
|
||||
RelativeRectangle DrawableComposite::getContentArea() const
|
||||
{
|
||||
jassert (markersX.getNumMarkers() >= 2 && markersX.getMarker (0)->name == contentLeftMarkerName && markersX.getMarker (1)->name == contentRightMarkerName);
|
||||
jassert (markersY.getNumMarkers() >= 2 && markersY.getMarker (0)->name == contentTopMarkerName && markersY.getMarker (1)->name == contentBottomMarkerName);
|
||||
|
||||
return RelativeRectangle (markersX.getMarker(0)->position, markersX.getMarker(1)->position,
|
||||
markersY.getMarker(0)->position, markersY.getMarker(1)->position);
|
||||
}
|
||||
|
||||
void DrawableComposite::setContentArea (const RelativeRectangle& newArea)
|
||||
{
|
||||
markersX.setMarker (contentLeftMarkerName, newArea.left);
|
||||
markersX.setMarker (contentRightMarkerName, newArea.right);
|
||||
markersY.setMarker (contentTopMarkerName, newArea.top);
|
||||
markersY.setMarker (contentBottomMarkerName, newArea.bottom);
|
||||
}
|
||||
|
||||
void DrawableComposite::setBoundingBox (const RelativeParallelogram& newBounds)
|
||||
{
|
||||
if (bounds != newBounds)
|
||||
{
|
||||
bounds = newBounds;
|
||||
|
||||
if (bounds.isDynamic())
|
||||
{
|
||||
Drawable::Positioner<DrawableComposite>* const p = new Drawable::Positioner<DrawableComposite> (*this);
|
||||
setPositioner (p);
|
||||
p->apply();
|
||||
}
|
||||
else
|
||||
{
|
||||
setPositioner (nullptr);
|
||||
recalculateCoordinates (nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DrawableComposite::resetBoundingBoxToContentArea()
|
||||
{
|
||||
const RelativeRectangle content (getContentArea());
|
||||
|
||||
setBoundingBox (RelativeParallelogram (RelativePoint (content.left, content.top),
|
||||
RelativePoint (content.right, content.top),
|
||||
RelativePoint (content.left, content.bottom)));
|
||||
}
|
||||
|
||||
void DrawableComposite::resetContentAreaAndBoundingBoxToFitChildren()
|
||||
{
|
||||
const Rectangle<float> activeArea (getDrawableBounds());
|
||||
|
||||
setContentArea (RelativeRectangle (RelativeCoordinate (activeArea.getX()),
|
||||
RelativeCoordinate (activeArea.getRight()),
|
||||
RelativeCoordinate (activeArea.getY()),
|
||||
RelativeCoordinate (activeArea.getBottom())));
|
||||
resetBoundingBoxToContentArea();
|
||||
}
|
||||
|
||||
bool DrawableComposite::registerCoordinates (RelativeCoordinatePositionerBase& pos)
|
||||
{
|
||||
bool ok = pos.addPoint (bounds.topLeft);
|
||||
ok = pos.addPoint (bounds.topRight) && ok;
|
||||
return pos.addPoint (bounds.bottomLeft) && ok;
|
||||
}
|
||||
|
||||
void DrawableComposite::recalculateCoordinates (Expression::Scope* scope)
|
||||
{
|
||||
Point<float> resolved[3];
|
||||
bounds.resolveThreePoints (resolved, scope);
|
||||
|
||||
const Rectangle<float> content (getContentArea().resolve (scope));
|
||||
|
||||
AffineTransform t (AffineTransform::fromTargetPoints (content.getX(), content.getY(), resolved[0].getX(), resolved[0].getY(),
|
||||
content.getRight(), content.getY(), resolved[1].getX(), resolved[1].getY(),
|
||||
content.getX(), content.getBottom(), resolved[2].getX(), resolved[2].getY()));
|
||||
|
||||
if (t.isSingularity())
|
||||
t = AffineTransform::identity;
|
||||
|
||||
setTransform (t);
|
||||
}
|
||||
|
||||
void DrawableComposite::parentHierarchyChanged()
|
||||
{
|
||||
DrawableComposite* parent = getParent();
|
||||
if (parent != nullptr)
|
||||
originRelativeToComponent = parent->originRelativeToComponent - getPosition();
|
||||
}
|
||||
|
||||
void DrawableComposite::childBoundsChanged (Component*)
|
||||
{
|
||||
updateBoundsToFitChildren();
|
||||
}
|
||||
|
||||
void DrawableComposite::childrenChanged()
|
||||
{
|
||||
updateBoundsToFitChildren();
|
||||
}
|
||||
|
||||
void DrawableComposite::updateBoundsToFitChildren()
|
||||
{
|
||||
if (! updateBoundsReentrant)
|
||||
{
|
||||
const ScopedValueSetter<bool> setter (updateBoundsReentrant, true, false);
|
||||
|
||||
Rectangle<int> childArea;
|
||||
|
||||
for (int i = getNumChildComponents(); --i >= 0;)
|
||||
childArea = childArea.getUnion (getChildComponent(i)->getBoundsInParent());
|
||||
|
||||
const Point<int> delta (childArea.getPosition());
|
||||
childArea += getPosition();
|
||||
|
||||
if (childArea != getBounds())
|
||||
{
|
||||
if (! delta.isOrigin())
|
||||
{
|
||||
originRelativeToComponent -= delta;
|
||||
|
||||
for (int i = getNumChildComponents(); --i >= 0;)
|
||||
{
|
||||
Component* const c = getChildComponent(i);
|
||||
|
||||
if (c != nullptr)
|
||||
c->setBounds (c->getBounds() - delta);
|
||||
}
|
||||
}
|
||||
|
||||
setBounds (childArea);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
const char* const DrawableComposite::contentLeftMarkerName = "left";
|
||||
const char* const DrawableComposite::contentRightMarkerName = "right";
|
||||
const char* const DrawableComposite::contentTopMarkerName = "top";
|
||||
const char* const DrawableComposite::contentBottomMarkerName = "bottom";
|
||||
|
||||
//==============================================================================
|
||||
const Identifier DrawableComposite::valueTreeType ("Group");
|
||||
|
||||
const Identifier DrawableComposite::ValueTreeWrapper::topLeft ("topLeft");
|
||||
const Identifier DrawableComposite::ValueTreeWrapper::topRight ("topRight");
|
||||
const Identifier DrawableComposite::ValueTreeWrapper::bottomLeft ("bottomLeft");
|
||||
const Identifier DrawableComposite::ValueTreeWrapper::childGroupTag ("Drawables");
|
||||
const Identifier DrawableComposite::ValueTreeWrapper::markerGroupTagX ("MarkersX");
|
||||
const Identifier DrawableComposite::ValueTreeWrapper::markerGroupTagY ("MarkersY");
|
||||
|
||||
//==============================================================================
|
||||
DrawableComposite::ValueTreeWrapper::ValueTreeWrapper (const ValueTree& state_)
|
||||
: ValueTreeWrapperBase (state_)
|
||||
{
|
||||
jassert (state.hasType (valueTreeType));
|
||||
}
|
||||
|
||||
ValueTree DrawableComposite::ValueTreeWrapper::getChildList() const
|
||||
{
|
||||
return state.getChildWithName (childGroupTag);
|
||||
}
|
||||
|
||||
ValueTree DrawableComposite::ValueTreeWrapper::getChildListCreating (UndoManager* undoManager)
|
||||
{
|
||||
return state.getOrCreateChildWithName (childGroupTag, undoManager);
|
||||
}
|
||||
|
||||
RelativeParallelogram DrawableComposite::ValueTreeWrapper::getBoundingBox() const
|
||||
{
|
||||
return RelativeParallelogram (state.getProperty (topLeft, "0, 0"),
|
||||
state.getProperty (topRight, "100, 0"),
|
||||
state.getProperty (bottomLeft, "0, 100"));
|
||||
}
|
||||
|
||||
void DrawableComposite::ValueTreeWrapper::setBoundingBox (const RelativeParallelogram& newBounds, UndoManager* undoManager)
|
||||
{
|
||||
state.setProperty (topLeft, newBounds.topLeft.toString(), undoManager);
|
||||
state.setProperty (topRight, newBounds.topRight.toString(), undoManager);
|
||||
state.setProperty (bottomLeft, newBounds.bottomLeft.toString(), undoManager);
|
||||
}
|
||||
|
||||
void DrawableComposite::ValueTreeWrapper::resetBoundingBoxToContentArea (UndoManager* undoManager)
|
||||
{
|
||||
const RelativeRectangle content (getContentArea());
|
||||
|
||||
setBoundingBox (RelativeParallelogram (RelativePoint (content.left, content.top),
|
||||
RelativePoint (content.right, content.top),
|
||||
RelativePoint (content.left, content.bottom)), undoManager);
|
||||
}
|
||||
|
||||
RelativeRectangle DrawableComposite::ValueTreeWrapper::getContentArea() const
|
||||
{
|
||||
MarkerList::ValueTreeWrapper markersX (getMarkerList (true));
|
||||
MarkerList::ValueTreeWrapper markersY (getMarkerList (false));
|
||||
|
||||
return RelativeRectangle (markersX.getMarker (markersX.getMarkerState (0)).position,
|
||||
markersX.getMarker (markersX.getMarkerState (1)).position,
|
||||
markersY.getMarker (markersY.getMarkerState (0)).position,
|
||||
markersY.getMarker (markersY.getMarkerState (1)).position);
|
||||
}
|
||||
|
||||
void DrawableComposite::ValueTreeWrapper::setContentArea (const RelativeRectangle& newArea, UndoManager* undoManager)
|
||||
{
|
||||
MarkerList::ValueTreeWrapper markersX (getMarkerListCreating (true, nullptr));
|
||||
MarkerList::ValueTreeWrapper markersY (getMarkerListCreating (false, nullptr));
|
||||
|
||||
markersX.setMarker (MarkerList::Marker (contentLeftMarkerName, newArea.left), undoManager);
|
||||
markersX.setMarker (MarkerList::Marker (contentRightMarkerName, newArea.right), undoManager);
|
||||
markersY.setMarker (MarkerList::Marker (contentTopMarkerName, newArea.top), undoManager);
|
||||
markersY.setMarker (MarkerList::Marker (contentBottomMarkerName, newArea.bottom), undoManager);
|
||||
}
|
||||
|
||||
MarkerList::ValueTreeWrapper DrawableComposite::ValueTreeWrapper::getMarkerList (bool xAxis) const
|
||||
{
|
||||
return state.getChildWithName (xAxis ? markerGroupTagX : markerGroupTagY);
|
||||
}
|
||||
|
||||
MarkerList::ValueTreeWrapper DrawableComposite::ValueTreeWrapper::getMarkerListCreating (bool xAxis, UndoManager* undoManager)
|
||||
{
|
||||
return state.getOrCreateChildWithName (xAxis ? markerGroupTagX : markerGroupTagY, undoManager);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void DrawableComposite::refreshFromValueTree (const ValueTree& tree, ComponentBuilder& builder)
|
||||
{
|
||||
const ValueTreeWrapper wrapper (tree);
|
||||
setComponentID (wrapper.getID());
|
||||
|
||||
wrapper.getMarkerList (true).applyTo (markersX);
|
||||
wrapper.getMarkerList (false).applyTo (markersY);
|
||||
|
||||
setBoundingBox (wrapper.getBoundingBox());
|
||||
|
||||
builder.updateChildComponents (*this, wrapper.getChildList());
|
||||
}
|
||||
|
||||
ValueTree DrawableComposite::createValueTree (ComponentBuilder::ImageProvider* imageProvider) const
|
||||
{
|
||||
ValueTree tree (valueTreeType);
|
||||
ValueTreeWrapper v (tree);
|
||||
|
||||
v.setID (getComponentID());
|
||||
v.setBoundingBox (bounds, nullptr);
|
||||
|
||||
ValueTree childList (v.getChildListCreating (nullptr));
|
||||
|
||||
for (int i = 0; i < getNumChildComponents(); ++i)
|
||||
{
|
||||
const Drawable* const d = dynamic_cast <const Drawable*> (getChildComponent(i));
|
||||
jassert (d != nullptr); // You can't save a mix of Drawables and normal components!
|
||||
|
||||
childList.addChild (d->createValueTree (imageProvider), -1, nullptr);
|
||||
}
|
||||
|
||||
v.getMarkerListCreating (true, nullptr).readFrom (markersX, nullptr);
|
||||
v.getMarkerListCreating (false, nullptr).readFrom (markersY, nullptr);
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
162
modules/juce_gui_basics/drawables/juce_DrawableComposite.h
Normal file
162
modules/juce_gui_basics/drawables/juce_DrawableComposite.h
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_DRAWABLECOMPOSITE_JUCEHEADER__
|
||||
#define __JUCE_DRAWABLECOMPOSITE_JUCEHEADER__
|
||||
|
||||
#include "juce_Drawable.h"
|
||||
#include "../positioning/juce_MarkerList.h"
|
||||
#include "../positioning/juce_RelativeParallelogram.h"
|
||||
#include "../positioning/juce_RelativeRectangle.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A drawable object which acts as a container for a set of other Drawables.
|
||||
|
||||
@see Drawable
|
||||
*/
|
||||
class JUCE_API DrawableComposite : public Drawable
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a composite Drawable. */
|
||||
DrawableComposite();
|
||||
|
||||
/** Creates a copy of a DrawableComposite. */
|
||||
DrawableComposite (const DrawableComposite& other);
|
||||
|
||||
/** Destructor. */
|
||||
~DrawableComposite();
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the parallelogram that defines the target position of the content rectangle when the drawable is rendered.
|
||||
@see setContentArea
|
||||
*/
|
||||
void setBoundingBox (const RelativeParallelogram& newBoundingBox);
|
||||
|
||||
/** Returns the parallelogram that defines the target position of the content rectangle when the drawable is rendered.
|
||||
@see setBoundingBox
|
||||
*/
|
||||
const RelativeParallelogram& getBoundingBox() const noexcept { return bounds; }
|
||||
|
||||
/** Changes the bounding box transform to match the content area, so that any sub-items will
|
||||
be drawn at their untransformed positions.
|
||||
*/
|
||||
void resetBoundingBoxToContentArea();
|
||||
|
||||
/** Returns the main content rectangle.
|
||||
The content area is actually defined by the markers named "left", "right", "top" and
|
||||
"bottom", but this method is a shortcut that returns them all at once.
|
||||
@see contentLeftMarkerName, contentRightMarkerName, contentTopMarkerName, contentBottomMarkerName
|
||||
*/
|
||||
RelativeRectangle getContentArea() const;
|
||||
|
||||
/** Changes the main content area.
|
||||
The content area is actually defined by the markers named "left", "right", "top" and
|
||||
"bottom", but this method is a shortcut that sets them all at once.
|
||||
@see setBoundingBox, contentLeftMarkerName, contentRightMarkerName, contentTopMarkerName, contentBottomMarkerName
|
||||
*/
|
||||
void setContentArea (const RelativeRectangle& newArea);
|
||||
|
||||
/** Resets the content area and the bounding transform to fit around the area occupied
|
||||
by the child components (ignoring any markers).
|
||||
*/
|
||||
void resetContentAreaAndBoundingBoxToFitChildren();
|
||||
|
||||
//==============================================================================
|
||||
/** The name of the marker that defines the left edge of the content area. */
|
||||
static const char* const contentLeftMarkerName;
|
||||
/** The name of the marker that defines the right edge of the content area. */
|
||||
static const char* const contentRightMarkerName;
|
||||
/** The name of the marker that defines the top edge of the content area. */
|
||||
static const char* const contentTopMarkerName;
|
||||
/** The name of the marker that defines the bottom edge of the content area. */
|
||||
static const char* const contentBottomMarkerName;
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
Drawable* createCopy() const;
|
||||
/** @internal */
|
||||
void refreshFromValueTree (const ValueTree& tree, ComponentBuilder& builder);
|
||||
/** @internal */
|
||||
ValueTree createValueTree (ComponentBuilder::ImageProvider* imageProvider) const;
|
||||
/** @internal */
|
||||
static const Identifier valueTreeType;
|
||||
/** @internal */
|
||||
Rectangle<float> getDrawableBounds() const;
|
||||
/** @internal */
|
||||
void childBoundsChanged (Component*);
|
||||
/** @internal */
|
||||
void childrenChanged();
|
||||
/** @internal */
|
||||
void parentHierarchyChanged();
|
||||
/** @internal */
|
||||
MarkerList* getMarkers (bool xAxis);
|
||||
|
||||
//==============================================================================
|
||||
/** Internally-used class for wrapping a DrawableComposite's state into a ValueTree. */
|
||||
class ValueTreeWrapper : public Drawable::ValueTreeWrapperBase
|
||||
{
|
||||
public:
|
||||
ValueTreeWrapper (const ValueTree& state);
|
||||
|
||||
ValueTree getChildList() const;
|
||||
ValueTree getChildListCreating (UndoManager* undoManager);
|
||||
|
||||
RelativeParallelogram getBoundingBox() const;
|
||||
void setBoundingBox (const RelativeParallelogram& newBounds, UndoManager* undoManager);
|
||||
void resetBoundingBoxToContentArea (UndoManager* undoManager);
|
||||
|
||||
RelativeRectangle getContentArea() const;
|
||||
void setContentArea (const RelativeRectangle& newArea, UndoManager* undoManager);
|
||||
|
||||
MarkerList::ValueTreeWrapper getMarkerList (bool xAxis) const;
|
||||
MarkerList::ValueTreeWrapper getMarkerListCreating (bool xAxis, UndoManager* undoManager);
|
||||
|
||||
static const Identifier topLeft, topRight, bottomLeft;
|
||||
|
||||
private:
|
||||
static const Identifier childGroupTag, markerGroupTagX, markerGroupTagY;
|
||||
};
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
RelativeParallelogram bounds;
|
||||
MarkerList markersX, markersY;
|
||||
bool updateBoundsReentrant;
|
||||
|
||||
friend class Drawable::Positioner<DrawableComposite>;
|
||||
bool registerCoordinates (RelativeCoordinatePositionerBase&);
|
||||
void recalculateCoordinates (Expression::Scope*);
|
||||
|
||||
void updateBoundsToFitChildren();
|
||||
|
||||
DrawableComposite& operator= (const DrawableComposite&);
|
||||
JUCE_LEAK_DETECTOR (DrawableComposite);
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCE_DRAWABLECOMPOSITE_JUCEHEADER__
|
||||
295
modules/juce_gui_basics/drawables/juce_DrawableImage.cpp
Normal file
295
modules/juce_gui_basics/drawables/juce_DrawableImage.cpp
Normal file
|
|
@ -0,0 +1,295 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
//==============================================================================
|
||||
DrawableImage::DrawableImage()
|
||||
: opacity (1.0f),
|
||||
overlayColour (0x00000000)
|
||||
{
|
||||
bounds.topRight = RelativePoint (Point<float> (1.0f, 0.0f));
|
||||
bounds.bottomLeft = RelativePoint (Point<float> (0.0f, 1.0f));
|
||||
}
|
||||
|
||||
DrawableImage::DrawableImage (const DrawableImage& other)
|
||||
: image (other.image),
|
||||
opacity (other.opacity),
|
||||
overlayColour (other.overlayColour),
|
||||
bounds (other.bounds)
|
||||
{
|
||||
}
|
||||
|
||||
DrawableImage::~DrawableImage()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void DrawableImage::setImage (const Image& imageToUse)
|
||||
{
|
||||
image = imageToUse;
|
||||
setBounds (imageToUse.getBounds());
|
||||
|
||||
bounds.topLeft = RelativePoint (Point<float> (0.0f, 0.0f));
|
||||
bounds.topRight = RelativePoint (Point<float> ((float) image.getWidth(), 0.0f));
|
||||
bounds.bottomLeft = RelativePoint (Point<float> (0.0f, (float) image.getHeight()));
|
||||
recalculateCoordinates (nullptr);
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
void DrawableImage::setOpacity (const float newOpacity)
|
||||
{
|
||||
opacity = newOpacity;
|
||||
}
|
||||
|
||||
void DrawableImage::setOverlayColour (const Colour& newOverlayColour)
|
||||
{
|
||||
overlayColour = newOverlayColour;
|
||||
}
|
||||
|
||||
void DrawableImage::setBoundingBox (const RelativeParallelogram& newBounds)
|
||||
{
|
||||
if (bounds != newBounds)
|
||||
{
|
||||
bounds = newBounds;
|
||||
|
||||
if (bounds.isDynamic())
|
||||
{
|
||||
Drawable::Positioner<DrawableImage>* const p = new Drawable::Positioner<DrawableImage> (*this);
|
||||
setPositioner (p);
|
||||
p->apply();
|
||||
}
|
||||
else
|
||||
{
|
||||
setPositioner (nullptr);
|
||||
recalculateCoordinates (nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool DrawableImage::registerCoordinates (RelativeCoordinatePositionerBase& pos)
|
||||
{
|
||||
bool ok = pos.addPoint (bounds.topLeft);
|
||||
ok = pos.addPoint (bounds.topRight) && ok;
|
||||
return pos.addPoint (bounds.bottomLeft) && ok;
|
||||
}
|
||||
|
||||
void DrawableImage::recalculateCoordinates (Expression::Scope* scope)
|
||||
{
|
||||
if (image.isValid())
|
||||
{
|
||||
Point<float> resolved[3];
|
||||
bounds.resolveThreePoints (resolved, scope);
|
||||
|
||||
const Point<float> tr (resolved[0] + (resolved[1] - resolved[0]) / (float) image.getWidth());
|
||||
const Point<float> bl (resolved[0] + (resolved[2] - resolved[0]) / (float) image.getHeight());
|
||||
|
||||
AffineTransform t (AffineTransform::fromTargetPoints (resolved[0].getX(), resolved[0].getY(),
|
||||
tr.getX(), tr.getY(),
|
||||
bl.getX(), bl.getY()));
|
||||
|
||||
if (t.isSingularity())
|
||||
t = AffineTransform::identity;
|
||||
|
||||
setTransform (t);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void DrawableImage::paint (Graphics& g)
|
||||
{
|
||||
if (image.isValid())
|
||||
{
|
||||
if (opacity > 0.0f && ! overlayColour.isOpaque())
|
||||
{
|
||||
g.setOpacity (opacity);
|
||||
g.drawImageAt (image, 0, 0, false);
|
||||
}
|
||||
|
||||
if (! overlayColour.isTransparent())
|
||||
{
|
||||
g.setColour (overlayColour.withMultipliedAlpha (opacity));
|
||||
g.drawImageAt (image, 0, 0, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle<float> DrawableImage::getDrawableBounds() const
|
||||
{
|
||||
return image.getBounds().toFloat();
|
||||
}
|
||||
|
||||
bool DrawableImage::hitTest (int x, int y)
|
||||
{
|
||||
return Drawable::hitTest (x, y) && image.isValid() && image.getPixelAt (x, y).getAlpha() >= 127;
|
||||
}
|
||||
|
||||
Drawable* DrawableImage::createCopy() const
|
||||
{
|
||||
return new DrawableImage (*this);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
const Identifier DrawableImage::valueTreeType ("Image");
|
||||
|
||||
const Identifier DrawableImage::ValueTreeWrapper::opacity ("opacity");
|
||||
const Identifier DrawableImage::ValueTreeWrapper::overlay ("overlay");
|
||||
const Identifier DrawableImage::ValueTreeWrapper::image ("image");
|
||||
const Identifier DrawableImage::ValueTreeWrapper::topLeft ("topLeft");
|
||||
const Identifier DrawableImage::ValueTreeWrapper::topRight ("topRight");
|
||||
const Identifier DrawableImage::ValueTreeWrapper::bottomLeft ("bottomLeft");
|
||||
|
||||
//==============================================================================
|
||||
DrawableImage::ValueTreeWrapper::ValueTreeWrapper (const ValueTree& state_)
|
||||
: ValueTreeWrapperBase (state_)
|
||||
{
|
||||
jassert (state.hasType (valueTreeType));
|
||||
}
|
||||
|
||||
var DrawableImage::ValueTreeWrapper::getImageIdentifier() const
|
||||
{
|
||||
return state [image];
|
||||
}
|
||||
|
||||
Value DrawableImage::ValueTreeWrapper::getImageIdentifierValue (UndoManager* undoManager)
|
||||
{
|
||||
return state.getPropertyAsValue (image, undoManager);
|
||||
}
|
||||
|
||||
void DrawableImage::ValueTreeWrapper::setImageIdentifier (const var& newIdentifier, UndoManager* undoManager)
|
||||
{
|
||||
state.setProperty (image, newIdentifier, undoManager);
|
||||
}
|
||||
|
||||
float DrawableImage::ValueTreeWrapper::getOpacity() const
|
||||
{
|
||||
return (float) state.getProperty (opacity, 1.0);
|
||||
}
|
||||
|
||||
Value DrawableImage::ValueTreeWrapper::getOpacityValue (UndoManager* undoManager)
|
||||
{
|
||||
if (! state.hasProperty (opacity))
|
||||
state.setProperty (opacity, 1.0, undoManager);
|
||||
|
||||
return state.getPropertyAsValue (opacity, undoManager);
|
||||
}
|
||||
|
||||
void DrawableImage::ValueTreeWrapper::setOpacity (float newOpacity, UndoManager* undoManager)
|
||||
{
|
||||
state.setProperty (opacity, newOpacity, undoManager);
|
||||
}
|
||||
|
||||
const Colour DrawableImage::ValueTreeWrapper::getOverlayColour() const
|
||||
{
|
||||
return Colour ((uint32) state [overlay].toString().getHexValue32());
|
||||
}
|
||||
|
||||
void DrawableImage::ValueTreeWrapper::setOverlayColour (const Colour& newColour, UndoManager* undoManager)
|
||||
{
|
||||
if (newColour.isTransparent())
|
||||
state.removeProperty (overlay, undoManager);
|
||||
else
|
||||
state.setProperty (overlay, String::toHexString ((int) newColour.getARGB()), undoManager);
|
||||
}
|
||||
|
||||
Value DrawableImage::ValueTreeWrapper::getOverlayColourValue (UndoManager* undoManager)
|
||||
{
|
||||
return state.getPropertyAsValue (overlay, undoManager);
|
||||
}
|
||||
|
||||
RelativeParallelogram DrawableImage::ValueTreeWrapper::getBoundingBox() const
|
||||
{
|
||||
return RelativeParallelogram (state.getProperty (topLeft, "0, 0"),
|
||||
state.getProperty (topRight, "100, 0"),
|
||||
state.getProperty (bottomLeft, "0, 100"));
|
||||
}
|
||||
|
||||
void DrawableImage::ValueTreeWrapper::setBoundingBox (const RelativeParallelogram& newBounds, UndoManager* undoManager)
|
||||
{
|
||||
state.setProperty (topLeft, newBounds.topLeft.toString(), undoManager);
|
||||
state.setProperty (topRight, newBounds.topRight.toString(), undoManager);
|
||||
state.setProperty (bottomLeft, newBounds.bottomLeft.toString(), undoManager);
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
void DrawableImage::refreshFromValueTree (const ValueTree& tree, ComponentBuilder& builder)
|
||||
{
|
||||
const ValueTreeWrapper controller (tree);
|
||||
setComponentID (controller.getID());
|
||||
|
||||
const float newOpacity = controller.getOpacity();
|
||||
const Colour newOverlayColour (controller.getOverlayColour());
|
||||
|
||||
Image newImage;
|
||||
const var imageIdentifier (controller.getImageIdentifier());
|
||||
|
||||
|
||||
jassert (builder.getImageProvider() != 0 || imageIdentifier.isVoid()); // if you're using images, you need to provide something that can load and save them!
|
||||
|
||||
if (builder.getImageProvider() != nullptr)
|
||||
newImage = builder.getImageProvider()->getImageForIdentifier (imageIdentifier);
|
||||
|
||||
const RelativeParallelogram newBounds (controller.getBoundingBox());
|
||||
|
||||
if (bounds != newBounds || newOpacity != opacity
|
||||
|| overlayColour != newOverlayColour || image != newImage)
|
||||
{
|
||||
repaint();
|
||||
opacity = newOpacity;
|
||||
overlayColour = newOverlayColour;
|
||||
|
||||
if (image != newImage)
|
||||
setImage (newImage);
|
||||
|
||||
setBoundingBox (newBounds);
|
||||
}
|
||||
}
|
||||
|
||||
ValueTree DrawableImage::createValueTree (ComponentBuilder::ImageProvider* imageProvider) const
|
||||
{
|
||||
ValueTree tree (valueTreeType);
|
||||
ValueTreeWrapper v (tree);
|
||||
|
||||
v.setID (getComponentID());
|
||||
v.setOpacity (opacity, nullptr);
|
||||
v.setOverlayColour (overlayColour, nullptr);
|
||||
v.setBoundingBox (bounds, nullptr);
|
||||
|
||||
if (image.isValid())
|
||||
{
|
||||
jassert (imageProvider != nullptr); // if you're using images, you need to provide something that can load and save them!
|
||||
|
||||
if (imageProvider != nullptr)
|
||||
v.setImageIdentifier (imageProvider->getIdentifierForImage (image), nullptr);
|
||||
}
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
142
modules/juce_gui_basics/drawables/juce_DrawableImage.h
Normal file
142
modules/juce_gui_basics/drawables/juce_DrawableImage.h
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_DRAWABLEIMAGE_JUCEHEADER__
|
||||
#define __JUCE_DRAWABLEIMAGE_JUCEHEADER__
|
||||
|
||||
#include "juce_Drawable.h"
|
||||
#include "../positioning/juce_RelativeParallelogram.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A drawable object which is a bitmap image.
|
||||
|
||||
@see Drawable
|
||||
*/
|
||||
class JUCE_API DrawableImage : public Drawable
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
DrawableImage();
|
||||
DrawableImage (const DrawableImage& other);
|
||||
|
||||
/** Destructor. */
|
||||
~DrawableImage();
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the image that this drawable will render. */
|
||||
void setImage (const Image& imageToUse);
|
||||
|
||||
/** Returns the current image. */
|
||||
const Image& getImage() const noexcept { return image; }
|
||||
|
||||
/** Sets the opacity to use when drawing the image. */
|
||||
void setOpacity (float newOpacity);
|
||||
|
||||
/** Returns the image's opacity. */
|
||||
float getOpacity() const noexcept { return opacity; }
|
||||
|
||||
/** Sets a colour to draw over the image's alpha channel.
|
||||
|
||||
By default this is transparent so isn't drawn, but if you set a non-transparent
|
||||
colour here, then it will be overlaid on the image, using the image's alpha
|
||||
channel as a mask.
|
||||
|
||||
This is handy for doing things like darkening or lightening an image by overlaying
|
||||
it with semi-transparent black or white.
|
||||
*/
|
||||
void setOverlayColour (const Colour& newOverlayColour);
|
||||
|
||||
/** Returns the overlay colour. */
|
||||
const Colour& getOverlayColour() const noexcept { return overlayColour; }
|
||||
|
||||
/** Sets the bounding box within which the image should be displayed. */
|
||||
void setBoundingBox (const RelativeParallelogram& newBounds);
|
||||
|
||||
/** Returns the position to which the image's top-left corner should be remapped in the target
|
||||
coordinate space when rendering this object.
|
||||
@see setTransform
|
||||
*/
|
||||
const RelativeParallelogram& getBoundingBox() const noexcept { return bounds; }
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void paint (Graphics& g);
|
||||
/** @internal */
|
||||
bool hitTest (int x, int y);
|
||||
/** @internal */
|
||||
Drawable* createCopy() const;
|
||||
/** @internal */
|
||||
Rectangle<float> getDrawableBounds() const;
|
||||
/** @internal */
|
||||
void refreshFromValueTree (const ValueTree& tree, ComponentBuilder& builder);
|
||||
/** @internal */
|
||||
ValueTree createValueTree (ComponentBuilder::ImageProvider* imageProvider) const;
|
||||
/** @internal */
|
||||
static const Identifier valueTreeType;
|
||||
|
||||
//==============================================================================
|
||||
/** Internally-used class for wrapping a DrawableImage's state into a ValueTree. */
|
||||
class ValueTreeWrapper : public Drawable::ValueTreeWrapperBase
|
||||
{
|
||||
public:
|
||||
ValueTreeWrapper (const ValueTree& state);
|
||||
|
||||
var getImageIdentifier() const;
|
||||
void setImageIdentifier (const var& newIdentifier, UndoManager* undoManager);
|
||||
Value getImageIdentifierValue (UndoManager* undoManager);
|
||||
|
||||
float getOpacity() const;
|
||||
void setOpacity (float newOpacity, UndoManager* undoManager);
|
||||
Value getOpacityValue (UndoManager* undoManager);
|
||||
|
||||
const Colour getOverlayColour() const;
|
||||
void setOverlayColour (const Colour& newColour, UndoManager* undoManager);
|
||||
Value getOverlayColourValue (UndoManager* undoManager);
|
||||
|
||||
RelativeParallelogram getBoundingBox() const;
|
||||
void setBoundingBox (const RelativeParallelogram& newBounds, UndoManager* undoManager);
|
||||
|
||||
static const Identifier opacity, overlay, image, topLeft, topRight, bottomLeft;
|
||||
};
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
Image image;
|
||||
float opacity;
|
||||
Colour overlayColour;
|
||||
RelativeParallelogram bounds;
|
||||
|
||||
friend class Drawable::Positioner<DrawableImage>;
|
||||
bool registerCoordinates (RelativeCoordinatePositionerBase&);
|
||||
void recalculateCoordinates (Expression::Scope*);
|
||||
|
||||
DrawableImage& operator= (const DrawableImage&);
|
||||
JUCE_LEAK_DETECTOR (DrawableImage);
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCE_DRAWABLEIMAGE_JUCEHEADER__
|
||||
577
modules/juce_gui_basics/drawables/juce_DrawablePath.cpp
Normal file
577
modules/juce_gui_basics/drawables/juce_DrawablePath.cpp
Normal file
|
|
@ -0,0 +1,577 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
//==============================================================================
|
||||
DrawablePath::DrawablePath()
|
||||
{
|
||||
}
|
||||
|
||||
DrawablePath::DrawablePath (const DrawablePath& other)
|
||||
: DrawableShape (other)
|
||||
{
|
||||
if (other.relativePath != nullptr)
|
||||
setPath (*other.relativePath);
|
||||
else
|
||||
setPath (other.path);
|
||||
}
|
||||
|
||||
DrawablePath::~DrawablePath()
|
||||
{
|
||||
}
|
||||
|
||||
Drawable* DrawablePath::createCopy() const
|
||||
{
|
||||
return new DrawablePath (*this);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void DrawablePath::setPath (const Path& newPath)
|
||||
{
|
||||
path = newPath;
|
||||
pathChanged();
|
||||
}
|
||||
|
||||
const Path& DrawablePath::getPath() const
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
const Path& DrawablePath::getStrokePath() const
|
||||
{
|
||||
return strokePath;
|
||||
}
|
||||
|
||||
void DrawablePath::applyRelativePath (const RelativePointPath& newRelativePath, Expression::Scope* scope)
|
||||
{
|
||||
Path newPath;
|
||||
newRelativePath.createPath (newPath, scope);
|
||||
|
||||
if (path != newPath)
|
||||
{
|
||||
path.swapWithPath (newPath);
|
||||
pathChanged();
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class DrawablePath::RelativePositioner : public RelativeCoordinatePositionerBase
|
||||
{
|
||||
public:
|
||||
RelativePositioner (DrawablePath& component_)
|
||||
: RelativeCoordinatePositionerBase (component_),
|
||||
owner (component_)
|
||||
{
|
||||
}
|
||||
|
||||
bool registerCoordinates()
|
||||
{
|
||||
bool ok = true;
|
||||
|
||||
jassert (owner.relativePath != nullptr);
|
||||
const RelativePointPath& path = *owner.relativePath;
|
||||
|
||||
for (int i = 0; i < path.elements.size(); ++i)
|
||||
{
|
||||
RelativePointPath::ElementBase* const e = path.elements.getUnchecked(i);
|
||||
|
||||
int numPoints;
|
||||
RelativePoint* const points = e->getControlPoints (numPoints);
|
||||
|
||||
for (int j = numPoints; --j >= 0;)
|
||||
ok = addPoint (points[j]) && ok;
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
void applyToComponentBounds()
|
||||
{
|
||||
jassert (owner.relativePath != nullptr);
|
||||
|
||||
ComponentScope scope (getComponent());
|
||||
owner.applyRelativePath (*owner.relativePath, &scope);
|
||||
}
|
||||
|
||||
void applyNewBounds (const Rectangle<int>&)
|
||||
{
|
||||
jassertfalse; // drawables can't be resized directly!
|
||||
}
|
||||
|
||||
private:
|
||||
DrawablePath& owner;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RelativePositioner);
|
||||
};
|
||||
|
||||
void DrawablePath::setPath (const RelativePointPath& newRelativePath)
|
||||
{
|
||||
if (newRelativePath.containsAnyDynamicPoints())
|
||||
{
|
||||
if (relativePath == nullptr || newRelativePath != *relativePath)
|
||||
{
|
||||
relativePath = new RelativePointPath (newRelativePath);
|
||||
|
||||
RelativePositioner* const p = new RelativePositioner (*this);
|
||||
setPositioner (p);
|
||||
p->apply();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
relativePath = nullptr;
|
||||
applyRelativePath (newRelativePath, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
const Identifier DrawablePath::valueTreeType ("Path");
|
||||
|
||||
const Identifier DrawablePath::ValueTreeWrapper::nonZeroWinding ("nonZeroWinding");
|
||||
const Identifier DrawablePath::ValueTreeWrapper::point1 ("p1");
|
||||
const Identifier DrawablePath::ValueTreeWrapper::point2 ("p2");
|
||||
const Identifier DrawablePath::ValueTreeWrapper::point3 ("p3");
|
||||
|
||||
//==============================================================================
|
||||
DrawablePath::ValueTreeWrapper::ValueTreeWrapper (const ValueTree& state_)
|
||||
: FillAndStrokeState (state_)
|
||||
{
|
||||
jassert (state.hasType (valueTreeType));
|
||||
}
|
||||
|
||||
ValueTree DrawablePath::ValueTreeWrapper::getPathState()
|
||||
{
|
||||
return state.getOrCreateChildWithName (path, nullptr);
|
||||
}
|
||||
|
||||
bool DrawablePath::ValueTreeWrapper::usesNonZeroWinding() const
|
||||
{
|
||||
return state [nonZeroWinding];
|
||||
}
|
||||
|
||||
void DrawablePath::ValueTreeWrapper::setUsesNonZeroWinding (bool b, UndoManager* undoManager)
|
||||
{
|
||||
state.setProperty (nonZeroWinding, b, undoManager);
|
||||
}
|
||||
|
||||
void DrawablePath::ValueTreeWrapper::readFrom (const RelativePointPath& relativePath, UndoManager* undoManager)
|
||||
{
|
||||
setUsesNonZeroWinding (relativePath.usesNonZeroWinding, undoManager);
|
||||
|
||||
ValueTree pathTree (getPathState());
|
||||
pathTree.removeAllChildren (undoManager);
|
||||
|
||||
for (int i = 0; i < relativePath.elements.size(); ++i)
|
||||
pathTree.addChild (relativePath.elements.getUnchecked(i)->createTree(), -1, undoManager);
|
||||
}
|
||||
|
||||
void DrawablePath::ValueTreeWrapper::writeTo (RelativePointPath& relativePath) const
|
||||
{
|
||||
relativePath.usesNonZeroWinding = usesNonZeroWinding();
|
||||
RelativePoint points[3];
|
||||
|
||||
const ValueTree pathTree (state.getChildWithName (path));
|
||||
const int num = pathTree.getNumChildren();
|
||||
for (int i = 0; i < num; ++i)
|
||||
{
|
||||
const Element e (pathTree.getChild(i));
|
||||
|
||||
const int numCps = e.getNumControlPoints();
|
||||
for (int j = 0; j < numCps; ++j)
|
||||
points[j] = e.getControlPoint (j);
|
||||
|
||||
RelativePointPath::ElementBase* newElement = 0;
|
||||
const Identifier t (e.getType());
|
||||
|
||||
if (t == Element::startSubPathElement) newElement = new RelativePointPath::StartSubPath (points[0]);
|
||||
else if (t == Element::closeSubPathElement) newElement = new RelativePointPath::CloseSubPath();
|
||||
else if (t == Element::lineToElement) newElement = new RelativePointPath::LineTo (points[0]);
|
||||
else if (t == Element::quadraticToElement) newElement = new RelativePointPath::QuadraticTo (points[0], points[1]);
|
||||
else if (t == Element::cubicToElement) newElement = new RelativePointPath::CubicTo (points[0], points[1], points[2]);
|
||||
else jassertfalse;
|
||||
|
||||
relativePath.addElement (newElement);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
const Identifier DrawablePath::ValueTreeWrapper::Element::mode ("mode");
|
||||
const Identifier DrawablePath::ValueTreeWrapper::Element::startSubPathElement ("Move");
|
||||
const Identifier DrawablePath::ValueTreeWrapper::Element::closeSubPathElement ("Close");
|
||||
const Identifier DrawablePath::ValueTreeWrapper::Element::lineToElement ("Line");
|
||||
const Identifier DrawablePath::ValueTreeWrapper::Element::quadraticToElement ("Quad");
|
||||
const Identifier DrawablePath::ValueTreeWrapper::Element::cubicToElement ("Cubic");
|
||||
|
||||
const char* DrawablePath::ValueTreeWrapper::Element::cornerMode = "corner";
|
||||
const char* DrawablePath::ValueTreeWrapper::Element::roundedMode = "round";
|
||||
const char* DrawablePath::ValueTreeWrapper::Element::symmetricMode = "symm";
|
||||
|
||||
DrawablePath::ValueTreeWrapper::Element::Element (const ValueTree& state_)
|
||||
: state (state_)
|
||||
{
|
||||
}
|
||||
|
||||
DrawablePath::ValueTreeWrapper::Element::~Element()
|
||||
{
|
||||
}
|
||||
|
||||
DrawablePath::ValueTreeWrapper DrawablePath::ValueTreeWrapper::Element::getParent() const
|
||||
{
|
||||
return ValueTreeWrapper (state.getParent().getParent());
|
||||
}
|
||||
|
||||
DrawablePath::ValueTreeWrapper::Element DrawablePath::ValueTreeWrapper::Element::getPreviousElement() const
|
||||
{
|
||||
return Element (state.getSibling (-1));
|
||||
}
|
||||
|
||||
int DrawablePath::ValueTreeWrapper::Element::getNumControlPoints() const noexcept
|
||||
{
|
||||
const Identifier i (state.getType());
|
||||
if (i == startSubPathElement || i == lineToElement) return 1;
|
||||
if (i == quadraticToElement) return 2;
|
||||
if (i == cubicToElement) return 3;
|
||||
return 0;
|
||||
}
|
||||
|
||||
RelativePoint DrawablePath::ValueTreeWrapper::Element::getControlPoint (const int index) const
|
||||
{
|
||||
jassert (index >= 0 && index < getNumControlPoints());
|
||||
return RelativePoint (state [index == 0 ? point1 : (index == 1 ? point2 : point3)].toString());
|
||||
}
|
||||
|
||||
Value DrawablePath::ValueTreeWrapper::Element::getControlPointValue (int index, UndoManager* undoManager) const
|
||||
{
|
||||
jassert (index >= 0 && index < getNumControlPoints());
|
||||
return state.getPropertyAsValue (index == 0 ? point1 : (index == 1 ? point2 : point3), undoManager);
|
||||
}
|
||||
|
||||
void DrawablePath::ValueTreeWrapper::Element::setControlPoint (const int index, const RelativePoint& point, UndoManager* undoManager)
|
||||
{
|
||||
jassert (index >= 0 && index < getNumControlPoints());
|
||||
state.setProperty (index == 0 ? point1 : (index == 1 ? point2 : point3), point.toString(), undoManager);
|
||||
}
|
||||
|
||||
RelativePoint DrawablePath::ValueTreeWrapper::Element::getStartPoint() const
|
||||
{
|
||||
const Identifier i (state.getType());
|
||||
|
||||
if (i == startSubPathElement)
|
||||
return getControlPoint (0);
|
||||
|
||||
jassert (i == lineToElement || i == quadraticToElement || i == cubicToElement || i == closeSubPathElement);
|
||||
|
||||
return getPreviousElement().getEndPoint();
|
||||
}
|
||||
|
||||
RelativePoint DrawablePath::ValueTreeWrapper::Element::getEndPoint() const
|
||||
{
|
||||
const Identifier i (state.getType());
|
||||
if (i == startSubPathElement || i == lineToElement) return getControlPoint (0);
|
||||
if (i == quadraticToElement) return getControlPoint (1);
|
||||
if (i == cubicToElement) return getControlPoint (2);
|
||||
|
||||
jassert (i == closeSubPathElement);
|
||||
return RelativePoint();
|
||||
}
|
||||
|
||||
float DrawablePath::ValueTreeWrapper::Element::getLength (Expression::Scope* scope) const
|
||||
{
|
||||
const Identifier i (state.getType());
|
||||
|
||||
if (i == lineToElement || i == closeSubPathElement)
|
||||
return getEndPoint().resolve (scope).getDistanceFrom (getStartPoint().resolve (scope));
|
||||
|
||||
if (i == cubicToElement)
|
||||
{
|
||||
Path p;
|
||||
p.startNewSubPath (getStartPoint().resolve (scope));
|
||||
p.cubicTo (getControlPoint (0).resolve (scope), getControlPoint (1).resolve (scope), getControlPoint (2).resolve (scope));
|
||||
return p.getLength();
|
||||
}
|
||||
|
||||
if (i == quadraticToElement)
|
||||
{
|
||||
Path p;
|
||||
p.startNewSubPath (getStartPoint().resolve (scope));
|
||||
p.quadraticTo (getControlPoint (0).resolve (scope), getControlPoint (1).resolve (scope));
|
||||
return p.getLength();
|
||||
}
|
||||
|
||||
jassert (i == startSubPathElement);
|
||||
return 0;
|
||||
}
|
||||
|
||||
String DrawablePath::ValueTreeWrapper::Element::getModeOfEndPoint() const
|
||||
{
|
||||
return state [mode].toString();
|
||||
}
|
||||
|
||||
void DrawablePath::ValueTreeWrapper::Element::setModeOfEndPoint (const String& newMode, UndoManager* undoManager)
|
||||
{
|
||||
if (state.hasType (cubicToElement))
|
||||
state.setProperty (mode, newMode, undoManager);
|
||||
}
|
||||
|
||||
void DrawablePath::ValueTreeWrapper::Element::convertToLine (UndoManager* undoManager)
|
||||
{
|
||||
const Identifier i (state.getType());
|
||||
|
||||
if (i == quadraticToElement || i == cubicToElement)
|
||||
{
|
||||
ValueTree newState (lineToElement);
|
||||
Element e (newState);
|
||||
e.setControlPoint (0, getEndPoint(), undoManager);
|
||||
state = newState;
|
||||
}
|
||||
}
|
||||
|
||||
void DrawablePath::ValueTreeWrapper::Element::convertToCubic (Expression::Scope* scope, UndoManager* undoManager)
|
||||
{
|
||||
const Identifier i (state.getType());
|
||||
|
||||
if (i == lineToElement || i == quadraticToElement)
|
||||
{
|
||||
ValueTree newState (cubicToElement);
|
||||
Element e (newState);
|
||||
|
||||
const RelativePoint start (getStartPoint());
|
||||
const RelativePoint end (getEndPoint());
|
||||
const Point<float> startResolved (start.resolve (scope));
|
||||
const Point<float> endResolved (end.resolve (scope));
|
||||
e.setControlPoint (0, startResolved + (endResolved - startResolved) * 0.3f, undoManager);
|
||||
e.setControlPoint (1, startResolved + (endResolved - startResolved) * 0.7f, undoManager);
|
||||
e.setControlPoint (2, end, undoManager);
|
||||
|
||||
state = newState;
|
||||
}
|
||||
}
|
||||
|
||||
void DrawablePath::ValueTreeWrapper::Element::convertToPathBreak (UndoManager* undoManager)
|
||||
{
|
||||
const Identifier i (state.getType());
|
||||
|
||||
if (i != startSubPathElement)
|
||||
{
|
||||
ValueTree newState (startSubPathElement);
|
||||
Element e (newState);
|
||||
e.setControlPoint (0, getEndPoint(), undoManager);
|
||||
state = newState;
|
||||
}
|
||||
}
|
||||
|
||||
namespace DrawablePathHelpers
|
||||
{
|
||||
Point<float> findCubicSubdivisionPoint (float proportion, const Point<float> points[4])
|
||||
{
|
||||
const Point<float> mid1 (points[0] + (points[1] - points[0]) * proportion),
|
||||
mid2 (points[1] + (points[2] - points[1]) * proportion),
|
||||
mid3 (points[2] + (points[3] - points[2]) * proportion);
|
||||
|
||||
const Point<float> newCp1 (mid1 + (mid2 - mid1) * proportion),
|
||||
newCp2 (mid2 + (mid3 - mid2) * proportion);
|
||||
|
||||
return newCp1 + (newCp2 - newCp1) * proportion;
|
||||
}
|
||||
|
||||
Point<float> findQuadraticSubdivisionPoint (float proportion, const Point<float> points[3])
|
||||
{
|
||||
const Point<float> mid1 (points[0] + (points[1] - points[0]) * proportion),
|
||||
mid2 (points[1] + (points[2] - points[1]) * proportion);
|
||||
|
||||
return mid1 + (mid2 - mid1) * proportion;
|
||||
}
|
||||
}
|
||||
|
||||
float DrawablePath::ValueTreeWrapper::Element::findProportionAlongLine (const Point<float>& targetPoint, Expression::Scope* scope) const
|
||||
{
|
||||
using namespace DrawablePathHelpers;
|
||||
const Identifier type (state.getType());
|
||||
float bestProp = 0;
|
||||
|
||||
if (type == cubicToElement)
|
||||
{
|
||||
RelativePoint rp1 (getStartPoint()), rp2 (getControlPoint (0)), rp3 (getControlPoint (1)), rp4 (getEndPoint());
|
||||
|
||||
const Point<float> points[] = { rp1.resolve (scope), rp2.resolve (scope), rp3.resolve (scope), rp4.resolve (scope) };
|
||||
|
||||
float bestDistance = std::numeric_limits<float>::max();
|
||||
|
||||
for (int i = 110; --i >= 0;)
|
||||
{
|
||||
float prop = i > 10 ? ((i - 10) / 100.0f) : (bestProp + ((i - 5) / 1000.0f));
|
||||
const Point<float> centre (findCubicSubdivisionPoint (prop, points));
|
||||
const float distance = centre.getDistanceFrom (targetPoint);
|
||||
|
||||
if (distance < bestDistance)
|
||||
{
|
||||
bestProp = prop;
|
||||
bestDistance = distance;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (type == quadraticToElement)
|
||||
{
|
||||
RelativePoint rp1 (getStartPoint()), rp2 (getControlPoint (0)), rp3 (getEndPoint());
|
||||
const Point<float> points[] = { rp1.resolve (scope), rp2.resolve (scope), rp3.resolve (scope) };
|
||||
|
||||
float bestDistance = std::numeric_limits<float>::max();
|
||||
|
||||
for (int i = 110; --i >= 0;)
|
||||
{
|
||||
float prop = i > 10 ? ((i - 10) / 100.0f) : (bestProp + ((i - 5) / 1000.0f));
|
||||
const Point<float> centre (findQuadraticSubdivisionPoint ((float) prop, points));
|
||||
const float distance = centre.getDistanceFrom (targetPoint);
|
||||
|
||||
if (distance < bestDistance)
|
||||
{
|
||||
bestProp = prop;
|
||||
bestDistance = distance;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (type == lineToElement)
|
||||
{
|
||||
RelativePoint rp1 (getStartPoint()), rp2 (getEndPoint());
|
||||
const Line<float> line (rp1.resolve (scope), rp2.resolve (scope));
|
||||
bestProp = line.findNearestProportionalPositionTo (targetPoint);
|
||||
}
|
||||
|
||||
return bestProp;
|
||||
}
|
||||
|
||||
ValueTree DrawablePath::ValueTreeWrapper::Element::insertPoint (const Point<float>& targetPoint, Expression::Scope* scope, UndoManager* undoManager)
|
||||
{
|
||||
ValueTree newTree;
|
||||
const Identifier type (state.getType());
|
||||
|
||||
if (type == cubicToElement)
|
||||
{
|
||||
float bestProp = findProportionAlongLine (targetPoint, scope);
|
||||
|
||||
RelativePoint rp1 (getStartPoint()), rp2 (getControlPoint (0)), rp3 (getControlPoint (1)), rp4 (getEndPoint());
|
||||
const Point<float> points[] = { rp1.resolve (scope), rp2.resolve (scope), rp3.resolve (scope), rp4.resolve (scope) };
|
||||
|
||||
const Point<float> mid1 (points[0] + (points[1] - points[0]) * bestProp),
|
||||
mid2 (points[1] + (points[2] - points[1]) * bestProp),
|
||||
mid3 (points[2] + (points[3] - points[2]) * bestProp);
|
||||
|
||||
const Point<float> newCp1 (mid1 + (mid2 - mid1) * bestProp),
|
||||
newCp2 (mid2 + (mid3 - mid2) * bestProp);
|
||||
|
||||
const Point<float> newCentre (newCp1 + (newCp2 - newCp1) * bestProp);
|
||||
|
||||
setControlPoint (0, mid1, undoManager);
|
||||
setControlPoint (1, newCp1, undoManager);
|
||||
setControlPoint (2, newCentre, undoManager);
|
||||
setModeOfEndPoint (roundedMode, undoManager);
|
||||
|
||||
Element newElement (newTree = ValueTree (cubicToElement));
|
||||
newElement.setControlPoint (0, newCp2, nullptr);
|
||||
newElement.setControlPoint (1, mid3, nullptr);
|
||||
newElement.setControlPoint (2, rp4, nullptr);
|
||||
|
||||
state.getParent().addChild (newTree, state.getParent().indexOf (state) + 1, undoManager);
|
||||
}
|
||||
else if (type == quadraticToElement)
|
||||
{
|
||||
float bestProp = findProportionAlongLine (targetPoint, scope);
|
||||
|
||||
RelativePoint rp1 (getStartPoint()), rp2 (getControlPoint (0)), rp3 (getEndPoint());
|
||||
const Point<float> points[] = { rp1.resolve (scope), rp2.resolve (scope), rp3.resolve (scope) };
|
||||
|
||||
const Point<float> mid1 (points[0] + (points[1] - points[0]) * bestProp),
|
||||
mid2 (points[1] + (points[2] - points[1]) * bestProp);
|
||||
|
||||
const Point<float> newCentre (mid1 + (mid2 - mid1) * bestProp);
|
||||
|
||||
setControlPoint (0, mid1, undoManager);
|
||||
setControlPoint (1, newCentre, undoManager);
|
||||
setModeOfEndPoint (roundedMode, undoManager);
|
||||
|
||||
Element newElement (newTree = ValueTree (quadraticToElement));
|
||||
newElement.setControlPoint (0, mid2, nullptr);
|
||||
newElement.setControlPoint (1, rp3, nullptr);
|
||||
|
||||
state.getParent().addChild (newTree, state.getParent().indexOf (state) + 1, undoManager);
|
||||
}
|
||||
else if (type == lineToElement)
|
||||
{
|
||||
RelativePoint rp1 (getStartPoint()), rp2 (getEndPoint());
|
||||
const Line<float> line (rp1.resolve (scope), rp2.resolve (scope));
|
||||
const Point<float> newPoint (line.findNearestPointTo (targetPoint));
|
||||
|
||||
setControlPoint (0, newPoint, undoManager);
|
||||
|
||||
Element newElement (newTree = ValueTree (lineToElement));
|
||||
newElement.setControlPoint (0, rp2, nullptr);
|
||||
|
||||
state.getParent().addChild (newTree, state.getParent().indexOf (state) + 1, undoManager);
|
||||
}
|
||||
else if (type == closeSubPathElement)
|
||||
{
|
||||
}
|
||||
|
||||
return newTree;
|
||||
}
|
||||
|
||||
void DrawablePath::ValueTreeWrapper::Element::removePoint (UndoManager* undoManager)
|
||||
{
|
||||
state.getParent().removeChild (state, undoManager);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void DrawablePath::refreshFromValueTree (const ValueTree& tree, ComponentBuilder& builder)
|
||||
{
|
||||
ValueTreeWrapper v (tree);
|
||||
setComponentID (v.getID());
|
||||
|
||||
refreshFillTypes (v, builder.getImageProvider());
|
||||
setStrokeType (v.getStrokeType());
|
||||
|
||||
RelativePointPath newRelativePath;
|
||||
v.writeTo (newRelativePath);
|
||||
setPath (newRelativePath);
|
||||
}
|
||||
|
||||
ValueTree DrawablePath::createValueTree (ComponentBuilder::ImageProvider* imageProvider) const
|
||||
{
|
||||
ValueTree tree (valueTreeType);
|
||||
ValueTreeWrapper v (tree);
|
||||
|
||||
v.setID (getComponentID());
|
||||
writeTo (v, imageProvider, nullptr);
|
||||
|
||||
if (relativePath != nullptr)
|
||||
v.readFrom (*relativePath, nullptr);
|
||||
else
|
||||
v.readFrom (RelativePointPath (path), nullptr);
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
149
modules/juce_gui_basics/drawables/juce_DrawablePath.h
Normal file
149
modules/juce_gui_basics/drawables/juce_DrawablePath.h
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_DRAWABLEPATH_JUCEHEADER__
|
||||
#define __JUCE_DRAWABLEPATH_JUCEHEADER__
|
||||
|
||||
#include "juce_DrawableShape.h"
|
||||
#include "../positioning/juce_RelativePointPath.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A drawable object which renders a filled or outlined shape.
|
||||
|
||||
For details on how to change the fill and stroke, see the DrawableShape class.
|
||||
|
||||
@see Drawable, DrawableShape
|
||||
*/
|
||||
class JUCE_API DrawablePath : public DrawableShape
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a DrawablePath. */
|
||||
DrawablePath();
|
||||
DrawablePath (const DrawablePath& other);
|
||||
|
||||
/** Destructor. */
|
||||
~DrawablePath();
|
||||
|
||||
//==============================================================================
|
||||
/** Changes the path that will be drawn.
|
||||
@see setFillColour, setStrokeType
|
||||
*/
|
||||
void setPath (const Path& newPath);
|
||||
|
||||
/** Sets the path using a RelativePointPath.
|
||||
Calling this will set up a Component::Positioner to automatically update the path
|
||||
if any of the points in the source path are dynamic.
|
||||
*/
|
||||
void setPath (const RelativePointPath& newPath);
|
||||
|
||||
/** Returns the current path. */
|
||||
const Path& getPath() const;
|
||||
|
||||
/** Returns the current path for the outline. */
|
||||
const Path& getStrokePath() const;
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
Drawable* createCopy() const;
|
||||
/** @internal */
|
||||
void refreshFromValueTree (const ValueTree& tree, ComponentBuilder& builder);
|
||||
/** @internal */
|
||||
ValueTree createValueTree (ComponentBuilder::ImageProvider* imageProvider) const;
|
||||
/** @internal */
|
||||
static const Identifier valueTreeType;
|
||||
|
||||
//==============================================================================
|
||||
/** Internally-used class for wrapping a DrawablePath's state into a ValueTree. */
|
||||
class ValueTreeWrapper : public DrawableShape::FillAndStrokeState
|
||||
{
|
||||
public:
|
||||
ValueTreeWrapper (const ValueTree& state);
|
||||
|
||||
bool usesNonZeroWinding() const;
|
||||
void setUsesNonZeroWinding (bool b, UndoManager* undoManager);
|
||||
|
||||
class Element
|
||||
{
|
||||
public:
|
||||
explicit Element (const ValueTree& state);
|
||||
~Element();
|
||||
|
||||
const Identifier getType() const noexcept { return state.getType(); }
|
||||
int getNumControlPoints() const noexcept;
|
||||
|
||||
RelativePoint getControlPoint (int index) const;
|
||||
Value getControlPointValue (int index, UndoManager*) const;
|
||||
RelativePoint getStartPoint() const;
|
||||
RelativePoint getEndPoint() const;
|
||||
void setControlPoint (int index, const RelativePoint& point, UndoManager*);
|
||||
float getLength (Expression::Scope*) const;
|
||||
|
||||
ValueTreeWrapper getParent() const;
|
||||
Element getPreviousElement() const;
|
||||
|
||||
String getModeOfEndPoint() const;
|
||||
void setModeOfEndPoint (const String& newMode, UndoManager*);
|
||||
|
||||
void convertToLine (UndoManager*);
|
||||
void convertToCubic (Expression::Scope*, UndoManager*);
|
||||
void convertToPathBreak (UndoManager* undoManager);
|
||||
ValueTree insertPoint (const Point<float>& targetPoint, Expression::Scope*, UndoManager*);
|
||||
void removePoint (UndoManager* undoManager);
|
||||
float findProportionAlongLine (const Point<float>& targetPoint, Expression::Scope*) const;
|
||||
|
||||
static const Identifier mode, startSubPathElement, closeSubPathElement,
|
||||
lineToElement, quadraticToElement, cubicToElement;
|
||||
static const char* cornerMode;
|
||||
static const char* roundedMode;
|
||||
static const char* symmetricMode;
|
||||
|
||||
ValueTree state;
|
||||
};
|
||||
|
||||
ValueTree getPathState();
|
||||
|
||||
void readFrom (const RelativePointPath& path, UndoManager* undoManager);
|
||||
void writeTo (RelativePointPath& path) const;
|
||||
|
||||
static const Identifier nonZeroWinding, point1, point2, point3;
|
||||
};
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
ScopedPointer<RelativePointPath> relativePath;
|
||||
|
||||
class RelativePositioner;
|
||||
friend class RelativePositioner;
|
||||
void applyRelativePath (const RelativePointPath&, Expression::Scope*);
|
||||
|
||||
DrawablePath& operator= (const DrawablePath&);
|
||||
JUCE_LEAK_DETECTOR (DrawablePath);
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCE_DRAWABLEPATH_JUCEHEADER__
|
||||
188
modules/juce_gui_basics/drawables/juce_DrawableRectangle.cpp
Normal file
188
modules/juce_gui_basics/drawables/juce_DrawableRectangle.cpp
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
//==============================================================================
|
||||
DrawableRectangle::DrawableRectangle()
|
||||
{
|
||||
}
|
||||
|
||||
DrawableRectangle::DrawableRectangle (const DrawableRectangle& other)
|
||||
: DrawableShape (other),
|
||||
bounds (other.bounds),
|
||||
cornerSize (other.cornerSize)
|
||||
{
|
||||
}
|
||||
|
||||
DrawableRectangle::~DrawableRectangle()
|
||||
{
|
||||
}
|
||||
|
||||
Drawable* DrawableRectangle::createCopy() const
|
||||
{
|
||||
return new DrawableRectangle (*this);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void DrawableRectangle::setRectangle (const RelativeParallelogram& newBounds)
|
||||
{
|
||||
if (bounds != newBounds)
|
||||
{
|
||||
bounds = newBounds;
|
||||
rebuildPath();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawableRectangle::setCornerSize (const RelativePoint& newSize)
|
||||
{
|
||||
if (cornerSize != newSize)
|
||||
{
|
||||
cornerSize = newSize;
|
||||
rebuildPath();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawableRectangle::rebuildPath()
|
||||
{
|
||||
if (bounds.isDynamic() || cornerSize.isDynamic())
|
||||
{
|
||||
Drawable::Positioner<DrawableRectangle>* const p = new Drawable::Positioner<DrawableRectangle> (*this);
|
||||
setPositioner (p);
|
||||
p->apply();
|
||||
}
|
||||
else
|
||||
{
|
||||
setPositioner (0);
|
||||
recalculateCoordinates (0);
|
||||
}
|
||||
}
|
||||
|
||||
bool DrawableRectangle::registerCoordinates (RelativeCoordinatePositionerBase& pos)
|
||||
{
|
||||
bool ok = pos.addPoint (bounds.topLeft);
|
||||
ok = pos.addPoint (bounds.topRight) && ok;
|
||||
ok = pos.addPoint (bounds.bottomLeft) && ok;
|
||||
return pos.addPoint (cornerSize) && ok;
|
||||
}
|
||||
|
||||
void DrawableRectangle::recalculateCoordinates (Expression::Scope* scope)
|
||||
{
|
||||
Point<float> points[3];
|
||||
bounds.resolveThreePoints (points, scope);
|
||||
|
||||
const float cornerSizeX = (float) cornerSize.x.resolve (scope);
|
||||
const float cornerSizeY = (float) cornerSize.y.resolve (scope);
|
||||
|
||||
const float w = Line<float> (points[0], points[1]).getLength();
|
||||
const float h = Line<float> (points[0], points[2]).getLength();
|
||||
|
||||
Path newPath;
|
||||
|
||||
if (cornerSizeX > 0 && cornerSizeY > 0)
|
||||
newPath.addRoundedRectangle (0, 0, w, h, cornerSizeX, cornerSizeY);
|
||||
else
|
||||
newPath.addRectangle (0, 0, w, h);
|
||||
|
||||
newPath.applyTransform (AffineTransform::fromTargetPoints (0, 0, points[0].getX(), points[0].getY(),
|
||||
w, 0, points[1].getX(), points[1].getY(),
|
||||
0, h, points[2].getX(), points[2].getY()));
|
||||
|
||||
if (path != newPath)
|
||||
{
|
||||
path.swapWithPath (newPath);
|
||||
pathChanged();
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
const Identifier DrawableRectangle::valueTreeType ("Rectangle");
|
||||
const Identifier DrawableRectangle::ValueTreeWrapper::topLeft ("topLeft");
|
||||
const Identifier DrawableRectangle::ValueTreeWrapper::topRight ("topRight");
|
||||
const Identifier DrawableRectangle::ValueTreeWrapper::bottomLeft ("bottomLeft");
|
||||
const Identifier DrawableRectangle::ValueTreeWrapper::cornerSize ("cornerSize");
|
||||
|
||||
//==============================================================================
|
||||
DrawableRectangle::ValueTreeWrapper::ValueTreeWrapper (const ValueTree& state_)
|
||||
: FillAndStrokeState (state_)
|
||||
{
|
||||
jassert (state.hasType (valueTreeType));
|
||||
}
|
||||
|
||||
RelativeParallelogram DrawableRectangle::ValueTreeWrapper::getRectangle() const
|
||||
{
|
||||
return RelativeParallelogram (state.getProperty (topLeft, "0, 0"),
|
||||
state.getProperty (topRight, "100, 0"),
|
||||
state.getProperty (bottomLeft, "0, 100"));
|
||||
}
|
||||
|
||||
void DrawableRectangle::ValueTreeWrapper::setRectangle (const RelativeParallelogram& newBounds, UndoManager* undoManager)
|
||||
{
|
||||
state.setProperty (topLeft, newBounds.topLeft.toString(), undoManager);
|
||||
state.setProperty (topRight, newBounds.topRight.toString(), undoManager);
|
||||
state.setProperty (bottomLeft, newBounds.bottomLeft.toString(), undoManager);
|
||||
}
|
||||
|
||||
void DrawableRectangle::ValueTreeWrapper::setCornerSize (const RelativePoint& newSize, UndoManager* undoManager)
|
||||
{
|
||||
state.setProperty (cornerSize, newSize.toString(), undoManager);
|
||||
}
|
||||
|
||||
RelativePoint DrawableRectangle::ValueTreeWrapper::getCornerSize() const
|
||||
{
|
||||
return RelativePoint (state [cornerSize]);
|
||||
}
|
||||
|
||||
Value DrawableRectangle::ValueTreeWrapper::getCornerSizeValue (UndoManager* undoManager) const
|
||||
{
|
||||
return state.getPropertyAsValue (cornerSize, undoManager);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void DrawableRectangle::refreshFromValueTree (const ValueTree& tree, ComponentBuilder& builder)
|
||||
{
|
||||
ValueTreeWrapper v (tree);
|
||||
setComponentID (v.getID());
|
||||
|
||||
refreshFillTypes (v, builder.getImageProvider());
|
||||
setStrokeType (v.getStrokeType());
|
||||
setRectangle (v.getRectangle());
|
||||
setCornerSize (v.getCornerSize());
|
||||
}
|
||||
|
||||
ValueTree DrawableRectangle::createValueTree (ComponentBuilder::ImageProvider* imageProvider) const
|
||||
{
|
||||
ValueTree tree (valueTreeType);
|
||||
ValueTreeWrapper v (tree);
|
||||
|
||||
v.setID (getComponentID());
|
||||
writeTo (v, imageProvider, nullptr);
|
||||
v.setRectangle (bounds, nullptr);
|
||||
v.setCornerSize (cornerSize, nullptr);
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
107
modules/juce_gui_basics/drawables/juce_DrawableRectangle.h
Normal file
107
modules/juce_gui_basics/drawables/juce_DrawableRectangle.h
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_DRAWABLERECTANGLE_JUCEHEADER__
|
||||
#define __JUCE_DRAWABLERECTANGLE_JUCEHEADER__
|
||||
|
||||
#include "juce_DrawableShape.h"
|
||||
#include "../positioning/juce_RelativeParallelogram.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A Drawable object which draws a rectangle.
|
||||
|
||||
For details on how to change the fill and stroke, see the DrawableShape class.
|
||||
|
||||
@see Drawable, DrawableShape
|
||||
*/
|
||||
class JUCE_API DrawableRectangle : public DrawableShape
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
DrawableRectangle();
|
||||
DrawableRectangle (const DrawableRectangle& other);
|
||||
|
||||
/** Destructor. */
|
||||
~DrawableRectangle();
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the rectangle's bounds. */
|
||||
void setRectangle (const RelativeParallelogram& newBounds);
|
||||
|
||||
/** Returns the rectangle's bounds. */
|
||||
const RelativeParallelogram& getRectangle() const noexcept { return bounds; }
|
||||
|
||||
/** Returns the corner size to be used. */
|
||||
const RelativePoint& getCornerSize() const noexcept { return cornerSize; }
|
||||
|
||||
/** Sets a new corner size for the rectangle */
|
||||
void setCornerSize (const RelativePoint& newSize);
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
Drawable* createCopy() const;
|
||||
/** @internal */
|
||||
void refreshFromValueTree (const ValueTree& tree, ComponentBuilder& builder);
|
||||
/** @internal */
|
||||
ValueTree createValueTree (ComponentBuilder::ImageProvider* imageProvider) const;
|
||||
/** @internal */
|
||||
static const Identifier valueTreeType;
|
||||
|
||||
//==============================================================================
|
||||
/** Internally-used class for wrapping a DrawableRectangle's state into a ValueTree. */
|
||||
class ValueTreeWrapper : public DrawableShape::FillAndStrokeState
|
||||
{
|
||||
public:
|
||||
ValueTreeWrapper (const ValueTree& state);
|
||||
|
||||
RelativeParallelogram getRectangle() const;
|
||||
void setRectangle (const RelativeParallelogram& newBounds, UndoManager*);
|
||||
|
||||
void setCornerSize (const RelativePoint& cornerSize, UndoManager*);
|
||||
RelativePoint getCornerSize() const;
|
||||
Value getCornerSizeValue (UndoManager*) const;
|
||||
|
||||
static const Identifier topLeft, topRight, bottomLeft, cornerSize;
|
||||
};
|
||||
|
||||
|
||||
private:
|
||||
friend class Drawable::Positioner<DrawableRectangle>;
|
||||
|
||||
RelativeParallelogram bounds;
|
||||
RelativePoint cornerSize;
|
||||
|
||||
void rebuildPath();
|
||||
bool registerCoordinates (RelativeCoordinatePositionerBase&);
|
||||
void recalculateCoordinates (Expression::Scope*);
|
||||
|
||||
DrawableRectangle& operator= (const DrawableRectangle&);
|
||||
JUCE_LEAK_DETECTOR (DrawableRectangle);
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCE_DRAWABLERECTANGLE_JUCEHEADER__
|
||||
459
modules/juce_gui_basics/drawables/juce_DrawableShape.cpp
Normal file
459
modules/juce_gui_basics/drawables/juce_DrawableShape.cpp
Normal file
|
|
@ -0,0 +1,459 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
//==============================================================================
|
||||
DrawableShape::DrawableShape()
|
||||
: strokeType (0.0f),
|
||||
mainFill (Colours::black),
|
||||
strokeFill (Colours::black)
|
||||
{
|
||||
}
|
||||
|
||||
DrawableShape::DrawableShape (const DrawableShape& other)
|
||||
: strokeType (other.strokeType),
|
||||
mainFill (other.mainFill),
|
||||
strokeFill (other.strokeFill)
|
||||
{
|
||||
}
|
||||
|
||||
DrawableShape::~DrawableShape()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class DrawableShape::RelativePositioner : public RelativeCoordinatePositionerBase
|
||||
{
|
||||
public:
|
||||
RelativePositioner (DrawableShape& component_, const DrawableShape::RelativeFillType& fill_, bool isMainFill_)
|
||||
: RelativeCoordinatePositionerBase (component_),
|
||||
owner (component_),
|
||||
fill (fill_),
|
||||
isMainFill (isMainFill_)
|
||||
{
|
||||
}
|
||||
|
||||
bool registerCoordinates()
|
||||
{
|
||||
bool ok = addPoint (fill.gradientPoint1);
|
||||
ok = addPoint (fill.gradientPoint2) && ok;
|
||||
return addPoint (fill.gradientPoint3) && ok;
|
||||
}
|
||||
|
||||
void applyToComponentBounds()
|
||||
{
|
||||
ComponentScope scope (owner);
|
||||
if (isMainFill ? owner.mainFill.recalculateCoords (&scope)
|
||||
: owner.strokeFill.recalculateCoords (&scope))
|
||||
owner.repaint();
|
||||
}
|
||||
|
||||
void applyNewBounds (const Rectangle<int>&)
|
||||
{
|
||||
jassertfalse; // drawables can't be resized directly!
|
||||
}
|
||||
|
||||
private:
|
||||
DrawableShape& owner;
|
||||
const DrawableShape::RelativeFillType fill;
|
||||
const bool isMainFill;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RelativePositioner);
|
||||
};
|
||||
|
||||
void DrawableShape::setFill (const FillType& newFill)
|
||||
{
|
||||
setFill (RelativeFillType (newFill));
|
||||
}
|
||||
|
||||
void DrawableShape::setStrokeFill (const FillType& newFill)
|
||||
{
|
||||
setStrokeFill (RelativeFillType (newFill));
|
||||
}
|
||||
|
||||
void DrawableShape::setFillInternal (RelativeFillType& fill, const RelativeFillType& newFill,
|
||||
ScopedPointer<RelativeCoordinatePositionerBase>& pos)
|
||||
{
|
||||
if (fill != newFill)
|
||||
{
|
||||
fill = newFill;
|
||||
pos = nullptr;
|
||||
|
||||
if (fill.isDynamic())
|
||||
{
|
||||
pos = new RelativePositioner (*this, fill, true);
|
||||
pos->apply();
|
||||
}
|
||||
else
|
||||
{
|
||||
fill.recalculateCoords (nullptr);
|
||||
}
|
||||
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawableShape::setFill (const RelativeFillType& newFill)
|
||||
{
|
||||
setFillInternal (mainFill, newFill, mainFillPositioner);
|
||||
}
|
||||
|
||||
void DrawableShape::setStrokeFill (const RelativeFillType& newFill)
|
||||
{
|
||||
setFillInternal (strokeFill, newFill, strokeFillPositioner);
|
||||
}
|
||||
|
||||
void DrawableShape::setStrokeType (const PathStrokeType& newStrokeType)
|
||||
{
|
||||
if (strokeType != newStrokeType)
|
||||
{
|
||||
strokeType = newStrokeType;
|
||||
strokeChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawableShape::setStrokeThickness (const float newThickness)
|
||||
{
|
||||
setStrokeType (PathStrokeType (newThickness, strokeType.getJointStyle(), strokeType.getEndStyle()));
|
||||
}
|
||||
|
||||
bool DrawableShape::isStrokeVisible() const noexcept
|
||||
{
|
||||
return strokeType.getStrokeThickness() > 0.0f && ! strokeFill.fill.isInvisible();
|
||||
}
|
||||
|
||||
void DrawableShape::refreshFillTypes (const FillAndStrokeState& newState, ComponentBuilder::ImageProvider* imageProvider)
|
||||
{
|
||||
setFill (newState.getFill (FillAndStrokeState::fill, imageProvider));
|
||||
setStrokeFill (newState.getFill (FillAndStrokeState::stroke, imageProvider));
|
||||
}
|
||||
|
||||
void DrawableShape::writeTo (FillAndStrokeState& state, ComponentBuilder::ImageProvider* imageProvider, UndoManager* undoManager) const
|
||||
{
|
||||
state.setFill (FillAndStrokeState::fill, mainFill, imageProvider, undoManager);
|
||||
state.setFill (FillAndStrokeState::stroke, strokeFill, imageProvider, undoManager);
|
||||
state.setStrokeType (strokeType, undoManager);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void DrawableShape::paint (Graphics& g)
|
||||
{
|
||||
transformContextToCorrectOrigin (g);
|
||||
|
||||
g.setFillType (mainFill.fill);
|
||||
g.fillPath (path);
|
||||
|
||||
if (isStrokeVisible())
|
||||
{
|
||||
g.setFillType (strokeFill.fill);
|
||||
g.fillPath (strokePath);
|
||||
}
|
||||
}
|
||||
|
||||
void DrawableShape::pathChanged()
|
||||
{
|
||||
strokeChanged();
|
||||
}
|
||||
|
||||
void DrawableShape::strokeChanged()
|
||||
{
|
||||
strokePath.clear();
|
||||
strokeType.createStrokedPath (strokePath, path, AffineTransform::identity, 4.0f);
|
||||
|
||||
setBoundsToEnclose (getDrawableBounds());
|
||||
repaint();
|
||||
}
|
||||
|
||||
Rectangle<float> DrawableShape::getDrawableBounds() const
|
||||
{
|
||||
if (isStrokeVisible())
|
||||
return strokePath.getBounds();
|
||||
else
|
||||
return path.getBounds();
|
||||
}
|
||||
|
||||
bool DrawableShape::hitTest (int x, int y)
|
||||
{
|
||||
bool allowsClicksOnThisComponent, allowsClicksOnChildComponents;
|
||||
getInterceptsMouseClicks (allowsClicksOnThisComponent, allowsClicksOnChildComponents);
|
||||
|
||||
if (! allowsClicksOnThisComponent)
|
||||
return false;
|
||||
|
||||
const float globalX = (float) (x - originRelativeToComponent.getX());
|
||||
const float globalY = (float) (y - originRelativeToComponent.getY());
|
||||
|
||||
return path.contains (globalX, globalY)
|
||||
|| (isStrokeVisible() && strokePath.contains (globalX, globalY));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
DrawableShape::RelativeFillType::RelativeFillType()
|
||||
{
|
||||
}
|
||||
|
||||
DrawableShape::RelativeFillType::RelativeFillType (const FillType& fill_)
|
||||
: fill (fill_)
|
||||
{
|
||||
if (fill.isGradient())
|
||||
{
|
||||
const ColourGradient& g = *fill.gradient;
|
||||
|
||||
gradientPoint1 = g.point1.transformedBy (fill.transform);
|
||||
gradientPoint2 = g.point2.transformedBy (fill.transform);
|
||||
gradientPoint3 = Point<float> (g.point1.getX() + g.point2.getY() - g.point1.getY(),
|
||||
g.point1.getY() + g.point1.getX() - g.point2.getX())
|
||||
.transformedBy (fill.transform);
|
||||
fill.transform = AffineTransform::identity;
|
||||
}
|
||||
}
|
||||
|
||||
DrawableShape::RelativeFillType::RelativeFillType (const RelativeFillType& other)
|
||||
: fill (other.fill),
|
||||
gradientPoint1 (other.gradientPoint1),
|
||||
gradientPoint2 (other.gradientPoint2),
|
||||
gradientPoint3 (other.gradientPoint3)
|
||||
{
|
||||
}
|
||||
|
||||
DrawableShape::RelativeFillType& DrawableShape::RelativeFillType::operator= (const RelativeFillType& other)
|
||||
{
|
||||
fill = other.fill;
|
||||
gradientPoint1 = other.gradientPoint1;
|
||||
gradientPoint2 = other.gradientPoint2;
|
||||
gradientPoint3 = other.gradientPoint3;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool DrawableShape::RelativeFillType::operator== (const RelativeFillType& other) const
|
||||
{
|
||||
return fill == other.fill
|
||||
&& ((! fill.isGradient())
|
||||
|| (gradientPoint1 == other.gradientPoint1
|
||||
&& gradientPoint2 == other.gradientPoint2
|
||||
&& gradientPoint3 == other.gradientPoint3));
|
||||
}
|
||||
|
||||
bool DrawableShape::RelativeFillType::operator!= (const RelativeFillType& other) const
|
||||
{
|
||||
return ! operator== (other);
|
||||
}
|
||||
|
||||
bool DrawableShape::RelativeFillType::recalculateCoords (Expression::Scope* scope)
|
||||
{
|
||||
if (fill.isGradient())
|
||||
{
|
||||
const Point<float> g1 (gradientPoint1.resolve (scope));
|
||||
const Point<float> g2 (gradientPoint2.resolve (scope));
|
||||
AffineTransform t;
|
||||
|
||||
ColourGradient& g = *fill.gradient;
|
||||
|
||||
if (g.isRadial)
|
||||
{
|
||||
const Point<float> g3 (gradientPoint3.resolve (scope));
|
||||
const Point<float> g3Source (g1.getX() + g2.getY() - g1.getY(),
|
||||
g1.getY() + g1.getX() - g2.getX());
|
||||
|
||||
t = AffineTransform::fromTargetPoints (g1.getX(), g1.getY(), g1.getX(), g1.getY(),
|
||||
g2.getX(), g2.getY(), g2.getX(), g2.getY(),
|
||||
g3Source.getX(), g3Source.getY(), g3.getX(), g3.getY());
|
||||
}
|
||||
|
||||
if (g.point1 != g1 || g.point2 != g2 || fill.transform != t)
|
||||
{
|
||||
g.point1 = g1;
|
||||
g.point2 = g2;
|
||||
fill.transform = t;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DrawableShape::RelativeFillType::isDynamic() const
|
||||
{
|
||||
return gradientPoint1.isDynamic() || gradientPoint2.isDynamic() || gradientPoint3.isDynamic();
|
||||
}
|
||||
|
||||
void DrawableShape::RelativeFillType::writeTo (ValueTree& v, ComponentBuilder::ImageProvider* imageProvider, UndoManager* undoManager) const
|
||||
{
|
||||
if (fill.isColour())
|
||||
{
|
||||
v.setProperty (FillAndStrokeState::type, "solid", undoManager);
|
||||
v.setProperty (FillAndStrokeState::colour, String::toHexString ((int) fill.colour.getARGB()), undoManager);
|
||||
}
|
||||
else if (fill.isGradient())
|
||||
{
|
||||
v.setProperty (FillAndStrokeState::type, "gradient", undoManager);
|
||||
v.setProperty (FillAndStrokeState::gradientPoint1, gradientPoint1.toString(), undoManager);
|
||||
v.setProperty (FillAndStrokeState::gradientPoint2, gradientPoint2.toString(), undoManager);
|
||||
v.setProperty (FillAndStrokeState::gradientPoint3, gradientPoint3.toString(), undoManager);
|
||||
|
||||
const ColourGradient& cg = *fill.gradient;
|
||||
v.setProperty (FillAndStrokeState::radial, cg.isRadial, undoManager);
|
||||
|
||||
String s;
|
||||
for (int i = 0; i < cg.getNumColours(); ++i)
|
||||
s << ' ' << cg.getColourPosition (i)
|
||||
<< ' ' << String::toHexString ((int) cg.getColour(i).getARGB());
|
||||
|
||||
v.setProperty (FillAndStrokeState::colours, s.trimStart(), undoManager);
|
||||
}
|
||||
else if (fill.isTiledImage())
|
||||
{
|
||||
v.setProperty (FillAndStrokeState::type, "image", undoManager);
|
||||
|
||||
if (imageProvider != nullptr)
|
||||
v.setProperty (FillAndStrokeState::imageId, imageProvider->getIdentifierForImage (fill.image), undoManager);
|
||||
|
||||
if (fill.getOpacity() < 1.0f)
|
||||
v.setProperty (FillAndStrokeState::imageOpacity, fill.getOpacity(), undoManager);
|
||||
else
|
||||
v.removeProperty (FillAndStrokeState::imageOpacity, undoManager);
|
||||
}
|
||||
else
|
||||
{
|
||||
jassertfalse;
|
||||
}
|
||||
}
|
||||
|
||||
bool DrawableShape::RelativeFillType::readFrom (const ValueTree& v, ComponentBuilder::ImageProvider* imageProvider)
|
||||
{
|
||||
const String newType (v [FillAndStrokeState::type].toString());
|
||||
|
||||
if (newType == "solid")
|
||||
{
|
||||
const String colourString (v [FillAndStrokeState::colour].toString());
|
||||
fill.setColour (Colour (colourString.isEmpty() ? (uint32) 0xff000000
|
||||
: (uint32) colourString.getHexValue32()));
|
||||
return true;
|
||||
}
|
||||
else if (newType == "gradient")
|
||||
{
|
||||
ColourGradient g;
|
||||
g.isRadial = v [FillAndStrokeState::radial];
|
||||
|
||||
StringArray colourSteps;
|
||||
colourSteps.addTokens (v [FillAndStrokeState::colours].toString(), false);
|
||||
|
||||
for (int i = 0; i < colourSteps.size() / 2; ++i)
|
||||
g.addColour (colourSteps[i * 2].getDoubleValue(),
|
||||
Colour ((uint32) colourSteps[i * 2 + 1].getHexValue32()));
|
||||
|
||||
fill.setGradient (g);
|
||||
|
||||
gradientPoint1 = RelativePoint (v [FillAndStrokeState::gradientPoint1]);
|
||||
gradientPoint2 = RelativePoint (v [FillAndStrokeState::gradientPoint2]);
|
||||
gradientPoint3 = RelativePoint (v [FillAndStrokeState::gradientPoint3]);
|
||||
return true;
|
||||
}
|
||||
else if (newType == "image")
|
||||
{
|
||||
Image im;
|
||||
if (imageProvider != nullptr)
|
||||
im = imageProvider->getImageForIdentifier (v [FillAndStrokeState::imageId]);
|
||||
|
||||
fill.setTiledImage (im, AffineTransform::identity);
|
||||
fill.setOpacity ((float) v.getProperty (FillAndStrokeState::imageOpacity, 1.0f));
|
||||
return true;
|
||||
}
|
||||
|
||||
jassertfalse;
|
||||
return false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
const Identifier DrawableShape::FillAndStrokeState::type ("type");
|
||||
const Identifier DrawableShape::FillAndStrokeState::colour ("colour");
|
||||
const Identifier DrawableShape::FillAndStrokeState::colours ("colours");
|
||||
const Identifier DrawableShape::FillAndStrokeState::fill ("Fill");
|
||||
const Identifier DrawableShape::FillAndStrokeState::stroke ("Stroke");
|
||||
const Identifier DrawableShape::FillAndStrokeState::path ("Path");
|
||||
const Identifier DrawableShape::FillAndStrokeState::jointStyle ("jointStyle");
|
||||
const Identifier DrawableShape::FillAndStrokeState::capStyle ("capStyle");
|
||||
const Identifier DrawableShape::FillAndStrokeState::strokeWidth ("strokeWidth");
|
||||
const Identifier DrawableShape::FillAndStrokeState::gradientPoint1 ("point1");
|
||||
const Identifier DrawableShape::FillAndStrokeState::gradientPoint2 ("point2");
|
||||
const Identifier DrawableShape::FillAndStrokeState::gradientPoint3 ("point3");
|
||||
const Identifier DrawableShape::FillAndStrokeState::radial ("radial");
|
||||
const Identifier DrawableShape::FillAndStrokeState::imageId ("imageId");
|
||||
const Identifier DrawableShape::FillAndStrokeState::imageOpacity ("imageOpacity");
|
||||
|
||||
DrawableShape::FillAndStrokeState::FillAndStrokeState (const ValueTree& state_)
|
||||
: Drawable::ValueTreeWrapperBase (state_)
|
||||
{
|
||||
}
|
||||
|
||||
DrawableShape::RelativeFillType DrawableShape::FillAndStrokeState::getFill (const Identifier& fillOrStrokeType, ComponentBuilder::ImageProvider* imageProvider) const
|
||||
{
|
||||
DrawableShape::RelativeFillType f;
|
||||
f.readFrom (state.getChildWithName (fillOrStrokeType), imageProvider);
|
||||
return f;
|
||||
}
|
||||
|
||||
ValueTree DrawableShape::FillAndStrokeState::getFillState (const Identifier& fillOrStrokeType)
|
||||
{
|
||||
ValueTree v (state.getChildWithName (fillOrStrokeType));
|
||||
if (v.isValid())
|
||||
return v;
|
||||
|
||||
setFill (fillOrStrokeType, FillType (Colours::black), nullptr, nullptr);
|
||||
return getFillState (fillOrStrokeType);
|
||||
}
|
||||
|
||||
void DrawableShape::FillAndStrokeState::setFill (const Identifier& fillOrStrokeType, const RelativeFillType& newFill,
|
||||
ComponentBuilder::ImageProvider* imageProvider, UndoManager* undoManager)
|
||||
{
|
||||
ValueTree v (state.getOrCreateChildWithName (fillOrStrokeType, undoManager));
|
||||
newFill.writeTo (v, imageProvider, undoManager);
|
||||
}
|
||||
|
||||
PathStrokeType DrawableShape::FillAndStrokeState::getStrokeType() const
|
||||
{
|
||||
const String jointStyleString (state [jointStyle].toString());
|
||||
const String capStyleString (state [capStyle].toString());
|
||||
|
||||
return PathStrokeType (state [strokeWidth],
|
||||
jointStyleString == "curved" ? PathStrokeType::curved
|
||||
: (jointStyleString == "bevel" ? PathStrokeType::beveled
|
||||
: PathStrokeType::mitered),
|
||||
capStyleString == "square" ? PathStrokeType::square
|
||||
: (capStyleString == "round" ? PathStrokeType::rounded
|
||||
: PathStrokeType::butt));
|
||||
}
|
||||
|
||||
void DrawableShape::FillAndStrokeState::setStrokeType (const PathStrokeType& newStrokeType, UndoManager* undoManager)
|
||||
{
|
||||
state.setProperty (strokeWidth, (double) newStrokeType.getStrokeThickness(), undoManager);
|
||||
state.setProperty (jointStyle, newStrokeType.getJointStyle() == PathStrokeType::mitered
|
||||
? "miter" : (newStrokeType.getJointStyle() == PathStrokeType::curved ? "curved" : "bevel"), undoManager);
|
||||
state.setProperty (capStyle, newStrokeType.getEndStyle() == PathStrokeType::butt
|
||||
? "butt" : (newStrokeType.getEndStyle() == PathStrokeType::square ? "square" : "round"), undoManager);
|
||||
}
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
184
modules/juce_gui_basics/drawables/juce_DrawableShape.h
Normal file
184
modules/juce_gui_basics/drawables/juce_DrawableShape.h
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_DRAWABLESHAPE_JUCEHEADER__
|
||||
#define __JUCE_DRAWABLESHAPE_JUCEHEADER__
|
||||
|
||||
#include "juce_Drawable.h"
|
||||
#include "../positioning/juce_RelativeCoordinatePositioner.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A base class implementing common functionality for Drawable classes which
|
||||
consist of some kind of filled and stroked outline.
|
||||
|
||||
@see DrawablePath, DrawableRectangle
|
||||
*/
|
||||
class JUCE_API DrawableShape : public Drawable
|
||||
{
|
||||
protected:
|
||||
//==============================================================================
|
||||
DrawableShape();
|
||||
DrawableShape (const DrawableShape&);
|
||||
|
||||
public:
|
||||
/** Destructor. */
|
||||
~DrawableShape();
|
||||
|
||||
//==============================================================================
|
||||
/** A FillType wrapper that allows the gradient coordinates to be implemented using RelativePoint.
|
||||
*/
|
||||
class RelativeFillType
|
||||
{
|
||||
public:
|
||||
RelativeFillType();
|
||||
RelativeFillType (const FillType& fill);
|
||||
RelativeFillType (const RelativeFillType&);
|
||||
RelativeFillType& operator= (const RelativeFillType&);
|
||||
|
||||
bool operator== (const RelativeFillType&) const;
|
||||
bool operator!= (const RelativeFillType&) const;
|
||||
|
||||
bool isDynamic() const;
|
||||
bool recalculateCoords (Expression::Scope* scope);
|
||||
|
||||
void writeTo (ValueTree& v, ComponentBuilder::ImageProvider*, UndoManager*) const;
|
||||
bool readFrom (const ValueTree& v, ComponentBuilder::ImageProvider*);
|
||||
|
||||
//==============================================================================
|
||||
FillType fill;
|
||||
RelativePoint gradientPoint1, gradientPoint2, gradientPoint3;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Sets a fill type for the path.
|
||||
This colour is used to fill the path - if you don't want the path to be
|
||||
filled (e.g. if you're just drawing an outline), set this to a transparent
|
||||
colour.
|
||||
|
||||
@see setPath, setStrokeFill
|
||||
*/
|
||||
void setFill (const FillType& newFill);
|
||||
|
||||
/** Sets a fill type for the path.
|
||||
This colour is used to fill the path - if you don't want the path to be
|
||||
filled (e.g. if you're just drawing an outline), set this to a transparent
|
||||
colour.
|
||||
|
||||
@see setPath, setStrokeFill
|
||||
*/
|
||||
void setFill (const RelativeFillType& newFill);
|
||||
|
||||
/** Returns the current fill type.
|
||||
@see setFill
|
||||
*/
|
||||
const RelativeFillType& getFill() const noexcept { return mainFill; }
|
||||
|
||||
/** Sets the fill type with which the outline will be drawn.
|
||||
@see setFill
|
||||
*/
|
||||
void setStrokeFill (const FillType& newStrokeFill);
|
||||
|
||||
/** Sets the fill type with which the outline will be drawn.
|
||||
@see setFill
|
||||
*/
|
||||
void setStrokeFill (const RelativeFillType& newStrokeFill);
|
||||
|
||||
/** Returns the current stroke fill.
|
||||
@see setStrokeFill
|
||||
*/
|
||||
const RelativeFillType& getStrokeFill() const noexcept { return strokeFill; }
|
||||
|
||||
/** Changes the properties of the outline that will be drawn around the path.
|
||||
If the stroke has 0 thickness, no stroke will be drawn.
|
||||
@see setStrokeThickness, setStrokeColour
|
||||
*/
|
||||
void setStrokeType (const PathStrokeType& newStrokeType);
|
||||
|
||||
/** Changes the stroke thickness.
|
||||
This is a shortcut for calling setStrokeType.
|
||||
*/
|
||||
void setStrokeThickness (float newThickness);
|
||||
|
||||
/** Returns the current outline style. */
|
||||
const PathStrokeType& getStrokeType() const noexcept { return strokeType; }
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
class FillAndStrokeState : public Drawable::ValueTreeWrapperBase
|
||||
{
|
||||
public:
|
||||
FillAndStrokeState (const ValueTree& state);
|
||||
|
||||
ValueTree getFillState (const Identifier& fillOrStrokeType);
|
||||
RelativeFillType getFill (const Identifier& fillOrStrokeType, ComponentBuilder::ImageProvider*) const;
|
||||
void setFill (const Identifier& fillOrStrokeType, const RelativeFillType& newFill,
|
||||
ComponentBuilder::ImageProvider*, UndoManager*);
|
||||
|
||||
PathStrokeType getStrokeType() const;
|
||||
void setStrokeType (const PathStrokeType& newStrokeType, UndoManager*);
|
||||
|
||||
static const Identifier type, colour, colours, fill, stroke, path, jointStyle, capStyle, strokeWidth,
|
||||
gradientPoint1, gradientPoint2, gradientPoint3, radial, imageId, imageOpacity;
|
||||
};
|
||||
|
||||
/** @internal */
|
||||
Rectangle<float> getDrawableBounds() const;
|
||||
/** @internal */
|
||||
void paint (Graphics& g);
|
||||
/** @internal */
|
||||
bool hitTest (int x, int y);
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
/** Called when the cached path should be updated. */
|
||||
void pathChanged();
|
||||
/** Called when the cached stroke should be updated. */
|
||||
void strokeChanged();
|
||||
/** True if there's a stroke with a non-zero thickness and non-transparent colour. */
|
||||
bool isStrokeVisible() const noexcept;
|
||||
/** Updates the details from a FillAndStrokeState object, returning true if something changed. */
|
||||
void refreshFillTypes (const FillAndStrokeState& newState, ComponentBuilder::ImageProvider*);
|
||||
/** Writes the stroke and fill details to a FillAndStrokeState object. */
|
||||
void writeTo (FillAndStrokeState& state, ComponentBuilder::ImageProvider*, UndoManager*) const;
|
||||
|
||||
//==============================================================================
|
||||
PathStrokeType strokeType;
|
||||
Path path, strokePath;
|
||||
|
||||
private:
|
||||
class RelativePositioner;
|
||||
RelativeFillType mainFill, strokeFill;
|
||||
ScopedPointer<RelativeCoordinatePositionerBase> mainFillPositioner, strokeFillPositioner;
|
||||
|
||||
void setFillInternal (RelativeFillType& fill, const RelativeFillType& newFill,
|
||||
ScopedPointer<RelativeCoordinatePositionerBase>& positioner);
|
||||
|
||||
DrawableShape& operator= (const DrawableShape&);
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCE_DRAWABLESHAPE_JUCEHEADER__
|
||||
321
modules/juce_gui_basics/drawables/juce_DrawableText.cpp
Normal file
321
modules/juce_gui_basics/drawables/juce_DrawableText.cpp
Normal file
|
|
@ -0,0 +1,321 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
//==============================================================================
|
||||
DrawableText::DrawableText()
|
||||
: colour (Colours::black),
|
||||
justification (Justification::centredLeft)
|
||||
{
|
||||
setBoundingBox (RelativeParallelogram (RelativePoint (0.0f, 0.0f),
|
||||
RelativePoint (50.0f, 0.0f),
|
||||
RelativePoint (0.0f, 20.0f)));
|
||||
setFont (Font (15.0f), true);
|
||||
}
|
||||
|
||||
DrawableText::DrawableText (const DrawableText& other)
|
||||
: bounds (other.bounds),
|
||||
fontSizeControlPoint (other.fontSizeControlPoint),
|
||||
font (other.font),
|
||||
text (other.text),
|
||||
colour (other.colour),
|
||||
justification (other.justification)
|
||||
{
|
||||
}
|
||||
|
||||
DrawableText::~DrawableText()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void DrawableText::setText (const String& newText)
|
||||
{
|
||||
if (text != newText)
|
||||
{
|
||||
text = newText;
|
||||
refreshBounds();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawableText::setColour (const Colour& newColour)
|
||||
{
|
||||
if (colour != newColour)
|
||||
{
|
||||
colour = newColour;
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawableText::setFont (const Font& newFont, bool applySizeAndScale)
|
||||
{
|
||||
if (font != newFont)
|
||||
{
|
||||
font = newFont;
|
||||
|
||||
if (applySizeAndScale)
|
||||
{
|
||||
setFontSizeControlPoint (RelativePoint (RelativeParallelogram::getPointForInternalCoord (resolvedPoints,
|
||||
Point<float> (font.getHorizontalScale() * font.getHeight(), font.getHeight()))));
|
||||
}
|
||||
|
||||
refreshBounds();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawableText::setJustification (const Justification& newJustification)
|
||||
{
|
||||
justification = newJustification;
|
||||
repaint();
|
||||
}
|
||||
|
||||
void DrawableText::setBoundingBox (const RelativeParallelogram& newBounds)
|
||||
{
|
||||
if (bounds != newBounds)
|
||||
{
|
||||
bounds = newBounds;
|
||||
refreshBounds();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawableText::setFontSizeControlPoint (const RelativePoint& newPoint)
|
||||
{
|
||||
if (fontSizeControlPoint != newPoint)
|
||||
{
|
||||
fontSizeControlPoint = newPoint;
|
||||
refreshBounds();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawableText::refreshBounds()
|
||||
{
|
||||
if (bounds.isDynamic() || fontSizeControlPoint.isDynamic())
|
||||
{
|
||||
Drawable::Positioner<DrawableText>* const p = new Drawable::Positioner<DrawableText> (*this);
|
||||
setPositioner (p);
|
||||
p->apply();
|
||||
}
|
||||
else
|
||||
{
|
||||
setPositioner (0);
|
||||
recalculateCoordinates (0);
|
||||
}
|
||||
}
|
||||
|
||||
bool DrawableText::registerCoordinates (RelativeCoordinatePositionerBase& pos)
|
||||
{
|
||||
bool ok = pos.addPoint (bounds.topLeft);
|
||||
ok = pos.addPoint (bounds.topRight) && ok;
|
||||
ok = pos.addPoint (bounds.bottomLeft) && ok;
|
||||
return pos.addPoint (fontSizeControlPoint) && ok;
|
||||
}
|
||||
|
||||
void DrawableText::recalculateCoordinates (Expression::Scope* scope)
|
||||
{
|
||||
bounds.resolveThreePoints (resolvedPoints, scope);
|
||||
|
||||
const float w = Line<float> (resolvedPoints[0], resolvedPoints[1]).getLength();
|
||||
const float h = Line<float> (resolvedPoints[0], resolvedPoints[2]).getLength();
|
||||
|
||||
const Point<float> fontCoords (RelativeParallelogram::getInternalCoordForPoint (resolvedPoints, fontSizeControlPoint.resolve (scope)));
|
||||
const float fontHeight = jlimit (0.01f, jmax (0.01f, h), fontCoords.getY());
|
||||
const float fontWidth = jlimit (0.01f, jmax (0.01f, w), fontCoords.getX());
|
||||
|
||||
scaledFont = font;
|
||||
scaledFont.setHeight (fontHeight);
|
||||
scaledFont.setHorizontalScale (fontWidth / fontHeight);
|
||||
|
||||
setBoundsToEnclose (getDrawableBounds());
|
||||
repaint();
|
||||
}
|
||||
|
||||
const AffineTransform DrawableText::getArrangementAndTransform (GlyphArrangement& glyphs) const
|
||||
{
|
||||
const float w = Line<float> (resolvedPoints[0], resolvedPoints[1]).getLength();
|
||||
const float h = Line<float> (resolvedPoints[0], resolvedPoints[2]).getLength();
|
||||
|
||||
glyphs.addFittedText (scaledFont, text, 0, 0, w, h, justification, 0x100000);
|
||||
|
||||
return AffineTransform::fromTargetPoints (0, 0, resolvedPoints[0].getX(), resolvedPoints[0].getY(),
|
||||
w, 0, resolvedPoints[1].getX(), resolvedPoints[1].getY(),
|
||||
0, h, resolvedPoints[2].getX(), resolvedPoints[2].getY());
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void DrawableText::paint (Graphics& g)
|
||||
{
|
||||
transformContextToCorrectOrigin (g);
|
||||
|
||||
g.setColour (colour);
|
||||
|
||||
GlyphArrangement ga;
|
||||
const AffineTransform transform (getArrangementAndTransform (ga));
|
||||
ga.draw (g, transform);
|
||||
}
|
||||
|
||||
Rectangle<float> DrawableText::getDrawableBounds() const
|
||||
{
|
||||
return RelativeParallelogram::getBoundingBox (resolvedPoints);
|
||||
}
|
||||
|
||||
Drawable* DrawableText::createCopy() const
|
||||
{
|
||||
return new DrawableText (*this);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
const Identifier DrawableText::valueTreeType ("Text");
|
||||
|
||||
const Identifier DrawableText::ValueTreeWrapper::text ("text");
|
||||
const Identifier DrawableText::ValueTreeWrapper::colour ("colour");
|
||||
const Identifier DrawableText::ValueTreeWrapper::font ("font");
|
||||
const Identifier DrawableText::ValueTreeWrapper::justification ("justification");
|
||||
const Identifier DrawableText::ValueTreeWrapper::topLeft ("topLeft");
|
||||
const Identifier DrawableText::ValueTreeWrapper::topRight ("topRight");
|
||||
const Identifier DrawableText::ValueTreeWrapper::bottomLeft ("bottomLeft");
|
||||
const Identifier DrawableText::ValueTreeWrapper::fontSizeAnchor ("fontSizeAnchor");
|
||||
|
||||
//==============================================================================
|
||||
DrawableText::ValueTreeWrapper::ValueTreeWrapper (const ValueTree& state_)
|
||||
: ValueTreeWrapperBase (state_)
|
||||
{
|
||||
jassert (state.hasType (valueTreeType));
|
||||
}
|
||||
|
||||
String DrawableText::ValueTreeWrapper::getText() const
|
||||
{
|
||||
return state [text].toString();
|
||||
}
|
||||
|
||||
void DrawableText::ValueTreeWrapper::setText (const String& newText, UndoManager* undoManager)
|
||||
{
|
||||
state.setProperty (text, newText, undoManager);
|
||||
}
|
||||
|
||||
Value DrawableText::ValueTreeWrapper::getTextValue (UndoManager* undoManager)
|
||||
{
|
||||
return state.getPropertyAsValue (text, undoManager);
|
||||
}
|
||||
|
||||
Colour DrawableText::ValueTreeWrapper::getColour() const
|
||||
{
|
||||
return Colour::fromString (state [colour].toString());
|
||||
}
|
||||
|
||||
void DrawableText::ValueTreeWrapper::setColour (const Colour& newColour, UndoManager* undoManager)
|
||||
{
|
||||
state.setProperty (colour, newColour.toString(), undoManager);
|
||||
}
|
||||
|
||||
Justification DrawableText::ValueTreeWrapper::getJustification() const
|
||||
{
|
||||
return Justification ((int) state [justification]);
|
||||
}
|
||||
|
||||
void DrawableText::ValueTreeWrapper::setJustification (const Justification& newJustification, UndoManager* undoManager)
|
||||
{
|
||||
state.setProperty (justification, newJustification.getFlags(), undoManager);
|
||||
}
|
||||
|
||||
Font DrawableText::ValueTreeWrapper::getFont() const
|
||||
{
|
||||
return Font::fromString (state [font]);
|
||||
}
|
||||
|
||||
void DrawableText::ValueTreeWrapper::setFont (const Font& newFont, UndoManager* undoManager)
|
||||
{
|
||||
state.setProperty (font, newFont.toString(), undoManager);
|
||||
}
|
||||
|
||||
Value DrawableText::ValueTreeWrapper::getFontValue (UndoManager* undoManager)
|
||||
{
|
||||
return state.getPropertyAsValue (font, undoManager);
|
||||
}
|
||||
|
||||
RelativeParallelogram DrawableText::ValueTreeWrapper::getBoundingBox() const
|
||||
{
|
||||
return RelativeParallelogram (state [topLeft].toString(), state [topRight].toString(), state [bottomLeft].toString());
|
||||
}
|
||||
|
||||
void DrawableText::ValueTreeWrapper::setBoundingBox (const RelativeParallelogram& newBounds, UndoManager* undoManager)
|
||||
{
|
||||
state.setProperty (topLeft, newBounds.topLeft.toString(), undoManager);
|
||||
state.setProperty (topRight, newBounds.topRight.toString(), undoManager);
|
||||
state.setProperty (bottomLeft, newBounds.bottomLeft.toString(), undoManager);
|
||||
}
|
||||
|
||||
RelativePoint DrawableText::ValueTreeWrapper::getFontSizeControlPoint() const
|
||||
{
|
||||
return state [fontSizeAnchor].toString();
|
||||
}
|
||||
|
||||
void DrawableText::ValueTreeWrapper::setFontSizeControlPoint (const RelativePoint& p, UndoManager* undoManager)
|
||||
{
|
||||
state.setProperty (fontSizeAnchor, p.toString(), undoManager);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void DrawableText::refreshFromValueTree (const ValueTree& tree, ComponentBuilder&)
|
||||
{
|
||||
ValueTreeWrapper v (tree);
|
||||
setComponentID (v.getID());
|
||||
|
||||
const RelativeParallelogram newBounds (v.getBoundingBox());
|
||||
const RelativePoint newFontPoint (v.getFontSizeControlPoint());
|
||||
const Colour newColour (v.getColour());
|
||||
const Justification newJustification (v.getJustification());
|
||||
const String newText (v.getText());
|
||||
const Font newFont (v.getFont());
|
||||
|
||||
if (text != newText || font != newFont || justification != newJustification
|
||||
|| colour != newColour || bounds != newBounds || newFontPoint != fontSizeControlPoint)
|
||||
{
|
||||
setBoundingBox (newBounds);
|
||||
setFontSizeControlPoint (newFontPoint);
|
||||
setColour (newColour);
|
||||
setFont (newFont, false);
|
||||
setJustification (newJustification);
|
||||
setText (newText);
|
||||
}
|
||||
}
|
||||
|
||||
ValueTree DrawableText::createValueTree (ComponentBuilder::ImageProvider*) const
|
||||
{
|
||||
ValueTree tree (valueTreeType);
|
||||
ValueTreeWrapper v (tree);
|
||||
|
||||
v.setID (getComponentID());
|
||||
v.setText (text, nullptr);
|
||||
v.setFont (font, nullptr);
|
||||
v.setJustification (justification, nullptr);
|
||||
v.setColour (colour, nullptr);
|
||||
v.setBoundingBox (bounds, nullptr);
|
||||
v.setFontSizeControlPoint (fontSizeControlPoint, nullptr);
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
153
modules/juce_gui_basics/drawables/juce_DrawableText.h
Normal file
153
modules/juce_gui_basics/drawables/juce_DrawableText.h
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_DRAWABLETEXT_JUCEHEADER__
|
||||
#define __JUCE_DRAWABLETEXT_JUCEHEADER__
|
||||
|
||||
#include "juce_Drawable.h"
|
||||
#include "../positioning/juce_RelativeParallelogram.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A drawable object which renders a line of text.
|
||||
|
||||
@see Drawable
|
||||
*/
|
||||
class JUCE_API DrawableText : public Drawable
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a DrawableText object. */
|
||||
DrawableText();
|
||||
DrawableText (const DrawableText& other);
|
||||
|
||||
/** Destructor. */
|
||||
~DrawableText();
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the text to display.*/
|
||||
void setText (const String& newText);
|
||||
|
||||
/** Sets the colour of the text. */
|
||||
void setColour (const Colour& newColour);
|
||||
|
||||
/** Returns the current text colour. */
|
||||
const Colour& getColour() const noexcept { return colour; }
|
||||
|
||||
/** Sets the font to use.
|
||||
Note that the font height and horizontal scale are actually based upon the position
|
||||
of the fontSizeAndScaleAnchor parameter to setBounds(). If applySizeAndScale is true, then
|
||||
the height and scale control point will be moved to match the dimensions of the font supplied;
|
||||
if it is false, then the new font's height and scale are ignored.
|
||||
*/
|
||||
void setFont (const Font& newFont, bool applySizeAndScale);
|
||||
|
||||
/** Changes the justification of the text within the bounding box. */
|
||||
void setJustification (const Justification& newJustification);
|
||||
|
||||
/** Returns the parallelogram that defines the text bounding box. */
|
||||
const RelativeParallelogram& getBoundingBox() const noexcept { return bounds; }
|
||||
|
||||
/** Sets the bounding box that contains the text. */
|
||||
void setBoundingBox (const RelativeParallelogram& newBounds);
|
||||
|
||||
/** Returns the point within the bounds that defines the font's size and scale. */
|
||||
const RelativePoint& getFontSizeControlPoint() const noexcept { return fontSizeControlPoint; }
|
||||
|
||||
/** Sets the control point that defines the font's height and horizontal scale.
|
||||
This position is a point within the bounding box parallelogram, whose Y position (relative
|
||||
to the parallelogram's origin and possibly distorted shape) specifies the font's height,
|
||||
and its X defines the font's horizontal scale.
|
||||
*/
|
||||
void setFontSizeControlPoint (const RelativePoint& newPoint);
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void paint (Graphics& g);
|
||||
/** @internal */
|
||||
Drawable* createCopy() const;
|
||||
/** @internal */
|
||||
void refreshFromValueTree (const ValueTree& tree, ComponentBuilder& builder);
|
||||
/** @internal */
|
||||
ValueTree createValueTree (ComponentBuilder::ImageProvider* imageProvider) const;
|
||||
/** @internal */
|
||||
static const Identifier valueTreeType;
|
||||
/** @internal */
|
||||
Rectangle<float> getDrawableBounds() const;
|
||||
|
||||
//==============================================================================
|
||||
/** Internally-used class for wrapping a DrawableText's state into a ValueTree. */
|
||||
class ValueTreeWrapper : public Drawable::ValueTreeWrapperBase
|
||||
{
|
||||
public:
|
||||
ValueTreeWrapper (const ValueTree& state);
|
||||
|
||||
String getText() const;
|
||||
void setText (const String& newText, UndoManager* undoManager);
|
||||
Value getTextValue (UndoManager* undoManager);
|
||||
|
||||
Colour getColour() const;
|
||||
void setColour (const Colour& newColour, UndoManager* undoManager);
|
||||
|
||||
Justification getJustification() const;
|
||||
void setJustification (const Justification& newJustification, UndoManager* undoManager);
|
||||
|
||||
Font getFont() const;
|
||||
void setFont (const Font& newFont, UndoManager* undoManager);
|
||||
Value getFontValue (UndoManager* undoManager);
|
||||
|
||||
RelativeParallelogram getBoundingBox() const;
|
||||
void setBoundingBox (const RelativeParallelogram& newBounds, UndoManager* undoManager);
|
||||
|
||||
RelativePoint getFontSizeControlPoint() const;
|
||||
void setFontSizeControlPoint (const RelativePoint& p, UndoManager* undoManager);
|
||||
|
||||
static const Identifier text, colour, font, justification, topLeft, topRight, bottomLeft, fontSizeAnchor;
|
||||
};
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
RelativeParallelogram bounds;
|
||||
RelativePoint fontSizeControlPoint;
|
||||
Point<float> resolvedPoints[3];
|
||||
Font font, scaledFont;
|
||||
String text;
|
||||
Colour colour;
|
||||
Justification justification;
|
||||
|
||||
friend class Drawable::Positioner<DrawableText>;
|
||||
bool registerCoordinates (RelativeCoordinatePositionerBase&);
|
||||
void recalculateCoordinates (Expression::Scope*);
|
||||
void refreshBounds();
|
||||
const AffineTransform getArrangementAndTransform (GlyphArrangement& glyphs) const;
|
||||
|
||||
DrawableText& operator= (const DrawableText&);
|
||||
JUCE_LEAK_DETECTOR (DrawableText);
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCE_DRAWABLETEXT_JUCEHEADER__
|
||||
1299
modules/juce_gui_basics/drawables/juce_SVGParser.cpp
Normal file
1299
modules/juce_gui_basics/drawables/juce_SVGParser.cpp
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
//==============================================================================
|
||||
DirectoryContentsDisplayComponent::DirectoryContentsDisplayComponent (DirectoryContentsList& listToShow)
|
||||
: fileList (listToShow)
|
||||
{
|
||||
}
|
||||
|
||||
DirectoryContentsDisplayComponent::~DirectoryContentsDisplayComponent()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
FileBrowserListener::~FileBrowserListener()
|
||||
{
|
||||
}
|
||||
|
||||
void DirectoryContentsDisplayComponent::addListener (FileBrowserListener* const listener)
|
||||
{
|
||||
listeners.add (listener);
|
||||
}
|
||||
|
||||
void DirectoryContentsDisplayComponent::removeListener (FileBrowserListener* const listener)
|
||||
{
|
||||
listeners.remove (listener);
|
||||
}
|
||||
|
||||
void DirectoryContentsDisplayComponent::sendSelectionChangeMessage()
|
||||
{
|
||||
Component::BailOutChecker checker (dynamic_cast <Component*> (this));
|
||||
listeners.callChecked (checker, &FileBrowserListener::selectionChanged);
|
||||
}
|
||||
|
||||
void DirectoryContentsDisplayComponent::sendMouseClickMessage (const File& file, const MouseEvent& e)
|
||||
{
|
||||
if (fileList.getDirectory().exists())
|
||||
{
|
||||
Component::BailOutChecker checker (dynamic_cast <Component*> (this));
|
||||
listeners.callChecked (checker, &FileBrowserListener::fileClicked, file, e);
|
||||
}
|
||||
}
|
||||
|
||||
void DirectoryContentsDisplayComponent::sendDoubleClickMessage (const File& file)
|
||||
{
|
||||
if (fileList.getDirectory().exists())
|
||||
{
|
||||
Component::BailOutChecker checker (dynamic_cast <Component*> (this));
|
||||
listeners.callChecked (checker, &FileBrowserListener::fileDoubleClicked, file);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_DIRECTORYCONTENTSDISPLAYCOMPONENT_JUCEHEADER__
|
||||
#define __JUCE_DIRECTORYCONTENTSDISPLAYCOMPONENT_JUCEHEADER__
|
||||
|
||||
#include "../components/juce_Component.h"
|
||||
#include "juce_DirectoryContentsList.h"
|
||||
#include "juce_FileBrowserListener.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A base class for components that display a list of the files in a directory.
|
||||
|
||||
@see DirectoryContentsList
|
||||
*/
|
||||
class JUCE_API DirectoryContentsDisplayComponent
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a DirectoryContentsDisplayComponent for a given list of files. */
|
||||
DirectoryContentsDisplayComponent (DirectoryContentsList& listToShow);
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~DirectoryContentsDisplayComponent();
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the number of files the user has got selected.
|
||||
@see getSelectedFile
|
||||
*/
|
||||
virtual int getNumSelectedFiles() const = 0;
|
||||
|
||||
/** Returns one of the files that the user has currently selected.
|
||||
The index should be in the range 0 to (getNumSelectedFiles() - 1).
|
||||
@see getNumSelectedFiles
|
||||
*/
|
||||
virtual const File getSelectedFile (int index) const = 0;
|
||||
|
||||
/** Deselects any selected files. */
|
||||
virtual void deselectAllFiles() = 0;
|
||||
|
||||
/** Scrolls this view to the top. */
|
||||
virtual void scrollToTop() = 0;
|
||||
|
||||
//==============================================================================
|
||||
/** Adds a listener to be told when files are selected or clicked.
|
||||
@see removeListener
|
||||
*/
|
||||
void addListener (FileBrowserListener* listener);
|
||||
|
||||
/** Removes a listener.
|
||||
@see addListener
|
||||
*/
|
||||
void removeListener (FileBrowserListener* listener);
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** A set of colour IDs to use to change the colour of various aspects of the list.
|
||||
|
||||
These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
|
||||
methods.
|
||||
|
||||
@see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
|
||||
*/
|
||||
enum ColourIds
|
||||
{
|
||||
highlightColourId = 0x1000540, /**< The colour to use to fill a highlighted row of the list. */
|
||||
textColourId = 0x1000541, /**< The colour for the text. */
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void sendSelectionChangeMessage();
|
||||
/** @internal */
|
||||
void sendDoubleClickMessage (const File& file);
|
||||
/** @internal */
|
||||
void sendMouseClickMessage (const File& file, const MouseEvent& e);
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
DirectoryContentsList& fileList;
|
||||
ListenerList <FileBrowserListener> listeners;
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DirectoryContentsDisplayComponent);
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCE_DIRECTORYCONTENTSDISPLAYCOMPONENT_JUCEHEADER__
|
||||
|
|
@ -0,0 +1,257 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
//==============================================================================
|
||||
DirectoryContentsList::DirectoryContentsList (const FileFilter* const fileFilter_,
|
||||
TimeSliceThread& thread_)
|
||||
: fileFilter (fileFilter_),
|
||||
thread (thread_),
|
||||
fileTypeFlags (File::ignoreHiddenFiles | File::findFiles),
|
||||
shouldStop (true)
|
||||
{
|
||||
}
|
||||
|
||||
DirectoryContentsList::~DirectoryContentsList()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
void DirectoryContentsList::setIgnoresHiddenFiles (const bool shouldIgnoreHiddenFiles)
|
||||
{
|
||||
setTypeFlags (shouldIgnoreHiddenFiles ? (fileTypeFlags | File::ignoreHiddenFiles)
|
||||
: (fileTypeFlags & ~File::ignoreHiddenFiles));
|
||||
}
|
||||
|
||||
bool DirectoryContentsList::ignoresHiddenFiles() const
|
||||
{
|
||||
return (fileTypeFlags & File::ignoreHiddenFiles) != 0;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
const File& DirectoryContentsList::getDirectory() const
|
||||
{
|
||||
return root;
|
||||
}
|
||||
|
||||
void DirectoryContentsList::setDirectory (const File& directory,
|
||||
const bool includeDirectories,
|
||||
const bool includeFiles)
|
||||
{
|
||||
jassert (includeDirectories || includeFiles); // you have to speciify at least one of these!
|
||||
|
||||
if (directory != root)
|
||||
{
|
||||
clear();
|
||||
root = directory;
|
||||
|
||||
// (this forces a refresh when setTypeFlags() is called, rather than triggering two refreshes)
|
||||
fileTypeFlags &= ~(File::findDirectories | File::findFiles);
|
||||
}
|
||||
|
||||
int newFlags = fileTypeFlags;
|
||||
if (includeDirectories) newFlags |= File::findDirectories; else newFlags &= ~File::findDirectories;
|
||||
if (includeFiles) newFlags |= File::findFiles; else newFlags &= ~File::findFiles;
|
||||
|
||||
setTypeFlags (newFlags);
|
||||
}
|
||||
|
||||
void DirectoryContentsList::setTypeFlags (const int newFlags)
|
||||
{
|
||||
if (fileTypeFlags != newFlags)
|
||||
{
|
||||
fileTypeFlags = newFlags;
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
|
||||
void DirectoryContentsList::clear()
|
||||
{
|
||||
shouldStop = true;
|
||||
thread.removeTimeSliceClient (this);
|
||||
|
||||
fileFindHandle = nullptr;
|
||||
|
||||
if (files.size() > 0)
|
||||
{
|
||||
files.clear();
|
||||
changed();
|
||||
}
|
||||
}
|
||||
|
||||
void DirectoryContentsList::refresh()
|
||||
{
|
||||
clear();
|
||||
|
||||
if (root.isDirectory())
|
||||
{
|
||||
fileFindHandle = new DirectoryIterator (root, false, "*", fileTypeFlags);
|
||||
shouldStop = false;
|
||||
thread.addTimeSliceClient (this);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
int DirectoryContentsList::getNumFiles() const
|
||||
{
|
||||
return files.size();
|
||||
}
|
||||
|
||||
bool DirectoryContentsList::getFileInfo (const int index,
|
||||
FileInfo& result) const
|
||||
{
|
||||
const ScopedLock sl (fileListLock);
|
||||
const FileInfo* const info = files [index];
|
||||
|
||||
if (info != nullptr)
|
||||
{
|
||||
result = *info;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
File DirectoryContentsList::getFile (const int index) const
|
||||
{
|
||||
const ScopedLock sl (fileListLock);
|
||||
const FileInfo* const info = files [index];
|
||||
|
||||
if (info != nullptr)
|
||||
return root.getChildFile (info->filename);
|
||||
|
||||
return File::nonexistent;
|
||||
}
|
||||
|
||||
bool DirectoryContentsList::isStillLoading() const
|
||||
{
|
||||
return fileFindHandle != nullptr;
|
||||
}
|
||||
|
||||
void DirectoryContentsList::changed()
|
||||
{
|
||||
sendChangeMessage();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
int DirectoryContentsList::useTimeSlice()
|
||||
{
|
||||
const uint32 startTime = Time::getApproximateMillisecondCounter();
|
||||
bool hasChanged = false;
|
||||
|
||||
for (int i = 100; --i >= 0;)
|
||||
{
|
||||
if (! checkNextFile (hasChanged))
|
||||
{
|
||||
if (hasChanged)
|
||||
changed();
|
||||
|
||||
return 500;
|
||||
}
|
||||
|
||||
if (shouldStop || (Time::getApproximateMillisecondCounter() > startTime + 150))
|
||||
break;
|
||||
}
|
||||
|
||||
if (hasChanged)
|
||||
changed();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool DirectoryContentsList::checkNextFile (bool& hasChanged)
|
||||
{
|
||||
if (fileFindHandle != nullptr)
|
||||
{
|
||||
bool fileFoundIsDir, isHidden, isReadOnly;
|
||||
int64 fileSize;
|
||||
Time modTime, creationTime;
|
||||
|
||||
if (fileFindHandle->next (&fileFoundIsDir, &isHidden, &fileSize,
|
||||
&modTime, &creationTime, &isReadOnly))
|
||||
{
|
||||
if (addFile (fileFindHandle->getFile(), fileFoundIsDir,
|
||||
fileSize, modTime, creationTime, isReadOnly))
|
||||
{
|
||||
hasChanged = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
fileFindHandle = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int DirectoryContentsList::compareElements (const DirectoryContentsList::FileInfo* const first,
|
||||
const DirectoryContentsList::FileInfo* const second)
|
||||
{
|
||||
#if JUCE_WINDOWS
|
||||
if (first->isDirectory != second->isDirectory)
|
||||
return first->isDirectory ? -1 : 1;
|
||||
#endif
|
||||
|
||||
return first->filename.compareIgnoreCase (second->filename);
|
||||
}
|
||||
|
||||
bool DirectoryContentsList::addFile (const File& file,
|
||||
const bool isDir,
|
||||
const int64 fileSize,
|
||||
const Time& modTime,
|
||||
const Time& creationTime,
|
||||
const bool isReadOnly)
|
||||
{
|
||||
if (fileFilter == nullptr
|
||||
|| ((! isDir) && fileFilter->isFileSuitable (file))
|
||||
|| (isDir && fileFilter->isDirectorySuitable (file)))
|
||||
{
|
||||
ScopedPointer <FileInfo> info (new FileInfo());
|
||||
|
||||
info->filename = file.getFileName();
|
||||
info->fileSize = fileSize;
|
||||
info->modificationTime = modTime;
|
||||
info->creationTime = creationTime;
|
||||
info->isDirectory = isDir;
|
||||
info->isReadOnly = isReadOnly;
|
||||
|
||||
const ScopedLock sl (fileListLock);
|
||||
|
||||
for (int i = files.size(); --i >= 0;)
|
||||
if (files.getUnchecked(i)->filename == info->filename)
|
||||
return false;
|
||||
|
||||
files.addSorted (*this, info.release());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
214
modules/juce_gui_basics/filebrowser/juce_DirectoryContentsList.h
Normal file
214
modules/juce_gui_basics/filebrowser/juce_DirectoryContentsList.h
Normal file
|
|
@ -0,0 +1,214 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_DIRECTORYCONTENTSLIST_JUCEHEADER__
|
||||
#define __JUCE_DIRECTORYCONTENTSLIST_JUCEHEADER__
|
||||
|
||||
#include "juce_FileFilter.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A class to asynchronously scan for details about the files in a directory.
|
||||
|
||||
This keeps a list of files and some information about them, using a background
|
||||
thread to scan for more files. As files are found, it broadcasts change messages
|
||||
to tell any listeners.
|
||||
|
||||
@see FileListComponent, FileBrowserComponent
|
||||
*/
|
||||
class JUCE_API DirectoryContentsList : public ChangeBroadcaster,
|
||||
public TimeSliceClient
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a directory list.
|
||||
|
||||
To set the directory it should point to, use setDirectory(), which will
|
||||
also start it scanning for files on the background thread.
|
||||
|
||||
When the background thread finds and adds new files to this list, the
|
||||
ChangeBroadcaster class will send a change message, so you can register
|
||||
listeners and update them when the list changes.
|
||||
|
||||
@param fileFilter an optional filter to select which files are
|
||||
included in the list. If this is 0, then all files
|
||||
and directories are included. Make sure that the
|
||||
filter doesn't get deleted during the lifetime of this
|
||||
object
|
||||
@param threadToUse a thread object that this list can use
|
||||
to scan for files as a background task. Make sure
|
||||
that the thread you give it has been started, or you
|
||||
won't get any files!
|
||||
*/
|
||||
DirectoryContentsList (const FileFilter* fileFilter,
|
||||
TimeSliceThread& threadToUse);
|
||||
|
||||
/** Destructor. */
|
||||
~DirectoryContentsList();
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the directory to look in for files.
|
||||
|
||||
If the directory that's passed in is different to the current one, this will
|
||||
also start the background thread scanning it for files.
|
||||
*/
|
||||
void setDirectory (const File& directory,
|
||||
bool includeDirectories,
|
||||
bool includeFiles);
|
||||
|
||||
/** Returns the directory that's currently being used. */
|
||||
const File& getDirectory() const;
|
||||
|
||||
/** Clears the list, and stops the thread scanning for files. */
|
||||
void clear();
|
||||
|
||||
/** Clears the list and restarts scanning the directory for files. */
|
||||
void refresh();
|
||||
|
||||
/** True if the background thread hasn't yet finished scanning for files. */
|
||||
bool isStillLoading() const;
|
||||
|
||||
/** Tells the list whether or not to ignore hidden files.
|
||||
|
||||
By default these are ignored.
|
||||
*/
|
||||
void setIgnoresHiddenFiles (bool shouldIgnoreHiddenFiles);
|
||||
|
||||
/** Returns true if hidden files are ignored.
|
||||
@see setIgnoresHiddenFiles
|
||||
*/
|
||||
bool ignoresHiddenFiles() const;
|
||||
|
||||
//==============================================================================
|
||||
/** Contains cached information about one of the files in a DirectoryContentsList.
|
||||
*/
|
||||
struct FileInfo
|
||||
{
|
||||
//==============================================================================
|
||||
/** The filename.
|
||||
|
||||
This isn't a full pathname, it's just the last part of the path, same as you'd
|
||||
get from File::getFileName().
|
||||
|
||||
To get the full pathname, use DirectoryContentsList::getDirectory().getChildFile (filename).
|
||||
*/
|
||||
String filename;
|
||||
|
||||
/** File size in bytes. */
|
||||
int64 fileSize;
|
||||
|
||||
/** File modification time.
|
||||
|
||||
As supplied by File::getLastModificationTime().
|
||||
*/
|
||||
Time modificationTime;
|
||||
|
||||
/** File creation time.
|
||||
|
||||
As supplied by File::getCreationTime().
|
||||
*/
|
||||
Time creationTime;
|
||||
|
||||
/** True if the file is a directory. */
|
||||
bool isDirectory;
|
||||
|
||||
/** True if the file is read-only. */
|
||||
bool isReadOnly;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the number of files currently available in the list.
|
||||
|
||||
The info about one of these files can be retrieved with getFileInfo() or
|
||||
getFile().
|
||||
|
||||
Obviously as the background thread runs and scans the directory for files, this
|
||||
number will change.
|
||||
|
||||
@see getFileInfo, getFile
|
||||
*/
|
||||
int getNumFiles() const;
|
||||
|
||||
/** Returns the cached information about one of the files in the list.
|
||||
|
||||
If the index is in-range, this will return true and will copy the file's details
|
||||
to the structure that is passed-in.
|
||||
|
||||
If it returns false, then the index wasn't in range, and the structure won't
|
||||
be affected.
|
||||
|
||||
@see getNumFiles, getFile
|
||||
*/
|
||||
bool getFileInfo (int index, FileInfo& resultInfo) const;
|
||||
|
||||
/** Returns one of the files in the list.
|
||||
|
||||
@param index should be less than getNumFiles(). If this is out-of-range, the
|
||||
return value will be File::nonexistent
|
||||
@see getNumFiles, getFileInfo
|
||||
*/
|
||||
File getFile (int index) const;
|
||||
|
||||
/** Returns the file filter being used.
|
||||
|
||||
The filter is specified in the constructor.
|
||||
*/
|
||||
const FileFilter* getFilter() const { return fileFilter; }
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
int useTimeSlice();
|
||||
/** @internal */
|
||||
TimeSliceThread& getTimeSliceThread() { return thread; }
|
||||
/** @internal */
|
||||
static int compareElements (const DirectoryContentsList::FileInfo* first,
|
||||
const DirectoryContentsList::FileInfo* second);
|
||||
|
||||
private:
|
||||
File root;
|
||||
const FileFilter* fileFilter;
|
||||
TimeSliceThread& thread;
|
||||
int fileTypeFlags;
|
||||
|
||||
CriticalSection fileListLock;
|
||||
OwnedArray <FileInfo> files;
|
||||
|
||||
ScopedPointer <DirectoryIterator> fileFindHandle;
|
||||
bool volatile shouldStop;
|
||||
|
||||
void changed();
|
||||
bool checkNextFile (bool& hasChanged);
|
||||
bool addFile (const File& file, bool isDir,
|
||||
const int64 fileSize, const Time& modTime,
|
||||
const Time& creationTime, bool isReadOnly);
|
||||
void setTypeFlags (int newFlags);
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DirectoryContentsList);
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCE_DIRECTORYCONTENTSLIST_JUCEHEADER__
|
||||
|
|
@ -0,0 +1,547 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
//==============================================================================
|
||||
FileBrowserComponent::FileBrowserComponent (int flags_,
|
||||
const File& initialFileOrDirectory,
|
||||
const FileFilter* fileFilter_,
|
||||
FilePreviewComponent* previewComp_)
|
||||
: FileFilter (String::empty),
|
||||
fileFilter (fileFilter_),
|
||||
flags (flags_),
|
||||
previewComp (previewComp_),
|
||||
currentPathBox ("path"),
|
||||
fileLabel ("f", TRANS ("file:")),
|
||||
thread ("Juce FileBrowser")
|
||||
{
|
||||
// You need to specify one or other of the open/save flags..
|
||||
jassert ((flags & (saveMode | openMode)) != 0);
|
||||
jassert ((flags & (saveMode | openMode)) != (saveMode | openMode));
|
||||
|
||||
// You need to specify at least one of these flags..
|
||||
jassert ((flags & (canSelectFiles | canSelectDirectories)) != 0);
|
||||
|
||||
String filename;
|
||||
|
||||
if (initialFileOrDirectory == File::nonexistent)
|
||||
{
|
||||
currentRoot = File::getCurrentWorkingDirectory();
|
||||
}
|
||||
else if (initialFileOrDirectory.isDirectory())
|
||||
{
|
||||
currentRoot = initialFileOrDirectory;
|
||||
}
|
||||
else
|
||||
{
|
||||
chosenFiles.add (initialFileOrDirectory);
|
||||
currentRoot = initialFileOrDirectory.getParentDirectory();
|
||||
filename = initialFileOrDirectory.getFileName();
|
||||
}
|
||||
|
||||
fileList = new DirectoryContentsList (this, thread);
|
||||
|
||||
if ((flags & useTreeView) != 0)
|
||||
{
|
||||
FileTreeComponent* const tree = new FileTreeComponent (*fileList);
|
||||
fileListComponent = tree;
|
||||
|
||||
if ((flags & canSelectMultipleItems) != 0)
|
||||
tree->setMultiSelectEnabled (true);
|
||||
|
||||
addAndMakeVisible (tree);
|
||||
}
|
||||
else
|
||||
{
|
||||
FileListComponent* const list = new FileListComponent (*fileList);
|
||||
fileListComponent = list;
|
||||
list->setOutlineThickness (1);
|
||||
|
||||
if ((flags & canSelectMultipleItems) != 0)
|
||||
list->setMultipleSelectionEnabled (true);
|
||||
|
||||
addAndMakeVisible (list);
|
||||
}
|
||||
|
||||
fileListComponent->addListener (this);
|
||||
|
||||
addAndMakeVisible (¤tPathBox);
|
||||
currentPathBox.setEditableText (true);
|
||||
resetRecentPaths();
|
||||
currentPathBox.addListener (this);
|
||||
|
||||
addAndMakeVisible (&filenameBox);
|
||||
filenameBox.setMultiLine (false);
|
||||
filenameBox.setSelectAllWhenFocused (true);
|
||||
filenameBox.setText (filename, false);
|
||||
filenameBox.addListener (this);
|
||||
filenameBox.setReadOnly ((flags & (filenameBoxIsReadOnly | canSelectMultipleItems)) != 0);
|
||||
|
||||
addAndMakeVisible (&fileLabel);
|
||||
fileLabel.attachToComponent (&filenameBox, true);
|
||||
|
||||
addAndMakeVisible (goUpButton = getLookAndFeel().createFileBrowserGoUpButton());
|
||||
goUpButton->addListener (this);
|
||||
goUpButton->setTooltip (TRANS ("go up to parent directory"));
|
||||
|
||||
if (previewComp != nullptr)
|
||||
addAndMakeVisible (previewComp);
|
||||
|
||||
setRoot (currentRoot);
|
||||
|
||||
thread.startThread (4);
|
||||
}
|
||||
|
||||
FileBrowserComponent::~FileBrowserComponent()
|
||||
{
|
||||
fileListComponent = nullptr;
|
||||
fileList = nullptr;
|
||||
thread.stopThread (10000);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void FileBrowserComponent::addListener (FileBrowserListener* const newListener)
|
||||
{
|
||||
listeners.add (newListener);
|
||||
}
|
||||
|
||||
void FileBrowserComponent::removeListener (FileBrowserListener* const listener)
|
||||
{
|
||||
listeners.remove (listener);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool FileBrowserComponent::isSaveMode() const noexcept
|
||||
{
|
||||
return (flags & saveMode) != 0;
|
||||
}
|
||||
|
||||
int FileBrowserComponent::getNumSelectedFiles() const noexcept
|
||||
{
|
||||
if (chosenFiles.size() == 0 && currentFileIsValid())
|
||||
return 1;
|
||||
|
||||
return chosenFiles.size();
|
||||
}
|
||||
|
||||
File FileBrowserComponent::getSelectedFile (int index) const noexcept
|
||||
{
|
||||
if ((flags & canSelectDirectories) != 0 && filenameBox.getText().isEmpty())
|
||||
return currentRoot;
|
||||
|
||||
if (! filenameBox.isReadOnly())
|
||||
return currentRoot.getChildFile (filenameBox.getText());
|
||||
|
||||
return chosenFiles[index];
|
||||
}
|
||||
|
||||
bool FileBrowserComponent::currentFileIsValid() const
|
||||
{
|
||||
if (isSaveMode())
|
||||
return ! getSelectedFile (0).isDirectory();
|
||||
else
|
||||
return getSelectedFile (0).exists();
|
||||
}
|
||||
|
||||
File FileBrowserComponent::getHighlightedFile() const noexcept
|
||||
{
|
||||
return fileListComponent->getSelectedFile (0);
|
||||
}
|
||||
|
||||
void FileBrowserComponent::deselectAllFiles()
|
||||
{
|
||||
fileListComponent->deselectAllFiles();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool FileBrowserComponent::isFileSuitable (const File& file) const
|
||||
{
|
||||
return (flags & canSelectFiles) != 0 && (fileFilter == nullptr || fileFilter->isFileSuitable (file));
|
||||
}
|
||||
|
||||
bool FileBrowserComponent::isDirectorySuitable (const File&) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FileBrowserComponent::isFileOrDirSuitable (const File& f) const
|
||||
{
|
||||
if (f.isDirectory())
|
||||
return (flags & canSelectDirectories) != 0
|
||||
&& (fileFilter == nullptr || fileFilter->isDirectorySuitable (f));
|
||||
|
||||
return (flags & canSelectFiles) != 0 && f.exists()
|
||||
&& (fileFilter == nullptr || fileFilter->isFileSuitable (f));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
const File& FileBrowserComponent::getRoot() const
|
||||
{
|
||||
return currentRoot;
|
||||
}
|
||||
|
||||
void FileBrowserComponent::setRoot (const File& newRootDirectory)
|
||||
{
|
||||
if (currentRoot != newRootDirectory)
|
||||
{
|
||||
fileListComponent->scrollToTop();
|
||||
|
||||
String path (newRootDirectory.getFullPathName());
|
||||
|
||||
if (path.isEmpty())
|
||||
path = File::separatorString;
|
||||
|
||||
StringArray rootNames, rootPaths;
|
||||
getRoots (rootNames, rootPaths);
|
||||
|
||||
if (! rootPaths.contains (path, true))
|
||||
{
|
||||
bool alreadyListed = false;
|
||||
|
||||
for (int i = currentPathBox.getNumItems(); --i >= 0;)
|
||||
{
|
||||
if (currentPathBox.getItemText (i).equalsIgnoreCase (path))
|
||||
{
|
||||
alreadyListed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (! alreadyListed)
|
||||
currentPathBox.addItem (path, currentPathBox.getNumItems() + 2);
|
||||
}
|
||||
}
|
||||
|
||||
currentRoot = newRootDirectory;
|
||||
fileList->setDirectory (currentRoot, true, true);
|
||||
|
||||
String currentRootName (currentRoot.getFullPathName());
|
||||
if (currentRootName.isEmpty())
|
||||
currentRootName = File::separatorString;
|
||||
|
||||
currentPathBox.setText (currentRootName, true);
|
||||
|
||||
goUpButton->setEnabled (currentRoot.getParentDirectory().isDirectory()
|
||||
&& currentRoot.getParentDirectory() != currentRoot);
|
||||
}
|
||||
|
||||
void FileBrowserComponent::resetRecentPaths()
|
||||
{
|
||||
currentPathBox.clear();
|
||||
|
||||
StringArray rootNames, rootPaths;
|
||||
getRoots (rootNames, rootPaths);
|
||||
|
||||
for (int i = 0; i < rootNames.size(); ++i)
|
||||
{
|
||||
if (rootNames[i].isEmpty())
|
||||
currentPathBox.addSeparator();
|
||||
else
|
||||
currentPathBox.addItem (rootNames[i], i + 1);
|
||||
}
|
||||
|
||||
currentPathBox.addSeparator();
|
||||
}
|
||||
|
||||
void FileBrowserComponent::goUp()
|
||||
{
|
||||
setRoot (getRoot().getParentDirectory());
|
||||
}
|
||||
|
||||
void FileBrowserComponent::refresh()
|
||||
{
|
||||
fileList->refresh();
|
||||
}
|
||||
|
||||
void FileBrowserComponent::setFileFilter (const FileFilter* const newFileFilter)
|
||||
{
|
||||
if (fileFilter != newFileFilter)
|
||||
{
|
||||
fileFilter = newFileFilter;
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
|
||||
const String FileBrowserComponent::getActionVerb() const
|
||||
{
|
||||
return isSaveMode() ? TRANS("Save") : TRANS("Open");
|
||||
}
|
||||
|
||||
FilePreviewComponent* FileBrowserComponent::getPreviewComponent() const noexcept
|
||||
{
|
||||
return previewComp;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void FileBrowserComponent::resized()
|
||||
{
|
||||
getLookAndFeel()
|
||||
.layoutFileBrowserComponent (*this, fileListComponent, previewComp,
|
||||
¤tPathBox, &filenameBox, goUpButton);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void FileBrowserComponent::sendListenerChangeMessage()
|
||||
{
|
||||
Component::BailOutChecker checker (this);
|
||||
|
||||
if (previewComp != nullptr)
|
||||
previewComp->selectedFileChanged (getSelectedFile (0));
|
||||
|
||||
// You shouldn't delete the browser when the file gets changed!
|
||||
jassert (! checker.shouldBailOut());
|
||||
|
||||
listeners.callChecked (checker, &FileBrowserListener::selectionChanged);
|
||||
}
|
||||
|
||||
void FileBrowserComponent::selectionChanged()
|
||||
{
|
||||
StringArray newFilenames;
|
||||
bool resetChosenFiles = true;
|
||||
|
||||
for (int i = 0; i < fileListComponent->getNumSelectedFiles(); ++i)
|
||||
{
|
||||
const File f (fileListComponent->getSelectedFile (i));
|
||||
|
||||
if (isFileOrDirSuitable (f))
|
||||
{
|
||||
if (resetChosenFiles)
|
||||
{
|
||||
chosenFiles.clear();
|
||||
resetChosenFiles = false;
|
||||
}
|
||||
|
||||
chosenFiles.add (f);
|
||||
newFilenames.add (f.getRelativePathFrom (getRoot()));
|
||||
}
|
||||
}
|
||||
|
||||
if (newFilenames.size() > 0)
|
||||
filenameBox.setText (newFilenames.joinIntoString (", "), false);
|
||||
|
||||
sendListenerChangeMessage();
|
||||
}
|
||||
|
||||
void FileBrowserComponent::fileClicked (const File& f, const MouseEvent& e)
|
||||
{
|
||||
Component::BailOutChecker checker (this);
|
||||
listeners.callChecked (checker, &FileBrowserListener::fileClicked, f, e);
|
||||
}
|
||||
|
||||
void FileBrowserComponent::fileDoubleClicked (const File& f)
|
||||
{
|
||||
if (f.isDirectory())
|
||||
{
|
||||
setRoot (f);
|
||||
|
||||
if ((flags & canSelectDirectories) != 0)
|
||||
filenameBox.setText (String::empty);
|
||||
}
|
||||
else
|
||||
{
|
||||
Component::BailOutChecker checker (this);
|
||||
listeners.callChecked (checker, &FileBrowserListener::fileDoubleClicked, f);
|
||||
}
|
||||
}
|
||||
|
||||
bool FileBrowserComponent::keyPressed (const KeyPress& key)
|
||||
{
|
||||
(void) key;
|
||||
|
||||
#if JUCE_LINUX || JUCE_WINDOWS
|
||||
if (key.getModifiers().isCommandDown()
|
||||
&& (key.getKeyCode() == 'H' || key.getKeyCode() == 'h'))
|
||||
{
|
||||
fileList->setIgnoresHiddenFiles (! fileList->ignoresHiddenFiles());
|
||||
fileList->refresh();
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void FileBrowserComponent::textEditorTextChanged (TextEditor&)
|
||||
{
|
||||
sendListenerChangeMessage();
|
||||
}
|
||||
|
||||
void FileBrowserComponent::textEditorReturnKeyPressed (TextEditor&)
|
||||
{
|
||||
if (filenameBox.getText().containsChar (File::separator))
|
||||
{
|
||||
const File f (currentRoot.getChildFile (filenameBox.getText()));
|
||||
|
||||
if (f.isDirectory())
|
||||
{
|
||||
setRoot (f);
|
||||
chosenFiles.clear();
|
||||
filenameBox.setText (String::empty);
|
||||
}
|
||||
else
|
||||
{
|
||||
setRoot (f.getParentDirectory());
|
||||
chosenFiles.clear();
|
||||
chosenFiles.add (f);
|
||||
filenameBox.setText (f.getFileName());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fileDoubleClicked (getSelectedFile (0));
|
||||
}
|
||||
}
|
||||
|
||||
void FileBrowserComponent::textEditorEscapeKeyPressed (TextEditor&)
|
||||
{
|
||||
}
|
||||
|
||||
void FileBrowserComponent::textEditorFocusLost (TextEditor&)
|
||||
{
|
||||
if (! isSaveMode())
|
||||
selectionChanged();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void FileBrowserComponent::buttonClicked (Button*)
|
||||
{
|
||||
goUp();
|
||||
}
|
||||
|
||||
void FileBrowserComponent::comboBoxChanged (ComboBox*)
|
||||
{
|
||||
const String newText (currentPathBox.getText().trim().unquoted());
|
||||
|
||||
if (newText.isNotEmpty())
|
||||
{
|
||||
const int index = currentPathBox.getSelectedId() - 1;
|
||||
|
||||
StringArray rootNames, rootPaths;
|
||||
getRoots (rootNames, rootPaths);
|
||||
|
||||
if (rootPaths [index].isNotEmpty())
|
||||
{
|
||||
setRoot (File (rootPaths [index]));
|
||||
}
|
||||
else
|
||||
{
|
||||
File f (newText);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (f.isDirectory())
|
||||
{
|
||||
setRoot (f);
|
||||
break;
|
||||
}
|
||||
|
||||
if (f.getParentDirectory() == f)
|
||||
break;
|
||||
|
||||
f = f.getParentDirectory();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FileBrowserComponent::getRoots (StringArray& rootNames, StringArray& rootPaths)
|
||||
{
|
||||
#if JUCE_WINDOWS
|
||||
Array<File> roots;
|
||||
File::findFileSystemRoots (roots);
|
||||
rootPaths.clear();
|
||||
|
||||
for (int i = 0; i < roots.size(); ++i)
|
||||
{
|
||||
const File& drive = roots.getReference(i);
|
||||
|
||||
String name (drive.getFullPathName());
|
||||
rootPaths.add (name);
|
||||
|
||||
if (drive.isOnHardDisk())
|
||||
{
|
||||
String volume (drive.getVolumeLabel());
|
||||
|
||||
if (volume.isEmpty())
|
||||
volume = TRANS("Hard Drive");
|
||||
|
||||
name << " [" << volume << ']';
|
||||
}
|
||||
else if (drive.isOnCDRomDrive())
|
||||
{
|
||||
name << TRANS(" [CD/DVD drive]");
|
||||
}
|
||||
|
||||
rootNames.add (name);
|
||||
}
|
||||
|
||||
rootPaths.add (String::empty);
|
||||
rootNames.add (String::empty);
|
||||
|
||||
rootPaths.add (File::getSpecialLocation (File::userDocumentsDirectory).getFullPathName());
|
||||
rootNames.add ("Documents");
|
||||
rootPaths.add (File::getSpecialLocation (File::userDesktopDirectory).getFullPathName());
|
||||
rootNames.add ("Desktop");
|
||||
|
||||
#elif JUCE_MAC
|
||||
rootPaths.add (File::getSpecialLocation (File::userHomeDirectory).getFullPathName());
|
||||
rootNames.add ("Home folder");
|
||||
rootPaths.add (File::getSpecialLocation (File::userDocumentsDirectory).getFullPathName());
|
||||
rootNames.add ("Documents");
|
||||
rootPaths.add (File::getSpecialLocation (File::userDesktopDirectory).getFullPathName());
|
||||
rootNames.add ("Desktop");
|
||||
|
||||
rootPaths.add (String::empty);
|
||||
rootNames.add (String::empty);
|
||||
|
||||
Array <File> volumes;
|
||||
File vol ("/Volumes");
|
||||
vol.findChildFiles (volumes, File::findDirectories, false);
|
||||
|
||||
for (int i = 0; i < volumes.size(); ++i)
|
||||
{
|
||||
const File& volume = volumes.getReference(i);
|
||||
|
||||
if (volume.isDirectory() && ! volume.getFileName().startsWithChar ('.'))
|
||||
{
|
||||
rootPaths.add (volume.getFullPathName());
|
||||
rootNames.add (volume.getFileName());
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
rootPaths.add ("/");
|
||||
rootNames.add ("/");
|
||||
rootPaths.add (File::getSpecialLocation (File::userHomeDirectory).getFullPathName());
|
||||
rootNames.add ("Home folder");
|
||||
rootPaths.add (File::getSpecialLocation (File::userDesktopDirectory).getFullPathName());
|
||||
rootNames.add ("Desktop");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
243
modules/juce_gui_basics/filebrowser/juce_FileBrowserComponent.h
Normal file
243
modules/juce_gui_basics/filebrowser/juce_FileBrowserComponent.h
Normal file
|
|
@ -0,0 +1,243 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_FILEBROWSERCOMPONENT_JUCEHEADER__
|
||||
#define __JUCE_FILEBROWSERCOMPONENT_JUCEHEADER__
|
||||
|
||||
#include "juce_DirectoryContentsDisplayComponent.h"
|
||||
#include "juce_FilePreviewComponent.h"
|
||||
#include "../widgets/juce_TextEditor.h"
|
||||
#include "../widgets/juce_ComboBox.h"
|
||||
#include "../buttons/juce_DrawableButton.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A component for browsing and selecting a file or directory to open or save.
|
||||
|
||||
This contains a FileListComponent and adds various boxes and controls for
|
||||
navigating and selecting a file. It can work in different modes so that it can
|
||||
be used for loading or saving a file, or for choosing a directory.
|
||||
|
||||
@see FileChooserDialogBox, FileChooser, FileListComponent
|
||||
*/
|
||||
class JUCE_API FileBrowserComponent : public Component,
|
||||
private FileBrowserListener,
|
||||
private TextEditorListener,
|
||||
private ButtonListener,
|
||||
private ComboBoxListener, // (can't use ComboBox::Listener due to idiotic VC2005 bug)
|
||||
private FileFilter
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Various options for the browser.
|
||||
|
||||
A combination of these is passed into the FileBrowserComponent constructor.
|
||||
*/
|
||||
enum FileChooserFlags
|
||||
{
|
||||
openMode = 1, /**< specifies that the component should allow the user to
|
||||
choose an existing file with the intention of opening it. */
|
||||
saveMode = 2, /**< specifies that the component should allow the user to specify
|
||||
the name of a file that will be used to save something. */
|
||||
canSelectFiles = 4, /**< specifies that the user can select files (can be used in
|
||||
conjunction with canSelectDirectories). */
|
||||
canSelectDirectories = 8, /**< specifies that the user can select directories (can be used in
|
||||
conjuction with canSelectFiles). */
|
||||
canSelectMultipleItems = 16, /**< specifies that the user can select multiple items. */
|
||||
useTreeView = 32, /**< specifies that a tree-view should be shown instead of a file list. */
|
||||
filenameBoxIsReadOnly = 64 /**< specifies that the user can't type directly into the filename box. */
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Creates a FileBrowserComponent.
|
||||
|
||||
@param flags A combination of flags from the FileChooserFlags enumeration,
|
||||
used to specify the component's behaviour. The flags must contain
|
||||
either openMode or saveMode, and canSelectFiles and/or
|
||||
canSelectDirectories.
|
||||
@param initialFileOrDirectory The file or directory that should be selected when
|
||||
the component begins. If this is File::nonexistent,
|
||||
a default directory will be chosen.
|
||||
@param fileFilter an optional filter to use to determine which files
|
||||
are shown. If this is 0 then all files are displayed. Note
|
||||
that a pointer is kept internally to this object, so
|
||||
make sure that it is not deleted before the browser object
|
||||
is deleted.
|
||||
@param previewComp an optional preview component that will be used to
|
||||
show previews of files that the user selects
|
||||
*/
|
||||
FileBrowserComponent (int flags,
|
||||
const File& initialFileOrDirectory,
|
||||
const FileFilter* fileFilter,
|
||||
FilePreviewComponent* previewComp);
|
||||
|
||||
/** Destructor. */
|
||||
~FileBrowserComponent();
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the number of files that the user has got selected.
|
||||
If multiple select isn't active, this will only be 0 or 1. To get the complete
|
||||
list of files they've chosen, pass an index to getCurrentFile().
|
||||
*/
|
||||
int getNumSelectedFiles() const noexcept;
|
||||
|
||||
/** Returns one of the files that the user has chosen.
|
||||
If the box has multi-select enabled, the index lets you specify which of the files
|
||||
to get - see getNumSelectedFiles() to find out how many files were chosen.
|
||||
@see getHighlightedFile
|
||||
*/
|
||||
File getSelectedFile (int index) const noexcept;
|
||||
|
||||
/** Deselects any files that are currently selected.
|
||||
*/
|
||||
void deselectAllFiles();
|
||||
|
||||
/** Returns true if the currently selected file(s) are usable.
|
||||
|
||||
This can be used to decide whether the user can press "ok" for the
|
||||
current file. What it does depends on the mode, so for example in an "open"
|
||||
mode, this only returns true if a file has been selected and if it exists.
|
||||
In a "save" mode, a non-existent file would also be valid.
|
||||
*/
|
||||
bool currentFileIsValid() const;
|
||||
|
||||
/** This returns the last item in the view that the user has highlighted.
|
||||
This may be different from getCurrentFile(), which returns the value
|
||||
that is shown in the filename box, and if there are multiple selections,
|
||||
this will only return one of them.
|
||||
@see getSelectedFile
|
||||
*/
|
||||
File getHighlightedFile() const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the directory whose contents are currently being shown in the listbox. */
|
||||
const File& getRoot() const;
|
||||
|
||||
/** Changes the directory that's being shown in the listbox. */
|
||||
void setRoot (const File& newRootDirectory);
|
||||
|
||||
/** Equivalent to pressing the "up" button to browse the parent directory. */
|
||||
void goUp();
|
||||
|
||||
/** Refreshes the directory that's currently being listed. */
|
||||
void refresh();
|
||||
|
||||
/** Changes the filter that's being used to sift the files. */
|
||||
void setFileFilter (const FileFilter* newFileFilter);
|
||||
|
||||
/** Returns a verb to describe what should happen when the file is accepted.
|
||||
|
||||
E.g. if browsing in "load file" mode, this will be "Open", if in "save file"
|
||||
mode, it'll be "Save", etc.
|
||||
*/
|
||||
virtual const String getActionVerb() const;
|
||||
|
||||
/** Returns true if the saveMode flag was set when this component was created.
|
||||
*/
|
||||
bool isSaveMode() const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Adds a listener to be told when the user selects and clicks on files.
|
||||
|
||||
@see removeListener
|
||||
*/
|
||||
void addListener (FileBrowserListener* listener);
|
||||
|
||||
/** Removes a listener.
|
||||
|
||||
@see addListener
|
||||
*/
|
||||
void removeListener (FileBrowserListener* listener);
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void resized();
|
||||
/** @internal */
|
||||
void buttonClicked (Button* b);
|
||||
/** @internal */
|
||||
void comboBoxChanged (ComboBox*);
|
||||
/** @internal */
|
||||
void textEditorTextChanged (TextEditor& editor);
|
||||
/** @internal */
|
||||
void textEditorReturnKeyPressed (TextEditor& editor);
|
||||
/** @internal */
|
||||
void textEditorEscapeKeyPressed (TextEditor& editor);
|
||||
/** @internal */
|
||||
void textEditorFocusLost (TextEditor& editor);
|
||||
/** @internal */
|
||||
bool keyPressed (const KeyPress& key);
|
||||
/** @internal */
|
||||
void selectionChanged();
|
||||
/** @internal */
|
||||
void fileClicked (const File& f, const MouseEvent& e);
|
||||
/** @internal */
|
||||
void fileDoubleClicked (const File& f);
|
||||
/** @internal */
|
||||
bool isFileSuitable (const File& file) const;
|
||||
/** @internal */
|
||||
bool isDirectorySuitable (const File&) const;
|
||||
|
||||
/** @internal */
|
||||
FilePreviewComponent* getPreviewComponent() const noexcept;
|
||||
|
||||
protected:
|
||||
/** Returns a list of names and paths for the default places the user might want to look.
|
||||
Use an empty string to indicate a section break.
|
||||
*/
|
||||
virtual void getRoots (StringArray& rootNames, StringArray& rootPaths);
|
||||
|
||||
/** Updates the items in the dropdown list of recent paths with the values from getRoots(). */
|
||||
void resetRecentPaths();
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
ScopedPointer <DirectoryContentsList> fileList;
|
||||
const FileFilter* fileFilter;
|
||||
|
||||
int flags;
|
||||
File currentRoot;
|
||||
Array<File> chosenFiles;
|
||||
ListenerList <FileBrowserListener> listeners;
|
||||
|
||||
ScopedPointer<DirectoryContentsDisplayComponent> fileListComponent;
|
||||
FilePreviewComponent* previewComp;
|
||||
ComboBox currentPathBox;
|
||||
TextEditor filenameBox;
|
||||
Label fileLabel;
|
||||
ScopedPointer<Button> goUpButton;
|
||||
|
||||
TimeSliceThread thread;
|
||||
|
||||
void sendListenerChangeMessage();
|
||||
bool isFileOrDirSuitable (const File& f) const;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileBrowserComponent);
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif // __JUCE_FILEBROWSERCOMPONENT_JUCEHEADER__
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_FILEBROWSERLISTENER_JUCEHEADER__
|
||||
#define __JUCE_FILEBROWSERLISTENER_JUCEHEADER__
|
||||
|
||||
#include "../mouse/juce_MouseEvent.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A listener for user selection events in a file browser.
|
||||
|
||||
This is used by a FileBrowserComponent or FileListComponent.
|
||||
*/
|
||||
class JUCE_API FileBrowserListener
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Destructor. */
|
||||
virtual ~FileBrowserListener();
|
||||
|
||||
//==============================================================================
|
||||
/** Callback when the user selects a different file in the browser. */
|
||||
virtual void selectionChanged() = 0;
|
||||
|
||||
/** Callback when the user clicks on a file in the browser. */
|
||||
virtual void fileClicked (const File& file, const MouseEvent& e) = 0;
|
||||
|
||||
/** Callback when the user double-clicks on a file in the browser. */
|
||||
virtual void fileDoubleClicked (const File& file) = 0;
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCE_FILEBROWSERLISTENER_JUCEHEADER__
|
||||
173
modules/juce_gui_basics/filebrowser/juce_FileChooser.cpp
Normal file
173
modules/juce_gui_basics/filebrowser/juce_FileChooser.cpp
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
//==============================================================================
|
||||
FileChooser::FileChooser (const String& chooserBoxTitle,
|
||||
const File& currentFileOrDirectory,
|
||||
const String& fileFilters,
|
||||
const bool useNativeDialogBox_)
|
||||
: title (chooserBoxTitle),
|
||||
filters (fileFilters),
|
||||
startingFile (currentFileOrDirectory),
|
||||
useNativeDialogBox (useNativeDialogBox_)
|
||||
{
|
||||
#if JUCE_LINUX
|
||||
useNativeDialogBox = false;
|
||||
#endif
|
||||
|
||||
if (! fileFilters.containsNonWhitespaceChars())
|
||||
filters = "*";
|
||||
}
|
||||
|
||||
FileChooser::~FileChooser()
|
||||
{
|
||||
}
|
||||
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
bool FileChooser::browseForFileToOpen (FilePreviewComponent* previewComponent)
|
||||
{
|
||||
return showDialog (false, true, false, false, false, previewComponent);
|
||||
}
|
||||
|
||||
bool FileChooser::browseForMultipleFilesToOpen (FilePreviewComponent* previewComponent)
|
||||
{
|
||||
return showDialog (false, true, false, false, true, previewComponent);
|
||||
}
|
||||
|
||||
bool FileChooser::browseForMultipleFilesOrDirectories (FilePreviewComponent* previewComponent)
|
||||
{
|
||||
return showDialog (true, true, false, false, true, previewComponent);
|
||||
}
|
||||
|
||||
bool FileChooser::browseForFileToSave (const bool warnAboutOverwritingExistingFiles)
|
||||
{
|
||||
return showDialog (false, true, true, warnAboutOverwritingExistingFiles, false, nullptr);
|
||||
}
|
||||
|
||||
bool FileChooser::browseForDirectory()
|
||||
{
|
||||
return showDialog (true, false, false, false, false, nullptr);
|
||||
}
|
||||
|
||||
bool FileChooser::showDialog (const bool selectsDirectories,
|
||||
const bool selectsFiles,
|
||||
const bool isSave,
|
||||
const bool warnAboutOverwritingExistingFiles,
|
||||
const bool selectMultipleFiles,
|
||||
FilePreviewComponent* const previewComponent)
|
||||
{
|
||||
WeakReference<Component> previouslyFocused (Component::getCurrentlyFocusedComponent());
|
||||
|
||||
results.clear();
|
||||
|
||||
// the preview component needs to be the right size before you pass it in here..
|
||||
jassert (previewComponent == nullptr || (previewComponent->getWidth() > 10
|
||||
&& previewComponent->getHeight() > 10));
|
||||
|
||||
#if JUCE_WINDOWS
|
||||
if (useNativeDialogBox && ! (selectsFiles && selectsDirectories))
|
||||
#elif JUCE_MAC
|
||||
if (useNativeDialogBox && (previewComponent == nullptr))
|
||||
#else
|
||||
if (false)
|
||||
#endif
|
||||
{
|
||||
showPlatformDialog (results, title, startingFile, filters,
|
||||
selectsDirectories, selectsFiles, isSave,
|
||||
warnAboutOverwritingExistingFiles,
|
||||
selectMultipleFiles,
|
||||
previewComponent);
|
||||
}
|
||||
else
|
||||
{
|
||||
WildcardFileFilter wildcard (selectsFiles ? filters : String::empty,
|
||||
selectsDirectories ? "*" : String::empty,
|
||||
String::empty);
|
||||
|
||||
int flags = isSave ? FileBrowserComponent::saveMode
|
||||
: FileBrowserComponent::openMode;
|
||||
|
||||
if (selectsFiles)
|
||||
flags |= FileBrowserComponent::canSelectFiles;
|
||||
|
||||
if (selectsDirectories)
|
||||
{
|
||||
flags |= FileBrowserComponent::canSelectDirectories;
|
||||
|
||||
if (! isSave)
|
||||
flags |= FileBrowserComponent::filenameBoxIsReadOnly;
|
||||
}
|
||||
|
||||
if (selectMultipleFiles)
|
||||
flags |= FileBrowserComponent::canSelectMultipleItems;
|
||||
|
||||
FileBrowserComponent browserComponent (flags, startingFile, &wildcard, previewComponent);
|
||||
|
||||
FileChooserDialogBox box (title, String::empty,
|
||||
browserComponent,
|
||||
warnAboutOverwritingExistingFiles,
|
||||
browserComponent.findColour (AlertWindow::backgroundColourId));
|
||||
|
||||
if (box.show())
|
||||
{
|
||||
for (int i = 0; i < browserComponent.getNumSelectedFiles(); ++i)
|
||||
results.add (browserComponent.getSelectedFile (i));
|
||||
}
|
||||
}
|
||||
|
||||
if (previouslyFocused != nullptr)
|
||||
previouslyFocused->grabKeyboardFocus();
|
||||
|
||||
return results.size() > 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
File FileChooser::getResult() const
|
||||
{
|
||||
// if you've used a multiple-file select, you should use the getResults() method
|
||||
// to retrieve all the files that were chosen.
|
||||
jassert (results.size() <= 1);
|
||||
|
||||
return results.getFirst();
|
||||
}
|
||||
|
||||
const Array<File>& FileChooser::getResults() const
|
||||
{
|
||||
return results;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
FilePreviewComponent::FilePreviewComponent()
|
||||
{
|
||||
}
|
||||
|
||||
FilePreviewComponent::~FilePreviewComponent()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
192
modules/juce_gui_basics/filebrowser/juce_FileChooser.h
Normal file
192
modules/juce_gui_basics/filebrowser/juce_FileChooser.h
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_FILECHOOSER_JUCEHEADER__
|
||||
#define __JUCE_FILECHOOSER_JUCEHEADER__
|
||||
|
||||
#include "juce_FilePreviewComponent.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Creates a dialog box to choose a file or directory to load or save.
|
||||
|
||||
To use a FileChooser:
|
||||
- create one (as a local stack variable is the neatest way)
|
||||
- call one of its browseFor.. methods
|
||||
- if this returns true, the user has selected a file, so you can retrieve it
|
||||
with the getResult() method.
|
||||
|
||||
e.g. @code
|
||||
void loadMooseFile()
|
||||
{
|
||||
FileChooser myChooser ("Please select the moose you want to load...",
|
||||
File::getSpecialLocation (File::userHomeDirectory),
|
||||
"*.moose");
|
||||
|
||||
if (myChooser.browseForFileToOpen())
|
||||
{
|
||||
File mooseFile (myChooser.getResult());
|
||||
|
||||
loadMoose (mooseFile);
|
||||
}
|
||||
}
|
||||
@endcode
|
||||
*/
|
||||
class JUCE_API FileChooser
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a FileChooser.
|
||||
|
||||
After creating one of these, use one of the browseFor... methods to display it.
|
||||
|
||||
@param dialogBoxTitle a text string to display in the dialog box to
|
||||
tell the user what's going on
|
||||
@param initialFileOrDirectory the file or directory that should be selected when
|
||||
the dialog box opens. If this parameter is set to
|
||||
File::nonexistent, a sensible default directory
|
||||
will be used instead.
|
||||
@param filePatternsAllowed a set of file patterns to specify which files can be
|
||||
selected - each pattern should be separated by a
|
||||
comma or semi-colon, e.g. "*" or "*.jpg;*.gif". An
|
||||
empty string means that all files are allowed
|
||||
@param useOSNativeDialogBox if true, then a native dialog box will be used if
|
||||
possible; if false, then a Juce-based browser dialog
|
||||
box will always be used
|
||||
@see browseForFileToOpen, browseForFileToSave, browseForDirectory
|
||||
*/
|
||||
FileChooser (const String& dialogBoxTitle,
|
||||
const File& initialFileOrDirectory = File::nonexistent,
|
||||
const String& filePatternsAllowed = String::empty,
|
||||
bool useOSNativeDialogBox = true);
|
||||
|
||||
/** Destructor. */
|
||||
~FileChooser();
|
||||
|
||||
//==============================================================================
|
||||
/** Shows a dialog box to choose a file to open.
|
||||
|
||||
This will display the dialog box modally, using an "open file" mode, so that
|
||||
it won't allow non-existent files or directories to be chosen.
|
||||
|
||||
@param previewComponent an optional component to display inside the dialog
|
||||
box to show special info about the files that the user
|
||||
is browsing. The component will not be deleted by this
|
||||
object, so the caller must take care of it.
|
||||
@returns true if the user selected a file, in which case, use the getResult()
|
||||
method to find out what it was. Returns false if they cancelled instead.
|
||||
@see browseForFileToSave, browseForDirectory
|
||||
*/
|
||||
bool browseForFileToOpen (FilePreviewComponent* previewComponent = 0);
|
||||
|
||||
/** Same as browseForFileToOpen, but allows the user to select multiple files.
|
||||
|
||||
The files that are returned can be obtained by calling getResults(). See
|
||||
browseForFileToOpen() for more info about the behaviour of this method.
|
||||
*/
|
||||
bool browseForMultipleFilesToOpen (FilePreviewComponent* previewComponent = 0);
|
||||
|
||||
/** Shows a dialog box to choose a file to save.
|
||||
|
||||
This will display the dialog box modally, using an "save file" mode, so it
|
||||
will allow non-existent files to be chosen, but not directories.
|
||||
|
||||
@param warnAboutOverwritingExistingFiles if true, the dialog box will ask
|
||||
the user if they're sure they want to overwrite a file that already
|
||||
exists
|
||||
@returns true if the user chose a file and pressed 'ok', in which case, use
|
||||
the getResult() method to find out what the file was. Returns false
|
||||
if they cancelled instead.
|
||||
@see browseForFileToOpen, browseForDirectory
|
||||
*/
|
||||
bool browseForFileToSave (bool warnAboutOverwritingExistingFiles);
|
||||
|
||||
/** Shows a dialog box to choose a directory.
|
||||
|
||||
This will display the dialog box modally, using an "open directory" mode, so it
|
||||
will only allow directories to be returned, not files.
|
||||
|
||||
@returns true if the user chose a directory and pressed 'ok', in which case, use
|
||||
the getResult() method to find out what they chose. Returns false
|
||||
if they cancelled instead.
|
||||
@see browseForFileToOpen, browseForFileToSave
|
||||
*/
|
||||
bool browseForDirectory();
|
||||
|
||||
/** Same as browseForFileToOpen, but allows the user to select multiple files and directories.
|
||||
|
||||
The files that are returned can be obtained by calling getResults(). See
|
||||
browseForFileToOpen() for more info about the behaviour of this method.
|
||||
*/
|
||||
bool browseForMultipleFilesOrDirectories (FilePreviewComponent* previewComponent = 0);
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the last file that was chosen by one of the browseFor methods.
|
||||
|
||||
After calling the appropriate browseFor... method, this method lets you
|
||||
find out what file or directory they chose.
|
||||
|
||||
Note that the file returned is only valid if the browse method returned true (i.e.
|
||||
if the user pressed 'ok' rather than cancelling).
|
||||
|
||||
If you're using a multiple-file select, then use the getResults() method instead,
|
||||
to obtain the list of all files chosen.
|
||||
|
||||
@see getResults
|
||||
*/
|
||||
File getResult() const;
|
||||
|
||||
/** Returns a list of all the files that were chosen during the last call to a
|
||||
browse method.
|
||||
|
||||
This array may be empty if no files were chosen, or can contain multiple entries
|
||||
if multiple files were chosen.
|
||||
|
||||
@see getResult
|
||||
*/
|
||||
const Array<File>& getResults() const;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
String title, filters;
|
||||
File startingFile;
|
||||
Array<File> results;
|
||||
bool useNativeDialogBox;
|
||||
|
||||
bool showDialog (bool selectsDirectories, bool selectsFiles, bool isSave,
|
||||
bool warnAboutOverwritingExistingFiles, bool selectMultipleFiles,
|
||||
FilePreviewComponent* previewComponent);
|
||||
|
||||
static void showPlatformDialog (Array<File>& results, const String& title, const File& file,
|
||||
const String& filters, bool selectsDirectories, bool selectsFiles,
|
||||
bool isSave, bool warnAboutOverwritingExistingFiles, bool selectMultipleFiles,
|
||||
FilePreviewComponent* previewComponent);
|
||||
|
||||
JUCE_LEAK_DETECTOR (FileChooser);
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCE_FILECHOOSER_JUCEHEADER__
|
||||
|
|
@ -0,0 +1,282 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
//==============================================================================
|
||||
class FileChooserDialogBox::ContentComponent : public Component
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
ContentComponent (const String& name, const String& instructions_, FileBrowserComponent& chooserComponent_)
|
||||
: Component (name),
|
||||
chooserComponent (chooserComponent_),
|
||||
okButton (chooserComponent_.getActionVerb()),
|
||||
cancelButton (TRANS ("Cancel")),
|
||||
newFolderButton (TRANS ("New Folder")),
|
||||
instructions (instructions_)
|
||||
{
|
||||
addAndMakeVisible (&chooserComponent);
|
||||
|
||||
addAndMakeVisible (&okButton);
|
||||
okButton.addShortcut (KeyPress::returnKey);
|
||||
|
||||
addAndMakeVisible (&cancelButton);
|
||||
cancelButton.addShortcut (KeyPress::escapeKey);
|
||||
|
||||
addChildComponent (&newFolderButton);
|
||||
|
||||
setInterceptsMouseClicks (false, true);
|
||||
}
|
||||
|
||||
void paint (Graphics& g)
|
||||
{
|
||||
g.setColour (getLookAndFeel().findColour (FileChooserDialogBox::titleTextColourId));
|
||||
text.draw (g);
|
||||
}
|
||||
|
||||
void resized()
|
||||
{
|
||||
const int buttonHeight = 26;
|
||||
|
||||
Rectangle<int> area (getLocalBounds());
|
||||
|
||||
getLookAndFeel().createFileChooserHeaderText (getName(), instructions, text, getWidth());
|
||||
const Rectangle<float> bb (text.getBoundingBox (0, text.getNumGlyphs(), false));
|
||||
area.removeFromTop (roundToInt (bb.getBottom()) + 10);
|
||||
|
||||
chooserComponent.setBounds (area.removeFromTop (area.getHeight() - buttonHeight - 20));
|
||||
Rectangle<int> buttonArea (area.reduced (16, 10));
|
||||
|
||||
okButton.changeWidthToFitText (buttonHeight);
|
||||
okButton.setBounds (buttonArea.removeFromRight (okButton.getWidth() + 16));
|
||||
|
||||
buttonArea.removeFromRight (16);
|
||||
|
||||
cancelButton.changeWidthToFitText (buttonHeight);
|
||||
cancelButton.setBounds (buttonArea.removeFromRight (cancelButton.getWidth()));
|
||||
|
||||
newFolderButton.changeWidthToFitText (buttonHeight);
|
||||
newFolderButton.setBounds (buttonArea.removeFromLeft (newFolderButton.getWidth()));
|
||||
}
|
||||
|
||||
FileBrowserComponent& chooserComponent;
|
||||
TextButton okButton, cancelButton, newFolderButton;
|
||||
|
||||
private:
|
||||
String instructions;
|
||||
GlyphArrangement text;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
FileChooserDialogBox::FileChooserDialogBox (const String& name,
|
||||
const String& instructions,
|
||||
FileBrowserComponent& chooserComponent,
|
||||
const bool warnAboutOverwritingExistingFiles_,
|
||||
const Colour& backgroundColour)
|
||||
: ResizableWindow (name, backgroundColour, true),
|
||||
warnAboutOverwritingExistingFiles (warnAboutOverwritingExistingFiles_)
|
||||
{
|
||||
content = new ContentComponent (name, instructions, chooserComponent);
|
||||
setContentOwned (content, false);
|
||||
|
||||
setResizable (true, true);
|
||||
setResizeLimits (300, 300, 1200, 1000);
|
||||
|
||||
content->okButton.addListener (this);
|
||||
content->cancelButton.addListener (this);
|
||||
content->newFolderButton.addListener (this);
|
||||
content->chooserComponent.addListener (this);
|
||||
|
||||
FileChooserDialogBox::selectionChanged();
|
||||
}
|
||||
|
||||
FileChooserDialogBox::~FileChooserDialogBox()
|
||||
{
|
||||
content->chooserComponent.removeListener (this);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
bool FileChooserDialogBox::show (int w, int h)
|
||||
{
|
||||
return showAt (-1, -1, w, h);
|
||||
}
|
||||
|
||||
bool FileChooserDialogBox::showAt (int x, int y, int w, int h)
|
||||
{
|
||||
if (w <= 0)
|
||||
{
|
||||
Component* const previewComp = content->chooserComponent.getPreviewComponent();
|
||||
|
||||
if (previewComp != nullptr)
|
||||
w = 400 + previewComp->getWidth();
|
||||
else
|
||||
w = 600;
|
||||
}
|
||||
|
||||
if (h <= 0)
|
||||
h = 500;
|
||||
|
||||
if (x < 0 || y < 0)
|
||||
centreWithSize (w, h);
|
||||
else
|
||||
setBounds (x, y, w, h);
|
||||
|
||||
const bool ok = (runModalLoop() != 0);
|
||||
setVisible (false);
|
||||
return ok;
|
||||
}
|
||||
#endif
|
||||
|
||||
void FileChooserDialogBox::centreWithDefaultSize (Component* componentToCentreAround)
|
||||
{
|
||||
Component* const previewComp = content->chooserComponent.getPreviewComponent();
|
||||
|
||||
centreAroundComponent (componentToCentreAround,
|
||||
previewComp != nullptr ? 400 + previewComp->getWidth() : 600,
|
||||
500);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void FileChooserDialogBox::buttonClicked (Button* button)
|
||||
{
|
||||
if (button == &(content->okButton))
|
||||
{
|
||||
okButtonPressed();
|
||||
}
|
||||
else if (button == &(content->cancelButton))
|
||||
{
|
||||
closeButtonPressed();
|
||||
}
|
||||
else if (button == &(content->newFolderButton))
|
||||
{
|
||||
createNewFolder();
|
||||
}
|
||||
}
|
||||
|
||||
void FileChooserDialogBox::closeButtonPressed()
|
||||
{
|
||||
setVisible (false);
|
||||
}
|
||||
|
||||
void FileChooserDialogBox::selectionChanged()
|
||||
{
|
||||
content->okButton.setEnabled (content->chooserComponent.currentFileIsValid());
|
||||
|
||||
content->newFolderButton.setVisible (content->chooserComponent.isSaveMode()
|
||||
&& content->chooserComponent.getRoot().isDirectory());
|
||||
}
|
||||
|
||||
void FileChooserDialogBox::fileClicked (const File&, const MouseEvent&)
|
||||
{
|
||||
}
|
||||
|
||||
void FileChooserDialogBox::fileDoubleClicked (const File&)
|
||||
{
|
||||
selectionChanged();
|
||||
content->okButton.triggerClick();
|
||||
}
|
||||
|
||||
void FileChooserDialogBox::okToOverwriteFileCallback (int result, FileChooserDialogBox* box)
|
||||
{
|
||||
if (result != 0 && box != nullptr)
|
||||
box->exitModalState (1);
|
||||
}
|
||||
|
||||
void FileChooserDialogBox::okButtonPressed()
|
||||
{
|
||||
if (warnAboutOverwritingExistingFiles
|
||||
&& content->chooserComponent.isSaveMode()
|
||||
&& content->chooserComponent.getSelectedFile(0).exists())
|
||||
{
|
||||
AlertWindow::showOkCancelBox (AlertWindow::WarningIcon,
|
||||
TRANS("File already exists"),
|
||||
TRANS("There's already a file called:")
|
||||
+ "\n\n" + content->chooserComponent.getSelectedFile(0).getFullPathName()
|
||||
+ "\n\n" + TRANS("Are you sure you want to overwrite it?"),
|
||||
TRANS("overwrite"),
|
||||
TRANS("cancel"),
|
||||
this,
|
||||
ModalCallbackFunction::forComponent (okToOverwriteFileCallback, this));
|
||||
}
|
||||
else
|
||||
{
|
||||
exitModalState (1);
|
||||
}
|
||||
}
|
||||
|
||||
void FileChooserDialogBox::createNewFolderCallback (int result, FileChooserDialogBox* box,
|
||||
Component::SafePointer<AlertWindow> alert)
|
||||
{
|
||||
if (result != 0 && alert != nullptr && box != nullptr)
|
||||
{
|
||||
alert->setVisible (false);
|
||||
box->createNewFolderConfirmed (alert->getTextEditorContents ("name"));
|
||||
}
|
||||
}
|
||||
|
||||
void FileChooserDialogBox::createNewFolder()
|
||||
{
|
||||
File parent (content->chooserComponent.getRoot());
|
||||
|
||||
if (parent.isDirectory())
|
||||
{
|
||||
AlertWindow* aw = new AlertWindow (TRANS("New Folder"),
|
||||
TRANS("Please enter the name for the folder"),
|
||||
AlertWindow::NoIcon, this);
|
||||
|
||||
aw->addTextEditor ("name", String::empty, String::empty, false);
|
||||
aw->addButton (TRANS("ok"), 1, KeyPress::returnKey);
|
||||
aw->addButton (TRANS("cancel"), KeyPress::escapeKey);
|
||||
|
||||
aw->enterModalState (true,
|
||||
ModalCallbackFunction::forComponent (createNewFolderCallback, this,
|
||||
Component::SafePointer<AlertWindow> (aw)),
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
||||
void FileChooserDialogBox::createNewFolderConfirmed (const String& nameFromDialog)
|
||||
{
|
||||
const String name (File::createLegalFileName (nameFromDialog));
|
||||
|
||||
if (! name.isEmpty())
|
||||
{
|
||||
const File parent (content->chooserComponent.getRoot());
|
||||
|
||||
if (! parent.getChildFile (name).createDirectory())
|
||||
{
|
||||
AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
|
||||
TRANS ("New Folder"),
|
||||
TRANS ("Couldn't create the folder!"));
|
||||
}
|
||||
|
||||
content->chooserComponent.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
167
modules/juce_gui_basics/filebrowser/juce_FileChooserDialogBox.h
Normal file
167
modules/juce_gui_basics/filebrowser/juce_FileChooserDialogBox.h
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_FILECHOOSERDIALOGBOX_JUCEHEADER__
|
||||
#define __JUCE_FILECHOOSERDIALOGBOX_JUCEHEADER__
|
||||
|
||||
#include "juce_FileBrowserComponent.h"
|
||||
#include "../windows/juce_ResizableWindow.h"
|
||||
#include "../buttons/juce_TextButton.h"
|
||||
#include "../windows/juce_AlertWindow.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A file open/save dialog box.
|
||||
|
||||
This is a Juce-based file dialog box; to use a native file chooser, see the
|
||||
FileChooser class.
|
||||
|
||||
To use one of these, create it and call its show() method. e.g.
|
||||
|
||||
@code
|
||||
{
|
||||
WildcardFileFilter wildcardFilter ("*.foo", String::empty, "Foo files");
|
||||
|
||||
FileBrowserComponent browser (FileBrowserComponent::canSelectFiles,
|
||||
File::nonexistent,
|
||||
&wildcardFilter,
|
||||
nullptr);
|
||||
|
||||
FileChooserDialogBox dialogBox ("Open some kind of file",
|
||||
"Please choose some kind of file that you want to open...",
|
||||
browser,
|
||||
false,
|
||||
Colours::lightgrey);
|
||||
|
||||
if (dialogBox.show())
|
||||
{
|
||||
File selectedFile = browser.getSelectedFile (0);
|
||||
|
||||
...etc..
|
||||
}
|
||||
}
|
||||
@endcode
|
||||
|
||||
@see FileChooser
|
||||
*/
|
||||
class JUCE_API FileChooserDialogBox : public ResizableWindow,
|
||||
public ButtonListener, // (can't use Button::Listener due to idiotic VC2005 bug)
|
||||
public FileBrowserListener
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a file chooser box.
|
||||
|
||||
@param title the main title to show at the top of the box
|
||||
@param instructions an optional longer piece of text to show below the title in
|
||||
a smaller font, describing in more detail what's required.
|
||||
@param browserComponent a FileBrowserComponent that will be shown inside this dialog
|
||||
box. Make sure you delete this after (but not before!) the
|
||||
dialog box has been deleted.
|
||||
@param warnAboutOverwritingExistingFiles if true, then the user will be asked to confirm
|
||||
if they try to select a file that already exists. (This
|
||||
flag is only used when saving files)
|
||||
@param backgroundColour the background colour for the top level window
|
||||
|
||||
@see FileBrowserComponent, FilePreviewComponent
|
||||
*/
|
||||
FileChooserDialogBox (const String& title,
|
||||
const String& instructions,
|
||||
FileBrowserComponent& browserComponent,
|
||||
bool warnAboutOverwritingExistingFiles,
|
||||
const Colour& backgroundColour);
|
||||
|
||||
/** Destructor. */
|
||||
~FileChooserDialogBox();
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
/** Displays and runs the dialog box modally.
|
||||
|
||||
This will show the box with the specified size, returning true if the user
|
||||
pressed 'ok', or false if they cancelled.
|
||||
|
||||
Leave the width or height as 0 to use the default size
|
||||
*/
|
||||
bool show (int width = 0, int height = 0);
|
||||
|
||||
/** Displays and runs the dialog box modally.
|
||||
|
||||
This will show the box with the specified size at the specified location,
|
||||
returning true if the user pressed 'ok', or false if they cancelled.
|
||||
|
||||
Leave the width or height as 0 to use the default size.
|
||||
*/
|
||||
bool showAt (int x, int y, int width, int height);
|
||||
#endif
|
||||
|
||||
/** Sets the size of this dialog box to its default and positions it either in the
|
||||
centre of the screen, or centred around a component that is provided.
|
||||
*/
|
||||
void centreWithDefaultSize (Component* componentToCentreAround = 0);
|
||||
|
||||
//==============================================================================
|
||||
/** A set of colour IDs to use to change the colour of various aspects of the box.
|
||||
|
||||
These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
|
||||
methods.
|
||||
|
||||
@see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
|
||||
*/
|
||||
enum ColourIds
|
||||
{
|
||||
titleTextColourId = 0x1000850, /**< The colour to use to draw the box's title. */
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void buttonClicked (Button* button);
|
||||
/** @internal */
|
||||
void closeButtonPressed();
|
||||
/** @internal */
|
||||
void selectionChanged();
|
||||
/** @internal */
|
||||
void fileClicked (const File& file, const MouseEvent& e);
|
||||
/** @internal */
|
||||
void fileDoubleClicked (const File& file);
|
||||
|
||||
private:
|
||||
class ContentComponent;
|
||||
ContentComponent* content;
|
||||
const bool warnAboutOverwritingExistingFiles;
|
||||
|
||||
void okButtonPressed();
|
||||
void createNewFolder();
|
||||
void createNewFolderConfirmed (const String& name);
|
||||
|
||||
static void okToOverwriteFileCallback (int result, FileChooserDialogBox*);
|
||||
static void createNewFolderCallback (int result, FileChooserDialogBox*, Component::SafePointer<AlertWindow>);
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileChooserDialogBox);
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCE_FILECHOOSERDIALOGBOX_JUCEHEADER__
|
||||
45
modules/juce_gui_basics/filebrowser/juce_FileFilter.cpp
Normal file
45
modules/juce_gui_basics/filebrowser/juce_FileFilter.cpp
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
//==============================================================================
|
||||
FileFilter::FileFilter (const String& filterDescription)
|
||||
: description (filterDescription)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
FileFilter::~FileFilter()
|
||||
{
|
||||
}
|
||||
|
||||
const String& FileFilter::getDescription() const noexcept
|
||||
{
|
||||
return description;
|
||||
}
|
||||
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
74
modules/juce_gui_basics/filebrowser/juce_FileFilter.h
Normal file
74
modules/juce_gui_basics/filebrowser/juce_FileFilter.h
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_FILEFILTER_JUCEHEADER__
|
||||
#define __JUCE_FILEFILTER_JUCEHEADER__
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Interface for deciding which files are suitable for something.
|
||||
|
||||
For example, this is used by DirectoryContentsList to select which files
|
||||
go into the list.
|
||||
|
||||
@see WildcardFileFilter, DirectoryContentsList, FileListComponent, FileBrowserComponent
|
||||
*/
|
||||
class JUCE_API FileFilter
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a filter with the given description.
|
||||
|
||||
The description can be returned later with the getDescription() method.
|
||||
*/
|
||||
FileFilter (const String& filterDescription);
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~FileFilter();
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the description that the filter was created with. */
|
||||
const String& getDescription() const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Should return true if this file is suitable for inclusion in whatever context
|
||||
the object is being used.
|
||||
*/
|
||||
virtual bool isFileSuitable (const File& file) const = 0;
|
||||
|
||||
/** Should return true if this directory is suitable for inclusion in whatever context
|
||||
the object is being used.
|
||||
*/
|
||||
virtual bool isDirectorySuitable (const File& file) const = 0;
|
||||
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
String description;
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCE_FILEFILTER_JUCEHEADER__
|
||||
253
modules/juce_gui_basics/filebrowser/juce_FileListComponent.cpp
Normal file
253
modules/juce_gui_basics/filebrowser/juce_FileListComponent.cpp
Normal file
|
|
@ -0,0 +1,253 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
Image juce_createIconForFile (const File& file);
|
||||
|
||||
|
||||
//==============================================================================
|
||||
FileListComponent::FileListComponent (DirectoryContentsList& listToShow)
|
||||
: ListBox (String::empty, nullptr),
|
||||
DirectoryContentsDisplayComponent (listToShow)
|
||||
{
|
||||
setModel (this);
|
||||
fileList.addChangeListener (this);
|
||||
}
|
||||
|
||||
FileListComponent::~FileListComponent()
|
||||
{
|
||||
fileList.removeChangeListener (this);
|
||||
}
|
||||
|
||||
int FileListComponent::getNumSelectedFiles() const
|
||||
{
|
||||
return getNumSelectedRows();
|
||||
}
|
||||
|
||||
const File FileListComponent::getSelectedFile (int index) const
|
||||
{
|
||||
return fileList.getFile (getSelectedRow (index));
|
||||
}
|
||||
|
||||
void FileListComponent::deselectAllFiles()
|
||||
{
|
||||
deselectAllRows();
|
||||
}
|
||||
|
||||
void FileListComponent::scrollToTop()
|
||||
{
|
||||
getVerticalScrollBar()->setCurrentRangeStart (0);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void FileListComponent::changeListenerCallback (ChangeBroadcaster*)
|
||||
{
|
||||
updateContent();
|
||||
|
||||
if (lastDirectory != fileList.getDirectory())
|
||||
{
|
||||
lastDirectory = fileList.getDirectory();
|
||||
deselectAllRows();
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class FileListItemComponent : public Component,
|
||||
public TimeSliceClient,
|
||||
public AsyncUpdater
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
FileListItemComponent (FileListComponent& owner_, TimeSliceThread& thread_)
|
||||
: owner (owner_), thread (thread_), index (0), highlighted (false)
|
||||
{
|
||||
}
|
||||
|
||||
~FileListItemComponent()
|
||||
{
|
||||
thread.removeTimeSliceClient (this);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void paint (Graphics& g)
|
||||
{
|
||||
getLookAndFeel().drawFileBrowserRow (g, getWidth(), getHeight(),
|
||||
file.getFileName(),
|
||||
&icon, fileSize, modTime,
|
||||
isDirectory, highlighted,
|
||||
index, owner);
|
||||
}
|
||||
|
||||
void mouseDown (const MouseEvent& e)
|
||||
{
|
||||
owner.selectRowsBasedOnModifierKeys (index, e.mods, false);
|
||||
owner.sendMouseClickMessage (file, e);
|
||||
}
|
||||
|
||||
void mouseDoubleClick (const MouseEvent&)
|
||||
{
|
||||
owner.sendDoubleClickMessage (file);
|
||||
}
|
||||
|
||||
void update (const File& root,
|
||||
const DirectoryContentsList::FileInfo* const fileInfo,
|
||||
const int index_,
|
||||
const bool highlighted_)
|
||||
{
|
||||
thread.removeTimeSliceClient (this);
|
||||
|
||||
if (highlighted_ != highlighted || index_ != index)
|
||||
{
|
||||
index = index_;
|
||||
highlighted = highlighted_;
|
||||
repaint();
|
||||
}
|
||||
|
||||
File newFile;
|
||||
String newFileSize, newModTime;
|
||||
|
||||
if (fileInfo != nullptr)
|
||||
{
|
||||
newFile = root.getChildFile (fileInfo->filename);
|
||||
newFileSize = File::descriptionOfSizeInBytes (fileInfo->fileSize);
|
||||
newModTime = fileInfo->modificationTime.formatted ("%d %b '%y %H:%M");
|
||||
}
|
||||
|
||||
if (newFile != file
|
||||
|| fileSize != newFileSize
|
||||
|| modTime != newModTime)
|
||||
{
|
||||
file = newFile;
|
||||
fileSize = newFileSize;
|
||||
modTime = newModTime;
|
||||
icon = Image::null;
|
||||
isDirectory = fileInfo != nullptr && fileInfo->isDirectory;
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
if (file != File::nonexistent && icon.isNull() && ! isDirectory)
|
||||
{
|
||||
updateIcon (true);
|
||||
|
||||
if (! icon.isValid())
|
||||
thread.addTimeSliceClient (this);
|
||||
}
|
||||
}
|
||||
|
||||
int useTimeSlice()
|
||||
{
|
||||
updateIcon (false);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void handleAsyncUpdate()
|
||||
{
|
||||
repaint();
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
FileListComponent& owner;
|
||||
TimeSliceThread& thread;
|
||||
File file;
|
||||
String fileSize, modTime;
|
||||
Image icon;
|
||||
int index;
|
||||
bool highlighted, isDirectory;
|
||||
|
||||
void updateIcon (const bool onlyUpdateIfCached)
|
||||
{
|
||||
if (icon.isNull())
|
||||
{
|
||||
const int hashCode = (file.getFullPathName() + "_iconCacheSalt").hashCode();
|
||||
Image im (ImageCache::getFromHashCode (hashCode));
|
||||
|
||||
if (im.isNull() && ! onlyUpdateIfCached)
|
||||
{
|
||||
im = juce_createIconForFile (file);
|
||||
|
||||
if (im.isValid())
|
||||
ImageCache::addImageToCache (im, hashCode);
|
||||
}
|
||||
|
||||
if (im.isValid())
|
||||
{
|
||||
icon = im;
|
||||
triggerAsyncUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileListItemComponent);
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
int FileListComponent::getNumRows()
|
||||
{
|
||||
return fileList.getNumFiles();
|
||||
}
|
||||
|
||||
void FileListComponent::paintListBoxItem (int, Graphics&, int, int, bool)
|
||||
{
|
||||
}
|
||||
|
||||
Component* FileListComponent::refreshComponentForRow (int row, bool isSelected, Component* existingComponentToUpdate)
|
||||
{
|
||||
FileListItemComponent* comp = dynamic_cast <FileListItemComponent*> (existingComponentToUpdate);
|
||||
|
||||
if (comp == nullptr)
|
||||
{
|
||||
delete existingComponentToUpdate;
|
||||
comp = new FileListItemComponent (*this, fileList.getTimeSliceThread());
|
||||
}
|
||||
|
||||
DirectoryContentsList::FileInfo fileInfo;
|
||||
|
||||
if (fileList.getFileInfo (row, fileInfo))
|
||||
comp->update (fileList.getDirectory(), &fileInfo, row, isSelected);
|
||||
else
|
||||
comp->update (fileList.getDirectory(), nullptr, row, isSelected);
|
||||
|
||||
return comp;
|
||||
}
|
||||
|
||||
void FileListComponent::selectedRowsChanged (int /*lastRowSelected*/)
|
||||
{
|
||||
sendSelectionChangeMessage();
|
||||
}
|
||||
|
||||
void FileListComponent::deleteKeyPressed (int /*currentSelectedRow*/)
|
||||
{
|
||||
}
|
||||
|
||||
void FileListComponent::returnKeyPressed (int currentSelectedRow)
|
||||
{
|
||||
sendDoubleClickMessage (fileList.getFile (currentSelectedRow));
|
||||
}
|
||||
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
103
modules/juce_gui_basics/filebrowser/juce_FileListComponent.h
Normal file
103
modules/juce_gui_basics/filebrowser/juce_FileListComponent.h
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_FILELISTCOMPONENT_JUCEHEADER__
|
||||
#define __JUCE_FILELISTCOMPONENT_JUCEHEADER__
|
||||
|
||||
#include "juce_DirectoryContentsDisplayComponent.h"
|
||||
#include "juce_FileBrowserListener.h"
|
||||
#include "../widgets/juce_ListBox.h"
|
||||
#include "../widgets/juce_TreeView.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A component that displays the files in a directory as a listbox.
|
||||
|
||||
This implements the DirectoryContentsDisplayComponent base class so that
|
||||
it can be used in a FileBrowserComponent.
|
||||
|
||||
To attach a listener to it, use its DirectoryContentsDisplayComponent base
|
||||
class and the FileBrowserListener class.
|
||||
|
||||
@see DirectoryContentsList, FileTreeComponent
|
||||
*/
|
||||
class JUCE_API FileListComponent : public ListBox,
|
||||
public DirectoryContentsDisplayComponent,
|
||||
private ListBoxModel,
|
||||
private ChangeListener
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a listbox to show the contents of a specified directory.
|
||||
*/
|
||||
FileListComponent (DirectoryContentsList& listToShow);
|
||||
|
||||
/** Destructor. */
|
||||
~FileListComponent();
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the number of files the user has got selected.
|
||||
@see getSelectedFile
|
||||
*/
|
||||
int getNumSelectedFiles() const;
|
||||
|
||||
/** Returns one of the files that the user has currently selected.
|
||||
The index should be in the range 0 to (getNumSelectedFiles() - 1).
|
||||
@see getNumSelectedFiles
|
||||
*/
|
||||
const File getSelectedFile (int index = 0) const;
|
||||
|
||||
/** Deselects any files that are currently selected. */
|
||||
void deselectAllFiles();
|
||||
|
||||
/** Scrolls to the top of the list. */
|
||||
void scrollToTop();
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void changeListenerCallback (ChangeBroadcaster*);
|
||||
/** @internal */
|
||||
int getNumRows();
|
||||
/** @internal */
|
||||
void paintListBoxItem (int, Graphics&, int, int, bool);
|
||||
/** @internal */
|
||||
Component* refreshComponentForRow (int rowNumber, bool isRowSelected, Component* existingComponentToUpdate);
|
||||
/** @internal */
|
||||
void selectedRowsChanged (int lastRowSelected);
|
||||
/** @internal */
|
||||
void deleteKeyPressed (int currentSelectedRow);
|
||||
/** @internal */
|
||||
void returnKeyPressed (int currentSelectedRow);
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
File lastDirectory;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileListComponent);
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCE_FILELISTCOMPONENT_JUCEHEADER__
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_FILEPREVIEWCOMPONENT_JUCEHEADER__
|
||||
#define __JUCE_FILEPREVIEWCOMPONENT_JUCEHEADER__
|
||||
|
||||
#include "../components/juce_Component.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Base class for components that live inside a file chooser dialog box and
|
||||
show previews of the files that get selected.
|
||||
|
||||
One of these allows special extra information to be displayed for files
|
||||
in a dialog box as the user selects them. Each time the current file or
|
||||
directory is changed, the selectedFileChanged() method will be called
|
||||
to allow it to update itself appropriately.
|
||||
|
||||
@see FileChooser, ImagePreviewComponent
|
||||
*/
|
||||
class JUCE_API FilePreviewComponent : public Component
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a FilePreviewComponent. */
|
||||
FilePreviewComponent();
|
||||
|
||||
/** Destructor. */
|
||||
~FilePreviewComponent();
|
||||
|
||||
/** Called to indicate that the user's currently selected file has changed.
|
||||
|
||||
@param newSelectedFile the newly selected file or directory, which may be
|
||||
File::nonexistent if none is selected.
|
||||
*/
|
||||
virtual void selectedFileChanged (const File& newSelectedFile) = 0;
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FilePreviewComponent);
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCE_FILEPREVIEWCOMPONENT_JUCEHEADER__
|
||||
|
|
@ -0,0 +1,271 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
//==============================================================================
|
||||
FileSearchPathListComponent::FileSearchPathListComponent()
|
||||
: addButton ("+"),
|
||||
removeButton ("-"),
|
||||
changeButton (TRANS ("change...")),
|
||||
upButton (String::empty, DrawableButton::ImageOnButtonBackground),
|
||||
downButton (String::empty, DrawableButton::ImageOnButtonBackground)
|
||||
{
|
||||
listBox.setModel (this);
|
||||
addAndMakeVisible (&listBox);
|
||||
listBox.setColour (ListBox::backgroundColourId, Colours::black.withAlpha (0.02f));
|
||||
listBox.setColour (ListBox::outlineColourId, Colours::black.withAlpha (0.1f));
|
||||
listBox.setOutlineThickness (1);
|
||||
|
||||
addAndMakeVisible (&addButton);
|
||||
addButton.addListener (this);
|
||||
addButton.setConnectedEdges (Button::ConnectedOnLeft | Button::ConnectedOnRight | Button::ConnectedOnBottom | Button::ConnectedOnTop);
|
||||
|
||||
addAndMakeVisible (&removeButton);
|
||||
removeButton.addListener (this);
|
||||
removeButton.setConnectedEdges (Button::ConnectedOnLeft | Button::ConnectedOnRight | Button::ConnectedOnBottom | Button::ConnectedOnTop);
|
||||
|
||||
addAndMakeVisible (&changeButton);
|
||||
changeButton.addListener (this);
|
||||
|
||||
addAndMakeVisible (&upButton);
|
||||
upButton.addListener (this);
|
||||
|
||||
{
|
||||
Path arrowPath;
|
||||
arrowPath.addArrow (Line<float> (50.0f, 100.0f, 50.0f, 0.0f), 40.0f, 100.0f, 50.0f);
|
||||
DrawablePath arrowImage;
|
||||
arrowImage.setFill (Colours::black.withAlpha (0.4f));
|
||||
arrowImage.setPath (arrowPath);
|
||||
|
||||
upButton.setImages (&arrowImage);
|
||||
}
|
||||
|
||||
addAndMakeVisible (&downButton);
|
||||
downButton.addListener (this);
|
||||
|
||||
{
|
||||
Path arrowPath;
|
||||
arrowPath.addArrow (Line<float> (50.0f, 0.0f, 50.0f, 100.0f), 40.0f, 100.0f, 50.0f);
|
||||
DrawablePath arrowImage;
|
||||
arrowImage.setFill (Colours::black.withAlpha (0.4f));
|
||||
arrowImage.setPath (arrowPath);
|
||||
|
||||
downButton.setImages (&arrowImage);
|
||||
}
|
||||
|
||||
updateButtons();
|
||||
}
|
||||
|
||||
FileSearchPathListComponent::~FileSearchPathListComponent()
|
||||
{
|
||||
}
|
||||
|
||||
void FileSearchPathListComponent::updateButtons()
|
||||
{
|
||||
const bool anythingSelected = listBox.getNumSelectedRows() > 0;
|
||||
|
||||
removeButton.setEnabled (anythingSelected);
|
||||
changeButton.setEnabled (anythingSelected);
|
||||
upButton.setEnabled (anythingSelected);
|
||||
downButton.setEnabled (anythingSelected);
|
||||
}
|
||||
|
||||
void FileSearchPathListComponent::changed()
|
||||
{
|
||||
listBox.updateContent();
|
||||
listBox.repaint();
|
||||
updateButtons();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void FileSearchPathListComponent::setPath (const FileSearchPath& newPath)
|
||||
{
|
||||
if (newPath.toString() != path.toString())
|
||||
{
|
||||
path = newPath;
|
||||
changed();
|
||||
}
|
||||
}
|
||||
|
||||
void FileSearchPathListComponent::setDefaultBrowseTarget (const File& newDefaultDirectory)
|
||||
{
|
||||
defaultBrowseTarget = newDefaultDirectory;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
int FileSearchPathListComponent::getNumRows()
|
||||
{
|
||||
return path.getNumPaths();
|
||||
}
|
||||
|
||||
void FileSearchPathListComponent::paintListBoxItem (int rowNumber, Graphics& g, int width, int height, bool rowIsSelected)
|
||||
{
|
||||
if (rowIsSelected)
|
||||
g.fillAll (findColour (TextEditor::highlightColourId));
|
||||
|
||||
g.setColour (findColour (ListBox::textColourId));
|
||||
Font f (height * 0.7f);
|
||||
f.setHorizontalScale (0.9f);
|
||||
g.setFont (f);
|
||||
|
||||
g.drawText (path [rowNumber].getFullPathName(),
|
||||
4, 0, width - 6, height,
|
||||
Justification::centredLeft, true);
|
||||
}
|
||||
|
||||
void FileSearchPathListComponent::deleteKeyPressed (int row)
|
||||
{
|
||||
if (isPositiveAndBelow (row, path.getNumPaths()))
|
||||
{
|
||||
path.remove (row);
|
||||
changed();
|
||||
}
|
||||
}
|
||||
|
||||
void FileSearchPathListComponent::returnKeyPressed (int row)
|
||||
{
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
FileChooser chooser (TRANS("Change folder..."), path [row], "*");
|
||||
|
||||
if (chooser.browseForDirectory())
|
||||
{
|
||||
path.remove (row);
|
||||
path.add (chooser.getResult(), row);
|
||||
changed();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void FileSearchPathListComponent::listBoxItemDoubleClicked (int row, const MouseEvent&)
|
||||
{
|
||||
returnKeyPressed (row);
|
||||
}
|
||||
|
||||
void FileSearchPathListComponent::selectedRowsChanged (int)
|
||||
{
|
||||
updateButtons();
|
||||
}
|
||||
|
||||
void FileSearchPathListComponent::paint (Graphics& g)
|
||||
{
|
||||
g.fillAll (findColour (backgroundColourId));
|
||||
}
|
||||
|
||||
void FileSearchPathListComponent::resized()
|
||||
{
|
||||
const int buttonH = 22;
|
||||
const int buttonY = getHeight() - buttonH - 4;
|
||||
listBox.setBounds (2, 2, getWidth() - 4, buttonY - 5);
|
||||
|
||||
addButton.setBounds (2, buttonY, buttonH, buttonH);
|
||||
removeButton.setBounds (addButton.getRight(), buttonY, buttonH, buttonH);
|
||||
|
||||
changeButton.changeWidthToFitText (buttonH);
|
||||
downButton.setSize (buttonH * 2, buttonH);
|
||||
upButton.setSize (buttonH * 2, buttonH);
|
||||
|
||||
downButton.setTopRightPosition (getWidth() - 2, buttonY);
|
||||
upButton.setTopRightPosition (downButton.getX() - 4, buttonY);
|
||||
changeButton.setTopRightPosition (upButton.getX() - 8, buttonY);
|
||||
}
|
||||
|
||||
bool FileSearchPathListComponent::isInterestedInFileDrag (const StringArray&)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void FileSearchPathListComponent::filesDropped (const StringArray& filenames, int, int mouseY)
|
||||
{
|
||||
for (int i = filenames.size(); --i >= 0;)
|
||||
{
|
||||
const File f (filenames[i]);
|
||||
|
||||
if (f.isDirectory())
|
||||
{
|
||||
const int row = listBox.getRowContainingPosition (0, mouseY - listBox.getY());
|
||||
path.add (f, row);
|
||||
changed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FileSearchPathListComponent::buttonClicked (Button* button)
|
||||
{
|
||||
const int currentRow = listBox.getSelectedRow();
|
||||
|
||||
if (button == &removeButton)
|
||||
{
|
||||
deleteKeyPressed (currentRow);
|
||||
}
|
||||
else if (button == &addButton)
|
||||
{
|
||||
File start (defaultBrowseTarget);
|
||||
|
||||
if (start == File::nonexistent)
|
||||
start = path [0];
|
||||
|
||||
if (start == File::nonexistent)
|
||||
start = File::getCurrentWorkingDirectory();
|
||||
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
FileChooser chooser (TRANS("Add a folder..."), start, "*");
|
||||
|
||||
if (chooser.browseForDirectory())
|
||||
path.add (chooser.getResult(), currentRow);
|
||||
#else
|
||||
jassertfalse; // needs rewriting to deal with non-modal environments
|
||||
#endif
|
||||
}
|
||||
else if (button == &changeButton)
|
||||
{
|
||||
returnKeyPressed (currentRow);
|
||||
}
|
||||
else if (button == &upButton)
|
||||
{
|
||||
if (currentRow > 0 && currentRow < path.getNumPaths())
|
||||
{
|
||||
const File f (path[currentRow]);
|
||||
path.remove (currentRow);
|
||||
path.add (f, currentRow - 1);
|
||||
listBox.selectRow (currentRow - 1);
|
||||
}
|
||||
}
|
||||
else if (button == &downButton)
|
||||
{
|
||||
if (currentRow >= 0 && currentRow < path.getNumPaths() - 1)
|
||||
{
|
||||
const File f (path[currentRow]);
|
||||
path.remove (currentRow);
|
||||
path.add (f, currentRow + 1);
|
||||
listBox.selectRow (currentRow + 1);
|
||||
}
|
||||
}
|
||||
|
||||
changed();
|
||||
}
|
||||
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_FILESEARCHPATHLISTCOMPONENT_JUCEHEADER__
|
||||
#define __JUCE_FILESEARCHPATHLISTCOMPONENT_JUCEHEADER__
|
||||
|
||||
#include "../widgets/juce_ListBox.h"
|
||||
#include "../buttons/juce_DrawableButton.h"
|
||||
#include "../buttons/juce_TextButton.h"
|
||||
#include "../mouse/juce_FileDragAndDropTarget.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Shows a set of file paths in a list, allowing them to be added, removed or
|
||||
re-ordered.
|
||||
|
||||
@see FileSearchPath
|
||||
*/
|
||||
class JUCE_API FileSearchPathListComponent : public Component,
|
||||
public SettableTooltipClient,
|
||||
public FileDragAndDropTarget,
|
||||
private ButtonListener, // (can't use Button::Listener due to idiotic VC2005 bug)
|
||||
private ListBoxModel
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an empty FileSearchPathListComponent. */
|
||||
FileSearchPathListComponent();
|
||||
|
||||
/** Destructor. */
|
||||
~FileSearchPathListComponent();
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the path as it is currently shown. */
|
||||
const FileSearchPath& getPath() const noexcept { return path; }
|
||||
|
||||
/** Changes the current path. */
|
||||
void setPath (const FileSearchPath& newPath);
|
||||
|
||||
/** Sets a file or directory to be the default starting point for the browser to show.
|
||||
|
||||
This is only used if the current file hasn't been set.
|
||||
*/
|
||||
void setDefaultBrowseTarget (const File& newDefaultDirectory);
|
||||
|
||||
/** A set of colour IDs to use to change the colour of various aspects of the label.
|
||||
|
||||
These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
|
||||
methods.
|
||||
|
||||
@see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
|
||||
*/
|
||||
enum ColourIds
|
||||
{
|
||||
backgroundColourId = 0x1004100, /**< The background colour to fill the component with.
|
||||
Make this transparent if you don't want the background to be filled. */
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
int getNumRows();
|
||||
/** @internal */
|
||||
void paintListBoxItem (int rowNumber, Graphics& g, int width, int height, bool rowIsSelected);
|
||||
/** @internal */
|
||||
void deleteKeyPressed (int lastRowSelected);
|
||||
/** @internal */
|
||||
void returnKeyPressed (int lastRowSelected);
|
||||
/** @internal */
|
||||
void listBoxItemDoubleClicked (int row, const MouseEvent&);
|
||||
/** @internal */
|
||||
void selectedRowsChanged (int lastRowSelected);
|
||||
/** @internal */
|
||||
void resized();
|
||||
/** @internal */
|
||||
void paint (Graphics& g);
|
||||
/** @internal */
|
||||
bool isInterestedInFileDrag (const StringArray& files);
|
||||
/** @internal */
|
||||
void filesDropped (const StringArray& files, int, int);
|
||||
/** @internal */
|
||||
void buttonClicked (Button* button);
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
FileSearchPath path;
|
||||
File defaultBrowseTarget;
|
||||
|
||||
ListBox listBox;
|
||||
TextButton addButton, removeButton, changeButton;
|
||||
DrawableButton upButton, downButton;
|
||||
|
||||
void changed();
|
||||
void updateButtons();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileSearchPathListComponent);
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCE_FILESEARCHPATHLISTCOMPONENT_JUCEHEADER__
|
||||
253
modules/juce_gui_basics/filebrowser/juce_FileTreeComponent.cpp
Normal file
253
modules/juce_gui_basics/filebrowser/juce_FileTreeComponent.cpp
Normal file
|
|
@ -0,0 +1,253 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
Image juce_createIconForFile (const File& file);
|
||||
|
||||
//==============================================================================
|
||||
class FileListTreeItem : public TreeViewItem,
|
||||
public TimeSliceClient,
|
||||
public AsyncUpdater,
|
||||
public ChangeListener
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
FileListTreeItem (FileTreeComponent& owner_,
|
||||
DirectoryContentsList* const parentContentsList_,
|
||||
const int indexInContentsList_,
|
||||
const File& file_,
|
||||
TimeSliceThread& thread_)
|
||||
: file (file_),
|
||||
owner (owner_),
|
||||
parentContentsList (parentContentsList_),
|
||||
indexInContentsList (indexInContentsList_),
|
||||
subContentsList (nullptr, false),
|
||||
thread (thread_)
|
||||
{
|
||||
DirectoryContentsList::FileInfo fileInfo;
|
||||
|
||||
if (parentContentsList_ != nullptr
|
||||
&& parentContentsList_->getFileInfo (indexInContentsList_, fileInfo))
|
||||
{
|
||||
fileSize = File::descriptionOfSizeInBytes (fileInfo.fileSize);
|
||||
modTime = fileInfo.modificationTime.formatted ("%d %b '%y %H:%M");
|
||||
isDirectory = fileInfo.isDirectory;
|
||||
}
|
||||
else
|
||||
{
|
||||
isDirectory = true;
|
||||
}
|
||||
}
|
||||
|
||||
~FileListTreeItem()
|
||||
{
|
||||
thread.removeTimeSliceClient (this);
|
||||
clearSubItems();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool mightContainSubItems() { return isDirectory; }
|
||||
const String getUniqueName() const { return file.getFullPathName(); }
|
||||
int getItemHeight() const { return 22; }
|
||||
|
||||
const var getDragSourceDescription() { return owner.getDragAndDropDescription(); }
|
||||
|
||||
void itemOpennessChanged (bool isNowOpen)
|
||||
{
|
||||
if (isNowOpen)
|
||||
{
|
||||
clearSubItems();
|
||||
|
||||
isDirectory = file.isDirectory();
|
||||
|
||||
if (isDirectory)
|
||||
{
|
||||
if (subContentsList == nullptr)
|
||||
{
|
||||
jassert (parentContentsList != nullptr);
|
||||
|
||||
DirectoryContentsList* const l = new DirectoryContentsList (parentContentsList->getFilter(), thread);
|
||||
l->setDirectory (file, true, true);
|
||||
|
||||
setSubContentsList (l, true);
|
||||
}
|
||||
|
||||
changeListenerCallback (nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setSubContentsList (DirectoryContentsList* newList, const bool canDeleteList)
|
||||
{
|
||||
OptionalScopedPointer<DirectoryContentsList> newPointer (newList, canDeleteList);
|
||||
subContentsList = newPointer;
|
||||
newList->addChangeListener (this);
|
||||
}
|
||||
|
||||
void changeListenerCallback (ChangeBroadcaster*)
|
||||
{
|
||||
clearSubItems();
|
||||
|
||||
if (isOpen() && subContentsList != nullptr)
|
||||
{
|
||||
for (int i = 0; i < subContentsList->getNumFiles(); ++i)
|
||||
{
|
||||
FileListTreeItem* const item
|
||||
= new FileListTreeItem (owner, subContentsList, i, subContentsList->getFile(i), thread);
|
||||
|
||||
addSubItem (item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void paintItem (Graphics& g, int width, int height)
|
||||
{
|
||||
if (file != File::nonexistent)
|
||||
{
|
||||
updateIcon (true);
|
||||
|
||||
if (icon.isNull())
|
||||
thread.addTimeSliceClient (this);
|
||||
}
|
||||
|
||||
owner.getLookAndFeel()
|
||||
.drawFileBrowserRow (g, width, height,
|
||||
file.getFileName(),
|
||||
&icon, fileSize, modTime,
|
||||
isDirectory, isSelected(),
|
||||
indexInContentsList, owner);
|
||||
}
|
||||
|
||||
void itemClicked (const MouseEvent& e)
|
||||
{
|
||||
owner.sendMouseClickMessage (file, e);
|
||||
}
|
||||
|
||||
void itemDoubleClicked (const MouseEvent& e)
|
||||
{
|
||||
TreeViewItem::itemDoubleClicked (e);
|
||||
|
||||
owner.sendDoubleClickMessage (file);
|
||||
}
|
||||
|
||||
void itemSelectionChanged (bool)
|
||||
{
|
||||
owner.sendSelectionChangeMessage();
|
||||
}
|
||||
|
||||
int useTimeSlice()
|
||||
{
|
||||
updateIcon (false);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void handleAsyncUpdate()
|
||||
{
|
||||
owner.repaint();
|
||||
}
|
||||
|
||||
const File file;
|
||||
|
||||
private:
|
||||
FileTreeComponent& owner;
|
||||
DirectoryContentsList* parentContentsList;
|
||||
int indexInContentsList;
|
||||
OptionalScopedPointer<DirectoryContentsList> subContentsList;
|
||||
bool isDirectory;
|
||||
TimeSliceThread& thread;
|
||||
Image icon;
|
||||
String fileSize;
|
||||
String modTime;
|
||||
|
||||
void updateIcon (const bool onlyUpdateIfCached)
|
||||
{
|
||||
if (icon.isNull())
|
||||
{
|
||||
const int hashCode = (file.getFullPathName() + "_iconCacheSalt").hashCode();
|
||||
Image im (ImageCache::getFromHashCode (hashCode));
|
||||
|
||||
if (im.isNull() && ! onlyUpdateIfCached)
|
||||
{
|
||||
im = juce_createIconForFile (file);
|
||||
|
||||
if (im.isValid())
|
||||
ImageCache::addImageToCache (im, hashCode);
|
||||
}
|
||||
|
||||
if (im.isValid())
|
||||
{
|
||||
icon = im;
|
||||
triggerAsyncUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileListTreeItem);
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
FileTreeComponent::FileTreeComponent (DirectoryContentsList& listToShow)
|
||||
: DirectoryContentsDisplayComponent (listToShow)
|
||||
{
|
||||
FileListTreeItem* const root
|
||||
= new FileListTreeItem (*this, 0, 0, listToShow.getDirectory(),
|
||||
listToShow.getTimeSliceThread());
|
||||
|
||||
root->setSubContentsList (&listToShow, false);
|
||||
setRootItemVisible (false);
|
||||
setRootItem (root);
|
||||
}
|
||||
|
||||
FileTreeComponent::~FileTreeComponent()
|
||||
{
|
||||
deleteRootItem();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
const File FileTreeComponent::getSelectedFile (const int index) const
|
||||
{
|
||||
const FileListTreeItem* const item = dynamic_cast <const FileListTreeItem*> (getSelectedItem (index));
|
||||
|
||||
return item != nullptr ? item->file
|
||||
: File::nonexistent;
|
||||
}
|
||||
|
||||
void FileTreeComponent::deselectAllFiles()
|
||||
{
|
||||
clearSelectedItems();
|
||||
}
|
||||
|
||||
void FileTreeComponent::scrollToTop()
|
||||
{
|
||||
getViewport()->getVerticalScrollBar()->setCurrentRangeStart (0);
|
||||
}
|
||||
|
||||
void FileTreeComponent::setDragAndDropDescription (const String& description)
|
||||
{
|
||||
dragAndDropDescription = description;
|
||||
}
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
94
modules/juce_gui_basics/filebrowser/juce_FileTreeComponent.h
Normal file
94
modules/juce_gui_basics/filebrowser/juce_FileTreeComponent.h
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_FILETREECOMPONENT_JUCEHEADER__
|
||||
#define __JUCE_FILETREECOMPONENT_JUCEHEADER__
|
||||
|
||||
#include "juce_DirectoryContentsDisplayComponent.h"
|
||||
#include "../widgets/juce_TreeView.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A component that displays the files in a directory as a treeview.
|
||||
|
||||
This implements the DirectoryContentsDisplayComponent base class so that
|
||||
it can be used in a FileBrowserComponent.
|
||||
|
||||
To attach a listener to it, use its DirectoryContentsDisplayComponent base
|
||||
class and the FileBrowserListener class.
|
||||
|
||||
@see DirectoryContentsList, FileListComponent
|
||||
*/
|
||||
class JUCE_API FileTreeComponent : public TreeView,
|
||||
public DirectoryContentsDisplayComponent
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a listbox to show the contents of a specified directory.
|
||||
*/
|
||||
FileTreeComponent (DirectoryContentsList& listToShow);
|
||||
|
||||
/** Destructor. */
|
||||
~FileTreeComponent();
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the number of files the user has got selected.
|
||||
@see getSelectedFile
|
||||
*/
|
||||
int getNumSelectedFiles() const { return TreeView::getNumSelectedItems(); }
|
||||
|
||||
/** Returns one of the files that the user has currently selected.
|
||||
The index should be in the range 0 to (getNumSelectedFiles() - 1).
|
||||
@see getNumSelectedFiles
|
||||
*/
|
||||
const File getSelectedFile (int index = 0) const;
|
||||
|
||||
/** Deselects any files that are currently selected. */
|
||||
void deselectAllFiles();
|
||||
|
||||
/** Scrolls the list to the top. */
|
||||
void scrollToTop();
|
||||
|
||||
/** Setting a name for this allows tree items to be dragged.
|
||||
|
||||
The string that you pass in here will be returned by the getDragSourceDescription()
|
||||
of the items in the tree. For more info, see TreeViewItem::getDragSourceDescription().
|
||||
*/
|
||||
void setDragAndDropDescription (const String& description);
|
||||
|
||||
/** Returns the last value that was set by setDragAndDropDescription().
|
||||
*/
|
||||
const String& getDragAndDropDescription() const noexcept { return dragAndDropDescription; }
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
String dragAndDropDescription;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileTreeComponent);
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCE_FILETREECOMPONENT_JUCEHEADER__
|
||||
250
modules/juce_gui_basics/filebrowser/juce_FilenameComponent.cpp
Normal file
250
modules/juce_gui_basics/filebrowser/juce_FilenameComponent.cpp
Normal file
|
|
@ -0,0 +1,250 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
//==============================================================================
|
||||
FilenameComponent::FilenameComponent (const String& name,
|
||||
const File& currentFile,
|
||||
const bool canEditFilename,
|
||||
const bool isDirectory,
|
||||
const bool isForSaving,
|
||||
const String& fileBrowserWildcard,
|
||||
const String& enforcedSuffix_,
|
||||
const String& textWhenNothingSelected)
|
||||
: Component (name),
|
||||
maxRecentFiles (30),
|
||||
isDir (isDirectory),
|
||||
isSaving (isForSaving),
|
||||
isFileDragOver (false),
|
||||
wildcard (fileBrowserWildcard),
|
||||
enforcedSuffix (enforcedSuffix_)
|
||||
{
|
||||
addAndMakeVisible (&filenameBox);
|
||||
filenameBox.setEditableText (canEditFilename);
|
||||
filenameBox.addListener (this);
|
||||
filenameBox.setTextWhenNothingSelected (textWhenNothingSelected);
|
||||
filenameBox.setTextWhenNoChoicesAvailable (TRANS ("(no recently selected files)"));
|
||||
|
||||
setBrowseButtonText ("...");
|
||||
|
||||
setCurrentFile (currentFile, true);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void FilenameComponent::setBrowseButtonText (const String& newBrowseButtonText)
|
||||
{
|
||||
browseButtonText = newBrowseButtonText;
|
||||
lookAndFeelChanged();
|
||||
}
|
||||
|
||||
void FilenameComponent::lookAndFeelChanged()
|
||||
{
|
||||
browseButton = nullptr;
|
||||
|
||||
addAndMakeVisible (browseButton = getLookAndFeel().createFilenameComponentBrowseButton (browseButtonText));
|
||||
browseButton->setConnectedEdges (Button::ConnectedOnLeft);
|
||||
resized();
|
||||
|
||||
browseButton->addListener (this);
|
||||
}
|
||||
|
||||
void FilenameComponent::setTooltip (const String& newTooltip)
|
||||
{
|
||||
SettableTooltipClient::setTooltip (newTooltip);
|
||||
filenameBox.setTooltip (newTooltip);
|
||||
}
|
||||
|
||||
void FilenameComponent::setDefaultBrowseTarget (const File& newDefaultDirectory)
|
||||
{
|
||||
defaultBrowseFile = newDefaultDirectory;
|
||||
}
|
||||
|
||||
void FilenameComponent::buttonClicked (Button*)
|
||||
{
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
FileChooser fc (TRANS("Choose a new file"),
|
||||
getCurrentFile() == File::nonexistent ? defaultBrowseFile
|
||||
: getCurrentFile(),
|
||||
wildcard);
|
||||
|
||||
if (isDir ? fc.browseForDirectory()
|
||||
: (isSaving ? fc.browseForFileToSave (false)
|
||||
: fc.browseForFileToOpen()))
|
||||
{
|
||||
setCurrentFile (fc.getResult(), true);
|
||||
}
|
||||
#else
|
||||
jassertfalse; // needs rewriting to deal with non-modal environments
|
||||
#endif
|
||||
}
|
||||
|
||||
void FilenameComponent::comboBoxChanged (ComboBox*)
|
||||
{
|
||||
setCurrentFile (getCurrentFile(), true);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void FilenameComponent::fileDragEnter (const StringArray&, int, int)
|
||||
{
|
||||
isFileDragOver = true;
|
||||
repaint();
|
||||
}
|
||||
|
||||
void FilenameComponent::fileDragExit (const StringArray&)
|
||||
{
|
||||
isFileDragOver = false;
|
||||
repaint();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
File FilenameComponent::getCurrentFile() const
|
||||
{
|
||||
File f (filenameBox.getText());
|
||||
|
||||
if (enforcedSuffix.isNotEmpty())
|
||||
f = f.withFileExtension (enforcedSuffix);
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
void FilenameComponent::setCurrentFile (File newFile,
|
||||
const bool addToRecentlyUsedList,
|
||||
const bool sendChangeNotification)
|
||||
{
|
||||
if (enforcedSuffix.isNotEmpty())
|
||||
newFile = newFile.withFileExtension (enforcedSuffix);
|
||||
|
||||
if (newFile.getFullPathName() != lastFilename)
|
||||
{
|
||||
lastFilename = newFile.getFullPathName();
|
||||
|
||||
if (addToRecentlyUsedList)
|
||||
addRecentlyUsedFile (newFile);
|
||||
|
||||
filenameBox.setText (lastFilename, true);
|
||||
|
||||
if (sendChangeNotification)
|
||||
triggerAsyncUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
void FilenameComponent::setFilenameIsEditable (const bool shouldBeEditable)
|
||||
{
|
||||
filenameBox.setEditableText (shouldBeEditable);
|
||||
}
|
||||
|
||||
StringArray FilenameComponent::getRecentlyUsedFilenames() const
|
||||
{
|
||||
StringArray names;
|
||||
|
||||
for (int i = 0; i < filenameBox.getNumItems(); ++i)
|
||||
names.add (filenameBox.getItemText (i));
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
void FilenameComponent::setRecentlyUsedFilenames (const StringArray& filenames)
|
||||
{
|
||||
if (filenames != getRecentlyUsedFilenames())
|
||||
{
|
||||
filenameBox.clear();
|
||||
|
||||
for (int i = 0; i < jmin (filenames.size(), maxRecentFiles); ++i)
|
||||
filenameBox.addItem (filenames[i], i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void FilenameComponent::setMaxNumberOfRecentFiles (const int newMaximum)
|
||||
{
|
||||
maxRecentFiles = jmax (1, newMaximum);
|
||||
|
||||
setRecentlyUsedFilenames (getRecentlyUsedFilenames());
|
||||
}
|
||||
|
||||
void FilenameComponent::addRecentlyUsedFile (const File& file)
|
||||
{
|
||||
StringArray files (getRecentlyUsedFilenames());
|
||||
|
||||
if (file.getFullPathName().isNotEmpty())
|
||||
{
|
||||
files.removeString (file.getFullPathName(), true);
|
||||
files.insert (0, file.getFullPathName());
|
||||
|
||||
setRecentlyUsedFilenames (files);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void FilenameComponent::addListener (FilenameComponentListener* const listener)
|
||||
{
|
||||
listeners.add (listener);
|
||||
}
|
||||
|
||||
void FilenameComponent::removeListener (FilenameComponentListener* const listener)
|
||||
{
|
||||
listeners.remove (listener);
|
||||
}
|
||||
|
||||
void FilenameComponent::handleAsyncUpdate()
|
||||
{
|
||||
Component::BailOutChecker checker (this);
|
||||
listeners.callChecked (checker, &FilenameComponentListener::filenameComponentChanged, this);
|
||||
}
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
219
modules/juce_gui_basics/filebrowser/juce_FilenameComponent.h
Normal file
219
modules/juce_gui_basics/filebrowser/juce_FilenameComponent.h
Normal file
|
|
@ -0,0 +1,219 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_FILENAMECOMPONENT_JUCEHEADER__
|
||||
#define __JUCE_FILENAMECOMPONENT_JUCEHEADER__
|
||||
|
||||
#include "../widgets/juce_ComboBox.h"
|
||||
#include "../buttons/juce_TextButton.h"
|
||||
#include "../mouse/juce_FileDragAndDropTarget.h"
|
||||
class FilenameComponent;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Listens for events happening to a FilenameComponent.
|
||||
|
||||
Use FilenameComponent::addListener() and FilenameComponent::removeListener() to
|
||||
register one of these objects for event callbacks when the filename is changed.
|
||||
|
||||
@see FilenameComponent
|
||||
*/
|
||||
class JUCE_API FilenameComponentListener
|
||||
{
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~FilenameComponentListener() {}
|
||||
|
||||
/** This method is called after the FilenameComponent's file has been changed. */
|
||||
virtual void filenameComponentChanged (FilenameComponent* fileComponentThatHasChanged) = 0;
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Shows a filename as an editable text box, with a 'browse' button and a
|
||||
drop-down list for recently selected files.
|
||||
|
||||
A handy component for dialogue boxes where you want the user to be able to
|
||||
select a file or directory.
|
||||
|
||||
Attach an FilenameComponentListener using the addListener() method, and it will
|
||||
get called each time the user changes the filename, either by browsing for a file
|
||||
and clicking 'ok', or by typing a new filename into the box and pressing return.
|
||||
|
||||
@see FileChooser, ComboBox
|
||||
*/
|
||||
class JUCE_API FilenameComponent : public Component,
|
||||
public SettableTooltipClient,
|
||||
public FileDragAndDropTarget,
|
||||
private AsyncUpdater,
|
||||
private ButtonListener, // (can't use Button::Listener due to idiotic VC2005 bug)
|
||||
private ComboBoxListener
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a FilenameComponent.
|
||||
|
||||
@param name the name for this component.
|
||||
@param currentFile the file to initially show in the box
|
||||
@param canEditFilename if true, the user can manually edit the filename; if false,
|
||||
they can only change it by browsing for a new file
|
||||
@param isDirectory if true, the file will be treated as a directory, and
|
||||
an appropriate directory browser used
|
||||
@param isForSaving if true, the file browser will allow non-existent files to
|
||||
be picked, as the file is assumed to be used for saving rather
|
||||
than loading
|
||||
@param fileBrowserWildcard a wildcard pattern to use in the file browser - e.g. "*.txt;*.foo".
|
||||
If an empty string is passed in, then the pattern is assumed to be "*"
|
||||
@param enforcedSuffix if this is non-empty, it is treated as a suffix that will be added
|
||||
to any filenames that are entered or chosen
|
||||
@param textWhenNothingSelected the message to display in the box before any filename is entered. (This
|
||||
will only appear if the initial file isn't valid)
|
||||
*/
|
||||
FilenameComponent (const String& name,
|
||||
const File& currentFile,
|
||||
bool canEditFilename,
|
||||
bool isDirectory,
|
||||
bool isForSaving,
|
||||
const String& fileBrowserWildcard,
|
||||
const String& enforcedSuffix,
|
||||
const String& textWhenNothingSelected);
|
||||
|
||||
/** Destructor. */
|
||||
~FilenameComponent();
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the currently displayed filename. */
|
||||
File getCurrentFile() const;
|
||||
|
||||
/** Changes the current filename.
|
||||
|
||||
If addToRecentlyUsedList is true, the filename will also be added to the
|
||||
drop-down list of recent files.
|
||||
|
||||
If sendChangeNotification is false, then the listeners won't be told of the
|
||||
change.
|
||||
*/
|
||||
void setCurrentFile (File newFile,
|
||||
bool addToRecentlyUsedList,
|
||||
bool sendChangeNotification = true);
|
||||
|
||||
/** Changes whether the use can type into the filename box.
|
||||
*/
|
||||
void setFilenameIsEditable (bool shouldBeEditable);
|
||||
|
||||
/** Sets a file or directory to be the default starting point for the browser to show.
|
||||
|
||||
This is only used if the current file hasn't been set.
|
||||
*/
|
||||
void setDefaultBrowseTarget (const File& newDefaultDirectory);
|
||||
|
||||
/** Returns all the entries on the recent files list.
|
||||
|
||||
This can be used in conjunction with setRecentlyUsedFilenames() for saving the
|
||||
state of this list.
|
||||
|
||||
@see setRecentlyUsedFilenames
|
||||
*/
|
||||
StringArray getRecentlyUsedFilenames() const;
|
||||
|
||||
/** Sets all the entries on the recent files list.
|
||||
|
||||
This can be used in conjunction with getRecentlyUsedFilenames() for saving the
|
||||
state of this list.
|
||||
|
||||
@see getRecentlyUsedFilenames, addRecentlyUsedFile
|
||||
*/
|
||||
void setRecentlyUsedFilenames (const StringArray& filenames);
|
||||
|
||||
/** Adds an entry to the recently-used files dropdown list.
|
||||
|
||||
If the file is already in the list, it will be moved to the top. A limit
|
||||
is also placed on the number of items that are kept in the list.
|
||||
|
||||
@see getRecentlyUsedFilenames, setRecentlyUsedFilenames, setMaxNumberOfRecentFiles
|
||||
*/
|
||||
void addRecentlyUsedFile (const File& file);
|
||||
|
||||
/** Changes the limit for the number of files that will be stored in the recent-file list.
|
||||
*/
|
||||
void setMaxNumberOfRecentFiles (int newMaximum);
|
||||
|
||||
/** Changes the text shown on the 'browse' button.
|
||||
|
||||
By default this button just says "..." but you can change it. The button itself
|
||||
can be changed using the look-and-feel classes, so it might not actually have any
|
||||
text on it.
|
||||
*/
|
||||
void setBrowseButtonText (const String& browseButtonText);
|
||||
|
||||
//==============================================================================
|
||||
/** Adds a listener that will be called when the selected file is changed. */
|
||||
void addListener (FilenameComponentListener* listener);
|
||||
|
||||
/** Removes a previously-registered listener. */
|
||||
void removeListener (FilenameComponentListener* listener);
|
||||
|
||||
/** Gives the component a tooltip. */
|
||||
void setTooltip (const String& newTooltip);
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void paintOverChildren (Graphics& g);
|
||||
/** @internal */
|
||||
void resized();
|
||||
/** @internal */
|
||||
void lookAndFeelChanged();
|
||||
/** @internal */
|
||||
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);
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
ComboBox filenameBox;
|
||||
String lastFilename;
|
||||
ScopedPointer<Button> browseButton;
|
||||
int maxRecentFiles;
|
||||
bool isDir, isSaving, isFileDragOver;
|
||||
String wildcard, enforcedSuffix, browseButtonText;
|
||||
ListenerList <FilenameComponentListener> listeners;
|
||||
File defaultBrowseFile;
|
||||
|
||||
void comboBoxChanged (ComboBox*);
|
||||
void buttonClicked (Button* button);
|
||||
void handleAsyncUpdate();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FilenameComponent);
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif // __JUCE_FILENAMECOMPONENT_JUCEHEADER__
|
||||
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
//==============================================================================
|
||||
ImagePreviewComponent::ImagePreviewComponent()
|
||||
{
|
||||
}
|
||||
|
||||
ImagePreviewComponent::~ImagePreviewComponent()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void ImagePreviewComponent::getThumbSize (int& w, int& h) const
|
||||
{
|
||||
const int availableW = proportionOfWidth (0.97f);
|
||||
const int availableH = getHeight() - 13 * 4;
|
||||
|
||||
const double scale = jmin (1.0,
|
||||
availableW / (double) w,
|
||||
availableH / (double) h);
|
||||
|
||||
w = roundToInt (scale * w);
|
||||
h = roundToInt (scale * h);
|
||||
}
|
||||
|
||||
void ImagePreviewComponent::selectedFileChanged (const File& file)
|
||||
{
|
||||
if (fileToLoad != file)
|
||||
{
|
||||
fileToLoad = file;
|
||||
startTimer (100);
|
||||
}
|
||||
}
|
||||
|
||||
void ImagePreviewComponent::timerCallback()
|
||||
{
|
||||
stopTimer();
|
||||
|
||||
currentThumbnail = Image::null;
|
||||
currentDetails = String::empty;
|
||||
repaint();
|
||||
|
||||
ScopedPointer <FileInputStream> in (fileToLoad.createInputStream());
|
||||
|
||||
if (in != nullptr)
|
||||
{
|
||||
ImageFileFormat* const format = ImageFileFormat::findImageFormatForStream (*in);
|
||||
|
||||
if (format != nullptr)
|
||||
{
|
||||
currentThumbnail = format->decodeImage (*in);
|
||||
|
||||
if (currentThumbnail.isValid())
|
||||
{
|
||||
int w = currentThumbnail.getWidth();
|
||||
int h = currentThumbnail.getHeight();
|
||||
|
||||
currentDetails
|
||||
<< fileToLoad.getFileName() << "\n"
|
||||
<< format->getFormatName() << "\n"
|
||||
<< w << " x " << h << " pixels\n"
|
||||
<< File::descriptionOfSizeInBytes (fileToLoad.getSize());
|
||||
|
||||
getThumbSize (w, h);
|
||||
|
||||
currentThumbnail = currentThumbnail.rescaled (w, h);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ImagePreviewComponent::paint (Graphics& g)
|
||||
{
|
||||
if (currentThumbnail.isValid())
|
||||
{
|
||||
g.setFont (13.0f);
|
||||
|
||||
int w = currentThumbnail.getWidth();
|
||||
int h = currentThumbnail.getHeight();
|
||||
getThumbSize (w, h);
|
||||
|
||||
const int numLines = 4;
|
||||
const int totalH = 13 * numLines + h + 4;
|
||||
const int y = (getHeight() - totalH) / 2;
|
||||
|
||||
g.drawImageWithin (currentThumbnail,
|
||||
(getWidth() - w) / 2, y, w, h,
|
||||
RectanglePlacement::centred | RectanglePlacement::onlyReduceInSize,
|
||||
false);
|
||||
|
||||
g.drawFittedText (currentDetails,
|
||||
0, y + h + 4, getWidth(), 100,
|
||||
Justification::centredTop, numLines);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_IMAGEPREVIEWCOMPONENT_JUCEHEADER__
|
||||
#define __JUCE_IMAGEPREVIEWCOMPONENT_JUCEHEADER__
|
||||
|
||||
#include "juce_FilePreviewComponent.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A simple preview component that shows thumbnails of image files.
|
||||
|
||||
@see FileChooserDialogBox, FilePreviewComponent
|
||||
*/
|
||||
class JUCE_API ImagePreviewComponent : public FilePreviewComponent,
|
||||
private Timer
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an ImagePreviewComponent. */
|
||||
ImagePreviewComponent();
|
||||
|
||||
/** Destructor. */
|
||||
~ImagePreviewComponent();
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void selectedFileChanged (const File& newSelectedFile);
|
||||
/** @internal */
|
||||
void paint (Graphics& g);
|
||||
/** @internal */
|
||||
void timerCallback();
|
||||
|
||||
private:
|
||||
File fileToLoad;
|
||||
Image currentThumbnail;
|
||||
String currentDetails;
|
||||
|
||||
void getThumbSize (int& w, int& h) const;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ImagePreviewComponent);
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCE_IMAGEPREVIEWCOMPONENT_JUCEHEADER__
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
//==============================================================================
|
||||
WildcardFileFilter::WildcardFileFilter (const String& fileWildcardPatterns,
|
||||
const String& directoryWildcardPatterns,
|
||||
const String& description_)
|
||||
: FileFilter (description_.isEmpty() ? fileWildcardPatterns
|
||||
: (description_ + " (" + fileWildcardPatterns + ")"))
|
||||
{
|
||||
parse (fileWildcardPatterns, fileWildcards);
|
||||
parse (directoryWildcardPatterns, directoryWildcards);
|
||||
}
|
||||
|
||||
WildcardFileFilter::~WildcardFileFilter()
|
||||
{
|
||||
}
|
||||
|
||||
bool WildcardFileFilter::isFileSuitable (const File& file) const
|
||||
{
|
||||
return match (file, fileWildcards);
|
||||
}
|
||||
|
||||
bool WildcardFileFilter::isDirectorySuitable (const File& file) const
|
||||
{
|
||||
return match (file, directoryWildcards);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void WildcardFileFilter::parse (const String& pattern, StringArray& result)
|
||||
{
|
||||
result.addTokens (pattern.toLowerCase(), ";,", "\"'");
|
||||
|
||||
result.trim();
|
||||
result.removeEmptyStrings();
|
||||
|
||||
// special case for *.*, because people use it to mean "any file", but it
|
||||
// would actually ignore files with no extension.
|
||||
for (int i = result.size(); --i >= 0;)
|
||||
if (result[i] == "*.*")
|
||||
result.set (i, "*");
|
||||
}
|
||||
|
||||
bool WildcardFileFilter::match (const File& file, const StringArray& wildcards)
|
||||
{
|
||||
const String filename (file.getFileName());
|
||||
|
||||
for (int i = wildcards.size(); --i >= 0;)
|
||||
if (filename.matchesWildcard (wildcards[i], true))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_WILDCARDFILEFILTER_JUCEHEADER__
|
||||
#define __JUCE_WILDCARDFILEFILTER_JUCEHEADER__
|
||||
|
||||
#include "juce_FileFilter.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A type of FileFilter that works by wildcard pattern matching.
|
||||
|
||||
This filter only allows files that match one of the specified patterns, but
|
||||
allows all directories through.
|
||||
|
||||
@see FileFilter, DirectoryContentsList, FileListComponent, FileBrowserComponent
|
||||
*/
|
||||
class JUCE_API WildcardFileFilter : public FileFilter
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/**
|
||||
Creates a wildcard filter for one or more patterns.
|
||||
|
||||
The wildcardPatterns parameter is a comma or semicolon-delimited set of
|
||||
patterns, e.g. "*.wav;*.aiff" would look for files ending in either .wav
|
||||
or .aiff.
|
||||
|
||||
Passing an empty string as a pattern will fail to match anything, so by leaving
|
||||
either the file or directory pattern parameter empty means you can control
|
||||
whether files or directories are found.
|
||||
|
||||
The description is a name to show the user in a list of possible patterns, so
|
||||
for the wav/aiff example, your description might be "audio files".
|
||||
*/
|
||||
WildcardFileFilter (const String& fileWildcardPatterns,
|
||||
const String& directoryWildcardPatterns,
|
||||
const String& description);
|
||||
|
||||
/** Destructor. */
|
||||
~WildcardFileFilter();
|
||||
|
||||
//==============================================================================
|
||||
/** Returns true if the filename matches one of the patterns specified. */
|
||||
bool isFileSuitable (const File& file) const;
|
||||
|
||||
/** This always returns true. */
|
||||
bool isDirectorySuitable (const File& file) const;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
StringArray fileWildcards, directoryWildcards;
|
||||
|
||||
static void parse (const String& pattern, StringArray& result);
|
||||
static bool match (const File& file, const StringArray& wildcards);
|
||||
|
||||
JUCE_LEAK_DETECTOR (WildcardFileFilter);
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif // __JUCE_WILDCARDFILEFILTER_JUCEHEADER__
|
||||
280
modules/juce_gui_basics/juce_gui_basics.cpp
Normal file
280
modules/juce_gui_basics/juce_gui_basics.cpp
Normal file
|
|
@ -0,0 +1,280 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#if defined (__JUCE_GUI_BASICS_JUCEHEADER__) && ! JUCE_AMALGAMATED_INCLUDE
|
||||
/* When you add this cpp file to your project, you mustn't include it in a file where you've
|
||||
already included any other headers - just put it inside a file on its own, possibly with your config
|
||||
flags preceding it, but don't include anything else. That also includes avoiding any automatic prefix
|
||||
header files that the compiler may be using.
|
||||
*/
|
||||
#error "Incorrect use of JUCE cpp file"
|
||||
#endif
|
||||
|
||||
#define JUCE_DONT_DEFINE_MACROS 1
|
||||
#include "../juce_core/native/juce_BasicNativeHeaders.h"
|
||||
#include "juce_gui_basics.h"
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_MAC
|
||||
#import <WebKit/WebKit.h>
|
||||
#define Point CarbonDummyPointName
|
||||
#define Component CarbonDummyCompName
|
||||
#import <Carbon/Carbon.h> // still needed for SetSystemUIMode()
|
||||
#undef Point
|
||||
#undef Component
|
||||
|
||||
#elif JUCE_IOS
|
||||
#if JUCE_OPENGL
|
||||
#include <OpenGLES/ES1/gl.h>
|
||||
#include <OpenGLES/ES1/glext.h>
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
#elif JUCE_WINDOWS
|
||||
#include <windowsx.h>
|
||||
#include <vfw.h>
|
||||
#include <commdlg.h>
|
||||
|
||||
#if JUCE_WEB_BROWSER
|
||||
#include <Exdisp.h>
|
||||
#include <exdispid.h>
|
||||
#endif
|
||||
|
||||
#if JUCE_MSVC && ! JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES
|
||||
#pragma comment(lib, "vfw32.lib")
|
||||
#pragma comment(lib, "imm32.lib")
|
||||
#endif
|
||||
|
||||
#if JUCE_OPENGL
|
||||
#include <gl/gl.h>
|
||||
|
||||
#if JUCE_MSVC && ! JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES
|
||||
#pragma comment(lib, "OpenGL32.Lib")
|
||||
#pragma comment(lib, "GlU32.Lib")
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if JUCE_QUICKTIME && JUCE_MSVC && ! JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES
|
||||
#pragma comment (lib, "QTMLClient.lib")
|
||||
#endif
|
||||
|
||||
#if JUCE_DIRECT2D && JUCE_MSVC && ! JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES
|
||||
#pragma comment (lib, "Dwrite.lib")
|
||||
#pragma comment (lib, "D2d1.lib")
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
#elif JUCE_LINUX
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/Xresource.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/Xmd.h>
|
||||
#include <X11/keysym.h>
|
||||
#include <X11/cursorfont.h>
|
||||
|
||||
#if JUCE_USE_XINERAMA
|
||||
/* If you're trying to use Xinerama, you'll need to install the "libxinerama-dev" package.. */
|
||||
#include <X11/extensions/Xinerama.h>
|
||||
#endif
|
||||
|
||||
#if JUCE_USE_XSHM
|
||||
#include <X11/extensions/XShm.h>
|
||||
#include <sys/shm.h>
|
||||
#include <sys/ipc.h>
|
||||
#endif
|
||||
|
||||
#if JUCE_USE_XRENDER
|
||||
// If you're missing these headers, try installing the libxrender-dev and libxcomposite-dev
|
||||
#include <X11/extensions/Xrender.h>
|
||||
#include <X11/extensions/Xcomposite.h>
|
||||
#endif
|
||||
|
||||
#if JUCE_USE_XCURSOR
|
||||
// If you're missing this header, try installing the libxcursor-dev package
|
||||
#include <X11/Xcursor/Xcursor.h>
|
||||
#endif
|
||||
|
||||
#undef SIZEOF
|
||||
#undef KeyPress
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
// START_AUTOINCLUDE components/*.cpp, mouse/*.cpp, keyboard/*.cpp, buttons/*.cpp,
|
||||
// drawables/*.cpp, filebrowser/*.cpp, layout/*.cpp, lookandfeel/*.cpp,
|
||||
// menus/*.cpp, positioning/*.cpp, properties/*.cpp, widgets/*.cpp,
|
||||
// windows/*.cpp, commands/*.cpp, application/*.cpp, misc/*.cpp
|
||||
#include "components/juce_Component.cpp"
|
||||
#include "components/juce_ComponentListener.cpp"
|
||||
#include "components/juce_Desktop.cpp"
|
||||
#include "components/juce_ModalComponentManager.cpp"
|
||||
#include "mouse/juce_ComponentDragger.cpp"
|
||||
#include "mouse/juce_DragAndDropContainer.cpp"
|
||||
#include "mouse/juce_MouseCursor.cpp"
|
||||
#include "mouse/juce_MouseEvent.cpp"
|
||||
#include "mouse/juce_MouseInputSource.cpp"
|
||||
#include "mouse/juce_MouseListener.cpp"
|
||||
#include "keyboard/juce_CaretComponent.cpp"
|
||||
#include "keyboard/juce_KeyboardFocusTraverser.cpp"
|
||||
#include "keyboard/juce_KeyListener.cpp"
|
||||
#include "keyboard/juce_KeyPress.cpp"
|
||||
#include "keyboard/juce_ModifierKeys.cpp"
|
||||
#include "buttons/juce_ArrowButton.cpp"
|
||||
#include "buttons/juce_Button.cpp"
|
||||
#include "buttons/juce_DrawableButton.cpp"
|
||||
#include "buttons/juce_HyperlinkButton.cpp"
|
||||
#include "buttons/juce_ImageButton.cpp"
|
||||
#include "buttons/juce_ShapeButton.cpp"
|
||||
#include "buttons/juce_TextButton.cpp"
|
||||
#include "buttons/juce_ToggleButton.cpp"
|
||||
#include "buttons/juce_ToolbarButton.cpp"
|
||||
#include "drawables/juce_Drawable.cpp"
|
||||
#include "drawables/juce_DrawableComposite.cpp"
|
||||
#include "drawables/juce_DrawableImage.cpp"
|
||||
#include "drawables/juce_DrawablePath.cpp"
|
||||
#include "drawables/juce_DrawableRectangle.cpp"
|
||||
#include "drawables/juce_DrawableShape.cpp"
|
||||
#include "drawables/juce_DrawableText.cpp"
|
||||
#include "drawables/juce_SVGParser.cpp"
|
||||
#include "filebrowser/juce_DirectoryContentsDisplayComponent.cpp"
|
||||
#include "filebrowser/juce_DirectoryContentsList.cpp"
|
||||
#include "filebrowser/juce_FileBrowserComponent.cpp"
|
||||
#include "filebrowser/juce_FileChooser.cpp"
|
||||
#include "filebrowser/juce_FileChooserDialogBox.cpp"
|
||||
#include "filebrowser/juce_FileFilter.cpp"
|
||||
#include "filebrowser/juce_FileListComponent.cpp"
|
||||
#include "filebrowser/juce_FilenameComponent.cpp"
|
||||
#include "filebrowser/juce_FileSearchPathListComponent.cpp"
|
||||
#include "filebrowser/juce_FileTreeComponent.cpp"
|
||||
#include "filebrowser/juce_ImagePreviewComponent.cpp"
|
||||
#include "filebrowser/juce_WildcardFileFilter.cpp"
|
||||
#include "layout/juce_ComponentAnimator.cpp"
|
||||
#include "layout/juce_ComponentBoundsConstrainer.cpp"
|
||||
#include "layout/juce_ComponentBuilder.cpp"
|
||||
#include "layout/juce_ComponentMovementWatcher.cpp"
|
||||
#include "layout/juce_GroupComponent.cpp"
|
||||
#include "layout/juce_MultiDocumentPanel.cpp"
|
||||
#include "layout/juce_ResizableBorderComponent.cpp"
|
||||
#include "layout/juce_ResizableCornerComponent.cpp"
|
||||
#include "layout/juce_ResizableEdgeComponent.cpp"
|
||||
#include "layout/juce_ScrollBar.cpp"
|
||||
#include "layout/juce_StretchableLayoutManager.cpp"
|
||||
#include "layout/juce_StretchableLayoutResizerBar.cpp"
|
||||
#include "layout/juce_StretchableObjectResizer.cpp"
|
||||
#include "layout/juce_TabbedButtonBar.cpp"
|
||||
#include "layout/juce_TabbedComponent.cpp"
|
||||
#include "layout/juce_Viewport.cpp"
|
||||
#include "lookandfeel/juce_LookAndFeel.cpp"
|
||||
#include "menus/juce_MenuBarComponent.cpp"
|
||||
#include "menus/juce_MenuBarModel.cpp"
|
||||
#include "menus/juce_PopupMenu.cpp"
|
||||
#include "positioning/juce_MarkerList.cpp"
|
||||
#include "positioning/juce_RelativeCoordinate.cpp"
|
||||
#include "positioning/juce_RelativeCoordinatePositioner.cpp"
|
||||
#include "positioning/juce_RelativeParallelogram.cpp"
|
||||
#include "positioning/juce_RelativePoint.cpp"
|
||||
#include "positioning/juce_RelativePointPath.cpp"
|
||||
#include "positioning/juce_RelativeRectangle.cpp"
|
||||
#include "properties/juce_BooleanPropertyComponent.cpp"
|
||||
#include "properties/juce_ButtonPropertyComponent.cpp"
|
||||
#include "properties/juce_ChoicePropertyComponent.cpp"
|
||||
#include "properties/juce_PropertyComponent.cpp"
|
||||
#include "properties/juce_PropertyPanel.cpp"
|
||||
#include "properties/juce_SliderPropertyComponent.cpp"
|
||||
#include "properties/juce_TextPropertyComponent.cpp"
|
||||
#include "widgets/juce_ComboBox.cpp"
|
||||
#include "widgets/juce_ImageComponent.cpp"
|
||||
#include "widgets/juce_Label.cpp"
|
||||
#include "widgets/juce_ListBox.cpp"
|
||||
#include "widgets/juce_ProgressBar.cpp"
|
||||
#include "widgets/juce_Slider.cpp"
|
||||
#include "widgets/juce_TableHeaderComponent.cpp"
|
||||
#include "widgets/juce_TableListBox.cpp"
|
||||
#include "widgets/juce_TextEditor.cpp"
|
||||
#include "widgets/juce_Toolbar.cpp"
|
||||
#include "widgets/juce_ToolbarItemComponent.cpp"
|
||||
#include "widgets/juce_ToolbarItemPalette.cpp"
|
||||
#include "widgets/juce_TreeView.cpp"
|
||||
#include "windows/juce_AlertWindow.cpp"
|
||||
#include "windows/juce_CallOutBox.cpp"
|
||||
#include "windows/juce_ComponentPeer.cpp"
|
||||
#include "windows/juce_DialogWindow.cpp"
|
||||
#include "windows/juce_DocumentWindow.cpp"
|
||||
#include "windows/juce_ResizableWindow.cpp"
|
||||
#include "windows/juce_ThreadWithProgressWindow.cpp"
|
||||
#include "windows/juce_TooltipWindow.cpp"
|
||||
#include "windows/juce_TopLevelWindow.cpp"
|
||||
#include "commands/juce_ApplicationCommandInfo.cpp"
|
||||
#include "commands/juce_ApplicationCommandManager.cpp"
|
||||
#include "commands/juce_ApplicationCommandTarget.cpp"
|
||||
#include "commands/juce_KeyPressMappingSet.cpp"
|
||||
#include "application/juce_Application.cpp"
|
||||
#include "misc/juce_BubbleComponent.cpp"
|
||||
#include "misc/juce_DropShadower.cpp"
|
||||
// END_AUTOINCLUDE
|
||||
|
||||
using namespace JUCE_NAMESPACE;
|
||||
|
||||
//==============================================================================
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
#if JUCE_MAC || JUCE_IOS
|
||||
#include "../juce_core/native/juce_osx_ObjCHelpers.h"
|
||||
#include "../juce_core/native/juce_mac_ObjCSuffix.h"
|
||||
#include "../juce_graphics/native/juce_mac_CoreGraphicsHelpers.h"
|
||||
#include "../juce_graphics/native/juce_mac_CoreGraphicsContext.h"
|
||||
|
||||
#if JUCE_IOS
|
||||
#include "native/juce_ios_UIViewComponentPeer.mm"
|
||||
#include "native/juce_ios_Windowing.mm"
|
||||
#else
|
||||
#include "native/juce_mac_NSViewComponentPeer.mm"
|
||||
#include "native/juce_mac_Windowing.mm"
|
||||
#include "native/juce_mac_MainMenu.mm"
|
||||
#endif
|
||||
|
||||
#include "native/juce_mac_MouseCursor.mm"
|
||||
#include "native/juce_mac_FileChooser.mm"
|
||||
|
||||
#elif JUCE_WINDOWS
|
||||
#include "../juce_core/native/juce_win32_ComSmartPtr.h"
|
||||
#include "../juce_events/native/juce_win32_HiddenMessageWindow.h"
|
||||
#include "native/juce_win32_Windowing.cpp"
|
||||
#include "native/juce_win32_DragAndDrop.cpp"
|
||||
#include "native/juce_win32_FileChooser.cpp"
|
||||
|
||||
#elif JUCE_LINUX
|
||||
#include "native/juce_linux_Clipboard.cpp"
|
||||
#include "native/juce_linux_Windowing.cpp"
|
||||
#include "native/juce_linux_FileChooser.cpp"
|
||||
|
||||
#elif JUCE_ANDROID
|
||||
#include "../juce_core/native/juce_android_JNIHelpers.h"
|
||||
#include "native/juce_android_Windowing.cpp"
|
||||
#include "native/juce_android_FileChooser.cpp"
|
||||
|
||||
#endif
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
442
modules/juce_gui_basics/juce_gui_basics.h
Normal file
442
modules/juce_gui_basics/juce_gui_basics.h
Normal file
|
|
@ -0,0 +1,442 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_GUI_BASICS_JUCEHEADER__
|
||||
#define __JUCE_GUI_BASICS_JUCEHEADER__
|
||||
|
||||
#include "../juce_graphics/juce_graphics.h"
|
||||
#include "../juce_data_structures/juce_data_structures.h"
|
||||
|
||||
//=============================================================================
|
||||
/** Config: JUCE_ENABLE_REPAINT_DEBUGGING
|
||||
If this option is turned on, each area of the screen that gets repainted will
|
||||
flash in a random colour, so that you can see exactly which bits of your
|
||||
components are being drawn.
|
||||
*/
|
||||
#ifndef JUCE_ENABLE_REPAINT_DEBUGGING
|
||||
#define JUCE_ENABLE_REPAINT_DEBUGGING 0
|
||||
#endif
|
||||
|
||||
/** JUCE_USE_XINERAMA: Enables Xinerama multi-monitor support (Linux only).
|
||||
Unless you specifically want to disable this, it's best to leave this option turned on.
|
||||
*/
|
||||
#ifndef JUCE_USE_XINERAMA
|
||||
#define JUCE_USE_XINERAMA 1
|
||||
#endif
|
||||
|
||||
/** Config: JUCE_USE_XSHM
|
||||
Enables X shared memory for faster rendering on Linux. This is best left turned on
|
||||
unless you have a good reason to disable it.
|
||||
*/
|
||||
#ifndef JUCE_USE_XSHM
|
||||
#define JUCE_USE_XSHM 1
|
||||
#endif
|
||||
|
||||
/** Config: JUCE_USE_XRENDER
|
||||
Enables XRender to allow semi-transparent windowing on Linux.
|
||||
*/
|
||||
#ifndef JUCE_USE_XRENDER
|
||||
#define JUCE_USE_XRENDER 0
|
||||
#endif
|
||||
|
||||
/** Config: JUCE_USE_XCURSOR
|
||||
Uses XCursor to allow ARGB cursor on Linux. This is best left turned on unless you have
|
||||
a good reason to disable it.
|
||||
*/
|
||||
#ifndef JUCE_USE_XCURSOR
|
||||
#define JUCE_USE_XCURSOR 1
|
||||
#endif
|
||||
|
||||
//=============================================================================
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
// START_AUTOINCLUDE components, mouse, keyboard, buttons, drawables,
|
||||
// filebrowser, layout, lookandfeel, menus, positioning, properties,
|
||||
// widgets, windows, commands, application, misc
|
||||
#ifndef __JUCE_COMPONENT_JUCEHEADER__
|
||||
#include "components/juce_Component.h"
|
||||
#endif
|
||||
#ifndef __JUCE_COMPONENTLISTENER_JUCEHEADER__
|
||||
#include "components/juce_ComponentListener.h"
|
||||
#endif
|
||||
#ifndef __JUCE_DESKTOP_JUCEHEADER__
|
||||
#include "components/juce_Desktop.h"
|
||||
#endif
|
||||
#ifndef __JUCE_MODALCOMPONENTMANAGER_JUCEHEADER__
|
||||
#include "components/juce_ModalComponentManager.h"
|
||||
#endif
|
||||
#ifndef __JUCE_COMPONENTDRAGGER_JUCEHEADER__
|
||||
#include "mouse/juce_ComponentDragger.h"
|
||||
#endif
|
||||
#ifndef __JUCE_DRAGANDDROPCONTAINER_JUCEHEADER__
|
||||
#include "mouse/juce_DragAndDropContainer.h"
|
||||
#endif
|
||||
#ifndef __JUCE_DRAGANDDROPTARGET_JUCEHEADER__
|
||||
#include "mouse/juce_DragAndDropTarget.h"
|
||||
#endif
|
||||
#ifndef __JUCE_FILEDRAGANDDROPTARGET_JUCEHEADER__
|
||||
#include "mouse/juce_FileDragAndDropTarget.h"
|
||||
#endif
|
||||
#ifndef __JUCE_LASSOCOMPONENT_JUCEHEADER__
|
||||
#include "mouse/juce_LassoComponent.h"
|
||||
#endif
|
||||
#ifndef __JUCE_MOUSECURSOR_JUCEHEADER__
|
||||
#include "mouse/juce_MouseCursor.h"
|
||||
#endif
|
||||
#ifndef __JUCE_MOUSEEVENT_JUCEHEADER__
|
||||
#include "mouse/juce_MouseEvent.h"
|
||||
#endif
|
||||
#ifndef __JUCE_MOUSEINPUTSOURCE_JUCEHEADER__
|
||||
#include "mouse/juce_MouseInputSource.h"
|
||||
#endif
|
||||
#ifndef __JUCE_MOUSELISTENER_JUCEHEADER__
|
||||
#include "mouse/juce_MouseListener.h"
|
||||
#endif
|
||||
#ifndef __JUCE_SELECTEDITEMSET_JUCEHEADER__
|
||||
#include "mouse/juce_SelectedItemSet.h"
|
||||
#endif
|
||||
#ifndef __JUCE_TOOLTIPCLIENT_JUCEHEADER__
|
||||
#include "mouse/juce_TooltipClient.h"
|
||||
#endif
|
||||
#ifndef __JUCE_CARETCOMPONENT_JUCEHEADER__
|
||||
#include "keyboard/juce_CaretComponent.h"
|
||||
#endif
|
||||
#ifndef __JUCE_KEYBOARDFOCUSTRAVERSER_JUCEHEADER__
|
||||
#include "keyboard/juce_KeyboardFocusTraverser.h"
|
||||
#endif
|
||||
#ifndef __JUCE_KEYLISTENER_JUCEHEADER__
|
||||
#include "keyboard/juce_KeyListener.h"
|
||||
#endif
|
||||
#ifndef __JUCE_KEYPRESS_JUCEHEADER__
|
||||
#include "keyboard/juce_KeyPress.h"
|
||||
#endif
|
||||
#ifndef __JUCE_MODIFIERKEYS_JUCEHEADER__
|
||||
#include "keyboard/juce_ModifierKeys.h"
|
||||
#endif
|
||||
#ifndef __JUCE_SYSTEMCLIPBOARD_JUCEHEADER__
|
||||
#include "keyboard/juce_SystemClipboard.h"
|
||||
#endif
|
||||
#ifndef __JUCE_TEXTEDITORKEYMAPPER_JUCEHEADER__
|
||||
#include "keyboard/juce_TextEditorKeyMapper.h"
|
||||
#endif
|
||||
#ifndef __JUCE_TEXTINPUTTARGET_JUCEHEADER__
|
||||
#include "keyboard/juce_TextInputTarget.h"
|
||||
#endif
|
||||
#ifndef __JUCE_ARROWBUTTON_JUCEHEADER__
|
||||
#include "buttons/juce_ArrowButton.h"
|
||||
#endif
|
||||
#ifndef __JUCE_BUTTON_JUCEHEADER__
|
||||
#include "buttons/juce_Button.h"
|
||||
#endif
|
||||
#ifndef __JUCE_DRAWABLEBUTTON_JUCEHEADER__
|
||||
#include "buttons/juce_DrawableButton.h"
|
||||
#endif
|
||||
#ifndef __JUCE_HYPERLINKBUTTON_JUCEHEADER__
|
||||
#include "buttons/juce_HyperlinkButton.h"
|
||||
#endif
|
||||
#ifndef __JUCE_IMAGEBUTTON_JUCEHEADER__
|
||||
#include "buttons/juce_ImageButton.h"
|
||||
#endif
|
||||
#ifndef __JUCE_SHAPEBUTTON_JUCEHEADER__
|
||||
#include "buttons/juce_ShapeButton.h"
|
||||
#endif
|
||||
#ifndef __JUCE_TEXTBUTTON_JUCEHEADER__
|
||||
#include "buttons/juce_TextButton.h"
|
||||
#endif
|
||||
#ifndef __JUCE_TOGGLEBUTTON_JUCEHEADER__
|
||||
#include "buttons/juce_ToggleButton.h"
|
||||
#endif
|
||||
#ifndef __JUCE_TOOLBARBUTTON_JUCEHEADER__
|
||||
#include "buttons/juce_ToolbarButton.h"
|
||||
#endif
|
||||
#ifndef __JUCE_DRAWABLE_JUCEHEADER__
|
||||
#include "drawables/juce_Drawable.h"
|
||||
#endif
|
||||
#ifndef __JUCE_DRAWABLECOMPOSITE_JUCEHEADER__
|
||||
#include "drawables/juce_DrawableComposite.h"
|
||||
#endif
|
||||
#ifndef __JUCE_DRAWABLEIMAGE_JUCEHEADER__
|
||||
#include "drawables/juce_DrawableImage.h"
|
||||
#endif
|
||||
#ifndef __JUCE_DRAWABLEPATH_JUCEHEADER__
|
||||
#include "drawables/juce_DrawablePath.h"
|
||||
#endif
|
||||
#ifndef __JUCE_DRAWABLERECTANGLE_JUCEHEADER__
|
||||
#include "drawables/juce_DrawableRectangle.h"
|
||||
#endif
|
||||
#ifndef __JUCE_DRAWABLESHAPE_JUCEHEADER__
|
||||
#include "drawables/juce_DrawableShape.h"
|
||||
#endif
|
||||
#ifndef __JUCE_DRAWABLETEXT_JUCEHEADER__
|
||||
#include "drawables/juce_DrawableText.h"
|
||||
#endif
|
||||
#ifndef __JUCE_DIRECTORYCONTENTSDISPLAYCOMPONENT_JUCEHEADER__
|
||||
#include "filebrowser/juce_DirectoryContentsDisplayComponent.h"
|
||||
#endif
|
||||
#ifndef __JUCE_DIRECTORYCONTENTSLIST_JUCEHEADER__
|
||||
#include "filebrowser/juce_DirectoryContentsList.h"
|
||||
#endif
|
||||
#ifndef __JUCE_FILEBROWSERCOMPONENT_JUCEHEADER__
|
||||
#include "filebrowser/juce_FileBrowserComponent.h"
|
||||
#endif
|
||||
#ifndef __JUCE_FILEBROWSERLISTENER_JUCEHEADER__
|
||||
#include "filebrowser/juce_FileBrowserListener.h"
|
||||
#endif
|
||||
#ifndef __JUCE_FILECHOOSER_JUCEHEADER__
|
||||
#include "filebrowser/juce_FileChooser.h"
|
||||
#endif
|
||||
#ifndef __JUCE_FILECHOOSERDIALOGBOX_JUCEHEADER__
|
||||
#include "filebrowser/juce_FileChooserDialogBox.h"
|
||||
#endif
|
||||
#ifndef __JUCE_FILEFILTER_JUCEHEADER__
|
||||
#include "filebrowser/juce_FileFilter.h"
|
||||
#endif
|
||||
#ifndef __JUCE_FILELISTCOMPONENT_JUCEHEADER__
|
||||
#include "filebrowser/juce_FileListComponent.h"
|
||||
#endif
|
||||
#ifndef __JUCE_FILENAMECOMPONENT_JUCEHEADER__
|
||||
#include "filebrowser/juce_FilenameComponent.h"
|
||||
#endif
|
||||
#ifndef __JUCE_FILEPREVIEWCOMPONENT_JUCEHEADER__
|
||||
#include "filebrowser/juce_FilePreviewComponent.h"
|
||||
#endif
|
||||
#ifndef __JUCE_FILESEARCHPATHLISTCOMPONENT_JUCEHEADER__
|
||||
#include "filebrowser/juce_FileSearchPathListComponent.h"
|
||||
#endif
|
||||
#ifndef __JUCE_FILETREECOMPONENT_JUCEHEADER__
|
||||
#include "filebrowser/juce_FileTreeComponent.h"
|
||||
#endif
|
||||
#ifndef __JUCE_IMAGEPREVIEWCOMPONENT_JUCEHEADER__
|
||||
#include "filebrowser/juce_ImagePreviewComponent.h"
|
||||
#endif
|
||||
#ifndef __JUCE_WILDCARDFILEFILTER_JUCEHEADER__
|
||||
#include "filebrowser/juce_WildcardFileFilter.h"
|
||||
#endif
|
||||
#ifndef __JUCE_COMPONENTANIMATOR_JUCEHEADER__
|
||||
#include "layout/juce_ComponentAnimator.h"
|
||||
#endif
|
||||
#ifndef __JUCE_COMPONENTBOUNDSCONSTRAINER_JUCEHEADER__
|
||||
#include "layout/juce_ComponentBoundsConstrainer.h"
|
||||
#endif
|
||||
#ifndef __JUCE_COMPONENTBUILDER_JUCEHEADER__
|
||||
#include "layout/juce_ComponentBuilder.h"
|
||||
#endif
|
||||
#ifndef __JUCE_COMPONENTMOVEMENTWATCHER_JUCEHEADER__
|
||||
#include "layout/juce_ComponentMovementWatcher.h"
|
||||
#endif
|
||||
#ifndef __JUCE_GROUPCOMPONENT_JUCEHEADER__
|
||||
#include "layout/juce_GroupComponent.h"
|
||||
#endif
|
||||
#ifndef __JUCE_MULTIDOCUMENTPANEL_JUCEHEADER__
|
||||
#include "layout/juce_MultiDocumentPanel.h"
|
||||
#endif
|
||||
#ifndef __JUCE_RESIZABLEBORDERCOMPONENT_JUCEHEADER__
|
||||
#include "layout/juce_ResizableBorderComponent.h"
|
||||
#endif
|
||||
#ifndef __JUCE_RESIZABLECORNERCOMPONENT_JUCEHEADER__
|
||||
#include "layout/juce_ResizableCornerComponent.h"
|
||||
#endif
|
||||
#ifndef __JUCE_RESIZABLEEDGECOMPONENT_JUCEHEADER__
|
||||
#include "layout/juce_ResizableEdgeComponent.h"
|
||||
#endif
|
||||
#ifndef __JUCE_SCROLLBAR_JUCEHEADER__
|
||||
#include "layout/juce_ScrollBar.h"
|
||||
#endif
|
||||
#ifndef __JUCE_STRETCHABLELAYOUTMANAGER_JUCEHEADER__
|
||||
#include "layout/juce_StretchableLayoutManager.h"
|
||||
#endif
|
||||
#ifndef __JUCE_STRETCHABLELAYOUTRESIZERBAR_JUCEHEADER__
|
||||
#include "layout/juce_StretchableLayoutResizerBar.h"
|
||||
#endif
|
||||
#ifndef __JUCE_STRETCHABLEOBJECTRESIZER_JUCEHEADER__
|
||||
#include "layout/juce_StretchableObjectResizer.h"
|
||||
#endif
|
||||
#ifndef __JUCE_TABBEDBUTTONBAR_JUCEHEADER__
|
||||
#include "layout/juce_TabbedButtonBar.h"
|
||||
#endif
|
||||
#ifndef __JUCE_TABBEDCOMPONENT_JUCEHEADER__
|
||||
#include "layout/juce_TabbedComponent.h"
|
||||
#endif
|
||||
#ifndef __JUCE_VIEWPORT_JUCEHEADER__
|
||||
#include "layout/juce_Viewport.h"
|
||||
#endif
|
||||
#ifndef __JUCE_LOOKANDFEEL_JUCEHEADER__
|
||||
#include "lookandfeel/juce_LookAndFeel.h"
|
||||
#endif
|
||||
#ifndef __JUCE_MENUBARCOMPONENT_JUCEHEADER__
|
||||
#include "menus/juce_MenuBarComponent.h"
|
||||
#endif
|
||||
#ifndef __JUCE_MENUBARMODEL_JUCEHEADER__
|
||||
#include "menus/juce_MenuBarModel.h"
|
||||
#endif
|
||||
#ifndef __JUCE_POPUPMENU_JUCEHEADER__
|
||||
#include "menus/juce_PopupMenu.h"
|
||||
#endif
|
||||
#ifndef __JUCE_MARKERLIST_JUCEHEADER__
|
||||
#include "positioning/juce_MarkerList.h"
|
||||
#endif
|
||||
#ifndef __JUCE_RELATIVECOORDINATE_JUCEHEADER__
|
||||
#include "positioning/juce_RelativeCoordinate.h"
|
||||
#endif
|
||||
#ifndef __JUCE_RELATIVECOORDINATEPOSITIONER_JUCEHEADER__
|
||||
#include "positioning/juce_RelativeCoordinatePositioner.h"
|
||||
#endif
|
||||
#ifndef __JUCE_RELATIVEPARALLELOGRAM_JUCEHEADER__
|
||||
#include "positioning/juce_RelativeParallelogram.h"
|
||||
#endif
|
||||
#ifndef __JUCE_RELATIVEPOINT_JUCEHEADER__
|
||||
#include "positioning/juce_RelativePoint.h"
|
||||
#endif
|
||||
#ifndef __JUCE_RELATIVEPOINTPATH_JUCEHEADER__
|
||||
#include "positioning/juce_RelativePointPath.h"
|
||||
#endif
|
||||
#ifndef __JUCE_RELATIVERECTANGLE_JUCEHEADER__
|
||||
#include "positioning/juce_RelativeRectangle.h"
|
||||
#endif
|
||||
#ifndef __JUCE_BOOLEANPROPERTYCOMPONENT_JUCEHEADER__
|
||||
#include "properties/juce_BooleanPropertyComponent.h"
|
||||
#endif
|
||||
#ifndef __JUCE_BUTTONPROPERTYCOMPONENT_JUCEHEADER__
|
||||
#include "properties/juce_ButtonPropertyComponent.h"
|
||||
#endif
|
||||
#ifndef __JUCE_CHOICEPROPERTYCOMPONENT_JUCEHEADER__
|
||||
#include "properties/juce_ChoicePropertyComponent.h"
|
||||
#endif
|
||||
#ifndef __JUCE_PROPERTYCOMPONENT_JUCEHEADER__
|
||||
#include "properties/juce_PropertyComponent.h"
|
||||
#endif
|
||||
#ifndef __JUCE_PROPERTYPANEL_JUCEHEADER__
|
||||
#include "properties/juce_PropertyPanel.h"
|
||||
#endif
|
||||
#ifndef __JUCE_SLIDERPROPERTYCOMPONENT_JUCEHEADER__
|
||||
#include "properties/juce_SliderPropertyComponent.h"
|
||||
#endif
|
||||
#ifndef __JUCE_TEXTPROPERTYCOMPONENT_JUCEHEADER__
|
||||
#include "properties/juce_TextPropertyComponent.h"
|
||||
#endif
|
||||
#ifndef __JUCE_COMBOBOX_JUCEHEADER__
|
||||
#include "widgets/juce_ComboBox.h"
|
||||
#endif
|
||||
#ifndef __JUCE_IMAGECOMPONENT_JUCEHEADER__
|
||||
#include "widgets/juce_ImageComponent.h"
|
||||
#endif
|
||||
#ifndef __JUCE_LABEL_JUCEHEADER__
|
||||
#include "widgets/juce_Label.h"
|
||||
#endif
|
||||
#ifndef __JUCE_LISTBOX_JUCEHEADER__
|
||||
#include "widgets/juce_ListBox.h"
|
||||
#endif
|
||||
#ifndef __JUCE_PROGRESSBAR_JUCEHEADER__
|
||||
#include "widgets/juce_ProgressBar.h"
|
||||
#endif
|
||||
#ifndef __JUCE_SLIDER_JUCEHEADER__
|
||||
#include "widgets/juce_Slider.h"
|
||||
#endif
|
||||
#ifndef __JUCE_TABLEHEADERCOMPONENT_JUCEHEADER__
|
||||
#include "widgets/juce_TableHeaderComponent.h"
|
||||
#endif
|
||||
#ifndef __JUCE_TABLELISTBOX_JUCEHEADER__
|
||||
#include "widgets/juce_TableListBox.h"
|
||||
#endif
|
||||
#ifndef __JUCE_TEXTEDITOR_JUCEHEADER__
|
||||
#include "widgets/juce_TextEditor.h"
|
||||
#endif
|
||||
#ifndef __JUCE_TOOLBAR_JUCEHEADER__
|
||||
#include "widgets/juce_Toolbar.h"
|
||||
#endif
|
||||
#ifndef __JUCE_TOOLBARITEMCOMPONENT_JUCEHEADER__
|
||||
#include "widgets/juce_ToolbarItemComponent.h"
|
||||
#endif
|
||||
#ifndef __JUCE_TOOLBARITEMFACTORY_JUCEHEADER__
|
||||
#include "widgets/juce_ToolbarItemFactory.h"
|
||||
#endif
|
||||
#ifndef __JUCE_TOOLBARITEMPALETTE_JUCEHEADER__
|
||||
#include "widgets/juce_ToolbarItemPalette.h"
|
||||
#endif
|
||||
#ifndef __JUCE_TREEVIEW_JUCEHEADER__
|
||||
#include "widgets/juce_TreeView.h"
|
||||
#endif
|
||||
#ifndef __JUCE_ALERTWINDOW_JUCEHEADER__
|
||||
#include "windows/juce_AlertWindow.h"
|
||||
#endif
|
||||
#ifndef __JUCE_CALLOUTBOX_JUCEHEADER__
|
||||
#include "windows/juce_CallOutBox.h"
|
||||
#endif
|
||||
#ifndef __JUCE_COMPONENTPEER_JUCEHEADER__
|
||||
#include "windows/juce_ComponentPeer.h"
|
||||
#endif
|
||||
#ifndef __JUCE_DIALOGWINDOW_JUCEHEADER__
|
||||
#include "windows/juce_DialogWindow.h"
|
||||
#endif
|
||||
#ifndef __JUCE_DOCUMENTWINDOW_JUCEHEADER__
|
||||
#include "windows/juce_DocumentWindow.h"
|
||||
#endif
|
||||
#ifndef __JUCE_NATIVEMESSAGEBOX_JUCEHEADER__
|
||||
#include "windows/juce_NativeMessageBox.h"
|
||||
#endif
|
||||
#ifndef __JUCE_RESIZABLEWINDOW_JUCEHEADER__
|
||||
#include "windows/juce_ResizableWindow.h"
|
||||
#endif
|
||||
#ifndef __JUCE_THREADWITHPROGRESSWINDOW_JUCEHEADER__
|
||||
#include "windows/juce_ThreadWithProgressWindow.h"
|
||||
#endif
|
||||
#ifndef __JUCE_TOOLTIPWINDOW_JUCEHEADER__
|
||||
#include "windows/juce_TooltipWindow.h"
|
||||
#endif
|
||||
#ifndef __JUCE_TOPLEVELWINDOW_JUCEHEADER__
|
||||
#include "windows/juce_TopLevelWindow.h"
|
||||
#endif
|
||||
#ifndef __JUCE_APPLICATIONCOMMANDID_JUCEHEADER__
|
||||
#include "commands/juce_ApplicationCommandID.h"
|
||||
#endif
|
||||
#ifndef __JUCE_APPLICATIONCOMMANDINFO_JUCEHEADER__
|
||||
#include "commands/juce_ApplicationCommandInfo.h"
|
||||
#endif
|
||||
#ifndef __JUCE_APPLICATIONCOMMANDMANAGER_JUCEHEADER__
|
||||
#include "commands/juce_ApplicationCommandManager.h"
|
||||
#endif
|
||||
#ifndef __JUCE_APPLICATIONCOMMANDTARGET_JUCEHEADER__
|
||||
#include "commands/juce_ApplicationCommandTarget.h"
|
||||
#endif
|
||||
#ifndef __JUCE_KEYPRESSMAPPINGSET_JUCEHEADER__
|
||||
#include "commands/juce_KeyPressMappingSet.h"
|
||||
#endif
|
||||
#ifndef __JUCE_APPLICATION_JUCEHEADER__
|
||||
#include "application/juce_Application.h"
|
||||
#endif
|
||||
#ifndef __JUCE_INITIALISATION_JUCEHEADER__
|
||||
#include "application/juce_Initialisation.h"
|
||||
#endif
|
||||
#ifndef __JUCE_BUBBLECOMPONENT_JUCEHEADER__
|
||||
#include "misc/juce_BubbleComponent.h"
|
||||
#endif
|
||||
#ifndef __JUCE_DROPSHADOWER_JUCEHEADER__
|
||||
#include "misc/juce_DropShadower.h"
|
||||
#endif
|
||||
// END_AUTOINCLUDE
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
|
||||
#endif // __JUCE_GUI_BASICS_JUCEHEADER__
|
||||
35
modules/juce_gui_basics/juce_module_info
Normal file
35
modules/juce_gui_basics/juce_module_info
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
{
|
||||
"id": "juce_gui_basics",
|
||||
"name": "JUCE GUI core classes",
|
||||
"version": "2.0.0",
|
||||
"description": "Basic user-interface components and related classes.",
|
||||
"website": "http://www.juce.com/juce",
|
||||
"license": "GPL/Commercial",
|
||||
|
||||
"dependencies": [ { "id": "juce_core", "version": "matching" },
|
||||
{ "id": "juce_events", "version": "matching" },
|
||||
{ "id": "juce_graphics", "version": "matching" },
|
||||
{ "id": "juce_data_structures", "version": "matching" } ],
|
||||
|
||||
"include": "juce_gui_basics.h",
|
||||
|
||||
"compile": [ { "file": "juce_gui_basics.cpp" } ],
|
||||
|
||||
"browse": [ "components/*",
|
||||
"mouse/*",
|
||||
"keyboard/*",
|
||||
"widgets/*",
|
||||
"windows/*",
|
||||
"menus/*",
|
||||
"layout/*",
|
||||
"buttons/*",
|
||||
"positioning/*",
|
||||
"drawables/*",
|
||||
"properties/*",
|
||||
"lookandfeel/*",
|
||||
"filebrowser/*",
|
||||
"commands/*",
|
||||
"misc/*",
|
||||
"application/*",
|
||||
"native/*" ]
|
||||
}
|
||||
63
modules/juce_gui_basics/keyboard/juce_CaretComponent.cpp
Normal file
63
modules/juce_gui_basics/keyboard/juce_CaretComponent.cpp
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
//==============================================================================
|
||||
CaretComponent::CaretComponent (Component* const keyFocusOwner)
|
||||
: owner (keyFocusOwner)
|
||||
{
|
||||
setAlwaysOnTop (true);
|
||||
setInterceptsMouseClicks (false, false);
|
||||
}
|
||||
|
||||
CaretComponent::~CaretComponent()
|
||||
{
|
||||
}
|
||||
|
||||
void CaretComponent::paint (Graphics& g)
|
||||
{
|
||||
g.fillAll (findColour (caretColourId, true));
|
||||
}
|
||||
|
||||
void CaretComponent::timerCallback()
|
||||
{
|
||||
setVisible (shouldBeShown() && ! isVisible());
|
||||
}
|
||||
|
||||
void CaretComponent::setCaretPosition (const Rectangle<int>& characterArea)
|
||||
{
|
||||
startTimer (380);
|
||||
setVisible (shouldBeShown());
|
||||
setBounds (characterArea.withWidth (2));
|
||||
}
|
||||
|
||||
bool CaretComponent::shouldBeShown() const
|
||||
{
|
||||
return owner == nullptr || (owner->hasKeyboardFocus (true)
|
||||
&& ! owner->isCurrentlyBlockedByAnotherModalComponent());
|
||||
}
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
83
modules/juce_gui_basics/keyboard/juce_CaretComponent.h
Normal file
83
modules/juce_gui_basics/keyboard/juce_CaretComponent.h
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_CARETCOMPONENT_JUCEHEADER__
|
||||
#define __JUCE_CARETCOMPONENT_JUCEHEADER__
|
||||
|
||||
#include "../components/juce_Component.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
*/
|
||||
class JUCE_API CaretComponent : public Component,
|
||||
public Timer
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates the caret component.
|
||||
The keyFocusOwner is an optional component which the caret will check, making
|
||||
itself visible only when the keyFocusOwner has keyboard focus.
|
||||
*/
|
||||
CaretComponent (Component* keyFocusOwner);
|
||||
|
||||
/** Destructor. */
|
||||
~CaretComponent();
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the caret's position to place it next to the given character.
|
||||
The area is the rectangle containing the entire character that the caret is
|
||||
positioned on, so by default a vertical-line caret may choose to just show itself
|
||||
at the left of this area. You can override this method to customise its size.
|
||||
This method will also force the caret to reset its timer and become visible (if
|
||||
appropriate), so that as it moves, you can see where it is.
|
||||
*/
|
||||
virtual void setCaretPosition (const Rectangle<int>& characterArea);
|
||||
|
||||
/** A set of colour IDs to use to change the colour of various aspects of the caret.
|
||||
These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
|
||||
methods.
|
||||
@see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
|
||||
*/
|
||||
enum ColourIds
|
||||
{
|
||||
caretColourId = 0x1000204, /**< The colour with which to draw the caret. */
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void paint (Graphics& g);
|
||||
/** @internal */
|
||||
void timerCallback();
|
||||
|
||||
private:
|
||||
Component* owner;
|
||||
bool shouldBeShown() const;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (CaretComponent);
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCE_CARETCOMPONENT_JUCEHEADER__
|
||||
34
modules/juce_gui_basics/keyboard/juce_KeyListener.cpp
Normal file
34
modules/juce_gui_basics/keyboard/juce_KeyListener.cpp
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
//==============================================================================
|
||||
bool KeyListener::keyStateChanged (const bool, Component*)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
80
modules/juce_gui_basics/keyboard/juce_KeyListener.h
Normal file
80
modules/juce_gui_basics/keyboard/juce_KeyListener.h
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_KEYLISTENER_JUCEHEADER__
|
||||
#define __JUCE_KEYLISTENER_JUCEHEADER__
|
||||
|
||||
#include "juce_KeyPress.h"
|
||||
class Component;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Receives callbacks when keys are pressed.
|
||||
|
||||
You can add a key listener to a component to be informed when that component
|
||||
gets key events. See the Component::addListener method for more details.
|
||||
|
||||
@see KeyPress, Component::addKeyListener, KeyPressMappingManager
|
||||
*/
|
||||
class JUCE_API KeyListener
|
||||
{
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~KeyListener() {}
|
||||
|
||||
//==============================================================================
|
||||
/** Called to indicate that a key has been pressed.
|
||||
|
||||
If your implementation returns true, then the key event is considered to have
|
||||
been consumed, and will not be passed on to any other components. If it returns
|
||||
false, then the key will be passed to other components that might want to use it.
|
||||
|
||||
@param key the keystroke, including modifier keys
|
||||
@param originatingComponent the component that received the key event
|
||||
@see keyStateChanged, Component::keyPressed
|
||||
*/
|
||||
virtual bool keyPressed (const KeyPress& key,
|
||||
Component* originatingComponent) = 0;
|
||||
|
||||
/** Called when any key is pressed or released.
|
||||
|
||||
When this is called, classes that might be interested in
|
||||
the state of one or more keys can use KeyPress::isKeyCurrentlyDown() to
|
||||
check whether their key has changed.
|
||||
|
||||
If your implementation returns true, then the key event is considered to have
|
||||
been consumed, and will not be passed on to any other components. If it returns
|
||||
false, then the key will be passed to other components that might want to use it.
|
||||
|
||||
@param originatingComponent the component that received the key event
|
||||
@param isKeyDown true if a key is being pressed, false if one is being released
|
||||
@see KeyPress, Component::keyStateChanged
|
||||
*/
|
||||
virtual bool keyStateChanged (bool isKeyDown, Component* originatingComponent);
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCE_KEYLISTENER_JUCEHEADER__
|
||||
294
modules/juce_gui_basics/keyboard/juce_KeyPress.cpp
Normal file
294
modules/juce_gui_basics/keyboard/juce_KeyPress.cpp
Normal file
|
|
@ -0,0 +1,294 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
//==============================================================================
|
||||
KeyPress::KeyPress() noexcept
|
||||
: keyCode (0),
|
||||
mods (0),
|
||||
textCharacter (0)
|
||||
{
|
||||
}
|
||||
|
||||
KeyPress::KeyPress (const int keyCode_,
|
||||
const ModifierKeys& mods_,
|
||||
const juce_wchar textCharacter_) noexcept
|
||||
: keyCode (keyCode_),
|
||||
mods (mods_),
|
||||
textCharacter (textCharacter_)
|
||||
{
|
||||
}
|
||||
|
||||
KeyPress::KeyPress (const int keyCode_) noexcept
|
||||
: keyCode (keyCode_),
|
||||
textCharacter (0)
|
||||
{
|
||||
}
|
||||
|
||||
KeyPress::KeyPress (const KeyPress& other) noexcept
|
||||
: keyCode (other.keyCode),
|
||||
mods (other.mods),
|
||||
textCharacter (other.textCharacter)
|
||||
{
|
||||
}
|
||||
|
||||
KeyPress& KeyPress::operator= (const KeyPress& other) noexcept
|
||||
{
|
||||
keyCode = other.keyCode;
|
||||
mods = other.mods;
|
||||
textCharacter = other.textCharacter;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool KeyPress::operator== (const KeyPress& other) const noexcept
|
||||
{
|
||||
return mods.getRawFlags() == other.mods.getRawFlags()
|
||||
&& (textCharacter == other.textCharacter
|
||||
|| textCharacter == 0
|
||||
|| other.textCharacter == 0)
|
||||
&& (keyCode == other.keyCode
|
||||
|| (keyCode < 256
|
||||
&& other.keyCode < 256
|
||||
&& CharacterFunctions::toLowerCase ((juce_wchar) keyCode)
|
||||
== CharacterFunctions::toLowerCase ((juce_wchar) other.keyCode)));
|
||||
}
|
||||
|
||||
bool KeyPress::operator!= (const KeyPress& other) const noexcept
|
||||
{
|
||||
return ! operator== (other);
|
||||
}
|
||||
|
||||
bool KeyPress::isCurrentlyDown() const
|
||||
{
|
||||
return isKeyCurrentlyDown (keyCode)
|
||||
&& (ModifierKeys::getCurrentModifiers().getRawFlags() & ModifierKeys::allKeyboardModifiers)
|
||||
== (mods.getRawFlags() & ModifierKeys::allKeyboardModifiers);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
namespace KeyPressHelpers
|
||||
{
|
||||
struct KeyNameAndCode
|
||||
{
|
||||
const char* name;
|
||||
int code;
|
||||
};
|
||||
|
||||
const KeyNameAndCode translations[] =
|
||||
{
|
||||
{ "spacebar", KeyPress::spaceKey },
|
||||
{ "return", KeyPress::returnKey },
|
||||
{ "escape", KeyPress::escapeKey },
|
||||
{ "backspace", KeyPress::backspaceKey },
|
||||
{ "cursor left", KeyPress::leftKey },
|
||||
{ "cursor right", KeyPress::rightKey },
|
||||
{ "cursor up", KeyPress::upKey },
|
||||
{ "cursor down", KeyPress::downKey },
|
||||
{ "page up", KeyPress::pageUpKey },
|
||||
{ "page down", KeyPress::pageDownKey },
|
||||
{ "home", KeyPress::homeKey },
|
||||
{ "end", KeyPress::endKey },
|
||||
{ "delete", KeyPress::deleteKey },
|
||||
{ "insert", KeyPress::insertKey },
|
||||
{ "tab", KeyPress::tabKey },
|
||||
{ "play", KeyPress::playKey },
|
||||
{ "stop", KeyPress::stopKey },
|
||||
{ "fast forward", KeyPress::fastForwardKey },
|
||||
{ "rewind", KeyPress::rewindKey }
|
||||
};
|
||||
|
||||
struct ModifierDescription
|
||||
{
|
||||
const char* name;
|
||||
int flag;
|
||||
};
|
||||
|
||||
static const ModifierDescription modifierNames[] =
|
||||
{
|
||||
{ "ctrl", ModifierKeys::ctrlModifier },
|
||||
{ "control", ModifierKeys::ctrlModifier },
|
||||
{ "ctl", ModifierKeys::ctrlModifier },
|
||||
{ "shift", ModifierKeys::shiftModifier },
|
||||
{ "shft", ModifierKeys::shiftModifier },
|
||||
{ "alt", ModifierKeys::altModifier },
|
||||
{ "option", ModifierKeys::altModifier },
|
||||
{ "command", ModifierKeys::commandModifier },
|
||||
{ "cmd", ModifierKeys::commandModifier }
|
||||
};
|
||||
|
||||
const char* numberPadPrefix() noexcept { return "numpad "; }
|
||||
|
||||
int getNumpadKeyCode (const String& desc)
|
||||
{
|
||||
if (desc.containsIgnoreCase (numberPadPrefix()))
|
||||
{
|
||||
const juce_wchar lastChar = desc.trimEnd().getLastCharacter();
|
||||
|
||||
switch (lastChar)
|
||||
{
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
return (int) (KeyPress::numberPad0 + lastChar - '0');
|
||||
|
||||
case '+': return KeyPress::numberPadAdd;
|
||||
case '-': return KeyPress::numberPadSubtract;
|
||||
case '*': return KeyPress::numberPadMultiply;
|
||||
case '/': return KeyPress::numberPadDivide;
|
||||
case '.': return KeyPress::numberPadDecimalPoint;
|
||||
case '=': return KeyPress::numberPadEquals;
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
if (desc.endsWith ("separator")) return KeyPress::numberPadSeparator;
|
||||
if (desc.endsWith ("delete")) return KeyPress::numberPadDelete;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
const KeyPress KeyPress::createFromDescription (const String& desc)
|
||||
{
|
||||
int modifiers = 0;
|
||||
|
||||
for (int i = 0; i < numElementsInArray (KeyPressHelpers::modifierNames); ++i)
|
||||
if (desc.containsWholeWordIgnoreCase (KeyPressHelpers::modifierNames[i].name))
|
||||
modifiers |= KeyPressHelpers::modifierNames[i].flag;
|
||||
|
||||
int key = 0;
|
||||
|
||||
for (int i = 0; i < numElementsInArray (KeyPressHelpers::translations); ++i)
|
||||
{
|
||||
if (desc.containsWholeWordIgnoreCase (String (KeyPressHelpers::translations[i].name)))
|
||||
{
|
||||
key = KeyPressHelpers::translations[i].code;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (key == 0)
|
||||
key = KeyPressHelpers::getNumpadKeyCode (desc);
|
||||
|
||||
if (key == 0)
|
||||
{
|
||||
// see if it's a function key..
|
||||
if (! desc.containsChar ('#')) // avoid mistaking hex-codes like "#f1"
|
||||
for (int i = 1; i <= 12; ++i)
|
||||
if (desc.containsWholeWordIgnoreCase ("f" + String (i)))
|
||||
key = F1Key + i - 1;
|
||||
|
||||
if (key == 0)
|
||||
{
|
||||
// give up and use the hex code..
|
||||
const int hexCode = desc.fromFirstOccurrenceOf ("#", false, false)
|
||||
.toLowerCase()
|
||||
.retainCharacters ("0123456789abcdef")
|
||||
.getHexValue32();
|
||||
|
||||
if (hexCode > 0)
|
||||
key = hexCode;
|
||||
else
|
||||
key = (int) CharacterFunctions::toUpperCase (desc.getLastCharacter());
|
||||
}
|
||||
}
|
||||
|
||||
return KeyPress (key, ModifierKeys (modifiers), 0);
|
||||
}
|
||||
|
||||
String KeyPress::getTextDescription() const
|
||||
{
|
||||
String desc;
|
||||
|
||||
if (keyCode > 0)
|
||||
{
|
||||
// some keyboard layouts use a shift-key to get the slash, but in those cases, we
|
||||
// want to store it as being a slash, not shift+whatever.
|
||||
if (textCharacter == '/')
|
||||
return "/";
|
||||
|
||||
if (mods.isCtrlDown())
|
||||
desc << "ctrl + ";
|
||||
|
||||
if (mods.isShiftDown())
|
||||
desc << "shift + ";
|
||||
|
||||
#if JUCE_MAC
|
||||
if (mods.isAltDown())
|
||||
desc << "option + ";
|
||||
|
||||
// only do this on the mac, because on Windows ctrl and command are the same,
|
||||
// and this would get confusing
|
||||
if (mods.isCommandDown())
|
||||
desc << "command + ";
|
||||
#else
|
||||
if (mods.isAltDown())
|
||||
desc << "alt + ";
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < numElementsInArray (KeyPressHelpers::translations); ++i)
|
||||
if (keyCode == KeyPressHelpers::translations[i].code)
|
||||
return desc + KeyPressHelpers::translations[i].name;
|
||||
|
||||
if (keyCode >= F1Key && keyCode <= F16Key) desc << 'F' << (1 + keyCode - F1Key);
|
||||
else if (keyCode >= numberPad0 && keyCode <= numberPad9) desc << KeyPressHelpers::numberPadPrefix() << (keyCode - numberPad0);
|
||||
else if (keyCode >= 33 && keyCode < 176) desc += CharacterFunctions::toUpperCase ((juce_wchar) keyCode);
|
||||
else if (keyCode == numberPadAdd) desc << KeyPressHelpers::numberPadPrefix() << '+';
|
||||
else if (keyCode == numberPadSubtract) desc << KeyPressHelpers::numberPadPrefix() << '-';
|
||||
else if (keyCode == numberPadMultiply) desc << KeyPressHelpers::numberPadPrefix() << '*';
|
||||
else if (keyCode == numberPadDivide) desc << KeyPressHelpers::numberPadPrefix() << '/';
|
||||
else if (keyCode == numberPadSeparator) desc << KeyPressHelpers::numberPadPrefix() << "separator";
|
||||
else if (keyCode == numberPadDecimalPoint) desc << KeyPressHelpers::numberPadPrefix() << '.';
|
||||
else if (keyCode == numberPadDelete) desc << KeyPressHelpers::numberPadPrefix() << "delete";
|
||||
else desc << '#' << String::toHexString (keyCode);
|
||||
}
|
||||
|
||||
return desc;
|
||||
}
|
||||
|
||||
String KeyPress::getTextDescriptionWithIcons() const
|
||||
{
|
||||
#if JUCE_MAC
|
||||
return getTextDescription().replace ("shift + ", String::charToString (0x21e7))
|
||||
.replace ("command + ", String::charToString (0x2318))
|
||||
.replace ("option + ", String::charToString (0x2325))
|
||||
.replace ("ctrl + ", String::charToString (0x2303))
|
||||
.replace ("return", String::charToString (0x23ce))
|
||||
.replace ("cursor left", String::charToString (0x2190))
|
||||
.replace ("cursor right", String::charToString (0x2192))
|
||||
.replace ("cursor up", String::charToString (0x2191))
|
||||
.replace ("cursor down", String::charToString (0x2193))
|
||||
.replace ("backspace", String::charToString (0x232b))
|
||||
.replace ("delete", String::charToString (0x2326));
|
||||
#else
|
||||
return getTextDescription();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
252
modules/juce_gui_basics/keyboard/juce_KeyPress.h
Normal file
252
modules/juce_gui_basics/keyboard/juce_KeyPress.h
Normal file
|
|
@ -0,0 +1,252 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_KEYPRESS_JUCEHEADER__
|
||||
#define __JUCE_KEYPRESS_JUCEHEADER__
|
||||
|
||||
#include "juce_ModifierKeys.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Represents a key press, including any modifier keys that are needed.
|
||||
|
||||
E.g. a KeyPress might represent CTRL+C, SHIFT+ALT+H, Spacebar, Escape, etc.
|
||||
|
||||
@see Component, KeyListener, Button::addShortcut, KeyPressMappingManager
|
||||
*/
|
||||
class JUCE_API KeyPress
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an (invalid) KeyPress.
|
||||
|
||||
@see isValid
|
||||
*/
|
||||
KeyPress() noexcept;
|
||||
|
||||
/** Creates a KeyPress for a key and some modifiers.
|
||||
|
||||
e.g.
|
||||
CTRL+C would be: KeyPress ('c', ModifierKeys::ctrlModifier)
|
||||
SHIFT+Escape would be: KeyPress (KeyPress::escapeKey, ModifierKeys::shiftModifier)
|
||||
|
||||
@param keyCode a code that represents the key - this value must be
|
||||
one of special constants listed in this class, or an
|
||||
8-bit character code such as a letter (case is ignored),
|
||||
digit or a simple key like "," or ".". Note that this
|
||||
isn't the same as the textCharacter parameter, so for example
|
||||
a keyCode of 'a' and a shift-key modifier should have a
|
||||
textCharacter value of 'A'.
|
||||
@param modifiers the modifiers to associate with the keystroke
|
||||
@param textCharacter the character that would be printed if someone typed
|
||||
this keypress into a text editor. This value may be
|
||||
null if the keypress is a non-printing character
|
||||
@see getKeyCode, isKeyCode, getModifiers
|
||||
*/
|
||||
KeyPress (int keyCode,
|
||||
const ModifierKeys& modifiers,
|
||||
juce_wchar textCharacter) noexcept;
|
||||
|
||||
/** Creates a keypress with a keyCode but no modifiers or text character.
|
||||
*/
|
||||
KeyPress (int keyCode) noexcept;
|
||||
|
||||
/** Creates a copy of another KeyPress. */
|
||||
KeyPress (const KeyPress& other) noexcept;
|
||||
|
||||
/** Copies this KeyPress from another one. */
|
||||
KeyPress& operator= (const KeyPress& other) noexcept;
|
||||
|
||||
/** Compares two KeyPress objects. */
|
||||
bool operator== (const KeyPress& other) const noexcept;
|
||||
|
||||
/** Compares two KeyPress objects. */
|
||||
bool operator!= (const KeyPress& other) const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns true if this is a valid KeyPress.
|
||||
|
||||
A null keypress can be created by the default constructor, in case it's
|
||||
needed.
|
||||
*/
|
||||
bool isValid() const noexcept { return keyCode != 0; }
|
||||
|
||||
/** Returns the key code itself.
|
||||
|
||||
This will either be one of the special constants defined in this class,
|
||||
or an 8-bit character code.
|
||||
*/
|
||||
int getKeyCode() const noexcept { return keyCode; }
|
||||
|
||||
/** Returns the key modifiers.
|
||||
|
||||
@see ModifierKeys
|
||||
*/
|
||||
const ModifierKeys getModifiers() const noexcept { return mods; }
|
||||
|
||||
/** Returns the character that is associated with this keypress.
|
||||
|
||||
This is the character that you'd expect to see printed if you press this
|
||||
keypress in a text editor or similar component.
|
||||
*/
|
||||
juce_wchar getTextCharacter() const noexcept { return textCharacter; }
|
||||
|
||||
/** Checks whether the KeyPress's key is the same as the one provided, without checking
|
||||
the modifiers.
|
||||
|
||||
The values for key codes can either be one of the special constants defined in
|
||||
this class, or an 8-bit character code.
|
||||
|
||||
@see getKeyCode
|
||||
*/
|
||||
bool isKeyCode (int keyCodeToCompare) const noexcept { return keyCode == keyCodeToCompare; }
|
||||
|
||||
//==============================================================================
|
||||
/** Converts a textual key description to a KeyPress.
|
||||
|
||||
This attempts to decode a textual version of a keypress, e.g. "CTRL + C" or "SPACE".
|
||||
|
||||
This isn't designed to cope with any kind of input, but should be given the
|
||||
strings that are created by the getTextDescription() method.
|
||||
|
||||
If the string can't be parsed, the object returned will be invalid.
|
||||
|
||||
@see getTextDescription
|
||||
*/
|
||||
static const KeyPress createFromDescription (const String& textVersion);
|
||||
|
||||
/** Creates a textual description of the key combination.
|
||||
|
||||
e.g. "CTRL + C" or "DELETE".
|
||||
|
||||
To store a keypress in a file, use this method, along with createFromDescription()
|
||||
to retrieve it later.
|
||||
*/
|
||||
String getTextDescription() const;
|
||||
|
||||
/** Creates a textual description of the key combination, using unicode icon symbols if possible.
|
||||
|
||||
On OSX, this uses the Apple symbols for command, option, shift, etc, instead of the textual
|
||||
modifier key descriptions that are returned by getTextDescription()
|
||||
*/
|
||||
String getTextDescriptionWithIcons() const;
|
||||
|
||||
//==============================================================================
|
||||
/** Checks whether the user is currently holding down the keys that make up this
|
||||
KeyPress.
|
||||
|
||||
Note that this will return false if any extra modifier keys are
|
||||
down - e.g. if the keypress is CTRL+X and the user is actually holding CTRL+ALT+x
|
||||
then it will be false.
|
||||
*/
|
||||
bool isCurrentlyDown() const;
|
||||
|
||||
/** Checks whether a particular key is held down, irrespective of modifiers.
|
||||
|
||||
The values for key codes can either be one of the special constants defined in
|
||||
this class, or an 8-bit character code.
|
||||
*/
|
||||
static bool isKeyCurrentlyDown (int keyCode);
|
||||
|
||||
//==============================================================================
|
||||
// Key codes
|
||||
//
|
||||
// Note that the actual values of these are platform-specific and may change
|
||||
// without warning, so don't store them anywhere as constants. For persisting/retrieving
|
||||
// KeyPress objects, use getTextDescription() and createFromDescription() instead.
|
||||
//
|
||||
|
||||
static const int spaceKey; /**< key-code for the space bar */
|
||||
static const int escapeKey; /**< key-code for the escape key */
|
||||
static const int returnKey; /**< key-code for the return key*/
|
||||
static const int tabKey; /**< key-code for the tab key*/
|
||||
|
||||
static const int deleteKey; /**< key-code for the delete key (not backspace) */
|
||||
static const int backspaceKey; /**< key-code for the backspace key */
|
||||
static const int insertKey; /**< key-code for the insert key */
|
||||
|
||||
static const int upKey; /**< key-code for the cursor-up key */
|
||||
static const int downKey; /**< key-code for the cursor-down key */
|
||||
static const int leftKey; /**< key-code for the cursor-left key */
|
||||
static const int rightKey; /**< key-code for the cursor-right key */
|
||||
static const int pageUpKey; /**< key-code for the page-up key */
|
||||
static const int pageDownKey; /**< key-code for the page-down key */
|
||||
static const int homeKey; /**< key-code for the home key */
|
||||
static const int endKey; /**< key-code for the end key */
|
||||
|
||||
static const int F1Key; /**< key-code for the F1 key */
|
||||
static const int F2Key; /**< key-code for the F2 key */
|
||||
static const int F3Key; /**< key-code for the F3 key */
|
||||
static const int F4Key; /**< key-code for the F4 key */
|
||||
static const int F5Key; /**< key-code for the F5 key */
|
||||
static const int F6Key; /**< key-code for the F6 key */
|
||||
static const int F7Key; /**< key-code for the F7 key */
|
||||
static const int F8Key; /**< key-code for the F8 key */
|
||||
static const int F9Key; /**< key-code for the F9 key */
|
||||
static const int F10Key; /**< key-code for the F10 key */
|
||||
static const int F11Key; /**< key-code for the F11 key */
|
||||
static const int F12Key; /**< key-code for the F12 key */
|
||||
static const int F13Key; /**< key-code for the F13 key */
|
||||
static const int F14Key; /**< key-code for the F14 key */
|
||||
static const int F15Key; /**< key-code for the F15 key */
|
||||
static const int F16Key; /**< key-code for the F16 key */
|
||||
|
||||
static const int numberPad0; /**< key-code for the 0 on the numeric keypad. */
|
||||
static const int numberPad1; /**< key-code for the 1 on the numeric keypad. */
|
||||
static const int numberPad2; /**< key-code for the 2 on the numeric keypad. */
|
||||
static const int numberPad3; /**< key-code for the 3 on the numeric keypad. */
|
||||
static const int numberPad4; /**< key-code for the 4 on the numeric keypad. */
|
||||
static const int numberPad5; /**< key-code for the 5 on the numeric keypad. */
|
||||
static const int numberPad6; /**< key-code for the 6 on the numeric keypad. */
|
||||
static const int numberPad7; /**< key-code for the 7 on the numeric keypad. */
|
||||
static const int numberPad8; /**< key-code for the 8 on the numeric keypad. */
|
||||
static const int numberPad9; /**< key-code for the 9 on the numeric keypad. */
|
||||
|
||||
static const int numberPadAdd; /**< key-code for the add sign on the numeric keypad. */
|
||||
static const int numberPadSubtract; /**< key-code for the subtract sign on the numeric keypad. */
|
||||
static const int numberPadMultiply; /**< key-code for the multiply sign on the numeric keypad. */
|
||||
static const int numberPadDivide; /**< key-code for the divide sign on the numeric keypad. */
|
||||
static const int numberPadSeparator; /**< key-code for the comma on the numeric keypad. */
|
||||
static const int numberPadDecimalPoint; /**< key-code for the decimal point sign on the numeric keypad. */
|
||||
static const int numberPadEquals; /**< key-code for the equals key on the numeric keypad. */
|
||||
static const int numberPadDelete; /**< key-code for the delete key on the numeric keypad. */
|
||||
|
||||
static const int playKey; /**< key-code for a multimedia 'play' key, (not all keyboards will have one) */
|
||||
static const int stopKey; /**< key-code for a multimedia 'stop' key, (not all keyboards will have one) */
|
||||
static const int fastForwardKey; /**< key-code for a multimedia 'fast-forward' key, (not all keyboards will have one) */
|
||||
static const int rewindKey; /**< key-code for a multimedia 'rewind' key, (not all keyboards will have one) */
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
int keyCode;
|
||||
ModifierKeys mods;
|
||||
juce_wchar textCharacter;
|
||||
|
||||
JUCE_LEAK_DETECTOR (KeyPress);
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCE_KEYPRESS_JUCEHEADER__
|
||||
146
modules/juce_gui_basics/keyboard/juce_KeyboardFocusTraverser.cpp
Normal file
146
modules/juce_gui_basics/keyboard/juce_KeyboardFocusTraverser.cpp
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
//==============================================================================
|
||||
KeyboardFocusTraverser::KeyboardFocusTraverser()
|
||||
{
|
||||
}
|
||||
|
||||
KeyboardFocusTraverser::~KeyboardFocusTraverser()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
namespace KeyboardFocusHelpers
|
||||
{
|
||||
// This will sort a set of components, so that they are ordered in terms of
|
||||
// left-to-right and then top-to-bottom.
|
||||
class ScreenPositionComparator
|
||||
{
|
||||
public:
|
||||
ScreenPositionComparator() {}
|
||||
|
||||
static int compareElements (const Component* const first, const Component* const second)
|
||||
{
|
||||
int explicitOrder1 = first->getExplicitFocusOrder();
|
||||
if (explicitOrder1 <= 0)
|
||||
explicitOrder1 = std::numeric_limits<int>::max() / 2;
|
||||
|
||||
int explicitOrder2 = second->getExplicitFocusOrder();
|
||||
if (explicitOrder2 <= 0)
|
||||
explicitOrder2 = std::numeric_limits<int>::max() / 2;
|
||||
|
||||
if (explicitOrder1 != explicitOrder2)
|
||||
return explicitOrder1 - explicitOrder2;
|
||||
|
||||
const int diff = first->getY() - second->getY();
|
||||
|
||||
return (diff == 0) ? first->getX() - second->getX()
|
||||
: diff;
|
||||
}
|
||||
};
|
||||
|
||||
static void findAllFocusableComponents (Component* const parent, Array <Component*>& comps)
|
||||
{
|
||||
if (parent->getNumChildComponents() > 0)
|
||||
{
|
||||
Array <Component*> localComps;
|
||||
ScreenPositionComparator comparator;
|
||||
|
||||
int i;
|
||||
for (i = parent->getNumChildComponents(); --i >= 0;)
|
||||
{
|
||||
Component* const c = parent->getChildComponent (i);
|
||||
|
||||
if (c->isVisible() && c->isEnabled())
|
||||
localComps.addSorted (comparator, c);
|
||||
}
|
||||
|
||||
for (i = 0; i < localComps.size(); ++i)
|
||||
{
|
||||
Component* const c = localComps.getUnchecked (i);
|
||||
|
||||
if (c->getWantsKeyboardFocus())
|
||||
comps.add (c);
|
||||
|
||||
if (! c->isFocusContainer())
|
||||
findAllFocusableComponents (c, comps);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace KeyboardFocusHelpers
|
||||
{
|
||||
Component* getIncrementedComponent (Component* const current, const int delta)
|
||||
{
|
||||
Component* focusContainer = current->getParentComponent();
|
||||
|
||||
if (focusContainer != nullptr)
|
||||
{
|
||||
while (focusContainer->getParentComponent() != nullptr && ! focusContainer->isFocusContainer())
|
||||
focusContainer = focusContainer->getParentComponent();
|
||||
|
||||
if (focusContainer != nullptr)
|
||||
{
|
||||
Array <Component*> comps;
|
||||
KeyboardFocusHelpers::findAllFocusableComponents (focusContainer, comps);
|
||||
|
||||
if (comps.size() > 0)
|
||||
{
|
||||
const int index = comps.indexOf (current);
|
||||
return comps [(index + comps.size() + delta) % comps.size()];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Component* KeyboardFocusTraverser::getNextComponent (Component* current)
|
||||
{
|
||||
return KeyboardFocusHelpers::getIncrementedComponent (current, 1);
|
||||
}
|
||||
|
||||
Component* KeyboardFocusTraverser::getPreviousComponent (Component* current)
|
||||
{
|
||||
return KeyboardFocusHelpers::getIncrementedComponent (current, -1);
|
||||
}
|
||||
|
||||
Component* KeyboardFocusTraverser::getDefaultComponent (Component* parentComponent)
|
||||
{
|
||||
Array <Component*> comps;
|
||||
|
||||
if (parentComponent != nullptr)
|
||||
KeyboardFocusHelpers::findAllFocusableComponents (parentComponent, comps);
|
||||
|
||||
return comps.getFirst();
|
||||
}
|
||||
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_KEYBOARDFOCUSTRAVERSER_JUCEHEADER__
|
||||
#define __JUCE_KEYBOARDFOCUSTRAVERSER_JUCEHEADER__
|
||||
|
||||
class Component;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Controls the order in which focus moves between components.
|
||||
|
||||
The default algorithm used by this class to work out the order of traversal
|
||||
is as follows:
|
||||
- if two components both have an explicit focus order specified, then the
|
||||
one with the lowest number comes first (see the Component::setExplicitFocusOrder()
|
||||
method).
|
||||
- any component with an explicit focus order greater than 0 comes before ones
|
||||
that don't have an order specified.
|
||||
- any unspecified components are traversed in a left-to-right, then top-to-bottom
|
||||
order.
|
||||
|
||||
If you need traversal in a more customised way, you can create a subclass
|
||||
of KeyboardFocusTraverser that uses your own algorithm, and use
|
||||
Component::createFocusTraverser() to create it.
|
||||
|
||||
@see Component::setExplicitFocusOrder, Component::createFocusTraverser
|
||||
*/
|
||||
class JUCE_API KeyboardFocusTraverser
|
||||
{
|
||||
public:
|
||||
KeyboardFocusTraverser();
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~KeyboardFocusTraverser();
|
||||
|
||||
/** Returns the component that should be given focus after the specified one
|
||||
when moving "forwards".
|
||||
|
||||
The default implementation will return the next component which is to the
|
||||
right of or below this one.
|
||||
|
||||
This may return 0 if there's no suitable candidate.
|
||||
*/
|
||||
virtual Component* getNextComponent (Component* current);
|
||||
|
||||
/** Returns the component that should be given focus after the specified one
|
||||
when moving "backwards".
|
||||
|
||||
The default implementation will return the next component which is to the
|
||||
left of or above this one.
|
||||
|
||||
This may return 0 if there's no suitable candidate.
|
||||
*/
|
||||
virtual Component* getPreviousComponent (Component* current);
|
||||
|
||||
/** Returns the component that should receive focus be default within the given
|
||||
parent component.
|
||||
|
||||
The default implementation will just return the foremost child component that
|
||||
wants focus.
|
||||
|
||||
This may return 0 if there's no suitable candidate.
|
||||
*/
|
||||
virtual Component* getDefaultComponent (Component* parentComponent);
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCE_KEYBOARDFOCUSTRAVERSER_JUCEHEADER__
|
||||
64
modules/juce_gui_basics/keyboard/juce_ModifierKeys.cpp
Normal file
64
modules/juce_gui_basics/keyboard/juce_ModifierKeys.cpp
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
//==============================================================================
|
||||
ModifierKeys::ModifierKeys (const int flags_) noexcept
|
||||
: flags (flags_)
|
||||
{
|
||||
}
|
||||
|
||||
ModifierKeys::ModifierKeys (const ModifierKeys& other) noexcept
|
||||
: flags (other.flags)
|
||||
{
|
||||
}
|
||||
|
||||
ModifierKeys& ModifierKeys::operator= (const ModifierKeys& other) noexcept
|
||||
{
|
||||
flags = other.flags;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ModifierKeys ModifierKeys::currentModifiers;
|
||||
|
||||
ModifierKeys ModifierKeys::getCurrentModifiers() noexcept
|
||||
{
|
||||
return currentModifiers;
|
||||
}
|
||||
|
||||
int ModifierKeys::getNumMouseButtonsDown() const noexcept
|
||||
{
|
||||
int num = 0;
|
||||
|
||||
if (isLeftButtonDown()) ++num;
|
||||
if (isRightButtonDown()) ++num;
|
||||
if (isMiddleButtonDown()) ++num;
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
215
modules/juce_gui_basics/keyboard/juce_ModifierKeys.h
Normal file
215
modules/juce_gui_basics/keyboard/juce_ModifierKeys.h
Normal file
|
|
@ -0,0 +1,215 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_MODIFIERKEYS_JUCEHEADER__
|
||||
#define __JUCE_MODIFIERKEYS_JUCEHEADER__
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Represents the state of the mouse buttons and modifier keys.
|
||||
|
||||
This is used both by mouse events and by KeyPress objects to describe
|
||||
the state of keys such as shift, control, alt, etc.
|
||||
|
||||
@see KeyPress, MouseEvent::mods
|
||||
*/
|
||||
class JUCE_API ModifierKeys
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a ModifierKeys object from a raw set of flags.
|
||||
|
||||
@param flags to represent the keys that are down
|
||||
@see shiftModifier, ctrlModifier, altModifier, leftButtonModifier,
|
||||
rightButtonModifier, commandModifier, popupMenuClickModifier
|
||||
*/
|
||||
ModifierKeys (int flags = 0) noexcept;
|
||||
|
||||
/** Creates a copy of another object. */
|
||||
ModifierKeys (const ModifierKeys& other) noexcept;
|
||||
|
||||
/** Copies this object from another one. */
|
||||
ModifierKeys& operator= (const ModifierKeys& other) noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Checks whether the 'command' key flag is set (or 'ctrl' on Windows/Linux).
|
||||
|
||||
This is a platform-agnostic way of checking for the operating system's
|
||||
preferred command-key modifier - so on the Mac it tests for the Apple key, on
|
||||
Windows/Linux, it's actually checking for the CTRL key.
|
||||
*/
|
||||
inline bool isCommandDown() const noexcept { return (flags & commandModifier) != 0; }
|
||||
|
||||
/** Checks whether the user is trying to launch a pop-up menu.
|
||||
|
||||
This checks for platform-specific modifiers that might indicate that the user
|
||||
is following the operating system's normal method of showing a pop-up menu.
|
||||
|
||||
So on Windows/Linux, this method is really testing for a right-click.
|
||||
On the Mac, it tests for either the CTRL key being down, or a right-click.
|
||||
*/
|
||||
inline bool isPopupMenu() const noexcept { return (flags & popupMenuClickModifier) != 0; }
|
||||
|
||||
/** Checks whether the flag is set for the left mouse-button. */
|
||||
inline bool isLeftButtonDown() const noexcept { return (flags & leftButtonModifier) != 0; }
|
||||
|
||||
/** Checks whether the flag is set for the right mouse-button.
|
||||
|
||||
Note that for detecting popup-menu clicks, you should be using isPopupMenu() instead, as
|
||||
this is platform-independent (and makes your code more explanatory too).
|
||||
*/
|
||||
inline bool isRightButtonDown() const noexcept { return (flags & rightButtonModifier) != 0; }
|
||||
|
||||
inline bool isMiddleButtonDown() const noexcept { return (flags & middleButtonModifier) != 0; }
|
||||
|
||||
/** Tests for any of the mouse-button flags. */
|
||||
inline bool isAnyMouseButtonDown() const noexcept { return (flags & allMouseButtonModifiers) != 0; }
|
||||
|
||||
/** Tests for any of the modifier key flags. */
|
||||
inline bool isAnyModifierKeyDown() const noexcept { return (flags & (shiftModifier | ctrlModifier | altModifier | commandModifier)) != 0; }
|
||||
|
||||
/** Checks whether the shift key's flag is set. */
|
||||
inline bool isShiftDown() const noexcept { return (flags & shiftModifier) != 0; }
|
||||
|
||||
/** Checks whether the CTRL key's flag is set.
|
||||
|
||||
Remember that it's better to use the platform-agnostic routines to test for command-key and
|
||||
popup-menu modifiers.
|
||||
|
||||
@see isCommandDown, isPopupMenu
|
||||
*/
|
||||
inline bool isCtrlDown() const noexcept { return (flags & ctrlModifier) != 0; }
|
||||
|
||||
/** Checks whether the shift key's flag is set. */
|
||||
inline bool isAltDown() const noexcept { return (flags & altModifier) != 0; }
|
||||
|
||||
//==============================================================================
|
||||
/** Flags that represent the different keys. */
|
||||
enum Flags
|
||||
{
|
||||
/** Shift key flag. */
|
||||
shiftModifier = 1,
|
||||
|
||||
/** CTRL key flag. */
|
||||
ctrlModifier = 2,
|
||||
|
||||
/** ALT key flag. */
|
||||
altModifier = 4,
|
||||
|
||||
/** Left mouse button flag. */
|
||||
leftButtonModifier = 16,
|
||||
|
||||
/** Right mouse button flag. */
|
||||
rightButtonModifier = 32,
|
||||
|
||||
/** Middle mouse button flag. */
|
||||
middleButtonModifier = 64,
|
||||
|
||||
#if JUCE_MAC
|
||||
/** Command key flag - on windows this is the same as the CTRL key flag. */
|
||||
commandModifier = 8,
|
||||
|
||||
/** Popup menu flag - on windows this is the same as rightButtonModifier, on the
|
||||
Mac it's the same as (rightButtonModifier | ctrlModifier). */
|
||||
popupMenuClickModifier = rightButtonModifier | ctrlModifier,
|
||||
#else
|
||||
/** Command key flag - on windows this is the same as the CTRL key flag. */
|
||||
commandModifier = ctrlModifier,
|
||||
|
||||
/** Popup menu flag - on windows this is the same as rightButtonModifier, on the
|
||||
Mac it's the same as (rightButtonModifier | ctrlModifier). */
|
||||
popupMenuClickModifier = rightButtonModifier,
|
||||
#endif
|
||||
|
||||
/** Represents a combination of all the shift, alt, ctrl and command key modifiers. */
|
||||
allKeyboardModifiers = shiftModifier | ctrlModifier | altModifier | commandModifier,
|
||||
|
||||
/** Represents a combination of all the mouse buttons at once. */
|
||||
allMouseButtonModifiers = leftButtonModifier | rightButtonModifier | middleButtonModifier,
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a copy of only the mouse-button flags */
|
||||
ModifierKeys withOnlyMouseButtons() const noexcept { return ModifierKeys (flags & allMouseButtonModifiers); }
|
||||
|
||||
/** Returns a copy of only the non-mouse flags */
|
||||
ModifierKeys withoutMouseButtons() const noexcept { return ModifierKeys (flags & ~allMouseButtonModifiers); }
|
||||
|
||||
bool operator== (const ModifierKeys& other) const noexcept { return flags == other.flags; }
|
||||
bool operator!= (const ModifierKeys& other) const noexcept { return flags != other.flags; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the raw flags for direct testing. */
|
||||
inline int getRawFlags() const noexcept { return flags; }
|
||||
|
||||
inline const ModifierKeys withoutFlags (int rawFlagsToClear) const noexcept { return ModifierKeys (flags & ~rawFlagsToClear); }
|
||||
inline const ModifierKeys withFlags (int rawFlagsToSet) const noexcept { return ModifierKeys (flags | rawFlagsToSet); }
|
||||
|
||||
/** Tests a combination of flags and returns true if any of them are set. */
|
||||
inline bool testFlags (const int flagsToTest) const noexcept { return (flags & flagsToTest) != 0; }
|
||||
|
||||
/** Returns the total number of mouse buttons that are down. */
|
||||
int getNumMouseButtonsDown() const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Creates a ModifierKeys object to represent the last-known state of the
|
||||
keyboard and mouse buttons.
|
||||
|
||||
@see getCurrentModifiersRealtime
|
||||
*/
|
||||
static ModifierKeys getCurrentModifiers() noexcept;
|
||||
|
||||
/** Creates a ModifierKeys object to represent the current state of the
|
||||
keyboard and mouse buttons.
|
||||
|
||||
This isn't often needed and isn't recommended, but will actively check all the
|
||||
mouse and key states rather than just returning their last-known state like
|
||||
getCurrentModifiers() does.
|
||||
|
||||
This is only needed in special circumstances for up-to-date modifier information
|
||||
at times when the app's event loop isn't running normally.
|
||||
|
||||
Another reason to avoid this method is that it's not stateless, and calling it may
|
||||
update the value returned by getCurrentModifiers(), which could cause subtle changes
|
||||
in the behaviour of some components.
|
||||
*/
|
||||
static ModifierKeys getCurrentModifiersRealtime() noexcept;
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
int flags;
|
||||
|
||||
static ModifierKeys currentModifiers;
|
||||
|
||||
friend class ComponentPeer;
|
||||
friend class MouseInputSource;
|
||||
friend class MouseInputSourceInternal;
|
||||
static void updateCurrentModifiers() noexcept;
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCE_MODIFIERKEYS_JUCEHEADER__
|
||||
48
modules/juce_gui_basics/keyboard/juce_SystemClipboard.h
Normal file
48
modules/juce_gui_basics/keyboard/juce_SystemClipboard.h
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_SYSTEMCLIPBOARD_JUCEHEADER__
|
||||
#define __JUCE_SYSTEMCLIPBOARD_JUCEHEADER__
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Handles reading/writing to the system's clipboard.
|
||||
*/
|
||||
class JUCE_API SystemClipboard
|
||||
{
|
||||
public:
|
||||
/** Copies a string of text onto the clipboard */
|
||||
static void copyTextToClipboard (const String& text);
|
||||
|
||||
/** Gets the current clipboard's contents.
|
||||
|
||||
Obviously this might have come from another app, so could contain
|
||||
anything..
|
||||
*/
|
||||
static String getTextFromClipboard();
|
||||
};
|
||||
|
||||
#endif // __JUCE_SYSTEMCLIPBOARD_JUCEHEADER__
|
||||
133
modules/juce_gui_basics/keyboard/juce_TextEditorKeyMapper.h
Normal file
133
modules/juce_gui_basics/keyboard/juce_TextEditorKeyMapper.h
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_TEXTEDITORKEYMAPPER_JUCEHEADER__
|
||||
#define __JUCE_TEXTEDITORKEYMAPPER_JUCEHEADER__
|
||||
|
||||
#include "juce_KeyPress.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** This class is used to invoke a range of text-editor navigation methods on
|
||||
an object, based upon a keypress event.
|
||||
|
||||
It's currently used internally by the TextEditor and CodeEditorComponent.
|
||||
*/
|
||||
template <class CallbackClass>
|
||||
struct TextEditorKeyMapper
|
||||
{
|
||||
/** Checks the keypress and invokes one of a range of navigation functions that
|
||||
the target class must implement, based on the key event.
|
||||
*/
|
||||
static bool invokeKeyFunction (CallbackClass& target, const KeyPress& key)
|
||||
{
|
||||
const bool isShiftDown = key.getModifiers().isShiftDown();
|
||||
const bool ctrlOrAltDown = key.getModifiers().isCtrlDown() || key.getModifiers().isAltDown();
|
||||
|
||||
if (key == KeyPress (KeyPress::downKey, ModifierKeys::ctrlModifier, 0)
|
||||
&& target.scrollUp())
|
||||
return true;
|
||||
|
||||
if (key == KeyPress (KeyPress::upKey, ModifierKeys::ctrlModifier, 0)
|
||||
&& target.scrollDown())
|
||||
return true;
|
||||
|
||||
#if JUCE_MAC
|
||||
if (key.getModifiers().isCommandDown())
|
||||
{
|
||||
if (key.isKeyCode (KeyPress::upKey))
|
||||
return target.moveCaretToTop (isShiftDown);
|
||||
|
||||
if (key.isKeyCode (KeyPress::downKey))
|
||||
return target.moveCaretToEnd (isShiftDown);
|
||||
|
||||
if (key.isKeyCode (KeyPress::leftKey))
|
||||
return target.moveCaretToStartOfLine (isShiftDown);
|
||||
|
||||
if (key.isKeyCode (KeyPress::rightKey))
|
||||
return target.moveCaretToEndOfLine (isShiftDown);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (key.isKeyCode (KeyPress::upKey))
|
||||
return target.moveCaretUp (isShiftDown);
|
||||
|
||||
if (key.isKeyCode (KeyPress::downKey))
|
||||
return target.moveCaretDown (isShiftDown);
|
||||
|
||||
if (key.isKeyCode (KeyPress::leftKey))
|
||||
return target.moveCaretLeft (ctrlOrAltDown, isShiftDown);
|
||||
|
||||
if (key.isKeyCode (KeyPress::rightKey))
|
||||
return target.moveCaretRight (ctrlOrAltDown, isShiftDown);
|
||||
|
||||
if (key.isKeyCode (KeyPress::pageUpKey))
|
||||
return target.pageUp (isShiftDown);
|
||||
|
||||
if (key.isKeyCode (KeyPress::pageDownKey))
|
||||
return target.pageDown (isShiftDown);
|
||||
|
||||
if (key.isKeyCode (KeyPress::homeKey))
|
||||
return ctrlOrAltDown ? target.moveCaretToTop (isShiftDown)
|
||||
: target.moveCaretToStartOfLine (isShiftDown);
|
||||
|
||||
if (key.isKeyCode (KeyPress::endKey))
|
||||
return ctrlOrAltDown ? target.moveCaretToEnd (isShiftDown)
|
||||
: target.moveCaretToEndOfLine (isShiftDown);
|
||||
|
||||
if (key == KeyPress ('c', ModifierKeys::commandModifier, 0)
|
||||
|| key == KeyPress (KeyPress::insertKey, ModifierKeys::ctrlModifier, 0))
|
||||
return target.copyToClipboard();
|
||||
|
||||
if (key == KeyPress ('x', ModifierKeys::commandModifier, 0)
|
||||
|| key == KeyPress (KeyPress::deleteKey, ModifierKeys::shiftModifier, 0))
|
||||
return target.cutToClipboard();
|
||||
|
||||
if (key == KeyPress ('v', ModifierKeys::commandModifier, 0)
|
||||
|| key == KeyPress (KeyPress::insertKey, ModifierKeys::shiftModifier, 0))
|
||||
return target.pasteFromClipboard();
|
||||
|
||||
if (key.isKeyCode (KeyPress::backspaceKey))
|
||||
return target.deleteBackwards (ctrlOrAltDown);
|
||||
|
||||
if (key.isKeyCode (KeyPress::deleteKey))
|
||||
return target.deleteForwards (ctrlOrAltDown);
|
||||
|
||||
if (key == KeyPress ('a', ModifierKeys::commandModifier, 0))
|
||||
return target.selectAll();
|
||||
|
||||
if (key == KeyPress ('z', ModifierKeys::commandModifier, 0))
|
||||
return target.undo();
|
||||
|
||||
if (key == KeyPress ('y', ModifierKeys::commandModifier, 0)
|
||||
|| key == KeyPress ('z', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0))
|
||||
return target.redo();
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCE_TEXTEDITORKEYMAPPER_JUCEHEADER__
|
||||
78
modules/juce_gui_basics/keyboard/juce_TextInputTarget.h
Normal file
78
modules/juce_gui_basics/keyboard/juce_TextInputTarget.h
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_TEXTINPUTTARGET_JUCEHEADER__
|
||||
#define __JUCE_TEXTINPUTTARGET_JUCEHEADER__
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
An abstract base class which can be implemented by components that function as
|
||||
text editors.
|
||||
|
||||
This class allows different types of text editor component to provide a uniform
|
||||
interface, which can be used by things like OS-specific input methods, on-screen
|
||||
keyboards, etc.
|
||||
*/
|
||||
class JUCE_API TextInputTarget
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** */
|
||||
TextInputTarget() {}
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~TextInputTarget() {}
|
||||
|
||||
/** Returns true if this input target is currently accepting input.
|
||||
For example, a text editor might return false if it's in read-only mode.
|
||||
*/
|
||||
virtual bool isTextInputActive() const = 0;
|
||||
|
||||
/** Returns the extents of the selected text region, or an empty range if
|
||||
nothing is selected,
|
||||
*/
|
||||
virtual const Range<int> getHighlightedRegion() const = 0;
|
||||
|
||||
/** Sets the currently-selected text region. */
|
||||
virtual void setHighlightedRegion (const Range<int>& newRange) = 0;
|
||||
|
||||
/** Sets a number of temporarily underlined sections.
|
||||
This is needed by MS Windows input method UI.
|
||||
*/
|
||||
virtual void setTemporaryUnderlining (const Array <Range<int> >& underlinedRegions) = 0;
|
||||
|
||||
/** Returns a specified sub-section of the text. */
|
||||
virtual const String getTextInRange (const Range<int>& range) const = 0;
|
||||
|
||||
/** Inserts some text, overwriting the selected text region, if there is one. */
|
||||
virtual void insertTextAtCaret (const String& textToInsert) = 0;
|
||||
|
||||
/** Returns the position of the caret, relative to the component's origin. */
|
||||
virtual const Rectangle<int> getCaretRectangle() = 0;
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCE_TEXTINPUTTARGET_JUCEHEADER__
|
||||
341
modules/juce_gui_basics/layout/juce_ComponentAnimator.cpp
Normal file
341
modules/juce_gui_basics/layout/juce_ComponentAnimator.cpp
Normal file
|
|
@ -0,0 +1,341 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
//==============================================================================
|
||||
class ComponentAnimator::AnimationTask
|
||||
{
|
||||
public:
|
||||
AnimationTask (Component* const comp)
|
||||
: component (comp)
|
||||
{
|
||||
}
|
||||
|
||||
void reset (const Rectangle<int>& finalBounds,
|
||||
float finalAlpha,
|
||||
int millisecondsToSpendMoving,
|
||||
bool useProxyComponent,
|
||||
double startSpeed_, double endSpeed_)
|
||||
{
|
||||
msElapsed = 0;
|
||||
msTotal = jmax (1, millisecondsToSpendMoving);
|
||||
lastProgress = 0;
|
||||
destination = finalBounds;
|
||||
destAlpha = finalAlpha;
|
||||
|
||||
isMoving = (finalBounds != component->getBounds());
|
||||
isChangingAlpha = (finalAlpha != component->getAlpha());
|
||||
|
||||
left = component->getX();
|
||||
top = component->getY();
|
||||
right = component->getRight();
|
||||
bottom = component->getBottom();
|
||||
alpha = component->getAlpha();
|
||||
|
||||
const double invTotalDistance = 4.0 / (startSpeed_ + endSpeed_ + 2.0);
|
||||
startSpeed = jmax (0.0, startSpeed_ * invTotalDistance);
|
||||
midSpeed = invTotalDistance;
|
||||
endSpeed = jmax (0.0, endSpeed_ * invTotalDistance);
|
||||
|
||||
if (useProxyComponent)
|
||||
proxy = new ProxyComponent (*component);
|
||||
else
|
||||
proxy = nullptr;
|
||||
|
||||
component->setVisible (! useProxyComponent);
|
||||
}
|
||||
|
||||
bool useTimeslice (const int elapsed)
|
||||
{
|
||||
Component* const c = proxy != nullptr ? static_cast <Component*> (proxy)
|
||||
: static_cast <Component*> (component);
|
||||
|
||||
if (c != nullptr)
|
||||
{
|
||||
msElapsed += elapsed;
|
||||
double newProgress = msElapsed / (double) msTotal;
|
||||
|
||||
if (newProgress >= 0 && newProgress < 1.0)
|
||||
{
|
||||
newProgress = timeToDistance (newProgress);
|
||||
const double delta = (newProgress - lastProgress) / (1.0 - lastProgress);
|
||||
jassert (newProgress >= lastProgress);
|
||||
lastProgress = newProgress;
|
||||
|
||||
if (delta < 1.0)
|
||||
{
|
||||
bool stillBusy = false;
|
||||
|
||||
if (isMoving)
|
||||
{
|
||||
left += (destination.getX() - left) * delta;
|
||||
top += (destination.getY() - top) * delta;
|
||||
right += (destination.getRight() - right) * delta;
|
||||
bottom += (destination.getBottom() - bottom) * delta;
|
||||
|
||||
const Rectangle<int> newBounds (roundToInt (left),
|
||||
roundToInt (top),
|
||||
roundToInt (right - left),
|
||||
roundToInt (bottom - top));
|
||||
|
||||
if (newBounds != destination)
|
||||
{
|
||||
c->setBounds (newBounds);
|
||||
stillBusy = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (isChangingAlpha)
|
||||
{
|
||||
alpha += (destAlpha - alpha) * delta;
|
||||
c->setAlpha ((float) alpha);
|
||||
stillBusy = true;
|
||||
}
|
||||
|
||||
if (stillBusy)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
moveToFinalDestination();
|
||||
return false;
|
||||
}
|
||||
|
||||
void moveToFinalDestination()
|
||||
{
|
||||
if (component != nullptr)
|
||||
{
|
||||
component->setAlpha ((float) destAlpha);
|
||||
component->setBounds (destination);
|
||||
|
||||
if (proxy != nullptr)
|
||||
component->setVisible (destAlpha > 0);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class ProxyComponent : public Component
|
||||
{
|
||||
public:
|
||||
ProxyComponent (Component& component)
|
||||
: image (component.createComponentSnapshot (component.getLocalBounds()))
|
||||
{
|
||||
setBounds (component.getBounds());
|
||||
setTransform (component.getTransform());
|
||||
setAlpha (component.getAlpha());
|
||||
setInterceptsMouseClicks (false, false);
|
||||
|
||||
Component* const parent = component.getParentComponent();
|
||||
|
||||
if (parent != nullptr)
|
||||
parent->addAndMakeVisible (this);
|
||||
else if (component.isOnDesktop() && component.getPeer() != nullptr)
|
||||
addToDesktop (component.getPeer()->getStyleFlags() | ComponentPeer::windowIgnoresKeyPresses);
|
||||
else
|
||||
jassertfalse; // seem to be trying to animate a component that's not visible..
|
||||
|
||||
setVisible (true);
|
||||
toBehind (&component);
|
||||
}
|
||||
|
||||
void paint (Graphics& g)
|
||||
{
|
||||
g.setOpacity (1.0f);
|
||||
g.drawImage (image, 0, 0, getWidth(), getHeight(),
|
||||
0, 0, image.getWidth(), image.getHeight());
|
||||
}
|
||||
|
||||
private:
|
||||
Image image;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProxyComponent);
|
||||
};
|
||||
|
||||
WeakReference<Component> component;
|
||||
ScopedPointer<Component> proxy;
|
||||
|
||||
Rectangle<int> destination;
|
||||
double destAlpha;
|
||||
|
||||
int msElapsed, msTotal;
|
||||
double startSpeed, midSpeed, endSpeed, lastProgress;
|
||||
double left, top, right, bottom, alpha;
|
||||
bool isMoving, isChangingAlpha;
|
||||
|
||||
private:
|
||||
double timeToDistance (const double time) const noexcept
|
||||
{
|
||||
return (time < 0.5) ? time * (startSpeed + time * (midSpeed - startSpeed))
|
||||
: 0.5 * (startSpeed + 0.5 * (midSpeed - startSpeed))
|
||||
+ (time - 0.5) * (midSpeed + (time - 0.5) * (endSpeed - midSpeed));
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
ComponentAnimator::ComponentAnimator()
|
||||
: lastTime (0)
|
||||
{
|
||||
}
|
||||
|
||||
ComponentAnimator::~ComponentAnimator()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
ComponentAnimator::AnimationTask* ComponentAnimator::findTaskFor (Component* const component) const
|
||||
{
|
||||
for (int i = tasks.size(); --i >= 0;)
|
||||
if (component == tasks.getUnchecked(i)->component.get())
|
||||
return tasks.getUnchecked(i);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ComponentAnimator::animateComponent (Component* const component,
|
||||
const Rectangle<int>& finalBounds,
|
||||
const float finalAlpha,
|
||||
const int millisecondsToSpendMoving,
|
||||
const bool useProxyComponent,
|
||||
const double startSpeed,
|
||||
const double endSpeed)
|
||||
{
|
||||
// the speeds must be 0 or greater!
|
||||
jassert (startSpeed >= 0 && endSpeed >= 0)
|
||||
|
||||
if (component != nullptr)
|
||||
{
|
||||
AnimationTask* at = findTaskFor (component);
|
||||
|
||||
if (at == nullptr)
|
||||
{
|
||||
at = new AnimationTask (component);
|
||||
tasks.add (at);
|
||||
sendChangeMessage();
|
||||
}
|
||||
|
||||
at->reset (finalBounds, finalAlpha, millisecondsToSpendMoving,
|
||||
useProxyComponent, startSpeed, endSpeed);
|
||||
|
||||
if (! isTimerRunning())
|
||||
{
|
||||
lastTime = Time::getMillisecondCounter();
|
||||
startTimer (1000 / 50);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ComponentAnimator::fadeOut (Component* component, int millisecondsToTake)
|
||||
{
|
||||
if (component != nullptr)
|
||||
{
|
||||
if (component->isShowing() && millisecondsToTake > 0)
|
||||
animateComponent (component, component->getBounds(), 0.0f, millisecondsToTake, true, 1.0, 1.0);
|
||||
|
||||
component->setVisible (false);
|
||||
}
|
||||
}
|
||||
|
||||
void ComponentAnimator::fadeIn (Component* component, int millisecondsToTake)
|
||||
{
|
||||
if (component != nullptr && ! (component->isVisible() && component->getAlpha() == 1.0f))
|
||||
{
|
||||
component->setAlpha (0.0f);
|
||||
component->setVisible (true);
|
||||
animateComponent (component, component->getBounds(), 1.0f, millisecondsToTake, false, 1.0, 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
void ComponentAnimator::cancelAllAnimations (const bool moveComponentsToTheirFinalPositions)
|
||||
{
|
||||
if (tasks.size() > 0)
|
||||
{
|
||||
if (moveComponentsToTheirFinalPositions)
|
||||
for (int i = tasks.size(); --i >= 0;)
|
||||
tasks.getUnchecked(i)->moveToFinalDestination();
|
||||
|
||||
tasks.clear();
|
||||
sendChangeMessage();
|
||||
}
|
||||
}
|
||||
|
||||
void ComponentAnimator::cancelAnimation (Component* const component,
|
||||
const bool moveComponentToItsFinalPosition)
|
||||
{
|
||||
AnimationTask* const at = findTaskFor (component);
|
||||
|
||||
if (at != nullptr)
|
||||
{
|
||||
if (moveComponentToItsFinalPosition)
|
||||
at->moveToFinalDestination();
|
||||
|
||||
tasks.removeObject (at);
|
||||
sendChangeMessage();
|
||||
}
|
||||
}
|
||||
|
||||
const Rectangle<int> ComponentAnimator::getComponentDestination (Component* const component)
|
||||
{
|
||||
jassert (component != nullptr);
|
||||
AnimationTask* const at = findTaskFor (component);
|
||||
|
||||
if (at != nullptr)
|
||||
return at->destination;
|
||||
|
||||
return component->getBounds();
|
||||
}
|
||||
|
||||
bool ComponentAnimator::isAnimating (Component* component) const
|
||||
{
|
||||
return findTaskFor (component) != nullptr;
|
||||
}
|
||||
|
||||
void ComponentAnimator::timerCallback()
|
||||
{
|
||||
const uint32 timeNow = Time::getMillisecondCounter();
|
||||
|
||||
if (lastTime == 0 || lastTime == timeNow)
|
||||
lastTime = timeNow;
|
||||
|
||||
const int elapsed = (int) (timeNow - lastTime);
|
||||
|
||||
for (int i = tasks.size(); --i >= 0;)
|
||||
{
|
||||
if (! tasks.getUnchecked(i)->useTimeslice (elapsed))
|
||||
{
|
||||
tasks.remove (i);
|
||||
sendChangeMessage();
|
||||
}
|
||||
}
|
||||
|
||||
lastTime = timeNow;
|
||||
|
||||
if (tasks.size() == 0)
|
||||
stopTimer();
|
||||
}
|
||||
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
161
modules/juce_gui_basics/layout/juce_ComponentAnimator.h
Normal file
161
modules/juce_gui_basics/layout/juce_ComponentAnimator.h
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_COMPONENTANIMATOR_JUCEHEADER__
|
||||
#define __JUCE_COMPONENTANIMATOR_JUCEHEADER__
|
||||
|
||||
#include "../components/juce_Component.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Animates a set of components, moving them to a new position and/or fading their
|
||||
alpha levels.
|
||||
|
||||
To animate a component, create a ComponentAnimator instance or (preferably) use the
|
||||
global animator object provided by Desktop::getAnimator(), and call its animateComponent()
|
||||
method to commence the movement.
|
||||
|
||||
If you're using your own ComponentAnimator instance, you'll need to make sure it isn't
|
||||
deleted before it finishes moving the components, or they'll be abandoned before reaching their
|
||||
destinations.
|
||||
|
||||
It's ok to delete components while they're being animated - the animator will detect this
|
||||
and safely stop using them.
|
||||
|
||||
The class is a ChangeBroadcaster and sends a notification when any components
|
||||
start or finish being animated.
|
||||
|
||||
@see Desktop::getAnimator
|
||||
*/
|
||||
class JUCE_API ComponentAnimator : public ChangeBroadcaster,
|
||||
private Timer
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a ComponentAnimator. */
|
||||
ComponentAnimator();
|
||||
|
||||
/** Destructor. */
|
||||
~ComponentAnimator();
|
||||
|
||||
//==============================================================================
|
||||
/** Starts a component moving from its current position to a specified position.
|
||||
|
||||
If the component is already in the middle of an animation, that will be abandoned,
|
||||
and a new animation will begin, moving the component from its current location.
|
||||
|
||||
The start and end speed parameters let you apply some acceleration to the component's
|
||||
movement.
|
||||
|
||||
@param component the component to move
|
||||
@param finalBounds the destination bounds to which the component should move. To leave the
|
||||
component in the same place, just pass component->getBounds() for this value
|
||||
@param finalAlpha the alpha value that the component should have at the end of the animation
|
||||
@param animationDurationMilliseconds how long the animation should last, in milliseconds
|
||||
@param useProxyComponent if true, this means the component should be replaced by an internally
|
||||
managed temporary component which is a snapshot of the original component.
|
||||
This avoids the component having to paint itself as it moves, so may
|
||||
be more efficient. This option also allows you to delete the original
|
||||
component immediately after starting the animation, because the animation
|
||||
can proceed without it. If you use a proxy, the original component will be
|
||||
made invisible by this call, and then will become visible again at the end
|
||||
of the animation. It'll also mean that the proxy component will be temporarily
|
||||
added to the component's parent, so avoid it if this might confuse the parent
|
||||
component, or if there's a chance the parent might decide to delete its children.
|
||||
@param startSpeed a value to indicate the relative start speed of the animation. If this is 0,
|
||||
the component will start by accelerating from rest; higher values mean that it
|
||||
will have an initial speed greater than zero. If the value if greater than 1, it
|
||||
will decelerate towards the middle of its journey. To move the component at a
|
||||
constant rate for its entire animation, set both the start and end speeds to 1.0
|
||||
@param endSpeed a relative speed at which the component should be moving when the animation finishes.
|
||||
If this is 0, the component will decelerate to a standstill at its final position;
|
||||
higher values mean the component will still be moving when it stops. To move the component
|
||||
at a constant rate for its entire animation, set both the start and end speeds to 1.0
|
||||
*/
|
||||
void animateComponent (Component* component,
|
||||
const Rectangle<int>& finalBounds,
|
||||
float finalAlpha,
|
||||
int animationDurationMilliseconds,
|
||||
bool useProxyComponent,
|
||||
double startSpeed,
|
||||
double endSpeed);
|
||||
|
||||
/** Begins a fade-out of this components alpha level.
|
||||
This is a quick way of invoking animateComponent() with a target alpha value of 0.0f, using
|
||||
a proxy. You're safe to delete the component after calling this method, and this won't
|
||||
interfere with the animation's progress.
|
||||
*/
|
||||
void fadeOut (Component* component, int millisecondsToTake);
|
||||
|
||||
/** Begins a fade-in of a component.
|
||||
This is a quick way of invoking animateComponent() with a target alpha value of 1.0f.
|
||||
*/
|
||||
void fadeIn (Component* component, int millisecondsToTake);
|
||||
|
||||
/** Stops a component if it's currently being animated.
|
||||
|
||||
If moveComponentToItsFinalPosition is true, then the component will
|
||||
be immediately moved to its destination position and size. If false, it will be
|
||||
left in whatever location it currently occupies.
|
||||
*/
|
||||
void cancelAnimation (Component* component,
|
||||
bool moveComponentToItsFinalPosition);
|
||||
|
||||
/** Clears all of the active animations.
|
||||
|
||||
If moveComponentsToTheirFinalPositions is true, all the components will
|
||||
be immediately set to their final positions. If false, they will be
|
||||
left in whatever locations they currently occupy.
|
||||
*/
|
||||
void cancelAllAnimations (bool moveComponentsToTheirFinalPositions);
|
||||
|
||||
/** Returns the destination position for a component.
|
||||
|
||||
If the component is being animated, this will return the target position that
|
||||
was specified when animateComponent() was called.
|
||||
|
||||
If the specified component isn't currently being animated, this method will just
|
||||
return its current position.
|
||||
*/
|
||||
const Rectangle<int> getComponentDestination (Component* component);
|
||||
|
||||
/** Returns true if the specified component is currently being animated. */
|
||||
bool isAnimating (Component* component) const;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
class AnimationTask;
|
||||
OwnedArray <AnimationTask> tasks;
|
||||
uint32 lastTime;
|
||||
|
||||
AnimationTask* findTaskFor (Component* component) const;
|
||||
void timerCallback();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComponentAnimator);
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCE_COMPONENTANIMATOR_JUCEHEADER__
|
||||
|
|
@ -0,0 +1,336 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
//==============================================================================
|
||||
ComponentBoundsConstrainer::ComponentBoundsConstrainer() noexcept
|
||||
: minW (0),
|
||||
maxW (0x3fffffff),
|
||||
minH (0),
|
||||
maxH (0x3fffffff),
|
||||
minOffTop (0),
|
||||
minOffLeft (0),
|
||||
minOffBottom (0),
|
||||
minOffRight (0),
|
||||
aspectRatio (0.0)
|
||||
{
|
||||
}
|
||||
|
||||
ComponentBoundsConstrainer::~ComponentBoundsConstrainer()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void ComponentBoundsConstrainer::setMinimumWidth (const int minimumWidth) noexcept
|
||||
{
|
||||
minW = minimumWidth;
|
||||
}
|
||||
|
||||
void ComponentBoundsConstrainer::setMaximumWidth (const int maximumWidth) noexcept
|
||||
{
|
||||
maxW = maximumWidth;
|
||||
}
|
||||
|
||||
void ComponentBoundsConstrainer::setMinimumHeight (const int minimumHeight) noexcept
|
||||
{
|
||||
minH = minimumHeight;
|
||||
}
|
||||
|
||||
void ComponentBoundsConstrainer::setMaximumHeight (const int maximumHeight) noexcept
|
||||
{
|
||||
maxH = maximumHeight;
|
||||
}
|
||||
|
||||
void ComponentBoundsConstrainer::setMinimumSize (const int minimumWidth, const int minimumHeight) noexcept
|
||||
{
|
||||
jassert (maxW >= minimumWidth);
|
||||
jassert (maxH >= minimumHeight);
|
||||
jassert (minimumWidth > 0 && minimumHeight > 0);
|
||||
|
||||
minW = minimumWidth;
|
||||
minH = minimumHeight;
|
||||
|
||||
if (minW > maxW)
|
||||
maxW = minW;
|
||||
|
||||
if (minH > maxH)
|
||||
maxH = minH;
|
||||
}
|
||||
|
||||
void ComponentBoundsConstrainer::setMaximumSize (const int maximumWidth, const int maximumHeight) noexcept
|
||||
{
|
||||
jassert (maximumWidth >= minW);
|
||||
jassert (maximumHeight >= minH);
|
||||
jassert (maximumWidth > 0 && maximumHeight > 0);
|
||||
|
||||
maxW = jmax (minW, maximumWidth);
|
||||
maxH = jmax (minH, maximumHeight);
|
||||
}
|
||||
|
||||
void ComponentBoundsConstrainer::setSizeLimits (const int minimumWidth,
|
||||
const int minimumHeight,
|
||||
const int maximumWidth,
|
||||
const int maximumHeight) noexcept
|
||||
{
|
||||
jassert (maximumWidth >= minimumWidth);
|
||||
jassert (maximumHeight >= minimumHeight);
|
||||
jassert (maximumWidth > 0 && maximumHeight > 0);
|
||||
jassert (minimumWidth > 0 && minimumHeight > 0);
|
||||
|
||||
minW = jmax (0, minimumWidth);
|
||||
minH = jmax (0, minimumHeight);
|
||||
maxW = jmax (minW, maximumWidth);
|
||||
maxH = jmax (minH, maximumHeight);
|
||||
}
|
||||
|
||||
void ComponentBoundsConstrainer::setMinimumOnscreenAmounts (const int minimumWhenOffTheTop,
|
||||
const int minimumWhenOffTheLeft,
|
||||
const int minimumWhenOffTheBottom,
|
||||
const int minimumWhenOffTheRight) noexcept
|
||||
{
|
||||
minOffTop = minimumWhenOffTheTop;
|
||||
minOffLeft = minimumWhenOffTheLeft;
|
||||
minOffBottom = minimumWhenOffTheBottom;
|
||||
minOffRight = minimumWhenOffTheRight;
|
||||
}
|
||||
|
||||
void ComponentBoundsConstrainer::setFixedAspectRatio (const double widthOverHeight) noexcept
|
||||
{
|
||||
aspectRatio = jmax (0.0, widthOverHeight);
|
||||
}
|
||||
|
||||
double ComponentBoundsConstrainer::getFixedAspectRatio() const noexcept
|
||||
{
|
||||
return aspectRatio;
|
||||
}
|
||||
|
||||
void ComponentBoundsConstrainer::setBoundsForComponent (Component* const component,
|
||||
const Rectangle<int>& targetBounds,
|
||||
const bool isStretchingTop,
|
||||
const bool isStretchingLeft,
|
||||
const bool isStretchingBottom,
|
||||
const bool isStretchingRight)
|
||||
{
|
||||
jassert (component != nullptr);
|
||||
|
||||
Rectangle<int> limits, bounds (targetBounds);
|
||||
BorderSize<int> border;
|
||||
|
||||
Component* const parent = component->getParentComponent();
|
||||
|
||||
if (parent == nullptr)
|
||||
{
|
||||
ComponentPeer* peer = component->getPeer();
|
||||
if (peer != nullptr)
|
||||
border = peer->getFrameSize();
|
||||
|
||||
limits = Desktop::getInstance().getMonitorAreaContaining (bounds.getCentre());
|
||||
}
|
||||
else
|
||||
{
|
||||
limits.setSize (parent->getWidth(), parent->getHeight());
|
||||
}
|
||||
|
||||
border.addTo (bounds);
|
||||
|
||||
checkBounds (bounds,
|
||||
border.addedTo (component->getBounds()), limits,
|
||||
isStretchingTop, isStretchingLeft,
|
||||
isStretchingBottom, isStretchingRight);
|
||||
|
||||
border.subtractFrom (bounds);
|
||||
|
||||
applyBoundsToComponent (component, bounds);
|
||||
}
|
||||
|
||||
void ComponentBoundsConstrainer::checkComponentBounds (Component* component)
|
||||
{
|
||||
setBoundsForComponent (component, component->getBounds(),
|
||||
false, false, false, false);
|
||||
}
|
||||
|
||||
void ComponentBoundsConstrainer::applyBoundsToComponent (Component* component,
|
||||
const Rectangle<int>& bounds)
|
||||
{
|
||||
Component::Positioner* const positioner = component->getPositioner();
|
||||
|
||||
if (positioner != nullptr)
|
||||
positioner->applyNewBounds (bounds);
|
||||
else
|
||||
component->setBounds (bounds);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void ComponentBoundsConstrainer::resizeStart()
|
||||
{
|
||||
}
|
||||
|
||||
void ComponentBoundsConstrainer::resizeEnd()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void ComponentBoundsConstrainer::checkBounds (Rectangle<int>& bounds,
|
||||
const Rectangle<int>& old,
|
||||
const Rectangle<int>& limits,
|
||||
const bool isStretchingTop,
|
||||
const bool isStretchingLeft,
|
||||
const bool isStretchingBottom,
|
||||
const bool isStretchingRight)
|
||||
{
|
||||
// constrain the size if it's being stretched..
|
||||
if (isStretchingLeft)
|
||||
bounds.setLeft (jlimit (old.getRight() - maxW, old.getRight() - minW, bounds.getX()));
|
||||
|
||||
if (isStretchingRight)
|
||||
bounds.setWidth (jlimit (minW, maxW, bounds.getWidth()));
|
||||
|
||||
if (isStretchingTop)
|
||||
bounds.setTop (jlimit (old.getBottom() - maxH, old.getBottom() - minH, bounds.getY()));
|
||||
|
||||
if (isStretchingBottom)
|
||||
bounds.setHeight (jlimit (minH, maxH, bounds.getHeight()));
|
||||
|
||||
if (bounds.isEmpty())
|
||||
return;
|
||||
|
||||
if (minOffTop > 0)
|
||||
{
|
||||
const int limit = limits.getY() + jmin (minOffTop - bounds.getHeight(), 0);
|
||||
|
||||
if (bounds.getY() < limit)
|
||||
{
|
||||
if (isStretchingTop)
|
||||
bounds.setTop (limits.getY());
|
||||
else
|
||||
bounds.setY (limit);
|
||||
}
|
||||
}
|
||||
|
||||
if (minOffLeft > 0)
|
||||
{
|
||||
const int limit = limits.getX() + jmin (minOffLeft - bounds.getWidth(), 0);
|
||||
|
||||
if (bounds.getX() < limit)
|
||||
{
|
||||
if (isStretchingLeft)
|
||||
bounds.setLeft (limits.getX());
|
||||
else
|
||||
bounds.setX (limit);
|
||||
}
|
||||
}
|
||||
|
||||
if (minOffBottom > 0)
|
||||
{
|
||||
const int limit = limits.getBottom() - jmin (minOffBottom, bounds.getHeight());
|
||||
|
||||
if (bounds.getY() > limit)
|
||||
{
|
||||
if (isStretchingBottom)
|
||||
bounds.setBottom (limits.getBottom());
|
||||
else
|
||||
bounds.setY (limit);
|
||||
}
|
||||
}
|
||||
|
||||
if (minOffRight > 0)
|
||||
{
|
||||
const int limit = limits.getRight() - jmin (minOffRight, bounds.getWidth());
|
||||
|
||||
if (bounds.getX() > limit)
|
||||
{
|
||||
if (isStretchingRight)
|
||||
bounds.setRight (limits.getRight());
|
||||
else
|
||||
bounds.setX (limit);
|
||||
}
|
||||
}
|
||||
|
||||
// constrain the aspect ratio if one has been specified..
|
||||
if (aspectRatio > 0.0)
|
||||
{
|
||||
bool adjustWidth;
|
||||
|
||||
if ((isStretchingTop || isStretchingBottom) && ! (isStretchingLeft || isStretchingRight))
|
||||
{
|
||||
adjustWidth = true;
|
||||
}
|
||||
else if ((isStretchingLeft || isStretchingRight) && ! (isStretchingTop || isStretchingBottom))
|
||||
{
|
||||
adjustWidth = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
const double oldRatio = (old.getHeight() > 0) ? std::abs (old.getWidth() / (double) old.getHeight()) : 0.0;
|
||||
const double newRatio = std::abs (bounds.getWidth() / (double) bounds.getHeight());
|
||||
|
||||
adjustWidth = (oldRatio > newRatio);
|
||||
}
|
||||
|
||||
if (adjustWidth)
|
||||
{
|
||||
bounds.setWidth (roundToInt (bounds.getHeight() * aspectRatio));
|
||||
|
||||
if (bounds.getWidth() > maxW || bounds.getWidth() < minW)
|
||||
{
|
||||
bounds.setWidth (jlimit (minW, maxW, bounds.getWidth()));
|
||||
bounds.setHeight (roundToInt (bounds.getWidth() / aspectRatio));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bounds.setHeight (roundToInt (bounds.getWidth() / aspectRatio));
|
||||
|
||||
if (bounds.getHeight() > maxH || bounds.getHeight() < minH)
|
||||
{
|
||||
bounds.setHeight (jlimit (minH, maxH, bounds.getHeight()));
|
||||
bounds.setWidth (roundToInt (bounds.getHeight() * aspectRatio));
|
||||
}
|
||||
}
|
||||
|
||||
if ((isStretchingTop || isStretchingBottom) && ! (isStretchingLeft || isStretchingRight))
|
||||
{
|
||||
bounds.setX (old.getX() + (old.getWidth() - bounds.getWidth()) / 2);
|
||||
}
|
||||
else if ((isStretchingLeft || isStretchingRight) && ! (isStretchingTop || isStretchingBottom))
|
||||
{
|
||||
bounds.setY (old.getY() + (old.getHeight() - bounds.getHeight()) / 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isStretchingLeft)
|
||||
bounds.setX (old.getRight() - bounds.getWidth());
|
||||
|
||||
if (isStretchingTop)
|
||||
bounds.setY (old.getBottom() - bounds.getHeight());
|
||||
}
|
||||
}
|
||||
|
||||
jassert (! bounds.isEmpty());
|
||||
}
|
||||
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
200
modules/juce_gui_basics/layout/juce_ComponentBoundsConstrainer.h
Normal file
200
modules/juce_gui_basics/layout/juce_ComponentBoundsConstrainer.h
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_COMPONENTBOUNDSCONSTRAINER_JUCEHEADER__
|
||||
#define __JUCE_COMPONENTBOUNDSCONSTRAINER_JUCEHEADER__
|
||||
|
||||
#include "../components/juce_Component.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A class that imposes restrictions on a Component's size or position.
|
||||
|
||||
This is used by classes such as ResizableCornerComponent,
|
||||
ResizableBorderComponent and ResizableWindow.
|
||||
|
||||
The base class can impose some basic size and position limits, but you can
|
||||
also subclass this for custom uses.
|
||||
|
||||
@see ResizableCornerComponent, ResizableBorderComponent, ResizableWindow
|
||||
*/
|
||||
class JUCE_API ComponentBoundsConstrainer
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** When first created, the object will not impose any restrictions on the components. */
|
||||
ComponentBoundsConstrainer() noexcept;
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~ComponentBoundsConstrainer();
|
||||
|
||||
//==============================================================================
|
||||
/** Imposes a minimum width limit. */
|
||||
void setMinimumWidth (int minimumWidth) noexcept;
|
||||
|
||||
/** Returns the current minimum width. */
|
||||
int getMinimumWidth() const noexcept { return minW; }
|
||||
|
||||
/** Imposes a maximum width limit. */
|
||||
void setMaximumWidth (int maximumWidth) noexcept;
|
||||
|
||||
/** Returns the current maximum width. */
|
||||
int getMaximumWidth() const noexcept { return maxW; }
|
||||
|
||||
/** Imposes a minimum height limit. */
|
||||
void setMinimumHeight (int minimumHeight) noexcept;
|
||||
|
||||
/** Returns the current minimum height. */
|
||||
int getMinimumHeight() const noexcept { return minH; }
|
||||
|
||||
/** Imposes a maximum height limit. */
|
||||
void setMaximumHeight (int maximumHeight) noexcept;
|
||||
|
||||
/** Returns the current maximum height. */
|
||||
int getMaximumHeight() const noexcept { return maxH; }
|
||||
|
||||
/** Imposes a minimum width and height limit. */
|
||||
void setMinimumSize (int minimumWidth,
|
||||
int minimumHeight) noexcept;
|
||||
|
||||
/** Imposes a maximum width and height limit. */
|
||||
void setMaximumSize (int maximumWidth,
|
||||
int maximumHeight) noexcept;
|
||||
|
||||
/** Set all the maximum and minimum dimensions. */
|
||||
void setSizeLimits (int minimumWidth,
|
||||
int minimumHeight,
|
||||
int maximumWidth,
|
||||
int maximumHeight) noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the amount by which the component is allowed to go off-screen.
|
||||
|
||||
The values indicate how many pixels must remain on-screen when dragged off
|
||||
one of its parent's edges, so e.g. if minimumWhenOffTheTop is set to 10, then
|
||||
when the component goes off the top of the screen, its y-position will be
|
||||
clipped so that there are always at least 10 pixels on-screen. In other words,
|
||||
the lowest y-position it can take would be (10 - the component's height).
|
||||
|
||||
If you pass 0 or less for one of these amounts, the component is allowed
|
||||
to move beyond that edge completely, with no restrictions at all.
|
||||
|
||||
If you pass a very large number (i.e. larger that the dimensions of the
|
||||
component itself), then the component won't be allowed to overlap that
|
||||
edge at all. So e.g. setting minimumWhenOffTheLeft to 0xffffff will mean that
|
||||
the component will bump into the left side of the screen and go no further.
|
||||
*/
|
||||
void setMinimumOnscreenAmounts (int minimumWhenOffTheTop,
|
||||
int minimumWhenOffTheLeft,
|
||||
int minimumWhenOffTheBottom,
|
||||
int minimumWhenOffTheRight) noexcept;
|
||||
|
||||
|
||||
/** Returns the minimum distance the bounds can be off-screen. @see setMinimumOnscreenAmounts */
|
||||
int getMinimumWhenOffTheTop() const noexcept { return minOffTop; }
|
||||
/** Returns the minimum distance the bounds can be off-screen. @see setMinimumOnscreenAmounts */
|
||||
int getMinimumWhenOffTheLeft() const noexcept { return minOffLeft; }
|
||||
/** Returns the minimum distance the bounds can be off-screen. @see setMinimumOnscreenAmounts */
|
||||
int getMinimumWhenOffTheBottom() const noexcept { return minOffBottom; }
|
||||
/** Returns the minimum distance the bounds can be off-screen. @see setMinimumOnscreenAmounts */
|
||||
int getMinimumWhenOffTheRight() const noexcept { return minOffRight; }
|
||||
|
||||
//==============================================================================
|
||||
/** Specifies a width-to-height ratio that the resizer should always maintain.
|
||||
|
||||
If the value is 0, no aspect ratio is enforced. If it's non-zero, the width
|
||||
will always be maintained as this multiple of the height.
|
||||
|
||||
@see setResizeLimits
|
||||
*/
|
||||
void setFixedAspectRatio (double widthOverHeight) noexcept;
|
||||
|
||||
/** Returns the aspect ratio that was set with setFixedAspectRatio().
|
||||
|
||||
If no aspect ratio is being enforced, this will return 0.
|
||||
*/
|
||||
double getFixedAspectRatio() const noexcept;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** This callback changes the given co-ordinates to impose whatever the current
|
||||
constraints are set to be.
|
||||
|
||||
@param bounds the target position that should be examined and adjusted
|
||||
@param previousBounds the component's current size
|
||||
@param limits the region in which the component can be positioned
|
||||
@param isStretchingTop whether the top edge of the component is being resized
|
||||
@param isStretchingLeft whether the left edge of the component is being resized
|
||||
@param isStretchingBottom whether the bottom edge of the component is being resized
|
||||
@param isStretchingRight whether the right edge of the component is being resized
|
||||
*/
|
||||
virtual void checkBounds (Rectangle<int>& bounds,
|
||||
const Rectangle<int>& previousBounds,
|
||||
const Rectangle<int>& limits,
|
||||
bool isStretchingTop,
|
||||
bool isStretchingLeft,
|
||||
bool isStretchingBottom,
|
||||
bool isStretchingRight);
|
||||
|
||||
/** This callback happens when the resizer is about to start dragging. */
|
||||
virtual void resizeStart();
|
||||
|
||||
/** This callback happens when the resizer has finished dragging. */
|
||||
virtual void resizeEnd();
|
||||
|
||||
/** Checks the given bounds, and then sets the component to the corrected size. */
|
||||
void setBoundsForComponent (Component* component,
|
||||
const Rectangle<int>& bounds,
|
||||
bool isStretchingTop,
|
||||
bool isStretchingLeft,
|
||||
bool isStretchingBottom,
|
||||
bool isStretchingRight);
|
||||
|
||||
/** Performs a check on the current size of a component, and moves or resizes
|
||||
it if it fails the constraints.
|
||||
*/
|
||||
void checkComponentBounds (Component* component);
|
||||
|
||||
/** Called by setBoundsForComponent() to apply a new constrained size to a
|
||||
component.
|
||||
|
||||
By default this just calls setBounds(), but it virtual in case it's needed for
|
||||
extremely cunning purposes.
|
||||
*/
|
||||
virtual void applyBoundsToComponent (Component* component,
|
||||
const Rectangle<int>& bounds);
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
int minW, maxW, minH, maxH;
|
||||
int minOffTop, minOffLeft, minOffBottom, minOffRight;
|
||||
double aspectRatio;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComponentBoundsConstrainer);
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCE_COMPONENTBOUNDSCONSTRAINER_JUCEHEADER__
|
||||
287
modules/juce_gui_basics/layout/juce_ComponentBuilder.cpp
Normal file
287
modules/juce_gui_basics/layout/juce_ComponentBuilder.cpp
Normal file
|
|
@ -0,0 +1,287 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
//=============================================================================
|
||||
namespace ComponentBuilderHelpers
|
||||
{
|
||||
String getStateId (const ValueTree& state)
|
||||
{
|
||||
return state [ComponentBuilder::idProperty].toString();
|
||||
}
|
||||
|
||||
Component* findComponentWithID (OwnedArray<Component>& components, const String& compId)
|
||||
{
|
||||
jassert (compId.isNotEmpty());
|
||||
|
||||
for (int i = components.size(); --i >= 0;)
|
||||
{
|
||||
Component* const c = components.getUnchecked (i);
|
||||
|
||||
if (c->getComponentID() == compId)
|
||||
return components.removeAndReturn (i);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Component* findComponentWithID (Component* const c, const String& compId)
|
||||
{
|
||||
jassert (compId.isNotEmpty());
|
||||
if (c->getComponentID() == compId)
|
||||
return c;
|
||||
|
||||
for (int i = c->getNumChildComponents(); --i >= 0;)
|
||||
{
|
||||
Component* const child = findComponentWithID (c->getChildComponent (i), compId);
|
||||
|
||||
if (child != nullptr)
|
||||
return child;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Component* createNewComponent (ComponentBuilder::TypeHandler& type,
|
||||
const ValueTree& state, Component* parent)
|
||||
{
|
||||
Component* const c = type.addNewComponentFromState (state, parent);
|
||||
jassert (c != nullptr && c->getParentComponent() == parent);
|
||||
c->setComponentID (getStateId (state));
|
||||
return c;
|
||||
}
|
||||
|
||||
void updateComponent (ComponentBuilder& builder, const ValueTree& state)
|
||||
{
|
||||
Component* topLevelComp = builder.getManagedComponent();
|
||||
|
||||
if (topLevelComp != nullptr)
|
||||
{
|
||||
ComponentBuilder::TypeHandler* const type = builder.getHandlerForState (state);
|
||||
const String uid (getStateId (state));
|
||||
|
||||
if (type == nullptr || uid.isEmpty())
|
||||
{
|
||||
// ..handle the case where a child of the actual state node has changed.
|
||||
if (state.getParent().isValid())
|
||||
updateComponent (builder, state.getParent());
|
||||
}
|
||||
else
|
||||
{
|
||||
Component* const changedComp = findComponentWithID (topLevelComp, uid);
|
||||
|
||||
if (changedComp != nullptr)
|
||||
type->updateComponentFromState (changedComp, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
const Identifier ComponentBuilder::idProperty ("id");
|
||||
|
||||
ComponentBuilder::ComponentBuilder (const ValueTree& state_)
|
||||
: state (state_), imageProvider (nullptr)
|
||||
{
|
||||
state.addListener (this);
|
||||
}
|
||||
|
||||
ComponentBuilder::~ComponentBuilder()
|
||||
{
|
||||
state.removeListener (this);
|
||||
|
||||
#if JUCE_DEBUG
|
||||
// Don't delete the managed component!! The builder owns that component, and will delete
|
||||
// it automatically when it gets deleted.
|
||||
jassert (componentRef.get() == static_cast <Component*> (component));
|
||||
#endif
|
||||
}
|
||||
|
||||
Component* ComponentBuilder::getManagedComponent()
|
||||
{
|
||||
if (component == nullptr)
|
||||
{
|
||||
component = createComponent();
|
||||
|
||||
#if JUCE_DEBUG
|
||||
componentRef = component;
|
||||
#endif
|
||||
}
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
Component* ComponentBuilder::createComponent()
|
||||
{
|
||||
jassert (types.size() > 0); // You need to register all the necessary types before you can load a component!
|
||||
|
||||
TypeHandler* const type = getHandlerForState (state);
|
||||
jassert (type != nullptr); // trying to create a component from an unknown type of ValueTree
|
||||
|
||||
return type != nullptr ? ComponentBuilderHelpers::createNewComponent (*type, state, nullptr) : nullptr;
|
||||
}
|
||||
|
||||
void ComponentBuilder::registerTypeHandler (ComponentBuilder::TypeHandler* const type)
|
||||
{
|
||||
jassert (type != nullptr);
|
||||
|
||||
// Don't try to move your types around! Once a type has been added to a builder, the
|
||||
// builder owns it, and you should leave it alone!
|
||||
jassert (type->builder == nullptr);
|
||||
|
||||
types.add (type);
|
||||
type->builder = this;
|
||||
}
|
||||
|
||||
ComponentBuilder::TypeHandler* ComponentBuilder::getHandlerForState (const ValueTree& s) const
|
||||
{
|
||||
const Identifier targetType (s.getType());
|
||||
|
||||
for (int i = 0; i < types.size(); ++i)
|
||||
{
|
||||
TypeHandler* const t = types.getUnchecked(i);
|
||||
|
||||
if (t->getType() == targetType)
|
||||
return t;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int ComponentBuilder::getNumHandlers() const noexcept
|
||||
{
|
||||
return types.size();
|
||||
}
|
||||
|
||||
ComponentBuilder::TypeHandler* ComponentBuilder::getHandler (const int index) const noexcept
|
||||
{
|
||||
return types [index];
|
||||
}
|
||||
|
||||
void ComponentBuilder::setImageProvider (ImageProvider* newImageProvider) noexcept
|
||||
{
|
||||
imageProvider = newImageProvider;
|
||||
}
|
||||
|
||||
ComponentBuilder::ImageProvider* ComponentBuilder::getImageProvider() const noexcept
|
||||
{
|
||||
return imageProvider;
|
||||
}
|
||||
|
||||
void ComponentBuilder::valueTreePropertyChanged (ValueTree& tree, const Identifier&)
|
||||
{
|
||||
ComponentBuilderHelpers::updateComponent (*this, tree);
|
||||
}
|
||||
|
||||
void ComponentBuilder::valueTreeChildAdded (ValueTree& tree, ValueTree&)
|
||||
{
|
||||
ComponentBuilderHelpers::updateComponent (*this, tree);
|
||||
}
|
||||
|
||||
void ComponentBuilder::valueTreeChildRemoved (ValueTree& tree, ValueTree&)
|
||||
{
|
||||
ComponentBuilderHelpers::updateComponent (*this, tree);
|
||||
}
|
||||
|
||||
void ComponentBuilder::valueTreeChildOrderChanged (ValueTree& tree)
|
||||
{
|
||||
ComponentBuilderHelpers::updateComponent (*this, tree);
|
||||
}
|
||||
|
||||
void ComponentBuilder::valueTreeParentChanged (ValueTree& tree)
|
||||
{
|
||||
ComponentBuilderHelpers::updateComponent (*this, tree);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
ComponentBuilder::TypeHandler::TypeHandler (const Identifier& valueTreeType_)
|
||||
: builder (nullptr),
|
||||
valueTreeType (valueTreeType_)
|
||||
{
|
||||
}
|
||||
|
||||
ComponentBuilder::TypeHandler::~TypeHandler()
|
||||
{
|
||||
}
|
||||
|
||||
ComponentBuilder* ComponentBuilder::TypeHandler::getBuilder() const noexcept
|
||||
{
|
||||
// A type handler needs to be registered with a ComponentBuilder before using it!
|
||||
jassert (builder != nullptr);
|
||||
return builder;
|
||||
}
|
||||
|
||||
void ComponentBuilder::updateChildComponents (Component& parent, const ValueTree& children)
|
||||
{
|
||||
using namespace ComponentBuilderHelpers;
|
||||
|
||||
const int numExistingChildComps = parent.getNumChildComponents();
|
||||
|
||||
Array <Component*> componentsInOrder;
|
||||
componentsInOrder.ensureStorageAllocated (numExistingChildComps);
|
||||
|
||||
{
|
||||
OwnedArray<Component> existingComponents;
|
||||
existingComponents.ensureStorageAllocated (numExistingChildComps);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < numExistingChildComps; ++i)
|
||||
existingComponents.add (parent.getChildComponent (i));
|
||||
|
||||
const int newNumChildren = children.getNumChildren();
|
||||
for (i = 0; i < newNumChildren; ++i)
|
||||
{
|
||||
const ValueTree childState (children.getChild (i));
|
||||
|
||||
ComponentBuilder::TypeHandler* const type = getHandlerForState (childState);
|
||||
jassert (type != nullptr);
|
||||
|
||||
if (type != nullptr)
|
||||
{
|
||||
Component* c = findComponentWithID (existingComponents, getStateId (childState));
|
||||
|
||||
if (c == nullptr)
|
||||
c = createNewComponent (*type, childState, &parent);
|
||||
|
||||
componentsInOrder.add (c);
|
||||
}
|
||||
}
|
||||
|
||||
// (remaining unused items in existingComponents get deleted here as it goes out of scope)
|
||||
}
|
||||
|
||||
// Make sure the z-order is correct..
|
||||
if (componentsInOrder.size() > 0)
|
||||
{
|
||||
componentsInOrder.getLast()->toFront (false);
|
||||
|
||||
for (int i = componentsInOrder.size() - 1; --i >= 0;)
|
||||
componentsInOrder.getUnchecked(i)->toBehind (componentsInOrder.getUnchecked (i + 1));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue