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.

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