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.

527 lines
15KB

  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. MultiDocumentPanelWindow::MultiDocumentPanelWindow (const Colour& backgroundColour)
  19. : DocumentWindow (String::empty, backgroundColour,
  20. DocumentWindow::maximiseButton | DocumentWindow::closeButton, false)
  21. {
  22. }
  23. MultiDocumentPanelWindow::~MultiDocumentPanelWindow()
  24. {
  25. }
  26. //==============================================================================
  27. void MultiDocumentPanelWindow::maximiseButtonPressed()
  28. {
  29. MultiDocumentPanel* const owner = getOwner();
  30. jassert (owner != nullptr); // these windows are only designed to be used inside a MultiDocumentPanel!
  31. if (owner != nullptr)
  32. owner->setLayoutMode (MultiDocumentPanel::MaximisedWindowsWithTabs);
  33. }
  34. void MultiDocumentPanelWindow::closeButtonPressed()
  35. {
  36. MultiDocumentPanel* const owner = getOwner();
  37. jassert (owner != nullptr); // these windows are only designed to be used inside a MultiDocumentPanel!
  38. if (owner != nullptr)
  39. owner->closeDocument (getContentComponent(), true);
  40. }
  41. void MultiDocumentPanelWindow::activeWindowStatusChanged()
  42. {
  43. DocumentWindow::activeWindowStatusChanged();
  44. updateOrder();
  45. }
  46. void MultiDocumentPanelWindow::broughtToFront()
  47. {
  48. DocumentWindow::broughtToFront();
  49. updateOrder();
  50. }
  51. void MultiDocumentPanelWindow::updateOrder()
  52. {
  53. MultiDocumentPanel* const owner = getOwner();
  54. if (owner != nullptr)
  55. owner->updateOrder();
  56. }
  57. MultiDocumentPanel* MultiDocumentPanelWindow::getOwner() const noexcept
  58. {
  59. return findParentComponentOfClass<MultiDocumentPanel>();
  60. }
  61. //==============================================================================
  62. class MDITabbedComponentInternal : public TabbedComponent
  63. {
  64. public:
  65. MDITabbedComponentInternal()
  66. : TabbedComponent (TabbedButtonBar::TabsAtTop)
  67. {
  68. }
  69. void currentTabChanged (int, const String&)
  70. {
  71. MultiDocumentPanel* const owner = findParentComponentOfClass<MultiDocumentPanel>();
  72. if (owner != nullptr)
  73. owner->updateOrder();
  74. }
  75. };
  76. //==============================================================================
  77. MultiDocumentPanel::MultiDocumentPanel()
  78. : mode (MaximisedWindowsWithTabs),
  79. backgroundColour (Colours::lightblue),
  80. maximumNumDocuments (0),
  81. numDocsBeforeTabsUsed (0)
  82. {
  83. setOpaque (true);
  84. }
  85. MultiDocumentPanel::~MultiDocumentPanel()
  86. {
  87. closeAllDocuments (false);
  88. }
  89. //==============================================================================
  90. namespace MultiDocHelpers
  91. {
  92. static bool shouldDeleteComp (Component* const c)
  93. {
  94. return c->getProperties() ["mdiDocumentDelete_"];
  95. }
  96. }
  97. bool MultiDocumentPanel::closeAllDocuments (const bool checkItsOkToCloseFirst)
  98. {
  99. while (components.size() > 0)
  100. if (! closeDocument (components.getLast(), checkItsOkToCloseFirst))
  101. return false;
  102. return true;
  103. }
  104. MultiDocumentPanelWindow* MultiDocumentPanel::createNewDocumentWindow()
  105. {
  106. return new MultiDocumentPanelWindow (backgroundColour);
  107. }
  108. void MultiDocumentPanel::addWindow (Component* component)
  109. {
  110. MultiDocumentPanelWindow* const dw = createNewDocumentWindow();
  111. dw->setResizable (true, false);
  112. dw->setContentNonOwned (component, true);
  113. dw->setName (component->getName());
  114. const var bkg (component->getProperties() ["mdiDocumentBkg_"]);
  115. dw->setBackgroundColour (bkg.isVoid() ? backgroundColour : Colour ((uint32) static_cast <int> (bkg)));
  116. int x = 4;
  117. Component* const topComp = getChildComponent (getNumChildComponents() - 1);
  118. if (topComp != nullptr && topComp->getX() == x && topComp->getY() == x)
  119. x += 16;
  120. dw->setTopLeftPosition (x, x);
  121. const var pos (component->getProperties() ["mdiDocumentPos_"]);
  122. if (pos.toString().isNotEmpty())
  123. dw->restoreWindowStateFromString (pos.toString());
  124. addAndMakeVisible (dw);
  125. dw->toFront (true);
  126. }
  127. bool MultiDocumentPanel::addDocument (Component* const component,
  128. const Colour& docColour,
  129. const bool deleteWhenRemoved)
  130. {
  131. // If you try passing a full DocumentWindow or ResizableWindow in here, you'll end up
  132. // with a frame-within-a-frame! Just pass in the bare content component.
  133. jassert (dynamic_cast <ResizableWindow*> (component) == nullptr);
  134. if (component == nullptr || (maximumNumDocuments > 0 && components.size() >= maximumNumDocuments))
  135. return false;
  136. components.add (component);
  137. component->getProperties().set ("mdiDocumentDelete_", deleteWhenRemoved);
  138. component->getProperties().set ("mdiDocumentBkg_", (int) docColour.getARGB());
  139. component->addComponentListener (this);
  140. if (mode == FloatingWindows)
  141. {
  142. if (isFullscreenWhenOneDocument())
  143. {
  144. if (components.size() == 1)
  145. {
  146. addAndMakeVisible (component);
  147. }
  148. else
  149. {
  150. if (components.size() == 2)
  151. addWindow (components.getFirst());
  152. addWindow (component);
  153. }
  154. }
  155. else
  156. {
  157. addWindow (component);
  158. }
  159. }
  160. else
  161. {
  162. if (tabComponent == nullptr && components.size() > numDocsBeforeTabsUsed)
  163. {
  164. addAndMakeVisible (tabComponent = new MDITabbedComponentInternal());
  165. Array <Component*> temp (components);
  166. for (int i = 0; i < temp.size(); ++i)
  167. tabComponent->addTab (temp[i]->getName(), docColour, temp[i], false);
  168. resized();
  169. }
  170. else
  171. {
  172. if (tabComponent != nullptr)
  173. tabComponent->addTab (component->getName(), docColour, component, false);
  174. else
  175. addAndMakeVisible (component);
  176. }
  177. setActiveDocument (component);
  178. }
  179. resized();
  180. activeDocumentChanged();
  181. return true;
  182. }
  183. bool MultiDocumentPanel::closeDocument (Component* component,
  184. const bool checkItsOkToCloseFirst)
  185. {
  186. if (components.contains (component))
  187. {
  188. if (checkItsOkToCloseFirst && ! tryToCloseDocument (component))
  189. return false;
  190. component->removeComponentListener (this);
  191. const bool shouldDelete = MultiDocHelpers::shouldDeleteComp (component);
  192. component->getProperties().remove ("mdiDocumentDelete_");
  193. component->getProperties().remove ("mdiDocumentBkg_");
  194. if (mode == FloatingWindows)
  195. {
  196. for (int i = getNumChildComponents(); --i >= 0;)
  197. {
  198. MultiDocumentPanelWindow* const dw = dynamic_cast <MultiDocumentPanelWindow*> (getChildComponent (i));
  199. if (dw != nullptr && dw->getContentComponent() == component)
  200. {
  201. ScopedPointer<MultiDocumentPanelWindow> (dw)->clearContentComponent();
  202. break;
  203. }
  204. }
  205. if (shouldDelete)
  206. delete component;
  207. components.removeValue (component);
  208. if (isFullscreenWhenOneDocument() && components.size() == 1)
  209. {
  210. for (int i = getNumChildComponents(); --i >= 0;)
  211. {
  212. ScopedPointer<MultiDocumentPanelWindow> dw (dynamic_cast <MultiDocumentPanelWindow*> (getChildComponent (i)));
  213. if (dw != nullptr)
  214. dw->clearContentComponent();
  215. }
  216. addAndMakeVisible (components.getFirst());
  217. }
  218. }
  219. else
  220. {
  221. jassert (components.indexOf (component) >= 0);
  222. if (tabComponent != nullptr)
  223. {
  224. for (int i = tabComponent->getNumTabs(); --i >= 0;)
  225. if (tabComponent->getTabContentComponent (i) == component)
  226. tabComponent->removeTab (i);
  227. }
  228. else
  229. {
  230. removeChildComponent (component);
  231. }
  232. if (shouldDelete)
  233. delete component;
  234. if (tabComponent != nullptr && tabComponent->getNumTabs() <= numDocsBeforeTabsUsed)
  235. tabComponent = nullptr;
  236. components.removeValue (component);
  237. if (components.size() > 0 && tabComponent == nullptr)
  238. addAndMakeVisible (components.getFirst());
  239. }
  240. resized();
  241. activeDocumentChanged();
  242. }
  243. else
  244. {
  245. jassertfalse;
  246. }
  247. return true;
  248. }
  249. int MultiDocumentPanel::getNumDocuments() const noexcept
  250. {
  251. return components.size();
  252. }
  253. Component* MultiDocumentPanel::getDocument (const int index) const noexcept
  254. {
  255. return components [index];
  256. }
  257. Component* MultiDocumentPanel::getActiveDocument() const noexcept
  258. {
  259. if (mode == FloatingWindows)
  260. {
  261. for (int i = getNumChildComponents(); --i >= 0;)
  262. {
  263. MultiDocumentPanelWindow* const dw = dynamic_cast <MultiDocumentPanelWindow*> (getChildComponent (i));
  264. if (dw != nullptr && dw->isActiveWindow())
  265. return dw->getContentComponent();
  266. }
  267. }
  268. return components.getLast();
  269. }
  270. void MultiDocumentPanel::setActiveDocument (Component* component)
  271. {
  272. if (mode == FloatingWindows)
  273. {
  274. component = getContainerComp (component);
  275. if (component != nullptr)
  276. component->toFront (true);
  277. }
  278. else if (tabComponent != nullptr)
  279. {
  280. jassert (components.indexOf (component) >= 0);
  281. for (int i = tabComponent->getNumTabs(); --i >= 0;)
  282. {
  283. if (tabComponent->getTabContentComponent (i) == component)
  284. {
  285. tabComponent->setCurrentTabIndex (i);
  286. break;
  287. }
  288. }
  289. }
  290. else
  291. {
  292. component->grabKeyboardFocus();
  293. }
  294. }
  295. void MultiDocumentPanel::activeDocumentChanged()
  296. {
  297. }
  298. void MultiDocumentPanel::setMaximumNumDocuments (const int newNumber)
  299. {
  300. maximumNumDocuments = newNumber;
  301. }
  302. void MultiDocumentPanel::useFullscreenWhenOneDocument (const bool shouldUseTabs)
  303. {
  304. numDocsBeforeTabsUsed = shouldUseTabs ? 1 : 0;
  305. }
  306. bool MultiDocumentPanel::isFullscreenWhenOneDocument() const noexcept
  307. {
  308. return numDocsBeforeTabsUsed != 0;
  309. }
  310. //==============================================================================
  311. void MultiDocumentPanel::setLayoutMode (const LayoutMode newLayoutMode)
  312. {
  313. if (mode != newLayoutMode)
  314. {
  315. mode = newLayoutMode;
  316. if (mode == FloatingWindows)
  317. {
  318. tabComponent = nullptr;
  319. }
  320. else
  321. {
  322. for (int i = getNumChildComponents(); --i >= 0;)
  323. {
  324. MultiDocumentPanelWindow* const dw = dynamic_cast <MultiDocumentPanelWindow*> (getChildComponent (i));
  325. if (dw != nullptr)
  326. {
  327. dw->getContentComponent()->getProperties().set ("mdiDocumentPos_", dw->getWindowStateAsString());
  328. dw->clearContentComponent();
  329. delete dw;
  330. }
  331. }
  332. }
  333. resized();
  334. const Array <Component*> tempComps (components);
  335. components.clear();
  336. for (int i = 0; i < tempComps.size(); ++i)
  337. {
  338. Component* const c = tempComps.getUnchecked(i);
  339. addDocument (c,
  340. Colour ((uint32) static_cast <int> (c->getProperties().getWithDefault ("mdiDocumentBkg_", (int) Colours::white.getARGB()))),
  341. MultiDocHelpers::shouldDeleteComp (c));
  342. }
  343. }
  344. }
  345. void MultiDocumentPanel::setBackgroundColour (const Colour& newBackgroundColour)
  346. {
  347. if (backgroundColour != newBackgroundColour)
  348. {
  349. backgroundColour = newBackgroundColour;
  350. setOpaque (newBackgroundColour.isOpaque());
  351. repaint();
  352. }
  353. }
  354. //==============================================================================
  355. void MultiDocumentPanel::paint (Graphics& g)
  356. {
  357. g.fillAll (backgroundColour);
  358. }
  359. void MultiDocumentPanel::resized()
  360. {
  361. if (mode == MaximisedWindowsWithTabs || components.size() == numDocsBeforeTabsUsed)
  362. {
  363. for (int i = getNumChildComponents(); --i >= 0;)
  364. getChildComponent (i)->setBounds (getLocalBounds());
  365. }
  366. setWantsKeyboardFocus (components.size() == 0);
  367. }
  368. Component* MultiDocumentPanel::getContainerComp (Component* c) const
  369. {
  370. if (mode == FloatingWindows)
  371. {
  372. for (int i = 0; i < getNumChildComponents(); ++i)
  373. {
  374. MultiDocumentPanelWindow* const dw = dynamic_cast <MultiDocumentPanelWindow*> (getChildComponent (i));
  375. if (dw != nullptr && dw->getContentComponent() == c)
  376. {
  377. c = dw;
  378. break;
  379. }
  380. }
  381. }
  382. return c;
  383. }
  384. void MultiDocumentPanel::componentNameChanged (Component&)
  385. {
  386. if (mode == FloatingWindows)
  387. {
  388. for (int i = 0; i < getNumChildComponents(); ++i)
  389. {
  390. MultiDocumentPanelWindow* const dw = dynamic_cast <MultiDocumentPanelWindow*> (getChildComponent (i));
  391. if (dw != nullptr)
  392. dw->setName (dw->getContentComponent()->getName());
  393. }
  394. }
  395. else if (tabComponent != nullptr)
  396. {
  397. for (int i = tabComponent->getNumTabs(); --i >= 0;)
  398. tabComponent->setTabName (i, tabComponent->getTabContentComponent (i)->getName());
  399. }
  400. }
  401. void MultiDocumentPanel::updateOrder()
  402. {
  403. const Array <Component*> oldList (components);
  404. if (mode == FloatingWindows)
  405. {
  406. components.clear();
  407. for (int i = 0; i < getNumChildComponents(); ++i)
  408. {
  409. MultiDocumentPanelWindow* const dw = dynamic_cast <MultiDocumentPanelWindow*> (getChildComponent (i));
  410. if (dw != nullptr)
  411. components.add (dw->getContentComponent());
  412. }
  413. }
  414. else
  415. {
  416. if (tabComponent != nullptr)
  417. {
  418. Component* const current = tabComponent->getCurrentContentComponent();
  419. if (current != nullptr)
  420. {
  421. components.removeValue (current);
  422. components.add (current);
  423. }
  424. }
  425. }
  426. if (components != oldList)
  427. activeDocumentChanged();
  428. }