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 &region() 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