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.

1550 lines
55KB

  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::getCpuSpeedInMegahertz())
  64. + "MHz Cores: " + String (SystemStats::getNumCpus())
  65. + " " + String (SystemStats::getMemorySizeInMegabytes()) + "MB");
  66. isRunningCommandLine = commandLine.isNotEmpty()
  67. && ! commandLine.startsWith ("-NSDocumentRevisionsDebugMode");
  68. initialiseBasics();
  69. licenseController.reset (new LicenseController);
  70. licenseController->addLicenseStatusChangedCallback (this);
  71. if (isRunningCommandLine)
  72. {
  73. auto appReturnCode = performCommandLine (ArgumentList ("Projucer", commandLine));
  74. if (appReturnCode != commandLineNotPerformed)
  75. {
  76. setApplicationReturnValue (appReturnCode);
  77. quit();
  78. return;
  79. }
  80. isRunningCommandLine = false;
  81. }
  82. if (sendCommandLineToPreexistingInstance())
  83. {
  84. DBG ("Another instance is running - quitting...");
  85. quit();
  86. return;
  87. }
  88. openDocumentManager.registerType (new ProjucerAppClasses::LiveBuildCodeEditorDocument::Type(), 2);
  89. childProcessCache.reset (new ChildProcessCache());
  90. initCommandManager();
  91. menuModel.reset (new MainMenuModel());
  92. settings->appearance.refreshPresetSchemeList();
  93. setColourScheme (settings->getGlobalProperties().getIntValue ("COLOUR SCHEME"), false);
  94. setEditorColourScheme (settings->getGlobalProperties().getIntValue ("EDITOR COLOUR SCHEME"), false);
  95. updateEditorColourSchemeIfNeeded();
  96. // do further initialisation in a moment when the message loop has started
  97. triggerAsyncUpdate();
  98. }
  99. }
  100. void ProjucerApplication::initialiseBasics()
  101. {
  102. LookAndFeel::setDefaultLookAndFeel (&lookAndFeel);
  103. settings.reset (new StoredSettings());
  104. ImageCache::setCacheTimeout (30 * 1000);
  105. icons.reset (new Icons());
  106. tooltipWindow.setMillisecondsBeforeTipAppears (1200);
  107. if (isRunningCommandLine)
  108. {
  109. rescanJUCEPathModules();
  110. rescanUserPathModules();
  111. }
  112. }
  113. bool ProjucerApplication::initialiseLogger (const char* filePrefix)
  114. {
  115. if (logger == nullptr)
  116. {
  117. #if JUCE_LINUX
  118. String folder = "~/.config/Projucer/Logs";
  119. #else
  120. String folder = "com.juce.projucer";
  121. #endif
  122. logger.reset (FileLogger::createDateStampedLogger (folder, filePrefix, ".txt",
  123. getApplicationName() + " " + getApplicationVersion()
  124. + " --- Build date: " __DATE__));
  125. Logger::setCurrentLogger (logger.get());
  126. }
  127. return logger != nullptr;
  128. }
  129. void ProjucerApplication::handleAsyncUpdate()
  130. {
  131. if (licenseController != nullptr)
  132. licenseController->startWebviewIfNeeded();
  133. rescanJUCEPathModules();
  134. rescanUserPathModules();
  135. #if JUCE_MAC
  136. PopupMenu extraAppleMenuItems;
  137. createExtraAppleMenuItems (extraAppleMenuItems);
  138. // workaround broken "Open Recent" submenu: not passing the
  139. // submenu's title here avoids the defect in JuceMainMenuHandler::addMenuItem
  140. MenuBarModel::setMacMainMenu (menuModel.get(), &extraAppleMenuItems); //, "Open Recent");
  141. #endif
  142. versionChecker.reset (new LatestVersionChecker());
  143. if (licenseController != nullptr)
  144. {
  145. setAnalyticsEnabled (licenseController->getState().applicationUsageDataState == LicenseState::ApplicationUsageData::enabled);
  146. Analytics::getInstance()->logEvent ("Startup", {}, ProjucerAnalyticsEvent::appEvent);
  147. }
  148. if (! isRunningCommandLine && settings->shouldAskUserToSetJUCEPath())
  149. showSetJUCEPathAlert();
  150. }
  151. void ProjucerApplication::initialiseWindows (const String& commandLine)
  152. {
  153. const String commandLineWithoutNSDebug (commandLine.replace ("-NSDocumentRevisionsDebugMode YES", StringRef()));
  154. if (commandLineWithoutNSDebug.trim().isNotEmpty() && ! commandLineWithoutNSDebug.trim().startsWithChar ('-'))
  155. anotherInstanceStarted (commandLine);
  156. else
  157. mainWindowList.reopenLastProjects();
  158. mainWindowList.createWindowIfNoneAreOpen();
  159. if (licenseController->getState().applicationUsageDataState == LicenseState::ApplicationUsageData::notChosenYet)
  160. showApplicationUsageDataAgreementPopup();
  161. }
  162. void ProjucerApplication::shutdown()
  163. {
  164. if (server != nullptr)
  165. {
  166. destroyClangServer (server);
  167. Logger::writeToLog ("Server shutdown cleanly");
  168. }
  169. versionChecker.reset();
  170. utf8Window.reset();
  171. svgPathWindow.reset();
  172. aboutWindow.reset();
  173. pathsWindow.reset();
  174. editorColourSchemeWindow.reset();
  175. pipCreatorWindow.reset();
  176. if (licenseController != nullptr)
  177. {
  178. licenseController->removeLicenseStatusChangedCallback (this);
  179. licenseController.reset();
  180. }
  181. mainWindowList.forceCloseAllWindows();
  182. openDocumentManager.clear();
  183. childProcessCache.reset();
  184. #if JUCE_MAC
  185. MenuBarModel::setMacMainMenu (nullptr);
  186. #endif
  187. menuModel.reset();
  188. commandManager.reset();
  189. settings.reset();
  190. LookAndFeel::setDefaultLookAndFeel (nullptr);
  191. // clean up after ourselves and delete any temp project files that may have
  192. // been created from PIPs
  193. deleteTemporaryFiles();
  194. if (! isRunningCommandLine)
  195. Logger::writeToLog ("Shutdown");
  196. deleteLogger();
  197. Analytics::getInstance()->logEvent ("Shutdown", {}, ProjucerAnalyticsEvent::appEvent);
  198. }
  199. struct AsyncQuitRetrier : private Timer
  200. {
  201. AsyncQuitRetrier() { startTimer (500); }
  202. void timerCallback() override
  203. {
  204. stopTimer();
  205. delete this;
  206. if (auto* app = JUCEApplicationBase::getInstance())
  207. app->systemRequestedQuit();
  208. }
  209. JUCE_DECLARE_NON_COPYABLE (AsyncQuitRetrier)
  210. };
  211. void ProjucerApplication::systemRequestedQuit()
  212. {
  213. if (server != nullptr)
  214. {
  215. sendQuitMessageToIDE (server);
  216. }
  217. else if (ModalComponentManager::getInstance()->cancelAllModalComponents())
  218. {
  219. new AsyncQuitRetrier();
  220. }
  221. else
  222. {
  223. if (closeAllMainWindows())
  224. quit();
  225. }
  226. }
  227. //==============================================================================
  228. void ProjucerApplication::licenseStateChanged (const LicenseState& state)
  229. {
  230. #if ! JUCER_ENABLE_GPL_MODE
  231. if (state.type != LicenseState::Type::notLoggedIn
  232. && state.type != LicenseState::Type::noLicenseChosenYet)
  233. #else
  234. ignoreUnused (state);
  235. #endif
  236. {
  237. initialiseWindows (getCommandLineParameters());
  238. }
  239. }
  240. void ProjucerApplication::doLogout()
  241. {
  242. if (licenseController != nullptr)
  243. {
  244. const LicenseState& state = licenseController->getState();
  245. if (state.type != LicenseState::Type::notLoggedIn && closeAllMainWindows())
  246. licenseController->logout();
  247. }
  248. }
  249. //==============================================================================
  250. String ProjucerApplication::getVersionDescription() const
  251. {
  252. String s;
  253. const Time buildDate (Time::getCompilationDate());
  254. s << "Projucer " << ProjectInfo::versionString
  255. << newLine
  256. << "Build date: " << buildDate.getDayOfMonth()
  257. << " " << Time::getMonthName (buildDate.getMonth(), true)
  258. << " " << buildDate.getYear();
  259. return s;
  260. }
  261. void ProjucerApplication::anotherInstanceStarted (const String& commandLine)
  262. {
  263. if (server == nullptr && ! commandLine.trim().startsWithChar ('-'))
  264. openFile (File (commandLine.unquoted()));
  265. }
  266. ProjucerApplication& ProjucerApplication::getApp()
  267. {
  268. ProjucerApplication* const app = dynamic_cast<ProjucerApplication*> (JUCEApplication::getInstance());
  269. jassert (app != nullptr);
  270. return *app;
  271. }
  272. ApplicationCommandManager& ProjucerApplication::getCommandManager()
  273. {
  274. auto* cm = ProjucerApplication::getApp().commandManager.get();
  275. jassert (cm != nullptr);
  276. return *cm;
  277. }
  278. //==============================================================================
  279. enum
  280. {
  281. recentProjectsBaseID = 100,
  282. openWindowsBaseID = 300,
  283. activeDocumentsBaseID = 400,
  284. colourSchemeBaseID = 1000,
  285. codeEditorColourSchemeBaseID = 1500,
  286. showPathsID = 1999,
  287. examplesBaseID = 2000
  288. };
  289. MenuBarModel* ProjucerApplication::getMenuModel()
  290. {
  291. return menuModel.get();
  292. }
  293. StringArray ProjucerApplication::getMenuNames()
  294. {
  295. return { "File", "Edit", "View", "Build", "Window", "Document", "GUI Editor", "Tools", "Help" };
  296. }
  297. void ProjucerApplication::createMenu (PopupMenu& menu, const String& menuName)
  298. {
  299. if (menuName == "File") createFileMenu (menu);
  300. else if (menuName == "Edit") createEditMenu (menu);
  301. else if (menuName == "View") createViewMenu (menu);
  302. else if (menuName == "Build") createBuildMenu (menu);
  303. else if (menuName == "Window") createWindowMenu (menu);
  304. else if (menuName == "Document") createDocumentMenu (menu);
  305. else if (menuName == "Tools") createToolsMenu (menu);
  306. else if (menuName == "Help") createHelpMenu (menu);
  307. else if (menuName == "GUI Editor") createGUIEditorMenu (menu);
  308. else jassertfalse; // names have changed?
  309. }
  310. void ProjucerApplication::createFileMenu (PopupMenu& menu)
  311. {
  312. menu.addCommandItem (commandManager.get(), CommandIDs::newProject);
  313. menu.addCommandItem (commandManager.get(), CommandIDs::newProjectFromClipboard);
  314. menu.addCommandItem (commandManager.get(), CommandIDs::newPIP);
  315. menu.addSeparator();
  316. menu.addCommandItem (commandManager.get(), CommandIDs::open);
  317. {
  318. PopupMenu recentFiles;
  319. settings->recentFiles.createPopupMenuItems (recentFiles, recentProjectsBaseID, true, true);
  320. if (recentFiles.getNumItems() > 0)
  321. {
  322. recentFiles.addSeparator();
  323. recentFiles.addCommandItem (commandManager.get(), CommandIDs::clearRecentFiles);
  324. }
  325. menu.addSubMenu ("Open Recent", recentFiles);
  326. }
  327. {
  328. PopupMenu examples;
  329. createExamplesPopupMenu (examples);
  330. menu.addSubMenu ("Open Example", examples);
  331. }
  332. menu.addSeparator();
  333. menu.addCommandItem (commandManager.get(), CommandIDs::closeDocument);
  334. menu.addCommandItem (commandManager.get(), CommandIDs::saveDocument);
  335. menu.addCommandItem (commandManager.get(), CommandIDs::saveDocumentAs);
  336. menu.addCommandItem (commandManager.get(), CommandIDs::saveAll);
  337. menu.addSeparator();
  338. menu.addCommandItem (commandManager.get(), CommandIDs::closeProject);
  339. menu.addCommandItem (commandManager.get(), CommandIDs::saveProject);
  340. menu.addSeparator();
  341. menu.addCommandItem (commandManager.get(), CommandIDs::openInIDE);
  342. menu.addCommandItem (commandManager.get(), CommandIDs::saveAndOpenInIDE);
  343. menu.addSeparator();
  344. #if ! JUCER_ENABLE_GPL_MODE
  345. menu.addCommandItem (commandManager.get(), CommandIDs::loginLogout);
  346. #endif
  347. #if ! JUCE_MAC
  348. menu.addCommandItem (commandManager.get(), CommandIDs::showAboutWindow);
  349. menu.addCommandItem (commandManager.get(), CommandIDs::showAppUsageWindow);
  350. menu.addCommandItem (commandManager.get(), CommandIDs::showGlobalPathsWindow);
  351. menu.addSeparator();
  352. menu.addCommandItem (commandManager.get(), StandardApplicationCommandIDs::quit);
  353. #endif
  354. }
  355. void ProjucerApplication::createEditMenu (PopupMenu& menu)
  356. {
  357. menu.addCommandItem (commandManager.get(), StandardApplicationCommandIDs::undo);
  358. menu.addCommandItem (commandManager.get(), StandardApplicationCommandIDs::redo);
  359. menu.addSeparator();
  360. menu.addCommandItem (commandManager.get(), StandardApplicationCommandIDs::cut);
  361. menu.addCommandItem (commandManager.get(), StandardApplicationCommandIDs::copy);
  362. menu.addCommandItem (commandManager.get(), StandardApplicationCommandIDs::paste);
  363. menu.addCommandItem (commandManager.get(), StandardApplicationCommandIDs::del);
  364. menu.addCommandItem (commandManager.get(), StandardApplicationCommandIDs::selectAll);
  365. menu.addCommandItem (commandManager.get(), StandardApplicationCommandIDs::deselectAll);
  366. menu.addSeparator();
  367. menu.addCommandItem (commandManager.get(), CommandIDs::showFindPanel);
  368. menu.addCommandItem (commandManager.get(), CommandIDs::findSelection);
  369. menu.addCommandItem (commandManager.get(), CommandIDs::findNext);
  370. menu.addCommandItem (commandManager.get(), CommandIDs::findPrevious);
  371. }
  372. void ProjucerApplication::createViewMenu (PopupMenu& menu)
  373. {
  374. menu.addCommandItem (commandManager.get(), CommandIDs::showProjectSettings);
  375. menu.addCommandItem (commandManager.get(), CommandIDs::showProjectTab);
  376. menu.addCommandItem (commandManager.get(), CommandIDs::showBuildTab);
  377. menu.addCommandItem (commandManager.get(), CommandIDs::showFileExplorerPanel);
  378. menu.addCommandItem (commandManager.get(), CommandIDs::showModulesPanel);
  379. menu.addCommandItem (commandManager.get(), CommandIDs::showExportersPanel);
  380. menu.addCommandItem (commandManager.get(), CommandIDs::showExporterSettings);
  381. menu.addSeparator();
  382. createColourSchemeItems (menu);
  383. }
  384. void ProjucerApplication::createBuildMenu (PopupMenu& menu)
  385. {
  386. menu.addCommandItem (commandManager.get(), CommandIDs::toggleBuildEnabled);
  387. menu.addCommandItem (commandManager.get(), CommandIDs::buildNow);
  388. menu.addCommandItem (commandManager.get(), CommandIDs::toggleContinuousBuild);
  389. menu.addSeparator();
  390. menu.addCommandItem (commandManager.get(), CommandIDs::launchApp);
  391. menu.addCommandItem (commandManager.get(), CommandIDs::killApp);
  392. menu.addCommandItem (commandManager.get(), CommandIDs::cleanAll);
  393. menu.addSeparator();
  394. menu.addCommandItem (commandManager.get(), CommandIDs::reinstantiateComp);
  395. menu.addCommandItem (commandManager.get(), CommandIDs::showWarnings);
  396. menu.addSeparator();
  397. menu.addCommandItem (commandManager.get(), CommandIDs::nextError);
  398. menu.addCommandItem (commandManager.get(), CommandIDs::prevError);
  399. }
  400. void ProjucerApplication::createColourSchemeItems (PopupMenu& menu)
  401. {
  402. PopupMenu colourSchemes;
  403. colourSchemes.addItem (colourSchemeBaseID + 0, "Dark", true, selectedColourSchemeIndex == 0);
  404. colourSchemes.addItem (colourSchemeBaseID + 1, "Grey", true, selectedColourSchemeIndex == 1);
  405. colourSchemes.addItem (colourSchemeBaseID + 2, "Light", true, selectedColourSchemeIndex == 2);
  406. menu.addSubMenu ("Colour Scheme", colourSchemes);
  407. //==========================================================================
  408. PopupMenu editorColourSchemes;
  409. auto& appearanceSettings = getAppSettings().appearance;
  410. appearanceSettings.refreshPresetSchemeList();
  411. auto schemes = appearanceSettings.getPresetSchemes();
  412. auto i = 0;
  413. for (auto s : schemes)
  414. {
  415. editorColourSchemes.addItem (codeEditorColourSchemeBaseID + i, s,
  416. editorColourSchemeWindow == nullptr,
  417. selectedEditorColourSchemeIndex == i);
  418. ++i;
  419. }
  420. numEditorColourSchemes = i;
  421. editorColourSchemes.addSeparator();
  422. editorColourSchemes.addItem (codeEditorColourSchemeBaseID + numEditorColourSchemes,
  423. "Create...", editorColourSchemeWindow == nullptr);
  424. menu.addSubMenu ("Editor Colour Scheme", editorColourSchemes);
  425. }
  426. void ProjucerApplication::createWindowMenu (PopupMenu& menu)
  427. {
  428. menu.addCommandItem (commandManager.get(), CommandIDs::goToPreviousWindow);
  429. menu.addCommandItem (commandManager.get(), CommandIDs::goToNextWindow);
  430. menu.addCommandItem (commandManager.get(), CommandIDs::closeWindow);
  431. menu.addSeparator();
  432. int counter = 0;
  433. for (auto* window : mainWindowList.windows)
  434. {
  435. if (window != nullptr)
  436. {
  437. if (auto* project = window->getProject())
  438. menu.addItem (openWindowsBaseID + counter++, project->getProjectNameString());
  439. }
  440. }
  441. menu.addSeparator();
  442. menu.addCommandItem (commandManager.get(), CommandIDs::closeAllWindows);
  443. }
  444. void ProjucerApplication::createDocumentMenu (PopupMenu& menu)
  445. {
  446. menu.addCommandItem (commandManager.get(), CommandIDs::goToPreviousDoc);
  447. menu.addCommandItem (commandManager.get(), CommandIDs::goToNextDoc);
  448. menu.addCommandItem (commandManager.get(), CommandIDs::goToCounterpart);
  449. menu.addSeparator();
  450. auto numDocs = jmin (50, openDocumentManager.getNumOpenDocuments());
  451. for (int i = 0; i < numDocs; ++i)
  452. {
  453. OpenDocumentManager::Document* doc = openDocumentManager.getOpenDocument(i);
  454. menu.addItem (activeDocumentsBaseID + i, doc->getName());
  455. }
  456. menu.addSeparator();
  457. menu.addCommandItem (commandManager.get(), CommandIDs::closeAllDocuments);
  458. }
  459. void ProjucerApplication::createToolsMenu (PopupMenu& menu)
  460. {
  461. menu.addCommandItem (commandManager.get(), CommandIDs::showUTF8Tool);
  462. menu.addCommandItem (commandManager.get(), CommandIDs::showSVGPathTool);
  463. menu.addCommandItem (commandManager.get(), CommandIDs::showTranslationTool);
  464. }
  465. void ProjucerApplication::createHelpMenu (PopupMenu& menu)
  466. {
  467. menu.addCommandItem (commandManager.get(), CommandIDs::showForum);
  468. menu.addSeparator();
  469. menu.addCommandItem (commandManager.get(), CommandIDs::showAPIModules);
  470. menu.addCommandItem (commandManager.get(), CommandIDs::showAPIClasses);
  471. menu.addCommandItem (commandManager.get(), CommandIDs::showTutorials);
  472. }
  473. void ProjucerApplication::createExtraAppleMenuItems (PopupMenu& menu)
  474. {
  475. menu.addCommandItem (commandManager.get(), CommandIDs::showAboutWindow);
  476. menu.addCommandItem (commandManager.get(), CommandIDs::showAppUsageWindow);
  477. menu.addSeparator();
  478. menu.addCommandItem (commandManager.get(), CommandIDs::showGlobalPathsWindow);
  479. }
  480. void ProjucerApplication::createExamplesPopupMenu (PopupMenu& menu) noexcept
  481. {
  482. numExamples = 0;
  483. for (auto& dir : getSortedExampleDirectories())
  484. {
  485. PopupMenu m;
  486. for (auto& f : getSortedExampleFilesInDirectory (dir))
  487. {
  488. m.addItem (examplesBaseID + numExamples, f.getFileNameWithoutExtension());
  489. ++numExamples;
  490. }
  491. menu.addSubMenu (dir.getFileName(), m);
  492. }
  493. if (numExamples == 0)
  494. {
  495. menu.addItem (showPathsID, "Set path to JUCE...");
  496. }
  497. else
  498. {
  499. menu.addSeparator();
  500. menu.addCommandItem (commandManager.get(), CommandIDs::launchDemoRunner);
  501. }
  502. }
  503. Array<File> ProjucerApplication::getSortedExampleDirectories() noexcept
  504. {
  505. Array<File> exampleDirectories;
  506. auto examplesPath = getJUCEExamplesDirectoryPathFromGlobal();
  507. if (! isValidJUCEExamplesDirectory (examplesPath))
  508. return {};
  509. DirectoryIterator iter (examplesPath, false, "*", File::findDirectories);
  510. while (iter.next())
  511. {
  512. auto exampleDirectory = iter.getFile();
  513. if (exampleDirectory.getNumberOfChildFiles (File::findFiles | File::ignoreHiddenFiles) > 0
  514. && exampleDirectory.getFileName() != "DemoRunner" && exampleDirectory.getFileName() != "Assets")
  515. exampleDirectories.add (exampleDirectory);
  516. }
  517. exampleDirectories.sort();
  518. return exampleDirectories;
  519. }
  520. Array<File> ProjucerApplication::getSortedExampleFilesInDirectory (const File& directory) const noexcept
  521. {
  522. Array<File> exampleFiles;
  523. DirectoryIterator iter (directory, false, "*.h", File::findFiles);
  524. while (iter.next())
  525. exampleFiles.add (iter.getFile());
  526. exampleFiles.sort();
  527. return exampleFiles;
  528. }
  529. bool ProjucerApplication::findWindowAndOpenPIP (const File& pip)
  530. {
  531. auto* window = mainWindowList.getFrontmostWindow();
  532. bool shouldCloseWindow = false;
  533. if (window == nullptr)
  534. {
  535. window = mainWindowList.getOrCreateEmptyWindow();
  536. shouldCloseWindow = true;
  537. }
  538. if (window->tryToOpenPIP (pip))
  539. return true;
  540. if (shouldCloseWindow)
  541. mainWindowList.closeWindow (window);
  542. return false;
  543. }
  544. File ProjucerApplication::getJUCEExamplesDirectoryPathFromGlobal() noexcept
  545. {
  546. auto globalPath = getAppSettings().getStoredPath (Ids::jucePath).toString();
  547. if (globalPath.isNotEmpty())
  548. return File (globalPath).getChildFile ("examples");
  549. return {};
  550. }
  551. void ProjucerApplication::findAndLaunchExample (int selectedIndex)
  552. {
  553. File example;
  554. for (auto& dir : getSortedExampleDirectories())
  555. {
  556. auto exampleFiles = getSortedExampleFilesInDirectory (dir);
  557. if (selectedIndex < exampleFiles.size())
  558. {
  559. example = exampleFiles.getUnchecked (selectedIndex);
  560. break;
  561. }
  562. selectedIndex -= exampleFiles.size();
  563. }
  564. // example doesn't exist?
  565. jassert (example != File());
  566. findWindowAndOpenPIP (example);
  567. StringPairArray data;
  568. data.set ("label", example.getFileNameWithoutExtension());
  569. Analytics::getInstance()->logEvent ("Example Opened", data, ProjucerAnalyticsEvent::exampleEvent);
  570. }
  571. File ProjucerApplication::findDemoRunnerExecutable() noexcept
  572. {
  573. auto buildsPath = getJUCEExamplesDirectoryPathFromGlobal().getChildFile ("DemoRunner").getChildFile ("Builds");
  574. if (! buildsPath.exists())
  575. return {};
  576. String extension;
  577. #if JUCE_MAC
  578. auto osxBuildFolder = buildsPath.getChildFile ("MacOSX").getChildFile ("build");
  579. auto demoRunnerExecutable = osxBuildFolder.getChildFile ("Release").getChildFile ("DemoRunner.app");
  580. if (demoRunnerExecutable.exists())
  581. return demoRunnerExecutable;
  582. demoRunnerExecutable = osxBuildFolder.getChildFile ("Debug").getChildFile ("DemoRunner.app");
  583. if (demoRunnerExecutable.exists())
  584. return demoRunnerExecutable;
  585. extension = ".app";
  586. #elif JUCE_WINDOWS
  587. auto windowsBuildFolder = buildsPath.getChildFile ("VisualStudio2017").getChildFile ("x64");
  588. auto demoRunnerExecutable = windowsBuildFolder.getChildFile ("Release").getChildFile ("App").getChildFile ("DemoRunner.exe");
  589. if (demoRunnerExecutable.existsAsFile())
  590. return demoRunnerExecutable;
  591. demoRunnerExecutable = windowsBuildFolder.getChildFile ("Debug").getChildFile ("App").getChildFile ("DemoRunner.exe");
  592. if (demoRunnerExecutable.existsAsFile())
  593. return demoRunnerExecutable;
  594. extension = ".exe";
  595. #elif JUCE_LINUX
  596. auto linuxBuildFolder = buildsPath.getChildFile ("LinuxMakefile").getChildFile ("build");
  597. auto demoRunnerExecutable = linuxBuildFolder.getChildFile ("DemoRunner");
  598. if (demoRunnerExecutable.existsAsFile())
  599. return demoRunnerExecutable;
  600. extension = {};
  601. #endif
  602. auto juceDir = getAppSettings().getStoredPath (Ids::jucePath).toString();
  603. if (juceDir.isNotEmpty())
  604. {
  605. auto precompiledFile = File (juceDir).getChildFile ("DemoRunner" + extension);
  606. #if JUCE_MAC
  607. if (precompiledFile.exists())
  608. #else
  609. if (precompiledFile.existsAsFile())
  610. #endif
  611. return precompiledFile;
  612. }
  613. return {};
  614. }
  615. File ProjucerApplication::findDemoRunnerProject() noexcept
  616. {
  617. auto buildsPath = getJUCEExamplesDirectoryPathFromGlobal().getChildFile ("DemoRunner").getChildFile ("Builds");
  618. if (! buildsPath.exists())
  619. return {};
  620. #if JUCE_MAC
  621. auto file = buildsPath.getChildFile ("MacOSX").getChildFile ("DemoRunner.xcodeproj");
  622. if (file.exists())
  623. return file;
  624. #elif JUCE_WINDOWS
  625. auto file = buildsPath.getChildFile ("VisualStudio2017").getChildFile ("DemoRunner.sln");
  626. if (file.existsAsFile())
  627. return file;
  628. #elif JUCE_LINUX
  629. auto file = buildsPath.getChildFile ("LinuxMakeFile").getChildFile ("Makefile");
  630. if (file.existsAsFile())
  631. return file;
  632. #endif
  633. return {};
  634. }
  635. void ProjucerApplication::launchDemoRunner()
  636. {
  637. if (findDemoRunnerExecutable() != File())
  638. {
  639. bool succeeded = true;
  640. if (! findDemoRunnerExecutable().startAsProcess())
  641. {
  642. AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, "Error", "There was an error opening the Demo Runner file.");
  643. succeeded = false;
  644. }
  645. StringPairArray data;
  646. data.set ("label", succeeded ? "Success" : "Failure");
  647. Analytics::getInstance()->logEvent ("Launch DemoRunner", data, ProjucerAnalyticsEvent::exampleEvent);
  648. }
  649. else if (findDemoRunnerProject() != File())
  650. {
  651. auto& lf = Desktop::getInstance().getDefaultLookAndFeel();
  652. demoRunnerAlert.reset (lf.createAlertWindow ("Open Project",
  653. "Couldn't find a compiled version of the Demo Runner."
  654. #if JUCE_LINUX
  655. " Do you want to build it now?", "Build project", "Cancel",
  656. #else
  657. " Do you want to open the project?", "Open project", "Cancel",
  658. #endif
  659. {},
  660. AlertWindow::QuestionIcon, 2,
  661. mainWindowList.getFrontmostWindow (false)));
  662. demoRunnerAlert->enterModalState (true, ModalCallbackFunction::create ([this] (int retVal)
  663. {
  664. demoRunnerAlert.reset (nullptr);
  665. StringPairArray data;
  666. data.set ("label", retVal == 1 ? "Opened" : "Cancelled");
  667. Analytics::getInstance()->logEvent ("Open DemoRunner Project", data, ProjucerAnalyticsEvent::exampleEvent);
  668. if (retVal == 1)
  669. {
  670. auto projectFile = findDemoRunnerProject();
  671. #if JUCE_LINUX
  672. String command ("make -C " + projectFile.getParentDirectory().getFullPathName() + " CONFIG=Release -j3");
  673. if (! makeProcess.start (command))
  674. AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, "Error", "Error building Demo Runner.");
  675. #else
  676. projectFile.startAsProcess();
  677. #endif
  678. }
  679. }), false);
  680. }
  681. else
  682. {
  683. jassertfalse;
  684. }
  685. }
  686. void ProjucerApplication::handleMainMenuCommand (int menuItemID)
  687. {
  688. if (menuItemID >= recentProjectsBaseID && menuItemID < (recentProjectsBaseID + 100))
  689. {
  690. // open a file from the "recent files" menu
  691. openFile (settings->recentFiles.getFile (menuItemID - recentProjectsBaseID));
  692. }
  693. else if (menuItemID >= openWindowsBaseID && menuItemID < (openWindowsBaseID + 100))
  694. {
  695. if (auto* window = mainWindowList.windows.getUnchecked (menuItemID - openWindowsBaseID))
  696. window->toFront (true);
  697. }
  698. else if (menuItemID >= activeDocumentsBaseID && menuItemID < (activeDocumentsBaseID + 200))
  699. {
  700. if (auto* doc = openDocumentManager.getOpenDocument (menuItemID - activeDocumentsBaseID))
  701. mainWindowList.openDocument (doc, true);
  702. else
  703. jassertfalse;
  704. }
  705. else if (menuItemID >= colourSchemeBaseID && menuItemID < (colourSchemeBaseID + 3))
  706. {
  707. setColourScheme (menuItemID - colourSchemeBaseID, true);
  708. updateEditorColourSchemeIfNeeded();
  709. }
  710. else if (menuItemID >= codeEditorColourSchemeBaseID && menuItemID < (codeEditorColourSchemeBaseID + numEditorColourSchemes))
  711. {
  712. setEditorColourScheme (menuItemID - codeEditorColourSchemeBaseID, true);
  713. }
  714. else if (menuItemID == (codeEditorColourSchemeBaseID + numEditorColourSchemes))
  715. {
  716. showEditorColourSchemeWindow();
  717. }
  718. else if (menuItemID == showPathsID)
  719. {
  720. showPathsWindow (true);
  721. }
  722. else if (menuItemID >= examplesBaseID && menuItemID < (examplesBaseID + numExamples))
  723. {
  724. findAndLaunchExample (menuItemID - examplesBaseID);
  725. }
  726. else
  727. {
  728. handleGUIEditorMenuCommand (menuItemID);
  729. }
  730. }
  731. //==============================================================================
  732. void ProjucerApplication::getAllCommands (Array <CommandID>& commands)
  733. {
  734. JUCEApplication::getAllCommands (commands);
  735. const CommandID ids[] = { CommandIDs::newProject,
  736. CommandIDs::newProjectFromClipboard,
  737. CommandIDs::newPIP,
  738. CommandIDs::open,
  739. CommandIDs::launchDemoRunner,
  740. CommandIDs::closeAllWindows,
  741. CommandIDs::closeAllDocuments,
  742. CommandIDs::clearRecentFiles,
  743. CommandIDs::saveAll,
  744. CommandIDs::showGlobalPathsWindow,
  745. CommandIDs::showUTF8Tool,
  746. CommandIDs::showSVGPathTool,
  747. CommandIDs::showAboutWindow,
  748. CommandIDs::showAppUsageWindow,
  749. CommandIDs::showForum,
  750. CommandIDs::showAPIModules,
  751. CommandIDs::showAPIClasses,
  752. CommandIDs::showTutorials,
  753. CommandIDs::loginLogout };
  754. commands.addArray (ids, numElementsInArray (ids));
  755. }
  756. void ProjucerApplication::getCommandInfo (CommandID commandID, ApplicationCommandInfo& result)
  757. {
  758. switch (commandID)
  759. {
  760. case CommandIDs::newProject:
  761. result.setInfo ("New Project...", "Creates a new JUCE project", CommandCategories::general, 0);
  762. result.defaultKeypresses.add (KeyPress ('n', ModifierKeys::commandModifier, 0));
  763. break;
  764. case CommandIDs::newProjectFromClipboard:
  765. result.setInfo ("New Project From Clipboard...", "Creates a new JUCE project from the clipboard contents", CommandCategories::general, 0);
  766. result.defaultKeypresses.add (KeyPress ('n', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0));
  767. break;
  768. case CommandIDs::newPIP:
  769. result.setInfo ("New PIP...", "Opens the PIP Creator utility for creating a new PIP", CommandCategories::general, 0);
  770. result.defaultKeypresses.add (KeyPress ('p', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0));
  771. break;
  772. case CommandIDs::launchDemoRunner:
  773. #if JUCE_LINUX
  774. if (makeProcess.isRunning())
  775. {
  776. result.setInfo ("Building Demo Runner...", "The Demo Runner project is currently building", CommandCategories::general, 0);
  777. result.setActive (false);
  778. }
  779. else
  780. #endif
  781. {
  782. result.setInfo ("Launch Demo Runner", "Launches the JUCE demo runner application, or the project if it can't be found", CommandCategories::general, 0);
  783. result.setActive (findDemoRunnerExecutable() != File() || findDemoRunnerProject() != File());
  784. }
  785. break;
  786. case CommandIDs::open:
  787. result.setInfo ("Open...", "Opens a JUCE project", CommandCategories::general, 0);
  788. result.defaultKeypresses.add (KeyPress ('o', ModifierKeys::commandModifier, 0));
  789. break;
  790. case CommandIDs::showGlobalPathsWindow:
  791. result.setInfo ("Global Paths...",
  792. "Shows the window to change the stored global paths.",
  793. CommandCategories::general, 0);
  794. break;
  795. case CommandIDs::closeAllWindows:
  796. result.setInfo ("Close All Windows", "Closes all open windows", CommandCategories::general, 0);
  797. result.setActive (mainWindowList.windows.size() > 0);
  798. break;
  799. case CommandIDs::closeAllDocuments:
  800. result.setInfo ("Close All Documents", "Closes all open documents", CommandCategories::general, 0);
  801. result.setActive (openDocumentManager.getNumOpenDocuments() > 0);
  802. break;
  803. case CommandIDs::clearRecentFiles:
  804. result.setInfo ("Clear Recent Files", "Clears all recent files from the menu", CommandCategories::general, 0);
  805. result.setActive (settings->recentFiles.getNumFiles() > 0);
  806. break;
  807. case CommandIDs::saveAll:
  808. result.setInfo ("Save All", "Saves all open documents", CommandCategories::general, 0);
  809. result.defaultKeypresses.add (KeyPress ('s', ModifierKeys::commandModifier | ModifierKeys::altModifier, 0));
  810. break;
  811. case CommandIDs::showUTF8Tool:
  812. result.setInfo ("UTF-8 String-Literal Helper", "Shows the UTF-8 string literal utility", CommandCategories::general, 0);
  813. break;
  814. case CommandIDs::showSVGPathTool:
  815. result.setInfo ("SVG Path Converter", "Shows the SVG->Path data conversion utility", CommandCategories::general, 0);
  816. break;
  817. case CommandIDs::showAboutWindow:
  818. result.setInfo ("About Projucer", "Shows the Projucer's 'About' page.", CommandCategories::general, 0);
  819. break;
  820. case CommandIDs::showAppUsageWindow:
  821. result.setInfo ("Application Usage Data", "Shows the application usage data agreement window", CommandCategories::general, 0);
  822. break;
  823. case CommandIDs::showForum:
  824. result.setInfo ("JUCE Community Forum", "Shows the JUCE community forum in a browser", CommandCategories::general, 0);
  825. break;
  826. case CommandIDs::showAPIModules:
  827. result.setInfo ("API Modules", "Shows the API modules documentation in a browser", CommandCategories::general, 0);
  828. break;
  829. case CommandIDs::showAPIClasses:
  830. result.setInfo ("API Classes", "Shows the API classes documentation in a browser", CommandCategories::general, 0);
  831. break;
  832. case CommandIDs::showTutorials:
  833. result.setInfo ("JUCE Tutorials", "Shows the JUCE tutorials in a browser", CommandCategories::general, 0);
  834. break;
  835. case CommandIDs::loginLogout:
  836. {
  837. bool isLoggedIn = false;
  838. String username;
  839. if (licenseController != nullptr)
  840. {
  841. const LicenseState state = licenseController->getState();
  842. isLoggedIn = (state.type != LicenseState::Type::notLoggedIn && state.type != LicenseState::Type::GPL);
  843. username = state.username;
  844. }
  845. result.setInfo (isLoggedIn
  846. ? String ("Sign out ") + username + "..."
  847. : String ("Sign in..."),
  848. "Log out of your JUCE account", CommandCategories::general, 0);
  849. }
  850. break;
  851. default:
  852. JUCEApplication::getCommandInfo (commandID, result);
  853. break;
  854. }
  855. }
  856. bool ProjucerApplication::perform (const InvocationInfo& info)
  857. {
  858. switch (info.commandID)
  859. {
  860. case CommandIDs::newProject: createNewProject(); break;
  861. case CommandIDs::newProjectFromClipboard: createNewProjectFromClipboard(); break;
  862. case CommandIDs::newPIP: createNewPIP(); break;
  863. case CommandIDs::open: askUserToOpenFile(); break;
  864. case CommandIDs::launchDemoRunner: launchDemoRunner(); break;
  865. case CommandIDs::saveAll: saveAllDocuments(); break;
  866. case CommandIDs::closeAllWindows: closeAllMainWindowsAndQuitIfNeeded(); break;
  867. case CommandIDs::closeAllDocuments: closeAllDocuments (true); break;
  868. case CommandIDs::clearRecentFiles: clearRecentFiles(); break;
  869. case CommandIDs::showUTF8Tool: showUTF8ToolWindow(); break;
  870. case CommandIDs::showSVGPathTool: showSVGPathDataToolWindow(); break;
  871. case CommandIDs::showGlobalPathsWindow: showPathsWindow (false); break;
  872. case CommandIDs::showAboutWindow: showAboutWindow(); break;
  873. case CommandIDs::showAppUsageWindow: showApplicationUsageDataAgreementPopup(); break;
  874. case CommandIDs::showForum: launchForumBrowser(); break;
  875. case CommandIDs::showAPIModules: launchModulesBrowser(); break;
  876. case CommandIDs::showAPIClasses: launchClassesBrowser(); break;
  877. case CommandIDs::showTutorials: launchTutorialsBrowser(); break;
  878. case CommandIDs::loginLogout: doLogout(); break;
  879. default: return JUCEApplication::perform (info);
  880. }
  881. return true;
  882. }
  883. //==============================================================================
  884. void ProjucerApplication::createNewProject()
  885. {
  886. auto* mw = mainWindowList.getOrCreateEmptyWindow();
  887. mw->showStartPage();
  888. mainWindowList.avoidSuperimposedWindows (mw);
  889. }
  890. void ProjucerApplication::createNewProjectFromClipboard()
  891. {
  892. auto tempFile = File::getSpecialLocation (File::SpecialLocationType::tempDirectory).getChildFile ("PIPs").getChildFile ("Clipboard")
  893. .getChildFile ("PIPFile_" + String (std::abs (Random::getSystemRandom().nextInt())) + ".h");
  894. if (tempFile.existsAsFile())
  895. tempFile.deleteFile();
  896. tempFile.create();
  897. tempFile.appendText (SystemClipboard::getTextFromClipboard());
  898. if (! findWindowAndOpenPIP (tempFile))
  899. {
  900. AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, "Error", "Couldn't create project from clipboard contents.");
  901. tempFile.deleteFile();
  902. }
  903. }
  904. void ProjucerApplication::createNewPIP()
  905. {
  906. showPIPCreatorWindow();
  907. }
  908. void ProjucerApplication::askUserToOpenFile()
  909. {
  910. FileChooser fc ("Open File");
  911. if (fc.browseForFileToOpen())
  912. openFile (fc.getResult());
  913. }
  914. bool ProjucerApplication::openFile (const File& file)
  915. {
  916. return mainWindowList.openFile (file);
  917. }
  918. void ProjucerApplication::saveAllDocuments()
  919. {
  920. openDocumentManager.saveAll();
  921. for (int i = 0; i < mainWindowList.windows.size(); ++i)
  922. if (auto* pcc = mainWindowList.windows.getUnchecked(i)->getProjectContentComponent())
  923. pcc->refreshProjectTreeFileStatuses();
  924. }
  925. bool ProjucerApplication::closeAllDocuments (bool askUserToSave)
  926. {
  927. return openDocumentManager.closeAll (askUserToSave);
  928. }
  929. bool ProjucerApplication::closeAllMainWindows()
  930. {
  931. return server != nullptr || mainWindowList.askAllWindowsToClose();
  932. }
  933. void ProjucerApplication::closeAllMainWindowsAndQuitIfNeeded()
  934. {
  935. if (closeAllMainWindows())
  936. {
  937. #if ! JUCE_MAC
  938. if (mainWindowList.windows.size() == 0)
  939. systemRequestedQuit();
  940. #endif
  941. }
  942. }
  943. void ProjucerApplication::clearRecentFiles()
  944. {
  945. settings->recentFiles.clear();
  946. settings->recentFiles.clearRecentFilesNatively();
  947. settings->flush();
  948. menuModel->menuItemsChanged();
  949. }
  950. //==============================================================================
  951. void ProjucerApplication::showUTF8ToolWindow()
  952. {
  953. if (utf8Window != nullptr)
  954. utf8Window->toFront (true);
  955. else
  956. new FloatingToolWindow ("UTF-8 String Literal Converter", "utf8WindowPos",
  957. new UTF8Component(), utf8Window, true,
  958. 500, 500, 300, 300, 1000, 1000);
  959. }
  960. void ProjucerApplication::showSVGPathDataToolWindow()
  961. {
  962. if (svgPathWindow != nullptr)
  963. svgPathWindow->toFront (true);
  964. else
  965. new FloatingToolWindow ("SVG Path Converter", "svgPathWindowPos",
  966. new SVGPathDataComponent(), svgPathWindow, true,
  967. 500, 500, 300, 300, 1000, 1000);
  968. }
  969. void ProjucerApplication::showAboutWindow()
  970. {
  971. if (aboutWindow != nullptr)
  972. aboutWindow->toFront (true);
  973. else
  974. new FloatingToolWindow ({}, {}, new AboutWindowComponent(),
  975. aboutWindow, false,
  976. 500, 300, 500, 300, 500, 300);
  977. }
  978. void ProjucerApplication::showApplicationUsageDataAgreementPopup()
  979. {
  980. if (applicationUsageDataWindow != nullptr)
  981. applicationUsageDataWindow->toFront (true);
  982. else
  983. new FloatingToolWindow ("Application Usage Analytics", {},
  984. new ApplicationUsageDataWindowComponent (isPaidOrGPL()), applicationUsageDataWindow, false,
  985. 400, 300, 400, 300, 400, 300);
  986. }
  987. void ProjucerApplication::dismissApplicationUsageDataAgreementPopup()
  988. {
  989. if (applicationUsageDataWindow != nullptr)
  990. applicationUsageDataWindow.reset();
  991. }
  992. void ProjucerApplication::showPathsWindow (bool highlightJUCEPath)
  993. {
  994. if (pathsWindow != nullptr)
  995. pathsWindow->toFront (true);
  996. else
  997. new FloatingToolWindow ("Global Paths", "pathsWindowPos",
  998. new GlobalPathsWindowComponent(), pathsWindow, false,
  999. 600, 650, 600, 650, 600, 650);
  1000. if (highlightJUCEPath)
  1001. if (auto* pathsComp = dynamic_cast<GlobalPathsWindowComponent*> (pathsWindow->getChildComponent (0)))
  1002. pathsComp->highlightJUCEPath();
  1003. }
  1004. void ProjucerApplication::showEditorColourSchemeWindow()
  1005. {
  1006. if (editorColourSchemeWindow != nullptr)
  1007. editorColourSchemeWindow->toFront (true);
  1008. else
  1009. new FloatingToolWindow ("Editor Colour Scheme", "editorColourSchemeWindowPos",
  1010. new EditorColourSchemeWindowComponent(), editorColourSchemeWindow, false,
  1011. 500, 500, 500, 500, 500, 500);
  1012. }
  1013. void ProjucerApplication::showPIPCreatorWindow()
  1014. {
  1015. if (pipCreatorWindow != nullptr)
  1016. pipCreatorWindow->toFront (true);
  1017. else
  1018. new FloatingToolWindow ("PIP Creator", "pipCreatorWindowPos",
  1019. new PIPCreatorWindowComponent(), pipCreatorWindow, false,
  1020. 600, 750, 600, 750, 600, 750);
  1021. }
  1022. void ProjucerApplication::launchForumBrowser()
  1023. {
  1024. URL forumLink ("https://forum.juce.com/");
  1025. if (forumLink.isWellFormed())
  1026. forumLink.launchInDefaultBrowser();
  1027. }
  1028. void ProjucerApplication::launchModulesBrowser()
  1029. {
  1030. URL modulesLink ("https://docs.juce.com/master/modules.html");
  1031. if (modulesLink.isWellFormed())
  1032. modulesLink.launchInDefaultBrowser();
  1033. }
  1034. void ProjucerApplication::launchClassesBrowser()
  1035. {
  1036. URL classesLink ("https://docs.juce.com/master/classes.html");
  1037. if (classesLink.isWellFormed())
  1038. classesLink.launchInDefaultBrowser();
  1039. }
  1040. void ProjucerApplication::launchTutorialsBrowser()
  1041. {
  1042. URL tutorialsLink ("https://juce.com/learn/tutorials");
  1043. if (tutorialsLink.isWellFormed())
  1044. tutorialsLink.launchInDefaultBrowser();
  1045. }
  1046. //==============================================================================
  1047. struct FileWithTime
  1048. {
  1049. FileWithTime (const File& f) : file (f), time (f.getLastModificationTime()) {}
  1050. FileWithTime() {}
  1051. bool operator< (const FileWithTime& other) const { return time < other.time; }
  1052. bool operator== (const FileWithTime& other) const { return time == other.time; }
  1053. File file;
  1054. Time time;
  1055. };
  1056. void ProjucerApplication::deleteLogger()
  1057. {
  1058. const int maxNumLogFilesToKeep = 50;
  1059. Logger::setCurrentLogger (nullptr);
  1060. if (logger != nullptr)
  1061. {
  1062. auto logFiles = logger->getLogFile().getParentDirectory().findChildFiles (File::findFiles, false);
  1063. if (logFiles.size() > maxNumLogFilesToKeep)
  1064. {
  1065. Array<FileWithTime> files;
  1066. for (auto& f : logFiles)
  1067. files.addUsingDefaultSort (f);
  1068. for (int i = 0; i < files.size() - maxNumLogFilesToKeep; ++i)
  1069. files.getReference(i).file.deleteFile();
  1070. }
  1071. }
  1072. logger.reset();
  1073. }
  1074. PropertiesFile::Options ProjucerApplication::getPropertyFileOptionsFor (const String& filename, bool isProjectSettings)
  1075. {
  1076. PropertiesFile::Options options;
  1077. options.applicationName = filename;
  1078. options.filenameSuffix = "settings";
  1079. options.osxLibrarySubFolder = "Application Support";
  1080. #if JUCE_LINUX
  1081. options.folderName = "~/.config/Projucer";
  1082. #else
  1083. options.folderName = "Projucer";
  1084. #endif
  1085. if (isProjectSettings)
  1086. options.folderName += "/ProjectSettings";
  1087. return options;
  1088. }
  1089. void ProjucerApplication::updateAllBuildTabs()
  1090. {
  1091. for (int i = 0; i < mainWindowList.windows.size(); ++i)
  1092. if (ProjectContentComponent* p = mainWindowList.windows.getUnchecked(i)->getProjectContentComponent())
  1093. p->rebuildProjectTabs();
  1094. }
  1095. void ProjucerApplication::initCommandManager()
  1096. {
  1097. commandManager.reset (new ApplicationCommandManager());
  1098. commandManager->registerAllCommandsForTarget (this);
  1099. {
  1100. CodeDocument doc;
  1101. CppCodeEditorComponent ed (File(), doc);
  1102. commandManager->registerAllCommandsForTarget (&ed);
  1103. }
  1104. registerGUIEditorCommands();
  1105. }
  1106. void ProjucerApplication::deleteTemporaryFiles() const noexcept
  1107. {
  1108. auto tempDirectory = File::getSpecialLocation (File::SpecialLocationType::tempDirectory).getChildFile ("PIPs");
  1109. if (tempDirectory.exists())
  1110. tempDirectory.deleteRecursively();
  1111. }
  1112. void ProjucerApplication::setAnalyticsEnabled (bool enabled)
  1113. {
  1114. resetAnalytics();
  1115. if (enabled)
  1116. setupAnalytics();
  1117. }
  1118. void ProjucerApplication::resetAnalytics() noexcept
  1119. {
  1120. auto analyticsInstance = Analytics::getInstance();
  1121. analyticsInstance->setUserId ({});
  1122. analyticsInstance->setUserProperties ({});
  1123. analyticsInstance->getDestinations().clear();
  1124. }
  1125. void ProjucerApplication::setupAnalytics()
  1126. {
  1127. Analytics::getInstance()->addDestination (new ProjucerAnalyticsDestination());
  1128. auto deviceString = SystemStats::getDeviceIdentifiers().joinIntoString (":");
  1129. auto deviceIdentifier = String::toHexString (deviceString.hashCode64());
  1130. Analytics::getInstance()->setUserId (deviceIdentifier);
  1131. StringPairArray userData;
  1132. userData.set ("cd1", getApplicationName());
  1133. userData.set ("cd2", getApplicationVersion());
  1134. userData.set ("cd3", SystemStats::getDeviceDescription());
  1135. userData.set ("cd4", deviceString);
  1136. userData.set ("cd5", SystemStats::getOperatingSystemName());
  1137. Analytics::getInstance()->setUserProperties (userData);
  1138. }
  1139. void ProjucerApplication::showSetJUCEPathAlert()
  1140. {
  1141. auto& lf = Desktop::getInstance().getDefaultLookAndFeel();
  1142. pathAlert.reset (lf.createAlertWindow ("Set JUCE Path", "Your global JUCE path is invalid. This path is used to access the JUCE examples and demo project - "
  1143. "would you like to set it now?",
  1144. "Set path", "Cancel", "Don't ask again",
  1145. AlertWindow::WarningIcon, 3,
  1146. mainWindowList.getFrontmostWindow (false)));
  1147. pathAlert->enterModalState (true, ModalCallbackFunction::create ([this] (int retVal)
  1148. {
  1149. pathAlert.reset (nullptr);
  1150. if (retVal == 1)
  1151. showPathsWindow (true);
  1152. else if (retVal == 0)
  1153. settings->setDontAskAboutJUCEPathAgain();
  1154. }));
  1155. }
  1156. void ProjucerApplication::rescanJUCEPathModules()
  1157. {
  1158. File jucePath (getAppSettings().getStoredPath (Ids::defaultJuceModulePath).toString());
  1159. if (isRunningCommandLine)
  1160. jucePathModuleList.scanPaths ({ jucePath });
  1161. else
  1162. jucePathModuleList.scanPathsAsync ({ jucePath });
  1163. }
  1164. static Array<File> getSanitisedUserModulePaths()
  1165. {
  1166. Array<File> paths;
  1167. for (auto p : StringArray::fromTokens (getAppSettings().getStoredPath (Ids::defaultUserModulePath).toString(), ";", {}))
  1168. {
  1169. p = p.replace ("~", File::getSpecialLocation (File::userHomeDirectory).getFullPathName());
  1170. paths.add (File::createFileWithoutCheckingPath (p.trim()));
  1171. }
  1172. return paths;
  1173. }
  1174. void ProjucerApplication::rescanUserPathModules()
  1175. {
  1176. if (isRunningCommandLine)
  1177. userPathsModuleList.scanPaths (getSanitisedUserModulePaths());
  1178. else
  1179. userPathsModuleList.scanPathsAsync (getSanitisedUserModulePaths());
  1180. }
  1181. void ProjucerApplication::selectEditorColourSchemeWithName (const String& schemeName)
  1182. {
  1183. auto& appearanceSettings = getAppSettings().appearance;
  1184. auto schemes = appearanceSettings.getPresetSchemes();
  1185. auto schemeIndex = schemes.indexOf (schemeName);
  1186. if (schemeIndex >= 0)
  1187. setEditorColourScheme (schemeIndex, true);
  1188. }
  1189. void ProjucerApplication::setColourScheme (int index, bool saveSetting)
  1190. {
  1191. switch (index)
  1192. {
  1193. case 0: lookAndFeel.setColourScheme (LookAndFeel_V4::getDarkColourScheme()); break;
  1194. case 1: lookAndFeel.setColourScheme (LookAndFeel_V4::getGreyColourScheme()); break;
  1195. case 2: lookAndFeel.setColourScheme (LookAndFeel_V4::getLightColourScheme()); break;
  1196. default: break;
  1197. }
  1198. lookAndFeel.setupColours();
  1199. mainWindowList.sendLookAndFeelChange();
  1200. if (utf8Window != nullptr) utf8Window->sendLookAndFeelChange();
  1201. if (svgPathWindow != nullptr) svgPathWindow->sendLookAndFeelChange();
  1202. if (aboutWindow != nullptr) aboutWindow->sendLookAndFeelChange();
  1203. if (applicationUsageDataWindow != nullptr) applicationUsageDataWindow->sendLookAndFeelChange();
  1204. if (pathsWindow != nullptr) pathsWindow->sendLookAndFeelChange();
  1205. if (editorColourSchemeWindow != nullptr) editorColourSchemeWindow->sendLookAndFeelChange();
  1206. if (pipCreatorWindow != nullptr) pipCreatorWindow->sendLookAndFeelChange();
  1207. auto* mcm = ModalComponentManager::getInstance();
  1208. for (auto i = 0; i < mcm->getNumModalComponents(); ++i)
  1209. mcm->getModalComponent (i)->sendLookAndFeelChange();
  1210. if (saveSetting)
  1211. {
  1212. auto& properties = settings->getGlobalProperties();
  1213. properties.setValue ("COLOUR SCHEME", index);
  1214. }
  1215. selectedColourSchemeIndex = index;
  1216. getCommandManager().commandStatusChanged();
  1217. }
  1218. void ProjucerApplication::setEditorColourScheme (int index, bool saveSetting)
  1219. {
  1220. auto& appearanceSettings = getAppSettings().appearance;
  1221. auto schemes = appearanceSettings.getPresetSchemes();
  1222. index = jmin (index, schemes.size() - 1);
  1223. appearanceSettings.selectPresetScheme (index);
  1224. if (saveSetting)
  1225. {
  1226. auto& properties = settings->getGlobalProperties();
  1227. properties.setValue ("EDITOR COLOUR SCHEME", index);
  1228. }
  1229. selectedEditorColourSchemeIndex = index;
  1230. getCommandManager().commandStatusChanged();
  1231. }
  1232. bool ProjucerApplication::isEditorColourSchemeADefaultScheme (const StringArray& schemes, int editorColourSchemeIndex)
  1233. {
  1234. auto& schemeName = schemes[editorColourSchemeIndex];
  1235. return (schemeName == "Default (Dark)" || schemeName == "Default (Light)");
  1236. }
  1237. int ProjucerApplication::getEditorColourSchemeForGUIColourScheme (const StringArray& schemes, int guiColourSchemeIndex)
  1238. {
  1239. auto defaultDarkEditorIndex = schemes.indexOf ("Default (Dark)");
  1240. auto defaultLightEditorIndex = schemes.indexOf ("Default (Light)");
  1241. // Can't find default code editor colour schemes!
  1242. jassert (defaultDarkEditorIndex != -1 && defaultLightEditorIndex != -1);
  1243. return (guiColourSchemeIndex == 2 ? defaultLightEditorIndex : defaultDarkEditorIndex);
  1244. }
  1245. void ProjucerApplication::updateEditorColourSchemeIfNeeded()
  1246. {
  1247. auto& appearanceSettings = getAppSettings().appearance;
  1248. auto schemes = appearanceSettings.getPresetSchemes();
  1249. if (isEditorColourSchemeADefaultScheme (schemes, selectedEditorColourSchemeIndex))
  1250. setEditorColourScheme (getEditorColourSchemeForGUIColourScheme (schemes, selectedColourSchemeIndex), true);
  1251. }