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.

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