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.

924 lines
30KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2015 - ROLI Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. #include "jucer_ProjectContentComponent.h"
  18. #include "jucer_Module.h"
  19. #include "../Application/jucer_MainWindow.h"
  20. #include "../Application/jucer_Application.h"
  21. #include "../Code Editor/jucer_SourceCodeEditor.h"
  22. #include "jucer_TreeItemTypes.h"
  23. //==============================================================================
  24. class FileTreePanel : public TreePanelBase
  25. {
  26. public:
  27. FileTreePanel (Project& p)
  28. : TreePanelBase (&p, "fileTreeState")
  29. {
  30. tree.setMultiSelectEnabled (true);
  31. setRoot (new FileTreeItemTypes::GroupItem (p.getMainGroup()));
  32. }
  33. void updateMissingFileStatuses()
  34. {
  35. if (FileTreeItemTypes::ProjectTreeItemBase* p = dynamic_cast<FileTreeItemTypes::ProjectTreeItemBase*> (rootItem.get()))
  36. p->checkFileStatus();
  37. }
  38. };
  39. //==============================================================================
  40. class ConfigTreePanel : public TreePanelBase
  41. {
  42. public:
  43. ConfigTreePanel (Project& p)
  44. : TreePanelBase (&p, "settingsTreeState")
  45. {
  46. tree.setMultiSelectEnabled (false);
  47. setRoot (new ConfigTreeItemTypes::RootItem (p));
  48. if (tree.getNumSelectedItems() == 0)
  49. tree.getRootItem()->setSelected (true, true);
  50. #if JUCE_MAC || JUCE_WINDOWS
  51. ApplicationCommandManager& commandManager = IntrojucerApp::getCommandManager();
  52. addAndMakeVisible (openProjectButton);
  53. openProjectButton.setCommandToTrigger (&commandManager, CommandIDs::openInIDE, true);
  54. openProjectButton.setButtonText (commandManager.getNameOfCommand (CommandIDs::openInIDE));
  55. openProjectButton.setColour (TextButton::buttonColourId, Colours::white.withAlpha (0.5f));
  56. addAndMakeVisible (saveAndOpenButton);
  57. saveAndOpenButton.setCommandToTrigger (&commandManager, CommandIDs::saveAndOpenInIDE, true);
  58. saveAndOpenButton.setButtonText (commandManager.getNameOfCommand (CommandIDs::saveAndOpenInIDE));
  59. saveAndOpenButton.setColour (TextButton::buttonColourId, Colours::white.withAlpha (0.5f));
  60. #endif
  61. }
  62. void resized()
  63. {
  64. Rectangle<int> r (getAvailableBounds());
  65. r.removeFromBottom (6);
  66. if (saveAndOpenButton.isVisible())
  67. saveAndOpenButton.setBounds (r.removeFromBottom (30).reduced (16, 4));
  68. if (openProjectButton.isVisible())
  69. openProjectButton.setBounds (r.removeFromBottom (30).reduced (16, 4));
  70. tree.setBounds (r);
  71. }
  72. void showProjectSettings()
  73. {
  74. if (ConfigTreeItemTypes::ConfigTreeItemBase* root = dynamic_cast<ConfigTreeItemTypes::ConfigTreeItemBase*> (rootItem.get()))
  75. if (root->isProjectSettings())
  76. root->setSelected (true, true);
  77. }
  78. void showModules()
  79. {
  80. if (ConfigTreeItemTypes::ConfigTreeItemBase* mods = getModulesItem())
  81. mods->setSelected (true, true);
  82. }
  83. void showModule (const String& moduleID)
  84. {
  85. if (ConfigTreeItemTypes::ConfigTreeItemBase* mods = getModulesItem())
  86. {
  87. mods->setOpen (true);
  88. for (int i = mods->getNumSubItems(); --i >= 0;)
  89. if (ConfigTreeItemTypes::ModuleItem* m = dynamic_cast<ConfigTreeItemTypes::ModuleItem*> (mods->getSubItem (i)))
  90. if (m->moduleID == moduleID)
  91. m->setSelected (true, true);
  92. }
  93. }
  94. TextButton openProjectButton, saveAndOpenButton;
  95. private:
  96. ConfigTreeItemTypes::ConfigTreeItemBase* getModulesItem()
  97. {
  98. if (ConfigTreeItemTypes::ConfigTreeItemBase* root = dynamic_cast<ConfigTreeItemTypes::ConfigTreeItemBase*> (rootItem.get()))
  99. if (root->isProjectSettings())
  100. if (ConfigTreeItemTypes::ConfigTreeItemBase* mods = dynamic_cast<ConfigTreeItemTypes::ConfigTreeItemBase*> (root->getSubItem (0)))
  101. if (mods->isModulesList())
  102. return mods;
  103. return nullptr;
  104. }
  105. };
  106. //==============================================================================
  107. struct LogoComponent : public Component
  108. {
  109. void paint (Graphics& g)
  110. {
  111. g.setColour (findColour (mainBackgroundColourId).contrasting (0.3f));
  112. Rectangle<int> r (getLocalBounds());
  113. g.setFont (15.0f);
  114. g.drawFittedText (getVersionInfo(), r.removeFromBottom (30), Justification::centred, 2);
  115. const Path& logo = getIcons().mainJuceLogo;
  116. g.fillPath (logo, RectanglePlacement (RectanglePlacement::centred)
  117. .getTransformToFit (logo.getBounds(), r.toFloat()));
  118. }
  119. static String getVersionInfo()
  120. {
  121. const Time buildDate (Time::getCompilationDate());
  122. String s;
  123. s << SystemStats::getJUCEVersion() << newLine
  124. << "Introjucer built: " << buildDate.getDayOfMonth()
  125. << " " << Time::getMonthName (buildDate.getMonth(), true)
  126. << " " << buildDate.getYear();
  127. return s;
  128. }
  129. };
  130. //==============================================================================
  131. ProjectContentComponent::ProjectContentComponent()
  132. : project (nullptr),
  133. currentDocument (nullptr),
  134. treeViewTabs (TabbedButtonBar::TabsAtTop)
  135. {
  136. setOpaque (true);
  137. setWantsKeyboardFocus (true);
  138. addAndMakeVisible (logo = new LogoComponent());
  139. treeSizeConstrainer.setMinimumWidth (200);
  140. treeSizeConstrainer.setMaximumWidth (500);
  141. treeViewTabs.setOutline (0);
  142. treeViewTabs.getTabbedButtonBar().setMinimumTabScaleFactor (0.3);
  143. IntrojucerApp::getApp().openDocumentManager.addListener (this);
  144. }
  145. ProjectContentComponent::~ProjectContentComponent()
  146. {
  147. IntrojucerApp::getApp().openDocumentManager.removeListener (this);
  148. logo = nullptr;
  149. setProject (nullptr);
  150. contentView = nullptr;
  151. removeChildComponent (&bubbleMessage);
  152. jassert (getNumChildComponents() <= 1);
  153. }
  154. void ProjectContentComponent::paint (Graphics& g)
  155. {
  156. IntrojucerLookAndFeel::fillWithBackgroundTexture (*this, g);
  157. }
  158. void ProjectContentComponent::paintOverChildren (Graphics& g)
  159. {
  160. if (resizerBar != nullptr)
  161. {
  162. const int shadowSize = 15;
  163. const int x = resizerBar->getX();
  164. ColourGradient cg (Colours::black.withAlpha (0.25f), (float) x, 0,
  165. Colours::transparentBlack, (float) (x - shadowSize), 0, false);
  166. cg.addColour (0.4, Colours::black.withAlpha (0.07f));
  167. cg.addColour (0.6, Colours::black.withAlpha (0.02f));
  168. g.setGradientFill (cg);
  169. g.fillRect (x - shadowSize, 0, shadowSize, getHeight());
  170. }
  171. }
  172. void ProjectContentComponent::resized()
  173. {
  174. Rectangle<int> r (getLocalBounds());
  175. if (treeViewTabs.isVisible())
  176. treeViewTabs.setBounds (r.removeFromLeft (treeViewTabs.getWidth()));
  177. if (resizerBar != nullptr)
  178. resizerBar->setBounds (r.withWidth (4));
  179. if (contentView != nullptr)
  180. contentView->setBounds (r);
  181. if (logo != nullptr)
  182. logo->setBounds (r.reduced (r.getWidth() / 4, r.getHeight() / 4));
  183. }
  184. void ProjectContentComponent::lookAndFeelChanged()
  185. {
  186. repaint();
  187. }
  188. void ProjectContentComponent::childBoundsChanged (Component* child)
  189. {
  190. if (child == &treeViewTabs)
  191. resized();
  192. }
  193. void ProjectContentComponent::setProject (Project* newProject)
  194. {
  195. if (project != newProject)
  196. {
  197. if (project != nullptr)
  198. project->removeChangeListener (this);
  199. contentView = nullptr;
  200. resizerBar = nullptr;
  201. deleteProjectTabs();
  202. project = newProject;
  203. rebuildProjectTabs();
  204. }
  205. }
  206. void ProjectContentComponent::rebuildProjectTabs()
  207. {
  208. deleteProjectTabs();
  209. if (project != nullptr)
  210. {
  211. addAndMakeVisible (treeViewTabs);
  212. createProjectTabs();
  213. PropertiesFile& settings = project->getStoredProperties();
  214. const String lastTabName (settings.getValue ("lastTab"));
  215. int lastTabIndex = treeViewTabs.getTabNames().indexOf (lastTabName);
  216. if (lastTabIndex < 0 || lastTabIndex > treeViewTabs.getNumTabs())
  217. lastTabIndex = 1;
  218. treeViewTabs.setCurrentTabIndex (lastTabIndex);
  219. int lastTreeWidth = settings.getValue ("projectPanelWidth").getIntValue();
  220. if (lastTreeWidth < 150)
  221. lastTreeWidth = 240;
  222. treeViewTabs.setBounds (0, 0, lastTreeWidth, getHeight());
  223. addAndMakeVisible (resizerBar = new ResizableEdgeComponent (&treeViewTabs, &treeSizeConstrainer,
  224. ResizableEdgeComponent::rightEdge));
  225. resizerBar->setAlwaysOnTop (true);
  226. project->addChangeListener (this);
  227. updateMissingFileStatuses();
  228. }
  229. else
  230. {
  231. treeViewTabs.setVisible (false);
  232. }
  233. resized();
  234. }
  235. void ProjectContentComponent::createProjectTabs()
  236. {
  237. jassert (project != nullptr);
  238. const Colour tabColour (Colours::transparentBlack);
  239. treeViewTabs.addTab ("Files", tabColour, new FileTreePanel (*project), true);
  240. treeViewTabs.addTab ("Config", tabColour, new ConfigTreePanel (*project), true);
  241. }
  242. void ProjectContentComponent::deleteProjectTabs()
  243. {
  244. if (project != nullptr && treeViewTabs.isShowing())
  245. {
  246. PropertiesFile& settings = project->getStoredProperties();
  247. if (treeViewTabs.getWidth() > 0)
  248. settings.setValue ("projectPanelWidth", treeViewTabs.getWidth());
  249. if (treeViewTabs.getNumTabs() > 0)
  250. settings.setValue ("lastTab", treeViewTabs.getCurrentTabName());
  251. }
  252. treeViewTabs.clearTabs();
  253. }
  254. void ProjectContentComponent::saveTreeViewState()
  255. {
  256. for (int i = treeViewTabs.getNumTabs(); --i >= 0;)
  257. if (TreePanelBase* t = dynamic_cast<TreePanelBase*> (treeViewTabs.getTabContentComponent (i)))
  258. t->saveOpenness();
  259. }
  260. void ProjectContentComponent::saveOpenDocumentList()
  261. {
  262. if (project != nullptr)
  263. {
  264. ScopedPointer<XmlElement> xml (recentDocumentList.createXML());
  265. if (xml != nullptr)
  266. project->getStoredProperties().setValue ("lastDocs", xml);
  267. }
  268. }
  269. void ProjectContentComponent::reloadLastOpenDocuments()
  270. {
  271. if (project != nullptr)
  272. {
  273. ScopedPointer<XmlElement> xml (project->getStoredProperties().getXmlValue ("lastDocs"));
  274. if (xml != nullptr)
  275. {
  276. recentDocumentList.restoreFromXML (*project, *xml);
  277. showDocument (recentDocumentList.getCurrentDocument(), true);
  278. }
  279. }
  280. }
  281. bool ProjectContentComponent::documentAboutToClose (OpenDocumentManager::Document* document)
  282. {
  283. hideDocument (document);
  284. return true;
  285. }
  286. void ProjectContentComponent::changeListenerCallback (ChangeBroadcaster*)
  287. {
  288. updateMissingFileStatuses();
  289. }
  290. void ProjectContentComponent::updateMissingFileStatuses()
  291. {
  292. if (FileTreePanel* tree = dynamic_cast<FileTreePanel*> (treeViewTabs.getTabContentComponent (0)))
  293. tree->updateMissingFileStatuses();
  294. }
  295. bool ProjectContentComponent::showEditorForFile (const File& f, bool grabFocus)
  296. {
  297. return getCurrentFile() == f
  298. || showDocument (IntrojucerApp::getApp().openDocumentManager.openFile (project, f), grabFocus);
  299. }
  300. File ProjectContentComponent::getCurrentFile() const
  301. {
  302. return currentDocument != nullptr ? currentDocument->getFile()
  303. : File::nonexistent;
  304. }
  305. bool ProjectContentComponent::showDocument (OpenDocumentManager::Document* doc, bool grabFocus)
  306. {
  307. if (doc == nullptr)
  308. return false;
  309. if (doc->hasFileBeenModifiedExternally())
  310. doc->reloadFromFile();
  311. if (doc == getCurrentDocument() && contentView != nullptr)
  312. {
  313. if (grabFocus)
  314. contentView->grabKeyboardFocus();
  315. return true;
  316. }
  317. recentDocumentList.newDocumentOpened (doc);
  318. bool opened = setEditorComponent (doc->createEditor(), doc);
  319. if (opened && grabFocus)
  320. contentView->grabKeyboardFocus();
  321. return opened;
  322. }
  323. void ProjectContentComponent::hideEditor()
  324. {
  325. currentDocument = nullptr;
  326. contentView = nullptr;
  327. updateMainWindowTitle();
  328. IntrojucerApp::getCommandManager().commandStatusChanged();
  329. resized();
  330. }
  331. void ProjectContentComponent::hideDocument (OpenDocumentManager::Document* doc)
  332. {
  333. if (doc == currentDocument)
  334. {
  335. if (OpenDocumentManager::Document* replacement = recentDocumentList.getClosestPreviousDocOtherThan (doc))
  336. showDocument (replacement, true);
  337. else
  338. hideEditor();
  339. }
  340. }
  341. bool ProjectContentComponent::setEditorComponent (Component* editor,
  342. OpenDocumentManager::Document* doc)
  343. {
  344. if (editor != nullptr)
  345. {
  346. contentView = nullptr;
  347. contentView = editor;
  348. currentDocument = doc;
  349. addAndMakeVisible (editor);
  350. resized();
  351. updateMainWindowTitle();
  352. IntrojucerApp::getCommandManager().commandStatusChanged();
  353. return true;
  354. }
  355. updateMainWindowTitle();
  356. return false;
  357. }
  358. void ProjectContentComponent::closeDocument()
  359. {
  360. if (currentDocument != nullptr)
  361. IntrojucerApp::getApp().openDocumentManager.closeDocument (currentDocument, true);
  362. else if (contentView != nullptr)
  363. if (! goToPreviousFile())
  364. hideEditor();
  365. }
  366. static void showSaveWarning (OpenDocumentManager::Document* currentDocument)
  367. {
  368. AlertWindow::showMessageBox (AlertWindow::WarningIcon,
  369. TRANS("Save failed!"),
  370. TRANS("Couldn't save the file:")
  371. + "\n" + currentDocument->getFile().getFullPathName());
  372. }
  373. void ProjectContentComponent::saveDocument()
  374. {
  375. if (currentDocument != nullptr)
  376. {
  377. if (! currentDocument->save())
  378. showSaveWarning (currentDocument);
  379. }
  380. else
  381. saveProject();
  382. updateMainWindowTitle();
  383. }
  384. void ProjectContentComponent::saveAs()
  385. {
  386. if (currentDocument != nullptr && ! currentDocument->saveAs())
  387. showSaveWarning (currentDocument);
  388. }
  389. bool ProjectContentComponent::goToPreviousFile()
  390. {
  391. OpenDocumentManager::Document* doc = recentDocumentList.getCurrentDocument();
  392. if (doc == nullptr || doc == getCurrentDocument())
  393. doc = recentDocumentList.getPrevious();
  394. return showDocument (doc, true);
  395. }
  396. bool ProjectContentComponent::goToNextFile()
  397. {
  398. return showDocument (recentDocumentList.getNext(), true);
  399. }
  400. bool ProjectContentComponent::canGoToCounterpart() const
  401. {
  402. return currentDocument != nullptr
  403. && currentDocument->getCounterpartFile().exists();
  404. }
  405. bool ProjectContentComponent::goToCounterpart()
  406. {
  407. if (currentDocument != nullptr)
  408. {
  409. const File file (currentDocument->getCounterpartFile());
  410. if (file.exists())
  411. return showEditorForFile (file, true);
  412. }
  413. return false;
  414. }
  415. bool ProjectContentComponent::saveProject()
  416. {
  417. return project != nullptr
  418. && project->save (true, true) == FileBasedDocument::savedOk;
  419. }
  420. void ProjectContentComponent::closeProject()
  421. {
  422. if (MainWindow* const mw = findParentComponentOfClass<MainWindow>())
  423. mw->closeCurrentProject();
  424. }
  425. void ProjectContentComponent::showFilesTab()
  426. {
  427. treeViewTabs.setCurrentTabIndex (0);
  428. }
  429. void ProjectContentComponent::showConfigTab()
  430. {
  431. treeViewTabs.setCurrentTabIndex (1);
  432. }
  433. void ProjectContentComponent::showProjectSettings()
  434. {
  435. showConfigTab();
  436. if (ConfigTreePanel* const tree = dynamic_cast<ConfigTreePanel*> (treeViewTabs.getCurrentContentComponent()))
  437. tree->showProjectSettings();
  438. }
  439. void ProjectContentComponent::showModules()
  440. {
  441. showConfigTab();
  442. if (ConfigTreePanel* const tree = dynamic_cast<ConfigTreePanel*> (treeViewTabs.getCurrentContentComponent()))
  443. tree->showModules();
  444. }
  445. void ProjectContentComponent::showModule (const String& moduleID)
  446. {
  447. showConfigTab();
  448. if (ConfigTreePanel* const tree = dynamic_cast<ConfigTreePanel*> (treeViewTabs.getCurrentContentComponent()))
  449. tree->showModule (moduleID);
  450. }
  451. StringArray ProjectContentComponent::getExportersWhichCanLaunch() const
  452. {
  453. StringArray s;
  454. if (project != nullptr)
  455. for (Project::ExporterIterator exporter (*project); exporter.next();)
  456. if (exporter->canLaunchProject())
  457. s.add (exporter->getName());
  458. return s;
  459. }
  460. void ProjectContentComponent::openInIDE (int exporterIndex, bool saveFirst)
  461. {
  462. if (saveFirst)
  463. saveProject();
  464. int i = 0;
  465. if (project != nullptr)
  466. for (Project::ExporterIterator exporter (*project); exporter.next();)
  467. if (exporter->canLaunchProject())
  468. if (i++ == exporterIndex && exporter->launchProject())
  469. break;
  470. }
  471. static void openIDEMenuCallback (int result, ProjectContentComponent* comp, bool saveFirst)
  472. {
  473. if (comp != nullptr && result > 0)
  474. comp->openInIDE (result - 1, saveFirst);
  475. }
  476. void ProjectContentComponent::openInIDE (bool saveFirst)
  477. {
  478. if (project != nullptr)
  479. {
  480. StringArray possibleExporters = getExportersWhichCanLaunch();
  481. if (possibleExporters.size() > 1)
  482. {
  483. PopupMenu menu;
  484. for (int i = 0; i < possibleExporters.size(); ++i)
  485. menu.addItem (i + 1, possibleExporters[i]);
  486. menu.showMenuAsync (PopupMenu::Options(),
  487. ModalCallbackFunction::forComponent (openIDEMenuCallback, this, saveFirst));
  488. }
  489. else
  490. {
  491. openInIDE (0, saveFirst);
  492. }
  493. }
  494. }
  495. void ProjectContentComponent::deleteSelectedTreeItems()
  496. {
  497. if (TreePanelBase* const tree = dynamic_cast<TreePanelBase*> (treeViewTabs.getCurrentContentComponent()))
  498. tree->deleteSelectedItems();
  499. }
  500. void ProjectContentComponent::updateMainWindowTitle()
  501. {
  502. if (MainWindow* mw = findParentComponentOfClass<MainWindow>())
  503. {
  504. String title;
  505. File file;
  506. bool edited = false;
  507. if (currentDocument != nullptr)
  508. {
  509. title = currentDocument->getName();
  510. edited = currentDocument->needsSaving();
  511. file = currentDocument->getFile();
  512. }
  513. if (ComponentPeer* peer = mw->getPeer())
  514. {
  515. if (! peer->setDocumentEditedStatus (edited))
  516. if (edited)
  517. title << "*";
  518. peer->setRepresentedFile (file);
  519. }
  520. mw->updateTitle (title);
  521. }
  522. }
  523. void ProjectContentComponent::showBubbleMessage (const Rectangle<int>& pos, const String& text)
  524. {
  525. addChildComponent (bubbleMessage);
  526. bubbleMessage.setColour (BubbleComponent::backgroundColourId, Colours::white.withAlpha (0.7f));
  527. bubbleMessage.setColour (BubbleComponent::outlineColourId, Colours::black.withAlpha (0.8f));
  528. bubbleMessage.setAlwaysOnTop (true);
  529. bubbleMessage.showAt (pos, AttributedString (text), 3000, true, false);
  530. }
  531. //==============================================================================
  532. void ProjectContentComponent::showTranslationTool()
  533. {
  534. if (translationTool != nullptr)
  535. {
  536. translationTool->toFront (true);
  537. }
  538. else if (project != nullptr)
  539. {
  540. new FloatingToolWindow ("Translation File Builder",
  541. "transToolWindowPos",
  542. new TranslationToolComponent(),
  543. translationTool,
  544. 600, 700,
  545. 600, 400, 10000, 10000);
  546. }
  547. }
  548. //==============================================================================
  549. ApplicationCommandTarget* ProjectContentComponent::getNextCommandTarget()
  550. {
  551. return findFirstTargetParentComponent();
  552. }
  553. void ProjectContentComponent::getAllCommands (Array <CommandID>& commands)
  554. {
  555. const CommandID ids[] = { CommandIDs::saveDocument,
  556. CommandIDs::saveDocumentAs,
  557. CommandIDs::closeDocument,
  558. CommandIDs::saveProject,
  559. CommandIDs::closeProject,
  560. CommandIDs::openInIDE,
  561. CommandIDs::saveAndOpenInIDE,
  562. CommandIDs::showFilePanel,
  563. CommandIDs::showConfigPanel,
  564. CommandIDs::showProjectSettings,
  565. CommandIDs::showProjectModules,
  566. CommandIDs::goToPreviousDoc,
  567. CommandIDs::goToNextDoc,
  568. CommandIDs::goToCounterpart,
  569. CommandIDs::deleteSelectedItem,
  570. CommandIDs::showTranslationTool };
  571. commands.addArray (ids, numElementsInArray (ids));
  572. }
  573. void ProjectContentComponent::getCommandInfo (const CommandID commandID, ApplicationCommandInfo& result)
  574. {
  575. String documentName;
  576. if (currentDocument != nullptr)
  577. documentName = " '" + currentDocument->getName().substring (0, 32) + "'";
  578. #if JUCE_MAC
  579. const ModifierKeys cmdCtrl (ModifierKeys::ctrlModifier | ModifierKeys::commandModifier);
  580. #else
  581. const ModifierKeys cmdCtrl (ModifierKeys::ctrlModifier | ModifierKeys::altModifier);
  582. #endif
  583. switch (commandID)
  584. {
  585. case CommandIDs::saveProject:
  586. result.setInfo ("Save Project",
  587. "Saves the current project",
  588. CommandCategories::general, 0);
  589. result.setActive (project != nullptr);
  590. break;
  591. case CommandIDs::closeProject:
  592. result.setInfo ("Close Project",
  593. "Closes the current project",
  594. CommandCategories::general, 0);
  595. result.setActive (project != nullptr);
  596. break;
  597. case CommandIDs::saveDocument:
  598. result.setInfo ("Save" + documentName,
  599. "Saves the current document",
  600. CommandCategories::general, 0);
  601. result.setActive (currentDocument != nullptr || project != nullptr);
  602. result.defaultKeypresses.add (KeyPress ('s', ModifierKeys::commandModifier, 0));
  603. break;
  604. case CommandIDs::saveDocumentAs:
  605. result.setInfo ("Save As...",
  606. "Saves the current document to a new location",
  607. CommandCategories::general, 0);
  608. result.setActive (currentDocument != nullptr);
  609. result.defaultKeypresses.add (KeyPress ('s', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0));
  610. break;
  611. case CommandIDs::closeDocument:
  612. result.setInfo ("Close" + documentName,
  613. "Closes the current document",
  614. CommandCategories::general, 0);
  615. result.setActive (contentView != nullptr);
  616. result.defaultKeypresses.add (KeyPress ('w', cmdCtrl, 0));
  617. break;
  618. case CommandIDs::goToPreviousDoc:
  619. result.setInfo ("Previous Document", "Go to previous document", CommandCategories::general, 0);
  620. result.setActive (recentDocumentList.canGoToPrevious());
  621. result.defaultKeypresses.add (KeyPress (KeyPress::leftKey, cmdCtrl, 0));
  622. break;
  623. case CommandIDs::goToNextDoc:
  624. result.setInfo ("Next Document", "Go to next document", CommandCategories::general, 0);
  625. result.setActive (recentDocumentList.canGoToNext());
  626. result.defaultKeypresses.add (KeyPress (KeyPress::rightKey, cmdCtrl, 0));
  627. break;
  628. case CommandIDs::goToCounterpart:
  629. result.setInfo ("Open corresponding header or cpp file", "Open counterpart file", CommandCategories::general, 0);
  630. result.setActive (canGoToCounterpart());
  631. result.defaultKeypresses.add (KeyPress (KeyPress::upKey, cmdCtrl, 0));
  632. break;
  633. case CommandIDs::openInIDE:
  634. #if JUCE_MAC
  635. result.setInfo ("Open in Xcode...",
  636. #elif JUCE_WINDOWS
  637. result.setInfo ("Open in Visual Studio...",
  638. #else
  639. result.setInfo ("Open as a Makefile...",
  640. #endif
  641. "Launches the project in an external IDE",
  642. CommandCategories::general, 0);
  643. result.setActive (ProjectExporter::canProjectBeLaunched (project));
  644. break;
  645. case CommandIDs::saveAndOpenInIDE:
  646. #if JUCE_MAC
  647. result.setInfo ("Save Project and Open in Xcode...",
  648. #elif JUCE_WINDOWS
  649. result.setInfo ("Save Project and Open in Visual Studio...",
  650. #else
  651. result.setInfo ("Save Project and Open as a Makefile...",
  652. #endif
  653. "Saves the project and launches it in an external IDE",
  654. CommandCategories::general, 0);
  655. result.setActive (ProjectExporter::canProjectBeLaunched (project));
  656. result.defaultKeypresses.add (KeyPress ('l', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0));
  657. break;
  658. case CommandIDs::showFilePanel:
  659. result.setInfo ("Show File Panel",
  660. "Shows the tree of files for this project",
  661. CommandCategories::general, 0);
  662. result.setActive (project != nullptr);
  663. result.defaultKeypresses.add (KeyPress ('p', ModifierKeys::commandModifier, 0));
  664. break;
  665. case CommandIDs::showConfigPanel:
  666. result.setInfo ("Show Config Panel",
  667. "Shows the build options for the project",
  668. CommandCategories::general, 0);
  669. result.setActive (project != nullptr);
  670. result.defaultKeypresses.add (KeyPress ('i', ModifierKeys::commandModifier, 0));
  671. break;
  672. case CommandIDs::showProjectSettings:
  673. result.setInfo ("Show Project Settings",
  674. "Shows the main project options page",
  675. CommandCategories::general, 0);
  676. result.setActive (project != nullptr);
  677. result.defaultKeypresses.add (KeyPress ('i', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0));
  678. break;
  679. case CommandIDs::showProjectModules:
  680. result.setInfo ("Show Project Modules",
  681. "Shows the project's list of modules",
  682. CommandCategories::general, 0);
  683. result.setActive (project != nullptr);
  684. result.defaultKeypresses.add (KeyPress ('m', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0));
  685. break;
  686. case CommandIDs::deleteSelectedItem:
  687. result.setInfo ("Delete Selected File", String::empty, CommandCategories::general, 0);
  688. result.defaultKeypresses.add (KeyPress (KeyPress::deleteKey, 0, 0));
  689. result.defaultKeypresses.add (KeyPress (KeyPress::backspaceKey, 0, 0));
  690. result.setActive (dynamic_cast<TreePanelBase*> (treeViewTabs.getCurrentContentComponent()) != nullptr);
  691. break;
  692. case CommandIDs::showTranslationTool:
  693. result.setInfo ("Translation File Builder", "Shows the translation file helper tool", CommandCategories::general, 0);
  694. break;
  695. default:
  696. break;
  697. }
  698. }
  699. bool ProjectContentComponent::perform (const InvocationInfo& info)
  700. {
  701. switch (info.commandID)
  702. {
  703. case CommandIDs::saveProject:
  704. case CommandIDs::closeProject:
  705. case CommandIDs::saveDocument:
  706. case CommandIDs::saveDocumentAs:
  707. case CommandIDs::closeDocument:
  708. case CommandIDs::goToPreviousDoc:
  709. case CommandIDs::goToNextDoc:
  710. case CommandIDs::goToCounterpart:
  711. case CommandIDs::saveAndOpenInIDE:
  712. if (reinvokeCommandAfterCancellingModalComps (info))
  713. {
  714. grabKeyboardFocus(); // to force any open labels to close their text editors
  715. return true;
  716. }
  717. break;
  718. default:
  719. break;
  720. }
  721. switch (info.commandID)
  722. {
  723. case CommandIDs::saveProject: saveProject(); break;
  724. case CommandIDs::closeProject: closeProject(); break;
  725. case CommandIDs::saveDocument: saveDocument(); break;
  726. case CommandIDs::saveDocumentAs: saveAs(); break;
  727. case CommandIDs::closeDocument: closeDocument(); break;
  728. case CommandIDs::goToPreviousDoc: goToPreviousFile(); break;
  729. case CommandIDs::goToNextDoc: goToNextFile(); break;
  730. case CommandIDs::goToCounterpart: goToCounterpart(); break;
  731. case CommandIDs::showFilePanel: showFilesTab(); break;
  732. case CommandIDs::showConfigPanel: showConfigTab(); break;
  733. case CommandIDs::showProjectSettings: showProjectSettings(); break;
  734. case CommandIDs::showProjectModules: showModules(); break;
  735. case CommandIDs::openInIDE: openInIDE (false); break;
  736. case CommandIDs::saveAndOpenInIDE: openInIDE (true); break;
  737. case CommandIDs::deleteSelectedItem: deleteSelectedTreeItems(); break;
  738. case CommandIDs::showTranslationTool: showTranslationTool(); break;
  739. default:
  740. return false;
  741. }
  742. return true;
  743. }
  744. void ProjectContentComponent::getSelectedProjectItemsBeingDragged (const DragAndDropTarget::SourceDetails& dragSourceDetails,
  745. OwnedArray<Project::Item>& selectedNodes)
  746. {
  747. FileTreeItemTypes::ProjectTreeItemBase::getSelectedProjectItemsBeingDragged (dragSourceDetails, selectedNodes);
  748. }