Browse Source

Changes and additions to provide alternatives to modal-loop functionality (Android can't run modal loops). New class ModalCallbackFunction providing quick objects for making callbacks to static functions. Changes to remove modal loops from most of the internal library classes. Added new methods to PopupMenu to provide easier async callbacks, and also a cleaner way of specifying options when showing a menu. Fix for PNG decoding of corrupted image files.

tags/2021-05-28
Julian Storer 14 years ago
parent
commit
2d10b0b43d
47 changed files with 2854 additions and 1554 deletions
  1. +3
    -3
      extras/JuceDemo/Source/demos/InterprocessCommsDemo.cpp
  2. +7
    -3
      extras/JuceDemo/Source/demos/ThreadingDemo.cpp
  3. +15
    -5
      extras/JuceDemo/Source/demos/TreeViewDemo.cpp
  4. +173
    -166
      extras/JuceDemo/Source/demos/WidgetsDemo.cpp
  5. +2
    -5
      extras/audio plugins/wrapper/Standalone/juce_StandaloneFilterWindow.cpp
  6. +639
    -406
      juce_amalgamated.cpp
  7. +895
    -489
      juce_amalgamated.h
  8. +6
    -6
      src/application/juce_ApplicationProperties.cpp
  9. +3
    -2
      src/audio/plugins/juce_PluginListComponent.cpp
  10. +11
    -0
      src/core/juce_PlatformDefs.h
  11. +1
    -1
      src/core/juce_StandardHeader.h
  12. +1
    -1
      src/events/juce_MessageManager.cpp
  13. +2
    -0
      src/events/juce_MessageManager.h
  14. +13
    -23
      src/gui/components/controls/juce_ComboBox.cpp
  15. +1
    -3
      src/gui/components/controls/juce_ComboBox.h
  16. +42
    -22
      src/gui/components/controls/juce_Slider.cpp
  17. +20
    -0
      src/gui/components/controls/juce_Slider.h
  18. +8
    -4
      src/gui/components/controls/juce_TableHeaderComponent.cpp
  19. +6
    -20
      src/gui/components/controls/juce_TextEditor.cpp
  20. +4
    -13
      src/gui/components/controls/juce_Toolbar.cpp
  21. +2
    -0
      src/gui/components/filebrowser/juce_FileChooser.cpp
  22. +125
    -78
      src/gui/components/filebrowser/juce_FileChooserDialogBox.cpp
  23. +11
    -15
      src/gui/components/filebrowser/juce_FileChooserDialogBox.h
  24. +33
    -2
      src/gui/components/juce_Component.cpp
  25. +8
    -1
      src/gui/components/juce_Component.h
  26. +8
    -15
      src/gui/components/juce_ModalComponentManager.cpp
  27. +222
    -1
      src/gui/components/juce_ModalComponentManager.h
  28. +72
    -34
      src/gui/components/keyboard/juce_KeyMappingEditorComponent.cpp
  29. +8
    -4
      src/gui/components/layout/juce_TabbedButtonBar.cpp
  30. +1
    -0
      src/gui/components/layout/juce_TabbedButtonBar.h
  31. +10
    -24
      src/gui/components/menus/juce_MenuBarComponent.cpp
  32. +1
    -2
      src/gui/components/menus/juce_MenuBarComponent.h
  33. +128
    -62
      src/gui/components/menus/juce_PopupMenu.cpp
  34. +43
    -4
      src/gui/components/menus/juce_PopupMenu.h
  35. +3
    -3
      src/gui/components/special/juce_AudioDeviceSelectorComponent.cpp
  36. +1
    -1
      src/gui/components/special/juce_PreferencesPanel.cpp
  37. +46
    -15
      src/gui/components/windows/juce_AlertWindow.cpp
  38. +89
    -14
      src/gui/components/windows/juce_AlertWindow.h
  39. +43
    -16
      src/gui/components/windows/juce_DialogWindow.cpp
  40. +45
    -0
      src/gui/components/windows/juce_DialogWindow.h
  41. +2
    -0
      src/gui/components/windows/juce_SplashScreen.cpp
  42. +2
    -0
      src/gui/components/windows/juce_ThreadWithProgressWindow.cpp
  43. +76
    -66
      src/gui/graphics/imaging/image_file_formats/juce_PNGLoader.cpp
  44. +16
    -19
      src/native/android/java/JuceAppActivity.java
  45. +0
    -6
      src/native/android/juce_android_Messaging.cpp
  46. +2
    -0
      src/native/mac/juce_mac_MessageManager.mm
  47. +5
    -0
      src/utilities/juce_FileBasedDocument.cpp

+ 3
- 3
extras/JuceDemo/Source/demos/InterprocessCommsDemo.cpp View File

@@ -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...");
} }
} }


+ 7
- 3
extras/JuceDemo/Source/demos/ThreadingDemo.cpp View File

@@ -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.


+ 15
- 5
extras/JuceDemo/Source/demos/TreeViewDemo.cpp View File

@@ -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());


+ 173
- 166
extras/JuceDemo/Source/demos/WidgetsDemo.cpp View File

