1 /*
2 SPDX-FileCopyrightText: 2004-05 Enrico Ros <eros.kde@email.it>
3 SPDX-FileCopyrightText: 2005 Piotr Szymanski <niedakh@gmail.com>
4 SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7 #ifndef _OKULAR_AREA_H_
8 #define _OKULAR_AREA_H_
9
10 #include <math.h>
11
12 #include <QColor>
13 #include <QDebug>
14 #include <QList>
15 #include <QPainterPath>
16 #include <QTransform>
17
18 #include "global.h"
19 #include "okularcore_export.h"
20
21 class QPolygonF;
22 class QRect;
23
24 namespace Okular
25 {
26 class Annotation;
27 class Action;
28 class NormalizedShape;
29
30 /**
31 * NormalizedPoint is a helper class which stores the coordinates
32 * of a normalized point.
33 *
34 * @par Normalized Coordinate System
35 * @parblock
36 * Normalized means that the coordinates are always between 0 and 1,
37 * unless the point shall be outside of the reference area.
38 *
39 * The reference area is a rectangle, and all normalized points
40 * with coordinates of 0 or 1 describe its edges.
41 *
42 * This allows to locate things on a reference area without knowing its
43 * (current or future) actual size. When the reference area is resized,
44 * all things which are described in normalized coordinates keep their
45 * proportional position on the area.
46 * @endparblock
47 *
48 * @par Transformation to and from Normalized Coordinates
49 * @parblock
50 * To transform normalized coordinates to coordinates on the reference area,
51 * just multiply them with the size of the reference area.
52 *
53 * To get normalized coordinates from a point on the reference area,
54 * just divide its coordinates with the size of the reference area.
55 *
56 * Many methods have parameters @c xScale and @c yScale,
57 * these are equal to the size of the reference area.
58 * @endparblock
59 *
60 * @par Normalized Coordinate System Applied to Pages
61 * @parblock
62 * Okular uses a normalized coordinate system mainly to describe
63 * positions on pages.
64 * This is useful because pages can be shown in different sizes (zoom),
65 * but all objects shall keep their proportional position on the page.
66 *
67 * Okular maps from page to normalized coordinates as follows:
68 * * Left edge of the page: x = 0
69 * * Right edge of the page: x = 1
70 * * Top edge of the page: y = 0
71 * * Bottom edge of the page: y = 1
72 * @endparblock
73 *
74 * @par Example: Draw a Point on a Page
75 * @parblock
76 * The point is given in normalized coordinates (0.5, 0.3).
77 *
78 * If you want to draw it on a 800x600 page,
79 * just multiply the x coordinate (0.5) with the page width (800),
80 * and the y coordinate (0.3) with the page height (600).
81 * So, the point will be drawn on the page at (400, 180).
82 *
83 * That allows you to zoom the page by just multiplying the normalized points with the
84 * zoomed page size.
85 * @endparblock
86 *
87 * @par Example: Select Text on a Page using Mouse Events
88 * @parblock
89 * The position of all glyphs and words is stored in normalized coordinates.
90 * (This is what TextPage actually does.)
91 * Mouse press and release events are given in page coordinates (400, 180) and (600, 450),
92 * while the page has a size of 800x600.
93 *
94 * If you want to search all text between the mouse click and release event,
95 * you need their normalized coordinates.
96 * Just divide the x coordinates (400 and 600) by the page width (800),
97 * and the y coordinates (180 and 450) by the page height (600).
98 * So, you have to search for all glyphs between (0.5, 0.3) and (0.75, 0.75).
99 *
100 * That allows you to process all glyphs and words without
101 * having to keep any of their positions in sync with the page.
102 * @endparblock
103 *
104 * @par Geometric operations
105 * @parblock
106 * NormalizedPoint supports basic geometric operations.
107 * * You can transform it with a QTransform matrix.
108 * * With the size of the reference area, you can calculate the squared
109 * absolute distance to another NormalizedPoint or a line of two NormalizedPoints.
110 *
111 * NormalizedRect provides additional geometric operations for rectangles.
112 * @endparblock
113 *
114 * @see NormalizedRect
115 */
116 class OKULARCORE_EXPORT NormalizedPoint
117 {
118 public:
119 /**
120 * Creates a normalized point at (0, 0).
121 */
122 NormalizedPoint();
123
124 /**
125 * Creates a new normalized point with the normalized coordinates (@p x, @p y ).
126 */
127 NormalizedPoint(double x, double y);
128
129 /**
130 * Creates a new normalized point from an absolute point (@p x, @p y)
131 * on a reference area of size @p xScale x @p yScale.
132 */
133 NormalizedPoint(int x, int y, int xScale, int yScale);
134
135 /**
136 * @internal
137 */
138 NormalizedPoint &operator=(const NormalizedPoint &);
139
140 NormalizedPoint(const NormalizedPoint &);
141 // TODO next ABI break, move the = default to here
142 ~NormalizedPoint(); // NOLINT(performance-trivially-destructible)
143
144 /**
145 * Transforms the normalized point with the operations defined by @p matrix.
146 */
147 void transform(const QTransform &matrix);
148
149 /**
150 * Returns squared distance to normalized point (@p x, @p y)
151 * on a reference area of size @p xScale x @p yScale.
152 * @since 0.17 (KDE 4.11)
153 */
154 double distanceSqr(double x, double y, double xScale, double yScale) const;
155
156 /**
157 * Returns squared distance of the normalized point (@p x, @p y)
158 * to the line segment from @p start to @p end
159 * on a reference area of size @p xScale x @p yScale.
160 * @since 0.17 (KDE 4.11)
161 */
162 static double distanceSqr(double x, double y, double xScale, double yScale, const NormalizedPoint &start, const NormalizedPoint &end);
163
164 /**
165 * The normalized x coordinate.
166 */
167 double x;
168
169 /**
170 * The normalized y coordinate.
171 */
172 double y;
173 };
174
175 /**
176 * A NormalizedRect is a rectangle which can be defined by two NormalizedPoints.
177 *
178 * It describes a rectangular area on a reference area of undefined size.
179 * For more information about the normalized coordinate system, see NormalizedPoint.
180 *
181 * In Okular, NormalizedRect can be used e. g. to describe bounding boxes of TextEntity objects,
182 * and the highlight area of text selections.
183 *
184 * If you need to describe an area which consists of multiple rectangles,
185 * you can use RegularAreaRect instead.
186 *
187 * @see NormalizedPoint, RegularAreaRect, TextEntity
188 */
189 class OKULARCORE_EXPORT NormalizedRect
190 {
191 public:
192 /**
193 * Creates a null normalized rectangle.
194 * @see isNull()
195 */
196 NormalizedRect();
197
198 /**
199 * Creates a normalized rectangle with the normalized coordinates
200 * @p left, @p top, @p right, @p bottom.
201 *
202 * If you need the x, y, width and height coordinates use the
203 * following formulas:
204 *
205 * @li x = left
206 * @li y = top
207 * @li width = right - left
208 * @li height = bottom - top
209 *
210 * @note
211 * The coordinates for @p left and @p top should be lower than
212 * @p right and @p bottom, respectively.
213 * At negative width or height the behaviour of some operations is undefined.
214 */
215 NormalizedRect(double left, double top, double right, double bottom);
216
217 /**
218 * Creates a normalized rectangle from the given @p rectangle
219 * on a reference area of size @p xScale x @p yScale.
220 *
221 * @note
222 * The rectangle should have positive width and height.
223 * You can use e. g. QRect::normalize() to ensure this.
224 * At negative width or height the behaviour of some operations is undefined.
225 */
226 NormalizedRect(const QRect &rectangle, double xScale, double yScale);
227
228 /**
229 * @internal
230 */
231 NormalizedRect(const NormalizedRect &);
232
233 /**
234 * @internal
235 */
236 NormalizedRect &operator=(const NormalizedRect &other);
237
238 // TODO next ABI break, move the = default to here
239 ~NormalizedRect(); // NOLINT(performance-trivially-destructible)
240
241 /**
242 * Build a normalized rect from a QRectF, which already has normalized coordinates.
243 */
244 static NormalizedRect fromQRectF(const QRectF &rect);
245
246 /**
247 * Returns whether this normalized rectangle is a null normalized rect.
248 */
249 bool isNull() const;
250
251 /**
252 * Returns whether the normalized rectangle contains the normalized point
253 * (@p x, @p y).
254 */
255 bool contains(double x, double y) const;
256
257 /**
258 * Returns whether the normalized rectangle intersects the @p other normalized
259 * rectangle.
260 */
261 bool intersects(const NormalizedRect &other) const;
262
263 /**
264 * This is an overloaded member function, provided for convenience. It behaves essentially
265 * like the above function.
266 */
267 bool intersects(const NormalizedRect *other) const;
268
269 /**
270 * Returns whether the normalized rectangle intersects an other normalized
271 * rectangle, which is defined by @p left, @p top, @p right and @p bottom.
272 */
273 bool intersects(double left, double top, double right, double bottom) const;
274
275 /**
276 * Returns the rectangle mapped to a reference area of @p xScale x @p yScale.
277 */
278 QRect geometry(int xScale, int yScale) const;
279
280 /**
281 * Same functionality as geometry, but the output is now rounded before typecasting to int
282 *
283 * @since 0.14 (KDE 4.8)
284 */
285 QRect roundedGeometry(int xScale, int yScale) const;
286
287 /**
288 * Returns the normalized bounding rectangle of the normalized rectangle
289 * combined with the @p other normalized rectangle.
290 */
291 NormalizedRect operator|(const NormalizedRect &other) const;
292
293 /**
294 * Sets the normalized rectangle to the normalized bounding rectangle
295 * of itself combined with the @p other normalized rectangle.
296 */
297 NormalizedRect &operator|=(const NormalizedRect &other);
298
299 /**
300 * Returns the intersection of this normalized rectangle with the specified
301 * @p other. If the rects do not intersect then the result is a null rectangle.
302 *
303 * @since 0.7 (KDE 4.1)
304 */
305 NormalizedRect operator&(const NormalizedRect &other) const;
306
307 /**
308 * Returns whether the normalized rectangle is equal to the @p other
309 * normalized rectangle.
310 */
311 bool operator==(const NormalizedRect &other) const;
312
313 /**
314 * Returns the center of the rectangle
315 * @since 0.10 (KDE 4.4)
316 */
317 NormalizedPoint center() const;
318
319 /**
320 * Transforms the normalized rectangle with the operations defined by @p matrix.
321 */
322 void transform(const QTransform &matrix);
323
324 /**
325 * Returns true if the point @p pt is located below the bottom of the rectangle
326 * @since 0.14 (KDE 4.8)
327 */
isBottom(const NormalizedPoint & pt)328 bool isBottom(const NormalizedPoint &pt) const
329 {
330 return bottom < pt.y;
331 }
332
333 /**
334 * Returns true if the point @p pt is located above the top of the rectangle
335 * @since 0.14 (KDE 4.8)
336 */
isTop(const NormalizedPoint & pt)337 bool isTop(const NormalizedPoint &pt) const
338 {
339 return top > pt.y;
340 }
341
342 /**
343 * Returns true if the point @p pt is located below the top of the rectangle
344 * @since 0.14 (KDE 4.8)
345 */
isBottomOrLevel(const NormalizedPoint & pt)346 bool isBottomOrLevel(const NormalizedPoint &pt) const
347 {
348 return top < pt.y;
349 }
350
351 /**
352 * Returns true if the point @p pt is located above the bottom of the rectangle
353 * @since 0.14 (KDE 4.8)
354 */
isTopOrLevel(const NormalizedPoint & pt)355 bool isTopOrLevel(const NormalizedPoint &pt) const
356 {
357 return bottom > pt.y;
358 }
359
360 /**
361 * Returns true if the point @p pt is located to the right of the left edge of the rectangle
362 * @since 0.14 (KDE 4.8)
363 */
isLeft(const NormalizedPoint & pt)364 bool isLeft(const NormalizedPoint &pt) const
365 {
366 return left < pt.x;
367 }
368
369 /**
370 * Returns true if the point @p pt is located to the left of the right edge of the rectangle
371 * @since 0.14 (KDE 4.8)
372 */
isRight(const NormalizedPoint & pt)373 bool isRight(const NormalizedPoint &pt) const
374 {
375 return right > pt.x;
376 }
377
378 /**
379 * Returns the squared distance of the normalized point (@p x, @p y)
380 * to the closest edge, or 0 if the point is within the rectangle;
381 * using a reference area of size @p xScale x @p yScale
382 * @since 0.17 (KDE 4.11)
383 */
distanceSqr(double x,double y,double xScale,double yScale)384 double distanceSqr(double x, double y, double xScale, double yScale) const
385 {
386 double distX = 0;
387 if (x < left)
388 distX = left - x;
389 else if (x > right)
390 distX = x - right;
391
392 double distY = 0;
393 if (top > y)
394 distY = top - y;
395 else if (bottom < y)
396 distY = y - bottom;
397 return pow(distX * xScale, 2) + pow(distY * yScale, 2);
398 }
399
400 /// @since 1.4
width()401 double width() const
402 {
403 return right - left;
404 }
405
406 /// @since 1.4
height()407 double height() const
408 {
409 return bottom - top;
410 }
411
412 /**
413 * The normalized left coordinate.
414 */
415 double left;
416
417 /**
418 * The normalized top coordinate.
419 */
420 double top;
421
422 /**
423 * The normalized right coordinate.
424 */
425 double right;
426
427 /**
428 * The normalized bottom coordinate.
429 */
430 double bottom;
431 };
432 // KDE_DUMMY_QHASH_FUNCTION(NormalizedRect)
433
434 /**
435 * @short An area with normalized coordinates that contains a reference to an object.
436 *
437 * These areas ("rects") contain a pointer to a document object
438 * (such as a hyperlink, an action, or something like that).
439 * The pointer is read and stored as 'void pointer' so cast is
440 * performed by accessors based on the value returned by objectType(). Objects
441 * are reparented to this class.
442 *
443 * Type / Class correspondence tab:
444 * - Action : class Action: description of an action
445 * - Image : class Image : description of an image (n/a)
446 * - Annotation: class Annotation: description of an annotation
447 *
448 * For more information about the normalized coordinate system, see NormalizedPoint.
449 *
450 * @see NormalizedPoint
451 */
452 class OKULARCORE_EXPORT ObjectRect
453 {
454 public:
455 /**
456 * Describes the type of storable object.
457 */
458 enum ObjectType {
459 Action, ///< An action
460 Image, ///< An image
461 OAnnotation, ///< An annotation
462 SourceRef ///< A source reference
463 };
464
465 /**
466 * Creates a new object rectangle.
467 *
468 * @param left The left coordinate of the rectangle.
469 * @param top The top coordinate of the rectangle.
470 * @param right The right coordinate of the rectangle.
471 * @param bottom The bottom coordinate of the rectangle.
472 * @param ellipse If true the rectangle describes an ellipse.
473 * @param type The type of the storable object @see ObjectType.
474 * @param object The pointer to the storable object.
475 */
476 ObjectRect(double left, double top, double right, double bottom, bool ellipse, ObjectType type, void *object);
477
478 /**
479 * This is an overloaded member function, provided for convenience.
480 */
481 ObjectRect(const NormalizedRect &r, bool ellipse, ObjectType type, void *object);
482
483 /**
484 * This is an overloaded member function, provided for convenience.
485 */
486 ObjectRect(const QPolygonF &poly, ObjectType type, void *object);
487
488 /**
489 * Destroys the object rectangle.
490 */
491 virtual ~ObjectRect();
492
493 ObjectRect(const ObjectRect &o) = delete;
494 ObjectRect &operator=(const ObjectRect &o) = delete;
495
496 /**
497 * Returns the object type of the object rectangle.
498 * @see ObjectType
499 */
500 ObjectType objectType() const;
501
502 /**
503 * Returns the storable object of the object rectangle.
504 */
505 const void *object() const;
506
507 /**
508 * Returns the region that is covered by the object rectangle.
509 */
510 const QPainterPath ®ion() const;
511
512 /**
513 * Returns the bounding rect of the object rectangle for the
514 * scaling factor @p xScale and @p yScale.
515 */
516 virtual QRect boundingRect(double xScale, double yScale) const;
517
518 /**
519 * Returns whether the object rectangle contains the point with absolute coordinates
520 * (@p x, @p y) at a page size of @p xScale x @p yScale.
521 */
522 virtual bool contains(double x, double y, double xScale, double yScale) const;
523
524 /**
525 * Transforms the object rectangle with the operations defined by @p matrix.
526 */
527 virtual void transform(const QTransform &matrix);
528
529 /**
530 * Returns the squared distance between the object
531 * and the point with
532 * normalized coordinates (@p x, @p y)
533 * at a page size of @p xScale x @p yScale.
534 *
535 * @since 0.8.2 (KDE 4.2.2)
536 */
537 // FIXME this should most probably be a virtual method
538 double distanceSqr(double x, double y, double xScale, double yScale) const;
539
540 protected:
541 ObjectType m_objectType;
542 void *m_object;
543 QPainterPath m_path;
544 QPainterPath m_transformedPath;
545 };
546
547 /**
548 * This class describes the object rectangle for an annotation.
549 */
550 class OKULARCORE_EXPORT AnnotationObjectRect : public ObjectRect
551 {
552 public:
553 /**
554 * Creates a new annotation object rectangle with the
555 * given @p annotation.
556 */
557 explicit AnnotationObjectRect(Annotation *annotation);
558
559 /**
560 * Destroys the annotation object rectangle.
561 */
562 ~AnnotationObjectRect() override;
563
564 /**
565 * Returns the annotation object of the annotation object rectangle.
566 */
567 Annotation *annotation() const;
568
569 /**
570 * Returns the bounding rect of the annotation object rectangle for the
571 * scaling factor @p xScale and @p yScale.
572 */
573 QRect boundingRect(double xScale, double yScale) const override;
574
575 /**
576 * Returns whether the annotation object rectangle contains the point @p x, @p y for the
577 * scaling factor @p xScale and @p yScale.
578 */
579 bool contains(double x, double y, double xScale, double yScale) const override;
580
581 /**
582 * Transforms the annotation object rectangle with the operations defined by @p matrix.
583 */
584 void transform(const QTransform &matrix) override;
585
586 private:
587 Annotation *m_annotation;
588 };
589
590 /**
591 * This class describes the object rectangle for a source reference.
592 */
593 class OKULARCORE_EXPORT SourceRefObjectRect : public ObjectRect
594 {
595 friend class ObjectRect;
596
597 public:
598 /**
599 * Creates a new source reference object rectangle.
600 *
601 * @param point The point of the source reference.
602 * @param srcRef The storable source reference object.
603 */
604 SourceRefObjectRect(const NormalizedPoint &point, void *srcRef);
605
606 /**
607 * Returns the bounding rect of the source reference object rectangle for the
608 * scaling factor @p xScale and @p yScale.
609 */
610 QRect boundingRect(double xScale, double yScale) const override;
611
612 /**
613 * Returns whether the source reference object rectangle contains the point @p x, @p y for the
614 * scaling factor @p xScale and @p yScale.
615 */
616 bool contains(double x, double y, double xScale, double yScale) const override;
617
618 private:
619 NormalizedPoint m_point;
620 };
621
622 /**
623 * This class is an object rect that doesn't own the given pointer, i.e. won't delete it on destruction
624 * @since 1.7
625 */
626 class OKULARCORE_EXPORT NonOwningObjectRect : public ObjectRect
627 {
628 public:
629 NonOwningObjectRect(double left, double top, double right, double bottom, bool ellipse, ObjectType type, void *object);
630 ~NonOwningObjectRect() override;
631 };
632
633 /// @cond PRIVATE
634 /** @internal */
635 /** @internal */
givePtr(T & t)636 template<typename T> T *givePtr(T &t)
637 {
638 return &t;
639 }
640
641 /** @internal */
deref(T & t)642 template<typename T> T &deref(T &t)
643 {
644 return t;
645 }
646 /// @endcond
647
648 /**
649 * @short An area with normalized coordinates, consisting of NormalizedShape objects.
650 *
651 * This is a template class to describe an area which consists of
652 * multiple shapes of the same type, intersecting or non-intersecting.
653 * The coordinates are normalized, and can be mapped to a reference area of defined size.
654 * For more information about the normalized coordinate system, see NormalizedPoint.
655 *
656 * Class NormalizedShape \b must have the following functions/operators defined:
657 * - bool contains( double, double ), whether it contains the given NormalizedPoint
658 * - bool intersects( NormalizedShape )
659 * - bool isNull()
660 * - Shape geometry( int, int ), which maps to the reference area
661 * - operator|=( NormalizedShape ), which unites two NormalizedShape's
662 *
663 * @see RegularAreaRect, NormalizedPoint
664 */
665 template<class NormalizedShape, class Shape> class RegularArea : public QList<NormalizedShape>
666 {
667 public:
668 /**
669 * Returns whether this area contains the normalized point (@p x, @p y).
670 */
671 bool contains(double x, double y) const;
672
673 /**
674 * Returns whether this area contains a NormalizedShape object that equals @p shape.
675 *
676 * @note
677 * The original NormalizedShape objects can be lost if simplify() was called.
678 */
679 bool contains(const NormalizedShape &shape) const;
680
681 /**
682 * Returns whether this area intersects with the given @p area.
683 */
684 bool intersects(const RegularArea<NormalizedShape, Shape> *area) const;
685
686 /**
687 * Returns whether the regular area intersects with the given @p shape.
688 */
689 bool intersects(const NormalizedShape &shape) const;
690
691 /**
692 * Appends the given @p area to this area.
693 */
694 void appendArea(const RegularArea<NormalizedShape, Shape> *area);
695
696 /**
697 * Appends the given @p shape to this area.
698 */
699 void appendShape(const NormalizedShape &shape, MergeSide side = MergeAll);
700
701 /**
702 * Simplifies this regular area by merging its intersecting subareas.
703 * This might change the effective geometry of this area.
704 */
705 void simplify();
706
707 /**
708 * Returns whether the regular area is a null area.
709 */
710 bool isNull() const;
711
712 /**
713 * Returns the subareas of this regular area
714 * mapped to a reference area of size @p xScale x @p yScale,
715 * then translated by @p dx and @p dy.
716 */
717 QList<Shape> geometry(int xScale, int yScale, int dx = 0, int dy = 0) const;
718
719 /**
720 * Transforms the regular area with the operations defined by @p matrix.
721 */
722 void transform(const QTransform &matrix);
723 };
724
simplify()725 template<class NormalizedShape, class Shape> void RegularArea<NormalizedShape, Shape>::simplify()
726 {
727 #ifdef DEBUG_REGULARAREA
728 int prev_end = this->count();
729 #endif
730 int end = this->count() - 1, x = 0;
731 for (int i = 0; i < end; ++i) {
732 if (givePtr((*this)[x])->intersects(deref((*this)[i + 1]))) {
733 deref((*this)[x]) |= deref((*this)[i + 1]);
734 this->removeAt(i + 1);
735 --end;
736 --i;
737 } else {
738 x = i + 1;
739 }
740 }
741 #ifdef DEBUG_REGULARAREA
742 qCDebug(OkularCoreDebug) << "from" << prev_end << "to" << this->count();
743 #endif
744 }
745
isNull()746 template<class NormalizedShape, class Shape> bool RegularArea<NormalizedShape, Shape>::isNull() const
747 {
748 if (this->isEmpty())
749 return true;
750
751 typename QList<NormalizedShape>::const_iterator it = this->begin(), itEnd = this->end();
752 for (; it != itEnd; ++it)
753 if (!givePtr(*it)->isNull())
754 return false;
755
756 return true;
757 }
758
intersects(const NormalizedShape & shape)759 template<class NormalizedShape, class Shape> bool RegularArea<NormalizedShape, Shape>::intersects(const NormalizedShape &shape) const
760 {
761 if (this->isEmpty())
762 return false;
763
764 typename QList<NormalizedShape>::const_iterator it = this->begin(), itEnd = this->end();
765 for (; it != itEnd; ++it)
766 if (!givePtr(*it)->isNull() && givePtr(*it)->intersects(shape))
767 return true;
768
769 return false;
770 }
771
intersects(const RegularArea<NormalizedShape,Shape> * area)772 template<class NormalizedShape, class Shape> bool RegularArea<NormalizedShape, Shape>::intersects(const RegularArea<NormalizedShape, Shape> *area) const
773 {
774 if (this->isEmpty())
775 return false;
776
777 typename QList<NormalizedShape>::const_iterator it = this->begin(), itEnd = this->end();
778 for (; it != itEnd; ++it) {
779 typename QList<NormalizedShape>::const_iterator areaIt = area->begin(), areaItEnd = area->end();
780 for (; areaIt != areaItEnd; ++areaIt) {
781 if (!(*it).isNull() && (*it).intersects(*areaIt))
782 return true;
783 }
784 }
785
786 return false;
787 }
788
appendArea(const RegularArea<NormalizedShape,Shape> * area)789 template<class NormalizedShape, class Shape> void RegularArea<NormalizedShape, Shape>::appendArea(const RegularArea<NormalizedShape, Shape> *area)
790 {
791 typename QList<NormalizedShape>::const_iterator areaIt = area->begin(), areaItEnd = area->end();
792 for (; areaIt != areaItEnd; ++areaIt)
793 this->append(*areaIt);
794 }
795
appendShape(const NormalizedShape & shape,MergeSide side)796 template<class NormalizedShape, class Shape> void RegularArea<NormalizedShape, Shape>::appendShape(const NormalizedShape &shape, MergeSide side)
797 {
798 int size = this->count();
799 // if the list is empty, adds the shape normally
800 if (size == 0) {
801 this->append(shape);
802 } else {
803 bool intersection = false;
804 NormalizedShape &last = (*this)[size - 1];
805 #define O_LAST givePtr(last)
806 #define O_LAST_R O_LAST->right
807 #define O_LAST_L O_LAST->left
808 #define O_LAST_T O_LAST->top
809 #define O_LAST_B O_LAST->bottom
810 #define O_NEW givePtr(shape)
811 #define O_NEW_R O_NEW->right
812 #define O_NEW_L O_NEW->left
813 #define O_NEW_T O_NEW->top
814 #define O_NEW_B O_NEW->bottom
815 switch (side) {
816 case MergeRight:
817 intersection = (O_LAST_R >= O_NEW_L) && (O_LAST_L <= O_NEW_R) && ((O_LAST_T <= O_NEW_T && O_LAST_B >= O_NEW_B) || (O_LAST_T >= O_NEW_T && O_LAST_B <= O_NEW_B));
818 break;
819 case MergeBottom:
820 intersection = (O_LAST_B >= O_NEW_T) && (O_LAST_T <= O_NEW_B) && ((O_LAST_R <= O_NEW_R && O_LAST_L >= O_NEW_L) || (O_LAST_R >= O_NEW_R && O_LAST_L <= O_NEW_L));
821 break;
822 case MergeLeft:
823 intersection = (O_LAST_L <= O_NEW_R) && (O_LAST_R >= O_NEW_L) && ((O_LAST_T <= O_NEW_T && O_LAST_B >= O_NEW_B) || (O_LAST_T >= O_NEW_T && O_LAST_B <= O_NEW_B));
824 break;
825 case MergeTop:
826 intersection = (O_LAST_T <= O_NEW_B) && (O_LAST_B >= O_NEW_T) && ((O_LAST_R <= O_NEW_R && O_LAST_L >= O_NEW_L) || (O_LAST_R >= O_NEW_R && O_LAST_L <= O_NEW_L));
827 break;
828 case MergeAll:
829 intersection = O_LAST->intersects(shape);
830 break;
831 }
832 #undef O_LAST
833 #undef O_LAST_R
834 #undef O_LAST_L
835 #undef O_LAST_T
836 #undef O_LAST_B
837 #undef O_NEW
838 #undef O_NEW_R
839 #undef O_NEW_L
840 #undef O_NEW_T
841 #undef O_NEW_B
842 // if the new shape intersects with the last shape in the list, then
843 // merge it with that and delete the shape
844 if (intersection) {
845 deref((*this)[size - 1]) |= deref(shape);
846 } else
847 this->append(shape);
848 }
849 }
850
contains(double x,double y)851 template<class NormalizedShape, class Shape> bool RegularArea<NormalizedShape, Shape>::contains(double x, double y) const
852 {
853 if (this->isEmpty())
854 return false;
855
856 typename QList<NormalizedShape>::const_iterator it = this->begin(), itEnd = this->end();
857 for (; it != itEnd; ++it)
858 if ((*it).contains(x, y))
859 return true;
860
861 return false;
862 }
863
contains(const NormalizedShape & shape)864 template<class NormalizedShape, class Shape> bool RegularArea<NormalizedShape, Shape>::contains(const NormalizedShape &shape) const
865 {
866 if (this->isEmpty())
867 return false;
868
869 return QList<NormalizedShape>::contains(shape);
870 }
871
geometry(int xScale,int yScale,int dx,int dy)872 template<class NormalizedShape, class Shape> QList<Shape> RegularArea<NormalizedShape, Shape>::geometry(int xScale, int yScale, int dx, int dy) const
873 {
874 if (this->isEmpty())
875 return QList<Shape>();
876
877 QList<Shape> ret;
878 Shape t;
879 typename QList<NormalizedShape>::const_iterator it = this->begin(), itEnd = this->end();
880 for (; it != itEnd; ++it) {
881 t = givePtr(*it)->geometry(xScale, yScale);
882 t.translate(dx, dy);
883 ret.append(t);
884 }
885
886 return ret;
887 }
888
transform(const QTransform & matrix)889 template<class NormalizedShape, class Shape> void RegularArea<NormalizedShape, Shape>::transform(const QTransform &matrix)
890 {
891 if (this->isEmpty())
892 return;
893
894 for (int i = 0; i < this->count(); ++i)
895 givePtr((*this)[i])->transform(matrix);
896 }
897
898 /**
899 * This is a list of NormalizedRect, to describe an area consisting of
900 * multiple rectangles using normalized coordinates.
901 *
902 * This area can be mapped to a reference area, resulting in a list of QRects.
903 * For more information about the normalized coordinate system, see NormalizedPoint.
904 *
905 * Okular uses this area e. g. to describe a text highlight area,
906 * which consists of multiple, intersecting or non-intersecting rectangles.
907 *
908 * @see NormalizedRect, NormalizedPoint
909 */
910 class OKULARCORE_EXPORT RegularAreaRect : public RegularArea<NormalizedRect, QRect>
911 {
912 public:
913 RegularAreaRect();
914 RegularAreaRect(const RegularAreaRect &rar);
915 ~RegularAreaRect();
916
917 RegularAreaRect &operator=(const RegularAreaRect &rar);
918
919 private:
920 class Private;
921 Private *const d;
922 };
923
924 /**
925 * This class stores the geometry of a highlighting area in normalized coordinates,
926 * together with highlighting specific information.
927 */
928 class HighlightAreaRect : public RegularAreaRect
929 {
930 public:
931 /**
932 * Creates a new highlight area rect with the coordinates of
933 * the given @p area.
934 */
935 explicit HighlightAreaRect(const RegularAreaRect *area = nullptr);
936
937 /**
938 * The search ID of the highlight owner.
939 */
940 int s_id;
941
942 /**
943 * The color of the highlight.
944 */
945 QColor color;
946 };
947
948 uint qHash(const Okular::NormalizedRect &r, uint seed = 0);
949 }
950
951 #ifndef QT_NO_DEBUG_STREAM
952 /**
953 * Debug operator for normalized @p point.
954 */
955 OKULARCORE_EXPORT QDebug operator<<(QDebug str, const Okular::NormalizedPoint &point);
956
957 /**
958 * Debug operator for normalized @p rect.
959 */
960 OKULARCORE_EXPORT QDebug operator<<(QDebug str, const Okular::NormalizedRect &rect);
961 #endif
962
963 #endif
964