diff --git a/Builds/Linux/Makefile b/Builds/Linux/Makefile
index 6f8708174d..b78d5fabec 100644
--- a/Builds/Linux/Makefile
+++ b/Builds/Linux/Makefile
@@ -127,6 +127,7 @@ OBJECTS := \
$(OBJDIR)/juce_Component_49f01dfa.o \
$(OBJDIR)/juce_ComponentListener_e0eda7ce.o \
$(OBJDIR)/juce_Desktop_e3b47b99.o \
+ $(OBJDIR)/juce_ModalComponentManager_b87bddba.o \
$(OBJDIR)/juce_ArrowButton_ebac7066.o \
$(OBJDIR)/juce_Button_886d3491.o \
$(OBJDIR)/juce_DrawableButton_e03899cf.o \
@@ -793,6 +794,11 @@ $(OBJDIR)/juce_Desktop_e3b47b99.o: ../../src/gui/components/juce_Desktop.cpp
@echo "Compiling juce_Desktop.cpp"
@$(CXX) $(CXXFLAGS) -o "$@" -c "$<"
+$(OBJDIR)/juce_ModalComponentManager_b87bddba.o: ../../src/gui/components/juce_ModalComponentManager.cpp
+ -@mkdir -p $(OBJDIR)
+ @echo "Compiling juce_ModalComponentManager.cpp"
+ @$(CXX) $(CXXFLAGS) -o "$@" -c "$<"
+
$(OBJDIR)/juce_ArrowButton_ebac7066.o: ../../src/gui/components/buttons/juce_ArrowButton.cpp
-@mkdir -p $(OBJDIR)
@echo "Compiling juce_ArrowButton.cpp"
diff --git a/Builds/MacOSX/Juce.xcodeproj/project.pbxproj b/Builds/MacOSX/Juce.xcodeproj/project.pbxproj
index 2fd68d1228..ebe1f3851a 100644
--- a/Builds/MacOSX/Juce.xcodeproj/project.pbxproj
+++ b/Builds/MacOSX/Juce.xcodeproj/project.pbxproj
@@ -96,6 +96,7 @@
621B7B0B518E63E69122C13E = { isa = PBXBuildFile; fileRef = D0D9267E200BD462361810F7; };
97982824572C18827047D92A = { isa = PBXBuildFile; fileRef = E13F33E386E1A0D5FC546521; };
927E7250FCE62E838599DF83 = { isa = PBXBuildFile; fileRef = 621B3A4B154182F69DDE2989; };
+ 9AE6891C35CE161CB1707B4B = { isa = PBXBuildFile; fileRef = 2FFF9AFE4BD9437CE096E52B; };
06994A713C38F415C4E8A009 = { isa = PBXBuildFile; fileRef = 18EE6576A9ED098632CE5155; };
98F737B7459895BFCDC7965E = { isa = PBXBuildFile; fileRef = 8B1C747E63EEF036AD9AF3D8; };
7033A0968B1C81A821CCC296 = { isa = PBXBuildFile; fileRef = 1C3D15546065C1A9AA5AA0C6; };
@@ -550,6 +551,8 @@
A0D6308567AAA50D1163D9D3 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_ComponentListener.h; path = ../../src/gui/components/juce_ComponentListener.h; sourceTree = SOURCE_ROOT; };
621B3A4B154182F69DDE2989 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_Desktop.cpp; path = ../../src/gui/components/juce_Desktop.cpp; sourceTree = SOURCE_ROOT; };
A1F58C1A972425C2B43DD1B3 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_Desktop.h; path = ../../src/gui/components/juce_Desktop.h; sourceTree = SOURCE_ROOT; };
+ 2FFF9AFE4BD9437CE096E52B = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_ModalComponentManager.cpp; path = ../../src/gui/components/juce_ModalComponentManager.cpp; sourceTree = SOURCE_ROOT; };
+ 41C8C324F13ADA3423FC3B0F = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_ModalComponentManager.h; path = ../../src/gui/components/juce_ModalComponentManager.h; sourceTree = SOURCE_ROOT; };
18EE6576A9ED098632CE5155 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_ArrowButton.cpp; path = ../../src/gui/components/buttons/juce_ArrowButton.cpp; sourceTree = SOURCE_ROOT; };
EB182DC4124FEFFFC87D12C4 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_ArrowButton.h; path = ../../src/gui/components/buttons/juce_ArrowButton.h; sourceTree = SOURCE_ROOT; };
8B1C747E63EEF036AD9AF3D8 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_Button.cpp; path = ../../src/gui/components/buttons/juce_Button.cpp; sourceTree = SOURCE_ROOT; };
@@ -1455,6 +1458,8 @@
A0D6308567AAA50D1163D9D3,
621B3A4B154182F69DDE2989,
A1F58C1A972425C2B43DD1B3,
+ 2FFF9AFE4BD9437CE096E52B,
+ 41C8C324F13ADA3423FC3B0F,
2DB55F83F4310F0C4E4E03AA,
17B11D96CDB313ED60D8CFE0,
BCB2FFE7C2A4084A267F57F2,
@@ -1930,6 +1935,7 @@
621B7B0B518E63E69122C13E,
97982824572C18827047D92A,
927E7250FCE62E838599DF83,
+ 9AE6891C35CE161CB1707B4B,
06994A713C38F415C4E8A009,
98F737B7459895BFCDC7965E,
7033A0968B1C81A821CCC296,
diff --git a/Builds/VisualStudio2005/Juce.vcproj b/Builds/VisualStudio2005/Juce.vcproj
index c4f4f4913a..91d3cc3947 100644
--- a/Builds/VisualStudio2005/Juce.vcproj
+++ b/Builds/VisualStudio2005/Juce.vcproj
@@ -349,6 +349,8 @@
+
+
diff --git a/Builds/VisualStudio2008/Juce.vcproj b/Builds/VisualStudio2008/Juce.vcproj
index c7d8aa47f1..0246a9ae70 100644
--- a/Builds/VisualStudio2008/Juce.vcproj
+++ b/Builds/VisualStudio2008/Juce.vcproj
@@ -349,6 +349,8 @@
+
+
diff --git a/Builds/VisualStudio2008_DLL/Juce.vcproj b/Builds/VisualStudio2008_DLL/Juce.vcproj
index 889644155b..c98fc80379 100644
--- a/Builds/VisualStudio2008_DLL/Juce.vcproj
+++ b/Builds/VisualStudio2008_DLL/Juce.vcproj
@@ -351,6 +351,8 @@
+
+
diff --git a/Builds/VisualStudio2010/Juce.vcxproj b/Builds/VisualStudio2010/Juce.vcxproj
index 993589cef0..598a1b5746 100644
--- a/Builds/VisualStudio2010/Juce.vcxproj
+++ b/Builds/VisualStudio2010/Juce.vcxproj
@@ -210,6 +210,7 @@
+
@@ -549,6 +550,7 @@
+
diff --git a/Builds/VisualStudio2010/Juce.vcxproj.filters b/Builds/VisualStudio2010/Juce.vcxproj.filters
index 85fb0ccddf..55a432946b 100644
--- a/Builds/VisualStudio2010/Juce.vcxproj.filters
+++ b/Builds/VisualStudio2010/Juce.vcxproj.filters
@@ -430,6 +430,9 @@
Juce\Source\gui\components
+
+ Juce\Source\gui\components
+
Juce\Source\gui\components\buttons
@@ -1521,6 +1524,9 @@
Juce\Source\gui\components
+
+ Juce\Source\gui\components
+
Juce\Source\gui\components\buttons
diff --git a/Builds/iPhone/Juce.xcodeproj/project.pbxproj b/Builds/iPhone/Juce.xcodeproj/project.pbxproj
index f5f13d37b6..ad213a3423 100644
--- a/Builds/iPhone/Juce.xcodeproj/project.pbxproj
+++ b/Builds/iPhone/Juce.xcodeproj/project.pbxproj
@@ -96,6 +96,7 @@
621B7B0B518E63E69122C13E = { isa = PBXBuildFile; fileRef = D0D9267E200BD462361810F7; };
97982824572C18827047D92A = { isa = PBXBuildFile; fileRef = E13F33E386E1A0D5FC546521; };
927E7250FCE62E838599DF83 = { isa = PBXBuildFile; fileRef = 621B3A4B154182F69DDE2989; };
+ 9AE6891C35CE161CB1707B4B = { isa = PBXBuildFile; fileRef = 2FFF9AFE4BD9437CE096E52B; };
06994A713C38F415C4E8A009 = { isa = PBXBuildFile; fileRef = 18EE6576A9ED098632CE5155; };
98F737B7459895BFCDC7965E = { isa = PBXBuildFile; fileRef = 8B1C747E63EEF036AD9AF3D8; };
7033A0968B1C81A821CCC296 = { isa = PBXBuildFile; fileRef = 1C3D15546065C1A9AA5AA0C6; };
@@ -550,6 +551,8 @@
A0D6308567AAA50D1163D9D3 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_ComponentListener.h; path = ../../src/gui/components/juce_ComponentListener.h; sourceTree = SOURCE_ROOT; };
621B3A4B154182F69DDE2989 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_Desktop.cpp; path = ../../src/gui/components/juce_Desktop.cpp; sourceTree = SOURCE_ROOT; };
A1F58C1A972425C2B43DD1B3 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_Desktop.h; path = ../../src/gui/components/juce_Desktop.h; sourceTree = SOURCE_ROOT; };
+ 2FFF9AFE4BD9437CE096E52B = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_ModalComponentManager.cpp; path = ../../src/gui/components/juce_ModalComponentManager.cpp; sourceTree = SOURCE_ROOT; };
+ 41C8C324F13ADA3423FC3B0F = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_ModalComponentManager.h; path = ../../src/gui/components/juce_ModalComponentManager.h; sourceTree = SOURCE_ROOT; };
18EE6576A9ED098632CE5155 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_ArrowButton.cpp; path = ../../src/gui/components/buttons/juce_ArrowButton.cpp; sourceTree = SOURCE_ROOT; };
EB182DC4124FEFFFC87D12C4 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_ArrowButton.h; path = ../../src/gui/components/buttons/juce_ArrowButton.h; sourceTree = SOURCE_ROOT; };
8B1C747E63EEF036AD9AF3D8 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_Button.cpp; path = ../../src/gui/components/buttons/juce_Button.cpp; sourceTree = SOURCE_ROOT; };
@@ -1455,6 +1458,8 @@
A0D6308567AAA50D1163D9D3,
621B3A4B154182F69DDE2989,
A1F58C1A972425C2B43DD1B3,
+ 2FFF9AFE4BD9437CE096E52B,
+ 41C8C324F13ADA3423FC3B0F,
2DB55F83F4310F0C4E4E03AA,
17B11D96CDB313ED60D8CFE0,
BCB2FFE7C2A4084A267F57F2,
@@ -1930,6 +1935,7 @@
621B7B0B518E63E69122C13E,
97982824572C18827047D92A,
927E7250FCE62E838599DF83,
+ 9AE6891C35CE161CB1707B4B,
06994A713C38F415C4E8A009,
98F737B7459895BFCDC7965E,
7033A0968B1C81A821CCC296,
diff --git a/Juce.jucer b/Juce.jucer
index 268f848a72..f2ea35a8c9 100644
--- a/Juce.jucer
+++ b/Juce.jucer
@@ -495,6 +495,10 @@
file="src/gui/components/juce_Desktop.cpp"/>
+
+
diff --git a/amalgamation/juce_amalgamated_template.cpp b/amalgamation/juce_amalgamated_template.cpp
index 53f3f0e569..2628e59bff 100644
--- a/amalgamation/juce_amalgamated_template.cpp
+++ b/amalgamation/juce_amalgamated_template.cpp
@@ -223,6 +223,7 @@
#include "../src/gui/components/juce_Component.cpp"
#include "../src/gui/components/juce_ComponentListener.cpp"
#include "../src/gui/components/juce_Desktop.cpp"
+ #include "../src/gui/components/juce_ModalComponentManager.cpp"
#include "../src/gui/components/buttons/juce_ArrowButton.cpp"
#include "../src/gui/components/buttons/juce_Button.cpp"
#include "../src/gui/components/buttons/juce_DrawableButton.cpp"
diff --git a/extras/Jucer (experimental)/Source/model/Project/jucer_ProjectExport_MSVC.h b/extras/Jucer (experimental)/Source/model/Project/jucer_ProjectExport_MSVC.h
index a01f65417c..f049d6b3cb 100644
--- a/extras/Jucer (experimental)/Source/model/Project/jucer_ProjectExport_MSVC.h
+++ b/extras/Jucer (experimental)/Source/model/Project/jucer_ProjectExport_MSVC.h
@@ -1184,7 +1184,7 @@ protected:
const String getProjectType() const
{
- if (project.isGUIApplication())
+ if (project.isGUIApplication() || project.isCommandLineApp())
return "Application";
else if (project.isAudioPlugin() || project.isBrowserPlugin())
return "DynamicLibrary";
diff --git a/extras/audio plugin host/Plugin Host.jucer b/extras/audio plugin host/Plugin Host.jucer
index a22e9acf38..4dd93359c1 100644
--- a/extras/audio plugin host/Plugin Host.jucer
+++ b/extras/audio plugin host/Plugin Host.jucer
@@ -13,12 +13,12 @@
jucerVersion="3.0.0">
+ juceFolder="../.."/>
+ rtasFolder="c:\SDKs\PT_80_SDK" juceFolder="../.." libraryType="1"/>
-
+ rtasFolder="c:\SDKs\PT_80_SDK" juceFolder="../.." libraryType="1"/>
+
+ juceFolder="../../.." objCExtraSuffix="JuceDemo"/>
+ rtasFolder="c:\SDKs\PT_80_SDK" juceFolder="../../.." libraryType="1"/>
+ rtasFolder="c:\SDKs\PT_80_SDK" juceFolder="../../.." libraryType="1"/>
+ juceFolder="../.."/>
+ juceFolder="../.."/>
+ rtasFolder="c:\SDKs\PT_80_SDK" juceFolder="../.." libraryType="1"/>
-
+ rtasFolder="c:\SDKs\PT_80_SDK" juceFolder="../.." libraryType="1"/>
+
modalComponentStack, modalComponentReturnValueKeys;
-static Array modalReturnValues;
+#define checkMessageManagerIsLocked jassert (MessageManager::getInstance()->currentThreadHasLockedMessageManager());
enum ComponentMessageNumbers
{
@@ -38520,9 +38528,8 @@ enum ComponentMessageNumbers
exitModalStateMessage = 0x7fff0002
};
-#define checkMessageManagerIsLocked jassert (MessageManager::getInstance()->currentThreadHasLockedMessageManager());
-
static uint32 nextComponentUID = 0;
+Component* Component::currentlyFocusedComponent = 0;
Component::Component()
: parentComponent_ (0),
@@ -38568,8 +38575,6 @@ Component::~Component()
if (flags.hasHeavyweightPeerFlag)
removeFromDesktop();
- modalComponentStack.removeValue (this);
-
for (int i = childComponentList_.size(); --i >= 0;)
childComponentList_.getUnchecked(i)->parentComponent_ = 0;
@@ -39776,59 +39781,17 @@ int Component::runModalLoop()
if (! MessageManager::getInstance()->isThisTheMessageThread())
{
// use a callback so this can be called from non-gui threads
- return (int) (pointer_sized_int)
- MessageManager::getInstance()
- ->callFunctionOnMessageThread (&runModalLoopCallback, this);
+ return (int) (pointer_sized_int) MessageManager::getInstance()
+ ->callFunctionOnMessageThread (&runModalLoopCallback, this);
}
- SafePointer prevFocused (getCurrentlyFocusedComponent());
-
if (! isCurrentlyModal())
- enterModalState();
+ enterModalState (true);
- JUCE_TRY
- {
- while (flags.currentlyModalFlag && flags.visibleFlag)
- {
- if (! MessageManager::getInstance()->runDispatchLoopUntil (20))
- break;
-
- // check whether this component was deleted during the last message
- if (! isValidMessageListener())
- break;
- }
- }
-#if JUCE_CATCH_UNHANDLED_EXCEPTIONS
- catch (const std::exception& e)
- {
- JUCEApplication::sendUnhandledException (&e, __FILE__, __LINE__);
- return 0;
- }
- catch (...)
- {
- JUCEApplication::sendUnhandledException (0, __FILE__, __LINE__);
- return 0;
- }
-#endif
-
- const int modalIndex = modalComponentReturnValueKeys.indexOf (this);
- int returnValue = 0;
-
- if (modalIndex >= 0)
- {
- modalComponentReturnValueKeys.remove (modalIndex);
- returnValue = modalReturnValues.remove (modalIndex);
- }
-
- modalComponentStack.removeValue (this);
-
- if (prevFocused != 0)
- prevFocused->grabKeyboardFocus();
-
- return returnValue;
+ return ModalComponentManager::getInstance()->runEventLoopForCurrentComponent();
}
-void Component::enterModalState (const bool takeKeyboardFocus_)
+void Component::enterModalState (const bool takeKeyboardFocus_, ModalComponentManager::Callback* const callback)
{
// if component methods are being called from threads other than the message
// thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
@@ -39840,10 +39803,7 @@ void Component::enterModalState (const bool takeKeyboardFocus_)
if (! isCurrentlyModal())
{
- modalComponentStack.add (this);
- modalComponentReturnValueKeys.add (this);
- modalReturnValues.add (0);
-
+ ModalComponentManager::getInstance()->startModal (this, callback);
flags.currentlyModalFlag = true;
setVisible (true);
@@ -39858,20 +39818,7 @@ void Component::exitModalState (const int returnValue)
{
if (MessageManager::getInstance()->isThisTheMessageThread())
{
- const int modalIndex = modalComponentReturnValueKeys.indexOf (this);
-
- if (modalIndex >= 0)
- {
- modalReturnValues.set (modalIndex, returnValue);
- }
- else
- {
- modalComponentReturnValueKeys.add (this);
- modalReturnValues.add (returnValue);
- }
-
- modalComponentStack.removeValue (this);
-
+ ModalComponentManager::getInstance()->endModal (this, returnValue);
flags.currentlyModalFlag = false;
bringModalComponentToFront();
@@ -39901,14 +39848,12 @@ bool Component::isCurrentlyBlockedByAnotherModalComponent() const
int JUCE_CALLTYPE Component::getNumCurrentlyModalComponents() throw()
{
- return modalComponentStack.size();
+ return ModalComponentManager::getInstance()->getNumModalComponents();
}
Component* JUCE_CALLTYPE Component::getCurrentlyModalComponent (int index) throw()
{
- Component* const c = static_cast (modalComponentStack [modalComponentStack.size() - index - 1]);
-
- return c->isValidComponent() ? c : 0;
+ return ModalComponentManager::getInstance()->getModalComponent (index);
}
void Component::bringModalComponentToFront()
@@ -41974,6 +41919,239 @@ END_JUCE_NAMESPACE
/*** End of inlined file: juce_Desktop.cpp ***/
+/*** Start of inlined file: juce_ModalComponentManager.cpp ***/
+BEGIN_JUCE_NAMESPACE
+
+class ModalComponentManager::ModalItem : public ComponentListener
+{
+public:
+ ModalItem (Component* const comp, Callback* const callback)
+ : component (comp), returnValue (0), isActive (true), isDeleted (false)
+ {
+ if (callback != 0)
+ callbacks.add (callback);
+
+ jassert (comp != 0);
+ component->addComponentListener (this);
+ }
+
+ ~ModalItem()
+ {
+ if (! isDeleted)
+ component->removeComponentListener (this);
+ }
+
+ void componentBeingDeleted (Component&)
+ {
+ isDeleted = true;
+ cancel();
+ }
+
+ void componentVisibilityChanged (Component&)
+ {
+ if (! component->isShowing())
+ cancel();
+ }
+
+ void componentParentHierarchyChanged (Component&)
+ {
+ if (! component->isShowing())
+ cancel();
+ }
+
+ void cancel()
+ {
+ if (isActive)
+ {
+ isActive = false;
+ ModalComponentManager::getInstance()->triggerAsyncUpdate();
+ }
+ }
+
+ Component* component;
+ OwnedArray callbacks;
+ int returnValue;
+ bool isActive, isDeleted;
+
+private:
+ ModalItem (const ModalItem&);
+ ModalItem& operator= (const ModalItem&);
+};
+
+ModalComponentManager::ModalComponentManager()
+{
+}
+
+ModalComponentManager::~ModalComponentManager()
+{
+ clearSingletonInstance();
+}
+
+juce_ImplementSingleton_SingleThreaded (ModalComponentManager);
+
+void ModalComponentManager::startModal (Component* component, Callback* callback)
+{
+ if (component != 0)
+ stack.add (new ModalItem (component, callback));
+}
+
+void ModalComponentManager::attachCallback (Component* component, Callback* callback)
+{
+ if (callback != 0)
+ {
+ ScopedPointer 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 0;
+}
+
+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)
+ {
+ for (int j = item->callbacks.size(); --j >= 0;)
+ item->callbacks.getUnchecked(j)->modalStateFinished (item->returnValue);
+
+ stack.remove (i);
+ }
+ }
+}
+
+class ModalComponentManager::ReturnValueRetriever : public ModalComponentManager::Callback
+{
+public:
+ ReturnValueRetriever (int& value_, bool& finished_) : value (value_), finished (finished_) {}
+ ~ReturnValueRetriever() {}
+
+ void modalStateFinished (int returnValue)
+ {
+ finished = true;
+ value = returnValue;
+ }
+
+private:
+ int& value;
+ bool& finished;
+
+ ReturnValueRetriever (const ReturnValueRetriever&);
+ ReturnValueRetriever& operator= (const ReturnValueRetriever&);
+};
+
+int ModalComponentManager::runEventLoopForCurrentComponent()
+{
+ // This can only be run from the message thread!
+ jassert (MessageManager::getInstance()->isThisTheMessageThread());
+
+ Component* currentlyModal = getModalComponent (0);
+
+ if (currentlyModal == 0)
+ return 0;
+
+ Component::SafePointer 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 != 0)
+ prevFocused->grabKeyboardFocus();
+
+ return returnValue;
+}
+
+END_JUCE_NAMESPACE
+/*** End of inlined file: juce_ModalComponentManager.cpp ***/
+
+
/*** Start of inlined file: juce_ArrowButton.cpp ***/
BEGIN_JUCE_NAMESPACE
@@ -46659,15 +46837,39 @@ void ComboBox::labelTextChanged (Label*)
triggerAsyncUpdate();
}
+class ComboBox::Callback : public ModalComponentManager::Callback
+{
+public:
+ Callback (ComboBox* const box_)
+ : box (box_)
+ {
+ }
+
+ void modalStateFinished (int returnValue)
+ {
+ if (box != 0)
+ {
+ box->menuActive = false;
+
+ if (returnValue != 0)
+ box->setSelectedId (returnValue);
+ }
+ }
+
+private:
+ Component::SafePointer box;
+
+ Callback (const Callback&);
+ Callback& operator= (const Callback&);
+};
+
void ComboBox::showPopup()
{
if (! menuActive)
{
const int selectedId = getSelectedId();
- Component::SafePointer deletionWatcher (this);
PopupMenu menu;
-
menu.setLookAndFeel (&getLookAndFeel());
for (int i = 0; i < items.size(); ++i)
@@ -46686,19 +46888,9 @@ void ComboBox::showPopup()
if (items.size() == 0)
menu.addItem (1, noChoicesMessage, false);
- const int itemHeight = jlimit (12, 24, getHeight());
-
menuActive = true;
- const int resultId = menu.showAt (this, selectedId,
- getWidth(), 1, itemHeight);
-
- if (deletionWatcher == 0)
- return;
-
- menuActive = false;
-
- if (resultId != 0)
- setSelectedId (resultId);
+ menu.showAt (this, selectedId, getWidth(), 1, jlimit (12, 24, getHeight()),
+ new Callback (this));
}
}
@@ -52776,6 +52968,27 @@ void TextEditor::paintOverChildren (Graphics& g)
getLookAndFeel().drawTextEditorOutline (g, getWidth(), getHeight(), *this);
}
+class TextEditorMenuPerformer : public ModalComponentManager::Callback
+{
+public:
+ TextEditorMenuPerformer (TextEditor* const editor_)
+ : editor (editor_)
+ {
+ }
+
+ void modalStateFinished (int returnValue)
+ {
+ if (editor != 0 && returnValue != 0)
+ editor->performPopupMenuAction (returnValue);
+ }
+
+private:
+ Component::SafePointer editor;
+
+ TextEditorMenuPerformer (const TextEditorMenuPerformer&);
+ TextEditorMenuPerformer& operator= (const TextEditorMenuPerformer&);
+};
+
void TextEditor::mouseDown (const MouseEvent& e)
{
beginDragAutoRepeat (100);
@@ -52794,12 +53007,7 @@ void TextEditor::mouseDown (const MouseEvent& e)
m.setLookAndFeel (&getLookAndFeel());
addPopupMenuItems (m, &e);
- menuActive = true;
- const int result = m.show();
- menuActive = false;
-
- if (result != 0)
- performPopupMenuAction (result);
+ m.show (0, 0, 0, 0, new TextEditorMenuPerformer (this));
}
}
}
@@ -53219,7 +53427,7 @@ void TextEditor::enablementChanged()
UndoManager* TextEditor::getUndoManager() throw()
{
- return isReadOnly() ? &undoManager : 0;
+ return isReadOnly() ? 0 : &undoManager;
}
void TextEditor::clearInternal (UndoManager* const um)
@@ -57558,6 +57766,11 @@ FileChooserDialogBox::~FileChooserDialogBox()
}
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)
{
@@ -57571,7 +57784,10 @@ bool FileChooserDialogBox::show (int w, int h)
if (h <= 0)
h = 500;
- centreWithSize (w, h);
+ if (x < 0 || y < 0)
+ centreWithSize (w, h);
+ else
+ setBounds (x, y, w, h);
const bool ok = (runModalLoop() != 0);
setVisible (false);
@@ -67191,29 +67407,13 @@ END_JUCE_NAMESPACE
/*** Start of inlined file: juce_MenuBarComponent.cpp ***/
BEGIN_JUCE_NAMESPACE
-class DummyMenuComponent : public Component
-{
- DummyMenuComponent (const DummyMenuComponent&);
- DummyMenuComponent& operator= (const DummyMenuComponent&);
-
-public:
- DummyMenuComponent() {}
- ~DummyMenuComponent() {}
-
- void inputAttemptWhenModal()
- {
- exitModalState (0);
- }
-};
-
MenuBarComponent::MenuBarComponent (MenuBarModel* model_)
: model (0),
itemUnderMouse (-1),
currentPopupIndex (-1),
- indexToShowAgain (-1),
+ topLevelIndexClicked (0),
lastMouseX (0),
- lastMouseY (0),
- inModalState (false)
+ lastMouseY (0)
{
setRepaintsOnMouseActivity (true);
setWantsKeyboardFocus (false);
@@ -67225,9 +67425,12 @@ MenuBarComponent::MenuBarComponent (MenuBarModel* model_)
MenuBarComponent::~MenuBarComponent()
{
setModel (0);
-
Desktop::getInstance().removeGlobalMouseListener (this);
- currentPopup = 0;
+}
+
+MenuBarModel* MenuBarComponent::getModel() const throw()
+{
+ return model;
}
void MenuBarComponent::setModel (MenuBarModel* const newModel)
@@ -67314,144 +67517,102 @@ void MenuBarComponent::repaintMenuItem (int index)
}
}
-void MenuBarComponent::updateItemUnderMouse (int x, int y)
+void MenuBarComponent::setItemUnderMouse (const int index)
{
- const int newItem = getItemAt (x, y);
-
- if (itemUnderMouse != newItem)
+ if (itemUnderMouse != index)
{
repaintMenuItem (itemUnderMouse);
- itemUnderMouse = newItem;
+ itemUnderMouse = index;
repaintMenuItem (itemUnderMouse);
}
}
-void MenuBarComponent::hideCurrentMenu()
+void MenuBarComponent::setOpenItem (int index)
{
- currentPopup = 0;
- repaint();
+ if (currentPopupIndex != index)
+ {
+ repaintMenuItem (currentPopupIndex);
+ currentPopupIndex = index;
+ repaintMenuItem (currentPopupIndex);
+
+ if (index >= 0)
+ Desktop::getInstance().addGlobalMouseListener (this);
+ else
+ Desktop::getInstance().removeGlobalMouseListener (this);
+ }
}
+void MenuBarComponent::updateItemUnderMouse (int x, int y)
+{
+ setItemUnderMouse (getItemAt (x, y));
+}
+
+class MenuBarComponent::AsyncCallback : public ModalComponentManager::Callback
+{
+public:
+ AsyncCallback (MenuBarComponent* const bar_, const int topLevelIndex_)
+ : bar (bar_), topLevelIndex (topLevelIndex_)
+ {
+ }
+
+ ~AsyncCallback() {}
+
+ void modalStateFinished (int returnValue)
+ {
+ if (bar != 0)
+ bar->menuDismissed (topLevelIndex, returnValue);
+ }
+
+private:
+ Component::SafePointer bar;
+ const int topLevelIndex;
+
+ AsyncCallback (const AsyncCallback&);
+ AsyncCallback& operator= (const AsyncCallback&);
+};
+
void MenuBarComponent::showMenu (int index)
{
if (index != currentPopupIndex)
{
- if (inModalState)
- {
- hideCurrentMenu();
- indexToShowAgain = index;
- return;
- }
-
- indexToShowAgain = -1;
- currentPopupIndex = -1;
- itemUnderMouse = index;
- currentPopup = 0;
+ PopupMenu::dismissAllActiveMenus();
menuBarItemsChanged (0);
- Component::SafePointer prevFocused (getCurrentlyFocusedComponent());
- Component::SafePointer deletionChecker (this);
+ setOpenItem (index);
+ setItemUnderMouse (index);
- enterModalState (false);
- inModalState = true;
- int result = 0;
- ApplicationCommandManager* managerOfChosenCommand = 0;
-
- Desktop::getInstance().addGlobalMouseListener (this);
-
- for (;;)
+ if (index >= 0)
{
- const int x = getScreenX() + xPositions [itemUnderMouse];
- const int w = xPositions [itemUnderMouse + 1] - xPositions [itemUnderMouse];
+ PopupMenu m (model->getMenuForIndex (itemUnderMouse,
+ menuNames [itemUnderMouse]));
- currentPopupIndex = itemUnderMouse;
- indexToShowAgain = -1;
- repaint();
+ if (m.lookAndFeel == 0)
+ m.setLookAndFeel (&getLookAndFeel());
- if (((unsigned int) itemUnderMouse) < (unsigned int) menuNames.size())
- {
- PopupMenu m (model->getMenuForIndex (itemUnderMouse,
- menuNames [itemUnderMouse]));
+ const Rectangle itemPos (xPositions [index], 0, xPositions [index + 1] - xPositions [index], getHeight());
- if (m.lookAndFeel == 0)
- m.setLookAndFeel (&getLookAndFeel());
-
- currentPopup = m.createMenuComponent (x, getScreenY(),
- w, getHeight(),
- 0, w, 0, 0,
- true, this,
- &managerOfChosenCommand,
- this);
- }
-
- if (currentPopup == 0)
- {
- currentPopup = new DummyMenuComponent();
- addAndMakeVisible (currentPopup);
- }
-
- currentPopup->enterModalState (false);
- currentPopup->toFront (false); // need to do this after making it modal, or it could
- // be stuck behind other comps that are already modal..
- result = currentPopup->runModalLoop();
-
- if (deletionChecker == 0)
- return;
-
- const int lastPopupIndex = currentPopupIndex;
- currentPopup = 0;
- currentPopupIndex = -1;
-
- if (result != 0)
- {
- topLevelIndexClicked = lastPopupIndex;
- break;
- }
- else if (indexToShowAgain >= 0)
- {
- menuBarItemsChanged (0);
- repaint();
- itemUnderMouse = indexToShowAgain;
-
- if (((unsigned int) itemUnderMouse) >= (unsigned int) menuNames.size())
- break;
- }
- else
- {
- break;
- }
- }
-
- Desktop::getInstance().removeGlobalMouseListener (this);
-
- inModalState = false;
- exitModalState (0);
-
- if (prevFocused != 0)
- prevFocused->grabKeyboardFocus();
-
- const Point mousePos (getMouseXYRelative());
- updateItemUnderMouse (mousePos.getX(), mousePos.getY());
- repaint();
-
- if (result != 0)
- {
- if (managerOfChosenCommand != 0)
- {
- ApplicationCommandTarget::InvocationInfo info (result);
- info.invocationMethod = ApplicationCommandTarget::InvocationInfo::fromMenu;
-
- managerOfChosenCommand->invoke (info, true);
- }
-
- postCommandMessage (result);
+ m.showMenu (itemPos + getScreenPosition(),
+ 0, itemPos.getWidth(), 0, 0, true, this,
+ new AsyncCallback (this, index));
}
}
}
+void MenuBarComponent::menuDismissed (int topLevelIndex, int itemId)
+{
+ topLevelIndexClicked = topLevelIndex;
+ postCommandMessage (itemId);
+}
+
void MenuBarComponent::handleCommandMessage (int commandId)
{
- if (model != 0)
+ const Point mousePos (getMouseXYRelative());
+ updateItemUnderMouse (mousePos.getX(), mousePos.getY());
+
+ if (! isCurrentlyBlockedByAnotherModalComponent())
+ setOpenItem (-1);
+
+ if (commandId != 0 && model != 0)
model->menuItemSelected (commandId, topLevelIndexClicked);
}
@@ -67482,7 +67643,6 @@ void MenuBarComponent::mouseDown (const MouseEvent& e)
void MenuBarComponent::mouseDrag (const MouseEvent& e)
{
const MouseEvent e2 (e.getEventRelativeTo (this));
-
const int item = getItemAt (e2.x, e2.y);
if (item >= 0)
@@ -67495,8 +67655,11 @@ void MenuBarComponent::mouseUp (const MouseEvent& e)
updateItemUnderMouse (e2.x, e2.y);
- if (itemUnderMouse < 0 && dynamic_cast (static_cast (currentPopup)) != 0)
- hideCurrentMenu();
+ if (itemUnderMouse < 0 && getLocalBounds().contains (e2.x, e2.y))
+ {
+ setOpenItem (-1);
+ PopupMenu::dismissAllActiveMenus();
+ }
}
void MenuBarComponent::mouseMove (const MouseEvent& e)
@@ -67542,11 +67705,6 @@ bool MenuBarComponent::keyPressed (const KeyPress& key)
return used;
}
-void MenuBarComponent::inputAttemptWhenModal()
-{
- hideCurrentMenu();
-}
-
void MenuBarComponent::menuBarItemsChanged (MenuBarModel* /*menuBarModel*/)
{
StringArray newNames;
@@ -67565,8 +67723,7 @@ void MenuBarComponent::menuBarItemsChanged (MenuBarModel* /*menuBarModel*/)
void MenuBarComponent::menuCommandInvoked (MenuBarModel* /*menuBarModel*/,
const ApplicationCommandTarget::InvocationInfo& info)
{
- if (model == 0
- || (info.commandFlags & ApplicationCommandInfo::dontTriggerVisualFeedback) != 0)
+ if (model == 0 || (info.commandFlags & ApplicationCommandInfo::dontTriggerVisualFeedback) != 0)
return;
for (int i = 0; i < menuNames.size(); ++i)
@@ -67575,10 +67732,8 @@ void MenuBarComponent::menuCommandInvoked (MenuBarModel* /*menuBarModel*/,
if (menu.containsCommandItem (info.commandID))
{
- itemUnderMouse = i;
- repaintMenuItem (i);
+ setItemUnderMouse (i);
startTimer (200);
-
break;
}
}
@@ -67927,8 +68082,7 @@ public:
static Window* create (const PopupMenu& menu,
const bool dismissOnMouseUp,
Window* const owner_,
- const int minX, const int maxX,
- const int minY, const int maxY,
+ const Rectangle& target,
const int minimumWidth,
const int maximumNumColumns,
const int standardItemHeight,
@@ -67966,14 +68120,14 @@ public:
mw->componentAttachedTo = componentAttachedTo;
mw->componentAttachedToOriginal = componentAttachedTo;
- mw->calculateWindowPos (minX, maxX, minY, maxY, alignToRectangle);
+ mw->calculateWindowPos (target, alignToRectangle);
mw->setTopLeftPosition (mw->windowPos.getX(),
mw->windowPos.getY());
mw->updateYPositions();
if (itemIdThatMustBeVisible != 0)
{
- const int y = minY - mw->windowPos.getY();
+ const int y = target.getY() - mw->windowPos.getY();
mw->ensureItemIsVisible (itemIdThatMustBeVisible,
(((unsigned int) y) < (unsigned int) mw->windowPos.getHeight()) ? y : -1);
}
@@ -68414,13 +68568,10 @@ private:
return false;
}
- void calculateWindowPos (const int minX, const int maxX,
- const int minY, const int maxY,
- const bool alignToRectangle)
+ void calculateWindowPos (const Rectangle& target, const bool alignToRectangle)
{
const Rectangle mon (Desktop::getInstance()
- .getMonitorAreaContaining (Point ((minX + maxX) / 2,
- (minY + maxY) / 2),
+ .getMonitorAreaContaining (target.getCentre(),
#if JUCE_MAC
true));
#else
@@ -68432,19 +68583,19 @@ private:
if (alignToRectangle)
{
- x = minX;
+ x = target.getX();
- const int spaceUnder = mon.getHeight() - (maxY - mon.getY());
- const int spaceOver = minY - mon.getY();
+ const int spaceUnder = mon.getHeight() - (target.getBottom() - mon.getY());
+ const int spaceOver = target.getY() - mon.getY();
if (heightToUse < spaceUnder - 30 || spaceUnder >= spaceOver)
- y = maxY;
+ y = target.getBottom();
else
- y = minY - heightToUse;
+ y = target.getY() - heightToUse;
}
else
{
- bool tendTowardsRight = (minX + maxX) / 2 < mon.getCentreX();
+ bool tendTowardsRight = target.getCentreX() < mon.getCentreX();
if (owner != 0)
{
@@ -68453,38 +68604,38 @@ private:
const bool ownerGoingRight = (owner->getX() + owner->getWidth() / 2
> owner->owner->getX() + owner->owner->getWidth() / 2);
- if (ownerGoingRight && maxX + widthToUse < mon.getRight() - 4)
+ if (ownerGoingRight && target.getRight() + widthToUse < mon.getRight() - 4)
tendTowardsRight = true;
- else if ((! ownerGoingRight) && minX > widthToUse + 4)
+ else if ((! ownerGoingRight) && target.getX() > widthToUse + 4)
tendTowardsRight = false;
}
- else if (maxX + widthToUse < mon.getRight() - 32)
+ else if (target.getRight() + widthToUse < mon.getRight() - 32)
{
tendTowardsRight = true;
}
}
- const int biggestSpace = jmax (mon.getRight() - maxX,
- minX - mon.getX()) - 32;
+ const int biggestSpace = jmax (mon.getRight() - target.getRight(),
+ target.getX() - mon.getX()) - 32;
if (biggestSpace < widthToUse)
{
- layoutMenuItems (biggestSpace + (maxX - minX) / 3, widthToUse, heightToUse);
+ layoutMenuItems (biggestSpace + target.getWidth() / 3, widthToUse, heightToUse);
if (numColumns > 1)
layoutMenuItems (biggestSpace - 4, widthToUse, heightToUse);
- tendTowardsRight = (mon.getRight() - maxX) >= (minX - mon.getX());
+ tendTowardsRight = (mon.getRight() - target.getRight()) >= (target.getX() - mon.getX());
}
if (tendTowardsRight)
- x = jmin (mon.getRight() - widthToUse - 4, maxX);
+ x = jmin (mon.getRight() - widthToUse - 4, target.getRight());
else
- x = jmax (mon.getX() + 4, minX - widthToUse);
+ x = jmax (mon.getX() + 4, target.getX() - widthToUse);
- y = minY;
- if ((minY + maxY) / 2 > mon.getCentreY())
- y = jmax (mon.getY(), maxY - heightToUse);
+ y = target.getY();
+ if (target.getCentreY() > mon.getCentreY())
+ y = jmax (mon.getY(), target.getBottom() - heightToUse);
}
x = jmax (mon.getX() + 1, jmin (mon.getRight() - (widthToUse + 6), x));
@@ -68717,13 +68868,10 @@ private:
if (childComp->isValidComponent() && childComp->itemInfo.hasActiveSubMenu())
{
- const Point topLeft (childComp->relativePositionToGlobal (Point()));
- const Point bottomRight (childComp->relativePositionToGlobal (Point (childComp->getWidth(), childComp->getHeight())));
-
activeSubMenu = Window::create (*(childComp->itemInfo.subMenu),
dismissOnMouseUp,
this,
- topLeft.getX(), bottomRight.getX(), topLeft.getY(), bottomRight.getY(),
+ childComp->getScreenBounds(),
0, maximumNumColumns,
standardItemHeight,
false, 0, menuBarComponent,
@@ -69097,7 +69245,7 @@ void PopupMenu::addSectionHeader (const String& title)
addCustomItem (0X4734a34f, new HeaderItemComponent (title));
}
-Component* PopupMenu::createMenuComponent (const int x, const int y, const int w, const int h,
+Component* PopupMenu::createMenuComponent (const Rectangle& target,
const int itemIdThatMustBeVisible,
const int minimumWidth,
const int maximumNumColumns,
@@ -69107,20 +69255,10 @@ Component* PopupMenu::createMenuComponent (const int x, const int y, const int w
ApplicationCommandManager** managerOfChosenCommand,
Component* const componentAttachedTo)
{
- Window* const pw
- = Window::create (*this,
- ModifierKeys::getCurrentModifiers().isAnyMouseButtonDown(),
- 0,
- x, x + w,
- y, y + h,
- minimumWidth,
- maximumNumColumns,
- standardItemHeight,
- alignToRectangle,
- itemIdThatMustBeVisible,
- menuBarComponent,
- managerOfChosenCommand,
- componentAttachedTo);
+ Window* const pw = Window::create (*this, ModifierKeys::getCurrentModifiers().isAnyMouseButtonDown(),
+ 0, target, minimumWidth, maximumNumColumns, standardItemHeight,
+ alignToRectangle, itemIdThatMustBeVisible, menuBarComponent,
+ managerOfChosenCommand, componentAttachedTo);
if (pw != 0)
pw->setVisible (true);
@@ -69128,56 +69266,86 @@ Component* PopupMenu::createMenuComponent (const int x, const int y, const int w
return pw;
}
-int PopupMenu::showMenu (const int x, const int y, const int w, const int h,
+// This invokes any command manager commands and deletes the menu window when it is dismissed
+class PopupMenuCompletionCallback : public ModalComponentManager::Callback
+{
+public:
+ PopupMenuCompletionCallback()
+ : managerOfChosenCommand (0)
+ {
+ }
+
+ ~PopupMenuCompletionCallback() {}
+
+ void modalStateFinished (int result)
+ {
+ if (managerOfChosenCommand != 0 && result != 0)
+ {
+ ApplicationCommandTarget::InvocationInfo info (result);
+ info.invocationMethod = ApplicationCommandTarget::InvocationInfo::fromMenu;
+
+ managerOfChosenCommand->invoke (info, true);
+ }
+ }
+
+ ApplicationCommandManager* managerOfChosenCommand;
+ ScopedPointer component;
+
+private:
+ PopupMenuCompletionCallback (const PopupMenuCompletionCallback&);
+ PopupMenuCompletionCallback& operator= (const PopupMenuCompletionCallback&);
+};
+
+int PopupMenu::showMenu (const Rectangle& target,
const int itemIdThatMustBeVisible,
const int minimumWidth,
const int maximumNumColumns,
const int standardItemHeight,
const bool alignToRectangle,
- Component* const componentAttachedTo)
+ Component* const componentAttachedTo,
+ ModalComponentManager::Callback* userCallback)
{
+ ScopedPointer userCallbackDeleter (userCallback);
+
Component::SafePointer prevFocused (Component::getCurrentlyFocusedComponent());
Component::SafePointer prevTopLevel ((prevFocused != 0) ? prevFocused->getTopLevelComponent() : 0);
-
Window::wasHiddenBecauseOfAppChange() = false;
- int result = 0;
- ApplicationCommandManager* managerOfChosenCommand = 0;
+ PopupMenuCompletionCallback* callback = new PopupMenuCompletionCallback();
+ ScopedPointer callbackDeleter (callback);
- ScopedPointer popupComp (createMenuComponent (x, y, w, h,
- itemIdThatMustBeVisible,
- minimumWidth,
- maximumNumColumns > 0 ? maximumNumColumns : 7,
- standardItemHeight,
- alignToRectangle, 0,
- &managerOfChosenCommand,
- componentAttachedTo));
+ callback->component = createMenuComponent (target,
+ itemIdThatMustBeVisible,
+ minimumWidth,
+ maximumNumColumns > 0 ? maximumNumColumns : 7,
+ standardItemHeight,
+ alignToRectangle, 0,
+ &callback->managerOfChosenCommand,
+ componentAttachedTo);
- if (popupComp != 0)
+ if (callback->component == 0)
+ return 0;
+
+ callbackDeleter.release();
+
+ callback->component->enterModalState (false, userCallbackDeleter.release());
+ callback->component->toFront (false); // need to do this after making it modal, or it could
+ // be stuck behind other comps that are already modal..
+
+ ModalComponentManager::getInstance()->attachCallback (callback->component, callback);
+
+ if (userCallback != 0)
+ return 0;
+
+ const int result = callback->component->runModalLoop();
+
+ if (! Window::wasHiddenBecauseOfAppChange())
{
- popupComp->enterModalState (false);
- popupComp->toFront (false); // need to do this after making it modal, or it could
- // be stuck behind other comps that are already modal..
+ if (prevTopLevel != 0)
+ prevTopLevel->toFront (true);
- result = popupComp->runModalLoop();
- popupComp = 0;
-
- if (! Window::wasHiddenBecauseOfAppChange())
- {
- if (prevTopLevel != 0)
- prevTopLevel->toFront (true);
-
- if (prevFocused != 0)
- prevFocused->grabKeyboardFocus();
- }
- }
-
- if (managerOfChosenCommand != 0 && result != 0)
- {
- ApplicationCommandTarget::InvocationInfo info (result);
- info.invocationMethod = ApplicationCommandTarget::InvocationInfo::fromMenu;
-
- managerOfChosenCommand->invoke (info, true);
+ if (prevFocused != 0)
+ prevFocused->grabKeyboardFocus();
}
return result;
@@ -69186,7 +69354,8 @@ int PopupMenu::showMenu (const int x, const int y, const int w, const int h,
int PopupMenu::show (const int itemIdThatMustBeVisible,
const int minimumWidth,
const int maximumNumColumns,
- const int standardItemHeight)
+ const int standardItemHeight,
+ ModalComponentManager::Callback* callback)
{
const Point mousePos (Desktop::getMousePosition());
@@ -69194,7 +69363,8 @@ int PopupMenu::show (const int itemIdThatMustBeVisible,
itemIdThatMustBeVisible,
minimumWidth,
maximumNumColumns,
- standardItemHeight);
+ standardItemHeight,
+ callback);
}
int PopupMenu::showAt (const int screenX,
@@ -69202,39 +69372,39 @@ int PopupMenu::showAt (const int screenX,
const int itemIdThatMustBeVisible,
const int minimumWidth,
const int maximumNumColumns,
- const int standardItemHeight)
+ const int standardItemHeight,
+ ModalComponentManager::Callback* callback)
{
- return showMenu (screenX, screenY, 1, 1,
+ return showMenu (Rectangle (screenX, screenY, 1, 1),
itemIdThatMustBeVisible,
minimumWidth, maximumNumColumns,
standardItemHeight,
- false, 0);
+ false, 0, callback);
}
int PopupMenu::showAt (Component* componentToAttachTo,
const int itemIdThatMustBeVisible,
const int minimumWidth,
const int maximumNumColumns,
- const int standardItemHeight)
+ const int standardItemHeight,
+ ModalComponentManager::Callback* callback)
{
if (componentToAttachTo != 0)
{
- return showMenu (componentToAttachTo->getScreenX(),
- componentToAttachTo->getScreenY(),
- componentToAttachTo->getWidth(),
- componentToAttachTo->getHeight(),
+ return showMenu (componentToAttachTo->getScreenBounds(),
itemIdThatMustBeVisible,
minimumWidth,
maximumNumColumns,
standardItemHeight,
- true, componentToAttachTo);
+ true, componentToAttachTo, callback);
}
else
{
return show (itemIdThatMustBeVisible,
minimumWidth,
maximumNumColumns,
- standardItemHeight);
+ standardItemHeight,
+ callback);
}
}
@@ -89958,7 +90128,7 @@ void Path::addArrow (const Line& line, float lineThickness,
startNewSubPath (line.getPointAlongLine (0, lineThickness));
lineTo (line.getPointAlongLine (0, -lineThickness));
- lineTo (reversed.getPointAlongLine (0, lineThickness));
+ lineTo (reversed.getPointAlongLine (arrowheadLength, lineThickness));
lineTo (reversed.getPointAlongLine (arrowheadLength, arrowheadWidth));
lineTo (line.getEnd());
lineTo (reversed.getPointAlongLine (arrowheadLength, -arrowheadWidth));
diff --git a/juce_amalgamated.h b/juce_amalgamated.h
index c6130e404c..c3c6925611 100644
--- a/juce_amalgamated.h
+++ b/juce_amalgamated.h
@@ -64,7 +64,7 @@
*/
#define JUCE_MAJOR_VERSION 1
#define JUCE_MINOR_VERSION 52
-#define JUCE_BUILDNUMBER 19
+#define JUCE_BUILDNUMBER 20
/** Current Juce version number.
@@ -14254,7 +14254,7 @@ private:
static classname* _singletonInstance; \
static JUCE_NAMESPACE::CriticalSection _singletonLock; \
\
- static classname* getInstance() \
+ static classname* JUCE_CALLTYPE getInstance() \
{ \
if (_singletonInstance == 0) \
{\
@@ -14282,12 +14282,12 @@ private:
return _singletonInstance; \
} \
\
- static inline classname* getInstanceWithoutCreating() throw() \
+ static inline classname* JUCE_CALLTYPE getInstanceWithoutCreating() throw() \
{ \
return _singletonInstance; \
} \
\
- static void deleteInstance() \
+ static void JUCE_CALLTYPE deleteInstance() \
{ \
const JUCE_NAMESPACE::ScopedLock sl (_singletonLock); \
if (_singletonInstance != 0) \
@@ -24927,6 +24927,167 @@ private:
#endif // __JUCE_BORDERSIZE_JUCEHEADER__
/*** End of inlined file: juce_BorderSize.h ***/
+
+/*** Start of inlined file: juce_ModalComponentManager.h ***/
+#ifndef __JUCE_MODALCOMPONENTMANAGER_JUCEHEADER__
+#define __JUCE_MODALCOMPONENTMANAGER_JUCEHEADER__
+
+
+/*** Start of inlined file: juce_DeletedAtShutdown.h ***/
+#ifndef __JUCE_DELETEDATSHUTDOWN_JUCEHEADER__
+#define __JUCE_DELETEDATSHUTDOWN_JUCEHEADER__
+
+/**
+ Classes derived from this will be automatically deleted when the application exits.
+
+ After JUCEApplication::shutdown() has been called, any objects derived from
+ DeletedAtShutdown which are still in existence will be deleted in the reverse
+ order to that in which they were created.
+
+ So if you've got a singleton and don't want to have to explicitly delete it, just
+ inherit from this and it'll be taken care of.
+*/
+class JUCE_API DeletedAtShutdown
+{
+protected:
+ /** Creates a DeletedAtShutdown object. */
+ DeletedAtShutdown();
+
+ /** Destructor.
+
+ It's ok to delete these objects explicitly - it's only the ones left
+ dangling at the end that will be deleted automatically.
+ */
+ virtual ~DeletedAtShutdown();
+
+public:
+ /** Deletes all extant objects.
+
+ This shouldn't be used by applications, as it's called automatically
+ in the shutdown code of the JUCEApplication class.
+ */
+ static void deleteAll();
+
+private:
+ DeletedAtShutdown (const DeletedAtShutdown&);
+ DeletedAtShutdown& operator= (const DeletedAtShutdown&);
+
+ static CriticalSection& getLock();
+ static Array & getObjects();
+};
+
+#endif // __JUCE_DELETEDATSHUTDOWN_JUCEHEADER__
+/*** End of inlined file: juce_DeletedAtShutdown.h ***/
+
+/**
+ 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().
+ */
+ 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);
+
+ /** Runs the event loop until the currently topmost modal component is dismissed, and
+ returns the exit code for that component.
+ */
+ int runEventLoopForCurrentComponent();
+
+ 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 ;
+ OwnedArray stack;
+
+ void startModal (Component* component, Callback* callback);
+ void endModal (Component* component, int returnValue);
+ void endModal (Component* component);
+
+};
+
+#endif // __JUCE_MODALCOMPONENTMANAGER_JUCEHEADER__
+/*** End of inlined file: juce_ModalComponentManager.h ***/
+
class LookAndFeel;
class MouseInputSource;
class MouseInputSourceInternal;
@@ -26576,7 +26737,7 @@ public:
passed into exitModalState().
@see enterModalState, exitModalState, isCurrentlyModal, getCurrentlyModalComponent,
- isCurrentlyBlockedByAnotherModalComponent, MessageManager::dispatchNextMessage
+ isCurrentlyBlockedByAnotherModalComponent, ModalComponentManager
*/
int runModalLoop();
@@ -26590,9 +26751,15 @@ public:
get the focus, which is usually what you'll want it to do. If not, it will leave
the focus unchanged.
- @see exitModalState, runModalLoop
+ The callback is an optional object which will receive a callback when the modal
+ component loses its modal status, either by being hidden or when exitModalState()
+ is called. If you pass an object in here, the system will take care of deleting it
+ later, after making the callback
+
+ @see exitModalState, runModalLoop, ModalComponentManager::attachCallback
*/
- void enterModalState (bool takeKeyboardFocus = true);
+ void enterModalState (bool takeKeyboardFocus = true,
+ ModalComponentManager::Callback* callback = 0);
/** Ends a component's modal state.
@@ -27717,53 +27884,6 @@ public:
#define __JUCE_DESKTOP_JUCEHEADER__
-/*** Start of inlined file: juce_DeletedAtShutdown.h ***/
-#ifndef __JUCE_DELETEDATSHUTDOWN_JUCEHEADER__
-#define __JUCE_DELETEDATSHUTDOWN_JUCEHEADER__
-
-/**
- Classes derived from this will be automatically deleted when the application exits.
-
- After JUCEApplication::shutdown() has been called, any objects derived from
- DeletedAtShutdown which are still in existence will be deleted in the reverse
- order to that in which they were created.
-
- So if you've got a singleton and don't want to have to explicitly delete it, just
- inherit from this and it'll be taken care of.
-*/
-class JUCE_API DeletedAtShutdown
-{
-protected:
- /** Creates a DeletedAtShutdown object. */
- DeletedAtShutdown();
-
- /** Destructor.
-
- It's ok to delete these objects explicitly - it's only the ones left
- dangling at the end that will be deleted automatically.
- */
- virtual ~DeletedAtShutdown();
-
-public:
- /** Deletes all extant objects.
-
- This shouldn't be used by applications, as it's called automatically
- in the shutdown code of the JUCEApplication class.
- */
- static void deleteAll();
-
-private:
- DeletedAtShutdown (const DeletedAtShutdown&);
- DeletedAtShutdown& operator= (const DeletedAtShutdown&);
-
- static CriticalSection& getLock();
- static Array & getObjects();
-};
-
-#endif // __JUCE_DELETEDATSHUTDOWN_JUCEHEADER__
-/*** End of inlined file: juce_DeletedAtShutdown.h ***/
-
-
/*** Start of inlined file: juce_Timer.h ***/
#ifndef __JUCE_TIMER_JUCEHEADER__
#define __JUCE_TIMER_JUCEHEADER__
@@ -34964,12 +35084,19 @@ public:
in zero.
@param standardItemHeight if this is non-zero, it will be used as the standard
height for menu items (apart from custom items)
+ @param callback if this is non-zero, the menu will be launched asynchronously,
+ returning immediately, and the callback will receive a
+ call when the menu is either dismissed or has an item
+ selected. This object will be owned and deleted by the
+ system, so make sure that it works safely and that any
+ pointers that it uses are safely within scope.
@see showAt
*/
int show (int itemIdThatMustBeVisible = 0,
int minimumWidth = 0,
int maximumNumColumns = 0,
- int standardItemHeight = 0);
+ int standardItemHeight = 0,
+ ModalComponentManager::Callback* callback = 0);
/** Displays the menu at a specific location.
@@ -34987,7 +35114,8 @@ public:
int itemIdThatMustBeVisible = 0,
int minimumWidth = 0,
int maximumNumColumns = 0,
- int standardItemHeight = 0);
+ int standardItemHeight = 0,
+ ModalComponentManager::Callback* callback = 0);
/** Displays the menu as if it's attached to a component such as a button.
@@ -34999,7 +35127,8 @@ public:
int itemIdThatMustBeVisible = 0,
int minimumWidth = 0,
int maximumNumColumns = 0,
- int standardItemHeight = 0);
+ int standardItemHeight = 0,
+ ModalComponentManager::Callback* callback = 0);
/** Closes any menus that are currently open.
@@ -35098,6 +35227,7 @@ private:
friend class ItemComponent;
friend class Window;
friend class PopupMenuCustomComponent;
+ friend class MenuBarComponent;
friend class OwnedArray - ;
friend class ScopedPointer ;
@@ -35107,16 +35237,16 @@ private:
void addSeparatorIfPending();
- int showMenu (int x, int y, int w, int h,
+ int showMenu (const Rectangle& target,
int itemIdThatMustBeVisible,
int minimumWidth,
int maximumNumColumns,
int standardItemHeight,
bool alignToRectangle,
- Component* componentAttachedTo);
+ Component* componentAttachedTo,
+ ModalComponentManager::Callback* callback);
- friend class MenuBarComponent;
- Component* createMenuComponent (int x, int y, int w, int h,
+ Component* createMenuComponent (const Rectangle& target,
int itemIdThatMustBeVisible,
int minimumWidth,
int maximumNumColumns,
@@ -35660,10 +35790,6 @@ public:
/** @internal */
bool isTextInputActive() const;
- juce_UseDebuggingNewOperator
-
-protected:
-
/** This adds the items to the popup menu.
By default it adds the cut/copy/paste items, but you can override this if
@@ -35701,6 +35827,10 @@ protected:
*/
virtual void performPopupMenuAction (int menuItemID);
+ juce_UseDebuggingNewOperator
+
+protected:
+
/** Scrolls the minimum distance needed to get the caret into view. */
void scrollToMakeSureCursorIsVisible();
@@ -36462,6 +36592,9 @@ private:
bool isRealItem() const throw();
};
+ class Callback;
+ friend class Callback;
+
OwnedArray items;
Value currentId;
int lastCurrentId;
@@ -50012,7 +50145,16 @@ public:
Leave the width or height as 0 to use the default size
*/
- bool show (int width = 0,int height = 0);
+ 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);
/** A set of colour IDs to use to change the colour of various aspects of the box.
@@ -50628,6 +50770,9 @@ private:
#endif
#ifndef __JUCE_DESKTOP_JUCEHEADER__
+#endif
+#ifndef __JUCE_MODALCOMPONENTMANAGER_JUCEHEADER__
+
#endif
#ifndef __JUCE_KEYBOARDFOCUSTRAVERSER_JUCEHEADER__
@@ -51809,6 +51954,10 @@ public:
*/
void setModel (MenuBarModel* newModel);
+ /** Returns the current menu bar model being used.
+ */
+ MenuBarModel* getModel() const throw();
+
/** Pops up one of the menu items.
This lets you manually open one of the menus - it could be triggered by a
@@ -51833,8 +51982,6 @@ public:
/** @internal */
void mouseMove (const MouseEvent& e);
/** @internal */
- void inputAttemptWhenModal();
- /** @internal */
void handleCommandMessage (int commandId);
/** @internal */
bool keyPressed (const KeyPress& key);
@@ -51847,20 +51994,22 @@ public:
juce_UseDebuggingNewOperator
private:
+ class AsyncCallback;
+ friend class AsyncCallback;
MenuBarModel* model;
StringArray menuNames;
Array xPositions;
- int itemUnderMouse, currentPopupIndex, topLevelIndexClicked, indexToShowAgain;
+ int itemUnderMouse, currentPopupIndex, topLevelIndexClicked;
int lastMouseX, lastMouseY;
- bool inModalState;
- ScopedPointer currentPopup;
int getItemAt (int x, int y);
+ void setItemUnderMouse (int index);
+ void setOpenItem (int index);
void updateItemUnderMouse (int x, int y);
- void hideCurrentMenu();
void timerCallback();
void repaintMenuItem (int index);
+ void menuDismissed (int topLevelIndex, int itemId);
MenuBarComponent (const MenuBarComponent&);
MenuBarComponent& operator= (const MenuBarComponent&);
diff --git a/src/core/juce_Singleton.h b/src/core/juce_Singleton.h
index d2b52cbb7e..9c91b05197 100644
--- a/src/core/juce_Singleton.h
+++ b/src/core/juce_Singleton.h
@@ -93,7 +93,7 @@
static classname* _singletonInstance; \
static JUCE_NAMESPACE::CriticalSection _singletonLock; \
\
- static classname* getInstance() \
+ static classname* JUCE_CALLTYPE getInstance() \
{ \
if (_singletonInstance == 0) \
{\
@@ -121,12 +121,12 @@
return _singletonInstance; \
} \
\
- static inline classname* getInstanceWithoutCreating() throw() \
+ static inline classname* JUCE_CALLTYPE getInstanceWithoutCreating() throw() \
{ \
return _singletonInstance; \
} \
\
- static void deleteInstance() \
+ static void JUCE_CALLTYPE deleteInstance() \
{ \
const JUCE_NAMESPACE::ScopedLock sl (_singletonLock); \
if (_singletonInstance != 0) \
diff --git a/src/core/juce_StandardHeader.h b/src/core/juce_StandardHeader.h
index 179a924aba..3ef52e264a 100644
--- a/src/core/juce_StandardHeader.h
+++ b/src/core/juce_StandardHeader.h
@@ -33,7 +33,7 @@
*/
#define JUCE_MAJOR_VERSION 1
#define JUCE_MINOR_VERSION 52
-#define JUCE_BUILDNUMBER 19
+#define JUCE_BUILDNUMBER 20
/** Current Juce version number.
diff --git a/src/gui/components/controls/juce_ComboBox.cpp b/src/gui/components/controls/juce_ComboBox.cpp
index 98dad7bbb9..395b335896 100644
--- a/src/gui/components/controls/juce_ComboBox.cpp
+++ b/src/gui/components/controls/juce_ComboBox.cpp
@@ -525,15 +525,40 @@ void ComboBox::labelTextChanged (Label*)
//==============================================================================
+class ComboBox::Callback : public ModalComponentManager::Callback
+{
+public:
+ Callback (ComboBox* const box_)
+ : box (box_)
+ {
+ }
+
+ void modalStateFinished (int returnValue)
+ {
+ if (box != 0)
+ {
+ box->menuActive = false;
+
+ if (returnValue != 0)
+ box->setSelectedId (returnValue);
+ }
+ }
+
+private:
+ Component::SafePointer box;
+
+ Callback (const Callback&);
+ Callback& operator= (const Callback&);
+};
+
+
void ComboBox::showPopup()
{
if (! menuActive)
{
const int selectedId = getSelectedId();
- Component::SafePointer deletionWatcher (this);
PopupMenu menu;
-
menu.setLookAndFeel (&getLookAndFeel());
for (int i = 0; i < items.size(); ++i)
@@ -552,19 +577,9 @@ void ComboBox::showPopup()
if (items.size() == 0)
menu.addItem (1, noChoicesMessage, false);
- const int itemHeight = jlimit (12, 24, getHeight());
-
menuActive = true;
- const int resultId = menu.showAt (this, selectedId,
- getWidth(), 1, itemHeight);
-
- if (deletionWatcher == 0)
- return;
-
- menuActive = false;
-
- if (resultId != 0)
- setSelectedId (resultId);
+ menu.showAt (this, selectedId, getWidth(), 1, jlimit (12, 24, getHeight()),
+ new Callback (this));
}
}
diff --git a/src/gui/components/controls/juce_ComboBox.h b/src/gui/components/controls/juce_ComboBox.h
index 4841f252b4..dc2bc25a4a 100644
--- a/src/gui/components/controls/juce_ComboBox.h
+++ b/src/gui/components/controls/juce_ComboBox.h
@@ -388,6 +388,9 @@ private:
bool isRealItem() const throw();
};
+ class Callback;
+ friend class Callback;
+
OwnedArray items;
Value currentId;
int lastCurrentId;
diff --git a/src/gui/components/controls/juce_TextEditor.cpp b/src/gui/components/controls/juce_TextEditor.cpp
index 8a12261cfe..ec4b9cd58a 100644
--- a/src/gui/components/controls/juce_TextEditor.cpp
+++ b/src/gui/components/controls/juce_TextEditor.cpp
@@ -1773,6 +1773,28 @@ void TextEditor::paintOverChildren (Graphics& g)
}
//==============================================================================
+class TextEditorMenuPerformer : public ModalComponentManager::Callback
+{
+public:
+ TextEditorMenuPerformer (TextEditor* const editor_)
+ : editor (editor_)
+ {
+ }
+
+ void modalStateFinished (int returnValue)
+ {
+ if (editor != 0 && returnValue != 0)
+ editor->performPopupMenuAction (returnValue);
+ }
+
+private:
+ Component::SafePointer editor;
+
+ TextEditorMenuPerformer (const TextEditorMenuPerformer&);
+ TextEditorMenuPerformer& operator= (const TextEditorMenuPerformer&);
+};
+
+
void TextEditor::mouseDown (const MouseEvent& e)
{
beginDragAutoRepeat (100);
@@ -1791,12 +1813,7 @@ void TextEditor::mouseDown (const MouseEvent& e)
m.setLookAndFeel (&getLookAndFeel());
addPopupMenuItems (m, &e);
- menuActive = true;
- const int result = m.show();
- menuActive = false;
-
- if (result != 0)
- performPopupMenuAction (result);
+ m.show (0, 0, 0, 0, new TextEditorMenuPerformer (this));
}
}
}
@@ -2221,7 +2238,7 @@ void TextEditor::enablementChanged()
//==============================================================================
UndoManager* TextEditor::getUndoManager() throw()
{
- return isReadOnly() ? &undoManager : 0;
+ return isReadOnly() ? 0 : &undoManager;
}
void TextEditor::clearInternal (UndoManager* const um)
diff --git a/src/gui/components/controls/juce_TextEditor.h b/src/gui/components/controls/juce_TextEditor.h
index 6ea365b90f..031e582c85 100644
--- a/src/gui/components/controls/juce_TextEditor.h
+++ b/src/gui/components/controls/juce_TextEditor.h
@@ -538,9 +538,6 @@ public:
/** @internal */
bool isTextInputActive() const;
- juce_UseDebuggingNewOperator
-
-protected:
//==============================================================================
/** This adds the items to the popup menu.
@@ -579,6 +576,10 @@ protected:
*/
virtual void performPopupMenuAction (int menuItemID);
+ //==============================================================================
+ juce_UseDebuggingNewOperator
+
+protected:
//==============================================================================
/** Scrolls the minimum distance needed to get the caret into view. */
void scrollToMakeSureCursorIsVisible();
diff --git a/src/gui/components/filebrowser/juce_FileChooserDialogBox.cpp b/src/gui/components/filebrowser/juce_FileChooserDialogBox.cpp
index 6f28a16f73..b83ea71659 100644
--- a/src/gui/components/filebrowser/juce_FileChooserDialogBox.cpp
+++ b/src/gui/components/filebrowser/juce_FileChooserDialogBox.cpp
@@ -75,6 +75,11 @@ FileChooserDialogBox::~FileChooserDialogBox()
//==============================================================================
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)
{
@@ -88,7 +93,10 @@ bool FileChooserDialogBox::show (int w, int h)
if (h <= 0)
h = 500;
- centreWithSize (w, h);
+ if (x < 0 || y < 0)
+ centreWithSize (w, h);
+ else
+ setBounds (x, y, w, h);
const bool ok = (runModalLoop() != 0);
setVisible (false);
diff --git a/src/gui/components/filebrowser/juce_FileChooserDialogBox.h b/src/gui/components/filebrowser/juce_FileChooserDialogBox.h
index b24ba49853..3baf0de6f8 100644
--- a/src/gui/components/filebrowser/juce_FileChooserDialogBox.h
+++ b/src/gui/components/filebrowser/juce_FileChooserDialogBox.h
@@ -103,8 +103,16 @@ public:
Leave the width or height as 0 to use the default size
*/
- bool show (int width = 0,int height = 0);
+ 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);
//==============================================================================
/** A set of colour IDs to use to change the colour of various aspects of the box.
diff --git a/src/gui/components/juce_Component.cpp b/src/gui/components/juce_Component.cpp
index 7ae6448d49..cc12be2fc7 100644
--- a/src/gui/components/juce_Component.cpp
+++ b/src/gui/components/juce_Component.cpp
@@ -39,15 +39,12 @@ BEGIN_JUCE_NAMESPACE
#include "../../events/juce_MessageManager.h"
#include "../../events/juce_Timer.h"
#include "../../core/juce_Time.h"
-#include "../../core/juce_Singleton.h"
#include "../../core/juce_PlatformUtilities.h"
#include "mouse/juce_MouseInputSource.h"
-//==============================================================================
-Component* Component::currentlyFocusedComponent = 0;
-static Array modalComponentStack, modalComponentReturnValueKeys;
-static Array modalReturnValues;
+//==============================================================================
+#define checkMessageManagerIsLocked jassert (MessageManager::getInstance()->currentThreadHasLockedMessageManager());
enum ComponentMessageNumbers
{
@@ -55,10 +52,8 @@ enum ComponentMessageNumbers
exitModalStateMessage = 0x7fff0002
};
-//==============================================================================
-#define checkMessageManagerIsLocked jassert (MessageManager::getInstance()->currentThreadHasLockedMessageManager());
-
static uint32 nextComponentUID = 0;
+Component* Component::currentlyFocusedComponent = 0;
//==============================================================================
@@ -106,8 +101,6 @@ Component::~Component()
if (flags.hasHeavyweightPeerFlag)
removeFromDesktop();
- modalComponentStack.removeValue (this);
-
for (int i = childComponentList_.size(); --i >= 0;)
childComponentList_.getUnchecked(i)->parentComponent_ = 0;
@@ -1330,59 +1323,17 @@ int Component::runModalLoop()
if (! MessageManager::getInstance()->isThisTheMessageThread())
{
// use a callback so this can be called from non-gui threads
- return (int) (pointer_sized_int)
- MessageManager::getInstance()
- ->callFunctionOnMessageThread (&runModalLoopCallback, this);
+ return (int) (pointer_sized_int) MessageManager::getInstance()
+ ->callFunctionOnMessageThread (&runModalLoopCallback, this);
}
- SafePointer prevFocused (getCurrentlyFocusedComponent());
-
if (! isCurrentlyModal())
- enterModalState();
+ enterModalState (true);
- JUCE_TRY
- {
- while (flags.currentlyModalFlag && flags.visibleFlag)
- {
- if (! MessageManager::getInstance()->runDispatchLoopUntil (20))
- break;
-
- // check whether this component was deleted during the last message
- if (! isValidMessageListener())
- break;
- }
- }
-#if JUCE_CATCH_UNHANDLED_EXCEPTIONS
- catch (const std::exception& e)
- {
- JUCEApplication::sendUnhandledException (&e, __FILE__, __LINE__);
- return 0;
- }
- catch (...)
- {
- JUCEApplication::sendUnhandledException (0, __FILE__, __LINE__);
- return 0;
- }
-#endif
-
- const int modalIndex = modalComponentReturnValueKeys.indexOf (this);
- int returnValue = 0;
-
- if (modalIndex >= 0)
- {
- modalComponentReturnValueKeys.remove (modalIndex);
- returnValue = modalReturnValues.remove (modalIndex);
- }
-
- modalComponentStack.removeValue (this);
-
- if (prevFocused != 0)
- prevFocused->grabKeyboardFocus();
-
- return returnValue;
+ return ModalComponentManager::getInstance()->runEventLoopForCurrentComponent();
}
-void Component::enterModalState (const bool takeKeyboardFocus_)
+void Component::enterModalState (const bool takeKeyboardFocus_, ModalComponentManager::Callback* const callback)
{
// if component methods are being called from threads other than the message
// thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
@@ -1394,10 +1345,7 @@ void Component::enterModalState (const bool takeKeyboardFocus_)
if (! isCurrentlyModal())
{
- modalComponentStack.add (this);
- modalComponentReturnValueKeys.add (this);
- modalReturnValues.add (0);
-
+ ModalComponentManager::getInstance()->startModal (this, callback);
flags.currentlyModalFlag = true;
setVisible (true);
@@ -1412,20 +1360,7 @@ void Component::exitModalState (const int returnValue)
{
if (MessageManager::getInstance()->isThisTheMessageThread())
{
- const int modalIndex = modalComponentReturnValueKeys.indexOf (this);
-
- if (modalIndex >= 0)
- {
- modalReturnValues.set (modalIndex, returnValue);
- }
- else
- {
- modalComponentReturnValueKeys.add (this);
- modalReturnValues.add (returnValue);
- }
-
- modalComponentStack.removeValue (this);
-
+ ModalComponentManager::getInstance()->endModal (this, returnValue);
flags.currentlyModalFlag = false;
bringModalComponentToFront();
@@ -1455,14 +1390,12 @@ bool Component::isCurrentlyBlockedByAnotherModalComponent() const
int JUCE_CALLTYPE Component::getNumCurrentlyModalComponents() throw()
{
- return modalComponentStack.size();
+ return ModalComponentManager::getInstance()->getNumModalComponents();
}
Component* JUCE_CALLTYPE Component::getCurrentlyModalComponent (int index) throw()
{
- Component* const c = static_cast (modalComponentStack [modalComponentStack.size() - index - 1]);
-
- return c->isValidComponent() ? c : 0;
+ return ModalComponentManager::getInstance()->getModalComponent (index);
}
void Component::bringModalComponentToFront()
diff --git a/src/gui/components/juce_Component.h b/src/gui/components/juce_Component.h
index 3485d85a20..7ef57b1d82 100644
--- a/src/gui/components/juce_Component.h
+++ b/src/gui/components/juce_Component.h
@@ -41,6 +41,8 @@
#include "../../text/juce_StringArray.h"
#include "../../containers/juce_Array.h"
#include "../../containers/juce_NamedValueSet.h"
+#include "juce_ModalComponentManager.h"
+
class LookAndFeel;
class MouseInputSource;
class MouseInputSourceInternal;
@@ -1728,7 +1730,7 @@ public:
passed into exitModalState().
@see enterModalState, exitModalState, isCurrentlyModal, getCurrentlyModalComponent,
- isCurrentlyBlockedByAnotherModalComponent, MessageManager::dispatchNextMessage
+ isCurrentlyBlockedByAnotherModalComponent, ModalComponentManager
*/
int runModalLoop();
@@ -1742,9 +1744,15 @@ public:
get the focus, which is usually what you'll want it to do. If not, it will leave
the focus unchanged.
- @see exitModalState, runModalLoop
+ The callback is an optional object which will receive a callback when the modal
+ component loses its modal status, either by being hidden or when exitModalState()
+ is called. If you pass an object in here, the system will take care of deleting it
+ later, after making the callback
+
+ @see exitModalState, runModalLoop, ModalComponentManager::attachCallback
*/
- void enterModalState (bool takeKeyboardFocus = true);
+ void enterModalState (bool takeKeyboardFocus = true,
+ ModalComponentManager::Callback* callback = 0);
/** Ends a component's modal state.
diff --git a/src/gui/components/juce_ModalComponentManager.cpp b/src/gui/components/juce_ModalComponentManager.cpp
new file mode 100644
index 0000000000..ce525f9373
--- /dev/null
+++ b/src/gui/components/juce_ModalComponentManager.cpp
@@ -0,0 +1,267 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library - "Jules' Utility Class Extensions"
+ Copyright 2004-10 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.
+
+ ==============================================================================
+*/
+
+#include "../../core/juce_StandardHeader.h"
+
+BEGIN_JUCE_NAMESPACE
+
+#include "juce_Component.h"
+#include "juce_ModalComponentManager.h"
+#include "../../events/juce_MessageManager.h"
+#include "../../application/juce_Application.h"
+
+
+//==============================================================================
+class ModalComponentManager::ModalItem : public ComponentListener
+{
+public:
+ ModalItem (Component* const comp, Callback* const callback)
+ : component (comp), returnValue (0), isActive (true), isDeleted (false)
+ {
+ if (callback != 0)
+ callbacks.add (callback);
+
+ jassert (comp != 0);
+ component->addComponentListener (this);
+ }
+
+ ~ModalItem()
+ {
+ if (! isDeleted)
+ component->removeComponentListener (this);
+ }
+
+ void componentBeingDeleted (Component&)
+ {
+ isDeleted = true;
+ cancel();
+ }
+
+ void componentVisibilityChanged (Component&)
+ {
+ if (! component->isShowing())
+ cancel();
+ }
+
+ void componentParentHierarchyChanged (Component&)
+ {
+ if (! component->isShowing())
+ cancel();
+ }
+
+ void cancel()
+ {
+ if (isActive)
+ {
+ isActive = false;
+ ModalComponentManager::getInstance()->triggerAsyncUpdate();
+ }
+ }
+
+ Component* component;
+ OwnedArray callbacks;
+ int returnValue;
+ bool isActive, isDeleted;
+
+private:
+ ModalItem (const ModalItem&);
+ ModalItem& operator= (const ModalItem&);
+};
+
+//==============================================================================
+ModalComponentManager::ModalComponentManager()
+{
+}
+
+ModalComponentManager::~ModalComponentManager()
+{
+ clearSingletonInstance();
+}
+
+juce_ImplementSingleton_SingleThreaded (ModalComponentManager);
+
+
+//==============================================================================
+void ModalComponentManager::startModal (Component* component, Callback* callback)
+{
+ if (component != 0)
+ stack.add (new ModalItem (component, callback));
+}
+
+void ModalComponentManager::attachCallback (Component* component, Callback* callback)
+{
+ if (callback != 0)
+ {
+ ScopedPointer 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 0;
+}
+
+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)
+ {
+ for (int j = item->callbacks.size(); --j >= 0;)
+ item->callbacks.getUnchecked(j)->modalStateFinished (item->returnValue);
+
+ stack.remove (i);
+ }
+ }
+}
+
+class ModalComponentManager::ReturnValueRetriever : public ModalComponentManager::Callback
+{
+public:
+ ReturnValueRetriever (int& value_, bool& finished_) : value (value_), finished (finished_) {}
+ ~ReturnValueRetriever() {}
+
+ void modalStateFinished (int returnValue)
+ {
+ finished = true;
+ value = returnValue;
+ }
+
+private:
+ int& value;
+ bool& finished;
+
+ ReturnValueRetriever (const ReturnValueRetriever&);
+ ReturnValueRetriever& operator= (const ReturnValueRetriever&);
+};
+
+int ModalComponentManager::runEventLoopForCurrentComponent()
+{
+ // This can only be run from the message thread!
+ jassert (MessageManager::getInstance()->isThisTheMessageThread());
+
+ Component* currentlyModal = getModalComponent (0);
+
+ if (currentlyModal == 0)
+ return 0;
+
+ Component::SafePointer 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 != 0)
+ prevFocused->grabKeyboardFocus();
+
+ return returnValue;
+}
+
+
+END_JUCE_NAMESPACE
diff --git a/src/gui/components/juce_ModalComponentManager.h b/src/gui/components/juce_ModalComponentManager.h
new file mode 100644
index 0000000000..8baf256d9a
--- /dev/null
+++ b/src/gui/components/juce_ModalComponentManager.h
@@ -0,0 +1,144 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library - "Jules' Utility Class Extensions"
+ Copyright 2004-10 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__
+
+#include "../../core/juce_Singleton.h"
+#include "../../events/juce_AsyncUpdater.h"
+#include "../../utilities/juce_DeletedAtShutdown.h"
+
+
+//==============================================================================
+/**
+ 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().
+ */
+ 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);
+
+ /** Runs the event loop until the currently topmost modal component is dismissed, and
+ returns the exit code for that component.
+ */
+ int runEventLoopForCurrentComponent();
+
+ //==============================================================================
+ 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 ;
+ OwnedArray stack;
+
+ void startModal (Component* component, Callback* callback);
+ void endModal (Component* component, int returnValue);
+ void endModal (Component* component);
+
+};
+
+
+#endif // __JUCE_MODALCOMPONENTMANAGER_JUCEHEADER__
diff --git a/src/gui/components/menus/juce_MenuBarComponent.cpp b/src/gui/components/menus/juce_MenuBarComponent.cpp
index 2604157c72..f04e9754d1 100644
--- a/src/gui/components/menus/juce_MenuBarComponent.cpp
+++ b/src/gui/components/menus/juce_MenuBarComponent.cpp
@@ -32,31 +32,14 @@ BEGIN_JUCE_NAMESPACE
#include "../lookandfeel/juce_LookAndFeel.h"
-//==============================================================================
-class DummyMenuComponent : public Component
-{
- DummyMenuComponent (const DummyMenuComponent&);
- DummyMenuComponent& operator= (const DummyMenuComponent&);
-
-public:
- DummyMenuComponent() {}
- ~DummyMenuComponent() {}
-
- void inputAttemptWhenModal()
- {
- exitModalState (0);
- }
-};
-
//==============================================================================
MenuBarComponent::MenuBarComponent (MenuBarModel* model_)
: model (0),
itemUnderMouse (-1),
currentPopupIndex (-1),
- indexToShowAgain (-1),
+ topLevelIndexClicked (0),
lastMouseX (0),
- lastMouseY (0),
- inModalState (false)
+ lastMouseY (0)
{
setRepaintsOnMouseActivity (true);
setWantsKeyboardFocus (false);
@@ -68,9 +51,12 @@ MenuBarComponent::MenuBarComponent (MenuBarModel* model_)
MenuBarComponent::~MenuBarComponent()
{
setModel (0);
-
Desktop::getInstance().removeGlobalMouseListener (this);
- currentPopup = 0;
+}
+
+MenuBarModel* MenuBarComponent::getModel() const throw()
+{
+ return model;
}
void MenuBarComponent::setModel (MenuBarModel* const newModel)
@@ -158,144 +144,102 @@ void MenuBarComponent::repaintMenuItem (int index)
}
}
-void MenuBarComponent::updateItemUnderMouse (int x, int y)
+void MenuBarComponent::setItemUnderMouse (const int index)
{
- const int newItem = getItemAt (x, y);
-
- if (itemUnderMouse != newItem)
+ if (itemUnderMouse != index)
{
repaintMenuItem (itemUnderMouse);
- itemUnderMouse = newItem;
+ itemUnderMouse = index;
repaintMenuItem (itemUnderMouse);
}
}
-void MenuBarComponent::hideCurrentMenu()
+void MenuBarComponent::setOpenItem (int index)
{
- currentPopup = 0;
- repaint();
+ if (currentPopupIndex != index)
+ {
+ repaintMenuItem (currentPopupIndex);
+ currentPopupIndex = index;
+ repaintMenuItem (currentPopupIndex);
+
+ if (index >= 0)
+ Desktop::getInstance().addGlobalMouseListener (this);
+ else
+ Desktop::getInstance().removeGlobalMouseListener (this);
+ }
}
+void MenuBarComponent::updateItemUnderMouse (int x, int y)
+{
+ setItemUnderMouse (getItemAt (x, y));
+}
+
+class MenuBarComponent::AsyncCallback : public ModalComponentManager::Callback
+{
+public:
+ AsyncCallback (MenuBarComponent* const bar_, const int topLevelIndex_)
+ : bar (bar_), topLevelIndex (topLevelIndex_)
+ {
+ }
+
+ ~AsyncCallback() {}
+
+ void modalStateFinished (int returnValue)
+ {
+ if (bar != 0)
+ bar->menuDismissed (topLevelIndex, returnValue);
+ }
+
+private:
+ Component::SafePointer bar;
+ const int topLevelIndex;
+
+ AsyncCallback (const AsyncCallback&);
+ AsyncCallback& operator= (const AsyncCallback&);
+};
+
void MenuBarComponent::showMenu (int index)
{
if (index != currentPopupIndex)
{
- if (inModalState)
- {
- hideCurrentMenu();
- indexToShowAgain = index;
- return;
- }
-
- indexToShowAgain = -1;
- currentPopupIndex = -1;
- itemUnderMouse = index;
- currentPopup = 0;
+ PopupMenu::dismissAllActiveMenus();
menuBarItemsChanged (0);
- Component::SafePointer prevFocused (getCurrentlyFocusedComponent());
- Component::SafePointer deletionChecker (this);
+ setOpenItem (index);
+ setItemUnderMouse (index);
- enterModalState (false);
- inModalState = true;
- int result = 0;
- ApplicationCommandManager* managerOfChosenCommand = 0;
-
- Desktop::getInstance().addGlobalMouseListener (this);
-
- for (;;)
+ if (index >= 0)
{
- const int x = getScreenX() + xPositions [itemUnderMouse];
- const int w = xPositions [itemUnderMouse + 1] - xPositions [itemUnderMouse];
+ PopupMenu m (model->getMenuForIndex (itemUnderMouse,
+ menuNames [itemUnderMouse]));
- currentPopupIndex = itemUnderMouse;
- indexToShowAgain = -1;
- repaint();
+ if (m.lookAndFeel == 0)
+ m.setLookAndFeel (&getLookAndFeel());
- if (((unsigned int) itemUnderMouse) < (unsigned int) menuNames.size())
- {
- PopupMenu m (model->getMenuForIndex (itemUnderMouse,
- menuNames [itemUnderMouse]));
+ const Rectangle itemPos (xPositions [index], 0, xPositions [index + 1] - xPositions [index], getHeight());
- if (m.lookAndFeel == 0)
- m.setLookAndFeel (&getLookAndFeel());
-
- currentPopup = m.createMenuComponent (x, getScreenY(),
- w, getHeight(),
- 0, w, 0, 0,
- true, this,
- &managerOfChosenCommand,
- this);
- }
-
- if (currentPopup == 0)
- {
- currentPopup = new DummyMenuComponent();
- addAndMakeVisible (currentPopup);
- }
-
- currentPopup->enterModalState (false);
- currentPopup->toFront (false); // need to do this after making it modal, or it could
- // be stuck behind other comps that are already modal..
- result = currentPopup->runModalLoop();
-
- if (deletionChecker == 0)
- return;
-
- const int lastPopupIndex = currentPopupIndex;
- currentPopup = 0;
- currentPopupIndex = -1;
-
- if (result != 0)
- {
- topLevelIndexClicked = lastPopupIndex;
- break;
- }
- else if (indexToShowAgain >= 0)
- {
- menuBarItemsChanged (0);
- repaint();
- itemUnderMouse = indexToShowAgain;
-
- if (((unsigned int) itemUnderMouse) >= (unsigned int) menuNames.size())
- break;
- }
- else
- {
- break;
- }
- }
-
- Desktop::getInstance().removeGlobalMouseListener (this);
-
- inModalState = false;
- exitModalState (0);
-
- if (prevFocused != 0)
- prevFocused->grabKeyboardFocus();
-
- const Point mousePos (getMouseXYRelative());
- updateItemUnderMouse (mousePos.getX(), mousePos.getY());
- repaint();
-
- if (result != 0)
- {
- if (managerOfChosenCommand != 0)
- {
- ApplicationCommandTarget::InvocationInfo info (result);
- info.invocationMethod = ApplicationCommandTarget::InvocationInfo::fromMenu;
-
- managerOfChosenCommand->invoke (info, true);
- }
-
- postCommandMessage (result);
+ m.showMenu (itemPos + getScreenPosition(),
+ 0, itemPos.getWidth(), 0, 0, true, this,
+ new AsyncCallback (this, index));
}
}
}
+void MenuBarComponent::menuDismissed (int topLevelIndex, int itemId)
+{
+ topLevelIndexClicked = topLevelIndex;
+ postCommandMessage (itemId);
+}
+
void MenuBarComponent::handleCommandMessage (int commandId)
{
- if (model != 0)
+ const Point mousePos (getMouseXYRelative());
+ updateItemUnderMouse (mousePos.getX(), mousePos.getY());
+
+ if (! isCurrentlyBlockedByAnotherModalComponent())
+ setOpenItem (-1);
+
+ if (commandId != 0 && model != 0)
model->menuItemSelected (commandId, topLevelIndexClicked);
}
@@ -327,7 +271,6 @@ void MenuBarComponent::mouseDown (const MouseEvent& e)
void MenuBarComponent::mouseDrag (const MouseEvent& e)
{
const MouseEvent e2 (e.getEventRelativeTo (this));
-
const int item = getItemAt (e2.x, e2.y);
if (item >= 0)
@@ -340,8 +283,11 @@ void MenuBarComponent::mouseUp (const MouseEvent& e)
updateItemUnderMouse (e2.x, e2.y);
- if (itemUnderMouse < 0 && dynamic_cast (static_cast (currentPopup)) != 0)
- hideCurrentMenu();
+ if (itemUnderMouse < 0 && getLocalBounds().contains (e2.x, e2.y))
+ {
+ setOpenItem (-1);
+ PopupMenu::dismissAllActiveMenus();
+ }
}
void MenuBarComponent::mouseMove (const MouseEvent& e)
@@ -387,11 +333,6 @@ bool MenuBarComponent::keyPressed (const KeyPress& key)
return used;
}
-void MenuBarComponent::inputAttemptWhenModal()
-{
- hideCurrentMenu();
-}
-
void MenuBarComponent::menuBarItemsChanged (MenuBarModel* /*menuBarModel*/)
{
StringArray newNames;
@@ -410,8 +351,7 @@ void MenuBarComponent::menuBarItemsChanged (MenuBarModel* /*menuBarModel*/)
void MenuBarComponent::menuCommandInvoked (MenuBarModel* /*menuBarModel*/,
const ApplicationCommandTarget::InvocationInfo& info)
{
- if (model == 0
- || (info.commandFlags & ApplicationCommandInfo::dontTriggerVisualFeedback) != 0)
+ if (model == 0 || (info.commandFlags & ApplicationCommandInfo::dontTriggerVisualFeedback) != 0)
return;
for (int i = 0; i < menuNames.size(); ++i)
@@ -420,10 +360,8 @@ void MenuBarComponent::menuCommandInvoked (MenuBarModel* /*menuBarModel*/,
if (menu.containsCommandItem (info.commandID))
{
- itemUnderMouse = i;
- repaintMenuItem (i);
+ setItemUnderMouse (i);
startTimer (200);
-
break;
}
}
diff --git a/src/gui/components/menus/juce_MenuBarComponent.h b/src/gui/components/menus/juce_MenuBarComponent.h
index 95d5ca61d6..0bc8857efe 100644
--- a/src/gui/components/menus/juce_MenuBarComponent.h
+++ b/src/gui/components/menus/juce_MenuBarComponent.h
@@ -60,6 +60,10 @@ public:
*/
void setModel (MenuBarModel* newModel);
+ /** Returns the current menu bar model being used.
+ */
+ MenuBarModel* getModel() const throw();
+
//==============================================================================
/** Pops up one of the menu items.
@@ -86,8 +90,6 @@ public:
/** @internal */
void mouseMove (const MouseEvent& e);
/** @internal */
- void inputAttemptWhenModal();
- /** @internal */
void handleCommandMessage (int commandId);
/** @internal */
bool keyPressed (const KeyPress& key);
@@ -102,20 +104,22 @@ public:
juce_UseDebuggingNewOperator
private:
+ class AsyncCallback;
+ friend class AsyncCallback;
MenuBarModel* model;
StringArray menuNames;
Array xPositions;
- int itemUnderMouse, currentPopupIndex, topLevelIndexClicked, indexToShowAgain;
+ int itemUnderMouse, currentPopupIndex, topLevelIndexClicked;
int lastMouseX, lastMouseY;
- bool inModalState;
- ScopedPointer currentPopup;
int getItemAt (int x, int y);
+ void setItemUnderMouse (int index);
+ void setOpenItem (int index);
void updateItemUnderMouse (int x, int y);
- void hideCurrentMenu();
void timerCallback();
void repaintMenuItem (int index);
+ void menuDismissed (int topLevelIndex, int itemId);
MenuBarComponent (const MenuBarComponent&);
MenuBarComponent& operator= (const MenuBarComponent&);
diff --git a/src/gui/components/menus/juce_PopupMenu.cpp b/src/gui/components/menus/juce_PopupMenu.cpp
index adc10b98b6..962824e116 100644
--- a/src/gui/components/menus/juce_PopupMenu.cpp
+++ b/src/gui/components/menus/juce_PopupMenu.cpp
@@ -309,8 +309,7 @@ public:
static Window* create (const PopupMenu& menu,
const bool dismissOnMouseUp,
Window* const owner_,
- const int minX, const int maxX,
- const int minY, const int maxY,
+ const Rectangle& target,
const int minimumWidth,
const int maximumNumColumns,
const int standardItemHeight,
@@ -348,14 +347,14 @@ public:
mw->componentAttachedTo = componentAttachedTo;
mw->componentAttachedToOriginal = componentAttachedTo;
- mw->calculateWindowPos (minX, maxX, minY, maxY, alignToRectangle);
+ mw->calculateWindowPos (target, alignToRectangle);
mw->setTopLeftPosition (mw->windowPos.getX(),
mw->windowPos.getY());
mw->updateYPositions();
if (itemIdThatMustBeVisible != 0)
{
- const int y = minY - mw->windowPos.getY();
+ const int y = target.getY() - mw->windowPos.getY();
mw->ensureItemIsVisible (itemIdThatMustBeVisible,
(((unsigned int) y) < (unsigned int) mw->windowPos.getHeight()) ? y : -1);
}
@@ -804,13 +803,10 @@ private:
}
//==============================================================================
- void calculateWindowPos (const int minX, const int maxX,
- const int minY, const int maxY,
- const bool alignToRectangle)
+ void calculateWindowPos (const Rectangle& target, const bool alignToRectangle)
{
const Rectangle mon (Desktop::getInstance()
- .getMonitorAreaContaining (Point ((minX + maxX) / 2,
- (minY + maxY) / 2),
+ .getMonitorAreaContaining (target.getCentre(),
#if JUCE_MAC
true));
#else
@@ -822,19 +818,19 @@ private:
if (alignToRectangle)
{
- x = minX;
+ x = target.getX();
- const int spaceUnder = mon.getHeight() - (maxY - mon.getY());
- const int spaceOver = minY - mon.getY();
+ const int spaceUnder = mon.getHeight() - (target.getBottom() - mon.getY());
+ const int spaceOver = target.getY() - mon.getY();
if (heightToUse < spaceUnder - 30 || spaceUnder >= spaceOver)
- y = maxY;
+ y = target.getBottom();
else
- y = minY - heightToUse;
+ y = target.getY() - heightToUse;
}
else
{
- bool tendTowardsRight = (minX + maxX) / 2 < mon.getCentreX();
+ bool tendTowardsRight = target.getCentreX() < mon.getCentreX();
if (owner != 0)
{
@@ -843,38 +839,38 @@ private:
const bool ownerGoingRight = (owner->getX() + owner->getWidth() / 2
> owner->owner->getX() + owner->owner->getWidth() / 2);
- if (ownerGoingRight && maxX + widthToUse < mon.getRight() - 4)
+ if (ownerGoingRight && target.getRight() + widthToUse < mon.getRight() - 4)
tendTowardsRight = true;
- else if ((! ownerGoingRight) && minX > widthToUse + 4)
+ else if ((! ownerGoingRight) && target.getX() > widthToUse + 4)
tendTowardsRight = false;
}
- else if (maxX + widthToUse < mon.getRight() - 32)
+ else if (target.getRight() + widthToUse < mon.getRight() - 32)
{
tendTowardsRight = true;
}
}
- const int biggestSpace = jmax (mon.getRight() - maxX,
- minX - mon.getX()) - 32;
+ const int biggestSpace = jmax (mon.getRight() - target.getRight(),
+ target.getX() - mon.getX()) - 32;
if (biggestSpace < widthToUse)
{
- layoutMenuItems (biggestSpace + (maxX - minX) / 3, widthToUse, heightToUse);
+ layoutMenuItems (biggestSpace + target.getWidth() / 3, widthToUse, heightToUse);
if (numColumns > 1)
layoutMenuItems (biggestSpace - 4, widthToUse, heightToUse);
- tendTowardsRight = (mon.getRight() - maxX) >= (minX - mon.getX());
+ tendTowardsRight = (mon.getRight() - target.getRight()) >= (target.getX() - mon.getX());
}
if (tendTowardsRight)
- x = jmin (mon.getRight() - widthToUse - 4, maxX);
+ x = jmin (mon.getRight() - widthToUse - 4, target.getRight());
else
- x = jmax (mon.getX() + 4, minX - widthToUse);
+ x = jmax (mon.getX() + 4, target.getX() - widthToUse);
- y = minY;
- if ((minY + maxY) / 2 > mon.getCentreY())
- y = jmax (mon.getY(), maxY - heightToUse);
+ y = target.getY();
+ if (target.getCentreY() > mon.getCentreY())
+ y = jmax (mon.getY(), target.getBottom() - heightToUse);
}
x = jmax (mon.getX() + 1, jmin (mon.getRight() - (widthToUse + 6), x));
@@ -1107,13 +1103,10 @@ private:
if (childComp->isValidComponent() && childComp->itemInfo.hasActiveSubMenu())
{
- const Point topLeft (childComp->relativePositionToGlobal (Point()));
- const Point bottomRight (childComp->relativePositionToGlobal (Point (childComp->getWidth(), childComp->getHeight())));
-
activeSubMenu = Window::create (*(childComp->itemInfo.subMenu),
dismissOnMouseUp,
this,
- topLeft.getX(), bottomRight.getX(), topLeft.getY(), bottomRight.getY(),
+ childComp->getScreenBounds(),
0, maximumNumColumns,
standardItemHeight,
false, 0, menuBarComponent,
@@ -1494,7 +1487,7 @@ void PopupMenu::addSectionHeader (const String& title)
}
//==============================================================================
-Component* PopupMenu::createMenuComponent (const int x, const int y, const int w, const int h,
+Component* PopupMenu::createMenuComponent (const Rectangle& target,
const int itemIdThatMustBeVisible,
const int minimumWidth,
const int maximumNumColumns,
@@ -1504,20 +1497,10 @@ Component* PopupMenu::createMenuComponent (const int x, const int y, const int w
ApplicationCommandManager** managerOfChosenCommand,
Component* const componentAttachedTo)
{
- Window* const pw
- = Window::create (*this,
- ModifierKeys::getCurrentModifiers().isAnyMouseButtonDown(),
- 0,
- x, x + w,
- y, y + h,
- minimumWidth,
- maximumNumColumns,
- standardItemHeight,
- alignToRectangle,
- itemIdThatMustBeVisible,
- menuBarComponent,
- managerOfChosenCommand,
- componentAttachedTo);
+ Window* const pw = Window::create (*this, ModifierKeys::getCurrentModifiers().isAnyMouseButtonDown(),
+ 0, target, minimumWidth, maximumNumColumns, standardItemHeight,
+ alignToRectangle, itemIdThatMustBeVisible, menuBarComponent,
+ managerOfChosenCommand, componentAttachedTo);
if (pw != 0)
pw->setVisible (true);
@@ -1525,56 +1508,87 @@ Component* PopupMenu::createMenuComponent (const int x, const int y, const int w
return pw;
}
-int PopupMenu::showMenu (const int x, const int y, const int w, const int h,
+// This invokes any command manager commands and deletes the menu window when it is dismissed
+class PopupMenuCompletionCallback : public ModalComponentManager::Callback
+{
+public:
+ PopupMenuCompletionCallback()
+ : managerOfChosenCommand (0)
+ {
+ }
+
+ ~PopupMenuCompletionCallback() {}
+
+ void modalStateFinished (int result)
+ {
+ if (managerOfChosenCommand != 0 && result != 0)
+ {
+ ApplicationCommandTarget::InvocationInfo info (result);
+ info.invocationMethod = ApplicationCommandTarget::InvocationInfo::fromMenu;
+
+ managerOfChosenCommand->invoke (info, true);
+ }
+ }
+
+ ApplicationCommandManager* managerOfChosenCommand;
+ ScopedPointer component;
+
+private:
+ PopupMenuCompletionCallback (const PopupMenuCompletionCallback&);
+ PopupMenuCompletionCallback& operator= (const PopupMenuCompletionCallback&);
+};
+
+
+int PopupMenu::showMenu (const Rectangle& target,
const int itemIdThatMustBeVisible,
const int minimumWidth,
const int maximumNumColumns,
const int standardItemHeight,
const bool alignToRectangle,
- Component* const componentAttachedTo)
+ Component* const componentAttachedTo,
+ ModalComponentManager::Callback* userCallback)
{
+ ScopedPointer userCallbackDeleter (userCallback);
+
Component::SafePointer prevFocused (Component::getCurrentlyFocusedComponent());
Component::SafePointer prevTopLevel ((prevFocused != 0) ? prevFocused->getTopLevelComponent() : 0);
-
Window::wasHiddenBecauseOfAppChange() = false;
- int result = 0;
- ApplicationCommandManager* managerOfChosenCommand = 0;
+ PopupMenuCompletionCallback* callback = new PopupMenuCompletionCallback();
+ ScopedPointer callbackDeleter (callback);
- ScopedPointer popupComp (createMenuComponent (x, y, w, h,
- itemIdThatMustBeVisible,
- minimumWidth,
- maximumNumColumns > 0 ? maximumNumColumns : 7,
- standardItemHeight,
- alignToRectangle, 0,
- &managerOfChosenCommand,
- componentAttachedTo));
+ callback->component = createMenuComponent (target,
+ itemIdThatMustBeVisible,
+ minimumWidth,
+ maximumNumColumns > 0 ? maximumNumColumns : 7,
+ standardItemHeight,
+ alignToRectangle, 0,
+ &callback->managerOfChosenCommand,
+ componentAttachedTo);
- if (popupComp != 0)
+ if (callback->component == 0)
+ return 0;
+
+ callbackDeleter.release();
+
+ callback->component->enterModalState (false, userCallbackDeleter.release());
+ callback->component->toFront (false); // need to do this after making it modal, or it could
+ // be stuck behind other comps that are already modal..
+
+ ModalComponentManager::getInstance()->attachCallback (callback->component, callback);
+
+ if (userCallback != 0)
+ return 0;
+
+ const int result = callback->component->runModalLoop();
+
+ if (! Window::wasHiddenBecauseOfAppChange())
{
- popupComp->enterModalState (false);
- popupComp->toFront (false); // need to do this after making it modal, or it could
- // be stuck behind other comps that are already modal..
+ if (prevTopLevel != 0)
+ prevTopLevel->toFront (true);
- result = popupComp->runModalLoop();
- popupComp = 0;
-
- if (! Window::wasHiddenBecauseOfAppChange())
- {
- if (prevTopLevel != 0)
- prevTopLevel->toFront (true);
-
- if (prevFocused != 0)
- prevFocused->grabKeyboardFocus();
- }
- }
-
- if (managerOfChosenCommand != 0 && result != 0)
- {
- ApplicationCommandTarget::InvocationInfo info (result);
- info.invocationMethod = ApplicationCommandTarget::InvocationInfo::fromMenu;
-
- managerOfChosenCommand->invoke (info, true);
+ if (prevFocused != 0)
+ prevFocused->grabKeyboardFocus();
}
return result;
@@ -1583,7 +1597,8 @@ int PopupMenu::showMenu (const int x, const int y, const int w, const int h,
int PopupMenu::show (const int itemIdThatMustBeVisible,
const int minimumWidth,
const int maximumNumColumns,
- const int standardItemHeight)
+ const int standardItemHeight,
+ ModalComponentManager::Callback* callback)
{
const Point mousePos (Desktop::getMousePosition());
@@ -1591,7 +1606,8 @@ int PopupMenu::show (const int itemIdThatMustBeVisible,
itemIdThatMustBeVisible,
minimumWidth,
maximumNumColumns,
- standardItemHeight);
+ standardItemHeight,
+ callback);
}
int PopupMenu::showAt (const int screenX,
@@ -1599,39 +1615,39 @@ int PopupMenu::showAt (const int screenX,
const int itemIdThatMustBeVisible,
const int minimumWidth,
const int maximumNumColumns,
- const int standardItemHeight)
+ const int standardItemHeight,
+ ModalComponentManager::Callback* callback)
{
- return showMenu (screenX, screenY, 1, 1,
+ return showMenu (Rectangle (screenX, screenY, 1, 1),
itemIdThatMustBeVisible,
minimumWidth, maximumNumColumns,
standardItemHeight,
- false, 0);
+ false, 0, callback);
}
int PopupMenu::showAt (Component* componentToAttachTo,
const int itemIdThatMustBeVisible,
const int minimumWidth,
const int maximumNumColumns,
- const int standardItemHeight)
+ const int standardItemHeight,
+ ModalComponentManager::Callback* callback)
{
if (componentToAttachTo != 0)
{
- return showMenu (componentToAttachTo->getScreenX(),
- componentToAttachTo->getScreenY(),
- componentToAttachTo->getWidth(),
- componentToAttachTo->getHeight(),
+ return showMenu (componentToAttachTo->getScreenBounds(),
itemIdThatMustBeVisible,
minimumWidth,
maximumNumColumns,
standardItemHeight,
- true, componentToAttachTo);
+ true, componentToAttachTo, callback);
}
else
{
return show (itemIdThatMustBeVisible,
minimumWidth,
maximumNumColumns,
- standardItemHeight);
+ standardItemHeight,
+ callback);
}
}
diff --git a/src/gui/components/menus/juce_PopupMenu.h b/src/gui/components/menus/juce_PopupMenu.h
index 69e0539b76..bde88d6136 100644
--- a/src/gui/components/menus/juce_PopupMenu.h
+++ b/src/gui/components/menus/juce_PopupMenu.h
@@ -240,12 +240,19 @@ public:
in zero.
@param standardItemHeight if this is non-zero, it will be used as the standard
height for menu items (apart from custom items)
+ @param callback if this is non-zero, the menu will be launched asynchronously,
+ returning immediately, and the callback will receive a
+ call when the menu is either dismissed or has an item
+ selected. This object will be owned and deleted by the
+ system, so make sure that it works safely and that any
+ pointers that it uses are safely within scope.
@see showAt
*/
int show (int itemIdThatMustBeVisible = 0,
int minimumWidth = 0,
int maximumNumColumns = 0,
- int standardItemHeight = 0);
+ int standardItemHeight = 0,
+ ModalComponentManager::Callback* callback = 0);
/** Displays the menu at a specific location.
@@ -264,7 +271,8 @@ public:
int itemIdThatMustBeVisible = 0,
int minimumWidth = 0,
int maximumNumColumns = 0,
- int standardItemHeight = 0);
+ int standardItemHeight = 0,
+ ModalComponentManager::Callback* callback = 0);
/** Displays the menu as if it's attached to a component such as a button.
@@ -276,7 +284,8 @@ public:
int itemIdThatMustBeVisible = 0,
int minimumWidth = 0,
int maximumNumColumns = 0,
- int standardItemHeight = 0);
+ int standardItemHeight = 0,
+ ModalComponentManager::Callback* callback = 0);
//==============================================================================
/** Closes any menus that are currently open.
@@ -383,6 +392,7 @@ private:
friend class ItemComponent;
friend class Window;
friend class PopupMenuCustomComponent;
+ friend class MenuBarComponent;
friend class OwnedArray
- ;
friend class ScopedPointer ;
@@ -392,16 +402,16 @@ private:
void addSeparatorIfPending();
- int showMenu (int x, int y, int w, int h,
+ int showMenu (const Rectangle& target,
int itemIdThatMustBeVisible,
int minimumWidth,
int maximumNumColumns,
int standardItemHeight,
bool alignToRectangle,
- Component* componentAttachedTo);
+ Component* componentAttachedTo,
+ ModalComponentManager::Callback* callback);
- friend class MenuBarComponent;
- Component* createMenuComponent (int x, int y, int w, int h,
+ Component* createMenuComponent (const Rectangle& target,
int itemIdThatMustBeVisible,
int minimumWidth,
int maximumNumColumns,
diff --git a/src/gui/graphics/geometry/juce_Path.cpp b/src/gui/graphics/geometry/juce_Path.cpp
index 972edbc031..85b45112c6 100644
--- a/src/gui/graphics/geometry/juce_Path.cpp
+++ b/src/gui/graphics/geometry/juce_Path.cpp
@@ -601,7 +601,7 @@ void Path::addArrow (const Line& line, float lineThickness,
startNewSubPath (line.getPointAlongLine (0, lineThickness));
lineTo (line.getPointAlongLine (0, -lineThickness));
- lineTo (reversed.getPointAlongLine (0, lineThickness));
+ lineTo (reversed.getPointAlongLine (arrowheadLength, lineThickness));
lineTo (reversed.getPointAlongLine (arrowheadLength, arrowheadWidth));
lineTo (line.getEnd());
lineTo (reversed.getPointAlongLine (arrowheadLength, -arrowheadWidth));
diff --git a/src/juce_app_includes.h b/src/juce_app_includes.h
index efd137f590..97e6ec7a45 100644
--- a/src/juce_app_includes.h
+++ b/src/juce_app_includes.h
@@ -404,6 +404,9 @@
#ifndef __JUCE_DESKTOP_JUCEHEADER__
#include "gui/components/juce_Desktop.h"
#endif
+#ifndef __JUCE_MODALCOMPONENTMANAGER_JUCEHEADER__
+ #include "gui/components/juce_ModalComponentManager.h"
+#endif
#ifndef __JUCE_KEYBOARDFOCUSTRAVERSER_JUCEHEADER__
#include "gui/components/keyboard/juce_KeyboardFocusTraverser.h"
#endif