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.

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