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.

854 lines
28KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-10 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_EditorCanvas.h"
  20. //==============================================================================
  21. class EditorCanvasBase::ResizeFrame : public EditorCanvasBase::OverlayItemComponent,
  22. public ValueTree::Listener
  23. {
  24. public:
  25. ResizeFrame (EditorCanvasBase* canvas_, const String& objectId_, const ValueTree& objectState_)
  26. : OverlayItemComponent (canvas_),
  27. objectState (objectState_),
  28. objectId (objectId_),
  29. borderThickness (4)
  30. {
  31. jassert (objectState.isValid());
  32. objectState.addListener (this);
  33. }
  34. ~ResizeFrame()
  35. {
  36. }
  37. void paint (Graphics& g)
  38. {
  39. g.setColour (resizableBorderColour);
  40. g.drawRect (0, 0, getWidth(), getHeight(), borderThickness);
  41. }
  42. void valueTreePropertyChanged (ValueTree&, const var::identifier&) { updatePosition(); }
  43. void valueTreeChildrenChanged (ValueTree& treeWhoseChildHasChanged) { updatePosition(); }
  44. void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged) {}
  45. void mouseEnter (const MouseEvent& e) { updateDragZone (e.getPosition()); }
  46. void mouseExit (const MouseEvent& e) { updateDragZone (e.getPosition()); }
  47. void mouseMove (const MouseEvent& e) { updateDragZone (e.getPosition()); }
  48. void mouseDown (const MouseEvent& e)
  49. {
  50. updateDragZone (e.getPosition());
  51. canvas->beginDrag (e.getEventRelativeTo (getParentComponent()), dragZone);
  52. canvas->showSizeGuides();
  53. }
  54. void mouseDrag (const MouseEvent& e)
  55. {
  56. canvas->continueDrag (e.getEventRelativeTo (getParentComponent()));
  57. autoScrollForMouseEvent (e);
  58. }
  59. void mouseUp (const MouseEvent& e)
  60. {
  61. canvas->hideSizeGuides();
  62. canvas->endDrag (e.getEventRelativeTo (getParentComponent()));
  63. updateDragZone (e.getPosition());
  64. }
  65. bool hitTest (int x, int y)
  66. {
  67. return ! getCentreArea().contains (x, y);
  68. }
  69. void updatePosition()
  70. {
  71. const Rectangle<int> bounds (canvas->getObjectPosition (objectState));
  72. setBoundsInTargetSpace (bounds.expanded (borderThickness, borderThickness));
  73. for (int i = sizeGuides.size(); --i >= 0;)
  74. {
  75. sizeGuides.getUnchecked(i)->setVisible (isVisible());
  76. sizeGuides.getUnchecked(i)->updatePosition (bounds);
  77. }
  78. }
  79. const String& getTargetObjectID() const { return objectId; }
  80. //==============================================================================
  81. class SizeGuideComponent : public OverlayItemComponent,
  82. public ComponentListener
  83. {
  84. public:
  85. enum Type { left, right, top, bottom };
  86. //==============================================================================
  87. SizeGuideComponent (EditorCanvasBase* canvas_, const ValueTree& state_, Type type_)
  88. : OverlayItemComponent (canvas_), state (state_), type (type_)
  89. {
  90. setAlwaysOnTop (true);
  91. canvas->addAndMakeVisible (this);
  92. setInterceptsMouseClicks (false, false);
  93. }
  94. //==============================================================================
  95. void paint (Graphics& g)
  96. {
  97. const float dashes[] = { 4.0f, 3.0f };
  98. g.setColour (resizableBorderColour);
  99. g.drawDashedLine (0.5f, 0.5f, getWidth() - 0.5f, getHeight() - 0.5f, dashes, 2, 1.0f);
  100. }
  101. //==============================================================================
  102. void updatePosition (const Rectangle<int>& bounds)
  103. {
  104. RectangleCoordinates coords (canvas->getObjectCoords (state));
  105. Coordinate coord (false);
  106. Rectangle<int> r;
  107. switch (type)
  108. {
  109. case left: coord = coords.left; r.setBounds (bounds.getX(), 0, 1, bounds.getY()); break;
  110. case right: coord = coords.right; r.setBounds (bounds.getRight(), 0, 1, bounds.getY()); break;
  111. case top: coord = coords.top; r.setBounds (0, bounds.getY(), bounds.getX(), 1); break;
  112. case bottom: coord = coords.bottom; r.setBounds (0, bounds.getBottom(), bounds.getX(), 1); break;
  113. default: jassertfalse; break;
  114. }
  115. setBoundsInTargetSpace (r);
  116. label.update (getParentComponent(), coord.toString(), resizableBorderColour.withAlpha (0.9f), getX(), getY(), type != left, type != top);
  117. }
  118. private:
  119. ValueTree state;
  120. Type type;
  121. FloatingLabelComponent label;
  122. };
  123. void showSizeGuides()
  124. {
  125. if (sizeGuides.size() == 0)
  126. {
  127. sizeGuides.add (new SizeGuideComponent (canvas, objectState, SizeGuideComponent::left));
  128. sizeGuides.add (new SizeGuideComponent (canvas, objectState, SizeGuideComponent::right));
  129. sizeGuides.add (new SizeGuideComponent (canvas, objectState, SizeGuideComponent::top));
  130. sizeGuides.add (new SizeGuideComponent (canvas, objectState, SizeGuideComponent::bottom));
  131. }
  132. }
  133. void hideSizeGuides()
  134. {
  135. sizeGuides.clear();
  136. }
  137. private:
  138. ValueTree objectState;
  139. String objectId;
  140. ResizableBorderComponent::Zone dragZone;
  141. const int borderThickness;
  142. OwnedArray <SizeGuideComponent> sizeGuides;
  143. const Rectangle<int> getCentreArea() const
  144. {
  145. return getLocalBounds().reduced (borderThickness, borderThickness);
  146. }
  147. void updateDragZone (const Point<int>& p)
  148. {
  149. ResizableBorderComponent::Zone newZone
  150. = ResizableBorderComponent::Zone::fromPositionOnBorder (getLocalBounds(),
  151. BorderSize (borderThickness), p);
  152. if (dragZone != newZone)
  153. {
  154. dragZone = newZone;
  155. setMouseCursor (newZone.getMouseCursor());
  156. }
  157. }
  158. };
  159. //==============================================================================
  160. class EditorCanvasBase::MarkerComponent : public EditorCanvasBase::OverlayItemComponent,
  161. public ValueTree::Listener
  162. {
  163. public:
  164. MarkerComponent (EditorCanvasBase* canvas_, const ValueTree& marker_, bool isX_, int headSize_)
  165. : OverlayItemComponent (canvas_), marker (marker_), isX (isX_), headSize (headSize_ - 2),
  166. dragStartPos (0), isDragging (false)
  167. {
  168. marker.addListener (this);
  169. }
  170. ~MarkerComponent()
  171. {
  172. marker.removeListener (this);
  173. }
  174. void paint (Graphics& g)
  175. {
  176. g.setColour (Colours::darkgreen.withAlpha (isMouseOverOrDragging() ? 0.8f : 0.4f));
  177. g.fillPath (path);
  178. }
  179. void updatePosition()
  180. {
  181. Coordinate coord (getMarkerList().getCoordinate (marker));
  182. const int pos = roundToInt (coord.resolve (getMarkerList()));
  183. const int width = 8;
  184. if (isX)
  185. setBoundsInTargetSpace (Rectangle<int> (pos - width, -headSize, width * 2, getParentHeight()));
  186. else
  187. setBoundsInTargetSpace (Rectangle<int> (-headSize, pos - width, getParentWidth(), width * 2));
  188. labelText = "name: " + getMarkerList().getName (marker) + "\nposition: " + coord.toString();
  189. updateLabel();
  190. }
  191. void updateLabel()
  192. {
  193. if (isMouseOverOrDragging() && (getWidth() > 1 || getHeight() > 1))
  194. label.update (getParentComponent(), labelText, Colours::darkgreen,
  195. isX ? getBounds().getCentreX() : getX() + headSize,
  196. isX ? getY() + headSize : getBounds().getCentreY(), true, true);
  197. else
  198. label.remove();
  199. }
  200. bool hitTest (int x, int y)
  201. {
  202. return (isX ? y : x) < headSize;
  203. }
  204. void resized()
  205. {
  206. const float lineThickness = 1.0f;
  207. path.clear();
  208. if (isX)
  209. {
  210. const float centre = getWidth() / 2 + 0.5f;
  211. path.addLineSegment (centre, 2.0f, centre, getHeight() + 1.0f, lineThickness);
  212. path.addTriangle (1.0f, 0.0f, centre * 2.0f - 1.0f, 0.0f, centre, headSize + 1.0f);
  213. }
  214. else
  215. {
  216. const float centre = getHeight() / 2 + 0.5f;
  217. path.addLineSegment (2.0f, centre, getWidth() + 1.0f, centre, lineThickness);
  218. path.addTriangle (0.0f, centre * 2.0f - 1.0f, 0.0f, 1.0f, headSize + 1.0f, centre);
  219. }
  220. updateLabel();
  221. }
  222. void mouseDown (const MouseEvent& e)
  223. {
  224. mouseDownPos = e.getEventRelativeTo (getParentComponent()).getMouseDownPosition();
  225. toFront (false);
  226. updateLabel();
  227. canvas->getSelection().selectOnly (MarkerListBase::getId (marker));
  228. if (e.mods.isPopupMenu())
  229. {
  230. isDragging = false;
  231. }
  232. else
  233. {
  234. isDragging = true;
  235. canvas->getUndoManager().beginNewTransaction();
  236. Coordinate coord (getMarkerList().getCoordinate (marker));
  237. dragStartPos = coord.resolve (getMarkerList());
  238. }
  239. }
  240. void mouseDrag (const MouseEvent& e)
  241. {
  242. if (isDragging)
  243. {
  244. autoScrollForMouseEvent (e);
  245. const MouseEvent e2 (e.getEventRelativeTo (getParentComponent()));
  246. canvas->getUndoManager().undoCurrentTransactionOnly();
  247. Rectangle<int> axis;
  248. if (isX)
  249. axis.setBounds (0, 0, getParentWidth(), headSize);
  250. else
  251. axis.setBounds (0, 0, headSize, getParentHeight());
  252. if (axis.expanded (30, 30).contains (e.x, e.y))
  253. {
  254. Coordinate coord (getMarkerList().getCoordinate (marker));
  255. // (can't use getDistanceFromDragStart() because it doesn't take into account auto-scrolling)
  256. coord.moveToAbsolute (jmax (0, roundToInt (dragStartPos + (isX ? e2.x - mouseDownPos.getX()
  257. : e2.y - mouseDownPos.getY()))),
  258. getMarkerList());
  259. getMarkerList().setCoordinate (marker, coord);
  260. }
  261. else
  262. {
  263. getMarkerList().deleteMarker (marker);
  264. }
  265. }
  266. }
  267. void mouseUp (const MouseEvent& e)
  268. {
  269. canvas->getUndoManager().beginNewTransaction();
  270. updateLabel();
  271. }
  272. void mouseEnter (const MouseEvent& e)
  273. {
  274. updateLabel();
  275. repaint();
  276. }
  277. void mouseExit (const MouseEvent& e)
  278. {
  279. updateLabel();
  280. repaint();
  281. }
  282. MarkerListBase& getMarkerList() { return canvas->getMarkerList (isX); }
  283. void valueTreePropertyChanged (ValueTree&, const var::identifier&) { updatePosition(); }
  284. void valueTreeChildrenChanged (ValueTree& treeWhoseChildHasChanged) {}
  285. void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged) {}
  286. ValueTree marker;
  287. const bool isX;
  288. private:
  289. const int headSize;
  290. Path path;
  291. double dragStartPos;
  292. bool isDragging;
  293. FloatingLabelComponent label;
  294. String labelText;
  295. Point<int> mouseDownPos;
  296. };
  297. //==============================================================================
  298. class EditorCanvasBase::OverlayComponent : public Component,
  299. public LassoSource <SelectedItems::ItemType>,
  300. public ChangeListener,
  301. public ValueTree::Listener
  302. {
  303. public:
  304. OverlayComponent (EditorCanvasBase* canvas_)
  305. : canvas (canvas_)
  306. {
  307. setWantsKeyboardFocus (true);
  308. getSelection().addChangeListener (this);
  309. markerRootX = canvas->getMarkerList (true).getGroup();
  310. markerRootY = canvas->getMarkerList (false).getGroup();
  311. markerRootX.addListener (this);
  312. markerRootY.addListener (this);
  313. }
  314. ~OverlayComponent()
  315. {
  316. lasso = 0;
  317. markerRootX.removeListener (this);
  318. markerRootY.removeListener (this);
  319. getSelection().removeChangeListener (this);
  320. deleteAllChildren();
  321. }
  322. //==============================================================================
  323. void mouseDown (const MouseEvent& e)
  324. {
  325. lasso = 0;
  326. mouseDownCompUID = SelectedItems::ItemType();
  327. isDraggingClickedComp = false;
  328. const MouseEvent e2 (e.getEventRelativeTo (canvas->getComponentHolder()));
  329. const SelectedItems::ItemType underMouse (canvas->findObjectIdAt (e2.getPosition()));
  330. if (e.mods.isPopupMenu())
  331. {
  332. if (underMouse.isNotEmpty() && ! getSelection().isSelected (underMouse))
  333. getSelection().selectOnly (underMouse);
  334. canvas->showPopupMenu (e2.getPosition());
  335. }
  336. else
  337. {
  338. if (underMouse.isEmpty() || e.mods.isAltDown())
  339. {
  340. canvas->deselectNonDraggableObjects();
  341. addAndMakeVisible (lasso = new LassoComponent <SelectedItems::ItemType>());
  342. lasso->beginLasso (e, this);
  343. }
  344. else
  345. {
  346. mouseDownCompUID = underMouse;
  347. canvas->deselectNonDraggableObjects();
  348. mouseDownResult = getSelection().addToSelectionOnMouseDown (mouseDownCompUID, e.mods);
  349. updateResizeFrames();
  350. hideSizeGuides();
  351. showSizeGuides();
  352. }
  353. }
  354. }
  355. void mouseDrag (const MouseEvent& e)
  356. {
  357. if (lasso != 0)
  358. {
  359. lasso->dragLasso (e);
  360. }
  361. else if (mouseDownCompUID.isNotEmpty() && (! e.mouseWasClicked()) && (! e.mods.isPopupMenu()))
  362. {
  363. if (! isDraggingClickedComp)
  364. {
  365. isDraggingClickedComp = true;
  366. getSelection().addToSelectionOnMouseUp (mouseDownCompUID, e.mods, true, mouseDownResult);
  367. canvas->beginDrag (e, ResizableBorderComponent::Zone (ResizableBorderComponent::Zone::centre));
  368. }
  369. canvas->continueDrag (e);
  370. showSizeGuides();
  371. }
  372. autoScrollForMouseEvent (e);
  373. }
  374. void mouseUp (const MouseEvent& e)
  375. {
  376. hideSizeGuides();
  377. if (lasso != 0)
  378. {
  379. lasso->endLasso();
  380. lasso = 0;
  381. if (e.mouseWasClicked())
  382. getSelection().deselectAll();
  383. }
  384. else if (! e.mods.isPopupMenu())
  385. {
  386. if (! isDraggingClickedComp)
  387. getSelection().addToSelectionOnMouseUp (mouseDownCompUID, e.mods, ! e.mouseWasClicked(), mouseDownResult);
  388. }
  389. canvas->endDrag (e);
  390. }
  391. void mouseDoubleClick (const MouseEvent& e)
  392. {
  393. const BorderSize& border = canvas->border;
  394. const Rectangle<int> xAxis (border.getLeft(), 0, getWidth() - border.getLeftAndRight(), border.getTop());
  395. const Rectangle<int> yAxis (0, border.getTop(), border.getLeft(), getHeight() - border.getTopAndBottom());
  396. if (xAxis.contains (e.x, e.y))
  397. {
  398. canvas->getMarkerList (true).createMarker ("Marker", e.x - xAxis.getX());
  399. }
  400. else if (yAxis.contains (e.x, e.y))
  401. {
  402. canvas->getMarkerList (false).createMarker ("Marker", e.y - yAxis.getY());
  403. }
  404. }
  405. void findLassoItemsInArea (Array <SelectedItems::ItemType>& itemsFound, const Rectangle<int>& area)
  406. {
  407. canvas->findLassoItemsInArea (itemsFound, area + relativePositionToOtherComponent (canvas->getComponentHolder(), Point<int>()));
  408. }
  409. SelectedItems& getSelection() { return canvas->getSelection(); }
  410. SelectedItems& getLassoSelection() { return getSelection(); }
  411. void resized()
  412. {
  413. updateMarkers();
  414. }
  415. void changeListenerCallback (void*)
  416. {
  417. updateResizeFrames();
  418. }
  419. void modifierKeysChanged (const ModifierKeys&)
  420. {
  421. Desktop::getInstance().getMainMouseSource().triggerFakeMove();
  422. }
  423. void showSizeGuides()
  424. {
  425. for (int i = getNumChildComponents(); --i >= 0;)
  426. {
  427. ResizeFrame* resizer = dynamic_cast <ResizeFrame*> (getChildComponent(i));
  428. if (resizer != 0)
  429. resizer->showSizeGuides();
  430. }
  431. }
  432. void hideSizeGuides()
  433. {
  434. for (int i = getNumChildComponents(); --i >= 0;)
  435. {
  436. ResizeFrame* resizer = dynamic_cast <ResizeFrame*> (getChildComponent(i));
  437. if (resizer != 0)
  438. resizer->hideSizeGuides();
  439. }
  440. }
  441. void valueTreePropertyChanged (ValueTree&, const var::identifier&) { updateMarkers(); }
  442. void valueTreeChildrenChanged (ValueTree& treeWhoseChildHasChanged) { updateMarkers(); }
  443. void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged) {}
  444. void updateResizeFrames()
  445. {
  446. SelectedItems& selection = getSelection();
  447. StringArray requiredIds;
  448. const int num = selection.getNumSelected();
  449. int i;
  450. for (i = 0; i < num; ++i)
  451. requiredIds.add (selection.getSelectedItem(i));
  452. for (i = getNumChildComponents(); --i >= 0;)
  453. {
  454. ResizeFrame* resizer = dynamic_cast <ResizeFrame*> (getChildComponent(i));
  455. if (resizer != 0)
  456. {
  457. if (selection.isSelected (resizer->getTargetObjectID()))
  458. requiredIds.removeString (resizer->getTargetObjectID());
  459. else
  460. delete resizer;
  461. }
  462. }
  463. for (i = requiredIds.size(); --i >= 0;)
  464. {
  465. const ValueTree state (canvas->getObjectState (requiredIds[i]));
  466. if (state.isValid()) // (the id may be a marker)
  467. {
  468. ResizeFrame* frame = new ResizeFrame (canvas, requiredIds[i], state);
  469. addAndMakeVisible (frame);
  470. frame->updatePosition();
  471. }
  472. }
  473. }
  474. private:
  475. //==============================================================================
  476. EditorCanvasBase* canvas;
  477. ValueTree markerRootX, markerRootY;
  478. ScopedPointer <LassoComponent <SelectedItems::ItemType> > lasso;
  479. bool mouseDownResult, isDraggingClickedComp;
  480. SelectedItems::ItemType mouseDownCompUID;
  481. void updateMarkers (bool isX)
  482. {
  483. Array<ValueTree> requiredMarkers;
  484. MarkerListBase& markerList = canvas->getMarkerList (isX);
  485. const int num = markerList.size();
  486. int i;
  487. for (i = 0; i < num; ++i)
  488. requiredMarkers.add (markerList.getMarker (i));
  489. for (i = getNumChildComponents(); --i >= 0;)
  490. {
  491. MarkerComponent* marker = dynamic_cast <MarkerComponent*> (getChildComponent(i));
  492. if (marker != 0 && marker->isX == isX)
  493. {
  494. if (requiredMarkers.contains (marker->marker))
  495. {
  496. marker->setVisible (true);
  497. marker->updatePosition();
  498. requiredMarkers.removeValue (marker->marker);
  499. }
  500. else
  501. {
  502. if (marker->isMouseButtonDown())
  503. marker->setBounds (-1, -1, 1, 1);
  504. else
  505. delete marker;
  506. }
  507. }
  508. }
  509. for (i = requiredMarkers.size(); --i >= 0;)
  510. {
  511. MarkerComponent* marker = new MarkerComponent (canvas, requiredMarkers.getReference(i),
  512. isX, isX ? canvas->border.getTop()
  513. : canvas->border.getLeft());
  514. addAndMakeVisible (marker);
  515. marker->updatePosition();
  516. }
  517. }
  518. void updateMarkers()
  519. {
  520. updateMarkers (true);
  521. updateMarkers (false);
  522. }
  523. };
  524. //==============================================================================
  525. class EditorCanvasBase::DocumentResizeFrame : public Component
  526. {
  527. public:
  528. DocumentResizeFrame (EditorCanvasBase* canvas_)
  529. : canvas (canvas_), dragStartWidth (0), dragStartHeight (0), resizerThickness (4)
  530. {
  531. }
  532. ~DocumentResizeFrame()
  533. {
  534. }
  535. void paint (Graphics& g)
  536. {
  537. const Rectangle<int> content (getContentArea());
  538. g.setColour (Colour::greyLevel (0.7f).withAlpha (0.4f));
  539. g.drawRect (content.expanded (resizerThickness, resizerThickness), resizerThickness);
  540. const int bottomGap = getHeight() - content.getBottom();
  541. g.setFont (bottomGap - 5.0f);
  542. g.setColour (Colours::grey);
  543. g.drawText (String (content.getWidth()) + " x " + String (content.getHeight()),
  544. 0, 0, jmax (content.getRight(), jmin (60, getWidth())), getHeight(), Justification::bottomRight, false);
  545. }
  546. void mouseMove (const MouseEvent& e)
  547. {
  548. updateDragZone (e.getPosition());
  549. }
  550. void mouseDown (const MouseEvent& e)
  551. {
  552. updateDragZone (e.getPosition());
  553. dragStartWidth = canvas->getCanvasWidth();
  554. dragStartHeight = canvas->getCanvasHeight();
  555. canvas->showSizeGuides();
  556. }
  557. void mouseDrag (const MouseEvent& e)
  558. {
  559. if (dragZone.isDraggingRightEdge())
  560. canvas->setCanvasWidth (jmax (1, dragStartWidth + e.getDistanceFromDragStartX()));
  561. if (dragZone.isDraggingBottomEdge())
  562. canvas->setCanvasHeight (jmax (1, dragStartHeight + e.getDistanceFromDragStartY()));
  563. }
  564. void mouseUp (const MouseEvent& e)
  565. {
  566. canvas->hideSizeGuides();
  567. updateDragZone (e.getPosition());
  568. }
  569. void updateDragZone (const Point<int>& p)
  570. {
  571. ResizableBorderComponent::Zone newZone
  572. = ResizableBorderComponent::Zone::fromPositionOnBorder (getContentArea().expanded (resizerThickness, resizerThickness),
  573. BorderSize (0, 0, resizerThickness, resizerThickness), p);
  574. if (dragZone != newZone)
  575. {
  576. dragZone = newZone;
  577. setMouseCursor (newZone.getMouseCursor());
  578. }
  579. }
  580. bool hitTest (int x, int y)
  581. {
  582. const Rectangle<int> content (getContentArea());
  583. return (x >= content.getRight() || y >= content.getBottom())
  584. && (! content.contains (x, y))
  585. && content.expanded (resizerThickness, resizerThickness).contains (x, y);
  586. }
  587. private:
  588. EditorCanvasBase* canvas;
  589. ResizableBorderComponent::Zone dragZone;
  590. int dragStartWidth, dragStartHeight;
  591. const int resizerThickness;
  592. const Rectangle<int> getContentArea() const { return canvas->getContentArea(); }
  593. };
  594. //==============================================================================
  595. EditorCanvasBase::EditorCanvasBase()
  596. : border (14)
  597. {
  598. setOpaque (true);
  599. }
  600. EditorCanvasBase::~EditorCanvasBase()
  601. {
  602. jassert (overlay == 0);
  603. }
  604. void EditorCanvasBase::initialise()
  605. {
  606. addAndMakeVisible (componentHolder = new Component());
  607. addAndMakeVisible (overlay = new OverlayComponent (this));
  608. overlay->addAndMakeVisible (resizeFrame = new DocumentResizeFrame (this));
  609. update();
  610. }
  611. void EditorCanvasBase::shutdown()
  612. {
  613. dragger = 0;
  614. deleteAndZero (overlay);
  615. deleteAllChildren();
  616. }
  617. //==============================================================================
  618. void EditorCanvasBase::paint (Graphics& g)
  619. {
  620. g.fillAll (Colours::white);
  621. g.setFont (border.getTop() - 5.0f);
  622. g.setColour (Colours::darkgrey);
  623. g.drawHorizontalLine (border.getTop() - 1, 2.0f, (float) getWidth() - border.getRight());
  624. g.drawVerticalLine (border.getLeft() - 1, 2.0f, (float) getHeight() - border.getBottom());
  625. drawXAxis (g, Rectangle<int> (border.getLeft(), 0, componentHolder->getWidth(), border.getTop()));
  626. drawYAxis (g, Rectangle<int> (0, border.getTop(), border.getLeft(), componentHolder->getHeight()));
  627. }
  628. void EditorCanvasBase::drawXAxis (Graphics& g, const Rectangle<int>& r)
  629. {
  630. TickIterator ticks (0, r.getWidth(), 1.0, 10, 50);
  631. float pos, tickLength;
  632. String label;
  633. while (ticks.getNextTick (pos, tickLength, label))
  634. {
  635. if (pos > 0)
  636. {
  637. g.drawVerticalLine (r.getX() + (int) pos, r.getBottom() - tickLength * r.getHeight(), (float) r.getBottom());
  638. g.drawSingleLineText (label, r.getX() + (int) pos + 2, (int) r.getBottom() - 6);
  639. }
  640. }
  641. }
  642. void EditorCanvasBase::drawYAxis (Graphics& g, const Rectangle<int>& r)
  643. {
  644. TickIterator ticks (0, r.getHeight(), 1.0, 10, 80);
  645. float pos, tickLength;
  646. String label;
  647. while (ticks.getNextTick (pos, tickLength, label))
  648. {
  649. if (pos > 0)
  650. {
  651. g.drawHorizontalLine (r.getY() + (int) pos, r.getRight() - tickLength * r.getWidth(), (float) r.getRight());
  652. g.drawTextAsPath (label, AffineTransform::rotation (float_Pi / -2.0f)
  653. .translated (r.getRight() - 6.0f, r.getY() + pos - 2.0f));
  654. }
  655. }
  656. }
  657. const Rectangle<int> EditorCanvasBase::getContentArea() const
  658. {
  659. return border.subtractedFrom (getLocalBounds());
  660. }
  661. //==============================================================================
  662. void EditorCanvasBase::update()
  663. {
  664. setSize ((int) getCanvasWidth() + border.getLeftAndRight(),
  665. (int) getCanvasHeight() + border.getTopAndBottom());
  666. updateComponents();
  667. }
  668. void EditorCanvasBase::resized()
  669. {
  670. componentHolder->setBounds (getContentArea());
  671. overlay->setBounds (getLocalBounds());
  672. resizeFrame->setBounds (getLocalBounds());
  673. updateComponents();
  674. }
  675. //==============================================================================
  676. void EditorCanvasBase::showSizeGuides() { overlay->showSizeGuides(); }
  677. void EditorCanvasBase::hideSizeGuides() { overlay->hideSizeGuides(); }
  678. //==============================================================================
  679. void EditorCanvasBase::beginDrag (const MouseEvent& e, const ResizableBorderComponent::Zone& zone)
  680. {
  681. dragger = createDragOperation (e, overlay, zone);
  682. }
  683. void EditorCanvasBase::continueDrag (const MouseEvent& e)
  684. {
  685. if (dragger != 0)
  686. dragger->drag (e);
  687. }
  688. void EditorCanvasBase::endDrag (const MouseEvent& e)
  689. {
  690. if (dragger != 0)
  691. {
  692. dragger->drag (e);
  693. dragger = 0;
  694. }
  695. }
  696. //==============================================================================
  697. EditorCanvasBase::OverlayItemComponent::OverlayItemComponent (EditorCanvasBase* canvas_)
  698. : canvas (canvas_)
  699. {
  700. }
  701. EditorCanvasBase::OverlayItemComponent::~OverlayItemComponent()
  702. {
  703. }
  704. void EditorCanvasBase::OverlayItemComponent::setBoundsInTargetSpace (const Rectangle<int>& r)
  705. {
  706. setBounds (r + canvas->getComponentHolder()->relativePositionToOtherComponent (getParentComponent(), Point<int>()));
  707. }