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.

1510 lines
54KB

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