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.

994 lines
32KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-9 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. #include "../../jucer_Headers.h"
  19. #include "jucer_ComponentEditor.h"
  20. //==============================================================================
  21. class SizeGuideComponent : public Component,
  22. public ComponentListener
  23. {
  24. public:
  25. enum Type
  26. {
  27. left, right, top, bottom
  28. };
  29. SizeGuideComponent (ComponentDocument& document_, const ValueTree& state_, Component* component_,
  30. Component& parentForOverlays, Type type_)
  31. : document (document_), state (state_), component (component_), type (type_),
  32. font (10.0f)
  33. {
  34. component->addComponentListener (this);
  35. setAlwaysOnTop (true);
  36. parentForOverlays.addAndMakeVisible (this);
  37. updatePosition();
  38. }
  39. ~SizeGuideComponent()
  40. {
  41. if (component != 0)
  42. component->removeComponentListener (this);
  43. }
  44. void updatePosition()
  45. {
  46. RectangleCoordinates coords (document.getCoordsFor (state));
  47. Coordinate coord (false);
  48. bool isHorizontal = false;
  49. switch (type)
  50. {
  51. case left: coord = coords.left; isHorizontal = true; break;
  52. case right: coord = coords.right; isHorizontal = true; break;
  53. case top: coord = coords.top; break;
  54. case bottom: coord = coords.bottom; break;
  55. default: jassertfalse; break;
  56. }
  57. setName (coord.toString());
  58. int textW = (int) font.getStringWidth (getName());
  59. int textH = (int) font.getHeight();
  60. Point<int> p1, p2;
  61. switch (type)
  62. {
  63. case left:
  64. p1 = Point<int> (component->getX(), 0);
  65. p2 = Point<int> (component->getX(), component->getY());
  66. textArea.setBounds (p1.getX() - textW - 2, 4, textW, textH);
  67. break;
  68. case right:
  69. p1 = Point<int> (component->getRight(), 0);
  70. p2 = Point<int> (component->getRight(), component->getY());
  71. textArea.setBounds (p1.getX() + 2, 4, textW, textH);
  72. break;
  73. case top:
  74. p1 = Point<int> (0, component->getY());
  75. p2 = Point<int> (component->getX(), component->getY());
  76. textArea.setBounds (4, p1.getY() - textH - 2, textW, textH);
  77. break;
  78. case bottom:
  79. p1 = Point<int> (0, component->getBottom());
  80. p2 = Point<int> (component->getX(), component->getBottom());
  81. textArea.setBounds (4, p1.getY() + 2, textW, textH);
  82. break;
  83. default:
  84. jassertfalse;
  85. break;
  86. }
  87. Rectangle<int> bounds (Rectangle<int> (p1, p2).expanded (2, 2).getUnion (textArea));
  88. bounds.setPosition (component->getParentComponent()->relativePositionToOtherComponent (getParentComponent(), bounds.getPosition()));
  89. setBounds (bounds);
  90. lineEnd1 = component->getParentComponent()->relativePositionToOtherComponent (this, p1);
  91. lineEnd2 = component->getParentComponent()->relativePositionToOtherComponent (this, p2);
  92. textArea.setPosition (component->getParentComponent()->relativePositionToOtherComponent (this, textArea.getPosition()));
  93. repaint();
  94. }
  95. void paint (Graphics& g)
  96. {
  97. const float dashes[] = { 4.0f, 3.0f };
  98. g.setColour (Colours::grey.withAlpha (0.4f));
  99. g.drawDashedLine (lineEnd1.getX() + 0.5f, lineEnd1.getY() + 0.5f,
  100. lineEnd2.getX() + 0.5f, lineEnd2.getY() + 0.5f,
  101. dashes, 2, 1.0f);
  102. g.setFont (font);
  103. g.setColour (Colours::white);
  104. for (int y = -1; y <= 1; ++y)
  105. for (int x = -1; x <= 1; ++x)
  106. g.drawText (getName(), textArea.getX() + x, textArea.getY() + y, textArea.getWidth(), textArea.getHeight(), Justification::centred, 1);
  107. g.setColour (Colours::black);
  108. g.drawText (getName(), textArea.getX(), textArea.getY(), textArea.getWidth(), textArea.getHeight(), Justification::centred, 1);
  109. }
  110. void componentMovedOrResized (Component&, bool, bool)
  111. {
  112. updatePosition();
  113. }
  114. void componentBeingDeleted (Component&)
  115. {
  116. setVisible (false);
  117. component = 0;
  118. }
  119. private:
  120. ComponentDocument& document;
  121. ValueTree state;
  122. Component* component;
  123. Type type;
  124. Font font;
  125. Point<int> lineEnd1, lineEnd2;
  126. Rectangle<int> textArea;
  127. };
  128. //==============================================================================
  129. static const double tickSizes[] = { 1.0, 2.0, 5.0,
  130. 10.0, 20.0, 50.0,
  131. 100.0, 200.0, 500.0, 1000.0 };
  132. class TickIterator
  133. {
  134. public:
  135. TickIterator (const double startValue_, const double endValue_, const double valuePerPixel_,
  136. int minPixelsPerTick, int minWidthForLabels)
  137. : startValue (startValue_),
  138. endValue (endValue_),
  139. valuePerPixel (valuePerPixel_)
  140. {
  141. tickLevelIndex = findLevelIndexForValue (valuePerPixel * minPixelsPerTick);
  142. labelLevelIndex = findLevelIndexForValue (valuePerPixel * minWidthForLabels);
  143. tickPosition = pixelsToValue (-minWidthForLabels);
  144. tickPosition = snapValueDown (tickPosition, tickLevelIndex);
  145. }
  146. bool getNextTick (float& pixelX, float& tickLength, String& label)
  147. {
  148. const double tickUnits = tickSizes [tickLevelIndex];
  149. tickPosition += tickUnits;
  150. const int totalLevels = sizeof (tickSizes) / sizeof (*tickSizes);
  151. int highestIndex = tickLevelIndex;
  152. while (++highestIndex < totalLevels)
  153. {
  154. const double ticksAtThisLevel = tickPosition / tickSizes [highestIndex];
  155. if (fabs (ticksAtThisLevel - floor (ticksAtThisLevel + 0.5)) > 0.000001)
  156. break;
  157. }
  158. --highestIndex;
  159. if (highestIndex >= labelLevelIndex)
  160. label = getDescriptionOfValue (tickPosition, labelLevelIndex);
  161. else
  162. label = String::empty;
  163. tickLength = (highestIndex + 1 - tickLevelIndex) / (float) (totalLevels + 1 - tickLevelIndex);
  164. pixelX = valueToPixels (tickPosition);
  165. return tickPosition < endValue;
  166. }
  167. private:
  168. double tickPosition;
  169. int tickLevelIndex, labelLevelIndex;
  170. const double startValue, endValue, valuePerPixel;
  171. int findLevelIndexForValue (const double value) const
  172. {
  173. int i;
  174. for (i = 0; i < sizeof (tickSizes) / sizeof (*tickSizes); ++i)
  175. if (tickSizes [i] >= value)
  176. break;
  177. return i;
  178. }
  179. double pixelsToValue (int pixels) const
  180. {
  181. return startValue + pixels * valuePerPixel;
  182. }
  183. float valueToPixels (double value) const
  184. {
  185. return (float) ((value - startValue) / valuePerPixel);
  186. }
  187. static double snapValueToNearest (const double t, const int valueLevelIndex)
  188. {
  189. const double unitsPerInterval = tickSizes [valueLevelIndex];
  190. return unitsPerInterval * floor (t / unitsPerInterval + 0.5);
  191. }
  192. static double snapValueDown (const double t, const int valueLevelIndex)
  193. {
  194. const double unitsPerInterval = tickSizes [valueLevelIndex];
  195. return unitsPerInterval * floor (t / unitsPerInterval);
  196. }
  197. static inline int roundDoubleToInt (const double value)
  198. {
  199. union { int asInt[2]; double asDouble; } n;
  200. n.asDouble = value + 6755399441055744.0;
  201. #if TARGET_RT_BIG_ENDIAN
  202. return n.asInt [1];
  203. #else
  204. return n.asInt [0];
  205. #endif
  206. }
  207. static const String getDescriptionOfValue (const double value, const int valueLevelIndex)
  208. {
  209. return String (roundToInt (value));
  210. }
  211. };
  212. //==============================================================================
  213. class ComponentEditor::Canvas : public Component,
  214. public ValueTree::Listener,
  215. public Timer
  216. {
  217. public:
  218. Canvas (ComponentEditor& editor_)
  219. : editor (editor_), border (14), resizerThickness (4)
  220. {
  221. setOpaque (true);
  222. addAndMakeVisible (componentHolder = new Component());
  223. addAndMakeVisible (overlay = new OverlayComponent (*this));
  224. setSize (500, 500);
  225. getDocument().getRoot().addListener (this);
  226. updateComponents();
  227. }
  228. ~Canvas()
  229. {
  230. getDocument().getRoot().removeListener (this);
  231. deleteAllChildren();
  232. }
  233. void paint (Graphics& g)
  234. {
  235. g.fillAll (Colours::white);
  236. g.setColour (Colour::greyLevel (0.9f));
  237. g.drawRect (getContentArea().expanded (resizerThickness, resizerThickness), resizerThickness);
  238. g.setFont (border.getBottom() - 5.0f);
  239. g.setColour (Colours::grey);
  240. g.drawText (String (componentHolder->getWidth()) + " x " + String (componentHolder->getHeight()),
  241. 0, 0, getWidth() - border.getRight(), getHeight(), Justification::bottomRight, false);
  242. g.setFont (border.getTop() - 5.0f);
  243. g.setColour (Colours::darkgrey);
  244. const float x = border.getLeft();
  245. const float y = border.getTop();
  246. g.drawHorizontalLine (y, 2.0f, getWidth() - border.getRight());
  247. g.drawVerticalLine (x, 2.0f, getHeight() - border.getBottom());
  248. {
  249. TickIterator ticks (0, componentHolder->getWidth(), 1.0, 10, 50);
  250. float pos, tickLength;
  251. String label;
  252. while (ticks.getNextTick (pos, tickLength, label))
  253. {
  254. if (pos > 0)
  255. {
  256. g.drawVerticalLine (x + pos, y - tickLength * y, y);
  257. g.drawSingleLineText (label, x + pos + 2, y - 6);
  258. }
  259. }
  260. }
  261. {
  262. TickIterator ticks (0, componentHolder->getHeight(), 1.0, 10, 80);
  263. float pos, tickLength;
  264. String label;
  265. while (ticks.getNextTick (pos, tickLength, label))
  266. {
  267. if (pos > 0)
  268. {
  269. g.drawHorizontalLine (y + pos, x - tickLength * x, x);
  270. g.drawTextAsPath (label, AffineTransform::rotation (float_Pi / -2.0f)
  271. .translated (x - 6, y + pos - 2));
  272. }
  273. }
  274. }
  275. }
  276. void resized()
  277. {
  278. componentHolder->setBounds (getContentArea());
  279. overlay->setBounds (componentHolder->getBounds());
  280. updateComponents();
  281. }
  282. void zoom (float newScale, const Point<int>& centre)
  283. {
  284. }
  285. ComponentEditor& getEditor() { return editor; }
  286. ComponentDocument& getDocument() { return editor.getDocument(); }
  287. ComponentDocument::SelectedItems& getSelection() { return selection; }
  288. Component* findComponentFor (const ValueTree& state)
  289. {
  290. ComponentDocument& doc = getDocument();
  291. for (int i = componentHolder->getNumChildComponents(); --i >= 0;)
  292. {
  293. Component* c = componentHolder->getChildComponent (i);
  294. if (doc.isStateForComponent (state, c))
  295. return c;
  296. }
  297. return 0;
  298. }
  299. void updateComponents()
  300. {
  301. ComponentDocument& doc = getDocument();
  302. int i;
  303. for (i = componentHolder->getNumChildComponents(); --i >= 0;)
  304. {
  305. Component* c = componentHolder->getChildComponent (i);
  306. if (! doc.containsComponent (c))
  307. {
  308. selection.deselect (c->getComponentUID());
  309. delete c;
  310. }
  311. }
  312. const int num = doc.getNumComponents();
  313. for (i = 0; i < num; ++i)
  314. {
  315. const ValueTree v (doc.getComponent (i));
  316. Component* c = findComponentFor (v);
  317. if (c == 0)
  318. {
  319. c = doc.createComponent (i);
  320. componentHolder->addAndMakeVisible (c);
  321. }
  322. doc.updateComponent (c);
  323. }
  324. startTimer (500);
  325. }
  326. void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, const var::identifier& property)
  327. {
  328. updateComponents();
  329. }
  330. void valueTreeChildrenChanged (ValueTree& treeWhoseChildHasChanged)
  331. {
  332. updateComponents();
  333. }
  334. void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged)
  335. {
  336. }
  337. Component* getComponentHolder() const { return componentHolder; }
  338. const Array<Component*> getSelectedComps() const
  339. {
  340. Array<Component*> comps;
  341. for (int i = 0; i < selection.getNumSelected(); ++i)
  342. {
  343. Component* c = getComponentForUID (selection.getSelectedItem (i));
  344. jassert (c != 0);
  345. if (c != 0)
  346. comps.add (c);
  347. }
  348. return comps;
  349. }
  350. void getSelectedItemProperties (Array <PropertyComponent*>& props)
  351. {
  352. //xxx needs to handle multiple selections..
  353. if (selection.getNumSelected() == 1)
  354. {
  355. Component* c = getComponentForUID (selection.getSelectedItem (0));
  356. getDocument().getComponentProperties (props, c);
  357. }
  358. }
  359. void timerCallback()
  360. {
  361. stopTimer();
  362. if (! Component::isMouseButtonDownAnywhere())
  363. getDocument().beginNewTransaction();
  364. }
  365. void mouseMove (const MouseEvent& e)
  366. {
  367. updateDragZone (e.getPosition());
  368. }
  369. void mouseDown (const MouseEvent& e)
  370. {
  371. updateDragZone (e.getPosition());
  372. dragStartSize = getBounds();
  373. showSizeGuides();
  374. }
  375. void mouseDrag (const MouseEvent& e)
  376. {
  377. if (dragZone.isDraggingRightEdge() || dragZone.isDraggingBottomEdge())
  378. {
  379. setSize (dragZone.isDraggingRightEdge() ? jmax (0, dragStartSize.getWidth() + e.getDistanceFromDragStartX())
  380. : dragStartSize.getWidth(),
  381. dragZone.isDraggingBottomEdge() ? jmax (0, dragStartSize.getHeight() + e.getDistanceFromDragStartY())
  382. : dragStartSize.getHeight());
  383. }
  384. }
  385. void mouseUp (const MouseEvent& e)
  386. {
  387. hideSizeGuides();
  388. updateDragZone (e.getPosition());
  389. }
  390. void updateDragZone (const Point<int>& p)
  391. {
  392. ResizableBorderComponent::Zone newZone
  393. = ResizableBorderComponent::Zone::fromPositionOnBorder (getContentArea().expanded (resizerThickness, resizerThickness),
  394. BorderSize (0, 0, resizerThickness, resizerThickness), p);
  395. if (dragZone != newZone)
  396. {
  397. dragZone = newZone;
  398. setMouseCursor (newZone.getMouseCursor());
  399. }
  400. }
  401. void showSizeGuides() { overlay->showSizeGuides(); }
  402. void hideSizeGuides() { overlay->hideSizeGuides(); }
  403. private:
  404. ComponentEditor& editor;
  405. const BorderSize border;
  406. const int resizerThickness;
  407. ResizableBorderComponent::Zone dragZone;
  408. Rectangle<int> dragStartSize;
  409. const Rectangle<int> getContentArea() const
  410. {
  411. return border.subtractedFrom (getLocalBounds());
  412. }
  413. //==============================================================================
  414. class ComponentResizeFrame : public Component,
  415. public ComponentListener
  416. {
  417. public:
  418. ComponentResizeFrame (Canvas& canvas_,
  419. Component* componentToAttachTo)
  420. : canvas (canvas_),
  421. component (componentToAttachTo),
  422. borderThickness (4)
  423. {
  424. componentMovedOrResized (*componentToAttachTo, true, true);
  425. componentToAttachTo->addComponentListener (this);
  426. }
  427. ~ComponentResizeFrame()
  428. {
  429. if (component != 0)
  430. component->removeComponentListener (this);
  431. }
  432. void paint (Graphics& g)
  433. {
  434. g.setColour (Colours::red.withAlpha (0.1f));
  435. g.drawRect (0, 0, getWidth(), getHeight(), borderThickness);
  436. }
  437. void mouseEnter (const MouseEvent& e)
  438. {
  439. //repaint();
  440. updateDragZone (e.getPosition());
  441. }
  442. void mouseExit (const MouseEvent& e)
  443. {
  444. //repaint();
  445. updateDragZone (e.getPosition());
  446. }
  447. void mouseMove (const MouseEvent& e)
  448. {
  449. updateDragZone (e.getPosition());
  450. }
  451. void mouseDown (const MouseEvent& e)
  452. {
  453. jassert (component != 0);
  454. if (component != 0)
  455. {
  456. updateDragZone (e.getPosition());
  457. canvas.getDocument().beginDrag (canvas.getSelectedComps(), e, getParentComponent(), dragZone);
  458. canvas.showSizeGuides();
  459. }
  460. }
  461. void mouseDrag (const MouseEvent& e)
  462. {
  463. if (component != 0)
  464. canvas.getDocument().continueDrag (e);
  465. }
  466. void mouseUp (const MouseEvent& e)
  467. {
  468. canvas.hideSizeGuides();
  469. if (component != 0)
  470. canvas.getDocument().endDrag (e);
  471. updateDragZone (e.getPosition());
  472. }
  473. void componentMovedOrResized (Component&, bool wasMoved, bool wasResized)
  474. {
  475. if (component != 0)
  476. setBounds (component->getBounds().expanded (borderThickness, borderThickness));
  477. }
  478. bool hitTest (int x, int y)
  479. {
  480. return ! getCentreArea().contains (x, y);
  481. }
  482. uint32 getTargetComponentUID() const { return component == 0 ? 0 : component->getComponentUID(); }
  483. void showSizeGuides()
  484. {
  485. if (sizeGuides.size() == 0)
  486. {
  487. const ValueTree v (canvas.getDocument().getComponentState (component));
  488. sizeGuides.add (new SizeGuideComponent (canvas.getDocument(), v, component, canvas, SizeGuideComponent::left));
  489. sizeGuides.add (new SizeGuideComponent (canvas.getDocument(), v, component, canvas, SizeGuideComponent::right));
  490. sizeGuides.add (new SizeGuideComponent (canvas.getDocument(), v, component, canvas, SizeGuideComponent::top));
  491. sizeGuides.add (new SizeGuideComponent (canvas.getDocument(), v, component, canvas, SizeGuideComponent::bottom));
  492. }
  493. }
  494. void hideSizeGuides()
  495. {
  496. sizeGuides.clear();
  497. }
  498. private:
  499. Canvas& canvas;
  500. Component::SafePointer<Component> component;
  501. ResizableBorderComponent::Zone dragZone;
  502. const int borderThickness;
  503. OwnedArray <SizeGuideComponent> sizeGuides;
  504. const Rectangle<int> getCentreArea() const
  505. {
  506. return getLocalBounds().reduced (borderThickness, borderThickness);
  507. }
  508. void updateDragZone (const Point<int>& p)
  509. {
  510. ResizableBorderComponent::Zone newZone
  511. = ResizableBorderComponent::Zone::fromPositionOnBorder (getLocalBounds(),
  512. BorderSize (borderThickness), p);
  513. if (dragZone != newZone)
  514. {
  515. dragZone = newZone;
  516. setMouseCursor (newZone.getMouseCursor());
  517. }
  518. }
  519. };
  520. //==============================================================================
  521. class OverlayComponent : public Component,
  522. public LassoSource <ComponentDocument::SelectedItems::ItemType>,
  523. public ChangeListener
  524. {
  525. public:
  526. OverlayComponent (Canvas& canvas_)
  527. : canvas (canvas_)
  528. {
  529. setAlwaysOnTop (true);
  530. setWantsKeyboardFocus (true);
  531. canvas.getSelection().addChangeListener (this);
  532. }
  533. ~OverlayComponent()
  534. {
  535. canvas.getSelection().removeChangeListener (this);
  536. lasso = 0;
  537. deleteAllChildren();
  538. }
  539. void mouseDown (const MouseEvent& e)
  540. {
  541. lasso = 0;
  542. mouseDownCompUID = 0;
  543. isDraggingClickedComp = false;
  544. if (e.mods.isPopupMenu())
  545. {
  546. PopupMenu m;
  547. canvas.getDocument().addNewComponentMenuItems (m);
  548. const int r = m.show();
  549. canvas.getDocument().performNewComponentMenuItem (r);
  550. }
  551. else
  552. {
  553. Component* underMouse = canvas.getComponentHolder()->getComponentAt (e.x, e.y);
  554. if (underMouse == canvas.getComponentHolder())
  555. underMouse = 0;
  556. if (underMouse == 0 || e.mods.isAltDown())
  557. {
  558. addAndMakeVisible (lasso = new LassoComponent <ComponentDocument::SelectedItems::ItemType>());
  559. lasso->beginLasso (e, this);
  560. }
  561. else
  562. {
  563. mouseDownCompUID = underMouse->getComponentUID();
  564. mouseDownResult = canvas.getSelection().addToSelectionOnMouseDown (mouseDownCompUID, e.mods);
  565. updateSelectedComponentResizeFrames();
  566. hideSizeGuides();
  567. showSizeGuides();
  568. }
  569. }
  570. }
  571. void mouseDrag (const MouseEvent& e)
  572. {
  573. if (lasso != 0)
  574. {
  575. lasso->dragLasso (e);
  576. }
  577. else if (mouseDownCompUID != 0 && (! e.mouseWasClicked()) && (! e.mods.isPopupMenu()))
  578. {
  579. if (! isDraggingClickedComp)
  580. {
  581. isDraggingClickedComp = true;
  582. canvas.getSelection().addToSelectionOnMouseUp (mouseDownCompUID, e.mods, true, mouseDownResult);
  583. canvas.getDocument().beginDrag (canvas.getSelectedComps(), e, getParentComponent(),
  584. ResizableBorderComponent::Zone (ResizableBorderComponent::Zone::centre));
  585. }
  586. canvas.getDocument().continueDrag (e);
  587. showSizeGuides();
  588. }
  589. }
  590. void mouseUp (const MouseEvent& e)
  591. {
  592. hideSizeGuides();
  593. if (lasso != 0)
  594. {
  595. lasso->endLasso();
  596. lasso = 0;
  597. if (e.mouseWasClicked())
  598. canvas.getSelection().deselectAll();
  599. }
  600. else if (! e.mods.isPopupMenu())
  601. {
  602. if (! isDraggingClickedComp)
  603. canvas.getSelection().addToSelectionOnMouseUp (mouseDownCompUID, e.mods, ! e.mouseWasClicked(), mouseDownResult);
  604. }
  605. canvas.getDocument().endDrag (e);
  606. }
  607. void findLassoItemsInArea (Array <ComponentDocument::SelectedItems::ItemType>& itemsFound, int x, int y, int width, int height)
  608. {
  609. const Rectangle<int> lassoArea (x, y, width, height);
  610. for (int i = canvas.getComponentHolder()->getNumChildComponents(); --i >= 0;)
  611. {
  612. Component* c = canvas.getComponentHolder()->getChildComponent(i);
  613. if (c->getBounds().intersects (lassoArea))
  614. itemsFound.add (c->getComponentUID());
  615. }
  616. }
  617. ComponentDocument::SelectedItems& getLassoSelection() { return canvas.getSelection(); }
  618. void changeListenerCallback (void*)
  619. {
  620. updateSelectedComponentResizeFrames();
  621. }
  622. void modifierKeysChanged (const ModifierKeys&)
  623. {
  624. Desktop::getInstance().getMainMouseSource().triggerFakeMove();
  625. }
  626. void showSizeGuides()
  627. {
  628. for (int i = getNumChildComponents(); --i >= 0;)
  629. {
  630. ComponentResizeFrame* resizer = dynamic_cast <ComponentResizeFrame*> (getChildComponent(i));
  631. if (resizer != 0)
  632. resizer->showSizeGuides();
  633. }
  634. }
  635. void hideSizeGuides()
  636. {
  637. for (int i = getNumChildComponents(); --i >= 0;)
  638. {
  639. ComponentResizeFrame* resizer = dynamic_cast <ComponentResizeFrame*> (getChildComponent(i));
  640. if (resizer != 0)
  641. resizer->hideSizeGuides();
  642. }
  643. }
  644. private:
  645. Canvas& canvas;
  646. ScopedPointer <LassoComponent <ComponentDocument::SelectedItems::ItemType> > lasso;
  647. bool mouseDownResult, isDraggingClickedComp;
  648. uint32 mouseDownCompUID;
  649. ComponentResizeFrame* getSelectorFrameFor (Component* c) const
  650. {
  651. for (int i = getNumChildComponents(); --i >= 0;)
  652. {
  653. ComponentResizeFrame* resizer = dynamic_cast <ComponentResizeFrame*> (getChildComponent(i));
  654. if (resizer != 0 && resizer->getTargetComponentUID() == c->getComponentUID())
  655. return resizer;
  656. }
  657. return 0;
  658. }
  659. void updateSelectedComponentResizeFrames()
  660. {
  661. ComponentDocument::SelectedItems& selection = canvas.getSelection();
  662. int i;
  663. for (i = getNumChildComponents(); --i >= 0;)
  664. {
  665. ComponentResizeFrame* resizer = dynamic_cast <ComponentResizeFrame*> (getChildComponent(i));
  666. if (resizer != 0 && ! selection.isSelected (resizer->getTargetComponentUID()))
  667. delete resizer;
  668. }
  669. for (i = canvas.getComponentHolder()->getNumChildComponents(); --i >= 0;)
  670. {
  671. Component* c = canvas.getComponentHolder()->getChildComponent(i);
  672. if (c != this && selection.isSelected (c->getComponentUID()) && getSelectorFrameFor (c) == 0)
  673. addAndMakeVisible (new ComponentResizeFrame (canvas, c));
  674. }
  675. }
  676. };
  677. Component* componentHolder;
  678. OverlayComponent* overlay;
  679. ComponentDocument::SelectedItems selection;
  680. Component* getComponentForUID (const uint32 uid) const
  681. {
  682. for (int i = componentHolder->getNumChildComponents(); --i >= 0;)
  683. if (componentHolder->getChildComponent (i)->getComponentUID() == uid)
  684. return componentHolder->getChildComponent (i);
  685. return 0;
  686. }
  687. };
  688. //==============================================================================
  689. class ComponentEditor::InfoPanel : public Component,
  690. public ChangeListener
  691. {
  692. public:
  693. InfoPanel (ComponentEditor& editor_)
  694. : editor (editor_)
  695. {
  696. setOpaque (true);
  697. addAndMakeVisible (props = new PropertyPanel());
  698. editor.getCanvas()->getSelection().addChangeListener (this);
  699. }
  700. ~InfoPanel()
  701. {
  702. editor.getCanvas()->getSelection().removeChangeListener (this);
  703. props->clear();
  704. deleteAllChildren();
  705. }
  706. void changeListenerCallback (void*)
  707. {
  708. Array <PropertyComponent*> newComps;
  709. editor.getCanvas()->getSelectedItemProperties (newComps);
  710. props->clear();
  711. props->addProperties (newComps);
  712. }
  713. void paint (Graphics& g)
  714. {
  715. g.fillAll (Colour::greyLevel (0.92f));
  716. }
  717. void resized()
  718. {
  719. props->setSize (getWidth(), getHeight());
  720. }
  721. private:
  722. ComponentEditor& editor;
  723. PropertyPanel* props;
  724. };
  725. //==============================================================================
  726. ComponentEditor::ComponentEditor (OpenDocumentManager::Document* document,
  727. Project* project_, ComponentDocument* componentDocument_)
  728. : DocumentEditorComponent (document),
  729. project (project_),
  730. componentDocument (componentDocument_),
  731. infoPanel (0)
  732. {
  733. setOpaque (true);
  734. addAndMakeVisible (viewport = new Viewport());
  735. if (document != 0)
  736. {
  737. viewport->setViewedComponent (new Canvas (*this));
  738. addAndMakeVisible (infoPanel = new InfoPanel (*this));
  739. }
  740. }
  741. ComponentEditor::~ComponentEditor()
  742. {
  743. deleteAndZero (infoPanel);
  744. deleteAllChildren();
  745. }
  746. void ComponentEditor::paint (Graphics& g)
  747. {
  748. g.fillAll (Colours::white);
  749. }
  750. void ComponentEditor::resized()
  751. {
  752. const int infoPanelWidth = 200;
  753. viewport->setBounds (0, 0, getWidth() - infoPanelWidth, getHeight());
  754. if (infoPanel != 0)
  755. infoPanel->setBounds (getWidth() - infoPanelWidth, 0, infoPanelWidth, getHeight());
  756. }
  757. ComponentEditor::Canvas* ComponentEditor::getCanvas() const
  758. {
  759. return dynamic_cast <Canvas*> (viewport->getViewedComponent());
  760. }
  761. void ComponentEditor::getAllCommands (Array <CommandID>& commands)
  762. {
  763. DocumentEditorComponent::getAllCommands (commands);
  764. const CommandID ids[] = { CommandIDs::undo,
  765. CommandIDs::redo };
  766. commands.addArray (ids, numElementsInArray (ids));
  767. }
  768. void ComponentEditor::getCommandInfo (CommandID commandID, ApplicationCommandInfo& result)
  769. {
  770. result.setActive (document != 0);
  771. switch (commandID)
  772. {
  773. case CommandIDs::undo:
  774. result.setInfo ("Undo", "Undoes the last change",
  775. CommandCategories::general, 0);
  776. result.defaultKeypresses.add (KeyPress ('z', ModifierKeys::commandModifier, 0));
  777. break;
  778. case CommandIDs::redo:
  779. result.setInfo ("Redo", "Redoes the last change",
  780. CommandCategories::general, 0);
  781. result.defaultKeypresses.add (KeyPress ('z', ModifierKeys::shiftModifier | ModifierKeys::commandModifier, 0));
  782. result.defaultKeypresses.add (KeyPress ('y', ModifierKeys::commandModifier, 0));
  783. break;
  784. default:
  785. DocumentEditorComponent::getCommandInfo (commandID, result);
  786. break;
  787. }
  788. }
  789. bool ComponentEditor::perform (const InvocationInfo& info)
  790. {
  791. switch (info.commandID)
  792. {
  793. case CommandIDs::undo:
  794. getDocument().getUndoManager()->undo();
  795. return true;
  796. case CommandIDs::redo:
  797. getDocument().getUndoManager()->redo();
  798. return true;
  799. default:
  800. break;
  801. }
  802. return DocumentEditorComponent::perform (info);
  803. }