|
- /*
- ==============================================================================
-
- This file is part of the JUCE library.
- Copyright (c) 2015 - ROLI Ltd.
-
- Permission is granted to use this software under the terms of either:
- a) the GPL v2 (or any later version)
- b) the Affero GPL v3
-
- Details of these licenses can be found at: www.gnu.org/licenses
-
- JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- ------------------------------------------------------------------------------
-
- To release a closed-source product which uses JUCE, commercial licenses are
- available: visit www.juce.com for more information.
-
- ==============================================================================
- */
-
- static juce_wchar getDefaultPasswordChar() noexcept
- {
- #if JUCE_LINUX
- return 0x2022;
- #else
- return 0x25cf;
- #endif
- }
-
- //==============================================================================
- AlertWindow::AlertWindow (const String& title,
- const String& message,
- AlertIconType iconType,
- Component* comp)
- : TopLevelWindow (title, true),
- alertIconType (iconType),
- associatedComponent (comp),
- escapeKeyCancels (true)
- {
- setAlwaysOnTop (juce_areThereAnyAlwaysOnTopWindows());
-
- if (message.isEmpty())
- text = " "; // to force an update if the message is empty
-
- setMessage (message);
-
- AlertWindow::lookAndFeelChanged();
- constrainer.setMinimumOnscreenAmounts (0x10000, 0x10000, 0x10000, 0x10000);
- }
-
- AlertWindow::~AlertWindow()
- {
- removeAllChildren();
- }
-
- void AlertWindow::userTriedToCloseWindow()
- {
- if (escapeKeyCancels || buttons.size() > 0)
- exitModalState (0);
- }
-
- //==============================================================================
- void AlertWindow::setMessage (const String& message)
- {
- const String newMessage (message.substring (0, 2048));
-
- if (text != newMessage)
- {
- text = newMessage;
- updateLayout (true);
- repaint();
- }
- }
-
- //==============================================================================
- void AlertWindow::buttonClicked (Button* button)
- {
- if (Component* parent = button->getParentComponent())
- parent->exitModalState (button->getCommandID());
- }
-
- //==============================================================================
- void AlertWindow::addButton (const String& name,
- const int returnValue,
- const KeyPress& shortcutKey1,
- const KeyPress& shortcutKey2)
- {
- TextButton* const b = new TextButton (name, String());
- buttons.add (b);
-
- b->setWantsKeyboardFocus (true);
- b->setMouseClickGrabsKeyboardFocus (false);
- b->setCommandToTrigger (0, returnValue, false);
- b->addShortcut (shortcutKey1);
- b->addShortcut (shortcutKey2);
- b->addListener (this);
- b->changeWidthToFitText (getLookAndFeel().getAlertWindowButtonHeight());
-
- addAndMakeVisible (b, 0);
-
- updateLayout (false);
- }
-
- int AlertWindow::getNumButtons() const
- {
- return buttons.size();
- }
-
- void AlertWindow::triggerButtonClick (const String& buttonName)
- {
- for (int i = buttons.size(); --i >= 0;)
- {
- TextButton* const b = buttons.getUnchecked(i);
-
- if (buttonName == b->getName())
- {
- b->triggerClick();
- break;
- }
- }
- }
-
- void AlertWindow::setEscapeKeyCancels (bool shouldEscapeKeyCancel)
- {
- escapeKeyCancels = shouldEscapeKeyCancel;
- }
-
- //==============================================================================
- void AlertWindow::addTextEditor (const String& name,
- const String& initialContents,
- const String& onScreenLabel,
- const bool isPasswordBox)
- {
- TextEditor* ed = new TextEditor (name, isPasswordBox ? getDefaultPasswordChar() : 0);
- ed->setSelectAllWhenFocused (true);
- ed->setEscapeAndReturnKeysConsumed (false);
- textBoxes.add (ed);
- allComps.add (ed);
-
- ed->setColour (TextEditor::outlineColourId, findColour (ComboBox::outlineColourId));
- ed->setFont (getLookAndFeel().getAlertWindowMessageFont());
- addAndMakeVisible (ed);
- ed->setText (initialContents);
- ed->setCaretPosition (initialContents.length());
- textboxNames.add (onScreenLabel);
-
- updateLayout (false);
- }
-
- TextEditor* AlertWindow::getTextEditor (const String& nameOfTextEditor) const
- {
- for (int i = textBoxes.size(); --i >= 0;)
- if (textBoxes.getUnchecked(i)->getName() == nameOfTextEditor)
- return textBoxes.getUnchecked(i);
-
- return nullptr;
- }
-
- String AlertWindow::getTextEditorContents (const String& nameOfTextEditor) const
- {
- if (TextEditor* const t = getTextEditor (nameOfTextEditor))
- return t->getText();
-
- return String();
- }
-
-
- //==============================================================================
- void AlertWindow::addComboBox (const String& name,
- const StringArray& items,
- const String& onScreenLabel)
- {
- ComboBox* const cb = new ComboBox (name);
- comboBoxes.add (cb);
- allComps.add (cb);
-
- cb->addItemList (items, 1);
-
- addAndMakeVisible (cb);
- cb->setSelectedItemIndex (0);
-
- comboBoxNames.add (onScreenLabel);
- updateLayout (false);
- }
-
- ComboBox* AlertWindow::getComboBoxComponent (const String& nameOfList) const
- {
- for (int i = comboBoxes.size(); --i >= 0;)
- if (comboBoxes.getUnchecked(i)->getName() == nameOfList)
- return comboBoxes.getUnchecked(i);
-
- return nullptr;
- }
-
- //==============================================================================
- class AlertTextComp : public TextEditor
- {
- public:
- AlertTextComp (AlertWindow& owner, const String& message, const Font& font)
- {
- setReadOnly (true);
- setMultiLine (true, true);
- setCaretVisible (false);
- setScrollbarsShown (true);
- lookAndFeelChanged();
- setWantsKeyboardFocus (false);
-
- setFont (font);
- setText (message, false);
-
- bestWidth = 2 * (int) std::sqrt (font.getHeight() * font.getStringWidth (message));
-
- if (owner.isColourSpecified (AlertWindow::textColourId))
- setColour (TextEditor::textColourId, owner.findColour (AlertWindow::textColourId));
-
- setColour (TextEditor::backgroundColourId, Colours::transparentBlack);
- setColour (TextEditor::outlineColourId, Colours::transparentBlack);
- setColour (TextEditor::shadowColourId, Colours::transparentBlack);
- }
-
- int getPreferredWidth() const noexcept { return bestWidth; }
-
- void updateLayout (const int width)
- {
- AttributedString s;
- s.setJustification (Justification::topLeft);
- s.append (getText(), getFont());
-
- TextLayout text;
- text.createLayoutWithBalancedLineLengths (s, width - 8.0f);
- setSize (width, jmin (width, (int) (text.getHeight() + getFont().getHeight())));
- }
-
- private:
- int bestWidth;
-
- JUCE_DECLARE_NON_COPYABLE (AlertTextComp)
- };
-
- void AlertWindow::addTextBlock (const String& textBlock)
- {
- AlertTextComp* const c = new AlertTextComp (*this, textBlock, getLookAndFeel().getAlertWindowMessageFont());
- textBlocks.add (c);
- allComps.add (c);
-
- addAndMakeVisible (c);
-
- updateLayout (false);
- }
-
- //==============================================================================
- void AlertWindow::addProgressBarComponent (double& progressValue)
- {
- ProgressBar* const pb = new ProgressBar (progressValue);
- progressBars.add (pb);
- allComps.add (pb);
-
- addAndMakeVisible (pb);
-
- updateLayout (false);
- }
-
- //==============================================================================
- void AlertWindow::addCustomComponent (Component* const component)
- {
- customComps.add (component);
- allComps.add (component);
-
- addAndMakeVisible (component);
-
- updateLayout (false);
- }
-
- int AlertWindow::getNumCustomComponents() const
- {
- return customComps.size();
- }
-
- Component* AlertWindow::getCustomComponent (const int index) const
- {
- return customComps [index];
- }
-
- Component* AlertWindow::removeCustomComponent (const int index)
- {
- Component* const c = getCustomComponent (index);
-
- if (c != nullptr)
- {
- customComps.removeFirstMatchingValue (c);
- allComps.removeFirstMatchingValue (c);
- removeChildComponent (c);
-
- updateLayout (false);
- }
-
- return c;
- }
-
- //==============================================================================
- void AlertWindow::paint (Graphics& g)
- {
- getLookAndFeel().drawAlertBox (g, *this, textArea, textLayout);
-
- g.setColour (findColour (textColourId));
- g.setFont (getLookAndFeel().getAlertWindowFont());
-
- for (int i = textBoxes.size(); --i >= 0;)
- {
- const TextEditor* const te = textBoxes.getUnchecked(i);
-
- g.drawFittedText (textboxNames[i],
- te->getX(), te->getY() - 14,
- te->getWidth(), 14,
- Justification::centredLeft, 1);
- }
-
- for (int i = comboBoxNames.size(); --i >= 0;)
- {
- const ComboBox* const cb = comboBoxes.getUnchecked(i);
-
- g.drawFittedText (comboBoxNames[i],
- cb->getX(), cb->getY() - 14,
- cb->getWidth(), 14,
- Justification::centredLeft, 1);
- }
-
- for (int i = customComps.size(); --i >= 0;)
- {
- const Component* const c = customComps.getUnchecked(i);
-
- g.drawFittedText (c->getName(),
- c->getX(), c->getY() - 14,
- c->getWidth(), 14,
- Justification::centredLeft, 1);
- }
- }
-
- void AlertWindow::updateLayout (const bool onlyIncreaseSize)
- {
- const int titleH = 24;
- const int iconWidth = 80;
-
- LookAndFeel& lf = getLookAndFeel();
-
- const Font messageFont (lf.getAlertWindowMessageFont());
-
- const int wid = jmax (messageFont.getStringWidth (text),
- messageFont.getStringWidth (getName()));
-
- const int sw = (int) std::sqrt (messageFont.getHeight() * wid);
- int w = jmin (300 + sw * 2, (int) (getParentWidth() * 0.7f));
- const int edgeGap = 10;
- const int labelHeight = 18;
- int iconSpace = 0;
-
- AttributedString attributedText;
- attributedText.append (getName(), lf.getAlertWindowTitleFont());
-
- if (text.isNotEmpty())
- attributedText.append ("\n\n" + text, messageFont);
-
- attributedText.setColour (findColour (textColourId));
-
- if (alertIconType == NoIcon)
- {
- attributedText.setJustification (Justification::centredTop);
- textLayout.createLayoutWithBalancedLineLengths (attributedText, (float) w);
- }
- else
- {
- attributedText.setJustification (Justification::topLeft);
- textLayout.createLayoutWithBalancedLineLengths (attributedText, (float) w);
- iconSpace = iconWidth;
- }
-
- w = jmax (350, (int) textLayout.getWidth() + iconSpace + edgeGap * 4);
- w = jmin (w, (int) (getParentWidth() * 0.7f));
-
- const int textLayoutH = (int) textLayout.getHeight();
- const int textBottom = 16 + titleH + textLayoutH;
- int h = textBottom;
-
- int buttonW = 40;
- for (int i = 0; i < buttons.size(); ++i)
- buttonW += 16 + buttons.getUnchecked (i)->getWidth();
-
- w = jmax (buttonW, w);
-
- h += (textBoxes.size() + comboBoxes.size() + progressBars.size()) * 50;
-
- if (buttons.size() > 0)
- h += 20 + buttons.getUnchecked (0)->getHeight();
-
- for (int i = customComps.size(); --i >= 0;)
- {
- Component* c = customComps.getUnchecked (i);
- w = jmax (w, (c->getWidth() * 100) / 80);
- h += 10 + c->getHeight();
-
- if (c->getName().isNotEmpty())
- h += labelHeight;
- }
-
- for (int i = textBlocks.size(); --i >= 0;)
- {
- const AlertTextComp* const ac = static_cast<const AlertTextComp*> (textBlocks.getUnchecked(i));
- w = jmax (w, ac->getPreferredWidth());
- }
-
- w = jmin (w, (int) (getParentWidth() * 0.7f));
-
- for (int i = textBlocks.size(); --i >= 0;)
- {
- AlertTextComp* const ac = static_cast<AlertTextComp*> (textBlocks.getUnchecked(i));
- ac->updateLayout ((int) (w * 0.8f));
- h += ac->getHeight() + 10;
- }
-
- h = jmin (getParentHeight() - 50, h);
-
- if (onlyIncreaseSize)
- {
- w = jmax (w, getWidth());
- h = jmax (h, getHeight());
- }
-
- if (! isVisible())
- centreAroundComponent (associatedComponent, w, h);
- else
- setBounds (getBounds().withSizeKeepingCentre (w, h));
-
- textArea.setBounds (edgeGap, edgeGap, w - (edgeGap * 2), h - edgeGap);
-
- const int spacer = 16;
- int totalWidth = -spacer;
-
- for (int i = buttons.size(); --i >= 0;)
- totalWidth += buttons.getUnchecked(i)->getWidth() + spacer;
-
- int x = (w - totalWidth) / 2;
- int y = (int) (getHeight() * 0.95f);
-
- for (int i = 0; i < buttons.size(); ++i)
- {
- TextButton* const c = buttons.getUnchecked(i);
- int ny = proportionOfHeight (0.95f) - c->getHeight();
- c->setTopLeftPosition (x, ny);
- if (ny < y)
- y = ny;
-
- x += c->getWidth() + spacer;
-
- c->toFront (false);
- }
-
- y = textBottom;
-
- for (int i = 0; i < allComps.size(); ++i)
- {
- Component* const c = allComps.getUnchecked(i);
- h = 22;
-
- const int comboIndex = comboBoxes.indexOf (dynamic_cast<ComboBox*> (c));
- if (comboIndex >= 0 && comboBoxNames [comboIndex].isNotEmpty())
- y += labelHeight;
-
- const int tbIndex = textBoxes.indexOf (dynamic_cast<TextEditor*> (c));
- if (tbIndex >= 0 && textboxNames[tbIndex].isNotEmpty())
- y += labelHeight;
-
- if (customComps.contains (c))
- {
- if (c->getName().isNotEmpty())
- y += labelHeight;
-
- c->setTopLeftPosition (proportionOfWidth (0.1f), y);
- h = c->getHeight();
- }
- else if (textBlocks.contains (c))
- {
- c->setTopLeftPosition ((getWidth() - c->getWidth()) / 2, y);
- h = c->getHeight();
- }
- else
- {
- c->setBounds (proportionOfWidth (0.1f), y, proportionOfWidth (0.8f), h);
- }
-
- y += h + 10;
- }
-
- setWantsKeyboardFocus (getNumChildComponents() == 0);
- }
-
- bool AlertWindow::containsAnyExtraComponents() const
- {
- return allComps.size() > 0;
- }
-
- //==============================================================================
- void AlertWindow::mouseDown (const MouseEvent& e)
- {
- dragger.startDraggingComponent (this, e);
- }
-
- void AlertWindow::mouseDrag (const MouseEvent& e)
- {
- dragger.dragComponent (this, e, &constrainer);
- }
-
- bool AlertWindow::keyPressed (const KeyPress& key)
- {
- for (int i = buttons.size(); --i >= 0;)
- {
- TextButton* const b = buttons.getUnchecked(i);
-
- if (b->isRegisteredForShortcut (key))
- {
- b->triggerClick();
- return true;
- }
- }
-
- if (key.isKeyCode (KeyPress::escapeKey) && escapeKeyCancels && buttons.size() == 0)
- {
- exitModalState (0);
- return true;
- }
-
- if (key.isKeyCode (KeyPress::returnKey) && buttons.size() == 1)
- {
- buttons.getUnchecked(0)->triggerClick();
- return true;
- }
-
- return false;
- }
-
- void AlertWindow::lookAndFeelChanged()
- {
- const int newFlags = getLookAndFeel().getAlertBoxWindowFlags();
-
- setUsingNativeTitleBar ((newFlags & ComponentPeer::windowHasTitleBar) != 0);
- setDropShadowEnabled (isOpaque() && (newFlags & ComponentPeer::windowHasDropShadow) != 0);
- updateLayout (false);
- }
-
- int AlertWindow::getDesktopWindowStyleFlags() const
- {
- return getLookAndFeel().getAlertBoxWindowFlags();
- }
-
- //==============================================================================
- class AlertWindowInfo
- {
- public:
- AlertWindowInfo (const String& t, const String& m, Component* component,
- AlertWindow::AlertIconType icon, int numButts,
- ModalComponentManager::Callback* cb, bool runModally)
- : title (t), message (m), iconType (icon), numButtons (numButts),
- returnValue (0), associatedComponent (component),
- callback (cb), modal (runModally)
- {
- }
-
- String title, message, button1, button2, button3;
-
- int invoke() const
- {
- MessageManager::getInstance()->callFunctionOnMessageThread (showCallback, (void*) this);
- return returnValue;
- }
-
- private:
- AlertWindow::AlertIconType iconType;
- int numButtons, returnValue;
- WeakReference<Component> associatedComponent;
- ModalComponentManager::Callback* callback;
- bool modal;
-
- void show()
- {
- LookAndFeel& lf = associatedComponent != nullptr ? associatedComponent->getLookAndFeel()
- : LookAndFeel::getDefaultLookAndFeel();
-
- ScopedPointer<Component> alertBox (lf.createAlertWindow (title, message, button1, button2, button3,
- iconType, numButtons, associatedComponent));
-
- jassert (alertBox != nullptr); // you have to return one of these!
-
- #if JUCE_MODAL_LOOPS_PERMITTED
- if (modal)
- {
- returnValue = alertBox->runModalLoop();
- }
- else
- #endif
- {
- ignoreUnused (modal);
-
- alertBox->enterModalState (true, callback, true);
- alertBox.release();
- }
- }
-
- static void* showCallback (void* userData)
- {
- static_cast<AlertWindowInfo*> (userData)->show();
- return nullptr;
- }
- };
-
- #if JUCE_MODAL_LOOPS_PERMITTED
- void AlertWindow::showMessageBox (AlertIconType iconType,
- const String& title,
- const String& message,
- const String& buttonText,
- Component* associatedComponent)
- {
- if (LookAndFeel::getDefaultLookAndFeel().isUsingNativeAlertWindows())
- {
- NativeMessageBox::showMessageBox (iconType, title, message, associatedComponent);
- }
- else
- {
- AlertWindowInfo info (title, message, associatedComponent, iconType, 1, nullptr, 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,
- ModalComponentManager::Callback* callback)
- {
- if (LookAndFeel::getDefaultLookAndFeel().isUsingNativeAlertWindows())
- {
- NativeMessageBox::showMessageBoxAsync (iconType, title, message, associatedComponent, callback);
- }
- else
- {
- AlertWindowInfo info (title, message, associatedComponent, iconType, 1, callback, false);
- info.button1 = buttonText.isEmpty() ? TRANS("OK") : buttonText;
-
- info.invoke();
- }
- }
-
- bool AlertWindow::showOkCancelBox (AlertIconType iconType,
- const String& title,
- const String& message,
- const String& button1Text,
- const String& button2Text,
- Component* associatedComponent,
- ModalComponentManager::Callback* callback)
- {
- if (LookAndFeel::getDefaultLookAndFeel().isUsingNativeAlertWindows())
- return NativeMessageBox::showOkCancelBox (iconType, title, message, associatedComponent, callback);
-
- AlertWindowInfo info (title, message, associatedComponent, iconType, 2, callback, callback == nullptr);
- info.button1 = button1Text.isEmpty() ? TRANS("OK") : button1Text;
- info.button2 = button2Text.isEmpty() ? TRANS("Cancel") : button2Text;
-
- return info.invoke() != 0;
- }
-
- int AlertWindow::showYesNoCancelBox (AlertIconType iconType,
- const String& title,
- const String& message,
- const String& button1Text,
- const String& button2Text,
- const String& button3Text,
- Component* associatedComponent,
- ModalComponentManager::Callback* callback)
- {
- if (LookAndFeel::getDefaultLookAndFeel().isUsingNativeAlertWindows())
- return NativeMessageBox::showYesNoCancelBox (iconType, title, message, associatedComponent, callback);
-
- AlertWindowInfo info (title, message, associatedComponent, iconType, 3, callback, callback == nullptr);
- info.button1 = button1Text.isEmpty() ? TRANS("Yes") : button1Text;
- info.button2 = button2Text.isEmpty() ? TRANS("No") : button2Text;
- info.button3 = button3Text.isEmpty() ? TRANS("Cancel") : button3Text;
-
- return info.invoke();
- }
-
- #if JUCE_MODAL_LOOPS_PERMITTED
- bool AlertWindow::showNativeDialogBox (const String& title,
- const String& bodyText,
- bool isOkCancel)
- {
- if (isOkCancel)
- return NativeMessageBox::showOkCancelBox (AlertWindow::NoIcon, title, bodyText);
-
- NativeMessageBox::showMessageBox (AlertWindow::NoIcon, title, bodyText);
- return true;
- }
- #endif
|