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