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 }