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.

534 lines
16KB

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