Chris@127: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@127: Chris@127: /* Chris@127: Sonic Visualiser Chris@127: An audio file viewer and annotation editor. Chris@127: Centre for Digital Music, Queen Mary, University of London. Chris@182: This file copyright 2006 Chris Cannam and QMUL. Chris@127: Chris@127: This program is free software; you can redistribute it and/or Chris@127: modify it under the terms of the GNU General Public License as Chris@127: published by the Free Software Foundation; either version 2 of the Chris@127: License, or (at your option) any later version. See the file Chris@127: COPYING included with this distribution for more information. Chris@127: */ Chris@127: Chris@127: #include "PaneStack.h" Chris@127: Chris@128: #include "Pane.h" Chris@127: #include "widgets/PropertyStack.h" Chris@323: #include "widgets/IconLoader.h" Chris@500: #include "widgets/ClickableLabel.h" Chris@128: #include "layer/Layer.h" Chris@128: #include "ViewManager.h" Chris@127: Chris@127: #include Chris@127: #include Chris@323: #include Chris@127: #include Chris@127: #include Chris@127: #include Chris@323: #include Chris@127: #include Chris@127: #include Chris@127: Chris@127: #include Chris@127: Chris@247: //#define DEBUG_PANE_STACK 1 Chris@247: Chris@127: PaneStack::PaneStack(QWidget *parent, ViewManager *viewManager) : Chris@127: QFrame(parent), Chris@127: m_currentPane(0), Chris@127: m_splitter(new QSplitter), Chris@127: m_propertyStackStack(new QStackedWidget), Chris@127: m_viewManager(viewManager), Chris@615: m_propertyStackMinWidth(100), Chris@127: m_layoutStyle(PropertyStackPerPaneLayout) Chris@127: { Chris@127: QHBoxLayout *layout = new QHBoxLayout; Chris@127: layout->setMargin(0); Chris@127: layout->setSpacing(0); Chris@127: Chris@127: m_splitter->setOrientation(Qt::Vertical); Chris@127: m_splitter->setOpaqueResize(false); Chris@127: Chris@127: layout->addWidget(m_splitter); Chris@127: layout->setStretchFactor(m_splitter, 1); Chris@127: layout->addWidget(m_propertyStackStack); Chris@127: m_propertyStackStack->hide(); Chris@127: Chris@127: setLayout(layout); Chris@127: } Chris@127: Chris@127: Pane * Chris@127: PaneStack::addPane(bool suppressPropertyBox) Chris@127: { Chris@539: return insertPane(getPaneCount(), suppressPropertyBox); Chris@539: } Chris@539: Chris@539: Pane * Chris@539: PaneStack::insertPane(int index, bool suppressPropertyBox) Chris@539: { Chris@127: QFrame *frame = new QFrame; Chris@495: Chris@495: QGridLayout *layout = new QGridLayout; Chris@127: layout->setMargin(0); Chris@127: layout->setSpacing(2); Chris@127: Chris@323: QPushButton *xButton = new QPushButton(frame); Chris@323: xButton->setIcon(IconLoader().load("cross")); Chris@323: xButton->setFixedSize(QSize(16, 16)); Chris@497: xButton->setFlat(true); Chris@495: layout->addWidget(xButton, 0, 0); Chris@323: connect(xButton, SIGNAL(clicked()), this, SLOT(paneDeleteButtonClicked())); Chris@323: Chris@500: ClickableLabel *currentIndicator = new ClickableLabel(frame); Chris@500: connect(currentIndicator, SIGNAL(clicked()), this, SLOT(indicatorClicked())); Chris@495: layout->addWidget(currentIndicator, 1, 0); Chris@495: layout->setRowStretch(1, 20); Chris@497: currentIndicator->setMinimumWidth(8); Chris@127: currentIndicator->setScaledContents(true); Chris@127: Chris@516: long initialCentreFrame = -1; Chris@516: for (int i = 0; i < m_panes.size(); ++i) { Chris@516: long f = m_panes[i].pane->getCentreFrame(); Chris@516: initialCentreFrame = f; Chris@516: break; Chris@516: } Chris@516: Chris@127: Pane *pane = new Pane(frame); Chris@516: if (initialCentreFrame >= 0) { Chris@516: pane->setViewManager(m_viewManager, initialCentreFrame); Chris@516: } else { Chris@516: pane->setViewManager(m_viewManager); Chris@516: } Chris@495: layout->addWidget(pane, 0, 1, 2, 1); Chris@495: layout->setColumnStretch(1, 20); Chris@127: Chris@127: QWidget *properties = 0; Chris@127: if (suppressPropertyBox) { Chris@127: properties = new QFrame(); Chris@127: } else { Chris@127: properties = new PropertyStack(frame, pane); Chris@127: connect(properties, SIGNAL(propertyContainerSelected(View *, PropertyContainer *)), Chris@127: this, SLOT(propertyContainerSelected(View *, PropertyContainer *))); Chris@190: connect(properties, SIGNAL(viewSelected(View *)), Chris@190: this, SLOT(viewSelected(View *))); Chris@189: connect(properties, SIGNAL(contextHelpChanged(const QString &)), Chris@189: this, SIGNAL(contextHelpChanged(const QString &))); Chris@127: } Chris@127: if (m_layoutStyle == PropertyStackPerPaneLayout) { Chris@495: layout->addWidget(properties, 0, 2, 2, 1); Chris@127: } else { Chris@127: properties->setParent(m_propertyStackStack); Chris@127: m_propertyStackStack->addWidget(properties); Chris@127: } Chris@606: layout->setColumnStretch(2, 0); Chris@127: Chris@127: PaneRec rec; Chris@127: rec.pane = pane; Chris@127: rec.propertyStack = properties; Chris@605: rec.xButton = xButton; Chris@127: rec.currentIndicator = currentIndicator; Chris@127: rec.frame = frame; Chris@127: rec.layout = layout; Chris@127: m_panes.push_back(rec); Chris@127: Chris@127: frame->setLayout(layout); Chris@539: m_splitter->insertWidget(index, frame); Chris@127: Chris@127: connect(pane, SIGNAL(propertyContainerAdded(PropertyContainer *)), Chris@127: this, SLOT(propertyContainerAdded(PropertyContainer *))); Chris@127: connect(pane, SIGNAL(propertyContainerRemoved(PropertyContainer *)), Chris@127: this, SLOT(propertyContainerRemoved(PropertyContainer *))); Chris@127: connect(pane, SIGNAL(paneInteractedWith()), Chris@127: this, SLOT(paneInteractedWith())); Chris@127: connect(pane, SIGNAL(rightButtonMenuRequested(QPoint)), Chris@127: this, SLOT(rightButtonMenuRequested(QPoint))); Chris@312: connect(pane, SIGNAL(dropAccepted(QStringList)), Chris@312: this, SLOT(paneDropAccepted(QStringList))); Chris@312: connect(pane, SIGNAL(dropAccepted(QString)), Chris@312: this, SLOT(paneDropAccepted(QString))); Chris@127: Chris@271: emit paneAdded(pane); Chris@271: emit paneAdded(); Chris@271: Chris@127: if (!m_currentPane) { Chris@127: setCurrentPane(pane); Chris@127: } Chris@127: Chris@605: showOrHidePaneAccessories(); Chris@605: Chris@127: return pane; Chris@127: } Chris@127: Chris@127: void Chris@235: PaneStack::setPropertyStackMinWidth(int mw) Chris@235: { Chris@235: for (std::vector::iterator i = m_panes.begin(); Chris@235: i != m_panes.end(); ++i) { Chris@235: i->propertyStack->setMinimumWidth(mw); Chris@235: } Chris@235: m_propertyStackMinWidth = mw; Chris@235: } Chris@235: Chris@235: void Chris@127: PaneStack::setLayoutStyle(LayoutStyle style) Chris@127: { Chris@127: if (style == m_layoutStyle) return; Chris@127: m_layoutStyle = style; Chris@127: Chris@127: std::vector::iterator i; Chris@127: Chris@127: switch (style) { Chris@127: Chris@179: case NoPropertyStacks: Chris@127: case SinglePropertyStackLayout: Chris@127: Chris@127: for (i = m_panes.begin(); i != m_panes.end(); ++i) { Chris@127: i->layout->removeWidget(i->propertyStack); Chris@127: i->propertyStack->setParent(m_propertyStackStack); Chris@127: m_propertyStackStack->addWidget(i->propertyStack); Chris@127: } Chris@179: m_propertyStackStack->setVisible(style != NoPropertyStacks); Chris@127: break; Chris@127: Chris@127: case PropertyStackPerPaneLayout: Chris@127: Chris@127: for (i = m_panes.begin(); i != m_panes.end(); ++i) { Chris@127: m_propertyStackStack->removeWidget(i->propertyStack); Chris@127: i->propertyStack->setParent(i->frame); Chris@495: i->layout->addWidget(i->propertyStack, 0, 2, 2, 1); Chris@127: i->propertyStack->show(); Chris@127: } Chris@127: m_propertyStackStack->hide(); Chris@127: break; Chris@127: } Chris@127: } Chris@127: Chris@127: Pane * Chris@127: PaneStack::getPane(int n) Chris@127: { Chris@277: if (n < m_panes.size()) { Chris@277: return m_panes[n].pane; Chris@277: } else { Chris@277: return 0; Chris@277: } Chris@277: } Chris@277: Chris@277: int Chris@277: PaneStack::getPaneIndex(Pane *pane) Chris@277: { Chris@277: for (int i = 0; i < getPaneCount(); ++i) { Chris@277: if (pane == getPane(i)) { Chris@277: return i; Chris@277: } Chris@277: } Chris@277: return -1; Chris@127: } Chris@127: Chris@127: Pane * Chris@127: PaneStack::getHiddenPane(int n) Chris@127: { Chris@127: return m_hiddenPanes[n].pane; Chris@127: } Chris@127: Chris@127: void Chris@127: PaneStack::deletePane(Pane *pane) Chris@127: { Chris@127: std::vector::iterator i; Chris@127: bool found = false; Chris@127: Chris@127: for (i = m_panes.begin(); i != m_panes.end(); ++i) { Chris@127: if (i->pane == pane) { Chris@127: m_panes.erase(i); Chris@127: found = true; Chris@127: break; Chris@127: } Chris@127: } Chris@127: Chris@127: if (!found) { Chris@127: Chris@127: for (i = m_hiddenPanes.begin(); i != m_hiddenPanes.end(); ++i) { Chris@127: if (i->pane == pane) { Chris@127: m_hiddenPanes.erase(i); Chris@127: found = true; Chris@127: break; Chris@127: } Chris@127: } Chris@127: Chris@127: if (!found) { Chris@682: cerr << "WARNING: PaneStack::deletePane(" << pane << "): Pane not found in visible or hidden panes, not deleting" << endl; Chris@127: return; Chris@127: } Chris@127: } Chris@127: Chris@271: emit paneAboutToBeDeleted(pane); Chris@271: Chris@127: delete pane->parent(); Chris@127: Chris@127: if (m_currentPane == pane) { Chris@127: if (m_panes.size() > 0) { Chris@127: setCurrentPane(m_panes[0].pane); Chris@127: } else { Chris@127: setCurrentPane(0); Chris@127: } Chris@127: } Chris@271: Chris@605: showOrHidePaneAccessories(); Chris@605: Chris@271: emit paneDeleted(); Chris@127: } Chris@127: Chris@605: void Chris@605: PaneStack::showOrHidePaneAccessories() Chris@605: { Chris@682: cerr << "PaneStack::showOrHidePaneAccessories: count == " << getPaneCount() << endl; Chris@605: Chris@605: bool multi = (getPaneCount() > 1); Chris@605: for (std::vector::iterator i = m_panes.begin(); Chris@605: i != m_panes.end(); ++i) { Chris@605: i->xButton->setVisible(multi); Chris@605: i->currentIndicator->setVisible(multi); Chris@605: } Chris@605: } Chris@605: Chris@127: int Chris@127: PaneStack::getPaneCount() const Chris@127: { Chris@127: return m_panes.size(); Chris@127: } Chris@127: Chris@127: int Chris@127: PaneStack::getHiddenPaneCount() const Chris@127: { Chris@127: return m_hiddenPanes.size(); Chris@127: } Chris@127: Chris@127: void Chris@127: PaneStack::hidePane(Pane *pane) Chris@127: { Chris@127: std::vector::iterator i = m_panes.begin(); Chris@127: Chris@127: while (i != m_panes.end()) { Chris@127: if (i->pane == pane) { Chris@127: Chris@127: m_hiddenPanes.push_back(*i); Chris@127: m_panes.erase(i); Chris@127: Chris@127: QWidget *pw = dynamic_cast(pane->parent()); Chris@127: if (pw) pw->hide(); Chris@127: Chris@127: if (m_currentPane == pane) { Chris@127: if (m_panes.size() > 0) { Chris@127: setCurrentPane(m_panes[0].pane); Chris@127: } else { Chris@127: setCurrentPane(0); Chris@127: } Chris@127: } Chris@127: Chris@605: showOrHidePaneAccessories(); Chris@605: emit paneHidden(pane); Chris@605: emit paneHidden(); Chris@127: return; Chris@127: } Chris@127: ++i; Chris@127: } Chris@127: Chris@682: cerr << "WARNING: PaneStack::hidePane(" << pane << "): Pane not found in visible panes" << endl; Chris@127: } Chris@127: Chris@127: void Chris@127: PaneStack::showPane(Pane *pane) Chris@127: { Chris@127: std::vector::iterator i = m_hiddenPanes.begin(); Chris@127: Chris@127: while (i != m_hiddenPanes.end()) { Chris@127: if (i->pane == pane) { Chris@127: m_panes.push_back(*i); Chris@127: m_hiddenPanes.erase(i); Chris@127: QWidget *pw = dynamic_cast(pane->parent()); Chris@127: if (pw) pw->show(); Chris@127: Chris@127: //!!! update current pane Chris@127: Chris@605: showOrHidePaneAccessories(); Chris@605: Chris@127: return; Chris@127: } Chris@127: ++i; Chris@127: } Chris@127: Chris@682: cerr << "WARNING: PaneStack::showPane(" << pane << "): Pane not found in hidden panes" << endl; Chris@127: } Chris@127: Chris@127: void Chris@127: PaneStack::setCurrentPane(Pane *pane) // may be null Chris@127: { Chris@127: if (m_currentPane == pane) return; Chris@127: Chris@127: std::vector::iterator i = m_panes.begin(); Chris@127: Chris@127: // We used to do this by setting the foreground and background Chris@127: // role, but it seems the background role is ignored and the Chris@127: // background drawn transparent in Qt 4.1 -- I can't quite see why Chris@127: Chris@127: QPixmap selectedMap(1, 1); Chris@127: selectedMap.fill(QApplication::palette().color(QPalette::Foreground)); Chris@127: Chris@127: QPixmap unselectedMap(1, 1); Chris@127: unselectedMap.fill(QApplication::palette().color(QPalette::Background)); Chris@127: Chris@127: bool found = false; Chris@127: Chris@127: while (i != m_panes.end()) { Chris@127: if (i->pane == pane) { Chris@127: i->currentIndicator->setPixmap(selectedMap); Chris@179: if (m_layoutStyle != PropertyStackPerPaneLayout) { Chris@127: m_propertyStackStack->setCurrentWidget(i->propertyStack); Chris@127: } Chris@127: found = true; Chris@127: } else { Chris@127: i->currentIndicator->setPixmap(unselectedMap); Chris@127: } Chris@127: ++i; Chris@127: } Chris@127: Chris@127: if (found || pane == 0) { Chris@127: m_currentPane = pane; Chris@127: emit currentPaneChanged(m_currentPane); Chris@127: } else { Chris@682: cerr << "WARNING: PaneStack::setCurrentPane(" << pane << "): pane is not a visible pane in this stack" << endl; Chris@127: } Chris@127: } Chris@127: Chris@127: void Chris@127: PaneStack::setCurrentLayer(Pane *pane, Layer *layer) // may be null Chris@127: { Chris@127: setCurrentPane(pane); Chris@127: Chris@127: if (m_currentPane) { Chris@127: Chris@127: std::vector::iterator i = m_panes.begin(); Chris@127: Chris@127: while (i != m_panes.end()) { Chris@127: Chris@127: if (i->pane == pane) { Chris@127: PropertyStack *stack = dynamic_cast Chris@127: (i->propertyStack); Chris@127: if (stack) { Chris@127: if (stack->containsContainer(layer)) { Chris@127: stack->setCurrentIndex(stack->getContainerIndex(layer)); Chris@127: emit currentLayerChanged(pane, layer); Chris@127: } else { Chris@127: stack->setCurrentIndex Chris@127: (stack->getContainerIndex Chris@127: (pane->getPropertyContainer(0))); Chris@127: emit currentLayerChanged(pane, 0); Chris@127: } Chris@127: } Chris@127: break; Chris@127: } Chris@127: ++i; Chris@127: } Chris@127: } Chris@127: } Chris@127: Chris@127: Pane * Chris@127: PaneStack::getCurrentPane() Chris@127: { Chris@127: return m_currentPane; Chris@127: } Chris@127: Chris@127: void Chris@127: PaneStack::propertyContainerAdded(PropertyContainer *) Chris@127: { Chris@127: sizePropertyStacks(); Chris@127: } Chris@127: Chris@127: void Chris@127: PaneStack::propertyContainerRemoved(PropertyContainer *) Chris@127: { Chris@127: sizePropertyStacks(); Chris@127: } Chris@127: Chris@127: void Chris@127: PaneStack::propertyContainerSelected(View *client, PropertyContainer *pc) Chris@127: { Chris@127: std::vector::iterator i = m_panes.begin(); Chris@127: Chris@127: while (i != m_panes.end()) { Chris@127: PropertyStack *stack = dynamic_cast(i->propertyStack); Chris@127: if (stack && Chris@127: stack->getClient() == client && Chris@127: stack->containsContainer(pc)) { Chris@127: setCurrentPane(i->pane); Chris@127: break; Chris@127: } Chris@127: ++i; Chris@127: } Chris@127: Chris@127: Layer *layer = dynamic_cast(pc); Chris@127: if (layer) emit currentLayerChanged(m_currentPane, layer); Chris@127: else emit currentLayerChanged(m_currentPane, 0); Chris@127: } Chris@127: Chris@127: void Chris@190: PaneStack::viewSelected(View *v) Chris@190: { Chris@190: Pane *p = dynamic_cast(v); Chris@190: if (p) setCurrentPane(p); Chris@190: } Chris@190: Chris@190: void Chris@127: PaneStack::paneInteractedWith() Chris@127: { Chris@127: Pane *pane = dynamic_cast(sender()); Chris@127: if (!pane) return; Chris@127: setCurrentPane(pane); Chris@127: } Chris@127: Chris@127: void Chris@127: PaneStack::rightButtonMenuRequested(QPoint position) Chris@127: { Chris@127: Pane *pane = dynamic_cast(sender()); Chris@127: if (!pane) return; Chris@127: emit rightButtonMenuRequested(pane, position); Chris@127: } Chris@127: Chris@127: void Chris@127: PaneStack::sizePropertyStacks() Chris@127: { Chris@127: int maxMinWidth = 0; Chris@127: Chris@235: if (m_propertyStackMinWidth > 0) maxMinWidth = m_propertyStackMinWidth; Chris@235: Chris@127: for (size_t i = 0; i < m_panes.size(); ++i) { Chris@127: if (!m_panes[i].propertyStack) continue; Chris@247: #ifdef DEBUG_PANE_STACK Chris@587: SVDEBUG << "PaneStack::sizePropertyStacks: " << i << ": min " Chris@243: << m_panes[i].propertyStack->minimumSizeHint().width() << ", hint " Chris@243: << m_panes[i].propertyStack->sizeHint().width() << ", current " Chris@585: << m_panes[i].propertyStack->width() << endl; Chris@247: #endif Chris@127: Chris@246: if (m_panes[i].propertyStack->sizeHint().width() > maxMinWidth) { Chris@246: maxMinWidth = m_panes[i].propertyStack->sizeHint().width(); Chris@127: } Chris@127: } Chris@127: Chris@247: #ifdef DEBUG_PANE_STACK Chris@587: SVDEBUG << "PaneStack::sizePropertyStacks: max min width " << maxMinWidth << endl; Chris@247: #endif Chris@127: Chris@127: int setWidth = maxMinWidth; Chris@127: Chris@127: m_propertyStackStack->setMaximumWidth(setWidth + 10); Chris@127: Chris@127: for (size_t i = 0; i < m_panes.size(); ++i) { Chris@127: if (!m_panes[i].propertyStack) continue; Chris@127: m_panes[i].propertyStack->setMinimumWidth(setWidth); Chris@127: } Chris@180: Chris@363: emit propertyStacksResized(setWidth); Chris@180: emit propertyStacksResized(); Chris@127: } Chris@127: Chris@312: void Chris@312: PaneStack::paneDropAccepted(QStringList uriList) Chris@312: { Chris@312: Pane *pane = dynamic_cast(sender()); Chris@312: emit dropAccepted(pane, uriList); Chris@312: } Chris@312: Chris@312: void Chris@312: PaneStack::paneDropAccepted(QString text) Chris@312: { Chris@312: Pane *pane = dynamic_cast(sender()); Chris@312: emit dropAccepted(pane, text); Chris@312: } Chris@127: Chris@320: void Chris@323: PaneStack::paneDeleteButtonClicked() Chris@323: { Chris@323: QObject *s = sender(); Chris@605: for (size_t i = 0; i < m_panes.size(); ++i) { Chris@605: if (m_panes[i].xButton == s) { Chris@605: emit paneDeleteButtonClicked(m_panes[i].pane); Chris@323: } Chris@323: } Chris@323: } Chris@323: Chris@323: void Chris@500: PaneStack::indicatorClicked() Chris@500: { Chris@500: QObject *s = sender(); Chris@500: Chris@500: for (size_t i = 0; i < m_panes.size(); ++i) { Chris@500: if (m_panes[i].currentIndicator == s) { Chris@500: setCurrentPane(m_panes[i].pane); Chris@500: return; Chris@500: } Chris@500: } Chris@500: } Chris@500: Chris@500: void Chris@320: PaneStack::sizePanesEqually() Chris@320: { Chris@320: QList sizes = m_splitter->sizes(); Chris@320: if (sizes.empty()) return; Chris@320: Chris@320: int count = sizes.size(); Chris@320: Chris@320: int total = 0; Chris@320: for (int i = 0; i < count; ++i) { Chris@320: total += sizes[i]; Chris@320: } Chris@320: Chris@320: if (total == 0) return; Chris@320: Chris@320: sizes.clear(); Chris@320: Chris@320: int each = total / count; Chris@320: int remaining = total; Chris@320: Chris@320: for (int i = 0; i < count; ++i) { Chris@320: if (i == count - 1) { Chris@320: sizes.push_back(remaining); Chris@320: } else { Chris@320: sizes.push_back(each); Chris@320: remaining -= each; Chris@320: } Chris@320: } Chris@320: Chris@320: /* Chris@682: cerr << "sizes: "; Chris@320: for (int i = 0; i < sizes.size(); ++i) { Chris@682: cerr << sizes[i] << " "; Chris@320: } Chris@682: cerr << endl; Chris@320: */ Chris@320: Chris@320: m_splitter->setSizes(sizes); Chris@320: } Chris@320: Chris@320: