svgui  1.9
PaneStack.cpp
Go to the documentation of this file.
00001 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
00002 
00003 /*
00004     Sonic Visualiser
00005     An audio file viewer and annotation editor.
00006     Centre for Digital Music, Queen Mary, University of London.
00007     This file copyright 2006 Chris Cannam and QMUL.
00008     
00009     This program is free software; you can redistribute it and/or
00010     modify it under the terms of the GNU General Public License as
00011     published by the Free Software Foundation; either version 2 of the
00012     License, or (at your option) any later version.  See the file
00013     COPYING included with this distribution for more information.
00014 */
00015 
00016 #include "PaneStack.h"
00017 
00018 #include "Pane.h"
00019 #include "widgets/PropertyStack.h"
00020 #include "widgets/IconLoader.h"
00021 #include "widgets/ClickableLabel.h"
00022 #include "layer/Layer.h"
00023 #include "ViewManager.h"
00024 
00025 #include <QApplication>
00026 #include <QHBoxLayout>
00027 #include <QVBoxLayout>
00028 #include <QPainter>
00029 #include <QPalette>
00030 #include <QLabel>
00031 #include <QPushButton>
00032 #include <QSplitter>
00033 #include <QStackedWidget>
00034 
00035 #include <iostream>
00036 
00037 //#define DEBUG_PANE_STACK 1
00038 
00039 PaneStack::PaneStack(QWidget *parent, ViewManager *viewManager) :
00040     QFrame(parent),
00041     m_currentPane(0),
00042     m_showAccessories(true),
00043     m_splitter(new QSplitter),
00044     m_propertyStackStack(new QStackedWidget),
00045     m_viewManager(viewManager),
00046     m_propertyStackMinWidth(100),
00047     m_layoutStyle(PropertyStackPerPaneLayout)
00048 {
00049     QHBoxLayout *layout = new QHBoxLayout;
00050     layout->setMargin(0);
00051     layout->setSpacing(0);
00052 
00053     m_splitter->setOrientation(Qt::Vertical);
00054     m_splitter->setOpaqueResize(false);
00055 
00056     layout->addWidget(m_splitter);
00057     layout->setStretchFactor(m_splitter, 1);
00058     layout->addWidget(m_propertyStackStack);
00059     m_propertyStackStack->hide();
00060 
00061     setLayout(layout);
00062 }
00063 
00064 void
00065 PaneStack::setShowPaneAccessories(bool show)
00066 {
00067     m_showAccessories = show;
00068 }
00069 
00070 Pane *
00071 PaneStack::addPane(bool suppressPropertyBox)
00072 {
00073     return insertPane(getPaneCount(), suppressPropertyBox);
00074 }
00075 
00076 Pane *
00077 PaneStack::insertPane(int index, bool suppressPropertyBox)
00078 {
00079     QFrame *frame = new QFrame;
00080 
00081     QGridLayout *layout = new QGridLayout;
00082     layout->setMargin(0);
00083     layout->setSpacing(2);
00084 
00085     QPushButton *xButton = new QPushButton(frame);
00086     xButton->setIcon(IconLoader().load("cross"));
00087     xButton->setFixedSize(QSize(16, 16));
00088     xButton->setFlat(true);
00089     xButton->setVisible(m_showAccessories);
00090     layout->addWidget(xButton, 0, 0);
00091     connect(xButton, SIGNAL(clicked()), this, SLOT(paneDeleteButtonClicked()));
00092 
00093     ClickableLabel *currentIndicator = new ClickableLabel(frame);
00094     connect(currentIndicator, SIGNAL(clicked()), this, SLOT(indicatorClicked()));
00095     layout->addWidget(currentIndicator, 1, 0);
00096     layout->setRowStretch(1, 20);
00097     currentIndicator->setMinimumWidth(8);
00098     currentIndicator->setScaledContents(true);
00099     currentIndicator->setVisible(m_showAccessories);
00100 
00101     int initialCentreFrame = -1;
00102     if (!m_panes.empty()) {
00103         initialCentreFrame = m_panes[0].pane->getCentreFrame();
00104     }
00105 
00106     Pane *pane = new Pane(frame);
00107     if (initialCentreFrame >= 0) {
00108         pane->setViewManager(m_viewManager, initialCentreFrame);
00109     } else {
00110         pane->setViewManager(m_viewManager);
00111     }
00112     layout->addWidget(pane, 0, 1, 2, 1);
00113     layout->setColumnStretch(1, 20);
00114 
00115     QWidget *properties = 0;
00116     if (suppressPropertyBox) {
00117         properties = new QFrame();
00118     } else {
00119         properties = new PropertyStack(frame, pane);
00120         connect(properties, SIGNAL(propertyContainerSelected(View *, PropertyContainer *)),
00121                 this, SLOT(propertyContainerSelected(View *, PropertyContainer *)));
00122         connect(properties, SIGNAL(viewSelected(View  *)),
00123                 this, SLOT(viewSelected(View *)));
00124         connect(properties, SIGNAL(contextHelpChanged(const QString &)),
00125                 this, SIGNAL(contextHelpChanged(const QString &)));
00126     }
00127     if (m_layoutStyle == PropertyStackPerPaneLayout) {
00128         layout->addWidget(properties, 0, 2, 2, 1);
00129     } else {
00130         properties->setParent(m_propertyStackStack);
00131         m_propertyStackStack->addWidget(properties);
00132     }
00133     layout->setColumnStretch(2, 0);
00134 
00135     PaneRec rec;
00136     rec.pane = pane;
00137     rec.propertyStack = properties;
00138     rec.xButton = xButton;
00139     rec.currentIndicator = currentIndicator;
00140     rec.frame = frame;
00141     rec.layout = layout;
00142     m_panes.push_back(rec);
00143 
00144     frame->setLayout(layout);
00145     m_splitter->insertWidget(index, frame);
00146 
00147     connect(pane, SIGNAL(propertyContainerAdded(PropertyContainer *)),
00148             this, SLOT(propertyContainerAdded(PropertyContainer *)));
00149     connect(pane, SIGNAL(propertyContainerRemoved(PropertyContainer *)),
00150             this, SLOT(propertyContainerRemoved(PropertyContainer *)));
00151     connect(pane, SIGNAL(paneInteractedWith()),
00152             this, SLOT(paneInteractedWith()));
00153     connect(pane, SIGNAL(rightButtonMenuRequested(QPoint)),
00154             this, SLOT(rightButtonMenuRequested(QPoint)));
00155     connect(pane, SIGNAL(dropAccepted(QStringList)),
00156             this, SLOT(paneDropAccepted(QStringList)));
00157     connect(pane, SIGNAL(dropAccepted(QString)),
00158             this, SLOT(paneDropAccepted(QString)));
00159     connect(pane, SIGNAL(doubleClickSelectInvoked(int)),
00160             this, SIGNAL(doubleClickSelectInvoked(int)));
00161 
00162     emit paneAdded(pane);
00163     emit paneAdded();
00164 
00165     if (!m_currentPane) {
00166         setCurrentPane(pane);
00167     }
00168 
00169     showOrHidePaneAccessories();
00170 
00171     return pane;
00172 }
00173 
00174 void
00175 PaneStack::setPropertyStackMinWidth(int mw)
00176 {
00177     for (std::vector<PaneRec>::iterator i = m_panes.begin();
00178          i != m_panes.end(); ++i) {
00179         i->propertyStack->setMinimumWidth(mw);
00180     }
00181     m_propertyStackMinWidth = mw;
00182 }
00183 
00184 void
00185 PaneStack::setLayoutStyle(LayoutStyle style)
00186 {
00187     if (style == m_layoutStyle) return;
00188     m_layoutStyle = style;
00189 
00190     std::vector<PaneRec>::iterator i;
00191 
00192     switch (style) {
00193 
00194     case NoPropertyStacks:
00195     case SinglePropertyStackLayout:
00196         
00197         for (i = m_panes.begin(); i != m_panes.end(); ++i) {
00198             i->layout->removeWidget(i->propertyStack);
00199             i->propertyStack->setParent(m_propertyStackStack);
00200             m_propertyStackStack->addWidget(i->propertyStack);
00201         }
00202         m_propertyStackStack->setVisible(style != NoPropertyStacks);
00203         break;
00204 
00205     case PropertyStackPerPaneLayout:
00206 
00207         for (i = m_panes.begin(); i != m_panes.end(); ++i) {
00208             m_propertyStackStack->removeWidget(i->propertyStack);
00209             i->propertyStack->setParent(i->frame);
00210             i->layout->addWidget(i->propertyStack, 0, 2, 2, 1);
00211             i->propertyStack->show();
00212         }
00213         m_propertyStackStack->hide();
00214         break;
00215     }
00216 }
00217 
00218 Pane *
00219 PaneStack::getPane(int n)
00220 {
00221     if (n < (int)m_panes.size()) {
00222         return m_panes[n].pane;
00223     } else {
00224         return 0;
00225     }
00226 }
00227 
00228 int
00229 PaneStack::getPaneIndex(Pane *pane)
00230 {
00231     for (int i = 0; i < getPaneCount(); ++i) {
00232         if (pane == getPane(i)) {
00233             return i;
00234         }
00235     }
00236     return -1;
00237 }
00238 
00239 Pane *
00240 PaneStack::getHiddenPane(int n)
00241 {
00242     return m_hiddenPanes[n].pane;
00243 }
00244 
00245 void
00246 PaneStack::deletePane(Pane *pane)
00247 {
00248     cerr << "PaneStack::deletePane(" << pane << ")" << endl;
00249 
00250     std::vector<PaneRec>::iterator i;
00251     bool found = false;
00252 
00253     QWidget *stack = 0;
00254 
00255     for (i = m_panes.begin(); i != m_panes.end(); ++i) {
00256         if (i->pane == pane) {
00257             stack = i->propertyStack;
00258             m_panes.erase(i);
00259             found = true;
00260             break;
00261         }
00262     }
00263 
00264     if (!found) {
00265 
00266         for (i = m_hiddenPanes.begin(); i != m_hiddenPanes.end(); ++i) {
00267             if (i->pane == pane) {
00268                 stack = i->propertyStack;
00269                 m_hiddenPanes.erase(i);
00270                 found = true;
00271                 break;
00272             }
00273         }
00274 
00275         if (!found) {
00276             cerr << "WARNING: PaneStack::deletePane(" << pane << "): Pane not found in visible or hidden panes, not deleting" << endl;
00277             return;
00278         }
00279     }
00280 
00281     emit paneAboutToBeDeleted(pane);
00282 
00283     cerr << "PaneStack::deletePane: about to delete parent " << pane->parent() << " of pane " << pane << endl;
00284 
00285     // The property stack associated with the parent was initially
00286     // created with the same parent as it, so it would be deleted when
00287     // we delete the pane's parent in a moment -- but it may have been
00288     // reparented depending on the layout. We'd better delete it
00289     // separately first. (This fixes a crash on opening a new layer
00290     // with a new unit type in it, when a long-defunct property box
00291     // could be signalled from the unit database to tell it that a new
00292     // unit had appeared.)
00293     delete stack;
00294 
00295     delete pane->parent();
00296 
00297     if (m_currentPane == pane) {
00298         if (m_panes.size() > 0) {
00299             setCurrentPane(m_panes[0].pane);
00300         } else {
00301             setCurrentPane(0);
00302         }
00303     }
00304 
00305     showOrHidePaneAccessories();
00306 
00307     emit paneDeleted();
00308 }
00309 
00310 void
00311 PaneStack::showOrHidePaneAccessories()
00312 {
00313     cerr << "PaneStack::showOrHidePaneAccessories: count == " << getPaneCount() << endl;
00314 
00315     bool multi = (getPaneCount() > 1);
00316     for (std::vector<PaneRec>::iterator i = m_panes.begin();
00317          i != m_panes.end(); ++i) {
00318         i->xButton->setVisible(multi && m_showAccessories);
00319         i->currentIndicator->setVisible(multi && m_showAccessories);
00320     }
00321 }
00322 
00323 int
00324 PaneStack::getPaneCount() const
00325 {
00326     return m_panes.size();
00327 }
00328 
00329 int
00330 PaneStack::getHiddenPaneCount() const
00331 {
00332     return m_hiddenPanes.size();
00333 }
00334 
00335 void
00336 PaneStack::hidePane(Pane *pane)
00337 {
00338     std::vector<PaneRec>::iterator i = m_panes.begin();
00339 
00340     while (i != m_panes.end()) {
00341         if (i->pane == pane) {
00342 
00343             m_hiddenPanes.push_back(*i);
00344             m_panes.erase(i);
00345 
00346             QWidget *pw = dynamic_cast<QWidget *>(pane->parent());
00347             if (pw) pw->hide();
00348 
00349             if (m_currentPane == pane) {
00350                 if (m_panes.size() > 0) {
00351                     setCurrentPane(m_panes[0].pane);
00352                 } else {
00353                     setCurrentPane(0);
00354                 }
00355             }
00356             
00357             showOrHidePaneAccessories();
00358             emit paneHidden(pane);
00359             emit paneHidden();
00360             return;
00361         }
00362         ++i;
00363     }
00364 
00365     cerr << "WARNING: PaneStack::hidePane(" << pane << "): Pane not found in visible panes" << endl;
00366 }
00367 
00368 void
00369 PaneStack::showPane(Pane *pane)
00370 {
00371     std::vector<PaneRec>::iterator i = m_hiddenPanes.begin();
00372 
00373     while (i != m_hiddenPanes.end()) {
00374         if (i->pane == pane) {
00375             m_panes.push_back(*i);
00376             m_hiddenPanes.erase(i);
00377             QWidget *pw = dynamic_cast<QWidget *>(pane->parent());
00378             if (pw) pw->show();
00379 
00381 
00382             showOrHidePaneAccessories();
00383 
00384             return;
00385         }
00386         ++i;
00387     }
00388 
00389     cerr << "WARNING: PaneStack::showPane(" << pane << "): Pane not found in hidden panes" << endl;
00390 }
00391 
00392 void
00393 PaneStack::setCurrentPane(Pane *pane) // may be null
00394 {
00395     if (m_currentPane == pane) return;
00396     
00397     std::vector<PaneRec>::iterator i = m_panes.begin();
00398 
00399     // We used to do this by setting the foreground and background
00400     // role, but it seems the background role is ignored and the
00401     // background drawn transparent in Qt 4.1 -- I can't quite see why
00402     
00403     QPixmap selectedMap(1, 1);
00404     selectedMap.fill(QApplication::palette().color(QPalette::Foreground));
00405     
00406     QPixmap unselectedMap(1, 1);
00407     unselectedMap.fill(QApplication::palette().color(QPalette::Background));
00408 
00409     bool found = false;
00410 
00411     while (i != m_panes.end()) {
00412         if (i->pane == pane) {
00413             i->currentIndicator->setPixmap(selectedMap);
00414             if (m_layoutStyle != PropertyStackPerPaneLayout) {
00415                 m_propertyStackStack->setCurrentWidget(i->propertyStack);
00416             }
00417             found = true;
00418         } else {
00419             i->currentIndicator->setPixmap(unselectedMap);
00420         }
00421         ++i;
00422     }
00423 
00424     if (found || pane == 0) {
00425         m_currentPane = pane;
00426         emit currentPaneChanged(m_currentPane);
00427     } else {
00428         cerr << "WARNING: PaneStack::setCurrentPane(" << pane << "): pane is not a visible pane in this stack" << endl;
00429     }
00430 }
00431 
00432 void
00433 PaneStack::setCurrentLayer(Pane *pane, Layer *layer) // may be null
00434 {
00435     setCurrentPane(pane);
00436 
00437     if (m_currentPane) {
00438 
00439         std::vector<PaneRec>::iterator i = m_panes.begin();
00440 
00441         while (i != m_panes.end()) {
00442 
00443             if (i->pane == pane) {
00444                 PropertyStack *stack = dynamic_cast<PropertyStack *>
00445                     (i->propertyStack);
00446                 if (stack) {
00447                     if (stack->containsContainer(layer)) {
00448                         stack->setCurrentIndex(stack->getContainerIndex(layer));
00449                         emit currentLayerChanged(pane, layer);
00450                     } else {
00451                         stack->setCurrentIndex
00452                             (stack->getContainerIndex
00453                              (pane->getPropertyContainer(0)));
00454                         emit currentLayerChanged(pane, 0);
00455                     }
00456                 }
00457                 break;
00458             }
00459             ++i;
00460         }
00461     }
00462 }
00463 
00464 Pane *
00465 PaneStack::getCurrentPane() 
00466 {
00467     return m_currentPane;
00468 }
00469 
00470 void
00471 PaneStack::propertyContainerAdded(PropertyContainer *)
00472 {
00473     sizePropertyStacks();
00474 }
00475 
00476 void
00477 PaneStack::propertyContainerRemoved(PropertyContainer *)
00478 {
00479     sizePropertyStacks();
00480 }
00481 
00482 void
00483 PaneStack::propertyContainerSelected(View *client, PropertyContainer *pc)
00484 {
00485     std::vector<PaneRec>::iterator i = m_panes.begin();
00486 
00487     while (i != m_panes.end()) {
00488         PropertyStack *stack = dynamic_cast<PropertyStack *>(i->propertyStack);
00489         if (stack &&
00490             stack->getClient() == client &&
00491             stack->containsContainer(pc)) {
00492             setCurrentPane(i->pane);
00493             break;
00494         }
00495         ++i;
00496     }
00497 
00498     Layer *layer = dynamic_cast<Layer *>(pc);
00499     if (layer) emit currentLayerChanged(m_currentPane, layer);
00500     else emit currentLayerChanged(m_currentPane, 0);
00501 }
00502 
00503 void
00504 PaneStack::viewSelected(View *v)
00505 {
00506     Pane *p = dynamic_cast<Pane *>(v);
00507     if (p) setCurrentPane(p);
00508 }
00509 
00510 void
00511 PaneStack::paneInteractedWith()
00512 {
00513     Pane *pane = dynamic_cast<Pane *>(sender());
00514     if (!pane) return;
00515     setCurrentPane(pane);
00516 }
00517 
00518 void
00519 PaneStack::rightButtonMenuRequested(QPoint position)
00520 {
00521     Pane *pane = dynamic_cast<Pane *>(sender());
00522     if (!pane) return;
00523     emit rightButtonMenuRequested(pane, position);
00524 }
00525 
00526 void
00527 PaneStack::sizePropertyStacks()
00528 {
00529     int maxMinWidth = 0;
00530 
00531     if (m_propertyStackMinWidth > 0) maxMinWidth = m_propertyStackMinWidth;
00532 
00533     for (int i = 0; i < (int)m_panes.size(); ++i) {
00534         if (!m_panes[i].propertyStack) continue;
00535 #ifdef DEBUG_PANE_STACK
00536         SVDEBUG << "PaneStack::sizePropertyStacks: " << i << ": min " 
00537                   << m_panes[i].propertyStack->minimumSizeHint().width() << ", hint "
00538                   << m_panes[i].propertyStack->sizeHint().width() << ", current "
00539                   << m_panes[i].propertyStack->width() << endl;
00540 #endif
00541 
00542         if (m_panes[i].propertyStack->sizeHint().width() > maxMinWidth) {
00543             maxMinWidth = m_panes[i].propertyStack->sizeHint().width();
00544         }
00545     }
00546 
00547 #ifdef DEBUG_PANE_STACK
00548     SVDEBUG << "PaneStack::sizePropertyStacks: max min width " << maxMinWidth << endl;
00549 #endif
00550 
00551     int setWidth = maxMinWidth;
00552 
00553     m_propertyStackStack->setMaximumWidth(setWidth + 10);
00554 
00555     for (int i = 0; i < (int)m_panes.size(); ++i) {
00556         if (!m_panes[i].propertyStack) continue;
00557         m_panes[i].propertyStack->setMinimumWidth(setWidth);
00558     }
00559 
00560     emit propertyStacksResized(setWidth);
00561     emit propertyStacksResized();
00562 }
00563     
00564 void
00565 PaneStack::paneDropAccepted(QStringList uriList)
00566 {
00567     Pane *pane = dynamic_cast<Pane *>(sender());
00568     emit dropAccepted(pane, uriList);
00569 }
00570     
00571 void
00572 PaneStack::paneDropAccepted(QString text)
00573 {
00574     Pane *pane = dynamic_cast<Pane *>(sender());
00575     emit dropAccepted(pane, text);
00576 }
00577 
00578 void
00579 PaneStack::paneDeleteButtonClicked()
00580 {
00581     QObject *s = sender();
00582     for (int i = 0; i < (int)m_panes.size(); ++i) {
00583         if (m_panes[i].xButton == s) {
00584             emit paneDeleteButtonClicked(m_panes[i].pane);
00585         }
00586     }
00587 }
00588 
00589 void
00590 PaneStack::indicatorClicked()
00591 {
00592     QObject *s = sender();
00593 
00594     for (int i = 0; i < (int)m_panes.size(); ++i) {
00595         if (m_panes[i].currentIndicator == s) {
00596             setCurrentPane(m_panes[i].pane);
00597             return;
00598         }
00599     }
00600 }
00601 
00602 void
00603 PaneStack::sizePanesEqually()
00604 {
00605     QList<int> sizes = m_splitter->sizes();
00606     if (sizes.empty()) return;
00607 
00608     int count = sizes.size();
00609 
00610     int fixed = 0, variable = 0, total = 0;
00611     int varicount = 0;
00612 
00613     for (int i = 0; i < count; ++i) {
00614         total += sizes[i];
00615     }
00616 
00617     variable = total;
00618 
00619     for (int i = 0; i < count; ++i) {
00620         int minh = m_panes[i].pane->minimumSize().height();
00621         if (minh == m_panes[i].pane->maximumSize().height()) {
00622             fixed += minh;
00623             variable -= minh;
00624         } else {
00625             varicount++;
00626         }
00627     }
00628 
00629     if (total == 0) return;
00630 
00631     sizes.clear();
00632 
00633     int each = (varicount > 0 ? (variable / varicount) : 0);
00634     int remaining = total;
00635 
00636     for (int i = 0; i < count; ++i) {
00637         if (i == count - 1) {
00638             sizes.push_back(remaining);
00639         } else {
00640             int minh = m_panes[i].pane->minimumSize().height();
00641             if (minh == m_panes[i].pane->maximumSize().height()) {
00642                 sizes.push_back(minh);
00643                 remaining -= minh;
00644             } else {
00645                 sizes.push_back(each);
00646                 remaining -= each;
00647             }
00648         }
00649     }
00650 
00651 /*
00652     cerr << "sizes: ";
00653     for (int i = 0; i < sizes.size(); ++i) {
00654         cerr << sizes[i] << " ";
00655     }
00656     cerr << endl;
00657 */
00658 
00659     m_splitter->setSizes(sizes);
00660 }
00661