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.

1380 lines
48KB

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