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.

993 lines
32KB

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