The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

314 lines
11KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2015 - ROLI Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. #pragma once
  18. class LoginForm : public Component,
  19. private ButtonListener,
  20. private TextEditor::Listener,
  21. private ProjucerLicenses::LoginCallback
  22. {
  23. public:
  24. LoginForm()
  25. : cancelButton (TRANS("Cancel")),
  26. loginButton (TRANS("Login")),
  27. registerButton (TRANS("Register")),
  28. userIDEditor ("User ID text editor"),
  29. passwordEditor ("Password TextEditor", juce_wchar (0x2022)),
  30. userIDLabel ("User-ID Label", TRANS("Username")),
  31. passwordLabel ("Password Label", TRANS("Password")),
  32. errorLabel ("Error Label", String()),
  33. rememberLoginCheckbox (TRANS("Remember login")),
  34. forgotPasswordButton (TRANS("Forgotten your password?"),
  35. URL ("https://auth.roli.com/forgot-password?referer=projucer")),
  36. rememberLogin (true)
  37. {
  38. setLookAndFeel (&lookAndFeel);
  39. ScopedPointer<XmlElement> svg (XmlDocument::parse (BinaryData::projucer_login_bg_svg));
  40. backgroundImage = Drawable::createFromSVG (*svg);
  41. initialiseTextField (passwordEditor, passwordLabel);
  42. addAndMakeVisible (passwordEditor);
  43. initialiseTextField (userIDEditor, userIDLabel);
  44. addAndMakeVisible (userIDEditor);
  45. String userName = ProjucerLicenses::getInstance()->getLoginName();
  46. userIDEditor.setText (userName.isEmpty() ? getLastUserName() : userName);
  47. initialiseLabel (errorLabel, Font::plain, ProjucerDialogLookAndFeel::getErrorTextColour());
  48. addChildComponent (errorLabel);
  49. addChildComponent (spinner);
  50. rememberLoginCheckbox.setColour (ToggleButton::textColourId, Colours::white);
  51. rememberLoginCheckbox.setColour (TextEditor::focusedOutlineColourId, Colours::transparentBlack);
  52. rememberLoginCheckbox.setToggleState (rememberLogin, dontSendNotification);
  53. addAndMakeVisible (rememberLoginCheckbox);
  54. rememberLoginCheckbox.addListener (this);
  55. forgotPasswordButton.setColour (HyperlinkButton::textColourId, Colours::white);
  56. forgotPasswordButton.setFont (ProjucerDialogLookAndFeel::getDialogFont().withHeight (lookAndFeel.labelFontSize), false, Justification::topLeft);
  57. addAndMakeVisible (forgotPasswordButton);
  58. initialiseButton (loginButton, KeyPress::returnKey);
  59. addAndMakeVisible (loginButton);
  60. initialiseButton (registerButton);
  61. addAndMakeVisible (registerButton);
  62. initialiseButton (cancelButton, KeyPress::escapeKey);
  63. addAndMakeVisible (cancelButton);
  64. cancelButton.getProperties().set ("isSecondaryButton", true);
  65. centreWithSize (425, 685);
  66. }
  67. ~LoginForm()
  68. {
  69. ProjucerApplication::getApp().hideLoginForm();
  70. }
  71. //==============================================================================
  72. void paint (Graphics& g) override
  73. {
  74. g.fillAll (Colour (0xff4d4d4d));
  75. g.setColour (Colours::black);
  76. backgroundImage->drawWithin (g, getLocalBounds().toFloat(), RectanglePlacement::centred, 1.0f);
  77. }
  78. void resized() override
  79. {
  80. const int xMargin = 81;
  81. const int yMargin = 132;
  82. const int labelHeight = 24;
  83. const int textFieldHeight = 33;
  84. Rectangle<int> r = getLocalBounds().reduced (xMargin, yMargin);
  85. r.setWidth (r.getWidth() + 1);
  86. Point<int> labelOffset = Point<int> (-6, 4);
  87. userIDLabel.setBounds (r.removeFromTop (labelHeight) + labelOffset);
  88. userIDEditor.setBounds (r.removeFromTop (textFieldHeight));
  89. passwordLabel.setBounds (r.removeFromTop (labelHeight) + labelOffset);
  90. passwordEditor.setBounds (r.removeFromTop (textFieldHeight));
  91. r.removeFromTop (6);
  92. rememberLoginCheckbox.setBounds (r.removeFromTop (labelHeight) + Point<int> (-4, 0));
  93. r.removeFromTop (8);
  94. errorLabel.setBounds (r.removeFromTop (43).withTrimmedLeft (15));
  95. spinner.setBounds (errorLabel.getBounds().withSizeKeepingCentre (20, 20) + Point<int> (-7, -10));
  96. const int buttonHeight = 40;
  97. const int buttonMargin = 13;
  98. loginButton.setBounds (r.removeFromTop (buttonHeight));
  99. r.removeFromTop (buttonMargin);
  100. registerButton.setBounds (r.withHeight (buttonHeight).removeFromLeft ((r.getWidth() - buttonMargin) / 2));
  101. cancelButton.setBounds (r.withHeight (buttonHeight).removeFromRight ((r.getWidth() - buttonMargin) / 2));
  102. r.removeFromTop (45);
  103. forgotPasswordButton.setBounds (r.withHeight (labelHeight) + Point<int> (-2, 0));
  104. }
  105. private:
  106. //==============================================================================
  107. struct Spinner : public Component,
  108. private Timer
  109. {
  110. Spinner()
  111. {
  112. setInterceptsMouseClicks (false, false);
  113. }
  114. void paint (Graphics& g) override
  115. {
  116. getLookAndFeel().drawSpinningWaitAnimation (g, Colours::white, 0, 0, getWidth(), getHeight());
  117. startTimer (50);
  118. }
  119. void timerCallback() override
  120. {
  121. if (isVisible())
  122. repaint();
  123. else
  124. stopTimer();
  125. }
  126. };
  127. //==============================================================================
  128. void initialiseTextField (TextEditor& textField, Label& associatedLabel)
  129. {
  130. textField.setColour (TextEditor::focusedOutlineColourId, Colours::transparentWhite);
  131. textField.setColour (TextEditor::highlightColourId, ProjucerDialogLookAndFeel::getErrorTextColour());
  132. textField.setFont (ProjucerDialogLookAndFeel::getDialogFont().withHeight (17));
  133. textField.addListener (this);
  134. associatedLabel.setColour (Label::textColourId, Colours::white);
  135. addAndMakeVisible (associatedLabel);
  136. }
  137. void initialiseButton (TextButton& button, const int associatedKeyPress = 0)
  138. {
  139. if (associatedKeyPress != 0)
  140. button.addShortcut (KeyPress (associatedKeyPress));
  141. button.addListener (this);
  142. }
  143. void initialiseLabel (Label& label, Font::FontStyleFlags fontFlags, Colour textColour)
  144. {
  145. label.setFont (Font (15.0f, fontFlags));
  146. label.setJustificationType (Justification::topLeft);
  147. label.setColour (Label::textColourId, textColour);
  148. }
  149. //==============================================================================
  150. void buttonClicked (Button* button) override
  151. {
  152. if (button == &cancelButton) cancelButtonClicked();
  153. if (button == &loginButton) loginButtonClicked();
  154. if (button == &registerButton) registerButtonClicked();
  155. if (button == &rememberLoginCheckbox) rememberLoginCheckboxClicked();
  156. }
  157. void cancelButtonClicked()
  158. {
  159. if (DialogWindow* parentDialog = findParentComponentOfClass<DialogWindow>())
  160. parentDialog->exitModalState (-1);
  161. }
  162. void loginButtonClicked()
  163. {
  164. loginName = userIDEditor.getText();
  165. getGlobalProperties().setValue ("lastUserName", loginName);
  166. password = passwordEditor.getText();
  167. if (! isValidEmail (loginName) || password.isEmpty())
  168. {
  169. handleInvalidLogin();
  170. return;
  171. }
  172. loginButton.setEnabled (false);
  173. cancelButton.setEnabled (false);
  174. registerButton.setEnabled (false);
  175. errorLabel.setVisible (false);
  176. spinner.setVisible (true);
  177. ProjucerLicenses::getInstance()->login (loginName, password, rememberLogin, this);
  178. }
  179. void registerButtonClicked()
  180. {
  181. URL (getServerURL() + "projucer_trial").launchInDefaultBrowser();
  182. }
  183. void textEditorReturnKeyPressed (TextEditor&) override
  184. {
  185. loginButtonClicked();
  186. }
  187. void rememberLoginCheckboxClicked()
  188. {
  189. rememberLogin = rememberLoginCheckbox.getToggleState();
  190. }
  191. String getLastUserName() const
  192. {
  193. return getGlobalProperties().getValue ("lastUserName");
  194. }
  195. void handleInvalidLogin()
  196. {
  197. if (!isValidEmail (loginName))
  198. loginError (TRANS ("Please enter a valid e-mail address"), true);
  199. if (password.isEmpty())
  200. loginError (TRANS ("Please specify a valid password"), false);
  201. }
  202. bool isValidEmail (const String& email)
  203. {
  204. return email.isNotEmpty();
  205. }
  206. void loginError (const String& errorMessage, bool hiliteUserID) override
  207. {
  208. spinner.setVisible (false);
  209. errorLabel.setText (errorMessage, dontSendNotification);
  210. errorLabel.setVisible (true);
  211. TextEditor& field = hiliteUserID ? userIDEditor : passwordEditor;
  212. field.setColour (TextEditor::focusedOutlineColourId, Colour (0x84F08080));
  213. field.toFront (true);
  214. loginButton.setEnabled (true);
  215. cancelButton.setEnabled (true);
  216. registerButton.setEnabled (true);
  217. ProjucerApplication::getApp().updateAllBuildTabs();
  218. }
  219. void loginSuccess (const String& username, const String& apiKey) override
  220. {
  221. ignoreUnused (username, apiKey);
  222. spinner.setVisible (false);
  223. if (DialogWindow* parentDialog = findParentComponentOfClass<DialogWindow>())
  224. {
  225. parentDialog->exitModalState (0);
  226. ProjucerApplication::getApp().updateAllBuildTabs();
  227. if (ProjucerLicenses::getInstance()->hasFreeToUseLicense())
  228. {
  229. AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon,
  230. "Free to use license info",
  231. "The free-to-use license expires on 31st January 2017 Midnight GMT");
  232. }
  233. }
  234. }
  235. TextButton cancelButton, loginButton, registerButton;
  236. TextEditor userIDEditor, passwordEditor;
  237. Label userIDLabel, passwordLabel, errorLabel;
  238. ToggleButton rememberLoginCheckbox;
  239. HyperlinkButton forgotPasswordButton;
  240. Spinner spinner;
  241. String loginName, password;
  242. bool rememberLogin;
  243. ScopedPointer<Drawable> backgroundImage;
  244. ProjucerDialogLookAndFeel lookAndFeel;
  245. static String getServerURL() { return "https://my.roli.com/"; }
  246. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LoginForm)
  247. };