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.

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