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.

980 lines
32KB

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