1 /***************************************************************************
2 * *
3 * copyright : (C) 2007 The University of Toronto *
4 * netterfield@astro.utoronto.ca *
5 * *
6 * This program is free software; you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
8 * the Free Software Foundation; either version 2 of the License, or *
9 * (at your option) any later version. *
10 * *
11 ***************************************************************************/
12
13 #include "viewgridlayout.h"
14
15 #include "view.h"
16 #include "viewitem.h"
17 #include "plotitem.h"
18 #include "layoutboxitem.h"
19 #include "sharedaxisboxitem.h"
20 #include "formatgridhelper.h"
21
22 #include <QDebug>
23
24 // 0 off, 1 On
25 #define DEBUG_LAYOUT 0
26 #define DEBUG_PLOT_STANDARDIZATION 0
27 #define DEBUG_SHAREDAXIS 0
28
29 // What factor should be used to determine "close" plot sizes.
30 #define PLOT_STANDARDIZATION_FACTOR .2
31
32 namespace Kst {
33
ViewGridLayout(ViewItem * parent)34 ViewGridLayout::ViewGridLayout(ViewItem *parent)
35 : QObject(parent),
36 _enabled(false),
37 _rowCount(0),
38 _columnCount(0),
39 _shareX(false),
40 _shareY(false) {
41 _spacing = parent->layoutSpacing();
42 _margin = parent->layoutMargins();
43 }
44
45
~ViewGridLayout()46 ViewGridLayout::~ViewGridLayout() {
47 }
48
49
parentItem() const50 ViewItem *ViewGridLayout::parentItem() const {
51 return qobject_cast<ViewItem*>(parent());
52 }
53
54
addViewItem(ViewItem * viewItem,int row,int column)55 void ViewGridLayout::addViewItem(ViewItem *viewItem, int row, int column) {
56 addViewItem(viewItem, row, column, 1, 1);
57 }
58
59
addViewItem(ViewItem * viewItem,int row,int column,int rowSpan,int columnSpan)60 void ViewGridLayout::addViewItem(ViewItem *viewItem, int row, int column, int rowSpan, int columnSpan) {
61 LayoutItem item;
62 item.viewItem = viewItem;
63 item.row = row;
64 item.column = column;
65 item.rowSpan = rowSpan;
66 item.columnSpan = columnSpan;
67 item.transform = viewItem->transform();
68 item.position = viewItem->pos();
69 item.rect = viewItem->rect();
70
71 //Update the row/column counts...
72 int maxRow = row + rowSpan;
73 int maxColumn = column + columnSpan;
74
75 _rowCount = maxRow > _rowCount ? maxRow : _rowCount;
76 _columnCount = maxColumn > _columnCount ? maxColumn : _columnCount;
77
78 //FIXME these could be consolidated
79 _items.append(item);
80 _itemInfos.insert(viewItem, item);
81 _itemLayouts.insert(qMakePair(item.row, item.column), item);
82 }
83
84
rowCount() const85 int ViewGridLayout::rowCount() const {
86 return _rowCount;
87 }
88
89
columnCount() const90 int ViewGridLayout::columnCount() const {
91 return _columnCount;
92 }
93
94
plotLabelMarginWidth(const PlotItem * plotItem) const95 qreal ViewGridLayout::plotLabelMarginWidth(const PlotItem *plotItem) const {
96 if (_itemInfos.contains(plotItem)) {
97 LayoutItem item = _itemInfos.value(plotItem);
98 if (_plotMarginWidth.contains(item.columnSpan))
99 return _plotMarginWidth.value(item.columnSpan).labelMargin;
100 }
101
102 return 0.0;
103 }
104
105
plotLabelMarginHeight(const PlotItem * plotItem) const106 qreal ViewGridLayout::plotLabelMarginHeight(const PlotItem *plotItem) const {
107 if (_itemInfos.contains(plotItem)) {
108 LayoutItem item = _itemInfos.value(plotItem);
109 if (_plotMarginHeight.contains(item.rowSpan))
110 return _plotMarginHeight.value(item.rowSpan).labelMargin;
111 }
112
113 return 0.0;
114 }
115
116
plotAxisMarginWidth(const PlotItem * plotItem) const117 qreal ViewGridLayout::plotAxisMarginWidth(const PlotItem *plotItem) const {
118 if (_itemInfos.contains(plotItem)) {
119 LayoutItem item = _itemInfos.value(plotItem);
120 if (_plotMarginWidth.contains(item.columnSpan))
121 return _plotMarginWidth.value(item.columnSpan).axisMargin;
122 }
123
124 return 0.0;
125 }
126
127
plotAxisMarginHeight(const PlotItem * plotItem) const128 qreal ViewGridLayout::plotAxisMarginHeight(const PlotItem *plotItem) const {
129 if (_itemInfos.contains(plotItem)) {
130 LayoutItem item = _itemInfos.value(plotItem);
131 if (_plotMarginHeight.contains(item.rowSpan))
132 return _plotMarginHeight.value(item.rowSpan).axisMargin;
133 }
134
135 return 0.0;
136 }
137
138
isEnabled() const139 bool ViewGridLayout::isEnabled() const {
140 return _enabled;
141 }
142
143
setEnabled(bool enabled)144 void ViewGridLayout::setEnabled(bool enabled) {
145 _enabled = enabled;
146 emit enabledChanged(_enabled);
147 }
148
149
reset()150 void ViewGridLayout::reset() {
151 foreach (const LayoutItem &item, _items) {
152 item.viewItem->setTransform(item.transform);
153 item.viewItem->setPos(item.position);
154 item.viewItem->setViewRect(item.rect);
155 }
156 }
157
158
sharePlots(ViewItem * item,QPainter * painter,bool creation)159 void ViewGridLayout::sharePlots(ViewItem *item, QPainter *painter, bool creation) {
160 Q_ASSERT(item);
161 Q_ASSERT(item->view());
162
163 // Gather all children of the SharedAxisBoxItem.
164 QList<ViewItem*> viewItems;
165 QList<QGraphicsItem*> list = item->QGraphicsItem::childItems();
166 if (list.isEmpty())
167 return; //not added to undostack
168
169 foreach (QGraphicsItem *graphicsItem, list) {
170 ViewItem *viewItem = dynamic_cast<ViewItem*>(graphicsItem);
171 if (!viewItem || viewItem->hasStaticGeometry() || !viewItem->allowsLayout() || viewItem->parentItem() != item)
172 continue;
173 viewItems.append(viewItem);
174 }
175
176 if (viewItems.isEmpty())
177 return; //not added to undostack
178
179
180 ViewGridLayout *layout = new ViewGridLayout(item);
181
182 FormatGridHelper grid(viewItems);
183
184 int n_views = viewItems.size();
185 for (int i_view = 0; i_view<n_views; i_view++) {
186 ViewItem *v = viewItems.at(i_view);
187 struct AutoFormatRC rc = grid.rcList.at(i_view);
188 layout->addViewItem(v, rc.row, rc.col, 1, 1);
189 }
190 layout->apply();
191
192 layout->shareAxis(painter, creation);
193
194 }
195
196
standardizePlotMargins(ViewItem * item,QPainter * painter)197 void ViewGridLayout::standardizePlotMargins(ViewItem *item, QPainter *painter) {
198 QList<PlotItem*> plotItems;
199 if (item->view()) {
200 QList<QGraphicsItem*> list = item->view()->items();
201 foreach (QGraphicsItem *item, list) {
202 ViewItem *viewItem = dynamic_cast<ViewItem*>(item);
203 if (!viewItem || !viewItem->isVisible())
204 continue;
205 if (PlotItem *plotItem = qobject_cast<PlotItem*>(viewItem)) {
206 plotItems.append(plotItem);
207 }
208 }
209 }
210
211 #if DEBUG_PLOT_STANDARDIZATION
212 qDebug() << "Ready to standarize" << plotItems.count() << "plots";
213 #endif
214
215 QMap<int, qreal> leftMarginWidths;
216 QMap<int, qreal> rightMarginWidths;
217 QMap<int, qreal> topMarginWidths;
218 QMap<int, qreal> bottomMarginHeights;
219 foreach (PlotItem* plotItem, plotItems) {
220 plotItem->calculateBorders(painter);
221 if (leftMarginWidths[floor(plotItem->width()*PLOT_STANDARDIZATION_FACTOR)] < plotItem->leftMarginSize()) {
222 leftMarginWidths[floor(plotItem->width()*PLOT_STANDARDIZATION_FACTOR)] = plotItem->leftMarginSize();
223 }
224 if (rightMarginWidths[floor(plotItem->width()*PLOT_STANDARDIZATION_FACTOR)] < plotItem->rightMarginSize()) {
225 rightMarginWidths[floor(plotItem->width()*PLOT_STANDARDIZATION_FACTOR)] = plotItem->rightMarginSize();
226 }
227 if (topMarginWidths[floor(plotItem->height()*PLOT_STANDARDIZATION_FACTOR)] < plotItem->topMarginSize()) {
228 topMarginWidths[floor(plotItem->height()*PLOT_STANDARDIZATION_FACTOR)] = plotItem->topMarginSize();
229 }
230 if (bottomMarginHeights[floor(plotItem->height()*PLOT_STANDARDIZATION_FACTOR)] < plotItem->bottomMarginSize()) {
231 bottomMarginHeights[floor(plotItem->height()*PLOT_STANDARDIZATION_FACTOR)] = plotItem->bottomMarginSize();
232 }
233 }
234
235 #if DEBUG_PLOT_STANDARDIZATION
236 qDebug() << "Maximum left margin widths" << leftMarginWidths;
237 qDebug() << "Maximum right margin widths" << rightMarginWidths;
238 qDebug() << "Maximum top margin widths" << topMarginWidths;
239 qDebug() << "Maximum bottom margin heights" << bottomMarginHeights;
240 #endif
241
242
243 foreach (PlotItem* plotItem, plotItems) {
244
245 #if DEBUG_PLOT_STANDARDIZATION
246 qDebug() << "Margin left width is " << plotItem->leftMarginSize() << "setting to" << leftMarginWidths[floor(plotItem->width()*PLOT_STANDARDIZATION_FACTOR)] - plotItem->leftMarginSize();
247 qDebug() << "Margin right width is " << plotItem->rightMarginSize() << "setting to" << rightMarginWidths[floor(plotItem->width()*PLOT_STANDARDIZATION_FACTOR)] - plotItem->rightMarginSize();
248 qDebug() << "Margin top width is " << plotItem->topMarginSize() << "setting to" << topMarginWidths[floor(plotItem->height()*PLOT_STANDARDIZATION_FACTOR)] - plotItem->topMarginSize();
249 qDebug() << "Margin bottom height is " << plotItem->bottomMarginSize() << "setting to" << bottomMarginHeights[floor(plotItem->height()*PLOT_STANDARDIZATION_FACTOR)] - plotItem->bottomMarginSize();
250 #endif
251
252 qreal leftPadding = leftMarginWidths[floor(plotItem->width()*PLOT_STANDARDIZATION_FACTOR)] - plotItem->leftMarginSize();
253 qreal rightPadding = rightMarginWidths[floor(plotItem->width()*PLOT_STANDARDIZATION_FACTOR)] - plotItem->rightMarginSize();
254 qreal topPadding = topMarginWidths[floor(plotItem->height()*PLOT_STANDARDIZATION_FACTOR)] - plotItem->topMarginSize();
255 qreal bottomPadding = bottomMarginHeights[floor(plotItem->height()*PLOT_STANDARDIZATION_FACTOR)] - plotItem->bottomMarginSize();
256
257 plotItem->setPadding(leftPadding, rightPadding, topPadding, bottomPadding);
258 }
259 }
260
261
apply()262 void ViewGridLayout::apply() {
263 updatePlotMargins();
264
265 //For now we divide up equally... can do stretch factors and such later...
266
267 QSizeF layoutSize(parentItem()->width() - _margin.width() * 2,
268 parentItem()->height() - _margin.height() * 2);
269
270 QPointF layoutTopLeft = parentItem()->rect().topLeft();
271 layoutTopLeft += QPointF(_margin.width(), _margin.height());
272
273 QRectF layoutRect(layoutTopLeft, layoutSize);
274
275 qreal itemWidth = layoutSize.width() / columnCount();
276 qreal itemHeight = layoutSize.height() / rowCount();
277
278 #if DEBUG_LAYOUT
279 qDebug() << "layouting" << _items.count()
280 << "itemWidth:" << itemWidth
281 << "itemHeight:" << itemHeight
282 << endl;
283 #endif
284
285 PlotItem *plot = 0;
286 foreach (const LayoutItem &item, _items) {
287 QPointF topLeft(itemWidth * item.column, itemHeight * item.row);
288 QSizeF size(itemWidth * item.columnSpan, itemHeight * item.rowSpan);
289 topLeft += layoutTopLeft;
290
291 QRectF itemRect(topLeft, size);
292
293 if (itemRect.top() != layoutRect.top())
294 itemRect.setTop(itemRect.top() + _spacing.height() / 2);
295 if (itemRect.left() != layoutRect.left())
296 itemRect.setLeft(itemRect.left() + _spacing.width() / 2);
297 if (itemRect.bottom() != layoutRect.bottom())
298 itemRect.setBottom(itemRect.bottom() - _spacing.height() / 2);
299 if (itemRect.right() != layoutRect.right())
300 itemRect.setRight(itemRect.right() - _spacing.width() / 2);
301
302 item.viewItem->resetTransform();
303 item.viewItem->setPos(itemRect.topLeft());
304
305 if (item.viewItem->fixedSize()) {
306 itemRect.setBottom(itemRect.top() + item.viewItem->rect().height());
307 itemRect.setRight(itemRect.left() + item.viewItem->rect().width());
308 } else if (item.viewItem->lockAspectRatio()) {
309 qreal newHeight = itemRect.height();
310 qreal newWidth = itemRect.width();
311
312 qreal aspectRatio = item.viewItem->rect().width() / item.viewItem->rect().height();
313 if ((newWidth / newHeight) > aspectRatio) {
314 // newWidth is too large. Use newHeight as key.
315 newWidth = newHeight * aspectRatio;
316 } else {
317 // newHeight is either too large, or perfect. use newWidth as key.
318 newHeight = newWidth / aspectRatio;
319 }
320 itemRect.setBottom(itemRect.top() + newHeight);
321 itemRect.setRight(itemRect.left() + newWidth);
322 }
323 item.viewItem->setViewRect(QRectF(QPoint(0,0), itemRect.size()));
324
325 if (PlotItem *plotItem = qobject_cast<PlotItem*>(item.viewItem)) {
326 plot = plotItem;
327 emit plotItem->updatePlotRect();
328 }
329
330 #if DEBUG_LAYOUT
331 qDebug() << "layout"
332 << "row:" << item.row
333 << "column:" << item.column
334 << "rowSpan:" << item.rowSpan
335 << "columnSpan:" << item.columnSpan
336 << "itemRect:" << itemRect
337 << endl;
338 #endif
339 }
340 if (plot) {
341 plot->setPlotBordersDirty(true);
342 }
343 }
344
345
shareAxis(QPainter * painter,bool creation)346 void ViewGridLayout::shareAxis(QPainter *painter, bool creation) {
347
348 SharedAxisBoxItem *shareBox = qobject_cast<SharedAxisBoxItem*>(parentItem());
349 if (!shareBox) {
350 return;
351 }
352
353 if (creation) {
354 calculateSharing();
355 updateSharedAxis();
356 shareBox->setXAxisShared(_shareX);
357 shareBox->setYAxisShared(_shareY);
358 } else {
359 _shareX = shareBox->isXAxisShared();
360 _shareY = shareBox->isYAxisShared();
361 unshareAxis();
362 updateSharedAxis();
363 }
364
365 // Determine area of layout. Minimal spacing on SharedAxisBoxItems.
366 QSizeF layoutSize(parentItem()->width() - 1, parentItem()->height() - 1);
367 QPointF layoutTopLeft = parentItem()->rect().topLeft();
368 layoutTopLeft += QPointF(0, 1);
369
370 QRectF layoutRect(layoutTopLeft, layoutSize);
371
372 QMap<int, int> leftLabelBounds;
373 QMap<int, int> rightLabelBounds;
374 QMap<int, int> topLabelBounds;
375 QMap<int, int> bottomLabelBounds;
376
377 #if DEBUG_SHAREDAXIS
378 qDebug() << "Creating Shared Axis Layout in rect " << layoutRect
379 << "rowCount" << rowCount() << "columnCount" << columnCount();
380 #endif
381
382 foreach (const LayoutItem &item, _items) {
383 if (PlotItem *plotItem = qobject_cast<PlotItem*>(item.viewItem)) {
384 plotItem->calculateBorders(painter);
385 if (plotItem->leftMarginSize() > leftLabelBounds[item.column]) {
386 leftLabelBounds[item.column] = plotItem->leftMarginSize();
387 }
388 if (plotItem->rightMarginSize() > rightLabelBounds[item.column]) {
389 rightLabelBounds[item.column] = plotItem->rightMarginSize();
390 }
391 if (plotItem->topMarginSize() > topLabelBounds[item.row]) {
392 topLabelBounds[item.row] = plotItem->topMarginSize();
393 }
394 if (plotItem->bottomMarginSize() > bottomLabelBounds[item.row]) {
395 bottomLabelBounds[item.row] = plotItem->bottomMarginSize();
396 }
397 }
398 }
399
400 // Adjust to make sure there is sufficient room for the tiedzoom box.
401 if (topLabelBounds[0] < parentItem()->tiedZoomSize().height()) {
402 topLabelBounds[0] = parentItem()->tiedZoomSize().height();
403 }
404 if (rightLabelBounds[columnCount()] < parentItem()->tiedZoomSize().width()) {
405 rightLabelBounds[columnCount()] = parentItem()->tiedZoomSize().width();
406 }
407
408 #if DEBUG_SHAREDAXIS
409 qDebug() << "Calculated maximum bounds for labels.";
410 qDebug() << "Left bounds by column" << leftLabelBounds;
411 qDebug() << "Right bounds by column" << rightLabelBounds;
412 qDebug() << "Top bounds by row" << topLabelBounds;
413 qDebug() << "Bottom bounds by row" << bottomLabelBounds;
414 #endif
415
416 bool blockMode = false;
417 bool rowMode = false;
418 bool colMode = false;
419
420 #if DEBUG_SHAREDAXIS
421 qDebug() << "Calculated sharing modes" << "sharing X = " << _shareX << "sharing Y = " << _shareY;
422 #endif
423
424 if (_shareX && _shareY) {
425 #if DEBUG_SHAREDAXIS
426 qDebug() << "Sharing X & Y, one block, all projectionRect's inside frame";
427 #endif
428 blockMode = true;
429 } else if (_shareX) {
430 if (columnCount() == 1) {
431 #if DEBUG_SHAREDAXIS
432 qDebug() << "Sharing only X, one column, use block logic";
433 #endif
434 blockMode = true;
435 } else {
436 #if DEBUG_SHAREDAXIS
437 qDebug() << "Sharing only X, multiple columns, use block for each column, columns divide space equally - number of columns" << columnCount();
438 #endif
439 colMode = true;
440 }
441 } else if (_shareY) {
442 if (rowCount() == 1) {
443 #if DEBUG_SHAREDAXIS
444 qDebug() << "Sharing only Y, one row, use block logic";
445 #endif
446 blockMode = true;
447 } else {
448 #if DEBUG_SHAREDAXIS
449 qDebug() << "Sharing only Y, multiple rows, use block for each row, rows divide space equally - number of rows" << rowCount();
450 #endif
451 rowMode = true;
452 }
453 }
454
455 int totalProjWidth;
456 int totalProjHeight;
457
458 if (blockMode) {
459 totalProjWidth = (layoutRect.width() - leftLabelBounds[0]) - rightLabelBounds[columnCount() - 1];
460 totalProjHeight = (layoutRect.height() - topLabelBounds[0]) - bottomLabelBounds[rowCount() - 1];
461 } else if (rowMode) {
462 int totalHeight = 0;
463 for (int i = 0; i < rowCount(); i++) {
464 totalHeight += topLabelBounds[i] + bottomLabelBounds[i];
465 }
466 totalProjWidth = layoutRect.width() - leftLabelBounds[0] - rightLabelBounds[columnCount() - 1];
467 totalProjHeight = layoutRect.height() - totalHeight;
468
469 } else if (colMode) {
470 int totalWidth = 0;
471 for (int i = 0; i < columnCount(); i++) {
472 totalWidth += leftLabelBounds[i] + rightLabelBounds[i];
473 }
474 totalProjWidth = layoutRect.width() - totalWidth;
475 totalProjHeight = layoutRect.height() - topLabelBounds[0] - bottomLabelBounds[rowCount() - 1];
476 } else {
477 return;
478 }
479
480 int columnProjWidth = totalProjWidth / (columnCount());
481 int rowProjHeight = totalProjHeight / (rowCount());
482
483 QMap<int, QMap<int, int> > cellHeights;
484 QMap<int, QMap<int, int> > cellWidths;
485
486 foreach (const LayoutItem &item, _items) {
487 if (PlotItem *plotItem = qobject_cast<PlotItem*>(item.viewItem)) {
488 int width = columnProjWidth;
489 if (plotItem->leftLabelDetails()->isVisible()) {
490 width += leftLabelBounds[item.column];
491 }
492 if (plotItem->rightLabelDetails()->isVisible()) {
493 width += rightLabelBounds[item.column];
494 }
495 cellWidths[item.row][item.column] = width;
496
497 int height = rowProjHeight;
498 if (plotItem->topLabelDetails()->isVisible()) {
499 height += topLabelBounds[item.row];
500 }
501 if (plotItem->bottomLabelDetails()->isVisible()) {
502 height += bottomLabelBounds[item.row];
503 }
504 cellHeights[item.row][item.column] = height;
505 }
506 }
507
508 #if DEBUG_SHAREDAXIS
509 qDebug() << "Calculated Total projectRect values - width = " << totalProjWidth << " height = " << totalProjHeight;
510 qDebug() << "Column Projection Width" << columnProjWidth << "Row Projection Height" << rowProjHeight;
511 qDebug() << "Calculated cell widths (row x column)" << cellWidths;
512 qDebug() << "Calculated cell heights (row x column)" << cellHeights;
513 #endif
514
515 foreach (const LayoutItem &item, _items) {
516 int columnStart = 0;
517 for (int i = 0; i < item.column; i++) {
518 columnStart += cellWidths[item.row][i];
519 }
520
521 int rowStart = 0;
522 for (int i = 0; i < item.row; i++) {
523 rowStart += cellHeights[i][item.column];
524 }
525
526 QPointF itemTopLeft(columnStart, rowStart);
527 itemTopLeft += layoutTopLeft;
528 QSizeF itemSize(cellWidths[item.row][item.column], cellHeights[item.row][item.column]);
529 QRectF itemRect(itemTopLeft, itemSize);
530
531 if (PlotItem *plotItem = qobject_cast<PlotItem*>(item.viewItem)) {
532 if (plotItem->leftLabelDetails()->isVisible()) {
533 plotItem->setLeftPadding(leftLabelBounds[item.column] - plotItem->leftMarginSize());
534 }
535 if (plotItem->rightLabelDetails()->isVisible()) {
536 plotItem->setRightPadding(rightLabelBounds[item.column] - plotItem->rightMarginSize());
537 }
538 if (plotItem->topLabelDetails()->isVisible()) {
539 plotItem->setTopPadding(topLabelBounds[item.row] - plotItem->topMarginSize());
540 }
541 if (plotItem->bottomLabelDetails()->isVisible()) {
542 plotItem->setBottomPadding(bottomLabelBounds[item.row] - plotItem->bottomMarginSize());
543 }
544 }
545
546 item.viewItem->resetTransform();
547 item.viewItem->setPos(itemRect.topLeft());
548 item.viewItem->setViewRect(QRectF(QPoint(0,0), itemRect.size()));
549
550 if (PlotItem *plotItem = qobject_cast<PlotItem*>(item.viewItem)) {
551 emit plotItem->updatePlotRect();
552 }
553
554 #if DEBUG_SHAREDAXIS
555 qDebug() << "Shared Axis Plot item details:"
556 << "row:" << item.row
557 << "column:" << item.column
558 << "itemRect:" << itemRect
559 << endl;
560 #endif
561 }
562 }
563
564
updatePlotMargins()565 void ViewGridLayout::updatePlotMargins() {
566 _plotMarginWidth.clear();
567 _plotMarginHeight.clear();
568 foreach (const LayoutItem &item, _items) {
569 PlotItem *plotItem = qobject_cast<PlotItem*>(item.viewItem);
570
571 if (!plotItem)
572 continue;
573
574 {
575 qreal labelMargin = plotItem->labelMarginWidth();
576 qreal axisMargin = plotItem->axisMarginWidth();
577
578 if (_plotMarginWidth.contains(item.columnSpan)) {
579 labelMargin = qMax(labelMargin, _plotMarginWidth.value(item.columnSpan).labelMargin);
580 axisMargin = qMax(axisMargin, _plotMarginWidth.value(item.columnSpan).axisMargin);
581 }
582
583 PlotMargins marginsForColumnSpan;
584 marginsForColumnSpan.labelMargin = labelMargin;
585 marginsForColumnSpan.axisMargin = axisMargin;
586 _plotMarginWidth.insert(item.columnSpan, marginsForColumnSpan);
587 }
588
589 {
590 qreal labelMargin = plotItem->labelMarginHeight();
591 qreal axisMargin = plotItem->axisMarginHeight();
592
593 if (_plotMarginHeight.contains(item.rowSpan)) {
594 labelMargin = qMax(labelMargin, _plotMarginHeight.value(item.rowSpan).labelMargin);
595 axisMargin = qMax(axisMargin, _plotMarginHeight.value(item.rowSpan).axisMargin);
596 }
597
598 PlotMargins marginsForRowSpan;
599 marginsForRowSpan.labelMargin = labelMargin;
600 marginsForRowSpan.axisMargin = axisMargin;
601 _plotMarginHeight.insert(item.rowSpan, marginsForRowSpan);
602 }
603 }
604 }
605
606
unshareAxis()607 void ViewGridLayout::unshareAxis() {
608 foreach (const LayoutItem &item, _items) {
609 PlotItem *plotItem = qobject_cast<PlotItem*>(item.viewItem);
610
611 if (!plotItem)
612 continue;
613
614 plotItem->setLabelsVisible(true);
615 plotItem->update();
616 }
617 }
618
619
updateSharedAxis()620 void ViewGridLayout::updateSharedAxis() {
621 if (!_shareX && !_shareY) {
622 return;
623 }
624 foreach (const LayoutItem &item, _items) {
625 PlotItem *plotItem = qobject_cast<PlotItem*>(item.viewItem);
626
627 if (!plotItem)
628 continue;
629
630 //same horizontal range and same row/rowspan
631 //same vertical range and same col/colspan
632 if (_shareX) {
633 shareAxisWithPlotAbove(item);
634 shareAxisWithPlotBelow(item);
635 }
636 if (_shareY) {
637 shareAxisWithPlotToLeft(item);
638 shareAxisWithPlotToRight(item);
639 }
640 }
641 }
642
643
calculateSharing()644 void ViewGridLayout::calculateSharing() {
645 bool xMatch = true;
646 bool yMatch = true;
647
648 bool first = true;
649
650 qreal xStart = 0.0, xStop = 0.0;
651 qreal yStart = 0.0, yStop = 0.0;
652
653 foreach (const LayoutItem &item, _items) {
654 PlotItem *plotItem = qobject_cast<PlotItem*>(item.viewItem);
655
656 if (!plotItem)
657 continue;
658
659 if (first) {
660 xStart = plotItem->projectionRect().left();
661 xStop = plotItem->projectionRect().right();
662 yStart = plotItem->projectionRect().top();
663 yStop = plotItem->projectionRect().bottom();
664 first = false;
665 } else {
666 if (xMatch && (plotItem->projectionRect().left() != xStart || plotItem->projectionRect().right() != xStop)) {
667 xMatch = false;
668 }
669 if (yMatch && (plotItem->projectionRect().top() != yStart || plotItem->projectionRect().bottom() != yStop)) {
670 yMatch = false;
671 }
672 }
673 }
674 if (xMatch || yMatch) {
675 _shareX = xMatch;
676 _shareY = yMatch;
677 } else {
678 _shareX = true;
679 _shareY = true;
680 }
681 }
682
683 // static function
updateProjections(ViewItem * item,bool forceXShare,bool forceYShare)684 void ViewGridLayout::updateProjections(ViewItem *item, bool forceXShare, bool forceYShare) {
685 bool xMatch = true;
686 bool yMatch = true;
687
688 bool first = true;
689
690 qreal xStart = 0.0, xStop = 0.0;
691 qreal yStart = 0.0, yStop = 0.0;
692 qreal xMin = 0.0, xMax = 0.0, yMin = 0.0, yMax = 0.0;
693
694 QList<ViewItem*> viewItems;
695 QList<QGraphicsItem*> list = item->QGraphicsItem::childItems();
696 if (list.isEmpty())
697 return; //not added to undostack
698
699 foreach (QGraphicsItem *graphicsItem, list) {
700 ViewItem *viewItem = dynamic_cast<ViewItem*>(graphicsItem);
701 if (!viewItem || viewItem->hasStaticGeometry() || !viewItem->allowsLayout() || viewItem->parentItem() != item)
702 continue;
703
704 PlotItem *plotItem = qobject_cast<PlotItem*>(viewItem);
705
706 if (!plotItem)
707 continue;
708
709 if (first) {
710 xStart = plotItem->projectionRect().left();
711 xStop = plotItem->projectionRect().right();
712 yStart = plotItem->projectionRect().top();
713 yStop = plotItem->projectionRect().bottom();
714 xMin = xStart;
715 xMax = xStop;
716 yMin = yStart;
717 yMax = yStop;
718 first = false;
719 } else {
720 if (xMatch && (plotItem->projectionRect().left() != xStart || plotItem->projectionRect().right() != xStop)) {
721 xMatch = false;
722 }
723 if (yMatch && (plotItem->projectionRect().top() != yStart || plotItem->projectionRect().bottom() != yStop)) {
724 yMatch = false;
725 }
726 if (xMin > plotItem->projectionRect().left()) {
727 xMin = plotItem->projectionRect().left();
728 }
729 if (xMax < plotItem->projectionRect().right()) {
730 xMax = plotItem->projectionRect().right();
731 }
732 if (yMin > plotItem->projectionRect().top()) {
733 yMin = plotItem->projectionRect().top();
734 }
735 if (yMax < plotItem->projectionRect().bottom()) {
736 yMax = plotItem->projectionRect().bottom();
737 }
738 }
739 }
740
741 xMatch = xMatch || forceXShare;
742 yMatch = yMatch || forceYShare;
743
744 if (!xMatch && !yMatch) {
745 xMatch = true;
746 yMatch = true;
747 }
748
749 QRectF projectionRect(QPointF(xMin, yMin), QPointF(xMax, yMax));
750
751 foreach (QGraphicsItem *graphicsItem, list) {
752 ViewItem *viewItem = dynamic_cast<ViewItem*>(graphicsItem);
753 if (!viewItem || viewItem->hasStaticGeometry() || !viewItem->allowsLayout() || viewItem->parentItem() != item)
754 continue;
755
756 if (PlotItem *plotItem = qobject_cast<PlotItem*>(viewItem)) {
757 if (xMatch && yMatch) {
758 plotItem->zoomFixedExpression(projectionRect, true);
759 } else if (xMatch) {
760 plotItem->zoomXRange(projectionRect, true);
761 } else if (yMatch) {
762 plotItem->zoomYRange(projectionRect, true);
763 }
764 }
765 }
766 }
767
768
shareAxisWithPlotToLeft(LayoutItem item)769 void ViewGridLayout::shareAxisWithPlotToLeft(LayoutItem item) {
770 PlotItem *plotItem = qobject_cast<PlotItem*>(item.viewItem);
771
772 QPair<int, int> key = qMakePair(item.row, item.column - 1);
773 if (!_itemLayouts.contains(key)) {
774 plotItem->setLeftSuppressed(false);
775 setSpacing(QSizeF(spacing().width(), spacing().height()));
776 return;
777 }
778
779 LayoutItem left = _itemLayouts.value(key);
780 PlotItem *leftItem = qobject_cast<PlotItem*>(left.viewItem);
781 if (!leftItem) {
782 plotItem->setLeftSuppressed(false);
783 setSpacing(QSizeF(spacing().width(), spacing().height()));
784 return;
785 }
786
787 if (item.rowSpan == left.rowSpan && item.columnSpan == left.columnSpan) {
788 plotItem->setLeftSuppressed(true);
789 leftItem->setRightSuppressed(true);
790 setSpacing(QSizeF(0.0, spacing().height()));
791 }
792 }
793
794
shareAxisWithPlotToRight(LayoutItem item)795 void ViewGridLayout::shareAxisWithPlotToRight(LayoutItem item) {
796 PlotItem *plotItem = qobject_cast<PlotItem*>(item.viewItem);
797
798 QPair<int, int> key = qMakePair(item.row, item.column + 1);
799 if (!_itemLayouts.contains(key)) {
800 plotItem->setRightSuppressed(false);
801 setSpacing(QSizeF(spacing().width(), spacing().height()));
802 return;
803 }
804
805
806 LayoutItem right = _itemLayouts.value(key);
807 PlotItem *rightItem = qobject_cast<PlotItem*>(right.viewItem);
808 if (!rightItem) {
809 plotItem->setRightSuppressed(false);
810 setSpacing(QSizeF(spacing().width(), spacing().height()));
811 return;
812 }
813
814 if (item.rowSpan == right.rowSpan && item.columnSpan == right.columnSpan) {
815 plotItem->setRightSuppressed(true);
816 rightItem->setLeftSuppressed(true);
817 setSpacing(QSizeF(0.0, spacing().height()));
818 }
819 }
820
821
shareAxisWithPlotAbove(LayoutItem item)822 void ViewGridLayout::shareAxisWithPlotAbove(LayoutItem item) {
823 PlotItem *plotItem = qobject_cast<PlotItem*>(item.viewItem);
824
825 QPair<int, int> key = qMakePair(item.row - 1, item.column);
826 if (!_itemLayouts.contains(key)) {
827 plotItem->setTopSuppressed(false);
828 setSpacing(QSizeF(spacing().width(), spacing().height()));
829 return;
830 }
831
832 LayoutItem top = _itemLayouts.value(key);
833 PlotItem *topItem = qobject_cast<PlotItem*>(top.viewItem);
834 if (!topItem) {
835 plotItem->setTopSuppressed(false);
836 setSpacing(QSizeF(spacing().width(), spacing().height()));
837 return;
838 }
839
840 if (item.rowSpan == top.rowSpan && item.columnSpan == top.columnSpan) {
841 plotItem->setTopSuppressed(true);
842 topItem->setBottomSuppressed(true);
843 setSpacing(QSizeF(spacing().width(), 0.0));
844 }
845 }
846
847
shareAxisWithPlotBelow(LayoutItem item)848 void ViewGridLayout::shareAxisWithPlotBelow(LayoutItem item) {
849 PlotItem *plotItem = qobject_cast<PlotItem*>(item.viewItem);
850
851 QPair<int, int> key = qMakePair(item.row + 1, item.column);
852 if (!_itemLayouts.contains(key)) {
853 plotItem->setBottomSuppressed(false);
854 setSpacing(QSizeF(spacing().width(), spacing().height()));
855 return;
856 }
857
858 LayoutItem bottom = _itemLayouts.value(key);
859 PlotItem *bottomItem = qobject_cast<PlotItem*>(bottom.viewItem);
860 if (!bottomItem) {
861 plotItem->setBottomSuppressed(false);
862 setSpacing(QSizeF(spacing().width(), spacing().height()));
863 return;
864 }
865
866 if (item.rowSpan == bottom.rowSpan && item.columnSpan == bottom.columnSpan) {
867 plotItem->setBottomSuppressed(true);
868 bottomItem->setTopSuppressed(true);
869 setSpacing(QSizeF(spacing().width(), 0.0));
870 }
871 }
872
873 }
874
875 // vim: ts=2 sw=2 et
876