File indexing completed on 2024-05-12 16:02:11

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