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.

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