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.

625 lines
21KB

  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. if (commandLine.trim().isNotEmpty() && ! commandLine.trim().startsWithChar ('-'))
  94. anotherInstanceStarted (commandLine);
  95. else
  96. mainWindowList.reopenLastProjects();
  97. mainWindowList.createWindowIfNoneAreOpen();
  98. }
  99. void shutdown() override
  100. {
  101. versionChecker = nullptr;
  102. appearanceEditorWindow = nullptr;
  103. globalPreferencesWindow = nullptr;
  104. utf8Window = nullptr;
  105. svgPathWindow = nullptr;
  106. mainWindowList.forceCloseAllWindows();
  107. openDocumentManager.clear();
  108. #if JUCE_MAC
  109. MenuBarModel::setMacMainMenu (nullptr);
  110. #endif
  111. menuModel = nullptr;
  112. commandManager = nullptr;
  113. settings = nullptr;
  114. LookAndFeel::setDefaultLookAndFeel (nullptr);
  115. if (! isRunningCommandLine)
  116. Logger::writeToLog ("Shutdown");
  117. deleteLogger();
  118. }
  119. //==============================================================================
  120. void systemRequestedQuit() override
  121. {
  122. if (ModalComponentManager::getInstance()->cancelAllModalComponents())
  123. {
  124. new AsyncQuitRetrier();
  125. }
  126. else
  127. {
  128. if (closeAllMainWindows())
  129. quit();
  130. }
  131. }
  132. //==============================================================================
  133. const String getApplicationName() override { return "Introjucer"; }
  134. const String getApplicationVersion() override { return ProjectInfo::versionString; }
  135. virtual String getVersionDescription() const
  136. {
  137. String s;
  138. const Time buildDate (Time::getCompilationDate());
  139. s << "Introjucer " << ProjectInfo::versionString
  140. << newLine
  141. << "Build date: " << buildDate.getDayOfMonth()
  142. << " " << Time::getMonthName (buildDate.getMonth(), true)
  143. << " " << buildDate.getYear();
  144. return s;
  145. }
  146. bool moreThanOneInstanceAllowed() override
  147. {
  148. return true; // this is handled manually in initialise()
  149. }
  150. void anotherInstanceStarted (const String& commandLine) override
  151. {
  152. openFile (File (commandLine.unquoted()));
  153. }
  154. static IntrojucerApp& getApp()
  155. {
  156. IntrojucerApp* const app = dynamic_cast<IntrojucerApp*> (JUCEApplication::getInstance());
  157. jassert (app != nullptr);
  158. return *app;
  159. }
  160. static ApplicationCommandManager& getCommandManager()
  161. {
  162. ApplicationCommandManager* cm = IntrojucerApp::getApp().commandManager;
  163. jassert (cm != nullptr);
  164. return *cm;
  165. }
  166. //==============================================================================
  167. class MainMenuModel : public MenuBarModel
  168. {
  169. public:
  170. MainMenuModel()
  171. {
  172. setApplicationCommandManagerToWatch (&getCommandManager());
  173. }
  174. StringArray getMenuBarNames() override
  175. {
  176. return getApp().getMenuNames();
  177. }
  178. PopupMenu getMenuForIndex (int /*topLevelMenuIndex*/, const String& menuName) override
  179. {
  180. PopupMenu menu;
  181. getApp().createMenu (menu, menuName);
  182. return menu;
  183. }
  184. void menuItemSelected (int menuItemID, int /*topLevelMenuIndex*/) override
  185. {
  186. getApp().handleMainMenuCommand (menuItemID);
  187. }
  188. };
  189. enum
  190. {
  191. recentProjectsBaseID = 100,
  192. activeDocumentsBaseID = 300,
  193. colourSchemeBaseID = 1000
  194. };
  195. virtual StringArray getMenuNames()
  196. {
  197. const char* const names[] = { "File", "Edit", "View", "Window", "GUI Editor", "Tools", nullptr };
  198. return StringArray (names);
  199. }
  200. virtual void createMenu (PopupMenu& menu, const String& menuName)
  201. {
  202. if (menuName == "File") createFileMenu (menu);
  203. else if (menuName == "Edit") createEditMenu (menu);
  204. else if (menuName == "View") createViewMenu (menu);
  205. else if (menuName == "Window") createWindowMenu (menu);
  206. else if (menuName == "Tools") createToolsMenu (menu);
  207. else if (menuName == "GUI Editor") createGUIEditorMenu (menu);
  208. else jassertfalse; // names have changed?
  209. }
  210. virtual void createFileMenu (PopupMenu& menu)
  211. {
  212. menu.addCommandItem (commandManager, CommandIDs::newProject);
  213. menu.addSeparator();
  214. menu.addCommandItem (commandManager, CommandIDs::open);
  215. PopupMenu recentFiles;
  216. settings->recentFiles.createPopupMenuItems (recentFiles, recentProjectsBaseID, true, true);
  217. menu.addSubMenu ("Open Recent", recentFiles);
  218. menu.addSeparator();
  219. menu.addCommandItem (commandManager, CommandIDs::closeDocument);
  220. menu.addCommandItem (commandManager, CommandIDs::saveDocument);
  221. menu.addCommandItem (commandManager, CommandIDs::saveDocumentAs);
  222. menu.addCommandItem (commandManager, CommandIDs::saveAll);
  223. menu.addSeparator();
  224. menu.addCommandItem (commandManager, CommandIDs::closeProject);
  225. menu.addCommandItem (commandManager, CommandIDs::saveProject);
  226. menu.addSeparator();
  227. menu.addCommandItem (commandManager, CommandIDs::openInIDE);
  228. menu.addCommandItem (commandManager, CommandIDs::saveAndOpenInIDE);
  229. #if ! JUCE_MAC
  230. menu.addSeparator();
  231. menu.addCommandItem (commandManager, StandardApplicationCommandIDs::quit);
  232. #endif
  233. }
  234. virtual void createEditMenu (PopupMenu& menu)
  235. {
  236. menu.addCommandItem (commandManager, StandardApplicationCommandIDs::undo);
  237. menu.addCommandItem (commandManager, StandardApplicationCommandIDs::redo);
  238. menu.addSeparator();
  239. menu.addCommandItem (commandManager, StandardApplicationCommandIDs::cut);
  240. menu.addCommandItem (commandManager, StandardApplicationCommandIDs::copy);
  241. menu.addCommandItem (commandManager, StandardApplicationCommandIDs::paste);
  242. menu.addCommandItem (commandManager, StandardApplicationCommandIDs::del);
  243. menu.addCommandItem (commandManager, StandardApplicationCommandIDs::selectAll);
  244. menu.addCommandItem (commandManager, StandardApplicationCommandIDs::deselectAll);
  245. menu.addSeparator();
  246. menu.addCommandItem (commandManager, CommandIDs::showFindPanel);
  247. menu.addCommandItem (commandManager, CommandIDs::findSelection);
  248. menu.addCommandItem (commandManager, CommandIDs::findNext);
  249. menu.addCommandItem (commandManager, CommandIDs::findPrevious);
  250. }
  251. virtual void createViewMenu (PopupMenu& menu)
  252. {
  253. menu.addCommandItem (commandManager, CommandIDs::showFilePanel);
  254. menu.addCommandItem (commandManager, CommandIDs::showConfigPanel);
  255. menu.addCommandItem (commandManager, CommandIDs::showProjectSettings);
  256. menu.addCommandItem (commandManager, CommandIDs::showProjectModules);
  257. menu.addSeparator();
  258. createColourSchemeItems (menu);
  259. }
  260. void createColourSchemeItems (PopupMenu& menu)
  261. {
  262. const StringArray presetSchemes (settings->appearance.getPresetSchemes());
  263. if (presetSchemes.size() > 0)
  264. {
  265. PopupMenu schemes;
  266. for (int i = 0; i < presetSchemes.size(); ++i)
  267. schemes.addItem (colourSchemeBaseID + i, presetSchemes[i]);
  268. menu.addSubMenu ("Colour Scheme", schemes);
  269. }
  270. }
  271. virtual void createWindowMenu (PopupMenu& menu)
  272. {
  273. menu.addCommandItem (commandManager, CommandIDs::closeWindow);
  274. menu.addSeparator();
  275. menu.addCommandItem (commandManager, CommandIDs::goToPreviousDoc);
  276. menu.addCommandItem (commandManager, CommandIDs::goToNextDoc);
  277. menu.addCommandItem (commandManager, CommandIDs::goToCounterpart);
  278. menu.addSeparator();
  279. const int numDocs = jmin (50, openDocumentManager.getNumOpenDocuments());
  280. for (int i = 0; i < numDocs; ++i)
  281. {
  282. OpenDocumentManager::Document* doc = openDocumentManager.getOpenDocument(i);
  283. menu.addItem (activeDocumentsBaseID + i, doc->getName());
  284. }
  285. menu.addSeparator();
  286. menu.addCommandItem (commandManager, CommandIDs::closeAllDocuments);
  287. }
  288. virtual void createToolsMenu (PopupMenu& menu)
  289. {
  290. menu.addCommandItem (commandManager, CommandIDs::showGlobalPreferences);
  291. menu.addSeparator();
  292. menu.addCommandItem (commandManager, CommandIDs::showUTF8Tool);
  293. menu.addCommandItem (commandManager, CommandIDs::showSVGPathTool);
  294. menu.addCommandItem (commandManager, CommandIDs::showTranslationTool);
  295. }
  296. virtual void handleMainMenuCommand (int menuItemID)
  297. {
  298. if (menuItemID >= recentProjectsBaseID && menuItemID < recentProjectsBaseID + 100)
  299. {
  300. // open a file from the "recent files" menu
  301. openFile (settings->recentFiles.getFile (menuItemID - recentProjectsBaseID));
  302. }
  303. else if (menuItemID >= activeDocumentsBaseID && menuItemID < activeDocumentsBaseID + 200)
  304. {
  305. if (OpenDocumentManager::Document* doc = openDocumentManager.getOpenDocument (menuItemID - activeDocumentsBaseID))
  306. mainWindowList.openDocument (doc, true);
  307. else
  308. jassertfalse;
  309. }
  310. else if (menuItemID >= colourSchemeBaseID && menuItemID < colourSchemeBaseID + 200)
  311. {
  312. settings->appearance.selectPresetScheme (menuItemID - colourSchemeBaseID);
  313. }
  314. else
  315. {
  316. handleGUIEditorMenuCommand (menuItemID);
  317. }
  318. }
  319. //==============================================================================
  320. void getAllCommands (Array <CommandID>& commands) override
  321. {
  322. JUCEApplication::getAllCommands (commands);
  323. const CommandID ids[] = { CommandIDs::newProject,
  324. CommandIDs::open,
  325. CommandIDs::closeAllDocuments,
  326. CommandIDs::saveAll,
  327. CommandIDs::showGlobalPreferences,
  328. CommandIDs::showUTF8Tool,
  329. CommandIDs::showSVGPathTool };
  330. commands.addArray (ids, numElementsInArray (ids));
  331. }
  332. void getCommandInfo (CommandID commandID, ApplicationCommandInfo& result) override
  333. {
  334. switch (commandID)
  335. {
  336. case CommandIDs::newProject:
  337. result.setInfo ("New Project...", "Creates a new Jucer project", CommandCategories::general, 0);
  338. result.defaultKeypresses.add (KeyPress ('n', ModifierKeys::commandModifier, 0));
  339. break;
  340. case CommandIDs::open:
  341. result.setInfo ("Open...", "Opens a Jucer project", CommandCategories::general, 0);
  342. result.defaultKeypresses.add (KeyPress ('o', ModifierKeys::commandModifier, 0));
  343. break;
  344. case CommandIDs::showGlobalPreferences:
  345. result.setInfo ("Global Preferences...", "Shows the global preferences window.", CommandCategories::general, 0);
  346. break;
  347. case CommandIDs::closeAllDocuments:
  348. result.setInfo ("Close All Documents", "Closes all open documents", CommandCategories::general, 0);
  349. result.setActive (openDocumentManager.getNumOpenDocuments() > 0);
  350. break;
  351. case CommandIDs::saveAll:
  352. result.setInfo ("Save All", "Saves all open documents", CommandCategories::general, 0);
  353. result.defaultKeypresses.add (KeyPress ('s', ModifierKeys::commandModifier | ModifierKeys::altModifier, 0));
  354. break;
  355. case CommandIDs::showUTF8Tool:
  356. result.setInfo ("UTF-8 String-Literal Helper", "Shows the UTF-8 string literal utility", CommandCategories::general, 0);
  357. break;
  358. case CommandIDs::showSVGPathTool:
  359. result.setInfo ("SVG Path Helper", "Shows the SVG->Path data conversion utility", CommandCategories::general, 0);
  360. break;
  361. default:
  362. JUCEApplication::getCommandInfo (commandID, result);
  363. break;
  364. }
  365. }
  366. bool perform (const InvocationInfo& info) override
  367. {
  368. switch (info.commandID)
  369. {
  370. case CommandIDs::newProject: createNewProject(); break;
  371. case CommandIDs::open: askUserToOpenFile(); break;
  372. case CommandIDs::saveAll: openDocumentManager.saveAll(); break;
  373. case CommandIDs::closeAllDocuments: closeAllDocuments (true); break;
  374. case CommandIDs::showUTF8Tool: showUTF8ToolWindow (utf8Window); break;
  375. case CommandIDs::showSVGPathTool: showSVGPathDataToolWindow (svgPathWindow); break;
  376. case CommandIDs::showGlobalPreferences: AppearanceSettings::showGlobalPreferences (globalPreferencesWindow); break;
  377. default: return JUCEApplication::perform (info);
  378. }
  379. return true;
  380. }
  381. //==============================================================================
  382. void createNewProject()
  383. {
  384. MainWindow* mw = mainWindowList.getOrCreateEmptyWindow();
  385. mw->showNewProjectWizard();
  386. mainWindowList.avoidSuperimposedWindows (mw);
  387. }
  388. virtual void updateNewlyOpenedProject (Project&) {}
  389. void askUserToOpenFile()
  390. {
  391. FileChooser fc ("Open File");
  392. if (fc.browseForFileToOpen())
  393. openFile (fc.getResult());
  394. }
  395. bool openFile (const File& file)
  396. {
  397. return mainWindowList.openFile (file);
  398. }
  399. bool closeAllDocuments (bool askUserToSave)
  400. {
  401. return openDocumentManager.closeAll (askUserToSave);
  402. }
  403. virtual bool closeAllMainWindows()
  404. {
  405. return mainWindowList.askAllWindowsToClose();
  406. }
  407. //==============================================================================
  408. struct FileWithTime
  409. {
  410. FileWithTime (const File& f) : file (f), time (f.getLastModificationTime()) {}
  411. FileWithTime() {}
  412. bool operator< (const FileWithTime& other) const { return time < other.time; }
  413. bool operator== (const FileWithTime& other) const { return time == other.time; }
  414. File file;
  415. Time time;
  416. };
  417. void deleteLogger()
  418. {
  419. const int maxNumLogFilesToKeep = 50;
  420. Logger::setCurrentLogger (nullptr);
  421. if (logger != nullptr)
  422. {
  423. Array<File> logFiles;
  424. logger->getLogFile().getParentDirectory().findChildFiles (logFiles, File::findFiles, false);
  425. if (logFiles.size() > maxNumLogFilesToKeep)
  426. {
  427. Array <FileWithTime> files;
  428. for (int i = 0; i < logFiles.size(); ++i)
  429. files.addUsingDefaultSort (logFiles.getReference(i));
  430. for (int i = 0; i < files.size() - maxNumLogFilesToKeep; ++i)
  431. files.getReference(i).file.deleteFile();
  432. }
  433. }
  434. logger = nullptr;
  435. }
  436. virtual void addExtraConfigItems (Project&, TreeViewItem&) {}
  437. #if JUCE_LINUX
  438. virtual String getLogFolderName() const { return "~/.config/Introjucer/Logs"; }
  439. #else
  440. virtual String getLogFolderName() const { return "com.juce.introjucer"; }
  441. #endif
  442. virtual PropertiesFile::Options getPropertyFileOptionsFor (const String& filename)
  443. {
  444. PropertiesFile::Options options;
  445. options.applicationName = filename;
  446. options.filenameSuffix = "settings";
  447. options.osxLibrarySubFolder = "Application Support";
  448. #if JUCE_LINUX
  449. options.folderName = "~/.config/Introjucer";
  450. #else
  451. options.folderName = "Introjucer";
  452. #endif
  453. return options;
  454. }
  455. virtual Component* createProjectContentComponent() const
  456. {
  457. return new ProjectContentComponent();
  458. }
  459. //==============================================================================
  460. virtual LatestVersionChecker* createVersionChecker() const
  461. {
  462. return new LatestVersionChecker();
  463. }
  464. //==============================================================================
  465. IntrojucerLookAndFeel lookAndFeel;
  466. ScopedPointer<StoredSettings> settings;
  467. ScopedPointer<Icons> icons;
  468. ScopedPointer<MainMenuModel> menuModel;
  469. MainWindowList mainWindowList;
  470. OpenDocumentManager openDocumentManager;
  471. ScopedPointer<ApplicationCommandManager> commandManager;
  472. ScopedPointer<Component> appearanceEditorWindow, globalPreferencesWindow, utf8Window, svgPathWindow;
  473. ScopedPointer<FileLogger> logger;
  474. bool isRunningCommandLine;
  475. private:
  476. ScopedPointer<LatestVersionChecker> versionChecker;
  477. class AsyncQuitRetrier : private Timer
  478. {
  479. public:
  480. AsyncQuitRetrier() { startTimer (500); }
  481. void timerCallback() override
  482. {
  483. stopTimer();
  484. delete this;
  485. if (JUCEApplicationBase* app = JUCEApplicationBase::getInstance())
  486. app->systemRequestedQuit();
  487. }
  488. JUCE_DECLARE_NON_COPYABLE (AsyncQuitRetrier)
  489. };
  490. void initCommandManager()
  491. {
  492. commandManager = new ApplicationCommandManager();
  493. commandManager->registerAllCommandsForTarget (this);
  494. {
  495. CodeDocument doc;
  496. CppCodeEditorComponent ed (File::nonexistent, doc);
  497. commandManager->registerAllCommandsForTarget (&ed);
  498. }
  499. registerGUIEditorCommands();
  500. }
  501. };
  502. #endif // JUCER_APPLICATION_H_INCLUDED