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.

871 lines
29KB

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