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.

1074 lines
37KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. void createGUIEditorMenu (PopupMenu&);
  20. void handleGUIEditorMenuCommand (int);
  21. void registerGUIEditorCommands();
  22. //==============================================================================
  23. struct ProjucerApplication::MainMenuModel : public MenuBarModel
  24. {
  25. MainMenuModel()
  26. {
  27. setApplicationCommandManagerToWatch (&getCommandManager());
  28. }
  29. StringArray getMenuBarNames() override
  30. {
  31. return getApp().getMenuNames();
  32. }
  33. PopupMenu getMenuForIndex (int /*topLevelMenuIndex*/, const String& menuName) override
  34. {
  35. PopupMenu menu;
  36. getApp().createMenu (menu, menuName);
  37. return menu;
  38. }
  39. void menuItemSelected (int menuItemID, int /*topLevelMenuIndex*/) override
  40. {
  41. getApp().handleMainMenuCommand (menuItemID);
  42. }
  43. };
  44. //==============================================================================
  45. ProjucerApplication::ProjucerApplication() : isRunningCommandLine (false)
  46. {
  47. }
  48. void ProjucerApplication::initialise (const String& commandLine)
  49. {
  50. if (commandLine.trimStart().startsWith ("--server"))
  51. {
  52. initialiseLogger ("Compiler_Log_");
  53. LookAndFeel::setDefaultLookAndFeel (&lookAndFeel);
  54. #if JUCE_MAC
  55. Process::setDockIconVisible (false);
  56. #endif
  57. server = createClangServer (commandLine);
  58. }
  59. else
  60. {
  61. initialiseLogger ("IDE_Log_");
  62. Logger::writeToLog (SystemStats::getOperatingSystemName());
  63. Logger::writeToLog ("CPU: " + String (SystemStats::getCpuSpeedInMegaherz())
  64. + "MHz Cores: " + String (SystemStats::getNumCpus())
  65. + " " + String (SystemStats::getMemorySizeInMegabytes()) + "MB");
  66. initialiseBasics();
  67. isRunningCommandLine = commandLine.isNotEmpty();
  68. licenseController = new LicenseController;
  69. licenseController->addLicenseStatusChangedCallback (this);
  70. if (isRunningCommandLine)
  71. {
  72. const int appReturnCode = performCommandLine (commandLine);
  73. if (appReturnCode != commandLineNotPerformed)
  74. {
  75. setApplicationReturnValue (appReturnCode);
  76. quit();
  77. return;
  78. }
  79. isRunningCommandLine = false;
  80. }
  81. if (sendCommandLineToPreexistingInstance())
  82. {
  83. DBG ("Another instance is running - quitting...");
  84. quit();
  85. return;
  86. }
  87. openDocumentManager.registerType (new ProjucerAppClasses::LiveBuildCodeEditorDocument::Type(), 2);
  88. childProcessCache = new ChildProcessCache();
  89. initCommandManager();
  90. menuModel = new MainMenuModel();
  91. settings->appearance.refreshPresetSchemeList();
  92. setColourScheme (settings->getGlobalProperties().getIntValue ("COLOUR SCHEME"), false);
  93. setEditorColourScheme (settings->getGlobalProperties().getIntValue ("EDITOR COLOUR SCHEME"), false);
  94. updateEditorColourSchemeIfNeeded();
  95. // do further initialisation in a moment when the message loop has started
  96. triggerAsyncUpdate();
  97. }
  98. }
  99. void ProjucerApplication::initialiseBasics()
  100. {
  101. LookAndFeel::setDefaultLookAndFeel (&lookAndFeel);
  102. settings = new StoredSettings();
  103. ImageCache::setCacheTimeout (30 * 1000);
  104. icons = new Icons();
  105. tooltipWindow.setMillisecondsBeforeTipAppears (1200);
  106. }
  107. bool ProjucerApplication::initialiseLogger (const char* filePrefix)
  108. {
  109. if (logger == nullptr)
  110. {
  111. #if JUCE_LINUX
  112. String folder = "~/.config/Projucer/Logs";
  113. #else
  114. String folder = "com.juce.projucer";
  115. #endif
  116. logger = FileLogger::createDateStampedLogger (folder, filePrefix, ".txt",
  117. getApplicationName() + " " + getApplicationVersion()
  118. + " --- Build date: " __DATE__);
  119. Logger::setCurrentLogger (logger);
  120. }
  121. return logger != nullptr;
  122. }
  123. void ProjucerApplication::handleAsyncUpdate()
  124. {
  125. if (licenseController != nullptr)
  126. licenseController->startWebviewIfNeeded();
  127. #if JUCE_MAC
  128. PopupMenu extraAppleMenuItems;
  129. createExtraAppleMenuItems (extraAppleMenuItems);
  130. // workaround broken "Open Recent" submenu: not passing the
  131. // submenu's title here avoids the defect in JuceMainMenuHandler::addMenuItem
  132. MenuBarModel::setMacMainMenu (menuModel, &extraAppleMenuItems); //, "Open Recent");
  133. #endif
  134. versionChecker = new LatestVersionChecker();
  135. }
  136. void ProjucerApplication::initialiseWindows (const String& commandLine)
  137. {
  138. const String commandLineWithoutNSDebug (commandLine.replace ("-NSDocumentRevisionsDebugMode YES", StringRef()));
  139. if (commandLineWithoutNSDebug.trim().isNotEmpty() && ! commandLineWithoutNSDebug.trim().startsWithChar ('-'))
  140. anotherInstanceStarted (commandLine);
  141. else
  142. mainWindowList.reopenLastProjects();
  143. mainWindowList.createWindowIfNoneAreOpen();
  144. if (licenseController->getState().applicationUsageDataState == LicenseState::ApplicationUsageData::notChosenYet)
  145. showApplicationUsageDataAgreementPopup();
  146. }
  147. void ProjucerApplication::shutdown()
  148. {
  149. if (server != nullptr)
  150. {
  151. destroyClangServer (server);
  152. Logger::writeToLog ("Server shutdown cleanly");
  153. }
  154. versionChecker.reset();
  155. utf8Window.reset();
  156. svgPathWindow.reset();
  157. aboutWindow.reset();
  158. pathsWindow.reset();
  159. editorColourSchemeWindow.reset();
  160. if (licenseController != nullptr)
  161. {
  162. licenseController->removeLicenseStatusChangedCallback (this);
  163. licenseController.reset();
  164. }
  165. mainWindowList.forceCloseAllWindows();
  166. openDocumentManager.clear();
  167. childProcessCache.reset();
  168. #if JUCE_MAC
  169. MenuBarModel::setMacMainMenu (nullptr);
  170. #endif
  171. menuModel.reset();
  172. commandManager.reset();
  173. settings.reset();
  174. LookAndFeel::setDefaultLookAndFeel (nullptr);
  175. if (! isRunningCommandLine)
  176. Logger::writeToLog ("Shutdown");
  177. deleteLogger();
  178. }
  179. struct AsyncQuitRetrier : private Timer
  180. {
  181. AsyncQuitRetrier() { startTimer (500); }
  182. void timerCallback() override
  183. {
  184. stopTimer();
  185. delete this;
  186. if (auto* app = JUCEApplicationBase::getInstance())
  187. app->systemRequestedQuit();
  188. }
  189. JUCE_DECLARE_NON_COPYABLE (AsyncQuitRetrier)
  190. };
  191. void ProjucerApplication::systemRequestedQuit()
  192. {
  193. if (server != nullptr)
  194. {
  195. sendQuitMessageToIDE (server);
  196. }
  197. else if (ModalComponentManager::getInstance()->cancelAllModalComponents())
  198. {
  199. new AsyncQuitRetrier();
  200. }
  201. else
  202. {
  203. if (closeAllMainWindows())
  204. quit();
  205. }
  206. }
  207. //==============================================================================
  208. void ProjucerApplication::licenseStateChanged (const LicenseState& state)
  209. {
  210. #if ! JUCER_ENABLE_GPL_MODE
  211. if (state.type != LicenseState::Type::notLoggedIn
  212. && state.type != LicenseState::Type::noLicenseChosenYet)
  213. #else
  214. ignoreUnused (state);
  215. #endif
  216. {
  217. initialiseWindows (getCommandLineParameters());
  218. }
  219. }
  220. void ProjucerApplication::doLogout()
  221. {
  222. if (licenseController != nullptr)
  223. {
  224. const LicenseState& state = licenseController->getState();
  225. if (state.type != LicenseState::Type::notLoggedIn && closeAllMainWindows())
  226. licenseController->logout();
  227. }
  228. }
  229. //==============================================================================
  230. String ProjucerApplication::getVersionDescription() const
  231. {
  232. String s;
  233. const Time buildDate (Time::getCompilationDate());
  234. s << "Projucer " << ProjectInfo::versionString
  235. << newLine
  236. << "Build date: " << buildDate.getDayOfMonth()
  237. << " " << Time::getMonthName (buildDate.getMonth(), true)
  238. << " " << buildDate.getYear();
  239. return s;
  240. }
  241. void ProjucerApplication::anotherInstanceStarted (const String& commandLine)
  242. {
  243. if (server == nullptr && ! commandLine.trim().startsWithChar ('-'))
  244. openFile (File (commandLine.unquoted()));
  245. }
  246. ProjucerApplication& ProjucerApplication::getApp()
  247. {
  248. ProjucerApplication* const app = dynamic_cast<ProjucerApplication*> (JUCEApplication::getInstance());
  249. jassert (app != nullptr);
  250. return *app;
  251. }
  252. ApplicationCommandManager& ProjucerApplication::getCommandManager()
  253. {
  254. ApplicationCommandManager* cm = ProjucerApplication::getApp().commandManager;
  255. jassert (cm != nullptr);
  256. return *cm;
  257. }
  258. //==============================================================================
  259. enum
  260. {
  261. recentProjectsBaseID = 100,
  262. openWindowsBaseID = 300,
  263. activeDocumentsBaseID = 400,
  264. colourSchemeBaseID = 1000,
  265. codeEditorColourSchemeBaseID = 2000,
  266. };
  267. MenuBarModel* ProjucerApplication::getMenuModel()
  268. {
  269. return menuModel.get();
  270. }
  271. StringArray ProjucerApplication::getMenuNames()
  272. {
  273. return { "File", "Edit", "View", "Build", "Window", "Document", "GUI Editor", "Tools", "Help" };
  274. }
  275. void ProjucerApplication::createMenu (PopupMenu& menu, const String& menuName)
  276. {
  277. if (menuName == "File") createFileMenu (menu);
  278. else if (menuName == "Edit") createEditMenu (menu);
  279. else if (menuName == "View") createViewMenu (menu);
  280. else if (menuName == "Build") createBuildMenu (menu);
  281. else if (menuName == "Window") createWindowMenu (menu);
  282. else if (menuName == "Document") createDocumentMenu (menu);
  283. else if (menuName == "Tools") createToolsMenu (menu);
  284. else if (menuName == "Help") createHelpMenu (menu);
  285. else if (menuName == "GUI Editor") createGUIEditorMenu (menu);
  286. else jassertfalse; // names have changed?
  287. }
  288. void ProjucerApplication::createFileMenu (PopupMenu& menu)
  289. {
  290. menu.addCommandItem (commandManager, CommandIDs::newProject);
  291. menu.addSeparator();
  292. menu.addCommandItem (commandManager, CommandIDs::open);
  293. {
  294. PopupMenu recentFiles;
  295. settings->recentFiles.createPopupMenuItems (recentFiles, recentProjectsBaseID, true, true);
  296. if (recentFiles.getNumItems() > 0)
  297. {
  298. recentFiles.addSeparator();
  299. recentFiles.addCommandItem (commandManager, CommandIDs::clearRecentFiles);
  300. }
  301. menu.addSubMenu ("Open Recent", recentFiles);
  302. }
  303. menu.addSeparator();
  304. menu.addCommandItem (commandManager, CommandIDs::closeDocument);
  305. menu.addCommandItem (commandManager, CommandIDs::saveDocument);
  306. menu.addCommandItem (commandManager, CommandIDs::saveDocumentAs);
  307. menu.addCommandItem (commandManager, CommandIDs::saveAll);
  308. menu.addSeparator();
  309. menu.addCommandItem (commandManager, CommandIDs::closeProject);
  310. menu.addCommandItem (commandManager, CommandIDs::saveProject);
  311. menu.addSeparator();
  312. menu.addCommandItem (commandManager, CommandIDs::openInIDE);
  313. menu.addCommandItem (commandManager, CommandIDs::saveAndOpenInIDE);
  314. menu.addSeparator();
  315. #if ! JUCER_ENABLE_GPL_MODE
  316. menu.addCommandItem (commandManager, CommandIDs::loginLogout);
  317. #endif
  318. #if ! JUCE_MAC
  319. menu.addCommandItem (commandManager, CommandIDs::showAboutWindow);
  320. menu.addCommandItem (commandManager, CommandIDs::showAppUsageWindow);
  321. menu.addCommandItem (commandManager, CommandIDs::showGlobalPathsWindow);
  322. menu.addSeparator();
  323. menu.addCommandItem (commandManager, StandardApplicationCommandIDs::quit);
  324. #endif
  325. }
  326. void ProjucerApplication::createEditMenu (PopupMenu& menu)
  327. {
  328. menu.addCommandItem (commandManager, StandardApplicationCommandIDs::undo);
  329. menu.addCommandItem (commandManager, StandardApplicationCommandIDs::redo);
  330. menu.addSeparator();
  331. menu.addCommandItem (commandManager, StandardApplicationCommandIDs::cut);
  332. menu.addCommandItem (commandManager, StandardApplicationCommandIDs::copy);
  333. menu.addCommandItem (commandManager, StandardApplicationCommandIDs::paste);
  334. menu.addCommandItem (commandManager, StandardApplicationCommandIDs::del);
  335. menu.addCommandItem (commandManager, StandardApplicationCommandIDs::selectAll);
  336. menu.addCommandItem (commandManager, StandardApplicationCommandIDs::deselectAll);
  337. menu.addSeparator();
  338. menu.addCommandItem (commandManager, CommandIDs::showFindPanel);
  339. menu.addCommandItem (commandManager, CommandIDs::findSelection);
  340. menu.addCommandItem (commandManager, CommandIDs::findNext);
  341. menu.addCommandItem (commandManager, CommandIDs::findPrevious);
  342. }
  343. void ProjucerApplication::createViewMenu (PopupMenu& menu)
  344. {
  345. menu.addCommandItem (commandManager, CommandIDs::showProjectSettings);
  346. menu.addCommandItem (commandManager, CommandIDs::showProjectTab);
  347. menu.addCommandItem (commandManager, CommandIDs::showBuildTab);
  348. menu.addCommandItem (commandManager, CommandIDs::showFileExplorerPanel);
  349. menu.addCommandItem (commandManager, CommandIDs::showModulesPanel);
  350. menu.addCommandItem (commandManager, CommandIDs::showExportersPanel);
  351. menu.addCommandItem (commandManager, CommandIDs::showExporterSettings);
  352. menu.addSeparator();
  353. createColourSchemeItems (menu);
  354. }
  355. void ProjucerApplication::createBuildMenu (PopupMenu& menu)
  356. {
  357. menu.addCommandItem (commandManager, CommandIDs::toggleBuildEnabled);
  358. menu.addCommandItem (commandManager, CommandIDs::buildNow);
  359. menu.addCommandItem (commandManager, CommandIDs::toggleContinuousBuild);
  360. menu.addSeparator();
  361. menu.addCommandItem (commandManager, CommandIDs::launchApp);
  362. menu.addCommandItem (commandManager, CommandIDs::killApp);
  363. menu.addCommandItem (commandManager, CommandIDs::cleanAll);
  364. menu.addSeparator();
  365. menu.addCommandItem (commandManager, CommandIDs::reinstantiateComp);
  366. menu.addCommandItem (commandManager, CommandIDs::showWarnings);
  367. menu.addSeparator();
  368. menu.addCommandItem (commandManager, CommandIDs::nextError);
  369. menu.addCommandItem (commandManager, CommandIDs::prevError);
  370. }
  371. void ProjucerApplication::createColourSchemeItems (PopupMenu& menu)
  372. {
  373. PopupMenu colourSchemes;
  374. colourSchemes.addItem (colourSchemeBaseID + 0, "Dark", true, selectedColourSchemeIndex == 0);
  375. colourSchemes.addItem (colourSchemeBaseID + 1, "Grey", true, selectedColourSchemeIndex == 1);
  376. colourSchemes.addItem (colourSchemeBaseID + 2, "Light", true, selectedColourSchemeIndex == 2);
  377. menu.addSubMenu ("Colour Scheme", colourSchemes);
  378. //==========================================================================
  379. PopupMenu editorColourSchemes;
  380. auto& appearanceSettings = getAppSettings().appearance;
  381. appearanceSettings.refreshPresetSchemeList();
  382. auto schemes = appearanceSettings.getPresetSchemes();
  383. auto i = 0;
  384. for (auto s : schemes)
  385. {
  386. editorColourSchemes.addItem (codeEditorColourSchemeBaseID + i, s,
  387. editorColourSchemeWindow == nullptr,
  388. selectedEditorColourSchemeIndex == i);
  389. ++i;
  390. }
  391. numEditorColourSchemes = i;
  392. editorColourSchemes.addSeparator();
  393. editorColourSchemes.addItem (codeEditorColourSchemeBaseID + numEditorColourSchemes,
  394. "Create...", editorColourSchemeWindow == nullptr);
  395. menu.addSubMenu ("Editor Colour Scheme", editorColourSchemes);
  396. }
  397. void ProjucerApplication::createWindowMenu (PopupMenu& menu)
  398. {
  399. menu.addCommandItem (commandManager, CommandIDs::goToPreviousWindow);
  400. menu.addCommandItem (commandManager, CommandIDs::goToNextWindow);
  401. menu.addCommandItem (commandManager, CommandIDs::closeWindow);
  402. menu.addSeparator();
  403. int counter = 0;
  404. for (auto* window : mainWindowList.windows)
  405. {
  406. if (window != nullptr)
  407. {
  408. if (auto* project = window->getProject())
  409. menu.addItem (openWindowsBaseID + counter++, project->getProjectNameString());
  410. }
  411. }
  412. menu.addSeparator();
  413. menu.addCommandItem (commandManager, CommandIDs::closeAllWindows);
  414. }
  415. void ProjucerApplication::createDocumentMenu (PopupMenu& menu)
  416. {
  417. menu.addCommandItem (commandManager, CommandIDs::goToPreviousDoc);
  418. menu.addCommandItem (commandManager, CommandIDs::goToNextDoc);
  419. menu.addCommandItem (commandManager, CommandIDs::goToCounterpart);
  420. menu.addSeparator();
  421. auto numDocs = jmin (50, openDocumentManager.getNumOpenDocuments());
  422. for (int i = 0; i < numDocs; ++i)
  423. {
  424. OpenDocumentManager::Document* doc = openDocumentManager.getOpenDocument(i);
  425. menu.addItem (activeDocumentsBaseID + i, doc->getName());
  426. }
  427. menu.addSeparator();
  428. menu.addCommandItem (commandManager, CommandIDs::closeAllDocuments);
  429. }
  430. void ProjucerApplication::createToolsMenu (PopupMenu& menu)
  431. {
  432. menu.addCommandItem (commandManager, CommandIDs::showUTF8Tool);
  433. menu.addCommandItem (commandManager, CommandIDs::showSVGPathTool);
  434. menu.addCommandItem (commandManager, CommandIDs::showTranslationTool);
  435. }
  436. void ProjucerApplication::createHelpMenu (PopupMenu& menu)
  437. {
  438. menu.addCommandItem (commandManager, CommandIDs::showForum);
  439. menu.addSeparator();
  440. menu.addCommandItem (commandManager, CommandIDs::showAPIModules);
  441. menu.addCommandItem (commandManager, CommandIDs::showAPIClasses);
  442. menu.addCommandItem (commandManager, CommandIDs::showTutorials);
  443. }
  444. void ProjucerApplication::createExtraAppleMenuItems (PopupMenu& menu)
  445. {
  446. menu.addCommandItem (commandManager, CommandIDs::showAboutWindow);
  447. menu.addCommandItem (commandManager, CommandIDs::showAppUsageWindow);
  448. menu.addSeparator();
  449. menu.addCommandItem (commandManager, CommandIDs::showGlobalPathsWindow);
  450. }
  451. void ProjucerApplication::handleMainMenuCommand (int menuItemID)
  452. {
  453. if (menuItemID >= recentProjectsBaseID && menuItemID < (recentProjectsBaseID + 100))
  454. {
  455. // open a file from the "recent files" menu
  456. openFile (settings->recentFiles.getFile (menuItemID - recentProjectsBaseID));
  457. }
  458. else if (menuItemID >= openWindowsBaseID && menuItemID < (openWindowsBaseID + 100))
  459. {
  460. if (auto* window = mainWindowList.windows.getUnchecked (menuItemID - openWindowsBaseID))
  461. window->toFront (true);
  462. }
  463. else if (menuItemID >= activeDocumentsBaseID && menuItemID < (activeDocumentsBaseID + 200))
  464. {
  465. if (auto* doc = openDocumentManager.getOpenDocument (menuItemID - activeDocumentsBaseID))
  466. mainWindowList.openDocument (doc, true);
  467. else
  468. jassertfalse;
  469. }
  470. else if (menuItemID >= colourSchemeBaseID && menuItemID < (colourSchemeBaseID + 3))
  471. {
  472. setColourScheme (menuItemID - colourSchemeBaseID, true);
  473. updateEditorColourSchemeIfNeeded();
  474. }
  475. else if (menuItemID >= codeEditorColourSchemeBaseID && menuItemID < (codeEditorColourSchemeBaseID + numEditorColourSchemes))
  476. {
  477. setEditorColourScheme (menuItemID - codeEditorColourSchemeBaseID, true);
  478. }
  479. else if (menuItemID == (codeEditorColourSchemeBaseID + numEditorColourSchemes))
  480. {
  481. showEditorColourSchemeWindow();
  482. }
  483. else
  484. {
  485. handleGUIEditorMenuCommand (menuItemID);
  486. }
  487. }
  488. //==============================================================================
  489. void ProjucerApplication::getAllCommands (Array <CommandID>& commands)
  490. {
  491. JUCEApplication::getAllCommands (commands);
  492. const CommandID ids[] = { CommandIDs::newProject,
  493. CommandIDs::open,
  494. CommandIDs::closeAllWindows,
  495. CommandIDs::closeAllDocuments,
  496. CommandIDs::clearRecentFiles,
  497. CommandIDs::saveAll,
  498. CommandIDs::showGlobalPathsWindow,
  499. CommandIDs::showUTF8Tool,
  500. CommandIDs::showSVGPathTool,
  501. CommandIDs::showAboutWindow,
  502. CommandIDs::showAppUsageWindow,
  503. CommandIDs::showForum,
  504. CommandIDs::showAPIModules,
  505. CommandIDs::showAPIClasses,
  506. CommandIDs::showTutorials,
  507. CommandIDs::loginLogout };
  508. commands.addArray (ids, numElementsInArray (ids));
  509. }
  510. void ProjucerApplication::getCommandInfo (CommandID commandID, ApplicationCommandInfo& result)
  511. {
  512. switch (commandID)
  513. {
  514. case CommandIDs::newProject:
  515. result.setInfo ("New Project...", "Creates a new Jucer project", CommandCategories::general, 0);
  516. result.defaultKeypresses.add (KeyPress ('n', ModifierKeys::commandModifier, 0));
  517. break;
  518. case CommandIDs::open:
  519. result.setInfo ("Open...", "Opens a Jucer project", CommandCategories::general, 0);
  520. result.defaultKeypresses.add (KeyPress ('o', ModifierKeys::commandModifier, 0));
  521. break;
  522. case CommandIDs::showGlobalPathsWindow:
  523. result.setInfo ("Global Search Paths...",
  524. "Shows the window to change the global search paths.",
  525. CommandCategories::general, 0);
  526. break;
  527. case CommandIDs::closeAllWindows:
  528. result.setInfo ("Close All Windows", "Closes all open windows", CommandCategories::general, 0);
  529. result.setActive (mainWindowList.windows.size() > 0);
  530. break;
  531. case CommandIDs::closeAllDocuments:
  532. result.setInfo ("Close All Documents", "Closes all open documents", CommandCategories::general, 0);
  533. result.setActive (openDocumentManager.getNumOpenDocuments() > 0);
  534. break;
  535. case CommandIDs::clearRecentFiles:
  536. result.setInfo ("Clear Recent Files", "Clears all recent files from the menu", CommandCategories::general, 0);
  537. result.setActive (settings->recentFiles.getNumFiles() > 0);
  538. break;
  539. case CommandIDs::saveAll:
  540. result.setInfo ("Save All", "Saves all open documents", CommandCategories::general, 0);
  541. result.defaultKeypresses.add (KeyPress ('s', ModifierKeys::commandModifier | ModifierKeys::altModifier, 0));
  542. break;
  543. case CommandIDs::showUTF8Tool:
  544. result.setInfo ("UTF-8 String-Literal Helper", "Shows the UTF-8 string literal utility", CommandCategories::general, 0);
  545. break;
  546. case CommandIDs::showSVGPathTool:
  547. result.setInfo ("SVG Path Converter", "Shows the SVG->Path data conversion utility", CommandCategories::general, 0);
  548. break;
  549. case CommandIDs::showAboutWindow:
  550. result.setInfo ("About Projucer", "Shows the Projucer's 'About' page.", CommandCategories::general, 0);
  551. break;
  552. case CommandIDs::showAppUsageWindow:
  553. result.setInfo ("Application Usage Data", "Shows the application usage data agreement window", CommandCategories::general, 0);
  554. break;
  555. case CommandIDs::showForum:
  556. result.setInfo ("JUCE Community Forum", "Shows the JUCE community forum in a browser", CommandCategories::general, 0);
  557. break;
  558. case CommandIDs::showAPIModules:
  559. result.setInfo ("API Modules", "Shows the API modules documentation in a browser", CommandCategories::general, 0);
  560. break;
  561. case CommandIDs::showAPIClasses:
  562. result.setInfo ("API Classes", "Shows the API classes documentation in a browser", CommandCategories::general, 0);
  563. break;
  564. case CommandIDs::showTutorials:
  565. result.setInfo ("JUCE Tutorials", "Shows the JUCE tutorials in a browser", CommandCategories::general, 0);
  566. break;
  567. case CommandIDs::loginLogout:
  568. {
  569. bool isLoggedIn = false;
  570. String username;
  571. if (licenseController != nullptr)
  572. {
  573. const LicenseState state = licenseController->getState();
  574. isLoggedIn = (state.type != LicenseState::Type::notLoggedIn && state.type != LicenseState::Type::GPL);
  575. username = state.username;
  576. }
  577. result.setInfo (isLoggedIn
  578. ? String ("Sign out ") + username + "..."
  579. : String ("Sign in..."),
  580. "Log out of your JUCE account", CommandCategories::general, 0);
  581. }
  582. break;
  583. default:
  584. JUCEApplication::getCommandInfo (commandID, result);
  585. break;
  586. }
  587. }
  588. bool ProjucerApplication::perform (const InvocationInfo& info)
  589. {
  590. switch (info.commandID)
  591. {
  592. case CommandIDs::newProject: createNewProject(); break;
  593. case CommandIDs::open: askUserToOpenFile(); break;
  594. case CommandIDs::saveAll: openDocumentManager.saveAll(); break;
  595. case CommandIDs::closeAllWindows: closeAllMainWindowsAndQuitIfNeeded(); break;
  596. case CommandIDs::closeAllDocuments: closeAllDocuments (true); break;
  597. case CommandIDs::clearRecentFiles: clearRecentFiles(); break;
  598. case CommandIDs::showUTF8Tool: showUTF8ToolWindow(); break;
  599. case CommandIDs::showSVGPathTool: showSVGPathDataToolWindow(); break;
  600. case CommandIDs::showGlobalPathsWindow: showPathsWindow(); break;
  601. case CommandIDs::showAboutWindow: showAboutWindow(); break;
  602. case CommandIDs::showAppUsageWindow: showApplicationUsageDataAgreementPopup(); break;
  603. case CommandIDs::showForum: launchForumBrowser(); break;
  604. case CommandIDs::showAPIModules: launchModulesBrowser(); break;
  605. case CommandIDs::showAPIClasses: launchClassesBrowser(); break;
  606. case CommandIDs::showTutorials: launchTutorialsBrowser(); break;
  607. case CommandIDs::loginLogout: doLogout(); break;
  608. default: return JUCEApplication::perform (info);
  609. }
  610. return true;
  611. }
  612. //==============================================================================
  613. void ProjucerApplication::createNewProject()
  614. {
  615. MainWindow* mw = mainWindowList.getOrCreateEmptyWindow();
  616. mw->showNewProjectWizard();
  617. mainWindowList.avoidSuperimposedWindows (mw);
  618. }
  619. void ProjucerApplication::updateNewlyOpenedProject (Project& p)
  620. {
  621. LiveBuildProjectSettings::updateNewlyOpenedProject (p);
  622. }
  623. void ProjucerApplication::askUserToOpenFile()
  624. {
  625. FileChooser fc ("Open File");
  626. if (fc.browseForFileToOpen())
  627. openFile (fc.getResult());
  628. }
  629. bool ProjucerApplication::openFile (const File& file)
  630. {
  631. return mainWindowList.openFile (file);
  632. }
  633. bool ProjucerApplication::closeAllDocuments (bool askUserToSave)
  634. {
  635. return openDocumentManager.closeAll (askUserToSave);
  636. }
  637. bool ProjucerApplication::closeAllMainWindows()
  638. {
  639. return server != nullptr || mainWindowList.askAllWindowsToClose();
  640. }
  641. void ProjucerApplication::closeAllMainWindowsAndQuitIfNeeded()
  642. {
  643. if (closeAllMainWindows())
  644. {
  645. #if ! JUCE_MAC
  646. if (mainWindowList.windows.size() == 0)
  647. systemRequestedQuit();
  648. #endif
  649. }
  650. }
  651. void ProjucerApplication::clearRecentFiles()
  652. {
  653. settings->recentFiles.clear();
  654. settings->recentFiles.clearRecentFilesNatively();
  655. settings->flush();
  656. menuModel->menuItemsChanged();
  657. }
  658. //==============================================================================
  659. void ProjucerApplication::showUTF8ToolWindow()
  660. {
  661. if (utf8Window != nullptr)
  662. utf8Window->toFront (true);
  663. else
  664. new FloatingToolWindow ("UTF-8 String Literal Converter",
  665. "utf8WindowPos",
  666. new UTF8Component(), utf8Window, true,
  667. 500, 500, 300, 300, 1000, 1000);
  668. }
  669. void ProjucerApplication::showSVGPathDataToolWindow()
  670. {
  671. if (svgPathWindow != nullptr)
  672. svgPathWindow->toFront (true);
  673. else
  674. new FloatingToolWindow ("SVG Path Converter",
  675. "svgPathWindowPos",
  676. new SVGPathDataComponent(), svgPathWindow, true,
  677. 500, 500, 300, 300, 1000, 1000);
  678. }
  679. void ProjucerApplication::showAboutWindow()
  680. {
  681. if (aboutWindow != nullptr)
  682. aboutWindow->toFront (true);
  683. else
  684. new FloatingToolWindow ({}, {}, new AboutWindowComponent(),
  685. aboutWindow, false,
  686. 500, 300, 500, 300, 500, 300);
  687. }
  688. void ProjucerApplication::showApplicationUsageDataAgreementPopup()
  689. {
  690. if (applicationUsageDataWindow != nullptr)
  691. applicationUsageDataWindow->toFront (true);
  692. else
  693. new FloatingToolWindow ("Application Usage Analytics",
  694. {}, new ApplicationUsageDataWindowComponent (isPaidOrGPL()),
  695. applicationUsageDataWindow, false,
  696. 400, 300, 400, 300, 400, 300);
  697. }
  698. void ProjucerApplication::dismissApplicationUsageDataAgreementPopup()
  699. {
  700. if (applicationUsageDataWindow != nullptr)
  701. applicationUsageDataWindow.reset();
  702. }
  703. void ProjucerApplication::showPathsWindow()
  704. {
  705. if (pathsWindow != nullptr)
  706. pathsWindow->toFront (true);
  707. else
  708. new FloatingToolWindow ("Global Search Paths",
  709. "pathsWindowPos",
  710. new GlobalSearchPathsWindowComponent(), pathsWindow, false,
  711. 600, 500, 600, 500, 600, 500);
  712. }
  713. void ProjucerApplication::showEditorColourSchemeWindow()
  714. {
  715. if (editorColourSchemeWindow != nullptr)
  716. editorColourSchemeWindow->toFront (true);
  717. else
  718. {
  719. new FloatingToolWindow ("Editor Colour Scheme",
  720. "editorColourSchemeWindowPos",
  721. new EditorColourSchemeWindowComponent(),
  722. editorColourSchemeWindow,
  723. false,
  724. 500, 500, 500, 500, 500, 500);
  725. }
  726. }
  727. void ProjucerApplication::launchForumBrowser()
  728. {
  729. URL forumLink ("https://forum.juce.com/");
  730. if (forumLink.isWellFormed())
  731. forumLink.launchInDefaultBrowser();
  732. }
  733. void ProjucerApplication::launchModulesBrowser()
  734. {
  735. URL modulesLink ("https://juce.com/doc/modules");
  736. if (modulesLink.isWellFormed())
  737. modulesLink.launchInDefaultBrowser();
  738. }
  739. void ProjucerApplication::launchClassesBrowser()
  740. {
  741. URL classesLink ("https://juce.com/doc/classes");
  742. if (classesLink.isWellFormed())
  743. classesLink.launchInDefaultBrowser();
  744. }
  745. void ProjucerApplication::launchTutorialsBrowser()
  746. {
  747. URL tutorialsLink ("https://juce.com/tutorials");
  748. if (tutorialsLink.isWellFormed())
  749. tutorialsLink.launchInDefaultBrowser();
  750. }
  751. //==============================================================================
  752. struct FileWithTime
  753. {
  754. FileWithTime (const File& f) : file (f), time (f.getLastModificationTime()) {}
  755. FileWithTime() {}
  756. bool operator< (const FileWithTime& other) const { return time < other.time; }
  757. bool operator== (const FileWithTime& other) const { return time == other.time; }
  758. File file;
  759. Time time;
  760. };
  761. void ProjucerApplication::deleteLogger()
  762. {
  763. const int maxNumLogFilesToKeep = 50;
  764. Logger::setCurrentLogger (nullptr);
  765. if (logger != nullptr)
  766. {
  767. auto logFiles = logger->getLogFile().getParentDirectory().findChildFiles (File::findFiles, false);
  768. if (logFiles.size() > maxNumLogFilesToKeep)
  769. {
  770. Array<FileWithTime> files;
  771. for (auto& f : logFiles)
  772. files.addUsingDefaultSort (f);
  773. for (int i = 0; i < files.size() - maxNumLogFilesToKeep; ++i)
  774. files.getReference(i).file.deleteFile();
  775. }
  776. }
  777. logger.reset();
  778. }
  779. PropertiesFile::Options ProjucerApplication::getPropertyFileOptionsFor (const String& filename, bool isProjectSettings)
  780. {
  781. PropertiesFile::Options options;
  782. options.applicationName = filename;
  783. options.filenameSuffix = "settings";
  784. options.osxLibrarySubFolder = "Application Support";
  785. #if JUCE_LINUX
  786. options.folderName = "~/.config/Projucer";
  787. #else
  788. options.folderName = "Projucer";
  789. #endif
  790. if (isProjectSettings)
  791. options.folderName += "/ProjectSettings";
  792. return options;
  793. }
  794. void ProjucerApplication::updateAllBuildTabs()
  795. {
  796. for (int i = 0; i < mainWindowList.windows.size(); ++i)
  797. if (ProjectContentComponent* p = mainWindowList.windows.getUnchecked(i)->getProjectContentComponent())
  798. p->rebuildProjectTabs();
  799. }
  800. void ProjucerApplication::initCommandManager()
  801. {
  802. commandManager = new ApplicationCommandManager();
  803. commandManager->registerAllCommandsForTarget (this);
  804. {
  805. CodeDocument doc;
  806. CppCodeEditorComponent ed (File(), doc);
  807. commandManager->registerAllCommandsForTarget (&ed);
  808. }
  809. registerGUIEditorCommands();
  810. }
  811. void ProjucerApplication::selectEditorColourSchemeWithName (const String& schemeName)
  812. {
  813. auto& appearanceSettings = getAppSettings().appearance;
  814. auto schemes = appearanceSettings.getPresetSchemes();
  815. auto schemeIndex = schemes.indexOf (schemeName);
  816. if (schemeIndex >= 0)
  817. setEditorColourScheme (schemeIndex, true);
  818. }
  819. void ProjucerApplication::setColourScheme (int index, bool saveSetting)
  820. {
  821. switch (index)
  822. {
  823. case 0: lookAndFeel.setColourScheme (LookAndFeel_V4::getDarkColourScheme()); break;
  824. case 1: lookAndFeel.setColourScheme (LookAndFeel_V4::getGreyColourScheme()); break;
  825. case 2: lookAndFeel.setColourScheme (LookAndFeel_V4::getLightColourScheme()); break;
  826. default: break;
  827. }
  828. lookAndFeel.setupColours();
  829. mainWindowList.sendLookAndFeelChange();
  830. if (utf8Window != nullptr) utf8Window->sendLookAndFeelChange();
  831. if (svgPathWindow != nullptr) svgPathWindow->sendLookAndFeelChange();
  832. if (aboutWindow != nullptr) aboutWindow->sendLookAndFeelChange();
  833. if (applicationUsageDataWindow != nullptr) applicationUsageDataWindow->sendLookAndFeelChange();
  834. if (pathsWindow != nullptr) pathsWindow->sendLookAndFeelChange();
  835. if (editorColourSchemeWindow != nullptr) editorColourSchemeWindow->sendLookAndFeelChange();
  836. auto* mcm = ModalComponentManager::getInstance();
  837. for (auto i = 0; i < mcm->getNumModalComponents(); ++i)
  838. mcm->getModalComponent (i)->sendLookAndFeelChange();
  839. if (saveSetting)
  840. {
  841. auto& properties = settings->getGlobalProperties();
  842. properties.setValue ("COLOUR SCHEME", index);
  843. }
  844. selectedColourSchemeIndex = index;
  845. getCommandManager().commandStatusChanged();
  846. }
  847. void ProjucerApplication::setEditorColourScheme (int index, bool saveSetting)
  848. {
  849. auto& appearanceSettings = getAppSettings().appearance;
  850. auto schemes = appearanceSettings.getPresetSchemes();
  851. index = jmin (index, schemes.size() - 1);
  852. appearanceSettings.selectPresetScheme (index);
  853. if (saveSetting)
  854. {
  855. auto& properties = settings->getGlobalProperties();
  856. properties.setValue ("EDITOR COLOUR SCHEME", index);
  857. }
  858. selectedEditorColourSchemeIndex = index;
  859. getCommandManager().commandStatusChanged();
  860. }
  861. bool ProjucerApplication::isEditorColourSchemeADefaultScheme (const StringArray& schemes, int editorColourSchemeIndex)
  862. {
  863. auto& schemeName = schemes[editorColourSchemeIndex];
  864. return (schemeName == "Default (Dark)" || schemeName == "Default (Light)");
  865. }
  866. int ProjucerApplication::getEditorColourSchemeForGUIColourScheme (const StringArray& schemes, int guiColourSchemeIndex)
  867. {
  868. auto defaultDarkEditorIndex = schemes.indexOf ("Default (Dark)");
  869. auto defaultLightEditorIndex = schemes.indexOf ("Default (Light)");
  870. // Can't find default code editor colour schemes!
  871. jassert (defaultDarkEditorIndex != -1 && defaultLightEditorIndex != -1);
  872. return (guiColourSchemeIndex == 2 ? defaultLightEditorIndex : defaultDarkEditorIndex);
  873. }
  874. void ProjucerApplication::updateEditorColourSchemeIfNeeded()
  875. {
  876. auto& appearanceSettings = getAppSettings().appearance;
  877. auto schemes = appearanceSettings.getPresetSchemes();
  878. if (isEditorColourSchemeADefaultScheme (schemes, selectedEditorColourSchemeIndex))
  879. setEditorColourScheme (getEditorColourSchemeForGUIColourScheme (schemes, selectedColourSchemeIndex), true);
  880. }