| @@ -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> fileChooser; | |||
| ScopedMessageBox messageBox; | |||
| }; | |||
| //============================================================================== | |||
| @@ -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<int> 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; | |||
| @@ -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) | |||
| @@ -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) | |||
| }; | |||
| @@ -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<MessageBoxOwnerComponent> 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<TextButton> 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 + ")"); | |||
| AlertWindow::showAsync (MessageBoxOptions() | |||
| .withIconType (MessageBoxIconType::InfoIcon) | |||
| .withTitle ("Sharing Text Result") | |||
| .withMessage ("Sharing text finished\nwith " + resultString) | |||
| .withButton ("OK"), | |||
| nullptr); | |||
| }); | |||
| ContentSharer::getInstance()->shareText ("I love JUCE!", [ptr = Component::SafePointer (this)] (bool success, const String& error) | |||
| { | |||
| if (ptr == nullptr) | |||
| return; | |||
| 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<URL> 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<Image> images { myImage, myImage2 }; | |||
| ContentSharer::getInstance()->shareImages (images, | |||
| [] (bool success, const String& error) | |||
| { | |||
| String resultString = success ? String ("success") | |||
| : ("failure\n (error: " + error + ")"); | |||
| AlertWindow::showAsync (MessageBoxOptions() | |||
| .withIconType (MessageBoxIconType::InfoIcon) | |||
| .withTitle ("Sharing Images Result") | |||
| .withMessage ("Sharing images finished\nwith " + resultString) | |||
| .withButton ("OK"), | |||
| nullptr); | |||
| }); | |||
| ContentSharer::getInstance()->shareImages (images, [ptr = Component::SafePointer (this)] (bool success, const String& error) | |||
| { | |||
| if (ptr == nullptr) | |||
| return; | |||
| 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); | |||
| }); | |||
| } | |||
| } | |||
| @@ -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) | |||
| }; | |||
| @@ -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) | |||
| }; | |||
| @@ -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<AudioPluginInstance> 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; | |||
| @@ -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; | |||
| }; | |||
| //============================================================================== | |||
| @@ -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<VoiceProduct> voiceProducts; | |||
| ScopedMessageBox messageBox; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VoicePurchases) | |||
| }; | |||
| @@ -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) | |||
| }; | |||
| @@ -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<PushNotifications::Notification>& 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<PushNotifications::Notification>& 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<PushNotifications::Channel> 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) | |||
| @@ -100,9 +100,10 @@ void PluginGraph::addPluginCallback (std::unique_ptr<AudioPluginInstance> 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 | |||
| { | |||
| @@ -110,6 +110,7 @@ private: | |||
| AudioPluginFormatManager& formatManager; | |||
| KnownPluginList& knownPlugins; | |||
| OwnedArray<PluginWindow> activePluginWindows; | |||
| ScopedMessageBox messageBox; | |||
| NodeID lastUID; | |||
| NodeID getNextUID() noexcept; | |||
| @@ -110,19 +110,20 @@ public: | |||
| return; | |||
| SafePointer<TemplateComponent> 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> 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> 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) | |||
| }; | |||
| @@ -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 <typename Callback> | |||
| 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<void (std::unique_ptr<Project>)> 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<void (ScopedMessageBox, std::unique_ptr<Project>)> callback) | |||
| { | |||
| prepareDirectory (targetFolder, [=] | |||
| return prepareDirectory (targetFolder, [=] | |||
| { | |||
| auto project = std::make_unique<Project> (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), {}); | |||
| }); | |||
| } | |||
| @@ -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<void (std::unique_ptr<Project>)> 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<void (ScopedMessageBox, std::unique_ptr<Project>)> callback); | |||
| } | |||
| @@ -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<FileChooser> chooser; | |||
| ScopedMessageBox messageBox; | |||
| }; | |||
| @@ -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<ProjucerApplication> { this }, cleanup] (bool openedSuccessfully) | |||
| openFile (tempFile, [parent = WeakReference { this }, cleanup] (bool openedSuccessfully) | |||
| { | |||
| if (parent == nullptr) | |||
| return; | |||
| @@ -217,6 +217,7 @@ private: | |||
| int selectedColourSchemeIndex = 0, selectedEditorColourSchemeIndex = 0; | |||
| std::unique_ptr<FileChooser> chooser; | |||
| ScopedMessageBox messageBox; | |||
| //============================================================================== | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProjucerApplication) | |||
| @@ -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<void()>&& cb) | |||
| DownloadAndInstallThread (const VersionInfo::Asset& a, const File& t, std::function<void (Result)>&& 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<void()> completionCallback; | |||
| std::function<void (Result)> 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); | |||
| } | |||
| })); | |||
| } | |||
| //============================================================================== | |||
| @@ -57,6 +57,7 @@ private: | |||
| std::unique_ptr<DownloadAndInstallThread> installer; | |||
| std::unique_ptr<Component> dialogWindow; | |||
| std::unique_ptr<FileChooser> chooser; | |||
| ScopedMessageBox messageBox; | |||
| JUCE_DECLARE_WEAK_REFERENCEABLE (LatestVersionCheckerAndUpdater) | |||
| }; | |||
| @@ -462,9 +462,10 @@ void MainWindow::openPIP (const File& pipFile, std::function<void (bool)> 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<void (bool)> 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<void (bool)> 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); | |||
| @@ -104,6 +104,8 @@ private: | |||
| std::unique_ptr<Component> blurOverlayComponent; | |||
| bool loginFormOpen = false; | |||
| ScopedMessageBox messageBox; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainWindow) | |||
| }; | |||
| @@ -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<OpenDocumentManager> { 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<OpenDocumentManager> { 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) | |||
| @@ -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<OpenDocumentManager>, | |||
| @@ -129,6 +127,7 @@ private: | |||
| OwnedArray<DocumentType> types; | |||
| OwnedArray<Document> documents; | |||
| Array<DocumentCloseListener*> listeners; | |||
| ScopedMessageBox messageBox; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenDocumentManager) | |||
| JUCE_DECLARE_WEAK_REFERENCEABLE (OpenDocumentManager) | |||
| @@ -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); | |||
| } | |||
| } | |||
| @@ -56,4 +56,5 @@ private: | |||
| JucerDocument& document; | |||
| std::unique_ptr<TableListBox> listBox; | |||
| TextButton addButton, reloadAllButton, delButton; | |||
| ScopedMessageBox messageBox; | |||
| }; | |||
| @@ -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(); | |||
| } | |||
| @@ -93,6 +93,7 @@ private: | |||
| JucerDocument* document; | |||
| OwnedArray<BinaryResource> resources; | |||
| std::unique_ptr<FileChooser> chooser; | |||
| ScopedMessageBox messageBox; | |||
| //============================================================================== | |||
| JUCE_DECLARE_WEAK_REFERENCEABLE (BinaryResources) | |||
| @@ -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()); | |||
| } | |||
| @@ -143,6 +143,7 @@ private: | |||
| ValueTree state; | |||
| std::unique_ptr<FileChooser> chooser; | |||
| ScopedMessageBox messageBox; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (EnabledModulesList) | |||
| }; | |||
| @@ -77,7 +77,10 @@ public: | |||
| void deleteItem() override | |||
| { | |||
| auto resultCallback = [safeThis = WeakReference<ExporterItem> { 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<ConfigItem> { 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<int> 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) | |||
| }; | |||
| //============================================================================== | |||
| @@ -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<FileChooser> 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<SourceFileItem> { 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); | |||
| } | |||
| } | |||
| @@ -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) | |||
| }; | |||
| @@ -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<ProjectContentComponent> { 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<ProjectContentComponent> { 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(); | |||
| }); | |||
| @@ -147,6 +147,7 @@ private: | |||
| int lastViewedTab = 0; | |||
| std::unique_ptr<WizardHolder> wizardHolder; | |||
| ScopedMessageBox messageBox; | |||
| //============================================================================== | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProjectContentComponent) | |||
| @@ -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); | |||
| } | |||
| } | |||
| } | |||
| @@ -659,6 +659,7 @@ private: | |||
| std::unique_ptr<FileChooser> chooser; | |||
| std::unique_ptr<ProjectSaver> saver; | |||
| ScopedMessageBox messageBox; | |||
| //============================================================================== | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Project) | |||
| @@ -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 | |||
| { | |||
| @@ -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, | |||
| @@ -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<void (File)> callback); | |||
| static void showFailedToWriteMessage (const File& file); | |||
| private: | |||
| std::unique_ptr<FileChooser> chooser; | |||
| ScopedMessageBox messageBox; | |||
| }; | |||
| //============================================================================== | |||
| @@ -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<MidiDeviceInfo> lastMidiDevices; | |||
| std::unique_ptr<FileChooser> stateFileChooser; | |||
| ScopedMessageBox messageBox; | |||
| private: | |||
| /* This class can be used to ensure that audio callbacks use buffers with a | |||
| @@ -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<bool> finished { false }; | |||
| std::unique_ptr<ThreadPool> pool; | |||
| std::set<String> 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 | |||
| @@ -130,6 +130,8 @@ private: | |||
| class Scanner; | |||
| std::unique_ptr<Scanner> currentScanner; | |||
| ScopedMessageBox messageBox; | |||
| void scanFinished (const StringArray&, const std::vector<String>&); | |||
| void updateList(); | |||
| void removeMissingPlugins(); | |||
| @@ -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<ChannelSelectorListBox> inputChanList, outputChanList; | |||
| ScopedMessageBox messageBox; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioDeviceSettingsPanel) | |||
| }; | |||
| @@ -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(); | |||
| } | |||
| @@ -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<AlertWindow>); | |||
| ScopedMessageBox messageBox; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileChooserDialogBox) | |||
| }; | |||
| @@ -26,10 +26,59 @@ | |||
| namespace juce | |||
| { | |||
| template <ScopedMessageBox::Pimpl::MapFn mapFn = nullptr> | |||
| static int showNativeBoxUnmanaged (const MessageBoxOptions& opts, ModalComponentManager::Callback* cb) | |||
| enum class ResultCodeMappingMode | |||
| { | |||
| return ScopedMessageBox::Pimpl::showUnmanaged<mapFn> (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<ScopedMessageBoxInterface> 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<void (int)> 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<ScopedMessageBoxInterface> inner; | |||
| int numButtons = 0; | |||
| }; | |||
| return mode == ResultCodeMappingMode::plainIndex ? ScopedMessageBoxInterface::create (opts) | |||
| : std::make_unique<Adapter> (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<ScopedMessageBox::Pimpl::messageBox> (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<ScopedMessageBox::Pimpl::messageBox> (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<ScopedMessageBox::Pimpl::okCancel> (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<ScopedMessageBox::Pimpl::yesNoCancel> (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<ScopedMessageBox::Pimpl::okCancel> (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<void (int)> 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 | |||
| @@ -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 | |||
| @@ -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<void (SafeParentPointer, int)> callback) | |||
| MessageBoxOptions getAskToSaveChangesOptions() const | |||
| { | |||
| 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<void (SafeParentPointer, int)> callback) | |||
| { | |||
| 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); | |||
| 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 <typename DoSaveDocument> | |||
| 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<void (SafeParentPointer, bool)> cb) | |||
| { | |||
| if (ptr != nullptr) | |||
| ptr->askToOverwriteFile (ptr, destination, std::move (cb)); | |||
| ptr->askToOverwriteFileAsync (ptr, destination, std::move (cb)); | |||
| }, | |||
| [parent] (const File& destination, std::function<void (Result)> 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<void (SafeParentPointer, bool)> 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<void (SafeParentPointer, bool)> callback) | |||
| { | |||
| if (parent == nullptr) | |||
| return false; | |||
| 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); | |||
| return; | |||
| 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 <typename Callback> | |||
| 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<FileChooser> asyncFc; | |||
| ScopedMessageBox messageBox; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl) | |||
| JUCE_DECLARE_WEAK_REFERENCEABLE (Pimpl) | |||
| @@ -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<KeyEntryWindow> 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(); | |||
| }); | |||
| }; | |||
| } | |||
| @@ -125,6 +125,7 @@ private: | |||
| class CategoryItem; | |||
| class ItemComponent; | |||
| std::unique_ptr<TopLevelItem> treeItem; | |||
| ScopedMessageBox messageBox; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (KeyMappingEditorComponent) | |||
| }; | |||
| @@ -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<TextButton> cancelButton; | |||
| @@ -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 | |||