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.

481 lines
13KB

  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. #include "../jucer_Headers.h"
  19. #include "jucer_Application.h"
  20. #include "jucer_MainWindow.h"
  21. #include "jucer_OpenDocumentManager.h"
  22. #include "../Code Editor/jucer_SourceCodeEditor.h"
  23. #include "../Project/jucer_NewProjectWizard.h"
  24. ScopedPointer<ApplicationCommandManager> commandManager;
  25. //==============================================================================
  26. MainWindow::MainWindow()
  27. : DocumentWindow (JucerApplication::getApp()->getApplicationName(),
  28. Colour::greyLevel (0.6f),
  29. DocumentWindow::allButtons,
  30. false)
  31. {
  32. setUsingNativeTitleBar (true);
  33. createProjectContentCompIfNeeded();
  34. #if ! JUCE_MAC
  35. setMenuBar (JucerApplication::getApp()->menuModel);
  36. #endif
  37. setResizable (true, false);
  38. centreWithSize (800, 600);
  39. // Register all the app commands..
  40. commandManager->registerAllCommandsForTarget (this);
  41. commandManager->registerAllCommandsForTarget (getProjectContentComponent());
  42. // update key mappings..
  43. {
  44. commandManager->getKeyMappings()->resetToDefaultMappings();
  45. ScopedPointer <XmlElement> keys (getAppProperties().getXmlValue ("keyMappings"));
  46. if (keys != nullptr)
  47. commandManager->getKeyMappings()->restoreFromXml (*keys);
  48. addKeyListener (commandManager->getKeyMappings());
  49. }
  50. // don't want the window to take focus when the title-bar is clicked..
  51. setWantsKeyboardFocus (false);
  52. //getPeer()->setCurrentRenderingEngine (0);
  53. getLookAndFeel().setColour (ColourSelector::backgroundColourId, Colours::transparentBlack);
  54. }
  55. MainWindow::~MainWindow()
  56. {
  57. #if ! JUCE_MAC
  58. setMenuBar (nullptr);
  59. #endif
  60. removeKeyListener (commandManager->getKeyMappings());
  61. // save the current size and position to our settings file..
  62. getAppProperties().setValue ("lastMainWindowPos", getWindowStateAsString());
  63. clearContentComponent();
  64. currentProject = nullptr;
  65. }
  66. void MainWindow::createProjectContentCompIfNeeded()
  67. {
  68. if (getProjectContentComponent() == nullptr)
  69. {
  70. clearContentComponent();
  71. setContentOwned (JucerApplication::getApp()->createProjectContentComponent(), false);
  72. }
  73. }
  74. void MainWindow::makeVisible()
  75. {
  76. restoreWindowPosition();
  77. setVisible (true);
  78. addToDesktop(); // (must add before restoring size so that fullscreen will work)
  79. restoreWindowPosition();
  80. getContentComponent()->grabKeyboardFocus();
  81. }
  82. ProjectContentComponent* MainWindow::getProjectContentComponent() const
  83. {
  84. return dynamic_cast <ProjectContentComponent*> (getContentComponent());
  85. }
  86. void MainWindow::closeButtonPressed()
  87. {
  88. JucerApplication::getApp()->mainWindowList.closeWindow (this);
  89. }
  90. bool MainWindow::closeProject (Project* project)
  91. {
  92. jassert (project == currentProject && project != nullptr);
  93. if (project == nullptr)
  94. return true;
  95. getAppProperties().setValue (getProjectWindowPosName(), getWindowStateAsString());
  96. if (! JucerApplication::getApp()->openDocumentManager.closeAllDocumentsUsingProject (*project, true))
  97. return false;
  98. ProjectContentComponent* const pcc = getProjectContentComponent();
  99. if (pcc != nullptr)
  100. pcc->saveTreeViewState();
  101. FileBasedDocument::SaveResult r = project->saveIfNeededAndUserAgrees();
  102. if (r == FileBasedDocument::savedOk)
  103. {
  104. setProject (nullptr);
  105. return true;
  106. }
  107. return false;
  108. }
  109. bool MainWindow::closeCurrentProject()
  110. {
  111. return currentProject == nullptr || closeProject (currentProject);
  112. }
  113. void MainWindow::setProject (Project* newProject)
  114. {
  115. createProjectContentCompIfNeeded();
  116. getProjectContentComponent()->setProject (newProject);
  117. currentProject = newProject;
  118. commandManager->commandStatusChanged();
  119. }
  120. void MainWindow::restoreWindowPosition()
  121. {
  122. String windowState;
  123. if (currentProject != nullptr)
  124. windowState = getAppProperties().getValue (getProjectWindowPosName());
  125. if (windowState.isEmpty())
  126. windowState = getAppProperties().getValue ("lastMainWindowPos");
  127. restoreWindowStateFromString (windowState);
  128. }
  129. bool MainWindow::canOpenFile (const File& file) const
  130. {
  131. return file.hasFileExtension (Project::projectFileExtension)
  132. || JucerApplication::getApp()->openDocumentManager.canOpenFile (file);
  133. }
  134. bool MainWindow::openFile (const File& file)
  135. {
  136. createProjectContentCompIfNeeded();
  137. if (file.hasFileExtension (Project::projectFileExtension))
  138. {
  139. ScopedPointer <Project> newDoc (new Project (file));
  140. if (newDoc->loadFrom (file, true)
  141. && closeCurrentProject())
  142. {
  143. setProject (newDoc.release());
  144. return true;
  145. }
  146. }
  147. else if (file.exists())
  148. {
  149. return getProjectContentComponent()->showEditorForFile (file);
  150. }
  151. return false;
  152. }
  153. bool MainWindow::isInterestedInFileDrag (const StringArray& filenames)
  154. {
  155. for (int i = filenames.size(); --i >= 0;)
  156. if (canOpenFile (filenames[i]))
  157. return true;
  158. return false;
  159. }
  160. void MainWindow::filesDropped (const StringArray& filenames, int mouseX, int mouseY)
  161. {
  162. for (int i = filenames.size(); --i >= 0;)
  163. {
  164. const File f (filenames[i]);
  165. if (canOpenFile (f) && openFile (f))
  166. break;
  167. }
  168. }
  169. void MainWindow::activeWindowStatusChanged()
  170. {
  171. DocumentWindow::activeWindowStatusChanged();
  172. if (getProjectContentComponent() != nullptr)
  173. getProjectContentComponent()->updateMissingFileStatuses();
  174. JucerApplication::getApp()->openDocumentManager.reloadModifiedFiles();
  175. }
  176. void MainWindow::updateTitle (const String& documentName)
  177. {
  178. String name (JucerApplication::getApp()->getApplicationName());
  179. if (currentProject != nullptr)
  180. name = currentProject->getDocumentTitle() + " - " + name;
  181. if (documentName.isNotEmpty())
  182. name = documentName + " - " + name;
  183. setName (name);
  184. }
  185. void MainWindow::showNewProjectWizard()
  186. {
  187. jassert (currentProject == nullptr);
  188. setContentOwned (NewProjectWizard::createComponent(), true);
  189. makeVisible();
  190. }
  191. //==============================================================================
  192. ApplicationCommandTarget* MainWindow::getNextCommandTarget()
  193. {
  194. return 0;
  195. }
  196. void MainWindow::getAllCommands (Array <CommandID>& commands)
  197. {
  198. const CommandID ids[] = { CommandIDs::closeWindow };
  199. commands.addArray (ids, numElementsInArray (ids));
  200. }
  201. void MainWindow::getCommandInfo (const CommandID commandID, ApplicationCommandInfo& result)
  202. {
  203. switch (commandID)
  204. {
  205. case CommandIDs::closeWindow:
  206. result.setInfo ("Close Window", "Closes the current window", CommandCategories::general, 0);
  207. result.defaultKeypresses.add (KeyPress ('w', ModifierKeys::commandModifier, 0));
  208. break;
  209. default:
  210. break;
  211. }
  212. }
  213. bool MainWindow::perform (const InvocationInfo& info)
  214. {
  215. switch (info.commandID)
  216. {
  217. case CommandIDs::closeWindow:
  218. closeButtonPressed();
  219. break;
  220. default:
  221. return false;
  222. }
  223. return true;
  224. }
  225. //==============================================================================
  226. MainWindowList::MainWindowList()
  227. {
  228. }
  229. void MainWindowList::forceCloseAllWindows()
  230. {
  231. windows.clear();
  232. }
  233. bool MainWindowList::askAllWindowsToClose()
  234. {
  235. saveCurrentlyOpenProjectList();
  236. while (windows.size() > 0)
  237. {
  238. if (! windows[0]->closeCurrentProject())
  239. return false;
  240. windows.remove (0);
  241. }
  242. return true;
  243. }
  244. void MainWindowList::createWindowIfNoneAreOpen()
  245. {
  246. if (windows.size() == 0)
  247. createNewMainWindow()->makeVisible();
  248. }
  249. void MainWindowList::closeWindow (MainWindow* w)
  250. {
  251. jassert (windows.contains (w));
  252. #if ! JUCE_MAC
  253. if (windows.size() == 1)
  254. {
  255. JUCEApplication::getInstance()->systemRequestedQuit();
  256. }
  257. else
  258. #endif
  259. {
  260. if (w->closeCurrentProject())
  261. {
  262. windows.removeObject (w);
  263. saveCurrentlyOpenProjectList();
  264. }
  265. }
  266. }
  267. void MainWindowList::openDocument (OpenDocumentManager::Document* doc)
  268. {
  269. MainWindow* const w = getOrCreateFrontmostWindow();
  270. w->makeVisible();
  271. w->getProjectContentComponent()->showDocument (doc);
  272. avoidSuperimposedWindows (w);
  273. }
  274. bool MainWindowList::openFile (const File& file)
  275. {
  276. for (int i = windows.size(); --i >= 0;)
  277. {
  278. MainWindow* const w = windows.getUnchecked(i);
  279. if (w->getProject() != nullptr && w->getProject()->getFile() == file)
  280. {
  281. w->toFront (true);
  282. return true;
  283. }
  284. }
  285. if (file.hasFileExtension (Project::projectFileExtension))
  286. {
  287. ScopedPointer <Project> newDoc (new Project (file));
  288. if (newDoc->loadFrom (file, true))
  289. {
  290. MainWindow* const w = getOrCreateEmptyWindow();
  291. w->setProject (newDoc.release());
  292. w->makeVisible();
  293. avoidSuperimposedWindows (w);
  294. return true;
  295. }
  296. }
  297. else if (file.exists())
  298. {
  299. MainWindow* const w = getOrCreateFrontmostWindow();
  300. const bool ok = w->openFile (file);
  301. w->makeVisible();
  302. avoidSuperimposedWindows (w);
  303. return ok;
  304. }
  305. return false;
  306. }
  307. MainWindow* MainWindowList::createNewMainWindow()
  308. {
  309. MainWindow* const w = new MainWindow();
  310. windows.add (w);
  311. w->restoreWindowPosition();
  312. avoidSuperimposedWindows (w);
  313. return w;
  314. }
  315. MainWindow* MainWindowList::getOrCreateFrontmostWindow()
  316. {
  317. if (windows.size() == 0)
  318. return createNewMainWindow();
  319. for (int i = Desktop::getInstance().getNumComponents(); --i >= 0;)
  320. {
  321. MainWindow* mw = dynamic_cast <MainWindow*> (Desktop::getInstance().getComponent (i));
  322. if (windows.contains (mw))
  323. return mw;
  324. }
  325. return windows.getLast();
  326. }
  327. MainWindow* MainWindowList::getOrCreateEmptyWindow()
  328. {
  329. if (windows.size() == 0)
  330. return createNewMainWindow();
  331. for (int i = Desktop::getInstance().getNumComponents(); --i >= 0;)
  332. {
  333. MainWindow* mw = dynamic_cast <MainWindow*> (Desktop::getInstance().getComponent (i));
  334. if (windows.contains (mw) && mw->getProject() == nullptr)
  335. return mw;
  336. }
  337. return createNewMainWindow();
  338. }
  339. void MainWindowList::avoidSuperimposedWindows (MainWindow* const mw)
  340. {
  341. for (int i = windows.size(); --i >= 0;)
  342. {
  343. MainWindow* const other = windows.getUnchecked(i);
  344. const Rectangle<int> b1 (mw->getBounds());
  345. const Rectangle<int> b2 (other->getBounds());
  346. if (mw != other
  347. && std::abs (b1.getX() - b2.getX()) < 3
  348. && std::abs (b1.getY() - b2.getY()) < 3
  349. && std::abs (b1.getRight() - b2.getRight()) < 3
  350. && std::abs (b1.getBottom() - b2.getBottom()) < 3)
  351. {
  352. int dx = 40, dy = 30;
  353. if (b1.getCentreX() >= mw->getScreenBounds().getCentreX()) dx = -dx;
  354. if (b1.getCentreY() >= mw->getScreenBounds().getCentreY()) dy = -dy;
  355. mw->setBounds (b1.translated (dx, dy));
  356. }
  357. }
  358. }
  359. void MainWindowList::saveCurrentlyOpenProjectList()
  360. {
  361. Array<File> projects;
  362. Desktop& desktop = Desktop::getInstance();
  363. for (int i = 0; i < desktop.getNumComponents(); ++i)
  364. {
  365. MainWindow* const mw = dynamic_cast <MainWindow*> (desktop.getComponent(i));
  366. if (mw != nullptr && mw->getProject() != nullptr)
  367. projects.add (mw->getProject()->getFile());
  368. }
  369. getAppSettings().setLastProjects (projects);
  370. }
  371. void MainWindowList::reopenLastProjects()
  372. {
  373. Array<File> projects (getAppSettings().getLastProjects());
  374. for (int i = 0; i < projects.size(); ++ i)
  375. openFile (projects.getReference(i));
  376. }