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.

553 lines
19KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. #ifndef __JUCER_APPLICATION_JUCEHEADER__
  19. #define __JUCER_APPLICATION_JUCEHEADER__
  20. #include "../jucer_Headers.h"
  21. #include "jucer_MainWindow.h"
  22. #include "jucer_JuceUpdater.h"
  23. #include "../Project/jucer_NewProjectWizard.h"
  24. #include "jucer_CommandLine.h"
  25. //==============================================================================
  26. class JucerApplication : public JUCEApplication
  27. {
  28. public:
  29. //==============================================================================
  30. JucerApplication()
  31. {
  32. }
  33. ~JucerApplication() {}
  34. //==============================================================================
  35. void initialise (const String& commandLine)
  36. {
  37. if (commandLine.isNotEmpty())
  38. {
  39. const int appReturnCode = performCommandLine (commandLine);
  40. if (appReturnCode != commandLineNotPerformed)
  41. {
  42. setApplicationReturnValue (appReturnCode);
  43. quit();
  44. return;
  45. }
  46. }
  47. commandManager = new ApplicationCommandManager();
  48. commandManager->registerAllCommandsForTarget (this);
  49. menuModel = new MainMenuModel();
  50. doExtraInitialisation();
  51. ImageCache::setCacheTimeout (30 * 1000);
  52. if (commandLine.trim().isNotEmpty() && ! commandLine.trim().startsWithChar ('-'))
  53. {
  54. anotherInstanceStarted (commandLine);
  55. }
  56. else
  57. {
  58. Array<File> projects (StoredSettings::getInstance()->getLastProjects());
  59. for (int i = 0; i < projects.size(); ++ i)
  60. openFile (projects.getReference(i));
  61. }
  62. if (mainWindows.size() == 0)
  63. createNewMainWindow()->makeVisible();
  64. #if JUCE_MAC
  65. MenuBarModel::setMacMainMenu (menuModel);
  66. #endif
  67. }
  68. void shutdown()
  69. {
  70. #if JUCE_MAC
  71. MenuBarModel::setMacMainMenu (nullptr);
  72. #endif
  73. menuModel = nullptr;
  74. StoredSettings::deleteInstance();
  75. mainWindows.clear();
  76. OpenDocumentManager::deleteInstance();
  77. commandManager = nullptr;
  78. }
  79. //==============================================================================
  80. void systemRequestedQuit()
  81. {
  82. if (cancelAnyModalComponents())
  83. {
  84. new AsyncQuitRetrier();
  85. return;
  86. }
  87. while (mainWindows.size() > 0)
  88. {
  89. if (! mainWindows[0]->closeCurrentProject())
  90. return;
  91. mainWindows.remove (0);
  92. }
  93. quit();
  94. }
  95. void closeWindow (MainWindow* w)
  96. {
  97. jassert (mainWindows.contains (w));
  98. mainWindows.removeObject (w);
  99. #if ! JUCE_MAC
  100. if (mainWindows.size() == 0)
  101. systemRequestedQuit();
  102. #endif
  103. updateRecentProjectList();
  104. }
  105. //==============================================================================
  106. const String getApplicationName()
  107. {
  108. return String (ProjectInfo::projectName) + " " + getApplicationVersion();
  109. }
  110. const String getApplicationVersion()
  111. {
  112. return ProjectInfo::versionString;
  113. }
  114. bool moreThanOneInstanceAllowed()
  115. {
  116. #ifndef JUCE_LINUX
  117. return false;
  118. #else
  119. return true; //xxx should be false but doesn't work on linux..
  120. #endif
  121. }
  122. void anotherInstanceStarted (const String& commandLine)
  123. {
  124. openFile (commandLine.unquoted());
  125. }
  126. virtual void doExtraInitialisation() {}
  127. //==============================================================================
  128. class MainMenuModel : public MenuBarModel
  129. {
  130. public:
  131. MainMenuModel()
  132. {
  133. setApplicationCommandManagerToWatch (commandManager);
  134. }
  135. const StringArray getMenuBarNames()
  136. {
  137. const char* const names[] = { "File", "Edit", "View", "Window", "Update", 0 };
  138. return StringArray ((const char**) names);
  139. }
  140. const PopupMenu getMenuForIndex (int topLevelMenuIndex, const String& menuName)
  141. {
  142. PopupMenu menu;
  143. if (topLevelMenuIndex == 0) // "File" menu
  144. {
  145. menu.addCommandItem (commandManager, CommandIDs::newProject);
  146. menu.addSeparator();
  147. menu.addCommandItem (commandManager, CommandIDs::open);
  148. PopupMenu recentFiles;
  149. StoredSettings::getInstance()->recentFiles.createPopupMenuItems (recentFiles, 100, true, true);
  150. menu.addSubMenu ("Open recent file", recentFiles);
  151. menu.addSeparator();
  152. menu.addCommandItem (commandManager, CommandIDs::closeDocument);
  153. menu.addCommandItem (commandManager, CommandIDs::saveDocument);
  154. menu.addCommandItem (commandManager, CommandIDs::saveDocumentAs);
  155. menu.addSeparator();
  156. menu.addCommandItem (commandManager, CommandIDs::closeProject);
  157. menu.addCommandItem (commandManager, CommandIDs::saveProject);
  158. menu.addCommandItem (commandManager, CommandIDs::saveProjectAs);
  159. menu.addSeparator();
  160. menu.addCommandItem (commandManager, CommandIDs::openInIDE);
  161. menu.addCommandItem (commandManager, CommandIDs::saveAndOpenInIDE);
  162. #if ! JUCE_MAC
  163. menu.addSeparator();
  164. menu.addCommandItem (commandManager, StandardApplicationCommandIDs::quit);
  165. #endif
  166. }
  167. else if (topLevelMenuIndex == 1) // "Edit" menu
  168. {
  169. menu.addCommandItem (commandManager, CommandIDs::undo);
  170. menu.addCommandItem (commandManager, CommandIDs::redo);
  171. menu.addSeparator();
  172. menu.addCommandItem (commandManager, StandardApplicationCommandIDs::cut);
  173. menu.addCommandItem (commandManager, StandardApplicationCommandIDs::copy);
  174. menu.addCommandItem (commandManager, StandardApplicationCommandIDs::paste);
  175. menu.addCommandItem (commandManager, StandardApplicationCommandIDs::del);
  176. menu.addCommandItem (commandManager, StandardApplicationCommandIDs::selectAll);
  177. menu.addCommandItem (commandManager, StandardApplicationCommandIDs::deselectAll);
  178. menu.addSeparator();
  179. menu.addCommandItem (commandManager, CommandIDs::toFront);
  180. menu.addCommandItem (commandManager, CommandIDs::toBack);
  181. menu.addSeparator();
  182. menu.addCommandItem (commandManager, CommandIDs::group);
  183. menu.addCommandItem (commandManager, CommandIDs::ungroup);
  184. menu.addSeparator();
  185. menu.addCommandItem (commandManager, CommandIDs::bringBackLostItems);
  186. }
  187. else if (topLevelMenuIndex == 2) // "View" menu
  188. {
  189. menu.addCommandItem (commandManager, CommandIDs::showProjectSettings);
  190. menu.addSeparator();
  191. menu.addCommandItem (commandManager, CommandIDs::test);
  192. menu.addSeparator();
  193. menu.addCommandItem (commandManager, CommandIDs::showGrid);
  194. menu.addCommandItem (commandManager, CommandIDs::enableSnapToGrid);
  195. menu.addSeparator();
  196. menu.addCommandItem (commandManager, CommandIDs::zoomIn);
  197. menu.addCommandItem (commandManager, CommandIDs::zoomOut);
  198. menu.addCommandItem (commandManager, CommandIDs::zoomNormal);
  199. menu.addSeparator();
  200. menu.addCommandItem (commandManager, CommandIDs::useTabbedWindows);
  201. }
  202. else if (topLevelMenuIndex == 3) // "Window" menu
  203. {
  204. menu.addCommandItem (commandManager, CommandIDs::closeWindow);
  205. menu.addSeparator();
  206. const int numDocs = jmin (50, OpenDocumentManager::getInstance()->getNumOpenDocuments());
  207. for (int i = 0; i < numDocs; ++i)
  208. {
  209. OpenDocumentManager::Document* doc = OpenDocumentManager::getInstance()->getOpenDocument(i);
  210. menu.addItem (300 + i, doc->getName());
  211. }
  212. menu.addSeparator();
  213. menu.addCommandItem (commandManager, CommandIDs::closeAllDocuments);
  214. }
  215. else if (topLevelMenuIndex == 4) // "Juce" menu
  216. {
  217. menu.addCommandItem (commandManager, CommandIDs::showJuceVersion);
  218. }
  219. return menu;
  220. }
  221. void menuItemSelected (int menuItemID, int topLevelMenuIndex)
  222. {
  223. if (menuItemID >= 100 && menuItemID < 200)
  224. {
  225. // open a file from the "recent files" menu
  226. const File file (StoredSettings::getInstance()->recentFiles.getFile (menuItemID - 100));
  227. getApp()->openFile (file);
  228. }
  229. else if (menuItemID >= 300 && menuItemID < 400)
  230. {
  231. OpenDocumentManager::Document* doc = OpenDocumentManager::getInstance()->getOpenDocument (menuItemID - 300);
  232. MainWindow* w = getApp()->getOrCreateFrontmostWindow();
  233. w->makeVisible();
  234. w->getProjectContentComponent()->showDocument (doc);
  235. }
  236. }
  237. private:
  238. JucerApplication* getApp() const
  239. {
  240. return static_cast<JucerApplication*> (JUCEApplication::getInstance());
  241. }
  242. };
  243. //==============================================================================
  244. void getAllCommands (Array <CommandID>& commands)
  245. {
  246. JUCEApplication::getAllCommands (commands);
  247. const CommandID ids[] = { CommandIDs::newProject,
  248. CommandIDs::open,
  249. CommandIDs::showPrefs,
  250. CommandIDs::closeAllDocuments,
  251. CommandIDs::saveAll,
  252. CommandIDs::showJuceVersion };
  253. commands.addArray (ids, numElementsInArray (ids));
  254. }
  255. void getCommandInfo (CommandID commandID, ApplicationCommandInfo& result)
  256. {
  257. switch (commandID)
  258. {
  259. case CommandIDs::newProject:
  260. result.setInfo ("New Project...", "Creates a new Jucer project", CommandCategories::general, 0);
  261. result.defaultKeypresses.add (KeyPress ('o', ModifierKeys::commandModifier, 0));
  262. break;
  263. case CommandIDs::open:
  264. result.setInfo ("Open...", "Opens a Jucer project", CommandCategories::general, 0);
  265. result.defaultKeypresses.add (KeyPress ('o', ModifierKeys::commandModifier, 0));
  266. break;
  267. case CommandIDs::showPrefs:
  268. result.setInfo ("Preferences...", "Shows the preferences panel.", CommandCategories::general, 0);
  269. result.defaultKeypresses.add (KeyPress (',', ModifierKeys::commandModifier, 0));
  270. break;
  271. case CommandIDs::closeAllDocuments:
  272. result.setInfo ("Close All Documents", "Closes all open documents", CommandCategories::general, 0);
  273. result.setActive (OpenDocumentManager::getInstance()->getNumOpenDocuments() > 0);
  274. break;
  275. case CommandIDs::saveAll:
  276. result.setInfo ("Save All", "Saves all open documents", CommandCategories::general, 0);
  277. result.setActive (OpenDocumentManager::getInstance()->anyFilesNeedSaving());
  278. break;
  279. case CommandIDs::showJuceVersion:
  280. result.setInfo ("Download the latest JUCE version", "Checks online for any Juce updates", CommandCategories::general, 0);
  281. break;
  282. default:
  283. JUCEApplication::getCommandInfo (commandID, result);
  284. break;
  285. }
  286. }
  287. bool perform (const InvocationInfo& info)
  288. {
  289. switch (info.commandID)
  290. {
  291. case CommandIDs::newProject: createNewProject(); break;
  292. case CommandIDs::open: askUserToOpenFile(); break;
  293. case CommandIDs::showPrefs: showPrefsPanel(); break;
  294. case CommandIDs::saveAll: OpenDocumentManager::getInstance()->saveAll(); break;
  295. case CommandIDs::closeAllDocuments: closeAllDocuments (true); break;
  296. case CommandIDs::showJuceVersion:
  297. {
  298. ModuleList list (ModuleList::getDefaultModulesFolder (nullptr));
  299. JuceUpdater::show (list, mainWindows[0]);
  300. break;
  301. }
  302. default: return JUCEApplication::perform (info);
  303. }
  304. return true;
  305. }
  306. //==============================================================================
  307. void showPrefsPanel()
  308. {
  309. jassertfalse;
  310. }
  311. void createNewProject()
  312. {
  313. MainWindow* mw = createNewMainWindow();
  314. ScopedPointer <Project> newProj (NewProjectWizard::runNewProjectWizard (mw));
  315. if (newProj != nullptr)
  316. {
  317. mw->setProject (newProj.release());
  318. mw->makeVisible();
  319. }
  320. else
  321. {
  322. closeWindow (mw);
  323. }
  324. }
  325. void askUserToOpenFile()
  326. {
  327. FileChooser fc ("Open File");
  328. if (fc.browseForFileToOpen())
  329. openFile (fc.getResult());
  330. }
  331. bool openFile (const File& file)
  332. {
  333. for (int j = mainWindows.size(); --j >= 0;)
  334. {
  335. if (mainWindows.getUnchecked(j)->getProject() != nullptr
  336. && mainWindows.getUnchecked(j)->getProject()->getFile() == file)
  337. {
  338. mainWindows.getUnchecked(j)->toFront (true);
  339. return true;
  340. }
  341. }
  342. if (file.hasFileExtension (Project::projectFileExtension))
  343. {
  344. ScopedPointer <Project> newDoc (new Project (file));
  345. if (newDoc->loadFrom (file, true))
  346. {
  347. MainWindow* w = getOrCreateEmptyWindow();
  348. w->setProject (newDoc.release());
  349. w->makeVisible();
  350. return true;
  351. }
  352. }
  353. else if (file.exists())
  354. {
  355. MainWindow* w = getOrCreateFrontmostWindow();
  356. const bool ok = w->openFile (file);
  357. w->makeVisible();
  358. return ok;
  359. }
  360. return false;
  361. }
  362. bool closeAllDocuments (bool askUserToSave)
  363. {
  364. for (int i = OpenDocumentManager::getInstance()->getNumOpenDocuments(); --i >= 0;)
  365. {
  366. OpenDocumentManager::Document* doc = OpenDocumentManager::getInstance()->getOpenDocument (i);
  367. for (int j = mainWindows.size(); --j >= 0;)
  368. mainWindows.getUnchecked(j)->getProjectContentComponent()->hideDocument (doc);
  369. if (! OpenDocumentManager::getInstance()->closeDocument (i, askUserToSave))
  370. return false;
  371. }
  372. return true;
  373. }
  374. void updateRecentProjectList()
  375. {
  376. Array<File> projects;
  377. for (int i = 0; i < mainWindows.size(); ++i)
  378. {
  379. MainWindow* mw = mainWindows[i];
  380. if (mw != nullptr && mw->getProject() != nullptr)
  381. projects.add (mw->getProject()->getFile());
  382. }
  383. StoredSettings::getInstance()->setLastProjects (projects);
  384. }
  385. ScopedPointer<MainMenuModel> menuModel;
  386. private:
  387. OwnedArray <MainWindow> mainWindows;
  388. MainWindow* createNewMainWindow()
  389. {
  390. MainWindow* mw = new MainWindow();
  391. for (int i = mainWindows.size(); --i >= 0;)
  392. if (mw->getBounds() == mainWindows.getUnchecked(i)->getBounds())
  393. mw->setBounds (mw->getBounds().translated (20, 20));
  394. mainWindows.add (mw);
  395. mw->restoreWindowPosition();
  396. return mw;
  397. }
  398. MainWindow* getOrCreateFrontmostWindow()
  399. {
  400. if (mainWindows.size() == 0)
  401. return createNewMainWindow();
  402. for (int i = Desktop::getInstance().getNumComponents(); --i >= 0;)
  403. {
  404. MainWindow* mw = dynamic_cast <MainWindow*> (Desktop::getInstance().getComponent (i));
  405. if (mainWindows.contains (mw))
  406. return mw;
  407. }
  408. return mainWindows.getLast();
  409. }
  410. MainWindow* getOrCreateEmptyWindow()
  411. {
  412. if (mainWindows.size() == 0)
  413. return createNewMainWindow();
  414. for (int i = Desktop::getInstance().getNumComponents(); --i >= 0;)
  415. {
  416. MainWindow* mw = dynamic_cast <MainWindow*> (Desktop::getInstance().getComponent (i));
  417. if (mainWindows.contains (mw) && mw->getProject() == nullptr)
  418. return mw;
  419. }
  420. return createNewMainWindow();
  421. }
  422. //==============================================================================
  423. static bool cancelAnyModalComponents()
  424. {
  425. const int numModal = ModalComponentManager::getInstance()->getNumModalComponents();
  426. for (int i = numModal; --i >= 0;)
  427. if (ModalComponentManager::getInstance()->getModalComponent(i) != nullptr)
  428. ModalComponentManager::getInstance()->getModalComponent(i)->exitModalState (0);
  429. return numModal > 0;
  430. }
  431. class AsyncQuitRetrier : public Timer
  432. {
  433. public:
  434. AsyncQuitRetrier() { startTimer (500); }
  435. void timerCallback()
  436. {
  437. stopTimer();
  438. delete this;
  439. if (JUCEApplication::getInstance() != nullptr)
  440. JUCEApplication::getInstance()->systemRequestedQuit();
  441. }
  442. JUCE_DECLARE_NON_COPYABLE (AsyncQuitRetrier);
  443. };
  444. };
  445. #endif // __JUCER_APPLICATION_JUCEHEADER__