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.

884 lines
29KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2020 - 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 6 End-User License
  8. Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
  9. End User License Agreement: www.juce.com/juce-6-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 "jucer_ProjectContentComponent.h"
  20. #include "Sidebar/jucer_Sidebar.h"
  21. NewFileWizard::Type* createGUIComponentWizard();
  22. //==============================================================================
  23. ProjectContentComponent::ProjectContentComponent()
  24. : sidebar (std::make_unique<Sidebar> (project))
  25. {
  26. setOpaque (true);
  27. setWantsKeyboardFocus (true);
  28. addAndMakeVisible (headerComponent);
  29. addAndMakeVisible (projectMessagesComponent);
  30. addAndMakeVisible (contentViewComponent);
  31. sidebarSizeConstrainer.setMinimumWidth (200);
  32. sidebarSizeConstrainer.setMaximumWidth (500);
  33. ProjucerApplication::getApp().openDocumentManager.addListener (this);
  34. getGlobalProperties().addChangeListener (this);
  35. }
  36. ProjectContentComponent::~ProjectContentComponent()
  37. {
  38. getGlobalProperties().removeChangeListener (this);
  39. ProjucerApplication::getApp().openDocumentManager.removeListener (this);
  40. setProject (nullptr);
  41. removeChildComponent (&bubbleMessage);
  42. }
  43. void ProjectContentComponent::paint (Graphics& g)
  44. {
  45. g.fillAll (findColour (backgroundColourId));
  46. }
  47. void ProjectContentComponent::resized()
  48. {
  49. auto r = getLocalBounds();
  50. r.removeFromRight (10);
  51. r.removeFromLeft (15);
  52. r.removeFromTop (5);
  53. projectMessagesComponent.setBounds (r.removeFromBottom (40).withWidth (100).reduced (0, 5));
  54. headerComponent.setBounds (r.removeFromTop (40));
  55. r.removeFromTop (10);
  56. auto sidebarArea = r.removeFromLeft (sidebar != nullptr && sidebar->getWidth() != 0 ? sidebar->getWidth()
  57. : r.getWidth() / 4);
  58. if (sidebar != nullptr && sidebar->isVisible())
  59. sidebar->setBounds (sidebarArea);
  60. if (resizerBar != nullptr)
  61. resizerBar->setBounds (r.withWidth (4));
  62. contentViewComponent.setBounds (r);
  63. headerComponent.sidebarTabsWidthChanged (sidebarArea.getWidth());
  64. }
  65. void ProjectContentComponent::lookAndFeelChanged()
  66. {
  67. repaint();
  68. if (translationTool != nullptr)
  69. translationTool->repaint();
  70. }
  71. void ProjectContentComponent::childBoundsChanged (Component* child)
  72. {
  73. if (child == sidebar.get())
  74. resized();
  75. }
  76. void ProjectContentComponent::setProject (Project* newProject)
  77. {
  78. if (project != newProject)
  79. {
  80. if (project != nullptr)
  81. project->removeChangeListener (this);
  82. hideEditor();
  83. resizerBar = nullptr;
  84. sidebar = nullptr;
  85. project = newProject;
  86. if (project != nullptr)
  87. {
  88. sidebar = std::make_unique<Sidebar> (project);
  89. addAndMakeVisible (sidebar.get());
  90. //==============================================================================
  91. resizerBar = std::make_unique<ResizableEdgeComponent> (sidebar.get(), &sidebarSizeConstrainer,
  92. ResizableEdgeComponent::rightEdge);
  93. addAndMakeVisible (resizerBar.get());
  94. resizerBar->setAlwaysOnTop (true);
  95. project->addChangeListener (this);
  96. updateMissingFileStatuses();
  97. headerComponent.setVisible (true);
  98. headerComponent.setCurrentProject (project);
  99. projectMessagesComponent.setVisible (true);
  100. }
  101. else
  102. {
  103. headerComponent.setVisible (false);
  104. projectMessagesComponent.setVisible (false);
  105. }
  106. projectMessagesComponent.setProject (project);
  107. resized();
  108. }
  109. }
  110. void ProjectContentComponent::saveTreeViewState()
  111. {
  112. }
  113. void ProjectContentComponent::saveOpenDocumentList()
  114. {
  115. if (project != nullptr)
  116. {
  117. std::unique_ptr<XmlElement> xml (recentDocumentList.createXML());
  118. if (xml != nullptr)
  119. project->getStoredProperties().setValue ("lastDocs", xml.get());
  120. }
  121. }
  122. void ProjectContentComponent::reloadLastOpenDocuments()
  123. {
  124. if (project != nullptr)
  125. {
  126. if (auto xml = project->getStoredProperties().getXmlValue ("lastDocs"))
  127. {
  128. recentDocumentList.restoreFromXML (*project, *xml);
  129. showDocument (recentDocumentList.getCurrentDocument(), true);
  130. }
  131. }
  132. }
  133. bool ProjectContentComponent::documentAboutToClose (OpenDocumentManager::Document* document)
  134. {
  135. hideDocument (document);
  136. return true;
  137. }
  138. void ProjectContentComponent::changeListenerCallback (ChangeBroadcaster* broadcaster)
  139. {
  140. if (broadcaster == project)
  141. updateMissingFileStatuses();
  142. }
  143. void ProjectContentComponent::refreshProjectTreeFileStatuses()
  144. {
  145. if (sidebar != nullptr)
  146. if (auto* fileTree = sidebar->getFileTreePanel())
  147. fileTree->repaint();
  148. }
  149. void ProjectContentComponent::updateMissingFileStatuses()
  150. {
  151. if (sidebar != nullptr)
  152. if (auto* tree = sidebar->getFileTreePanel())
  153. tree->updateMissingFileStatuses();
  154. }
  155. bool ProjectContentComponent::showEditorForFile (const File& fileToShow, bool grabFocus)
  156. {
  157. if (getCurrentFile() != fileToShow)
  158. return showDocument (ProjucerApplication::getApp().openDocumentManager.openFile (project, fileToShow), grabFocus);
  159. return true;
  160. }
  161. bool ProjectContentComponent::hasFileInRecentList (const File& f) const
  162. {
  163. return recentDocumentList.contains (f);
  164. }
  165. File ProjectContentComponent::getCurrentFile() const
  166. {
  167. return currentDocument != nullptr ? currentDocument->getFile()
  168. : File();
  169. }
  170. bool ProjectContentComponent::showDocument (OpenDocumentManager::Document* doc, bool grabFocus)
  171. {
  172. if (doc == nullptr)
  173. return false;
  174. if (doc->hasFileBeenModifiedExternally())
  175. doc->reloadFromFile();
  176. if (doc != getCurrentDocument())
  177. {
  178. recentDocumentList.newDocumentOpened (doc);
  179. setEditorDocument (doc->createEditor(), doc);
  180. }
  181. if (grabFocus && contentViewComponent.isShowing())
  182. contentViewComponent.grabKeyboardFocus();
  183. return true;
  184. }
  185. void ProjectContentComponent::hideEditor()
  186. {
  187. currentDocument = nullptr;
  188. contentViewComponent.setContent ({}, {});
  189. ProjucerApplication::getCommandManager().commandStatusChanged();
  190. resized();
  191. }
  192. void ProjectContentComponent::hideDocument (OpenDocumentManager::Document* doc)
  193. {
  194. if (doc != currentDocument)
  195. return;
  196. if (auto* replacement = recentDocumentList.getClosestPreviousDocOtherThan (currentDocument))
  197. showDocument (replacement, true);
  198. else
  199. hideEditor();
  200. }
  201. void ProjectContentComponent::setScrollableEditorComponent (std::unique_ptr<Component> component)
  202. {
  203. jassert (component.get() != nullptr);
  204. class ContentViewport : public Component
  205. {
  206. public:
  207. ContentViewport (std::unique_ptr<Component> content)
  208. {
  209. contentViewport.setViewedComponent (content.release(), true);
  210. addAndMakeVisible (contentViewport);
  211. }
  212. void resized() override
  213. {
  214. contentViewport.setBounds (getLocalBounds());
  215. }
  216. private:
  217. Viewport contentViewport;
  218. };
  219. contentViewComponent.setContent (std::make_unique<ContentViewport> (std::move (component)), {});
  220. currentDocument = nullptr;
  221. ProjucerApplication::getCommandManager().commandStatusChanged();
  222. }
  223. void ProjectContentComponent::setEditorDocument (std::unique_ptr<Component> component, OpenDocumentManager::Document* doc)
  224. {
  225. currentDocument = doc;
  226. contentViewComponent.setContent (std::move (component),
  227. currentDocument != nullptr ? currentDocument->getFile().getFileName()
  228. : String());
  229. ProjucerApplication::getCommandManager().commandStatusChanged();
  230. }
  231. Component* ProjectContentComponent::getEditorComponent()
  232. {
  233. return contentViewComponent.getCurrentComponent();
  234. }
  235. void ProjectContentComponent::closeDocument()
  236. {
  237. if (currentDocument != nullptr)
  238. {
  239. ProjucerApplication::getApp().openDocumentManager
  240. .closeDocument (currentDocument, OpenDocumentManager::SaveIfNeeded::yes);
  241. return;
  242. }
  243. if (! goToPreviousFile())
  244. hideEditor();
  245. }
  246. static void showSaveWarning (OpenDocumentManager::Document* currentDocument)
  247. {
  248. AlertWindow::showMessageBox (AlertWindow::WarningIcon,
  249. TRANS("Save failed!"),
  250. TRANS("Couldn't save the file:")
  251. + "\n" + currentDocument->getFile().getFullPathName());
  252. }
  253. void ProjectContentComponent::saveDocument()
  254. {
  255. if (currentDocument != nullptr)
  256. {
  257. if (! currentDocument->save())
  258. showSaveWarning (currentDocument);
  259. refreshProjectTreeFileStatuses();
  260. }
  261. else
  262. {
  263. saveProject();
  264. }
  265. }
  266. void ProjectContentComponent::saveAs()
  267. {
  268. if (currentDocument != nullptr)
  269. {
  270. if (! currentDocument->saveAs())
  271. showSaveWarning (currentDocument);
  272. refreshProjectTreeFileStatuses();
  273. }
  274. }
  275. bool ProjectContentComponent::goToPreviousFile()
  276. {
  277. auto* doc = recentDocumentList.getCurrentDocument();
  278. if (doc == nullptr || doc == getCurrentDocument())
  279. doc = recentDocumentList.getPrevious();
  280. return showDocument (doc, true);
  281. }
  282. bool ProjectContentComponent::goToNextFile()
  283. {
  284. return showDocument (recentDocumentList.getNext(), true);
  285. }
  286. bool ProjectContentComponent::canGoToCounterpart() const
  287. {
  288. return currentDocument != nullptr
  289. && currentDocument->getCounterpartFile().exists();
  290. }
  291. bool ProjectContentComponent::goToCounterpart()
  292. {
  293. if (currentDocument != nullptr)
  294. {
  295. auto file = currentDocument->getCounterpartFile();
  296. if (file.exists())
  297. return showEditorForFile (file, true);
  298. }
  299. return false;
  300. }
  301. bool ProjectContentComponent::saveProject()
  302. {
  303. if (project != nullptr)
  304. return (project->save (true, true) == FileBasedDocument::savedOk);
  305. return false;
  306. }
  307. void ProjectContentComponent::closeProject()
  308. {
  309. if (auto* mw = findParentComponentOfClass<MainWindow>())
  310. mw->closeCurrentProject (OpenDocumentManager::SaveIfNeeded::yes);
  311. }
  312. void ProjectContentComponent::showProjectSettings()
  313. {
  314. setScrollableEditorComponent (std::make_unique<ProjectSettingsComponent> (*project));
  315. }
  316. void ProjectContentComponent::showCurrentExporterSettings()
  317. {
  318. if (auto selected = headerComponent.getSelectedExporter())
  319. showExporterSettings (selected->getUniqueName());
  320. }
  321. void ProjectContentComponent::showExporterSettings (const String& exporterName)
  322. {
  323. if (exporterName.isEmpty())
  324. return;
  325. showExportersPanel();
  326. if (sidebar == nullptr)
  327. return;
  328. if (auto* exportersPanel = sidebar->getExportersTreePanel())
  329. {
  330. if (auto* exporters = dynamic_cast<TreeItemTypes::ExportersTreeRoot*> (exportersPanel->rootItem.get()))
  331. {
  332. for (auto i = exporters->getNumSubItems(); i >= 0; --i)
  333. {
  334. if (auto* e = dynamic_cast<TreeItemTypes::ExporterItem*> (exporters->getSubItem (i)))
  335. {
  336. if (e->getDisplayName() == exporterName)
  337. {
  338. if (e->isSelected())
  339. e->setSelected (false, true);
  340. e->setSelected (true, true);
  341. }
  342. }
  343. }
  344. }
  345. }
  346. }
  347. void ProjectContentComponent::showModule (const String& moduleID)
  348. {
  349. showModulesPanel();
  350. if (sidebar == nullptr)
  351. return;
  352. if (auto* modsPanel = sidebar->getModuleTreePanel())
  353. {
  354. if (auto* mods = dynamic_cast<TreeItemTypes::EnabledModulesItem*> (modsPanel->rootItem.get()))
  355. {
  356. for (auto i = mods->getNumSubItems(); --i >= 0;)
  357. {
  358. if (auto* m = dynamic_cast<TreeItemTypes::ModuleItem*> (mods->getSubItem (i)))
  359. {
  360. if (m->moduleID == moduleID)
  361. {
  362. if (m->isSelected())
  363. m->setSelected (false, true);
  364. m->setSelected (true, true);
  365. }
  366. }
  367. }
  368. }
  369. }
  370. }
  371. StringArray ProjectContentComponent::getExportersWhichCanLaunch() const
  372. {
  373. StringArray s;
  374. if (project != nullptr)
  375. for (Project::ExporterIterator exporter (*project); exporter.next();)
  376. if (exporter->canLaunchProject())
  377. s.add (exporter->getUniqueName());
  378. return s;
  379. }
  380. void ProjectContentComponent::openInSelectedIDE (bool saveFirst)
  381. {
  382. if (project != nullptr)
  383. if (auto selectedExporter = headerComponent.getSelectedExporter())
  384. project->openProjectInIDE (*selectedExporter, saveFirst);
  385. }
  386. void ProjectContentComponent::showNewExporterMenu()
  387. {
  388. if (project != nullptr)
  389. {
  390. PopupMenu menu;
  391. menu.addSectionHeader ("Create a new export target:");
  392. SafePointer<ProjectContentComponent> safeThis (this);
  393. for (auto& exporterInfo : ProjectExporter::getExporterTypeInfos())
  394. {
  395. PopupMenu::Item item;
  396. item.itemID = -1;
  397. item.text = exporterInfo.displayName;
  398. item.image = [exporterInfo]
  399. {
  400. auto drawableImage = std::make_unique<DrawableImage>();
  401. drawableImage->setImage (exporterInfo.icon);
  402. return drawableImage;
  403. }();
  404. item.action = [safeThis, exporterInfo]
  405. {
  406. if (safeThis != nullptr)
  407. if (auto* p = safeThis->getProject())
  408. p->addNewExporter (exporterInfo.identifier);
  409. };
  410. menu.addItem (item);
  411. }
  412. menu.showMenuAsync ({});
  413. }
  414. }
  415. void ProjectContentComponent::deleteSelectedTreeItems()
  416. {
  417. if (sidebar != nullptr)
  418. if (auto* tree = sidebar->getTreeWithSelectedItems())
  419. tree->deleteSelectedItems();
  420. }
  421. void ProjectContentComponent::showBubbleMessage (Rectangle<int> pos, const String& text)
  422. {
  423. addChildComponent (bubbleMessage);
  424. bubbleMessage.setColour (BubbleComponent::backgroundColourId, Colours::white.withAlpha (0.7f));
  425. bubbleMessage.setColour (BubbleComponent::outlineColourId, Colours::black.withAlpha (0.8f));
  426. bubbleMessage.setAlwaysOnTop (true);
  427. bubbleMessage.showAt (pos, AttributedString (text), 3000, true, false);
  428. }
  429. //==============================================================================
  430. void ProjectContentComponent::showTranslationTool()
  431. {
  432. if (translationTool != nullptr)
  433. {
  434. translationTool->toFront (true);
  435. }
  436. else if (project != nullptr)
  437. {
  438. new FloatingToolWindow ("Translation File Builder",
  439. "transToolWindowPos",
  440. new TranslationToolComponent(),
  441. translationTool, true,
  442. 600, 700,
  443. 600, 400, 10000, 10000);
  444. }
  445. }
  446. //==============================================================================
  447. struct AsyncCommandRetrier : public Timer
  448. {
  449. AsyncCommandRetrier (const ApplicationCommandTarget::InvocationInfo& i) : info (i)
  450. {
  451. info.originatingComponent = nullptr;
  452. startTimer (500);
  453. }
  454. void timerCallback() override
  455. {
  456. stopTimer();
  457. ProjucerApplication::getCommandManager().invoke (info, true);
  458. delete this;
  459. }
  460. ApplicationCommandTarget::InvocationInfo info;
  461. JUCE_DECLARE_NON_COPYABLE (AsyncCommandRetrier)
  462. };
  463. static bool reinvokeCommandAfterCancellingModalComps (const ApplicationCommandTarget::InvocationInfo& info)
  464. {
  465. if (ModalComponentManager::getInstance()->cancelAllModalComponents())
  466. {
  467. new AsyncCommandRetrier (info);
  468. return true;
  469. }
  470. return false;
  471. }
  472. //==============================================================================
  473. ApplicationCommandTarget* ProjectContentComponent::getNextCommandTarget()
  474. {
  475. return findFirstTargetParentComponent();
  476. }
  477. void ProjectContentComponent::getAllCommands (Array <CommandID>& commands)
  478. {
  479. commands.addArray ({ CommandIDs::saveProject,
  480. CommandIDs::closeProject,
  481. CommandIDs::saveDocument,
  482. CommandIDs::saveDocumentAs,
  483. CommandIDs::closeDocument,
  484. CommandIDs::goToPreviousDoc,
  485. CommandIDs::goToNextDoc,
  486. CommandIDs::goToCounterpart,
  487. CommandIDs::showProjectSettings,
  488. CommandIDs::showFileExplorerPanel,
  489. CommandIDs::showModulesPanel,
  490. CommandIDs::showExportersPanel,
  491. CommandIDs::showExporterSettings,
  492. CommandIDs::openInIDE,
  493. CommandIDs::saveAndOpenInIDE,
  494. CommandIDs::createNewExporter,
  495. CommandIDs::deleteSelectedItem,
  496. CommandIDs::showTranslationTool,
  497. CommandIDs::addNewGUIFile });
  498. }
  499. void ProjectContentComponent::getCommandInfo (const CommandID commandID, ApplicationCommandInfo& result)
  500. {
  501. String documentName;
  502. if (currentDocument != nullptr)
  503. documentName = " '" + currentDocument->getName().substring (0, 32) + "'";
  504. #if JUCE_MAC
  505. auto cmdCtrl = (ModifierKeys::ctrlModifier | ModifierKeys::commandModifier);
  506. #else
  507. auto cmdCtrl = (ModifierKeys::ctrlModifier | ModifierKeys::altModifier);
  508. #endif
  509. switch (commandID)
  510. {
  511. case CommandIDs::saveProject:
  512. result.setInfo ("Save Project",
  513. "Saves the current project",
  514. CommandCategories::general, 0);
  515. result.setActive (project != nullptr && ! project->isSaveAndExportDisabled() && ! project->isCurrentlySaving());
  516. result.defaultKeypresses.add ({ 'p', ModifierKeys::commandModifier, 0 });
  517. break;
  518. case CommandIDs::closeProject:
  519. result.setInfo ("Close Project",
  520. "Closes the current project",
  521. CommandCategories::general, 0);
  522. result.setActive (project != nullptr);
  523. break;
  524. case CommandIDs::saveDocument:
  525. result.setInfo ("Save" + documentName,
  526. "Saves the current document",
  527. CommandCategories::general, 0);
  528. result.setActive (currentDocument != nullptr || (project != nullptr && ! project->isCurrentlySaving()));
  529. result.defaultKeypresses.add ({ 's', ModifierKeys::commandModifier, 0 });
  530. break;
  531. case CommandIDs::saveDocumentAs:
  532. result.setInfo ("Save As...",
  533. "Saves the current document to a new location",
  534. CommandCategories::general, 0);
  535. result.setActive (currentDocument != nullptr);
  536. result.defaultKeypresses.add ({ 's', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0 });
  537. break;
  538. case CommandIDs::closeDocument:
  539. result.setInfo ("Close" + documentName,
  540. "Closes the current document",
  541. CommandCategories::general, 0);
  542. result.setActive (currentDocument != nullptr);
  543. result.defaultKeypresses.add ({ 'w', cmdCtrl, 0 });
  544. break;
  545. case CommandIDs::goToPreviousDoc:
  546. result.setInfo ("Previous Document",
  547. "Go to previous document",
  548. CommandCategories::general, 0);
  549. result.setActive (recentDocumentList.canGoToPrevious());
  550. result.defaultKeypresses.add ({ KeyPress::leftKey, cmdCtrl, 0 });
  551. break;
  552. case CommandIDs::goToNextDoc:
  553. result.setInfo ("Next Document",
  554. "Go to next document",
  555. CommandCategories::general, 0);
  556. result.setActive (recentDocumentList.canGoToNext());
  557. result.defaultKeypresses.add ({ KeyPress::rightKey, cmdCtrl, 0 });
  558. break;
  559. case CommandIDs::goToCounterpart:
  560. result.setInfo ("Open Counterpart File",
  561. "Open corresponding header or cpp file",
  562. CommandCategories::general, 0);
  563. result.setActive (canGoToCounterpart());
  564. result.defaultKeypresses.add ({ KeyPress::upKey, cmdCtrl, 0 });
  565. break;
  566. case CommandIDs::showProjectSettings:
  567. result.setInfo ("Show Project Settings",
  568. "Shows the main project options page",
  569. CommandCategories::general, 0);
  570. result.setActive (project != nullptr);
  571. result.defaultKeypresses.add ({ 'x', cmdCtrl, 0 });
  572. break;
  573. case CommandIDs::showFileExplorerPanel:
  574. result.setInfo ("Show File Explorer Panel",
  575. "Shows the panel containing the tree of files for this project",
  576. CommandCategories::general, 0);
  577. result.setActive (project != nullptr);
  578. result.defaultKeypresses.add ({ 'f', cmdCtrl, 0 });
  579. break;
  580. case CommandIDs::showModulesPanel:
  581. result.setInfo ("Show Modules Panel",
  582. "Shows the panel containing the project's list of modules",
  583. CommandCategories::general, 0);
  584. result.setActive (project != nullptr);
  585. result.defaultKeypresses.add ({ 'm', cmdCtrl, 0 });
  586. break;
  587. case CommandIDs::showExportersPanel:
  588. result.setInfo ("Show Exporters Panel",
  589. "Shows the panel containing the project's list of exporters",
  590. CommandCategories::general, 0);
  591. result.setActive (project != nullptr);
  592. result.defaultKeypresses.add ({ 'e', cmdCtrl, 0 });
  593. break;
  594. case CommandIDs::showExporterSettings:
  595. result.setInfo ("Show Exporter Settings",
  596. "Shows the settings page for the currently selected exporter",
  597. CommandCategories::general, 0);
  598. result.setActive (project != nullptr);
  599. result.defaultKeypresses.add ({ 'e', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0 });
  600. break;
  601. case CommandIDs::openInIDE:
  602. result.setInfo ("Open in IDE...",
  603. "Launches the project in an external IDE",
  604. CommandCategories::general, 0);
  605. result.setActive (ProjectExporter::canProjectBeLaunched (project) && ! project->isSaveAndExportDisabled());
  606. break;
  607. case CommandIDs::saveAndOpenInIDE:
  608. result.setInfo ("Save Project and Open in IDE...",
  609. "Saves the project and launches it in an external IDE",
  610. CommandCategories::general, 0);
  611. result.setActive (ProjectExporter::canProjectBeLaunched (project) && ! project->isSaveAndExportDisabled() && ! project->isCurrentlySaving());
  612. result.defaultKeypresses.add ({ 'l', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0 });
  613. break;
  614. case CommandIDs::createNewExporter:
  615. result.setInfo ("Create New Exporter...",
  616. "Creates a new exporter for a compiler type",
  617. CommandCategories::general, 0);
  618. result.setActive (project != nullptr);
  619. break;
  620. case CommandIDs::deleteSelectedItem:
  621. result.setInfo ("Delete Selected File",
  622. String(),
  623. CommandCategories::general, 0);
  624. result.defaultKeypresses.add ({ KeyPress::deleteKey, 0, 0 });
  625. result.defaultKeypresses.add ({ KeyPress::backspaceKey, 0, 0 });
  626. break;
  627. case CommandIDs::showTranslationTool:
  628. result.setInfo ("Translation File Builder",
  629. "Shows the translation file helper tool",
  630. CommandCategories::general, 0);
  631. break;
  632. case CommandIDs::addNewGUIFile:
  633. result.setInfo ("Add new GUI Component...",
  634. "Adds a new GUI Component file to the project",
  635. CommandCategories::general,
  636. (! ProjucerApplication::getApp().isGUIEditorEnabled() ? ApplicationCommandInfo::isDisabled : 0));
  637. break;
  638. default:
  639. break;
  640. }
  641. }
  642. bool ProjectContentComponent::perform (const InvocationInfo& info)
  643. {
  644. // don't allow the project to be saved again if it's currently saving
  645. if (isSaveCommand (info.commandID) && (project != nullptr && project->isCurrentlySaving()))
  646. return false;
  647. switch (info.commandID)
  648. {
  649. case CommandIDs::saveProject:
  650. case CommandIDs::closeProject:
  651. case CommandIDs::saveDocument:
  652. case CommandIDs::saveDocumentAs:
  653. case CommandIDs::closeDocument:
  654. case CommandIDs::goToPreviousDoc:
  655. case CommandIDs::goToNextDoc:
  656. case CommandIDs::goToCounterpart:
  657. case CommandIDs::saveAndOpenInIDE:
  658. if (reinvokeCommandAfterCancellingModalComps (info))
  659. {
  660. grabKeyboardFocus(); // to force any open labels to close their text editors
  661. return true;
  662. }
  663. break;
  664. default:
  665. break;
  666. }
  667. if (isCurrentlyBlockedByAnotherModalComponent())
  668. return false;
  669. switch (info.commandID)
  670. {
  671. case CommandIDs::saveProject: saveProject(); break;
  672. case CommandIDs::closeProject: closeProject(); break;
  673. case CommandIDs::saveDocument: saveDocument(); break;
  674. case CommandIDs::saveDocumentAs: saveAs(); break;
  675. case CommandIDs::closeDocument: closeDocument(); break;
  676. case CommandIDs::goToPreviousDoc: goToPreviousFile(); break;
  677. case CommandIDs::goToNextDoc: goToNextFile(); break;
  678. case CommandIDs::goToCounterpart: goToCounterpart(); break;
  679. case CommandIDs::showProjectSettings: showProjectSettings(); break;
  680. case CommandIDs::showFileExplorerPanel: showFilesPanel(); break;
  681. case CommandIDs::showModulesPanel: showModulesPanel(); break;
  682. case CommandIDs::showExportersPanel: showExportersPanel(); break;
  683. case CommandIDs::showExporterSettings: showCurrentExporterSettings(); break;
  684. case CommandIDs::openInIDE: openInSelectedIDE (false); break;
  685. case CommandIDs::saveAndOpenInIDE: openInSelectedIDE (true); break;
  686. case CommandIDs::createNewExporter: showNewExporterMenu(); break;
  687. case CommandIDs::deleteSelectedItem: deleteSelectedTreeItems(); break;
  688. case CommandIDs::showTranslationTool: showTranslationTool(); break;
  689. case CommandIDs::addNewGUIFile: addNewGUIFile(); break;
  690. default:
  691. return false;
  692. }
  693. return true;
  694. }
  695. bool ProjectContentComponent::isSaveCommand (const CommandID id)
  696. {
  697. return (id == CommandIDs::saveProject || id == CommandIDs::saveDocument || id == CommandIDs::saveAndOpenInIDE);
  698. }
  699. void ProjectContentComponent::getSelectedProjectItemsBeingDragged (const DragAndDropTarget::SourceDetails& dragSourceDetails,
  700. OwnedArray<Project::Item>& selectedNodes)
  701. {
  702. TreeItemTypes::FileTreeItemBase::getSelectedProjectItemsBeingDragged (dragSourceDetails, selectedNodes);
  703. }
  704. void ProjectContentComponent::addNewGUIFile()
  705. {
  706. if (project != nullptr)
  707. {
  708. std::unique_ptr<NewFileWizard::Type> wizard (createGUIComponentWizard());
  709. wizard->createNewFile (*project, project->getMainGroup());
  710. }
  711. }
  712. //==============================================================================
  713. void ProjectContentComponent::showProjectPanel (const int index)
  714. {
  715. if (sidebar != nullptr)
  716. sidebar->showPanel (index);
  717. }