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.

1196 lines
45KB

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