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.

1014 lines
33KB

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