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.

1596 lines
57KB

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