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.

1584 lines
55KB

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