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.

1316 lines
49KB

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