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.

817 lines
26KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2013 - Raw Material Software 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 "../Application/jucer_MainWindow.h"
  19. #include "../Application/jucer_Application.h"
  20. #include "../Code Editor/jucer_SourceCodeEditor.h"
  21. #include "jucer_ConfigPage.h"
  22. #include "jucer_TreeViewTypes.h"
  23. #include "../Project Saving/jucer_ProjectExporter.h"
  24. #include "../Utility/jucer_TranslationTool.h"
  25. //==============================================================================
  26. class FileTreeTab : public TreePanelBase
  27. {
  28. public:
  29. FileTreeTab (Project& p)
  30. : TreePanelBase (&p, "fileTreeState")
  31. {
  32. tree.setMultiSelectEnabled (true);
  33. setRoot (new GroupTreeViewItem (p.getMainGroup()));
  34. }
  35. };
  36. //==============================================================================
  37. class ConfigTreeTab : public TreePanelBase
  38. {
  39. public:
  40. ConfigTreeTab (Project& p)
  41. : TreePanelBase (&p, "settingsTreeState")
  42. {
  43. tree.setMultiSelectEnabled (false);
  44. setRoot (createProjectConfigTreeViewRoot (p));
  45. if (tree.getNumSelectedItems() == 0)
  46. tree.getRootItem()->setSelected (true, true);
  47. #if JUCE_MAC || JUCE_WINDOWS
  48. addAndMakeVisible (&openProjectButton);
  49. openProjectButton.setCommandToTrigger (commandManager, CommandIDs::openInIDE, true);
  50. openProjectButton.setButtonText (commandManager->getNameOfCommand (CommandIDs::openInIDE));
  51. openProjectButton.setColour (TextButton::buttonColourId, Colours::white.withAlpha (0.5f));
  52. addAndMakeVisible (&saveAndOpenButton);
  53. saveAndOpenButton.setCommandToTrigger (commandManager, CommandIDs::saveAndOpenInIDE, true);
  54. saveAndOpenButton.setButtonText (commandManager->getNameOfCommand (CommandIDs::saveAndOpenInIDE));
  55. saveAndOpenButton.setColour (TextButton::buttonColourId, Colours::white.withAlpha (0.5f));
  56. #endif
  57. }
  58. void resized()
  59. {
  60. Rectangle<int> r (getAvailableBounds());
  61. r.removeFromBottom (6);
  62. if (saveAndOpenButton.isVisible())
  63. saveAndOpenButton.setBounds (r.removeFromBottom (30).reduced (16, 4));
  64. if (openProjectButton.isVisible())
  65. openProjectButton.setBounds (r.removeFromBottom (30).reduced (16, 4));
  66. tree.setBounds (r);
  67. }
  68. TextButton openProjectButton, saveAndOpenButton;
  69. };
  70. //==============================================================================
  71. class LogoComponent : public Component
  72. {
  73. public:
  74. LogoComponent() {}
  75. void paint (Graphics& g)
  76. {
  77. const Path& logo = getIcons().mainJuceLogo;
  78. const AffineTransform trans (RectanglePlacement (RectanglePlacement::centred)
  79. .getTransformToFit (logo.getBounds(),
  80. getLocalBounds().toFloat()));
  81. g.setColour (findColour (mainBackgroundColourId).contrasting (0.3f));
  82. g.fillPath (logo, trans);
  83. }
  84. };
  85. //==============================================================================
  86. ProjectContentComponent::ProjectContentComponent()
  87. : project (nullptr),
  88. currentDocument (nullptr),
  89. treeViewTabs (TabbedButtonBar::TabsAtTop)
  90. {
  91. setOpaque (true);
  92. setWantsKeyboardFocus (true);
  93. addAndMakeVisible (logo = new LogoComponent());
  94. treeSizeConstrainer.setMinimumWidth (200);
  95. treeSizeConstrainer.setMaximumWidth (500);
  96. treeViewTabs.setOutline (0);
  97. treeViewTabs.getTabbedButtonBar().setMinimumTabScaleFactor (0.3);
  98. IntrojucerApp::getApp().openDocumentManager.addListener (this);
  99. }
  100. ProjectContentComponent::~ProjectContentComponent()
  101. {
  102. IntrojucerApp::getApp().openDocumentManager.removeListener (this);
  103. logo = nullptr;
  104. setProject (nullptr);
  105. contentView = nullptr;
  106. removeChildComponent (&bubbleMessage);
  107. jassert (getNumChildComponents() <= 1);
  108. }
  109. void ProjectContentComponent::paint (Graphics& g)
  110. {
  111. IntrojucerLookAndFeel::fillWithBackgroundTexture (*this, g);
  112. }
  113. void ProjectContentComponent::paintOverChildren (Graphics& g)
  114. {
  115. if (resizerBar != nullptr)
  116. {
  117. const int shadowSize = 15;
  118. const int x = resizerBar->getX();
  119. ColourGradient cg (Colours::black.withAlpha (0.25f), (float) x, 0,
  120. Colours::transparentBlack, (float) (x - shadowSize), 0, false);
  121. cg.addColour (0.4, Colours::black.withAlpha (0.07f));
  122. cg.addColour (0.6, Colours::black.withAlpha (0.02f));
  123. g.setGradientFill (cg);
  124. g.fillRect (x - shadowSize, 0, shadowSize, getHeight());
  125. }
  126. }
  127. void ProjectContentComponent::resized()
  128. {
  129. Rectangle<int> r (getLocalBounds());
  130. if (treeViewTabs.isVisible())
  131. treeViewTabs.setBounds (r.removeFromLeft (treeViewTabs.getWidth()));
  132. if (resizerBar != nullptr)
  133. resizerBar->setBounds (r.withWidth (4));
  134. if (contentView != nullptr)
  135. contentView->setBounds (r);
  136. logo->setBounds (r.reduced (r.getWidth() / 4, r.getHeight() / 4));
  137. }
  138. void ProjectContentComponent::lookAndFeelChanged()
  139. {
  140. repaint();
  141. }
  142. void ProjectContentComponent::childBoundsChanged (Component* child)
  143. {
  144. if (child == &treeViewTabs)
  145. resized();
  146. }
  147. void ProjectContentComponent::setProject (Project* newProject)
  148. {
  149. if (project != newProject)
  150. {
  151. if (project != nullptr)
  152. project->removeChangeListener (this);
  153. contentView = nullptr;
  154. resizerBar = nullptr;
  155. deleteProjectTabs();
  156. project = newProject;
  157. rebuildProjectTabs();
  158. }
  159. }
  160. void ProjectContentComponent::rebuildProjectTabs()
  161. {
  162. deleteProjectTabs();
  163. if (project != nullptr)
  164. {
  165. addAndMakeVisible (&treeViewTabs);
  166. createProjectTabs();
  167. PropertiesFile& settings = project->getStoredProperties();
  168. const String lastTabName (settings.getValue ("lastTab"));
  169. int lastTabIndex = treeViewTabs.getTabNames().indexOf (lastTabName);
  170. if (lastTabIndex < 0 || lastTabIndex > treeViewTabs.getNumTabs())
  171. lastTabIndex = 1;
  172. treeViewTabs.setCurrentTabIndex (lastTabIndex);
  173. int lastTreeWidth = settings.getValue ("projectPanelWidth").getIntValue();
  174. if (lastTreeWidth < 150)
  175. lastTreeWidth = 240;
  176. treeViewTabs.setBounds (0, 0, lastTreeWidth, getHeight());
  177. addAndMakeVisible (resizerBar = new ResizableEdgeComponent (&treeViewTabs, &treeSizeConstrainer,
  178. ResizableEdgeComponent::rightEdge));
  179. resizerBar->setAlwaysOnTop (true);
  180. project->addChangeListener (this);
  181. updateMissingFileStatuses();
  182. }
  183. else
  184. {
  185. treeViewTabs.setVisible (false);
  186. }
  187. resized();
  188. }
  189. void ProjectContentComponent::createProjectTabs()
  190. {
  191. jassert (project != nullptr);
  192. const Colour tabColour (Colours::transparentBlack);
  193. treeViewTabs.addTab ("Files", tabColour, new FileTreeTab (*project), true);
  194. treeViewTabs.addTab ("Config", tabColour, new ConfigTreeTab (*project), true);
  195. }
  196. void ProjectContentComponent::deleteProjectTabs()
  197. {
  198. if (project != nullptr && treeViewTabs.isShowing())
  199. {
  200. PropertiesFile& settings = project->getStoredProperties();
  201. if (treeViewTabs.getWidth() > 0)
  202. settings.setValue ("projectPanelWidth", treeViewTabs.getWidth());
  203. if (treeViewTabs.getNumTabs() > 0)
  204. settings.setValue ("lastTab", treeViewTabs.getCurrentTabName());
  205. }
  206. treeViewTabs.clearTabs();
  207. }
  208. TreeView* ProjectContentComponent::getFilesTreeView() const
  209. {
  210. if (FileTreeTab* ft = dynamic_cast<FileTreeTab*> (treeViewTabs.getTabContentComponent (0)))
  211. return &(ft->tree);
  212. return nullptr;
  213. }
  214. ProjectTreeViewBase* ProjectContentComponent::getFilesTreeRoot() const
  215. {
  216. if (TreeView* tv = getFilesTreeView())
  217. return dynamic_cast <ProjectTreeViewBase*> (tv->getRootItem());
  218. return nullptr;
  219. }
  220. void ProjectContentComponent::saveTreeViewState()
  221. {
  222. for (int i = treeViewTabs.getNumTabs(); --i >= 0;)
  223. if (TreePanelBase* t = dynamic_cast<TreePanelBase*> (treeViewTabs.getTabContentComponent (i)))
  224. t->saveOpenness();
  225. }
  226. void ProjectContentComponent::saveOpenDocumentList()
  227. {
  228. if (project != nullptr)
  229. {
  230. ScopedPointer<XmlElement> xml (recentDocumentList.createXML());
  231. if (xml != nullptr)
  232. project->getStoredProperties().setValue ("lastDocs", xml);
  233. }
  234. }
  235. void ProjectContentComponent::reloadLastOpenDocuments()
  236. {
  237. if (project != nullptr)
  238. {
  239. ScopedPointer<XmlElement> xml (project->getStoredProperties().getXmlValue ("lastDocs"));
  240. if (xml != nullptr)
  241. {
  242. recentDocumentList.restoreFromXML (*project, *xml);
  243. showDocument (recentDocumentList.getCurrentDocument(), true);
  244. }
  245. }
  246. }
  247. void ProjectContentComponent::documentAboutToClose (OpenDocumentManager::Document* document)
  248. {
  249. hideDocument (document);
  250. }
  251. void ProjectContentComponent::changeListenerCallback (ChangeBroadcaster*)
  252. {
  253. updateMissingFileStatuses();
  254. }
  255. void ProjectContentComponent::updateMissingFileStatuses()
  256. {
  257. if (ProjectTreeViewBase* p = getFilesTreeRoot())
  258. p->checkFileStatus();
  259. }
  260. bool ProjectContentComponent::showEditorForFile (const File& f, bool grabFocus)
  261. {
  262. return getCurrentFile() == f
  263. || showDocument (IntrojucerApp::getApp().openDocumentManager.openFile (project, f), grabFocus);
  264. }
  265. File ProjectContentComponent::getCurrentFile() const
  266. {
  267. return currentDocument != nullptr ? currentDocument->getFile()
  268. : File::nonexistent;
  269. }
  270. bool ProjectContentComponent::showDocument (OpenDocumentManager::Document* doc, bool grabFocus)
  271. {
  272. if (doc == nullptr)
  273. return false;
  274. if (doc->hasFileBeenModifiedExternally())
  275. doc->reloadFromFile();
  276. if (doc == getCurrentDocument() && contentView != nullptr)
  277. {
  278. if (grabFocus)
  279. contentView->grabKeyboardFocus();
  280. return true;
  281. }
  282. recentDocumentList.newDocumentOpened (doc);
  283. bool opened = setEditorComponent (doc->createEditor(), doc);
  284. if (opened && grabFocus)
  285. contentView->grabKeyboardFocus();
  286. return opened;
  287. }
  288. void ProjectContentComponent::hideEditor()
  289. {
  290. currentDocument = nullptr;
  291. contentView = nullptr;
  292. updateMainWindowTitle();
  293. commandManager->commandStatusChanged();
  294. resized();
  295. }
  296. void ProjectContentComponent::hideDocument (OpenDocumentManager::Document* doc)
  297. {
  298. if (doc == currentDocument)
  299. {
  300. if (OpenDocumentManager::Document* replacement = recentDocumentList.getClosestPreviousDocOtherThan (doc))
  301. showDocument (replacement, true);
  302. else
  303. hideEditor();
  304. }
  305. }
  306. bool ProjectContentComponent::setEditorComponent (Component* editor,
  307. OpenDocumentManager::Document* doc)
  308. {
  309. if (editor != nullptr)
  310. {
  311. contentView = nullptr;
  312. contentView = editor;
  313. currentDocument = doc;
  314. addAndMakeVisible (editor);
  315. resized();
  316. updateMainWindowTitle();
  317. commandManager->commandStatusChanged();
  318. return true;
  319. }
  320. updateMainWindowTitle();
  321. return false;
  322. }
  323. void ProjectContentComponent::closeDocument()
  324. {
  325. if (currentDocument != nullptr)
  326. IntrojucerApp::getApp().openDocumentManager.closeDocument (currentDocument, true);
  327. else if (contentView != nullptr)
  328. if (! goToPreviousFile())
  329. hideEditor();
  330. }
  331. static void showSaveWarning (OpenDocumentManager::Document* currentDocument)
  332. {
  333. AlertWindow::showMessageBox (AlertWindow::WarningIcon,
  334. TRANS("Save failed!"),
  335. TRANS("Couldn't save the file:")
  336. + "\n" + currentDocument->getFile().getFullPathName());
  337. }
  338. void ProjectContentComponent::saveDocument()
  339. {
  340. if (currentDocument != nullptr)
  341. {
  342. if (! currentDocument->save())
  343. showSaveWarning (currentDocument);
  344. }
  345. else
  346. saveProject();
  347. updateMainWindowTitle();
  348. }
  349. void ProjectContentComponent::saveAs()
  350. {
  351. if (currentDocument != nullptr && ! currentDocument->saveAs())
  352. showSaveWarning (currentDocument);
  353. }
  354. bool ProjectContentComponent::goToPreviousFile()
  355. {
  356. OpenDocumentManager::Document* doc = recentDocumentList.getCurrentDocument();
  357. if (doc == nullptr || doc == getCurrentDocument())
  358. doc = recentDocumentList.getPrevious();
  359. return showDocument (doc, true);
  360. }
  361. bool ProjectContentComponent::goToNextFile()
  362. {
  363. return showDocument (recentDocumentList.getNext(), true);
  364. }
  365. bool ProjectContentComponent::canGoToCounterpart() const
  366. {
  367. return currentDocument != nullptr
  368. && currentDocument->getCounterpartFile().exists();
  369. }
  370. bool ProjectContentComponent::goToCounterpart()
  371. {
  372. if (currentDocument != nullptr)
  373. {
  374. const File file (currentDocument->getCounterpartFile());
  375. if (file.exists())
  376. return showEditorForFile (file, true);
  377. }
  378. return false;
  379. }
  380. bool ProjectContentComponent::saveProject()
  381. {
  382. return project != nullptr
  383. && project->save (true, true) == FileBasedDocument::savedOk;
  384. }
  385. void ProjectContentComponent::closeProject()
  386. {
  387. if (MainWindow* const mw = findParentComponentOfClass<MainWindow>())
  388. mw->closeCurrentProject();
  389. }
  390. StringArray ProjectContentComponent::getExportersWhichCanLaunch() const
  391. {
  392. StringArray s;
  393. for (Project::ExporterIterator exporter (*project); exporter.next();)
  394. if (exporter->canLaunchProject())
  395. s.add (exporter->getName());
  396. return s;
  397. }
  398. void ProjectContentComponent::openInIDE (const String& exporterName)
  399. {
  400. if (project != nullptr)
  401. for (Project::ExporterIterator exporter (*project); exporter.next();)
  402. if (exporter->getName() == exporterName && exporter->launchProject())
  403. break;
  404. }
  405. static void openIDEMenuCallback (int result, ProjectContentComponent* comp)
  406. {
  407. if (comp != nullptr && result > 0)
  408. comp->openInIDE (comp->getExportersWhichCanLaunch() [result - 1]);
  409. }
  410. void ProjectContentComponent::openInIDE()
  411. {
  412. if (project != nullptr)
  413. {
  414. StringArray possibleExporters = getExportersWhichCanLaunch();
  415. if (possibleExporters.size() > 1)
  416. {
  417. PopupMenu menu;
  418. for (int i = 0; i < possibleExporters.size(); ++i)
  419. menu.addItem (i + 1, possibleExporters[i]);
  420. menu.showMenuAsync (PopupMenu::Options(),
  421. ModalCallbackFunction::forComponent (openIDEMenuCallback, this));
  422. }
  423. else
  424. {
  425. openInIDE (possibleExporters[0]);
  426. }
  427. }
  428. }
  429. void ProjectContentComponent::deleteSelectedTreeItems()
  430. {
  431. if (TreePanelBase* const tree = dynamic_cast<TreePanelBase*> (treeViewTabs.getCurrentContentComponent()))
  432. tree->deleteSelectedItems();
  433. }
  434. void ProjectContentComponent::updateMainWindowTitle()
  435. {
  436. if (MainWindow* mw = findParentComponentOfClass<MainWindow>())
  437. {
  438. String title;
  439. File file;
  440. bool edited = false;
  441. if (currentDocument != nullptr)
  442. {
  443. title = currentDocument->getName();
  444. edited = currentDocument->needsSaving();
  445. file = currentDocument->getFile();
  446. }
  447. if (ComponentPeer* peer = mw->getPeer())
  448. {
  449. if (! peer->setDocumentEditedStatus (edited))
  450. if (edited)
  451. title << "*";
  452. peer->setRepresentedFile (file);
  453. }
  454. mw->updateTitle (title);
  455. }
  456. }
  457. void ProjectContentComponent::showBubbleMessage (const 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,
  478. 600, 700,
  479. 600, 400, 10000, 10000);
  480. }
  481. }
  482. //==============================================================================
  483. ApplicationCommandTarget* ProjectContentComponent::getNextCommandTarget()
  484. {
  485. return findFirstTargetParentComponent();
  486. }
  487. void ProjectContentComponent::getAllCommands (Array <CommandID>& commands)
  488. {
  489. const CommandID ids[] = { CommandIDs::saveDocument,
  490. CommandIDs::saveDocumentAs,
  491. CommandIDs::closeDocument,
  492. CommandIDs::saveProject,
  493. CommandIDs::closeProject,
  494. CommandIDs::openInIDE,
  495. CommandIDs::saveAndOpenInIDE,
  496. CommandIDs::showFilePanel,
  497. CommandIDs::showConfigPanel,
  498. CommandIDs::goToPreviousDoc,
  499. CommandIDs::goToNextDoc,
  500. CommandIDs::goToCounterpart,
  501. CommandIDs::deleteSelectedItem,
  502. CommandIDs::showTranslationTool };
  503. commands.addArray (ids, numElementsInArray (ids));
  504. }
  505. void ProjectContentComponent::getCommandInfo (const CommandID commandID, ApplicationCommandInfo& result)
  506. {
  507. String documentName;
  508. if (currentDocument != nullptr)
  509. documentName = " '" + currentDocument->getName().substring (0, 32) + "'";
  510. #if JUCE_MAC
  511. const ModifierKeys cmdCtrl (ModifierKeys::ctrlModifier | ModifierKeys::commandModifier);
  512. #else
  513. const ModifierKeys cmdCtrl (ModifierKeys::ctrlModifier | ModifierKeys::altModifier);
  514. #endif
  515. switch (commandID)
  516. {
  517. case CommandIDs::saveProject:
  518. result.setInfo ("Save Project",
  519. "Saves the current project",
  520. CommandCategories::general, 0);
  521. result.setActive (project != nullptr);
  522. break;
  523. case CommandIDs::closeProject:
  524. result.setInfo ("Close Project",
  525. "Closes the current project",
  526. CommandCategories::general, 0);
  527. result.setActive (project != nullptr);
  528. break;
  529. case CommandIDs::saveDocument:
  530. result.setInfo ("Save" + documentName,
  531. "Saves the current document",
  532. CommandCategories::general, 0);
  533. result.setActive (currentDocument != nullptr || project != nullptr);
  534. result.defaultKeypresses.add (KeyPress ('s', ModifierKeys::commandModifier, 0));
  535. break;
  536. case CommandIDs::saveDocumentAs:
  537. result.setInfo ("Save As...",
  538. "Saves the current document to a new location",
  539. CommandCategories::general, 0);
  540. result.setActive (currentDocument != nullptr || project != nullptr);
  541. result.defaultKeypresses.add (KeyPress ('s', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0));
  542. break;
  543. case CommandIDs::closeDocument:
  544. result.setInfo ("Close" + documentName,
  545. "Closes the current document",
  546. CommandCategories::general, 0);
  547. result.setActive (contentView != nullptr);
  548. result.defaultKeypresses.add (KeyPress ('w', cmdCtrl, 0));
  549. break;
  550. case CommandIDs::goToPreviousDoc:
  551. result.setInfo ("Previous Document", "Go to previous document", CommandCategories::general, 0);
  552. result.setActive (recentDocumentList.canGoToPrevious());
  553. result.defaultKeypresses.add (KeyPress (KeyPress::leftKey, cmdCtrl, 0));
  554. break;
  555. case CommandIDs::goToNextDoc:
  556. result.setInfo ("Next Document", "Go to next document", CommandCategories::general, 0);
  557. result.setActive (recentDocumentList.canGoToNext());
  558. result.defaultKeypresses.add (KeyPress (KeyPress::rightKey, cmdCtrl, 0));
  559. break;
  560. case CommandIDs::goToCounterpart:
  561. result.setInfo ("Open corresponding header or cpp file", "Open counterpart file", CommandCategories::general, 0);
  562. result.setActive (canGoToCounterpart());
  563. result.defaultKeypresses.add (KeyPress (KeyPress::upKey, cmdCtrl, 0));
  564. break;
  565. case CommandIDs::openInIDE:
  566. #if JUCE_MAC
  567. result.setInfo ("Open in Xcode...",
  568. #elif JUCE_WINDOWS
  569. result.setInfo ("Open in Visual Studio...",
  570. #else
  571. result.setInfo ("Open as a Makefile...",
  572. #endif
  573. "Launches the project in an external IDE",
  574. CommandCategories::general, 0);
  575. result.setActive (ProjectExporter::canProjectBeLaunched (project));
  576. break;
  577. case CommandIDs::saveAndOpenInIDE:
  578. #if JUCE_MAC
  579. result.setInfo ("Save Project and Open in Xcode...",
  580. #elif JUCE_WINDOWS
  581. result.setInfo ("Save Project and Open in Visual Studio...",
  582. #else
  583. result.setInfo ("Save Project and Open as a Makefile...",
  584. #endif
  585. "Saves the project and launches it in an external IDE",
  586. CommandCategories::general, 0);
  587. result.setActive (ProjectExporter::canProjectBeLaunched (project));
  588. result.defaultKeypresses.add (KeyPress ('l', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0));
  589. break;
  590. case CommandIDs::showFilePanel:
  591. result.setInfo ("Show File Panel",
  592. "Shows the tree of files for this project",
  593. CommandCategories::general, 0);
  594. result.setActive (project != nullptr);
  595. result.defaultKeypresses.add (KeyPress ('p', ModifierKeys::commandModifier, 0));
  596. break;
  597. case CommandIDs::showConfigPanel:
  598. result.setInfo ("Show Config Panel",
  599. "Shows the build options for the project",
  600. CommandCategories::general, 0);
  601. result.setActive (project != nullptr);
  602. result.defaultKeypresses.add (KeyPress ('i', ModifierKeys::commandModifier, 0));
  603. break;
  604. case CommandIDs::deleteSelectedItem:
  605. result.setInfo ("Delete Selected File", String::empty, CommandCategories::general, 0);
  606. result.defaultKeypresses.add (KeyPress (KeyPress::deleteKey, 0, 0));
  607. result.defaultKeypresses.add (KeyPress (KeyPress::backspaceKey, 0, 0));
  608. result.setActive (dynamic_cast<TreePanelBase*> (treeViewTabs.getCurrentContentComponent()) != nullptr);
  609. break;
  610. case CommandIDs::showTranslationTool:
  611. result.setInfo ("Translation File Builder", "Shows the translation file helper tool", CommandCategories::general, 0);
  612. break;
  613. default:
  614. break;
  615. }
  616. }
  617. bool ProjectContentComponent::perform (const InvocationInfo& info)
  618. {
  619. switch (info.commandID)
  620. {
  621. case CommandIDs::saveProject:
  622. case CommandIDs::closeProject:
  623. case CommandIDs::saveDocument:
  624. case CommandIDs::saveDocumentAs:
  625. case CommandIDs::closeDocument:
  626. case CommandIDs::goToPreviousDoc:
  627. case CommandIDs::goToNextDoc:
  628. case CommandIDs::goToCounterpart:
  629. case CommandIDs::saveAndOpenInIDE:
  630. if (reinvokeCommandAfterCancellingModalComps (info))
  631. {
  632. grabKeyboardFocus(); // to force any open labels to close their text editors
  633. return true;
  634. }
  635. break;
  636. default:
  637. break;
  638. }
  639. switch (info.commandID)
  640. {
  641. case CommandIDs::saveProject: saveProject(); break;
  642. case CommandIDs::closeProject: closeProject(); break;
  643. case CommandIDs::saveDocument: saveDocument(); break;
  644. case CommandIDs::saveDocumentAs: saveAs(); break;
  645. case CommandIDs::closeDocument: closeDocument(); break;
  646. case CommandIDs::goToPreviousDoc: goToPreviousFile(); break;
  647. case CommandIDs::goToNextDoc: goToNextFile(); break;
  648. case CommandIDs::goToCounterpart: goToCounterpart(); break;
  649. case CommandIDs::showFilePanel: treeViewTabs.setCurrentTabIndex (0); break;
  650. case CommandIDs::showConfigPanel: treeViewTabs.setCurrentTabIndex (1); break;
  651. case CommandIDs::openInIDE: openInIDE(); break;
  652. case CommandIDs::deleteSelectedItem: deleteSelectedTreeItems(); break;
  653. case CommandIDs::saveAndOpenInIDE:
  654. if (saveProject())
  655. openInIDE();
  656. break;
  657. case CommandIDs::showTranslationTool: showTranslationTool(); break;
  658. default:
  659. return false;
  660. }
  661. return true;
  662. }