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.

927 lines
31KB

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