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.

1515 lines
53KB

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