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.

468 lines
12KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-7 by Raw Material Software ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the
  7. GNU General Public License, as published by the Free Software Foundation;
  8. either version 2 of the License, or (at your option) any later version.
  9. JUCE is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with JUCE; if not, visit www.gnu.org/licenses or write to the
  15. Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  16. Boston, MA 02111-1307 USA
  17. ------------------------------------------------------------------------------
  18. If you'd like to release a closed-source product which uses JUCE, commercial
  19. licenses are also available: visit www.rawmaterialsoftware.com/juce for
  20. more information.
  21. ==============================================================================
  22. */
  23. #include "../../../../juce_core/basics/juce_StandardHeader.h"
  24. BEGIN_JUCE_NAMESPACE
  25. #include "juce_Label.h"
  26. #include "../lookandfeel/juce_LookAndFeel.h"
  27. //==============================================================================
  28. Label::Label (const String& componentName,
  29. const String& labelText)
  30. : Component (componentName),
  31. text (labelText),
  32. font (15.0f),
  33. justification (Justification::centredLeft),
  34. editor (0),
  35. listeners (2),
  36. ownerComponent (0),
  37. deletionWatcher (0),
  38. horizontalBorderSize (3),
  39. verticalBorderSize (1),
  40. minimumHorizontalScale (0.7f),
  41. editSingleClick (false),
  42. editDoubleClick (false),
  43. lossOfFocusDiscardsChanges (false)
  44. {
  45. setColour (TextEditor::textColourId, Colours::black);
  46. setColour (TextEditor::backgroundColourId, Colours::transparentBlack);
  47. setColour (TextEditor::outlineColourId, Colours::transparentBlack);
  48. }
  49. Label::~Label()
  50. {
  51. if (ownerComponent != 0 && ! deletionWatcher->hasBeenDeleted())
  52. ownerComponent->removeComponentListener (this);
  53. deleteAndZero (deletionWatcher);
  54. if (editor != 0)
  55. delete editor;
  56. }
  57. //==============================================================================
  58. void Label::setText (const String& newText,
  59. const bool broadcastChangeMessage)
  60. {
  61. hideEditor (true);
  62. if (text != newText)
  63. {
  64. text = newText;
  65. repaint();
  66. textWasChanged();
  67. if (ownerComponent != 0 && ! deletionWatcher->hasBeenDeleted())
  68. componentMovedOrResized (*ownerComponent, true, true);
  69. if (broadcastChangeMessage)
  70. callChangeListeners();
  71. }
  72. }
  73. const String Label::getText (const bool returnActiveEditorContents) const throw()
  74. {
  75. return (returnActiveEditorContents && isBeingEdited())
  76. ? editor->getText()
  77. : text;
  78. }
  79. void Label::setFont (const Font& newFont) throw()
  80. {
  81. font = newFont;
  82. repaint();
  83. }
  84. const Font& Label::getFont() const throw()
  85. {
  86. return font;
  87. }
  88. void Label::setEditable (const bool editOnSingleClick,
  89. const bool editOnDoubleClick,
  90. const bool lossOfFocusDiscardsChanges_) throw()
  91. {
  92. editSingleClick = editOnSingleClick;
  93. editDoubleClick = editOnDoubleClick;
  94. lossOfFocusDiscardsChanges = lossOfFocusDiscardsChanges_;
  95. setWantsKeyboardFocus (editOnSingleClick || editOnDoubleClick);
  96. setFocusContainer (editOnSingleClick || editOnDoubleClick);
  97. }
  98. void Label::setJustificationType (const Justification& justification_) throw()
  99. {
  100. justification = justification_;
  101. repaint();
  102. }
  103. void Label::setBorderSize (int h, int v)
  104. {
  105. horizontalBorderSize = h;
  106. verticalBorderSize = v;
  107. repaint();
  108. }
  109. //==============================================================================
  110. void Label::attachToComponent (Component* owner,
  111. const bool onLeft)
  112. {
  113. if (ownerComponent != 0 && ! deletionWatcher->hasBeenDeleted())
  114. ownerComponent->removeComponentListener (this);
  115. deleteAndZero (deletionWatcher);
  116. ownerComponent = owner;
  117. leftOfOwnerComp = onLeft;
  118. if (ownerComponent != 0)
  119. {
  120. deletionWatcher = new ComponentDeletionWatcher (owner);
  121. setVisible (owner->isVisible());
  122. ownerComponent->addComponentListener (this);
  123. componentParentHierarchyChanged (*ownerComponent);
  124. componentMovedOrResized (*ownerComponent, true, true);
  125. }
  126. }
  127. void Label::componentMovedOrResized (Component& component,
  128. bool /*wasMoved*/,
  129. bool /*wasResized*/)
  130. {
  131. if (leftOfOwnerComp)
  132. {
  133. setSize (jmin (getFont().getStringWidth (text) + 8, component.getX()),
  134. component.getHeight());
  135. setTopRightPosition (component.getX(), component.getY());
  136. }
  137. else
  138. {
  139. setSize (component.getWidth(),
  140. 8 + roundFloatToInt (getFont().getHeight()));
  141. setTopLeftPosition (component.getX(), component.getY() - getHeight());
  142. }
  143. }
  144. void Label::componentParentHierarchyChanged (Component& component)
  145. {
  146. if (component.getParentComponent() != 0)
  147. component.getParentComponent()->addChildComponent (this);
  148. }
  149. void Label::componentVisibilityChanged (Component& component)
  150. {
  151. setVisible (component.isVisible());
  152. }
  153. //==============================================================================
  154. void Label::textWasEdited()
  155. {
  156. }
  157. void Label::textWasChanged()
  158. {
  159. }
  160. void Label::showEditor()
  161. {
  162. if (editor == 0)
  163. {
  164. addAndMakeVisible (editor = createEditorComponent());
  165. editor->setText (getText(), false);
  166. editor->addListener (this);
  167. editor->grabKeyboardFocus();
  168. editor->setHighlightedRegion (0, text.length());
  169. editor->addListener (this);
  170. resized();
  171. repaint();
  172. editorShown (editor);
  173. enterModalState();
  174. editor->grabKeyboardFocus();
  175. }
  176. }
  177. void Label::editorShown (TextEditor* editorComponent)
  178. {
  179. }
  180. void Label::editorAboutToBeHidden (TextEditor* editorComponent)
  181. {
  182. }
  183. bool Label::updateFromTextEditorContents()
  184. {
  185. jassert (editor != 0);
  186. const String newText (editor->getText());
  187. if (text != newText)
  188. {
  189. text = newText;
  190. repaint();
  191. textWasChanged();
  192. if (ownerComponent != 0 && ! deletionWatcher->hasBeenDeleted())
  193. componentMovedOrResized (*ownerComponent, true, true);
  194. return true;
  195. }
  196. return false;
  197. }
  198. void Label::hideEditor (const bool discardCurrentEditorContents)
  199. {
  200. if (editor != 0)
  201. {
  202. editorAboutToBeHidden (editor);
  203. const bool changed = (! discardCurrentEditorContents)
  204. && updateFromTextEditorContents();
  205. deleteAndZero (editor);
  206. repaint();
  207. if (changed)
  208. textWasEdited();
  209. exitModalState (0);
  210. if (changed && isValidComponent())
  211. callChangeListeners();
  212. }
  213. }
  214. void Label::inputAttemptWhenModal()
  215. {
  216. if (editor != 0)
  217. {
  218. if (lossOfFocusDiscardsChanges)
  219. textEditorEscapeKeyPressed (*editor);
  220. else
  221. textEditorReturnKeyPressed (*editor);
  222. }
  223. }
  224. bool Label::isBeingEdited() const throw()
  225. {
  226. return editor != 0;
  227. }
  228. TextEditor* Label::createEditorComponent()
  229. {
  230. TextEditor* const ed = new TextEditor (getName());
  231. ed->setFont (font);
  232. // copy these colours from our own settings..
  233. const int cols[] = { TextEditor::backgroundColourId,
  234. TextEditor::textColourId,
  235. TextEditor::highlightColourId,
  236. TextEditor::highlightedTextColourId,
  237. TextEditor::caretColourId,
  238. TextEditor::outlineColourId,
  239. TextEditor::focusedOutlineColourId,
  240. TextEditor::shadowColourId };
  241. for (int i = 0; i < numElementsInArray (cols); ++i)
  242. ed->setColour (cols[i], findColour (cols[i]));
  243. return ed;
  244. }
  245. //==============================================================================
  246. void Label::paint (Graphics& g)
  247. {
  248. getLookAndFeel().drawLabel (g, *this);
  249. }
  250. void Label::mouseUp (const MouseEvent& e)
  251. {
  252. if (editSingleClick
  253. && e.mouseWasClicked()
  254. && contains (e.x, e.y)
  255. && ! e.mods.isPopupMenu())
  256. {
  257. showEditor();
  258. }
  259. }
  260. void Label::mouseDoubleClick (const MouseEvent& e)
  261. {
  262. if (editDoubleClick && ! e.mods.isPopupMenu())
  263. showEditor();
  264. }
  265. void Label::resized()
  266. {
  267. if (editor != 0)
  268. editor->setBoundsInset (BorderSize (0));
  269. }
  270. void Label::focusGained (FocusChangeType cause)
  271. {
  272. if (editSingleClick && cause == focusChangedByTabKey)
  273. showEditor();
  274. }
  275. void Label::enablementChanged()
  276. {
  277. repaint();
  278. }
  279. void Label::colourChanged()
  280. {
  281. repaint();
  282. }
  283. void Label::setMinimumHorizontalScale (const float newScale)
  284. {
  285. if (minimumHorizontalScale != newScale)
  286. {
  287. minimumHorizontalScale = newScale;
  288. repaint();
  289. }
  290. }
  291. //==============================================================================
  292. // We'll use a custom focus traverser here to make sure focus goes from the
  293. // text editor to another component rather than back to the label itself.
  294. class LabelKeyboardFocusTraverser : public KeyboardFocusTraverser
  295. {
  296. public:
  297. LabelKeyboardFocusTraverser() {}
  298. Component* getNextComponent (Component* current)
  299. {
  300. return KeyboardFocusTraverser::getNextComponent (dynamic_cast <TextEditor*> (current) != 0
  301. ? current->getParentComponent() : current);
  302. }
  303. Component* getPreviousComponent (Component* current)
  304. {
  305. return KeyboardFocusTraverser::getPreviousComponent (dynamic_cast <TextEditor*> (current) != 0
  306. ? current->getParentComponent() : current);
  307. }
  308. };
  309. KeyboardFocusTraverser* Label::createFocusTraverser()
  310. {
  311. return new LabelKeyboardFocusTraverser();
  312. }
  313. //==============================================================================
  314. void Label::addListener (LabelListener* const listener) throw()
  315. {
  316. jassert (listener != 0);
  317. if (listener != 0)
  318. listeners.add (listener);
  319. }
  320. void Label::removeListener (LabelListener* const listener) throw()
  321. {
  322. listeners.removeValue (listener);
  323. }
  324. void Label::callChangeListeners()
  325. {
  326. for (int i = listeners.size(); --i >= 0;)
  327. {
  328. ((LabelListener*) listeners.getUnchecked (i))->labelTextChanged (this);
  329. i = jmin (i, listeners.size());
  330. }
  331. }
  332. //==============================================================================
  333. void Label::textEditorTextChanged (TextEditor& ed)
  334. {
  335. if (editor != 0)
  336. {
  337. jassert (&ed == editor);
  338. if (! (hasKeyboardFocus (true) || isCurrentlyBlockedByAnotherModalComponent()))
  339. {
  340. if (lossOfFocusDiscardsChanges)
  341. textEditorEscapeKeyPressed (ed);
  342. else
  343. textEditorReturnKeyPressed (ed);
  344. }
  345. }
  346. }
  347. void Label::textEditorReturnKeyPressed (TextEditor& ed)
  348. {
  349. if (editor != 0)
  350. {
  351. jassert (&ed == editor);
  352. (void) ed;
  353. const bool changed = updateFromTextEditorContents();
  354. hideEditor (true);
  355. if (changed)
  356. {
  357. textWasEdited();
  358. if (isValidComponent())
  359. callChangeListeners();
  360. }
  361. }
  362. }
  363. void Label::textEditorEscapeKeyPressed (TextEditor& ed)
  364. {
  365. if (editor != 0)
  366. {
  367. jassert (&ed == editor);
  368. (void) ed;
  369. editor->setText (text, false);
  370. hideEditor (true);
  371. }
  372. }
  373. void Label::textEditorFocusLost (TextEditor& ed)
  374. {
  375. textEditorTextChanged (ed);
  376. }
  377. END_JUCE_NAMESPACE