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.

936 lines
31KB

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