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.

1439 lines
50KB

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