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.

433 lines
14KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. //==============================================================================
  19. String hexString8Digits (int value);
  20. String createAlphaNumericUID();
  21. String createGUID (const String& seed); // Turns a seed into a windows GUID
  22. String escapeSpaces (const String& text); // replaces spaces with blackslash-space
  23. String addQuotesIfContainsSpaces (const String& text);
  24. StringPairArray parsePreprocessorDefs (const String& defs);
  25. StringPairArray mergePreprocessorDefs (StringPairArray inheritedDefs, const StringPairArray& overridingDefs);
  26. String createGCCPreprocessorFlags (const StringPairArray& defs);
  27. String replacePreprocessorDefs (const StringPairArray& definitions, String sourceString);
  28. StringArray getSearchPathsFromString (const String& searchPath);
  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. bool cancelAnyModalComponents();
  38. bool reinvokeCommandAfterCancellingModalComps (const ApplicationCommandTarget::InvocationInfo&);
  39. //==============================================================================
  40. struct Icon
  41. {
  42. Icon() : path (nullptr) {}
  43. Icon (const Path& p, const Colour& c) : path (&p), colour (c) {}
  44. Icon (const Path* p, const Colour& c) : path (p), colour (c) {}
  45. void draw (Graphics& g, const Rectangle<float>& area, bool isCrossedOut) const
  46. {
  47. if (path != nullptr)
  48. {
  49. g.setColour (colour);
  50. const RectanglePlacement placement (RectanglePlacement::centred | RectanglePlacement::onlyReduceInSize);
  51. g.fillPath (*path, placement.getTransformToFit (path->getBounds(), area));
  52. if (isCrossedOut)
  53. {
  54. g.setColour (Colours::red.withAlpha (0.8f));
  55. g.drawLine ((float) area.getX(), area.getY() + area.getHeight() * 0.2f,
  56. (float) area.getRight(), area.getY() + area.getHeight() * 0.8f, 3.0f);
  57. }
  58. }
  59. }
  60. Icon withContrastingColourTo (const Colour& background) const
  61. {
  62. return Icon (path, background.contrasting (colour, 0.6f));
  63. }
  64. const Path* path;
  65. Colour colour;
  66. };
  67. //==============================================================================
  68. class RolloverHelpComp : public Component,
  69. private Timer
  70. {
  71. public:
  72. RolloverHelpComp();
  73. void paint (Graphics& g);
  74. void timerCallback();
  75. private:
  76. Component* lastComp;
  77. String lastTip;
  78. static String findTip (Component*);
  79. };
  80. //==============================================================================
  81. class PropertyListBuilder
  82. {
  83. public:
  84. PropertyListBuilder() {}
  85. void add (PropertyComponent* propertyComp)
  86. {
  87. components.add (propertyComp);
  88. }
  89. void add (PropertyComponent* propertyComp, const String& tooltip)
  90. {
  91. propertyComp->setTooltip (tooltip);
  92. add (propertyComp);
  93. }
  94. void addSearchPathProperty (const Value& value, const String& name, const String& mainHelpText)
  95. {
  96. add (new TextPropertyComponent (value, name, 16384, true),
  97. mainHelpText + " Use semi-colons or new-lines to separate multiple paths.");
  98. }
  99. void setPreferredHeight (int height)
  100. {
  101. for (int j = components.size(); --j >= 0;)
  102. components.getUnchecked(j)->setPreferredHeight (height);
  103. }
  104. Array <PropertyComponent*> components;
  105. private:
  106. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PropertyListBuilder)
  107. };
  108. //==============================================================================
  109. class FloatingLabelComponent : public Component
  110. {
  111. public:
  112. FloatingLabelComponent();
  113. void remove();
  114. void update (Component* parent, const String& text, const Colour& textColour,
  115. int x, int y, bool toRight, bool below);
  116. void paint (Graphics& g);
  117. private:
  118. Font font;
  119. Colour colour;
  120. GlyphArrangement glyphs;
  121. };
  122. //==============================================================================
  123. // A ValueSource which takes an input source, and forwards any changes in it.
  124. // This class is a handy way to create sources which re-map a value.
  125. class ValueSourceFilter : public Value::ValueSource,
  126. public Value::Listener
  127. {
  128. public:
  129. ValueSourceFilter (const Value& source) : sourceValue (source)
  130. {
  131. sourceValue.addListener (this);
  132. }
  133. void valueChanged (Value&) { sendChangeMessage (true); }
  134. protected:
  135. Value sourceValue;
  136. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ValueSourceFilter)
  137. };
  138. //==============================================================================
  139. class FloatingToolWindow : public DialogWindow
  140. {
  141. public:
  142. FloatingToolWindow (const String& title,
  143. const String& windowPosPropertyName,
  144. Component* content,
  145. ScopedPointer<Component>& ownerPointer,
  146. int defaultW, int defaultH,
  147. int minW, int minH,
  148. int maxW, int maxH)
  149. : DialogWindow (title, Colours::darkgrey, true, true),
  150. windowPosProperty (windowPosPropertyName),
  151. owner (ownerPointer)
  152. {
  153. setUsingNativeTitleBar (true);
  154. setResizable (true, true);
  155. setResizeLimits (minW, minH, maxW, maxH);
  156. setContentOwned (content, false);
  157. const String windowState (getGlobalProperties().getValue (windowPosProperty));
  158. if (windowState.isNotEmpty())
  159. restoreWindowStateFromString (windowState);
  160. else
  161. centreAroundComponent (Component::getCurrentlyFocusedComponent(), defaultW, defaultH);
  162. setVisible (true);
  163. owner = this;
  164. }
  165. ~FloatingToolWindow()
  166. {
  167. getGlobalProperties().setValue (windowPosProperty, getWindowStateAsString());
  168. }
  169. void closeButtonPressed()
  170. {
  171. owner = nullptr;
  172. }
  173. private:
  174. String windowPosProperty;
  175. ScopedPointer<Component>& owner;
  176. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FloatingToolWindow)
  177. };
  178. //==============================================================================
  179. class PopupColourSelector : public Component,
  180. public ChangeListener,
  181. public Value::Listener,
  182. public ButtonListener
  183. {
  184. public:
  185. PopupColourSelector (const Value& colour,
  186. const Colour& defaultCol,
  187. const bool canResetToDefault)
  188. : defaultButton ("Reset to Default"),
  189. colourValue (colour),
  190. defaultColour (defaultCol)
  191. {
  192. addAndMakeVisible (&selector);
  193. selector.setName ("Colour");
  194. selector.setCurrentColour (getColour());
  195. selector.addChangeListener (this);
  196. if (canResetToDefault)
  197. {
  198. addAndMakeVisible (&defaultButton);
  199. defaultButton.addListener (this);
  200. }
  201. colourValue.addListener (this);
  202. setSize (300, 400);
  203. }
  204. void resized()
  205. {
  206. if (defaultButton.isVisible())
  207. {
  208. selector.setBounds (0, 0, getWidth(), getHeight() - 30);
  209. defaultButton.changeWidthToFitText (22);
  210. defaultButton.setTopLeftPosition (10, getHeight() - 26);
  211. }
  212. else
  213. {
  214. selector.setBounds (0, 0, getWidth(), getHeight());
  215. }
  216. }
  217. Colour getColour() const
  218. {
  219. if (colourValue.toString().isEmpty())
  220. return defaultColour;
  221. return Colour::fromString (colourValue.toString());
  222. }
  223. void setColour (const Colour& newColour)
  224. {
  225. if (getColour() != newColour)
  226. {
  227. if (newColour == defaultColour && defaultButton.isVisible())
  228. colourValue = var::null;
  229. else
  230. colourValue = newColour.toDisplayString (true);
  231. }
  232. }
  233. void buttonClicked (Button*)
  234. {
  235. setColour (defaultColour);
  236. selector.setCurrentColour (defaultColour);
  237. }
  238. void changeListenerCallback (ChangeBroadcaster*)
  239. {
  240. if (selector.getCurrentColour() != getColour())
  241. setColour (selector.getCurrentColour());
  242. }
  243. void valueChanged (Value&)
  244. {
  245. selector.setCurrentColour (getColour());
  246. }
  247. private:
  248. StoredSettings::ColourSelectorWithSwatches selector;
  249. TextButton defaultButton;
  250. Value colourValue;
  251. Colour defaultColour;
  252. };
  253. //==============================================================================
  254. /**
  255. A component that shows a colour swatch with hex ARGB value, and which pops up
  256. a colour selector when you click it.
  257. */
  258. class ColourEditorComponent : public Component,
  259. public Value::Listener
  260. {
  261. public:
  262. ColourEditorComponent (UndoManager* um, const Value& colour,
  263. const Colour& defaultCol, const bool canReset)
  264. : undoManager (um), colourValue (colour), defaultColour (defaultCol),
  265. canResetToDefault (canReset)
  266. {
  267. colourValue.addListener (this);
  268. }
  269. void paint (Graphics& g)
  270. {
  271. const Colour colour (getColour());
  272. g.fillAll (Colours::grey);
  273. g.fillCheckerBoard (getLocalBounds().reduced (2),
  274. 10, 10,
  275. Colour (0xffdddddd).overlaidWith (colour),
  276. Colour (0xffffffff).overlaidWith (colour));
  277. g.setColour (Colours::white.overlaidWith (colour).contrasting());
  278. g.setFont (Font (getHeight() * 0.6f, Font::bold));
  279. g.drawFittedText (colour.toDisplayString (true), getLocalBounds().reduced (2, 1),
  280. Justification::centred, 1);
  281. }
  282. Colour getColour() const
  283. {
  284. if (colourValue.toString().isEmpty())
  285. return defaultColour;
  286. return Colour::fromString (colourValue.toString());
  287. }
  288. void setColour (const Colour& newColour)
  289. {
  290. if (getColour() != newColour)
  291. {
  292. if (newColour == defaultColour && canResetToDefault)
  293. colourValue = var::null;
  294. else
  295. colourValue = newColour.toDisplayString (true);
  296. }
  297. }
  298. void resetToDefault()
  299. {
  300. setColour (defaultColour);
  301. }
  302. void refresh()
  303. {
  304. const Colour col (getColour());
  305. if (col != lastColour)
  306. {
  307. lastColour = col;
  308. repaint();
  309. }
  310. }
  311. void mouseDown (const MouseEvent&)
  312. {
  313. if (undoManager != nullptr)
  314. undoManager->beginNewTransaction();
  315. CallOutBox::launchAsynchronously (new PopupColourSelector (colourValue,
  316. defaultColour,
  317. canResetToDefault),
  318. getScreenBounds(), nullptr);
  319. }
  320. void valueChanged (Value&)
  321. {
  322. refresh();
  323. }
  324. private:
  325. UndoManager* undoManager;
  326. Value colourValue;
  327. Colour lastColour;
  328. const Colour defaultColour;
  329. const bool canResetToDefault;
  330. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ColourEditorComponent)
  331. };
  332. //==============================================================================
  333. class ColourPropertyComponent : public PropertyComponent
  334. {
  335. public:
  336. ColourPropertyComponent (UndoManager* undoManager, const String& name, const Value& colour,
  337. const Colour& defaultColour, bool canResetToDefault)
  338. : PropertyComponent (name),
  339. colourEditor (undoManager, colour, defaultColour, canResetToDefault)
  340. {
  341. addAndMakeVisible (&colourEditor);
  342. }
  343. void resized()
  344. {
  345. colourEditor.setBounds (getLookAndFeel().getPropertyComponentContentPosition (*this));
  346. }
  347. void refresh() {}
  348. protected:
  349. ColourEditorComponent colourEditor;
  350. };