1
0
Fork 0
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:
Julian Storer 2011-08-12 10:04:52 +01:00
parent 1a21c89755
commit b70e0a28d2
1527 changed files with 90380 additions and 396643 deletions

View 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

View 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__

View 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__

View 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

View 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__

View 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

View 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__

View 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

View 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__

View 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

View 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__

View 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

View 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__

View 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

View 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__

View 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

View 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__

View 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

View 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__

View 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

View 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__

View 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__

View file

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

View 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__

View file

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

View 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_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__

View 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.
==============================================================================
*/
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

View 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__

View 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

View 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__

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

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

View 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__

View 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

View 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__

View file

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

View 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__

View 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

View 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__

View 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

View 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__

View 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

View 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__

View 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

View 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__

View 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

View 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__

View 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

View 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__

View 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

View 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__

File diff suppressed because it is too large Load diff

View 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.
==============================================================================
*/
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

View file

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

View file

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

View 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__

View file

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

View 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__

View file

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

View 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

View 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__

View file

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

View 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__

View 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

View 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__

View 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

View 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__

View file

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

View file

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

View 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.
==============================================================================
*/
#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__

View 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

View 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__

View 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

View 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__

View 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
//==============================================================================
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

View file

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

View 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.
==============================================================================
*/
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

View file

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

View 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

View 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__

View 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/*" ]
}

View 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

View 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__

View 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

View 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__

View 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

View 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__

View 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

View file

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

View 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

View 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__

View 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__

View 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__

View 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__

View 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

View 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__

View file

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

View 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__

View 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