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.

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