diff --git a/examples/Assets/DSPDemos_Common.h b/examples/Assets/DSPDemos_Common.h index 9fa1d5c3a0..26d0e3b966 100644 --- a/examples/Assets/DSPDemos_Common.h +++ b/examples/Assets/DSPDemos_Common.h @@ -596,13 +596,17 @@ private: const auto u = fc.getURLResult(); if (! audioFileReader.loadURL (u)) - NativeMessageBox::showAsync (MessageBoxOptions() - .withIconType (MessageBoxIconType::WarningIcon) - .withTitle ("Error loading file") - .withMessage ("Unable to load audio file"), - nullptr); + { + auto options = MessageBoxOptions().withIconType (MessageBoxIconType::WarningIcon) + .withTitle ("Error loading file") + .withMessage ("Unable to load audio file") + .withButton ("OK"); + messageBox = NativeMessageBox::showScopedAsync (options, nullptr); + } else + { thumbnailComp.setCurrentURL (u); + } } fileChooser = nullptr; @@ -629,6 +633,7 @@ private: AudioFileReaderComponent& audioFileReader; std::unique_ptr fileChooser; + ScopedMessageBox messageBox; }; //============================================================================== diff --git a/examples/Audio/MPEDemo.h b/examples/Audio/MPEDemo.h index 10615ee733..2479272886 100644 --- a/examples/Audio/MPEDemo.h +++ b/examples/Audio/MPEDemo.h @@ -279,13 +279,14 @@ private: return legacyStartChannel.getText().getIntValue() <= legacyEndChannel.getText().getIntValue(); } - void handleInvalidLegacyModeParameters() const + void handleInvalidLegacyModeParameters() { - AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, - "Invalid legacy mode channel layout", - "Cannot set legacy mode start/end channel:\n" - "The end channel must not be less than the start channel!", - "Got it"); + auto options = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::WarningIcon, + "Invalid legacy mode channel layout", + "Cannot set legacy mode start/end channel:\n" + "The end channel must not be less than the start channel!", + "Got it"); + messageBox = AlertWindow::showScopedAsync (options, nullptr); } Range getLegacyModeChannelRange() const @@ -338,6 +339,8 @@ private: ComboBox numberOfVoices; Label numberOfVoicesLabel { {}, "Number of synth voices"}; + ScopedMessageBox messageBox; + static constexpr int defaultMemberChannels = 15, defaultMasterPitchbendRange = 2, defaultNotePitchbendRange = 48; diff --git a/examples/GUI/AccessibilityDemo.h b/examples/GUI/AccessibilityDemo.h index aff7e54fec..c3641a132c 100644 --- a/examples/GUI/AccessibilityDemo.h +++ b/examples/GUI/AccessibilityDemo.h @@ -195,7 +195,13 @@ private: addAndMakeVisible (textButton); shapeButton.setShape (getJUCELogoPath(), false, true, false); - shapeButton.onClick = [] { AlertWindow::showMessageBoxAsync (MessageBoxIconType::InfoIcon, "Alert", "This is an AlertWindow"); }; + shapeButton.onClick = [this] + { + auto options = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::InfoIcon, + "Alert", + "This is an AlertWindow"); + messageBox = AlertWindow::showScopedAsync (options, nullptr); + }; shapeButton.setHasFocusOutline (true); addAndMakeVisible (shapeButton); } @@ -250,6 +256,7 @@ private: Colours::darkorange, Colours::darkorange.brighter (0.5f), Colours::darkorange.brighter (0.75f) }; + ScopedMessageBox messageBox; //============================================================================== JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ButtonsComponent) diff --git a/examples/GUI/CameraDemo.h b/examples/GUI/CameraDemo.h index 0765e6692e..14e64c2873 100644 --- a/examples/GUI/CameraDemo.h +++ b/examples/GUI/CameraDemo.h @@ -252,8 +252,10 @@ private: } else { - AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, "Camera open failed", - "Camera open failed, reason: " + error); + auto options = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::WarningIcon, + "Camera open failed", + "Camera open failed, reason: " + error); + messageBox = AlertWindow::showScopedAsync (options, nullptr); } snapshotButton .setEnabled (cameraDevice.get() != nullptr && ! contentSharingPending); @@ -365,9 +367,10 @@ private: void errorOccurred (const String& error) { - AlertWindow::showMessageBoxAsync (MessageBoxIconType::InfoIcon, - "Camera Device Error", - "An error has occurred: " + error + " Camera will be closed."); + auto options = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::InfoIcon, + "Camera Device Error", + "An error has occurred: " + error + " Camera will be closed."); + messageBox = AlertWindow::showScopedAsync (options, nullptr); cameraDevice.reset(); @@ -378,14 +381,17 @@ private: void sharingFinished (bool success, bool isCapture) { - AlertWindow::showMessageBoxAsync (MessageBoxIconType::InfoIcon, - isCapture ? "Image sharing result" : "Video sharing result", - success ? "Success!" : "Failed!"); + auto options = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::InfoIcon, + isCapture ? "Image sharing result" : "Video sharing result", + success ? "Success!" : "Failed!"); + messageBox = AlertWindow::showScopedAsync (options, nullptr); contentSharingPending = false; snapshotButton .setEnabled (true); recordMovieButton.setEnabled (true); } + ScopedMessageBox messageBox; + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CameraDemo) }; diff --git a/examples/GUI/DialogsDemo.h b/examples/GUI/DialogsDemo.h index 23a2c86243..31fc48100b 100644 --- a/examples/GUI/DialogsDemo.h +++ b/examples/GUI/DialogsDemo.h @@ -48,12 +48,19 @@ #include "../Assets/DemoUtilities.h" +//============================================================================== +struct MessageBoxOwnerComponent : public Component +{ + ScopedMessageBox messageBox; +}; + //============================================================================== class DemoBackgroundThread : public ThreadWithProgressWindow { public: - DemoBackgroundThread() - : ThreadWithProgressWindow ("busy doing some important things...", true, true) + explicit DemoBackgroundThread (MessageBoxOwnerComponent& comp) + : ThreadWithProgressWindow ("busy doing some important things...", true, true), + owner (&comp) { setStatusMessage ("Getting ready..."); } @@ -91,21 +98,26 @@ public: { const String messageString (userPressedCancel ? "You pressed cancel!" : "Thread finished ok!"); - AlertWindow::showAsync (MessageBoxOptions() - .withIconType (MessageBoxIconType::InfoIcon) - .withTitle ("Progress window") - .withMessage (messageString) - .withButton ("OK"), - nullptr); + if (owner != nullptr) + { + owner->messageBox = AlertWindow::showScopedAsync (MessageBoxOptions() + .withIconType (MessageBoxIconType::InfoIcon) + .withTitle ("Progress window") + .withMessage (messageString) + .withButton ("OK"), + nullptr); + } // ..and clean up by deleting our thread object.. delete this; } + + Component::SafePointer owner; }; //============================================================================== -class DialogsDemo : public Component +class DialogsDemo : public MessageBoxOwnerComponent { public: enum DialogType @@ -114,7 +126,7 @@ public: warningAlertWindow, infoAlertWindow, questionAlertWindow, - okCancelAlertWindow, + yesNoCancelAlertWindow, extraComponentsAlertWindow, calloutBoxWindow, progressWindow, @@ -140,7 +152,7 @@ public: "Alert Window With Warning Icon", "Alert Window With Info Icon", "Alert Window With Question Icon", - "OK Cancel Alert Window", + "Yes No Cancel Alert Window", "Alert Window With Extra Components", "CalloutBox", "Thread With Progress Window", @@ -168,18 +180,19 @@ public: setSize (500, 500); - RuntimePermissions::request (RuntimePermissions::readExternalStorage, - [] (bool granted) - { - if (! granted) - AlertWindow::showAsync (MessageBoxOptions() - .withIconType (MessageBoxIconType::WarningIcon) - .withTitle ("Permissions warning") - .withMessage ("External storage access permission not granted, some files" - " may be inaccessible.") - .withButton ("OK"), - nullptr); - }); + RuntimePermissions::request (RuntimePermissions::readExternalStorage, [ptr = Component::SafePointer (this)] (bool granted) + { + if (granted || ptr == nullptr) + return; + + ptr->messageBox = AlertWindow::showScopedAsync (MessageBoxOptions() + .withIconType (MessageBoxIconType::WarningIcon) + .withTitle ("Permissions warning") + .withMessage ("External storage access permission not granted, some files" + " may be inaccessible.") + .withButton ("OK"), + nullptr); + }); } //============================================================================== @@ -214,49 +227,51 @@ private: OwnedArray windowButtons; ToggleButton nativeButton; - struct AlertBoxResultChosen + auto getAlertBoxResultChosen() { - void operator() (int result) const noexcept + return [ptr = Component::SafePointer (this)] (int result) { - AlertWindow::showAsync (MessageBoxOptions() - .withIconType (MessageBoxIconType::InfoIcon) - .withTitle ("Alert Box") - .withMessage ("Result code: " + String (result)) - .withButton ("OK"), - nullptr); - } - }; + if (ptr != nullptr) + ptr->messageBox = AlertWindow::showScopedAsync (MessageBoxOptions() + .withIconType (MessageBoxIconType::InfoIcon) + .withTitle ("Alert Box") + .withMessage ("Result code: " + String (result)) + .withButton ("OK"), + nullptr); + }; + } - struct AsyncAlertBoxResultChosen + auto getAsyncAlertBoxResultChosen() { - void operator() (int result) const noexcept + return [ptr = Component::SafePointer (this)] (int result) { - auto& aw = *demo.asyncAlertWindow; + if (ptr == nullptr) + return; + + auto& aw = *ptr->asyncAlertWindow; aw.exitModalState (result); aw.setVisible (false); if (result == 0) { - AlertBoxResultChosen{} (result); + ptr->getAlertBoxResultChosen() (result); return; } auto optionIndexChosen = aw.getComboBoxComponent ("option")->getSelectedItemIndex(); auto text = aw.getTextEditorContents ("text"); - AlertWindow::showAsync (MessageBoxOptions() - .withIconType (MessageBoxIconType::InfoIcon) - .withTitle ("Alert Box") - .withMessage ("Result code: " + String (result) + newLine - + "Option index chosen: " + String (optionIndexChosen) + newLine - + "Text: " + text) - .withButton ("OK"), - nullptr); - } - - DialogsDemo& demo; - }; + ptr->messageBox = AlertWindow::showScopedAsync (MessageBoxOptions() + .withIconType (MessageBoxIconType::InfoIcon) + .withTitle ("Alert Box") + .withMessage ("Result code: " + String (result) + newLine + + "Option index chosen: " + String (optionIndexChosen) + newLine + + "Text: " + text) + .withButton ("OK"), + nullptr); + }; + } void showWindow (Component& button, DialogType type) { @@ -268,16 +283,19 @@ private: if (type == infoAlertWindow) icon = MessageBoxIconType::InfoIcon; if (type == questionAlertWindow) icon = MessageBoxIconType::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"); + auto options = MessageBoxOptions::makeOptionsOk (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."); + messageBox = AlertWindow::showScopedAsync (options, nullptr); } - else if (type == okCancelAlertWindow) + else if (type == yesNoCancelAlertWindow) { - AlertWindow::showOkCancelBox (MessageBoxIconType::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.", - {}, {}, {}, - ModalCallbackFunction::create (AlertBoxResultChosen{})); + auto options = MessageBoxOptions::makeOptionsYesNoCancel (MessageBoxIconType::QuestionIcon, + "This is a yes/no/cancel AlertWindow", + "And this is the AlertWindow's message. " + "Blah blah blah blah blah blah blah blah blah blah blah blah blah."); + messageBox = AlertWindow::showScopedAsync (options, getAlertBoxResultChosen()); } else if (type == calloutBoxWindow) { @@ -301,13 +319,13 @@ private: asyncAlertWindow->addButton ("OK", 1, KeyPress (KeyPress::returnKey, 0, 0)); asyncAlertWindow->addButton ("Cancel", 0, KeyPress (KeyPress::escapeKey, 0, 0)); - asyncAlertWindow->enterModalState (true, ModalCallbackFunction::create (AsyncAlertBoxResultChosen { *this })); + asyncAlertWindow->enterModalState (true, ModalCallbackFunction::create (getAsyncAlertBoxResultChosen())); } else if (type == progressWindow) { // This will launch our ThreadWithProgressWindow in a modal state. (Our subclass // will take care of deleting the object when the task has finished) - (new DemoBackgroundThread())->launchThread(); + (new DemoBackgroundThread (*this))->launchThread(); } else if (type >= loadChooser && type <= saveChooser) { @@ -318,9 +336,10 @@ private: fc.reset (new FileChooser ("Choose a file to open...", File::getCurrentWorkingDirectory(), "*", useNativeVersion)); - fc->launchAsync (FileBrowserComponent::canSelectMultipleItems | FileBrowserComponent::openMode - | FileBrowserComponent::canSelectFiles, - [] (const FileChooser& chooser) + fc->launchAsync (FileBrowserComponent::canSelectMultipleItems + | FileBrowserComponent::openMode + | FileBrowserComponent::canSelectFiles, + [this] (const FileChooser& chooser) { String chosen; auto results = chooser.getURLResults(); @@ -329,12 +348,12 @@ private: chosen << (result.isLocalFile() ? result.getLocalFile().getFullPathName() : result.toString (false)) << "\n"; - AlertWindow::showAsync (MessageBoxOptions() - .withIconType (MessageBoxIconType::InfoIcon) - .withTitle ("File Chooser...") - .withMessage ("You picked: " + chosen) - .withButton ("OK"), - nullptr); + messageBox = AlertWindow::showScopedAsync (MessageBoxOptions() + .withIconType (MessageBoxIconType::InfoIcon) + .withTitle ("File Chooser...") + .withMessage ("You picked: " + chosen) + .withButton ("OK"), + nullptr); }); } else if (type == loadWithPreviewChooser) @@ -344,9 +363,10 @@ private: fc.reset (new FileChooser ("Choose an image to open...", File::getCurrentWorkingDirectory(), "*.jpg;*.jpeg;*.png;*.gif", useNativeVersion)); - fc->launchAsync (FileBrowserComponent::openMode | FileBrowserComponent::canSelectFiles - | FileBrowserComponent::canSelectMultipleItems, - [] (const FileChooser& chooser) + fc->launchAsync (FileBrowserComponent::openMode + | FileBrowserComponent::canSelectFiles + | FileBrowserComponent::canSelectMultipleItems, + [this] (const FileChooser& chooser) { String chosen; auto results = chooser.getURLResults(); @@ -355,12 +375,12 @@ private: chosen << (result.isLocalFile() ? result.getLocalFile().getFullPathName() : result.toString (false)) << "\n"; - AlertWindow::showAsync (MessageBoxOptions() - .withIconType (MessageBoxIconType::InfoIcon) - .withTitle ("File Chooser...") - .withMessage ("You picked: " + chosen) - .withButton ("OK"), - nullptr); + messageBox = AlertWindow::showScopedAsync (MessageBoxOptions() + .withIconType (MessageBoxIconType::InfoIcon) + .withTitle ("File Chooser...") + .withMessage ("You picked: " + chosen) + .withButton ("OK"), + nullptr); }, &imagePreview); } @@ -385,7 +405,7 @@ private: "*", useNativeVersion)); fc->launchAsync (FileBrowserComponent::saveMode | FileBrowserComponent::canSelectFiles, - [fileToSave] (const FileChooser& chooser) + [this, fileToSave] (const FileChooser& chooser) { auto result = chooser.getURLResult(); auto name = result.isEmpty() ? String() @@ -409,12 +429,12 @@ private: } #endif - AlertWindow::showAsync (MessageBoxOptions() - .withIconType (MessageBoxIconType::InfoIcon) - .withTitle ("File Chooser...") - .withMessage ("You picked: " + name) - .withButton ("OK"), - nullptr); + messageBox = AlertWindow::showScopedAsync (MessageBoxOptions() + .withIconType (MessageBoxIconType::InfoIcon) + .withTitle ("File Chooser...") + .withMessage ("You picked: " + name) + .withButton ("OK"), + nullptr); }); } else if (type == directoryChooser) @@ -425,35 +445,37 @@ private: useNativeVersion)); fc->launchAsync (FileBrowserComponent::openMode | FileBrowserComponent::canSelectDirectories, - [] (const FileChooser& chooser) + [this] (const FileChooser& chooser) { auto result = chooser.getURLResult(); auto name = result.isLocalFile() ? result.getLocalFile().getFullPathName() : result.toString (true); - AlertWindow::showAsync (MessageBoxOptions() - .withIconType (MessageBoxIconType::InfoIcon) - .withTitle ("File Chooser...") - .withMessage ("You picked: " + name) - .withButton ("OK"), - nullptr); + messageBox = AlertWindow::showScopedAsync (MessageBoxOptions() + .withIconType (MessageBoxIconType::InfoIcon) + .withTitle ("File Chooser...") + .withMessage ("You picked: " + name) + .withButton ("OK"), + nullptr); }); } } else if (type == shareText) { - ContentSharer::getInstance()->shareText ("I love JUCE!", - [] (bool success, const String& error) - { - auto resultString = success ? String ("success") : ("failure\n (error: " + error + ")"); + ContentSharer::getInstance()->shareText ("I love JUCE!", [ptr = Component::SafePointer (this)] (bool success, const String& error) + { + if (ptr == nullptr) + return; - AlertWindow::showAsync (MessageBoxOptions() - .withIconType (MessageBoxIconType::InfoIcon) - .withTitle ("Sharing Text Result") - .withMessage ("Sharing text finished\nwith " + resultString) - .withButton ("OK"), - nullptr); - }); + auto resultString = success ? String ("success") : ("failure\n (error: " + error + ")"); + + ptr->messageBox = AlertWindow::showScopedAsync (MessageBoxOptions() + .withIconType (MessageBoxIconType::InfoIcon) + .withTitle ("Sharing Text Result") + .withMessage ("Sharing text finished\nwith " + resultString) + .withButton ("OK"), + nullptr); + }); } else if (type == shareFile) { @@ -467,18 +489,20 @@ private: Array urls; urls.add (URL (fileToSave)); - ContentSharer::getInstance()->shareFiles (urls, - [] (bool success, const String& error) - { - auto resultString = success ? String ("success") : ("failure\n (error: " + error + ")"); + ContentSharer::getInstance()->shareFiles (urls, [ptr = Component::SafePointer (this)] (bool success, const String& error) + { + if (ptr == nullptr) + return; - AlertWindow::showAsync (MessageBoxOptions() - .withIconType (MessageBoxIconType::InfoIcon) - .withTitle ("Sharing Files Result") - .withMessage ("Sharing files finished\nwith " + resultString) - .withButton ("OK"), - nullptr); - }); + auto resultString = success ? String ("success") : ("failure\n (error: " + error + ")"); + + ptr->messageBox = AlertWindow::showScopedAsync (MessageBoxOptions() + .withIconType (MessageBoxIconType::InfoIcon) + .withTitle ("Sharing Files Result") + .withMessage ("Sharing files finished\nwith " + resultString) + .withButton ("OK"), + nullptr); + }); } } @@ -495,19 +519,21 @@ private: Array images { myImage, myImage2 }; - ContentSharer::getInstance()->shareImages (images, - [] (bool success, const String& error) - { - String resultString = success ? String ("success") - : ("failure\n (error: " + error + ")"); + ContentSharer::getInstance()->shareImages (images, [ptr = Component::SafePointer (this)] (bool success, const String& error) + { + if (ptr == nullptr) + return; - AlertWindow::showAsync (MessageBoxOptions() - .withIconType (MessageBoxIconType::InfoIcon) - .withTitle ("Sharing Images Result") - .withMessage ("Sharing images finished\nwith " + resultString) - .withButton ("OK"), - nullptr); - }); + String resultString = success ? String ("success") + : ("failure\n (error: " + error + ")"); + + ptr->messageBox = AlertWindow::showScopedAsync (MessageBoxOptions() + .withIconType (MessageBoxIconType::InfoIcon) + .withTitle ("Sharing Images Result") + .withMessage ("Sharing images finished\nwith " + resultString) + .withButton ("OK"), + nullptr); + }); } } diff --git a/examples/GUI/PropertiesDemo.h b/examples/GUI/PropertiesDemo.h index a242d68371..346b1a79bf 100644 --- a/examples/GUI/PropertiesDemo.h +++ b/examples/GUI/PropertiesDemo.h @@ -61,8 +61,10 @@ public: void buttonClicked() override { ++counter; - AlertWindow::showMessageBoxAsync (MessageBoxIconType::InfoIcon, "Action Button Pressed", - "Pressing this type of property component can trigger an action such as showing an alert window!"); + auto options = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::InfoIcon, + "Action Button Pressed", + "Pressing this type of property component can trigger an action such as showing an alert window!"); + messageBox = AlertWindow::showScopedAsync (options, nullptr); refresh(); } @@ -73,6 +75,7 @@ public: private: int counter = 0; + ScopedMessageBox messageBox; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DemoButtonPropertyComponent) }; diff --git a/examples/GUI/VideoDemo.h b/examples/GUI/VideoDemo.h index 086f5aa0a1..4fd79408e8 100644 --- a/examples/GUI/VideoDemo.h +++ b/examples/GUI/VideoDemo.h @@ -134,12 +134,15 @@ private: } else { - AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, - "Couldn't load the file!", - result.getErrorMessage()); + auto options = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::WarningIcon, + "Couldn't load the file!", + result.getErrorMessage()); + messageBox = AlertWindow::showScopedAsync (options, nullptr); } } + ScopedMessageBox messageBox; + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MovieComponentWithFileBrowser) }; diff --git a/examples/Plugins/HostPluginDemo.h b/examples/Plugins/HostPluginDemo.h index 589e9a0f31..804f96898b 100644 --- a/examples/Plugins/HostPluginDemo.h +++ b/examples/Plugins/HostPluginDemo.h @@ -213,11 +213,10 @@ public: { if (error.isNotEmpty()) { - NativeMessageBox::showMessageBoxAsync (MessageBoxIconType::WarningIcon, - "Plugin Load Failed", - error, - nullptr, - nullptr); + auto options = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::WarningIcon, + "Plugin Load Failed", + error); + messageBox = AlertWindow::showScopedAsync (options, nullptr); return; } @@ -281,6 +280,7 @@ private: std::unique_ptr inner; EditorStyle editorStyle = EditorStyle{}; bool active = false; + ScopedMessageBox messageBox; static constexpr const char* innerStateTag = "inner_state"; static constexpr const char* editorStyleTag = "editor_style"; @@ -298,7 +298,6 @@ private: } }; - constexpr const char* HostAudioProcessorImpl::innerStateTag; constexpr const char* HostAudioProcessorImpl::editorStyleTag; diff --git a/examples/Plugins/SamplerPluginDemo.h b/examples/Plugins/SamplerPluginDemo.h index 289e9ff352..3d6d640bfe 100644 --- a/examples/Plugins/SamplerPluginDemo.h +++ b/examples/Plugins/SamplerPluginDemo.h @@ -1192,7 +1192,7 @@ private: } } - bool isLegacyModeValid() const + bool isLegacyModeValid() { if (! areLegacyModeParametersValid()) { @@ -1233,13 +1233,14 @@ private: return getFirstChannel() <= getLastChannel(); } - void handleInvalidLegacyModeParameters() const + void handleInvalidLegacyModeParameters() { - AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, - "Invalid legacy mode channel layout", - "Cannot set legacy mode start/end channel:\n" - "The end channel must not be less than the start channel!", - "Got it"); + auto options = MessageBoxOptions::makeOptionsOk (AlertWindow::WarningIcon, + "Invalid legacy mode channel layout", + "Cannot set legacy mode start/end channel:\n" + "The end channel must not be less than the start channel!", + "Got it"); + messageBox = AlertWindow::showScopedAsync (options, nullptr); } MPESettingsDataModel dataModel; @@ -1251,6 +1252,7 @@ private: legacyPitchbendRangeLabel { {}, "Pitchbend range (semitones)" }; UndoManager* undoManager; + ScopedMessageBox messageBox; }; //============================================================================== diff --git a/examples/Utilities/InAppPurchasesDemo.h b/examples/Utilities/InAppPurchasesDemo.h index ffc0508272..1cdc782a85 100644 --- a/examples/Utilities/InAppPurchasesDemo.h +++ b/examples/Utilities/InAppPurchasesDemo.h @@ -156,12 +156,13 @@ private: voiceProduct.purchasePrice = "In-App purchases unavailable"; } - AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, - "In-app purchase is unavailable!", - "In-App purchases are not available. This either means you are trying " - "to use IAP on a platform that does not support IAP or you haven't setup " - "your app correctly to work with IAP.", - "OK"); + auto options = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::WarningIcon, + "In-app purchase is unavailable!", + "In-App purchases are not available. This either means you are trying " + "to use IAP on a platform that does not support IAP or you haven't setup " + "your app correctly to work with IAP.", + "OK"); + messageBox = AlertWindow::showScopedAsync (options, nullptr); } else { @@ -178,11 +179,12 @@ private: } } - AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, - "Your credit card will be charged!", - "You are running the sample code for JUCE In-App purchases. " - "Although this is only sample code, it will still CHARGE YOUR CREDIT CARD!", - "Understood!"); + auto options = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::WarningIcon, + "Your credit card will be charged!", + "You are running the sample code for JUCE In-App purchases. " + "Although this is only sample code, it will still CHARGE YOUR CREDIT CARD!", + "Understood!"); + messageBox = AlertWindow::showScopedAsync (options, nullptr); } guiUpdater.triggerAsyncUpdate(); @@ -264,6 +266,7 @@ private: AsyncUpdater& guiUpdater; bool havePurchasesBeenRestored = false, havePricesBeenFetched = false, purchaseInProgress = false; Array voiceProducts; + ScopedMessageBox messageBox; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VoicePurchases) }; diff --git a/examples/Utilities/OSCDemo.h b/examples/Utilities/OSCDemo.h index 0637135992..1ec2e05ac8 100644 --- a/examples/Utilities/OSCDemo.h +++ b/examples/Utilities/OSCDemo.h @@ -222,16 +222,17 @@ private: //============================================================================== void showConnectionErrorMessage (const String& messageText) { - AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, - "Connection error", - messageText, - "OK"); + auto options = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::WarningIcon, + "Connection error", + messageText); + messageBox = AlertWindow::showScopedAsync (options, nullptr); } //============================================================================== Slider rotaryKnob; OSCSender sender1, sender2; Label senderLabel { {}, "Sender" }; + ScopedMessageBox messageBox; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OSCSenderDemo) }; @@ -273,15 +274,16 @@ private: void showConnectionErrorMessage (const String& messageText) { - AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, - "Connection error", - messageText, - "OK"); + auto options = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::WarningIcon, + "Connection error", + messageText); + messageBox = AlertWindow::showScopedAsync (options, nullptr); } //============================================================================== Slider rotaryKnob; Label receiverLabel { {}, "Receiver" }; + ScopedMessageBox messageBox; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OSCReceiverDemo) }; @@ -403,28 +405,28 @@ private: //============================================================================== void handleConnectError (int failedPort) { - AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, - "OSC Connection error", - "Error: could not connect to port " + String (failedPort), - "OK"); + auto options = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::WarningIcon, + "OSC Connection error", + "Error: could not connect to port " + String (failedPort)); + messageBox = AlertWindow::showScopedAsync (options, nullptr); } //============================================================================== void handleDisconnectError() { - AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, - "Unknown error", - "An unknown error occurred while trying to disconnect from UDP port.", - "OK"); + auto options = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::WarningIcon, + "Unknown error", + "An unknown error occurred while trying to disconnect from UDP port."); + messageBox = AlertWindow::showScopedAsync (options, nullptr); } //============================================================================== void handleInvalidPortNumberEntered() { - AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, - "Invalid port number", - "Error: you have entered an invalid UDP port number.", - "OK"); + auto options = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::WarningIcon, + "Invalid port number", + "Error: you have entered an invalid UDP port number."); + messageBox = AlertWindow::showScopedAsync (options, nullptr); } //============================================================================== @@ -457,6 +459,8 @@ private: connectionStatusLabel.setJustificationType (Justification::centredRight); } + ScopedMessageBox messageBox; + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OSCMonitorDemo) }; diff --git a/examples/Utilities/PushNotificationsDemo.h b/examples/Utilities/PushNotificationsDemo.h index 9788dd6691..0637e705e3 100644 --- a/examples/Utilities/PushNotificationsDemo.h +++ b/examples/Utilities/PushNotificationsDemo.h @@ -185,21 +185,25 @@ public: { PushNotifications::getInstance()->removeAllPendingLocalNotifications(); }; #endif - remoteView.getDeviceTokenButton.onClick = [] + remoteView.getDeviceTokenButton.onClick = [this] { String token = PushNotifications::getInstance()->getDeviceToken(); DBG ("token = " + token); if (token.isEmpty()) + { showRemoteInstructions(); + } else - NativeMessageBox::showAsync (MessageBoxOptions() + { + auto options = MessageBoxOptions() .withIconType (MessageBoxIconType::InfoIcon) .withTitle ("Device token") .withMessage (token) - .withButton ("OK"), - nullptr); + .withButton ("OK"); + messageBox = NativeMessageBox::showScopedAsync (options, nullptr); + } }; #if JUCE_ANDROID @@ -313,12 +317,12 @@ private: String requiredFields = "all required fields"; #endif - NativeMessageBox::showAsync (MessageBoxOptions() + auto options = MessageBoxOptions() .withIconType (MessageBoxIconType::InfoIcon) .withTitle ("Incorrect notifications setup") .withMessage ("Please make sure that " + requiredFields + " are set.") - .withButton ("OK"), - nullptr); + .withButton ("OK"); + messageBox = NativeMessageBox::showScopedAsync (options, nullptr); return; } @@ -565,14 +569,14 @@ private: { ignoreUnused (isLocalNotification); - NativeMessageBox::showAsync (MessageBoxOptions() + auto options = MessageBoxOptions() .withIconType (MessageBoxIconType::InfoIcon) .withTitle ("Received notification") .withMessage ("ID: " + n.identifier + ", title: " + n.title + ", body: " + n.body) - .withButton ("OK"), - nullptr); + .withButton ("OK"); + messageBox = NativeMessageBox::showScopedAsync (options, nullptr); } void handleNotificationAction (bool isLocalNotification, @@ -582,7 +586,7 @@ private: { ignoreUnused (isLocalNotification); - NativeMessageBox::showAsync (MessageBoxOptions() + auto options = MessageBoxOptions() .withIconType (MessageBoxIconType::InfoIcon) .withTitle ("Received notification action") .withMessage ("ID: " + n.identifier @@ -590,22 +594,22 @@ private: + ", body: " + n.body + ", action: " + actionIdentifier + ", optionalResponse: " + optionalResponse) - .withButton ("OK"), - nullptr); + .withButton ("OK"); + messageBox = NativeMessageBox::showScopedAsync (options, nullptr); PushNotifications::getInstance()->removeDeliveredNotification (n.identifier); } void localNotificationDismissedByUser (const PushNotifications::Notification& n) override { - NativeMessageBox::showAsync (MessageBoxOptions() + auto options = MessageBoxOptions() .withIconType (MessageBoxIconType::InfoIcon) .withTitle ("Notification dismissed by a user") .withMessage ("ID: " + n.identifier + ", title: " + n.title + ", body: " + n.body) - .withButton ("OK"), - nullptr); + .withButton ("OK"); + messageBox = NativeMessageBox::showScopedAsync (options, nullptr); } void deliveredNotificationsListReceived (const Array& notifs) override @@ -615,12 +619,12 @@ private: for (auto& n : notifs) text << "(" << n.identifier << ", " << n.title << ", " << n.body << "), "; - NativeMessageBox::showAsync (MessageBoxOptions() + auto options = MessageBoxOptions() .withIconType (MessageBoxIconType::InfoIcon) .withTitle ("Received notification list") .withMessage (text) - .withButton ("OK"), - nullptr); + .withButton ("OK"); + messageBox = NativeMessageBox::showScopedAsync (options, nullptr); } void pendingLocalNotificationsListReceived (const Array& notifs) override @@ -630,54 +634,54 @@ private: for (auto& n : notifs) text << "(" << n.identifier << ", " << n.title << ", " << n.body << "), "; - NativeMessageBox::showAsync (MessageBoxOptions() + auto options = MessageBoxOptions() .withIconType (MessageBoxIconType::InfoIcon) .withTitle ("Pending notification list") .withMessage (text) - .withButton ("OK"), - nullptr); + .withButton ("OK"); + messageBox = NativeMessageBox::showScopedAsync (options, nullptr); } void deviceTokenRefreshed (const String& token) override { - NativeMessageBox::showAsync (MessageBoxOptions() + auto options = MessageBoxOptions() .withIconType (MessageBoxIconType::InfoIcon) .withTitle ("Device token refreshed") .withMessage (token) - .withButton ("OK"), - nullptr); + .withButton ("OK"); + messageBox = NativeMessageBox::showScopedAsync (options, nullptr); } #if JUCE_ANDROID void remoteNotificationsDeleted() override { - NativeMessageBox::showAsync (MessageBoxOptions() + auto options = MessageBoxOptions() .withIconType (MessageBoxIconType::InfoIcon) .withTitle ("Remote notifications deleted") .withMessage ("Some of the pending messages were removed!") - .withButton ("OK"), - nullptr); + .withButton ("OK"); + messageBox = NativeMessageBox::showScopedAsync (options, nullptr); } void upstreamMessageSent (const String& messageId) override { - NativeMessageBox::showAsync (MessageBoxOptions() + auto options = MessageBoxOptions() .withIconType (MessageBoxIconType::InfoIcon) .withTitle ("Upstream message sent") .withMessage ("Message id: " + messageId) - .withButton ("OK"), - nullptr); + .withButton ("OK"); + messageBox = NativeMessageBox::showScopedAsync (options, nullptr); } void upstreamMessageSendingError (const String& messageId, const String& error) override { - NativeMessageBox::showAsync (MessageBoxOptions() + auto options = MessageBoxOptions() .withIconType (MessageBoxIconType::InfoIcon) .withTitle ("Upstream message sending error") .withMessage ("Message id: " + messageId + "\nerror: " + error) - .withButton ("OK"), - nullptr); + .withButton ("OK"); + messageBox = NativeMessageBox::showScopedAsync (options, nullptr); } static Array getAndroidChannels() @@ -1207,8 +1211,8 @@ private: struct DemoTabbedComponent : public TabbedComponent { - explicit DemoTabbedComponent (TabbedButtonBar::Orientation orientation) - : TabbedComponent (orientation) + DemoTabbedComponent (PushNotificationsDemo& demoIn, TabbedButtonBar::Orientation orientation) + : TabbedComponent (orientation), demo (demoIn) { } @@ -1216,27 +1220,28 @@ private: { if (! showedRemoteInstructions && newCurrentTabName == "Remote") { - PushNotificationsDemo::showRemoteInstructions(); + demo.showRemoteInstructions(); showedRemoteInstructions = true; } } private: bool showedRemoteInstructions = false; + PushNotificationsDemo& demo; }; - static void showRemoteInstructions() + void showRemoteInstructions() { #if JUCE_IOS || JUCE_MAC - NativeMessageBox::showAsync (MessageBoxOptions() + auto options = MessageBoxOptions() .withIconType (MessageBoxIconType::InfoIcon) .withTitle ("Remote Notifications instructions") .withMessage ("In order to be able to test remote notifications " "ensure that the app is signed and that you register " "the bundle ID for remote notifications in " "Apple Developer Center.") - .withButton ("OK"), - nullptr); + .withButton ("OK"); + messageBox = NativeMessageBox::showScopedAsync (options, nullptr); #endif } @@ -1246,10 +1251,11 @@ private: AuxActionsView auxActionsView; TabbedComponent localNotificationsTabs { TabbedButtonBar::TabsAtTop }; RemoteView remoteView; - DemoTabbedComponent mainTabs { TabbedButtonBar::TabsAtTop }; + DemoTabbedComponent mainTabs { *this, TabbedButtonBar::TabsAtTop }; TextButton sendButton { "Send!" }; Label notAvailableYetLabel { "notAvailableYetLabel", "Push Notifications feature is not available on this platform yet!" }; + ScopedMessageBox messageBox; //============================================================================== JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PushNotificationsDemo) diff --git a/extras/AudioPluginHost/Source/Plugins/PluginGraph.cpp b/extras/AudioPluginHost/Source/Plugins/PluginGraph.cpp index 0701dd82c1..95f441ef9d 100644 --- a/extras/AudioPluginHost/Source/Plugins/PluginGraph.cpp +++ b/extras/AudioPluginHost/Source/Plugins/PluginGraph.cpp @@ -100,9 +100,10 @@ void PluginGraph::addPluginCallback (std::unique_ptr instan { if (instance == nullptr) { - AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, - TRANS("Couldn't create plugin"), - error); + auto options = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::WarningIcon, + TRANS ("Couldn't create plugin"), + error); + messageBox = AlertWindow::showScopedAsync (options, nullptr); } else { diff --git a/extras/AudioPluginHost/Source/Plugins/PluginGraph.h b/extras/AudioPluginHost/Source/Plugins/PluginGraph.h index 7bc12f23ae..86ca82f5b1 100644 --- a/extras/AudioPluginHost/Source/Plugins/PluginGraph.h +++ b/extras/AudioPluginHost/Source/Plugins/PluginGraph.h @@ -110,6 +110,7 @@ private: AudioPluginFormatManager& formatManager; KnownPluginList& knownPlugins; OwnedArray activePluginWindows; + ScopedMessageBox messageBox; NodeID lastUID; NodeID getNextUID() noexcept; diff --git a/extras/Projucer/Source/Application/StartPage/jucer_ContentComponents.h b/extras/Projucer/Source/Application/StartPage/jucer_ContentComponents.h index 71abe6a9d3..5c90b0567b 100644 --- a/extras/Projucer/Source/Application/StartPage/jucer_ContentComponents.h +++ b/extras/Projucer/Source/Application/StartPage/jucer_ContentComponents.h @@ -110,19 +110,20 @@ public: return; SafePointer safeThis { this }; - NewProjectWizard::createNewProject (projectTemplate, - dir.getChildFile (projectNameValue.get().toString()), - projectNameValue.get(), - modulesValue.get(), - exportersValue.get(), - fileOptionsValue.get(), - modulePathValue.getCurrentValue(), - modulePathValue.getWrappedValueTreePropertyWithDefault().isUsingDefault(), - [safeThis, dir] (std::unique_ptr project) + messageBox = NewProjectWizard::createNewProject (projectTemplate, + dir.getChildFile (projectNameValue.get().toString()), + projectNameValue.get(), + modulesValue.get(), + exportersValue.get(), + fileOptionsValue.get(), + modulePathValue.getCurrentValue(), + modulePathValue.getWrappedValueTreePropertyWithDefault().isUsingDefault(), + [safeThis, dir] (ScopedMessageBox mb, std::unique_ptr project) { if (safeThis == nullptr) return; + safeThis->messageBox = std::move (mb); safeThis->projectCreatedCallback (std::move (project)); getAppSettings().lastWizardFolder = dir; }); @@ -249,6 +250,8 @@ private: return builder.components; } + ScopedMessageBox messageBox; + //============================================================================== JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TemplateComponent) }; diff --git a/extras/Projucer/Source/Application/StartPage/jucer_NewProjectWizard.cpp b/extras/Projucer/Source/Application/StartPage/jucer_NewProjectWizard.cpp index c7e655fec0..d0811591b3 100644 --- a/extras/Projucer/Source/Application/StartPage/jucer_NewProjectWizard.cpp +++ b/extras/Projucer/Source/Application/StartPage/jucer_NewProjectWizard.cpp @@ -237,57 +237,51 @@ File NewProjectWizard::getLastWizardFolder() return lastFolderFallback; } -static void displayFailedFilesMessage (const StringArray& failedFiles) +static ScopedMessageBox displayFailedFilesMessage (const StringArray& failedFiles) { - AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, - TRANS("Errors in Creating Project!"), - TRANS("The following files couldn't be written:") - + "\n\n" - + failedFiles.joinIntoString ("\n", 0, 10)); + auto options = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::WarningIcon, + TRANS ("Errors in Creating Project!"), + TRANS ("The following files couldn't be written:") + + "\n\n" + + failedFiles.joinIntoString ("\n", 0, 10)); + return AlertWindow::showScopedAsync (options, nullptr); } template -static void prepareDirectory (const File& targetFolder, Callback&& callback) +static ScopedMessageBox prepareDirectory (const File& targetFolder, Callback&& callback) { StringArray failedFiles; if (! targetFolder.exists()) { if (! targetFolder.createDirectory()) - { - displayFailedFilesMessage ({ targetFolder.getFullPathName() }); - return; - } + return displayFailedFilesMessage ({ targetFolder.getFullPathName() }); } else if (FileHelpers::containsAnyNonHiddenFiles (targetFolder)) { - AlertWindow::showOkCancelBox (MessageBoxIconType::InfoIcon, - TRANS("New JUCE Project"), - TRANS("You chose the folder:\n\nXFLDRX\n\n").replace ("XFLDRX", targetFolder.getFullPathName()) - + TRANS("This folder isn't empty - are you sure you want to create the project there?") - + "\n\n" - + TRANS("Any existing files with the same names may be overwritten by the new files."), - {}, - {}, - nullptr, - ModalCallbackFunction::create ([callback] (int result) - { - if (result != 0) - callback(); - })); - - return; + auto options = MessageBoxOptions::makeOptionsOkCancel (MessageBoxIconType::InfoIcon, + TRANS ("New JUCE Project"), + TRANS ("You chose the folder:\n\nXFLDRX\n\n").replace ("XFLDRX", targetFolder.getFullPathName()) + + TRANS ("This folder isn't empty - are you sure you want to create the project there?") + + "\n\n" + + TRANS ("Any existing files with the same names may be overwritten by the new files.")); + return AlertWindow::showScopedAsync (options, [callback] (int result) + { + if (result != 0) + callback(); + }); } callback(); + return ScopedMessageBox(); } -void NewProjectWizard::createNewProject (const NewProjectTemplates::ProjectTemplate& projectTemplate, - const File& targetFolder, const String& name, var modules, var exporters, var fileOptions, - const String& modulePath, bool useGlobalModulePath, - std::function)> callback) +ScopedMessageBox NewProjectWizard::createNewProject (const NewProjectTemplates::ProjectTemplate& projectTemplate, + const File& targetFolder, const String& name, var modules, var exporters, var fileOptions, + const String& modulePath, bool useGlobalModulePath, + std::function)> callback) { - prepareDirectory (targetFolder, [=] + return prepareDirectory (targetFolder, [=] { auto project = std::make_unique (targetFolder.getChildFile (File::createLegalFileName (name)) .withFileExtension (Project::projectFileExtension)); @@ -310,18 +304,18 @@ void NewProjectWizard::createNewProject (const NewProjectTemplates::ProjectTempl { uniqueProject->setChangedFlag (false); uniqueProject->loadFrom (uniqueProject->getFile(), true); - callback (std::move (uniqueProject)); + callback ({}, std::move (uniqueProject)); return; } auto failedFilesCopy = failedFiles; failedFilesCopy.add (uniqueProject->getFile().getFullPathName()); - displayFailedFilesMessage (failedFilesCopy); + callback (displayFailedFilesMessage (failedFilesCopy), {}); }); return; } - displayFailedFilesMessage (failedFiles); + callback (displayFailedFilesMessage (failedFiles), {}); }); } diff --git a/extras/Projucer/Source/Application/StartPage/jucer_NewProjectWizard.h b/extras/Projucer/Source/Application/StartPage/jucer_NewProjectWizard.h index 4ed698b7cf..96cdade7fc 100644 --- a/extras/Projucer/Source/Application/StartPage/jucer_NewProjectWizard.h +++ b/extras/Projucer/Source/Application/StartPage/jucer_NewProjectWizard.h @@ -32,8 +32,8 @@ namespace NewProjectWizard { File getLastWizardFolder(); - void createNewProject (const NewProjectTemplates::ProjectTemplate& projectTemplate, - const File& targetFolder, const String& name, var modules, var exporters, var fileOptions, - const String& modulePath, bool useGlobalModulePath, - std::function)> callback); + ScopedMessageBox createNewProject (const NewProjectTemplates::ProjectTemplate& projectTemplate, + const File& targetFolder, const String& name, var modules, var exporters, var fileOptions, + const String& modulePath, bool useGlobalModulePath, + std::function)> callback); } diff --git a/extras/Projucer/Source/Application/Windows/jucer_TranslationToolWindowComponent.h b/extras/Projucer/Source/Application/Windows/jucer_TranslationToolWindowComponent.h index 86de5470e2..55d29eb516 100644 --- a/extras/Projucer/Source/Application/Windows/jucer_TranslationToolWindowComponent.h +++ b/extras/Projucer/Source/Application/Windows/jucer_TranslationToolWindowComponent.h @@ -122,10 +122,11 @@ private: if (postStrings.size() != preStrings.size()) { - AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, - TRANS("Error"), - TRANS("The pre- and post-translation text doesn't match!\n\n" - "Perhaps it got mangled by the translator?")); + auto options = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::WarningIcon, + TRANS ("Error"), + TRANS ("The pre- and post-translation text doesn't match!\n\n" + "Perhaps it got mangled by the translator?")); + messageBox = AlertWindow::showScopedAsync (options, nullptr); return; } @@ -136,10 +137,16 @@ private: void scanProject() { if (Project* project = ProjucerApplication::getApp().mainWindowList.getFrontmostProject()) + { setPreTranslationText (TranslationHelpers::getPreTranslationText (*project)); + } else - AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, "Translation Tool", - "This will only work when you have a project open!"); + { + auto options = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::WarningIcon, + "Translation Tool", + "This will only work when you have a project open!"); + messageBox = AlertWindow::showScopedAsync (options, nullptr); + } } void scanFolder() @@ -195,4 +202,5 @@ private: loadTranslationButton { "Load existing translation file..."}; std::unique_ptr chooser; + ScopedMessageBox messageBox; }; diff --git a/extras/Projucer/Source/Application/jucer_Application.cpp b/extras/Projucer/Source/Application/jucer_Application.cpp index 1b1dfc1d80..319b391293 100644 --- a/extras/Projucer/Source/Application/jucer_Application.cpp +++ b/extras/Projucer/Source/Application/jucer_Application.cpp @@ -1098,13 +1098,14 @@ void ProjucerApplication::createNewProjectFromClipboard() tempFile.create(); tempFile.appendText (SystemClipboard::getTextFromClipboard()); - auto cleanup = [tempFile] (String errorString) + auto cleanup = [parent = WeakReference { this }, tempFile] (String errorString) { - if (errorString.isNotEmpty()) - { - AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, "Error", errorString); - tempFile.deleteFile(); - } + if (parent == nullptr || errorString.isEmpty()) + return; + + auto options = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::WarningIcon, "Error", errorString); + parent->messageBox = AlertWindow::showScopedAsync (options, nullptr); + tempFile.deleteFile(); }; if (! isPIPFile (tempFile)) @@ -1113,7 +1114,7 @@ void ProjucerApplication::createNewProjectFromClipboard() return; } - openFile (tempFile, [parent = WeakReference { this }, cleanup] (bool openedSuccessfully) + openFile (tempFile, [parent = WeakReference { this }, cleanup] (bool openedSuccessfully) { if (parent == nullptr) return; diff --git a/extras/Projucer/Source/Application/jucer_Application.h b/extras/Projucer/Source/Application/jucer_Application.h index 8468c05412..9e94627455 100644 --- a/extras/Projucer/Source/Application/jucer_Application.h +++ b/extras/Projucer/Source/Application/jucer_Application.h @@ -217,6 +217,7 @@ private: int selectedColourSchemeIndex = 0, selectedEditorColourSchemeIndex = 0; std::unique_ptr chooser; + ScopedMessageBox messageBox; //============================================================================== JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProjucerApplication) diff --git a/extras/Projucer/Source/Application/jucer_AutoUpdater.cpp b/extras/Projucer/Source/Application/jucer_AutoUpdater.cpp index 204d649be5..826c0bb442 100644 --- a/extras/Projucer/Source/Application/jucer_AutoUpdater.cpp +++ b/extras/Projucer/Source/Application/jucer_AutoUpdater.cpp @@ -56,11 +56,14 @@ void LatestVersionCheckerAndUpdater::run() if (info == nullptr) { if (! backgroundCheck) - AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, - "Update Server Communication Error", - "Failed to communicate with the JUCE update server.\n" - "Please try again in a few minutes.\n\n" - "If this problem persists you can download the latest version of JUCE from juce.com"); + { + auto options = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::WarningIcon, + "Update Server Communication Error", + "Failed to communicate with the JUCE update server.\n" + "Please try again in a few minutes.\n\n" + "If this problem persists you can download the latest version of JUCE from juce.com"); + messageBox = AlertWindow::showScopedAsync (options, nullptr); + } return; } @@ -68,9 +71,12 @@ void LatestVersionCheckerAndUpdater::run() if (! info->isNewerVersionThanCurrent()) { if (! backgroundCheck) - AlertWindow::showMessageBoxAsync (MessageBoxIconType::InfoIcon, - "No New Version Available", - "Your JUCE version is up to date."); + { + auto options = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::InfoIcon, + "No New Version Available", + "Your JUCE version is up to date."); + messageBox = AlertWindow::showScopedAsync (options, nullptr); + } return; } @@ -109,9 +115,12 @@ void LatestVersionCheckerAndUpdater::run() } if (! backgroundCheck) - AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, - "Failed to find any new downloads", - "Please try again in a few minutes."); + { + auto options = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::WarningIcon, + "Failed to find any new downloads", + "Please try again in a few minutes."); + messageBox = AlertWindow::showScopedAsync (options, nullptr); + } } //============================================================================== @@ -275,33 +284,38 @@ void LatestVersionCheckerAndUpdater::askUserForLocationToDownload (const Version { if (targetFolder.getChildFile (".git").isDirectory()) { - AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, "Downloading New JUCE Version", - targetFolderPath + "\n\nis a GIT repository!\n\nYou should use a \"git pull\" to update it to the latest version."); + auto options = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::WarningIcon, + "Downloading New JUCE Version", + targetFolderPath + "\n\n" + "is a GIT repository!\n\n" + "You should use a \"git pull\" to update it to the latest version."); + if (weakThis != nullptr) + weakThis->messageBox = AlertWindow::showScopedAsync (options, nullptr); return; } - AlertWindow::showOkCancelBox (MessageBoxIconType::WarningIcon, - "Overwrite Existing JUCE Folder?", - "Do you want to replace the folder\n\n" + targetFolderPath + "\n\nwith the latest version from juce.com?\n\n" - "This will move the existing folder to " + targetFolderPath + "_old.\n\n" - "Replacing the folder that contains the currently running Projucer executable may not work on Windows.", - {}, - {}, - nullptr, - ModalCallbackFunction::create (onResult)); + auto options = MessageBoxOptions::makeOptionsOkCancel (MessageBoxIconType::WarningIcon, + "Overwrite Existing JUCE Folder?", + "Do you want to replace the folder\n\n" + targetFolderPath + "\n\n" + "with the latest version from juce.com?\n\n" + "This will move the existing folder to " + targetFolderPath + "_old.\n\n" + "Replacing the folder that contains the currently running Projucer executable may not work on Windows."); + if (weakThis != nullptr) + weakThis->messageBox = AlertWindow::showScopedAsync (options, onResult); + return; } if (targetFolder.exists()) { - AlertWindow::showOkCancelBox (MessageBoxIconType::WarningIcon, - "Existing File Or Directory", - "Do you want to move\n\n" + targetFolderPath + "\n\nto\n\n" + targetFolderPath + "_old?", - {}, - {}, - nullptr, - ModalCallbackFunction::create (onResult)); + auto options = MessageBoxOptions::makeOptionsOkCancel (MessageBoxIconType::WarningIcon, + "Existing File Or Directory", + "Do you want to move\n\n" + targetFolderPath + "\n\n" + "to\n\n" + targetFolderPath + "_old?"); + if (weakThis != nullptr) + weakThis->messageBox = AlertWindow::showScopedAsync (options, onResult); + return; } @@ -369,7 +383,7 @@ void LatestVersionCheckerAndUpdater::addNotificationToOpenProjects (const Versio class DownloadAndInstallThread : private ThreadWithProgressWindow { public: - DownloadAndInstallThread (const VersionInfo::Asset& a, const File& t, std::function&& cb) + DownloadAndInstallThread (const VersionInfo::Asset& a, const File& t, std::function&& cb) : ThreadWithProgressWindow ("Downloading New Version", true, true), asset (a), targetFolder (t), completionCallback (std::move (cb)) { @@ -387,12 +401,10 @@ private: if (result.wasOk() && ! threadShouldExit()) result = install (zipData); - if (result.failed()) - MessageManager::callAsync ([result] { AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, - "Installation Failed", - result.getErrorMessage()); }); - else - MessageManager::callAsync (completionCallback); + MessageManager::callAsync ([result, callback = completionCallback] + { + callback (result); + }); } Result download (MemoryBlock& dest) @@ -517,7 +529,7 @@ private: VersionInfo::Asset asset; File targetFolder; - std::function completionCallback; + std::function completionCallback; }; static void restartProcess (const File& targetFolder) @@ -548,12 +560,22 @@ static void restartProcess (const File& targetFolder) void LatestVersionCheckerAndUpdater::downloadAndInstall (const VersionInfo::Asset& asset, const File& targetFolder) { - installer.reset (new DownloadAndInstallThread (asset, targetFolder, - [this, targetFolder] - { - installer.reset(); - restartProcess (targetFolder); - })); + installer.reset (new DownloadAndInstallThread (asset, targetFolder, [this, targetFolder] (const auto result) + { + if (result.failed()) + { + auto options = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::WarningIcon, + "Installation Failed", + result.getErrorMessage()); + + messageBox = AlertWindow::showScopedAsync (options, nullptr); + } + else + { + installer.reset(); + restartProcess (targetFolder); + } + })); } //============================================================================== diff --git a/extras/Projucer/Source/Application/jucer_AutoUpdater.h b/extras/Projucer/Source/Application/jucer_AutoUpdater.h index cd9dc1778f..cc4f705e9a 100644 --- a/extras/Projucer/Source/Application/jucer_AutoUpdater.h +++ b/extras/Projucer/Source/Application/jucer_AutoUpdater.h @@ -57,6 +57,7 @@ private: std::unique_ptr installer; std::unique_ptr dialogWindow; std::unique_ptr chooser; + ScopedMessageBox messageBox; JUCE_DECLARE_WEAK_REFERENCEABLE (LatestVersionCheckerAndUpdater) }; diff --git a/extras/Projucer/Source/Application/jucer_MainWindow.cpp b/extras/Projucer/Source/Application/jucer_MainWindow.cpp index f52fd4ff71..78494748a5 100644 --- a/extras/Projucer/Source/Application/jucer_MainWindow.cpp +++ b/extras/Projucer/Source/Application/jucer_MainWindow.cpp @@ -462,9 +462,10 @@ void MainWindow::openPIP (const File& pipFile, std::function callba if (generatorResult != Result::ok()) { - AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, - "PIP Error.", - generatorResult.getErrorMessage()); + auto options = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::WarningIcon, + "PIP Error.", + generatorResult.getErrorMessage()); + messageBox = AlertWindow::showScopedAsync (options, nullptr); if (callback != nullptr) callback (false); @@ -474,9 +475,10 @@ void MainWindow::openPIP (const File& pipFile, std::function callba if (! generator->createMainCpp()) { - AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, - "PIP Error.", - "Failed to create Main.cpp."); + auto options = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::WarningIcon, + "PIP Error.", + "Failed to create Main.cpp."); + messageBox = AlertWindow::showScopedAsync (options, nullptr); if (callback != nullptr) callback (false); @@ -491,9 +493,10 @@ void MainWindow::openPIP (const File& pipFile, std::function callba if (! openedSuccessfully) { - AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, - "PIP Error.", - "Failed to open .jucer file."); + auto options = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::WarningIcon, + "PIP Error.", + "Failed to open .jucer file."); + parent->messageBox = AlertWindow::showScopedAsync (options, nullptr); if (callback != nullptr) callback (false); diff --git a/extras/Projucer/Source/Application/jucer_MainWindow.h b/extras/Projucer/Source/Application/jucer_MainWindow.h index a9234089db..b7bfad8f80 100644 --- a/extras/Projucer/Source/Application/jucer_MainWindow.h +++ b/extras/Projucer/Source/Application/jucer_MainWindow.h @@ -104,6 +104,8 @@ private: std::unique_ptr blurOverlayComponent; bool loginFormOpen = false; + ScopedMessageBox messageBox; + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainWindow) }; diff --git a/extras/Projucer/Source/CodeEditor/jucer_OpenDocumentManager.cpp b/extras/Projucer/Source/CodeEditor/jucer_OpenDocumentManager.cpp index 8ed2cdedcd..7a1901b72a 100644 --- a/extras/Projucer/Source/CodeEditor/jucer_OpenDocumentManager.cpp +++ b/extras/Projucer/Source/CodeEditor/jucer_OpenDocumentManager.cpp @@ -176,15 +176,14 @@ void OpenDocumentManager::saveIfNeededAndUserAgrees (OpenDocumentManager::Docume return; } - AlertWindow::showYesNoCancelBox (MessageBoxIconType::QuestionIcon, - TRANS("Closing document..."), - TRANS("Do you want to save the changes to \"") - + doc->getName() + "\"?", - TRANS("Save"), - TRANS("Discard changes"), - TRANS("Cancel"), - nullptr, - ModalCallbackFunction::create ([parent = WeakReference { this }, doc, callback] (int r) + auto options = MessageBoxOptions::makeOptionsYesNoCancel (MessageBoxIconType::QuestionIcon, + TRANS ("Closing document..."), + TRANS ("Do you want to save the changes to \"") + + doc->getName() + "\"?", + TRANS ("Save"), + TRANS ("Discard changes"), + TRANS ("Cancel")); + messageBox = AlertWindow::showScopedAsync (options, [parent = WeakReference { this }, doc, callback] (int r) { if (parent == nullptr) return; @@ -204,7 +203,7 @@ void OpenDocumentManager::saveIfNeededAndUserAgrees (OpenDocumentManager::Docume if (callback != nullptr) callback (r == 2 ? FileBasedDocument::savedOk : FileBasedDocument::userCancelledSave); - })); + }); } bool OpenDocumentManager::closeDocumentWithoutSaving (Document* doc) diff --git a/extras/Projucer/Source/CodeEditor/jucer_OpenDocumentManager.h b/extras/Projucer/Source/CodeEditor/jucer_OpenDocumentManager.h index 862e65bdf7..161763e4d9 100644 --- a/extras/Projucer/Source/CodeEditor/jucer_OpenDocumentManager.h +++ b/extras/Projucer/Source/CodeEditor/jucer_OpenDocumentManager.h @@ -108,8 +108,7 @@ public: class DocumentType { public: - DocumentType() {} - virtual ~DocumentType() {} + virtual ~DocumentType() = default; virtual bool canOpenFile (const File& file) = 0; virtual Document* openFile (Project* project, const File& file) = 0; @@ -117,7 +116,6 @@ public: void registerType (DocumentType* type, int index = -1); - private: //============================================================================== void closeLastDocumentUsingProjectRecursive (WeakReference, @@ -129,6 +127,7 @@ private: OwnedArray types; OwnedArray documents; Array listeners; + ScopedMessageBox messageBox; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenDocumentManager) JUCE_DECLARE_WEAK_REFERENCEABLE (OpenDocumentManager) diff --git a/extras/Projucer/Source/ComponentEditor/UI/jucer_ResourceEditorPanel.cpp b/extras/Projucer/Source/ComponentEditor/UI/jucer_ResourceEditorPanel.cpp index 03d73fa43e..f70d9f4c0e 100644 --- a/extras/Projucer/Source/ComponentEditor/UI/jucer_ResourceEditorPanel.cpp +++ b/extras/Projucer/Source/ComponentEditor/UI/jucer_ResourceEditorPanel.cpp @@ -265,8 +265,11 @@ void ResourceEditorPanel::reloadAll() failed.add (document.getResources().getResourceNames() [i]); if (failed.size() > 0) - AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, - TRANS("Reloading resources"), - TRANS("The following resources couldn't be reloaded from their original files:\n\n") - + failed.joinIntoString (", ")); + { + auto options = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::WarningIcon, + TRANS ("Reloading resources"), + TRANS ("The following resources couldn't be reloaded from their original files:\n\n") + + failed.joinIntoString (", ")); + messageBox = AlertWindow::showScopedAsync (options, nullptr); + } } diff --git a/extras/Projucer/Source/ComponentEditor/UI/jucer_ResourceEditorPanel.h b/extras/Projucer/Source/ComponentEditor/UI/jucer_ResourceEditorPanel.h index e54678a8dd..40b0cbc8f3 100644 --- a/extras/Projucer/Source/ComponentEditor/UI/jucer_ResourceEditorPanel.h +++ b/extras/Projucer/Source/ComponentEditor/UI/jucer_ResourceEditorPanel.h @@ -56,4 +56,5 @@ private: JucerDocument& document; std::unique_ptr listBox; TextButton addButton, reloadAllButton, delButton; + ScopedMessageBox messageBox; }; diff --git a/extras/Projucer/Source/ComponentEditor/jucer_BinaryResources.cpp b/extras/Projucer/Source/ComponentEditor/jucer_BinaryResources.cpp index f9e393e466..6695ef0048 100644 --- a/extras/Projucer/Source/ComponentEditor/jucer_BinaryResources.cpp +++ b/extras/Projucer/Source/ComponentEditor/jucer_BinaryResources.cpp @@ -160,9 +160,10 @@ void BinaryResources::browseForResource (const String& title, { if (! safeThis->add (resourceName, result)) { - AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, - TRANS("Adding Resource"), - TRANS("Failed to load the file!")); + auto options = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::WarningIcon, + TRANS ("Adding Resource"), + TRANS ("Failed to load the file!")); + safeThis->messageBox = AlertWindow::showScopedAsync (options, nullptr); resourceName.clear(); } diff --git a/extras/Projucer/Source/ComponentEditor/jucer_BinaryResources.h b/extras/Projucer/Source/ComponentEditor/jucer_BinaryResources.h index 34e5ca2628..a0f32fc6eb 100644 --- a/extras/Projucer/Source/ComponentEditor/jucer_BinaryResources.h +++ b/extras/Projucer/Source/ComponentEditor/jucer_BinaryResources.h @@ -93,6 +93,7 @@ private: JucerDocument* document; OwnedArray resources; std::unique_ptr chooser; + ScopedMessageBox messageBox; //============================================================================== JUCE_DECLARE_WEAK_REFERENCEABLE (BinaryResources) diff --git a/extras/Projucer/Source/Project/Modules/jucer_Modules.cpp b/extras/Projucer/Source/Project/Modules/jucer_Modules.cpp index b5ba791491..7cfe3f5bc8 100644 --- a/extras/Projucer/Source/Project/Modules/jucer_Modules.cpp +++ b/extras/Projucer/Source/Project/Modules/jucer_Modules.cpp @@ -687,19 +687,24 @@ void EnabledModulesList::addModuleOfferingToCopy (const File& f, bool isFromUser if (! m.isValid()) { - AlertWindow::showMessageBoxAsync (MessageBoxIconType::InfoIcon, - "Add Module", "This wasn't a valid module folder!"); + auto options = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::InfoIcon, + "Add Module", + "This wasn't a valid module folder!"); + messageBox = AlertWindow::showScopedAsync (options, nullptr); return; } if (isModuleEnabled (m.getID())) { - AlertWindow::showMessageBoxAsync (MessageBoxIconType::InfoIcon, - "Add Module", "The project already contains this module!"); + auto options = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::InfoIcon, + "Add Module", + "The project already contains this module!"); + messageBox = AlertWindow::showScopedAsync (options, nullptr); return; } - addModule (m.getModuleFolder(), areMostModulesCopiedLocally(), + addModule (m.getModuleFolder(), + areMostModulesCopiedLocally(), isFromUserSpecifiedFolder ? false : areMostModulesUsingGlobalPath()); } diff --git a/extras/Projucer/Source/Project/Modules/jucer_Modules.h b/extras/Projucer/Source/Project/Modules/jucer_Modules.h index 9b2a4a5a83..554009b2f9 100644 --- a/extras/Projucer/Source/Project/Modules/jucer_Modules.h +++ b/extras/Projucer/Source/Project/Modules/jucer_Modules.h @@ -143,6 +143,7 @@ private: ValueTree state; std::unique_ptr chooser; + ScopedMessageBox messageBox; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (EnabledModulesList) }; diff --git a/extras/Projucer/Source/Project/UI/Sidebar/jucer_ExporterTreeItems.h b/extras/Projucer/Source/Project/UI/Sidebar/jucer_ExporterTreeItems.h index 77fe778ef3..adec1a752e 100644 --- a/extras/Projucer/Source/Project/UI/Sidebar/jucer_ExporterTreeItems.h +++ b/extras/Projucer/Source/Project/UI/Sidebar/jucer_ExporterTreeItems.h @@ -77,7 +77,10 @@ public: void deleteItem() override { - auto resultCallback = [safeThis = WeakReference { this }] (int result) + auto options = MessageBoxOptions::makeOptionsOkCancel (MessageBoxIconType::WarningIcon, + "Delete Exporter", + "Are you sure you want to delete this export target?"); + messageBox = AlertWindow::showScopedAsync (options, [safeThis = WeakReference { this }] (int result) { if (safeThis == nullptr || result == 0) return; @@ -87,15 +90,7 @@ public: auto parent = safeThis->exporter->settings.getParent(); parent.removeChild (safeThis->exporter->settings, safeThis->project.getUndoManagerFor (parent)); - }; - - AlertWindow::showOkCancelBox (MessageBoxIconType::WarningIcon, - "Delete Exporter", - "Are you sure you want to delete this export target?", - "", - "", - nullptr, - ModalCallbackFunction::create (std::move (resultCallback))); + }); } void addSubItems() override @@ -180,6 +175,8 @@ private: Value targetLocationValue; + ScopedMessageBox messageBox; + void valueChanged (Value& value) override { if (value == exporter->getTargetLocationValue()) @@ -243,13 +240,10 @@ public: void deleteItem() override { - AlertWindow::showOkCancelBox (MessageBoxIconType::WarningIcon, - "Delete Configuration", - "Are you sure you want to delete this configuration?", - "", - "", - nullptr, - ModalCallbackFunction::create ([parent = WeakReference { this }] (int result) + auto options = MessageBoxOptions::makeOptionsOkCancel (MessageBoxIconType::WarningIcon, + "Delete Configuration", + "Are you sure you want to delete this configuration?"); + messageBox = AlertWindow::showScopedAsync (options, [parent = WeakReference { this }] (int result) { if (parent == nullptr) return; @@ -259,7 +253,7 @@ public: parent->closeSettingsPage(); parent->config->removeFromExporter(); - })); + }); } void showPopupMenu (Point p) override @@ -293,6 +287,7 @@ private: ProjectExporter::BuildConfiguration::Ptr config; ProjectExporter& exporter; ValueTree configTree; + ScopedMessageBox messageBox; //============================================================================== class SettingsComp : public Component @@ -321,7 +316,6 @@ private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConfigItem) JUCE_DECLARE_WEAK_REFERENCEABLE (ConfigItem) - }; //============================================================================== diff --git a/extras/Projucer/Source/Project/UI/Sidebar/jucer_FileTreeItems.h b/extras/Projucer/Source/Project/UI/Sidebar/jucer_FileTreeItems.h index 481e7b1f13..d1e0b87ce8 100644 --- a/extras/Projucer/Source/Project/UI/Sidebar/jucer_FileTreeItems.h +++ b/extras/Projucer/Source/Project/UI/Sidebar/jucer_FileTreeItems.h @@ -138,15 +138,14 @@ public: if (filesToTrash.size() > maxFilesToList) fileList << "\n...plus " << (filesToTrash.size() - maxFilesToList) << " more files..."; - AlertWindow::showYesNoCancelBox (MessageBoxIconType::NoIcon, - "Delete Project Items", - "As well as removing the selected item(s) from the project, do you also want to move their files to the trash:\n\n" - + fileList, - "Just remove references", - "Also move files to Trash", - "Cancel", - tree->getTopLevelComponent(), - ModalCallbackFunction::create ([treeRootItem, filesToTrash, doDelete] (int r) mutable + auto options = MessageBoxOptions::makeOptionsYesNoCancel (MessageBoxIconType::NoIcon, + "Delete Project Items", + "As well as removing the selected item(s) from the project, do you also want to move their files to the trash:\n\n" + fileList, + "Just remove references", + "Also move files to Trash", + "Cancel", + tree->getTopLevelComponent()); + messageBox = AlertWindow::showScopedAsync (options, [treeRootItem, filesToTrash, doDelete] (int r) mutable { if (treeRootItem == nullptr) return; @@ -158,7 +157,7 @@ public: filesToTrash.clear(); doDelete (filesToTrash); - })); + }); return; } @@ -471,6 +470,8 @@ protected: return -1; } + ScopedMessageBox messageBox; + private: std::unique_ptr chooser; @@ -521,10 +522,13 @@ public: { if (newName != File::createLegalFileName (newName)) { - AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, - "File Rename", - "That filename contained some illegal characters!"); - triggerAsyncRename (item); + auto options = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::WarningIcon, + "File Rename", + "That filename contained some illegal characters!"); + messageBox = AlertWindow::showScopedAsync (options, [this, item = item] (int) + { + triggerAsyncRename (item); + }); return; } @@ -538,42 +542,40 @@ public: if (correspondingItem.isValid()) { - AlertWindow::showOkCancelBox (MessageBoxIconType::NoIcon, - "File Rename", - "Do you also want to rename the corresponding file \"" + correspondingFile.getFileName() + "\" to match?", - {}, - {}, - nullptr, - ModalCallbackFunction::create ([parent = WeakReference { this }, - oldFile, newFile, correspondingFile, correspondingItem] (int result) mutable + auto options = MessageBoxOptions::makeOptionsOkCancel (MessageBoxIconType::NoIcon, + "File Rename", + "Do you also want to rename the corresponding file \"" + correspondingFile.getFileName() + "\" to match?"); + messageBox = AlertWindow::showScopedAsync (options, [parent = WeakReference { this }, oldFile, newFile, correspondingFile, correspondingItem] (int result) mutable { if (parent == nullptr || result == 0) return; if (! parent->item.renameFile (newFile)) { - AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, - "File Rename", - "Failed to rename \"" + oldFile.getFullPathName() + "\"!\n\nCheck your file permissions!"); + auto opts = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::WarningIcon, + "File Rename", + "Failed to rename \"" + oldFile.getFullPathName() + "\"!\n\nCheck your file permissions!"); + parent->messageBox = AlertWindow::showScopedAsync (opts, nullptr); return; } if (! correspondingItem.renameFile (newFile.withFileExtension (correspondingFile.getFileExtension()))) { - AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, - "File Rename", - "Failed to rename \"" + correspondingFile.getFullPathName() + "\"!\n\nCheck your file permissions!"); + auto opts = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::WarningIcon, + "File Rename", + "Failed to rename \"" + correspondingFile.getFullPathName() + "\"!\n\nCheck your file permissions!"); + parent->messageBox = AlertWindow::showScopedAsync (opts, nullptr); } - - })); + }); } } if (! item.renameFile (newFile)) { - AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, - "File Rename", - "Failed to rename the file!\n\nCheck your file permissions!"); + auto options = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::WarningIcon, + "File Rename", + "Failed to rename the file!\n\nCheck your file permissions!"); + messageBox = AlertWindow::showScopedAsync (options, nullptr); } } diff --git a/extras/Projucer/Source/Project/UI/Sidebar/jucer_ModuleTreeItems.h b/extras/Projucer/Source/Project/UI/Sidebar/jucer_ModuleTreeItems.h index 5360756dac..cbaae794d2 100644 --- a/extras/Projucer/Source/Project/UI/Sidebar/jucer_ModuleTreeItems.h +++ b/extras/Projucer/Source/Project/UI/Sidebar/jucer_ModuleTreeItems.h @@ -375,10 +375,11 @@ private: { missingDependencies = enabledModules.getExtraDependenciesNeeded (moduleID); - AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, - "Adding Missing Dependencies", - "Couldn't locate some of these modules - you'll need to find their " - "folders manually and add them to the list."); + auto options = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::WarningIcon, + "Adding Missing Dependencies", + "Couldn't locate some of these modules - you'll need to find their " + "folders manually and add them to the list."); + messageBox = AlertWindow::showScopedAsync (options, nullptr); } } @@ -393,6 +394,7 @@ private: String moduleID; StringArray missingDependencies; TextButton fixButton { "Add Required Modules" }; + ScopedMessageBox messageBox; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MissingDependenciesComponent) }; diff --git a/extras/Projucer/Source/Project/UI/jucer_ProjectContentComponent.cpp b/extras/Projucer/Source/Project/UI/jucer_ProjectContentComponent.cpp index 28b72d7b6b..0ceae94e7a 100644 --- a/extras/Projucer/Source/Project/UI/jucer_ProjectContentComponent.cpp +++ b/extras/Projucer/Source/Project/UI/jucer_ProjectContentComponent.cpp @@ -318,25 +318,26 @@ void ProjectContentComponent::closeDocument() hideEditor(); } -static void showSaveWarning (OpenDocumentManager::Document* currentDocument) +static ScopedMessageBox showSaveWarning (OpenDocumentManager::Document* currentDocument) { - AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, - TRANS("Save failed!"), - TRANS("Couldn't save the file:") - + "\n" + currentDocument->getFile().getFullPathName()); + auto options = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::WarningIcon, + TRANS ("Save failed!"), + TRANS ("Couldn't save the file:") + + "\n" + currentDocument->getFile().getFullPathName()); + return AlertWindow::showScopedAsync (options, nullptr); } void ProjectContentComponent::saveDocumentAsync() { if (currentDocument != nullptr) { - currentDocument->saveAsync ([parent = SafePointer { this }] (bool savedSuccessfully) + currentDocument->saveAsync ([parent = SafePointer { this }] (bool savedSuccessfully) { if (parent == nullptr) return; if (! savedSuccessfully) - showSaveWarning (parent->currentDocument); + parent->messageBox = showSaveWarning (parent->currentDocument); parent->refreshProjectTreeFileStatuses(); }); @@ -351,13 +352,13 @@ void ProjectContentComponent::saveAsAsync() { if (currentDocument != nullptr) { - currentDocument->saveAsAsync ([parent = SafePointer { this }] (bool savedSuccessfully) + currentDocument->saveAsAsync ([parent = SafePointer { this }] (bool savedSuccessfully) { if (parent == nullptr) return; if (! savedSuccessfully) - showSaveWarning (parent->currentDocument); + parent->messageBox = showSaveWarning (parent->currentDocument); parent->refreshProjectTreeFileStatuses(); }); diff --git a/extras/Projucer/Source/Project/UI/jucer_ProjectContentComponent.h b/extras/Projucer/Source/Project/UI/jucer_ProjectContentComponent.h index cfba44cdc9..179ed9b5d2 100644 --- a/extras/Projucer/Source/Project/UI/jucer_ProjectContentComponent.h +++ b/extras/Projucer/Source/Project/UI/jucer_ProjectContentComponent.h @@ -147,6 +147,7 @@ private: int lastViewedTab = 0; std::unique_ptr wizardHolder; + ScopedMessageBox messageBox; //============================================================================== JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProjectContentComponent) diff --git a/extras/Projucer/Source/Project/jucer_Project.cpp b/extras/Projucer/Source/Project/jucer_Project.cpp index 3e4c025831..9195972619 100644 --- a/extras/Projucer/Source/Project/jucer_Project.cpp +++ b/extras/Projucer/Source/Project/jucer_Project.cpp @@ -462,7 +462,8 @@ void Project::removeDefunctExporters() warningMessage << "\n" << TRANS ("These exporters have been removed from the project. If you save the project they will be also erased from the .jucer file."); - AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, warningTitle, warningMessage); + auto options = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::WarningIcon, warningTitle, warningMessage); + messageBox = AlertWindow::showScopedAsync (options, nullptr); } } } diff --git a/extras/Projucer/Source/Project/jucer_Project.h b/extras/Projucer/Source/Project/jucer_Project.h index e23f50269d..1a67f41a12 100644 --- a/extras/Projucer/Source/Project/jucer_Project.h +++ b/extras/Projucer/Source/Project/jucer_Project.h @@ -659,6 +659,7 @@ private: std::unique_ptr chooser; std::unique_ptr saver; + ScopedMessageBox messageBox; //============================================================================== JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Project) diff --git a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Xcode.h b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Xcode.h index 892babbdd4..66b48dd2ac 100644 --- a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Xcode.h +++ b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Xcode.h @@ -771,10 +771,16 @@ public: "Since JUCE 4.2, this is instead done using \"AU/VST/VST2/AAX Binary Location\" in the Xcode (OS X) configuration settings.\n\n" "Click 'Update' to remove the script (otherwise your plug-in may not compile correctly)."; - if (AlertWindow::showOkCancelBox (MessageBoxIconType::WarningIcon, - "Project settings: " + project.getDocumentTitle(), - alertWindowText, "Update", "Cancel", nullptr, nullptr)) - postbuildCommandValue.resetToDefault(); + auto options = MessageBoxOptions::makeOptionsOkCancel (MessageBoxIconType::WarningIcon, + "Project settings: " + project.getDocumentTitle(), + alertWindowText, + "Update", + "Cancel"); + messageBox = AlertWindow::showScopedAsync (options, [this] (int result) + { + if (result != 0) + postbuildCommandValue.resetToDefault(); + }); } } @@ -3637,6 +3643,7 @@ private: iosContentSharingValue, iosBackgroundAudioValue, iosBackgroundBleValue, iosPushNotificationsValue, iosAppGroupsValue, iCloudPermissionsValue, networkingMulticastValue, iosDevelopmentTeamIDValue, iosAppGroupsIDValue, keepCustomXcodeSchemesValue, useHeaderMapValue, customLaunchStoryboardValue, exporterBundleIdentifierValue, suppressPlistResourceUsageValue, useLegacyBuildSystemValue, buildNumber; + ScopedMessageBox messageBox; struct SandboxFileAccessProperty { diff --git a/extras/Projucer/Source/Utility/Helpers/jucer_NewFileWizard.cpp b/extras/Projucer/Source/Utility/Helpers/jucer_NewFileWizard.cpp index a86479561a..1f67c0fd93 100644 --- a/extras/Projucer/Source/Utility/Helpers/jucer_NewFileWizard.cpp +++ b/extras/Projucer/Source/Utility/Helpers/jucer_NewFileWizard.cpp @@ -64,14 +64,14 @@ public: void createNewFile (Project&, Project::Item parent) override { - askUserToChooseNewFile ("SourceCode.cpp", "*.cpp", parent, [parent] (File newFile) + askUserToChooseNewFile ("SourceCode.cpp", "*.cpp", parent, [this, parent] (File newFile) { if (newFile != File()) - create (parent, newFile, "jucer_NewCppFileTemplate_cpp"); + create (*this, parent, newFile, "jucer_NewCppFileTemplate_cpp"); }); } - static bool create (Project::Item parent, const File& newFile, const char* templateName) + static bool create (NewFileWizard::Type& wizard, Project::Item parent, const File& newFile, const char* templateName) { if (fillInNewCppFileTemplate (newFile, parent, templateName)) { @@ -79,7 +79,7 @@ public: return true; } - showFailedToWriteMessage (newFile); + wizard.showFailedToWriteMessage (newFile); return false; } }; @@ -92,14 +92,14 @@ public: void createNewFile (Project&, Project::Item parent) override { - askUserToChooseNewFile ("SourceCode.h", "*.h", parent, [parent] (File newFile) + askUserToChooseNewFile ("SourceCode.h", "*.h", parent, [this, parent] (File newFile) { if (newFile != File()) - create (parent, newFile, "jucer_NewCppFileTemplate_h"); + create (*this, parent, newFile, "jucer_NewCppFileTemplate_h"); }); } - static bool create (Project::Item parent, const File& newFile, const char* templateName) + static bool create (NewFileWizard::Type& wizard, Project::Item parent, const File& newFile, const char* templateName) { if (fillInNewCppFileTemplate (newFile, parent, templateName)) { @@ -107,7 +107,7 @@ public: return true; } - showFailedToWriteMessage (newFile); + wizard.showFailedToWriteMessage (newFile); return false; } }; @@ -120,10 +120,10 @@ public: void createNewFile (Project&, Project::Item parent) override { - askUserToChooseNewFile ("SourceCode.h", "*.h;*.cpp", parent, [parent] (File newFile) + askUserToChooseNewFile ("SourceCode.h", "*.h;*.cpp", parent, [this, parent] (File newFile) { - if (NewCppFileWizard::create (parent, newFile.withFileExtension ("h"), "jucer_NewCppFileTemplate_h")) - NewCppFileWizard::create (parent, newFile.withFileExtension ("cpp"), "jucer_NewCppFileTemplate_cpp"); + if (NewCppFileWizard::create (*this, parent, newFile.withFileExtension ("h"), "jucer_NewCppFileTemplate_h")) + NewCppFileWizard::create (*this, parent, newFile.withFileExtension ("cpp"), "jucer_NewCppFileTemplate_cpp"); }); } }; @@ -139,7 +139,7 @@ public: createNewFileInternal (parent); } - static bool create (const String& className, Project::Item parent, + static bool create (NewFileWizard::Type& wizard, const String& className, Project::Item parent, const File& newFile, const char* templateName) { auto content = fillInBasicTemplateFields (newFile, parent, templateName) @@ -154,15 +154,15 @@ public: return true; } - showFailedToWriteMessage (newFile); + wizard.showFailedToWriteMessage (newFile); return false; } private: virtual void createFiles (Project::Item parent, const String& className, const File& newFile) { - if (create (className, parent, newFile.withFileExtension ("h"), "jucer_NewComponentTemplate_h")) - create (className, parent, newFile.withFileExtension ("cpp"), "jucer_NewComponentTemplate_cpp"); + if (create (*this, className, parent, newFile.withFileExtension ("h"), "jucer_NewComponentTemplate_h")) + create (*this, className, parent, newFile.withFileExtension ("cpp"), "jucer_NewComponentTemplate_cpp"); } static String getClassNameFieldName() { return "Class Name"; } @@ -227,17 +227,17 @@ public: void createFiles (Project::Item parent, const String& className, const File& newFile) override { - create (className, parent, newFile.withFileExtension ("h"), "jucer_NewInlineComponentTemplate_h"); + create (*this, className, parent, newFile.withFileExtension ("h"), "jucer_NewInlineComponentTemplate_h"); } }; - //============================================================================== void NewFileWizard::Type::showFailedToWriteMessage (const File& file) { - AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, - "Failed to Create File!", - "Couldn't write to the file: " + file.getFullPathName()); + auto options = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::WarningIcon, + "Failed to Create File!", + "Couldn't write to the file: " + file.getFullPathName()); + messageBox = AlertWindow::showScopedAsync (options, nullptr); } void NewFileWizard::Type::askUserToChooseNewFile (const String& suggestedFilename, const String& wildcard, diff --git a/extras/Projucer/Source/Utility/Helpers/jucer_NewFileWizard.h b/extras/Projucer/Source/Utility/Helpers/jucer_NewFileWizard.h index 8b84ba2b41..7df471132b 100644 --- a/extras/Projucer/Source/Utility/Helpers/jucer_NewFileWizard.h +++ b/extras/Projucer/Source/Utility/Helpers/jucer_NewFileWizard.h @@ -39,23 +39,23 @@ public: class Type { public: - Type() {} - virtual ~Type() {} + virtual ~Type() = default; //============================================================================== virtual String getName() = 0; virtual void createNewFile (Project&, Project::Item projectGroupToAddTo) = 0; + void showFailedToWriteMessage (const File& file); + protected: //============================================================================== void askUserToChooseNewFile (const String& suggestedFilename, const String& wildcard, const Project::Item& projectGroupToAddTo, std::function callback); - static void showFailedToWriteMessage (const File& file); - private: std::unique_ptr chooser; + ScopedMessageBox messageBox; }; //============================================================================== diff --git a/modules/juce_audio_plugin_client/Standalone/juce_StandaloneFilterWindow.h b/modules/juce_audio_plugin_client/Standalone/juce_StandaloneFilterWindow.h index 6a96c9beba..13d02e24a3 100644 --- a/modules/juce_audio_plugin_client/Standalone/juce_StandaloneFilterWindow.h +++ b/modules/juce_audio_plugin_client/Standalone/juce_StandaloneFilterWindow.h @@ -209,9 +209,12 @@ public: processor->getStateInformation (data); if (! fc.getResult().replaceWithData (data.getData(), data.getSize())) - AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, - TRANS("Error whilst saving"), - TRANS("Couldn't write to the specified file!")); + { + auto opts = MessageBoxOptions::makeOptionsOk (AlertWindow::WarningIcon, + TRANS ("Error whilst saving"), + TRANS ("Couldn't write to the specified file!")); + messageBox = AlertWindow::showScopedAsync (opts, nullptr); + } }); } @@ -234,11 +237,16 @@ public: MemoryBlock data; if (fc.getResult().loadFileAsData (data)) + { processor->setStateInformation (data.getData(), (int) data.getSize()); + } else - AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, - TRANS("Error whilst loading"), - TRANS("Couldn't read from the specified file!")); + { + auto opts = MessageBoxOptions::makeOptionsOk (AlertWindow::WarningIcon, + TRANS ("Error whilst loading"), + TRANS ("Couldn't read from the specified file!")); + messageBox = AlertWindow::showScopedAsync (opts, nullptr); + } }); } @@ -417,6 +425,7 @@ public: Array lastMidiDevices; std::unique_ptr stateFileChooser; + ScopedMessageBox messageBox; private: /* This class can be used to ensure that audio callbacks use buffers with a diff --git a/modules/juce_audio_processors/scanning/juce_PluginListComponent.cpp b/modules/juce_audio_processors/scanning/juce_PluginListComponent.cpp index 008c74753b..455393fb85 100644 --- a/modules/juce_audio_processors/scanning/juce_PluginListComponent.cpp +++ b/modules/juce_audio_processors/scanning/juce_PluginListComponent.cpp @@ -64,7 +64,7 @@ public: if (columnId == nameCol) text = list.getBlacklistedFiles() [row - list.getNumTypes()]; else if (columnId == descCol) - text = TRANS("Deactivated after failing to initialise correctly"); + text = TRANS ("Deactivated after failing to initialise correctly"); } else { @@ -155,11 +155,11 @@ PluginListComponent::PluginListComponent (AudioPluginFormatManager& manager, Kno TableHeaderComponent& header = table.getHeader(); - header.addColumn (TRANS("Name"), TableModel::nameCol, 200, 100, 700, TableHeaderComponent::defaultFlags | TableHeaderComponent::sortedForwards); - header.addColumn (TRANS("Format"), TableModel::typeCol, 80, 80, 80, TableHeaderComponent::notResizable); - header.addColumn (TRANS("Category"), TableModel::categoryCol, 100, 100, 200); - header.addColumn (TRANS("Manufacturer"), TableModel::manufacturerCol, 200, 100, 300); - header.addColumn (TRANS("Description"), TableModel::descCol, 300, 100, 500, TableHeaderComponent::notSortable); + header.addColumn (TRANS ("Name"), TableModel::nameCol, 200, 100, 700, TableHeaderComponent::defaultFlags | TableHeaderComponent::sortedForwards); + header.addColumn (TRANS ("Format"), TableModel::typeCol, 80, 80, 80, TableHeaderComponent::notResizable); + header.addColumn (TRANS ("Category"), TableModel::categoryCol, 100, 100, 200); + header.addColumn (TRANS ("Manufacturer"), TableModel::manufacturerCol, 200, 100, 300); + header.addColumn (TRANS ("Description"), TableModel::descCol, 300, 100, 500, TableHeaderComponent::notSortable); table.setHeaderHeight (22); table.setRowHeight (20); @@ -289,7 +289,7 @@ void PluginListComponent::removePluginItem (int index) PopupMenu PluginListComponent::createOptionsMenu() { PopupMenu menu; - menu.addItem (PopupMenu::Item (TRANS("Clear list")) + menu.addItem (PopupMenu::Item (TRANS ("Clear list")) .setAction ([this] { list.clear(); })); menu.addSeparator(); @@ -306,18 +306,18 @@ PopupMenu PluginListComponent::createOptionsMenu() menu.addSeparator(); - menu.addItem (PopupMenu::Item (TRANS("Remove selected plug-in from list")) + menu.addItem (PopupMenu::Item (TRANS ("Remove selected plug-in from list")) .setEnabled (table.getNumSelectedRows() > 0) .setAction ([this] { removeSelectedPlugins(); })); - menu.addItem (PopupMenu::Item (TRANS("Remove any plug-ins whose files no longer exist")) + menu.addItem (PopupMenu::Item (TRANS ("Remove any plug-ins whose files no longer exist")) .setAction ([this] { removeMissingPlugins(); })); menu.addSeparator(); auto selectedRow = table.getSelectedRow(); - menu.addItem (PopupMenu::Item (TRANS("Show folder containing selected plug-in")) + menu.addItem (PopupMenu::Item (TRANS ("Show folder containing selected plug-in")) .setEnabled (canShowFolderForPlugin (list, selectedRow)) .setAction ([this, selectedRow] { showFolderForPlugin (list, selectedRow); })); @@ -337,10 +337,10 @@ PopupMenu PluginListComponent::createMenuForRow (int rowNumber) if (rowNumber >= 0 && rowNumber < tableModel->getNumRows()) { - menu.addItem (PopupMenu::Item (TRANS("Remove plug-in from list")) + menu.addItem (PopupMenu::Item (TRANS ("Remove plug-in from list")) .setAction ([this, rowNumber] { removePluginItem (rowNumber); })); - menu.addItem (PopupMenu::Item (TRANS("Show folder containing plug-in")) + menu.addItem (PopupMenu::Item (TRANS ("Show folder containing plug-in")) .setEnabled (canShowFolderForPlugin (list, rowNumber)) .setAction ([this, rowNumber] { showFolderForPlugin (list, rowNumber); })); } @@ -391,7 +391,7 @@ public: formatToScan (format), filesOrIdentifiersToScan (filesOrIdentifiers), propertiesToUse (properties), - pathChooserWindow (TRANS("Select folders to scan..."), String(), MessageBoxIconType::NoIcon), + pathChooserWindow (TRANS ("Select folders to scan..."), String(), MessageBoxIconType::NoIcon), progressWindow (title, text, MessageBoxIconType::NoIcon), numThreads (threads), allowAsync (allowPluginsWhichRequireAsynchronousInstantiation) @@ -417,8 +417,8 @@ public: pathList.setPath (path); pathChooserWindow.addCustomComponent (&pathList); - pathChooserWindow.addButton (TRANS("Scan"), 1, KeyPress (KeyPress::returnKey)); - pathChooserWindow.addButton (TRANS("Cancel"), 0, KeyPress (KeyPress::escapeKey)); + pathChooserWindow.addButton (TRANS ("Scan"), 1, KeyPress (KeyPress::returnKey)); + pathChooserWindow.addButton (TRANS ("Cancel"), 0, KeyPress (KeyPress::escapeKey)); pathChooserWindow.enterModalState (true, ModalCallbackFunction::forComponent (startScanCallback, @@ -455,6 +455,7 @@ private: std::atomic finished { false }; std::unique_ptr pool; std::set initiallyBlacklistedFiles; + ScopedMessageBox messageBox; static void startScanCallback (int result, AlertWindow* alert, Scanner* scanner) { @@ -476,18 +477,23 @@ private: if (File::isAbsolutePath (f) && isStupidPath (File (f))) { - AlertWindow::showOkCancelBox (MessageBoxIconType::WarningIcon, - TRANS("Plugin Scanning"), - TRANS("If you choose to scan folders that contain non-plugin files, " - "then scanning may take a long time, and can cause crashes when " - "attempting to load unsuitable files.") - + newLine - + TRANS ("Are you sure you want to scan the folder \"XYZ\"?") - .replace ("XYZ", f), - TRANS ("Scan"), - String(), - nullptr, - ModalCallbackFunction::create (warnAboutStupidPathsCallback, this)); + auto options = MessageBoxOptions::makeOptionsOkCancel (MessageBoxIconType::WarningIcon, + TRANS ("Plugin Scanning"), + TRANS ("If you choose to scan folders that contain non-plugin files, " + "then scanning may take a long time, and can cause crashes when " + "attempting to load unsuitable files.") + + newLine + + TRANS ("Are you sure you want to scan the folder \"XYZ\"?") + .replace ("XYZ", f), + TRANS ("Scan")); + messageBox = AlertWindow::showScopedAsync (options, [this] (int result) + { + if (result != 0) + startScan(); + else + finishedScan(); + }); + return; } } @@ -524,14 +530,6 @@ private: return false; } - static void warnAboutStupidPathsCallback (int result, Scanner* scanner) - { - if (result != 0) - scanner->startScan(); - else - scanner->finishedScan(); - } - void startScan() { pathChooserWindow.setVisible (false); @@ -549,7 +547,7 @@ private: propertiesToUse->saveIfNeeded(); } - progressWindow.addButton (TRANS("Cancel"), 0, KeyPress (KeyPress::escapeKey)); + progressWindow.addButton (TRANS ("Cancel"), 0, KeyPress (KeyPress::escapeKey)); progressWindow.addProgressBarComponent (progress); progressWindow.enterModalState(); @@ -599,7 +597,7 @@ private: if (finished) finishedScan(); else - progressWindow.setMessage (TRANS("Testing") + ":\n\n" + pluginBeingScanned); + progressWindow.setMessage (TRANS ("Testing") + ":\n\n" + pluginBeingScanned); } bool doNextScan() @@ -639,8 +637,8 @@ void PluginListComponent::scanFor (AudioPluginFormat& format) void PluginListComponent::scanFor (AudioPluginFormat& format, const StringArray& filesOrIdentifiersToScan) { currentScanner.reset (new Scanner (*this, format, filesOrIdentifiersToScan, propertiesToUse, allowAsync, numThreads, - dialogTitle.isNotEmpty() ? dialogTitle : TRANS("Scanning for plug-ins..."), - dialogText.isNotEmpty() ? dialogText : TRANS("Searching for all possible plug-in files..."))); + dialogTitle.isNotEmpty() ? dialogTitle : TRANS ("Scanning for plug-ins..."), + dialogText.isNotEmpty() ? dialogText : TRANS ("Searching for all possible plug-in files..."))); } bool PluginListComponent::isScanning() const noexcept @@ -672,9 +670,12 @@ void PluginListComponent::scanFinished (const StringArray& failedFiles, currentScanner.reset(); // mustn't delete this before using the failed files array if (! warnings.isEmpty()) - AlertWindow::showMessageBoxAsync (MessageBoxIconType::InfoIcon, - TRANS("Scan complete"), - warnings.joinIntoString ("\n\n")); + { + auto options = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::InfoIcon, + TRANS ("Scan complete"), + warnings.joinIntoString ("\n\n")); + messageBox = AlertWindow::showScopedAsync (options, nullptr); + } } } // namespace juce diff --git a/modules/juce_audio_processors/scanning/juce_PluginListComponent.h b/modules/juce_audio_processors/scanning/juce_PluginListComponent.h index 42f7433c95..96c3750258 100644 --- a/modules/juce_audio_processors/scanning/juce_PluginListComponent.h +++ b/modules/juce_audio_processors/scanning/juce_PluginListComponent.h @@ -130,6 +130,8 @@ private: class Scanner; std::unique_ptr currentScanner; + ScopedMessageBox messageBox; + void scanFinished (const StringArray&, const std::vector&); void updateList(); void removeMissingPlugins(); diff --git a/modules/juce_audio_utils/gui/juce_AudioDeviceSelectorComponent.cpp b/modules/juce_audio_utils/gui/juce_AudioDeviceSelectorComponent.cpp index 809162f583..8dfb6d6588 100644 --- a/modules/juce_audio_utils/gui/juce_AudioDeviceSelectorComponent.cpp +++ b/modules/juce_audio_utils/gui/juce_AudioDeviceSelectorComponent.cpp @@ -401,9 +401,11 @@ public: } if (error.isNotEmpty()) - AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, - TRANS("Error when trying to open audio device!"), - error); + messageBox = AlertWindow::showScopedAsync (MessageBoxOptions().withIconType (MessageBoxIconType::WarningIcon) + .withTitle (TRANS ("Error when trying to open audio device!")) + .withMessage (error) + .withButton (TRANS ("OK")), + nullptr); } bool showDeviceControlPanel() @@ -966,6 +968,7 @@ public: private: std::unique_ptr inputChanList, outputChanList; + ScopedMessageBox messageBox; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioDeviceSettingsPanel) }; diff --git a/modules/juce_gui_basics/filebrowser/juce_FileChooserDialogBox.cpp b/modules/juce_gui_basics/filebrowser/juce_FileChooserDialogBox.cpp index 814c73ad3a..f531da182d 100644 --- a/modules/juce_gui_basics/filebrowser/juce_FileChooserDialogBox.cpp +++ b/modules/juce_gui_basics/filebrowser/juce_FileChooserDialogBox.cpp @@ -182,28 +182,26 @@ void FileChooserDialogBox::fileDoubleClicked (const File&) void FileChooserDialogBox::fileClicked (const File&, const MouseEvent&) {} void FileChooserDialogBox::browserRootChanged (const File&) {} -void FileChooserDialogBox::okToOverwriteFileCallback (int result, FileChooserDialogBox* box) -{ - if (result != 0 && box != nullptr) - box->exitModalState (1); -} - void FileChooserDialogBox::okButtonPressed() { if (warnAboutOverwritingExistingFiles && content->chooserComponent.isSaveMode() && content->chooserComponent.getSelectedFile(0).exists()) { - AlertWindow::showOkCancelBox (MessageBoxIconType::WarningIcon, - TRANS("File already exists"), - TRANS("There's already a file called: FLNM") - .replace ("FLNM", content->chooserComponent.getSelectedFile(0).getFullPathName()) - + "\n\n" - + TRANS("Are you sure you want to overwrite it?"), - TRANS("Overwrite"), - TRANS("Cancel"), - this, - ModalCallbackFunction::forComponent (okToOverwriteFileCallback, this)); + auto options = MessageBoxOptions::makeOptionsOkCancel (MessageBoxIconType::WarningIcon, + TRANS ("File already exists"), + TRANS ("There's already a file called: FLNM") + .replace ("FLNM", content->chooserComponent.getSelectedFile(0).getFullPathName()) + + "\n\n" + + TRANS ("Are you sure you want to overwrite it?"), + TRANS ("Overwrite"), + TRANS ("Cancel"), + this); + messageBox = AlertWindow::showScopedAsync (options, [this] (int result) + { + if (result != 0) + exitModalState (1); + }); } else { @@ -251,9 +249,12 @@ void FileChooserDialogBox::createNewFolderConfirmed (const String& nameFromDialo auto parent = content->chooserComponent.getRoot(); if (! parent.getChildFile (name).createDirectory()) - AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, - TRANS ("New Folder"), - TRANS ("Couldn't create the folder!")); + { + auto options = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::WarningIcon, + TRANS ("New Folder"), + TRANS ("Couldn't create the folder!")); + messageBox = AlertWindow::showScopedAsync (options, nullptr); + } content->chooserComponent.refresh(); } diff --git a/modules/juce_gui_basics/filebrowser/juce_FileChooserDialogBox.h b/modules/juce_gui_basics/filebrowser/juce_FileChooserDialogBox.h index bf1e147de5..972d0e5c6d 100644 --- a/modules/juce_gui_basics/filebrowser/juce_FileChooserDialogBox.h +++ b/modules/juce_gui_basics/filebrowser/juce_FileChooserDialogBox.h @@ -158,9 +158,10 @@ private: void createNewFolder(); void createNewFolderConfirmed (const String& name); - static void okToOverwriteFileCallback (int result, FileChooserDialogBox*); static void createNewFolderCallback (int result, FileChooserDialogBox*, Component::SafePointer); + ScopedMessageBox messageBox; + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileChooserDialogBox) }; diff --git a/modules/juce_gui_basics/windows/juce_NativeMessageBox.cpp b/modules/juce_gui_basics/windows/juce_NativeMessageBox.cpp index 6292d3a49b..f06e76aca6 100644 --- a/modules/juce_gui_basics/windows/juce_NativeMessageBox.cpp +++ b/modules/juce_gui_basics/windows/juce_NativeMessageBox.cpp @@ -26,10 +26,59 @@ namespace juce { -template -static int showNativeBoxUnmanaged (const MessageBoxOptions& opts, ModalComponentManager::Callback* cb) +enum class ResultCodeMappingMode { - return ScopedMessageBox::Pimpl::showUnmanaged (ScopedMessageBoxInterface::create (opts), cb); + plainIndex, // The result code is equal to the index of the selected button. + // This is used for NativeMessageBox::show, showAsync, and showMessageBox. + alertWindow, // The result code is mapped in the same way as AlertWindow, i.e. if there + // are N buttons then button X will return ((X + 1) % N). +}; + +static std::unique_ptr makeNativeMessageBoxWithMappedResult (const MessageBoxOptions& opts, + ResultCodeMappingMode mode) +{ + class Adapter : public ScopedMessageBoxInterface + { + public: + explicit Adapter (const MessageBoxOptions& options) + : inner (ScopedMessageBoxInterface::create (options)), + numButtons (options.getNumButtons()) {} + + void runAsync (std::function fn) override + { + inner->runAsync ([fn, n = numButtons] (int result) + { + fn (map (result, n)); + }); + } + + int runSync() override + { + return map (inner->runSync(), numButtons); + } + + void close() override + { + inner->close(); + } + + private: + static int map (int button, int numButtons) { return (button + 1) % numButtons; } + + std::unique_ptr inner; + int numButtons = 0; + }; + + return mode == ResultCodeMappingMode::plainIndex ? ScopedMessageBoxInterface::create (opts) + : std::make_unique (opts); +} + +static int showNativeBoxUnmanaged (const MessageBoxOptions& opts, + ModalComponentManager::Callback* cb, + ResultCodeMappingMode mode) +{ + auto implementation = makeNativeMessageBoxWithMappedResult (opts, mode); + return ScopedMessageBox::Pimpl::showUnmanaged (std::move (implementation), cb); } #if JUCE_MODAL_LOOPS_PERMITTED @@ -37,17 +86,18 @@ void JUCE_CALLTYPE NativeMessageBox::showMessageBox (MessageBoxIconType iconType const String& title, const String& message, Component* associatedComponent) { - showNativeBoxUnmanaged (MessageBoxOptions().withIconType (iconType) - .withTitle (title) - .withMessage (message) - .withButton (TRANS("OK")) - .withAssociatedComponent (associatedComponent), - nullptr); + showNativeBoxUnmanaged (MessageBoxOptions().withIconType (iconType) + .withTitle (title) + .withMessage (message) + .withButton (TRANS("OK")) + .withAssociatedComponent (associatedComponent), + nullptr, + ResultCodeMappingMode::plainIndex); } int JUCE_CALLTYPE NativeMessageBox::show (const MessageBoxOptions& options) { - return showNativeBoxUnmanaged<> (options, nullptr); + return showNativeBoxUnmanaged (options, nullptr, ResultCodeMappingMode::plainIndex); } #endif @@ -56,12 +106,8 @@ void JUCE_CALLTYPE NativeMessageBox::showMessageBoxAsync (MessageBoxIconType ico Component* associatedComponent, ModalComponentManager::Callback* callback) { - showNativeBoxUnmanaged (MessageBoxOptions().withIconType (iconType) - .withTitle (title) - .withMessage (message) - .withButton (TRANS("OK")) - .withAssociatedComponent (associatedComponent), - callback); + auto options = MessageBoxOptions::makeOptionsOk (iconType, title, message, {}, associatedComponent); + showNativeBoxUnmanaged (options, callback, ResultCodeMappingMode::alertWindow); } bool JUCE_CALLTYPE NativeMessageBox::showOkCancelBox (MessageBoxIconType iconType, @@ -69,13 +115,8 @@ bool JUCE_CALLTYPE NativeMessageBox::showOkCancelBox (MessageBoxIconType iconTyp Component* associatedComponent, ModalComponentManager::Callback* callback) { - return showNativeBoxUnmanaged (MessageBoxOptions().withIconType (iconType) - .withTitle (title) - .withMessage (message) - .withButton (TRANS("OK")) - .withButton (TRANS("Cancel")) - .withAssociatedComponent (associatedComponent), - callback) != 0; + auto options = MessageBoxOptions::makeOptionsOkCancel (iconType, title, message, {}, {}, associatedComponent); + return showNativeBoxUnmanaged (options, callback, ResultCodeMappingMode::alertWindow) != 0; } int JUCE_CALLTYPE NativeMessageBox::showYesNoCancelBox (MessageBoxIconType iconType, @@ -83,14 +124,8 @@ int JUCE_CALLTYPE NativeMessageBox::showYesNoCancelBox (MessageBoxIconType iconT Component* associatedComponent, ModalComponentManager::Callback* callback) { - return showNativeBoxUnmanaged (MessageBoxOptions().withIconType (iconType) - .withTitle (title) - .withMessage (message) - .withButton (TRANS("Yes")) - .withButton (TRANS("No")) - .withButton (TRANS("Cancel")) - .withAssociatedComponent (associatedComponent), - callback); + auto options = MessageBoxOptions::makeOptionsYesNoCancel (iconType, title, message, {}, {}, {}, associatedComponent); + return showNativeBoxUnmanaged (options, callback, ResultCodeMappingMode::alertWindow); } int JUCE_CALLTYPE NativeMessageBox::showYesNoBox (MessageBoxIconType iconType, @@ -98,19 +133,14 @@ int JUCE_CALLTYPE NativeMessageBox::showYesNoBox (MessageBoxIconType iconType, Component* associatedComponent, ModalComponentManager::Callback* callback) { - return showNativeBoxUnmanaged (MessageBoxOptions().withIconType (iconType) - .withTitle (title) - .withMessage (message) - .withButton (TRANS("Yes")) - .withButton (TRANS("No")) - .withAssociatedComponent (associatedComponent), - callback); + auto options = MessageBoxOptions::makeOptionsYesNo (iconType, title, message, {}, {}, associatedComponent); + return showNativeBoxUnmanaged (options, callback, ResultCodeMappingMode::alertWindow); } void JUCE_CALLTYPE NativeMessageBox::showAsync (const MessageBoxOptions& options, ModalComponentManager::Callback* callback) { - showNativeBoxUnmanaged<> (options, callback); + showNativeBoxUnmanaged (options, callback, ResultCodeMappingMode::plainIndex); } void JUCE_CALLTYPE NativeMessageBox::showAsync (const MessageBoxOptions& options, @@ -121,7 +151,8 @@ void JUCE_CALLTYPE NativeMessageBox::showAsync (const MessageBoxOptions& options ScopedMessageBox NativeMessageBox::showScopedAsync (const MessageBoxOptions& options, std::function callback) { - return ScopedMessageBox::Pimpl::show (ScopedMessageBoxInterface::create (options), std::move (callback)); + auto implementation = makeNativeMessageBoxWithMappedResult (options, ResultCodeMappingMode::alertWindow); + return ScopedMessageBox::Pimpl::show (std::move (implementation), std::move (callback)); } } // namespace juce diff --git a/modules/juce_gui_basics/windows/juce_NativeMessageBox.h b/modules/juce_gui_basics/windows/juce_NativeMessageBox.h index f1236dafa5..2980c6441c 100644 --- a/modules/juce_gui_basics/windows/juce_NativeMessageBox.h +++ b/modules/juce_gui_basics/windows/juce_NativeMessageBox.h @@ -262,6 +262,23 @@ public: 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. + This function is always asynchronous, even if the callback is null. + + For consistency with AlertWindow, the result codes returned by the alert window are as + follows. + - One button: + - button[0] returns 0 + - Two buttons: + - button[0] returns 1 + - button[1] returns 0 + - Three buttons: + - button[0] returns 1 + - button[1] returns 2 + - button[2] returns 0 + + Another way of expressing this is that, when there are N buttons, then the result code for + button X is equal to ((X + 1) % N). + @param options the options to use when creating the dialog. @param callback if this is non-null, the callback will receive a call to its modalStateFinished() when the box is dismissed with the index of the diff --git a/modules/juce_gui_extra/documents/juce_FileBasedDocument.cpp b/modules/juce_gui_extra/documents/juce_FileBasedDocument.cpp index d41832d7af..44dfc6e1a5 100644 --- a/modules/juce_gui_extra/documents/juce_FileBasedDocument.cpp +++ b/modules/juce_gui_extra/documents/juce_FileBasedDocument.cpp @@ -214,7 +214,7 @@ public: [] (SafeParentPointer ptr, auto cb) { if (ptr != nullptr) - ptr->askToSaveChanges (ptr, std::move (cb)); + ptr->askToSaveChangesAsync (ptr, std::move (cb)); }, [parent] (bool askUserForFileIfNotSpecified, bool showMessageOnFailure, @@ -337,12 +337,15 @@ private: MouseCursor::hideWaitCursor(); if (showMessageOnFailure) - AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, - TRANS ("Failed to open file..."), - TRANS ("There was an error while trying to load the file: FLNM") - .replace ("FLNM", "\n" + newFile.getFullPathName()) - + "\n\n" - + result.getErrorMessage()); + { + auto options = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::WarningIcon, + TRANS ("Failed to open file..."), + TRANS ("There was an error while trying to load the file: FLNM") + .replace ("FLNM", "\n" + newFile.getFullPathName()) + + "\n\n" + + result.getErrorMessage()); + parent->messageBox = AlertWindow::showScopedAsync (options, nullptr); + } if (completed != nullptr) completed (result); @@ -435,28 +438,35 @@ private: } //============================================================================== - int askToSaveChanges (SafeParentPointer parent, - std::function callback) + MessageBoxOptions getAskToSaveChangesOptions() const { - auto* modalCallback = callback == nullptr - ? nullptr - : ModalCallbackFunction::create ([parent, callback = std::move (callback)] (int alertResult) - { - if (parent != nullptr) - callback (parent, alertResult); - }); - - return AlertWindow::showYesNoCancelBox (MessageBoxIconType::QuestionIcon, - TRANS ("Closing document..."), - TRANS ("Do you want to save the changes to \"DCNM\"?") - .replace ("DCNM", document.getDocumentTitle()), - TRANS ("Save"), - TRANS ("Discard changes"), - TRANS ("Cancel"), - nullptr, - modalCallback); + return MessageBoxOptions::makeOptionsYesNoCancel (MessageBoxIconType::QuestionIcon, + TRANS ("Closing document..."), + TRANS ("Do you want to save the changes to \"DCNM\"?") + .replace ("DCNM", document.getDocumentTitle()), + TRANS ("Save"), + TRANS ("Discard changes"), + TRANS ("Cancel")); } + void askToSaveChangesAsync (SafeParentPointer parent, + std::function callback) + { + messageBox = AlertWindow::showScopedAsync (getAskToSaveChangesOptions(), + [parent, callback = std::move (callback)] (int alertResult) + { + if (parent != nullptr) + callback (parent, alertResult); + }); + } + + #if JUCE_MODAL_LOOPS_PERMITTED + int askToSaveChangesSync() + { + return AlertWindow::show (getAskToSaveChangesOptions()); + } + #endif + //============================================================================== template void saveInternal (SafeParentPointer parent, @@ -508,13 +518,16 @@ private: MouseCursor::hideWaitCursor(); if (showMessageOnFailure) - AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, - TRANS ("Error writing to file..."), - TRANS ("An error occurred while trying to save \"DCNM\" to the file: FLNM") - .replace ("DCNM", parent->document.getDocumentTitle()) - .replace ("FLNM", "\n" + newFile.getFullPathName()) - + "\n\n" - + result.getErrorMessage()); + { + auto options = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::WarningIcon, + TRANS ("Error writing to file..."), + TRANS ("An error occurred while trying to save \"DCNM\" to the file: FLNM") + .replace ("DCNM", parent->document.getDocumentTitle()) + .replace ("FLNM", "\n" + newFile.getFullPathName()) + + "\n\n" + + result.getErrorMessage()); + parent->messageBox = AlertWindow::showScopedAsync (options, nullptr); + } parent->document.sendChangeMessage(); // because the filename may have changed @@ -615,7 +628,7 @@ private: [] (SafeParentPointer ptr, const File& destination, std::function cb) { if (ptr != nullptr) - ptr->askToOverwriteFile (ptr, destination, std::move (cb)); + ptr->askToOverwriteFileAsync (ptr, destination, std::move (cb)); }, [parent] (const File& destination, std::function cb) { @@ -660,38 +673,45 @@ private: [] (SafeParentPointer ptr, const File& destination, auto cb) { if (ptr != nullptr) - ptr->askToOverwriteFile (ptr, destination, std::move (cb)); + ptr->askToOverwriteFileAsync (ptr, destination, std::move (cb)); }); } //============================================================================== - bool askToOverwriteFile (SafeParentPointer parent, - const File& newFile, - std::function callback) + MessageBoxOptions getAskToOverwriteFileOptions (const File& newFile) const + { + return MessageBoxOptions::makeOptionsOkCancel (MessageBoxIconType::WarningIcon, + TRANS ("File already exists"), + TRANS ("There's already a file called: FLNM") + .replace ("FLNM", newFile.getFullPathName()) + + "\n\n" + + TRANS ("Are you sure you want to overwrite it?"), + TRANS ("Overwrite"), + TRANS ("Cancel")); + } + + void askToOverwriteFileAsync (SafeParentPointer parent, + const File& newFile, + std::function callback) { if (parent == nullptr) - return false; + return; - auto* modalCallback = callback == nullptr - ? nullptr - : ModalCallbackFunction::create ([parent, callback = std::move (callback)] (int r) - { - if (parent != nullptr) - callback (parent, r == 1); - }); - - return AlertWindow::showOkCancelBox (MessageBoxIconType::WarningIcon, - TRANS ("File already exists"), - TRANS ("There's already a file called: FLNM") - .replace ("FLNM", newFile.getFullPathName()) - + "\n\n" - + TRANS ("Are you sure you want to overwrite it?"), - TRANS ("Overwrite"), - TRANS ("Cancel"), - nullptr, - modalCallback); + messageBox = AlertWindow::showScopedAsync (getAskToOverwriteFileOptions (newFile), + [parent, callback = std::move (callback)] (int r) + { + if (parent != nullptr) + callback (parent, r != 1); + }); } + #if JUCE_MODAL_LOOPS_PERMITTED + bool askToOverwriteFileSync (const File& newFile) + { + return AlertWindow::show (getAskToOverwriteFileOptions (newFile)); + } + #endif + //============================================================================== void getSaveAsFilenameAsync (SafeParentPointer parent, bool warnAboutOverwritingExistingFiles, @@ -886,7 +906,7 @@ private: template void askToSaveChangesSync (SafeParentPointer parent, Callback&& callback) { - callback (parent, askToSaveChanges (parent, nullptr)); + callback (parent, askToSaveChangesSync()); } //============================================================================== @@ -908,7 +928,7 @@ private: const File& newFile, Callback&& callback) { - callback (parent, askToOverwriteFile (parent, newFile, nullptr)); + callback (parent, askToOverwriteFileSync (newFile)); } //============================================================================== @@ -945,6 +965,7 @@ private: bool changedSinceSave = false; String fileExtension, fileWildcard, openFileDialogTitle, saveFileDialogTitle; std::unique_ptr asyncFc; + ScopedMessageBox messageBox; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl) JUCE_DECLARE_WEAK_REFERENCEABLE (Pimpl) diff --git a/modules/juce_gui_extra/misc/juce_KeyMappingEditorComponent.cpp b/modules/juce_gui_extra/misc/juce_KeyMappingEditorComponent.cpp index f89f0c7f98..72a97fad14 100644 --- a/modules/juce_gui_extra/misc/juce_KeyMappingEditorComponent.cpp +++ b/modules/juce_gui_extra/misc/juce_KeyMappingEditorComponent.cpp @@ -142,12 +142,6 @@ public: JUCE_DECLARE_NON_COPYABLE (KeyEntryWindow) }; - static void assignNewKeyCallback (int result, ChangeKeyButton* button, KeyPress newKey) - { - if (result != 0 && button != nullptr) - button->setNewKey (newKey, true); - } - void setNewKey (const KeyPress& newKey, bool dontAskUser) { if (newKey.isValid()) @@ -165,17 +159,20 @@ public: } else { - AlertWindow::showOkCancelBox (MessageBoxIconType::WarningIcon, - TRANS("Change key-mapping"), - TRANS("This key is already assigned to the command \"CMDN\"") - .replace ("CMDN", owner.getCommandManager().getNameOfCommand (previousCommand)) - + "\n\n" - + TRANS("Do you want to re-assign it to this new command instead?"), - TRANS("Re-assign"), - TRANS("Cancel"), - this, - ModalCallbackFunction::forComponent (assignNewKeyCallback, - this, KeyPress (newKey))); + auto options = MessageBoxOptions::makeOptionsOkCancel (MessageBoxIconType::WarningIcon, + TRANS("Change key-mapping"), + TRANS("This key is already assigned to the command \"CMDN\"") + .replace ("CMDN", owner.getCommandManager().getNameOfCommand (previousCommand)) + + "\n\n" + + TRANS("Do you want to re-assign it to this new command instead?"), + TRANS("Re-assign"), + TRANS("Cancel"), + this); + messageBox = AlertWindow::showScopedAsync (options, [this, newKey] (int result) + { + if (result != 0) + setNewKey (newKey, true); + }); } } } @@ -205,6 +202,7 @@ private: const CommandID commandID; const int keyNum; std::unique_ptr currentKeyEntryWindow; + ScopedMessageBox messageBox; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChangeKeyButton) }; @@ -383,12 +381,6 @@ private: KeyMappingEditorComponent& owner; }; -static void resetKeyMappingsToDefaultsCallback (int result, KeyMappingEditorComponent* owner) -{ - if (result != 0 && owner != nullptr) - owner->getMappings().resetToDefaultMappings(); -} - //============================================================================== KeyMappingEditorComponent::KeyMappingEditorComponent (KeyPressMappingSet& mappingManager, const bool showResetToDefaultButton) @@ -403,12 +395,17 @@ KeyMappingEditorComponent::KeyMappingEditorComponent (KeyPressMappingSet& mappin resetButton.onClick = [this] { - AlertWindow::showOkCancelBox (MessageBoxIconType::QuestionIcon, - TRANS("Reset to defaults"), - TRANS("Are you sure you want to reset all the key-mappings to their default state?"), - TRANS("Reset"), - {}, this, - ModalCallbackFunction::forComponent (resetKeyMappingsToDefaultsCallback, this)); + auto options = MessageBoxOptions::makeOptionsOkCancel (MessageBoxIconType::QuestionIcon, + TRANS("Reset to defaults"), + TRANS("Are you sure you want to reset all the key-mappings to their default state?"), + TRANS("Reset"), + {}, + this); + messageBox = AlertWindow::showScopedAsync (options, [this] (int result) + { + if (result != 0) + getMappings().resetToDefaultMappings(); + }); }; } diff --git a/modules/juce_gui_extra/misc/juce_KeyMappingEditorComponent.h b/modules/juce_gui_extra/misc/juce_KeyMappingEditorComponent.h index 422df3d49f..45d514aeaf 100644 --- a/modules/juce_gui_extra/misc/juce_KeyMappingEditorComponent.h +++ b/modules/juce_gui_extra/misc/juce_KeyMappingEditorComponent.h @@ -125,6 +125,7 @@ private: class CategoryItem; class ItemComponent; std::unique_ptr treeItem; + ScopedMessageBox messageBox; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (KeyMappingEditorComponent) }; diff --git a/modules/juce_product_unlocking/marketplace/juce_OnlineUnlockForm.cpp b/modules/juce_product_unlocking/marketplace/juce_OnlineUnlockForm.cpp index 90dda0546b..a8b174523a 100644 --- a/modules/juce_product_unlocking/marketplace/juce_OnlineUnlockForm.cpp +++ b/modules/juce_product_unlocking/marketplace/juce_OnlineUnlockForm.cpp @@ -100,15 +100,17 @@ struct OnlineUnlockForm::OverlayComp : public Component, if (result.errorMessage.isNotEmpty()) { - AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, - TRANS("Registration Failed"), - result.errorMessage); + auto options = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::WarningIcon, + TRANS ("Registration Failed"), + result.errorMessage); + messageBox = AlertWindow::showScopedAsync (options, nullptr); } else if (result.informativeMessage.isNotEmpty()) { - AlertWindow::showMessageBoxAsync (MessageBoxIconType::InfoIcon, - TRANS("Registration Complete!"), - result.informativeMessage); + auto options = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::InfoIcon, + TRANS ("Registration Complete!"), + result.informativeMessage); + messageBox = AlertWindow::showScopedAsync (options, nullptr); } else if (result.urlToLaunch.isNotEmpty()) { @@ -143,6 +145,7 @@ struct OnlineUnlockForm::OverlayComp : public Component, Spinner spinner; OnlineUnlockStatus::UnlockResult result; String email, password; + ScopedMessageBox messageBox; std::unique_ptr cancelButton; diff --git a/modules/juce_product_unlocking/native/juce_ios_InAppPurchases.cpp b/modules/juce_product_unlocking/native/juce_ios_InAppPurchases.cpp index 6176d50b04..59c8f141c9 100644 --- a/modules/juce_product_unlocking/native/juce_ios_InAppPurchases.cpp +++ b/modules/juce_product_unlocking/native/juce_ios_InAppPurchases.cpp @@ -111,7 +111,13 @@ struct InAppPurchases::Pimpl : public SKDelegateAndPaymentObserver int64 getContentLength() const override { return download.contentLength; } Status getStatus() const override { return SKDownloadStateToDownloadStatus (download.downloadState); } #else - int64 getContentLength() const override { return download.expectedContentLength; } + int64 getContentLength() const override + { + if (@available (macOS 10.15, *)) + return download.expectedContentLength; + + return download.contentLength.longLongValue; + } Status getStatus() const override { return SKDownloadStateToDownloadStatus (download.state); } #endif