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.

1335 lines
50KB

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