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.

1522 lines
53KB

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