From 2d10b0b43de74fcbe7ac33a332480bbc85037b22 Mon Sep 17 00:00:00 2001 From: Julian Storer Date: Fri, 11 Feb 2011 14:27:28 +0000 Subject: [PATCH] Changes and additions to provide alternatives to modal-loop functionality (Android can't run modal loops). New class ModalCallbackFunction providing quick objects for making callbacks to static functions. Changes to remove modal loops from most of the internal library classes. Added new methods to PopupMenu to provide easier async callbacks, and also a cleaner way of specifying options when showing a menu. Fix for PNG decoding of corrupted image files. --- .../Source/demos/InterprocessCommsDemo.cpp | 6 +- .../JuceDemo/Source/demos/ThreadingDemo.cpp | 10 +- extras/JuceDemo/Source/demos/TreeViewDemo.cpp | 20 +- extras/JuceDemo/Source/demos/WidgetsDemo.cpp | 339 ++-- .../juce_StandaloneFilterWindow.cpp | 7 +- juce_amalgamated.cpp | 1045 ++++++++----- juce_amalgamated.h | 1384 +++++++++++------ .../juce_ApplicationProperties.cpp | 12 +- .../plugins/juce_PluginListComponent.cpp | 5 +- src/core/juce_PlatformDefs.h | 11 + src/core/juce_StandardHeader.h | 2 +- src/events/juce_MessageManager.cpp | 2 +- src/events/juce_MessageManager.h | 2 + src/gui/components/controls/juce_ComboBox.cpp | 36 +- src/gui/components/controls/juce_ComboBox.h | 4 +- src/gui/components/controls/juce_Slider.cpp | 64 +- src/gui/components/controls/juce_Slider.h | 20 + .../controls/juce_TableHeaderComponent.cpp | 12 +- .../components/controls/juce_TextEditor.cpp | 26 +- src/gui/components/controls/juce_Toolbar.cpp | 17 +- .../filebrowser/juce_FileChooser.cpp | 2 + .../filebrowser/juce_FileChooserDialogBox.cpp | 203 ++- .../filebrowser/juce_FileChooserDialogBox.h | 26 +- src/gui/components/juce_Component.cpp | 35 +- src/gui/components/juce_Component.h | 9 +- .../components/juce_ModalComponentManager.cpp | 23 +- .../components/juce_ModalComponentManager.h | 223 ++- .../juce_KeyMappingEditorComponent.cpp | 106 +- .../layout/juce_TabbedButtonBar.cpp | 12 +- .../components/layout/juce_TabbedButtonBar.h | 1 + .../menus/juce_MenuBarComponent.cpp | 34 +- .../components/menus/juce_MenuBarComponent.h | 3 +- src/gui/components/menus/juce_PopupMenu.cpp | 190 ++- src/gui/components/menus/juce_PopupMenu.h | 47 +- .../juce_AudioDeviceSelectorComponent.cpp | 6 +- .../special/juce_PreferencesPanel.cpp | 2 +- .../components/windows/juce_AlertWindow.cpp | 61 +- src/gui/components/windows/juce_AlertWindow.h | 103 +- .../components/windows/juce_DialogWindow.cpp | 59 +- .../components/windows/juce_DialogWindow.h | 45 + .../components/windows/juce_SplashScreen.cpp | 2 + .../windows/juce_ThreadWithProgressWindow.cpp | 2 + .../image_file_formats/juce_PNGLoader.cpp | 142 +- src/native/android/java/JuceAppActivity.java | 35 +- src/native/android/juce_android_Messaging.cpp | 6 - src/native/mac/juce_mac_MessageManager.mm | 2 + src/utilities/juce_FileBasedDocument.cpp | 5 + 47 files changed, 2854 insertions(+), 1554 deletions(-) diff --git a/extras/JuceDemo/Source/demos/InterprocessCommsDemo.cpp b/extras/JuceDemo/Source/demos/InterprocessCommsDemo.cpp index 14c5eb52bf..2460620ef9 100644 --- a/extras/JuceDemo/Source/demos/InterprocessCommsDemo.cpp +++ b/extras/JuceDemo/Source/demos/InterprocessCommsDemo.cpp @@ -225,9 +225,9 @@ public: { modeSelector.setSelectedId (8); - AlertWindow::showMessageBox (AlertWindow::WarningIcon, - "Interprocess Comms Demo", - "Failed to open the socket or pipe..."); + AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, + "Interprocess Comms Demo", + "Failed to open the socket or pipe..."); } } diff --git a/extras/JuceDemo/Source/demos/ThreadingDemo.cpp b/extras/JuceDemo/Source/demos/ThreadingDemo.cpp index f07d297c98..d86a17c5cf 100644 --- a/extras/JuceDemo/Source/demos/ThreadingDemo.cpp +++ b/extras/JuceDemo/Source/demos/ThreadingDemo.cpp @@ -320,10 +320,14 @@ public: m.addItem (1, "Use one thread per ball", true, ! isUsingPool); m.addItem (2, "Use a thread pool", true, isUsingPool); - const int res = m.showAt (&controlButton); + m.showMenuAsync (PopupMenu::Options().withTargetComponent (&controlButton), + ModalCallbackFunction::forComponent (menuItemChosenCallback, this)); + } - if (res != 0) - setUsingPool (res == 2); + static void menuItemChosenCallback (int result, ThreadingDemo* demoComponent) + { + if (demoComponent != 0) + demoComponent->setUsingPool (result == 2); } // this gets called when a component is added or removed from a parent component. diff --git a/extras/JuceDemo/Source/demos/TreeViewDemo.cpp b/extras/JuceDemo/Source/demos/TreeViewDemo.cpp index f8dc165541..cbf31760f4 100644 --- a/extras/JuceDemo/Source/demos/TreeViewDemo.cpp +++ b/extras/JuceDemo/Source/demos/TreeViewDemo.cpp @@ -212,24 +212,34 @@ public: treeView != 0 ? treeView->areOpenCloseButtonsVisible() : fileTreeComp->areOpenCloseButtonsVisible()); - const int r = m.showAt (&typeButton); + m.showMenuAsync (PopupMenu::Options().withTargetComponent (&typeButton), + ModalCallbackFunction::forComponent (menuItemChosenCallback, this)); + } - if (r == 1) + static void menuItemChosenCallback (int result, TreeViewDemo* demoComponent) + { + if (demoComponent != 0) + demoComponent->menuItemChosenCallback (result); + } + + void menuItemChosenCallback (int result) + { + if (result == 1) { showCustomTreeView(); } - else if (r == 2) + else if (result == 2) { showFileTreeComp(); } - else if (r == 3) + else if (result == 3) { if (treeView != 0) treeView->setRootItemVisible (! treeView->isRootItemVisible()); else fileTreeComp->setRootItemVisible (! fileTreeComp->isRootItemVisible()); } - else if (r == 4) + else if (result == 4) { if (treeView != 0) treeView->setOpenCloseButtonsVisible (! treeView->areOpenCloseButtonsVisible()); diff --git a/extras/JuceDemo/Source/demos/WidgetsDemo.cpp b/extras/JuceDemo/Source/demos/WidgetsDemo.cpp index f62823d0a6..f52b0fd714 100644 --- a/extras/JuceDemo/Source/demos/WidgetsDemo.cpp +++ b/extras/JuceDemo/Source/demos/WidgetsDemo.cpp @@ -1211,197 +1211,204 @@ public: m.addSubMenu ("File chooser dialogs", fileChoosers); - int result = m.showAt (&menuButton); + m.showMenuAsync (PopupMenu::Options().withTargetComponent (&menuButton), + ModalCallbackFunction::forComponent (menuItemChosenCallback, this)); + } + } - if (result != 0) - { - // user chose something from the menu.. + //============================================================================== + // This gets called when our popup menu has an item selected or is dismissed. + static void menuItemChosenCallback (int result, WidgetsDemo* demoComponent) + { + if (result != 0 && demoComponent != 0) + demoComponent->performDemoMenuItem (result); + } - if (result >= 100 && result < 105) - { - AlertWindow::AlertIconType icon = AlertWindow::NoIcon; - - if (result == 101) - icon = AlertWindow::WarningIcon; - else if (result == 102) - icon = AlertWindow::InfoIcon; - else if (result == 103) - icon = AlertWindow::QuestionIcon; - - AlertWindow::showMessageBox (icon, - "This is an AlertWindow", - "And this is the AlertWindow's message. Blah blah blah blah blah blah blah blah blah blah blah blah blah.", - "ok"); - } - else if (result == 110) - { - bool userPickedOk - = AlertWindow::showOkCancelBox (AlertWindow::QuestionIcon, - "This is an ok/cancel AlertWindow", - "And this is the AlertWindow's message. Blah blah blah blah blah blah blah blah blah blah blah blah blah."); + void performDemoMenuItem (int result) + { + if (result >= 100 && result < 105) + { + AlertWindow::AlertIconType icon = AlertWindow::NoIcon; + + if (result == 101) + icon = AlertWindow::WarningIcon; + else if (result == 102) + icon = AlertWindow::InfoIcon; + else if (result == 103) + icon = AlertWindow::QuestionIcon; + + AlertWindow::showMessageBoxAsync (icon, + "This is an AlertWindow", + "And this is the AlertWindow's message. Blah blah blah blah blah blah blah blah blah blah blah blah blah.", + "ok"); + } + else if (result == 110) + { + bool userPickedOk + = AlertWindow::showOkCancelBox (AlertWindow::QuestionIcon, + "This is an ok/cancel AlertWindow", + "And this is the AlertWindow's message. Blah blah blah blah blah blah blah blah blah blah blah blah blah."); - (void) userPickedOk; // (just avoids a compiler warning about unused variables) - } - else if (result == 111) - { - AlertWindow w ("AlertWindow demo..", - "This AlertWindow has a couple of extra components added to show how to add drop-down lists and text entry boxes.", - AlertWindow::QuestionIcon); + (void) userPickedOk; // (just avoids a compiler warning about unused variables) + } + else if (result == 111) + { + AlertWindow w ("AlertWindow demo..", + "This AlertWindow has a couple of extra components added to show how to add drop-down lists and text entry boxes.", + AlertWindow::QuestionIcon); - w.addTextEditor ("text", "enter some text here", "text field:"); + w.addTextEditor ("text", "enter some text here", "text field:"); - StringArray options; - options.add ("option 1"); - options.add ("option 2"); - options.add ("option 3"); - options.add ("option 4"); - w.addComboBox ("option", options, "some options"); + StringArray options; + options.add ("option 1"); + options.add ("option 2"); + options.add ("option 3"); + options.add ("option 4"); + w.addComboBox ("option", options, "some options"); - w.addButton ("ok", 1, KeyPress (KeyPress::returnKey, 0, 0)); - w.addButton ("cancel", 0, KeyPress (KeyPress::escapeKey, 0, 0)); + w.addButton ("ok", 1, KeyPress (KeyPress::returnKey, 0, 0)); + w.addButton ("cancel", 0, KeyPress (KeyPress::escapeKey, 0, 0)); - if (w.runModalLoop() != 0) // is they picked 'ok' - { - // this is the item they chose in the drop-down list.. - const int optionIndexChosen = w.getComboBoxComponent ("option")->getSelectedItemIndex(); - (void) optionIndexChosen; // (just avoids a compiler warning about unused variables) + if (w.runModalLoop() != 0) // is they picked 'ok' + { + // this is the item they chose in the drop-down list.. + const int optionIndexChosen = w.getComboBoxComponent ("option")->getSelectedItemIndex(); + (void) optionIndexChosen; // (just avoids a compiler warning about unused variables) - // this is the text they entered.. - String text = w.getTextEditorContents ("text"); + // this is the text they entered.. + String text = w.getTextEditorContents ("text"); - } - } - else if (result == 112) - { - DemoBackgroundThread demoThread; + } + } + else if (result == 112) + { + DemoBackgroundThread demoThread; - if (demoThread.runThread()) - { - // thread finished normally.. - AlertWindow::showMessageBox (AlertWindow::WarningIcon, - "Progress window", - "Thread finished ok!"); - } - else - { - // user pressed the cancel button.. - AlertWindow::showMessageBox (AlertWindow::WarningIcon, - "Progress window", - "You pressed cancel!"); - } - } - else if (result == 120) - { - ColourSelectorDialogWindow colourDialog; + if (demoThread.runThread()) + { + // thread finished normally.. + AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, + "Progress window", + "Thread finished ok!"); + } + else + { + // user pressed the cancel button.. + AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, + "Progress window", + "You pressed cancel!"); + } + } + else if (result == 120) + { + ColourSelectorDialogWindow colourDialog; - // this will run an event loop until the dialog's closeButtonPressed() - // method causes the loop to exit. - colourDialog.runModalLoop(); - } - else if (result == 140) - { + // this will run an event loop until the dialog's closeButtonPressed() + // method causes the loop to exit. + colourDialog.runModalLoop(); + } + else if (result == 140) + { #if JUCE_MAC - AppleRemoteTestWindow test; - test.runModalLoop(); + AppleRemoteTestWindow test; + test.runModalLoop(); #endif - } - else if (result >= 121 && result < 139) - { - const bool useNativeVersion = result < 130; - if (result > 130) - result -= 10; + } + else if (result >= 121 && result < 139) + { + const bool useNativeVersion = result < 130; + if (result > 130) + result -= 10; - if (result == 121) - { - FileChooser fc ("Choose a file to open...", - File::getCurrentWorkingDirectory(), - "*", - useNativeVersion); - - if (fc.browseForMultipleFilesToOpen()) - { - String chosen; - for (int i = 0; i < fc.getResults().size(); ++i) - chosen << fc.getResults().getReference(i).getFullPathName() << "\n"; - - AlertWindow::showMessageBox (AlertWindow::InfoIcon, - "File Chooser...", - "You picked: " + chosen); - } - } - else if (result == 124) - { - ImagePreviewComponent imagePreview; - imagePreview.setSize (200, 200); - - FileChooser fc ("Choose an image to open...", - File::getCurrentWorkingDirectory(), - "*.jpg;*.jpeg;*.png;*.gif", - useNativeVersion); - - if (fc.browseForMultipleFilesToOpen (&imagePreview)) - { - String chosen; - for (int i = 0; i < fc.getResults().size(); ++i) - chosen << fc.getResults().getReference(i).getFullPathName() << "\n"; - - AlertWindow::showMessageBox (AlertWindow::InfoIcon, - "File Chooser...", - "You picked: " + chosen); - } - } - else if (result == 122) - { - FileChooser fc ("Choose a file to save...", - File::getCurrentWorkingDirectory(), - "*", - useNativeVersion); - - if (fc.browseForFileToSave (true)) - { - File chosenFile = fc.getResult(); - - AlertWindow::showMessageBox (AlertWindow::InfoIcon, - "File Chooser...", - "You picked: " + chosenFile.getFullPathName()); - } - } - else if (result == 123) - { - FileChooser fc ("Choose a directory...", - File::getCurrentWorkingDirectory(), - "*", - useNativeVersion); - - if (fc.browseForDirectory()) - { - File chosenDirectory = fc.getResult(); - - AlertWindow::showMessageBox (AlertWindow::InfoIcon, - "File Chooser...", - "You picked: " + chosenDirectory.getFullPathName()); - } - } - } - else if (result == 1001) + if (result == 121) + { + FileChooser fc ("Choose a file to open...", + File::getCurrentWorkingDirectory(), + "*", + useNativeVersion); + + if (fc.browseForMultipleFilesToOpen()) { - tabs.setOrientation (TabbedButtonBar::TabsAtTop); + String chosen; + for (int i = 0; i < fc.getResults().size(); ++i) + chosen << fc.getResults().getReference(i).getFullPathName() << "\n"; + + AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon, + "File Chooser...", + "You picked: " + chosen); } - else if (result == 1002) + } + else if (result == 124) + { + ImagePreviewComponent imagePreview; + imagePreview.setSize (200, 200); + + FileChooser fc ("Choose an image to open...", + File::getCurrentWorkingDirectory(), + "*.jpg;*.jpeg;*.png;*.gif", + useNativeVersion); + + if (fc.browseForMultipleFilesToOpen (&imagePreview)) { - tabs.setOrientation (TabbedButtonBar::TabsAtBottom); + String chosen; + for (int i = 0; i < fc.getResults().size(); ++i) + chosen << fc.getResults().getReference(i).getFullPathName() << "\n"; + + AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon, + "File Chooser...", + "You picked: " + chosen); } - else if (result == 1003) + } + else if (result == 122) + { + FileChooser fc ("Choose a file to save...", + File::getCurrentWorkingDirectory(), + "*", + useNativeVersion); + + if (fc.browseForFileToSave (true)) { - tabs.setOrientation (TabbedButtonBar::TabsAtLeft); + File chosenFile = fc.getResult(); + + AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon, + "File Chooser...", + "You picked: " + chosenFile.getFullPathName()); } - else if (result == 1004) + } + else if (result == 123) + { + FileChooser fc ("Choose a directory...", + File::getCurrentWorkingDirectory(), + "*", + useNativeVersion); + + if (fc.browseForDirectory()) { - tabs.setOrientation (TabbedButtonBar::TabsAtRight); + File chosenDirectory = fc.getResult(); + + AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon, + "File Chooser...", + "You picked: " + chosenDirectory.getFullPathName()); } } } + else if (result == 1001) + { + tabs.setOrientation (TabbedButtonBar::TabsAtTop); + } + else if (result == 1002) + { + tabs.setOrientation (TabbedButtonBar::TabsAtBottom); + } + else if (result == 1003) + { + tabs.setOrientation (TabbedButtonBar::TabsAtLeft); + } + else if (result == 1004) + { + tabs.setOrientation (TabbedButtonBar::TabsAtRight); + } } void sliderValueChanged (Slider*) diff --git a/extras/audio plugins/wrapper/Standalone/juce_StandaloneFilterWindow.cpp b/extras/audio plugins/wrapper/Standalone/juce_StandaloneFilterWindow.cpp index 06d8b332cd..11c37d38f8 100644 --- a/extras/audio plugins/wrapper/Standalone/juce_StandaloneFilterWindow.cpp +++ b/extras/audio plugins/wrapper/Standalone/juce_StandaloneFilterWindow.cpp @@ -62,7 +62,7 @@ StandaloneFilterWindow::StandaloneFilterWindow (const String& title, deviceManager = new AudioFilterStreamingDeviceManager(); deviceManager->setFilter (filter); - XmlElement* savedState = 0; + ScopedPointer savedState; if (globalSettings != 0) savedState = globalSettings->getXmlValue ("audioSetup"); @@ -72,8 +72,6 @@ StandaloneFilterWindow::StandaloneFilterWindow (const String& title, savedState, true); - delete savedState; - if (globalSettings != 0) { MemoryBlock data; @@ -114,9 +112,8 @@ StandaloneFilterWindow::~StandaloneFilterWindow() if (globalSettings != 0 && deviceManager != 0) { - XmlElement* const xml = deviceManager->createStateXml(); + ScopedPointer xml (deviceManager->createStateXml()); globalSettings->setValue ("audioSetup", xml); - delete xml; } deviceManager = 0; diff --git a/juce_amalgamated.cpp b/juce_amalgamated.cpp index 549963c9dd..5cc28f5b27 100644 --- a/juce_amalgamated.cpp +++ b/juce_amalgamated.cpp @@ -18992,12 +18992,12 @@ bool ApplicationProperties::testWriteAccess (const bool testUserSettings, if (commonProps != 0 && ! commonOk) filenames << '\n' << commonProps->getFile().getFullPathName(); - AlertWindow::showMessageBox (AlertWindow::WarningIcon, - appName + TRANS(" - Unable to save settings"), - TRANS("An error occurred when trying to save the application's settings file...\n\nIn order to save and restore its settings, ") - + appName + TRANS(" needs to be able to write to the following files:\n") - + filenames - + TRANS("\n\nMake sure that these files aren't read-only, and that the disk isn't full.")); + AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, + appName + TRANS(" - Unable to save settings"), + TRANS("An error occurred when trying to save the application's settings file...\n\nIn order to save and restore its settings, ") + + appName + TRANS(" needs to be able to write to the following files:\n") + + filenames + + TRANS("\n\nMake sure that these files aren't read-only, and that the disk isn't full.")); } return false; @@ -19413,6 +19413,7 @@ void FileBasedDocument::setFile (const File& newFile) } } +#if JUCE_MODAL_LOOPS_PERMITTED bool FileBasedDocument::loadFrom (const File& newFile, const bool showMessageOnFailure) { @@ -19456,6 +19457,7 @@ bool FileBasedDocument::loadFrom (const File& newFile, return false; } +#endif bool FileBasedDocument::loadFromUserSpecifiedFile (const bool showMessageOnFailure) { @@ -19478,6 +19480,7 @@ FileBasedDocument::SaveResult FileBasedDocument::save (const bool askUserForFile showMessageOnFailure); } +#if JUCE_MODAL_LOOPS_PERMITTED FileBasedDocument::SaveResult FileBasedDocument::saveAs (const File& newFile, const bool warnAboutOverwritingExistingFiles, const bool askUserForFileIfNotSpecified, @@ -19623,6 +19626,7 @@ FileBasedDocument::SaveResult FileBasedDocument::saveAsInteractive (const bool w return userCancelledSave; } +#endif END_JUCE_NAMESPACE /*** End of inlined file: juce_FileBasedDocument.cpp ***/ @@ -30879,7 +30883,7 @@ void PluginListComponent::buttonClicked (Button* button) menu.addItem (10 + i, "Scan for new or updated " + format->getName() + " plugins..."); } - const int r = menu.showAt (&optionsButton); + const int r = menu.showMenu (PopupMenu::Options().withTargetComponent (&optionsButton)); if (r == 1) { @@ -30950,6 +30954,7 @@ void PluginListComponent::filesDropped (const StringArray& files, int, int) list.scanAndAddDragAndDroppedFiles (files, typesFound); } +#if JUCE_MODAL_LOOPS_PERMITTED void PluginListComponent::scanFor (AudioPluginFormat* format) { if (format == 0) @@ -31025,6 +31030,7 @@ void PluginListComponent::scanFor (AudioPluginFormat* format) + shortNames.joinIntoString (", ")); } } +#endif END_JUCE_NAMESPACE /*** End of inlined file: juce_PluginListComponent.cpp ***/ @@ -38777,7 +38783,7 @@ void MessageManager::deliverMessage (Message* const message) JUCE_CATCH_EXCEPTION } -#if ! (JUCE_MAC || JUCE_IOS || JUCE_ANDROID) +#if JUCE_MODAL_LOOPS_PERMITTED && ! (JUCE_MAC || JUCE_IOS) void MessageManager::runDispatchLoop() { jassert (isThisTheMessageThread()); // must only be called by the message thread @@ -39616,10 +39622,12 @@ class Component::ComponentHelpers { public: + #if JUCE_MODAL_LOOPS_PERMITTED static void* runModalLoopCallback (void* userData) { return (void*) (pointer_sized_int) static_cast (userData)->runModalLoop(); } + #endif static const Identifier getColourPropertyId (const int colourId) { @@ -40907,6 +40915,7 @@ void Component::internalHierarchyChanged() } } +#if JUCE_MODAL_LOOPS_PERMITTED int Component::runModalLoop() { if (! MessageManager::getInstance()->isThisTheMessageThread()) @@ -40921,8 +40930,29 @@ int Component::runModalLoop() return ModalComponentManager::getInstance()->runEventLoopForCurrentComponent(); } +#endif -void Component::enterModalState (const bool shouldTakeKeyboardFocus, ModalComponentManager::Callback* const callback) +class ModalAutoDeleteCallback : public ModalComponentManager::Callback +{ +public: + ModalAutoDeleteCallback (Component* const comp_) + : comp (comp_) + {} + + void modalStateFinished (int) + { + delete comp.get(); + } + +private: + WeakReference comp; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ModalAutoDeleteCallback); +}; + +void Component::enterModalState (const bool shouldTakeKeyboardFocus, + ModalComponentManager::Callback* callback, + const bool deleteWhenDismissed) { // 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. @@ -40934,7 +40964,13 @@ void Component::enterModalState (const bool shouldTakeKeyboardFocus, ModalCompon if (! isCurrentlyModal()) { - ModalComponentManager::getInstance()->startModal (this, callback); + ModalComponentManager* const mcm = ModalComponentManager::getInstance(); + mcm->startModal (this); + mcm->attachCallback (this, callback); + + if (deleteWhenDismissed) + mcm->attachCallback (this, new ModalAutoDeleteCallback (this)); + flags.currentlyModalFlag = true; setVisible (true); @@ -42768,14 +42804,11 @@ BEGIN_JUCE_NAMESPACE class ModalComponentManager::ModalItem : public ComponentMovementWatcher { public: - ModalItem (Component* const comp, Callback* const callback) + ModalItem (Component* const comp) : ComponentMovementWatcher (comp), component (comp), returnValue (0), isActive (true) { jassert (comp != 0); - - if (callback != 0) - callbacks.add (callback); } void componentMovedOrResized (bool, bool) {} @@ -42829,10 +42862,10 @@ ModalComponentManager::~ModalComponentManager() juce_ImplementSingleton_SingleThreaded (ModalComponentManager); -void ModalComponentManager::startModal (Component* component, Callback* callback) +void ModalComponentManager::startModal (Component* component) { if (component != 0) - stack.add (new ModalItem (component, callback)); + stack.add (new ModalItem (component)); } void ModalComponentManager::attachCallback (Component* component, Callback* callback) @@ -42926,17 +42959,13 @@ void ModalComponentManager::handleAsyncUpdate() for (int i = stack.size(); --i >= 0;) { const ModalItem* const item = stack.getUnchecked(i); + if (! item->isActive) { + ScopedPointer item (stack.removeAndReturn (i)); + for (int j = item->callbacks.size(); --j >= 0;) - { item->callbacks.getUnchecked(j)->modalStateFinished (item->returnValue); - - if (! stack.contains (item)) - break; - } - - stack.removeObject (item); } } } @@ -42969,11 +42998,11 @@ void ModalComponentManager::bringModalComponentsToFront() } } +#if JUCE_MODAL_LOOPS_PERMITTED class ModalComponentManager::ReturnValueRetriever : public ModalComponentManager::Callback { public: ReturnValueRetriever (int& value_, bool& finished_) : value (value_), finished (finished_) {} - ~ReturnValueRetriever() {} void modalStateFinished (int returnValue) { @@ -43019,6 +43048,7 @@ int ModalComponentManager::runEventLoopForCurrentComponent() return returnValue; } +#endif END_JUCE_NAMESPACE /*** End of inlined file: juce_ModalComponentManager.cpp ***/ @@ -47695,30 +47725,16 @@ void ComboBox::labelTextChanged (Label*) triggerAsyncUpdate(); } -class ComboBox::Callback : public ModalComponentManager::Callback +void ComboBox::popupMenuFinishedCallback (int result, ComboBox* box) { -public: - Callback (ComboBox* const box_) - : box (box_) + if (box != 0) { - } - - void modalStateFinished (int returnValue) - { - if (box != 0) - { - box->menuActive = false; + box->menuActive = false; - if (returnValue != 0) - box->setSelectedId (returnValue); - } + if (result != 0) + box->setSelectedId (result); } - -private: - Component::SafePointer box; - - JUCE_DECLARE_NON_COPYABLE (Callback); -}; +} void ComboBox::showPopup() { @@ -47746,8 +47762,13 @@ void ComboBox::showPopup() menu.addItem (1, noChoicesMessage, false); menuActive = true; - menu.showAt (this, selectedId, getWidth(), 1, jlimit (12, 24, getHeight()), - new Callback (this)); + + menu.showMenuAsync (PopupMenu::Options().withTargetComponent (this) + .withItemThatMustBeVisible (selectedId) + .withMinimumWidth (getWidth()) + .withMaximumNumColumns (1) + .withStandardItemHeight (jlimit (12, 24, getHeight())), + ModalCallbackFunction::forComponent (popupMenuFinishedCallback, this)); } } @@ -49256,10 +49277,6 @@ public: setAlwaysOnTop (true); } - ~SliderPopupDisplayComponent() - { - } - void paintContent (Graphics& g, int w, int h) { g.setFont (font); @@ -49811,6 +49828,31 @@ void Slider::setMaxValue (double newValue, const bool sendUpdateMessage, const b } } +void Slider::setMinAndMaxValues (double newMinValue, double newMaxValue, bool sendUpdateMessage, bool sendMessageSynchronously) +{ + // The maximum value only applies to sliders that are in two- or three-value mode. + jassert (style == TwoValueHorizontal || style == TwoValueVertical + || style == ThreeValueHorizontal || style == ThreeValueVertical); + + if (newMaxValue < newMinValue) + swapVariables (newMaxValue, newMinValue); + + newMinValue = constrainedValue (newMinValue); + newMaxValue = constrainedValue (newMaxValue); + + if (lastValueMax != newMaxValue || lastValueMin != newMinValue) + { + lastValueMax = newMaxValue; + lastValueMin = newMinValue; + valueMin = newMinValue; + valueMax = newMaxValue; + repaint(); + + if (sendUpdateMessage) + triggerChangeMessage (sendMessageSynchronously); + } +} + void Slider::setDoubleClickReturnValue (const bool isDoubleClickEnabled, const double valueToSetOnDoubleClick) { @@ -50190,6 +50232,21 @@ void Slider::focusOfChildComponentChanged (FocusChangeType) repaint(); } +static void sliderMenuCallback (int result, Slider* slider) +{ + if (slider != 0) + { + switch (result) + { + case 1: slider->setVelocityBasedMode (! slider->getVelocityBasedMode()); break; + case 2: slider->setSliderStyle (Slider::Rotary); break; + case 3: slider->setSliderStyle (Slider::RotaryHorizontalDrag); break; + case 4: slider->setSliderStyle (Slider::RotaryVerticalDrag); break; + default: break; + } + } +} + void Slider::mouseDown (const MouseEvent& e) { mouseWasHidden = false; @@ -50220,24 +50277,8 @@ void Slider::mouseDown (const MouseEvent& e) m.addSubMenu (TRANS ("rotary mode"), rotaryMenu); } - const int r = m.show(); - - if (r == 1) - { - setVelocityBasedMode (! isVelocityBased); - } - else if (r == 2) - { - setSliderStyle (Rotary); - } - else if (r == 3) - { - setSliderStyle (RotaryHorizontalDrag); - } - else if (r == 4) - { - setSliderStyle (RotaryVerticalDrag); - } + m.showMenuAsync (PopupMenu::Options(), + ModalCallbackFunction::forComponent (sliderMenuCallback, this)); } else if (maximum > minimum) { @@ -51505,6 +51546,12 @@ void TableHeaderComponent::updateColumnUnderMouse (int x, int y) } } +static void tableHeaderMenuCallback (int result, TableHeaderComponent* tableHeader, int columnIdClicked) +{ + if (tableHeader != 0 && result != 0) + tableHeader->reactToMenuItem (result, columnIdClicked); +} + void TableHeaderComponent::showColumnChooserMenu (const int columnIdClicked) { PopupMenu m; @@ -51514,10 +51561,8 @@ void TableHeaderComponent::showColumnChooserMenu (const int columnIdClicked) { m.setLookAndFeel (&getLookAndFeel()); - const int result = m.show(); - - if (result != 0) - reactToMenuItem (result, columnIdClicked); + m.showMenuAsync (PopupMenu::Options(), + ModalCallbackFunction::forComponent (tableHeaderMenuCallback, this, columnIdClicked)); } } @@ -53689,25 +53734,11 @@ void TextEditor::paintOverChildren (Graphics& g) getLookAndFeel().drawTextEditorOutline (g, getWidth(), getHeight(), *this); } -class TextEditorMenuPerformer : public ModalComponentManager::Callback +static void textEditorMenuCallback (int menuResult, TextEditor* editor) { -public: - TextEditorMenuPerformer (TextEditor* const editor_) - : editor (editor_) - { - } - - void modalStateFinished (int returnValue) - { - if (editor != 0 && returnValue != 0) - editor->performPopupMenuAction (returnValue); - } - -private: - Component::SafePointer editor; - - JUCE_DECLARE_NON_COPYABLE (TextEditorMenuPerformer); -}; + if (editor != 0 && menuResult != 0) + editor->performPopupMenuAction (menuResult); +} void TextEditor::mouseDown (const MouseEvent& e) { @@ -53727,7 +53758,8 @@ void TextEditor::mouseDown (const MouseEvent& e) m.setLookAndFeel (&getLookAndFeel()); addPopupMenuItems (m, &e); - m.show (0, 0, 0, 0, new TextEditorMenuPerformer (this)); + m.showMenuAsync (PopupMenu::Options(), + ModalCallbackFunction::forComponent (textEditorMenuCallback, this)); } } } @@ -55066,7 +55098,7 @@ void Toolbar::buttonClicked (Button*) { PopupMenu m; m.addCustomItem (1, new MissingItemsComponent (*this, getThickness())); - m.showAt (missingItemsButton); + m.showMenuAsync (PopupMenu::Options().withTargetComponent (missingItemsButton), 0); } } @@ -55189,6 +55221,7 @@ public: ~ToolbarCustomisationDialog() { + toolbar->setEditingActive (false); setContentComponent (0, true); } @@ -55345,18 +55378,8 @@ void Toolbar::showCustomisationDialog (ToolbarItemFactory& factory, const int op { setEditingActive (true); - #if JUCE_DEBUG - WeakReference checker (this); - #endif - - ToolbarCustomisationDialog dw (factory, this, optionFlags); - dw.runModalLoop(); - - #if JUCE_DEBUG - jassert (checker != 0); // Don't delete the toolbar while it's being customised! - #endif - - setEditingActive (false); + (new ToolbarCustomisationDialog (factory, this, optionFlags)) + ->enterModalState (true, 0, true); } END_JUCE_NAMESPACE @@ -58309,6 +58332,7 @@ const Array& FileChooser::getResults() const return results; } +#if JUCE_MODAL_LOOPS_PERMITTED bool FileChooser::showDialog (const bool selectsDirectories, const bool selectsFiles, const bool isSave, @@ -58380,6 +58404,7 @@ bool FileChooser::showDialog (const bool selectsDirectories, return results.size() > 0; } +#endif FilePreviewComponent::FilePreviewComponent() { @@ -58396,6 +58421,70 @@ END_JUCE_NAMESPACE /*** Start of inlined file: juce_FileChooserDialogBox.cpp ***/ BEGIN_JUCE_NAMESPACE +class FileChooserDialogBox::ContentComponent : public Component +{ +public: + + ContentComponent (const String& name, const String& instructions_, FileBrowserComponent& chooserComponent_) + : Component (name), + chooserComponent (chooserComponent_), + okButton (chooserComponent_.getActionVerb()), + cancelButton (TRANS ("Cancel")), + newFolderButton (TRANS ("New Folder")), + instructions (instructions_) + { + addAndMakeVisible (&chooserComponent); + + addAndMakeVisible (&okButton); + okButton.addShortcut (KeyPress::returnKey); + + addAndMakeVisible (&cancelButton); + cancelButton.addShortcut (KeyPress::escapeKey); + + addChildComponent (&newFolderButton); + + setInterceptsMouseClicks (false, true); + } + + void paint (Graphics& g) + { + g.setColour (getLookAndFeel().findColour (FileChooserDialogBox::titleTextColourId)); + text.draw (g); + } + + void resized() + { + const int buttonHeight = 26; + + Rectangle area (getLocalBounds()); + + getLookAndFeel().createFileChooserHeaderText (getName(), instructions, text, getWidth()); + const Rectangle bb (text.getBoundingBox (0, text.getNumGlyphs(), false)); + area.removeFromTop (roundToInt (bb.getBottom()) + 10); + + chooserComponent.setBounds (area.removeFromTop (area.getHeight() - buttonHeight - 20)); + Rectangle buttonArea (area.reduced (16, 10)); + + okButton.changeWidthToFitText (buttonHeight); + okButton.setBounds (buttonArea.removeFromRight (okButton.getWidth() + 16)); + + buttonArea.removeFromRight (16); + + cancelButton.changeWidthToFitText (buttonHeight); + cancelButton.setBounds (buttonArea.removeFromRight (cancelButton.getWidth())); + + newFolderButton.changeWidthToFitText (buttonHeight); + newFolderButton.setBounds (buttonArea.removeFromLeft (newFolderButton.getWidth())); + } + + FileBrowserComponent& chooserComponent; + TextButton okButton, cancelButton, newFolderButton; + +private: + String instructions; + GlyphArrangement text; +}; + FileChooserDialogBox::FileChooserDialogBox (const String& name, const String& instructions, FileBrowserComponent& chooserComponent, @@ -58452,6 +58541,15 @@ bool FileChooserDialogBox::showAt (int x, int y, int w, int h) return ok; } +void FileChooserDialogBox::centreWithDefaultSize (Component* componentToCentreAround) +{ + Component* const previewComp = content->chooserComponent.getPreviewComponent(); + + centreAroundComponent (componentToCentreAround, + previewComp != 0 ? 400 + previewComp->getWidth() : 600, + 500); +} + void FileChooserDialogBox::buttonClicked (Button* button) { if (button == &(content->okButton)) @@ -58491,107 +58589,82 @@ void FileChooserDialogBox::fileDoubleClicked (const File&) content->okButton.triggerClick(); } +void FileChooserDialogBox::okToOverwriteFileCallback (int result, FileChooserDialogBox* box) +{ + if (result != 0 && box != 0) + box->exitModalState (1); +} + void FileChooserDialogBox::okButtonPressed() { - if ((! (warnAboutOverwritingExistingFiles - && content->chooserComponent.isSaveMode() - && content->chooserComponent.getSelectedFile(0).exists())) - || AlertWindow::showOkCancelBox (AlertWindow::WarningIcon, + if (warnAboutOverwritingExistingFiles + && content->chooserComponent.isSaveMode() + && content->chooserComponent.getSelectedFile(0).exists()) + { + AlertWindow::showOkCancelBox (AlertWindow::WarningIcon, TRANS("File already exists"), TRANS("There's already a file called:") + "\n\n" + content->chooserComponent.getSelectedFile(0).getFullPathName() + "\n\n" + TRANS("Are you sure you want to overwrite it?"), TRANS("overwrite"), - TRANS("cancel"))) + TRANS("cancel"), + this, + ModalCallbackFunction::forComponent (okToOverwriteFileCallback, this)); + } + else { exitModalState (1); } } -void FileChooserDialogBox::createNewFolder() +void FileChooserDialogBox::createNewFolderCallback (int result, FileChooserDialogBox* box, + Component::SafePointer alert) { - File parent (content->chooserComponent.getRoot()); - - if (parent.isDirectory()) + if (result != 0 && alert != 0 && box != 0) { - AlertWindow aw (TRANS("New Folder"), - TRANS("Please enter the name for the folder"), - AlertWindow::NoIcon, this); - - aw.addTextEditor ("name", String::empty, String::empty, false); - aw.addButton (TRANS("ok"), 1, KeyPress::returnKey); - aw.addButton (TRANS("cancel"), KeyPress::escapeKey); - - if (aw.runModalLoop() != 0) - { - aw.setVisible (false); - - const String name (File::createLegalFileName (aw.getTextEditorContents ("name"))); - - if (! name.isEmpty()) - { - if (! parent.getChildFile (name).createDirectory()) - { - AlertWindow::showMessageBox (AlertWindow::WarningIcon, - TRANS ("New Folder"), - TRANS ("Couldn't create the folder!")); - } - - content->chooserComponent.refresh(); - } - } + alert->setVisible (false); + box->createNewFolderConfirmed (alert->getTextEditorContents ("name")); } } -FileChooserDialogBox::ContentComponent::ContentComponent (const String& name, const String& instructions_, FileBrowserComponent& chooserComponent_) - : Component (name), instructions (instructions_), - chooserComponent (chooserComponent_), - okButton (chooserComponent_.getActionVerb()), - cancelButton (TRANS ("Cancel")), - newFolderButton (TRANS ("New Folder")) +void FileChooserDialogBox::createNewFolder() { - addAndMakeVisible (&chooserComponent); - - addAndMakeVisible (&okButton); - okButton.addShortcut (KeyPress::returnKey); - - addAndMakeVisible (&cancelButton); - cancelButton.addShortcut (KeyPress::escapeKey); + File parent (content->chooserComponent.getRoot()); - addChildComponent (&newFolderButton); + if (parent.isDirectory()) + { + AlertWindow* aw = new AlertWindow (TRANS("New Folder"), + TRANS("Please enter the name for the folder"), + AlertWindow::NoIcon, this); - setInterceptsMouseClicks (false, true); -} + aw->addTextEditor ("name", String::empty, String::empty, false); + aw->addButton (TRANS("ok"), 1, KeyPress::returnKey); + aw->addButton (TRANS("cancel"), KeyPress::escapeKey); -void FileChooserDialogBox::ContentComponent::paint (Graphics& g) -{ - g.setColour (getLookAndFeel().findColour (FileChooserDialogBox::titleTextColourId)); - text.draw (g); + aw->enterModalState (true, + ModalCallbackFunction::forComponent (createNewFolderCallback, this, + Component::SafePointer (aw)), + true); + } } -void FileChooserDialogBox::ContentComponent::resized() +void FileChooserDialogBox::createNewFolderConfirmed (const String& nameFromDialog) { - const int buttonHeight = 26; - - Rectangle area (getLocalBounds()); - - getLookAndFeel().createFileChooserHeaderText (getName(), instructions, text, getWidth()); - const Rectangle bb (text.getBoundingBox (0, text.getNumGlyphs(), false)); - area.removeFromTop (roundToInt (bb.getBottom()) + 10); - - chooserComponent.setBounds (area.removeFromTop (area.getHeight() - buttonHeight - 20)); - Rectangle buttonArea (area.reduced (16, 10)); + const String name (File::createLegalFileName (nameFromDialog)); - okButton.changeWidthToFitText (buttonHeight); - okButton.setBounds (buttonArea.removeFromRight (okButton.getWidth() + 16)); - - buttonArea.removeFromRight (16); + if (! name.isEmpty()) + { + const File parent (content->chooserComponent.getRoot()); - cancelButton.changeWidthToFitText (buttonHeight); - cancelButton.setBounds (buttonArea.removeFromRight (cancelButton.getWidth())); + if (! parent.getChildFile (name).createDirectory()) + { + AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, + TRANS ("New Folder"), + TRANS ("Couldn't create the folder!")); + } - newFolderButton.changeWidthToFitText (buttonHeight); - newFolderButton.setBounds (buttonArea.removeFromLeft (newFolderButton.getWidth())); + content->chooserComponent.refresh(); + } } END_JUCE_NAMESPACE @@ -59862,6 +59935,19 @@ public: keyNum >= 0 ? getName() : String::empty); } + static void menuCallback (int result, ChangeKeyButton* button) + { + if (button != 0) + { + switch (result) + { + case 1: button->assignNewKey(); break; + case 2: button->owner.getMappings().removeKeyPress (button->commandID, button->keyNum); break; + default: break; + } + } + } + void clicked() { if (keyNum >= 0) @@ -59872,12 +59958,8 @@ public: m.addSeparator(); m.addItem (2, TRANS("remove this key-mapping")); - switch (m.show()) - { - case 1: assignNewKey(); break; - case 2: owner.getMappings().removeKeyPress (commandID, keyNum); break; - default: break; - } + m.showMenuAsync (PopupMenu::Options(), + ModalCallbackFunction::forComponent (menuCallback, this)); } else { @@ -59946,42 +60028,65 @@ public: JUCE_DECLARE_NON_COPYABLE (KeyEntryWindow); }; - void assignNewKey() + static void assignNewKeyCallback (int result, ChangeKeyButton* button, KeyPress newKey) { - KeyEntryWindow entryWindow (owner); + if (result != 0 && button != 0) + button->setNewKey (newKey, true); + } - if (entryWindow.runModalLoop() != 0) + void setNewKey (const KeyPress& newKey, bool dontAskUser) + { + if (newKey.isValid()) { - entryWindow.setVisible (false); + const CommandID previousCommand = owner.getMappings().findCommandForKeyPress (newKey); - if (entryWindow.lastPress.isValid()) + if (previousCommand == 0 || dontAskUser) { - const CommandID previousCommand = owner.getMappings().findCommandForKeyPress (entryWindow.lastPress); + owner.getMappings().removeKeyPress (newKey); - if (previousCommand == 0 - || AlertWindow::showOkCancelBox (AlertWindow::WarningIcon, - TRANS("Change key-mapping"), - TRANS("This key is already assigned to the command \"") - + owner.getMappings().getCommandManager()->getNameOfCommand (previousCommand) - + TRANS("\"\n\nDo you want to re-assign it to this new command instead?"), - TRANS("Re-assign"), - TRANS("Cancel"))) - { - owner.getMappings().removeKeyPress (entryWindow.lastPress); - - if (keyNum >= 0) - owner.getMappings().removeKeyPress (commandID, keyNum); + if (keyNum >= 0) + owner.getMappings().removeKeyPress (commandID, keyNum); - owner.getMappings().addKeyPress (commandID, entryWindow.lastPress, keyNum); - } + owner.getMappings().addKeyPress (commandID, newKey, keyNum); + } + else + { + AlertWindow::showOkCancelBox (AlertWindow::WarningIcon, + TRANS("Change key-mapping"), + TRANS("This key is already assigned to the command \"") + + owner.getMappings().getCommandManager()->getNameOfCommand (previousCommand) + + TRANS("\"\n\nDo you want to re-assign it to this new command instead?"), + TRANS("Re-assign"), + TRANS("Cancel"), + this, + ModalCallbackFunction::forComponent (assignNewKeyCallback, + this, KeyPress (newKey))); } } } + static void keyChosen (int result, ChangeKeyButton* button) + { + if (result != 0 && button != 0 && button->currentKeyEntryWindow != 0) + { + button->currentKeyEntryWindow->setVisible (false); + button->setNewKey (button->currentKeyEntryWindow->lastPress, false); + } + + button->currentKeyEntryWindow = 0; + } + + void assignNewKey() + { + currentKeyEntryWindow = new KeyEntryWindow (owner); + currentKeyEntryWindow->enterModalState (true, ModalCallbackFunction::forComponent (keyChosen, this)); + } + private: KeyMappingEditorComponent& owner; const CommandID commandID; const int keyNum; + ScopedPointer currentKeyEntryWindow; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChangeKeyButton); }; @@ -60163,15 +60268,21 @@ public: } } + static void resetToDefaultsCallback (int result, KeyMappingEditorComponent* owner) + { + if (result != 0 && owner != 0) + owner->getMappings().resetToDefaultMappings(); + } + void buttonClicked (Button*) { - if (AlertWindow::showOkCancelBox (AlertWindow::QuestionIcon, - TRANS("Reset to defaults"), - TRANS("Are you sure you want to reset all the key-mappings to their default state?"), - TRANS("Reset"))) - { - owner.getMappings().resetToDefaultMappings(); - } + AlertWindow::showOkCancelBox (AlertWindow::QuestionIcon, + TRANS("Reset to defaults"), + TRANS("Are you sure you want to reset all the key-mappings to their default state?"), + TRANS("Reset"), + String::empty, + &owner, + ModalCallbackFunction::forComponent (resetToDefaultsCallback, &owner)); } private: @@ -64102,6 +64213,12 @@ void TabbedButtonBar::setTabBackgroundColour (const int tabIndex, const Colour& } } +void TabbedButtonBar::extraItemsMenuCallback (int result, TabbedButtonBar* bar) +{ + if (bar != 0 && result > 0) + bar->setCurrentTabIndex (result - 1); +} + void TabbedButtonBar::showExtraItemsMenu() { PopupMenu m; @@ -64114,10 +64231,8 @@ void TabbedButtonBar::showExtraItemsMenu() m.addItem (i + 1, tab->name, true, i == currentTabIndex); } - const int res = m.showAt (extraTabsButton); - - if (res != 0) - setCurrentTabIndex (res - 1); + m.showMenuAsync (PopupMenu::Options().withTargetComponent (extraTabsButton), + ModalCallbackFunction::forComponent (extraItemsMenuCallback, this)); } void TabbedButtonBar::currentTabChanged (const int, const String&) @@ -68509,27 +68624,6 @@ 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_) - { - } - - void modalStateFinished (int returnValue) - { - if (bar != 0) - bar->menuDismissed (topLevelIndex, returnValue); - } - -private: - Component::SafePointer bar; - const int topLevelIndex; - - JUCE_DECLARE_NON_COPYABLE (AsyncCallback); -}; - void MenuBarComponent::showMenu (int index) { if (index != currentPopupIndex) @@ -68550,13 +68644,20 @@ void MenuBarComponent::showMenu (int index) const Rectangle itemPos (xPositions [index], 0, xPositions [index + 1] - xPositions [index], getHeight()); - m.showMenu (localAreaToGlobal (itemPos), - 0, itemPos.getWidth(), 0, 0, this, - new AsyncCallback (this, index)); + m.showMenuAsync (PopupMenu::Options().withTargetComponent (this) + .withTargetScreenArea (localAreaToGlobal (itemPos)) + .withMinimumWidth (itemPos.getWidth()), + ModalCallbackFunction::forComponent (menuBarMenuDismissedCallback, this, index)); } } } +void MenuBarComponent::menuBarMenuDismissedCallback (int result, MenuBarComponent* bar, int topLevelIndex) +{ + if (bar != 0) + bar->menuDismissed (topLevelIndex, result); +} + void MenuBarComponent::menuDismissed (int topLevelIndex, int itemId) { topLevelIndexClicked = topLevelIndex; @@ -68968,6 +69069,8 @@ namespace PopupMenuSettings const int borderSize = 2; const int timerInterval = 50; const int dismissCommandId = 0x6287345f; + + static bool menuWasHiddenBecauseOfAppChange = false; } class PopupMenu::Window : public Component, @@ -69353,7 +69456,7 @@ public: { if (now > lastFocused + 10) { - wasHiddenBecauseOfAppChange() = true; + PopupMenuSettings::menuWasHiddenBecauseOfAppChange = true; dismissMenu (0); return; // may have been deleted by the previous call.. @@ -69388,12 +69491,6 @@ public: return activeMenuWindows; } - static bool& wasHiddenBecauseOfAppChange() throw() - { - static bool b = false; - return b; - } - private: Window* owner; OwnedArray items; @@ -70108,13 +70205,81 @@ void PopupMenu::addSectionHeader (const String& title) addCustomItem (0X4734a34f, new HeaderItemComponent (title)); } +PopupMenu::Options::Options() + : targetComponent (0), + visibleItemID (0), + minWidth (0), + maxColumns (0), + standardHeight (0) +{ + targetArea.setPosition (Desktop::getMousePosition()); +} + +const PopupMenu::Options PopupMenu::Options::withTargetComponent (Component* comp) const +{ + Options o (*this); + o.targetComponent = comp; + + if (comp != 0) + o.targetArea = comp->getScreenBounds(); + + return o; +} + +const PopupMenu::Options PopupMenu::Options::withTargetScreenArea (const Rectangle& area) const +{ + Options o (*this); + o.targetArea = area; + return o; +} + +const PopupMenu::Options PopupMenu::Options::withMinimumWidth (int w) const +{ + Options o (*this); + o.minWidth = w; + return o; +} + +const PopupMenu::Options PopupMenu::Options::withMaximumNumColumns (int cols) const +{ + Options o (*this); + o.maxColumns = cols; + return o; +} + +const PopupMenu::Options PopupMenu::Options::withStandardItemHeight (int height) const +{ + Options o (*this); + o.standardHeight = height; + return o; +} + +const PopupMenu::Options PopupMenu::Options::withItemThatMustBeVisible (int idOfItemToBeVisible) const +{ + Options o (*this); + o.visibleItemID = idOfItemToBeVisible; + return o; +} + +Component* PopupMenu::createWindow (const Options& options, + ApplicationCommandManager** managerOfChosenCommand) const +{ + return Window::create (*this, ModifierKeys::getCurrentModifiers().isAnyMouseButtonDown(), + 0, options.targetArea, options.minWidth, options.maxColumns > 0 ? options.maxColumns : 7, + options.standardHeight, ! options.targetArea.isEmpty(), options.visibleItemID, + managerOfChosenCommand, options.targetComponent); +} + // This invokes any command manager commands and deletes the menu window when it is dismissed class PopupMenuCompletionCallback : public ModalComponentManager::Callback { public: PopupMenuCompletionCallback() - : managerOfChosenCommand (0) + : managerOfChosenCommand (0), + prevFocused (Component::getCurrentlyFocusedComponent()), + prevTopLevel (prevFocused != 0 ? prevFocused->getTopLevelComponent() : 0) { + PopupMenuSettings::menuWasHiddenBecauseOfAppChange = false; } void modalStateFinished (int result) @@ -70129,63 +70294,60 @@ public: // (this would be the place to fade out the component, if that's what's required) component = 0; + + if (! PopupMenuSettings::menuWasHiddenBecauseOfAppChange) + { + if (prevTopLevel != 0) + prevTopLevel->toFront (true); + + if (prevFocused != 0) + prevFocused->grabKeyboardFocus(); + } } ApplicationCommandManager* managerOfChosenCommand; ScopedPointer component; + WeakReference prevFocused, prevTopLevel; private: JUCE_DECLARE_NON_COPYABLE (PopupMenuCompletionCallback); }; -int PopupMenu::showMenu (const Rectangle& target, - const int itemIdThatMustBeVisible, - const int minimumWidth, - const int maximumNumColumns, - const int standardItemHeight, - Component* const componentAttachedTo, - ModalComponentManager::Callback* userCallback) +int PopupMenu::showWithOptionalCallback (const Options& options, ModalComponentManager::Callback* const userCallback, + const bool canBeModal) { ScopedPointer userCallbackDeleter (userCallback); + ScopedPointer callback (new PopupMenuCompletionCallback()); - WeakReference prevFocused (Component::getCurrentlyFocusedComponent()); - WeakReference prevTopLevel ((prevFocused != 0) ? prevFocused->getTopLevelComponent() : 0); - Window::wasHiddenBecauseOfAppChange() = false; - - PopupMenuCompletionCallback* callback = new PopupMenuCompletionCallback(); - ScopedPointer callbackDeleter (callback); - - callback->component = Window::create (*this, ModifierKeys::getCurrentModifiers().isAnyMouseButtonDown(), - 0, target, minimumWidth, maximumNumColumns > 0 ? maximumNumColumns : 7, - standardItemHeight, ! target.isEmpty(), itemIdThatMustBeVisible, - &callback->managerOfChosenCommand, componentAttachedTo); - - if (callback->component == 0) + Component* window = createWindow (options, &(callback->managerOfChosenCommand)); + if (window == 0) return 0; - 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.. + callback->component = window; - ModalComponentManager::getInstance()->attachCallback (callback->component, callback); + window->enterModalState (false, userCallbackDeleter.release()); + ModalComponentManager::getInstance()->attachCallback (window, callback.release()); - callbackDeleter.release(); + window->toFront (false); // need to do this after making it modal, or it could + // be stuck behind other comps that are already modal.. - if (userCallback != 0) - return 0; - - const int result = callback->component->runModalLoop(); + return (userCallback == 0 && canBeModal) ? window->runModalLoop() : 0; +} - if (! Window::wasHiddenBecauseOfAppChange()) - { - if (prevTopLevel != 0) - prevTopLevel->toFront (true); +#if JUCE_MODAL_LOOPS_PERMITTED +int PopupMenu::showMenu (const Options& options) +{ + return showWithOptionalCallback (options, 0, true); +} +#endif - if (prevFocused != 0) - prevFocused->grabKeyboardFocus(); - } +void PopupMenu::showMenuAsync (const Options& options, ModalComponentManager::Callback* userCallback) +{ + #if ! JUCE_MODAL_LOOPS_PERMITTED + jassert (userCallback != 0); + #endif - return result; + showWithOptionalCallback (options, userCallback, false); } int PopupMenu::show (const int itemIdThatMustBeVisible, @@ -70193,9 +70355,11 @@ int PopupMenu::show (const int itemIdThatMustBeVisible, const int standardItemHeight, ModalComponentManager::Callback* callback) { - return showMenu (Rectangle().withPosition (Desktop::getMousePosition()), - itemIdThatMustBeVisible, minimumWidth, maximumNumColumns, - standardItemHeight, 0, callback); + return showWithOptionalCallback (Options().withItemThatMustBeVisible (itemIdThatMustBeVisible) + .withMinimumWidth (minimumWidth) + .withMaximumNumColumns (maximumNumColumns) + .withStandardItemHeight (standardItemHeight), + callback, true); } int PopupMenu::showAt (const Rectangle& screenAreaToAttachTo, @@ -70204,9 +70368,12 @@ int PopupMenu::showAt (const Rectangle& screenAreaToAttachTo, const int standardItemHeight, ModalComponentManager::Callback* callback) { - return showMenu (screenAreaToAttachTo, - itemIdThatMustBeVisible, minimumWidth, maximumNumColumns, - standardItemHeight, 0, callback); + return showWithOptionalCallback (Options().withTargetScreenArea (screenAreaToAttachTo) + .withItemThatMustBeVisible (itemIdThatMustBeVisible) + .withMinimumWidth (minimumWidth) + .withMaximumNumColumns (maximumNumColumns) + .withStandardItemHeight (standardItemHeight), + callback, true); } int PopupMenu::showAt (Component* componentToAttachTo, @@ -70215,17 +70382,15 @@ int PopupMenu::showAt (Component* componentToAttachTo, const int standardItemHeight, ModalComponentManager::Callback* callback) { + Options options (Options().withItemThatMustBeVisible (itemIdThatMustBeVisible) + .withMinimumWidth (minimumWidth) + .withMaximumNumColumns (maximumNumColumns) + .withStandardItemHeight (standardItemHeight)); + if (componentToAttachTo != 0) - { - return showMenu (componentToAttachTo->getScreenBounds(), - itemIdThatMustBeVisible, minimumWidth, maximumNumColumns, - standardItemHeight, componentToAttachTo, callback); - } - else - { - return show (itemIdThatMustBeVisible, minimumWidth, maximumNumColumns, - standardItemHeight, callback); - } + options = options.withTargetComponent (componentToAttachTo); + + return showWithOptionalCallback (options, callback, true); } bool JUCE_CALLTYPE PopupMenu::dismissAllActiveMenus() @@ -72823,9 +72988,9 @@ public: if (error.isNotEmpty()) { - AlertWindow::showMessageBox (AlertWindow::WarningIcon, - "Error when trying to open audio device!", - error); + AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, + "Error when trying to open audio device!", + error); } } @@ -75827,7 +75992,7 @@ void PreferencesPanel::addSettingsPage (const String& title, const void* imageDa void PreferencesPanel::showInDialogBox (const String& dialogTitle, int dialogWidth, int dialogHeight, const Colour& backgroundColour) { setSize (dialogWidth, dialogHeight); - DialogWindow::showModalDialog (dialogTitle, this, 0, backgroundColour, false); + DialogWindow::showDialog (dialogTitle, this, 0, backgroundColour, false); } void PreferencesPanel::resized() @@ -76467,24 +76632,29 @@ class AlertWindowInfo { public: AlertWindowInfo (const String& title_, const String& message_, Component* component, - AlertWindow::AlertIconType iconType_, int numButtons_) + AlertWindow::AlertIconType iconType_, int numButtons_, + ModalComponentManager::Callback* callback_, bool modal_) : title (title_), message (message_), iconType (iconType_), - numButtons (numButtons_), returnValue (0), associatedComponent (component) + numButtons (numButtons_), returnValue (0), associatedComponent (component), + callback (callback_), modal (modal_) { } String title, message, button1, button2, button3; - AlertWindow::AlertIconType iconType; - int numButtons, returnValue; - WeakReference associatedComponent; - int showModal() const + int invoke() const { MessageManager::getInstance()->callFunctionOnMessageThread (showCallback, (void*) this); return returnValue; } private: + AlertWindow::AlertIconType iconType; + int numButtons, returnValue; + WeakReference associatedComponent; + ModalComponentManager::Callback* callback; + bool modal; + void show() { LookAndFeel& lf = associatedComponent != 0 ? associatedComponent->getLookAndFeel() @@ -76495,7 +76665,17 @@ private: jassert (alertBox != 0); // you have to return one of these! - returnValue = alertBox->runModalLoop(); + #if JUCE_MODAL_LOOPS_PERMITTED + if (modal) + { + returnValue = alertBox->runModalLoop(); + } + else + #endif + { + alertBox->enterModalState (true, callback, true); + alertBox.release(); + } } static void* showCallback (void* userData) @@ -76505,16 +76685,30 @@ private: } }; +#if JUCE_MODAL_LOOPS_PERMITTED void AlertWindow::showMessageBox (AlertIconType iconType, const String& title, const String& message, const String& buttonText, Component* associatedComponent) { - AlertWindowInfo info (title, message, associatedComponent, iconType, 1); + AlertWindowInfo info (title, message, associatedComponent, iconType, 1, 0, true); info.button1 = buttonText.isEmpty() ? TRANS("ok") : buttonText; - info.showModal(); + info.invoke(); +} +#endif + +void AlertWindow::showMessageBoxAsync (AlertIconType iconType, + const String& title, + const String& message, + const String& buttonText, + Component* associatedComponent) +{ + AlertWindowInfo info (title, message, associatedComponent, iconType, 1, 0, false); + info.button1 = buttonText.isEmpty() ? TRANS("ok") : buttonText; + + info.invoke(); } bool AlertWindow::showOkCancelBox (AlertIconType iconType, @@ -76522,13 +76716,14 @@ bool AlertWindow::showOkCancelBox (AlertIconType iconType, const String& message, const String& button1Text, const String& button2Text, - Component* associatedComponent) + Component* associatedComponent, + ModalComponentManager::Callback* callback) { - AlertWindowInfo info (title, message, associatedComponent, iconType, 2); + AlertWindowInfo info (title, message, associatedComponent, iconType, 2, callback, callback == 0); info.button1 = button1Text.isEmpty() ? TRANS("ok") : button1Text; info.button2 = button2Text.isEmpty() ? TRANS("cancel") : button2Text; - return info.showModal() != 0; + return info.invoke() != 0; } int AlertWindow::showYesNoCancelBox (AlertIconType iconType, @@ -76537,14 +76732,15 @@ int AlertWindow::showYesNoCancelBox (AlertIconType iconType, const String& button1Text, const String& button2Text, const String& button3Text, - Component* associatedComponent) + Component* associatedComponent, + ModalComponentManager::Callback* callback) { - AlertWindowInfo info (title, message, associatedComponent, iconType, 3); + AlertWindowInfo info (title, message, associatedComponent, iconType, 3, callback, callback == 0); info.button1 = button1Text.isEmpty() ? TRANS("yes") : button1Text; info.button2 = button2Text.isEmpty() ? TRANS("no") : button2Text; info.button3 = button3Text.isEmpty() ? TRANS("cancel") : button3Text; - return info.showModal(); + return info.invoke(); } END_JUCE_NAMESPACE @@ -77346,16 +77542,29 @@ void DialogWindow::resized() } } -// (Sadly, this can't be made a local class inside the showModalDialog function, because the -// VC compiler complains about the undefined copy constructor) class TempDialogWindow : public DialogWindow { public: - TempDialogWindow (const String& title, const Colour& colour, const bool escapeCloses) - : DialogWindow (title, colour, escapeCloses, true) + TempDialogWindow (const String& title, + Component* contentComponent, + Component* componentToCentreAround, + const Colour& colour, + const bool escapeKeyTriggersCloseButton, + const bool shouldBeResizable, + const bool useBottomRightCornerResizer) + : DialogWindow (title, colour, escapeKeyTriggersCloseButton, true) { if (! JUCEApplication::isStandaloneApp()) setAlwaysOnTop (true); // for a plugin, make it always-on-top because the host windows are often top-level + + setContentComponent (contentComponent, true, true); + centreAroundComponent (componentToCentreAround, getWidth(), getHeight()); + setResizable (shouldBeResizable, useBottomRightCornerResizer); + } + + ~TempDialogWindow() + { + setContentComponent (0, false); } void closeButtonPressed() @@ -77367,23 +77576,37 @@ private: JUCE_DECLARE_NON_COPYABLE (TempDialogWindow); }; +void DialogWindow::showDialog (const String& dialogTitle, + Component* const contentComponent, + Component* const componentToCentreAround, + const Colour& backgroundColour, + const bool escapeKeyTriggersCloseButton, + const bool shouldBeResizable, + const bool useBottomRightCornerResizer) +{ + TempDialogWindow* dw = new TempDialogWindow (dialogTitle, contentComponent, componentToCentreAround, + backgroundColour, escapeKeyTriggersCloseButton, + shouldBeResizable, useBottomRightCornerResizer); + + dw->enterModalState (true, 0, true); +} + +#if JUCE_MODAL_LOOPS_PERMITTED int DialogWindow::showModalDialog (const String& dialogTitle, - Component* contentComponent, - Component* componentToCentreAround, - const Colour& colour, + Component* const contentComponent, + Component* const componentToCentreAround, + const Colour& backgroundColour, const bool escapeKeyTriggersCloseButton, const bool shouldBeResizable, const bool useBottomRightCornerResizer) { - TempDialogWindow dw (dialogTitle, colour, escapeKeyTriggersCloseButton); + TempDialogWindow dw (dialogTitle, contentComponent, componentToCentreAround, + backgroundColour, escapeKeyTriggersCloseButton, + shouldBeResizable, useBottomRightCornerResizer); - dw.setContentComponent (contentComponent, true, true); - dw.centreAroundComponent (componentToCentreAround, dw.getWidth(), dw.getHeight()); - dw.setResizable (shouldBeResizable, useBottomRightCornerResizer); - const int result = dw.runModalLoop(); - dw.setContentComponent (0, false); - return result; + return dw.runModalLoop(); } +#endif END_JUCE_NAMESPACE /*** End of inlined file: juce_DialogWindow.cpp ***/ @@ -78345,7 +78568,9 @@ void SplashScreen::show (const String& title, addToDesktop (useDropShadow ? ComponentPeer::windowHasDropShadow : 0); toFront (false); + #if JUCE_MODAL_LOOPS_PERMITTED MessageManager::getInstance()->runDispatchLoopUntil (300); + #endif repaint(); @@ -78406,6 +78631,7 @@ ThreadWithProgressWindow::~ThreadWithProgressWindow() stopThread (timeOutMsWhenCancelling); } +#if JUCE_MODAL_LOOPS_PERMITTED bool ThreadWithProgressWindow::runThread (const int priority) { startThread (priority); @@ -78424,6 +78650,7 @@ bool ThreadWithProgressWindow::runThread (const int priority) return finishedNaturally; } +#endif void ThreadWithProgressWindow::setProgress (const double newProgress) { @@ -236985,101 +237212,111 @@ const Image PNGImageFormat::decodeImage (InputStream& in) if (pngReadStruct != 0) { - pngInfoStruct = png_create_info_struct (pngReadStruct); - - if (pngInfoStruct == 0) + try { - png_destroy_read_struct (&pngReadStruct, 0, 0); - return Image::null; - } + pngInfoStruct = png_create_info_struct (pngReadStruct); - png_set_error_fn (pngReadStruct, 0, PNGHelpers::errorCallback, PNGHelpers::errorCallback ); + if (pngInfoStruct == 0) + { + png_destroy_read_struct (&pngReadStruct, 0, 0); + return Image::null; + } - // read the header.. - png_set_read_fn (pngReadStruct, &in, PNGHelpers::readCallback); + png_set_error_fn (pngReadStruct, 0, PNGHelpers::errorCallback, PNGHelpers::errorCallback ); - png_uint_32 width, height; - int bitDepth, colorType, interlaceType; + // read the header.. + png_set_read_fn (pngReadStruct, &in, PNGHelpers::readCallback); - png_read_info (pngReadStruct, pngInfoStruct); + png_uint_32 width, height; + int bitDepth, colorType, interlaceType; - png_get_IHDR (pngReadStruct, pngInfoStruct, - &width, &height, - &bitDepth, &colorType, - &interlaceType, 0, 0); + png_read_info (pngReadStruct, pngInfoStruct); - if (bitDepth == 16) - png_set_strip_16 (pngReadStruct); + png_get_IHDR (pngReadStruct, pngInfoStruct, + &width, &height, + &bitDepth, &colorType, + &interlaceType, 0, 0); - if (colorType == PNG_COLOR_TYPE_PALETTE) - png_set_expand (pngReadStruct); + if (bitDepth == 16) + png_set_strip_16 (pngReadStruct); - if (bitDepth < 8) - png_set_expand (pngReadStruct); + if (colorType == PNG_COLOR_TYPE_PALETTE) + png_set_expand (pngReadStruct); - if (png_get_valid (pngReadStruct, pngInfoStruct, PNG_INFO_tRNS)) - png_set_expand (pngReadStruct); + if (bitDepth < 8) + png_set_expand (pngReadStruct); - if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) - png_set_gray_to_rgb (pngReadStruct); + if (png_get_valid (pngReadStruct, pngInfoStruct, PNG_INFO_tRNS)) + png_set_expand (pngReadStruct); - png_set_add_alpha (pngReadStruct, 0xff, PNG_FILLER_AFTER); + if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_gray_to_rgb (pngReadStruct); - bool hasAlphaChan = (colorType & PNG_COLOR_MASK_ALPHA) != 0 - || pngInfoStruct->num_trans > 0; + png_set_add_alpha (pngReadStruct, 0xff, PNG_FILLER_AFTER); - // Load the image into a temp buffer in the pnglib format.. - HeapBlock tempBuffer (height * (width << 2)); + bool hasAlphaChan = (colorType & PNG_COLOR_MASK_ALPHA) != 0 + || pngInfoStruct->num_trans > 0; - { - HeapBlock rows (height); - for (int y = (int) height; --y >= 0;) - rows[y] = (png_bytep) (tempBuffer + (width << 2) * y); + // Load the image into a temp buffer in the pnglib format.. + HeapBlock tempBuffer (height * (width << 2)); - png_read_image (pngReadStruct, rows); - png_read_end (pngReadStruct, pngInfoStruct); - } + { + HeapBlock rows (height); + for (int y = (int) height; --y >= 0;) + rows[y] = (png_bytep) (tempBuffer + (width << 2) * y); - png_destroy_read_struct (&pngReadStruct, &pngInfoStruct, 0); + try + { + png_read_image (pngReadStruct, rows); + png_read_end (pngReadStruct, pngInfoStruct); + } + catch (PNGHelpers::PNGErrorStruct&) + {} + } - // now convert the data to a juce image format.. - image = Image (hasAlphaChan ? Image::ARGB : Image::RGB, - (int) width, (int) height, hasAlphaChan); + png_destroy_read_struct (&pngReadStruct, &pngInfoStruct, 0); - image.getProperties()->set ("originalImageHadAlpha", image.hasAlphaChannel()); - hasAlphaChan = image.hasAlphaChannel(); // (the native image creator may not give back what we expect) + // now convert the data to a juce image format.. + image = Image (hasAlphaChan ? Image::ARGB : Image::RGB, + (int) width, (int) height, hasAlphaChan); - const Image::BitmapData destData (image, Image::BitmapData::writeOnly); - uint8* srcRow = tempBuffer; - uint8* destRow = destData.data; + image.getProperties()->set ("originalImageHadAlpha", image.hasAlphaChannel()); + hasAlphaChan = image.hasAlphaChannel(); // (the native image creator may not give back what we expect) - for (int y = 0; y < (int) height; ++y) - { - const uint8* src = srcRow; - srcRow += (width << 2); - uint8* dest = destRow; - destRow += destData.lineStride; + const Image::BitmapData destData (image, Image::BitmapData::writeOnly); + uint8* srcRow = tempBuffer; + uint8* destRow = destData.data; - if (hasAlphaChan) + for (int y = 0; y < (int) height; ++y) { - for (int i = (int) width; --i >= 0;) + const uint8* src = srcRow; + srcRow += (width << 2); + uint8* dest = destRow; + destRow += destData.lineStride; + + if (hasAlphaChan) { - ((PixelARGB*) dest)->setARGB (src[3], src[0], src[1], src[2]); - ((PixelARGB*) dest)->premultiply(); - dest += destData.pixelStride; - src += 4; + for (int i = (int) width; --i >= 0;) + { + ((PixelARGB*) dest)->setARGB (src[3], src[0], src[1], src[2]); + ((PixelARGB*) dest)->premultiply(); + dest += destData.pixelStride; + src += 4; + } } - } - else - { - for (int i = (int) width; --i >= 0;) + else { - ((PixelRGB*) dest)->setARGB (0, src[0], src[1], src[2]); - dest += destData.pixelStride; - src += 4; + for (int i = (int) width; --i >= 0;) + { + ((PixelRGB*) dest)->setARGB (0, src[0], src[1], src[2]); + dest += destData.pixelStride; + src += 4; + } } } } + catch (PNGHelpers::PNGErrorStruct&) + {} } return image; @@ -276024,6 +276261,7 @@ namespace } } +#if JUCE_MODAL_LOOPS_PERMITTED bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor) { jassert (isThisTheMessageThread()); // must only be called by the message thread @@ -276050,6 +276288,7 @@ bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor) return ! quitMessagePosted; } +#endif void MessageManager::doPlatformSpecificInitialisation() { @@ -280757,12 +280996,6 @@ void MessageManager::stopDispatchLoop() quitMessagePosted = true; } -bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor) -{ - juce_dispatchNextMessageOnSystemQueue (false); - return false; -} - #endif /*** End of inlined file: juce_android_Messaging.cpp ***/ diff --git a/juce_amalgamated.h b/juce_amalgamated.h index 5b3edb26b3..eb80726d68 100644 --- a/juce_amalgamated.h +++ b/juce_amalgamated.h @@ -73,7 +73,7 @@ namespace JuceDummyNamespace {} */ #define JUCE_MAJOR_VERSION 1 #define JUCE_MINOR_VERSION 53 -#define JUCE_BUILDNUMBER 29 +#define JUCE_BUILDNUMBER 30 /** Current Juce version number. @@ -773,6 +773,15 @@ namespace JuceDummyNamespace {} #define JUCE_DEPRECATED(functionDef) functionDef #endif +#if JUCE_ANDROID && ! DOXYGEN + #define JUCE_MODAL_LOOPS_PERMITTED 0 +#else + /** Some operating environments don't provide a modal loop mechanism, so this flag can be + used to disable any functions that try to run a modal loop. + */ + #define JUCE_MODAL_LOOPS_PERMITTED 1 +#endif + #endif // __JUCE_PLATFORMDEFS_JUCEHEADER__ /*** End of inlined file: juce_PlatformDefs.h ***/ @@ -29190,6 +29199,9 @@ public: You can register a callback using Component::enterModalState() or ModalComponentManager::attachCallback(). + + For some quick ways of creating callback objects, see the ModalCallbackFunction class. + @see ModalCallbackFunction */ class Callback { @@ -29247,10 +29259,12 @@ public: /** Brings any modal components to the front. */ void bringModalComponentsToFront(); + #if JUCE_MODAL_LOOPS_PERMITTED /** Runs the event loop until the currently topmost modal component is dismissed, and returns the exit code for that component. */ int runEventLoopForCurrentComponent(); + #endif juce_DeclareSingleton_SingleThreaded_Minimal (ModalComponentManager); @@ -29267,6 +29281,7 @@ protected: void handleAsyncUpdate(); private: + class ModalItem; class ReturnValueRetriever; @@ -29274,13 +29289,224 @@ private: friend class OwnedArray ; OwnedArray stack; - void startModal (Component* component, Callback* callback); + void startModal (Component* component); void endModal (Component* component, int returnValue); void endModal (Component* component); JUCE_DECLARE_NON_COPYABLE (ModalComponentManager); }; +/** + This class provides some handy utility methods for creating ModalComponentManager::Callback + objects that will invoke a static function with some parameters when a modal component is dismissed. +*/ +class ModalCallbackFunction +{ +public: + + /** This is a utility function to create a ModalComponentManager::Callback that will + call a static function with a parameter. + + The function that you supply must take two parameters - the first being an int, which is + the result code that was used when the modal component was dismissed, and the second + can be a custom type. Note that this custom value will be copied and stored, so it must + be a primitive type or a class that provides copy-by-value semantics. + + E.g. @code + static void myCallbackFunction (int modalResult, double customValue) + { + if (modalResult == 1) + doSomethingWith (customValue); + } + + Component* someKindOfComp; + ... + someKindOfComp->enterModalState (ModalCallbackFunction::create (myCallbackFunction, 3.0)); + @endcode + @see ModalComponentManager::Callback + */ + template + static ModalComponentManager::Callback* create (void (*functionToCall) (int, ParamType), + ParamType parameterValue) + { + return new FunctionCaller1 (functionToCall, parameterValue); + } + + /** This is a utility function to create a ModalComponentManager::Callback that will + call a static function with two custom parameters. + + The function that you supply must take three parameters - the first being an int, which is + the result code that was used when the modal component was dismissed, and the next two are + your custom types. Note that these custom values will be copied and stored, so they must + be primitive types or classes that provide copy-by-value semantics. + + E.g. @code + static void myCallbackFunction (int modalResult, double customValue1, String customValue2) + { + if (modalResult == 1) + doSomethingWith (customValue1, customValue2); + } + + Component* someKindOfComp; + ... + someKindOfComp->enterModalState (ModalCallbackFunction::create (myCallbackFunction, 3.0, String ("xyz"))); + @endcode + @see ModalComponentManager::Callback + */ + template + static ModalComponentManager::Callback* withParam (void (*functionToCall) (int, ParamType1, ParamType2), + ParamType1 parameterValue1, + ParamType2 parameterValue2) + { + return new FunctionCaller2 (functionToCall, parameterValue1, parameterValue2); + } + + /** This is a utility function to create a ModalComponentManager::Callback that will + call a static function with a component. + + The function that you supply must take two parameters - the first being an int, which is + the result code that was used when the modal component was dismissed, and the second + can be a Component class. The component will be stored as a WeakReference, so that if it gets + deleted before this callback is invoked, the pointer that is passed to the function will be null. + + E.g. @code + static void myCallbackFunction (int modalResult, Slider* mySlider) + { + if (modalResult == 1 && mySlider != 0) // (must check that mySlider isn't null in case it was deleted..) + mySlider->setValue (0.0); + } + + Component* someKindOfComp; + Slider* mySlider; + ... + someKindOfComp->enterModalState (ModalCallbackFunction::forComponent (myCallbackFunction, mySlider)); + @endcode + @see ModalComponentManager::Callback + */ + template + static ModalComponentManager::Callback* forComponent (void (*functionToCall) (int, ComponentType*), + ComponentType* component) + { + return new ComponentCaller1 (functionToCall, component); + } + + /** Creates a ModalComponentManager::Callback that will call a static function with a component. + + The function that you supply must take three parameters - the first being an int, which is + the result code that was used when the modal component was dismissed, the second being a Component + class, and the third being a custom type (which must be a primitive type or have copy-by-value semantics). + The component will be stored as a WeakReference, so that if it gets deleted before this callback is + invoked, the pointer that is passed into the function will be null. + + E.g. @code + static void myCallbackFunction (int modalResult, Slider* mySlider, String customParam) + { + if (modalResult == 1 && mySlider != 0) // (must check that mySlider isn't null in case it was deleted..) + mySlider->setName (customParam); + } + + Component* someKindOfComp; + Slider* mySlider; + ... + someKindOfComp->enterModalState (ModalCallbackFunction::forComponent (myCallbackFunction, mySlider, String ("hello"))); + @endcode + @see ModalComponentManager::Callback + */ + template + static ModalComponentManager::Callback* forComponent (void (*functionToCall) (int, ComponentType*, ParamType), + ComponentType* component, + ParamType param) + { + return new ComponentCaller2 (functionToCall, component, param); + } + +private: + + template + class FunctionCaller1 : public ModalComponentManager::Callback + { + public: + typedef void (*FunctionType) (int, ParamType); + + FunctionCaller1 (FunctionType& function_, ParamType& param_) + : function (function_), param (param_) {} + + void modalStateFinished (int returnValue) { function (returnValue, param); } + + private: + const FunctionType function; + ParamType param; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FunctionCaller1); + }; + + template + class FunctionCaller2 : public ModalComponentManager::Callback + { + public: + typedef void (*FunctionType) (int, ParamType1, ParamType2); + + FunctionCaller2 (FunctionType& function_, ParamType1& param1_, ParamType2& param2_) + : function (function_), param1 (param1_), param2 (param2_) {} + + void modalStateFinished (int returnValue) { function (returnValue, param1, param2); } + + private: + const FunctionType function; + ParamType1 param1; + ParamType2 param2; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FunctionCaller2); + }; + + template + class ComponentCaller1 : public ModalComponentManager::Callback + { + public: + typedef void (*FunctionType) (int, ComponentType*); + + ComponentCaller1 (FunctionType& function_, ComponentType* comp_) + : function (function_), comp (comp_) {} + + void modalStateFinished (int returnValue) + { + function (returnValue, static_cast (comp.get())); + } + + private: + const FunctionType function; + WeakReference comp; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComponentCaller1); + }; + + template + class ComponentCaller2 : public ModalComponentManager::Callback + { + public: + typedef void (*FunctionType) (int, ComponentType*, ParamType1); + + ComponentCaller2 (FunctionType& function_, ComponentType* comp_, ParamType1 param1_) + : function (function_), comp (comp_), param1 (param1_) {} + + void modalStateFinished (int returnValue) + { + function (returnValue, static_cast (comp.get()), param1); + } + + private: + const FunctionType function; + WeakReference comp; + ParamType1 param1; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComponentCaller2); + }; + + ModalCallbackFunction(); + ~ModalCallbackFunction(); + JUCE_DECLARE_NON_COPYABLE (ModalCallbackFunction); +}; + #endif // __JUCE_MODALCOMPONENTMANAGER_JUCEHEADER__ /*** End of inlined file: juce_ModalComponentManager.h ***/ @@ -31111,7 +31337,9 @@ public: @see enterModalState, exitModalState, isCurrentlyModal, getCurrentlyModalComponent, isCurrentlyBlockedByAnotherModalComponent, ModalComponentManager */ + #if JUCE_MODAL_LOOPS_PERMITTED int runModalLoop(); + #endif /** Puts the component into a modal state. @@ -31128,10 +31356,15 @@ public: is called. If you pass an object in here, the system will take care of deleting it later, after making the callback + If deleteWhenDismissed is true, then when it is dismissed, the component will be + deleted and then the callback will be called. (This will safely handle the situation + where the component is deleted before its exitModalState() method is called). + @see exitModalState, runModalLoop, ModalComponentManager::attachCallback */ void enterModalState (bool takeKeyboardFocus = true, - ModalComponentManager::Callback* callback = 0); + ModalComponentManager::Callback* callback = 0, + bool deleteWhenDismissed = false); /** Ends a component's modal state. @@ -40520,6 +40753,37 @@ public: /** Returns true if the menu contains any items that can be used. */ bool containsAnyActiveItems() const throw(); + /** Class used to create a set of options to pass to the show() method. + You can chain together a series of calls to this class's methods to create + a set of whatever options you want to specify. + E.g. @code + PopupMenu menu; + ... + menu.showMenu (PopupMenu::Options().withMaximumWidth (100), + .withMaximumNumColumns (3) + .withTargetComponent (myComp)); + @endcode + */ + class JUCE_API Options + { + public: + Options(); + + const Options withTargetComponent (Component* targetComponent) const; + const Options withTargetScreenArea (const Rectangle& targetArea) const; + const Options withMinimumWidth (int minWidth) const; + const Options withMaximumNumColumns (int maxNumColumns) const; + const Options withStandardItemHeight (int standardHeight) const; + const Options withItemThatMustBeVisible (int idOfItemToBeVisible) const; + + private: + friend class PopupMenu; + Rectangle targetArea; + Component* targetComponent; + int visibleItemID, minWidth, maxColumns, standardHeight; + }; + + #if JUCE_MODAL_LOOPS_PERMITTED /** Displays the menu and waits for the user to pick something. This will display the menu modally, and return the ID of the item that the @@ -40592,6 +40856,15 @@ public: int standardItemHeight = 0, ModalComponentManager::Callback* callback = 0); + /** Displays and runs the menu modally, with a set of options. + */ + int showMenu (const Options& options); + #endif + + /** Runs the menu asynchronously, with a user-provided callback that will receive the result. */ + void showMenuAsync (const Options& options, + ModalComponentManager::Callback* callback); + /** Closes any menus that are currently open. This might be useful if you have a situation where your window is being closed @@ -40754,10 +41027,8 @@ private: bool separatorPending; void addSeparatorIfPending(); - - int showMenu (const Rectangle& target, int itemIdThatMustBeVisible, - int minimumWidth, int maximumNumColumns, int standardItemHeight, - Component* componentAttachedTo, ModalComponentManager::Callback* callback); + Component* createWindow (const Options&, ApplicationCommandManager**) const; + int showWithOptionalCallback (const Options&, ModalComponentManager::Callback*, bool); JUCE_LEAK_DETECTOR (PopupMenu); }; @@ -42098,9 +42369,6 @@ private: bool isEnabled : 1, isHeading : 1; }; - class Callback; - friend class Callback; - OwnedArray items; Value currentId; int lastCurrentId; @@ -42112,6 +42380,7 @@ private: ItemInfo* getItemForId (int itemId) const throw(); ItemInfo* getItemForIndex (int index) const throw(); bool selectIfEnabled (int index); + static void popupMenuFinishedCallback (int, ComboBox*); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComboBox); }; @@ -47421,12 +47690,14 @@ public: */ bool hasStopMessageBeenSent() const throw() { return quitMessagePosted; } + #if JUCE_MODAL_LOOPS_PERMITTED /** Synchronously dispatches messages until a given time has elapsed. Returns false if a quit message has been posted by a call to stopDispatchLoop(), otherwise returns true. */ bool runDispatchLoopUntil (int millisecondsToRunFor); + #endif /** Calls a function using the message-thread. @@ -51335,6 +51606,26 @@ public: bool sendMessageSynchronously = false, bool allowNudgingOfOtherValues = false); + /** For a slider with two or three thumbs, this sets the minimum and maximum thumb positions. + + This will trigger a callback to Slider::Listener::sliderValueChanged() for any listeners + that are registered, and will synchronously call the valueChanged() method in case subclasses + want to handle it. + + @param newMinValue the new minimum value to set - this will be snapped to the + nearest interval if one has been set. + @param newMaxValue the new minimum value to set - this will be snapped to the + nearest interval if one has been set. + @param sendUpdateMessage if false, a change to the value will not trigger a call to + any Slider::Listeners or the valueChanged() method + @param sendMessageSynchronously if true, then a call to the Slider::Listeners will be made + synchronously; if false, it will be asynchronous + @see setMaxValue, setMinValue, setValue + */ + void setMinAndMaxValues (double newMinValue, double newMaxValue, + bool sendUpdateMessage = true, + bool sendMessageSynchronously = false); + /** A class for receiving callbacks from a Slider. To be told when a slider's value changes, you can register a Slider::Listener @@ -55450,6 +55741,543 @@ private: #endif // __JUCE_GLYPHARRANGEMENT_JUCEHEADER__ /*** End of inlined file: juce_GlyphArrangement.h ***/ + +/*** Start of inlined file: juce_AlertWindow.h ***/ +#ifndef __JUCE_ALERTWINDOW_JUCEHEADER__ +#define __JUCE_ALERTWINDOW_JUCEHEADER__ + + +/*** Start of inlined file: juce_TextLayout.h ***/ +#ifndef __JUCE_TEXTLAYOUT_JUCEHEADER__ +#define __JUCE_TEXTLAYOUT_JUCEHEADER__ + +class Graphics; + +/** + A laid-out arrangement of text. + + You can add text in different fonts to a TextLayout object, then call its + layout() method to word-wrap it into lines. The layout can then be drawn + using a graphics context. + + It's handy if you've got a message to display, because you can format it, + measure the extent of the layout, and then create a suitably-sized window + to show it in. + + @see Font, Graphics::drawFittedText, GlyphArrangement +*/ +class JUCE_API TextLayout +{ +public: + + /** Creates an empty text layout. + + Text can then be appended using the appendText() method. + */ + TextLayout(); + + /** Creates a copy of another layout object. */ + TextLayout (const TextLayout& other); + + /** Creates a text layout from an initial string and font. */ + TextLayout (const String& text, const Font& font); + + /** Destructor. */ + ~TextLayout(); + + /** Copies another layout onto this one. */ + TextLayout& operator= (const TextLayout& layoutToCopy); + + /** Clears the layout, removing all its text. */ + void clear(); + + /** Adds a string to the end of the arrangement. + + The string will be broken onto new lines wherever it contains + carriage-returns or linefeeds. After adding it, you can call layout() + to wrap long lines into a paragraph and justify it. + */ + void appendText (const String& textToAppend, + const Font& fontToUse); + + /** Replaces all the text with a new string. + + This is equivalent to calling clear() followed by appendText(). + */ + void setText (const String& newText, + const Font& fontToUse); + + /** Returns true if the layout has not had any text added yet. */ + bool isEmpty() const; + + /** Breaks the text up to form a paragraph with the given width. + + @param maximumWidth any text wider than this will be split + across multiple lines + @param justification how the lines are to be laid-out horizontally + @param attemptToBalanceLineLengths if true, it will try to split the lines at a + width that keeps all the lines of text at a + similar length - this is good when you're displaying + a short message and don't want it to get split + onto two lines with only a couple of words on + the second line, which looks untidy. + */ + void layout (int maximumWidth, + const Justification& justification, + bool attemptToBalanceLineLengths); + + /** Returns the overall width of the entire text layout. */ + int getWidth() const; + + /** Returns the overall height of the entire text layout. */ + int getHeight() const; + + /** Returns the total number of lines of text. */ + int getNumLines() const { return totalLines; } + + /** Returns the width of a particular line of text. + + @param lineNumber the line, from 0 to (getNumLines() - 1) + */ + int getLineWidth (int lineNumber) const; + + /** Renders the text at a specified position using a graphics context. + */ + void draw (Graphics& g, int topLeftX, int topLeftY) const; + + /** Renders the text within a specified rectangle using a graphics context. + + The justification flags dictate how the block of text should be positioned + within the rectangle. + */ + void drawWithin (Graphics& g, + int x, int y, int w, int h, + const Justification& layoutFlags) const; + +private: + + class Token; + friend class OwnedArray ; + OwnedArray tokens; + int totalLines; + + JUCE_LEAK_DETECTOR (TextLayout); +}; + +#endif // __JUCE_TEXTLAYOUT_JUCEHEADER__ +/*** End of inlined file: juce_TextLayout.h ***/ + +/** A window that displays a message and has buttons for the user to react to it. + + For simple dialog boxes with just a couple of buttons on them, there are + some static methods for running these. + + For more complex dialogs, an AlertWindow can be created, then it can have some + buttons and components added to it, and its runModalLoop() method is then used to + show it. The value returned by runModalLoop() shows which button the + user pressed to dismiss the box. + + @see ThreadWithProgressWindow +*/ +class JUCE_API AlertWindow : public TopLevelWindow, + private ButtonListener // (can't use Button::Listener due to idiotic VC2005 bug) +{ +public: + + /** The type of icon to show in the dialog box. */ + enum AlertIconType + { + NoIcon, /**< No icon will be shown on the dialog box. */ + QuestionIcon, /**< A question-mark icon, for dialog boxes that need the + user to answer a question. */ + WarningIcon, /**< An exclamation mark to indicate that the dialog is a + warning about something and shouldn't be ignored. */ + InfoIcon /**< An icon that indicates that the dialog box is just + giving the user some information, which doesn't require + a response from them. */ + }; + + /** Creates an AlertWindow. + + @param title the headline to show at the top of the dialog box + @param message a longer, more descriptive message to show underneath the + headline + @param iconType the type of icon to display + @param associatedComponent if this is non-null, it specifies the component that the + alert window should be associated with. Depending on the look + and feel, this might be used for positioning of the alert window. + */ + AlertWindow (const String& title, + const String& message, + AlertIconType iconType, + Component* associatedComponent = 0); + + /** Destroys the AlertWindow */ + ~AlertWindow(); + + /** Returns the type of alert icon that was specified when the window + was created. */ + AlertIconType getAlertType() const throw() { return alertIconType; } + + /** Changes the dialog box's message. + + This will also resize the window to fit the new message if required. + */ + void setMessage (const String& message); + + /** Adds a button to the window. + + @param name the text to show on the button + @param returnValue the value that should be returned from runModalLoop() + if this is the button that the user presses. + @param shortcutKey1 an optional key that can be pressed to trigger this button + @param shortcutKey2 a second optional key that can be pressed to trigger this button + */ + void addButton (const String& name, + int returnValue, + const KeyPress& shortcutKey1 = KeyPress(), + const KeyPress& shortcutKey2 = KeyPress()); + + /** Returns the number of buttons that the window currently has. */ + int getNumButtons() const; + + /** Invokes a click of one of the buttons. */ + void triggerButtonClick (const String& buttonName); + + /** Adds a textbox to the window for entering strings. + + @param name an internal name for the text-box. This is the name to pass to + the getTextEditorContents() method to find out what the + user typed-in. + @param initialContents a string to show in the text box when it's first shown + @param onScreenLabel if this is non-empty, it will be displayed next to the + text-box to label it. + @param isPasswordBox if true, the text editor will display asterisks instead of + the actual text + @see getTextEditorContents + */ + void addTextEditor (const String& name, + const String& initialContents, + const String& onScreenLabel = String::empty, + bool isPasswordBox = false); + + /** Returns the contents of a named textbox. + + After showing an AlertWindow that contains a text editor, this can be + used to find out what the user has typed into it. + + @param nameOfTextEditor the name of the text box that you're interested in + @see addTextEditor + */ + const String getTextEditorContents (const String& nameOfTextEditor) const; + + /** Returns a pointer to a textbox that was added with addTextEditor(). */ + TextEditor* getTextEditor (const String& nameOfTextEditor) const; + + /** Adds a drop-down list of choices to the box. + + After the box has been shown, the getComboBoxComponent() method can + be used to find out which item the user picked. + + @param name the label to use for the drop-down list + @param items the list of items to show in it + @param onScreenLabel if this is non-empty, it will be displayed next to the + combo-box to label it. + @see getComboBoxComponent + */ + void addComboBox (const String& name, + const StringArray& items, + const String& onScreenLabel = String::empty); + + /** Returns a drop-down list that was added to the AlertWindow. + + @param nameOfList the name that was passed into the addComboBox() method + when creating the drop-down + @returns the ComboBox component, or 0 if none was found for the given name. + */ + ComboBox* getComboBoxComponent (const String& nameOfList) const; + + /** Adds a block of text. + + This is handy for adding a multi-line note next to a textbox or combo-box, + to provide more details about what's going on. + */ + void addTextBlock (const String& text); + + /** Adds a progress-bar to the window. + + @param progressValue a variable that will be repeatedly checked while the + dialog box is visible, to see how far the process has + got. The value should be in the range 0 to 1.0 + */ + void addProgressBarComponent (double& progressValue); + + /** Adds a user-defined component to the dialog box. + + @param component the component to add - its size should be set up correctly + before it is passed in. The caller is responsible for deleting + the component later on - the AlertWindow won't delete it. + */ + void addCustomComponent (Component* component); + + /** Returns the number of custom components in the dialog box. + + @see getCustomComponent, addCustomComponent + */ + int getNumCustomComponents() const; + + /** Returns one of the custom components in the dialog box. + + @param index a value 0 to (getNumCustomComponents() - 1). Out-of-range indexes + will return 0 + @see getNumCustomComponents, addCustomComponent + */ + Component* getCustomComponent (int index) const; + + /** Removes one of the custom components in the dialog box. + + Note that this won't delete it, it just removes the component from the window + + @param index a value 0 to (getNumCustomComponents() - 1). Out-of-range indexes + will return 0 + @returns the component that was removed (or null) + @see getNumCustomComponents, addCustomComponent + */ + Component* removeCustomComponent (int index); + + /** Returns true if the window contains any components other than just buttons.*/ + bool containsAnyExtraComponents() const; + + // easy-to-use message box functions: + + /** Shows a dialog box that just has a message and a single button to get rid of it. + + If the callback parameter is null, the box is shown modally, and the method will + block until the user has clicked the button (or pressed the escape or return keys). + If the callback parameter is non-null, the box will be displayed and placed into a + modal state, but this method will return immediately, and the callback will be invoked + later when the user dismisses the box. + + @param iconType the type of icon to show + @param title the headline to show at the top of the box + @param message a longer, more descriptive message to show underneath the + headline + @param buttonText the text to show in the button - if this string is empty, the + default string "ok" (or a localised version) will be used. + @param associatedComponent if this is non-null, it specifies the component that the + alert window should be associated with. Depending on the look + and feel, this might be used for positioning of the alert window. + */ + #if JUCE_MODAL_LOOPS_PERMITTED + static void JUCE_CALLTYPE showMessageBox (AlertIconType iconType, + const String& title, + const String& message, + const String& buttonText = String::empty, + Component* associatedComponent = 0); + #endif + + /** Shows a dialog box that just has a message and a single button to get rid of it. + + If the callback parameter is null, the box is shown modally, and the method will + block until the user has clicked the button (or pressed the escape or return keys). + If the callback parameter is non-null, the box will be displayed and placed into a + modal state, but this method will return immediately, and the callback will be invoked + later when the user dismisses the box. + + @param iconType the type of icon to show + @param title the headline to show at the top of the box + @param message a longer, more descriptive message to show underneath the + headline + @param buttonText the text to show in the button - if this string is empty, the + default string "ok" (or a localised version) will be used. + @param associatedComponent if this is non-null, it specifies the component that the + alert window should be associated with. Depending on the look + and feel, this might be used for positioning of the alert window. + */ + static void JUCE_CALLTYPE showMessageBoxAsync (AlertIconType iconType, + const String& title, + const String& message, + const String& buttonText = String::empty, + Component* associatedComponent = 0); + + /** Shows a dialog box with two buttons. + + Ideal for ok/cancel or yes/no choices. The return key can also be used + to trigger the first button, and the escape key for the second button. + + If the callback parameter is null, the box is shown modally, and the method will + block until the user has clicked the button (or pressed the escape or return keys). + If the callback parameter is non-null, the box will be displayed and placed into a + modal state, but this method will return immediately, and the callback will be invoked + later when the user dismisses the box. + + @param iconType the type of icon to show + @param title the headline to show at the top of the box + @param message a longer, more descriptive message to show underneath the + headline + @param button1Text the text to show in the first button - if this string is + empty, the default string "ok" (or a localised version of it) + will be used. + @param button2Text the text to show in the second button - if this string is + empty, the default string "cancel" (or a localised version of it) + will be used. + @param associatedComponent if this is non-null, it specifies the component that the + alert window should be associated with. Depending on the look + and feel, this might be used for positioning of the alert window. + @param callback if this is non-null, the menu will be launched asynchronously, + returning immediately, and the callback will receive a call to its + modalStateFinished() when the box is dismissed, with its parameter + being 1 if the ok button was pressed, or 0 for cancel, The callback object + will be owned and deleted by the system, so make sure that it works + safely and doesn't keep any references to objects that might be deleted + before it gets called. + @returns true if button 1 was clicked, false if it was button 2. If the callback parameter + is not null, the method always returns false, and the user's choice is delivered + later by the callback. + */ + static bool JUCE_CALLTYPE showOkCancelBox (AlertIconType iconType, + const String& title, + const String& message, + #if JUCE_MODAL_LOOPS_PERMITTED + const String& button1Text = String::empty, + const String& button2Text = String::empty, + Component* associatedComponent = 0, + ModalComponentManager::Callback* callback = 0); + #else + const String& button1Text, + const String& button2Text, + Component* associatedComponent, + ModalComponentManager::Callback* callback); + #endif + + /** Shows a dialog box with three buttons. + + Ideal for yes/no/cancel boxes. + + The escape key can be used to trigger the third button. + + If the callback parameter is null, the box is shown modally, and the method will + block until the user has clicked the button (or pressed the escape or return keys). + If the callback parameter is non-null, the box will be displayed and placed into a + modal state, but this method will return immediately, and the callback will be invoked + later when the user dismisses the box. + + @param iconType the type of icon to show + @param title the headline to show at the top of the box + @param message a longer, more descriptive message to show underneath the + headline + @param button1Text the text to show in the first button - if an empty string, then + "yes" will be used (or a localised version of it) + @param button2Text the text to show in the first button - if an empty string, then + "no" will be used (or a localised version of it) + @param button3Text the text to show in the first button - if an empty string, then + "cancel" will be used (or a localised version of it) + @param associatedComponent if this is non-null, it specifies the component that the + alert window should be associated with. Depending on the look + and feel, this might be used for positioning of the alert window. + @param callback if this is non-null, the menu will be launched asynchronously, + returning immediately, and the callback will receive a call to its + modalStateFinished() when the box is dismissed, with its parameter + being 1 if the "yes" button was pressed, 2 for the "no" button, or 0 + if it was cancelled, The callback object will be owned and deleted by the + system, so make sure that it works safely and doesn't keep any references + to objects that might be deleted before it gets called. + + @returns If the callback parameter has been set, this returns 0. Otherwise, it + returns one of the following values: + - 0 if the third button was pressed (normally used for 'cancel') + - 1 if the first button was pressed (normally used for 'yes') + - 2 if the middle button was pressed (normally used for 'no') + */ + static int JUCE_CALLTYPE showYesNoCancelBox (AlertIconType iconType, + const String& title, + const String& message, + #if JUCE_MODAL_LOOPS_PERMITTED + const String& button1Text = String::empty, + const String& button2Text = String::empty, + const String& button3Text = String::empty, + Component* associatedComponent = 0, + ModalComponentManager::Callback* callback = 0); + #else + const String& button1Text, + const String& button2Text, + const String& button3Text, + Component* associatedComponent, + ModalComponentManager::Callback* callback); + #endif + + /** Shows an operating-system native dialog box. + + @param title the title to use at the top + @param bodyText the longer message to show + @param isOkCancel if true, this will show an ok/cancel box, if false, + it'll show a box with just an ok button + @returns true if the ok button was pressed, false if they pressed cancel. + */ + static bool JUCE_CALLTYPE showNativeDialogBox (const String& title, + const String& bodyText, + bool isOkCancel); + + /** A set of colour IDs to use to change the colour of various aspects of the alert box. + + These constants can be used either via the Component::setColour(), or LookAndFeel::setColour() + methods. + + @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour + */ + enum ColourIds + { + backgroundColourId = 0x1001800, /**< The background colour for the window. */ + textColourId = 0x1001810, /**< The colour for the text. */ + outlineColourId = 0x1001820 /**< An optional colour to use to draw a border around the window. */ + }; + +protected: + + /** @internal */ + void paint (Graphics& g); + /** @internal */ + void mouseDown (const MouseEvent& e); + /** @internal */ + void mouseDrag (const MouseEvent& e); + /** @internal */ + bool keyPressed (const KeyPress& key); + /** @internal */ + void buttonClicked (Button* button); + /** @internal */ + void lookAndFeelChanged(); + /** @internal */ + void userTriedToCloseWindow(); + /** @internal */ + int getDesktopWindowStyleFlags() const; + +private: + + String text; + TextLayout textLayout; + AlertIconType alertIconType; + ComponentBoundsConstrainer constrainer; + ComponentDragger dragger; + Rectangle textArea; + OwnedArray buttons; + OwnedArray textBoxes; + OwnedArray comboBoxes; + OwnedArray progressBars; + Array customComps; + OwnedArray textBlocks; + Array allComps; + StringArray textboxNames, comboBoxNames; + Font font; + Component* associatedComponent; + + void updateLayout (bool onlyIncreaseSize); + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AlertWindow); +}; + +#endif // __JUCE_ALERTWINDOW_JUCEHEADER__ +/*** End of inlined file: juce_AlertWindow.h ***/ + /** A file open/save dialog box. @@ -55530,6 +56358,11 @@ public: */ bool showAt (int x, int y, int width, int height); + /** Sets the size of this dialog box to its default and positions it either in the + centre of the screen, or centred around a component that is provided. + */ + void centreWithDefaultSize (Component* componentToCentreAround = 0); + /** A set of colour IDs to use to change the colour of various aspects of the box. These constants can be used either via the Component::setColour(), or LookAndFeel::setColour() @@ -55554,26 +56387,16 @@ public: void fileDoubleClicked (const File& file); private: - class ContentComponent : public Component - { - public: - ContentComponent (const String& name, const String& instructions, FileBrowserComponent& chooserComponent); - - void paint (Graphics& g); - void resized(); - - String instructions; - GlyphArrangement text; - - FileBrowserComponent& chooserComponent; - TextButton okButton, cancelButton, newFolderButton; - }; - + class ContentComponent; ContentComponent* content; const bool warnAboutOverwritingExistingFiles; void okButtonPressed(); void createNewFolder(); + void createNewFolderConfirmed (const String& name); + + static void okToOverwriteFileCallback (int result, FileChooserDialogBox*); + static void createNewFolderCallback (int result, FileChooserDialogBox*, Component::SafePointer); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileChooserDialogBox); }; @@ -56913,6 +57736,7 @@ private: ScopedPointer