| @@ -225,9 +225,9 @@ public: | |||||
| { | { | ||||
| modeSelector.setSelectedId (8); | modeSelector.setSelectedId (8); | ||||
| AlertWindow::showMessageBox (AlertWindow::WarningIcon, | |||||
| "Interprocess Comms Demo", | |||||
| "Failed to open the socket or pipe..."); | |||||
| AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, | |||||
| "Interprocess Comms Demo", | |||||
| "Failed to open the socket or pipe..."); | |||||
| } | } | ||||
| } | } | ||||
| @@ -320,10 +320,14 @@ public: | |||||
| m.addItem (1, "Use one thread per ball", true, ! isUsingPool); | m.addItem (1, "Use one thread per ball", true, ! isUsingPool); | ||||
| m.addItem (2, "Use a thread pool", true, isUsingPool); | m.addItem (2, "Use a thread pool", true, isUsingPool); | ||||
| const int res = m.showAt (&controlButton); | |||||
| m.showMenuAsync (PopupMenu::Options().withTargetComponent (&controlButton), | |||||
| ModalCallbackFunction::forComponent (menuItemChosenCallback, this)); | |||||
| } | |||||
| if (res != 0) | |||||
| setUsingPool (res == 2); | |||||
| static void menuItemChosenCallback (int result, ThreadingDemo* demoComponent) | |||||
| { | |||||
| if (demoComponent != 0) | |||||
| demoComponent->setUsingPool (result == 2); | |||||
| } | } | ||||
| // this gets called when a component is added or removed from a parent component. | // this gets called when a component is added or removed from a parent component. | ||||
| @@ -212,24 +212,34 @@ public: | |||||
| treeView != 0 ? treeView->areOpenCloseButtonsVisible() | treeView != 0 ? treeView->areOpenCloseButtonsVisible() | ||||
| : fileTreeComp->areOpenCloseButtonsVisible()); | : fileTreeComp->areOpenCloseButtonsVisible()); | ||||
| const int r = m.showAt (&typeButton); | |||||
| m.showMenuAsync (PopupMenu::Options().withTargetComponent (&typeButton), | |||||
| ModalCallbackFunction::forComponent (menuItemChosenCallback, this)); | |||||
| } | |||||
| if (r == 1) | |||||
| static void menuItemChosenCallback (int result, TreeViewDemo* demoComponent) | |||||
| { | |||||
| if (demoComponent != 0) | |||||
| demoComponent->menuItemChosenCallback (result); | |||||
| } | |||||
| void menuItemChosenCallback (int result) | |||||
| { | |||||
| if (result == 1) | |||||
| { | { | ||||
| showCustomTreeView(); | showCustomTreeView(); | ||||
| } | } | ||||
| else if (r == 2) | |||||
| else if (result == 2) | |||||
| { | { | ||||
| showFileTreeComp(); | showFileTreeComp(); | ||||
| } | } | ||||
| else if (r == 3) | |||||
| else if (result == 3) | |||||
| { | { | ||||
| if (treeView != 0) | if (treeView != 0) | ||||
| treeView->setRootItemVisible (! treeView->isRootItemVisible()); | treeView->setRootItemVisible (! treeView->isRootItemVisible()); | ||||
| else | else | ||||
| fileTreeComp->setRootItemVisible (! fileTreeComp->isRootItemVisible()); | fileTreeComp->setRootItemVisible (! fileTreeComp->isRootItemVisible()); | ||||
| } | } | ||||
| else if (r == 4) | |||||
| else if (result == 4) | |||||
| { | { | ||||
| if (treeView != 0) | if (treeView != 0) | ||||
| treeView->setOpenCloseButtonsVisible (! treeView->areOpenCloseButtonsVisible()); | treeView->setOpenCloseButtonsVisible (! treeView->areOpenCloseButtonsVisible()); | ||||
| @@ -1211,197 +1211,204 @@ public: | |||||
| m.addSubMenu ("File chooser dialogs", fileChoosers); | m.addSubMenu ("File chooser dialogs", fileChoosers); | ||||
| int result = m.showAt (&menuButton); | |||||
| m.showMenuAsync (PopupMenu::Options().withTargetComponent (&menuButton), | |||||
| ModalCallbackFunction::forComponent (menuItemChosenCallback, this)); | |||||
| } | |||||
| } | |||||
| if (result != 0) | |||||
| { | |||||
| // user chose something from the menu.. | |||||
| //============================================================================== | |||||
| // This gets called when our popup menu has an item selected or is dismissed. | |||||
| static void menuItemChosenCallback (int result, WidgetsDemo* demoComponent) | |||||
| { | |||||
| if (result != 0 && demoComponent != 0) | |||||
| demoComponent->performDemoMenuItem (result); | |||||
| } | |||||
| if (result >= 100 && result < 105) | |||||
| { | |||||
| AlertWindow::AlertIconType icon = AlertWindow::NoIcon; | |||||
| if (result == 101) | |||||
| icon = AlertWindow::WarningIcon; | |||||
| else if (result == 102) | |||||
| icon = AlertWindow::InfoIcon; | |||||
| else if (result == 103) | |||||
| icon = AlertWindow::QuestionIcon; | |||||
| AlertWindow::showMessageBox (icon, | |||||
| "This is an AlertWindow", | |||||
| "And this is the AlertWindow's message. Blah blah blah blah blah blah blah blah blah blah blah blah blah.", | |||||
| "ok"); | |||||
| } | |||||
| else if (result == 110) | |||||
| { | |||||
| bool userPickedOk | |||||
| = AlertWindow::showOkCancelBox (AlertWindow::QuestionIcon, | |||||
| "This is an ok/cancel AlertWindow", | |||||
| "And this is the AlertWindow's message. Blah blah blah blah blah blah blah blah blah blah blah blah blah."); | |||||
| void performDemoMenuItem (int result) | |||||
| { | |||||
| if (result >= 100 && result < 105) | |||||
| { | |||||
| AlertWindow::AlertIconType icon = AlertWindow::NoIcon; | |||||
| if (result == 101) | |||||
| icon = AlertWindow::WarningIcon; | |||||
| else if (result == 102) | |||||
| icon = AlertWindow::InfoIcon; | |||||
| else if (result == 103) | |||||
| icon = AlertWindow::QuestionIcon; | |||||
| AlertWindow::showMessageBoxAsync (icon, | |||||
| "This is an AlertWindow", | |||||
| "And this is the AlertWindow's message. Blah blah blah blah blah blah blah blah blah blah blah blah blah.", | |||||
| "ok"); | |||||
| } | |||||
| else if (result == 110) | |||||
| { | |||||
| bool userPickedOk | |||||
| = AlertWindow::showOkCancelBox (AlertWindow::QuestionIcon, | |||||
| "This is an ok/cancel AlertWindow", | |||||
| "And this is the AlertWindow's message. Blah blah blah blah blah blah blah blah blah blah blah blah blah."); | |||||
| (void) userPickedOk; // (just avoids a compiler warning about unused variables) | |||||
| } | |||||
| else if (result == 111) | |||||
| { | |||||
| AlertWindow w ("AlertWindow demo..", | |||||
| "This AlertWindow has a couple of extra components added to show how to add drop-down lists and text entry boxes.", | |||||
| AlertWindow::QuestionIcon); | |||||
| (void) userPickedOk; // (just avoids a compiler warning about unused variables) | |||||
| } | |||||
| else if (result == 111) | |||||
| { | |||||
| AlertWindow w ("AlertWindow demo..", | |||||
| "This AlertWindow has a couple of extra components added to show how to add drop-down lists and text entry boxes.", | |||||
| AlertWindow::QuestionIcon); | |||||
| w.addTextEditor ("text", "enter some text here", "text field:"); | |||||
| w.addTextEditor ("text", "enter some text here", "text field:"); | |||||
| StringArray options; | |||||
| options.add ("option 1"); | |||||
| options.add ("option 2"); | |||||
| options.add ("option 3"); | |||||
| options.add ("option 4"); | |||||
| w.addComboBox ("option", options, "some options"); | |||||
| StringArray options; | |||||
| options.add ("option 1"); | |||||
| options.add ("option 2"); | |||||
| options.add ("option 3"); | |||||
| options.add ("option 4"); | |||||
| w.addComboBox ("option", options, "some options"); | |||||
| w.addButton ("ok", 1, KeyPress (KeyPress::returnKey, 0, 0)); | |||||
| w.addButton ("cancel", 0, KeyPress (KeyPress::escapeKey, 0, 0)); | |||||
| w.addButton ("ok", 1, KeyPress (KeyPress::returnKey, 0, 0)); | |||||
| w.addButton ("cancel", 0, KeyPress (KeyPress::escapeKey, 0, 0)); | |||||
| if (w.runModalLoop() != 0) // is they picked 'ok' | |||||
| { | |||||
| // this is the item they chose in the drop-down list.. | |||||
| const int optionIndexChosen = w.getComboBoxComponent ("option")->getSelectedItemIndex(); | |||||
| (void) optionIndexChosen; // (just avoids a compiler warning about unused variables) | |||||
| if (w.runModalLoop() != 0) // is they picked 'ok' | |||||
| { | |||||
| // this is the item they chose in the drop-down list.. | |||||
| const int optionIndexChosen = w.getComboBoxComponent ("option")->getSelectedItemIndex(); | |||||
| (void) optionIndexChosen; // (just avoids a compiler warning about unused variables) | |||||
| // this is the text they entered.. | |||||
| String text = w.getTextEditorContents ("text"); | |||||
| // this is the text they entered.. | |||||
| String text = w.getTextEditorContents ("text"); | |||||
| } | |||||
| } | |||||
| else if (result == 112) | |||||
| { | |||||
| DemoBackgroundThread demoThread; | |||||
| } | |||||
| } | |||||
| else if (result == 112) | |||||
| { | |||||
| DemoBackgroundThread demoThread; | |||||
| if (demoThread.runThread()) | |||||
| { | |||||
| // thread finished normally.. | |||||
| AlertWindow::showMessageBox (AlertWindow::WarningIcon, | |||||
| "Progress window", | |||||
| "Thread finished ok!"); | |||||
| } | |||||
| else | |||||
| { | |||||
| // user pressed the cancel button.. | |||||
| AlertWindow::showMessageBox (AlertWindow::WarningIcon, | |||||
| "Progress window", | |||||
| "You pressed cancel!"); | |||||
| } | |||||
| } | |||||
| else if (result == 120) | |||||
| { | |||||
| ColourSelectorDialogWindow colourDialog; | |||||
| if (demoThread.runThread()) | |||||
| { | |||||
| // thread finished normally.. | |||||
| AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, | |||||
| "Progress window", | |||||
| "Thread finished ok!"); | |||||
| } | |||||
| else | |||||
| { | |||||
| // user pressed the cancel button.. | |||||
| AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, | |||||
| "Progress window", | |||||
| "You pressed cancel!"); | |||||
| } | |||||
| } | |||||
| else if (result == 120) | |||||
| { | |||||
| ColourSelectorDialogWindow colourDialog; | |||||
| // this will run an event loop until the dialog's closeButtonPressed() | |||||
| // method causes the loop to exit. | |||||
| colourDialog.runModalLoop(); | |||||
| } | |||||
| else if (result == 140) | |||||
| { | |||||
| // this will run an event loop until the dialog's closeButtonPressed() | |||||
| // method causes the loop to exit. | |||||
| colourDialog.runModalLoop(); | |||||
| } | |||||
| else if (result == 140) | |||||
| { | |||||
| #if JUCE_MAC | #if JUCE_MAC | ||||
| AppleRemoteTestWindow test; | |||||
| test.runModalLoop(); | |||||
| AppleRemoteTestWindow test; | |||||
| test.runModalLoop(); | |||||
| #endif | #endif | ||||
| } | |||||
| else if (result >= 121 && result < 139) | |||||
| { | |||||
| const bool useNativeVersion = result < 130; | |||||
| if (result > 130) | |||||
| result -= 10; | |||||
| } | |||||
| else if (result >= 121 && result < 139) | |||||
| { | |||||
| const bool useNativeVersion = result < 130; | |||||
| if (result > 130) | |||||
| result -= 10; | |||||
| if (result == 121) | |||||
| { | |||||
| FileChooser fc ("Choose a file to open...", | |||||
| File::getCurrentWorkingDirectory(), | |||||
| "*", | |||||
| useNativeVersion); | |||||
| if (fc.browseForMultipleFilesToOpen()) | |||||
| { | |||||
| String chosen; | |||||
| for (int i = 0; i < fc.getResults().size(); ++i) | |||||
| chosen << fc.getResults().getReference(i).getFullPathName() << "\n"; | |||||
| AlertWindow::showMessageBox (AlertWindow::InfoIcon, | |||||
| "File Chooser...", | |||||
| "You picked: " + chosen); | |||||
| } | |||||
| } | |||||
| else if (result == 124) | |||||
| { | |||||
| ImagePreviewComponent imagePreview; | |||||
| imagePreview.setSize (200, 200); | |||||
| FileChooser fc ("Choose an image to open...", | |||||
| File::getCurrentWorkingDirectory(), | |||||
| "*.jpg;*.jpeg;*.png;*.gif", | |||||
| useNativeVersion); | |||||
| if (fc.browseForMultipleFilesToOpen (&imagePreview)) | |||||
| { | |||||
| String chosen; | |||||
| for (int i = 0; i < fc.getResults().size(); ++i) | |||||
| chosen << fc.getResults().getReference(i).getFullPathName() << "\n"; | |||||
| AlertWindow::showMessageBox (AlertWindow::InfoIcon, | |||||
| "File Chooser...", | |||||
| "You picked: " + chosen); | |||||
| } | |||||
| } | |||||
| else if (result == 122) | |||||
| { | |||||
| FileChooser fc ("Choose a file to save...", | |||||
| File::getCurrentWorkingDirectory(), | |||||
| "*", | |||||
| useNativeVersion); | |||||
| if (fc.browseForFileToSave (true)) | |||||
| { | |||||
| File chosenFile = fc.getResult(); | |||||
| AlertWindow::showMessageBox (AlertWindow::InfoIcon, | |||||
| "File Chooser...", | |||||
| "You picked: " + chosenFile.getFullPathName()); | |||||
| } | |||||
| } | |||||
| else if (result == 123) | |||||
| { | |||||
| FileChooser fc ("Choose a directory...", | |||||
| File::getCurrentWorkingDirectory(), | |||||
| "*", | |||||
| useNativeVersion); | |||||
| if (fc.browseForDirectory()) | |||||
| { | |||||
| File chosenDirectory = fc.getResult(); | |||||
| AlertWindow::showMessageBox (AlertWindow::InfoIcon, | |||||
| "File Chooser...", | |||||
| "You picked: " + chosenDirectory.getFullPathName()); | |||||
| } | |||||
| } | |||||
| } | |||||
| else if (result == 1001) | |||||
| if (result == 121) | |||||
| { | |||||
| FileChooser fc ("Choose a file to open...", | |||||
| File::getCurrentWorkingDirectory(), | |||||
| "*", | |||||
| useNativeVersion); | |||||
| if (fc.browseForMultipleFilesToOpen()) | |||||
| { | { | ||||
| tabs.setOrientation (TabbedButtonBar::TabsAtTop); | |||||
| String chosen; | |||||
| for (int i = 0; i < fc.getResults().size(); ++i) | |||||
| chosen << fc.getResults().getReference(i).getFullPathName() << "\n"; | |||||
| AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon, | |||||
| "File Chooser...", | |||||
| "You picked: " + chosen); | |||||
| } | } | ||||
| else if (result == 1002) | |||||
| } | |||||
| else if (result == 124) | |||||
| { | |||||
| ImagePreviewComponent imagePreview; | |||||
| imagePreview.setSize (200, 200); | |||||
| FileChooser fc ("Choose an image to open...", | |||||
| File::getCurrentWorkingDirectory(), | |||||
| "*.jpg;*.jpeg;*.png;*.gif", | |||||
| useNativeVersion); | |||||
| if (fc.browseForMultipleFilesToOpen (&imagePreview)) | |||||
| { | { | ||||
| tabs.setOrientation (TabbedButtonBar::TabsAtBottom); | |||||
| String chosen; | |||||
| for (int i = 0; i < fc.getResults().size(); ++i) | |||||
| chosen << fc.getResults().getReference(i).getFullPathName() << "\n"; | |||||
| AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon, | |||||
| "File Chooser...", | |||||
| "You picked: " + chosen); | |||||
| } | } | ||||
| else if (result == 1003) | |||||
| } | |||||
| else if (result == 122) | |||||
| { | |||||
| FileChooser fc ("Choose a file to save...", | |||||
| File::getCurrentWorkingDirectory(), | |||||
| "*", | |||||
| useNativeVersion); | |||||
| if (fc.browseForFileToSave (true)) | |||||
| { | { | ||||
| tabs.setOrientation (TabbedButtonBar::TabsAtLeft); | |||||
| File chosenFile = fc.getResult(); | |||||
| AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon, | |||||
| "File Chooser...", | |||||
| "You picked: " + chosenFile.getFullPathName()); | |||||
| } | } | ||||
| else if (result == 1004) | |||||
| } | |||||
| else if (result == 123) | |||||
| { | |||||
| FileChooser fc ("Choose a directory...", | |||||
| File::getCurrentWorkingDirectory(), | |||||
| "*", | |||||
| useNativeVersion); | |||||
| if (fc.browseForDirectory()) | |||||
| { | { | ||||
| tabs.setOrientation (TabbedButtonBar::TabsAtRight); | |||||
| File chosenDirectory = fc.getResult(); | |||||
| AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon, | |||||
| "File Chooser...", | |||||
| "You picked: " + chosenDirectory.getFullPathName()); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| else if (result == 1001) | |||||
| { | |||||
| tabs.setOrientation (TabbedButtonBar::TabsAtTop); | |||||
| } | |||||
| else if (result == 1002) | |||||
| { | |||||
| tabs.setOrientation (TabbedButtonBar::TabsAtBottom); | |||||
| } | |||||
| else if (result == 1003) | |||||
| { | |||||
| tabs.setOrientation (TabbedButtonBar::TabsAtLeft); | |||||
| } | |||||
| else if (result == 1004) | |||||
| { | |||||
| tabs.setOrientation (TabbedButtonBar::TabsAtRight); | |||||
| } | |||||
| } | } | ||||
| void sliderValueChanged (Slider*) | void sliderValueChanged (Slider*) | ||||
| @@ -62,7 +62,7 @@ StandaloneFilterWindow::StandaloneFilterWindow (const String& title, | |||||
| deviceManager = new AudioFilterStreamingDeviceManager(); | deviceManager = new AudioFilterStreamingDeviceManager(); | ||||
| deviceManager->setFilter (filter); | deviceManager->setFilter (filter); | ||||
| XmlElement* savedState = 0; | |||||
| ScopedPointer<XmlElement> savedState; | |||||
| if (globalSettings != 0) | if (globalSettings != 0) | ||||
| savedState = globalSettings->getXmlValue ("audioSetup"); | savedState = globalSettings->getXmlValue ("audioSetup"); | ||||
| @@ -72,8 +72,6 @@ StandaloneFilterWindow::StandaloneFilterWindow (const String& title, | |||||
| savedState, | savedState, | ||||
| true); | true); | ||||
| delete savedState; | |||||
| if (globalSettings != 0) | if (globalSettings != 0) | ||||
| { | { | ||||
| MemoryBlock data; | MemoryBlock data; | ||||
| @@ -114,9 +112,8 @@ StandaloneFilterWindow::~StandaloneFilterWindow() | |||||
| if (globalSettings != 0 && deviceManager != 0) | if (globalSettings != 0 && deviceManager != 0) | ||||
| { | { | ||||
| XmlElement* const xml = deviceManager->createStateXml(); | |||||
| ScopedPointer<XmlElement> xml (deviceManager->createStateXml()); | |||||
| globalSettings->setValue ("audioSetup", xml); | globalSettings->setValue ("audioSetup", xml); | ||||
| delete xml; | |||||
| } | } | ||||
| deviceManager = 0; | deviceManager = 0; | ||||
| @@ -86,12 +86,12 @@ bool ApplicationProperties::testWriteAccess (const bool testUserSettings, | |||||
| if (commonProps != 0 && ! commonOk) | if (commonProps != 0 && ! commonOk) | ||||
| filenames << '\n' << commonProps->getFile().getFullPathName(); | filenames << '\n' << commonProps->getFile().getFullPathName(); | ||||
| AlertWindow::showMessageBox (AlertWindow::WarningIcon, | |||||
| appName + TRANS(" - Unable to save settings"), | |||||
| TRANS("An error occurred when trying to save the application's settings file...\n\nIn order to save and restore its settings, ") | |||||
| + appName + TRANS(" needs to be able to write to the following files:\n") | |||||
| + filenames | |||||
| + TRANS("\n\nMake sure that these files aren't read-only, and that the disk isn't full.")); | |||||
| AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, | |||||
| appName + TRANS(" - Unable to save settings"), | |||||
| TRANS("An error occurred when trying to save the application's settings file...\n\nIn order to save and restore its settings, ") | |||||
| + appName + TRANS(" needs to be able to write to the following files:\n") | |||||
| + filenames | |||||
| + TRANS("\n\nMake sure that these files aren't read-only, and that the disk isn't full.")); | |||||
| } | } | ||||
| return false; | return false; | ||||
| @@ -154,7 +154,7 @@ void PluginListComponent::buttonClicked (Button* button) | |||||
| menu.addItem (10 + i, "Scan for new or updated " + format->getName() + " plugins..."); | menu.addItem (10 + i, "Scan for new or updated " + format->getName() + " plugins..."); | ||||
| } | } | ||||
| const int r = menu.showAt (&optionsButton); | |||||
| const int r = menu.showMenu (PopupMenu::Options().withTargetComponent (&optionsButton)); | |||||
| if (r == 1) | if (r == 1) | ||||
| { | { | ||||
| @@ -225,6 +225,7 @@ void PluginListComponent::filesDropped (const StringArray& files, int, int) | |||||
| list.scanAndAddDragAndDroppedFiles (files, typesFound); | list.scanAndAddDragAndDroppedFiles (files, typesFound); | ||||
| } | } | ||||
| #if JUCE_MODAL_LOOPS_PERMITTED | |||||
| void PluginListComponent::scanFor (AudioPluginFormat* format) | void PluginListComponent::scanFor (AudioPluginFormat* format) | ||||
| { | { | ||||
| if (format == 0) | if (format == 0) | ||||
| @@ -300,6 +301,6 @@ void PluginListComponent::scanFor (AudioPluginFormat* format) | |||||
| + shortNames.joinIntoString (", ")); | + shortNames.joinIntoString (", ")); | ||||
| } | } | ||||
| } | } | ||||
| #endif | |||||
| END_JUCE_NAMESPACE | END_JUCE_NAMESPACE | ||||
| @@ -282,5 +282,16 @@ | |||||
| #define JUCE_DEPRECATED(functionDef) functionDef | #define JUCE_DEPRECATED(functionDef) functionDef | ||||
| #endif | #endif | ||||
| //============================================================================== | |||||
| #if JUCE_ANDROID && ! DOXYGEN | |||||
| #define JUCE_MODAL_LOOPS_PERMITTED 0 | |||||
| #else | |||||
| /** Some operating environments don't provide a modal loop mechanism, so this flag can be | |||||
| used to disable any functions that try to run a modal loop. | |||||
| */ | |||||
| #define JUCE_MODAL_LOOPS_PERMITTED 1 | |||||
| #endif | |||||
| #endif // __JUCE_PLATFORMDEFS_JUCEHEADER__ | #endif // __JUCE_PLATFORMDEFS_JUCEHEADER__ | ||||
| @@ -33,7 +33,7 @@ | |||||
| */ | */ | ||||
| #define JUCE_MAJOR_VERSION 1 | #define JUCE_MAJOR_VERSION 1 | ||||
| #define JUCE_MINOR_VERSION 53 | #define JUCE_MINOR_VERSION 53 | ||||
| #define JUCE_BUILDNUMBER 29 | |||||
| #define JUCE_BUILDNUMBER 30 | |||||
| /** Current Juce version number. | /** Current Juce version number. | ||||
| @@ -127,7 +127,7 @@ void MessageManager::deliverMessage (Message* const message) | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| #if ! (JUCE_MAC || JUCE_IOS || JUCE_ANDROID) | |||||
| #if JUCE_MODAL_LOOPS_PERMITTED && ! (JUCE_MAC || JUCE_IOS) | |||||
| void MessageManager::runDispatchLoop() | void MessageManager::runDispatchLoop() | ||||
| { | { | ||||
| jassert (isThisTheMessageThread()); // must only be called by the message thread | jassert (isThisTheMessageThread()); // must only be called by the message thread | ||||
| @@ -78,12 +78,14 @@ public: | |||||
| */ | */ | ||||
| bool hasStopMessageBeenSent() const throw() { return quitMessagePosted; } | bool hasStopMessageBeenSent() const throw() { return quitMessagePosted; } | ||||
| #if JUCE_MODAL_LOOPS_PERMITTED | |||||
| /** Synchronously dispatches messages until a given time has elapsed. | /** Synchronously dispatches messages until a given time has elapsed. | ||||
| Returns false if a quit message has been posted by a call to stopDispatchLoop(), | Returns false if a quit message has been posted by a call to stopDispatchLoop(), | ||||
| otherwise returns true. | otherwise returns true. | ||||
| */ | */ | ||||
| bool runDispatchLoopUntil (int millisecondsToRunFor); | bool runDispatchLoopUntil (int millisecondsToRunFor); | ||||
| #endif | |||||
| //============================================================================== | //============================================================================== | ||||
| /** Calls a function using the message-thread. | /** Calls a function using the message-thread. | ||||
| @@ -503,31 +503,16 @@ void ComboBox::labelTextChanged (Label*) | |||||
| //============================================================================== | //============================================================================== | ||||
| class ComboBox::Callback : public ModalComponentManager::Callback | |||||
| void ComboBox::popupMenuFinishedCallback (int result, ComboBox* box) | |||||
| { | { | ||||
| public: | |||||
| Callback (ComboBox* const box_) | |||||
| : box (box_) | |||||
| if (box != 0) | |||||
| { | { | ||||
| } | |||||
| box->menuActive = false; | |||||
| void modalStateFinished (int returnValue) | |||||
| { | |||||
| if (box != 0) | |||||
| { | |||||
| box->menuActive = false; | |||||
| if (returnValue != 0) | |||||
| box->setSelectedId (returnValue); | |||||
| } | |||||
| if (result != 0) | |||||
| box->setSelectedId (result); | |||||
| } | } | ||||
| private: | |||||
| Component::SafePointer<ComboBox> box; | |||||
| JUCE_DECLARE_NON_COPYABLE (Callback); | |||||
| }; | |||||
| } | |||||
| void ComboBox::showPopup() | void ComboBox::showPopup() | ||||
| { | { | ||||
| @@ -555,8 +540,13 @@ void ComboBox::showPopup() | |||||
| menu.addItem (1, noChoicesMessage, false); | menu.addItem (1, noChoicesMessage, false); | ||||
| menuActive = true; | menuActive = true; | ||||
| menu.showAt (this, selectedId, getWidth(), 1, jlimit (12, 24, getHeight()), | |||||
| new Callback (this)); | |||||
| menu.showMenuAsync (PopupMenu::Options().withTargetComponent (this) | |||||
| .withItemThatMustBeVisible (selectedId) | |||||
| .withMinimumWidth (getWidth()) | |||||
| .withMaximumNumColumns (1) | |||||
| .withStandardItemHeight (jlimit (12, 24, getHeight())), | |||||
| ModalCallbackFunction::forComponent (popupMenuFinishedCallback, this)); | |||||
| } | } | ||||
| } | } | ||||
| @@ -387,9 +387,6 @@ private: | |||||
| bool isEnabled : 1, isHeading : 1; | bool isEnabled : 1, isHeading : 1; | ||||
| }; | }; | ||||
| class Callback; | |||||
| friend class Callback; | |||||
| OwnedArray <ItemInfo> items; | OwnedArray <ItemInfo> items; | ||||
| Value currentId; | Value currentId; | ||||
| int lastCurrentId; | int lastCurrentId; | ||||
| @@ -401,6 +398,7 @@ private: | |||||
| ItemInfo* getItemForId (int itemId) const throw(); | ItemInfo* getItemForId (int itemId) const throw(); | ||||
| ItemInfo* getItemForIndex (int index) const throw(); | ItemInfo* getItemForIndex (int index) const throw(); | ||||
| bool selectIfEnabled (int index); | bool selectIfEnabled (int index); | ||||
| static void popupMenuFinishedCallback (int, ComboBox*); | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComboBox); | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComboBox); | ||||
| }; | }; | ||||
| @@ -48,10 +48,6 @@ public: | |||||
| setAlwaysOnTop (true); | setAlwaysOnTop (true); | ||||
| } | } | ||||
| ~SliderPopupDisplayComponent() | |||||
| { | |||||
| } | |||||
| void paintContent (Graphics& g, int w, int h) | void paintContent (Graphics& g, int w, int h) | ||||
| { | { | ||||
| g.setFont (font); | g.setFont (font); | ||||
| @@ -609,6 +605,31 @@ void Slider::setMaxValue (double newValue, const bool sendUpdateMessage, const b | |||||
| } | } | ||||
| } | } | ||||
| void Slider::setMinAndMaxValues (double newMinValue, double newMaxValue, bool sendUpdateMessage, bool sendMessageSynchronously) | |||||
| { | |||||
| // The maximum value only applies to sliders that are in two- or three-value mode. | |||||
| jassert (style == TwoValueHorizontal || style == TwoValueVertical | |||||
| || style == ThreeValueHorizontal || style == ThreeValueVertical); | |||||
| if (newMaxValue < newMinValue) | |||||
| swapVariables (newMaxValue, newMinValue); | |||||
| newMinValue = constrainedValue (newMinValue); | |||||
| newMaxValue = constrainedValue (newMaxValue); | |||||
| if (lastValueMax != newMaxValue || lastValueMin != newMinValue) | |||||
| { | |||||
| lastValueMax = newMaxValue; | |||||
| lastValueMin = newMinValue; | |||||
| valueMin = newMinValue; | |||||
| valueMax = newMaxValue; | |||||
| repaint(); | |||||
| if (sendUpdateMessage) | |||||
| triggerChangeMessage (sendMessageSynchronously); | |||||
| } | |||||
| } | |||||
| void Slider::setDoubleClickReturnValue (const bool isDoubleClickEnabled, | void Slider::setDoubleClickReturnValue (const bool isDoubleClickEnabled, | ||||
| const double valueToSetOnDoubleClick) | const double valueToSetOnDoubleClick) | ||||
| { | { | ||||
| @@ -993,6 +1014,21 @@ void Slider::focusOfChildComponentChanged (FocusChangeType) | |||||
| repaint(); | repaint(); | ||||
| } | } | ||||
| static void sliderMenuCallback (int result, Slider* slider) | |||||
| { | |||||
| if (slider != 0) | |||||
| { | |||||
| switch (result) | |||||
| { | |||||
| case 1: slider->setVelocityBasedMode (! slider->getVelocityBasedMode()); break; | |||||
| case 2: slider->setSliderStyle (Slider::Rotary); break; | |||||
| case 3: slider->setSliderStyle (Slider::RotaryHorizontalDrag); break; | |||||
| case 4: slider->setSliderStyle (Slider::RotaryVerticalDrag); break; | |||||
| default: break; | |||||
| } | |||||
| } | |||||
| } | |||||
| void Slider::mouseDown (const MouseEvent& e) | void Slider::mouseDown (const MouseEvent& e) | ||||
| { | { | ||||
| mouseWasHidden = false; | mouseWasHidden = false; | ||||
| @@ -1023,24 +1059,8 @@ void Slider::mouseDown (const MouseEvent& e) | |||||
| m.addSubMenu (TRANS ("rotary mode"), rotaryMenu); | m.addSubMenu (TRANS ("rotary mode"), rotaryMenu); | ||||
| } | } | ||||
| const int r = m.show(); | |||||
| if (r == 1) | |||||
| { | |||||
| setVelocityBasedMode (! isVelocityBased); | |||||
| } | |||||
| else if (r == 2) | |||||
| { | |||||
| setSliderStyle (Rotary); | |||||
| } | |||||
| else if (r == 3) | |||||
| { | |||||
| setSliderStyle (RotaryHorizontalDrag); | |||||
| } | |||||
| else if (r == 4) | |||||
| { | |||||
| setSliderStyle (RotaryVerticalDrag); | |||||
| } | |||||
| m.showMenuAsync (PopupMenu::Options(), | |||||
| ModalCallbackFunction::forComponent (sliderMenuCallback, this)); | |||||
| } | } | ||||
| else if (maximum > minimum) | else if (maximum > minimum) | ||||
| { | { | ||||
| @@ -477,6 +477,26 @@ public: | |||||
| bool sendMessageSynchronously = false, | bool sendMessageSynchronously = false, | ||||
| bool allowNudgingOfOtherValues = false); | bool allowNudgingOfOtherValues = false); | ||||
| /** For a slider with two or three thumbs, this sets the minimum and maximum thumb positions. | |||||
| This will trigger a callback to Slider::Listener::sliderValueChanged() for any listeners | |||||
| that are registered, and will synchronously call the valueChanged() method in case subclasses | |||||
| want to handle it. | |||||
| @param newMinValue the new minimum value to set - this will be snapped to the | |||||
| nearest interval if one has been set. | |||||
| @param newMaxValue the new minimum value to set - this will be snapped to the | |||||
| nearest interval if one has been set. | |||||
| @param sendUpdateMessage if false, a change to the value will not trigger a call to | |||||
| any Slider::Listeners or the valueChanged() method | |||||
| @param sendMessageSynchronously if true, then a call to the Slider::Listeners will be made | |||||
| synchronously; if false, it will be asynchronous | |||||
| @see setMaxValue, setMinValue, setValue | |||||
| */ | |||||
| void setMinAndMaxValues (double newMinValue, double newMaxValue, | |||||
| bool sendUpdateMessage = true, | |||||
| bool sendMessageSynchronously = false); | |||||
| //============================================================================== | //============================================================================== | ||||
| /** A class for receiving callbacks from a Slider. | /** A class for receiving callbacks from a Slider. | ||||
| @@ -913,6 +913,12 @@ void TableHeaderComponent::updateColumnUnderMouse (int x, int y) | |||||
| } | } | ||||
| } | } | ||||
| static void tableHeaderMenuCallback (int result, TableHeaderComponent* tableHeader, int columnIdClicked) | |||||
| { | |||||
| if (tableHeader != 0 && result != 0) | |||||
| tableHeader->reactToMenuItem (result, columnIdClicked); | |||||
| } | |||||
| void TableHeaderComponent::showColumnChooserMenu (const int columnIdClicked) | void TableHeaderComponent::showColumnChooserMenu (const int columnIdClicked) | ||||
| { | { | ||||
| PopupMenu m; | PopupMenu m; | ||||
| @@ -922,10 +928,8 @@ void TableHeaderComponent::showColumnChooserMenu (const int columnIdClicked) | |||||
| { | { | ||||
| m.setLookAndFeel (&getLookAndFeel()); | m.setLookAndFeel (&getLookAndFeel()); | ||||
| const int result = m.show(); | |||||
| if (result != 0) | |||||
| reactToMenuItem (result, columnIdClicked); | |||||
| m.showMenuAsync (PopupMenu::Options(), | |||||
| ModalCallbackFunction::forComponent (tableHeaderMenuCallback, this, columnIdClicked)); | |||||
| } | } | ||||
| } | } | ||||
| @@ -1759,26 +1759,11 @@ void TextEditor::paintOverChildren (Graphics& g) | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| class TextEditorMenuPerformer : public ModalComponentManager::Callback | |||||
| static void textEditorMenuCallback (int menuResult, TextEditor* editor) | |||||
| { | { | ||||
| public: | |||||
| TextEditorMenuPerformer (TextEditor* const editor_) | |||||
| : editor (editor_) | |||||
| { | |||||
| } | |||||
| void modalStateFinished (int returnValue) | |||||
| { | |||||
| if (editor != 0 && returnValue != 0) | |||||
| editor->performPopupMenuAction (returnValue); | |||||
| } | |||||
| private: | |||||
| Component::SafePointer<TextEditor> editor; | |||||
| JUCE_DECLARE_NON_COPYABLE (TextEditorMenuPerformer); | |||||
| }; | |||||
| if (editor != 0 && menuResult != 0) | |||||
| editor->performPopupMenuAction (menuResult); | |||||
| } | |||||
| void TextEditor::mouseDown (const MouseEvent& e) | void TextEditor::mouseDown (const MouseEvent& e) | ||||
| { | { | ||||
| @@ -1798,7 +1783,8 @@ void TextEditor::mouseDown (const MouseEvent& e) | |||||
| m.setLookAndFeel (&getLookAndFeel()); | m.setLookAndFeel (&getLookAndFeel()); | ||||
| addPopupMenuItems (m, &e); | addPopupMenuItems (m, &e); | ||||
| m.show (0, 0, 0, 0, new TextEditorMenuPerformer (this)); | |||||
| m.showMenuAsync (PopupMenu::Options(), | |||||
| ModalCallbackFunction::forComponent (textEditorMenuCallback, this)); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -558,7 +558,7 @@ void Toolbar::buttonClicked (Button*) | |||||
| { | { | ||||
| PopupMenu m; | PopupMenu m; | ||||
| m.addCustomItem (1, new MissingItemsComponent (*this, getThickness())); | m.addCustomItem (1, new MissingItemsComponent (*this, getThickness())); | ||||
| m.showAt (missingItemsButton); | |||||
| m.showMenuAsync (PopupMenu::Options().withTargetComponent (missingItemsButton), 0); | |||||
| } | } | ||||
| } | } | ||||
| @@ -685,6 +685,7 @@ public: | |||||
| ~ToolbarCustomisationDialog() | ~ToolbarCustomisationDialog() | ||||
| { | { | ||||
| toolbar->setEditingActive (false); | |||||
| setContentComponent (0, true); | setContentComponent (0, true); | ||||
| } | } | ||||
| @@ -841,18 +842,8 @@ void Toolbar::showCustomisationDialog (ToolbarItemFactory& factory, const int op | |||||
| { | { | ||||
| setEditingActive (true); | setEditingActive (true); | ||||
| #if JUCE_DEBUG | |||||
| WeakReference<Component> checker (this); | |||||
| #endif | |||||
| ToolbarCustomisationDialog dw (factory, this, optionFlags); | |||||
| dw.runModalLoop(); | |||||
| #if JUCE_DEBUG | |||||
| jassert (checker != 0); // Don't delete the toolbar while it's being customised! | |||||
| #endif | |||||
| setEditingActive (false); | |||||
| (new ToolbarCustomisationDialog (factory, this, optionFlags)) | |||||
| ->enterModalState (true, 0, true); | |||||
| } | } | ||||
| @@ -95,6 +95,7 @@ const Array<File>& FileChooser::getResults() const | |||||
| return results; | return results; | ||||
| } | } | ||||
| #if JUCE_MODAL_LOOPS_PERMITTED | |||||
| bool FileChooser::showDialog (const bool selectsDirectories, | bool FileChooser::showDialog (const bool selectsDirectories, | ||||
| const bool selectsFiles, | const bool selectsFiles, | ||||
| const bool isSave, | const bool isSave, | ||||
| @@ -166,6 +167,7 @@ bool FileChooser::showDialog (const bool selectsDirectories, | |||||
| return results.size() > 0; | return results.size() > 0; | ||||
| } | } | ||||
| #endif | |||||
| //============================================================================== | //============================================================================== | ||||
| FilePreviewComponent::FilePreviewComponent() | FilePreviewComponent::FilePreviewComponent() | ||||
| @@ -30,9 +30,73 @@ BEGIN_JUCE_NAMESPACE | |||||
| #include "juce_FileChooserDialogBox.h" | #include "juce_FileChooserDialogBox.h" | ||||
| #include "../lookandfeel/juce_LookAndFeel.h" | #include "../lookandfeel/juce_LookAndFeel.h" | ||||
| #include "../../../text/juce_LocalisedStrings.h" | #include "../../../text/juce_LocalisedStrings.h" | ||||
| #include "../windows/juce_AlertWindow.h" | |||||
| //============================================================================== | |||||
| class FileChooserDialogBox::ContentComponent : public Component | |||||
| { | |||||
| public: | |||||
| //============================================================================== | |||||
| ContentComponent (const String& name, const String& instructions_, FileBrowserComponent& chooserComponent_) | |||||
| : Component (name), | |||||
| chooserComponent (chooserComponent_), | |||||
| okButton (chooserComponent_.getActionVerb()), | |||||
| cancelButton (TRANS ("Cancel")), | |||||
| newFolderButton (TRANS ("New Folder")), | |||||
| instructions (instructions_) | |||||
| { | |||||
| addAndMakeVisible (&chooserComponent); | |||||
| addAndMakeVisible (&okButton); | |||||
| okButton.addShortcut (KeyPress::returnKey); | |||||
| addAndMakeVisible (&cancelButton); | |||||
| cancelButton.addShortcut (KeyPress::escapeKey); | |||||
| addChildComponent (&newFolderButton); | |||||
| setInterceptsMouseClicks (false, true); | |||||
| } | |||||
| void paint (Graphics& g) | |||||
| { | |||||
| g.setColour (getLookAndFeel().findColour (FileChooserDialogBox::titleTextColourId)); | |||||
| text.draw (g); | |||||
| } | |||||
| void resized() | |||||
| { | |||||
| const int buttonHeight = 26; | |||||
| Rectangle<int> area (getLocalBounds()); | |||||
| getLookAndFeel().createFileChooserHeaderText (getName(), instructions, text, getWidth()); | |||||
| const Rectangle<float> bb (text.getBoundingBox (0, text.getNumGlyphs(), false)); | |||||
| area.removeFromTop (roundToInt (bb.getBottom()) + 10); | |||||
| chooserComponent.setBounds (area.removeFromTop (area.getHeight() - buttonHeight - 20)); | |||||
| Rectangle<int> buttonArea (area.reduced (16, 10)); | |||||
| okButton.changeWidthToFitText (buttonHeight); | |||||
| okButton.setBounds (buttonArea.removeFromRight (okButton.getWidth() + 16)); | |||||
| buttonArea.removeFromRight (16); | |||||
| cancelButton.changeWidthToFitText (buttonHeight); | |||||
| cancelButton.setBounds (buttonArea.removeFromRight (cancelButton.getWidth())); | |||||
| newFolderButton.changeWidthToFitText (buttonHeight); | |||||
| newFolderButton.setBounds (buttonArea.removeFromLeft (newFolderButton.getWidth())); | |||||
| } | |||||
| FileBrowserComponent& chooserComponent; | |||||
| TextButton okButton, cancelButton, newFolderButton; | |||||
| private: | |||||
| String instructions; | |||||
| GlyphArrangement text; | |||||
| }; | |||||
| //============================================================================== | //============================================================================== | ||||
| FileChooserDialogBox::FileChooserDialogBox (const String& name, | FileChooserDialogBox::FileChooserDialogBox (const String& name, | ||||
| const String& instructions, | const String& instructions, | ||||
| @@ -91,6 +155,15 @@ bool FileChooserDialogBox::showAt (int x, int y, int w, int h) | |||||
| return ok; | return ok; | ||||
| } | } | ||||
| void FileChooserDialogBox::centreWithDefaultSize (Component* componentToCentreAround) | |||||
| { | |||||
| Component* const previewComp = content->chooserComponent.getPreviewComponent(); | |||||
| centreAroundComponent (componentToCentreAround, | |||||
| previewComp != 0 ? 400 + previewComp->getWidth() : 600, | |||||
| 500); | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| void FileChooserDialogBox::buttonClicked (Button* button) | void FileChooserDialogBox::buttonClicked (Button* button) | ||||
| { | { | ||||
| @@ -131,108 +204,82 @@ void FileChooserDialogBox::fileDoubleClicked (const File&) | |||||
| content->okButton.triggerClick(); | content->okButton.triggerClick(); | ||||
| } | } | ||||
| void FileChooserDialogBox::okToOverwriteFileCallback (int result, FileChooserDialogBox* box) | |||||
| { | |||||
| if (result != 0 && box != 0) | |||||
| box->exitModalState (1); | |||||
| } | |||||
| void FileChooserDialogBox::okButtonPressed() | void FileChooserDialogBox::okButtonPressed() | ||||
| { | { | ||||
| if ((! (warnAboutOverwritingExistingFiles | |||||
| && content->chooserComponent.isSaveMode() | |||||
| && content->chooserComponent.getSelectedFile(0).exists())) | |||||
| || AlertWindow::showOkCancelBox (AlertWindow::WarningIcon, | |||||
| if (warnAboutOverwritingExistingFiles | |||||
| && content->chooserComponent.isSaveMode() | |||||
| && content->chooserComponent.getSelectedFile(0).exists()) | |||||
| { | |||||
| AlertWindow::showOkCancelBox (AlertWindow::WarningIcon, | |||||
| TRANS("File already exists"), | TRANS("File already exists"), | ||||
| TRANS("There's already a file called:") | TRANS("There's already a file called:") | ||||
| + "\n\n" + content->chooserComponent.getSelectedFile(0).getFullPathName() | + "\n\n" + content->chooserComponent.getSelectedFile(0).getFullPathName() | ||||
| + "\n\n" + TRANS("Are you sure you want to overwrite it?"), | + "\n\n" + TRANS("Are you sure you want to overwrite it?"), | ||||
| TRANS("overwrite"), | TRANS("overwrite"), | ||||
| TRANS("cancel"))) | |||||
| TRANS("cancel"), | |||||
| this, | |||||
| ModalCallbackFunction::forComponent (okToOverwriteFileCallback, this)); | |||||
| } | |||||
| else | |||||
| { | { | ||||
| exitModalState (1); | exitModalState (1); | ||||
| } | } | ||||
| } | } | ||||
| void FileChooserDialogBox::createNewFolder() | |||||
| void FileChooserDialogBox::createNewFolderCallback (int result, FileChooserDialogBox* box, | |||||
| Component::SafePointer<AlertWindow> alert) | |||||
| { | { | ||||
| File parent (content->chooserComponent.getRoot()); | |||||
| if (parent.isDirectory()) | |||||
| if (result != 0 && alert != 0 && box != 0) | |||||
| { | { | ||||
| AlertWindow aw (TRANS("New Folder"), | |||||
| TRANS("Please enter the name for the folder"), | |||||
| AlertWindow::NoIcon, this); | |||||
| aw.addTextEditor ("name", String::empty, String::empty, false); | |||||
| aw.addButton (TRANS("ok"), 1, KeyPress::returnKey); | |||||
| aw.addButton (TRANS("cancel"), KeyPress::escapeKey); | |||||
| if (aw.runModalLoop() != 0) | |||||
| { | |||||
| aw.setVisible (false); | |||||
| const String name (File::createLegalFileName (aw.getTextEditorContents ("name"))); | |||||
| if (! name.isEmpty()) | |||||
| { | |||||
| if (! parent.getChildFile (name).createDirectory()) | |||||
| { | |||||
| AlertWindow::showMessageBox (AlertWindow::WarningIcon, | |||||
| TRANS ("New Folder"), | |||||
| TRANS ("Couldn't create the folder!")); | |||||
| } | |||||
| content->chooserComponent.refresh(); | |||||
| } | |||||
| } | |||||
| alert->setVisible (false); | |||||
| box->createNewFolderConfirmed (alert->getTextEditorContents ("name")); | |||||
| } | } | ||||
| } | } | ||||
| //============================================================================== | |||||
| FileChooserDialogBox::ContentComponent::ContentComponent (const String& name, const String& instructions_, FileBrowserComponent& chooserComponent_) | |||||
| : Component (name), instructions (instructions_), | |||||
| chooserComponent (chooserComponent_), | |||||
| okButton (chooserComponent_.getActionVerb()), | |||||
| cancelButton (TRANS ("Cancel")), | |||||
| newFolderButton (TRANS ("New Folder")) | |||||
| void FileChooserDialogBox::createNewFolder() | |||||
| { | { | ||||
| addAndMakeVisible (&chooserComponent); | |||||
| addAndMakeVisible (&okButton); | |||||
| okButton.addShortcut (KeyPress::returnKey); | |||||
| addAndMakeVisible (&cancelButton); | |||||
| cancelButton.addShortcut (KeyPress::escapeKey); | |||||
| addChildComponent (&newFolderButton); | |||||
| setInterceptsMouseClicks (false, true); | |||||
| } | |||||
| File parent (content->chooserComponent.getRoot()); | |||||
| void FileChooserDialogBox::ContentComponent::paint (Graphics& g) | |||||
| { | |||||
| g.setColour (getLookAndFeel().findColour (FileChooserDialogBox::titleTextColourId)); | |||||
| text.draw (g); | |||||
| if (parent.isDirectory()) | |||||
| { | |||||
| AlertWindow* aw = new AlertWindow (TRANS("New Folder"), | |||||
| TRANS("Please enter the name for the folder"), | |||||
| AlertWindow::NoIcon, this); | |||||
| aw->addTextEditor ("name", String::empty, String::empty, false); | |||||
| aw->addButton (TRANS("ok"), 1, KeyPress::returnKey); | |||||
| aw->addButton (TRANS("cancel"), KeyPress::escapeKey); | |||||
| aw->enterModalState (true, | |||||
| ModalCallbackFunction::forComponent (createNewFolderCallback, this, | |||||
| Component::SafePointer<AlertWindow> (aw)), | |||||
| true); | |||||
| } | |||||
| } | } | ||||
| void FileChooserDialogBox::ContentComponent::resized() | |||||
| void FileChooserDialogBox::createNewFolderConfirmed (const String& nameFromDialog) | |||||
| { | { | ||||
| const int buttonHeight = 26; | |||||
| const String name (File::createLegalFileName (nameFromDialog)); | |||||
| Rectangle<int> area (getLocalBounds()); | |||||
| getLookAndFeel().createFileChooserHeaderText (getName(), instructions, text, getWidth()); | |||||
| const Rectangle<float> bb (text.getBoundingBox (0, text.getNumGlyphs(), false)); | |||||
| area.removeFromTop (roundToInt (bb.getBottom()) + 10); | |||||
| chooserComponent.setBounds (area.removeFromTop (area.getHeight() - buttonHeight - 20)); | |||||
| Rectangle<int> buttonArea (area.reduced (16, 10)); | |||||
| okButton.changeWidthToFitText (buttonHeight); | |||||
| okButton.setBounds (buttonArea.removeFromRight (okButton.getWidth() + 16)); | |||||
| buttonArea.removeFromRight (16); | |||||
| if (! name.isEmpty()) | |||||
| { | |||||
| const File parent (content->chooserComponent.getRoot()); | |||||
| cancelButton.changeWidthToFitText (buttonHeight); | |||||
| cancelButton.setBounds (buttonArea.removeFromRight (cancelButton.getWidth())); | |||||
| if (! parent.getChildFile (name).createDirectory()) | |||||
| { | |||||
| AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, | |||||
| TRANS ("New Folder"), | |||||
| TRANS ("Couldn't create the folder!")); | |||||
| } | |||||
| newFolderButton.changeWidthToFitText (buttonHeight); | |||||
| newFolderButton.setBounds (buttonArea.removeFromLeft (newFolderButton.getWidth())); | |||||
| content->chooserComponent.refresh(); | |||||
| } | |||||
| } | } | ||||
| @@ -30,6 +30,7 @@ | |||||
| #include "../windows/juce_ResizableWindow.h" | #include "../windows/juce_ResizableWindow.h" | ||||
| #include "../buttons/juce_TextButton.h" | #include "../buttons/juce_TextButton.h" | ||||
| #include "../../graphics/fonts/juce_GlyphArrangement.h" | #include "../../graphics/fonts/juce_GlyphArrangement.h" | ||||
| #include "../windows/juce_AlertWindow.h" | |||||
| //============================================================================== | //============================================================================== | ||||
| @@ -114,6 +115,11 @@ public: | |||||
| */ | */ | ||||
| bool showAt (int x, int y, int width, int height); | bool showAt (int x, int y, int width, int height); | ||||
| /** Sets the size of this dialog box to its default and positions it either in the | |||||
| centre of the screen, or centred around a component that is provided. | |||||
| */ | |||||
| void centreWithDefaultSize (Component* componentToCentreAround = 0); | |||||
| //============================================================================== | //============================================================================== | ||||
| /** A set of colour IDs to use to change the colour of various aspects of the box. | /** A set of colour IDs to use to change the colour of various aspects of the box. | ||||
| @@ -140,26 +146,16 @@ public: | |||||
| void fileDoubleClicked (const File& file); | void fileDoubleClicked (const File& file); | ||||
| private: | private: | ||||
| class ContentComponent : public Component | |||||
| { | |||||
| public: | |||||
| ContentComponent (const String& name, const String& instructions, FileBrowserComponent& chooserComponent); | |||||
| void paint (Graphics& g); | |||||
| void resized(); | |||||
| String instructions; | |||||
| GlyphArrangement text; | |||||
| FileBrowserComponent& chooserComponent; | |||||
| TextButton okButton, cancelButton, newFolderButton; | |||||
| }; | |||||
| class ContentComponent; | |||||
| ContentComponent* content; | ContentComponent* content; | ||||
| const bool warnAboutOverwritingExistingFiles; | const bool warnAboutOverwritingExistingFiles; | ||||
| void okButtonPressed(); | void okButtonPressed(); | ||||
| void createNewFolder(); | void createNewFolder(); | ||||
| void createNewFolderConfirmed (const String& name); | |||||
| static void okToOverwriteFileCallback (int result, FileChooserDialogBox*); | |||||
| static void createNewFolderCallback (int result, FileChooserDialogBox*, Component::SafePointer<AlertWindow>); | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileChooserDialogBox); | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileChooserDialogBox); | ||||
| }; | }; | ||||
| @@ -214,10 +214,12 @@ class Component::ComponentHelpers | |||||
| { | { | ||||
| public: | public: | ||||
| //============================================================================== | //============================================================================== | ||||
| #if JUCE_MODAL_LOOPS_PERMITTED | |||||
| static void* runModalLoopCallback (void* userData) | static void* runModalLoopCallback (void* userData) | ||||
| { | { | ||||
| return (void*) (pointer_sized_int) static_cast <Component*> (userData)->runModalLoop(); | return (void*) (pointer_sized_int) static_cast <Component*> (userData)->runModalLoop(); | ||||
| } | } | ||||
| #endif | |||||
| static const Identifier getColourPropertyId (const int colourId) | static const Identifier getColourPropertyId (const int colourId) | ||||
| { | { | ||||
| @@ -1525,6 +1527,7 @@ void Component::internalHierarchyChanged() | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| #if JUCE_MODAL_LOOPS_PERMITTED | |||||
| int Component::runModalLoop() | int Component::runModalLoop() | ||||
| { | { | ||||
| if (! MessageManager::getInstance()->isThisTheMessageThread()) | if (! MessageManager::getInstance()->isThisTheMessageThread()) | ||||
| @@ -1539,8 +1542,30 @@ int Component::runModalLoop() | |||||
| return ModalComponentManager::getInstance()->runEventLoopForCurrentComponent(); | return ModalComponentManager::getInstance()->runEventLoopForCurrentComponent(); | ||||
| } | } | ||||
| #endif | |||||
| void Component::enterModalState (const bool shouldTakeKeyboardFocus, ModalComponentManager::Callback* const callback) | |||||
| //============================================================================== | |||||
| class ModalAutoDeleteCallback : public ModalComponentManager::Callback | |||||
| { | |||||
| public: | |||||
| ModalAutoDeleteCallback (Component* const comp_) | |||||
| : comp (comp_) | |||||
| {} | |||||
| void modalStateFinished (int) | |||||
| { | |||||
| delete comp.get(); | |||||
| } | |||||
| private: | |||||
| WeakReference<Component> comp; | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ModalAutoDeleteCallback); | |||||
| }; | |||||
| void Component::enterModalState (const bool shouldTakeKeyboardFocus, | |||||
| ModalComponentManager::Callback* callback, | |||||
| const bool deleteWhenDismissed) | |||||
| { | { | ||||
| // if component methods are being called from threads other than the message | // if component methods are being called from threads other than the message | ||||
| // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe. | // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe. | ||||
| @@ -1552,7 +1577,13 @@ void Component::enterModalState (const bool shouldTakeKeyboardFocus, ModalCompon | |||||
| if (! isCurrentlyModal()) | if (! isCurrentlyModal()) | ||||
| { | { | ||||
| ModalComponentManager::getInstance()->startModal (this, callback); | |||||
| ModalComponentManager* const mcm = ModalComponentManager::getInstance(); | |||||
| mcm->startModal (this); | |||||
| mcm->attachCallback (this, callback); | |||||
| if (deleteWhenDismissed) | |||||
| mcm->attachCallback (this, new ModalAutoDeleteCallback (this)); | |||||
| flags.currentlyModalFlag = true; | flags.currentlyModalFlag = true; | ||||
| setVisible (true); | setVisible (true); | ||||
| @@ -1906,7 +1906,9 @@ public: | |||||
| @see enterModalState, exitModalState, isCurrentlyModal, getCurrentlyModalComponent, | @see enterModalState, exitModalState, isCurrentlyModal, getCurrentlyModalComponent, | ||||
| isCurrentlyBlockedByAnotherModalComponent, ModalComponentManager | isCurrentlyBlockedByAnotherModalComponent, ModalComponentManager | ||||
| */ | */ | ||||
| #if JUCE_MODAL_LOOPS_PERMITTED | |||||
| int runModalLoop(); | int runModalLoop(); | ||||
| #endif | |||||
| /** Puts the component into a modal state. | /** Puts the component into a modal state. | ||||
| @@ -1923,10 +1925,15 @@ public: | |||||
| is called. If you pass an object in here, the system will take care of deleting it | is called. If you pass an object in here, the system will take care of deleting it | ||||
| later, after making the callback | later, after making the callback | ||||
| If deleteWhenDismissed is true, then when it is dismissed, the component will be | |||||
| deleted and then the callback will be called. (This will safely handle the situation | |||||
| where the component is deleted before its exitModalState() method is called). | |||||
| @see exitModalState, runModalLoop, ModalComponentManager::attachCallback | @see exitModalState, runModalLoop, ModalComponentManager::attachCallback | ||||
| */ | */ | ||||
| void enterModalState (bool takeKeyboardFocus = true, | void enterModalState (bool takeKeyboardFocus = true, | ||||
| ModalComponentManager::Callback* callback = 0); | |||||
| ModalComponentManager::Callback* callback = 0, | |||||
| bool deleteWhenDismissed = false); | |||||
| /** Ends a component's modal state. | /** Ends a component's modal state. | ||||
| @@ -39,14 +39,11 @@ BEGIN_JUCE_NAMESPACE | |||||
| class ModalComponentManager::ModalItem : public ComponentMovementWatcher | class ModalComponentManager::ModalItem : public ComponentMovementWatcher | ||||
| { | { | ||||
| public: | public: | ||||
| ModalItem (Component* const comp, Callback* const callback) | |||||
| ModalItem (Component* const comp) | |||||
| : ComponentMovementWatcher (comp), | : ComponentMovementWatcher (comp), | ||||
| component (comp), returnValue (0), isActive (true) | component (comp), returnValue (0), isActive (true) | ||||
| { | { | ||||
| jassert (comp != 0); | jassert (comp != 0); | ||||
| if (callback != 0) | |||||
| callbacks.add (callback); | |||||
| } | } | ||||
| void componentMovedOrResized (bool, bool) {} | void componentMovedOrResized (bool, bool) {} | ||||
| @@ -103,10 +100,10 @@ juce_ImplementSingleton_SingleThreaded (ModalComponentManager); | |||||
| //============================================================================== | //============================================================================== | ||||
| void ModalComponentManager::startModal (Component* component, Callback* callback) | |||||
| void ModalComponentManager::startModal (Component* component) | |||||
| { | { | ||||
| if (component != 0) | if (component != 0) | ||||
| stack.add (new ModalItem (component, callback)); | |||||
| stack.add (new ModalItem (component)); | |||||
| } | } | ||||
| void ModalComponentManager::attachCallback (Component* component, Callback* callback) | void ModalComponentManager::attachCallback (Component* component, Callback* callback) | ||||
| @@ -200,17 +197,13 @@ void ModalComponentManager::handleAsyncUpdate() | |||||
| for (int i = stack.size(); --i >= 0;) | for (int i = stack.size(); --i >= 0;) | ||||
| { | { | ||||
| const ModalItem* const item = stack.getUnchecked(i); | const ModalItem* const item = stack.getUnchecked(i); | ||||
| if (! item->isActive) | if (! item->isActive) | ||||
| { | { | ||||
| ScopedPointer<ModalItem> item (stack.removeAndReturn (i)); | |||||
| for (int j = item->callbacks.size(); --j >= 0;) | for (int j = item->callbacks.size(); --j >= 0;) | ||||
| { | |||||
| item->callbacks.getUnchecked(j)->modalStateFinished (item->returnValue); | item->callbacks.getUnchecked(j)->modalStateFinished (item->returnValue); | ||||
| if (! stack.contains (item)) | |||||
| break; | |||||
| } | |||||
| stack.removeObject (item); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -243,11 +236,11 @@ void ModalComponentManager::bringModalComponentsToFront() | |||||
| } | } | ||||
| } | } | ||||
| #if JUCE_MODAL_LOOPS_PERMITTED | |||||
| class ModalComponentManager::ReturnValueRetriever : public ModalComponentManager::Callback | class ModalComponentManager::ReturnValueRetriever : public ModalComponentManager::Callback | ||||
| { | { | ||||
| public: | public: | ||||
| ReturnValueRetriever (int& value_, bool& finished_) : value (value_), finished (finished_) {} | ReturnValueRetriever (int& value_, bool& finished_) : value (value_), finished (finished_) {} | ||||
| ~ReturnValueRetriever() {} | |||||
| void modalStateFinished (int returnValue) | void modalStateFinished (int returnValue) | ||||
| { | { | ||||
| @@ -293,6 +286,6 @@ int ModalComponentManager::runEventLoopForCurrentComponent() | |||||
| return returnValue; | return returnValue; | ||||
| } | } | ||||
| #endif | |||||
| END_JUCE_NAMESPACE | END_JUCE_NAMESPACE | ||||
| @@ -51,6 +51,9 @@ public: | |||||
| You can register a callback using Component::enterModalState() or | You can register a callback using Component::enterModalState() or | ||||
| ModalComponentManager::attachCallback(). | ModalComponentManager::attachCallback(). | ||||
| For some quick ways of creating callback objects, see the ModalCallbackFunction class. | |||||
| @see ModalCallbackFunction | |||||
| */ | */ | ||||
| class Callback | class Callback | ||||
| { | { | ||||
| @@ -109,10 +112,12 @@ public: | |||||
| /** Brings any modal components to the front. */ | /** Brings any modal components to the front. */ | ||||
| void bringModalComponentsToFront(); | void bringModalComponentsToFront(); | ||||
| #if JUCE_MODAL_LOOPS_PERMITTED | |||||
| /** Runs the event loop until the currently topmost modal component is dismissed, and | /** Runs the event loop until the currently topmost modal component is dismissed, and | ||||
| returns the exit code for that component. | returns the exit code for that component. | ||||
| */ | */ | ||||
| int runEventLoopForCurrentComponent(); | int runEventLoopForCurrentComponent(); | ||||
| #endif | |||||
| //============================================================================== | //============================================================================== | ||||
| juce_DeclareSingleton_SingleThreaded_Minimal (ModalComponentManager); | juce_DeclareSingleton_SingleThreaded_Minimal (ModalComponentManager); | ||||
| @@ -130,6 +135,7 @@ protected: | |||||
| void handleAsyncUpdate(); | void handleAsyncUpdate(); | ||||
| private: | private: | ||||
| //============================================================================== | |||||
| class ModalItem; | class ModalItem; | ||||
| class ReturnValueRetriever; | class ReturnValueRetriever; | ||||
| @@ -137,12 +143,227 @@ private: | |||||
| friend class OwnedArray <ModalItem>; | friend class OwnedArray <ModalItem>; | ||||
| OwnedArray <ModalItem> stack; | OwnedArray <ModalItem> stack; | ||||
| void startModal (Component* component, Callback* callback); | |||||
| void startModal (Component* component); | |||||
| void endModal (Component* component, int returnValue); | void endModal (Component* component, int returnValue); | ||||
| void endModal (Component* component); | void endModal (Component* component); | ||||
| JUCE_DECLARE_NON_COPYABLE (ModalComponentManager); | JUCE_DECLARE_NON_COPYABLE (ModalComponentManager); | ||||
| }; | }; | ||||
| //============================================================================== | |||||
| /** | |||||
| This class provides some handy utility methods for creating ModalComponentManager::Callback | |||||
| objects that will invoke a static function with some parameters when a modal component is dismissed. | |||||
| */ | |||||
| class ModalCallbackFunction | |||||
| { | |||||
| public: | |||||
| //============================================================================== | |||||
| /** This is a utility function to create a ModalComponentManager::Callback that will | |||||
| call a static function with a parameter. | |||||
| The function that you supply must take two parameters - the first being an int, which is | |||||
| the result code that was used when the modal component was dismissed, and the second | |||||
| can be a custom type. Note that this custom value will be copied and stored, so it must | |||||
| be a primitive type or a class that provides copy-by-value semantics. | |||||
| E.g. @code | |||||
| static void myCallbackFunction (int modalResult, double customValue) | |||||
| { | |||||
| if (modalResult == 1) | |||||
| doSomethingWith (customValue); | |||||
| } | |||||
| Component* someKindOfComp; | |||||
| ... | |||||
| someKindOfComp->enterModalState (ModalCallbackFunction::create (myCallbackFunction, 3.0)); | |||||
| @endcode | |||||
| @see ModalComponentManager::Callback | |||||
| */ | |||||
| template <typename ParamType> | |||||
| static ModalComponentManager::Callback* create (void (*functionToCall) (int, ParamType), | |||||
| ParamType parameterValue) | |||||
| { | |||||
| return new FunctionCaller1 <ParamType> (functionToCall, parameterValue); | |||||
| } | |||||
| //============================================================================== | |||||
| /** This is a utility function to create a ModalComponentManager::Callback that will | |||||
| call a static function with two custom parameters. | |||||
| The function that you supply must take three parameters - the first being an int, which is | |||||
| the result code that was used when the modal component was dismissed, and the next two are | |||||
| your custom types. Note that these custom values will be copied and stored, so they must | |||||
| be primitive types or classes that provide copy-by-value semantics. | |||||
| E.g. @code | |||||
| static void myCallbackFunction (int modalResult, double customValue1, String customValue2) | |||||
| { | |||||
| if (modalResult == 1) | |||||
| doSomethingWith (customValue1, customValue2); | |||||
| } | |||||
| Component* someKindOfComp; | |||||
| ... | |||||
| someKindOfComp->enterModalState (ModalCallbackFunction::create (myCallbackFunction, 3.0, String ("xyz"))); | |||||
| @endcode | |||||
| @see ModalComponentManager::Callback | |||||
| */ | |||||
| template <typename ParamType1, typename ParamType2> | |||||
| static ModalComponentManager::Callback* withParam (void (*functionToCall) (int, ParamType1, ParamType2), | |||||
| ParamType1 parameterValue1, | |||||
| ParamType2 parameterValue2) | |||||
| { | |||||
| return new FunctionCaller2 <ParamType1, ParamType2> (functionToCall, parameterValue1, parameterValue2); | |||||
| } | |||||
| //============================================================================== | |||||
| /** This is a utility function to create a ModalComponentManager::Callback that will | |||||
| call a static function with a component. | |||||
| The function that you supply must take two parameters - the first being an int, which is | |||||
| the result code that was used when the modal component was dismissed, and the second | |||||
| can be a Component class. The component will be stored as a WeakReference, so that if it gets | |||||
| deleted before this callback is invoked, the pointer that is passed to the function will be null. | |||||
| E.g. @code | |||||
| static void myCallbackFunction (int modalResult, Slider* mySlider) | |||||
| { | |||||
| if (modalResult == 1 && mySlider != 0) // (must check that mySlider isn't null in case it was deleted..) | |||||
| mySlider->setValue (0.0); | |||||
| } | |||||
| Component* someKindOfComp; | |||||
| Slider* mySlider; | |||||
| ... | |||||
| someKindOfComp->enterModalState (ModalCallbackFunction::forComponent (myCallbackFunction, mySlider)); | |||||
| @endcode | |||||
| @see ModalComponentManager::Callback | |||||
| */ | |||||
| template <class ComponentType> | |||||
| static ModalComponentManager::Callback* forComponent (void (*functionToCall) (int, ComponentType*), | |||||
| ComponentType* component) | |||||
| { | |||||
| return new ComponentCaller1 <ComponentType> (functionToCall, component); | |||||
| } | |||||
| //============================================================================== | |||||
| /** Creates a ModalComponentManager::Callback that will call a static function with a component. | |||||
| The function that you supply must take three parameters - the first being an int, which is | |||||
| the result code that was used when the modal component was dismissed, the second being a Component | |||||
| class, and the third being a custom type (which must be a primitive type or have copy-by-value semantics). | |||||
| The component will be stored as a WeakReference, so that if it gets deleted before this callback is | |||||
| invoked, the pointer that is passed into the function will be null. | |||||
| E.g. @code | |||||
| static void myCallbackFunction (int modalResult, Slider* mySlider, String customParam) | |||||
| { | |||||
| if (modalResult == 1 && mySlider != 0) // (must check that mySlider isn't null in case it was deleted..) | |||||
| mySlider->setName (customParam); | |||||
| } | |||||
| Component* someKindOfComp; | |||||
| Slider* mySlider; | |||||
| ... | |||||
| someKindOfComp->enterModalState (ModalCallbackFunction::forComponent (myCallbackFunction, mySlider, String ("hello"))); | |||||
| @endcode | |||||
| @see ModalComponentManager::Callback | |||||
| */ | |||||
| template <class ComponentType, typename ParamType> | |||||
| static ModalComponentManager::Callback* forComponent (void (*functionToCall) (int, ComponentType*, ParamType), | |||||
| ComponentType* component, | |||||
| ParamType param) | |||||
| { | |||||
| return new ComponentCaller2 <ComponentType, ParamType> (functionToCall, component, param); | |||||
| } | |||||
| private: | |||||
| //============================================================================== | |||||
| template <typename ParamType> | |||||
| class FunctionCaller1 : public ModalComponentManager::Callback | |||||
| { | |||||
| public: | |||||
| typedef void (*FunctionType) (int, ParamType); | |||||
| FunctionCaller1 (FunctionType& function_, ParamType& param_) | |||||
| : function (function_), param (param_) {} | |||||
| void modalStateFinished (int returnValue) { function (returnValue, param); } | |||||
| private: | |||||
| const FunctionType function; | |||||
| ParamType param; | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FunctionCaller1); | |||||
| }; | |||||
| template <typename ParamType1, typename ParamType2> | |||||
| class FunctionCaller2 : public ModalComponentManager::Callback | |||||
| { | |||||
| public: | |||||
| typedef void (*FunctionType) (int, ParamType1, ParamType2); | |||||
| FunctionCaller2 (FunctionType& function_, ParamType1& param1_, ParamType2& param2_) | |||||
| : function (function_), param1 (param1_), param2 (param2_) {} | |||||
| void modalStateFinished (int returnValue) { function (returnValue, param1, param2); } | |||||
| private: | |||||
| const FunctionType function; | |||||
| ParamType1 param1; | |||||
| ParamType2 param2; | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FunctionCaller2); | |||||
| }; | |||||
| template <typename ComponentType> | |||||
| class ComponentCaller1 : public ModalComponentManager::Callback | |||||
| { | |||||
| public: | |||||
| typedef void (*FunctionType) (int, ComponentType*); | |||||
| ComponentCaller1 (FunctionType& function_, ComponentType* comp_) | |||||
| : function (function_), comp (comp_) {} | |||||
| void modalStateFinished (int returnValue) | |||||
| { | |||||
| function (returnValue, static_cast <ComponentType*> (comp.get())); | |||||
| } | |||||
| private: | |||||
| const FunctionType function; | |||||
| WeakReference<Component> comp; | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComponentCaller1); | |||||
| }; | |||||
| template <typename ComponentType, typename ParamType1> | |||||
| class ComponentCaller2 : public ModalComponentManager::Callback | |||||
| { | |||||
| public: | |||||
| typedef void (*FunctionType) (int, ComponentType*, ParamType1); | |||||
| ComponentCaller2 (FunctionType& function_, ComponentType* comp_, ParamType1 param1_) | |||||
| : function (function_), comp (comp_), param1 (param1_) {} | |||||
| void modalStateFinished (int returnValue) | |||||
| { | |||||
| function (returnValue, static_cast <ComponentType*> (comp.get()), param1); | |||||
| } | |||||
| private: | |||||
| const FunctionType function; | |||||
| WeakReference<Component> comp; | |||||
| ParamType1 param1; | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComponentCaller2); | |||||
| }; | |||||
| ModalCallbackFunction(); | |||||
| ~ModalCallbackFunction(); | |||||
| JUCE_DECLARE_NON_COPYABLE (ModalCallbackFunction); | |||||
| }; | |||||
| #endif // __JUCE_MODALCOMPONENTMANAGER_JUCEHEADER__ | #endif // __JUCE_MODALCOMPONENTMANAGER_JUCEHEADER__ | ||||
| @@ -65,6 +65,19 @@ public: | |||||
| keyNum >= 0 ? getName() : String::empty); | keyNum >= 0 ? getName() : String::empty); | ||||
| } | } | ||||
| static void menuCallback (int result, ChangeKeyButton* button) | |||||
| { | |||||
| if (button != 0) | |||||
| { | |||||
| switch (result) | |||||
| { | |||||
| case 1: button->assignNewKey(); break; | |||||
| case 2: button->owner.getMappings().removeKeyPress (button->commandID, button->keyNum); break; | |||||
| default: break; | |||||
| } | |||||
| } | |||||
| } | |||||
| void clicked() | void clicked() | ||||
| { | { | ||||
| if (keyNum >= 0) | if (keyNum >= 0) | ||||
| @@ -75,12 +88,8 @@ public: | |||||
| m.addSeparator(); | m.addSeparator(); | ||||
| m.addItem (2, TRANS("remove this key-mapping")); | m.addItem (2, TRANS("remove this key-mapping")); | ||||
| switch (m.show()) | |||||
| { | |||||
| case 1: assignNewKey(); break; | |||||
| case 2: owner.getMappings().removeKeyPress (commandID, keyNum); break; | |||||
| default: break; | |||||
| } | |||||
| m.showMenuAsync (PopupMenu::Options(), | |||||
| ModalCallbackFunction::forComponent (menuCallback, this)); | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| @@ -150,42 +159,65 @@ public: | |||||
| JUCE_DECLARE_NON_COPYABLE (KeyEntryWindow); | JUCE_DECLARE_NON_COPYABLE (KeyEntryWindow); | ||||
| }; | }; | ||||
| void assignNewKey() | |||||
| static void assignNewKeyCallback (int result, ChangeKeyButton* button, KeyPress newKey) | |||||
| { | { | ||||
| KeyEntryWindow entryWindow (owner); | |||||
| if (result != 0 && button != 0) | |||||
| button->setNewKey (newKey, true); | |||||
| } | |||||
| if (entryWindow.runModalLoop() != 0) | |||||
| void setNewKey (const KeyPress& newKey, bool dontAskUser) | |||||
| { | |||||
| if (newKey.isValid()) | |||||
| { | { | ||||
| entryWindow.setVisible (false); | |||||
| const CommandID previousCommand = owner.getMappings().findCommandForKeyPress (newKey); | |||||
| if (entryWindow.lastPress.isValid()) | |||||
| if (previousCommand == 0 || dontAskUser) | |||||
| { | { | ||||
| const CommandID previousCommand = owner.getMappings().findCommandForKeyPress (entryWindow.lastPress); | |||||
| if (previousCommand == 0 | |||||
| || AlertWindow::showOkCancelBox (AlertWindow::WarningIcon, | |||||
| TRANS("Change key-mapping"), | |||||
| TRANS("This key is already assigned to the command \"") | |||||
| + owner.getMappings().getCommandManager()->getNameOfCommand (previousCommand) | |||||
| + TRANS("\"\n\nDo you want to re-assign it to this new command instead?"), | |||||
| TRANS("Re-assign"), | |||||
| TRANS("Cancel"))) | |||||
| { | |||||
| owner.getMappings().removeKeyPress (entryWindow.lastPress); | |||||
| owner.getMappings().removeKeyPress (newKey); | |||||
| if (keyNum >= 0) | |||||
| owner.getMappings().removeKeyPress (commandID, keyNum); | |||||
| if (keyNum >= 0) | |||||
| owner.getMappings().removeKeyPress (commandID, keyNum); | |||||
| owner.getMappings().addKeyPress (commandID, entryWindow.lastPress, keyNum); | |||||
| } | |||||
| owner.getMappings().addKeyPress (commandID, newKey, keyNum); | |||||
| } | } | ||||
| else | |||||
| { | |||||
| AlertWindow::showOkCancelBox (AlertWindow::WarningIcon, | |||||
| TRANS("Change key-mapping"), | |||||
| TRANS("This key is already assigned to the command \"") | |||||
| + owner.getMappings().getCommandManager()->getNameOfCommand (previousCommand) | |||||
| + TRANS("\"\n\nDo you want to re-assign it to this new command instead?"), | |||||
| TRANS("Re-assign"), | |||||
| TRANS("Cancel"), | |||||
| this, | |||||
| ModalCallbackFunction::forComponent (assignNewKeyCallback, | |||||
| this, KeyPress (newKey))); | |||||
| } | |||||
| } | |||||
| } | |||||
| static void keyChosen (int result, ChangeKeyButton* button) | |||||
| { | |||||
| if (result != 0 && button != 0 && button->currentKeyEntryWindow != 0) | |||||
| { | |||||
| button->currentKeyEntryWindow->setVisible (false); | |||||
| button->setNewKey (button->currentKeyEntryWindow->lastPress, false); | |||||
| } | } | ||||
| button->currentKeyEntryWindow = 0; | |||||
| } | |||||
| void assignNewKey() | |||||
| { | |||||
| currentKeyEntryWindow = new KeyEntryWindow (owner); | |||||
| currentKeyEntryWindow->enterModalState (true, ModalCallbackFunction::forComponent (keyChosen, this)); | |||||
| } | } | ||||
| private: | private: | ||||
| KeyMappingEditorComponent& owner; | KeyMappingEditorComponent& owner; | ||||
| const CommandID commandID; | const CommandID commandID; | ||||
| const int keyNum; | const int keyNum; | ||||
| ScopedPointer<KeyEntryWindow> currentKeyEntryWindow; | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChangeKeyButton); | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChangeKeyButton); | ||||
| }; | }; | ||||
| @@ -372,15 +404,21 @@ public: | |||||
| } | } | ||||
| } | } | ||||
| static void resetToDefaultsCallback (int result, KeyMappingEditorComponent* owner) | |||||
| { | |||||
| if (result != 0 && owner != 0) | |||||
| owner->getMappings().resetToDefaultMappings(); | |||||
| } | |||||
| void buttonClicked (Button*) | void buttonClicked (Button*) | ||||
| { | { | ||||
| if (AlertWindow::showOkCancelBox (AlertWindow::QuestionIcon, | |||||
| TRANS("Reset to defaults"), | |||||
| TRANS("Are you sure you want to reset all the key-mappings to their default state?"), | |||||
| TRANS("Reset"))) | |||||
| { | |||||
| owner.getMappings().resetToDefaultMappings(); | |||||
| } | |||||
| AlertWindow::showOkCancelBox (AlertWindow::QuestionIcon, | |||||
| TRANS("Reset to defaults"), | |||||
| TRANS("Are you sure you want to reset all the key-mappings to their default state?"), | |||||
| TRANS("Reset"), | |||||
| String::empty, | |||||
| &owner, | |||||
| ModalCallbackFunction::forComponent (resetToDefaultsCallback, &owner)); | |||||
| } | } | ||||
| private: | private: | ||||
| @@ -474,6 +474,12 @@ void TabbedButtonBar::setTabBackgroundColour (const int tabIndex, const Colour& | |||||
| } | } | ||||
| } | } | ||||
| void TabbedButtonBar::extraItemsMenuCallback (int result, TabbedButtonBar* bar) | |||||
| { | |||||
| if (bar != 0 && result > 0) | |||||
| bar->setCurrentTabIndex (result - 1); | |||||
| } | |||||
| void TabbedButtonBar::showExtraItemsMenu() | void TabbedButtonBar::showExtraItemsMenu() | ||||
| { | { | ||||
| PopupMenu m; | PopupMenu m; | ||||
| @@ -486,10 +492,8 @@ void TabbedButtonBar::showExtraItemsMenu() | |||||
| m.addItem (i + 1, tab->name, true, i == currentTabIndex); | m.addItem (i + 1, tab->name, true, i == currentTabIndex); | ||||
| } | } | ||||
| const int res = m.showAt (extraTabsButton); | |||||
| if (res != 0) | |||||
| setCurrentTabIndex (res - 1); | |||||
| m.showMenuAsync (PopupMenu::Options().withTargetComponent (extraTabsButton), | |||||
| ModalCallbackFunction::forComponent (extraItemsMenuCallback, this)); | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -300,6 +300,7 @@ private: | |||||
| ScopedPointer<Button> extraTabsButton; | ScopedPointer<Button> extraTabsButton; | ||||
| void showExtraItemsMenu(); | void showExtraItemsMenu(); | ||||
| static void extraItemsMenuCallback (int, TabbedButtonBar*); | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TabbedButtonBar); | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TabbedButtonBar); | ||||
| }; | }; | ||||
| @@ -172,27 +172,6 @@ void MenuBarComponent::updateItemUnderMouse (int x, int y) | |||||
| setItemUnderMouse (getItemAt (x, y)); | setItemUnderMouse (getItemAt (x, y)); | ||||
| } | } | ||||
| class MenuBarComponent::AsyncCallback : public ModalComponentManager::Callback | |||||
| { | |||||
| public: | |||||
| AsyncCallback (MenuBarComponent* const bar_, const int topLevelIndex_) | |||||
| : bar (bar_), topLevelIndex (topLevelIndex_) | |||||
| { | |||||
| } | |||||
| void modalStateFinished (int returnValue) | |||||
| { | |||||
| if (bar != 0) | |||||
| bar->menuDismissed (topLevelIndex, returnValue); | |||||
| } | |||||
| private: | |||||
| Component::SafePointer<MenuBarComponent> bar; | |||||
| const int topLevelIndex; | |||||
| JUCE_DECLARE_NON_COPYABLE (AsyncCallback); | |||||
| }; | |||||
| void MenuBarComponent::showMenu (int index) | void MenuBarComponent::showMenu (int index) | ||||
| { | { | ||||
| if (index != currentPopupIndex) | if (index != currentPopupIndex) | ||||
| @@ -213,13 +192,20 @@ void MenuBarComponent::showMenu (int index) | |||||
| const Rectangle<int> itemPos (xPositions [index], 0, xPositions [index + 1] - xPositions [index], getHeight()); | const Rectangle<int> itemPos (xPositions [index], 0, xPositions [index + 1] - xPositions [index], getHeight()); | ||||
| m.showMenu (localAreaToGlobal (itemPos), | |||||
| 0, itemPos.getWidth(), 0, 0, this, | |||||
| new AsyncCallback (this, index)); | |||||
| m.showMenuAsync (PopupMenu::Options().withTargetComponent (this) | |||||
| .withTargetScreenArea (localAreaToGlobal (itemPos)) | |||||
| .withMinimumWidth (itemPos.getWidth()), | |||||
| ModalCallbackFunction::forComponent (menuBarMenuDismissedCallback, this, index)); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| void MenuBarComponent::menuBarMenuDismissedCallback (int result, MenuBarComponent* bar, int topLevelIndex) | |||||
| { | |||||
| if (bar != 0) | |||||
| bar->menuDismissed (topLevelIndex, result); | |||||
| } | |||||
| void MenuBarComponent::menuDismissed (int topLevelIndex, int itemId) | void MenuBarComponent::menuDismissed (int topLevelIndex, int itemId) | ||||
| { | { | ||||
| topLevelIndexClicked = topLevelIndex; | topLevelIndexClicked = topLevelIndex; | ||||
| @@ -101,8 +101,6 @@ public: | |||||
| private: | private: | ||||
| //============================================================================== | //============================================================================== | ||||
| class AsyncCallback; | |||||
| friend class AsyncCallback; | |||||
| MenuBarModel* model; | MenuBarModel* model; | ||||
| StringArray menuNames; | StringArray menuNames; | ||||
| @@ -117,6 +115,7 @@ private: | |||||
| void timerCallback(); | void timerCallback(); | ||||
| void repaintMenuItem (int index); | void repaintMenuItem (int index); | ||||
| void menuDismissed (int topLevelIndex, int itemId); | void menuDismissed (int topLevelIndex, int itemId); | ||||
| static void menuBarMenuDismissedCallback (int, MenuBarComponent*, int); | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MenuBarComponent); | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MenuBarComponent); | ||||
| }; | }; | ||||
| @@ -233,6 +233,8 @@ namespace PopupMenuSettings | |||||
| const int borderSize = 2; | const int borderSize = 2; | ||||
| const int timerInterval = 50; | const int timerInterval = 50; | ||||
| const int dismissCommandId = 0x6287345f; | const int dismissCommandId = 0x6287345f; | ||||
| static bool menuWasHiddenBecauseOfAppChange = false; | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -624,7 +626,7 @@ public: | |||||
| { | { | ||||
| if (now > lastFocused + 10) | if (now > lastFocused + 10) | ||||
| { | { | ||||
| wasHiddenBecauseOfAppChange() = true; | |||||
| PopupMenuSettings::menuWasHiddenBecauseOfAppChange = true; | |||||
| dismissMenu (0); | dismissMenu (0); | ||||
| return; // may have been deleted by the previous call.. | return; // may have been deleted by the previous call.. | ||||
| @@ -659,12 +661,6 @@ public: | |||||
| return activeMenuWindows; | return activeMenuWindows; | ||||
| } | } | ||||
| static bool& wasHiddenBecauseOfAppChange() throw() | |||||
| { | |||||
| static bool b = false; | |||||
| return b; | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| private: | private: | ||||
| Window* owner; | Window* owner; | ||||
| @@ -1388,14 +1384,83 @@ void PopupMenu::addSectionHeader (const String& title) | |||||
| addCustomItem (0X4734a34f, new HeaderItemComponent (title)); | addCustomItem (0X4734a34f, new HeaderItemComponent (title)); | ||||
| } | } | ||||
| //============================================================================== | |||||
| PopupMenu::Options::Options() | |||||
| : targetComponent (0), | |||||
| visibleItemID (0), | |||||
| minWidth (0), | |||||
| maxColumns (0), | |||||
| standardHeight (0) | |||||
| { | |||||
| targetArea.setPosition (Desktop::getMousePosition()); | |||||
| } | |||||
| const PopupMenu::Options PopupMenu::Options::withTargetComponent (Component* comp) const | |||||
| { | |||||
| Options o (*this); | |||||
| o.targetComponent = comp; | |||||
| if (comp != 0) | |||||
| o.targetArea = comp->getScreenBounds(); | |||||
| return o; | |||||
| } | |||||
| const PopupMenu::Options PopupMenu::Options::withTargetScreenArea (const Rectangle<int>& area) const | |||||
| { | |||||
| Options o (*this); | |||||
| o.targetArea = area; | |||||
| return o; | |||||
| } | |||||
| const PopupMenu::Options PopupMenu::Options::withMinimumWidth (int w) const | |||||
| { | |||||
| Options o (*this); | |||||
| o.minWidth = w; | |||||
| return o; | |||||
| } | |||||
| const PopupMenu::Options PopupMenu::Options::withMaximumNumColumns (int cols) const | |||||
| { | |||||
| Options o (*this); | |||||
| o.maxColumns = cols; | |||||
| return o; | |||||
| } | |||||
| const PopupMenu::Options PopupMenu::Options::withStandardItemHeight (int height) const | |||||
| { | |||||
| Options o (*this); | |||||
| o.standardHeight = height; | |||||
| return o; | |||||
| } | |||||
| const PopupMenu::Options PopupMenu::Options::withItemThatMustBeVisible (int idOfItemToBeVisible) const | |||||
| { | |||||
| Options o (*this); | |||||
| o.visibleItemID = idOfItemToBeVisible; | |||||
| return o; | |||||
| } | |||||
| Component* PopupMenu::createWindow (const Options& options, | |||||
| ApplicationCommandManager** managerOfChosenCommand) const | |||||
| { | |||||
| return Window::create (*this, ModifierKeys::getCurrentModifiers().isAnyMouseButtonDown(), | |||||
| 0, options.targetArea, options.minWidth, options.maxColumns > 0 ? options.maxColumns : 7, | |||||
| options.standardHeight, ! options.targetArea.isEmpty(), options.visibleItemID, | |||||
| managerOfChosenCommand, options.targetComponent); | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| // This invokes any command manager commands and deletes the menu window when it is dismissed | // This invokes any command manager commands and deletes the menu window when it is dismissed | ||||
| class PopupMenuCompletionCallback : public ModalComponentManager::Callback | class PopupMenuCompletionCallback : public ModalComponentManager::Callback | ||||
| { | { | ||||
| public: | public: | ||||
| PopupMenuCompletionCallback() | PopupMenuCompletionCallback() | ||||
| : managerOfChosenCommand (0) | |||||
| : managerOfChosenCommand (0), | |||||
| prevFocused (Component::getCurrentlyFocusedComponent()), | |||||
| prevTopLevel (prevFocused != 0 ? prevFocused->getTopLevelComponent() : 0) | |||||
| { | { | ||||
| PopupMenuSettings::menuWasHiddenBecauseOfAppChange = false; | |||||
| } | } | ||||
| void modalStateFinished (int result) | void modalStateFinished (int result) | ||||
| @@ -1410,74 +1475,74 @@ public: | |||||
| // (this would be the place to fade out the component, if that's what's required) | // (this would be the place to fade out the component, if that's what's required) | ||||
| component = 0; | component = 0; | ||||
| if (! PopupMenuSettings::menuWasHiddenBecauseOfAppChange) | |||||
| { | |||||
| if (prevTopLevel != 0) | |||||
| prevTopLevel->toFront (true); | |||||
| if (prevFocused != 0) | |||||
| prevFocused->grabKeyboardFocus(); | |||||
| } | |||||
| } | } | ||||
| ApplicationCommandManager* managerOfChosenCommand; | ApplicationCommandManager* managerOfChosenCommand; | ||||
| ScopedPointer<Component> component; | ScopedPointer<Component> component; | ||||
| WeakReference<Component> prevFocused, prevTopLevel; | |||||
| private: | private: | ||||
| JUCE_DECLARE_NON_COPYABLE (PopupMenuCompletionCallback); | JUCE_DECLARE_NON_COPYABLE (PopupMenuCompletionCallback); | ||||
| }; | }; | ||||
| int PopupMenu::showMenu (const Rectangle<int>& target, | |||||
| const int itemIdThatMustBeVisible, | |||||
| const int minimumWidth, | |||||
| const int maximumNumColumns, | |||||
| const int standardItemHeight, | |||||
| Component* const componentAttachedTo, | |||||
| ModalComponentManager::Callback* userCallback) | |||||
| int PopupMenu::showWithOptionalCallback (const Options& options, ModalComponentManager::Callback* const userCallback, | |||||
| const bool canBeModal) | |||||
| { | { | ||||
| ScopedPointer<ModalComponentManager::Callback> userCallbackDeleter (userCallback); | ScopedPointer<ModalComponentManager::Callback> userCallbackDeleter (userCallback); | ||||
| ScopedPointer<PopupMenuCompletionCallback> callback (new PopupMenuCompletionCallback()); | |||||
| WeakReference<Component> prevFocused (Component::getCurrentlyFocusedComponent()); | |||||
| WeakReference<Component> prevTopLevel ((prevFocused != 0) ? prevFocused->getTopLevelComponent() : 0); | |||||
| Window::wasHiddenBecauseOfAppChange() = false; | |||||
| PopupMenuCompletionCallback* callback = new PopupMenuCompletionCallback(); | |||||
| ScopedPointer<PopupMenuCompletionCallback> callbackDeleter (callback); | |||||
| callback->component = Window::create (*this, ModifierKeys::getCurrentModifiers().isAnyMouseButtonDown(), | |||||
| 0, target, minimumWidth, maximumNumColumns > 0 ? maximumNumColumns : 7, | |||||
| standardItemHeight, ! target.isEmpty(), itemIdThatMustBeVisible, | |||||
| &callback->managerOfChosenCommand, componentAttachedTo); | |||||
| if (callback->component == 0) | |||||
| Component* window = createWindow (options, &(callback->managerOfChosenCommand)); | |||||
| if (window == 0) | |||||
| return 0; | return 0; | ||||
| callback->component->enterModalState (false, userCallbackDeleter.release()); | |||||
| callback->component->toFront (false); // need to do this after making it modal, or it could | |||||
| // be stuck behind other comps that are already modal.. | |||||
| callback->component = window; | |||||
| ModalComponentManager::getInstance()->attachCallback (callback->component, callback); | |||||
| window->enterModalState (false, userCallbackDeleter.release()); | |||||
| ModalComponentManager::getInstance()->attachCallback (window, callback.release()); | |||||
| callbackDeleter.release(); | |||||
| window->toFront (false); // need to do this after making it modal, or it could | |||||
| // be stuck behind other comps that are already modal.. | |||||
| if (userCallback != 0) | |||||
| return 0; | |||||
| const int result = callback->component->runModalLoop(); | |||||
| return (userCallback == 0 && canBeModal) ? window->runModalLoop() : 0; | |||||
| } | |||||
| if (! Window::wasHiddenBecauseOfAppChange()) | |||||
| { | |||||
| if (prevTopLevel != 0) | |||||
| prevTopLevel->toFront (true); | |||||
| //============================================================================== | |||||
| #if JUCE_MODAL_LOOPS_PERMITTED | |||||
| int PopupMenu::showMenu (const Options& options) | |||||
| { | |||||
| return showWithOptionalCallback (options, 0, true); | |||||
| } | |||||
| #endif | |||||
| if (prevFocused != 0) | |||||
| prevFocused->grabKeyboardFocus(); | |||||
| } | |||||
| void PopupMenu::showMenuAsync (const Options& options, ModalComponentManager::Callback* userCallback) | |||||
| { | |||||
| #if ! JUCE_MODAL_LOOPS_PERMITTED | |||||
| jassert (userCallback != 0); | |||||
| #endif | |||||
| return result; | |||||
| showWithOptionalCallback (options, userCallback, false); | |||||
| } | } | ||||
| //============================================================================== | |||||
| int PopupMenu::show (const int itemIdThatMustBeVisible, | int PopupMenu::show (const int itemIdThatMustBeVisible, | ||||
| const int minimumWidth, const int maximumNumColumns, | const int minimumWidth, const int maximumNumColumns, | ||||
| const int standardItemHeight, | const int standardItemHeight, | ||||
| ModalComponentManager::Callback* callback) | ModalComponentManager::Callback* callback) | ||||
| { | { | ||||
| return showMenu (Rectangle<int>().withPosition (Desktop::getMousePosition()), | |||||
| itemIdThatMustBeVisible, minimumWidth, maximumNumColumns, | |||||
| standardItemHeight, 0, callback); | |||||
| return showWithOptionalCallback (Options().withItemThatMustBeVisible (itemIdThatMustBeVisible) | |||||
| .withMinimumWidth (minimumWidth) | |||||
| .withMaximumNumColumns (maximumNumColumns) | |||||
| .withStandardItemHeight (standardItemHeight), | |||||
| callback, true); | |||||
| } | } | ||||
| int PopupMenu::showAt (const Rectangle<int>& screenAreaToAttachTo, | int PopupMenu::showAt (const Rectangle<int>& screenAreaToAttachTo, | ||||
| @@ -1486,9 +1551,12 @@ int PopupMenu::showAt (const Rectangle<int>& screenAreaToAttachTo, | |||||
| const int standardItemHeight, | const int standardItemHeight, | ||||
| ModalComponentManager::Callback* callback) | ModalComponentManager::Callback* callback) | ||||
| { | { | ||||
| return showMenu (screenAreaToAttachTo, | |||||
| itemIdThatMustBeVisible, minimumWidth, maximumNumColumns, | |||||
| standardItemHeight, 0, callback); | |||||
| return showWithOptionalCallback (Options().withTargetScreenArea (screenAreaToAttachTo) | |||||
| .withItemThatMustBeVisible (itemIdThatMustBeVisible) | |||||
| .withMinimumWidth (minimumWidth) | |||||
| .withMaximumNumColumns (maximumNumColumns) | |||||
| .withStandardItemHeight (standardItemHeight), | |||||
| callback, true); | |||||
| } | } | ||||
| int PopupMenu::showAt (Component* componentToAttachTo, | int PopupMenu::showAt (Component* componentToAttachTo, | ||||
| @@ -1497,17 +1565,15 @@ int PopupMenu::showAt (Component* componentToAttachTo, | |||||
| const int standardItemHeight, | const int standardItemHeight, | ||||
| ModalComponentManager::Callback* callback) | ModalComponentManager::Callback* callback) | ||||
| { | { | ||||
| Options options (Options().withItemThatMustBeVisible (itemIdThatMustBeVisible) | |||||
| .withMinimumWidth (minimumWidth) | |||||
| .withMaximumNumColumns (maximumNumColumns) | |||||
| .withStandardItemHeight (standardItemHeight)); | |||||
| if (componentToAttachTo != 0) | if (componentToAttachTo != 0) | ||||
| { | |||||
| return showMenu (componentToAttachTo->getScreenBounds(), | |||||
| itemIdThatMustBeVisible, minimumWidth, maximumNumColumns, | |||||
| standardItemHeight, componentToAttachTo, callback); | |||||
| } | |||||
| else | |||||
| { | |||||
| return show (itemIdThatMustBeVisible, minimumWidth, maximumNumColumns, | |||||
| standardItemHeight, callback); | |||||
| } | |||||
| options = options.withTargetComponent (componentToAttachTo); | |||||
| return showWithOptionalCallback (options, callback, true); | |||||
| } | } | ||||
| bool JUCE_CALLTYPE PopupMenu::dismissAllActiveMenus() | bool JUCE_CALLTYPE PopupMenu::dismissAllActiveMenus() | ||||
| @@ -206,6 +206,38 @@ public: | |||||
| bool containsAnyActiveItems() const throw(); | bool containsAnyActiveItems() const throw(); | ||||
| //============================================================================== | //============================================================================== | ||||
| /** Class used to create a set of options to pass to the show() method. | |||||
| You can chain together a series of calls to this class's methods to create | |||||
| a set of whatever options you want to specify. | |||||
| E.g. @code | |||||
| PopupMenu menu; | |||||
| ... | |||||
| menu.showMenu (PopupMenu::Options().withMaximumWidth (100), | |||||
| .withMaximumNumColumns (3) | |||||
| .withTargetComponent (myComp)); | |||||
| @endcode | |||||
| */ | |||||
| class JUCE_API Options | |||||
| { | |||||
| public: | |||||
| Options(); | |||||
| const Options withTargetComponent (Component* targetComponent) const; | |||||
| const Options withTargetScreenArea (const Rectangle<int>& targetArea) const; | |||||
| const Options withMinimumWidth (int minWidth) const; | |||||
| const Options withMaximumNumColumns (int maxNumColumns) const; | |||||
| const Options withStandardItemHeight (int standardHeight) const; | |||||
| const Options withItemThatMustBeVisible (int idOfItemToBeVisible) const; | |||||
| private: | |||||
| friend class PopupMenu; | |||||
| Rectangle<int> targetArea; | |||||
| Component* targetComponent; | |||||
| int visibleItemID, minWidth, maxColumns, standardHeight; | |||||
| }; | |||||
| //============================================================================== | |||||
| #if JUCE_MODAL_LOOPS_PERMITTED | |||||
| /** Displays the menu and waits for the user to pick something. | /** Displays the menu and waits for the user to pick something. | ||||
| This will display the menu modally, and return the ID of the item that the | This will display the menu modally, and return the ID of the item that the | ||||
| @@ -279,6 +311,15 @@ public: | |||||
| int standardItemHeight = 0, | int standardItemHeight = 0, | ||||
| ModalComponentManager::Callback* callback = 0); | ModalComponentManager::Callback* callback = 0); | ||||
| /** Displays and runs the menu modally, with a set of options. | |||||
| */ | |||||
| int showMenu (const Options& options); | |||||
| #endif | |||||
| /** Runs the menu asynchronously, with a user-provided callback that will receive the result. */ | |||||
| void showMenuAsync (const Options& options, | |||||
| ModalComponentManager::Callback* callback); | |||||
| //============================================================================== | //============================================================================== | ||||
| /** Closes any menus that are currently open. | /** Closes any menus that are currently open. | ||||
| @@ -448,10 +489,8 @@ private: | |||||
| bool separatorPending; | bool separatorPending; | ||||
| void addSeparatorIfPending(); | void addSeparatorIfPending(); | ||||
| int showMenu (const Rectangle<int>& target, int itemIdThatMustBeVisible, | |||||
| int minimumWidth, int maximumNumColumns, int standardItemHeight, | |||||
| Component* componentAttachedTo, ModalComponentManager::Callback* callback); | |||||
| Component* createWindow (const Options&, ApplicationCommandManager**) const; | |||||
| int showWithOptionalCallback (const Options&, ModalComponentManager::Callback*, bool); | |||||
| JUCE_LEAK_DETECTOR (PopupMenu); | JUCE_LEAK_DETECTOR (PopupMenu); | ||||
| }; | }; | ||||
| @@ -368,9 +368,9 @@ public: | |||||
| if (error.isNotEmpty()) | if (error.isNotEmpty()) | ||||
| { | { | ||||
| AlertWindow::showMessageBox (AlertWindow::WarningIcon, | |||||
| "Error when trying to open audio device!", | |||||
| error); | |||||
| AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, | |||||
| "Error when trying to open audio device!", | |||||
| error); | |||||
| } | } | ||||
| } | } | ||||
| @@ -83,7 +83,7 @@ void PreferencesPanel::addSettingsPage (const String& title, const void* imageDa | |||||
| void PreferencesPanel::showInDialogBox (const String& dialogTitle, int dialogWidth, int dialogHeight, const Colour& backgroundColour) | void PreferencesPanel::showInDialogBox (const String& dialogTitle, int dialogWidth, int dialogHeight, const Colour& backgroundColour) | ||||
| { | { | ||||
| setSize (dialogWidth, dialogHeight); | setSize (dialogWidth, dialogHeight); | ||||
| DialogWindow::showModalDialog (dialogTitle, this, 0, backgroundColour, false); | |||||
| DialogWindow::showDialog (dialogTitle, this, 0, backgroundColour, false); | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -608,24 +608,29 @@ class AlertWindowInfo | |||||
| { | { | ||||
| public: | public: | ||||
| AlertWindowInfo (const String& title_, const String& message_, Component* component, | AlertWindowInfo (const String& title_, const String& message_, Component* component, | ||||
| AlertWindow::AlertIconType iconType_, int numButtons_) | |||||
| AlertWindow::AlertIconType iconType_, int numButtons_, | |||||
| ModalComponentManager::Callback* callback_, bool modal_) | |||||
| : title (title_), message (message_), iconType (iconType_), | : title (title_), message (message_), iconType (iconType_), | ||||
| numButtons (numButtons_), returnValue (0), associatedComponent (component) | |||||
| numButtons (numButtons_), returnValue (0), associatedComponent (component), | |||||
| callback (callback_), modal (modal_) | |||||
| { | { | ||||
| } | } | ||||
| String title, message, button1, button2, button3; | String title, message, button1, button2, button3; | ||||
| AlertWindow::AlertIconType iconType; | |||||
| int numButtons, returnValue; | |||||
| WeakReference<Component> associatedComponent; | |||||
| int showModal() const | |||||
| int invoke() const | |||||
| { | { | ||||
| MessageManager::getInstance()->callFunctionOnMessageThread (showCallback, (void*) this); | MessageManager::getInstance()->callFunctionOnMessageThread (showCallback, (void*) this); | ||||
| return returnValue; | return returnValue; | ||||
| } | } | ||||
| private: | private: | ||||
| AlertWindow::AlertIconType iconType; | |||||
| int numButtons, returnValue; | |||||
| WeakReference<Component> associatedComponent; | |||||
| ModalComponentManager::Callback* callback; | |||||
| bool modal; | |||||
| void show() | void show() | ||||
| { | { | ||||
| LookAndFeel& lf = associatedComponent != 0 ? associatedComponent->getLookAndFeel() | LookAndFeel& lf = associatedComponent != 0 ? associatedComponent->getLookAndFeel() | ||||
| @@ -636,7 +641,17 @@ private: | |||||
| jassert (alertBox != 0); // you have to return one of these! | jassert (alertBox != 0); // you have to return one of these! | ||||
| returnValue = alertBox->runModalLoop(); | |||||
| #if JUCE_MODAL_LOOPS_PERMITTED | |||||
| if (modal) | |||||
| { | |||||
| returnValue = alertBox->runModalLoop(); | |||||
| } | |||||
| else | |||||
| #endif | |||||
| { | |||||
| alertBox->enterModalState (true, callback, true); | |||||
| alertBox.release(); | |||||
| } | |||||
| } | } | ||||
| static void* showCallback (void* userData) | static void* showCallback (void* userData) | ||||
| @@ -646,16 +661,30 @@ private: | |||||
| } | } | ||||
| }; | }; | ||||
| #if JUCE_MODAL_LOOPS_PERMITTED | |||||
| void AlertWindow::showMessageBox (AlertIconType iconType, | void AlertWindow::showMessageBox (AlertIconType iconType, | ||||
| const String& title, | const String& title, | ||||
| const String& message, | const String& message, | ||||
| const String& buttonText, | const String& buttonText, | ||||
| Component* associatedComponent) | Component* associatedComponent) | ||||
| { | { | ||||
| AlertWindowInfo info (title, message, associatedComponent, iconType, 1); | |||||
| AlertWindowInfo info (title, message, associatedComponent, iconType, 1, 0, true); | |||||
| info.button1 = buttonText.isEmpty() ? TRANS("ok") : buttonText; | |||||
| info.invoke(); | |||||
| } | |||||
| #endif | |||||
| void AlertWindow::showMessageBoxAsync (AlertIconType iconType, | |||||
| const String& title, | |||||
| const String& message, | |||||
| const String& buttonText, | |||||
| Component* associatedComponent) | |||||
| { | |||||
| AlertWindowInfo info (title, message, associatedComponent, iconType, 1, 0, false); | |||||
| info.button1 = buttonText.isEmpty() ? TRANS("ok") : buttonText; | info.button1 = buttonText.isEmpty() ? TRANS("ok") : buttonText; | ||||
| info.showModal(); | |||||
| info.invoke(); | |||||
| } | } | ||||
| bool AlertWindow::showOkCancelBox (AlertIconType iconType, | bool AlertWindow::showOkCancelBox (AlertIconType iconType, | ||||
| @@ -663,13 +692,14 @@ bool AlertWindow::showOkCancelBox (AlertIconType iconType, | |||||
| const String& message, | const String& message, | ||||
| const String& button1Text, | const String& button1Text, | ||||
| const String& button2Text, | const String& button2Text, | ||||
| Component* associatedComponent) | |||||
| Component* associatedComponent, | |||||
| ModalComponentManager::Callback* callback) | |||||
| { | { | ||||
| AlertWindowInfo info (title, message, associatedComponent, iconType, 2); | |||||
| AlertWindowInfo info (title, message, associatedComponent, iconType, 2, callback, callback == 0); | |||||
| info.button1 = button1Text.isEmpty() ? TRANS("ok") : button1Text; | info.button1 = button1Text.isEmpty() ? TRANS("ok") : button1Text; | ||||
| info.button2 = button2Text.isEmpty() ? TRANS("cancel") : button2Text; | info.button2 = button2Text.isEmpty() ? TRANS("cancel") : button2Text; | ||||
| return info.showModal() != 0; | |||||
| return info.invoke() != 0; | |||||
| } | } | ||||
| int AlertWindow::showYesNoCancelBox (AlertIconType iconType, | int AlertWindow::showYesNoCancelBox (AlertIconType iconType, | ||||
| @@ -678,14 +708,15 @@ int AlertWindow::showYesNoCancelBox (AlertIconType iconType, | |||||
| const String& button1Text, | const String& button1Text, | ||||
| const String& button2Text, | const String& button2Text, | ||||
| const String& button3Text, | const String& button3Text, | ||||
| Component* associatedComponent) | |||||
| Component* associatedComponent, | |||||
| ModalComponentManager::Callback* callback) | |||||
| { | { | ||||
| AlertWindowInfo info (title, message, associatedComponent, iconType, 3); | |||||
| AlertWindowInfo info (title, message, associatedComponent, iconType, 3, callback, callback == 0); | |||||
| info.button1 = button1Text.isEmpty() ? TRANS("yes") : button1Text; | info.button1 = button1Text.isEmpty() ? TRANS("yes") : button1Text; | ||||
| info.button2 = button2Text.isEmpty() ? TRANS("no") : button2Text; | info.button2 = button2Text.isEmpty() ? TRANS("no") : button2Text; | ||||
| info.button3 = button3Text.isEmpty() ? TRANS("cancel") : button3Text; | info.button3 = button3Text.isEmpty() ? TRANS("cancel") : button3Text; | ||||
| return info.showModal(); | |||||
| return info.invoke(); | |||||
| } | } | ||||
| END_JUCE_NAMESPACE | END_JUCE_NAMESPACE | ||||
| @@ -73,7 +73,7 @@ public: | |||||
| @param message a longer, more descriptive message to show underneath the | @param message a longer, more descriptive message to show underneath the | ||||
| headline | headline | ||||
| @param iconType the type of icon to display | @param iconType the type of icon to display | ||||
| @param associatedComponent if this is non-zero, it specifies the component that the | |||||
| @param associatedComponent if this is non-null, it specifies the component that the | |||||
| alert window should be associated with. Depending on the look | alert window should be associated with. Depending on the look | ||||
| and feel, this might be used for positioning of the alert window. | and feel, this might be used for positioning of the alert window. | ||||
| */ | */ | ||||
| @@ -218,7 +218,7 @@ public: | |||||
| @param index a value 0 to (getNumCustomComponents() - 1). Out-of-range indexes | @param index a value 0 to (getNumCustomComponents() - 1). Out-of-range indexes | ||||
| will return 0 | will return 0 | ||||
| @returns the component that was removed (or zero) | |||||
| @returns the component that was removed (or null) | |||||
| @see getNumCustomComponents, addCustomComponent | @see getNumCustomComponents, addCustomComponent | ||||
| */ | */ | ||||
| Component* removeCustomComponent (int index); | Component* removeCustomComponent (int index); | ||||
| @@ -232,8 +232,11 @@ public: | |||||
| /** Shows a dialog box that just has a message and a single button to get rid of it. | /** Shows a dialog box that just has a message and a single button to get rid of it. | ||||
| The box is shown modally, and the method returns after the user | |||||
| has clicked the button (or pressed the escape or return keys). | |||||
| If the callback parameter is null, the box is shown modally, and the method will | |||||
| block until the user has clicked the button (or pressed the escape or return keys). | |||||
| If the callback parameter is non-null, the box will be displayed and placed into a | |||||
| modal state, but this method will return immediately, and the callback will be invoked | |||||
| later when the user dismisses the box. | |||||
| @param iconType the type of icon to show | @param iconType the type of icon to show | ||||
| @param title the headline to show at the top of the box | @param title the headline to show at the top of the box | ||||
| @@ -241,21 +244,53 @@ public: | |||||
| headline | headline | ||||
| @param buttonText the text to show in the button - if this string is empty, the | @param buttonText the text to show in the button - if this string is empty, the | ||||
| default string "ok" (or a localised version) will be used. | default string "ok" (or a localised version) will be used. | ||||
| @param associatedComponent if this is non-zero, it specifies the component that the | |||||
| @param associatedComponent if this is non-null, it specifies the component that the | |||||
| alert window should be associated with. Depending on the look | alert window should be associated with. Depending on the look | ||||
| and feel, this might be used for positioning of the alert window. | and feel, this might be used for positioning of the alert window. | ||||
| */ | */ | ||||
| #if JUCE_MODAL_LOOPS_PERMITTED | |||||
| static void JUCE_CALLTYPE showMessageBox (AlertIconType iconType, | static void JUCE_CALLTYPE showMessageBox (AlertIconType iconType, | ||||
| const String& title, | const String& title, | ||||
| const String& message, | const String& message, | ||||
| const String& buttonText = String::empty, | const String& buttonText = String::empty, | ||||
| Component* associatedComponent = 0); | Component* associatedComponent = 0); | ||||
| #endif | |||||
| /** Shows a dialog box that just has a message and a single button to get rid of it. | |||||
| If the callback parameter is null, the box is shown modally, and the method will | |||||
| block until the user has clicked the button (or pressed the escape or return keys). | |||||
| If the callback parameter is non-null, the box will be displayed and placed into a | |||||
| modal state, but this method will return immediately, and the callback will be invoked | |||||
| later when the user dismisses the box. | |||||
| @param iconType the type of icon to show | |||||
| @param title the headline to show at the top of the box | |||||
| @param message a longer, more descriptive message to show underneath the | |||||
| headline | |||||
| @param buttonText the text to show in the button - if this string is empty, the | |||||
| default string "ok" (or a localised version) will be used. | |||||
| @param associatedComponent if this is non-null, it specifies the component that the | |||||
| alert window should be associated with. Depending on the look | |||||
| and feel, this might be used for positioning of the alert window. | |||||
| */ | |||||
| static void JUCE_CALLTYPE showMessageBoxAsync (AlertIconType iconType, | |||||
| const String& title, | |||||
| const String& message, | |||||
| const String& buttonText = String::empty, | |||||
| Component* associatedComponent = 0); | |||||
| /** Shows a dialog box with two buttons. | /** Shows a dialog box with two buttons. | ||||
| Ideal for ok/cancel or yes/no choices. The return key can also be used | Ideal for ok/cancel or yes/no choices. The return key can also be used | ||||
| to trigger the first button, and the escape key for the second button. | to trigger the first button, and the escape key for the second button. | ||||
| If the callback parameter is null, the box is shown modally, and the method will | |||||
| block until the user has clicked the button (or pressed the escape or return keys). | |||||
| If the callback parameter is non-null, the box will be displayed and placed into a | |||||
| modal state, but this method will return immediately, and the callback will be invoked | |||||
| later when the user dismisses the box. | |||||
| @param iconType the type of icon to show | @param iconType the type of icon to show | ||||
| @param title the headline to show at the top of the box | @param title the headline to show at the top of the box | ||||
| @param message a longer, more descriptive message to show underneath the | @param message a longer, more descriptive message to show underneath the | ||||
| @@ -266,17 +301,34 @@ public: | |||||
| @param button2Text the text to show in the second button - if this string is | @param button2Text the text to show in the second button - if this string is | ||||
| empty, the default string "cancel" (or a localised version of it) | empty, the default string "cancel" (or a localised version of it) | ||||
| will be used. | will be used. | ||||
| @param associatedComponent if this is non-zero, it specifies the component that the | |||||
| alert window should be associated with. Depending on the look | |||||
| and feel, this might be used for positioning of the alert window. | |||||
| @returns true if button 1 was clicked, false if it was button 2 | |||||
| @param associatedComponent if this is non-null, it specifies the component that the | |||||
| alert window should be associated with. Depending on the look | |||||
| and feel, this might be used for positioning of the alert window. | |||||
| @param callback if this is non-null, the menu will be launched asynchronously, | |||||
| returning immediately, and the callback will receive a call to its | |||||
| modalStateFinished() when the box is dismissed, with its parameter | |||||
| being 1 if the ok button was pressed, or 0 for cancel, The callback object | |||||
| will be owned and deleted by the system, so make sure that it works | |||||
| safely and doesn't keep any references to objects that might be deleted | |||||
| before it gets called. | |||||
| @returns true if button 1 was clicked, false if it was button 2. If the callback parameter | |||||
| is not null, the method always returns false, and the user's choice is delivered | |||||
| later by the callback. | |||||
| */ | */ | ||||
| static bool JUCE_CALLTYPE showOkCancelBox (AlertIconType iconType, | static bool JUCE_CALLTYPE showOkCancelBox (AlertIconType iconType, | ||||
| const String& title, | const String& title, | ||||
| const String& message, | const String& message, | ||||
| #if JUCE_MODAL_LOOPS_PERMITTED | |||||
| const String& button1Text = String::empty, | const String& button1Text = String::empty, | ||||
| const String& button2Text = String::empty, | const String& button2Text = String::empty, | ||||
| Component* associatedComponent = 0); | |||||
| Component* associatedComponent = 0, | |||||
| ModalComponentManager::Callback* callback = 0); | |||||
| #else | |||||
| const String& button1Text, | |||||
| const String& button2Text, | |||||
| Component* associatedComponent, | |||||
| ModalComponentManager::Callback* callback); | |||||
| #endif | |||||
| /** Shows a dialog box with three buttons. | /** Shows a dialog box with three buttons. | ||||
| @@ -284,6 +336,12 @@ public: | |||||
| The escape key can be used to trigger the third button. | The escape key can be used to trigger the third button. | ||||
| If the callback parameter is null, the box is shown modally, and the method will | |||||
| block until the user has clicked the button (or pressed the escape or return keys). | |||||
| If the callback parameter is non-null, the box will be displayed and placed into a | |||||
| modal state, but this method will return immediately, and the callback will be invoked | |||||
| later when the user dismisses the box. | |||||
| @param iconType the type of icon to show | @param iconType the type of icon to show | ||||
| @param title the headline to show at the top of the box | @param title the headline to show at the top of the box | ||||
| @param message a longer, more descriptive message to show underneath the | @param message a longer, more descriptive message to show underneath the | ||||
| @@ -294,11 +352,19 @@ public: | |||||
| "no" will be used (or a localised version of it) | "no" will be used (or a localised version of it) | ||||
| @param button3Text the text to show in the first button - if an empty string, then | @param button3Text the text to show in the first button - if an empty string, then | ||||
| "cancel" will be used (or a localised version of it) | "cancel" will be used (or a localised version of it) | ||||
| @param associatedComponent if this is non-zero, it specifies the component that the | |||||
| @param associatedComponent if this is non-null, it specifies the component that the | |||||
| alert window should be associated with. Depending on the look | alert window should be associated with. Depending on the look | ||||
| and feel, this might be used for positioning of the alert window. | and feel, this might be used for positioning of the alert window. | ||||
| @returns one of the following values: | |||||
| @param callback if this is non-null, the menu will be launched asynchronously, | |||||
| returning immediately, and the callback will receive a call to its | |||||
| modalStateFinished() when the box is dismissed, with its parameter | |||||
| being 1 if the "yes" button was pressed, 2 for the "no" button, or 0 | |||||
| if it was cancelled, The callback object will be owned and deleted by the | |||||
| system, so make sure that it works safely and doesn't keep any references | |||||
| to objects that might be deleted before it gets called. | |||||
| @returns If the callback parameter has been set, this returns 0. Otherwise, it | |||||
| returns one of the following values: | |||||
| - 0 if the third button was pressed (normally used for 'cancel') | - 0 if the third button was pressed (normally used for 'cancel') | ||||
| - 1 if the first button was pressed (normally used for 'yes') | - 1 if the first button was pressed (normally used for 'yes') | ||||
| - 2 if the middle button was pressed (normally used for 'no') | - 2 if the middle button was pressed (normally used for 'no') | ||||
| @@ -306,10 +372,19 @@ public: | |||||
| static int JUCE_CALLTYPE showYesNoCancelBox (AlertIconType iconType, | static int JUCE_CALLTYPE showYesNoCancelBox (AlertIconType iconType, | ||||
| const String& title, | const String& title, | ||||
| const String& message, | const String& message, | ||||
| #if JUCE_MODAL_LOOPS_PERMITTED | |||||
| const String& button1Text = String::empty, | const String& button1Text = String::empty, | ||||
| const String& button2Text = String::empty, | const String& button2Text = String::empty, | ||||
| const String& button3Text = String::empty, | const String& button3Text = String::empty, | ||||
| Component* associatedComponent = 0); | |||||
| Component* associatedComponent = 0, | |||||
| ModalComponentManager::Callback* callback = 0); | |||||
| #else | |||||
| const String& button1Text, | |||||
| const String& button2Text, | |||||
| const String& button3Text, | |||||
| Component* associatedComponent, | |||||
| ModalComponentManager::Callback* callback); | |||||
| #endif | |||||
| //============================================================================== | //============================================================================== | ||||
| /** Shows an operating-system native dialog box. | /** Shows an operating-system native dialog box. | ||||
| @@ -60,16 +60,30 @@ void DialogWindow::resized() | |||||
| } | } | ||||
| } | } | ||||
| // (Sadly, this can't be made a local class inside the showModalDialog function, because the | |||||
| // VC compiler complains about the undefined copy constructor) | |||||
| //============================================================================== | |||||
| class TempDialogWindow : public DialogWindow | class TempDialogWindow : public DialogWindow | ||||
| { | { | ||||
| public: | public: | ||||
| TempDialogWindow (const String& title, const Colour& colour, const bool escapeCloses) | |||||
| : DialogWindow (title, colour, escapeCloses, true) | |||||
| TempDialogWindow (const String& title, | |||||
| Component* contentComponent, | |||||
| Component* componentToCentreAround, | |||||
| const Colour& colour, | |||||
| const bool escapeKeyTriggersCloseButton, | |||||
| const bool shouldBeResizable, | |||||
| const bool useBottomRightCornerResizer) | |||||
| : DialogWindow (title, colour, escapeKeyTriggersCloseButton, true) | |||||
| { | { | ||||
| if (! JUCEApplication::isStandaloneApp()) | if (! JUCEApplication::isStandaloneApp()) | ||||
| setAlwaysOnTop (true); // for a plugin, make it always-on-top because the host windows are often top-level | setAlwaysOnTop (true); // for a plugin, make it always-on-top because the host windows are often top-level | ||||
| setContentComponent (contentComponent, true, true); | |||||
| centreAroundComponent (componentToCentreAround, getWidth(), getHeight()); | |||||
| setResizable (shouldBeResizable, useBottomRightCornerResizer); | |||||
| } | |||||
| ~TempDialogWindow() | |||||
| { | |||||
| setContentComponent (0, false); | |||||
| } | } | ||||
| void closeButtonPressed() | void closeButtonPressed() | ||||
| @@ -83,23 +97,36 @@ private: | |||||
| //============================================================================== | //============================================================================== | ||||
| void DialogWindow::showDialog (const String& dialogTitle, | |||||
| Component* const contentComponent, | |||||
| Component* const componentToCentreAround, | |||||
| const Colour& backgroundColour, | |||||
| const bool escapeKeyTriggersCloseButton, | |||||
| const bool shouldBeResizable, | |||||
| const bool useBottomRightCornerResizer) | |||||
| { | |||||
| TempDialogWindow* dw = new TempDialogWindow (dialogTitle, contentComponent, componentToCentreAround, | |||||
| backgroundColour, escapeKeyTriggersCloseButton, | |||||
| shouldBeResizable, useBottomRightCornerResizer); | |||||
| dw->enterModalState (true, 0, true); | |||||
| } | |||||
| #if JUCE_MODAL_LOOPS_PERMITTED | |||||
| int DialogWindow::showModalDialog (const String& dialogTitle, | int DialogWindow::showModalDialog (const String& dialogTitle, | ||||
| Component* contentComponent, | |||||
| Component* componentToCentreAround, | |||||
| const Colour& colour, | |||||
| Component* const contentComponent, | |||||
| Component* const componentToCentreAround, | |||||
| const Colour& backgroundColour, | |||||
| const bool escapeKeyTriggersCloseButton, | const bool escapeKeyTriggersCloseButton, | ||||
| const bool shouldBeResizable, | const bool shouldBeResizable, | ||||
| const bool useBottomRightCornerResizer) | const bool useBottomRightCornerResizer) | ||||
| { | { | ||||
| TempDialogWindow dw (dialogTitle, colour, escapeKeyTriggersCloseButton); | |||||
| dw.setContentComponent (contentComponent, true, true); | |||||
| dw.centreAroundComponent (componentToCentreAround, dw.getWidth(), dw.getHeight()); | |||||
| dw.setResizable (shouldBeResizable, useBottomRightCornerResizer); | |||||
| const int result = dw.runModalLoop(); | |||||
| dw.setContentComponent (0, false); | |||||
| return result; | |||||
| } | |||||
| TempDialogWindow dw (dialogTitle, contentComponent, componentToCentreAround, | |||||
| backgroundColour, escapeKeyTriggersCloseButton, | |||||
| shouldBeResizable, useBottomRightCornerResizer); | |||||
| return dw.runModalLoop(); | |||||
| } | |||||
| #endif | |||||
| END_JUCE_NAMESPACE | END_JUCE_NAMESPACE | ||||
| @@ -77,6 +77,49 @@ public: | |||||
| ~DialogWindow(); | ~DialogWindow(); | ||||
| //============================================================================== | //============================================================================== | ||||
| /** Easy way of quickly showing a dialog box containing a given component. | |||||
| This will open and display a DialogWindow containing a given component, making it | |||||
| modal, but returning immediately to allow the dialog to finish in its own time. If | |||||
| you want to block and run a modal loop until the dialog is dismissed, use showModalDialog() | |||||
| instead. | |||||
| To close the dialog programatically, you should call exitModalState (returnValue) on | |||||
| the DialogWindow that is created. To find a pointer to this window from your | |||||
| contentComponent, you can do something like this: | |||||
| @code | |||||
| Dialogwindow* dw = contentComponent->findParentComponentOfClass ((DialogWindow*) 0); | |||||
| if (dw != 0) | |||||
| dw->exitModalState (1234); | |||||
| @endcode | |||||
| @param dialogTitle the dialog box's title | |||||
| @param contentComponent the content component for the dialog box. Make sure | |||||
| that this has been set to the size you want it to | |||||
| be before calling this method. The component won't | |||||
| be deleted by this call, so you can re-use it or delete | |||||
| it afterwards | |||||
| @param componentToCentreAround if this is non-zero, it indicates a component that | |||||
| you'd like to show this dialog box in front of. See the | |||||
| DocumentWindow::centreAroundComponent() method for more | |||||
| info on this parameter | |||||
| @param backgroundColour a colour to use for the dialog box's background colour | |||||
| @param escapeKeyTriggersCloseButton if true, then pressing the escape key will cause the | |||||
| close button to be triggered | |||||
| @param shouldBeResizable if true, the dialog window has either a resizable border, or | |||||
| a corner resizer | |||||
| @param useBottomRightCornerResizer if shouldBeResizable is true, this indicates whether | |||||
| to use a border or corner resizer component. See ResizableWindow::setResizable() | |||||
| */ | |||||
| static void showDialog (const String& dialogTitle, | |||||
| Component* contentComponent, | |||||
| Component* componentToCentreAround, | |||||
| const Colour& backgroundColour, | |||||
| bool escapeKeyTriggersCloseButton, | |||||
| bool shouldBeResizable = false, | |||||
| bool useBottomRightCornerResizer = false); | |||||
| /** Easy way of quickly showing a dialog box containing a given component. | /** Easy way of quickly showing a dialog box containing a given component. | ||||
| This will open and display a DialogWindow containing a given component, returning | This will open and display a DialogWindow containing a given component, returning | ||||
| @@ -112,6 +155,7 @@ public: | |||||
| @param useBottomRightCornerResizer if shouldBeResizable is true, this indicates whether | @param useBottomRightCornerResizer if shouldBeResizable is true, this indicates whether | ||||
| to use a border or corner resizer component. See ResizableWindow::setResizable() | to use a border or corner resizer component. See ResizableWindow::setResizable() | ||||
| */ | */ | ||||
| #if JUCE_MODAL_LOOPS_PERMITTED | |||||
| static int showModalDialog (const String& dialogTitle, | static int showModalDialog (const String& dialogTitle, | ||||
| Component* contentComponent, | Component* contentComponent, | ||||
| Component* componentToCentreAround, | Component* componentToCentreAround, | ||||
| @@ -119,6 +163,7 @@ public: | |||||
| bool escapeKeyTriggersCloseButton, | bool escapeKeyTriggersCloseButton, | ||||
| bool shouldBeResizable = false, | bool shouldBeResizable = false, | ||||
| bool useBottomRightCornerResizer = false); | bool useBottomRightCornerResizer = false); | ||||
| #endif | |||||
| protected: | protected: | ||||
| /** @internal */ | /** @internal */ | ||||
| @@ -83,7 +83,9 @@ void SplashScreen::show (const String& title, | |||||
| addToDesktop (useDropShadow ? ComponentPeer::windowHasDropShadow : 0); | addToDesktop (useDropShadow ? ComponentPeer::windowHasDropShadow : 0); | ||||
| toFront (false); | toFront (false); | ||||
| #if JUCE_MODAL_LOOPS_PERMITTED | |||||
| MessageManager::getInstance()->runDispatchLoopUntil (300); | MessageManager::getInstance()->runDispatchLoopUntil (300); | ||||
| #endif | |||||
| repaint(); | repaint(); | ||||
| @@ -56,6 +56,7 @@ ThreadWithProgressWindow::~ThreadWithProgressWindow() | |||||
| stopThread (timeOutMsWhenCancelling); | stopThread (timeOutMsWhenCancelling); | ||||
| } | } | ||||
| #if JUCE_MODAL_LOOPS_PERMITTED | |||||
| bool ThreadWithProgressWindow::runThread (const int priority) | bool ThreadWithProgressWindow::runThread (const int priority) | ||||
| { | { | ||||
| startThread (priority); | startThread (priority); | ||||
| @@ -74,6 +75,7 @@ bool ThreadWithProgressWindow::runThread (const int priority) | |||||
| return finishedNaturally; | return finishedNaturally; | ||||
| } | } | ||||
| #endif | |||||
| void ThreadWithProgressWindow::setProgress (const double newProgress) | void ThreadWithProgressWindow::setProgress (const double newProgress) | ||||
| { | { | ||||
| @@ -170,101 +170,111 @@ const Image PNGImageFormat::decodeImage (InputStream& in) | |||||
| if (pngReadStruct != 0) | if (pngReadStruct != 0) | ||||
| { | { | ||||
| pngInfoStruct = png_create_info_struct (pngReadStruct); | |||||
| if (pngInfoStruct == 0) | |||||
| try | |||||
| { | { | ||||
| png_destroy_read_struct (&pngReadStruct, 0, 0); | |||||
| return Image::null; | |||||
| } | |||||
| pngInfoStruct = png_create_info_struct (pngReadStruct); | |||||
| png_set_error_fn (pngReadStruct, 0, PNGHelpers::errorCallback, PNGHelpers::errorCallback ); | |||||
| if (pngInfoStruct == 0) | |||||
| { | |||||
| png_destroy_read_struct (&pngReadStruct, 0, 0); | |||||
| return Image::null; | |||||
| } | |||||
| // read the header.. | |||||
| png_set_read_fn (pngReadStruct, &in, PNGHelpers::readCallback); | |||||
| png_set_error_fn (pngReadStruct, 0, PNGHelpers::errorCallback, PNGHelpers::errorCallback ); | |||||
| png_uint_32 width, height; | |||||
| int bitDepth, colorType, interlaceType; | |||||
| // read the header.. | |||||
| png_set_read_fn (pngReadStruct, &in, PNGHelpers::readCallback); | |||||
| png_read_info (pngReadStruct, pngInfoStruct); | |||||
| png_uint_32 width, height; | |||||
| int bitDepth, colorType, interlaceType; | |||||
| png_get_IHDR (pngReadStruct, pngInfoStruct, | |||||
| &width, &height, | |||||
| &bitDepth, &colorType, | |||||
| &interlaceType, 0, 0); | |||||
| png_read_info (pngReadStruct, pngInfoStruct); | |||||
| if (bitDepth == 16) | |||||
| png_set_strip_16 (pngReadStruct); | |||||
| png_get_IHDR (pngReadStruct, pngInfoStruct, | |||||
| &width, &height, | |||||
| &bitDepth, &colorType, | |||||
| &interlaceType, 0, 0); | |||||
| if (colorType == PNG_COLOR_TYPE_PALETTE) | |||||
| png_set_expand (pngReadStruct); | |||||
| if (bitDepth == 16) | |||||
| png_set_strip_16 (pngReadStruct); | |||||
| if (bitDepth < 8) | |||||
| png_set_expand (pngReadStruct); | |||||
| if (colorType == PNG_COLOR_TYPE_PALETTE) | |||||
| png_set_expand (pngReadStruct); | |||||
| if (png_get_valid (pngReadStruct, pngInfoStruct, PNG_INFO_tRNS)) | |||||
| png_set_expand (pngReadStruct); | |||||
| if (bitDepth < 8) | |||||
| png_set_expand (pngReadStruct); | |||||
| if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) | |||||
| png_set_gray_to_rgb (pngReadStruct); | |||||
| if (png_get_valid (pngReadStruct, pngInfoStruct, PNG_INFO_tRNS)) | |||||
| png_set_expand (pngReadStruct); | |||||
| png_set_add_alpha (pngReadStruct, 0xff, PNG_FILLER_AFTER); | |||||
| if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) | |||||
| png_set_gray_to_rgb (pngReadStruct); | |||||
| bool hasAlphaChan = (colorType & PNG_COLOR_MASK_ALPHA) != 0 | |||||
| || pngInfoStruct->num_trans > 0; | |||||
| png_set_add_alpha (pngReadStruct, 0xff, PNG_FILLER_AFTER); | |||||
| // Load the image into a temp buffer in the pnglib format.. | |||||
| HeapBlock <uint8> tempBuffer (height * (width << 2)); | |||||
| bool hasAlphaChan = (colorType & PNG_COLOR_MASK_ALPHA) != 0 | |||||
| || pngInfoStruct->num_trans > 0; | |||||
| { | |||||
| HeapBlock <png_bytep> rows (height); | |||||
| for (int y = (int) height; --y >= 0;) | |||||
| rows[y] = (png_bytep) (tempBuffer + (width << 2) * y); | |||||
| // Load the image into a temp buffer in the pnglib format.. | |||||
| HeapBlock <uint8> tempBuffer (height * (width << 2)); | |||||
| png_read_image (pngReadStruct, rows); | |||||
| png_read_end (pngReadStruct, pngInfoStruct); | |||||
| } | |||||
| { | |||||
| HeapBlock <png_bytep> rows (height); | |||||
| for (int y = (int) height; --y >= 0;) | |||||
| rows[y] = (png_bytep) (tempBuffer + (width << 2) * y); | |||||
| png_destroy_read_struct (&pngReadStruct, &pngInfoStruct, 0); | |||||
| try | |||||
| { | |||||
| png_read_image (pngReadStruct, rows); | |||||
| png_read_end (pngReadStruct, pngInfoStruct); | |||||
| } | |||||
| catch (PNGHelpers::PNGErrorStruct&) | |||||
| {} | |||||
| } | |||||
| // now convert the data to a juce image format.. | |||||
| image = Image (hasAlphaChan ? Image::ARGB : Image::RGB, | |||||
| (int) width, (int) height, hasAlphaChan); | |||||
| png_destroy_read_struct (&pngReadStruct, &pngInfoStruct, 0); | |||||
| image.getProperties()->set ("originalImageHadAlpha", image.hasAlphaChannel()); | |||||
| hasAlphaChan = image.hasAlphaChannel(); // (the native image creator may not give back what we expect) | |||||
| // now convert the data to a juce image format.. | |||||
| image = Image (hasAlphaChan ? Image::ARGB : Image::RGB, | |||||
| (int) width, (int) height, hasAlphaChan); | |||||
| const Image::BitmapData destData (image, Image::BitmapData::writeOnly); | |||||
| uint8* srcRow = tempBuffer; | |||||
| uint8* destRow = destData.data; | |||||
| image.getProperties()->set ("originalImageHadAlpha", image.hasAlphaChannel()); | |||||
| hasAlphaChan = image.hasAlphaChannel(); // (the native image creator may not give back what we expect) | |||||
| for (int y = 0; y < (int) height; ++y) | |||||
| { | |||||
| const uint8* src = srcRow; | |||||
| srcRow += (width << 2); | |||||
| uint8* dest = destRow; | |||||
| destRow += destData.lineStride; | |||||
| const Image::BitmapData destData (image, Image::BitmapData::writeOnly); | |||||
| uint8* srcRow = tempBuffer; | |||||
| uint8* destRow = destData.data; | |||||
| if (hasAlphaChan) | |||||
| for (int y = 0; y < (int) height; ++y) | |||||
| { | { | ||||
| for (int i = (int) width; --i >= 0;) | |||||
| const uint8* src = srcRow; | |||||
| srcRow += (width << 2); | |||||
| uint8* dest = destRow; | |||||
| destRow += destData.lineStride; | |||||
| if (hasAlphaChan) | |||||
| { | { | ||||
| ((PixelARGB*) dest)->setARGB (src[3], src[0], src[1], src[2]); | |||||
| ((PixelARGB*) dest)->premultiply(); | |||||
| dest += destData.pixelStride; | |||||
| src += 4; | |||||
| for (int i = (int) width; --i >= 0;) | |||||
| { | |||||
| ((PixelARGB*) dest)->setARGB (src[3], src[0], src[1], src[2]); | |||||
| ((PixelARGB*) dest)->premultiply(); | |||||
| dest += destData.pixelStride; | |||||
| src += 4; | |||||
| } | |||||
| } | } | ||||
| } | |||||
| else | |||||
| { | |||||
| for (int i = (int) width; --i >= 0;) | |||||
| else | |||||
| { | { | ||||
| ((PixelRGB*) dest)->setARGB (0, src[0], src[1], src[2]); | |||||
| dest += destData.pixelStride; | |||||
| src += 4; | |||||
| for (int i = (int) width; --i >= 0;) | |||||
| { | |||||
| ((PixelRGB*) dest)->setARGB (0, src[0], src[1], src[2]); | |||||
| dest += destData.pixelStride; | |||||
| src += 4; | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| catch (PNGHelpers::PNGErrorStruct&) | |||||
| {} | |||||
| } | } | ||||
| return image; | return image; | ||||
| @@ -32,13 +32,12 @@ import android.view.ViewGroup; | |||||
| import android.view.Display; | import android.view.Display; | ||||
| import android.view.WindowManager; | import android.view.WindowManager; | ||||
| import android.graphics.Paint; | import android.graphics.Paint; | ||||
| import android.graphics.Path; | |||||
| import android.text.ClipboardManager; | import android.text.ClipboardManager; | ||||
| import com.juce.ComponentPeerView; | import com.juce.ComponentPeerView; | ||||
| //============================================================================== | //============================================================================== | ||||
| public class JuceAppActivity extends Activity | |||||
| public final class JuceAppActivity extends Activity | |||||
| { | { | ||||
| //============================================================================== | //============================================================================== | ||||
| static | static | ||||
| @@ -47,12 +46,10 @@ public class JuceAppActivity extends Activity | |||||
| } | } | ||||
| @Override | @Override | ||||
| public void onCreate (Bundle savedInstanceState) | |||||
| public final void onCreate (Bundle savedInstanceState) | |||||
| { | { | ||||
| super.onCreate (savedInstanceState); | super.onCreate (savedInstanceState); | ||||
| messageHandler = new android.os.Handler(); | |||||
| viewHolder = new ViewHolder (this); | viewHolder = new ViewHolder (this); | ||||
| setContentView (viewHolder); | setContentView (viewHolder); | ||||
| @@ -65,7 +62,7 @@ public class JuceAppActivity extends Activity | |||||
| } | } | ||||
| @Override | @Override | ||||
| protected void onDestroy() | |||||
| protected final void onDestroy() | |||||
| { | { | ||||
| quitApp(); | quitApp(); | ||||
| super.onDestroy(); | super.onDestroy(); | ||||
| @@ -77,21 +74,21 @@ public class JuceAppActivity extends Activity | |||||
| public native void quitApp(); | public native void quitApp(); | ||||
| //============================================================================== | //============================================================================== | ||||
| public static void printToConsole (String s) | |||||
| public static final void printToConsole (String s) | |||||
| { | { | ||||
| System.out.println (s); | |||||
| android.util.Log.i ("Juce", s); | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| public native void deliverMessage (long value); | public native void deliverMessage (long value); | ||||
| private android.os.Handler messageHandler; | |||||
| private android.os.Handler messageHandler = new android.os.Handler(); | |||||
| public void postMessage (long value) | |||||
| public final void postMessage (long value) | |||||
| { | { | ||||
| messageHandler.post (new MessageCallback (this, value)); | messageHandler.post (new MessageCallback (this, value)); | ||||
| } | } | ||||
| class MessageCallback implements java.lang.Runnable | |||||
| final class MessageCallback implements Runnable | |||||
| { | { | ||||
| public MessageCallback (JuceAppActivity app_, long value_) | public MessageCallback (JuceAppActivity app_, long value_) | ||||
| { | { | ||||
| @@ -99,7 +96,7 @@ public class JuceAppActivity extends Activity | |||||
| value = value_; | value = value_; | ||||
| } | } | ||||
| public void run() | |||||
| public final void run() | |||||
| { | { | ||||
| app.deliverMessage (value); | app.deliverMessage (value); | ||||
| } | } | ||||
| @@ -111,19 +108,19 @@ public class JuceAppActivity extends Activity | |||||
| //============================================================================== | //============================================================================== | ||||
| private ViewHolder viewHolder; | private ViewHolder viewHolder; | ||||
| public ComponentPeerView createNewView (boolean opaque) | |||||
| public final ComponentPeerView createNewView (boolean opaque) | |||||
| { | { | ||||
| ComponentPeerView v = new ComponentPeerView (this, opaque); | ComponentPeerView v = new ComponentPeerView (this, opaque); | ||||
| viewHolder.addView (v); | viewHolder.addView (v); | ||||
| return v; | return v; | ||||
| } | } | ||||
| public void deleteView (ComponentPeerView view) | |||||
| public final void deleteView (ComponentPeerView view) | |||||
| { | { | ||||
| viewHolder.removeView (view); | viewHolder.removeView (view); | ||||
| } | } | ||||
| class ViewHolder extends ViewGroup | |||||
| final class ViewHolder extends ViewGroup | |||||
| { | { | ||||
| public ViewHolder (Context context) | public ViewHolder (Context context) | ||||
| { | { | ||||
| @@ -132,24 +129,24 @@ public class JuceAppActivity extends Activity | |||||
| setFocusable (false); | setFocusable (false); | ||||
| } | } | ||||
| protected void onLayout (boolean changed, int left, int top, int right, int bottom) | |||||
| protected final void onLayout (boolean changed, int left, int top, int right, int bottom) | |||||
| { | { | ||||
| } | } | ||||
| } | } | ||||
| public void excludeClipRegion (android.graphics.Canvas canvas, float left, float top, float right, float bottom) | |||||
| public final void excludeClipRegion (android.graphics.Canvas canvas, float left, float top, float right, float bottom) | |||||
| { | { | ||||
| canvas.clipRect (left, top, right, bottom, android.graphics.Region.Op.DIFFERENCE); | canvas.clipRect (left, top, right, bottom, android.graphics.Region.Op.DIFFERENCE); | ||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| public String getClipboardContent() | |||||
| public final String getClipboardContent() | |||||
| { | { | ||||
| ClipboardManager clipboard = (ClipboardManager) getSystemService (CLIPBOARD_SERVICE); | ClipboardManager clipboard = (ClipboardManager) getSystemService (CLIPBOARD_SERVICE); | ||||
| return clipboard.getText().toString(); | return clipboard.getText().toString(); | ||||
| } | } | ||||
| public void setClipboardContent (String newText) | |||||
| public final void setClipboardContent (String newText) | |||||
| { | { | ||||
| ClipboardManager clipboard = (ClipboardManager) getSystemService (CLIPBOARD_SERVICE); | ClipboardManager clipboard = (ClipboardManager) getSystemService (CLIPBOARD_SERVICE); | ||||
| clipboard.setText (newText); | clipboard.setText (newText); | ||||
| @@ -121,10 +121,4 @@ void MessageManager::stopDispatchLoop() | |||||
| quitMessagePosted = true; | quitMessagePosted = true; | ||||
| } | } | ||||
| bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor) | |||||
| { | |||||
| juce_dispatchNextMessageOnSystemQueue (false); | |||||
| return false; | |||||
| } | |||||
| #endif | #endif | ||||
| @@ -393,6 +393,7 @@ namespace | |||||
| } | } | ||||
| } | } | ||||
| #if JUCE_MODAL_LOOPS_PERMITTED | |||||
| bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor) | bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor) | ||||
| { | { | ||||
| jassert (isThisTheMessageThread()); // must only be called by the message thread | jassert (isThisTheMessageThread()); // must only be called by the message thread | ||||
| @@ -419,6 +420,7 @@ bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor) | |||||
| return ! quitMessagePosted; | return ! quitMessagePosted; | ||||
| } | } | ||||
| #endif | |||||
| //============================================================================== | //============================================================================== | ||||
| void MessageManager::doPlatformSpecificInitialisation() | void MessageManager::doPlatformSpecificInitialisation() | ||||
| @@ -78,6 +78,7 @@ void FileBasedDocument::setFile (const File& newFile) | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| #if JUCE_MODAL_LOOPS_PERMITTED | |||||
| bool FileBasedDocument::loadFrom (const File& newFile, | bool FileBasedDocument::loadFrom (const File& newFile, | ||||
| const bool showMessageOnFailure) | const bool showMessageOnFailure) | ||||
| { | { | ||||
| @@ -121,6 +122,7 @@ bool FileBasedDocument::loadFrom (const File& newFile, | |||||
| return false; | return false; | ||||
| } | } | ||||
| #endif | |||||
| bool FileBasedDocument::loadFromUserSpecifiedFile (const bool showMessageOnFailure) | bool FileBasedDocument::loadFromUserSpecifiedFile (const bool showMessageOnFailure) | ||||
| { | { | ||||
| @@ -144,6 +146,7 @@ FileBasedDocument::SaveResult FileBasedDocument::save (const bool askUserForFile | |||||
| showMessageOnFailure); | showMessageOnFailure); | ||||
| } | } | ||||
| #if JUCE_MODAL_LOOPS_PERMITTED | |||||
| FileBasedDocument::SaveResult FileBasedDocument::saveAs (const File& newFile, | FileBasedDocument::SaveResult FileBasedDocument::saveAs (const File& newFile, | ||||
| const bool warnAboutOverwritingExistingFiles, | const bool warnAboutOverwritingExistingFiles, | ||||
| const bool askUserForFileIfNotSpecified, | const bool askUserForFileIfNotSpecified, | ||||
| @@ -289,5 +292,7 @@ FileBasedDocument::SaveResult FileBasedDocument::saveAsInteractive (const bool w | |||||
| return userCancelledSave; | return userCancelledSave; | ||||
| } | } | ||||
| #endif | |||||
| END_JUCE_NAMESPACE | END_JUCE_NAMESPACE | ||||