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.

1489 lines
53KB

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