annotate view/PaneStack.cpp @ 317:e251c3599ea8

* Make RemoteFile far more pervasive, and use it for local files as well so that we can handle both transparently. Make it shallow copy with reference counting, so it can be used by value without having to worry about the cache file lifetime. Use RemoteFile for MainWindow file-open functions, etc
author Chris Cannam
date Thu, 18 Oct 2007 15:31:20 +0000
parents 6de6f78b13a1
children 2a50c1ecc990
rev   line source
Chris@127 1
Chris@127 2 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@127 3
Chris@127 4 /*
Chris@127 5 Sonic Visualiser
Chris@127 6 An audio file viewer and annotation editor.
Chris@127 7 Centre for Digital Music, Queen Mary, University of London.
Chris@182 8 This file copyright 2006 Chris Cannam and QMUL.
Chris@127 9
Chris@127 10 This program is free software; you can redistribute it and/or
Chris@127 11 modify it under the terms of the GNU General Public License as
Chris@127 12 published by the Free Software Foundation; either version 2 of the
Chris@127 13 License, or (at your option) any later version. See the file
Chris@127 14 COPYING included with this distribution for more information.
Chris@127 15 */
Chris@127 16
Chris@127 17 #include "PaneStack.h"
Chris@127 18
Chris@128 19 #include "Pane.h"
Chris@127 20 #include "widgets/PropertyStack.h"
Chris@128 21 #include "layer/Layer.h"
Chris@128 22 #include "ViewManager.h"
Chris@127 23
Chris@127 24 #include <QApplication>
Chris@127 25 #include <QHBoxLayout>
Chris@127 26 #include <QPainter>
Chris@127 27 #include <QPalette>
Chris@127 28 #include <QLabel>
Chris@127 29 #include <QSplitter>
Chris@127 30 #include <QStackedWidget>
Chris@127 31
Chris@127 32 #include <iostream>
Chris@127 33
Chris@247 34 //#define DEBUG_PANE_STACK 1
Chris@247 35
Chris@127 36 PaneStack::PaneStack(QWidget *parent, ViewManager *viewManager) :
Chris@127 37 QFrame(parent),
Chris@127 38 m_currentPane(0),
Chris@127 39 m_splitter(new QSplitter),
Chris@127 40 m_propertyStackStack(new QStackedWidget),
Chris@127 41 m_viewManager(viewManager),
Chris@127 42 m_layoutStyle(PropertyStackPerPaneLayout)
Chris@127 43 {
Chris@127 44 QHBoxLayout *layout = new QHBoxLayout;
Chris@127 45 layout->setMargin(0);
Chris@127 46 layout->setSpacing(0);
Chris@127 47
Chris@127 48 m_splitter->setOrientation(Qt::Vertical);
Chris@127 49 m_splitter->setOpaqueResize(false);
Chris@127 50
Chris@127 51 layout->addWidget(m_splitter);
Chris@127 52 layout->setStretchFactor(m_splitter, 1);
Chris@127 53 layout->addWidget(m_propertyStackStack);
Chris@127 54 m_propertyStackStack->hide();
Chris@127 55
Chris@127 56 setLayout(layout);
Chris@127 57 }
Chris@127 58
Chris@127 59 Pane *
Chris@127 60 PaneStack::addPane(bool suppressPropertyBox)
Chris@127 61 {
Chris@127 62 QFrame *frame = new QFrame;
Chris@127 63 QHBoxLayout *layout = new QHBoxLayout;
Chris@127 64 layout->setMargin(0);
Chris@127 65 layout->setSpacing(2);
Chris@127 66
Chris@127 67 QLabel *currentIndicator = new QLabel(frame);
Chris@127 68 currentIndicator->setFixedWidth(QPainter(this).fontMetrics().width("x"));
Chris@127 69 layout->addWidget(currentIndicator);
Chris@127 70 layout->setStretchFactor(currentIndicator, 1);
Chris@127 71 currentIndicator->setScaledContents(true);
Chris@127 72
Chris@127 73 Pane *pane = new Pane(frame);
Chris@127 74 pane->setViewManager(m_viewManager);
Chris@127 75 layout->addWidget(pane);
Chris@127 76 layout->setStretchFactor(pane, 10);
Chris@127 77
Chris@127 78 QWidget *properties = 0;
Chris@127 79 if (suppressPropertyBox) {
Chris@127 80 properties = new QFrame();
Chris@127 81 } else {
Chris@127 82 properties = new PropertyStack(frame, pane);
Chris@127 83 connect(properties, SIGNAL(propertyContainerSelected(View *, PropertyContainer *)),
Chris@127 84 this, SLOT(propertyContainerSelected(View *, PropertyContainer *)));
Chris@190 85 connect(properties, SIGNAL(viewSelected(View *)),
Chris@190 86 this, SLOT(viewSelected(View *)));
Chris@189 87 connect(properties, SIGNAL(contextHelpChanged(const QString &)),
Chris@189 88 this, SIGNAL(contextHelpChanged(const QString &)));
Chris@127 89 }
Chris@127 90 if (m_layoutStyle == PropertyStackPerPaneLayout) {
Chris@127 91 layout->addWidget(properties);
Chris@127 92 } else {
Chris@127 93 properties->setParent(m_propertyStackStack);
Chris@127 94 m_propertyStackStack->addWidget(properties);
Chris@127 95 }
Chris@127 96 layout->setStretchFactor(properties, 1);
Chris@127 97
Chris@127 98 PaneRec rec;
Chris@127 99 rec.pane = pane;
Chris@127 100 rec.propertyStack = properties;
Chris@127 101 rec.currentIndicator = currentIndicator;
Chris@127 102 rec.frame = frame;
Chris@127 103 rec.layout = layout;
Chris@127 104 m_panes.push_back(rec);
Chris@127 105
Chris@127 106 frame->setLayout(layout);
Chris@127 107 m_splitter->addWidget(frame);
Chris@127 108
Chris@127 109 connect(pane, SIGNAL(propertyContainerAdded(PropertyContainer *)),
Chris@127 110 this, SLOT(propertyContainerAdded(PropertyContainer *)));
Chris@127 111 connect(pane, SIGNAL(propertyContainerRemoved(PropertyContainer *)),
Chris@127 112 this, SLOT(propertyContainerRemoved(PropertyContainer *)));
Chris@127 113 connect(pane, SIGNAL(paneInteractedWith()),
Chris@127 114 this, SLOT(paneInteractedWith()));
Chris@127 115 connect(pane, SIGNAL(rightButtonMenuRequested(QPoint)),
Chris@127 116 this, SLOT(rightButtonMenuRequested(QPoint)));
Chris@312 117 connect(pane, SIGNAL(dropAccepted(QStringList)),
Chris@312 118 this, SLOT(paneDropAccepted(QStringList)));
Chris@312 119 connect(pane, SIGNAL(dropAccepted(QString)),
Chris@312 120 this, SLOT(paneDropAccepted(QString)));
Chris@127 121
Chris@271 122 emit paneAdded(pane);
Chris@271 123 emit paneAdded();
Chris@271 124
Chris@127 125 if (!m_currentPane) {
Chris@127 126 setCurrentPane(pane);
Chris@127 127 }
Chris@127 128
Chris@127 129 return pane;
Chris@127 130 }
Chris@127 131
Chris@127 132 void
Chris@235 133 PaneStack::setPropertyStackMinWidth(int mw)
Chris@235 134 {
Chris@235 135 for (std::vector<PaneRec>::iterator i = m_panes.begin();
Chris@235 136 i != m_panes.end(); ++i) {
Chris@235 137 i->propertyStack->setMinimumWidth(mw);
Chris@235 138 }
Chris@235 139 m_propertyStackMinWidth = mw;
Chris@235 140 }
Chris@235 141
Chris@235 142 void
Chris@127 143 PaneStack::setLayoutStyle(LayoutStyle style)
Chris@127 144 {
Chris@127 145 if (style == m_layoutStyle) return;
Chris@127 146 m_layoutStyle = style;
Chris@127 147
Chris@127 148 std::vector<PaneRec>::iterator i;
Chris@127 149
Chris@127 150 switch (style) {
Chris@127 151
Chris@179 152 case NoPropertyStacks:
Chris@127 153 case SinglePropertyStackLayout:
Chris@127 154
Chris@127 155 for (i = m_panes.begin(); i != m_panes.end(); ++i) {
Chris@127 156 i->layout->removeWidget(i->propertyStack);
Chris@127 157 i->propertyStack->setParent(m_propertyStackStack);
Chris@127 158 m_propertyStackStack->addWidget(i->propertyStack);
Chris@127 159 }
Chris@179 160 m_propertyStackStack->setVisible(style != NoPropertyStacks);
Chris@127 161 break;
Chris@127 162
Chris@127 163 case PropertyStackPerPaneLayout:
Chris@127 164
Chris@127 165 for (i = m_panes.begin(); i != m_panes.end(); ++i) {
Chris@127 166 m_propertyStackStack->removeWidget(i->propertyStack);
Chris@127 167 i->propertyStack->setParent(i->frame);
Chris@127 168 i->layout->addWidget(i->propertyStack);
Chris@127 169 i->propertyStack->show();
Chris@127 170 }
Chris@127 171 m_propertyStackStack->hide();
Chris@127 172 break;
Chris@127 173 }
Chris@127 174 }
Chris@127 175
Chris@127 176 Pane *
Chris@127 177 PaneStack::getPane(int n)
Chris@127 178 {
Chris@277 179 if (n < m_panes.size()) {
Chris@277 180 return m_panes[n].pane;
Chris@277 181 } else {
Chris@277 182 return 0;
Chris@277 183 }
Chris@277 184 }
Chris@277 185
Chris@277 186 int
Chris@277 187 PaneStack::getPaneIndex(Pane *pane)
Chris@277 188 {
Chris@277 189 for (int i = 0; i < getPaneCount(); ++i) {
Chris@277 190 if (pane == getPane(i)) {
Chris@277 191 return i;
Chris@277 192 }
Chris@277 193 }
Chris@277 194 return -1;
Chris@127 195 }
Chris@127 196
Chris@127 197 Pane *
Chris@127 198 PaneStack::getHiddenPane(int n)
Chris@127 199 {
Chris@127 200 return m_hiddenPanes[n].pane;
Chris@127 201 }
Chris@127 202
Chris@127 203 void
Chris@127 204 PaneStack::deletePane(Pane *pane)
Chris@127 205 {
Chris@127 206 std::vector<PaneRec>::iterator i;
Chris@127 207 bool found = false;
Chris@127 208
Chris@127 209 for (i = m_panes.begin(); i != m_panes.end(); ++i) {
Chris@127 210 if (i->pane == pane) {
Chris@127 211 m_panes.erase(i);
Chris@127 212 found = true;
Chris@127 213 break;
Chris@127 214 }
Chris@127 215 }
Chris@127 216
Chris@127 217 if (!found) {
Chris@127 218
Chris@127 219 for (i = m_hiddenPanes.begin(); i != m_hiddenPanes.end(); ++i) {
Chris@127 220 if (i->pane == pane) {
Chris@127 221 m_hiddenPanes.erase(i);
Chris@127 222 found = true;
Chris@127 223 break;
Chris@127 224 }
Chris@127 225 }
Chris@127 226
Chris@127 227 if (!found) {
Chris@127 228 std::cerr << "WARNING: PaneStack::deletePane(" << pane << "): Pane not found in visible or hidden panes, not deleting" << std::endl;
Chris@127 229 return;
Chris@127 230 }
Chris@127 231 }
Chris@127 232
Chris@271 233 emit paneAboutToBeDeleted(pane);
Chris@271 234
Chris@127 235 delete pane->parent();
Chris@127 236
Chris@127 237 if (m_currentPane == pane) {
Chris@127 238 if (m_panes.size() > 0) {
Chris@127 239 setCurrentPane(m_panes[0].pane);
Chris@127 240 } else {
Chris@127 241 setCurrentPane(0);
Chris@127 242 }
Chris@127 243 }
Chris@271 244
Chris@271 245 emit paneDeleted();
Chris@127 246 }
Chris@127 247
Chris@127 248 int
Chris@127 249 PaneStack::getPaneCount() const
Chris@127 250 {
Chris@127 251 return m_panes.size();
Chris@127 252 }
Chris@127 253
Chris@127 254 int
Chris@127 255 PaneStack::getHiddenPaneCount() const
Chris@127 256 {
Chris@127 257 return m_hiddenPanes.size();
Chris@127 258 }
Chris@127 259
Chris@127 260 void
Chris@127 261 PaneStack::hidePane(Pane *pane)
Chris@127 262 {
Chris@127 263 std::vector<PaneRec>::iterator i = m_panes.begin();
Chris@127 264
Chris@127 265 while (i != m_panes.end()) {
Chris@127 266 if (i->pane == pane) {
Chris@127 267
Chris@127 268 m_hiddenPanes.push_back(*i);
Chris@127 269 m_panes.erase(i);
Chris@127 270
Chris@127 271 QWidget *pw = dynamic_cast<QWidget *>(pane->parent());
Chris@127 272 if (pw) pw->hide();
Chris@127 273
Chris@127 274 if (m_currentPane == pane) {
Chris@127 275 if (m_panes.size() > 0) {
Chris@127 276 setCurrentPane(m_panes[0].pane);
Chris@127 277 } else {
Chris@127 278 setCurrentPane(0);
Chris@127 279 }
Chris@127 280 }
Chris@127 281
Chris@127 282 return;
Chris@127 283 }
Chris@127 284 ++i;
Chris@127 285 }
Chris@127 286
Chris@127 287 std::cerr << "WARNING: PaneStack::hidePane(" << pane << "): Pane not found in visible panes" << std::endl;
Chris@127 288 }
Chris@127 289
Chris@127 290 void
Chris@127 291 PaneStack::showPane(Pane *pane)
Chris@127 292 {
Chris@127 293 std::vector<PaneRec>::iterator i = m_hiddenPanes.begin();
Chris@127 294
Chris@127 295 while (i != m_hiddenPanes.end()) {
Chris@127 296 if (i->pane == pane) {
Chris@127 297 m_panes.push_back(*i);
Chris@127 298 m_hiddenPanes.erase(i);
Chris@127 299 QWidget *pw = dynamic_cast<QWidget *>(pane->parent());
Chris@127 300 if (pw) pw->show();
Chris@127 301
Chris@127 302 //!!! update current pane
Chris@127 303
Chris@127 304 return;
Chris@127 305 }
Chris@127 306 ++i;
Chris@127 307 }
Chris@127 308
Chris@127 309 std::cerr << "WARNING: PaneStack::showPane(" << pane << "): Pane not found in hidden panes" << std::endl;
Chris@127 310 }
Chris@127 311
Chris@127 312 void
Chris@127 313 PaneStack::setCurrentPane(Pane *pane) // may be null
Chris@127 314 {
Chris@127 315 if (m_currentPane == pane) return;
Chris@127 316
Chris@127 317 std::vector<PaneRec>::iterator i = m_panes.begin();
Chris@127 318
Chris@127 319 // We used to do this by setting the foreground and background
Chris@127 320 // role, but it seems the background role is ignored and the
Chris@127 321 // background drawn transparent in Qt 4.1 -- I can't quite see why
Chris@127 322
Chris@127 323 QPixmap selectedMap(1, 1);
Chris@127 324 selectedMap.fill(QApplication::palette().color(QPalette::Foreground));
Chris@127 325
Chris@127 326 QPixmap unselectedMap(1, 1);
Chris@127 327 unselectedMap.fill(QApplication::palette().color(QPalette::Background));
Chris@127 328
Chris@127 329 bool found = false;
Chris@127 330
Chris@127 331 while (i != m_panes.end()) {
Chris@127 332 if (i->pane == pane) {
Chris@127 333 i->currentIndicator->setPixmap(selectedMap);
Chris@179 334 if (m_layoutStyle != PropertyStackPerPaneLayout) {
Chris@127 335 m_propertyStackStack->setCurrentWidget(i->propertyStack);
Chris@127 336 }
Chris@127 337 found = true;
Chris@127 338 } else {
Chris@127 339 i->currentIndicator->setPixmap(unselectedMap);
Chris@127 340 }
Chris@127 341 ++i;
Chris@127 342 }
Chris@127 343
Chris@127 344 if (found || pane == 0) {
Chris@127 345 m_currentPane = pane;
Chris@127 346 emit currentPaneChanged(m_currentPane);
Chris@127 347 } else {
Chris@127 348 std::cerr << "WARNING: PaneStack::setCurrentPane(" << pane << "): pane is not a visible pane in this stack" << std::endl;
Chris@127 349 }
Chris@127 350 }
Chris@127 351
Chris@127 352 void
Chris@127 353 PaneStack::setCurrentLayer(Pane *pane, Layer *layer) // may be null
Chris@127 354 {
Chris@127 355 setCurrentPane(pane);
Chris@127 356
Chris@127 357 if (m_currentPane) {
Chris@127 358
Chris@127 359 std::vector<PaneRec>::iterator i = m_panes.begin();
Chris@127 360
Chris@127 361 while (i != m_panes.end()) {
Chris@127 362
Chris@127 363 if (i->pane == pane) {
Chris@127 364 PropertyStack *stack = dynamic_cast<PropertyStack *>
Chris@127 365 (i->propertyStack);
Chris@127 366 if (stack) {
Chris@127 367 if (stack->containsContainer(layer)) {
Chris@127 368 stack->setCurrentIndex(stack->getContainerIndex(layer));
Chris@127 369 emit currentLayerChanged(pane, layer);
Chris@127 370 } else {
Chris@127 371 stack->setCurrentIndex
Chris@127 372 (stack->getContainerIndex
Chris@127 373 (pane->getPropertyContainer(0)));
Chris@127 374 emit currentLayerChanged(pane, 0);
Chris@127 375 }
Chris@127 376 }
Chris@127 377 break;
Chris@127 378 }
Chris@127 379 ++i;
Chris@127 380 }
Chris@127 381 }
Chris@127 382 }
Chris@127 383
Chris@127 384 Pane *
Chris@127 385 PaneStack::getCurrentPane()
Chris@127 386 {
Chris@127 387 return m_currentPane;
Chris@127 388 }
Chris@127 389
Chris@127 390 void
Chris@127 391 PaneStack::propertyContainerAdded(PropertyContainer *)
Chris@127 392 {
Chris@127 393 sizePropertyStacks();
Chris@127 394 }
Chris@127 395
Chris@127 396 void
Chris@127 397 PaneStack::propertyContainerRemoved(PropertyContainer *)
Chris@127 398 {
Chris@127 399 sizePropertyStacks();
Chris@127 400 }
Chris@127 401
Chris@127 402 void
Chris@127 403 PaneStack::propertyContainerSelected(View *client, PropertyContainer *pc)
Chris@127 404 {
Chris@127 405 std::vector<PaneRec>::iterator i = m_panes.begin();
Chris@127 406
Chris@127 407 while (i != m_panes.end()) {
Chris@127 408 PropertyStack *stack = dynamic_cast<PropertyStack *>(i->propertyStack);
Chris@127 409 if (stack &&
Chris@127 410 stack->getClient() == client &&
Chris@127 411 stack->containsContainer(pc)) {
Chris@127 412 setCurrentPane(i->pane);
Chris@127 413 break;
Chris@127 414 }
Chris@127 415 ++i;
Chris@127 416 }
Chris@127 417
Chris@127 418 Layer *layer = dynamic_cast<Layer *>(pc);
Chris@127 419 if (layer) emit currentLayerChanged(m_currentPane, layer);
Chris@127 420 else emit currentLayerChanged(m_currentPane, 0);
Chris@127 421 }
Chris@127 422
Chris@127 423 void
Chris@190 424 PaneStack::viewSelected(View *v)
Chris@190 425 {
Chris@190 426 Pane *p = dynamic_cast<Pane *>(v);
Chris@190 427 if (p) setCurrentPane(p);
Chris@190 428 }
Chris@190 429
Chris@190 430 void
Chris@127 431 PaneStack::paneInteractedWith()
Chris@127 432 {
Chris@127 433 Pane *pane = dynamic_cast<Pane *>(sender());
Chris@127 434 if (!pane) return;
Chris@127 435 setCurrentPane(pane);
Chris@127 436 }
Chris@127 437
Chris@127 438 void
Chris@127 439 PaneStack::rightButtonMenuRequested(QPoint position)
Chris@127 440 {
Chris@127 441 Pane *pane = dynamic_cast<Pane *>(sender());
Chris@127 442 if (!pane) return;
Chris@127 443 emit rightButtonMenuRequested(pane, position);
Chris@127 444 }
Chris@127 445
Chris@127 446 void
Chris@127 447 PaneStack::sizePropertyStacks()
Chris@127 448 {
Chris@127 449 int maxMinWidth = 0;
Chris@127 450
Chris@235 451 if (m_propertyStackMinWidth > 0) maxMinWidth = m_propertyStackMinWidth;
Chris@235 452
Chris@127 453 for (size_t i = 0; i < m_panes.size(); ++i) {
Chris@127 454 if (!m_panes[i].propertyStack) continue;
Chris@247 455 #ifdef DEBUG_PANE_STACK
Chris@243 456 std::cerr << "PaneStack::sizePropertyStacks: " << i << ": min "
Chris@243 457 << m_panes[i].propertyStack->minimumSizeHint().width() << ", hint "
Chris@243 458 << m_panes[i].propertyStack->sizeHint().width() << ", current "
Chris@243 459 << m_panes[i].propertyStack->width() << std::endl;
Chris@247 460 #endif
Chris@127 461
Chris@246 462 if (m_panes[i].propertyStack->sizeHint().width() > maxMinWidth) {
Chris@246 463 maxMinWidth = m_panes[i].propertyStack->sizeHint().width();
Chris@127 464 }
Chris@127 465 }
Chris@127 466
Chris@247 467 #ifdef DEBUG_PANE_STACK
Chris@247 468 std::cerr << "PaneStack::sizePropertyStacks: max min width " << maxMinWidth << std::endl;
Chris@247 469 #endif
Chris@127 470
Chris@238 471 //#ifdef Q_WS_MAC
Chris@127 472 // This is necessary to compensate for cb->setMinimumSize(10, 10)
Chris@127 473 // in PropertyBox in the Mac version (to avoid a mysterious crash)
Chris@247 474 // ... no longer necessary with qt4.2
Chris@238 475 // int setWidth = maxMinWidth * 3 / 2;
Chris@238 476 //#else
Chris@127 477 int setWidth = maxMinWidth;
Chris@238 478 //#endif
Chris@127 479
Chris@127 480 m_propertyStackStack->setMaximumWidth(setWidth + 10);
Chris@127 481
Chris@127 482 for (size_t i = 0; i < m_panes.size(); ++i) {
Chris@127 483 if (!m_panes[i].propertyStack) continue;
Chris@127 484 m_panes[i].propertyStack->setMinimumWidth(setWidth);
Chris@127 485 }
Chris@180 486
Chris@180 487 emit propertyStacksResized();
Chris@127 488 }
Chris@127 489
Chris@312 490 void
Chris@312 491 PaneStack::paneDropAccepted(QStringList uriList)
Chris@312 492 {
Chris@312 493 Pane *pane = dynamic_cast<Pane *>(sender());
Chris@312 494 emit dropAccepted(pane, uriList);
Chris@312 495 }
Chris@312 496
Chris@312 497 void
Chris@312 498 PaneStack::paneDropAccepted(QString text)
Chris@312 499 {
Chris@312 500 Pane *pane = dynamic_cast<Pane *>(sender());
Chris@312 501 emit dropAccepted(pane, text);
Chris@312 502 }
Chris@127 503