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.

972 lines
34KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. void createGUIEditorMenu (PopupMenu&);
  20. void handleGUIEditorMenuCommand (int);
  21. void registerGUIEditorCommands();
  22. void attachCallback (Button& button, std::function<void()> callback)
  23. {
  24. struct ButtonCallback : public Button::Listener,
  25. private ComponentListener
  26. {
  27. ButtonCallback (Button& b, std::function<void()> f) : target (b), fn (f)
  28. {
  29. target.addListener (this);
  30. target.addComponentListener (this);
  31. }
  32. ~ButtonCallback()
  33. {
  34. target.removeListener (this);
  35. }
  36. void componentBeingDeleted (Component&) override { delete this; }
  37. void buttonClicked (Button*) override { fn(); }
  38. Button& target;
  39. std::function<void()> fn;
  40. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ButtonCallback)
  41. };
  42. new ButtonCallback (button, callback);
  43. }
  44. //==============================================================================
  45. struct ProjucerApplication::MainMenuModel : public MenuBarModel
  46. {
  47. MainMenuModel()
  48. {
  49. setApplicationCommandManagerToWatch (&getCommandManager());
  50. }
  51. StringArray getMenuBarNames() override
  52. {
  53. return getApp().getMenuNames();
  54. }
  55. PopupMenu getMenuForIndex (int /*topLevelMenuIndex*/, const String& menuName) override
  56. {
  57. PopupMenu menu;
  58. getApp().createMenu (menu, menuName);
  59. return menu;
  60. }
  61. void menuItemSelected (int menuItemID, int /*topLevelMenuIndex*/) override
  62. {
  63. getApp().handleMainMenuCommand (menuItemID);
  64. }
  65. };
  66. //==============================================================================
  67. ProjucerApplication::ProjucerApplication() : isRunningCommandLine (false)
  68. {
  69. }
  70. void ProjucerApplication::initialise (const String& commandLine)
  71. {
  72. if (commandLine.trimStart().startsWith ("--server"))
  73. {
  74. initialiseLogger ("Compiler_Log_");
  75. LookAndFeel::setDefaultLookAndFeel (&lookAndFeel);
  76. #if JUCE_MAC
  77. Process::setDockIconVisible (false);
  78. #endif
  79. server = createClangServer (commandLine);
  80. }
  81. else
  82. {
  83. initialiseLogger ("IDE_Log_");
  84. Logger::writeToLog (SystemStats::getOperatingSystemName());
  85. Logger::writeToLog ("CPU: " + String (SystemStats::getCpuSpeedInMegaherz())
  86. + "MHz Cores: " + String (SystemStats::getNumCpus())
  87. + " " + String (SystemStats::getMemorySizeInMegabytes()) + "MB");
  88. initialiseBasics();
  89. isRunningCommandLine = commandLine.isNotEmpty();
  90. licenseController = new LicenseController;
  91. licenseController->addLicenseStatusChangedCallback (this);
  92. if (isRunningCommandLine)
  93. {
  94. const int appReturnCode = performCommandLine (commandLine);
  95. if (appReturnCode != commandLineNotPerformed)
  96. {
  97. setApplicationReturnValue (appReturnCode);
  98. quit();
  99. return;
  100. }
  101. isRunningCommandLine = false;
  102. }
  103. if (sendCommandLineToPreexistingInstance())
  104. {
  105. DBG ("Another instance is running - quitting...");
  106. quit();
  107. return;
  108. }
  109. openDocumentManager.registerType (new ProjucerAppClasses::LiveBuildCodeEditorDocument::Type(), 2);
  110. childProcessCache = new ChildProcessCache();
  111. initCommandManager();
  112. menuModel = new MainMenuModel();
  113. settings->appearance.refreshPresetSchemeList();
  114. setColourScheme (settings->getGlobalProperties().getIntValue ("COLOUR SCHEME"), false);
  115. setEditorColourScheme (settings->getGlobalProperties().getIntValue ("EDITOR COLOUR SCHEME"), false);
  116. updateEditorColourSchemeIfNeeded();
  117. // do further initialisation in a moment when the message loop has started
  118. triggerAsyncUpdate();
  119. }
  120. }
  121. void ProjucerApplication::initialiseBasics()
  122. {
  123. LookAndFeel::setDefaultLookAndFeel (&lookAndFeel);
  124. settings = new StoredSettings();
  125. ImageCache::setCacheTimeout (30 * 1000);
  126. icons = new Icons();
  127. tooltipWindow.setMillisecondsBeforeTipAppears (1200);
  128. }
  129. bool ProjucerApplication::initialiseLogger (const char* filePrefix)
  130. {
  131. if (logger == nullptr)
  132. {
  133. #if JUCE_LINUX
  134. String folder = "~/.config/Projucer/Logs";
  135. #else
  136. String folder = "com.juce.projucer";
  137. #endif
  138. logger = FileLogger::createDateStampedLogger (folder, filePrefix, ".txt",
  139. getApplicationName() + " " + getApplicationVersion()
  140. + " --- Build date: " __DATE__);
  141. Logger::setCurrentLogger (logger);
  142. }
  143. return logger != nullptr;
  144. }
  145. void ProjucerApplication::handleAsyncUpdate()
  146. {
  147. if (licenseController != nullptr)
  148. licenseController->startWebviewIfNeeded();
  149. #if JUCE_MAC
  150. PopupMenu extraAppleMenuItems;
  151. createExtraAppleMenuItems (extraAppleMenuItems);
  152. // workaround broken "Open Recent" submenu: not passing the
  153. // submenu's title here avoids the defect in JuceMainMenuHandler::addMenuItem
  154. MenuBarModel::setMacMainMenu (menuModel, &extraAppleMenuItems); //, "Open Recent");
  155. #endif
  156. versionChecker = new LatestVersionChecker();
  157. }
  158. void ProjucerApplication::initialiseWindows (const String& commandLine)
  159. {
  160. const String commandLineWithoutNSDebug (commandLine.replace ("-NSDocumentRevisionsDebugMode YES", StringRef()));
  161. if (commandLineWithoutNSDebug.trim().isNotEmpty() && ! commandLineWithoutNSDebug.trim().startsWithChar ('-'))
  162. anotherInstanceStarted (commandLine);
  163. else
  164. mainWindowList.reopenLastProjects();
  165. mainWindowList.createWindowIfNoneAreOpen();
  166. if (licenseController->getState().applicationUsageDataState == LicenseState::ApplicationUsageData::notChosenYet)
  167. showApplicationUsageDataAgreementPopup();
  168. }
  169. void ProjucerApplication::shutdown()
  170. {
  171. if (server != nullptr)
  172. {
  173. destroyClangServer (server);
  174. Logger::writeToLog ("Server shutdown cleanly");
  175. }
  176. versionChecker.reset();
  177. utf8Window.reset();
  178. svgPathWindow.reset();
  179. aboutWindow.reset();
  180. pathsWindow.reset();
  181. editorColourSchemeWindow.reset();
  182. if (licenseController != nullptr)
  183. {
  184. licenseController->removeLicenseStatusChangedCallback (this);
  185. licenseController.reset();
  186. }
  187. mainWindowList.forceCloseAllWindows();
  188. openDocumentManager.clear();
  189. childProcessCache.reset();
  190. #if JUCE_MAC
  191. MenuBarModel::setMacMainMenu (nullptr);
  192. #endif
  193. menuModel.reset();
  194. commandManager.reset();
  195. settings.reset();
  196. LookAndFeel::setDefaultLookAndFeel (nullptr);
  197. if (! isRunningCommandLine)
  198. Logger::writeToLog ("Shutdown");
  199. deleteLogger();
  200. }
  201. struct AsyncQuitRetrier : private Timer
  202. {
  203. AsyncQuitRetrier() { startTimer (500); }
  204. void timerCallback() override
  205. {
  206. stopTimer();
  207. delete this;
  208. if (auto* app = JUCEApplicationBase::getInstance())
  209. app->systemRequestedQuit();
  210. }
  211. JUCE_DECLARE_NON_COPYABLE (AsyncQuitRetrier)
  212. };
  213. void ProjucerApplication::systemRequestedQuit()
  214. {
  215. if (server != nullptr)
  216. {
  217. sendQuitMessageToIDE (server);
  218. }
  219. else if (ModalComponentManager::getInstance()->cancelAllModalComponents())
  220. {
  221. new AsyncQuitRetrier();
  222. }
  223. else
  224. {
  225. if (closeAllMainWindows())
  226. quit();
  227. }
  228. }
  229. //==============================================================================
  230. void ProjucerApplication::licenseStateChanged (const LicenseState& state)
  231. {
  232. #if ! JUCER_ENABLE_GPL_MODE
  233. if (state.type != LicenseState::Type::notLoggedIn
  234. && state.type != LicenseState::Type::noLicenseChosenYet)
  235. #else
  236. ignoreUnused (state);
  237. #endif
  238. {
  239. initialiseWindows (getCommandLineParameters());
  240. }
  241. }
  242. void ProjucerApplication::doLogout()
  243. {
  244. if (licenseController != nullptr)
  245. {
  246. const LicenseState& state = licenseController->getState();
  247. if (state.type != LicenseState::Type::notLoggedIn && closeAllMainWindows())
  248. licenseController->logout();
  249. }
  250. }
  251. //==============================================================================
  252. String ProjucerApplication::getVersionDescription() const
  253. {
  254. String s;
  255. const Time buildDate (Time::getCompilationDate());
  256. s << "Projucer " << ProjectInfo::versionString
  257. << newLine
  258. << "Build date: " << buildDate.getDayOfMonth()
  259. << " " << Time::getMonthName (buildDate.getMonth(), true)
  260. << " " << buildDate.getYear();
  261. return s;
  262. }
  263. void ProjucerApplication::anotherInstanceStarted (const String& commandLine)
  264. {
  265. if (server == nullptr && ! commandLine.trim().startsWithChar ('-'))
  266. openFile (File (commandLine.unquoted()));
  267. }
  268. ProjucerApplication& ProjucerApplication::getApp()
  269. {
  270. ProjucerApplication* const app = dynamic_cast<ProjucerApplication*> (JUCEApplication::getInstance());
  271. jassert (app != nullptr);
  272. return *app;
  273. }
  274. ApplicationCommandManager& ProjucerApplication::getCommandManager()
  275. {
  276. ApplicationCommandManager* cm = ProjucerApplication::getApp().commandManager;
  277. jassert (cm != nullptr);
  278. return *cm;
  279. }
  280. //==============================================================================
  281. enum
  282. {
  283. recentProjectsBaseID = 100,
  284. activeDocumentsBaseID = 300,
  285. colourSchemeBaseID = 1000,
  286. codeEditorColourSchemeBaseID = 2000,
  287. };
  288. MenuBarModel* ProjucerApplication::getMenuModel()
  289. {
  290. return menuModel.get();
  291. }
  292. StringArray ProjucerApplication::getMenuNames()
  293. {
  294. return { "File", "Edit", "View", "Build", "Window", "GUI Editor", "Tools" };
  295. }
  296. void ProjucerApplication::createMenu (PopupMenu& menu, const String& menuName)
  297. {
  298. if (menuName == "File") createFileMenu (menu);
  299. else if (menuName == "Edit") createEditMenu (menu);
  300. else if (menuName == "View") createViewMenu (menu);
  301. else if (menuName == "Build") createBuildMenu (menu);
  302. else if (menuName == "Window") createWindowMenu (menu);
  303. else if (menuName == "Tools") createToolsMenu (menu);
  304. else if (menuName == "GUI Editor") createGUIEditorMenu (menu);
  305. else jassertfalse; // names have changed?
  306. }
  307. void ProjucerApplication::createFileMenu (PopupMenu& menu)
  308. {
  309. menu.addCommandItem (commandManager, CommandIDs::newProject);
  310. menu.addSeparator();
  311. menu.addCommandItem (commandManager, CommandIDs::open);
  312. PopupMenu recentFiles;
  313. settings->recentFiles.createPopupMenuItems (recentFiles, recentProjectsBaseID, true, true);
  314. menu.addSubMenu ("Open Recent", recentFiles);
  315. menu.addSeparator();
  316. menu.addCommandItem (commandManager, CommandIDs::closeDocument);
  317. menu.addCommandItem (commandManager, CommandIDs::saveDocument);
  318. menu.addCommandItem (commandManager, CommandIDs::saveDocumentAs);
  319. menu.addCommandItem (commandManager, CommandIDs::saveAll);
  320. menu.addSeparator();
  321. menu.addCommandItem (commandManager, CommandIDs::closeProject);
  322. menu.addCommandItem (commandManager, CommandIDs::saveProject);
  323. menu.addSeparator();
  324. menu.addCommandItem (commandManager, CommandIDs::openInIDE);
  325. menu.addCommandItem (commandManager, CommandIDs::saveAndOpenInIDE);
  326. menu.addSeparator();
  327. #if ! JUCER_ENABLE_GPL_MODE
  328. menu.addCommandItem (commandManager, CommandIDs::loginLogout);
  329. #endif
  330. #if ! JUCE_MAC
  331. menu.addCommandItem (commandManager, CommandIDs::showAboutWindow);
  332. menu.addCommandItem (commandManager, CommandIDs::showAppUsageWindow);
  333. menu.addCommandItem (commandManager, CommandIDs::showGlobalPathsWindow);
  334. menu.addSeparator();
  335. menu.addCommandItem (commandManager, StandardApplicationCommandIDs::quit);
  336. #endif
  337. }
  338. void ProjucerApplication::createEditMenu (PopupMenu& menu)
  339. {
  340. menu.addCommandItem (commandManager, StandardApplicationCommandIDs::undo);
  341. menu.addCommandItem (commandManager, StandardApplicationCommandIDs::redo);
  342. menu.addSeparator();
  343. menu.addCommandItem (commandManager, StandardApplicationCommandIDs::cut);
  344. menu.addCommandItem (commandManager, StandardApplicationCommandIDs::copy);
  345. menu.addCommandItem (commandManager, StandardApplicationCommandIDs::paste);
  346. menu.addCommandItem (commandManager, StandardApplicationCommandIDs::del);
  347. menu.addCommandItem (commandManager, StandardApplicationCommandIDs::selectAll);
  348. menu.addCommandItem (commandManager, StandardApplicationCommandIDs::deselectAll);
  349. menu.addSeparator();
  350. menu.addCommandItem (commandManager, CommandIDs::showFindPanel);
  351. menu.addCommandItem (commandManager, CommandIDs::findSelection);
  352. menu.addCommandItem (commandManager, CommandIDs::findNext);
  353. menu.addCommandItem (commandManager, CommandIDs::findPrevious);
  354. }
  355. void ProjucerApplication::createViewMenu (PopupMenu& menu)
  356. {
  357. menu.addCommandItem (commandManager, CommandIDs::showProjectSettings);
  358. menu.addCommandItem (commandManager, CommandIDs::showProjectTab);
  359. menu.addCommandItem (commandManager, CommandIDs::showBuildTab);
  360. menu.addCommandItem (commandManager, CommandIDs::showFileExplorerPanel);
  361. menu.addCommandItem (commandManager, CommandIDs::showModulesPanel);
  362. menu.addCommandItem (commandManager, CommandIDs::showExportersPanel);
  363. menu.addCommandItem (commandManager, CommandIDs::showExporterSettings);
  364. menu.addSeparator();
  365. createColourSchemeItems (menu);
  366. }
  367. void ProjucerApplication::createBuildMenu (PopupMenu& menu)
  368. {
  369. menu.addCommandItem (commandManager, CommandIDs::toggleBuildEnabled);
  370. menu.addCommandItem (commandManager, CommandIDs::buildNow);
  371. menu.addCommandItem (commandManager, CommandIDs::toggleContinuousBuild);
  372. menu.addSeparator();
  373. menu.addCommandItem (commandManager, CommandIDs::launchApp);
  374. menu.addCommandItem (commandManager, CommandIDs::killApp);
  375. menu.addCommandItem (commandManager, CommandIDs::cleanAll);
  376. menu.addSeparator();
  377. menu.addCommandItem (commandManager, CommandIDs::reinstantiateComp);
  378. menu.addCommandItem (commandManager, CommandIDs::showWarnings);
  379. menu.addSeparator();
  380. menu.addCommandItem (commandManager, CommandIDs::nextError);
  381. menu.addCommandItem (commandManager, CommandIDs::prevError);
  382. }
  383. void ProjucerApplication::createColourSchemeItems (PopupMenu& menu)
  384. {
  385. PopupMenu colourSchemes;
  386. colourSchemes.addItem (colourSchemeBaseID + 0, "Dark", true, selectedColourSchemeIndex == 0);
  387. colourSchemes.addItem (colourSchemeBaseID + 1, "Grey", true, selectedColourSchemeIndex == 1);
  388. colourSchemes.addItem (colourSchemeBaseID + 2, "Light", true, selectedColourSchemeIndex == 2);
  389. menu.addSubMenu ("Colour Scheme", colourSchemes);
  390. //==========================================================================
  391. PopupMenu editorColourSchemes;
  392. auto& appearanceSettings = getAppSettings().appearance;
  393. appearanceSettings.refreshPresetSchemeList();
  394. auto schemes = appearanceSettings.getPresetSchemes();
  395. auto i = 0;
  396. for (auto s : schemes)
  397. {
  398. editorColourSchemes.addItem (codeEditorColourSchemeBaseID + i, s,
  399. editorColourSchemeWindow == nullptr,
  400. selectedEditorColourSchemeIndex == i);
  401. ++i;
  402. }
  403. numEditorColourSchemes = i;
  404. editorColourSchemes.addSeparator();
  405. editorColourSchemes.addItem (codeEditorColourSchemeBaseID + numEditorColourSchemes,
  406. "Create...", editorColourSchemeWindow == nullptr);
  407. menu.addSubMenu ("Editor Colour Scheme", editorColourSchemes);
  408. }
  409. void ProjucerApplication::createWindowMenu (PopupMenu& menu)
  410. {
  411. menu.addCommandItem (commandManager, CommandIDs::goToPreviousWindow);
  412. menu.addCommandItem (commandManager, CommandIDs::goToNextWindow);
  413. menu.addCommandItem (commandManager, CommandIDs::closeWindow);
  414. menu.addSeparator();
  415. menu.addCommandItem (commandManager, CommandIDs::goToPreviousDoc);
  416. menu.addCommandItem (commandManager, CommandIDs::goToNextDoc);
  417. menu.addCommandItem (commandManager, CommandIDs::goToCounterpart);
  418. menu.addSeparator();
  419. const int numDocs = jmin (50, openDocumentManager.getNumOpenDocuments());
  420. for (int i = 0; i < numDocs; ++i)
  421. {
  422. OpenDocumentManager::Document* doc = openDocumentManager.getOpenDocument(i);
  423. menu.addItem (activeDocumentsBaseID + i, doc->getName());
  424. }
  425. menu.addSeparator();
  426. menu.addCommandItem (commandManager, CommandIDs::closeAllDocuments);
  427. }
  428. void ProjucerApplication::createToolsMenu (PopupMenu& menu)
  429. {
  430. menu.addCommandItem (commandManager, CommandIDs::showUTF8Tool);
  431. menu.addCommandItem (commandManager, CommandIDs::showSVGPathTool);
  432. menu.addCommandItem (commandManager, CommandIDs::showTranslationTool);
  433. }
  434. void ProjucerApplication::createExtraAppleMenuItems (PopupMenu& menu)
  435. {
  436. menu.addCommandItem (commandManager, CommandIDs::showAboutWindow);
  437. menu.addCommandItem (commandManager, CommandIDs::showAppUsageWindow);
  438. menu.addSeparator();
  439. menu.addCommandItem (commandManager, CommandIDs::showGlobalPathsWindow);
  440. }
  441. void ProjucerApplication::handleMainMenuCommand (int menuItemID)
  442. {
  443. if (menuItemID >= recentProjectsBaseID && menuItemID < (recentProjectsBaseID + 100))
  444. {
  445. // open a file from the "recent files" menu
  446. openFile (settings->recentFiles.getFile (menuItemID - recentProjectsBaseID));
  447. }
  448. else if (menuItemID >= activeDocumentsBaseID && menuItemID < (activeDocumentsBaseID + 200))
  449. {
  450. if (OpenDocumentManager::Document* doc = openDocumentManager.getOpenDocument (menuItemID - activeDocumentsBaseID))
  451. mainWindowList.openDocument (doc, true);
  452. else
  453. jassertfalse;
  454. }
  455. else if (menuItemID >= colourSchemeBaseID && menuItemID < (colourSchemeBaseID + 3))
  456. {
  457. setColourScheme (menuItemID - colourSchemeBaseID, true);
  458. updateEditorColourSchemeIfNeeded();
  459. }
  460. else if (menuItemID >= codeEditorColourSchemeBaseID && menuItemID < (codeEditorColourSchemeBaseID + numEditorColourSchemes))
  461. {
  462. setEditorColourScheme (menuItemID - codeEditorColourSchemeBaseID, true);
  463. }
  464. else if (menuItemID == (codeEditorColourSchemeBaseID + numEditorColourSchemes))
  465. {
  466. showEditorColourSchemeWindow();
  467. }
  468. else
  469. {
  470. handleGUIEditorMenuCommand (menuItemID);
  471. }
  472. }
  473. //==============================================================================
  474. void ProjucerApplication::getAllCommands (Array <CommandID>& commands)
  475. {
  476. JUCEApplication::getAllCommands (commands);
  477. const CommandID ids[] = { CommandIDs::newProject,
  478. CommandIDs::open,
  479. CommandIDs::closeAllDocuments,
  480. CommandIDs::saveAll,
  481. CommandIDs::showGlobalPathsWindow,
  482. CommandIDs::showUTF8Tool,
  483. CommandIDs::showSVGPathTool,
  484. CommandIDs::showAboutWindow,
  485. CommandIDs::showAppUsageWindow,
  486. CommandIDs::loginLogout };
  487. commands.addArray (ids, numElementsInArray (ids));
  488. }
  489. void ProjucerApplication::getCommandInfo (CommandID commandID, ApplicationCommandInfo& result)
  490. {
  491. switch (commandID)
  492. {
  493. case CommandIDs::newProject:
  494. result.setInfo ("New Project...", "Creates a new Jucer project", CommandCategories::general, 0);
  495. result.defaultKeypresses.add (KeyPress ('n', ModifierKeys::commandModifier, 0));
  496. break;
  497. case CommandIDs::open:
  498. result.setInfo ("Open...", "Opens a Jucer project", CommandCategories::general, 0);
  499. result.defaultKeypresses.add (KeyPress ('o', ModifierKeys::commandModifier, 0));
  500. break;
  501. case CommandIDs::showGlobalPathsWindow:
  502. result.setInfo ("Global Search Paths...",
  503. "Shows the window to change the global search paths.",
  504. CommandCategories::general, 0);
  505. break;
  506. case CommandIDs::closeAllDocuments:
  507. result.setInfo ("Close All Documents", "Closes all open documents", CommandCategories::general, 0);
  508. result.setActive (openDocumentManager.getNumOpenDocuments() > 0);
  509. break;
  510. case CommandIDs::saveAll:
  511. result.setInfo ("Save All", "Saves all open documents", CommandCategories::general, 0);
  512. result.defaultKeypresses.add (KeyPress ('s', ModifierKeys::commandModifier | ModifierKeys::altModifier, 0));
  513. break;
  514. case CommandIDs::showUTF8Tool:
  515. result.setInfo ("UTF-8 String-Literal Helper", "Shows the UTF-8 string literal utility", CommandCategories::general, 0);
  516. break;
  517. case CommandIDs::showSVGPathTool:
  518. result.setInfo ("SVG Path Converter", "Shows the SVG->Path data conversion utility", CommandCategories::general, 0);
  519. break;
  520. case CommandIDs::showAboutWindow:
  521. result.setInfo ("About Projucer", "Shows the Projucer's 'About' page.", CommandCategories::general, 0);
  522. break;
  523. case CommandIDs::showAppUsageWindow:
  524. result.setInfo ("Application Usage Data", "Shows the application usage data agreement window", CommandCategories::general, 0);
  525. break;
  526. case CommandIDs::loginLogout:
  527. {
  528. bool isLoggedIn = false;
  529. String username;
  530. if (licenseController != nullptr)
  531. {
  532. const LicenseState state = licenseController->getState();
  533. isLoggedIn = (state.type != LicenseState::Type::notLoggedIn && state.type != LicenseState::Type::GPL);
  534. username = state.username;
  535. }
  536. result.setInfo (isLoggedIn
  537. ? String ("Sign out ") + username + "..."
  538. : String ("Sign in..."),
  539. "Log out of your JUCE account", CommandCategories::general, 0);
  540. }
  541. break;
  542. default:
  543. JUCEApplication::getCommandInfo (commandID, result);
  544. break;
  545. }
  546. }
  547. bool ProjucerApplication::perform (const InvocationInfo& info)
  548. {
  549. switch (info.commandID)
  550. {
  551. case CommandIDs::newProject: createNewProject(); break;
  552. case CommandIDs::open: askUserToOpenFile(); break;
  553. case CommandIDs::saveAll: openDocumentManager.saveAll(); break;
  554. case CommandIDs::closeAllDocuments: closeAllDocuments (true); break;
  555. case CommandIDs::showUTF8Tool: showUTF8ToolWindow(); break;
  556. case CommandIDs::showSVGPathTool: showSVGPathDataToolWindow(); break;
  557. case CommandIDs::showGlobalPathsWindow: showPathsWindow(); break;
  558. case CommandIDs::showAboutWindow: showAboutWindow(); break;
  559. case CommandIDs::showAppUsageWindow: showApplicationUsageDataAgreementPopup(); break;
  560. case CommandIDs::loginLogout: doLogout(); break;
  561. default: return JUCEApplication::perform (info);
  562. }
  563. return true;
  564. }
  565. //==============================================================================
  566. void ProjucerApplication::createNewProject()
  567. {
  568. MainWindow* mw = mainWindowList.getOrCreateEmptyWindow();
  569. mw->showNewProjectWizard();
  570. mainWindowList.avoidSuperimposedWindows (mw);
  571. }
  572. void ProjucerApplication::updateNewlyOpenedProject (Project& p)
  573. {
  574. LiveBuildProjectSettings::updateNewlyOpenedProject (p);
  575. }
  576. void ProjucerApplication::askUserToOpenFile()
  577. {
  578. FileChooser fc ("Open File");
  579. if (fc.browseForFileToOpen())
  580. openFile (fc.getResult());
  581. }
  582. bool ProjucerApplication::openFile (const File& file)
  583. {
  584. return mainWindowList.openFile (file);
  585. }
  586. bool ProjucerApplication::closeAllDocuments (bool askUserToSave)
  587. {
  588. return openDocumentManager.closeAll (askUserToSave);
  589. }
  590. bool ProjucerApplication::closeAllMainWindows()
  591. {
  592. return server != nullptr || mainWindowList.askAllWindowsToClose();
  593. }
  594. //==============================================================================
  595. void ProjucerApplication::showUTF8ToolWindow()
  596. {
  597. if (utf8Window != nullptr)
  598. utf8Window->toFront (true);
  599. else
  600. new FloatingToolWindow ("UTF-8 String Literal Converter",
  601. "utf8WindowPos",
  602. new UTF8Component(), utf8Window, true,
  603. 500, 500, 300, 300, 1000, 1000);
  604. }
  605. void ProjucerApplication::showSVGPathDataToolWindow()
  606. {
  607. if (svgPathWindow != nullptr)
  608. svgPathWindow->toFront (true);
  609. else
  610. new FloatingToolWindow ("SVG Path Converter",
  611. "svgPathWindowPos",
  612. new SVGPathDataComponent(), svgPathWindow, true,
  613. 500, 500, 300, 300, 1000, 1000);
  614. }
  615. void ProjucerApplication::showAboutWindow()
  616. {
  617. if (aboutWindow != nullptr)
  618. aboutWindow->toFront (true);
  619. else
  620. new FloatingToolWindow ({}, {}, new AboutWindowComponent(),
  621. aboutWindow, false,
  622. 500, 300, 500, 300, 500, 300);
  623. }
  624. void ProjucerApplication::showApplicationUsageDataAgreementPopup()
  625. {
  626. if (applicationUsageDataWindow != nullptr)
  627. applicationUsageDataWindow->toFront (true);
  628. else
  629. new FloatingToolWindow ("Application Usage Analytics",
  630. {}, new ApplicationUsageDataWindowComponent (isPaidOrGPL()),
  631. applicationUsageDataWindow, false,
  632. 400, 300, 400, 300, 400, 300);
  633. }
  634. void ProjucerApplication::dismissApplicationUsageDataAgreementPopup()
  635. {
  636. if (applicationUsageDataWindow != nullptr)
  637. applicationUsageDataWindow.reset();
  638. }
  639. void ProjucerApplication::showPathsWindow()
  640. {
  641. if (pathsWindow != nullptr)
  642. pathsWindow->toFront (true);
  643. else
  644. new FloatingToolWindow ("Global Search Paths",
  645. "pathsWindowPos",
  646. new GlobalSearchPathsWindowComponent(), pathsWindow, false,
  647. 600, 500, 600, 500, 600, 500);
  648. }
  649. void ProjucerApplication::showEditorColourSchemeWindow()
  650. {
  651. if (editorColourSchemeWindow != nullptr)
  652. editorColourSchemeWindow->toFront (true);
  653. else
  654. {
  655. new FloatingToolWindow ("Editor Colour Scheme",
  656. "editorColourSchemeWindowPos",
  657. new EditorColourSchemeWindowComponent(),
  658. editorColourSchemeWindow,
  659. false,
  660. 500, 500, 500, 500, 500, 500);
  661. }
  662. }
  663. //==============================================================================
  664. struct FileWithTime
  665. {
  666. FileWithTime (const File& f) : file (f), time (f.getLastModificationTime()) {}
  667. FileWithTime() {}
  668. bool operator< (const FileWithTime& other) const { return time < other.time; }
  669. bool operator== (const FileWithTime& other) const { return time == other.time; }
  670. File file;
  671. Time time;
  672. };
  673. void ProjucerApplication::deleteLogger()
  674. {
  675. const int maxNumLogFilesToKeep = 50;
  676. Logger::setCurrentLogger (nullptr);
  677. if (logger != nullptr)
  678. {
  679. Array<File> logFiles;
  680. logger->getLogFile().getParentDirectory().findChildFiles (logFiles, File::findFiles, false);
  681. if (logFiles.size() > maxNumLogFilesToKeep)
  682. {
  683. Array <FileWithTime> files;
  684. for (int i = 0; i < logFiles.size(); ++i)
  685. files.addUsingDefaultSort (logFiles.getReference(i));
  686. for (int i = 0; i < files.size() - maxNumLogFilesToKeep; ++i)
  687. files.getReference(i).file.deleteFile();
  688. }
  689. }
  690. logger.reset();
  691. }
  692. PropertiesFile::Options ProjucerApplication::getPropertyFileOptionsFor (const String& filename, bool isProjectSettings)
  693. {
  694. PropertiesFile::Options options;
  695. options.applicationName = filename;
  696. options.filenameSuffix = "settings";
  697. options.osxLibrarySubFolder = "Application Support";
  698. #if JUCE_LINUX
  699. options.folderName = "~/.config/Projucer";
  700. #else
  701. options.folderName = "Projucer";
  702. #endif
  703. if (isProjectSettings)
  704. options.folderName += "/ProjectSettings";
  705. return options;
  706. }
  707. void ProjucerApplication::updateAllBuildTabs()
  708. {
  709. for (int i = 0; i < mainWindowList.windows.size(); ++i)
  710. if (ProjectContentComponent* p = mainWindowList.windows.getUnchecked(i)->getProjectContentComponent())
  711. p->rebuildProjectTabs();
  712. }
  713. void ProjucerApplication::initCommandManager()
  714. {
  715. commandManager = new ApplicationCommandManager();
  716. commandManager->registerAllCommandsForTarget (this);
  717. {
  718. CodeDocument doc;
  719. CppCodeEditorComponent ed (File(), doc);
  720. commandManager->registerAllCommandsForTarget (&ed);
  721. }
  722. registerGUIEditorCommands();
  723. }
  724. void ProjucerApplication::selectEditorColourSchemeWithName (const String& schemeName)
  725. {
  726. auto& appearanceSettings = getAppSettings().appearance;
  727. auto schemes = appearanceSettings.getPresetSchemes();
  728. auto schemeIndex = schemes.indexOf (schemeName);
  729. if (schemeIndex >= 0)
  730. setEditorColourScheme (schemeIndex, true);
  731. }
  732. void ProjucerApplication::setColourScheme (int index, bool saveSetting)
  733. {
  734. switch (index)
  735. {
  736. case 0: lookAndFeel.setColourScheme (LookAndFeel_V4::getDarkColourScheme()); break;
  737. case 1: lookAndFeel.setColourScheme (LookAndFeel_V4::getGreyColourScheme()); break;
  738. case 2: lookAndFeel.setColourScheme (LookAndFeel_V4::getLightColourScheme()); break;
  739. default: break;
  740. }
  741. lookAndFeel.setupColours();
  742. mainWindowList.sendLookAndFeelChange();
  743. if (utf8Window != nullptr) utf8Window->sendLookAndFeelChange();
  744. if (svgPathWindow != nullptr) svgPathWindow->sendLookAndFeelChange();
  745. if (aboutWindow != nullptr) aboutWindow->sendLookAndFeelChange();
  746. if (applicationUsageDataWindow != nullptr) applicationUsageDataWindow->sendLookAndFeelChange();
  747. if (pathsWindow != nullptr) pathsWindow->sendLookAndFeelChange();
  748. if (editorColourSchemeWindow != nullptr) editorColourSchemeWindow->sendLookAndFeelChange();
  749. auto* mcm = ModalComponentManager::getInstance();
  750. for (auto i = 0; i < mcm->getNumModalComponents(); ++i)
  751. mcm->getModalComponent (i)->sendLookAndFeelChange();
  752. if (saveSetting)
  753. {
  754. auto& properties = settings->getGlobalProperties();
  755. properties.setValue ("COLOUR SCHEME", index);
  756. }
  757. selectedColourSchemeIndex = index;
  758. getCommandManager().commandStatusChanged();
  759. }
  760. void ProjucerApplication::setEditorColourScheme (int index, bool saveSetting)
  761. {
  762. auto& appearanceSettings = getAppSettings().appearance;
  763. auto schemes = appearanceSettings.getPresetSchemes();
  764. index = jmin (index, schemes.size() - 1);
  765. appearanceSettings.selectPresetScheme (index);
  766. if (saveSetting)
  767. {
  768. auto& properties = settings->getGlobalProperties();
  769. properties.setValue ("EDITOR COLOUR SCHEME", index);
  770. }
  771. selectedEditorColourSchemeIndex = index;
  772. getCommandManager().commandStatusChanged();
  773. }
  774. bool ProjucerApplication::isEditorColourSchemeADefaultScheme (const StringArray& schemes, int editorColourSchemeIndex)
  775. {
  776. auto& schemeName = schemes[editorColourSchemeIndex];
  777. return (schemeName == "Default (Dark)" || schemeName == "Default (Light)");
  778. }
  779. int ProjucerApplication::getEditorColourSchemeForGUIColourScheme (const StringArray& schemes, int guiColourSchemeIndex)
  780. {
  781. auto defaultDarkEditorIndex = schemes.indexOf ("Default (Dark)");
  782. auto defaultLightEditorIndex = schemes.indexOf ("Default (Light)");
  783. // Can't find default code editor colour schemes!
  784. jassert (defaultDarkEditorIndex != -1 && defaultLightEditorIndex != -1);
  785. return (guiColourSchemeIndex == 2 ? defaultLightEditorIndex : defaultDarkEditorIndex);
  786. }
  787. void ProjucerApplication::updateEditorColourSchemeIfNeeded()
  788. {
  789. auto& appearanceSettings = getAppSettings().appearance;
  790. auto schemes = appearanceSettings.getPresetSchemes();
  791. if (isEditorColourSchemeADefaultScheme (schemes, selectedEditorColourSchemeIndex))
  792. setEditorColourScheme (getEditorColourSchemeForGUIColourScheme (schemes, selectedColourSchemeIndex), true);
  793. }