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.

627 lines
22KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2015 - ROLI Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. #ifndef JUCER_APPLICATION_H_INCLUDED
  18. #define JUCER_APPLICATION_H_INCLUDED
  19. #include "../jucer_Headers.h"
  20. #include "jucer_MainWindow.h"
  21. #include "jucer_CommandLine.h"
  22. #include "../Project/jucer_Module.h"
  23. #include "jucer_AutoUpdater.h"
  24. #include "../Code Editor/jucer_SourceCodeEditor.h"
  25. void createGUIEditorMenu (PopupMenu&);
  26. void handleGUIEditorMenuCommand (int);
  27. void registerGUIEditorCommands();
  28. //==============================================================================
  29. class IntrojucerApp : public JUCEApplication
  30. {
  31. public:
  32. //==============================================================================
  33. IntrojucerApp() : isRunningCommandLine (false) {}
  34. //==============================================================================
  35. void initialise (const String& commandLine) override
  36. {
  37. initialiseBasics();
  38. if (commandLine.isNotEmpty())
  39. {
  40. const int appReturnCode = performCommandLine (commandLine);
  41. if (appReturnCode != commandLineNotPerformed)
  42. {
  43. isRunningCommandLine = true;
  44. setApplicationReturnValue (appReturnCode);
  45. quit();
  46. return;
  47. }
  48. }
  49. if (sendCommandLineToPreexistingInstance())
  50. {
  51. DBG ("Another instance is running - quitting...");
  52. quit();
  53. return;
  54. }
  55. if (! initialiseLog())
  56. {
  57. quit();
  58. return;
  59. }
  60. initCommandManager();
  61. menuModel = new MainMenuModel();
  62. settings->appearance.refreshPresetSchemeList();
  63. initialiseWindows (commandLine);
  64. #if JUCE_MAC
  65. MenuBarModel::setMacMainMenu (menuModel, nullptr, "Open Recent");
  66. #endif
  67. versionChecker = createVersionChecker();
  68. }
  69. void initialiseBasics()
  70. {
  71. LookAndFeel::setDefaultLookAndFeel (&lookAndFeel);
  72. settings = new StoredSettings();
  73. ImageCache::setCacheTimeout (30 * 1000);
  74. icons = new Icons();
  75. }
  76. virtual bool initialiseLog()
  77. {
  78. return initialiseLogger ("log_");
  79. }
  80. bool initialiseLogger (const char* filePrefix)
  81. {
  82. if (logger == nullptr)
  83. {
  84. logger = FileLogger::createDateStampedLogger (getLogFolderName(), filePrefix, ".txt",
  85. getApplicationName() + " " + getApplicationVersion()
  86. + " --- Build date: " __DATE__);
  87. Logger::setCurrentLogger (logger);
  88. }
  89. return logger != nullptr;
  90. }
  91. virtual void initialiseWindows (const String& commandLine)
  92. {
  93. const String commandLineWithoutNSDebug (commandLine.replace ("-NSDocumentRevisionsDebugMode YES", ""));
  94. if (commandLineWithoutNSDebug.trim().isNotEmpty() && ! commandLineWithoutNSDebug.trim().startsWithChar ('-'))
  95. anotherInstanceStarted (commandLine);
  96. else
  97. mainWindowList.reopenLastProjects();
  98. mainWindowList.createWindowIfNoneAreOpen();
  99. }
  100. void shutdown() override
  101. {
  102. versionChecker = nullptr;
  103. appearanceEditorWindow = nullptr;
  104. globalPreferencesWindow = nullptr;
  105. utf8Window = nullptr;
  106. svgPathWindow = nullptr;
  107. mainWindowList.forceCloseAllWindows();
  108. openDocumentManager.clear();
  109. #if JUCE_MAC
  110. MenuBarModel::setMacMainMenu (nullptr);
  111. #endif
  112. menuModel = nullptr;
  113. commandManager = nullptr;
  114. settings = nullptr;
  115. LookAndFeel::setDefaultLookAndFeel (nullptr);
  116. if (! isRunningCommandLine)
  117. Logger::writeToLog ("Shutdown");
  118. deleteLogger();
  119. }
  120. //==============================================================================
  121. void systemRequestedQuit() override
  122. {
  123. if (ModalComponentManager::getInstance()->cancelAllModalComponents())
  124. {
  125. new AsyncQuitRetrier();
  126. }
  127. else
  128. {
  129. if (closeAllMainWindows())
  130. quit();
  131. }
  132. }
  133. //==============================================================================
  134. const String getApplicationName() override { return "Introjucer"; }
  135. const String getApplicationVersion() override { return ProjectInfo::versionString; }
  136. virtual String getVersionDescription() const
  137. {
  138. String s;
  139. const Time buildDate (Time::getCompilationDate());
  140. s << "Introjucer " << ProjectInfo::versionString
  141. << newLine
  142. << "Build date: " << buildDate.getDayOfMonth()
  143. << " " << Time::getMonthName (buildDate.getMonth(), true)
  144. << " " << buildDate.getYear();
  145. return s;
  146. }
  147. bool moreThanOneInstanceAllowed() override
  148. {
  149. return true; // this is handled manually in initialise()
  150. }
  151. void anotherInstanceStarted (const String& commandLine) override
  152. {
  153. openFile (File (commandLine.unquoted()));
  154. }
  155. static IntrojucerApp& getApp()
  156. {
  157. IntrojucerApp* const app = dynamic_cast<IntrojucerApp*> (JUCEApplication::getInstance());
  158. jassert (app != nullptr);
  159. return *app;
  160. }
  161. static ApplicationCommandManager& getCommandManager()
  162. {
  163. ApplicationCommandManager* cm = IntrojucerApp::getApp().commandManager;
  164. jassert (cm != nullptr);
  165. return *cm;
  166. }
  167. //==============================================================================
  168. class MainMenuModel : public MenuBarModel
  169. {
  170. public:
  171. MainMenuModel()
  172. {
  173. setApplicationCommandManagerToWatch (&getCommandManager());
  174. }
  175. StringArray getMenuBarNames() override
  176. {
  177. return getApp().getMenuNames();
  178. }
  179. PopupMenu getMenuForIndex (int /*topLevelMenuIndex*/, const String& menuName) override
  180. {
  181. PopupMenu menu;
  182. getApp().createMenu (menu, menuName);
  183. return menu;
  184. }
  185. void menuItemSelected (int menuItemID, int /*topLevelMenuIndex*/) override
  186. {
  187. getApp().handleMainMenuCommand (menuItemID);
  188. }
  189. };
  190. enum
  191. {
  192. recentProjectsBaseID = 100,
  193. activeDocumentsBaseID = 300,
  194. colourSchemeBaseID = 1000
  195. };
  196. virtual StringArray getMenuNames()
  197. {
  198. const char* const names[] = { "File", "Edit", "View", "Window", "GUI Editor", "Tools", nullptr };
  199. return StringArray (names);
  200. }
  201. virtual void createMenu (PopupMenu& menu, const String& menuName)
  202. {
  203. if (menuName == "File") createFileMenu (menu);
  204. else if (menuName == "Edit") createEditMenu (menu);
  205. else if (menuName == "View") createViewMenu (menu);
  206. else if (menuName == "Window") createWindowMenu (menu);
  207. else if (menuName == "Tools") createToolsMenu (menu);
  208. else if (menuName == "GUI Editor") createGUIEditorMenu (menu);
  209. else jassertfalse; // names have changed?
  210. }
  211. virtual void createFileMenu (PopupMenu& menu)
  212. {
  213. menu.addCommandItem (commandManager, CommandIDs::newProject);
  214. menu.addSeparator();
  215. menu.addCommandItem (commandManager, CommandIDs::open);
  216. PopupMenu recentFiles;
  217. settings->recentFiles.createPopupMenuItems (recentFiles, recentProjectsBaseID, true, true);
  218. menu.addSubMenu ("Open Recent", recentFiles);
  219. menu.addSeparator();
  220. menu.addCommandItem (commandManager, CommandIDs::closeDocument);
  221. menu.addCommandItem (commandManager, CommandIDs::saveDocument);
  222. menu.addCommandItem (commandManager, CommandIDs::saveDocumentAs);
  223. menu.addCommandItem (commandManager, CommandIDs::saveAll);
  224. menu.addSeparator();
  225. menu.addCommandItem (commandManager, CommandIDs::closeProject);
  226. menu.addCommandItem (commandManager, CommandIDs::saveProject);
  227. menu.addSeparator();
  228. menu.addCommandItem (commandManager, CommandIDs::openInIDE);
  229. menu.addCommandItem (commandManager, CommandIDs::saveAndOpenInIDE);
  230. #if ! JUCE_MAC
  231. menu.addSeparator();
  232. menu.addCommandItem (commandManager, StandardApplicationCommandIDs::quit);
  233. #endif
  234. }
  235. virtual void createEditMenu (PopupMenu& menu)
  236. {
  237. menu.addCommandItem (commandManager, StandardApplicationCommandIDs::undo);
  238. menu.addCommandItem (commandManager, StandardApplicationCommandIDs::redo);
  239. menu.addSeparator();
  240. menu.addCommandItem (commandManager, StandardApplicationCommandIDs::cut);
  241. menu.addCommandItem (commandManager, StandardApplicationCommandIDs::copy);
  242. menu.addCommandItem (commandManager, StandardApplicationCommandIDs::paste);
  243. menu.addCommandItem (commandManager, StandardApplicationCommandIDs::del);
  244. menu.addCommandItem (commandManager, StandardApplicationCommandIDs::selectAll);
  245. menu.addCommandItem (commandManager, StandardApplicationCommandIDs::deselectAll);
  246. menu.addSeparator();
  247. menu.addCommandItem (commandManager, CommandIDs::showFindPanel);
  248. menu.addCommandItem (commandManager, CommandIDs::findSelection);
  249. menu.addCommandItem (commandManager, CommandIDs::findNext);
  250. menu.addCommandItem (commandManager, CommandIDs::findPrevious);
  251. }
  252. virtual void createViewMenu (PopupMenu& menu)
  253. {
  254. menu.addCommandItem (commandManager, CommandIDs::showFilePanel);
  255. menu.addCommandItem (commandManager, CommandIDs::showConfigPanel);
  256. menu.addCommandItem (commandManager, CommandIDs::showProjectSettings);
  257. menu.addCommandItem (commandManager, CommandIDs::showProjectModules);
  258. menu.addSeparator();
  259. createColourSchemeItems (menu);
  260. }
  261. void createColourSchemeItems (PopupMenu& menu)
  262. {
  263. const StringArray presetSchemes (settings->appearance.getPresetSchemes());
  264. if (presetSchemes.size() > 0)
  265. {
  266. PopupMenu schemes;
  267. for (int i = 0; i < presetSchemes.size(); ++i)
  268. schemes.addItem (colourSchemeBaseID + i, presetSchemes[i]);
  269. menu.addSubMenu ("Colour Scheme", schemes);
  270. }
  271. }
  272. virtual void createWindowMenu (PopupMenu& menu)
  273. {
  274. menu.addCommandItem (commandManager, CommandIDs::closeWindow);
  275. menu.addSeparator();
  276. menu.addCommandItem (commandManager, CommandIDs::goToPreviousDoc);
  277. menu.addCommandItem (commandManager, CommandIDs::goToNextDoc);
  278. menu.addCommandItem (commandManager, CommandIDs::goToCounterpart);
  279. menu.addSeparator();
  280. const int numDocs = jmin (50, openDocumentManager.getNumOpenDocuments());
  281. for (int i = 0; i < numDocs; ++i)
  282. {
  283. OpenDocumentManager::Document* doc = openDocumentManager.getOpenDocument(i);
  284. menu.addItem (activeDocumentsBaseID + i, doc->getName());
  285. }
  286. menu.addSeparator();
  287. menu.addCommandItem (commandManager, CommandIDs::closeAllDocuments);
  288. }
  289. virtual void createToolsMenu (PopupMenu& menu)
  290. {
  291. menu.addCommandItem (commandManager, CommandIDs::showGlobalPreferences);
  292. menu.addSeparator();
  293. menu.addCommandItem (commandManager, CommandIDs::showUTF8Tool);
  294. menu.addCommandItem (commandManager, CommandIDs::showSVGPathTool);
  295. menu.addCommandItem (commandManager, CommandIDs::showTranslationTool);
  296. }
  297. virtual void handleMainMenuCommand (int menuItemID)
  298. {
  299. if (menuItemID >= recentProjectsBaseID && menuItemID < recentProjectsBaseID + 100)
  300. {
  301. // open a file from the "recent files" menu
  302. openFile (settings->recentFiles.getFile (menuItemID - recentProjectsBaseID));
  303. }
  304. else if (menuItemID >= activeDocumentsBaseID && menuItemID < activeDocumentsBaseID + 200)
  305. {
  306. if (OpenDocumentManager::Document* doc = openDocumentManager.getOpenDocument (menuItemID - activeDocumentsBaseID))
  307. mainWindowList.openDocument (doc, true);
  308. else
  309. jassertfalse;
  310. }
  311. else if (menuItemID >= colourSchemeBaseID && menuItemID < colourSchemeBaseID + 200)
  312. {
  313. settings->appearance.selectPresetScheme (menuItemID - colourSchemeBaseID);
  314. }
  315. else
  316. {
  317. handleGUIEditorMenuCommand (menuItemID);
  318. }
  319. }
  320. //==============================================================================
  321. void getAllCommands (Array <CommandID>& commands) override
  322. {
  323. JUCEApplication::getAllCommands (commands);
  324. const CommandID ids[] = { CommandIDs::newProject,
  325. CommandIDs::open,
  326. CommandIDs::closeAllDocuments,
  327. CommandIDs::saveAll,
  328. CommandIDs::showGlobalPreferences,
  329. CommandIDs::showUTF8Tool,
  330. CommandIDs::showSVGPathTool };
  331. commands.addArray (ids, numElementsInArray (ids));
  332. }
  333. void getCommandInfo (CommandID commandID, ApplicationCommandInfo& result) override
  334. {
  335. switch (commandID)
  336. {
  337. case CommandIDs::newProject:
  338. result.setInfo ("New Project...", "Creates a new Jucer project", CommandCategories::general, 0);
  339. result.defaultKeypresses.add (KeyPress ('n', ModifierKeys::commandModifier, 0));
  340. break;
  341. case CommandIDs::open:
  342. result.setInfo ("Open...", "Opens a Jucer project", CommandCategories::general, 0);
  343. result.defaultKeypresses.add (KeyPress ('o', ModifierKeys::commandModifier, 0));
  344. break;
  345. case CommandIDs::showGlobalPreferences:
  346. result.setInfo ("Global Preferences...", "Shows the global preferences window.", CommandCategories::general, 0);
  347. break;
  348. case CommandIDs::closeAllDocuments:
  349. result.setInfo ("Close All Documents", "Closes all open documents", CommandCategories::general, 0);
  350. result.setActive (openDocumentManager.getNumOpenDocuments() > 0);
  351. break;
  352. case CommandIDs::saveAll:
  353. result.setInfo ("Save All", "Saves all open documents", CommandCategories::general, 0);
  354. result.defaultKeypresses.add (KeyPress ('s', ModifierKeys::commandModifier | ModifierKeys::altModifier, 0));
  355. break;
  356. case CommandIDs::showUTF8Tool:
  357. result.setInfo ("UTF-8 String-Literal Helper", "Shows the UTF-8 string literal utility", CommandCategories::general, 0);
  358. break;
  359. case CommandIDs::showSVGPathTool:
  360. result.setInfo ("SVG Path Helper", "Shows the SVG->Path data conversion utility", CommandCategories::general, 0);
  361. break;
  362. default:
  363. JUCEApplication::getCommandInfo (commandID, result);
  364. break;
  365. }
  366. }
  367. bool perform (const InvocationInfo& info) override
  368. {
  369. switch (info.commandID)
  370. {
  371. case CommandIDs::newProject: createNewProject(); break;
  372. case CommandIDs::open: askUserToOpenFile(); break;
  373. case CommandIDs::saveAll: openDocumentManager.saveAll(); break;
  374. case CommandIDs::closeAllDocuments: closeAllDocuments (true); break;
  375. case CommandIDs::showUTF8Tool: showUTF8ToolWindow (utf8Window); break;
  376. case CommandIDs::showSVGPathTool: showSVGPathDataToolWindow (svgPathWindow); break;
  377. case CommandIDs::showGlobalPreferences: AppearanceSettings::showGlobalPreferences (globalPreferencesWindow); break;
  378. default: return JUCEApplication::perform (info);
  379. }
  380. return true;
  381. }
  382. //==============================================================================
  383. void createNewProject()
  384. {
  385. MainWindow* mw = mainWindowList.getOrCreateEmptyWindow();
  386. mw->showNewProjectWizard();
  387. mainWindowList.avoidSuperimposedWindows (mw);
  388. }
  389. virtual void updateNewlyOpenedProject (Project&) {}
  390. void askUserToOpenFile()
  391. {
  392. FileChooser fc ("Open File");
  393. if (fc.browseForFileToOpen())
  394. openFile (fc.getResult());
  395. }
  396. bool openFile (const File& file)
  397. {
  398. return mainWindowList.openFile (file);
  399. }
  400. bool closeAllDocuments (bool askUserToSave)
  401. {
  402. return openDocumentManager.closeAll (askUserToSave);
  403. }
  404. virtual bool closeAllMainWindows()
  405. {
  406. return mainWindowList.askAllWindowsToClose();
  407. }
  408. //==============================================================================
  409. struct FileWithTime
  410. {
  411. FileWithTime (const File& f) : file (f), time (f.getLastModificationTime()) {}
  412. FileWithTime() {}
  413. bool operator< (const FileWithTime& other) const { return time < other.time; }
  414. bool operator== (const FileWithTime& other) const { return time == other.time; }
  415. File file;
  416. Time time;
  417. };
  418. void deleteLogger()
  419. {
  420. const int maxNumLogFilesToKeep = 50;
  421. Logger::setCurrentLogger (nullptr);
  422. if (logger != nullptr)
  423. {
  424. Array<File> logFiles;
  425. logger->getLogFile().getParentDirectory().findChildFiles (logFiles, File::findFiles, false);
  426. if (logFiles.size() > maxNumLogFilesToKeep)
  427. {
  428. Array <FileWithTime> files;
  429. for (int i = 0; i < logFiles.size(); ++i)
  430. files.addUsingDefaultSort (logFiles.getReference(i));
  431. for (int i = 0; i < files.size() - maxNumLogFilesToKeep; ++i)
  432. files.getReference(i).file.deleteFile();
  433. }
  434. }
  435. logger = nullptr;
  436. }
  437. virtual void addExtraConfigItems (Project&, TreeViewItem&) {}
  438. #if JUCE_LINUX
  439. virtual String getLogFolderName() const { return "~/.config/Introjucer/Logs"; }
  440. #else
  441. virtual String getLogFolderName() const { return "com.juce.introjucer"; }
  442. #endif
  443. virtual PropertiesFile::Options getPropertyFileOptionsFor (const String& filename)
  444. {
  445. PropertiesFile::Options options;
  446. options.applicationName = filename;
  447. options.filenameSuffix = "settings";
  448. options.osxLibrarySubFolder = "Application Support";
  449. #if JUCE_LINUX
  450. options.folderName = "~/.config/Introjucer";
  451. #else
  452. options.folderName = "Introjucer";
  453. #endif
  454. return options;
  455. }
  456. virtual Component* createProjectContentComponent() const
  457. {
  458. return new ProjectContentComponent();
  459. }
  460. //==============================================================================
  461. virtual LatestVersionChecker* createVersionChecker() const
  462. {
  463. return new LatestVersionChecker();
  464. }
  465. //==============================================================================
  466. IntrojucerLookAndFeel lookAndFeel;
  467. ScopedPointer<StoredSettings> settings;
  468. ScopedPointer<Icons> icons;
  469. ScopedPointer<MainMenuModel> menuModel;
  470. MainWindowList mainWindowList;
  471. OpenDocumentManager openDocumentManager;
  472. ScopedPointer<ApplicationCommandManager> commandManager;
  473. ScopedPointer<Component> appearanceEditorWindow, globalPreferencesWindow, utf8Window, svgPathWindow;
  474. ScopedPointer<FileLogger> logger;
  475. bool isRunningCommandLine;
  476. private:
  477. ScopedPointer<LatestVersionChecker> versionChecker;
  478. class AsyncQuitRetrier : private Timer
  479. {
  480. public:
  481. AsyncQuitRetrier() { startTimer (500); }
  482. void timerCallback() override
  483. {
  484. stopTimer();
  485. delete this;
  486. if (JUCEApplicationBase* app = JUCEApplicationBase::getInstance())
  487. app->systemRequestedQuit();
  488. }
  489. JUCE_DECLARE_NON_COPYABLE (AsyncQuitRetrier)
  490. };
  491. void initCommandManager()
  492. {
  493. commandManager = new ApplicationCommandManager();
  494. commandManager->registerAllCommandsForTarget (this);
  495. {
  496. CodeDocument doc;
  497. CppCodeEditorComponent ed (File::nonexistent, doc);
  498. commandManager->registerAllCommandsForTarget (&ed);
  499. }
  500. registerGUIEditorCommands();
  501. }
  502. };
  503. #endif // JUCER_APPLICATION_H_INCLUDED