svgui  1.9
PaneStack.cpp
Go to the documentation of this file.
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2 
3 /*
4  Sonic Visualiser
5  An audio file viewer and annotation editor.
6  Centre for Digital Music, Queen Mary, University of London.
7  This file copyright 2006 Chris Cannam and QMUL.
8 
9  This program is free software; you can redistribute it and/or
10  modify it under the terms of the GNU General Public License as
11  published by the Free Software Foundation; either version 2 of the
12  License, or (at your option) any later version. See the file
13  COPYING included with this distribution for more information.
14 */
15 
16 #include "PaneStack.h"
17 
18 #include "Pane.h"
19 #include "widgets/PropertyStack.h"
20 #include "widgets/IconLoader.h"
21 #include "widgets/ClickableLabel.h"
22 #include "layer/Layer.h"
23 #include "ViewManager.h"
24 
25 #include <QApplication>
26 #include <QHBoxLayout>
27 #include <QVBoxLayout>
28 #include <QPainter>
29 #include <QPalette>
30 #include <QLabel>
31 #include <QPushButton>
32 #include <QSplitter>
33 #include <QStackedWidget>
34 
35 #include <iostream>
36 
37 //#define DEBUG_PANE_STACK 1
38 
39 PaneStack::PaneStack(QWidget *parent, ViewManager *viewManager) :
40  QFrame(parent),
41  m_currentPane(0),
42  m_showAccessories(true),
43  m_splitter(new QSplitter),
44  m_propertyStackStack(new QStackedWidget),
45  m_viewManager(viewManager),
46  m_propertyStackMinWidth(100),
47  m_layoutStyle(PropertyStackPerPaneLayout)
48 {
49  QHBoxLayout *layout = new QHBoxLayout;
50  layout->setMargin(0);
51  layout->setSpacing(0);
52 
53  m_splitter->setOrientation(Qt::Vertical);
54  m_splitter->setOpaqueResize(false);
55 
56  layout->addWidget(m_splitter);
57  layout->setStretchFactor(m_splitter, 1);
58  layout->addWidget(m_propertyStackStack);
59  m_propertyStackStack->hide();
60 
61  setLayout(layout);
62 }
63 
64 void
66 {
67  m_showAccessories = show;
68 }
69 
70 Pane *
71 PaneStack::addPane(bool suppressPropertyBox)
72 {
73  return insertPane(getPaneCount(), suppressPropertyBox);
74 }
75 
76 Pane *
77 PaneStack::insertPane(int index, bool suppressPropertyBox)
78 {
79  QFrame *frame = new QFrame;
80 
81  QGridLayout *layout = new QGridLayout;
82  layout->setMargin(0);
83  layout->setSpacing(2);
84 
85  QPushButton *xButton = new QPushButton(frame);
86  xButton->setIcon(IconLoader().load("cross"));
87  xButton->setFixedSize(QSize(16, 16));
88  xButton->setFlat(true);
89  xButton->setVisible(m_showAccessories);
90  layout->addWidget(xButton, 0, 0);
91  connect(xButton, SIGNAL(clicked()), this, SLOT(paneDeleteButtonClicked()));
92 
93  ClickableLabel *currentIndicator = new ClickableLabel(frame);
94  connect(currentIndicator, SIGNAL(clicked()), this, SLOT(indicatorClicked()));
95  layout->addWidget(currentIndicator, 1, 0);
96  layout->setRowStretch(1, 20);
97  currentIndicator->setMinimumWidth(8);
98  currentIndicator->setScaledContents(true);
99  currentIndicator->setVisible(m_showAccessories);
100 
101  int initialCentreFrame = -1;
102  if (!m_panes.empty()) {
103  initialCentreFrame = m_panes[0].pane->getCentreFrame();
104  }
105 
106  Pane *pane = new Pane(frame);
107  if (initialCentreFrame >= 0) {
108  pane->setViewManager(m_viewManager, initialCentreFrame);
109  } else {
111  }
112  layout->addWidget(pane, 0, 1, 2, 1);
113  layout->setColumnStretch(1, 20);
114 
115  QWidget *properties = 0;
116  if (suppressPropertyBox) {
117  properties = new QFrame();
118  } else {
119  properties = new PropertyStack(frame, pane);
120  connect(properties, SIGNAL(propertyContainerSelected(View *, PropertyContainer *)),
121  this, SLOT(propertyContainerSelected(View *, PropertyContainer *)));
122  connect(properties, SIGNAL(viewSelected(View *)),
123  this, SLOT(viewSelected(View *)));
124  connect(properties, SIGNAL(contextHelpChanged(const QString &)),
125  this, SIGNAL(contextHelpChanged(const QString &)));
126  }
128  layout->addWidget(properties, 0, 2, 2, 1);
129  } else {
130  properties->setParent(m_propertyStackStack);
131  m_propertyStackStack->addWidget(properties);
132  }
133  layout->setColumnStretch(2, 0);
134 
135  PaneRec rec;
136  rec.pane = pane;
137  rec.propertyStack = properties;
138  rec.xButton = xButton;
139  rec.currentIndicator = currentIndicator;
140  rec.frame = frame;
141  rec.layout = layout;
142  m_panes.push_back(rec);
143 
144  frame->setLayout(layout);
145  m_splitter->insertWidget(index, frame);
146 
147  connect(pane, SIGNAL(propertyContainerAdded(PropertyContainer *)),
148  this, SLOT(propertyContainerAdded(PropertyContainer *)));
149  connect(pane, SIGNAL(propertyContainerRemoved(PropertyContainer *)),
150  this, SLOT(propertyContainerRemoved(PropertyContainer *)));
151  connect(pane, SIGNAL(paneInteractedWith()),
152  this, SLOT(paneInteractedWith()));
153  connect(pane, SIGNAL(rightButtonMenuRequested(QPoint)),
154  this, SLOT(rightButtonMenuRequested(QPoint)));
155  connect(pane, SIGNAL(dropAccepted(QStringList)),
156  this, SLOT(paneDropAccepted(QStringList)));
157  connect(pane, SIGNAL(dropAccepted(QString)),
158  this, SLOT(paneDropAccepted(QString)));
159  connect(pane, SIGNAL(doubleClickSelectInvoked(int)),
160  this, SIGNAL(doubleClickSelectInvoked(int)));
161 
162  emit paneAdded(pane);
163  emit paneAdded();
164 
165  if (!m_currentPane) {
166  setCurrentPane(pane);
167  }
168 
170 
171  return pane;
172 }
173 
174 void
176 {
177  for (std::vector<PaneRec>::iterator i = m_panes.begin();
178  i != m_panes.end(); ++i) {
179  i->propertyStack->setMinimumWidth(mw);
180  }
182 }
183 
184 void
186 {
187  if (style == m_layoutStyle) return;
188  m_layoutStyle = style;
189 
190  std::vector<PaneRec>::iterator i;
191 
192  switch (style) {
193 
194  case NoPropertyStacks:
196 
197  for (i = m_panes.begin(); i != m_panes.end(); ++i) {
198  i->layout->removeWidget(i->propertyStack);
199  i->propertyStack->setParent(m_propertyStackStack);
200  m_propertyStackStack->addWidget(i->propertyStack);
201  }
202  m_propertyStackStack->setVisible(style != NoPropertyStacks);
203  break;
204 
206 
207  for (i = m_panes.begin(); i != m_panes.end(); ++i) {
208  m_propertyStackStack->removeWidget(i->propertyStack);
209  i->propertyStack->setParent(i->frame);
210  i->layout->addWidget(i->propertyStack, 0, 2, 2, 1);
211  i->propertyStack->show();
212  }
213  m_propertyStackStack->hide();
214  break;
215  }
216 }
217 
218 Pane *
220 {
221  if (n < (int)m_panes.size()) {
222  return m_panes[n].pane;
223  } else {
224  return 0;
225  }
226 }
227 
228 int
230 {
231  for (int i = 0; i < getPaneCount(); ++i) {
232  if (pane == getPane(i)) {
233  return i;
234  }
235  }
236  return -1;
237 }
238 
239 Pane *
241 {
242  return m_hiddenPanes[n].pane;
243 }
244 
245 void
247 {
248  cerr << "PaneStack::deletePane(" << pane << ")" << endl;
249 
250  std::vector<PaneRec>::iterator i;
251  bool found = false;
252 
253  QWidget *stack = 0;
254 
255  for (i = m_panes.begin(); i != m_panes.end(); ++i) {
256  if (i->pane == pane) {
257  stack = i->propertyStack;
258  m_panes.erase(i);
259  found = true;
260  break;
261  }
262  }
263 
264  if (!found) {
265 
266  for (i = m_hiddenPanes.begin(); i != m_hiddenPanes.end(); ++i) {
267  if (i->pane == pane) {
268  stack = i->propertyStack;
269  m_hiddenPanes.erase(i);
270  found = true;
271  break;
272  }
273  }
274 
275  if (!found) {
276  cerr << "WARNING: PaneStack::deletePane(" << pane << "): Pane not found in visible or hidden panes, not deleting" << endl;
277  return;
278  }
279  }
280 
281  emit paneAboutToBeDeleted(pane);
282 
283  cerr << "PaneStack::deletePane: about to delete parent " << pane->parent() << " of pane " << pane << endl;
284 
285  // The property stack associated with the parent was initially
286  // created with the same parent as it, so it would be deleted when
287  // we delete the pane's parent in a moment -- but it may have been
288  // reparented depending on the layout. We'd better delete it
289  // separately first. (This fixes a crash on opening a new layer
290  // with a new unit type in it, when a long-defunct property box
291  // could be signalled from the unit database to tell it that a new
292  // unit had appeared.)
293  delete stack;
294 
295  delete pane->parent();
296 
297  if (m_currentPane == pane) {
298  if (m_panes.size() > 0) {
299  setCurrentPane(m_panes[0].pane);
300  } else {
301  setCurrentPane(0);
302  }
303  }
304 
306 
307  emit paneDeleted();
308 }
309 
310 void
312 {
313  cerr << "PaneStack::showOrHidePaneAccessories: count == " << getPaneCount() << endl;
314 
315  bool multi = (getPaneCount() > 1);
316  for (std::vector<PaneRec>::iterator i = m_panes.begin();
317  i != m_panes.end(); ++i) {
318  i->xButton->setVisible(multi && m_showAccessories);
319  i->currentIndicator->setVisible(multi && m_showAccessories);
320  }
321 }
322 
323 int
325 {
326  return m_panes.size();
327 }
328 
329 int
331 {
332  return m_hiddenPanes.size();
333 }
334 
335 void
337 {
338  std::vector<PaneRec>::iterator i = m_panes.begin();
339 
340  while (i != m_panes.end()) {
341  if (i->pane == pane) {
342 
343  m_hiddenPanes.push_back(*i);
344  m_panes.erase(i);
345 
346  QWidget *pw = dynamic_cast<QWidget *>(pane->parent());
347  if (pw) pw->hide();
348 
349  if (m_currentPane == pane) {
350  if (m_panes.size() > 0) {
351  setCurrentPane(m_panes[0].pane);
352  } else {
353  setCurrentPane(0);
354  }
355  }
356 
358  emit paneHidden(pane);
359  emit paneHidden();
360  return;
361  }
362  ++i;
363  }
364 
365  cerr << "WARNING: PaneStack::hidePane(" << pane << "): Pane not found in visible panes" << endl;
366 }
367 
368 void
370 {
371  std::vector<PaneRec>::iterator i = m_hiddenPanes.begin();
372 
373  while (i != m_hiddenPanes.end()) {
374  if (i->pane == pane) {
375  m_panes.push_back(*i);
376  m_hiddenPanes.erase(i);
377  QWidget *pw = dynamic_cast<QWidget *>(pane->parent());
378  if (pw) pw->show();
379 
381 
383 
384  return;
385  }
386  ++i;
387  }
388 
389  cerr << "WARNING: PaneStack::showPane(" << pane << "): Pane not found in hidden panes" << endl;
390 }
391 
392 void
393 PaneStack::setCurrentPane(Pane *pane) // may be null
394 {
395  if (m_currentPane == pane) return;
396 
397  std::vector<PaneRec>::iterator i = m_panes.begin();
398 
399  // We used to do this by setting the foreground and background
400  // role, but it seems the background role is ignored and the
401  // background drawn transparent in Qt 4.1 -- I can't quite see why
402 
403  QPixmap selectedMap(1, 1);
404  selectedMap.fill(QApplication::palette().color(QPalette::Foreground));
405 
406  QPixmap unselectedMap(1, 1);
407  unselectedMap.fill(QApplication::palette().color(QPalette::Background));
408 
409  bool found = false;
410 
411  while (i != m_panes.end()) {
412  if (i->pane == pane) {
413  i->currentIndicator->setPixmap(selectedMap);
415  m_propertyStackStack->setCurrentWidget(i->propertyStack);
416  }
417  found = true;
418  } else {
419  i->currentIndicator->setPixmap(unselectedMap);
420  }
421  ++i;
422  }
423 
424  if (found || pane == 0) {
425  m_currentPane = pane;
427  } else {
428  cerr << "WARNING: PaneStack::setCurrentPane(" << pane << "): pane is not a visible pane in this stack" << endl;
429  }
430 }
431 
432 void
433 PaneStack::setCurrentLayer(Pane *pane, Layer *layer) // may be null
434 {
435  setCurrentPane(pane);
436 
437  if (m_currentPane) {
438 
439  std::vector<PaneRec>::iterator i = m_panes.begin();
440 
441  while (i != m_panes.end()) {
442 
443  if (i->pane == pane) {
444  PropertyStack *stack = dynamic_cast<PropertyStack *>
445  (i->propertyStack);
446  if (stack) {
447  if (stack->containsContainer(layer)) {
448  stack->setCurrentIndex(stack->getContainerIndex(layer));
449  emit currentLayerChanged(pane, layer);
450  } else {
451  stack->setCurrentIndex
452  (stack->getContainerIndex
453  (pane->getPropertyContainer(0)));
454  emit currentLayerChanged(pane, 0);
455  }
456  }
457  break;
458  }
459  ++i;
460  }
461  }
462 }
463 
464 Pane *
466 {
467  return m_currentPane;
468 }
469 
470 void
472 {
474 }
475 
476 void
478 {
480 }
481 
482 void
483 PaneStack::propertyContainerSelected(View *client, PropertyContainer *pc)
484 {
485  std::vector<PaneRec>::iterator i = m_panes.begin();
486 
487  while (i != m_panes.end()) {
488  PropertyStack *stack = dynamic_cast<PropertyStack *>(i->propertyStack);
489  if (stack &&
490  stack->getClient() == client &&
491  stack->containsContainer(pc)) {
492  setCurrentPane(i->pane);
493  break;
494  }
495  ++i;
496  }
497 
498  Layer *layer = dynamic_cast<Layer *>(pc);
499  if (layer) emit currentLayerChanged(m_currentPane, layer);
500  else emit currentLayerChanged(m_currentPane, 0);
501 }
502 
503 void
505 {
506  Pane *p = dynamic_cast<Pane *>(v);
507  if (p) setCurrentPane(p);
508 }
509 
510 void
512 {
513  Pane *pane = dynamic_cast<Pane *>(sender());
514  if (!pane) return;
515  setCurrentPane(pane);
516 }
517 
518 void
520 {
521  Pane *pane = dynamic_cast<Pane *>(sender());
522  if (!pane) return;
523  emit rightButtonMenuRequested(pane, position);
524 }
525 
526 void
528 {
529  int maxMinWidth = 0;
530 
531  if (m_propertyStackMinWidth > 0) maxMinWidth = m_propertyStackMinWidth;
532 
533  for (int i = 0; i < (int)m_panes.size(); ++i) {
534  if (!m_panes[i].propertyStack) continue;
535 #ifdef DEBUG_PANE_STACK
536  SVDEBUG << "PaneStack::sizePropertyStacks: " << i << ": min "
537  << m_panes[i].propertyStack->minimumSizeHint().width() << ", hint "
538  << m_panes[i].propertyStack->sizeHint().width() << ", current "
539  << m_panes[i].propertyStack->width() << endl;
540 #endif
541 
542  if (m_panes[i].propertyStack->sizeHint().width() > maxMinWidth) {
543  maxMinWidth = m_panes[i].propertyStack->sizeHint().width();
544  }
545  }
546 
547 #ifdef DEBUG_PANE_STACK
548  SVDEBUG << "PaneStack::sizePropertyStacks: max min width " << maxMinWidth << endl;
549 #endif
550 
551  int setWidth = maxMinWidth;
552 
553  m_propertyStackStack->setMaximumWidth(setWidth + 10);
554 
555  for (int i = 0; i < (int)m_panes.size(); ++i) {
556  if (!m_panes[i].propertyStack) continue;
557  m_panes[i].propertyStack->setMinimumWidth(setWidth);
558  }
559 
560  emit propertyStacksResized(setWidth);
561  emit propertyStacksResized();
562 }
563 
564 void
565 PaneStack::paneDropAccepted(QStringList uriList)
566 {
567  Pane *pane = dynamic_cast<Pane *>(sender());
568  emit dropAccepted(pane, uriList);
569 }
570 
571 void
573 {
574  Pane *pane = dynamic_cast<Pane *>(sender());
575  emit dropAccepted(pane, text);
576 }
577 
578 void
580 {
581  QObject *s = sender();
582  for (int i = 0; i < (int)m_panes.size(); ++i) {
583  if (m_panes[i].xButton == s) {
584  emit paneDeleteButtonClicked(m_panes[i].pane);
585  }
586  }
587 }
588 
589 void
591 {
592  QObject *s = sender();
593 
594  for (int i = 0; i < (int)m_panes.size(); ++i) {
595  if (m_panes[i].currentIndicator == s) {
596  setCurrentPane(m_panes[i].pane);
597  return;
598  }
599  }
600 }
601 
602 void
604 {
605  QList<int> sizes = m_splitter->sizes();
606  if (sizes.empty()) return;
607 
608  int count = sizes.size();
609 
610  int fixed = 0, variable = 0, total = 0;
611  int varicount = 0;
612 
613  for (int i = 0; i < count; ++i) {
614  total += sizes[i];
615  }
616 
617  variable = total;
618 
619  for (int i = 0; i < count; ++i) {
620  int minh = m_panes[i].pane->minimumSize().height();
621  if (minh == m_panes[i].pane->maximumSize().height()) {
622  fixed += minh;
623  variable -= minh;
624  } else {
625  varicount++;
626  }
627  }
628 
629  if (total == 0) return;
630 
631  sizes.clear();
632 
633  int each = (varicount > 0 ? (variable / varicount) : 0);
634  int remaining = total;
635 
636  for (int i = 0; i < count; ++i) {
637  if (i == count - 1) {
638  sizes.push_back(remaining);
639  } else {
640  int minh = m_panes[i].pane->minimumSize().height();
641  if (minh == m_panes[i].pane->maximumSize().height()) {
642  sizes.push_back(minh);
643  remaining -= minh;
644  } else {
645  sizes.push_back(each);
646  remaining -= each;
647  }
648  }
649  }
650 
651 /*
652  cerr << "sizes: ";
653  for (int i = 0; i < sizes.size(); ++i) {
654  cerr << sizes[i] << " ";
655  }
656  cerr << endl;
657 */
658 
659  m_splitter->setSizes(sizes);
660 }
661 
Pane * getHiddenPane(int n)
Definition: PaneStack.cpp:240
Pane * getPane(int n)
Definition: PaneStack.cpp:219
int getPaneCount() const
Definition: PaneStack.cpp:324
Definition: Pane.h:34
virtual const PropertyContainer * getPropertyContainer(int i) const
Definition: View.cpp:171
The base class for visual representations of the data found in a Model.
Definition: Layer.h:52
void setShowPaneAccessories(bool show)
Definition: PaneStack.cpp:65
void hidePane(Pane *pane)
Definition: PaneStack.cpp:336
void propertyStacksResized()
PaneStack(QWidget *parent, ViewManager *viewManager)
Definition: PaneStack.cpp:39
int m_propertyStackMinWidth
Definition: PaneStack.h:132
void dropAccepted(Pane *pane, QStringList uriList)
void paneHidden()
ViewManager * m_viewManager
Definition: PaneStack.h:131
void sizePanesEqually()
Definition: PaneStack.cpp:603
void paneDeleteButtonClicked()
Definition: PaneStack.cpp:579
std::vector< PaneRec > m_panes
Definition: PaneStack.h:123
QLabel * currentIndicator
Definition: PaneStack.h:118
void paneAdded()
void doubleClickSelectInvoked(int frame)
int getHiddenPaneCount() const
Definition: PaneStack.cpp:330
QPushButton * xButton
Definition: PaneStack.h:117
void paneAboutToBeDeleted(Pane *pane)
void propertyContainerAdded(PropertyContainer *)
Definition: PaneStack.cpp:471
virtual void setViewManager(ViewManager *m)
Definition: View.cpp:692
QSplitter * m_splitter
Definition: PaneStack.h:128
LayoutStyle m_layoutStyle
Definition: PaneStack.h:137
void propertyContainerRemoved(PropertyContainer *)
Definition: PaneStack.cpp:477
View * getClient()
Definition: PropertyStack.h:36
QWidget * propertyStack
Definition: PaneStack.h:116
int getPaneIndex(Pane *pane)
Definition: PaneStack.cpp:229
Pane * getCurrentPane()
Definition: PaneStack.cpp:465
void indicatorClicked()
Definition: PaneStack.cpp:590
void showOrHidePaneAccessories()
Definition: PaneStack.cpp:311
void propertyContainerSelected(View *client, PropertyContainer *)
Definition: PaneStack.cpp:483
void deletePane(Pane *pane)
Definition: PaneStack.cpp:246
void setLayoutStyle(LayoutStyle style)
Definition: PaneStack.cpp:185
void contextHelpChanged(const QString &)
void paneDeleted()
void setCurrentLayer(Pane *pane, Layer *layer)
Definition: PaneStack.cpp:433
int getContainerIndex(PropertyContainer *container) const
void setPropertyStackMinWidth(int mw)
Definition: PaneStack.cpp:175
void currentPaneChanged(Pane *pane)
QGridLayout * layout
Definition: PaneStack.h:120
QStackedWidget * m_propertyStackStack
Definition: PaneStack.h:129
bool containsContainer(PropertyContainer *container) const
View is the base class of widgets that display one or more overlaid views of data against a horizonta...
Definition: View.h:50
Pane * m_currentPane
Definition: PaneStack.h:111
void setCurrentPane(Pane *pane)
Definition: PaneStack.cpp:393
The ViewManager manages properties that may need to be synchronised between separate Views.
Definition: ViewManager.h:73
void showPane(Pane *pane)
Definition: PaneStack.cpp:369
Pane * addPane(bool suppressPropertyBox=false)
Definition: PaneStack.cpp:71
void viewSelected(View *v)
Definition: PaneStack.cpp:504
void sizePropertyStacks()
Definition: PaneStack.cpp:527
void rightButtonMenuRequested(Pane *pane, QPoint position)
bool m_showAccessories
Definition: PaneStack.h:126
void paneInteractedWith()
Definition: PaneStack.cpp:511
std::vector< PaneRec > m_hiddenPanes
Definition: PaneStack.h:124
void paneDropAccepted(QStringList)
Definition: PaneStack.cpp:565
void currentLayerChanged(Pane *pane, Layer *layer)
Pane * insertPane(int index, bool suppressPropertyBox=false)
Definition: PaneStack.cpp:77