@@ -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*)


+ 2
- 5
extras/audio plugins/wrapper/Standalone/juce_StandaloneFilterWindow.cpp View File

@@ -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;


+ 639
- 406
juce_amalgamated.cpp
File diff suppressed because it is too large
View File


+ 895
- 489
juce_amalgamated.h
File diff suppressed because it is too large
View File


+ 6
- 6
src/application/juce_ApplicationProperties.cpp View File

@@ -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;


+ 3
- 2
src/audio/plugins/juce_PluginListComponent.cpp View File

@@ -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

+ 11
- 0
src/core/juce_PlatformDefs.h View File

@@ -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__

+ 1
- 1
src/core/juce_StandardHeader.h View File

@@ -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.


+ 1
- 1
src/events/juce_MessageManager.cpp View File

@@ -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


+ 2
- 0
src/events/juce_MessageManager.h View File

@@ -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.


+ 13
- 23
src/gui/components/controls/juce_ComboBox.cpp View File

@@ -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));
} }
} }


+ 1
- 3
src/gui/components/controls/juce_ComboBox.h View File

@@ -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);
}; };


+ 42
- 22
src/gui/components/controls/juce_Slider.cpp View File

@@ -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)
{ {


+ 20
- 0
src/gui/components/controls/juce_Slider.h View File

@@ -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.


+ 8
- 4
src/gui/components/controls/juce_TableHeaderComponent.cpp View File

@@ -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));
} }
} }


+ 6
- 20
src/gui/components/controls/juce_TextEditor.cpp View File

@@ -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));
} }
} }
} }


+ 4
- 13
src/gui/components/controls/juce_Toolbar.cpp View File

@@ -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);
} }


+ 2
- 0
src/gui/components/filebrowser/juce_FileChooser.cpp View File

@@ -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()


+ 125
- 78
src/gui/components/filebrowser/juce_FileChooserDialogBox.cpp View File

@@ -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();
}
} }


+ 11
- 15
src/gui/components/filebrowser/juce_FileChooserDialogBox.h View File

@@ -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);
}; };


+ 33
- 2
src/gui/components/juce_Component.cpp View File

@@ -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);


+ 8
- 1
src/gui/components/juce_Component.h View File

@@ -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.


+ 8
- 15
src/gui/components/juce_ModalComponentManager.cpp View File

@@ -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

+ 222
- 1
src/gui/components/juce_ModalComponentManager.h View File

@@ -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__

+ 72
- 34
src/gui/components/keyboard/juce_KeyMappingEditorComponent.cpp View File

@@ -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:


+ 8
- 4
src/gui/components/layout/juce_TabbedButtonBar.cpp View File

@@ -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));
} }
//============================================================================== //==============================================================================


+ 1
- 0
src/gui/components/layout/juce_TabbedButtonBar.h View File

@@ -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);
}; };


+ 10
- 24
src/gui/components/menus/juce_MenuBarComponent.cpp View File

@@ -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;


+ 1
- 2
src/gui/components/menus/juce_MenuBarComponent.h View File

@@ -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);
}; };


+ 128
- 62
src/gui/components/menus/juce_PopupMenu.cpp View File

@@ -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()


+ 43
- 4
src/gui/components/menus/juce_PopupMenu.h View File

@@ -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);
}; };


+ 3
- 3
src/gui/components/special/juce_AudioDeviceSelectorComponent.cpp View File

@@ -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);
} }
} }


+ 1
- 1
src/gui/components/special/juce_PreferencesPanel.cpp View File

@@ -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);
} }
//============================================================================== //==============================================================================


+ 46
- 15
src/gui/components/windows/juce_AlertWindow.cpp View File

@@ -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

+ 89
- 14
src/gui/components/windows/juce_AlertWindow.h View File

@@ -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.


+ 43
- 16
src/gui/components/windows/juce_DialogWindow.cpp View File

@@ -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

+ 45
- 0
src/gui/components/windows/juce_DialogWindow.h View File

@@ -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 */


+ 2
- 0
src/gui/components/windows/juce_SplashScreen.cpp View File

@@ -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();


+ 2
- 0
src/gui/components/windows/juce_ThreadWithProgressWindow.cpp View File

@@ -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)
{ {


+ 76
- 66
src/gui/graphics/imaging/image_file_formats/juce_PNGLoader.cpp View File

@@ -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;


+ 16
- 19
src/native/android/java/JuceAppActivity.java View File

@@ -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);


+ 0
- 6
src/native/android/juce_android_Messaging.cpp View File

@@ -121,10 +121,4 @@ void MessageManager::stopDispatchLoop()
quitMessagePosted = true; quitMessagePosted = true;
} }
bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor)
{
juce_dispatchNextMessageOnSystemQueue (false);
return false;
}
#endif #endif

+ 2
- 0
src/native/mac/juce_mac_MessageManager.mm View File

@@ -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()


+ 5
- 0
src/utilities/juce_FileBasedDocument.cpp View File

@@ -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

Loading…
Cancel
Save