1 /*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2020 - Raw Material Software Limited
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 By using JUCE, you agree to the terms of both the JUCE 6 End-User License
11 Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
12
13 End User License Agreement: www.juce.com/juce-6-licence
14 Privacy Policy: www.juce.com/juce-privacy-policy
15
16 Or: You may also use this code under the terms of the GPL v3 (see
17 www.gnu.org/licenses).
18
19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21 DISCLAIMED.
22
23 ==============================================================================
24 */
25
26 namespace juce
27 {
28
29 struct Spinner : public Component,
30 private Timer
31 {
Spinnerjuce::Spinner32 Spinner() { startTimer (1000 / 50); }
timerCallbackjuce::Spinner33 void timerCallback() override { repaint(); }
34
paintjuce::Spinner35 void paint (Graphics& g) override
36 {
37 getLookAndFeel().drawSpinningWaitAnimation (g, Colours::darkgrey, 0, 0, getWidth(), getHeight());
38 }
39 };
40
41 struct OnlineUnlockForm::OverlayComp : public Component,
42 private Thread,
43 private Timer,
44 private Button::Listener
45 {
OverlayCompjuce::OnlineUnlockForm::OverlayComp46 OverlayComp (OnlineUnlockForm& f, bool hasCancelButton = false)
47 : Thread (String()), form (f)
48 {
49 result.succeeded = false;
50 email = form.emailBox.getText();
51 password = form.passwordBox.getText();
52 addAndMakeVisible (spinner);
53
54 if (hasCancelButton)
55 {
56 cancelButton.reset (new TextButton (TRANS ("Cancel")));
57 addAndMakeVisible (cancelButton.get());
58 cancelButton->addListener (this);
59 }
60
61 startThread (4);
62 }
63
~OverlayCompjuce::OnlineUnlockForm::OverlayComp64 ~OverlayComp() override
65 {
66 stopThread (10000);
67 }
68
paintjuce::OnlineUnlockForm::OverlayComp69 void paint (Graphics& g) override
70 {
71 g.fillAll (Colours::white.withAlpha (0.97f));
72
73 g.setColour (Colours::black);
74 g.setFont (15.0f);
75
76 g.drawFittedText (TRANS("Contacting XYZ...").replace ("XYZ", form.status.getWebsiteName()),
77 getLocalBounds().reduced (20, 0).removeFromTop (proportionOfHeight (0.6f)),
78 Justification::centred, 5);
79 }
80
resizedjuce::OnlineUnlockForm::OverlayComp81 void resized() override
82 {
83 const int spinnerSize = 40;
84 spinner.setBounds ((getWidth() - spinnerSize) / 2, proportionOfHeight (0.6f), spinnerSize, spinnerSize);
85
86 if (cancelButton != nullptr)
87 cancelButton->setBounds (getLocalBounds().removeFromBottom (50).reduced (getWidth() / 4, 5));
88 }
89
runjuce::OnlineUnlockForm::OverlayComp90 void run() override
91 {
92 result = form.status.attemptWebserverUnlock (email, password);
93 startTimer (100);
94 }
95
timerCallbackjuce::OnlineUnlockForm::OverlayComp96 void timerCallback() override
97 {
98 spinner.setVisible (false);
99 stopTimer();
100
101 if (result.errorMessage.isNotEmpty())
102 {
103 AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
104 TRANS("Registration Failed"),
105 result.errorMessage);
106 }
107 else if (result.informativeMessage.isNotEmpty())
108 {
109 AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon,
110 TRANS("Registration Complete!"),
111 result.informativeMessage);
112 }
113 else if (result.urlToLaunch.isNotEmpty())
114 {
115 URL url (result.urlToLaunch);
116 url.launchInDefaultBrowser();
117 }
118
119 // (local copies because we're about to delete this)
120 const bool worked = result.succeeded;
121 OnlineUnlockForm& f = form;
122
123 delete this;
124
125 if (worked)
126 f.dismiss();
127 }
128
buttonClickedjuce::OnlineUnlockForm::OverlayComp129 void buttonClicked (Button* button) override
130 {
131 if (button == cancelButton.get())
132 {
133 form.status.userCancelled();
134
135 spinner.setVisible (false);
136 stopTimer();
137
138 delete this;
139 }
140 }
141
142 OnlineUnlockForm& form;
143 Spinner spinner;
144 OnlineUnlockStatus::UnlockResult result;
145 String email, password;
146
147 std::unique_ptr<TextButton> cancelButton;
148
149 JUCE_LEAK_DETECTOR (OnlineUnlockForm::OverlayComp)
150 };
151
getDefaultPasswordChar()152 static juce_wchar getDefaultPasswordChar() noexcept
153 {
154 #if JUCE_LINUX || JUCE_BSD
155 return 0x2022;
156 #else
157 return 0x25cf;
158 #endif
159 }
160
OnlineUnlockForm(OnlineUnlockStatus & s,const String & userInstructions,bool hasCancelButton,bool overlayHasCancelButton)161 OnlineUnlockForm::OnlineUnlockForm (OnlineUnlockStatus& s,
162 const String& userInstructions,
163 bool hasCancelButton,
164 bool overlayHasCancelButton)
165 : message (String(), userInstructions),
166 passwordBox (String(), getDefaultPasswordChar()),
167 registerButton (TRANS("Register")),
168 cancelButton (TRANS ("Cancel")),
169 status (s),
170 showOverlayCancelButton (overlayHasCancelButton)
171 {
172 // Please supply a message to tell your users what to do!
173 jassert (userInstructions.isNotEmpty());
174
175 setOpaque (true);
176
177 emailBox.setText (status.getUserEmail());
178 message.setJustificationType (Justification::centred);
179
180 addAndMakeVisible (message);
181 addAndMakeVisible (emailBox);
182 addAndMakeVisible (passwordBox);
183 addAndMakeVisible (registerButton);
184
185 if (hasCancelButton)
186 addAndMakeVisible (cancelButton);
187
188 emailBox.setEscapeAndReturnKeysConsumed (false);
189 passwordBox.setEscapeAndReturnKeysConsumed (false);
190
191 registerButton.addShortcut (KeyPress (KeyPress::returnKey));
192
193 registerButton.addListener (this);
194 cancelButton.addListener (this);
195
196 lookAndFeelChanged();
197 setSize (500, 250);
198 }
199
~OnlineUnlockForm()200 OnlineUnlockForm::~OnlineUnlockForm()
201 {
202 unlockingOverlay.deleteAndZero();
203 }
204
paint(Graphics & g)205 void OnlineUnlockForm::paint (Graphics& g)
206 {
207 g.fillAll (Colours::lightgrey);
208 }
209
resized()210 void OnlineUnlockForm::resized()
211 {
212 /* If you're writing a plugin, then DO NOT USE A POP-UP A DIALOG WINDOW!
213 Plugins that create external windows are incredibly annoying for users, and
214 cause all sorts of headaches for hosts. Don't be the person who writes that
215 plugin that irritates everyone with a nagging dialog box every time they scan!
216 */
217 jassert (JUCEApplicationBase::isStandaloneApp() || findParentComponentOfClass<DialogWindow>() == nullptr);
218
219 const int buttonHeight = 22;
220
221 auto r = getLocalBounds().reduced (10, 20);
222
223 auto buttonArea = r.removeFromBottom (buttonHeight);
224 registerButton.changeWidthToFitText (buttonHeight);
225 cancelButton.changeWidthToFitText (buttonHeight);
226
227 const int gap = 20;
228 buttonArea = buttonArea.withSizeKeepingCentre (registerButton.getWidth()
229 + (cancelButton.isVisible() ? gap + cancelButton.getWidth() : 0),
230 buttonHeight);
231 registerButton.setBounds (buttonArea.removeFromLeft (registerButton.getWidth()));
232 buttonArea.removeFromLeft (gap);
233 cancelButton.setBounds (buttonArea);
234
235 r.removeFromBottom (20);
236
237 // (force use of a default system font to make sure it has the password blob character)
238 Font font (Font::getDefaultTypefaceForFont (Font (Font::getDefaultSansSerifFontName(),
239 Font::getDefaultStyle(),
240 5.0f)));
241
242 const int boxHeight = 24;
243 passwordBox.setBounds (r.removeFromBottom (boxHeight));
244 passwordBox.setInputRestrictions (64);
245 passwordBox.setFont (font);
246
247 r.removeFromBottom (20);
248 emailBox.setBounds (r.removeFromBottom (boxHeight));
249 emailBox.setInputRestrictions (512);
250 emailBox.setFont (font);
251
252 r.removeFromBottom (20);
253
254 message.setBounds (r);
255
256 if (unlockingOverlay != nullptr)
257 unlockingOverlay->setBounds (getLocalBounds());
258 }
259
lookAndFeelChanged()260 void OnlineUnlockForm::lookAndFeelChanged()
261 {
262 Colour labelCol (findColour (TextEditor::backgroundColourId).contrasting (0.5f));
263
264 emailBox.setTextToShowWhenEmpty (TRANS("Email Address"), labelCol);
265 passwordBox.setTextToShowWhenEmpty (TRANS("Password"), labelCol);
266 }
267
showBubbleMessage(const String & text,Component & target)268 void OnlineUnlockForm::showBubbleMessage (const String& text, Component& target)
269 {
270 bubble.reset (new BubbleMessageComponent (500));
271 addChildComponent (bubble.get());
272
273 AttributedString attString;
274 attString.append (text, Font (16.0f));
275
276 bubble->showAt (getLocalArea (&target, target.getLocalBounds()),
277 attString, 500, // numMillisecondsBeforeRemoving
278 true, // removeWhenMouseClicked
279 false); // deleteSelfAfterUse
280 }
281
buttonClicked(Button * b)282 void OnlineUnlockForm::buttonClicked (Button* b)
283 {
284 if (b == ®isterButton)
285 attemptRegistration();
286 else if (b == &cancelButton)
287 dismiss();
288 }
289
attemptRegistration()290 void OnlineUnlockForm::attemptRegistration()
291 {
292 if (unlockingOverlay == nullptr)
293 {
294 if (emailBox.getText().trim().length() < 3)
295 {
296 showBubbleMessage (TRANS ("Please enter a valid email address!"), emailBox);
297 return;
298 }
299
300 if (passwordBox.getText().trim().length() < 3)
301 {
302 showBubbleMessage (TRANS ("Please enter a valid password!"), passwordBox);
303 return;
304 }
305
306 status.setUserEmail (emailBox.getText());
307
308 addAndMakeVisible (unlockingOverlay = new OverlayComp (*this, showOverlayCancelButton));
309 resized();
310 unlockingOverlay->enterModalState();
311 }
312 }
313
dismiss()314 void OnlineUnlockForm::dismiss()
315 {
316 delete this;
317 }
318
319 } // namespace juce
320