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.

549 lines
16KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-7 by Raw Material Software ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the
  7. GNU General Public License, as published by the Free Software Foundation;
  8. either version 2 of the License, or (at your option) any later version.
  9. JUCE is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with JUCE; if not, visit www.gnu.org/licenses or write to the
  15. Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  16. Boston, MA 02111-1307 USA
  17. ------------------------------------------------------------------------------
  18. If you'd like to release a closed-source product which uses JUCE, commercial
  19. licenses are also available: visit www.rawmaterialsoftware.com/juce for
  20. more information.
  21. ==============================================================================
  22. */
  23. #include "../../../../juce_core/basics/juce_StandardHeader.h"
  24. BEGIN_JUCE_NAMESPACE
  25. #include "juce_MultiDocumentPanel.h"
  26. #include "../lookandfeel/juce_LookAndFeel.h"
  27. //==============================================================================
  28. MultiDocumentPanelWindow::MultiDocumentPanelWindow (const Colour& backgroundColour)
  29. : DocumentWindow (String::empty, backgroundColour,
  30. DocumentWindow::maximiseButton | DocumentWindow::closeButton, false)
  31. {
  32. }
  33. MultiDocumentPanelWindow::~MultiDocumentPanelWindow()
  34. {
  35. }
  36. //==============================================================================
  37. void MultiDocumentPanelWindow::maximiseButtonPressed()
  38. {
  39. MultiDocumentPanel* const owner = getOwner();
  40. jassert (owner != 0); // these windows are only designed to be used inside a MultiDocumentPanel!
  41. if (owner != 0)
  42. owner->setLayoutMode (MultiDocumentPanel::MaximisedWindowsWithTabs);
  43. }
  44. void MultiDocumentPanelWindow::closeButtonPressed()
  45. {
  46. MultiDocumentPanel* const owner = getOwner();
  47. jassert (owner != 0); // these windows are only designed to be used inside a MultiDocumentPanel!
  48. if (owner != 0)
  49. owner->closeDocument (getContentComponent(), true);
  50. }
  51. void MultiDocumentPanelWindow::activeWindowStatusChanged()
  52. {
  53. DocumentWindow::activeWindowStatusChanged();
  54. updateOrder();
  55. }
  56. void MultiDocumentPanelWindow::broughtToFront()
  57. {
  58. DocumentWindow::broughtToFront();
  59. updateOrder();
  60. }
  61. void MultiDocumentPanelWindow::updateOrder()
  62. {
  63. MultiDocumentPanel* const owner = getOwner();
  64. if (owner != 0)
  65. owner->updateOrder();
  66. }
  67. MultiDocumentPanel* MultiDocumentPanelWindow::getOwner() const throw()
  68. {
  69. // (unable to use the syntax findParentComponentOfClass <MultiDocumentPanel> () because of a VC6 compiler bug)
  70. return findParentComponentOfClass ((MultiDocumentPanel*) 0);
  71. }
  72. //==============================================================================
  73. class MDITabbedComponentInternal : public TabbedComponent
  74. {
  75. public:
  76. MDITabbedComponentInternal()
  77. : TabbedComponent (TabbedButtonBar::TabsAtTop)
  78. {
  79. }
  80. ~MDITabbedComponentInternal()
  81. {
  82. }
  83. void currentTabChanged (const int, const String&)
  84. {
  85. // (unable to use the syntax findParentComponentOfClass <MultiDocumentPanel> () because of a VC6 compiler bug)
  86. MultiDocumentPanel* const owner = findParentComponentOfClass ((MultiDocumentPanel*) 0);
  87. if (owner != 0)
  88. owner->updateOrder();
  89. }
  90. };
  91. //==============================================================================
  92. MultiDocumentPanel::MultiDocumentPanel()
  93. : mode (MaximisedWindowsWithTabs),
  94. tabComponent (0),
  95. backgroundColour (Colours::lightblue),
  96. maximumNumDocuments (0),
  97. numDocsBeforeTabsUsed (0)
  98. {
  99. setOpaque (true);
  100. }
  101. MultiDocumentPanel::~MultiDocumentPanel()
  102. {
  103. closeAllDocuments (false);
  104. }
  105. //==============================================================================
  106. static bool shouldDeleteComp (Component* const c)
  107. {
  108. return c->getComponentPropertyBool (T("mdiDocumentDelete_"), false);
  109. }
  110. bool MultiDocumentPanel::closeAllDocuments (const bool checkItsOkToCloseFirst)
  111. {
  112. while (components.size() > 0)
  113. if (! closeDocument (components.getLast(), checkItsOkToCloseFirst))
  114. return false;
  115. return true;
  116. }
  117. MultiDocumentPanelWindow* MultiDocumentPanel::createNewDocumentWindow()
  118. {
  119. return new MultiDocumentPanelWindow (backgroundColour);
  120. }
  121. void MultiDocumentPanel::addWindow (Component* component)
  122. {
  123. MultiDocumentPanelWindow* const dw = createNewDocumentWindow();
  124. dw->setResizable (true, false);
  125. dw->setContentComponent (component, false, true);
  126. dw->setName (component->getName());
  127. dw->setBackgroundColour (component->getComponentPropertyColour (T("mdiDocumentBkg_"), false, backgroundColour));
  128. int x = 4;
  129. Component* const topComp = getChildComponent (getNumChildComponents() - 1);
  130. if (topComp != 0 && topComp->getX() == x && topComp->getY() == x)
  131. x += 16;
  132. dw->setTopLeftPosition (x, x);
  133. if (component->getComponentProperty (T("mdiDocumentPos_"), false, String::empty).isNotEmpty())
  134. dw->restoreWindowStateFromString (component->getComponentProperty (T("mdiDocumentPos_"), false, String::empty));
  135. addAndMakeVisible (dw);
  136. dw->toFront (true);
  137. }
  138. bool MultiDocumentPanel::addDocument (Component* const component,
  139. const Colour& docColour,
  140. const bool deleteWhenRemoved)
  141. {
  142. // If you try passing a full DocumentWindow or ResizableWindow in here, you'll end up
  143. // with a frame-within-a-frame! Just pass in the bare content component.
  144. jassert (dynamic_cast <ResizableWindow*> (component) == 0);
  145. if (component == 0 || (maximumNumDocuments > 0 && components.size() >= maximumNumDocuments))
  146. return false;
  147. components.add (component);
  148. component->setComponentProperty (T("mdiDocumentDelete_"), deleteWhenRemoved);
  149. component->setComponentProperty (T("mdiDocumentBkg_"), docColour);
  150. component->addComponentListener (this);
  151. if (mode == FloatingWindows)
  152. {
  153. if (isFullscreenWhenOneDocument())
  154. {
  155. if (components.size() == 1)
  156. {
  157. addAndMakeVisible (component);
  158. }
  159. else
  160. {
  161. if (components.size() == 2)
  162. addWindow (components.getFirst());
  163. addWindow (component);
  164. }
  165. }
  166. else
  167. {
  168. addWindow (component);
  169. }
  170. }
  171. else
  172. {
  173. if (tabComponent == 0 && components.size() > numDocsBeforeTabsUsed)
  174. {
  175. addAndMakeVisible (tabComponent = new MDITabbedComponentInternal());
  176. Array <Component*> temp (components);
  177. for (int i = 0; i < temp.size(); ++i)
  178. tabComponent->addTab (temp[i]->getName(), docColour, temp[i], false);
  179. resized();
  180. }
  181. else
  182. {
  183. if (tabComponent != 0)
  184. tabComponent->addTab (component->getName(), docColour, component, false);
  185. else
  186. addAndMakeVisible (component);
  187. }
  188. setActiveDocument (component);
  189. }
  190. resized();
  191. activeDocumentChanged();
  192. return true;
  193. }
  194. bool MultiDocumentPanel::closeDocument (Component* component,
  195. const bool checkItsOkToCloseFirst)
  196. {
  197. if (components.contains (component))
  198. {
  199. if (checkItsOkToCloseFirst && ! tryToCloseDocument (component))
  200. return false;
  201. component->removeComponentListener (this);
  202. const bool shouldDelete = shouldDeleteComp (component);
  203. component->removeComponentProperty (T("mdiDocumentDelete_"));
  204. component->removeComponentProperty (T("mdiDocumentBkg_"));
  205. if (mode == FloatingWindows)
  206. {
  207. for (int i = getNumChildComponents(); --i >= 0;)
  208. {
  209. MultiDocumentPanelWindow* const dw = dynamic_cast <MultiDocumentPanelWindow*> (getChildComponent (i));
  210. if (dw != 0 && dw->getContentComponent() == component)
  211. {
  212. dw->setContentComponent (0, false);
  213. delete dw;
  214. break;
  215. }
  216. }
  217. if (shouldDelete)
  218. delete component;
  219. components.removeValue (component);
  220. if (isFullscreenWhenOneDocument() && components.size() == 1)
  221. {
  222. for (int i = getNumChildComponents(); --i >= 0;)
  223. {
  224. MultiDocumentPanelWindow* const dw = dynamic_cast <MultiDocumentPanelWindow*> (getChildComponent (i));
  225. if (dw != 0)
  226. {
  227. dw->setContentComponent (0, false);
  228. delete dw;
  229. }
  230. }
  231. addAndMakeVisible (components.getFirst());
  232. }
  233. }
  234. else
  235. {
  236. jassert (components.indexOf (component) >= 0);
  237. if (tabComponent != 0)
  238. {
  239. for (int i = tabComponent->getNumTabs(); --i >= 0;)
  240. if (tabComponent->getTabContentComponent (i) == component)
  241. tabComponent->removeTab (i);
  242. }
  243. else
  244. {
  245. removeChildComponent (component);
  246. }
  247. if (shouldDelete)
  248. delete component;
  249. if (tabComponent != 0 && tabComponent->getNumTabs() <= numDocsBeforeTabsUsed)
  250. deleteAndZero (tabComponent);
  251. components.removeValue (component);
  252. if (components.size() > 0 && tabComponent == 0)
  253. addAndMakeVisible (components.getFirst());
  254. }
  255. resized();
  256. activeDocumentChanged();
  257. }
  258. else
  259. {
  260. jassertfalse
  261. }
  262. return true;
  263. }
  264. int MultiDocumentPanel::getNumDocuments() const throw()
  265. {
  266. return components.size();
  267. }
  268. Component* MultiDocumentPanel::getDocument (const int index) const throw()
  269. {
  270. return components [index];
  271. }
  272. Component* MultiDocumentPanel::getActiveDocument() const throw()
  273. {
  274. if (mode == FloatingWindows)
  275. {
  276. for (int i = getNumChildComponents(); --i >= 0;)
  277. {
  278. MultiDocumentPanelWindow* const dw = dynamic_cast <MultiDocumentPanelWindow*> (getChildComponent (i));
  279. if (dw != 0 && dw->isActiveWindow())
  280. return dw->getContentComponent();
  281. }
  282. }
  283. return components.getLast();
  284. }
  285. void MultiDocumentPanel::setActiveDocument (Component* component)
  286. {
  287. if (mode == FloatingWindows)
  288. {
  289. component = getContainerComp (component);
  290. if (component != 0)
  291. component->toFront (true);
  292. }
  293. else if (tabComponent != 0)
  294. {
  295. jassert (components.indexOf (component) >= 0);
  296. for (int i = tabComponent->getNumTabs(); --i >= 0;)
  297. {
  298. if (tabComponent->getTabContentComponent (i) == component)
  299. {
  300. tabComponent->setCurrentTabIndex (i);
  301. break;
  302. }
  303. }
  304. }
  305. else
  306. {
  307. component->grabKeyboardFocus();
  308. }
  309. }
  310. void MultiDocumentPanel::activeDocumentChanged()
  311. {
  312. }
  313. void MultiDocumentPanel::setMaximumNumDocuments (const int newNumber)
  314. {
  315. maximumNumDocuments = newNumber;
  316. }
  317. void MultiDocumentPanel::useFullscreenWhenOneDocument (const bool shouldUseTabs)
  318. {
  319. numDocsBeforeTabsUsed = shouldUseTabs ? 1 : 0;
  320. }
  321. bool MultiDocumentPanel::isFullscreenWhenOneDocument() const throw()
  322. {
  323. return numDocsBeforeTabsUsed != 0;
  324. }
  325. //==============================================================================
  326. void MultiDocumentPanel::setLayoutMode (const LayoutMode newLayoutMode)
  327. {
  328. if (mode != newLayoutMode)
  329. {
  330. mode = newLayoutMode;
  331. if (mode == FloatingWindows)
  332. {
  333. deleteAndZero (tabComponent);
  334. }
  335. else
  336. {
  337. for (int i = getNumChildComponents(); --i >= 0;)
  338. {
  339. MultiDocumentPanelWindow* const dw = dynamic_cast <MultiDocumentPanelWindow*> (getChildComponent (i));
  340. if (dw != 0)
  341. {
  342. dw->getContentComponent()->setComponentProperty (T("mdiDocumentPos_"), dw->getWindowStateAsString());
  343. dw->setContentComponent (0, false);
  344. delete dw;
  345. }
  346. }
  347. }
  348. resized();
  349. const Array <Component*> tempComps (components);
  350. components.clear();
  351. for (int i = 0; i < tempComps.size(); ++i)
  352. {
  353. Component* const c = tempComps.getUnchecked(i);
  354. addDocument (c,
  355. c->getComponentPropertyColour (T("mdiDocumentBkg_"), false, Colours::white),
  356. shouldDeleteComp (c));
  357. }
  358. }
  359. }
  360. void MultiDocumentPanel::setBackgroundColour (const Colour& newBackgroundColour)
  361. {
  362. if (backgroundColour != newBackgroundColour)
  363. {
  364. backgroundColour = newBackgroundColour;
  365. setOpaque (newBackgroundColour.isOpaque());
  366. repaint();
  367. }
  368. }
  369. //==============================================================================
  370. void MultiDocumentPanel::paint (Graphics& g)
  371. {
  372. g.fillAll (backgroundColour);
  373. }
  374. void MultiDocumentPanel::resized()
  375. {
  376. if (mode == MaximisedWindowsWithTabs || components.size() == numDocsBeforeTabsUsed)
  377. {
  378. for (int i = getNumChildComponents(); --i >= 0;)
  379. getChildComponent (i)->setBounds (0, 0, getWidth(), getHeight());
  380. }
  381. setWantsKeyboardFocus (components.size() == 0);
  382. }
  383. Component* MultiDocumentPanel::getContainerComp (Component* c) const
  384. {
  385. if (mode == FloatingWindows)
  386. {
  387. for (int i = 0; i < getNumChildComponents(); ++i)
  388. {
  389. MultiDocumentPanelWindow* const dw = dynamic_cast <MultiDocumentPanelWindow*> (getChildComponent (i));
  390. if (dw != 0 && dw->getContentComponent() == c)
  391. {
  392. c = dw;
  393. break;
  394. }
  395. }
  396. }
  397. return c;
  398. }
  399. void MultiDocumentPanel::componentNameChanged (Component&)
  400. {
  401. if (mode == FloatingWindows)
  402. {
  403. for (int i = 0; i < getNumChildComponents(); ++i)
  404. {
  405. MultiDocumentPanelWindow* const dw = dynamic_cast <MultiDocumentPanelWindow*> (getChildComponent (i));
  406. if (dw != 0)
  407. dw->setName (dw->getContentComponent()->getName());
  408. }
  409. }
  410. else if (tabComponent != 0)
  411. {
  412. for (int i = tabComponent->getNumTabs(); --i >= 0;)
  413. tabComponent->setTabName (i, tabComponent->getTabContentComponent (i)->getName());
  414. }
  415. }
  416. void MultiDocumentPanel::updateOrder()
  417. {
  418. const Array <Component*> oldList (components);
  419. if (mode == FloatingWindows)
  420. {
  421. components.clear();
  422. for (int i = 0; i < getNumChildComponents(); ++i)
  423. {
  424. MultiDocumentPanelWindow* const dw = dynamic_cast <MultiDocumentPanelWindow*> (getChildComponent (i));
  425. if (dw != 0)
  426. components.add (dw->getContentComponent());
  427. }
  428. }
  429. else
  430. {
  431. if (tabComponent != 0)
  432. {
  433. Component* const current = tabComponent->getCurrentContentComponent();
  434. if (current != 0)
  435. {
  436. components.removeValue (current);
  437. components.add (current);
  438. }
  439. }
  440. }
  441. if (components != oldList)
  442. activeDocumentChanged();
  443. }
  444. END_JUCE_NAMESPACE