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.

915 lines
30KB

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