1 /* This file is part of the KDE project
2 Copyright (C) 1998, 1999 Reginald Stadlbauer <reggie@kde.org>
3 Copyright (C) 2006 Peter Simonsson <peter.simonsson@gmail.com>
4 Copyright (C) 2007 C. Boemann <cbo@boemann.dk>
5 Copyright (C) 2007-2008 Jan Hambrecht <jaham@gmx.net>
6 Copyright (C) 2007 Thomas Zander <zander@kde.org>
7
8 This library is free software; you can redistribute it and/or
9 modify it under the terms of the GNU Library General Public
10 License as published by the Free Software Foundation; either
11 version 2 of the License, or (at your option) any later version.
12
13 This library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Library General Public License for more details.
17
18 You should have received a copy of the GNU Library General Public License
19 along with this library; see the file COPYING.LIB. If not, write to
20 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 Boston, MA 02110-1301, USA.
22 */
23
24 #include "KReportRuler_p.h"
25 #include "KReportDesign_p.h"
26 #include "KReportZoomHandler_p.h"
27
28 #include <QPainter>
29 #include <QMenu>
30 #include <QMouseEvent>
31 #include <QFontDatabase>
32
33 // the distance in pixels of a mouse position considered outside the rule
34 static const int OutsideRulerThreshold = 20;
35 //
36 static const int fullStepMarkerLength = 6;
37 static const int halfStepMarkerLength = 6;
38 static const int quarterStepMarkerLength = 3;
39 static const int measurementTextAboveBelowMargin = 1;
40
41 class RulerTabChooser : public QWidget
42 {
43 public:
RulerTabChooser(QWidget * parent)44 RulerTabChooser(QWidget *parent) : QWidget(parent), m_type(QTextOption::LeftTab), m_showTabs(false) {}
~RulerTabChooser()45 ~RulerTabChooser() override {}
46
type()47 inline QTextOption::TabType type() {return m_type;}
setShowTabs(bool showTabs)48 void setShowTabs(bool showTabs) { if (m_showTabs == showTabs) return; m_showTabs = showTabs; update(); }
49 void mousePressEvent(QMouseEvent *) override;
50
51 void paintEvent(QPaintEvent *) override;
52
53 private:
54 QTextOption::TabType m_type;
55 bool m_showTabs :1;
56 };
57
58 // ----
59
60 class PaintingStrategy
61 {
62 public:
63 /// constructor
PaintingStrategy()64 PaintingStrategy() {}
65 /// destructor
~PaintingStrategy()66 virtual ~PaintingStrategy() {}
67
68 /**
69 * Draw the background of the ruler.
70 * @param ruler the ruler to draw on.
71 * @param painter the painter we can paint with.
72 */
73 virtual QRectF drawBackground(const KReportRuler::Private *ruler, QPainter *painter) = 0;
74
75 /**
76 * Draw the indicators for text-tabs.
77 * @param ruler the ruler to draw on.
78 * @param painter the painter we can paint with.
79 */
80 virtual void drawTabs(const KReportRuler::Private *ruler, QPainter *painter) = 0;
81
82 /**
83 * Draw the indicators for the measurements which typically are drawn every [unit].
84 * @param ruler the ruler to draw on.
85 * @param painter the painter we can paint with.
86 * @param rectangle
87 */
88 virtual void drawMeasurements(const KReportRuler::Private *ruler, QPainter *painter, const QRectF &rectangle) = 0;
89
90 /**
91 * Draw the indicators for the indents of a text paragraph
92 * @param ruler the ruler to draw on.
93 * @param painter the painter we can paint with.
94 */
95 virtual void drawIndents(const KReportRuler::Private *ruler, QPainter *painter) = 0;
96
97 /**
98 *returns the size suggestion for a ruler with this strategy.
99 */
100 virtual QSize sizeHint() = 0;
101 };
102
103 // ----
104
105 class HorizontalPaintingStrategy : public PaintingStrategy
106 {
107 public:
HorizontalPaintingStrategy()108 HorizontalPaintingStrategy() : lengthInPixel(1) {}
109
110 QRectF drawBackground(const KReportRuler::Private *ruler, QPainter *painter) override;
111 void drawTabs(const KReportRuler::Private *ruler, QPainter *painter) override;
112 void drawMeasurements(const KReportRuler::Private *ruler, QPainter *painter, const QRectF &rectangle) override;
113 void drawIndents(const KReportRuler::Private *ruler, QPainter *painter) override;
114 QSize sizeHint() override;
115
116 private:
117 qreal lengthInPixel;
118 };
119
120 // ----
121
122 class VerticalPaintingStrategy : public PaintingStrategy
123 {
124 public:
VerticalPaintingStrategy()125 VerticalPaintingStrategy() : lengthInPixel(1) {}
126
127 QRectF drawBackground(const KReportRuler::Private *ruler, QPainter *painter) override;
drawTabs(const KReportRuler::Private *,QPainter *)128 void drawTabs(const KReportRuler::Private *, QPainter *) override {}
129 void drawMeasurements(const KReportRuler::Private *ruler, QPainter *painter, const QRectF &rectangle) override;
drawIndents(const KReportRuler::Private *,QPainter *)130 void drawIndents(const KReportRuler::Private *, QPainter *) override {}
131 QSize sizeHint() override;
132
133 private:
134 qreal lengthInPixel;
135 };
136
137 class HorizontalDistancesPaintingStrategy : public HorizontalPaintingStrategy
138 {
139 public:
HorizontalDistancesPaintingStrategy()140 HorizontalDistancesPaintingStrategy() {}
141
142 void drawMeasurements(const KReportRuler::Private *ruler, QPainter *painter, const QRectF &rectangle) override;
143
144 private:
145 void drawDistanceLine(const KReportRuler::Private *d, QPainter *painter, qreal start, qreal end);
146 };
147
148 // ----
149
150 class KReportRuler::Private
151 {
152 public:
153 Private(KReportRuler *parent, const KReportZoomHandler &zoomHandler, Qt::Orientation orientation);
154 ~Private();
155
156 void emitTabChanged();
157
158 KReportUnit unit;
159 const Qt::Orientation orientation;
160 const KReportZoomHandler * const viewConverter;
161
162 int offset;
163 qreal rulerLength;
164 qreal activeRangeStart;
165 qreal activeRangeEnd;
166 qreal activeOverrideRangeStart;
167 qreal activeOverrideRangeEnd;
168
169 int mouseCoordinate;
170 int showMousePosition;
171
172 bool showSelectionBorders;
173 qreal firstSelectionBorder;
174 qreal secondSelectionBorder;
175
176 bool showIndents;
177 qreal firstLineIndent;
178 qreal paragraphIndent;
179 qreal endIndent;
180
181 bool showTabs;
182 bool relativeTabs;
183 bool tabMoved; // set to true on first move of a selected tab
184 QList<KReportRuler::Tab> tabs;
185 int originalIndex; //index of selected tab before we started dragging it.
186 int currentIndex; //index of selected tab or selected HotSpot - only valid when selected indicates tab or hotspot
187 KReportRuler::Tab deletedTab;
188 qreal tabDistance;
189
190 struct HotSpotData {
191 qreal position;
192 int id;
193 };
194 QList<HotSpotData> hotspots;
195
196 bool rightToLeft;
197 enum class Selection {
198 None,
199 Tab,
200 FirstLineIndent,
201 ParagraphIndent,
202 EndIndent,
203 HotSpot
204 };
205 Selection selected;
206 int selectOffset;
207
208 QList<QAction*> popupActions;
209
210 RulerTabChooser *tabChooser;
211
212 // Cached painting strategies
213 PaintingStrategy * normalPaintingStrategy;
214 PaintingStrategy * distancesPaintingStrategy;
215
216 // Current painting strategy
217 PaintingStrategy * paintingStrategy;
218
219 KReportRuler *ruler;
220
221 qreal numberStepForUnit() const;
222 /// @return The rounding of value to the nearest multiple of stepValue
223 qreal doSnapping(qreal value) const;
224 Selection selectionAtPosition(const QPoint &pos, int *selectOffset = nullptr);
225 int hotSpotIndex(const QPoint &pos);
226 qreal effectiveActiveRangeStart() const;
227 qreal effectiveActiveRangeEnd() const;
228
229 friend class VerticalPaintingStrategy;
230 friend class HorizontalPaintingStrategy;
231 };
232
233 // ----
234
mousePressEvent(QMouseEvent *)235 void RulerTabChooser::mousePressEvent(QMouseEvent *)
236 {
237 if (! m_showTabs) {
238 return;
239 }
240
241 switch(m_type) {
242 case QTextOption::LeftTab:
243 m_type = QTextOption::RightTab;
244 break;
245 case QTextOption::RightTab:
246 m_type = QTextOption::CenterTab;
247 break;
248 case QTextOption::CenterTab:
249 m_type = QTextOption::DelimiterTab;
250 break;
251 case QTextOption::DelimiterTab:
252 m_type = QTextOption::LeftTab;
253 break;
254 }
255 update();
256 }
257
paintEvent(QPaintEvent *)258 void RulerTabChooser::paintEvent(QPaintEvent *)
259 {
260 if (! m_showTabs) {
261 return;
262 }
263
264 QPainter painter(this);
265 QPolygonF polygon;
266
267 painter.setPen(palette().color(QPalette::Text));
268 painter.setBrush(palette().color(QPalette::Text));
269 painter.setRenderHint( QPainter::Antialiasing );
270
271 qreal x = qreal(width())/2.0;
272 painter.translate(0,-height()/2+5);
273
274 switch (m_type) {
275 case QTextOption::LeftTab:
276 polygon << QPointF(x+0.5, height() - 8.5)
277 << QPointF(x+6.5, height() - 2.5)
278 << QPointF(x+0.5, height() - 2.5);
279 painter.drawPolygon(polygon);
280 break;
281 case QTextOption::RightTab:
282 polygon << QPointF(x+0.5, height() - 8.5)
283 << QPointF(x-5.5, height() - 2.5)
284 << QPointF(x+0.5, height() - 2.5);
285 painter.drawPolygon(polygon);
286 break;
287 case QTextOption::CenterTab:
288 polygon << QPointF(x+0.5, height() - 8.5)
289 << QPointF(x-5.5, height() - 2.5)
290 << QPointF(x+6.5, height() - 2.5);
291 painter.drawPolygon(polygon);
292 break;
293 case QTextOption::DelimiterTab:
294 polygon << QPointF(x-5.5, height() - 2.5)
295 << QPointF(x+6.5, height() - 2.5);
296 painter.drawPolyline(polygon);
297 polygon << QPointF(x+0.5, height() - 2.5)
298 << QPointF(x+0.5, height() - 8.5);
299 painter.drawPolyline(polygon);
300 break;
301 default:
302 break;
303 }
304 }
305
compareTabs(const KReportRuler::Tab & tab1,const KReportRuler::Tab & tab2)306 static int compareTabs(const KReportRuler::Tab &tab1, const KReportRuler::Tab &tab2)
307 {
308 return tab1.position < tab2.position;
309 }
310
drawBackground(const KReportRuler::Private * d,QPainter * painter)311 QRectF HorizontalPaintingStrategy::drawBackground(const KReportRuler::Private *d, QPainter *painter)
312 {
313 lengthInPixel = d->viewConverter->documentToViewX(d->rulerLength);
314 QRectF rectangle;
315 rectangle.setX(qMax(0, d->offset));
316 rectangle.setY(0);
317 rectangle.setWidth(qMin(qreal(d->ruler->width() - 1.0 - rectangle.x()),
318 (d->offset >= 0) ? lengthInPixel : lengthInPixel + d->offset));
319 rectangle.setHeight(d->ruler->height() - 1);
320 QRectF activeRangeRectangle;
321 activeRangeRectangle.setX(qMax(rectangle.x() + 1,
322 d->viewConverter->documentToViewX(d->effectiveActiveRangeStart()) + d->offset));
323 activeRangeRectangle.setY(rectangle.y() + 1);
324 activeRangeRectangle.setRight(qMin(rectangle.right() - 1,
325 d->viewConverter->documentToViewX(d->effectiveActiveRangeEnd()) + d->offset));
326 activeRangeRectangle.setHeight(rectangle.height() - 2);
327
328 painter->setPen(d->ruler->palette().color(QPalette::Mid));
329 painter->drawRect(rectangle);
330
331 if(d->effectiveActiveRangeStart() != d->effectiveActiveRangeEnd())
332 painter->fillRect(activeRangeRectangle, d->ruler->palette().brush(QPalette::Base));
333
334 if(d->showSelectionBorders) {
335 // Draw first selection border
336 if(d->firstSelectionBorder > 0) {
337 qreal border = d->viewConverter->documentToViewX(d->firstSelectionBorder) + d->offset;
338 painter->drawLine(QPointF(border, rectangle.y() + 1), QPointF(border, rectangle.bottom() - 1));
339 }
340 // Draw second selection border
341 if(d->secondSelectionBorder > 0) {
342 qreal border = d->viewConverter->documentToViewX(d->secondSelectionBorder) + d->offset;
343 painter->drawLine(QPointF(border, rectangle.y() + 1), QPointF(border, rectangle.bottom() - 1));
344 }
345 }
346
347 return rectangle;
348 }
349
drawTabs(const KReportRuler::Private * d,QPainter * painter)350 void HorizontalPaintingStrategy::drawTabs(const KReportRuler::Private *d, QPainter *painter)
351 {
352 if (! d->showTabs)
353 return;
354 QPolygonF polygon;
355
356 const QColor tabColor = d->ruler->palette().color(QPalette::Text);
357 painter->setPen(tabColor);
358 painter->setBrush(tabColor);
359 painter->setRenderHint( QPainter::Antialiasing );
360
361 qreal position = -10000;
362
363 foreach (const KReportRuler::Tab & t, d->tabs) {
364 qreal x;
365 if (d->rightToLeft) {
366 x = d->viewConverter->documentToViewX(d->effectiveActiveRangeEnd()
367 - (d->relativeTabs ? d->paragraphIndent : 0) - t.position) + d->offset;
368 } else {
369 x = d->viewConverter->documentToViewX(d->effectiveActiveRangeStart()
370 + (d->relativeTabs ? d->paragraphIndent : 0) + t.position) + d->offset;
371 }
372 position = qMax(position, t.position);
373
374 polygon.clear();
375 switch (t.type) {
376 case QTextOption::LeftTab:
377 polygon << QPointF(x+0.5, d->ruler->height() - 6.5)
378 << QPointF(x+6.5, d->ruler->height() - 0.5)
379 << QPointF(x+0.5, d->ruler->height() - 0.5);
380 painter->drawPolygon(polygon);
381 break;
382 case QTextOption::RightTab:
383 polygon << QPointF(x+0.5, d->ruler->height() - 6.5)
384 << QPointF(x-5.5, d->ruler->height() - 0.5)
385 << QPointF(x+0.5, d->ruler->height() - 0.5);
386 painter->drawPolygon(polygon);
387 break;
388 case QTextOption::CenterTab:
389 polygon << QPointF(x+0.5, d->ruler->height() - 6.5)
390 << QPointF(x-5.5, d->ruler->height() - 0.5)
391 << QPointF(x+6.5, d->ruler->height() - 0.5);
392 painter->drawPolygon(polygon);
393 break;
394 case QTextOption::DelimiterTab:
395 polygon << QPointF(x-5.5, d->ruler->height() - 0.5)
396 << QPointF(x+6.5, d->ruler->height() - 0.5);
397 painter->drawPolyline(polygon);
398 polygon << QPointF(x+0.5, d->ruler->height() - 0.5)
399 << QPointF(x+0.5, d->ruler->height() - 6.5);
400 painter->drawPolyline(polygon);
401 break;
402 default:
403 break;
404 }
405 }
406
407 // and also draw the regular interval tab that are non editable
408 if (d->tabDistance > 0.0) {
409 // first possible position
410 position = qMax(position, d->relativeTabs ? 0 : d->paragraphIndent);
411 if (position < 0) {
412 position = int(position / d->tabDistance) * d->tabDistance;
413 } else {
414 position = (int(position / d->tabDistance) + 1) * d->tabDistance;
415 }
416 while (position < d->effectiveActiveRangeEnd() - d->effectiveActiveRangeStart()
417 - d->endIndent) {
418 qreal x;
419 if (d->rightToLeft) {
420 x = d->viewConverter->documentToViewX(d->effectiveActiveRangeEnd()
421 - (d->relativeTabs ? d->paragraphIndent : 0) - position) + d->offset;
422 } else {
423 x = d->viewConverter->documentToViewX(d->effectiveActiveRangeStart()
424 + (d->relativeTabs ? d->paragraphIndent : 0) + position) + d->offset;
425 }
426
427 polygon.clear();
428 polygon << QPointF(x+0.5, d->ruler->height() - 3.5)
429 << QPointF(x+4.5, d->ruler->height() - 0.5)
430 << QPointF(x+0.5, d->ruler->height() - 0.5);
431 painter->drawPolygon(polygon);
432
433 position += d->tabDistance;
434 }
435 }
436 }
437
drawMeasurements(const KReportRuler::Private * d,QPainter * painter,const QRectF & rectangle)438 void HorizontalPaintingStrategy::drawMeasurements(const KReportRuler::Private *d, QPainter *painter, const QRectF &rectangle)
439 {
440 qreal numberStep = d->numberStepForUnit(); // number step in unit
441 // QRectF activeRangeRectangle;
442 int numberStepPixel = qRound(d->viewConverter->documentToViewX(d->unit.fromUserValue(numberStep)));
443 // const bool adjustMillimeters = (d->unit.type() == KReportUnit::Millimeter);
444
445 const QFont font = QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont);
446 const QFontMetrics fontMetrics(font);
447 painter->setFont(font);
448
449 if (numberStepPixel == 0 || numberStep == 0)
450 return;
451
452 // Calc the longest text length
453 int textLength = 0;
454 for(int i = 0; i < lengthInPixel; i += numberStepPixel) {
455 int number = qRound((i / numberStepPixel) * numberStep);
456
457 textLength = qMax(textLength, fontMetrics.width(QString::number(number)));
458 }
459 textLength += 4; // Add some padding
460
461 // Change number step so all digits fits
462 while(textLength > numberStepPixel) {
463 numberStepPixel += numberStepPixel;
464 numberStep += numberStep;
465 }
466
467 int start=0;
468 // Calc the first number step
469 if(d->offset < 0)
470 start = qAbs(d->offset);
471
472 // make a little hack so rulers shows correctly inversed number aligned
473 const qreal lengthInUnit = d->unit.toUserValue(d->rulerLength);
474 const qreal hackyLength = lengthInUnit - fmod(lengthInUnit, numberStep);
475 if(d->rightToLeft) {
476 start -= int(d->viewConverter->documentToViewX(fmod(d->rulerLength,
477 d->unit.fromUserValue(numberStep))));
478 }
479
480 int stepCount = (start / numberStepPixel) + 1;
481 int halfStepCount = (start / qRound(numberStepPixel * 0.5)) + 1;
482 int quarterStepCount = (start / qRound(numberStepPixel * 0.25)) + 1;
483
484 int pos = 0;
485 const QPen numberPen(d->ruler->palette().color(QPalette::Text));
486 const QPen markerPen(d->ruler->palette().color(QPalette::Inactive, QPalette::Text));
487 painter->setPen(markerPen);
488
489 if(d->offset > 0)
490 painter->translate(d->offset, 0);
491
492 const int len = qRound(rectangle.width()) + start;
493 int nextStep = qRound(d->viewConverter->documentToViewX(
494 d->unit.fromUserValue(numberStep * stepCount)));
495 int nextHalfStep = qRound(d->viewConverter->documentToViewX(d->unit.fromUserValue(
496 numberStep * 0.5 * halfStepCount)));
497 int nextQuarterStep = qRound(d->viewConverter->documentToViewX(d->unit.fromUserValue(
498 numberStep * 0.25 * quarterStepCount)));
499
500 for(int i = start; i < len; ++i) {
501 pos = i - start;
502
503 if(i == nextStep) {
504 if(pos != 0)
505 painter->drawLine(QPointF(pos, rectangle.bottom()-1),
506 QPointF(pos, rectangle.bottom() - fullStepMarkerLength));
507
508 int number = qRound(stepCount * numberStep);
509
510 QString numberText = QString::number(number);
511 int x = pos;
512 if (d->rightToLeft) { // this is done in a hacky way with the fine tuning done above
513 numberText = QString::number(hackyLength - stepCount * numberStep);
514 }
515 painter->setPen(numberPen);
516 painter->drawText(QPointF(x-fontMetrics.width(numberText)/2.0,
517 rectangle.bottom() -fullStepMarkerLength -measurementTextAboveBelowMargin),
518 numberText);
519 painter->setPen(markerPen);
520
521 ++stepCount;
522 nextStep = qRound(d->viewConverter->documentToViewX(
523 d->unit.fromUserValue(numberStep * stepCount)));
524 ++halfStepCount;
525 nextHalfStep = qRound(d->viewConverter->documentToViewX(d->unit.fromUserValue(
526 numberStep * 0.5 * halfStepCount)));
527 ++quarterStepCount;
528 nextQuarterStep = qRound(d->viewConverter->documentToViewX(d->unit.fromUserValue(
529 numberStep * 0.25 * quarterStepCount)));
530 }
531 else if(i == nextHalfStep) {
532 if(pos != 0)
533 painter->drawLine(QPointF(pos, rectangle.bottom()-1),
534 QPointF(pos, rectangle.bottom() - halfStepMarkerLength));
535
536 ++halfStepCount;
537 nextHalfStep = qRound(d->viewConverter->documentToViewX(d->unit.fromUserValue(
538 numberStep * 0.5 * halfStepCount)));
539 ++quarterStepCount;
540 nextQuarterStep = qRound(d->viewConverter->documentToViewX(d->unit.fromUserValue(
541 numberStep * 0.25 * quarterStepCount)));
542 }
543 else if(i == nextQuarterStep) {
544 if(pos != 0)
545 painter->drawLine(QPointF(pos, rectangle.bottom()-1),
546 QPointF(pos, rectangle.bottom() - quarterStepMarkerLength));
547
548 ++quarterStepCount;
549 nextQuarterStep = qRound(d->viewConverter->documentToViewX(d->unit.fromUserValue(
550 numberStep * 0.25 * quarterStepCount)));
551 }
552 }
553
554 // Draw the mouse indicator
555 const int mouseCoord = d->mouseCoordinate - start;
556 if (d->selected == KReportRuler::Private::Selection::None
557 || d->selected == KReportRuler::Private::Selection::HotSpot)
558 {
559 const qreal top = rectangle.y() + 1;
560 const qreal bottom = rectangle.bottom() -1;
561 if (d->selected == KReportRuler::Private::Selection::None && d->showMousePosition
562 && mouseCoord > 0 && mouseCoord < rectangle.width())
563 {
564 painter->drawLine(QPointF(mouseCoord, top), QPointF(mouseCoord, bottom));
565 }
566 foreach (const KReportRuler::Private::HotSpotData & hp, d->hotspots) {
567 const qreal x = d->viewConverter->documentToViewX(hp.position) + d->offset;
568 painter->drawLine(QPointF(x, top), QPointF(x, bottom));
569 }
570 }
571 }
572
drawIndents(const KReportRuler::Private * d,QPainter * painter)573 void HorizontalPaintingStrategy::drawIndents(const KReportRuler::Private *d, QPainter *painter)
574 {
575 QPolygonF polygon;
576
577 painter->setBrush(d->ruler->palette().brush(QPalette::Base));
578 painter->setRenderHint( QPainter::Antialiasing );
579
580 qreal x;
581 // Draw first line start indent
582 if (d->rightToLeft)
583 x = d->effectiveActiveRangeEnd() - d->firstLineIndent - d->paragraphIndent;
584 else
585 x = d->effectiveActiveRangeStart() + d->firstLineIndent + d->paragraphIndent;
586 // convert and use the +0.5 to go to nearest integer so that the 0.5 added below ensures sharp lines
587 x = int(d->viewConverter->documentToViewX(x) + d->offset + 0.5);
588 polygon << QPointF(x+6.5, 0.5)
589 << QPointF(x+0.5, 8.5)
590 << QPointF(x-5.5, 0.5)
591 << QPointF(x+5.5, 0.5);
592 painter->drawPolygon(polygon);
593
594 // draw the hanging indent.
595 if (d->rightToLeft)
596 x = d->effectiveActiveRangeStart() + d->endIndent;
597 else
598 x = d->effectiveActiveRangeStart() + d->paragraphIndent;
599 // convert and use the +0.5 to go to nearest integer so that the 0.5 added below ensures sharp lines
600 x = int(d->viewConverter->documentToViewX(x) + d->offset + 0.5);
601 const int bottom = d->ruler->height();
602 polygon.clear();
603 polygon << QPointF(x+6.5, bottom - 0.5)
604 << QPointF(x+0.5, bottom - 8.5)
605 << QPointF(x-5.5, bottom - 0.5)
606 << QPointF(x+5.5, bottom - 0.5);
607 painter->drawPolygon(polygon);
608
609 // Draw end-indent or paragraph indent if mode is rightToLeft
610 qreal diff;
611 if (d->rightToLeft)
612 diff = d->viewConverter->documentToViewX(d->effectiveActiveRangeEnd()
613 - d->paragraphIndent) + d->offset - x;
614 else
615 diff = d->viewConverter->documentToViewX(d->effectiveActiveRangeEnd() - d->endIndent)
616 + d->offset - x;
617 polygon.translate(diff, 0);
618 painter->drawPolygon(polygon);
619 }
620
sizeHint()621 QSize HorizontalPaintingStrategy::sizeHint()
622 {
623 // assumes that digits for the number only use glyphs which do not go below the baseline
624 const QFontMetrics fm(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont));
625 const int digitsHeight = fm.ascent() + 1; // +1 for baseline
626 const int minimum = digitsHeight + fullStepMarkerLength + 2*measurementTextAboveBelowMargin;
627
628 return QSize(0, minimum);
629 }
630
drawBackground(const KReportRuler::Private * d,QPainter * painter)631 QRectF VerticalPaintingStrategy::drawBackground(const KReportRuler::Private *d, QPainter *painter)
632 {
633 lengthInPixel = d->viewConverter->documentToViewY(d->rulerLength);
634 QRectF rectangle;
635 rectangle.setX(0);
636 rectangle.setY(qMax(0, d->offset));
637 rectangle.setWidth(d->ruler->width() - 1.0);
638 rectangle.setHeight(qMin(qreal(d->ruler->height() - 1.0 - rectangle.y()),
639 (d->offset >= 0) ? lengthInPixel : lengthInPixel + d->offset));
640
641 QRectF activeRangeRectangle;
642 activeRangeRectangle.setX(rectangle.x() + 1);
643 activeRangeRectangle.setY(qMax(rectangle.y() + 1,
644 d->viewConverter->documentToViewY(d->effectiveActiveRangeStart()) + d->offset));
645 activeRangeRectangle.setWidth(rectangle.width() - 2);
646 activeRangeRectangle.setBottom(qMin(rectangle.bottom() - 1,
647 d->viewConverter->documentToViewY(d->effectiveActiveRangeEnd()) + d->offset));
648
649 painter->setPen(d->ruler->palette().color(QPalette::Mid));
650 painter->drawRect(rectangle);
651
652 if(d->effectiveActiveRangeStart() != d->effectiveActiveRangeEnd())
653 painter->fillRect(activeRangeRectangle, d->ruler->palette().brush(QPalette::Base));
654
655 if(d->showSelectionBorders) {
656 // Draw first selection border
657 if(d->firstSelectionBorder > 0) {
658 qreal border = d->viewConverter->documentToViewY(d->firstSelectionBorder) + d->offset;
659 painter->drawLine(QPointF(rectangle.x() + 1, border), QPointF(rectangle.right() - 1, border));
660 }
661 // Draw second selection border
662 if(d->secondSelectionBorder > 0) {
663 qreal border = d->viewConverter->documentToViewY(d->secondSelectionBorder) + d->offset;
664 painter->drawLine(QPointF(rectangle.x() + 1, border), QPointF(rectangle.right() - 1, border));
665 }
666 }
667
668 return rectangle;
669 }
670
drawMeasurements(const KReportRuler::Private * d,QPainter * painter,const QRectF & rectangle)671 void VerticalPaintingStrategy::drawMeasurements(const KReportRuler::Private *d, QPainter *painter, const QRectF &rectangle)
672 {
673 qreal numberStep = d->numberStepForUnit(); // number step in unit
674 int numberStepPixel = qRound(d->viewConverter->documentToViewY( d->unit.fromUserValue(numberStep)));
675 if (numberStepPixel <= 0)
676 return;
677
678 const QFont font = QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont);
679 const QFontMetrics fontMetrics(font);
680 painter->setFont(font);
681
682 // Calc the longest text length
683 int textLength = 0;
684
685 for(int i = 0; i < lengthInPixel; i += numberStepPixel) {
686 int number = qRound((i / numberStepPixel) * numberStep);
687 textLength = qMax(textLength, fontMetrics.width(QString::number(number)));
688 }
689 textLength += 4; // Add some padding
690
691 if (numberStepPixel == 0 || numberStep == 0)
692 return;
693 // Change number step so all digits will fit
694 while(textLength > numberStepPixel) {
695 numberStepPixel += numberStepPixel;
696 numberStep += numberStep;
697 }
698
699 // Calc the first number step
700 const int start = d->offset < 0 ? qAbs(d->offset) : 0;
701
702 // make a little hack so rulers shows correctly inversed number aligned
703 int stepCount = (start / numberStepPixel) + 1;
704 int halfStepCount = (start / qRound(numberStepPixel * 0.5)) + 1;
705 int quarterStepCount = (start / qRound(numberStepPixel * 0.25)) + 1;
706
707 const QPen numberPen(d->ruler->palette().color(QPalette::Text));
708 const QPen markerPen(d->ruler->palette().color(QPalette::Inactive, QPalette::Text));
709 painter->setPen(markerPen);
710
711 if(d->offset > 0)
712 painter->translate(0, d->offset);
713
714 const int len = qRound(rectangle.height()) + start;
715 int nextStep = qRound(d->viewConverter->documentToViewY(
716 d->unit.fromUserValue(numberStep * stepCount)));
717 int nextHalfStep = qRound(d->viewConverter->documentToViewY(d->unit.fromUserValue(
718 numberStep * 0.5 * halfStepCount)));
719 int nextQuarterStep = qRound(d->viewConverter->documentToViewY(d->unit.fromUserValue(
720 numberStep * 0.25 * quarterStepCount)));
721
722 int pos = 0;
723 for(int i = start; i < len; ++i) {
724 pos = i - start;
725
726 if(i == nextStep) {
727 painter->save();
728 painter->translate(rectangle.right()-fullStepMarkerLength, pos);
729 if(pos != 0)
730 painter->drawLine(QPointF(0, 0), QPointF(fullStepMarkerLength-1, 0));
731
732 painter->rotate(-90);
733 int number = qRound(stepCount * numberStep);
734 QString numberText = QString::number(number);
735 painter->setPen(numberPen);
736 painter->drawText(QPointF(-fontMetrics.width(numberText) / 2.0, -measurementTextAboveBelowMargin), numberText);
737 painter->restore();
738
739 ++stepCount;
740 nextStep = qRound(d->viewConverter->documentToViewY(
741 d->unit.fromUserValue(numberStep * stepCount)));
742 ++halfStepCount;
743 nextHalfStep = qRound(d->viewConverter->documentToViewY(d->unit.fromUserValue(
744 numberStep * 0.5 * halfStepCount)));
745 ++quarterStepCount;
746 nextQuarterStep = qRound(d->viewConverter->documentToViewY(d->unit.fromUserValue(
747 numberStep * 0.25 * quarterStepCount)));
748 } else if(i == nextHalfStep) {
749 if(pos != 0)
750 painter->drawLine(QPointF(rectangle.right() - halfStepMarkerLength, pos),
751 QPointF(rectangle.right() - 1, pos));
752
753 ++halfStepCount;
754 nextHalfStep = qRound(d->viewConverter->documentToViewY(d->unit.fromUserValue(
755 numberStep * 0.5 * halfStepCount)));
756 ++quarterStepCount;
757 nextQuarterStep = qRound(d->viewConverter->documentToViewY(d->unit.fromUserValue(
758 numberStep * 0.25 * quarterStepCount)));
759 } else if(i == nextQuarterStep) {
760 if(pos != 0)
761 painter->drawLine(QPointF(rectangle.right() - quarterStepMarkerLength, pos),
762 QPointF(rectangle.right() - 1, pos));
763
764 ++quarterStepCount;
765 nextQuarterStep = qRound(d->viewConverter->documentToViewY(d->unit.fromUserValue(
766 numberStep * 0.25 * quarterStepCount)));
767 }
768 }
769
770 // Draw the mouse indicator
771 const int mouseCoord = d->mouseCoordinate - start;
772 if (d->selected == KReportRuler::Private::Selection::None
773 || d->selected == KReportRuler::Private::Selection::HotSpot)
774 {
775 const qreal left = rectangle.left() + 1;
776 const qreal right = rectangle.right() -1;
777 if (d->selected == KReportRuler::Private::Selection::None && d->showMousePosition
778 && mouseCoord > 0 && mouseCoord < rectangle.height())
779 {
780 painter->drawLine(QPointF(left, mouseCoord), QPointF(right, mouseCoord));
781 }
782 foreach (const KReportRuler::Private::HotSpotData & hp, d->hotspots) {
783 const qreal y = d->viewConverter->documentToViewY(hp.position) + d->offset;
784 painter->drawLine(QPointF(left, y), QPointF(right, y));
785 }
786 }
787 }
788
sizeHint()789 QSize VerticalPaintingStrategy::sizeHint()
790 {
791 // assumes that digits for the number only use glyphs which do not go below the baseline
792 const QFontMetrics fm(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont));
793 const int digitsHeight = fm.ascent() + 1; // +1 for baseline
794 const int minimum = digitsHeight + fullStepMarkerLength + 2*measurementTextAboveBelowMargin;
795
796 return QSize(minimum, 0);
797 }
798
799
drawDistanceLine(const KReportRuler::Private * d,QPainter * painter,qreal start,qreal end)800 void HorizontalDistancesPaintingStrategy::drawDistanceLine(const KReportRuler::Private *d,
801 QPainter *painter, qreal start,
802 qreal end)
803 {
804
805 // Don't draw too short lines
806 if (qMax(start, end) - qMin(start, end) < 1)
807 return;
808
809 painter->save();
810 painter->translate(d->offset, d->ruler->height() / 2);
811 painter->setPen(d->ruler->palette().color(QPalette::Text));
812 painter->setBrush(d->ruler->palette().color(QPalette::Text));
813
814 QLineF line(QPointF(d->viewConverter->documentToViewX(start), 0),
815 QPointF(d->viewConverter->documentToViewX(end), 0));
816 QPointF midPoint = line.pointAt(0.5);
817
818 // Draw the label text
819 const QFont font = QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont);
820 const QFontMetrics fontMetrics(font);
821 QString label = d->unit.toUserStringValue(
822 d->viewConverter->viewToDocumentX(line.length())) + QLatin1String("") + d->unit.symbol();
823 QPointF labelPosition = QPointF(midPoint.x() - fontMetrics.width(label)/2,
824 midPoint.y() + fontMetrics.ascent()/2);
825 painter->setFont(font);
826 painter->drawText(labelPosition, label);
827
828 // Draw the arrow lines
829 qreal arrowLength = (line.length() - fontMetrics.width(label)) / 2 - 2;
830 arrowLength = qMax(qreal(0.0), arrowLength);
831 QLineF startArrow(line.p1(), line.pointAt(arrowLength / line.length()));
832 QLineF endArrow(line.p2(), line.pointAt(1.0 - arrowLength / line.length()));
833 painter->drawLine(startArrow);
834 painter->drawLine(endArrow);
835
836 // Draw the arrow heads
837 QPolygonF arrowHead;
838 arrowHead << line.p1() << QPointF(line.x1()+3, line.y1()-3)
839 << QPointF(line.x1()+3, line.y1()+3);
840 painter->drawPolygon(arrowHead);
841 arrowHead.clear();
842 arrowHead << line.p2() << QPointF(line.x2()-3, line.y2()-3)
843 << QPointF(line.x2()-3, line.y2()+3);
844 painter->drawPolygon(arrowHead);
845
846 painter->restore();
847 }
848
drawMeasurements(const KReportRuler::Private * d,QPainter * painter,const QRectF &)849 void HorizontalDistancesPaintingStrategy::drawMeasurements(const KReportRuler::Private *d,
850 QPainter *painter, const QRectF&)
851 {
852 QList<qreal> points;
853 points << 0.0;
854 points << d->effectiveActiveRangeStart() + d->paragraphIndent + d->firstLineIndent;
855 points << d->effectiveActiveRangeStart() + d->paragraphIndent;
856 points << d->effectiveActiveRangeEnd() - d->endIndent;
857 points << d->effectiveActiveRangeStart();
858 points << d->effectiveActiveRangeEnd();
859 points << d->rulerLength;
860 qSort(points.begin(), points.end());
861 QListIterator<qreal> i(points);
862 i.next();
863 while (i.hasNext() && i.hasPrevious()) {
864 drawDistanceLine(d, painter, i.peekPrevious(), i.peekNext());
865 i.next();
866 }
867 }
868
Private(KReportRuler * parent,const KReportZoomHandler & zoomHandler,Qt::Orientation o)869 KReportRuler::Private::Private(KReportRuler *parent,
870 const KReportZoomHandler &zoomHandler, Qt::Orientation o)
871 : unit(DEFAULT_UNIT),
872 orientation(o),
873 viewConverter(&zoomHandler),
874 offset(0),
875 rulerLength(0),
876 activeRangeStart(0),
877 activeRangeEnd(0),
878 activeOverrideRangeStart(0),
879 activeOverrideRangeEnd(0),
880 mouseCoordinate(-1),
881 showMousePosition(0),
882 showSelectionBorders(false),
883 firstSelectionBorder(0),
884 secondSelectionBorder(0),
885 showIndents(false),
886 firstLineIndent(0),
887 paragraphIndent(0),
888 endIndent(0),
889 showTabs(false),
890 relativeTabs(false),
891 tabMoved(false),
892 originalIndex(-1),
893 currentIndex(0),
894 tabDistance(0.0),
895 rightToLeft(false),
896 selected(Selection::None),
897 selectOffset(0),
898 tabChooser(nullptr),
899 normalPaintingStrategy(o == Qt::Horizontal ?
900 (PaintingStrategy*)new HorizontalPaintingStrategy() : (PaintingStrategy*)new VerticalPaintingStrategy()),
901 distancesPaintingStrategy((PaintingStrategy*)new HorizontalDistancesPaintingStrategy()),
902 paintingStrategy(normalPaintingStrategy),
903 ruler(parent)
904 {
905 }
906
~Private()907 KReportRuler::Private::~Private()
908 {
909 delete normalPaintingStrategy;
910 delete distancesPaintingStrategy;
911 }
912
numberStepForUnit() const913 qreal KReportRuler::Private::numberStepForUnit() const
914 {
915 switch(unit.type()) {
916 case KReportUnit::Type::Inch:
917 case KReportUnit::Type::Centimeter:
918 case KReportUnit::Type::Decimeter:
919 case KReportUnit::Type::Millimeter:
920 return 1.0;
921 case KReportUnit::Type::Pica:
922 case KReportUnit::Type::Cicero:
923 return 10.0;
924 case KReportUnit::Type::Point:
925 default:
926 return 100.0;
927 }
928 }
929
doSnapping(qreal value) const930 qreal KReportRuler::Private::doSnapping(qreal value) const
931 {
932 qreal numberStep = unit.fromUserValue(numberStepForUnit()/4.0);
933 return numberStep * qRound(value / numberStep);
934 }
935
selectionAtPosition(const QPoint & pos,int * selectOffset)936 KReportRuler::Private::Selection KReportRuler::Private::selectionAtPosition(const QPoint & pos, int *selectOffset )
937 {
938 const int height = ruler->height();
939 if (rightToLeft) {
940 int x = int(viewConverter->documentToViewX(effectiveActiveRangeEnd() - firstLineIndent - paragraphIndent) + offset);
941 if (pos.x() >= x - 8 && pos.x() <= x +8 && pos.y() < height / 2) {
942 if (selectOffset)
943 *selectOffset = x - pos.x();
944 return KReportRuler::Private::Selection::FirstLineIndent;
945 }
946
947 x = int(viewConverter->documentToViewX(effectiveActiveRangeEnd() - paragraphIndent) + offset);
948 if (pos.x() >= x - 8 && pos.x() <= x +8 && pos.y() > height / 2) {
949 if (selectOffset)
950 *selectOffset = x - pos.x();
951 return KReportRuler::Private::Selection::ParagraphIndent;
952 }
953
954 x = int(viewConverter->documentToViewX(effectiveActiveRangeStart() + endIndent) + offset);
955 if (pos.x() >= x - 8 && pos.x() <= x + 8) {
956 if (selectOffset)
957 *selectOffset = x - pos.x();
958 return KReportRuler::Private::Selection::EndIndent;
959 }
960 }
961 else {
962 int x = int(viewConverter->documentToViewX(effectiveActiveRangeStart() + firstLineIndent + paragraphIndent) + offset);
963 if (pos.x() >= x -8 && pos.x() <= x + 8 && pos.y() < height / 2) {
964 if (selectOffset)
965 *selectOffset = x - pos.x();
966 return KReportRuler::Private::Selection::FirstLineIndent;
967 }
968
969 x = int(viewConverter->documentToViewX(effectiveActiveRangeStart() + paragraphIndent) + offset);
970 if (pos.x() >= x - 8 && pos.x() <= x + 8 && pos.y() > height/2) {
971 if (selectOffset)
972 *selectOffset = x - pos.x();
973 return KReportRuler::Private::Selection::ParagraphIndent;
974 }
975
976 x = int(viewConverter->documentToViewX(effectiveActiveRangeEnd() - endIndent) + offset);
977 if (pos.x() >= x - 8 && pos.x() <= x + 8) {
978 if (selectOffset)
979 *selectOffset = x - pos.x();
980 return KReportRuler::Private::Selection::EndIndent;
981 }
982 }
983
984 return KReportRuler::Private::Selection::None;
985 }
986
hotSpotIndex(const QPoint & pos)987 int KReportRuler::Private::hotSpotIndex(const QPoint & pos)
988 {
989 for(int counter = 0; counter < hotspots.count(); counter++) {
990 bool hit;
991 if (orientation == Qt::Horizontal)
992 hit = qAbs(viewConverter->documentToViewX(hotspots[counter].position) - pos.x() + offset) < 3;
993 else
994 hit = qAbs(viewConverter->documentToViewY(hotspots[counter].position) - pos.y() + offset) < 3;
995
996 if (hit)
997 return counter;
998 }
999 return -1;
1000 }
1001
effectiveActiveRangeStart() const1002 qreal KReportRuler::Private::effectiveActiveRangeStart() const
1003 {
1004 if (activeOverrideRangeStart != activeOverrideRangeEnd) {
1005 return activeOverrideRangeStart;
1006 } else {
1007 return activeRangeStart;
1008 }
1009 }
1010
effectiveActiveRangeEnd() const1011 qreal KReportRuler::Private::effectiveActiveRangeEnd() const
1012 {
1013 if (activeOverrideRangeStart != activeOverrideRangeEnd) {
1014 return activeOverrideRangeEnd;
1015 } else {
1016 return activeRangeEnd;
1017 }
1018 }
1019
emitTabChanged()1020 void KReportRuler::Private::emitTabChanged()
1021 {
1022 KReportRuler::Tab tab;
1023 if (currentIndex >= 0)
1024 tab = tabs[currentIndex];
1025 emit ruler->tabChanged(originalIndex, currentIndex >= 0 ? &tab : nullptr);
1026 }
1027
1028
KReportRuler(QWidget * parent,Qt::Orientation orientation,const KReportZoomHandler & zoomHandler)1029 KReportRuler::KReportRuler(QWidget* parent, Qt::Orientation orientation,
1030 const KReportZoomHandler &zoomHandler)
1031 : QWidget(parent)
1032 , d(new KReportRuler::Private(this, zoomHandler, orientation))
1033 {
1034 setMouseTracking( true );
1035 }
1036
~KReportRuler()1037 KReportRuler::~KReportRuler()
1038 {
1039 delete d;
1040 }
1041
unit() const1042 KReportUnit KReportRuler::unit() const
1043 {
1044 return d->unit;
1045 }
1046
setUnit(const KReportUnit & unit)1047 void KReportRuler::setUnit(const KReportUnit &unit)
1048 {
1049 d->unit = unit;
1050 update();
1051 }
1052
rulerLength() const1053 qreal KReportRuler::rulerLength() const
1054 {
1055 return d->rulerLength;
1056 }
1057
orientation() const1058 Qt::Orientation KReportRuler::orientation() const
1059 {
1060 return d->orientation;
1061 }
1062
setOffset(int offset)1063 void KReportRuler::setOffset(int offset)
1064 {
1065 d->offset = offset;
1066 update();
1067 }
1068
setRulerLength(qreal length)1069 void KReportRuler::setRulerLength(qreal length)
1070 {
1071 d->rulerLength = length;
1072 update();
1073 }
1074
paintEvent(QPaintEvent * event)1075 void KReportRuler::paintEvent(QPaintEvent* event)
1076 {
1077 QPainter painter(this);
1078 painter.setClipRegion(event->region());
1079 painter.save();
1080 QRectF rectangle = d->paintingStrategy->drawBackground(d, &painter);
1081 painter.restore();
1082 painter.save();
1083 d->paintingStrategy->drawMeasurements(d, &painter, rectangle);
1084 painter.restore();
1085 if (d->showIndents) {
1086 painter.save();
1087 d->paintingStrategy->drawIndents(d, &painter);
1088 painter.restore();
1089 }
1090 d->paintingStrategy->drawTabs(d, &painter);
1091 }
1092
minimumSizeHint() const1093 QSize KReportRuler::minimumSizeHint() const
1094 {
1095 return d->paintingStrategy->sizeHint();
1096 }
1097
sizeHint() const1098 QSize KReportRuler::sizeHint() const
1099 {
1100 return d->paintingStrategy->sizeHint();
1101 }
1102
setActiveRange(qreal start,qreal end)1103 void KReportRuler::setActiveRange(qreal start, qreal end)
1104 {
1105 d->activeRangeStart = start;
1106 d->activeRangeEnd = end;
1107 update();
1108 }
1109
setOverrideActiveRange(qreal start,qreal end)1110 void KReportRuler::setOverrideActiveRange(qreal start, qreal end)
1111 {
1112 d->activeOverrideRangeStart = start;
1113 d->activeOverrideRangeEnd = end;
1114 update();
1115 }
1116
updateMouseCoordinate(int coordinate)1117 void KReportRuler::updateMouseCoordinate(int coordinate)
1118 {
1119 if(d->mouseCoordinate == coordinate)
1120 return;
1121 d->mouseCoordinate = coordinate;
1122 update();
1123 }
1124
setShowMousePosition(bool show)1125 void KReportRuler::setShowMousePosition(bool show)
1126 {
1127 d->showMousePosition = show;
1128 update();
1129 }
1130
setRightToLeft(bool isRightToLeft)1131 void KReportRuler::setRightToLeft(bool isRightToLeft)
1132 {
1133 d->rightToLeft = isRightToLeft;
1134 update();
1135 }
1136
setShowIndents(bool show)1137 void KReportRuler::setShowIndents(bool show)
1138 {
1139 d->showIndents = show;
1140 update();
1141 }
1142
setFirstLineIndent(qreal indent)1143 void KReportRuler::setFirstLineIndent(qreal indent)
1144 {
1145 d->firstLineIndent = indent;
1146 if (d->showIndents) {
1147 update();
1148 }
1149 }
1150
setParagraphIndent(qreal indent)1151 void KReportRuler::setParagraphIndent(qreal indent)
1152 {
1153 d->paragraphIndent = indent;
1154 if (d->showIndents) {
1155 update();
1156 }
1157 }
1158
setEndIndent(qreal indent)1159 void KReportRuler::setEndIndent(qreal indent)
1160 {
1161 d->endIndent = indent;
1162 if (d->showIndents) {
1163 update();
1164 }
1165 }
1166
firstLineIndent() const1167 qreal KReportRuler::firstLineIndent() const
1168 {
1169 return d->firstLineIndent;
1170 }
1171
paragraphIndent() const1172 qreal KReportRuler::paragraphIndent() const
1173 {
1174 return d->paragraphIndent;
1175 }
1176
endIndent() const1177 qreal KReportRuler::endIndent() const
1178 {
1179 return d->endIndent;
1180 }
1181
tabChooser()1182 QWidget *KReportRuler::tabChooser()
1183 {
1184 if ((d->tabChooser == nullptr) && (d->orientation == Qt::Horizontal)) {
1185 d->tabChooser = new RulerTabChooser(parentWidget());
1186 d->tabChooser->setShowTabs(d->showTabs);
1187 }
1188
1189 return d->tabChooser;
1190 }
1191
setShowSelectionBorders(bool show)1192 void KReportRuler::setShowSelectionBorders(bool show)
1193 {
1194 d->showSelectionBorders = show;
1195 update();
1196 }
1197
updateSelectionBorders(qreal first,qreal second)1198 void KReportRuler::updateSelectionBorders(qreal first, qreal second)
1199 {
1200 d->firstSelectionBorder = first;
1201 d->secondSelectionBorder = second;
1202
1203 if(d->showSelectionBorders)
1204 update();
1205 }
1206
setShowTabs(bool show)1207 void KReportRuler::setShowTabs(bool show)
1208 {
1209 if (d->showTabs == show) {
1210 return;
1211 }
1212
1213 d->showTabs = show;
1214 if (d->tabChooser) {
1215 d->tabChooser->setShowTabs(show);
1216 }
1217 update();
1218 }
1219
setRelativeTabs(bool relative)1220 void KReportRuler::setRelativeTabs(bool relative)
1221 {
1222 d->relativeTabs = relative;
1223 if (d->showTabs) {
1224 update();
1225 }
1226 }
1227
updateTabs(const QList<KReportRuler::Tab> & tabs,qreal tabDistance)1228 void KReportRuler::updateTabs(const QList<KReportRuler::Tab> &tabs, qreal tabDistance)
1229 {
1230 d->tabs = tabs;
1231 d->tabDistance = tabDistance;
1232 if (d->showTabs) {
1233 update();
1234 }
1235 }
1236
tabs() const1237 QList<KReportRuler::Tab> KReportRuler::tabs() const
1238 {
1239 QList<Tab> answer = d->tabs;
1240 qSort(answer.begin(), answer.end(), compareTabs);
1241
1242 return answer;
1243 }
1244
setPopupActionList(const QList<QAction * > & popupActionList)1245 void KReportRuler::setPopupActionList(const QList<QAction*> &popupActionList)
1246 {
1247 d->popupActions = popupActionList;
1248 }
1249
popupActionList() const1250 QList<QAction*> KReportRuler::popupActionList() const
1251 {
1252 return d->popupActions;
1253 }
1254
mousePressEvent(QMouseEvent * ev)1255 void KReportRuler::mousePressEvent ( QMouseEvent* ev )
1256 {
1257 d->tabMoved = false;
1258 d->selected = KReportRuler::Private::Selection::None;
1259 if (ev->button() == Qt::RightButton && !d->popupActions.isEmpty())
1260 QMenu::exec(d->popupActions, ev->globalPos());
1261 if (ev->button() != Qt::LeftButton) {
1262 ev->ignore();
1263 return;
1264 }
1265
1266 QPoint pos = ev->pos();
1267
1268 if (d->showTabs) {
1269 int i = 0;
1270 int x;
1271 foreach (const Tab & t, d->tabs) {
1272 if (d->rightToLeft) {
1273 x = d->viewConverter->documentToViewX(d->effectiveActiveRangeEnd()
1274 - (d->relativeTabs ? d->paragraphIndent : 0) - t.position) + d->offset;
1275 } else {
1276 x = d->viewConverter->documentToViewX(d->effectiveActiveRangeStart()
1277 + (d->relativeTabs ? d->paragraphIndent : 0) + t.position) + d->offset;
1278 }
1279 if (pos.x() >= x-6 && pos.x() <= x+6) {
1280 d->selected = KReportRuler::Private::Selection::Tab;
1281 d->selectOffset = x - pos.x();
1282 d->currentIndex = i;
1283 break;
1284 }
1285 i++;
1286 }
1287 d->originalIndex = d->currentIndex;
1288 }
1289
1290 if (d->selected == KReportRuler::Private::Selection::None)
1291 d->selected = d->selectionAtPosition(ev->pos(), &d->selectOffset);
1292 if (d->selected == KReportRuler::Private::Selection::None) {
1293 int hotSpotIndex = d->hotSpotIndex(ev->pos());
1294 if (hotSpotIndex >= 0) {
1295 d->selected = KReportRuler::Private::Selection::HotSpot;
1296 update();
1297 }
1298 }
1299
1300 if (d->showTabs && d->selected == KReportRuler::Private::Selection::None) {
1301 // still haven't found something so let assume the user wants to add a tab
1302 qreal tabpos;
1303 if (d->rightToLeft) {
1304 tabpos = d->viewConverter->viewToDocumentX(pos.x() - d->offset)
1305 + d->effectiveActiveRangeEnd() + (d->relativeTabs ? d->paragraphIndent : 0);
1306 } else {
1307 tabpos = d->viewConverter->viewToDocumentX(pos.x() - d->offset)
1308 - d->effectiveActiveRangeStart() - (d->relativeTabs ? d->paragraphIndent : 0);
1309 }
1310 Tab t(tabpos, d->tabChooser ? d->tabChooser->type() :
1311 d->rightToLeft ? QTextOption::RightTab :
1312 QTextOption::LeftTab);
1313 d->tabs.append(t);
1314 d->selectOffset = 0;
1315 d->selected = KReportRuler::Private::Selection::Tab;
1316 d->currentIndex = d->tabs.count() - 1;
1317 d->originalIndex = -1; // new!
1318 update();
1319 }
1320 if (d->orientation == Qt::Horizontal && (ev->modifiers() & Qt::ShiftModifier) &&
1321 (d->selected == KReportRuler::Private::Selection::FirstLineIndent ||
1322 d->selected == KReportRuler::Private::Selection::ParagraphIndent ||
1323 d->selected == KReportRuler::Private::Selection::Tab ||
1324 d->selected == KReportRuler::Private::Selection::EndIndent))
1325 d->paintingStrategy = d->distancesPaintingStrategy;
1326
1327 if (d->selected != KReportRuler::Private::Selection::None)
1328 emit aboutToChange();
1329 }
1330
mouseReleaseEvent(QMouseEvent * ev)1331 void KReportRuler::mouseReleaseEvent ( QMouseEvent* ev )
1332 {
1333 ev->accept();
1334 if (d->selected == KReportRuler::Private::Selection::Tab) {
1335 if (d->originalIndex >= 0 && !d->tabMoved) {
1336 int type = d->tabs[d->currentIndex].type;
1337 type++;
1338 if (type > 3)
1339 type = 0;
1340 d->tabs[d->currentIndex].type = static_cast<QTextOption::TabType> (type);
1341 update();
1342 }
1343 d->emitTabChanged();
1344 }
1345 else if( d->selected != KReportRuler::Private::Selection::None)
1346 emit indentsChanged(true);
1347 else
1348 ev->ignore();
1349
1350 d->paintingStrategy = d->normalPaintingStrategy;
1351 d->selected = KReportRuler::Private::Selection::None;
1352 }
1353
mouseMoveEvent(QMouseEvent * ev)1354 void KReportRuler::mouseMoveEvent ( QMouseEvent* ev )
1355 {
1356 QPoint pos = ev->pos();
1357
1358 qreal activeLength = d->effectiveActiveRangeEnd() - d->effectiveActiveRangeStart();
1359
1360 switch (d->selected) {
1361 case KReportRuler::Private::Selection::FirstLineIndent:
1362 if (d->rightToLeft)
1363 d->firstLineIndent = d->effectiveActiveRangeEnd() - d->paragraphIndent -
1364 d->viewConverter->viewToDocumentX(pos.x() + d->selectOffset - d->offset);
1365 else
1366 d->firstLineIndent = d->viewConverter->viewToDocumentX(pos.x() + d->selectOffset
1367 - d->offset) - d->effectiveActiveRangeStart() - d->paragraphIndent;
1368 if( ! (ev->modifiers() & Qt::ShiftModifier)) {
1369 d->firstLineIndent = d->doSnapping(d->firstLineIndent);
1370 d->paintingStrategy = d->normalPaintingStrategy;
1371 } else {
1372 if (d->orientation == Qt::Horizontal)
1373 d->paintingStrategy = d->distancesPaintingStrategy;
1374 }
1375
1376 emit indentsChanged(false);
1377 break;
1378 case KReportRuler::Private::Selection::ParagraphIndent:
1379 if (d->rightToLeft)
1380 d->paragraphIndent = d->effectiveActiveRangeEnd() -
1381 d->viewConverter->viewToDocumentX(pos.x() + d->selectOffset - d->offset);
1382 else
1383 d->paragraphIndent = d->viewConverter->viewToDocumentX(pos.x() + d->selectOffset
1384 - d->offset) - d->effectiveActiveRangeStart();
1385 if( ! (ev->modifiers() & Qt::ShiftModifier)) {
1386 d->paragraphIndent = d->doSnapping(d->paragraphIndent);
1387 d->paintingStrategy = d->normalPaintingStrategy;
1388 } else {
1389 if (d->orientation == Qt::Horizontal)
1390 d->paintingStrategy = d->distancesPaintingStrategy;
1391 }
1392
1393 if (d->paragraphIndent + d->endIndent > activeLength)
1394 d->paragraphIndent = activeLength - d->endIndent;
1395 emit indentsChanged(false);
1396 break;
1397 case KReportRuler::Private::Selection::EndIndent:
1398 if (d->rightToLeft)
1399 d->endIndent = d->viewConverter->viewToDocumentX(pos.x()
1400 + d->selectOffset - d->offset) - d->effectiveActiveRangeStart();
1401 else
1402 d->endIndent = d->effectiveActiveRangeEnd() - d->viewConverter->viewToDocumentX(pos.x()
1403 + d->selectOffset - d->offset);
1404 if (!(ev->modifiers() & Qt::ShiftModifier)) {
1405 d->endIndent = d->doSnapping(d->endIndent);
1406 d->paintingStrategy = d->normalPaintingStrategy;
1407 } else {
1408 if (d->orientation == Qt::Horizontal)
1409 d->paintingStrategy = d->distancesPaintingStrategy;
1410 }
1411
1412 if (d->paragraphIndent + d->endIndent > activeLength)
1413 d->endIndent = activeLength - d->paragraphIndent;
1414 emit indentsChanged(false);
1415 break;
1416 case KReportRuler::Private::Selection::Tab:
1417 d->tabMoved = true;
1418 if (d->currentIndex < 0) { // tab is deleted.
1419 if (ev->pos().y() < height()) { // reinstante it.
1420 d->currentIndex = d->tabs.count();
1421 d->tabs.append(d->deletedTab);
1422 } else {
1423 break;
1424 }
1425 }
1426 if (d->rightToLeft)
1427 d->tabs[d->currentIndex].position = d->effectiveActiveRangeEnd() -
1428 d->viewConverter->viewToDocumentX(pos.x() + d->selectOffset - d->offset);
1429 else
1430 d->tabs[d->currentIndex].position = d->viewConverter->viewToDocumentX(pos.x() + d->selectOffset
1431 - d->offset) - d->effectiveActiveRangeStart();
1432 if (!(ev->modifiers() & Qt::ShiftModifier))
1433 d->tabs[d->currentIndex].position = d->doSnapping(d->tabs[d->currentIndex].position);
1434 if (d->tabs[d->currentIndex].position < 0)
1435 d->tabs[d->currentIndex].position = 0;
1436 if (d->tabs[d->currentIndex].position > activeLength)
1437 d->tabs[d->currentIndex].position = activeLength;
1438
1439 if (ev->pos().y() > height() + OutsideRulerThreshold ) { // moved out of the ruler, delete it.
1440 d->deletedTab = d->tabs.takeAt(d->currentIndex);
1441 d->currentIndex = -1;
1442 // was that a temporary added tab?
1443 if ( d->originalIndex == -1 )
1444 emit guideLineCreated(d->orientation,
1445 d->orientation == Qt::Horizontal
1446 ? d->viewConverter->viewToDocumentY(ev->pos().y())
1447 : d->viewConverter->viewToDocumentX(ev->pos().x()));
1448 }
1449
1450 d->emitTabChanged();
1451 break;
1452 case KReportRuler::Private::Selection::HotSpot:
1453 qreal newPos;
1454 if (d->orientation == Qt::Horizontal)
1455 newPos= d->viewConverter->viewToDocumentX(pos.x() - d->offset);
1456 else
1457 newPos= d->viewConverter->viewToDocumentY(pos.y() - d->offset);
1458 d->hotspots[d->currentIndex].position = newPos;
1459 emit hotSpotChanged(d->hotspots[d->currentIndex].id, newPos);
1460 break;
1461 case KReportRuler::Private::Selection::None:
1462 d->mouseCoordinate = (d->orientation == Qt::Horizontal ? pos.x() : pos.y()) - d->offset;
1463 int hotSpotIndex = d->hotSpotIndex(pos);
1464 if (hotSpotIndex >= 0) {
1465 setCursor(QCursor( d->orientation == Qt::Horizontal ? Qt::SplitHCursor : Qt::SplitVCursor ));
1466 break;
1467 }
1468 unsetCursor();
1469
1470 KReportRuler::Private::Selection selection = d->selectionAtPosition(pos);
1471 QString text;
1472 switch(selection) {
1473 case KReportRuler::Private::Selection::FirstLineIndent: text = tr("First line indent"); break;
1474 case KReportRuler::Private::Selection::ParagraphIndent: text = tr("Left indent"); break;
1475 case KReportRuler::Private::Selection::EndIndent: text = tr("Right indent"); break;
1476 case KReportRuler::Private::Selection::None:
1477 if (ev->buttons() & Qt::LeftButton) {
1478 if (d->orientation == Qt::Horizontal && ev->pos().y() > height() + OutsideRulerThreshold)
1479 emit guideLineCreated(d->orientation, d->viewConverter->viewToDocumentY(ev->pos().y()));
1480 else if (d->orientation == Qt::Vertical && ev->pos().x() > width() + OutsideRulerThreshold)
1481 emit guideLineCreated(d->orientation, d->viewConverter->viewToDocumentX(ev->pos().x()));
1482 }
1483 break;
1484 default:
1485 break;
1486 }
1487 setToolTip(text);
1488 }
1489 update();
1490 }
1491
clearHotSpots()1492 void KReportRuler::clearHotSpots()
1493 {
1494 if (d->hotspots.isEmpty())
1495 return;
1496 d->hotspots.clear();
1497 update();
1498 }
1499
setHotSpot(qreal position,int id)1500 void KReportRuler::setHotSpot(qreal position, int id)
1501 {
1502 int hotspotCount = d->hotspots.count();
1503 for (int i = 0; i < hotspotCount; ++i) {
1504 KReportRuler::Private::HotSpotData & hs = d->hotspots[i];
1505 if (hs.id == id) {
1506 hs.position = position;
1507 update();
1508 return;
1509 }
1510 }
1511 // not there yet, then insert it.
1512 KReportRuler::Private::HotSpotData hs;
1513 hs.position = position;
1514 hs.id = id;
1515 d->hotspots.append(hs);
1516 }
1517
removeHotSpot(int id)1518 bool KReportRuler::removeHotSpot(int id)
1519 {
1520 QList<KReportRuler::Private::HotSpotData>::Iterator iter = d->hotspots.begin();
1521 while(iter != d->hotspots.end()) {
1522 if (iter->id == id) {
1523 d->hotspots.erase(iter);
1524 update();
1525 return true;
1526 }
1527 }
1528 return false;
1529 }
1530