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.

1611 lines
57KB

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