File indexing completed on 2024-04-28 04:42:11

0001 /* This file is part of the KDE project
0002    Copyright (C) 1998, 1999 Reginald Stadlbauer <reggie@kde.org>
0003    Copyright (C) 2006 Peter Simonsson <peter.simonsson@gmail.com>
0004    Copyright (C) 2007 C. Boemann <cbo@boemann.dk>
0005    Copyright (C) 2007-2008 Jan Hambrecht <jaham@gmx.net>
0006    Copyright (C) 2007 Thomas Zander <zander@kde.org>
0007 
0008    This library is free software; you can redistribute it and/or
0009    modify it under the terms of the GNU Library General Public
0010    License as published by the Free Software Foundation; either
0011    version 2 of the License, or (at your option) any later version.
0012 
0013    This library is distributed in the hope that it will be useful,
0014    but WITHOUT ANY WARRANTY; without even the implied warranty of
0015    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0016    Library General Public License for more details.
0017 
0018    You should have received a copy of the GNU Library General Public License
0019    along with this library; see the file COPYING.LIB.  If not, write to
0020    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0021    Boston, MA 02110-1301, USA.
0022 */
0023 
0024 #include "KReportRuler_p.h"
0025 #include "KReportDesign_p.h"
0026 #include "KReportZoomHandler_p.h"
0027 
0028 #include <QPainter>
0029 #include <QMenu>
0030 #include <QMouseEvent>
0031 #include <QFontDatabase>
0032 
0033 // the distance in pixels of a mouse position considered outside the rule
0034 static const int OutsideRulerThreshold = 20;
0035 //
0036 static const int fullStepMarkerLength = 6;
0037 static const int halfStepMarkerLength = 6;
0038 static const int quarterStepMarkerLength = 3;
0039 static const int measurementTextAboveBelowMargin = 1;
0040 
0041 class RulerTabChooser : public QWidget
0042 {
0043 public:
0044     RulerTabChooser(QWidget *parent) : QWidget(parent), m_type(QTextOption::LeftTab), m_showTabs(false) {}
0045     ~RulerTabChooser() override {}
0046 
0047     inline QTextOption::TabType type() {return m_type;}
0048     void setShowTabs(bool showTabs) { if (m_showTabs == showTabs) return; m_showTabs = showTabs; update(); }
0049     void mousePressEvent(QMouseEvent *) override;
0050 
0051     void paintEvent(QPaintEvent *) override;
0052 
0053 private:
0054     QTextOption::TabType m_type;
0055     bool m_showTabs :1;
0056 };
0057 
0058 // ----
0059 
0060 class PaintingStrategy
0061 {
0062 public:
0063     /// constructor
0064     PaintingStrategy() {}
0065     /// destructor
0066     virtual ~PaintingStrategy() {}
0067 
0068     /**
0069      * Draw the background of the ruler.
0070      * @param ruler the ruler to draw on.
0071      * @param painter the painter we can paint with.
0072      */
0073     virtual QRectF drawBackground(const KReportRuler::Private *ruler, QPainter *painter) = 0;
0074 
0075     /**
0076      * Draw the indicators for text-tabs.
0077      * @param ruler the ruler to draw on.
0078      * @param painter the painter we can paint with.
0079      */
0080     virtual void drawTabs(const KReportRuler::Private *ruler, QPainter *painter) = 0;
0081 
0082     /**
0083      * Draw the indicators for the measurements which typically are drawn every [unit].
0084      * @param ruler the ruler to draw on.
0085      * @param painter the painter we can paint with.
0086      * @param rectangle
0087      */
0088     virtual void drawMeasurements(const KReportRuler::Private *ruler, QPainter *painter, const QRectF &rectangle) = 0;
0089 
0090     /**
0091      * Draw the indicators for the indents of a text paragraph
0092      * @param ruler the ruler to draw on.
0093      * @param painter the painter we can paint with.
0094      */
0095     virtual void drawIndents(const KReportRuler::Private *ruler, QPainter *painter) = 0;
0096 
0097     /**
0098      *returns the size suggestion for a ruler with this strategy.
0099      */
0100     virtual QSize sizeHint() = 0;
0101 };
0102 
0103 // ----
0104 
0105 class HorizontalPaintingStrategy : public PaintingStrategy
0106 {
0107 public:
0108     HorizontalPaintingStrategy() : lengthInPixel(1) {}
0109 
0110     QRectF drawBackground(const KReportRuler::Private *ruler, QPainter *painter) override;
0111     void drawTabs(const KReportRuler::Private *ruler, QPainter *painter) override;
0112     void drawMeasurements(const KReportRuler::Private *ruler, QPainter *painter, const QRectF &rectangle) override;
0113     void drawIndents(const KReportRuler::Private *ruler, QPainter *painter) override;
0114     QSize sizeHint() override;
0115 
0116 private:
0117     qreal lengthInPixel;
0118 };
0119 
0120 // ----
0121 
0122 class VerticalPaintingStrategy : public PaintingStrategy
0123 {
0124 public:
0125     VerticalPaintingStrategy() : lengthInPixel(1) {}
0126 
0127     QRectF drawBackground(const KReportRuler::Private *ruler, QPainter *painter) override;
0128     void drawTabs(const KReportRuler::Private *, QPainter *) override {}
0129     void drawMeasurements(const KReportRuler::Private *ruler, QPainter *painter, const QRectF &rectangle) override;
0130     void drawIndents(const KReportRuler::Private *, QPainter *) override {}
0131     QSize sizeHint() override;
0132 
0133 private:
0134     qreal lengthInPixel;
0135 };
0136 
0137 class HorizontalDistancesPaintingStrategy : public HorizontalPaintingStrategy
0138 {
0139 public:
0140     HorizontalDistancesPaintingStrategy() {}
0141 
0142     void drawMeasurements(const KReportRuler::Private *ruler, QPainter *painter, const QRectF &rectangle) override;
0143 
0144 private:
0145     void drawDistanceLine(const KReportRuler::Private *d, QPainter *painter, qreal start, qreal end);
0146 };
0147 
0148 // ----
0149 
0150 class KReportRuler::Private
0151 {
0152 public:
0153     Private(KReportRuler *parent, const KReportZoomHandler &zoomHandler, Qt::Orientation orientation);
0154     ~Private();
0155 
0156     void emitTabChanged();
0157 
0158     KReportUnit unit;
0159     const Qt::Orientation orientation;
0160     const KReportZoomHandler * const viewConverter;
0161 
0162     int offset;
0163     qreal rulerLength;
0164     qreal activeRangeStart;
0165     qreal activeRangeEnd;
0166     qreal activeOverrideRangeStart;
0167     qreal activeOverrideRangeEnd;
0168 
0169     int mouseCoordinate;
0170     int showMousePosition;
0171 
0172     bool showSelectionBorders;
0173     qreal firstSelectionBorder;
0174     qreal secondSelectionBorder;
0175 
0176     bool showIndents;
0177     qreal firstLineIndent;
0178     qreal paragraphIndent;
0179     qreal endIndent;
0180 
0181     bool showTabs;
0182     bool relativeTabs;
0183     bool tabMoved; // set to true on first move of a selected tab
0184     QList<KReportRuler::Tab> tabs;
0185     int originalIndex; //index of selected tab before we started dragging it.
0186     int currentIndex; //index of selected tab or selected HotSpot - only valid when selected indicates tab or hotspot
0187     KReportRuler::Tab deletedTab;
0188     qreal tabDistance;
0189 
0190     struct HotSpotData {
0191         qreal position;
0192         int id;
0193     };
0194     QList<HotSpotData> hotspots;
0195 
0196     bool rightToLeft;
0197     enum class Selection {
0198         None,
0199         Tab,
0200         FirstLineIndent,
0201         ParagraphIndent,
0202         EndIndent,
0203         HotSpot
0204     };
0205     Selection selected;
0206     int selectOffset;
0207 
0208     QList<QAction*> popupActions;
0209 
0210     RulerTabChooser *tabChooser;
0211 
0212     // Cached painting strategies
0213     PaintingStrategy * normalPaintingStrategy;
0214     PaintingStrategy * distancesPaintingStrategy;
0215 
0216     // Current painting strategy
0217     PaintingStrategy * paintingStrategy;
0218 
0219     KReportRuler *ruler;
0220 
0221     qreal numberStepForUnit() const;
0222     /// @return The rounding of value to the nearest multiple of stepValue
0223     qreal doSnapping(qreal value) const;
0224     Selection selectionAtPosition(const QPoint &pos, int *selectOffset = nullptr);
0225     int hotSpotIndex(const QPoint &pos);
0226     qreal effectiveActiveRangeStart() const;
0227     qreal effectiveActiveRangeEnd() const;
0228 
0229     friend class VerticalPaintingStrategy;
0230     friend class HorizontalPaintingStrategy;
0231 };
0232 
0233 // ----
0234 
0235 void RulerTabChooser::mousePressEvent(QMouseEvent *)
0236 {
0237     if (! m_showTabs) {
0238         return;
0239     }
0240 
0241     switch(m_type) {
0242     case QTextOption::LeftTab:
0243         m_type = QTextOption::RightTab;
0244         break;
0245     case QTextOption::RightTab:
0246         m_type = QTextOption::CenterTab;
0247         break;
0248     case QTextOption::CenterTab:
0249         m_type = QTextOption::DelimiterTab;
0250         break;
0251     case QTextOption::DelimiterTab:
0252         m_type = QTextOption::LeftTab;
0253         break;
0254     }
0255     update();
0256 }
0257 
0258 void RulerTabChooser::paintEvent(QPaintEvent *)
0259 {
0260     if (! m_showTabs) {
0261         return;
0262     }
0263 
0264     QPainter painter(this);
0265     QPolygonF polygon;
0266 
0267     painter.setPen(palette().color(QPalette::Text));
0268     painter.setBrush(palette().color(QPalette::Text));
0269     painter.setRenderHint( QPainter::Antialiasing );
0270 
0271     qreal x = qreal(width())/2.0;
0272     painter.translate(0,-height()/2+5);
0273 
0274     switch (m_type) {
0275     case QTextOption::LeftTab:
0276         polygon << QPointF(x+0.5, height() - 8.5)
0277             << QPointF(x+6.5, height() - 2.5)
0278             << QPointF(x+0.5, height() - 2.5);
0279         painter.drawPolygon(polygon);
0280         break;
0281     case QTextOption::RightTab:
0282         polygon << QPointF(x+0.5, height() - 8.5)
0283             << QPointF(x-5.5, height() - 2.5)
0284             << QPointF(x+0.5, height() - 2.5);
0285         painter.drawPolygon(polygon);
0286         break;
0287     case QTextOption::CenterTab:
0288         polygon << QPointF(x+0.5, height() - 8.5)
0289             << QPointF(x-5.5, height() - 2.5)
0290             << QPointF(x+6.5, height() - 2.5);
0291         painter.drawPolygon(polygon);
0292         break;
0293     case QTextOption::DelimiterTab:
0294         polygon << QPointF(x-5.5, height() - 2.5)
0295             << QPointF(x+6.5, height() - 2.5);
0296         painter.drawPolyline(polygon);
0297         polygon << QPointF(x+0.5, height() - 2.5)
0298             << QPointF(x+0.5, height() - 8.5);
0299         painter.drawPolyline(polygon);
0300         break;
0301     default:
0302         break;
0303     }
0304 }
0305 
0306 static int compareTabs(const KReportRuler::Tab &tab1, const KReportRuler::Tab &tab2)
0307 {
0308     return tab1.position < tab2.position;
0309 }
0310 
0311 QRectF HorizontalPaintingStrategy::drawBackground(const KReportRuler::Private *d, QPainter *painter)
0312 {
0313     lengthInPixel = d->viewConverter->documentToViewX(d->rulerLength);
0314     QRectF rectangle;
0315     rectangle.setX(qMax(0, d->offset));
0316     rectangle.setY(0);
0317     rectangle.setWidth(qMin(qreal(d->ruler->width() - 1.0 - rectangle.x()),
0318                             (d->offset >= 0) ? lengthInPixel : lengthInPixel + d->offset));
0319     rectangle.setHeight(d->ruler->height() - 1);
0320     QRectF activeRangeRectangle;
0321     activeRangeRectangle.setX(qMax(rectangle.x() + 1,
0322           d->viewConverter->documentToViewX(d->effectiveActiveRangeStart()) + d->offset));
0323     activeRangeRectangle.setY(rectangle.y() + 1);
0324     activeRangeRectangle.setRight(qMin(rectangle.right() - 1,
0325           d->viewConverter->documentToViewX(d->effectiveActiveRangeEnd()) + d->offset));
0326     activeRangeRectangle.setHeight(rectangle.height() - 2);
0327 
0328     painter->setPen(d->ruler->palette().color(QPalette::Mid));
0329     painter->drawRect(rectangle);
0330 
0331     if(d->effectiveActiveRangeStart() != d->effectiveActiveRangeEnd())
0332         painter->fillRect(activeRangeRectangle, d->ruler->palette().brush(QPalette::Base));
0333 
0334     if(d->showSelectionBorders) {
0335         // Draw first selection border
0336         if(d->firstSelectionBorder > 0) {
0337             qreal border = d->viewConverter->documentToViewX(d->firstSelectionBorder) + d->offset;
0338             painter->drawLine(QPointF(border, rectangle.y() + 1), QPointF(border, rectangle.bottom() - 1));
0339         }
0340         // Draw second selection border
0341         if(d->secondSelectionBorder > 0) {
0342             qreal border = d->viewConverter->documentToViewX(d->secondSelectionBorder) + d->offset;
0343             painter->drawLine(QPointF(border, rectangle.y() + 1), QPointF(border, rectangle.bottom() - 1));
0344         }
0345     }
0346 
0347     return rectangle;
0348 }
0349 
0350 void HorizontalPaintingStrategy::drawTabs(const KReportRuler::Private *d, QPainter *painter)
0351 {
0352     if (! d->showTabs)
0353         return;
0354     QPolygonF polygon;
0355 
0356     const QColor tabColor = d->ruler->palette().color(QPalette::Text);
0357     painter->setPen(tabColor);
0358     painter->setBrush(tabColor);
0359     painter->setRenderHint( QPainter::Antialiasing );
0360 
0361     qreal position = -10000;
0362 
0363     foreach (const KReportRuler::Tab & t, d->tabs) {
0364         qreal x;
0365         if (d->rightToLeft) {
0366             x = d->viewConverter->documentToViewX(d->effectiveActiveRangeEnd()
0367                     - (d->relativeTabs ? d->paragraphIndent : 0) - t.position) + d->offset;
0368         } else {
0369             x = d->viewConverter->documentToViewX(d->effectiveActiveRangeStart()
0370                     + (d->relativeTabs ? d->paragraphIndent : 0) + t.position) + d->offset;
0371         }
0372         position = qMax(position, t.position);
0373 
0374         polygon.clear();
0375         switch (t.type) {
0376         case QTextOption::LeftTab:
0377             polygon << QPointF(x+0.5, d->ruler->height() - 6.5)
0378                 << QPointF(x+6.5, d->ruler->height() - 0.5)
0379                 << QPointF(x+0.5, d->ruler->height() - 0.5);
0380             painter->drawPolygon(polygon);
0381             break;
0382         case QTextOption::RightTab:
0383             polygon << QPointF(x+0.5, d->ruler->height() - 6.5)
0384                 << QPointF(x-5.5, d->ruler->height() - 0.5)
0385                 << QPointF(x+0.5, d->ruler->height() - 0.5);
0386             painter->drawPolygon(polygon);
0387             break;
0388         case QTextOption::CenterTab:
0389             polygon << QPointF(x+0.5, d->ruler->height() - 6.5)
0390                 << QPointF(x-5.5, d->ruler->height() - 0.5)
0391                 << QPointF(x+6.5, d->ruler->height() - 0.5);
0392             painter->drawPolygon(polygon);
0393             break;
0394         case QTextOption::DelimiterTab:
0395             polygon << QPointF(x-5.5, d->ruler->height() - 0.5)
0396                 << QPointF(x+6.5, d->ruler->height() - 0.5);
0397             painter->drawPolyline(polygon);
0398             polygon << QPointF(x+0.5, d->ruler->height() - 0.5)
0399                 << QPointF(x+0.5, d->ruler->height() - 6.5);
0400             painter->drawPolyline(polygon);
0401             break;
0402         default:
0403             break;
0404         }
0405     }
0406 
0407     // and also draw the regular interval tab that are non editable
0408     if (d->tabDistance > 0.0) {
0409         // first possible position
0410         position = qMax(position, d->relativeTabs ? 0 : d->paragraphIndent);
0411         if (position < 0) {
0412             position = int(position / d->tabDistance) * d->tabDistance;
0413         } else {
0414             position = (int(position / d->tabDistance) + 1) * d->tabDistance;
0415         }
0416         while (position < d->effectiveActiveRangeEnd() - d->effectiveActiveRangeStart()
0417                 - d->endIndent) {
0418             qreal x;
0419             if (d->rightToLeft) {
0420                 x = d->viewConverter->documentToViewX(d->effectiveActiveRangeEnd()
0421                         - (d->relativeTabs ? d->paragraphIndent : 0) - position) + d->offset;
0422             } else {
0423                 x = d->viewConverter->documentToViewX(d->effectiveActiveRangeStart()
0424                         + (d->relativeTabs ? d->paragraphIndent : 0) + position) + d->offset;
0425             }
0426 
0427             polygon.clear();
0428             polygon << QPointF(x+0.5, d->ruler->height() - 3.5)
0429                 << QPointF(x+4.5, d->ruler->height() - 0.5)
0430                 << QPointF(x+0.5, d->ruler->height() - 0.5);
0431             painter->drawPolygon(polygon);
0432 
0433             position += d->tabDistance;
0434         }
0435     }
0436 }
0437 
0438 void HorizontalPaintingStrategy::drawMeasurements(const KReportRuler::Private *d, QPainter *painter, const QRectF &rectangle)
0439 {
0440     qreal numberStep = d->numberStepForUnit(); // number step in unit
0441 //    QRectF activeRangeRectangle;
0442     int numberStepPixel = qRound(d->viewConverter->documentToViewX(d->unit.fromUserValue(numberStep)));
0443 //    const bool adjustMillimeters = (d->unit.type() == KReportUnit::Millimeter);
0444 
0445     const QFont font = QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont);
0446     const QFontMetrics fontMetrics(font);
0447     painter->setFont(font);
0448 
0449     if (numberStepPixel == 0 || numberStep == 0)
0450         return;
0451 
0452     // Calc the longest text length
0453     int textLength = 0;
0454     for(int i = 0; i < lengthInPixel; i += numberStepPixel) {
0455         int number = qRound((i / numberStepPixel) * numberStep);
0456 
0457         textLength = qMax(textLength, fontMetrics.horizontalAdvance(QString::number(number)));
0458     }
0459     textLength += 4;  // Add some padding
0460 
0461     // Change number step so all digits fits
0462     while(textLength > numberStepPixel) {
0463         numberStepPixel += numberStepPixel;
0464         numberStep += numberStep;
0465     }
0466 
0467     int start=0;
0468     // Calc the first number step
0469     if(d->offset < 0)
0470         start = qAbs(d->offset);
0471 
0472     // make a little hack so rulers shows correctly inversed number aligned
0473     const qreal lengthInUnit = d->unit.toUserValue(d->rulerLength);
0474     const qreal hackyLength = lengthInUnit - fmod(lengthInUnit, numberStep);
0475     if(d->rightToLeft) {
0476         start -= int(d->viewConverter->documentToViewX(fmod(d->rulerLength,
0477                     d->unit.fromUserValue(numberStep))));
0478     }
0479 
0480     int stepCount = (start / numberStepPixel) + 1;
0481     int halfStepCount = (start / qRound(numberStepPixel * 0.5)) + 1;
0482     int quarterStepCount = (start / qRound(numberStepPixel * 0.25)) + 1;
0483 
0484     const QPen numberPen(d->ruler->palette().color(QPalette::Text));
0485     const QPen markerPen(d->ruler->palette().color(QPalette::Inactive, QPalette::Text));
0486     painter->setPen(markerPen);
0487 
0488     if(d->offset > 0)
0489         painter->translate(d->offset, 0);
0490 
0491     const int len = qRound(rectangle.width()) + start;
0492     int nextStep = qRound(d->viewConverter->documentToViewX(
0493         d->unit.fromUserValue(numberStep * stepCount)));
0494     int nextHalfStep = qRound(d->viewConverter->documentToViewX(d->unit.fromUserValue(
0495         numberStep * 0.5 * halfStepCount)));
0496     int nextQuarterStep = qRound(d->viewConverter->documentToViewX(d->unit.fromUserValue(
0497         numberStep * 0.25 * quarterStepCount)));
0498 
0499     for(int i = start; i < len; ++i) {
0500         const int pos = i - start;
0501 
0502         if(i == nextStep) {
0503             if(pos != 0)
0504                 painter->drawLine(QPointF(pos, rectangle.bottom()-1),
0505                                  QPointF(pos, rectangle.bottom() - fullStepMarkerLength));
0506 
0507             int number = qRound(stepCount * numberStep);
0508 
0509             QString numberText = QString::number(number);
0510             int x = pos;
0511             if (d->rightToLeft) { // this is done in a hacky way with the fine tuning done above
0512                 numberText = QString::number(hackyLength - stepCount * numberStep);
0513             }
0514             painter->setPen(numberPen);
0515             painter->drawText(QPointF(x-fontMetrics.horizontalAdvance(numberText)/2.0,
0516                                      rectangle.bottom() -fullStepMarkerLength -measurementTextAboveBelowMargin),
0517                              numberText);
0518             painter->setPen(markerPen);
0519 
0520             ++stepCount;
0521             nextStep = qRound(d->viewConverter->documentToViewX(
0522                 d->unit.fromUserValue(numberStep * stepCount)));
0523             ++halfStepCount;
0524             nextHalfStep = qRound(d->viewConverter->documentToViewX(d->unit.fromUserValue(
0525                 numberStep * 0.5 * halfStepCount)));
0526             ++quarterStepCount;
0527             nextQuarterStep = qRound(d->viewConverter->documentToViewX(d->unit.fromUserValue(
0528                 numberStep * 0.25 * quarterStepCount)));
0529         }
0530         else if(i == nextHalfStep) {
0531             if(pos != 0)
0532                 painter->drawLine(QPointF(pos, rectangle.bottom()-1),
0533                                  QPointF(pos, rectangle.bottom() - halfStepMarkerLength));
0534 
0535             ++halfStepCount;
0536             nextHalfStep = qRound(d->viewConverter->documentToViewX(d->unit.fromUserValue(
0537                 numberStep * 0.5 * halfStepCount)));
0538             ++quarterStepCount;
0539             nextQuarterStep = qRound(d->viewConverter->documentToViewX(d->unit.fromUserValue(
0540                 numberStep * 0.25 * quarterStepCount)));
0541         }
0542         else if(i == nextQuarterStep) {
0543             if(pos != 0)
0544                 painter->drawLine(QPointF(pos, rectangle.bottom()-1),
0545                                  QPointF(pos, rectangle.bottom() - quarterStepMarkerLength));
0546 
0547             ++quarterStepCount;
0548             nextQuarterStep = qRound(d->viewConverter->documentToViewX(d->unit.fromUserValue(
0549                 numberStep * 0.25 * quarterStepCount)));
0550         }
0551     }
0552 
0553     // Draw the mouse indicator
0554     const int mouseCoord = d->mouseCoordinate - start;
0555     if (d->selected == KReportRuler::Private::Selection::None
0556         || d->selected == KReportRuler::Private::Selection::HotSpot)
0557     {
0558         const qreal top = rectangle.y() + 1;
0559         const qreal bottom = rectangle.bottom() -1;
0560         if (d->selected == KReportRuler::Private::Selection::None && d->showMousePosition
0561             && mouseCoord > 0 && mouseCoord < rectangle.width())
0562         {
0563             painter->drawLine(QPointF(mouseCoord, top), QPointF(mouseCoord, bottom));
0564         }
0565         foreach (const KReportRuler::Private::HotSpotData & hp, d->hotspots) {
0566             const qreal x = d->viewConverter->documentToViewX(hp.position) + d->offset;
0567             painter->drawLine(QPointF(x, top), QPointF(x, bottom));
0568         }
0569     }
0570 }
0571 
0572 void HorizontalPaintingStrategy::drawIndents(const KReportRuler::Private *d, QPainter *painter)
0573 {
0574     QPolygonF polygon;
0575 
0576     painter->setBrush(d->ruler->palette().brush(QPalette::Base));
0577     painter->setRenderHint( QPainter::Antialiasing );
0578 
0579     qreal x;
0580     // Draw first line start indent
0581     if (d->rightToLeft)
0582         x = d->effectiveActiveRangeEnd() - d->firstLineIndent - d->paragraphIndent;
0583     else
0584         x = d->effectiveActiveRangeStart() + d->firstLineIndent + d->paragraphIndent;
0585     // convert and use the +0.5 to go to nearest integer so that the 0.5 added below ensures sharp lines
0586     x = int(d->viewConverter->documentToViewX(x) + d->offset + 0.5);
0587     polygon << QPointF(x+6.5, 0.5)
0588         << QPointF(x+0.5, 8.5)
0589         << QPointF(x-5.5, 0.5)
0590         << QPointF(x+5.5, 0.5);
0591     painter->drawPolygon(polygon);
0592 
0593     // draw the hanging indent.
0594     if (d->rightToLeft)
0595         x = d->effectiveActiveRangeStart() + d->endIndent;
0596     else
0597         x = d->effectiveActiveRangeStart() + d->paragraphIndent;
0598     // convert and use the +0.5 to go to nearest integer so that the 0.5 added below ensures sharp lines
0599     x = int(d->viewConverter->documentToViewX(x) + d->offset + 0.5);
0600     const int bottom = d->ruler->height();
0601     polygon.clear();
0602     polygon << QPointF(x+6.5, bottom - 0.5)
0603         << QPointF(x+0.5, bottom - 8.5)
0604         << QPointF(x-5.5, bottom - 0.5)
0605         << QPointF(x+5.5, bottom - 0.5);
0606     painter->drawPolygon(polygon);
0607 
0608     // Draw end-indent or paragraph indent if mode is rightToLeft
0609     qreal diff;
0610     if (d->rightToLeft)
0611         diff = d->viewConverter->documentToViewX(d->effectiveActiveRangeEnd()
0612                      - d->paragraphIndent) + d->offset - x;
0613     else
0614         diff = d->viewConverter->documentToViewX(d->effectiveActiveRangeEnd() - d->endIndent)
0615                 + d->offset - x;
0616     polygon.translate(diff, 0);
0617     painter->drawPolygon(polygon);
0618 }
0619 
0620 QSize HorizontalPaintingStrategy::sizeHint()
0621 {
0622     // assumes that digits for the number only use glyphs which do not go below the baseline
0623     const QFontMetrics fm(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont));
0624     const int digitsHeight = fm.ascent() + 1; // +1 for baseline
0625     const int minimum = digitsHeight + fullStepMarkerLength + 2*measurementTextAboveBelowMargin;
0626 
0627     return QSize(0, minimum);
0628 }
0629 
0630 QRectF VerticalPaintingStrategy::drawBackground(const KReportRuler::Private *d, QPainter *painter)
0631 {
0632     lengthInPixel = d->viewConverter->documentToViewY(d->rulerLength);
0633     QRectF rectangle;
0634     rectangle.setX(0);
0635     rectangle.setY(qMax(0, d->offset));
0636     rectangle.setWidth(d->ruler->width() - 1.0);
0637     rectangle.setHeight(qMin(qreal(d->ruler->height() - 1.0 - rectangle.y()),
0638                              (d->offset >= 0) ? lengthInPixel : lengthInPixel + d->offset));
0639 
0640     QRectF activeRangeRectangle;
0641     activeRangeRectangle.setX(rectangle.x() + 1);
0642     activeRangeRectangle.setY(qMax(rectangle.y() + 1,
0643         d->viewConverter->documentToViewY(d->effectiveActiveRangeStart()) + d->offset));
0644     activeRangeRectangle.setWidth(rectangle.width() - 2);
0645     activeRangeRectangle.setBottom(qMin(rectangle.bottom() - 1,
0646         d->viewConverter->documentToViewY(d->effectiveActiveRangeEnd()) + d->offset));
0647 
0648     painter->setPen(d->ruler->palette().color(QPalette::Mid));
0649     painter->drawRect(rectangle);
0650 
0651     if(d->effectiveActiveRangeStart() != d->effectiveActiveRangeEnd())
0652         painter->fillRect(activeRangeRectangle, d->ruler->palette().brush(QPalette::Base));
0653 
0654     if(d->showSelectionBorders) {
0655         // Draw first selection border
0656         if(d->firstSelectionBorder > 0) {
0657             qreal border = d->viewConverter->documentToViewY(d->firstSelectionBorder) + d->offset;
0658             painter->drawLine(QPointF(rectangle.x() + 1, border), QPointF(rectangle.right() - 1, border));
0659         }
0660         // Draw second selection border
0661         if(d->secondSelectionBorder > 0) {
0662             qreal border = d->viewConverter->documentToViewY(d->secondSelectionBorder) + d->offset;
0663             painter->drawLine(QPointF(rectangle.x() + 1, border), QPointF(rectangle.right() - 1, border));
0664         }
0665     }
0666 
0667     return rectangle;
0668 }
0669 
0670 void VerticalPaintingStrategy::drawMeasurements(const KReportRuler::Private *d, QPainter *painter, const QRectF &rectangle)
0671 {
0672     qreal numberStep = d->numberStepForUnit(); // number step in unit
0673     int numberStepPixel = qRound(d->viewConverter->documentToViewY( d->unit.fromUserValue(numberStep)));
0674     if (numberStepPixel <= 0)
0675         return;
0676 
0677     const QFont font = QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont);
0678     const QFontMetrics fontMetrics(font);
0679     painter->setFont(font);
0680 
0681     // Calc the longest text length
0682     int textLength = 0;
0683 
0684     for(int i = 0; i < lengthInPixel; i += numberStepPixel) {
0685         int number = qRound((i / numberStepPixel) * numberStep);
0686         textLength = qMax(textLength, fontMetrics.horizontalAdvance(QString::number(number)));
0687     }
0688     textLength += 4;  // Add some padding
0689 
0690     if (numberStepPixel == 0 || numberStep == 0)
0691         return;
0692     // Change number step so all digits will fit
0693     while(textLength > numberStepPixel) {
0694         numberStepPixel += numberStepPixel;
0695         numberStep += numberStep;
0696     }
0697 
0698     // Calc the first number step
0699     const int start = d->offset < 0 ? qAbs(d->offset) : 0;
0700 
0701     // make a little hack so rulers shows correctly inversed number aligned
0702     int stepCount = (start / numberStepPixel) + 1;
0703     int halfStepCount = (start / qRound(numberStepPixel * 0.5)) + 1;
0704     int quarterStepCount = (start / qRound(numberStepPixel * 0.25)) + 1;
0705 
0706     const QPen numberPen(d->ruler->palette().color(QPalette::Text));
0707     const QPen markerPen(d->ruler->palette().color(QPalette::Inactive, QPalette::Text));
0708     painter->setPen(markerPen);
0709 
0710     if(d->offset > 0)
0711         painter->translate(0, d->offset);
0712 
0713     const int len = qRound(rectangle.height()) + start;
0714     int nextStep = qRound(d->viewConverter->documentToViewY(
0715         d->unit.fromUserValue(numberStep * stepCount)));
0716     int nextHalfStep = qRound(d->viewConverter->documentToViewY(d->unit.fromUserValue(
0717         numberStep * 0.5 * halfStepCount)));
0718     int nextQuarterStep = qRound(d->viewConverter->documentToViewY(d->unit.fromUserValue(
0719         numberStep * 0.25 * quarterStepCount)));
0720 
0721     for(int i = start; i < len; ++i) {
0722         const int pos = i - start;
0723 
0724         if(i == nextStep) {
0725             painter->save();
0726             painter->translate(rectangle.right()-fullStepMarkerLength, pos);
0727             if(pos != 0)
0728                 painter->drawLine(QPointF(0, 0), QPointF(fullStepMarkerLength-1, 0));
0729 
0730             painter->rotate(-90);
0731             int number = qRound(stepCount * numberStep);
0732             QString numberText = QString::number(number);
0733             painter->setPen(numberPen);
0734             painter->drawText(QPointF(-fontMetrics.horizontalAdvance(numberText) / 2.0, -measurementTextAboveBelowMargin), numberText);
0735             painter->restore();
0736 
0737             ++stepCount;
0738             nextStep = qRound(d->viewConverter->documentToViewY(
0739                 d->unit.fromUserValue(numberStep * stepCount)));
0740             ++halfStepCount;
0741             nextHalfStep = qRound(d->viewConverter->documentToViewY(d->unit.fromUserValue(
0742                 numberStep * 0.5 * halfStepCount)));
0743             ++quarterStepCount;
0744             nextQuarterStep = qRound(d->viewConverter->documentToViewY(d->unit.fromUserValue(
0745                 numberStep * 0.25 * quarterStepCount)));
0746         } else if(i == nextHalfStep) {
0747             if(pos != 0)
0748                 painter->drawLine(QPointF(rectangle.right() - halfStepMarkerLength, pos),
0749                                  QPointF(rectangle.right() - 1, pos));
0750 
0751             ++halfStepCount;
0752             nextHalfStep = qRound(d->viewConverter->documentToViewY(d->unit.fromUserValue(
0753                 numberStep * 0.5 * halfStepCount)));
0754             ++quarterStepCount;
0755             nextQuarterStep = qRound(d->viewConverter->documentToViewY(d->unit.fromUserValue(
0756                 numberStep * 0.25 * quarterStepCount)));
0757         } else if(i == nextQuarterStep) {
0758             if(pos != 0)
0759                 painter->drawLine(QPointF(rectangle.right() - quarterStepMarkerLength, pos),
0760                                  QPointF(rectangle.right() - 1, pos));
0761 
0762             ++quarterStepCount;
0763             nextQuarterStep = qRound(d->viewConverter->documentToViewY(d->unit.fromUserValue(
0764                 numberStep * 0.25 * quarterStepCount)));
0765         }
0766     }
0767 
0768     // Draw the mouse indicator
0769     const int mouseCoord = d->mouseCoordinate - start;
0770     if (d->selected == KReportRuler::Private::Selection::None
0771         || d->selected == KReportRuler::Private::Selection::HotSpot)
0772     {
0773         const qreal left = rectangle.left() + 1;
0774         const qreal right = rectangle.right() -1;
0775         if (d->selected == KReportRuler::Private::Selection::None && d->showMousePosition
0776             && mouseCoord > 0 && mouseCoord < rectangle.height())
0777         {
0778             painter->drawLine(QPointF(left, mouseCoord), QPointF(right, mouseCoord));
0779         }
0780         foreach (const KReportRuler::Private::HotSpotData & hp, d->hotspots) {
0781             const qreal y = d->viewConverter->documentToViewY(hp.position) + d->offset;
0782             painter->drawLine(QPointF(left, y), QPointF(right, y));
0783         }
0784     }
0785 }
0786 
0787 QSize VerticalPaintingStrategy::sizeHint()
0788 {
0789     // assumes that digits for the number only use glyphs which do not go below the baseline
0790     const QFontMetrics fm(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont));
0791     const int digitsHeight = fm.ascent() + 1; // +1 for baseline
0792     const int minimum = digitsHeight + fullStepMarkerLength + 2*measurementTextAboveBelowMargin;
0793 
0794     return QSize(minimum, 0);
0795 }
0796 
0797 
0798 void HorizontalDistancesPaintingStrategy::drawDistanceLine(const KReportRuler::Private *d,
0799                                                            QPainter *painter, qreal start,
0800                                                            qreal end)
0801 {
0802 
0803     // Don't draw too short lines
0804     if (qMax(start, end) - qMin(start, end) < 1)
0805         return;
0806 
0807     painter->save();
0808     painter->translate(d->offset, d->ruler->height() / 2);
0809     painter->setPen(d->ruler->palette().color(QPalette::Text));
0810     painter->setBrush(d->ruler->palette().color(QPalette::Text));
0811 
0812     QLineF line(QPointF(d->viewConverter->documentToViewX(start), 0),
0813             QPointF(d->viewConverter->documentToViewX(end), 0));
0814     QPointF midPoint = line.pointAt(0.5);
0815 
0816     // Draw the label text
0817     const QFont font = QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont);
0818     const QFontMetrics fontMetrics(font);
0819     QString label = d->unit.toUserStringValue(
0820             d->viewConverter->viewToDocumentX(line.length())) + QLatin1String("") + d->unit.symbol();
0821     QPointF labelPosition = QPointF(midPoint.x() - fontMetrics.horizontalAdvance(label)/2,
0822             midPoint.y() + fontMetrics.ascent()/2);
0823     painter->setFont(font);
0824     painter->drawText(labelPosition, label);
0825 
0826     // Draw the arrow lines
0827     qreal arrowLength = (line.length() - fontMetrics.horizontalAdvance(label)) / 2 - 2;
0828     arrowLength = qMax(qreal(0.0), arrowLength);
0829     QLineF startArrow(line.p1(), line.pointAt(arrowLength / line.length()));
0830     QLineF endArrow(line.p2(), line.pointAt(1.0 - arrowLength / line.length()));
0831     painter->drawLine(startArrow);
0832     painter->drawLine(endArrow);
0833 
0834     // Draw the arrow heads
0835     QPolygonF arrowHead;
0836     arrowHead << line.p1() << QPointF(line.x1()+3, line.y1()-3)
0837         << QPointF(line.x1()+3, line.y1()+3);
0838     painter->drawPolygon(arrowHead);
0839     arrowHead.clear();
0840     arrowHead << line.p2() << QPointF(line.x2()-3, line.y2()-3)
0841         << QPointF(line.x2()-3, line.y2()+3);
0842     painter->drawPolygon(arrowHead);
0843 
0844     painter->restore();
0845 }
0846 
0847 void HorizontalDistancesPaintingStrategy::drawMeasurements(const KReportRuler::Private *d,
0848                                                            QPainter *painter, const QRectF&)
0849 {
0850     QList<qreal> points;
0851     points << 0.0;
0852     points << d->effectiveActiveRangeStart() + d->paragraphIndent + d->firstLineIndent;
0853     points << d->effectiveActiveRangeStart() + d->paragraphIndent;
0854     points << d->effectiveActiveRangeEnd() - d->endIndent;
0855     points << d->effectiveActiveRangeStart();
0856     points << d->effectiveActiveRangeEnd();
0857     points << d->rulerLength;
0858     std::sort(points.begin(), points.end());
0859     QListIterator<qreal> i(points);
0860     i.next();
0861     while (i.hasNext() && i.hasPrevious()) {
0862         drawDistanceLine(d, painter, i.peekPrevious(), i.peekNext());
0863         i.next();
0864     }
0865 }
0866 
0867 KReportRuler::Private::Private(KReportRuler *parent,
0868                                          const KReportZoomHandler &zoomHandler, Qt::Orientation o)
0869     : unit(DEFAULT_UNIT),
0870     orientation(o),
0871     viewConverter(&zoomHandler),
0872     offset(0),
0873     rulerLength(0),
0874     activeRangeStart(0),
0875     activeRangeEnd(0),
0876     activeOverrideRangeStart(0),
0877     activeOverrideRangeEnd(0),
0878     mouseCoordinate(-1),
0879     showMousePosition(0),
0880     showSelectionBorders(false),
0881     firstSelectionBorder(0),
0882     secondSelectionBorder(0),
0883     showIndents(false),
0884     firstLineIndent(0),
0885     paragraphIndent(0),
0886     endIndent(0),
0887     showTabs(false),
0888     relativeTabs(false),
0889     tabMoved(false),
0890     originalIndex(-1),
0891     currentIndex(0),
0892     tabDistance(0.0),
0893     rightToLeft(false),
0894     selected(Selection::None),
0895     selectOffset(0),
0896     tabChooser(nullptr),
0897     normalPaintingStrategy(o == Qt::Horizontal ?
0898               static_cast<PaintingStrategy*>(new HorizontalPaintingStrategy())
0899             : static_cast<PaintingStrategy*>(new VerticalPaintingStrategy())),
0900     distancesPaintingStrategy(new HorizontalDistancesPaintingStrategy()),
0901     paintingStrategy(normalPaintingStrategy),
0902     ruler(parent)
0903 {
0904 }
0905 
0906 KReportRuler::Private::~Private()
0907 {
0908     delete normalPaintingStrategy;
0909     delete distancesPaintingStrategy;
0910 }
0911 
0912 qreal KReportRuler::Private::numberStepForUnit() const
0913 {
0914     switch(unit.type()) {
0915         case KReportUnit::Type::Inch:
0916         case KReportUnit::Type::Centimeter:
0917         case KReportUnit::Type::Decimeter:
0918         case KReportUnit::Type::Millimeter:
0919             return 1.0;
0920         case KReportUnit::Type::Pica:
0921         case KReportUnit::Type::Cicero:
0922             return 10.0;
0923         case KReportUnit::Type::Point:
0924         default:
0925             return 100.0;
0926     }
0927 }
0928 
0929 qreal KReportRuler::Private::doSnapping(qreal value) const
0930 {
0931     qreal numberStep = unit.fromUserValue(numberStepForUnit()/4.0);
0932     return numberStep * qRound(value / numberStep);
0933 }
0934 
0935 KReportRuler::Private::Selection KReportRuler::Private::selectionAtPosition(const QPoint & pos, int *selectOffset )
0936 {
0937     const int height = ruler->height();
0938     if (rightToLeft) {
0939         int x = int(viewConverter->documentToViewX(effectiveActiveRangeEnd() - firstLineIndent - paragraphIndent) + offset);
0940         if (pos.x() >= x - 8 && pos.x() <= x +8 && pos.y() < height / 2) {
0941             if (selectOffset)
0942                 *selectOffset = x - pos.x();
0943             return KReportRuler::Private::Selection::FirstLineIndent;
0944         }
0945 
0946         x = int(viewConverter->documentToViewX(effectiveActiveRangeEnd() - paragraphIndent) + offset);
0947         if (pos.x() >= x - 8 && pos.x() <= x +8 && pos.y() > height / 2) {
0948             if (selectOffset)
0949                 *selectOffset = x - pos.x();
0950             return KReportRuler::Private::Selection::ParagraphIndent;
0951         }
0952 
0953         x = int(viewConverter->documentToViewX(effectiveActiveRangeStart() + endIndent) + offset);
0954         if (pos.x() >= x - 8 && pos.x() <= x + 8) {
0955             if (selectOffset)
0956                 *selectOffset = x - pos.x();
0957             return KReportRuler::Private::Selection::EndIndent;
0958         }
0959     }
0960     else {
0961         int x = int(viewConverter->documentToViewX(effectiveActiveRangeStart() + firstLineIndent + paragraphIndent) + offset);
0962         if (pos.x() >= x -8 && pos.x() <= x + 8 && pos.y() < height / 2) {
0963             if (selectOffset)
0964                 *selectOffset = x - pos.x();
0965             return KReportRuler::Private::Selection::FirstLineIndent;
0966         }
0967 
0968         x = int(viewConverter->documentToViewX(effectiveActiveRangeStart() + paragraphIndent) + offset);
0969         if (pos.x() >= x - 8 && pos.x() <= x + 8 && pos.y() > height/2) {
0970             if (selectOffset)
0971                 *selectOffset = x - pos.x();
0972             return KReportRuler::Private::Selection::ParagraphIndent;
0973         }
0974 
0975         x = int(viewConverter->documentToViewX(effectiveActiveRangeEnd() - endIndent) + offset);
0976         if (pos.x() >= x - 8 && pos.x() <= x + 8) {
0977             if (selectOffset)
0978                 *selectOffset = x - pos.x();
0979             return KReportRuler::Private::Selection::EndIndent;
0980         }
0981     }
0982 
0983     return KReportRuler::Private::Selection::None;
0984 }
0985 
0986 int KReportRuler::Private::hotSpotIndex(const QPoint & pos)
0987 {
0988     for(int counter = 0; counter < hotspots.count(); counter++) {
0989         bool hit;
0990         if (orientation == Qt::Horizontal)
0991             hit = qAbs(viewConverter->documentToViewX(hotspots[counter].position) - pos.x() + offset) < 3;
0992         else
0993             hit = qAbs(viewConverter->documentToViewY(hotspots[counter].position) - pos.y() + offset) < 3;
0994 
0995         if (hit)
0996             return counter;
0997     }
0998     return -1;
0999 }
1000 
1001 qreal KReportRuler::Private::effectiveActiveRangeStart() const
1002 {
1003     if (activeOverrideRangeStart != activeOverrideRangeEnd) {
1004         return activeOverrideRangeStart;
1005     } else {
1006         return activeRangeStart;
1007     }
1008 }
1009 
1010 qreal KReportRuler::Private::effectiveActiveRangeEnd() const
1011 {
1012     if (activeOverrideRangeStart != activeOverrideRangeEnd) {
1013         return activeOverrideRangeEnd;
1014     } else {
1015         return activeRangeEnd;
1016     }
1017 }
1018 
1019 void KReportRuler::Private::emitTabChanged()
1020 {
1021     KReportRuler::Tab tab;
1022     if (currentIndex >= 0)
1023         tab = tabs[currentIndex];
1024     emit ruler->tabChanged(originalIndex, currentIndex >= 0 ? &tab : nullptr);
1025 }
1026 
1027 
1028 KReportRuler::KReportRuler(QWidget* parent, Qt::Orientation orientation,
1029                            const KReportZoomHandler &zoomHandler)
1030   : QWidget(parent)
1031   , d(new KReportRuler::Private(this, zoomHandler, orientation))
1032 {
1033     setMouseTracking( true );
1034 }
1035 
1036 KReportRuler::~KReportRuler()
1037 {
1038     delete d;
1039 }
1040 
1041 KReportUnit KReportRuler::unit() const
1042 {
1043     return d->unit;
1044 }
1045 
1046 void KReportRuler::setUnit(const KReportUnit &unit)
1047 {
1048     d->unit = unit;
1049     update();
1050 }
1051 
1052 qreal KReportRuler::rulerLength() const
1053 {
1054     return d->rulerLength;
1055 }
1056 
1057 Qt::Orientation KReportRuler::orientation() const
1058 {
1059     return d->orientation;
1060 }
1061 
1062 void KReportRuler::setOffset(int offset)
1063 {
1064     d->offset = offset;
1065     update();
1066 }
1067 
1068 void KReportRuler::setRulerLength(qreal length)
1069 {
1070     d->rulerLength = length;
1071     update();
1072 }
1073 
1074 void KReportRuler::paintEvent(QPaintEvent* event)
1075 {
1076     QPainter painter(this);
1077     painter.setClipRegion(event->region());
1078     painter.save();
1079     QRectF rectangle = d->paintingStrategy->drawBackground(d, &painter);
1080     painter.restore();
1081     painter.save();
1082     d->paintingStrategy->drawMeasurements(d, &painter, rectangle);
1083     painter.restore();
1084     if (d->showIndents) {
1085         painter.save();
1086         d->paintingStrategy->drawIndents(d, &painter);
1087         painter.restore();
1088     }
1089     d->paintingStrategy->drawTabs(d, &painter);
1090 }
1091 
1092 QSize KReportRuler::minimumSizeHint() const
1093 {
1094     return d->paintingStrategy->sizeHint();
1095 }
1096 
1097 QSize KReportRuler::sizeHint() const
1098 {
1099     return d->paintingStrategy->sizeHint();
1100 }
1101 
1102 void KReportRuler::setActiveRange(qreal start, qreal end)
1103 {
1104     d->activeRangeStart = start;
1105     d->activeRangeEnd = end;
1106     update();
1107 }
1108 
1109 void KReportRuler::setOverrideActiveRange(qreal start, qreal end)
1110 {
1111     d->activeOverrideRangeStart = start;
1112     d->activeOverrideRangeEnd = end;
1113     update();
1114 }
1115 
1116 void KReportRuler::updateMouseCoordinate(int coordinate)
1117 {
1118     if(d->mouseCoordinate == coordinate)
1119         return;
1120     d->mouseCoordinate = coordinate;
1121     update();
1122 }
1123 
1124 void KReportRuler::setShowMousePosition(bool show)
1125 {
1126     d->showMousePosition = show;
1127     update();
1128 }
1129 
1130 void KReportRuler::setRightToLeft(bool isRightToLeft)
1131 {
1132     d->rightToLeft = isRightToLeft;
1133     update();
1134 }
1135 
1136 void KReportRuler::setShowIndents(bool show)
1137 {
1138     d->showIndents = show;
1139     update();
1140 }
1141 
1142 void KReportRuler::setFirstLineIndent(qreal indent)
1143 {
1144     d->firstLineIndent = indent;
1145     if (d->showIndents) {
1146         update();
1147     }
1148 }
1149 
1150 void KReportRuler::setParagraphIndent(qreal indent)
1151 {
1152     d->paragraphIndent = indent;
1153     if (d->showIndents) {
1154         update();
1155     }
1156 }
1157 
1158 void KReportRuler::setEndIndent(qreal indent)
1159 {
1160     d->endIndent = indent;
1161     if (d->showIndents) {
1162         update();
1163     }
1164 }
1165 
1166 qreal KReportRuler::firstLineIndent() const
1167 {
1168     return d->firstLineIndent;
1169 }
1170 
1171 qreal KReportRuler::paragraphIndent() const
1172 {
1173     return d->paragraphIndent;
1174 }
1175 
1176 qreal KReportRuler::endIndent() const
1177 {
1178     return d->endIndent;
1179 }
1180 
1181 QWidget *KReportRuler::tabChooser()
1182 {
1183     if ((d->tabChooser == nullptr) && (d->orientation == Qt::Horizontal)) {
1184         d->tabChooser = new RulerTabChooser(parentWidget());
1185         d->tabChooser->setShowTabs(d->showTabs);
1186     }
1187 
1188     return d->tabChooser;
1189 }
1190 
1191 void KReportRuler::setShowSelectionBorders(bool show)
1192 {
1193     d->showSelectionBorders = show;
1194     update();
1195 }
1196 
1197 void KReportRuler::updateSelectionBorders(qreal first, qreal second)
1198 {
1199     d->firstSelectionBorder = first;
1200     d->secondSelectionBorder = second;
1201 
1202     if(d->showSelectionBorders)
1203         update();
1204 }
1205 
1206 void KReportRuler::setShowTabs(bool show)
1207 {
1208     if (d->showTabs == show) {
1209         return;
1210     }
1211 
1212     d->showTabs = show;
1213     if (d->tabChooser) {
1214         d->tabChooser->setShowTabs(show);
1215     }
1216     update();
1217 }
1218 
1219 void KReportRuler::setRelativeTabs(bool relative)
1220 {
1221     d->relativeTabs = relative;
1222     if (d->showTabs) {
1223         update();
1224     }
1225 }
1226 
1227 void KReportRuler::updateTabs(const QList<KReportRuler::Tab> &tabs, qreal tabDistance)
1228 {
1229     d->tabs = tabs;
1230     d->tabDistance = tabDistance;
1231     if (d->showTabs) {
1232         update();
1233     }
1234 }
1235 
1236 QList<KReportRuler::Tab> KReportRuler::tabs() const
1237 {
1238     QList<Tab> answer = d->tabs;
1239     std::sort(answer.begin(), answer.end(), compareTabs);
1240 
1241     return answer;
1242 }
1243 
1244 void KReportRuler::setPopupActionList(const QList<QAction*> &popupActionList)
1245 {
1246     d->popupActions = popupActionList;
1247 }
1248 
1249 QList<QAction*> KReportRuler::popupActionList() const
1250 {
1251     return d->popupActions;
1252 }
1253 
1254 void KReportRuler::mousePressEvent ( QMouseEvent* ev )
1255 {
1256     d->tabMoved = false;
1257     d->selected = KReportRuler::Private::Selection::None;
1258     if (ev->button() == Qt::RightButton && !d->popupActions.isEmpty())
1259         QMenu::exec(d->popupActions, ev->globalPos());
1260     if (ev->button() != Qt::LeftButton) {
1261         ev->ignore();
1262         return;
1263     }
1264 
1265     QPoint pos = ev->pos();
1266 
1267     if (d->showTabs) {
1268         int i = 0;
1269         int x;
1270         foreach (const Tab & t, d->tabs) {
1271             if (d->rightToLeft) {
1272                 x = d->viewConverter->documentToViewX(d->effectiveActiveRangeEnd()
1273                         - (d->relativeTabs ? d->paragraphIndent : 0) - t.position) + d->offset;
1274             } else {
1275                 x = d->viewConverter->documentToViewX(d->effectiveActiveRangeStart()
1276                         + (d->relativeTabs ? d->paragraphIndent : 0) + t.position) + d->offset;
1277             }
1278             if (pos.x() >= x-6 && pos.x() <= x+6) {
1279                 d->selected = KReportRuler::Private::Selection::Tab;
1280                 d->selectOffset = x - pos.x();
1281                 d->currentIndex = i;
1282                 break;
1283             }
1284             i++;
1285         }
1286         d->originalIndex = d->currentIndex;
1287     }
1288 
1289     if (d->selected == KReportRuler::Private::Selection::None)
1290         d->selected = d->selectionAtPosition(ev->pos(), &d->selectOffset);
1291     if (d->selected == KReportRuler::Private::Selection::None) {
1292         int hotSpotIndex = d->hotSpotIndex(ev->pos());
1293         if (hotSpotIndex >= 0) {
1294             d->selected = KReportRuler::Private::Selection::HotSpot;
1295             update();
1296         }
1297     }
1298 
1299     if (d->showTabs && d->selected == KReportRuler::Private::Selection::None) {
1300         // still haven't found something so let assume the user wants to add a tab
1301         qreal tabpos;
1302         if (d->rightToLeft) {
1303             tabpos = d->viewConverter->viewToDocumentX(pos.x() - d->offset)
1304                     + d->effectiveActiveRangeEnd() + (d->relativeTabs ? d->paragraphIndent : 0);
1305         } else {
1306             tabpos = d->viewConverter->viewToDocumentX(pos.x() - d->offset)
1307                     - d->effectiveActiveRangeStart() - (d->relativeTabs ? d->paragraphIndent : 0);
1308         }
1309         Tab t(tabpos, d->tabChooser ?  d->tabChooser->type() :
1310                          d->rightToLeft ? QTextOption::RightTab :
1311                                           QTextOption::LeftTab);
1312         d->tabs.append(t);
1313         d->selectOffset = 0;
1314         d->selected = KReportRuler::Private::Selection::Tab;
1315         d->currentIndex = d->tabs.count() - 1;
1316         d->originalIndex = -1; // new!
1317         update();
1318     }
1319     if (d->orientation == Qt::Horizontal && (ev->modifiers() & Qt::ShiftModifier) &&
1320             (d->selected == KReportRuler::Private::Selection::FirstLineIndent ||
1321              d->selected == KReportRuler::Private::Selection::ParagraphIndent ||
1322              d->selected == KReportRuler::Private::Selection::Tab ||
1323              d->selected == KReportRuler::Private::Selection::EndIndent))
1324         d->paintingStrategy = d->distancesPaintingStrategy;
1325 
1326     if (d->selected != KReportRuler::Private::Selection::None)
1327         emit aboutToChange();
1328 }
1329 
1330 void KReportRuler::mouseReleaseEvent ( QMouseEvent* ev )
1331 {
1332     ev->accept();
1333     if (d->selected == KReportRuler::Private::Selection::Tab) {
1334         if (d->originalIndex >= 0 && !d->tabMoved) {
1335             int type = d->tabs[d->currentIndex].type;
1336             type++;
1337             if (type > 3)
1338                 type = 0;
1339             d->tabs[d->currentIndex].type = static_cast<QTextOption::TabType> (type);
1340             update();
1341         }
1342         d->emitTabChanged();
1343     }
1344     else if( d->selected != KReportRuler::Private::Selection::None)
1345         emit indentsChanged(true);
1346     else
1347         ev->ignore();
1348 
1349     d->paintingStrategy = d->normalPaintingStrategy;
1350     d->selected = KReportRuler::Private::Selection::None;
1351 }
1352 
1353 void KReportRuler::mouseMoveEvent ( QMouseEvent* ev )
1354 {
1355     QPoint pos = ev->pos();
1356 
1357     qreal activeLength = d->effectiveActiveRangeEnd() - d->effectiveActiveRangeStart();
1358 
1359     switch (d->selected) {
1360     case KReportRuler::Private::Selection::FirstLineIndent:
1361         if (d->rightToLeft)
1362             d->firstLineIndent = d->effectiveActiveRangeEnd() - d->paragraphIndent -
1363                 d->viewConverter->viewToDocumentX(pos.x() + d->selectOffset - d->offset);
1364         else
1365             d->firstLineIndent = d->viewConverter->viewToDocumentX(pos.x() + d->selectOffset
1366                 - d->offset) - d->effectiveActiveRangeStart() - d->paragraphIndent;
1367         if( ! (ev->modifiers() & Qt::ShiftModifier)) {
1368             d->firstLineIndent = d->doSnapping(d->firstLineIndent);
1369             d->paintingStrategy = d->normalPaintingStrategy;
1370         } else {
1371             if (d->orientation == Qt::Horizontal)
1372                 d->paintingStrategy = d->distancesPaintingStrategy;
1373         }
1374 
1375         emit indentsChanged(false);
1376         break;
1377     case KReportRuler::Private::Selection::ParagraphIndent:
1378         if (d->rightToLeft)
1379             d->paragraphIndent = d->effectiveActiveRangeEnd() -
1380                 d->viewConverter->viewToDocumentX(pos.x() + d->selectOffset - d->offset);
1381         else
1382             d->paragraphIndent = d->viewConverter->viewToDocumentX(pos.x() + d->selectOffset
1383                 - d->offset) - d->effectiveActiveRangeStart();
1384         if( ! (ev->modifiers() & Qt::ShiftModifier)) {
1385             d->paragraphIndent = d->doSnapping(d->paragraphIndent);
1386             d->paintingStrategy = d->normalPaintingStrategy;
1387         } else {
1388             if (d->orientation == Qt::Horizontal)
1389                 d->paintingStrategy = d->distancesPaintingStrategy;
1390         }
1391 
1392         if (d->paragraphIndent + d->endIndent > activeLength)
1393             d->paragraphIndent = activeLength - d->endIndent;
1394         emit indentsChanged(false);
1395         break;
1396     case KReportRuler::Private::Selection::EndIndent:
1397         if (d->rightToLeft)
1398             d->endIndent = d->viewConverter->viewToDocumentX(pos.x()
1399                  + d->selectOffset - d->offset) - d->effectiveActiveRangeStart();
1400         else
1401             d->endIndent = d->effectiveActiveRangeEnd() - d->viewConverter->viewToDocumentX(pos.x()
1402                  + d->selectOffset - d->offset);
1403         if (!(ev->modifiers() & Qt::ShiftModifier)) {
1404             d->endIndent = d->doSnapping(d->endIndent);
1405             d->paintingStrategy = d->normalPaintingStrategy;
1406         } else {
1407             if (d->orientation == Qt::Horizontal)
1408                 d->paintingStrategy = d->distancesPaintingStrategy;
1409         }
1410 
1411         if (d->paragraphIndent + d->endIndent > activeLength)
1412             d->endIndent = activeLength - d->paragraphIndent;
1413         emit indentsChanged(false);
1414         break;
1415     case KReportRuler::Private::Selection::Tab:
1416         d->tabMoved = true;
1417         if (d->currentIndex < 0) { // tab is deleted.
1418             if (ev->pos().y() < height()) { // reinstante it.
1419                 d->currentIndex = d->tabs.count();
1420                 d->tabs.append(d->deletedTab);
1421             } else {
1422                 break;
1423             }
1424         }
1425         if (d->rightToLeft)
1426             d->tabs[d->currentIndex].position = d->effectiveActiveRangeEnd() -
1427                 d->viewConverter->viewToDocumentX(pos.x() + d->selectOffset - d->offset);
1428         else
1429             d->tabs[d->currentIndex].position = d->viewConverter->viewToDocumentX(pos.x() + d->selectOffset
1430                 - d->offset) - d->effectiveActiveRangeStart();
1431         if (!(ev->modifiers() & Qt::ShiftModifier))
1432             d->tabs[d->currentIndex].position = d->doSnapping(d->tabs[d->currentIndex].position);
1433         if (d->tabs[d->currentIndex].position < 0)
1434             d->tabs[d->currentIndex].position = 0;
1435         if (d->tabs[d->currentIndex].position > activeLength)
1436             d->tabs[d->currentIndex].position = activeLength;
1437 
1438         if (ev->pos().y() > height() + OutsideRulerThreshold ) { // moved out of the ruler, delete it.
1439             d->deletedTab = d->tabs.takeAt(d->currentIndex);
1440             d->currentIndex = -1;
1441             // was that a temporary added tab?
1442             if ( d->originalIndex == -1 )
1443                 emit guideLineCreated(d->orientation,
1444                         d->orientation == Qt::Horizontal
1445                         ? d->viewConverter->viewToDocumentY(ev->pos().y())
1446                         : d->viewConverter->viewToDocumentX(ev->pos().x()));
1447         }
1448 
1449         d->emitTabChanged();
1450         break;
1451     case KReportRuler::Private::Selection::HotSpot:
1452         qreal newPos;
1453         if (d->orientation == Qt::Horizontal)
1454             newPos= d->viewConverter->viewToDocumentX(pos.x() - d->offset);
1455         else
1456             newPos= d->viewConverter->viewToDocumentY(pos.y() - d->offset);
1457         d->hotspots[d->currentIndex].position = newPos;
1458         emit hotSpotChanged(d->hotspots[d->currentIndex].id, newPos);
1459         break;
1460     case KReportRuler::Private::Selection::None:
1461         d->mouseCoordinate = (d->orientation == Qt::Horizontal ?  pos.x() : pos.y()) - d->offset;
1462         int hotSpotIndex = d->hotSpotIndex(pos);
1463         if (hotSpotIndex >= 0) {
1464             setCursor(QCursor( d->orientation == Qt::Horizontal ? Qt::SplitHCursor : Qt::SplitVCursor ));
1465             break;
1466         }
1467         unsetCursor();
1468 
1469         KReportRuler::Private::Selection selection = d->selectionAtPosition(pos);
1470         QString text;
1471         switch(selection) {
1472         case KReportRuler::Private::Selection::FirstLineIndent: text = tr("First line indent"); break;
1473         case KReportRuler::Private::Selection::ParagraphIndent: text = tr("Left indent"); break;
1474         case KReportRuler::Private::Selection::EndIndent: text = tr("Right indent"); break;
1475         case KReportRuler::Private::Selection::None:
1476             if (ev->buttons() & Qt::LeftButton) {
1477                 if (d->orientation == Qt::Horizontal && ev->pos().y() > height() + OutsideRulerThreshold)
1478                     emit guideLineCreated(d->orientation, d->viewConverter->viewToDocumentY(ev->pos().y()));
1479                 else if (d->orientation == Qt::Vertical && ev->pos().x() > width() + OutsideRulerThreshold)
1480                     emit guideLineCreated(d->orientation, d->viewConverter->viewToDocumentX(ev->pos().x()));
1481             }
1482             break;
1483         default:
1484             break;
1485         }
1486         setToolTip(text);
1487     }
1488     update();
1489 }
1490 
1491 void KReportRuler::clearHotSpots()
1492 {
1493     if (d->hotspots.isEmpty())
1494         return;
1495     d->hotspots.clear();
1496     update();
1497 }
1498 
1499 void KReportRuler::setHotSpot(qreal position, int id)
1500 {
1501     int hotspotCount = d->hotspots.count();
1502     for (int i = 0; i < hotspotCount; ++i) {
1503         KReportRuler::Private::HotSpotData & hs = d->hotspots[i];
1504         if (hs.id == id) {
1505             hs.position = position;
1506             update();
1507             return;
1508         }
1509     }
1510     // not there yet, then insert it.
1511     KReportRuler::Private::HotSpotData hs;
1512     hs.position = position;
1513     hs.id = id;
1514     d->hotspots.append(hs);
1515 }
1516 
1517 bool KReportRuler::removeHotSpot(int id)
1518 {
1519     QList<KReportRuler::Private::HotSpotData>::Iterator iter = d->hotspots.begin();
1520     while(iter != d->hotspots.end()) {
1521         if (iter->id == id) {
1522             d->hotspots.erase(iter);
1523             update();
1524             return true;
1525         }
1526     }
1527     return false;
1528 }