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.

1340 lines
50KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. #include "../../Application/jucer_Headers.h"
  20. #include "../../Settings/jucer_AppearanceSettings.h"
  21. #include "../../Application/jucer_Application.h"
  22. #include "jucer_JucerDocumentEditor.h"
  23. #include "jucer_TestComponent.h"
  24. #include "../jucer_ObjectTypes.h"
  25. #include "jucer_ComponentLayoutPanel.h"
  26. #include "jucer_PaintRoutinePanel.h"
  27. #include "jucer_ResourceEditorPanel.h"
  28. #include "../Properties/jucer_ComponentTextProperty.h"
  29. #include "../Properties/jucer_ComponentChoiceProperty.h"
  30. #include "../UI/jucer_JucerCommandIDs.h"
  31. //==============================================================================
  32. class ExtraMethodsList : public PropertyComponent,
  33. public ListBoxModel,
  34. public ChangeListener
  35. {
  36. public:
  37. ExtraMethodsList (JucerDocument& doc)
  38. : PropertyComponent ("extra callbacks", 250),
  39. document (doc)
  40. {
  41. listBox.reset (new ListBox (String(), this));
  42. addAndMakeVisible (listBox.get());
  43. listBox->setRowHeight (22);
  44. document.addChangeListener (this);
  45. }
  46. ~ExtraMethodsList()
  47. {
  48. document.removeChangeListener (this);
  49. }
  50. int getNumRows() override
  51. {
  52. return methods.size();
  53. }
  54. void paintListBoxItem (int row, Graphics& g, int width, int height, bool rowIsSelected) override
  55. {
  56. if (row < 0 || row >= getNumRows())
  57. return;
  58. if (rowIsSelected)
  59. {
  60. g.fillAll (findColour (TextEditor::highlightColourId));
  61. g.setColour (findColour (defaultHighlightedTextColourId));
  62. }
  63. else
  64. {
  65. g.setColour (findColour (defaultTextColourId));
  66. }
  67. g.setFont (height * 0.6f);
  68. g.drawText (returnValues [row] + " " + baseClasses [row] + "::" + methods [row],
  69. 30, 0, width - 32, height,
  70. Justification::centredLeft, true);
  71. getLookAndFeel().drawTickBox (g, *this, 6, 2, 18, 18, document.isOptionalMethodEnabled (methods [row]), true, false, false);
  72. }
  73. void listBoxItemClicked (int row, const MouseEvent& e) override
  74. {
  75. if (row < 0 || row >= getNumRows())
  76. return;
  77. if (e.x < 30)
  78. document.setOptionalMethodEnabled (methods [row],
  79. ! document.isOptionalMethodEnabled (methods [row]));
  80. }
  81. void paint (Graphics& g) override
  82. {
  83. g.fillAll (Colours::white);
  84. }
  85. void resized() override
  86. {
  87. listBox->setBounds (getLocalBounds());
  88. }
  89. void refresh() override
  90. {
  91. baseClasses.clear();
  92. returnValues.clear();
  93. methods.clear();
  94. initialContents.clear();
  95. document.getOptionalMethods (baseClasses, returnValues, methods, initialContents);
  96. listBox->updateContent();
  97. listBox->repaint();
  98. }
  99. void changeListenerCallback (ChangeBroadcaster*) override
  100. {
  101. refresh();
  102. }
  103. private:
  104. JucerDocument& document;
  105. std::unique_ptr<ListBox> listBox;
  106. StringArray baseClasses, returnValues, methods, initialContents;
  107. };
  108. //==============================================================================
  109. class ClassPropertiesPanel : public Component,
  110. private ChangeListener
  111. {
  112. public:
  113. ClassPropertiesPanel (JucerDocument& doc)
  114. : document (doc)
  115. {
  116. addAndMakeVisible (panel1);
  117. addAndMakeVisible (panel2);
  118. Array <PropertyComponent*> props;
  119. props.add (new ComponentClassNameProperty (doc));
  120. props.add (new TemplateFileProperty (doc));
  121. props.add (new ComponentCompNameProperty (doc));
  122. props.add (new ComponentParentClassesProperty (doc));
  123. props.add (new ComponentConstructorParamsProperty (doc));
  124. props.add (new ComponentInitialisersProperty (doc));
  125. props.add (new ComponentInitialSizeProperty (doc, true));
  126. props.add (new ComponentInitialSizeProperty (doc, false));
  127. props.add (new FixedSizeProperty (doc));
  128. panel1.addSection ("General class settings", props);
  129. Array <PropertyComponent*> props2;
  130. props2.add (new ExtraMethodsList (doc));
  131. panel2.addSection ("Extra callback methods to generate", props2);
  132. doc.addExtraClassProperties (panel1);
  133. doc.addChangeListener (this);
  134. }
  135. ~ClassPropertiesPanel()
  136. {
  137. document.removeChangeListener (this);
  138. }
  139. void resized() override
  140. {
  141. int pw = jmin (getWidth() / 2 - 20, 350);
  142. panel1.setBounds (10, 6, pw, getHeight() - 12);
  143. panel2.setBounds (panel1.getRight() + 20, panel1.getY(), pw, panel1.getHeight());
  144. }
  145. void paint (Graphics& g) override
  146. {
  147. g.fillAll (findColour (secondaryBackgroundColourId));
  148. }
  149. void changeListenerCallback (ChangeBroadcaster*) override
  150. {
  151. panel1.refreshAll();
  152. panel2.refreshAll();
  153. }
  154. private:
  155. JucerDocument& document;
  156. PropertyPanel panel1, panel2;
  157. //==============================================================================
  158. class ComponentClassNameProperty : public ComponentTextProperty <Component>
  159. {
  160. public:
  161. ComponentClassNameProperty (JucerDocument& doc)
  162. : ComponentTextProperty <Component> ("Class name", 128, false, 0, doc)
  163. {}
  164. void setText (const String& newText) override { document.setClassName (newText); }
  165. String getText() const override { return document.getClassName(); }
  166. };
  167. //==============================================================================
  168. class ComponentCompNameProperty : public ComponentTextProperty <Component>
  169. {
  170. public:
  171. ComponentCompNameProperty (JucerDocument& doc)
  172. : ComponentTextProperty <Component> ("Component name", 200, false, 0, doc)
  173. {}
  174. void setText (const String& newText) override { document.setComponentName (newText); }
  175. String getText() const override { return document.getComponentName(); }
  176. };
  177. //==============================================================================
  178. class ComponentParentClassesProperty : public ComponentTextProperty <Component>
  179. {
  180. public:
  181. ComponentParentClassesProperty (JucerDocument& doc)
  182. : ComponentTextProperty <Component> ("Parent classes", 512, false, 0, doc)
  183. {}
  184. void setText (const String& newText) override { document.setParentClasses (newText); }
  185. String getText() const override { return document.getParentClassString(); }
  186. };
  187. //==============================================================================
  188. class ComponentConstructorParamsProperty : public ComponentTextProperty <Component>
  189. {
  190. public:
  191. ComponentConstructorParamsProperty (JucerDocument& doc)
  192. : ComponentTextProperty <Component> ("Constructor params", 2048, false, 0, doc)
  193. {}
  194. void setText (const String& newText) override { document.setConstructorParams (newText); }
  195. String getText() const override { return document.getConstructorParams(); }
  196. };
  197. //==============================================================================
  198. class ComponentInitialisersProperty : public ComponentTextProperty <Component>
  199. {
  200. public:
  201. ComponentInitialisersProperty (JucerDocument& doc)
  202. : ComponentTextProperty <Component> ("Member initialisers", 16384, true, 0, doc)
  203. {
  204. preferredHeight = 24 * 3;
  205. }
  206. void setText (const String& newText) override { document.setVariableInitialisers (newText); }
  207. String getText() const override { return document.getVariableInitialisers(); }
  208. };
  209. //==============================================================================
  210. class ComponentInitialSizeProperty : public ComponentTextProperty <Component>
  211. {
  212. public:
  213. ComponentInitialSizeProperty (JucerDocument& doc, const bool isWidth_)
  214. : ComponentTextProperty <Component> (isWidth_ ? "Initial width"
  215. : "Initial height",
  216. 10, false, 0, doc),
  217. isWidth (isWidth_)
  218. {}
  219. void setText (const String& newText) override
  220. {
  221. if (isWidth)
  222. document.setInitialSize (newText.getIntValue(), document.getInitialHeight());
  223. else
  224. document.setInitialSize (document.getInitialWidth(), newText.getIntValue());
  225. }
  226. String getText() const override
  227. {
  228. return String (isWidth ? document.getInitialWidth()
  229. : document.getInitialHeight());
  230. }
  231. private:
  232. const bool isWidth;
  233. };
  234. //==============================================================================
  235. class FixedSizeProperty : public ComponentChoiceProperty <Component>
  236. {
  237. public:
  238. FixedSizeProperty (JucerDocument& doc)
  239. : ComponentChoiceProperty <Component> ("Fixed size", 0, doc)
  240. {
  241. choices.add ("Resize component to fit workspace");
  242. choices.add ("Keep component size fixed");
  243. }
  244. void setIndex (int newIndex) { document.setFixedSize (newIndex != 0); }
  245. int getIndex() const { return document.isFixedSize() ? 1 : 0; }
  246. };
  247. //==============================================================================
  248. class TemplateFileProperty : public ComponentTextProperty <Component>
  249. {
  250. public:
  251. TemplateFileProperty (JucerDocument& doc)
  252. : ComponentTextProperty <Component> ("Template file", 2048, false, 0, doc)
  253. {}
  254. void setText (const String& newText) override { document.setTemplateFile (newText); }
  255. String getText() const override { return document.getTemplateFile(); }
  256. };
  257. };
  258. static const Colour tabColour (Colour (0xff888888));
  259. static SourceCodeEditor* createCodeEditor (const File& file, SourceCodeDocument& sourceCodeDoc)
  260. {
  261. return new SourceCodeEditor (&sourceCodeDoc,
  262. new CppCodeEditorComponent (file, sourceCodeDoc.getCodeDocument()));
  263. }
  264. //==============================================================================
  265. JucerDocumentEditor::JucerDocumentEditor (JucerDocument* const doc)
  266. : document (doc),
  267. tabbedComponent (doc)
  268. {
  269. setOpaque (true);
  270. if (document != nullptr)
  271. {
  272. setSize (document->getInitialWidth(),
  273. document->getInitialHeight());
  274. addAndMakeVisible (tabbedComponent);
  275. tabbedComponent.setOutline (0);
  276. tabbedComponent.addTab ("Class", tabColour, new ClassPropertiesPanel (*document), true);
  277. if (document->getComponentLayout() != nullptr)
  278. tabbedComponent.addTab ("Subcomponents", tabColour,
  279. compLayoutPanel = new ComponentLayoutPanel (*document, *document->getComponentLayout()), true);
  280. tabbedComponent.addTab ("Resources", tabColour, new ResourceEditorPanel (*document), true);
  281. tabbedComponent.addTab ("Code", tabColour, createCodeEditor (document->getCppFile(),
  282. document->getCppDocument()), true);
  283. updateTabs();
  284. restoreLastSelectedTab();
  285. document->addChangeListener (this);
  286. resized();
  287. refreshPropertiesPanel();
  288. changeListenerCallback (nullptr);
  289. if (auto* project = document->getCppDocument().getProject())
  290. {
  291. if (project->shouldSendGUIBuilderAnalyticsEvent())
  292. Analytics::getInstance()->logEvent ("GUI Builder", {}, ProjucerAnalyticsEvent::projectEvent);
  293. }
  294. }
  295. }
  296. JucerDocumentEditor::~JucerDocumentEditor()
  297. {
  298. saveLastSelectedTab();
  299. tabbedComponent.clearTabs();
  300. }
  301. void JucerDocumentEditor::refreshPropertiesPanel() const
  302. {
  303. for (int i = tabbedComponent.getNumTabs(); --i >= 0;)
  304. {
  305. if (ComponentLayoutPanel* layoutPanel = dynamic_cast<ComponentLayoutPanel*> (tabbedComponent.getTabContentComponent (i)))
  306. {
  307. if (layoutPanel->isVisible())
  308. layoutPanel->updatePropertiesList();
  309. }
  310. else
  311. {
  312. if (PaintRoutinePanel* pr = dynamic_cast<PaintRoutinePanel*> (tabbedComponent.getTabContentComponent (i)))
  313. if (pr->isVisible())
  314. pr->updatePropertiesList();
  315. }
  316. }
  317. }
  318. void JucerDocumentEditor::updateTabs()
  319. {
  320. const StringArray paintRoutineNames (document->getPaintRoutineNames());
  321. for (int i = tabbedComponent.getNumTabs(); --i >= 0;)
  322. {
  323. if (dynamic_cast<PaintRoutinePanel*> (tabbedComponent.getTabContentComponent (i)) != 0
  324. && ! paintRoutineNames.contains (tabbedComponent.getTabNames() [i]))
  325. {
  326. tabbedComponent.removeTab (i);
  327. }
  328. }
  329. for (int i = 0; i < document->getNumPaintRoutines(); ++i)
  330. {
  331. if (! tabbedComponent.getTabNames().contains (paintRoutineNames [i]))
  332. {
  333. int index, numPaintRoutinesSeen = 0;
  334. for (index = 1; index < tabbedComponent.getNumTabs(); ++index)
  335. {
  336. if (dynamic_cast<PaintRoutinePanel*> (tabbedComponent.getTabContentComponent (index)) != nullptr)
  337. {
  338. if (++numPaintRoutinesSeen == i)
  339. {
  340. ++index;
  341. break;
  342. }
  343. }
  344. }
  345. if (numPaintRoutinesSeen == 0)
  346. index = document->getComponentLayout() != nullptr ? 2 : 1;
  347. tabbedComponent.addTab (paintRoutineNames[i], tabColour,
  348. new PaintRoutinePanel (*document,
  349. *document->getPaintRoutine (i),
  350. this), true, index);
  351. }
  352. }
  353. }
  354. //==============================================================================
  355. void JucerDocumentEditor::paint (Graphics& g)
  356. {
  357. g.fillAll (findColour (backgroundColourId));
  358. }
  359. void JucerDocumentEditor::resized()
  360. {
  361. tabbedComponent.setBounds (getLocalBounds().withTrimmedLeft (12));
  362. }
  363. void JucerDocumentEditor::changeListenerCallback (ChangeBroadcaster*)
  364. {
  365. setName (document->getClassName());
  366. updateTabs();
  367. }
  368. //==============================================================================
  369. ApplicationCommandTarget* JucerDocumentEditor::getNextCommandTarget()
  370. {
  371. return findFirstTargetParentComponent();
  372. }
  373. ComponentLayout* JucerDocumentEditor::getCurrentLayout() const
  374. {
  375. if (ComponentLayoutPanel* panel = dynamic_cast<ComponentLayoutPanel*> (tabbedComponent.getCurrentContentComponent()))
  376. return &(panel->layout);
  377. return nullptr;
  378. }
  379. PaintRoutine* JucerDocumentEditor::getCurrentPaintRoutine() const
  380. {
  381. if (PaintRoutinePanel* panel = dynamic_cast<PaintRoutinePanel*> (tabbedComponent.getCurrentContentComponent()))
  382. return &(panel->getPaintRoutine());
  383. return nullptr;
  384. }
  385. void JucerDocumentEditor::showLayout()
  386. {
  387. if (getCurrentLayout() == nullptr)
  388. {
  389. for (int i = 0; i < tabbedComponent.getNumTabs(); ++i)
  390. {
  391. if (dynamic_cast<ComponentLayoutPanel*> (tabbedComponent.getTabContentComponent (i)) != nullptr)
  392. {
  393. tabbedComponent.setCurrentTabIndex (i);
  394. break;
  395. }
  396. }
  397. }
  398. }
  399. void JucerDocumentEditor::showGraphics (PaintRoutine* routine)
  400. {
  401. if (getCurrentPaintRoutine() != routine || routine == 0)
  402. {
  403. for (int i = 0; i < tabbedComponent.getNumTabs(); ++i)
  404. {
  405. if (PaintRoutinePanel* pr = dynamic_cast<PaintRoutinePanel*> (tabbedComponent.getTabContentComponent (i)))
  406. {
  407. if (routine == &(pr->getPaintRoutine()) || routine == nullptr)
  408. {
  409. tabbedComponent.setCurrentTabIndex (i);
  410. break;
  411. }
  412. }
  413. }
  414. }
  415. }
  416. //==============================================================================
  417. void JucerDocumentEditor::setViewportToLastPos (Viewport* vp, EditingPanelBase& editor)
  418. {
  419. vp->setViewPosition (lastViewportX, lastViewportY);
  420. editor.setZoom (currentZoomLevel);
  421. }
  422. void JucerDocumentEditor::storeLastViewportPos (Viewport* vp, EditingPanelBase& editor)
  423. {
  424. lastViewportX = vp->getViewPositionX();
  425. lastViewportY = vp->getViewPositionY();
  426. currentZoomLevel = editor.getZoom();
  427. }
  428. void JucerDocumentEditor::setZoom (double scale)
  429. {
  430. scale = jlimit (1.0 / 4.0, 32.0, scale);
  431. if (EditingPanelBase* panel = dynamic_cast<EditingPanelBase*> (tabbedComponent.getCurrentContentComponent()))
  432. panel->setZoom (scale);
  433. }
  434. double JucerDocumentEditor::getZoom() const
  435. {
  436. if (EditingPanelBase* panel = dynamic_cast<EditingPanelBase*> (tabbedComponent.getCurrentContentComponent()))
  437. return panel->getZoom();
  438. return 1.0;
  439. }
  440. static double snapToIntegerZoom (double zoom)
  441. {
  442. if (zoom >= 1.0)
  443. return (double) (int) (zoom + 0.5);
  444. return 1.0 / (int) (1.0 / zoom + 0.5);
  445. }
  446. void JucerDocumentEditor::addElement (const int index)
  447. {
  448. if (PaintRoutinePanel* const panel = dynamic_cast<PaintRoutinePanel*> (tabbedComponent.getCurrentContentComponent()))
  449. {
  450. PaintRoutine* const currentPaintRoutine = & (panel->getPaintRoutine());
  451. const Rectangle<int> area (panel->getComponentArea());
  452. document->beginTransaction();
  453. PaintElement* e = ObjectTypes::createNewElement (index, currentPaintRoutine);
  454. e->setInitialBounds (area.getWidth(), area.getHeight());
  455. e = currentPaintRoutine->addNewElement (e, -1, true);
  456. if (e != nullptr)
  457. {
  458. const int randomness = jmin (80, area.getWidth() / 2, area.getHeight() / 2);
  459. int x = area.getX() + area.getWidth() / 2 + Random::getSystemRandom().nextInt (randomness) - randomness / 2;
  460. int y = area.getY() + area.getHeight() / 2 + Random::getSystemRandom().nextInt (randomness) - randomness / 2;
  461. x = document->snapPosition (x);
  462. y = document->snapPosition (y);
  463. panel->xyToTargetXY (x, y);
  464. Rectangle<int> r (e->getCurrentBounds (area));
  465. r.setPosition (x, y);
  466. e->setCurrentBounds (r, area, true);
  467. currentPaintRoutine->getSelectedElements().selectOnly (e);
  468. }
  469. document->beginTransaction();
  470. }
  471. }
  472. void JucerDocumentEditor::addComponent (const int index)
  473. {
  474. showLayout();
  475. if (ComponentLayoutPanel* const panel = dynamic_cast<ComponentLayoutPanel*> (tabbedComponent.getCurrentContentComponent()))
  476. {
  477. const Rectangle<int> area (panel->getComponentArea());
  478. document->beginTransaction ("Add new " + ObjectTypes::componentTypeHandlers [index]->getTypeName());
  479. const int randomness = jmin (80, area.getWidth() / 2, area.getHeight() / 2);
  480. int x = area.getWidth() / 2 + Random::getSystemRandom().nextInt (randomness) - randomness / 2;
  481. int y = area.getHeight() / 2 + Random::getSystemRandom().nextInt (randomness) - randomness / 2;
  482. x = document->snapPosition (x);
  483. y = document->snapPosition (y);
  484. panel->xyToTargetXY (x, y);
  485. if (Component* newOne = panel->layout.addNewComponent (ObjectTypes::componentTypeHandlers [index], x, y))
  486. panel->layout.getSelectedSet().selectOnly (newOne);
  487. document->beginTransaction();
  488. }
  489. }
  490. //==============================================================================
  491. void JucerDocumentEditor::saveLastSelectedTab() const
  492. {
  493. if (document != nullptr)
  494. {
  495. if (auto* project = document->getCppDocument().getProject())
  496. {
  497. auto& projectProps = project->getStoredProperties();
  498. std::unique_ptr<XmlElement> root (projectProps.getXmlValue ("GUIComponentsLastTab"));
  499. if (root == nullptr)
  500. root.reset (new XmlElement ("FILES"));
  501. auto fileName = document->getCppFile().getFileName();
  502. auto* child = root->getChildByName (fileName);
  503. if (child == nullptr)
  504. child = root->createNewChildElement (fileName);
  505. child->setAttribute ("tab", tabbedComponent.getCurrentTabIndex());
  506. projectProps.setValue ("GUIComponentsLastTab", root.get());
  507. }
  508. }
  509. }
  510. void JucerDocumentEditor::restoreLastSelectedTab()
  511. {
  512. if (document != nullptr)
  513. {
  514. if (auto* project = document->getCppDocument().getProject())
  515. {
  516. std::unique_ptr<XmlElement> root (project->getStoredProperties().getXmlValue ("GUIComponentsLastTab"));
  517. if (root != nullptr)
  518. {
  519. auto* child = root->getChildByName (document->getCppFile().getFileName());
  520. if (child != nullptr)
  521. tabbedComponent.setCurrentTabIndex (child->getIntAttribute ("tab"));
  522. }
  523. }
  524. }
  525. }
  526. //==============================================================================
  527. bool JucerDocumentEditor::isSomethingSelected() const
  528. {
  529. if (auto* layout = getCurrentLayout())
  530. return layout->getSelectedSet().getNumSelected() > 0;
  531. if (auto* routine = getCurrentPaintRoutine())
  532. return routine->getSelectedElements().getNumSelected() > 0;
  533. return false;
  534. }
  535. bool JucerDocumentEditor::areMultipleThingsSelected() const
  536. {
  537. if (auto* layout = getCurrentLayout())
  538. return layout->getSelectedSet().getNumSelected() > 1;
  539. if (auto* routine = getCurrentPaintRoutine())
  540. return routine->getSelectedElements().getNumSelected() > 1;
  541. return false;
  542. }
  543. //==============================================================================
  544. void JucerDocumentEditor::getAllCommands (Array <CommandID>& commands)
  545. {
  546. const CommandID ids[] =
  547. {
  548. JucerCommandIDs::test,
  549. JucerCommandIDs::toFront,
  550. JucerCommandIDs::toBack,
  551. JucerCommandIDs::group,
  552. JucerCommandIDs::ungroup,
  553. JucerCommandIDs::bringBackLostItems,
  554. JucerCommandIDs::enableSnapToGrid,
  555. JucerCommandIDs::showGrid,
  556. JucerCommandIDs::editCompLayout,
  557. JucerCommandIDs::editCompGraphics,
  558. JucerCommandIDs::zoomIn,
  559. JucerCommandIDs::zoomOut,
  560. JucerCommandIDs::zoomNormal,
  561. JucerCommandIDs::spaceBarDrag,
  562. JucerCommandIDs::compOverlay0,
  563. JucerCommandIDs::compOverlay33,
  564. JucerCommandIDs::compOverlay66,
  565. JucerCommandIDs::compOverlay100,
  566. JucerCommandIDs::alignTop,
  567. JucerCommandIDs::alignRight,
  568. JucerCommandIDs::alignBottom,
  569. JucerCommandIDs::alignLeft,
  570. StandardApplicationCommandIDs::undo,
  571. StandardApplicationCommandIDs::redo,
  572. StandardApplicationCommandIDs::cut,
  573. StandardApplicationCommandIDs::copy,
  574. StandardApplicationCommandIDs::paste,
  575. StandardApplicationCommandIDs::del,
  576. StandardApplicationCommandIDs::selectAll,
  577. StandardApplicationCommandIDs::deselectAll
  578. };
  579. commands.addArray (ids, numElementsInArray (ids));
  580. for (int i = 0; i < ObjectTypes::numComponentTypes; ++i)
  581. commands.add (JucerCommandIDs::newComponentBase + i);
  582. for (int i = 0; i < ObjectTypes::numElementTypes; ++i)
  583. commands.add (JucerCommandIDs::newElementBase + i);
  584. }
  585. void JucerDocumentEditor::getCommandInfo (const CommandID commandID, ApplicationCommandInfo& result)
  586. {
  587. ComponentLayout* const currentLayout = getCurrentLayout();
  588. PaintRoutine* const currentPaintRoutine = getCurrentPaintRoutine();
  589. const int cmd = ModifierKeys::commandModifier;
  590. const int shift = ModifierKeys::shiftModifier;
  591. if (commandID >= JucerCommandIDs::newComponentBase
  592. && commandID < JucerCommandIDs::newComponentBase + ObjectTypes::numComponentTypes)
  593. {
  594. const int index = commandID - JucerCommandIDs::newComponentBase;
  595. result.setInfo ("New " + ObjectTypes::componentTypeHandlers [index]->getTypeName(),
  596. "Creates a new " + ObjectTypes::componentTypeHandlers [index]->getTypeName(),
  597. CommandCategories::editing, 0);
  598. return;
  599. }
  600. if (commandID >= JucerCommandIDs::newElementBase
  601. && commandID < JucerCommandIDs::newElementBase + ObjectTypes::numElementTypes)
  602. {
  603. const int index = commandID - JucerCommandIDs::newElementBase;
  604. result.setInfo (String ("New ") + ObjectTypes::elementTypeNames [index],
  605. String ("Adds a new ") + ObjectTypes::elementTypeNames [index],
  606. CommandCategories::editing, 0);
  607. result.setActive (currentPaintRoutine != nullptr);
  608. return;
  609. }
  610. switch (commandID)
  611. {
  612. case JucerCommandIDs::toFront:
  613. result.setInfo (TRANS("Bring to front"), TRANS("Brings the currently selected component to the front."), CommandCategories::editing, 0);
  614. result.setActive (isSomethingSelected());
  615. result.defaultKeypresses.add (KeyPress ('f', cmd, 0));
  616. break;
  617. case JucerCommandIDs::toBack:
  618. result.setInfo (TRANS("Send to back"), TRANS("Sends the currently selected component to the back."), CommandCategories::editing, 0);
  619. result.setActive (isSomethingSelected());
  620. result.defaultKeypresses.add (KeyPress ('b', cmd, 0));
  621. break;
  622. case JucerCommandIDs::group:
  623. result.setInfo (TRANS("Group selected items"), TRANS("Turns the currently selected elements into a single group object."), CommandCategories::editing, 0);
  624. result.setActive (currentPaintRoutine != nullptr && currentPaintRoutine->getSelectedElements().getNumSelected() > 1);
  625. result.defaultKeypresses.add (KeyPress ('k', cmd, 0));
  626. break;
  627. case JucerCommandIDs::ungroup:
  628. result.setInfo (TRANS("Ungroup selected items"), TRANS("Turns the currently selected elements into a single group object."), CommandCategories::editing, 0);
  629. result.setActive (currentPaintRoutine != nullptr
  630. && currentPaintRoutine->getSelectedElements().getNumSelected() == 1
  631. && currentPaintRoutine->getSelectedElements().getSelectedItem (0)->getTypeName() == "Group");
  632. result.defaultKeypresses.add (KeyPress ('k', cmd | shift, 0));
  633. break;
  634. case JucerCommandIDs::test:
  635. result.setInfo (TRANS("Test component..."), TRANS("Runs the current component interactively."), CommandCategories::view, 0);
  636. result.defaultKeypresses.add (KeyPress ('t', cmd, 0));
  637. break;
  638. case JucerCommandIDs::enableSnapToGrid:
  639. result.setInfo (TRANS("Enable snap-to-grid"), TRANS("Toggles whether components' positions are aligned to a grid."), CommandCategories::view, 0);
  640. result.setTicked (document != nullptr && document->isSnapActive (false));
  641. result.defaultKeypresses.add (KeyPress ('g', cmd, 0));
  642. break;
  643. case JucerCommandIDs::showGrid:
  644. result.setInfo (TRANS("Show snap-to-grid"), TRANS("Toggles whether the snapping grid is displayed on-screen."), CommandCategories::view, 0);
  645. result.setTicked (document != nullptr && document->isSnapShown());
  646. result.defaultKeypresses.add (KeyPress ('g', cmd | shift, 0));
  647. break;
  648. case JucerCommandIDs::editCompLayout:
  649. result.setInfo (TRANS("Edit sub-component layout"), TRANS("Switches to the sub-component editor view."), CommandCategories::view, 0);
  650. result.setTicked (currentLayout != nullptr);
  651. result.defaultKeypresses.add (KeyPress ('n', cmd, 0));
  652. break;
  653. case JucerCommandIDs::editCompGraphics:
  654. result.setInfo (TRANS("Edit background graphics"), TRANS("Switches to the background graphics editor view."), CommandCategories::view, 0);
  655. result.setTicked (currentPaintRoutine != nullptr);
  656. result.defaultKeypresses.add (KeyPress ('m', cmd, 0));
  657. break;
  658. case JucerCommandIDs::bringBackLostItems:
  659. result.setInfo (TRANS("Retrieve offscreen items"), TRANS("Moves any items that are lost beyond the edges of the screen back to the centre."), CommandCategories::editing, 0);
  660. result.setActive (currentPaintRoutine != nullptr || currentLayout != nullptr);
  661. result.defaultKeypresses.add (KeyPress ('m', cmd, 0));
  662. break;
  663. case JucerCommandIDs::zoomIn:
  664. result.setInfo (TRANS("Zoom in"), TRANS("Zooms in on the current component."), CommandCategories::editing, 0);
  665. result.setActive (currentPaintRoutine != nullptr || currentLayout != nullptr);
  666. result.defaultKeypresses.add (KeyPress (']', cmd, 0));
  667. break;
  668. case JucerCommandIDs::zoomOut:
  669. result.setInfo (TRANS("Zoom out"), TRANS("Zooms out on the current component."), CommandCategories::editing, 0);
  670. result.setActive (currentPaintRoutine != nullptr || currentLayout != nullptr);
  671. result.defaultKeypresses.add (KeyPress ('[', cmd, 0));
  672. break;
  673. case JucerCommandIDs::zoomNormal:
  674. result.setInfo (TRANS("Zoom to 100%"), TRANS("Restores the zoom level to normal."), CommandCategories::editing, 0);
  675. result.setActive (currentPaintRoutine != nullptr || currentLayout != nullptr);
  676. result.defaultKeypresses.add (KeyPress ('1', cmd, 0));
  677. break;
  678. case JucerCommandIDs::spaceBarDrag:
  679. result.setInfo (TRANS("Scroll while dragging mouse"), TRANS("When held down, this key lets you scroll around by dragging with the mouse."),
  680. CommandCategories::view, ApplicationCommandInfo::wantsKeyUpDownCallbacks);
  681. result.setActive (currentPaintRoutine != nullptr || currentLayout != nullptr);
  682. result.defaultKeypresses.add (KeyPress (KeyPress::spaceKey, 0, 0));
  683. break;
  684. case JucerCommandIDs::compOverlay0:
  685. case JucerCommandIDs::compOverlay33:
  686. case JucerCommandIDs::compOverlay66:
  687. case JucerCommandIDs::compOverlay100:
  688. {
  689. int amount = 0, num = 0;
  690. if (commandID == JucerCommandIDs::compOverlay33)
  691. {
  692. amount = 33;
  693. num = 1;
  694. }
  695. else if (commandID == JucerCommandIDs::compOverlay66)
  696. {
  697. amount = 66;
  698. num = 2;
  699. }
  700. else if (commandID == JucerCommandIDs::compOverlay100)
  701. {
  702. amount = 100;
  703. num = 3;
  704. }
  705. result.defaultKeypresses.add (KeyPress ('2' + num, cmd, 0));
  706. int currentAmount = 0;
  707. if (document != nullptr && document->getComponentOverlayOpacity() > 0.9f)
  708. currentAmount = 100;
  709. else if (document != nullptr && document->getComponentOverlayOpacity() > 0.6f)
  710. currentAmount = 66;
  711. else if (document != nullptr && document->getComponentOverlayOpacity() > 0.3f)
  712. currentAmount = 33;
  713. result.setInfo (commandID == JucerCommandIDs::compOverlay0
  714. ? TRANS("No component overlay")
  715. : TRANS("Overlay with opacity of 123%").replace ("123", String (amount)),
  716. TRANS("Changes the opacity of the components that are shown over the top of the graphics editor."),
  717. CommandCategories::view, 0);
  718. result.setActive (currentPaintRoutine != nullptr && document->getComponentLayout() != nullptr);
  719. result.setTicked (amount == currentAmount);
  720. }
  721. break;
  722. case JucerCommandIDs::alignTop:
  723. result.setInfo (TRANS ("Align top"),
  724. TRANS ("Aligns the top edges of all selected components to the first component that was selected."),
  725. CommandCategories::editing, 0);
  726. result.setActive (areMultipleThingsSelected());
  727. break;
  728. case JucerCommandIDs::alignRight:
  729. result.setInfo (TRANS ("Align right"),
  730. TRANS ("Aligns the right edges of all selected components to the first component that was selected."),
  731. CommandCategories::editing, 0);
  732. result.setActive (areMultipleThingsSelected());
  733. break;
  734. case JucerCommandIDs::alignBottom:
  735. result.setInfo (TRANS ("Align bottom"),
  736. TRANS ("Aligns the bottom edges of all selected components to the first component that was selected."),
  737. CommandCategories::editing, 0);
  738. result.setActive (areMultipleThingsSelected());
  739. break;
  740. case JucerCommandIDs::alignLeft:
  741. result.setInfo (TRANS ("Align left"),
  742. TRANS ("Aligns the left edges of all selected components to the first component that was selected."),
  743. CommandCategories::editing, 0);
  744. result.setActive (areMultipleThingsSelected());
  745. break;
  746. case StandardApplicationCommandIDs::undo:
  747. result.setInfo (TRANS ("Undo"), TRANS ("Undo"), "Editing", 0);
  748. result.setActive (document != nullptr && document->getUndoManager().canUndo());
  749. result.defaultKeypresses.add (KeyPress ('z', cmd, 0));
  750. break;
  751. case StandardApplicationCommandIDs::redo:
  752. result.setInfo (TRANS ("Redo"), TRANS ("Redo"), "Editing", 0);
  753. result.setActive (document != nullptr && document->getUndoManager().canRedo());
  754. result.defaultKeypresses.add (KeyPress ('z', cmd | shift, 0));
  755. break;
  756. case StandardApplicationCommandIDs::cut:
  757. result.setInfo (TRANS ("Cut"), String(), "Editing", 0);
  758. result.setActive (isSomethingSelected());
  759. result.defaultKeypresses.add (KeyPress ('x', cmd, 0));
  760. break;
  761. case StandardApplicationCommandIDs::copy:
  762. result.setInfo (TRANS ("Copy"), String(), "Editing", 0);
  763. result.setActive (isSomethingSelected());
  764. result.defaultKeypresses.add (KeyPress ('c', cmd, 0));
  765. break;
  766. case StandardApplicationCommandIDs::paste:
  767. {
  768. result.setInfo (TRANS ("Paste"), String(), "Editing", 0);
  769. result.defaultKeypresses.add (KeyPress ('v', cmd, 0));
  770. bool canPaste = false;
  771. std::unique_ptr<XmlElement> doc (XmlDocument::parse (SystemClipboard::getTextFromClipboard()));
  772. if (doc != nullptr)
  773. {
  774. if (doc->hasTagName (ComponentLayout::clipboardXmlTag))
  775. canPaste = (currentLayout != nullptr);
  776. else if (doc->hasTagName (PaintRoutine::clipboardXmlTag))
  777. canPaste = (currentPaintRoutine != nullptr);
  778. }
  779. result.setActive (canPaste);
  780. }
  781. break;
  782. case StandardApplicationCommandIDs::del:
  783. result.setInfo (TRANS ("Delete"), String(), "Editing", 0);
  784. result.setActive (isSomethingSelected());
  785. break;
  786. case StandardApplicationCommandIDs::selectAll:
  787. result.setInfo (TRANS ("Select All"), String(), "Editing", 0);
  788. result.setActive (currentPaintRoutine != nullptr || currentLayout != nullptr);
  789. result.defaultKeypresses.add (KeyPress ('a', cmd, 0));
  790. break;
  791. case StandardApplicationCommandIDs::deselectAll:
  792. result.setInfo (TRANS ("Deselect All"), String(), "Editing", 0);
  793. result.setActive (currentPaintRoutine != nullptr || currentLayout != nullptr);
  794. result.defaultKeypresses.add (KeyPress ('d', cmd, 0));
  795. break;
  796. default:
  797. break;
  798. }
  799. }
  800. bool JucerDocumentEditor::perform (const InvocationInfo& info)
  801. {
  802. ComponentLayout* const currentLayout = getCurrentLayout();
  803. PaintRoutine* const currentPaintRoutine = getCurrentPaintRoutine();
  804. document->beginTransaction();
  805. if (info.commandID >= JucerCommandIDs::newComponentBase
  806. && info.commandID < JucerCommandIDs::newComponentBase + ObjectTypes::numComponentTypes)
  807. {
  808. addComponent (info.commandID - JucerCommandIDs::newComponentBase);
  809. return true;
  810. }
  811. if (info.commandID >= JucerCommandIDs::newElementBase
  812. && info.commandID < JucerCommandIDs::newElementBase + ObjectTypes::numElementTypes)
  813. {
  814. addElement (info.commandID - JucerCommandIDs::newElementBase);
  815. return true;
  816. }
  817. switch (info.commandID)
  818. {
  819. case StandardApplicationCommandIDs::undo:
  820. document->getUndoManager().undo();
  821. document->dispatchPendingMessages();
  822. break;
  823. case StandardApplicationCommandIDs::redo:
  824. document->getUndoManager().redo();
  825. document->dispatchPendingMessages();
  826. break;
  827. case JucerCommandIDs::test:
  828. TestComponent::showInDialogBox (*document);
  829. break;
  830. case JucerCommandIDs::enableSnapToGrid:
  831. document->setSnappingGrid (document->getSnappingGridSize(),
  832. ! document->isSnapActive (false),
  833. document->isSnapShown());
  834. break;
  835. case JucerCommandIDs::showGrid:
  836. document->setSnappingGrid (document->getSnappingGridSize(),
  837. document->isSnapActive (false),
  838. ! document->isSnapShown());
  839. break;
  840. case JucerCommandIDs::editCompLayout:
  841. showLayout();
  842. break;
  843. case JucerCommandIDs::editCompGraphics:
  844. showGraphics (0);
  845. break;
  846. case JucerCommandIDs::zoomIn: setZoom (snapToIntegerZoom (getZoom() * 2.0)); break;
  847. case JucerCommandIDs::zoomOut: setZoom (snapToIntegerZoom (getZoom() / 2.0)); break;
  848. case JucerCommandIDs::zoomNormal: setZoom (1.0); break;
  849. case JucerCommandIDs::spaceBarDrag:
  850. if (EditingPanelBase* panel = dynamic_cast<EditingPanelBase*> (tabbedComponent.getCurrentContentComponent()))
  851. panel->dragKeyHeldDown (info.isKeyDown);
  852. break;
  853. case JucerCommandIDs::compOverlay0:
  854. case JucerCommandIDs::compOverlay33:
  855. case JucerCommandIDs::compOverlay66:
  856. case JucerCommandIDs::compOverlay100:
  857. {
  858. int amount = 0;
  859. if (info.commandID == JucerCommandIDs::compOverlay33)
  860. amount = 33;
  861. else if (info.commandID == JucerCommandIDs::compOverlay66)
  862. amount = 66;
  863. else if (info.commandID == JucerCommandIDs::compOverlay100)
  864. amount = 100;
  865. document->setComponentOverlayOpacity (amount * 0.01f);
  866. }
  867. break;
  868. case JucerCommandIDs::bringBackLostItems:
  869. if (EditingPanelBase* panel = dynamic_cast<EditingPanelBase*> (tabbedComponent.getCurrentContentComponent()))
  870. {
  871. int w = panel->getComponentArea().getWidth();
  872. int h = panel->getComponentArea().getHeight();
  873. if (currentPaintRoutine != nullptr)
  874. currentPaintRoutine->bringLostItemsBackOnScreen (panel->getComponentArea());
  875. else if (currentLayout != nullptr)
  876. currentLayout->bringLostItemsBackOnScreen (w, h);
  877. }
  878. break;
  879. case JucerCommandIDs::toFront:
  880. if (currentLayout != nullptr)
  881. currentLayout->selectedToFront();
  882. else if (currentPaintRoutine != nullptr)
  883. currentPaintRoutine->selectedToFront();
  884. break;
  885. case JucerCommandIDs::toBack:
  886. if (currentLayout != nullptr)
  887. currentLayout->selectedToBack();
  888. else if (currentPaintRoutine != nullptr)
  889. currentPaintRoutine->selectedToBack();
  890. break;
  891. case JucerCommandIDs::group:
  892. if (currentPaintRoutine != nullptr)
  893. currentPaintRoutine->groupSelected();
  894. break;
  895. case JucerCommandIDs::ungroup:
  896. if (currentPaintRoutine != nullptr)
  897. currentPaintRoutine->ungroupSelected();
  898. break;
  899. case JucerCommandIDs::alignTop:
  900. if (currentLayout != nullptr)
  901. currentLayout->alignTop();
  902. else if (currentPaintRoutine != nullptr)
  903. currentPaintRoutine->alignTop();
  904. break;
  905. case JucerCommandIDs::alignRight:
  906. if (currentLayout != nullptr)
  907. currentLayout->alignRight();
  908. else if (currentPaintRoutine != nullptr)
  909. currentPaintRoutine->alignRight();
  910. break;
  911. case JucerCommandIDs::alignBottom:
  912. if (currentLayout != nullptr)
  913. currentLayout->alignBottom();
  914. else if (currentPaintRoutine != nullptr)
  915. currentPaintRoutine->alignBottom();
  916. break;
  917. case JucerCommandIDs::alignLeft:
  918. if (currentLayout != nullptr)
  919. currentLayout->alignLeft();
  920. else if (currentPaintRoutine != nullptr)
  921. currentPaintRoutine->alignLeft();
  922. break;
  923. case StandardApplicationCommandIDs::cut:
  924. if (currentLayout != nullptr)
  925. {
  926. currentLayout->copySelectedToClipboard();
  927. currentLayout->deleteSelected();
  928. }
  929. else if (currentPaintRoutine != nullptr)
  930. {
  931. currentPaintRoutine->copySelectedToClipboard();
  932. currentPaintRoutine->deleteSelected();
  933. }
  934. break;
  935. case StandardApplicationCommandIDs::copy:
  936. if (currentLayout != nullptr)
  937. currentLayout->copySelectedToClipboard();
  938. else if (currentPaintRoutine != nullptr)
  939. currentPaintRoutine->copySelectedToClipboard();
  940. break;
  941. case StandardApplicationCommandIDs::paste:
  942. {
  943. std::unique_ptr<XmlElement> doc (XmlDocument::parse (SystemClipboard::getTextFromClipboard()));
  944. if (doc != nullptr)
  945. {
  946. if (doc->hasTagName (ComponentLayout::clipboardXmlTag))
  947. {
  948. if (currentLayout != nullptr)
  949. currentLayout->paste();
  950. }
  951. else if (doc->hasTagName (PaintRoutine::clipboardXmlTag))
  952. {
  953. if (currentPaintRoutine != nullptr)
  954. currentPaintRoutine->paste();
  955. }
  956. }
  957. }
  958. break;
  959. case StandardApplicationCommandIDs::del:
  960. if (currentLayout != nullptr)
  961. currentLayout->deleteSelected();
  962. else if (currentPaintRoutine != nullptr)
  963. currentPaintRoutine->deleteSelected();
  964. break;
  965. case StandardApplicationCommandIDs::selectAll:
  966. if (currentLayout != nullptr)
  967. currentLayout->selectAll();
  968. else if (currentPaintRoutine != nullptr)
  969. currentPaintRoutine->selectAll();
  970. break;
  971. case StandardApplicationCommandIDs::deselectAll:
  972. if (currentLayout != nullptr)
  973. {
  974. currentLayout->getSelectedSet().deselectAll();
  975. }
  976. else if (currentPaintRoutine != nullptr)
  977. {
  978. currentPaintRoutine->getSelectedElements().deselectAll();
  979. currentPaintRoutine->getSelectedPoints().deselectAll();
  980. }
  981. break;
  982. default:
  983. return false;
  984. }
  985. document->beginTransaction();
  986. return true;
  987. }
  988. bool JucerDocumentEditor::keyPressed (const KeyPress& key)
  989. {
  990. if (key.isKeyCode (KeyPress::deleteKey) || key.isKeyCode (KeyPress::backspaceKey))
  991. {
  992. ProjucerApplication::getCommandManager().invokeDirectly (StandardApplicationCommandIDs::del, true);
  993. return true;
  994. }
  995. return false;
  996. }
  997. JucerDocumentEditor* JucerDocumentEditor::getActiveDocumentHolder()
  998. {
  999. ApplicationCommandInfo info (0);
  1000. return dynamic_cast<JucerDocumentEditor*> (ProjucerApplication::getCommandManager()
  1001. .getTargetForCommand (JucerCommandIDs::editCompLayout, info));
  1002. }
  1003. Image JucerDocumentEditor::createComponentLayerSnapshot() const
  1004. {
  1005. if (compLayoutPanel != nullptr)
  1006. return compLayoutPanel->createComponentSnapshot();
  1007. return {};
  1008. }
  1009. const int gridSnapMenuItemBase = 0x8723620;
  1010. const int snapSizes[] = { 2, 3, 4, 5, 6, 8, 10, 12, 16, 20, 24, 32 };
  1011. void createGUIEditorMenu (PopupMenu&);
  1012. void createGUIEditorMenu (PopupMenu& menu)
  1013. {
  1014. auto* commandManager = &ProjucerApplication::getCommandManager();
  1015. menu.addCommandItem (commandManager, JucerCommandIDs::editCompLayout);
  1016. menu.addCommandItem (commandManager, JucerCommandIDs::editCompGraphics);
  1017. menu.addSeparator();
  1018. PopupMenu newComps;
  1019. for (int i = 0; i < ObjectTypes::numComponentTypes; ++i)
  1020. newComps.addCommandItem (commandManager, JucerCommandIDs::newComponentBase + i);
  1021. menu.addSubMenu ("Add new component", newComps);
  1022. PopupMenu newElements;
  1023. for (int i = 0; i < ObjectTypes::numElementTypes; ++i)
  1024. newElements.addCommandItem (commandManager, JucerCommandIDs::newElementBase + i);
  1025. menu.addSubMenu ("Add new graphic element", newElements);
  1026. menu.addSeparator();
  1027. menu.addCommandItem (commandManager, StandardApplicationCommandIDs::cut);
  1028. menu.addCommandItem (commandManager, StandardApplicationCommandIDs::copy);
  1029. menu.addCommandItem (commandManager, StandardApplicationCommandIDs::paste);
  1030. menu.addCommandItem (commandManager, StandardApplicationCommandIDs::del);
  1031. menu.addCommandItem (commandManager, StandardApplicationCommandIDs::selectAll);
  1032. menu.addCommandItem (commandManager, StandardApplicationCommandIDs::deselectAll);
  1033. menu.addSeparator();
  1034. menu.addCommandItem (commandManager, JucerCommandIDs::toFront);
  1035. menu.addCommandItem (commandManager, JucerCommandIDs::toBack);
  1036. menu.addSeparator();
  1037. menu.addCommandItem (commandManager, JucerCommandIDs::group);
  1038. menu.addCommandItem (commandManager, JucerCommandIDs::ungroup);
  1039. menu.addSeparator();
  1040. menu.addCommandItem (commandManager, JucerCommandIDs::bringBackLostItems);
  1041. menu.addSeparator();
  1042. menu.addCommandItem (commandManager, JucerCommandIDs::showGrid);
  1043. menu.addCommandItem (commandManager, JucerCommandIDs::enableSnapToGrid);
  1044. auto* holder = JucerDocumentEditor::getActiveDocumentHolder();
  1045. {
  1046. auto currentSnapSize = holder != nullptr ? holder->getDocument()->getSnappingGridSize() : -1;
  1047. PopupMenu m;
  1048. for (int i = 0; i < numElementsInArray (snapSizes); ++i)
  1049. m.addItem (gridSnapMenuItemBase + i, String (snapSizes[i]) + " pixels",
  1050. true, snapSizes[i] == currentSnapSize);
  1051. menu.addSubMenu ("Grid size", m, currentSnapSize >= 0);
  1052. }
  1053. menu.addSeparator();
  1054. menu.addCommandItem (commandManager, JucerCommandIDs::zoomIn);
  1055. menu.addCommandItem (commandManager, JucerCommandIDs::zoomOut);
  1056. menu.addCommandItem (commandManager, JucerCommandIDs::zoomNormal);
  1057. menu.addSeparator();
  1058. menu.addCommandItem (commandManager, JucerCommandIDs::test);
  1059. menu.addSeparator();
  1060. {
  1061. PopupMenu overlays;
  1062. overlays.addCommandItem (commandManager, JucerCommandIDs::compOverlay0);
  1063. overlays.addCommandItem (commandManager, JucerCommandIDs::compOverlay33);
  1064. overlays.addCommandItem (commandManager, JucerCommandIDs::compOverlay66);
  1065. overlays.addCommandItem (commandManager, JucerCommandIDs::compOverlay100);
  1066. menu.addSubMenu ("Component Overlay", overlays, holder != nullptr);
  1067. }
  1068. }
  1069. void handleGUIEditorMenuCommand (int);
  1070. void handleGUIEditorMenuCommand (int menuItemID)
  1071. {
  1072. if (auto* ed = JucerDocumentEditor::getActiveDocumentHolder())
  1073. {
  1074. int gridIndex = menuItemID - gridSnapMenuItemBase;
  1075. if (isPositiveAndBelow (gridIndex, numElementsInArray (snapSizes)))
  1076. {
  1077. auto& doc = *ed->getDocument();
  1078. doc.setSnappingGrid (snapSizes [gridIndex],
  1079. doc.isSnapActive (false),
  1080. doc.isSnapShown());
  1081. }
  1082. }
  1083. }
  1084. void registerGUIEditorCommands();
  1085. void registerGUIEditorCommands()
  1086. {
  1087. JucerDocumentEditor dh (nullptr);
  1088. ProjucerApplication::getCommandManager().registerAllCommandsForTarget (&dh);
  1089. }