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.

861 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. resizers.clear();
  321. deleteAllChildren();
  322. }
  323. //==============================================================================
  324. void mouseDown (const MouseEvent& e)
  325. {
  326. lasso = 0;
  327. mouseDownCompUID = SelectedItems::ItemType();
  328. isDraggingClickedComp = false;
  329. const MouseEvent e2 (e.getEventRelativeTo (canvas->getComponentHolder()));
  330. const SelectedItems::ItemType underMouse (canvas->findObjectIdAt (e2.getPosition()));
  331. if (e.mods.isPopupMenu())
  332. {
  333. if (underMouse.isNotEmpty() && ! getSelection().isSelected (underMouse))
  334. getSelection().selectOnly (underMouse);
  335. canvas->showPopupMenu (e2.getPosition());
  336. }
  337. else
  338. {
  339. if (underMouse.isEmpty() || e.mods.isAltDown())
  340. {
  341. canvas->deselectNonDraggableObjects();
  342. addAndMakeVisible (lasso = new LassoComponent <SelectedItems::ItemType>());
  343. lasso->beginLasso (e, this);
  344. }
  345. else
  346. {
  347. mouseDownCompUID = underMouse;
  348. canvas->deselectNonDraggableObjects();
  349. mouseDownResult = getSelection().addToSelectionOnMouseDown (mouseDownCompUID, e.mods);
  350. updateResizeFrames();
  351. hideSizeGuides();
  352. showSizeGuides();
  353. }
  354. }
  355. }
  356. void mouseDrag (const MouseEvent& e)
  357. {
  358. if (lasso != 0)
  359. {
  360. lasso->dragLasso (e);
  361. }
  362. else if (mouseDownCompUID.isNotEmpty() && (! e.mouseWasClicked()) && (! e.mods.isPopupMenu()))
  363. {
  364. if (! isDraggingClickedComp)
  365. {
  366. isDraggingClickedComp = true;
  367. getSelection().addToSelectionOnMouseUp (mouseDownCompUID, e.mods, true, mouseDownResult);
  368. canvas->beginDrag (e, ResizableBorderComponent::Zone (ResizableBorderComponent::Zone::centre));
  369. }
  370. canvas->continueDrag (e);
  371. showSizeGuides();
  372. }
  373. autoScrollForMouseEvent (e);
  374. }
  375. void mouseUp (const MouseEvent& e)
  376. {
  377. hideSizeGuides();
  378. if (lasso != 0)
  379. {
  380. lasso->endLasso();
  381. lasso = 0;
  382. if (e.mouseWasClicked())
  383. getSelection().deselectAll();
  384. }
  385. else if (! e.mods.isPopupMenu())
  386. {
  387. if (! isDraggingClickedComp)
  388. getSelection().addToSelectionOnMouseUp (mouseDownCompUID, e.mods, ! e.mouseWasClicked(), mouseDownResult);
  389. }
  390. canvas->endDrag (e);
  391. }
  392. void mouseDoubleClick (const MouseEvent& e)
  393. {
  394. const BorderSize& border = canvas->border;
  395. const Rectangle<int> xAxis (border.getLeft(), 0, getWidth() - border.getLeftAndRight(), border.getTop());
  396. const Rectangle<int> yAxis (0, border.getTop(), border.getLeft(), getHeight() - border.getTopAndBottom());
  397. if (xAxis.contains (e.x, e.y))
  398. {
  399. canvas->getMarkerList (true).createMarker ("Marker", e.x - xAxis.getX());
  400. }
  401. else if (yAxis.contains (e.x, e.y))
  402. {
  403. canvas->getMarkerList (false).createMarker ("Marker", e.y - yAxis.getY());
  404. }
  405. }
  406. void findLassoItemsInArea (Array <SelectedItems::ItemType>& itemsFound, const Rectangle<int>& area)
  407. {
  408. canvas->findLassoItemsInArea (itemsFound, area + relativePositionToOtherComponent (canvas->getComponentHolder(), Point<int>()));
  409. }
  410. SelectedItems& getSelection() { return canvas->getSelection(); }
  411. SelectedItems& getLassoSelection() { return getSelection(); }
  412. void resized()
  413. {
  414. updateMarkers();
  415. updateResizeFrames();
  416. }
  417. void changeListenerCallback (void*)
  418. {
  419. updateResizeFrames();
  420. }
  421. void modifierKeysChanged (const ModifierKeys&)
  422. {
  423. Desktop::getInstance().getMainMouseSource().triggerFakeMove();
  424. }
  425. void showSizeGuides()
  426. {
  427. for (int i = getNumChildComponents(); --i >= 0;)
  428. {
  429. ResizeFrame* resizer = dynamic_cast <ResizeFrame*> (getChildComponent(i));
  430. if (resizer != 0)
  431. resizer->showSizeGuides();
  432. }
  433. }
  434. void hideSizeGuides()
  435. {
  436. for (int i = getNumChildComponents(); --i >= 0;)
  437. {
  438. ResizeFrame* resizer = dynamic_cast <ResizeFrame*> (getChildComponent(i));
  439. if (resizer != 0)
  440. resizer->hideSizeGuides();
  441. }
  442. }
  443. void valueTreePropertyChanged (ValueTree&, const var::identifier&) { updateMarkers(); }
  444. void valueTreeChildrenChanged (ValueTree& treeWhoseChildHasChanged) { updateMarkers(); }
  445. void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged) {}
  446. void updateResizeFrames()
  447. {
  448. SelectedItems& selection = getSelection();
  449. StringArray requiredIds;
  450. const int num = selection.getNumSelected();
  451. int i;
  452. for (i = 0; i < num; ++i)
  453. requiredIds.add (selection.getSelectedItem(i));
  454. for (i = resizers.size(); --i >= 0;)
  455. {
  456. ResizeFrame* resizer = resizers.getUnchecked(i);
  457. const int index = requiredIds.indexOf (resizer->getTargetObjectID());
  458. if (index >= 0)
  459. {
  460. resizer->updatePosition();
  461. requiredIds.remove (index);
  462. }
  463. else
  464. {
  465. resizers.remove (i);
  466. }
  467. }
  468. for (i = requiredIds.size(); --i >= 0;)
  469. {
  470. const ValueTree state (canvas->getObjectState (requiredIds[i]));
  471. if (state.isValid()) // (the id may be a marker)
  472. {
  473. ResizeFrame* frame = new ResizeFrame (canvas, requiredIds[i], state);
  474. resizers.add (frame);
  475. addAndMakeVisible (frame);
  476. frame->updatePosition();
  477. }
  478. }
  479. }
  480. private:
  481. //==============================================================================
  482. EditorCanvasBase* canvas;
  483. ValueTree markerRootX, markerRootY;
  484. ScopedPointer <LassoComponent <SelectedItems::ItemType> > lasso;
  485. bool mouseDownResult, isDraggingClickedComp;
  486. SelectedItems::ItemType mouseDownCompUID;
  487. OwnedArray <ResizeFrame> resizers;
  488. void updateMarkers (bool isX)
  489. {
  490. Array<ValueTree> requiredMarkers;
  491. MarkerListBase& markerList = canvas->getMarkerList (isX);
  492. const int num = markerList.size();
  493. int i;
  494. for (i = 0; i < num; ++i)
  495. requiredMarkers.add (markerList.getMarker (i));
  496. for (i = getNumChildComponents(); --i >= 0;)
  497. {
  498. MarkerComponent* marker = dynamic_cast <MarkerComponent*> (getChildComponent(i));
  499. if (marker != 0 && marker->isX == isX)
  500. {
  501. if (requiredMarkers.contains (marker->marker))
  502. {
  503. marker->setVisible (true);
  504. marker->updatePosition();
  505. requiredMarkers.removeValue (marker->marker);
  506. }
  507. else
  508. {
  509. if (marker->isMouseButtonDown())
  510. marker->setBounds (-1, -1, 1, 1);
  511. else
  512. delete marker;
  513. }
  514. }
  515. }
  516. for (i = requiredMarkers.size(); --i >= 0;)
  517. {
  518. MarkerComponent* marker = new MarkerComponent (canvas, requiredMarkers.getReference(i),
  519. isX, isX ? canvas->border.getTop()
  520. : canvas->border.getLeft());
  521. addAndMakeVisible (marker);
  522. marker->updatePosition();
  523. }
  524. }
  525. void updateMarkers()
  526. {
  527. updateMarkers (true);
  528. updateMarkers (false);
  529. }
  530. };
  531. //==============================================================================
  532. class EditorCanvasBase::DocumentResizeFrame : public Component
  533. {
  534. public:
  535. DocumentResizeFrame (EditorCanvasBase* canvas_)
  536. : canvas (canvas_), dragStartWidth (0), dragStartHeight (0), resizerThickness (4)
  537. {
  538. }
  539. ~DocumentResizeFrame()
  540. {
  541. }
  542. void paint (Graphics& g)
  543. {
  544. const Rectangle<int> content (getContentArea());
  545. g.setColour (Colour::greyLevel (0.7f).withAlpha (0.4f));
  546. g.drawRect (content.expanded (resizerThickness, resizerThickness), resizerThickness);
  547. const int bottomGap = getHeight() - content.getBottom();
  548. g.setFont (bottomGap - 5.0f);
  549. g.setColour (Colours::grey);
  550. g.drawText (String (content.getWidth()) + " x " + String (content.getHeight()),
  551. 0, 0, jmax (content.getRight(), jmin (60, getWidth())), getHeight(), Justification::bottomRight, false);
  552. }
  553. void mouseMove (const MouseEvent& e)
  554. {
  555. updateDragZone (e.getPosition());
  556. }
  557. void mouseDown (const MouseEvent& e)
  558. {
  559. updateDragZone (e.getPosition());
  560. dragStartWidth = canvas->getCanvasWidth();
  561. dragStartHeight = canvas->getCanvasHeight();
  562. canvas->showSizeGuides();
  563. }
  564. void mouseDrag (const MouseEvent& e)
  565. {
  566. if (dragZone.isDraggingRightEdge())
  567. canvas->setCanvasWidth (jmax (1, dragStartWidth + e.getDistanceFromDragStartX()));
  568. if (dragZone.isDraggingBottomEdge())
  569. canvas->setCanvasHeight (jmax (1, dragStartHeight + e.getDistanceFromDragStartY()));
  570. }
  571. void mouseUp (const MouseEvent& e)
  572. {
  573. canvas->hideSizeGuides();
  574. updateDragZone (e.getPosition());
  575. }
  576. void updateDragZone (const Point<int>& p)
  577. {
  578. ResizableBorderComponent::Zone newZone
  579. = ResizableBorderComponent::Zone::fromPositionOnBorder (getContentArea().expanded (resizerThickness, resizerThickness),
  580. BorderSize (0, 0, resizerThickness, resizerThickness), p);
  581. if (dragZone != newZone)
  582. {
  583. dragZone = newZone;
  584. setMouseCursor (newZone.getMouseCursor());
  585. }
  586. }
  587. bool hitTest (int x, int y)
  588. {
  589. const Rectangle<int> content (getContentArea());
  590. return (x >= content.getRight() || y >= content.getBottom())
  591. && (! content.contains (x, y))
  592. && content.expanded (resizerThickness, resizerThickness).contains (x, y);
  593. }
  594. private:
  595. EditorCanvasBase* canvas;
  596. ResizableBorderComponent::Zone dragZone;
  597. int dragStartWidth, dragStartHeight;
  598. const int resizerThickness;
  599. const Rectangle<int> getContentArea() const { return canvas->getContentArea(); }
  600. };
  601. //==============================================================================
  602. EditorCanvasBase::EditorCanvasBase()
  603. : border (14)
  604. {
  605. setOpaque (true);
  606. }
  607. EditorCanvasBase::~EditorCanvasBase()
  608. {
  609. jassert (overlay == 0);
  610. }
  611. void EditorCanvasBase::initialise()
  612. {
  613. addAndMakeVisible (componentHolder = new Component());
  614. addAndMakeVisible (overlay = new OverlayComponent (this));
  615. overlay->addAndMakeVisible (resizeFrame = new DocumentResizeFrame (this));
  616. update();
  617. }
  618. void EditorCanvasBase::shutdown()
  619. {
  620. dragger = 0;
  621. deleteAndZero (overlay);
  622. deleteAllChildren();
  623. }
  624. //==============================================================================
  625. void EditorCanvasBase::paint (Graphics& g)
  626. {
  627. g.fillAll (Colours::white);
  628. g.setFont (border.getTop() - 5.0f);
  629. g.setColour (Colours::darkgrey);
  630. g.drawHorizontalLine (border.getTop() - 1, 2.0f, (float) getWidth() - border.getRight());
  631. g.drawVerticalLine (border.getLeft() - 1, 2.0f, (float) getHeight() - border.getBottom());
  632. drawXAxis (g, Rectangle<int> (border.getLeft(), 0, componentHolder->getWidth(), border.getTop()));
  633. drawYAxis (g, Rectangle<int> (0, border.getTop(), border.getLeft(), componentHolder->getHeight()));
  634. }
  635. void EditorCanvasBase::drawXAxis (Graphics& g, const Rectangle<int>& r)
  636. {
  637. TickIterator ticks (0, r.getWidth(), 1.0, 10, 50);
  638. float pos, tickLength;
  639. String label;
  640. while (ticks.getNextTick (pos, tickLength, label))
  641. {
  642. if (pos > 0)
  643. {
  644. g.drawVerticalLine (r.getX() + (int) pos, r.getBottom() - tickLength * r.getHeight(), (float) r.getBottom());
  645. g.drawSingleLineText (label, r.getX() + (int) pos + 2, (int) r.getBottom() - 6);
  646. }
  647. }
  648. }
  649. void EditorCanvasBase::drawYAxis (Graphics& g, const Rectangle<int>& r)
  650. {
  651. TickIterator ticks (0, r.getHeight(), 1.0, 10, 80);
  652. float pos, tickLength;
  653. String label;
  654. while (ticks.getNextTick (pos, tickLength, label))
  655. {
  656. if (pos > 0)
  657. {
  658. g.drawHorizontalLine (r.getY() + (int) pos, r.getRight() - tickLength * r.getWidth(), (float) r.getRight());
  659. g.drawTextAsPath (label, AffineTransform::rotation (float_Pi / -2.0f)
  660. .translated (r.getRight() - 6.0f, r.getY() + pos - 2.0f));
  661. }
  662. }
  663. }
  664. const Rectangle<int> EditorCanvasBase::getContentArea() const
  665. {
  666. return border.subtractedFrom (getLocalBounds());
  667. }
  668. //==============================================================================
  669. void EditorCanvasBase::update()
  670. {
  671. setSize ((int) getCanvasWidth() + border.getLeftAndRight(),
  672. (int) getCanvasHeight() + border.getTopAndBottom());
  673. updateComponents();
  674. }
  675. void EditorCanvasBase::resized()
  676. {
  677. componentHolder->setBounds (getContentArea());
  678. overlay->setBounds (getLocalBounds());
  679. resizeFrame->setBounds (getLocalBounds());
  680. updateComponents();
  681. }
  682. //==============================================================================
  683. void EditorCanvasBase::showSizeGuides() { overlay->showSizeGuides(); }
  684. void EditorCanvasBase::hideSizeGuides() { overlay->hideSizeGuides(); }
  685. //==============================================================================
  686. void EditorCanvasBase::beginDrag (const MouseEvent& e, const ResizableBorderComponent::Zone& zone)
  687. {
  688. dragger = createDragOperation (e, overlay, zone);
  689. }
  690. void EditorCanvasBase::continueDrag (const MouseEvent& e)
  691. {
  692. if (dragger != 0)
  693. dragger->drag (e);
  694. }
  695. void EditorCanvasBase::endDrag (const MouseEvent& e)
  696. {
  697. if (dragger != 0)
  698. {
  699. dragger->drag (e);
  700. dragger = 0;
  701. }
  702. }
  703. //==============================================================================
  704. EditorCanvasBase::OverlayItemComponent::OverlayItemComponent (EditorCanvasBase* canvas_)
  705. : canvas (canvas_)
  706. {
  707. }
  708. EditorCanvasBase::OverlayItemComponent::~OverlayItemComponent()
  709. {
  710. }
  711. void EditorCanvasBase::OverlayItemComponent::setBoundsInTargetSpace (const Rectangle<int>& r)
  712. {
  713. setBounds (r + canvas->getComponentHolder()->relativePositionToOtherComponent (getParentComponent(), Point<int>()));
  714. }