svgui
1.9
|
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