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