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.

499 lines
16KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2013 - Raw Material Software 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. //==============================================================================
  18. String hexString8Digits (int value);
  19. String createAlphaNumericUID();
  20. String createGUID (const String& seed); // Turns a seed into a windows GUID
  21. String escapeSpaces (const String& text); // replaces spaces with blackslash-space
  22. String addQuotesIfContainsSpaces (const String& text);
  23. StringPairArray parsePreprocessorDefs (const String& defs);
  24. StringPairArray mergePreprocessorDefs (StringPairArray inheritedDefs, const StringPairArray& overridingDefs);
  25. String createGCCPreprocessorFlags (const StringPairArray& defs);
  26. String replacePreprocessorDefs (const StringPairArray& definitions, String sourceString);
  27. StringArray getSearchPathsFromString (const String& searchPath);
  28. StringArray getCommaOrWhitespaceSeparatedItems (const String&);
  29. void setValueIfVoid (Value value, const var& defaultValue);
  30. void addPlistDictionaryKey (XmlElement* xml, const String& key, const String& value);
  31. void addPlistDictionaryKeyBool (XmlElement* xml, const String& key, bool value);
  32. void addPlistDictionaryKeyInt (XmlElement* xml, const String& key, int value);
  33. //==============================================================================
  34. int indexOfLineStartingWith (const StringArray& lines, const String& text, int startIndex);
  35. void autoScrollForMouseEvent (const MouseEvent& e, bool scrollX = true, bool scrollY = true);
  36. void showUTF8ToolWindow (ScopedPointer<Component>& ownerPointer);
  37. void showSVGPathDataToolWindow (ScopedPointer<Component>& ownerPointer);
  38. bool cancelAnyModalComponents();
  39. bool reinvokeCommandAfterCancellingModalComps (const ApplicationCommandTarget::InvocationInfo&);
  40. StringArray getCleanedStringArray (StringArray);
  41. //==============================================================================
  42. class RolloverHelpComp : public Component,
  43. private Timer
  44. {
  45. public:
  46. RolloverHelpComp();
  47. void paint (Graphics&) override;
  48. void timerCallback() override;
  49. private:
  50. Component* lastComp;
  51. String lastTip;
  52. static String findTip (Component*);
  53. };
  54. //==============================================================================
  55. class PropertyListBuilder
  56. {
  57. public:
  58. PropertyListBuilder() {}
  59. void add (PropertyComponent* propertyComp)
  60. {
  61. components.add (propertyComp);
  62. }
  63. void add (PropertyComponent* propertyComp, const String& tooltip)
  64. {
  65. propertyComp->setTooltip (tooltip);
  66. add (propertyComp);
  67. }
  68. void addSearchPathProperty (const Value& value, const String& name, const String& mainHelpText)
  69. {
  70. add (new TextPropertyComponent (value, name, 16384, true),
  71. mainHelpText + " Use semi-colons or new-lines to separate multiple paths.");
  72. }
  73. void setPreferredHeight (int height)
  74. {
  75. for (int j = components.size(); --j >= 0;)
  76. components.getUnchecked(j)->setPreferredHeight (height);
  77. }
  78. Array <PropertyComponent*> components;
  79. private:
  80. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PropertyListBuilder)
  81. };
  82. //==============================================================================
  83. // A ValueSource which takes an input source, and forwards any changes in it.
  84. // This class is a handy way to create sources which re-map a value.
  85. class ValueSourceFilter : public Value::ValueSource,
  86. public Value::Listener
  87. {
  88. public:
  89. ValueSourceFilter (const Value& source) : sourceValue (source)
  90. {
  91. sourceValue.addListener (this);
  92. }
  93. void valueChanged (Value&) override { sendChangeMessage (true); }
  94. protected:
  95. Value sourceValue;
  96. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ValueSourceFilter)
  97. };
  98. //==============================================================================
  99. class FloatingToolWindow : public DialogWindow
  100. {
  101. public:
  102. FloatingToolWindow (const String& title,
  103. const String& windowPosPropertyName,
  104. Component* content,
  105. ScopedPointer<Component>& ownerPointer,
  106. int defaultW, int defaultH,
  107. int minW, int minH,
  108. int maxW, int maxH)
  109. : DialogWindow (title, Colours::darkgrey, true, true),
  110. windowPosProperty (windowPosPropertyName),
  111. owner (ownerPointer)
  112. {
  113. setUsingNativeTitleBar (true);
  114. setResizable (true, true);
  115. setResizeLimits (minW, minH, maxW, maxH);
  116. setContentOwned (content, false);
  117. const String windowState (getGlobalProperties().getValue (windowPosProperty));
  118. if (windowState.isNotEmpty())
  119. restoreWindowStateFromString (windowState);
  120. else
  121. centreAroundComponent (Component::getCurrentlyFocusedComponent(), defaultW, defaultH);
  122. setVisible (true);
  123. owner = this;
  124. }
  125. ~FloatingToolWindow()
  126. {
  127. getGlobalProperties().setValue (windowPosProperty, getWindowStateAsString());
  128. }
  129. void closeButtonPressed() override
  130. {
  131. owner = nullptr;
  132. }
  133. private:
  134. String windowPosProperty;
  135. ScopedPointer<Component>& owner;
  136. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FloatingToolWindow)
  137. };
  138. //==============================================================================
  139. class PopupColourSelector : public Component,
  140. public ChangeListener,
  141. public Value::Listener,
  142. public ButtonListener
  143. {
  144. public:
  145. PopupColourSelector (const Value& colour,
  146. Colour defaultCol,
  147. const bool canResetToDefault)
  148. : defaultButton ("Reset to Default"),
  149. colourValue (colour),
  150. defaultColour (defaultCol)
  151. {
  152. addAndMakeVisible (selector);
  153. selector.setName ("Colour");
  154. selector.setCurrentColour (getColour());
  155. selector.addChangeListener (this);
  156. if (canResetToDefault)
  157. {
  158. addAndMakeVisible (defaultButton);
  159. defaultButton.addListener (this);
  160. }
  161. colourValue.addListener (this);
  162. setSize (300, 400);
  163. }
  164. void resized()
  165. {
  166. if (defaultButton.isVisible())
  167. {
  168. selector.setBounds (0, 0, getWidth(), getHeight() - 30);
  169. defaultButton.changeWidthToFitText (22);
  170. defaultButton.setTopLeftPosition (10, getHeight() - 26);
  171. }
  172. else
  173. {
  174. selector.setBounds (getLocalBounds());
  175. }
  176. }
  177. Colour getColour() const
  178. {
  179. if (colourValue.toString().isEmpty())
  180. return defaultColour;
  181. return Colour::fromString (colourValue.toString());
  182. }
  183. void setColour (Colour newColour)
  184. {
  185. if (getColour() != newColour)
  186. {
  187. if (newColour == defaultColour && defaultButton.isVisible())
  188. colourValue = var();
  189. else
  190. colourValue = newColour.toDisplayString (true);
  191. }
  192. }
  193. void buttonClicked (Button*) override
  194. {
  195. setColour (defaultColour);
  196. selector.setCurrentColour (defaultColour);
  197. }
  198. void changeListenerCallback (ChangeBroadcaster*) override
  199. {
  200. if (selector.getCurrentColour() != getColour())
  201. setColour (selector.getCurrentColour());
  202. }
  203. void valueChanged (Value&) override
  204. {
  205. selector.setCurrentColour (getColour());
  206. }
  207. private:
  208. StoredSettings::ColourSelectorWithSwatches selector;
  209. TextButton defaultButton;
  210. Value colourValue;
  211. Colour defaultColour;
  212. };
  213. //==============================================================================
  214. /**
  215. A component that shows a colour swatch with hex ARGB value, and which pops up
  216. a colour selector when you click it.
  217. */
  218. class ColourEditorComponent : public Component,
  219. public Value::Listener
  220. {
  221. public:
  222. ColourEditorComponent (UndoManager* um, const Value& colour,
  223. Colour defaultCol, const bool canReset)
  224. : undoManager (um), colourValue (colour), defaultColour (defaultCol),
  225. canResetToDefault (canReset)
  226. {
  227. colourValue.addListener (this);
  228. }
  229. void paint (Graphics& g)
  230. {
  231. const Colour colour (getColour());
  232. g.fillAll (Colours::grey);
  233. g.fillCheckerBoard (getLocalBounds().reduced (2),
  234. 10, 10,
  235. Colour (0xffdddddd).overlaidWith (colour),
  236. Colour (0xffffffff).overlaidWith (colour));
  237. g.setColour (Colours::white.overlaidWith (colour).contrasting());
  238. g.setFont (Font (getHeight() * 0.6f, Font::bold));
  239. g.drawFittedText (colour.toDisplayString (true), getLocalBounds().reduced (2, 1),
  240. Justification::centred, 1);
  241. }
  242. Colour getColour() const
  243. {
  244. if (colourValue.toString().isEmpty())
  245. return defaultColour;
  246. return Colour::fromString (colourValue.toString());
  247. }
  248. void setColour (Colour newColour)
  249. {
  250. if (getColour() != newColour)
  251. {
  252. if (newColour == defaultColour && canResetToDefault)
  253. colourValue = var::null;
  254. else
  255. colourValue = newColour.toDisplayString (true);
  256. }
  257. }
  258. void resetToDefault()
  259. {
  260. setColour (defaultColour);
  261. }
  262. void refresh()
  263. {
  264. const Colour col (getColour());
  265. if (col != lastColour)
  266. {
  267. lastColour = col;
  268. repaint();
  269. }
  270. }
  271. void mouseDown (const MouseEvent&) override
  272. {
  273. if (undoManager != nullptr)
  274. undoManager->beginNewTransaction();
  275. CallOutBox::launchAsynchronously (new PopupColourSelector (colourValue,
  276. defaultColour,
  277. canResetToDefault),
  278. getScreenBounds(), nullptr);
  279. }
  280. void valueChanged (Value&) override
  281. {
  282. refresh();
  283. }
  284. private:
  285. UndoManager* undoManager;
  286. Value colourValue;
  287. Colour lastColour;
  288. const Colour defaultColour;
  289. const bool canResetToDefault;
  290. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ColourEditorComponent)
  291. };
  292. //==============================================================================
  293. class ColourPropertyComponent : public PropertyComponent
  294. {
  295. public:
  296. ColourPropertyComponent (UndoManager* undoManager, const String& name, const Value& colour,
  297. Colour defaultColour, bool canResetToDefault)
  298. : PropertyComponent (name),
  299. colourEditor (undoManager, colour, defaultColour, canResetToDefault)
  300. {
  301. addAndMakeVisible (colourEditor);
  302. }
  303. void resized() override
  304. {
  305. colourEditor.setBounds (getLookAndFeel().getPropertyComponentContentPosition (*this));
  306. }
  307. void refresh() override {}
  308. protected:
  309. ColourEditorComponent colourEditor;
  310. };
  311. //==============================================================================
  312. class FilePathPropertyComponent : public PropertyComponent
  313. {
  314. public:
  315. /** A Property Component for selecting files or folders.
  316. The user may drag files over the property box, enter the path
  317. manually and/or click the '...' button to open a file selection
  318. dialog box
  319. */
  320. FilePathPropertyComponent (Value valueToControl,
  321. const String& propertyDescription,
  322. bool isDirectory,
  323. const String& wildcards = "*",
  324. const File& rootToUseForRelativePaths = File::nonexistent)
  325. : PropertyComponent (propertyDescription),
  326. innerComp (valueToControl, isDirectory, wildcards, rootToUseForRelativePaths)
  327. {
  328. addAndMakeVisible (innerComp);
  329. }
  330. void refresh() override {} // N/A
  331. private:
  332. struct InnerComponent : public Component,
  333. public FileDragAndDropTarget,
  334. private Button::Listener
  335. {
  336. InnerComponent (Value v, bool isDir, const String& wc, const File& rt)
  337. : value (v),
  338. isDirectory (isDir),
  339. highlightForDragAndDrop (false),
  340. wildcards (wc),
  341. root (rt),
  342. button ("...")
  343. {
  344. addAndMakeVisible (textbox);
  345. textbox.getTextValue().referTo (value);
  346. addAndMakeVisible (button);
  347. button.addListener (this);
  348. }
  349. void paintOverChildren (Graphics& g) override
  350. {
  351. if (highlightForDragAndDrop)
  352. {
  353. g.setColour (Colours::green.withAlpha (0.1f));
  354. g.fillRect (getLocalBounds());
  355. }
  356. }
  357. void resized() override
  358. {
  359. Rectangle<int> r (getLocalBounds());
  360. button.setBounds (r.removeFromRight (24));
  361. textbox.setBounds (r);
  362. }
  363. bool isInterestedInFileDrag (const StringArray&) override { return true; }
  364. void fileDragEnter (const StringArray&, int, int) override { highlightForDragAndDrop = true; repaint(); }
  365. void fileDragExit (const StringArray&) override { highlightForDragAndDrop = false; repaint(); }
  366. void filesDropped (const StringArray& files, int, int) override
  367. {
  368. const File firstFile (files[0]);
  369. if (isDirectory)
  370. setTo (firstFile.isDirectory() ? firstFile
  371. : firstFile.getParentDirectory());
  372. else
  373. setTo (firstFile);
  374. }
  375. void buttonClicked (Button*) override
  376. {
  377. const File currentFile (root.getChildFile (value.toString()));
  378. if (isDirectory)
  379. {
  380. FileChooser chooser ("Select directory", currentFile);
  381. if (chooser.browseForDirectory())
  382. setTo (chooser.getResult());
  383. }
  384. else
  385. {
  386. FileChooser chooser ("Select file", currentFile, wildcards);
  387. if (chooser.browseForFileToOpen())
  388. setTo (chooser.getResult());
  389. }
  390. }
  391. void setTo (const File& f)
  392. {
  393. value = (root == File::nonexistent) ? f.getFullPathName()
  394. : f.getRelativePathFrom (root);
  395. }
  396. Value value;
  397. bool isDirectory, highlightForDragAndDrop;
  398. String wildcards;
  399. File root;
  400. TextEditor textbox;
  401. TextButton button;
  402. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (InnerComponent)
  403. };
  404. InnerComponent innerComp; // Used so that the PropertyComponent auto first-child positioning works
  405. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FilePathPropertyComponent)
  406. };