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.

1544 lines
55KB

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