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.

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