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