area.h 31.2 KB
Newer Older
1 2 3 4 5 6 7 8 9
/***************************************************************************
 *   Copyright (C) 2004-05 by Enrico Ros <eros.kde@email.it>               *
 *   Copyright (C) 2005 by Piotr Szymanski <niedakh@gmail.com>             *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 ***************************************************************************/

10 11 12
#ifndef _OKULAR_AREA_H_
#define _OKULAR_AREA_H_

13 14
#include <math.h>

Yuri Chornoivan's avatar
Yuri Chornoivan committed
15 16 17 18 19
#include <QList>
#include <QColor>
#include <QPainterPath>
#include <QTransform>
#include <QDebug>
20

21
#include "global.h"
22
#include "okularcore_export.h"
23

Pino Toscano's avatar
Pino Toscano committed
24
class QPolygonF;
25 26
class QRect;

27 28 29
namespace Okular {

class Annotation;
Pino Toscano's avatar
Pino Toscano committed
30
class Action;
31 32
class NormalizedShape;

33
/**
34
 * NormalizedPoint is a helper class which stores the coordinates
35
 * of a normalized point.
36
 *
37 38 39 40
 * @par Normalized Coordinate System
 * @parblock
 * Normalized means that the coordinates are always between 0 and 1,
 * unless the point shall be outside of the reference area.
41
 *
42 43
 * The reference area is a rectangle, and all normalized points
 * with coordinates of 0 or 1 describe its edges.
44
 *
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
 * This allows to locate things on a reference area without knowing its
 * (current or future) actual size. When the reference area is resized,
 * all things which are described in normalized coordinates keep their
 * proportional position on the area.
 * @endparblock
 *
 * @par Transformation to and from Normalized Coordinates
 * @parblock
 * To transform normalized coordinates to coordinates on the reference area,
 * just multiply them with the size of the reference area.
 *
 * To get normalized coordinates from a point on the reference area,
 * just divide its coordinates with the size of the reference area.
 *
 * Many methods have parameters @c xScale and @c yScale,
 * these are equal to the size of the reference area.
 * @endparblock
 *
 * @par Normalized Coordinate System Applied to Pages
 * @parblock
 * Okular uses a normalized coordinate system mainly to describe
 * positions on pages.
 * This is useful because pages can be shown in different sizes (zoom),
 * but all objects shall keep their proportional position on the page.
 *
 * Okular maps from page to normalized coordinates as follows:
 *  * Left edge of the page: x = 0
 *  * Right edge of the page: x = 1
 *  * Top edge of the page: y = 0
 *  * Bottom edge of the page: y = 1
 * @endparblock
 *
 * @par Example: Draw a Point on a Page
 * @parblock
 * The point is given in normalized coordinates (0.5, 0.3).
 *
 * If you want to draw it on a 800x600 page,
 * just multiply the x coordinate (0.5) with the page width (800),
 * and the y coordinate (0.3) with the page height (600).
 * So, the point will be drawn on the page at (400, 180).
 *
 * That allows you to zoom the page by just multiplying the normalized points with the
 * zoomed page size.
 * @endparblock
 *
 * @par Example: Select Text on a Page using Mouse Events
 * @parblock
 * The positon of all glyphs and words is stored in normalized coordinates.
 * (This is what TextPage actually does.)
 * Mouse press and release events are given in page coordinates (400, 180) and (600, 450),
 * while the page has a size of 800x600.
 *
 * If you want to search all text between the mouse click and release event,
 * you need their normalized coordinates.
 * Just divide the x coordinates (400 and 600) by the page width (800),
 * and the y coordinates (180 and 450) by the page height (600).
 * So, you have to search for all glyphs between (0.5, 0.3) and (0.75, 0.75).
 *
 * That allows you to process all glyphs and words without
 * having to keep any of their positions in sync with the page.
 * @endparblock
 *
 * @par Geometric operations
 * @parblock
 * NormalizedPoint supports basic geometric operations.
 *  * You can transform it with a QTransform matrix.
 *  * With the size of the reference area, you can calculate the squared
 *    absolute distance to another NormalizedPoint or a line of two NormalizedPoints.
 *
 * NormalizedRect provides additional geometric operations for rectangles.
 * @endparblock
 *
 * @see NormalizedRect
118
 */
119
class OKULARCORE_EXPORT NormalizedPoint
120 121
{
    public:
122
        /**
123
         * Creates a normalized point at (0, 0).
124
         */
125
        NormalizedPoint();
126

127 128 129 130 131 132
        /**
         * Creates a new normalized point with the normalized coordinates (@p x, @p y ).
         */
        NormalizedPoint( double x, double y );

        /**
133 134
         * Creates a new normalized point from an absolute point (@p x, @p y)
         * on a reference area of size @p xScale x @p yScale.
135 136 137
         */
        NormalizedPoint( int x, int y, int xScale, int yScale );

138 139 140
        /**
         * @internal
         */
141 142
        NormalizedPoint& operator=( const NormalizedPoint& );

143 144 145
        NormalizedPoint( const NormalizedPoint& );
        ~NormalizedPoint();

146 147 148
        /**
         * Transforms the normalized point with the operations defined by @p matrix.
         */
149
        void transform( const QTransform &matrix );
150

151
        /**
152 153
         * Returns squared distance to normalized point (@p x, @p y)
         * on a reference area of size @p xScale x @p yScale.
154 155 156 157 158
         * @since 0.17 (KDE 4.11)
         */
        double distanceSqr( double x, double y, double xScale, double yScale ) const;

        /**
159 160 161
         * Returns squared distance of the normalized point (@p x, @p y)
         * to the line segment from @p start to @p end
         * on a reference area of size @p xScale x @p yScale.
162 163 164 165
         * @since 0.17 (KDE 4.11)
         */
        static double distanceSqr( double x, double y, double xScale, double yScale, const NormalizedPoint& start, const NormalizedPoint& end );

166 167 168 169 170 171 172 173 174
        /**
         * The normalized x coordinate.
         */
        double x;

        /**
         * The normalized y coordinate.
         */
        double y;
175 176
};

177

178
/**
179 180 181 182 183 184 185 186 187 188 189 190
 * A NormalizedRect is a rectangle which can be defined by two NormalizedPoints.
 *
 * It describes a rectangular area on a reference area of undefined size.
 * For more information about the normalized coordinate system, see NormalizedPoint.
 *
 * In Okular, NormalizedRect can be used e. g. to describe bounding boxes of TextEntity objects,
 * and the highlight area of text selections.
 *
 * If you need to describe an area which consists of multiple rectangles,
 * you can use RegularAreaRect instead.
 *
 * @see NormalizedPoint, RegularAreaRect, TextEntity
191
 */
192
class OKULARCORE_EXPORT NormalizedRect
193 194
{
    public:
195 196 197 198
        /**
         * Creates a null normalized rectangle.
         * @see isNull()
         */
199
        NormalizedRect();
200 201 202 203 204 205 206 207 208 209 210 211

        /**
         * Creates a normalized rectangle with the normalized coordinates
         * @p left, @p top, @p right, @p bottom.
         *
         * If you need the x, y, width and height coordinates use the
         * following formulas:
         *
         * @li x = left
         * @li y = top
         * @li width = right - left
         * @li height = bottom - top
212 213 214 215 216
         *
         * @note
         * The coordinates for @p left and @p top should be lower than
         * @p right and @p bottom, respectively.
         * At negative width or height the behaviour of some operations is undefined.
217 218 219 220
         */
        NormalizedRect( double left, double top, double right, double bottom );

        /**
221 222 223 224 225 226 227
         * Creates a normalized rectangle from the given @p rectangle
         * on a reference area of size @p xScale x @p yScale.
         *
         * @note
         * The rectangle should have positive width and height.
         * You can use e. g. QRect::normalize() to ensure this.
         * At negative width or height the behaviour of some operations is undefined.
228 229 230
         */
        NormalizedRect( const QRect &rectangle, double xScale, double yScale );

231 232 233
        /**
         * @internal
         */
234
        NormalizedRect( const NormalizedRect& );
235 236 237 238

        /**
         * @internal
         */
239 240
        NormalizedRect& operator=( const NormalizedRect &other );

241 242
        ~NormalizedRect();

Pino Toscano's avatar
Pino Toscano committed
243
        /**
244
         * Build a normalized rect from a QRectF, which already has normalized coordinates.
Pino Toscano's avatar
Pino Toscano committed
245 246 247
         */
        static NormalizedRect fromQRectF( const QRectF &rect );

248 249 250
        /**
         * Returns whether this normalized rectangle is a null normalized rect.
         */
251
        bool isNull() const;
252 253

        /**
254 255
         * Returns whether the normalized rectangle contains the normalized point
         * (@p x, @p y).
256
         */
257
        bool contains( double x, double y ) const;
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277

        /**
         * Returns whether the normalized rectangle intersects the @p other normalized
         * rectangle.
         */
        bool intersects( const NormalizedRect &other ) const;

        /**
         * This is an overloaded member function, provided for convenience. It behaves essentially
         * like the above function.
         */
        bool intersects( const NormalizedRect *other ) const;

        /**
         * Returns whether the normalized rectangle intersects an other normalized
         * rectangle, which is defined by @p left, @p top, @p right and @p bottom.
         */
        bool intersects( double left, double top, double right, double bottom ) const;

        /**
278
         * Returns the rectangle mapped to a reference area of @p xScale x @p yScale.
279
         */
280
        QRect geometry( int xScale, int yScale ) const;
281

282
        /**
283
         * Same functionality as geometry, but the output is now rounded before typecasting to int
284
         *
Albert Astals Cid's avatar
Albert Astals Cid committed
285
         * @since 0.14 (KDE 4.8)
286
         */
287 288
        QRect roundedGeometry( int xScale, int yScale ) const;

289 290 291 292 293 294 295 296 297 298 299 300
        /**
         * Returns the normalized bounding rectangle of the normalized rectangle
         * combined with the @p other normalized rectangle.
         */
        NormalizedRect operator|( const NormalizedRect &other ) const;

        /**
         * Sets the normalized rectangle to the normalized bounding rectangle
         * of itself combined with the @p other normalized rectangle.
         */
        NormalizedRect& operator|=( const NormalizedRect &other );

301 302
        /**
         * Returns the intersection of this normalized rectangle with the specified
303
         * @p other. If the rects do not intersect then the result is a null rectangle.
304 305 306 307 308
         *
         * @since 0.7 (KDE 4.1)
         */
        NormalizedRect operator&( const NormalizedRect &other ) const;

309 310 311 312 313 314
        /**
         * Returns whether the normalized rectangle is equal to the @p other
         * normalized rectangle.
         */
        bool operator==( const NormalizedRect &other ) const;

315 316 317 318 319 320
        /**
         * Returns the center of the rectangle
         * @since 0.10 (KDE 4.4)
         */
        NormalizedPoint center() const;

321 322 323
        /**
         * Transforms the normalized rectangle with the operations defined by @p matrix.
         */
324
        void transform( const QTransform &matrix );
325

326
        /**
327
         * Returns true if the point @p pt is located below the bottom of the rectangle
Albert Astals Cid's avatar
Albert Astals Cid committed
328
         * @since 0.14 (KDE 4.8)
329
         */
330
        bool isBottom(const NormalizedPoint& pt) const
331
        {
332 333 334
            return bottom < pt.y;
        }

335
        /**
336
         * Returns true if the point @p pt is located above the top of the rectangle
Albert Astals Cid's avatar
Albert Astals Cid committed
337
         * @since 0.14 (KDE 4.8)
338
         */
339
        bool isTop(const NormalizedPoint& pt) const
340
        {
341 342 343
            return top > pt.y;
        }

344
        /**
345
         * Returns true if the point @p pt is located below the top of the rectangle
Albert Astals Cid's avatar
Albert Astals Cid committed
346
         * @since 0.14 (KDE 4.8)
347
         */
348
        bool isBottomOrLevel(const NormalizedPoint& pt) const
349
        {
350 351 352
            return top < pt.y;
        }

353
        /**
354
         * Returns true if the point @p pt is located above the bottom of the rectangle
Albert Astals Cid's avatar
Albert Astals Cid committed
355
         * @since 0.14 (KDE 4.8)
356
         */
357
        bool isTopOrLevel(const NormalizedPoint& pt) const
358
        {
359 360 361
            return bottom > pt.y;
        }

362
        /**
363
         * Returns true if the point @p pt is located to the right of the left edge of the rectangle
Albert Astals Cid's avatar
Albert Astals Cid committed
364
         * @since 0.14 (KDE 4.8)
365
         */
366
        bool isLeft(const NormalizedPoint& pt) const
367
        {
368
            return left < pt.x;
369 370
        }

371
        /**
372
         * Returns true if the point @p pt is located to the left of the right edge of the rectangle
Albert Astals Cid's avatar
Albert Astals Cid committed
373
         * @since 0.14 (KDE 4.8)
374
         */
375
        bool isRight(const NormalizedPoint& pt) const
376
        {
377
            return right > pt.x;
378 379
        }

380
        /**
381 382 383
         * Returns the squared distance of the normalized point (@p x, @p y)
         * to the closest edge, or 0 if the point is within the rectangle;
         * using a reference area of size @p xScale x @p yScale
384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401
         * @since 0.17 (KDE 4.11)
         */
        double distanceSqr(double x, double y, double xScale, double yScale) const
        {
            double distX = 0;
            if ( x < left )
                distX = left - x;
            else if ( x > right )
                distX = x - right;

            double distY = 0;
            if ( top > y )
                distY = top - y;
            else if (bottom < y)
                distY = y - bottom;
            return pow( distX * xScale, 2 ) + pow( distY * yScale, 2 );
        }

402 403 404 405 406 407 408 409 410 411 412 413
        /// @since 1.4
        double width() const
        {
            return right - left;
        }

        /// @since 1.4
        double height() const
        {
            return bottom - top;
        }

414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433
        /**
         * The normalized left coordinate.
         */
        double left;

        /**
         * The normalized top coordinate.
         */
        double top;

        /**
         * The normalized right coordinate.
         */
        double right;

        /**
         * The normalized bottom coordinate.
         */
        double bottom;
};
434
//KDE_DUMMY_QHASH_FUNCTION(NormalizedRect)
435 436

/**
437
 * @short An area with normalized coordinates that contains a reference to an object.
438
 *
439 440 441
 * These areas ("rects") contain a pointer to a document object
 * (such as a hyperlink, an action, or something like that).
 * The pointer is read and stored as 'void pointer' so cast is
442 443 444
 * performed by accessors based on the value returned by objectType(). Objects
 * are reparented to this class.
 *
Yuri Chornoivan's avatar
Yuri Chornoivan committed
445
 * Type / Class correspondence tab:
Pino Toscano's avatar
Pino Toscano committed
446
 *  - Action    : class Action: description of an action
447
 *  - Image     : class Image : description of an image (n/a)
448
 *  - Annotation: class Annotation: description of an annotation
449 450 451 452
 *
 * For more information about the normalized coordinate system, see NormalizedPoint.
 *
 * @see NormalizedPoint
453
 */
454
class OKULARCORE_EXPORT ObjectRect
455 456
{
    public:
457 458 459 460 461
        /**
         * Describes the type of storable object.
         */
        enum ObjectType
        {
Pino Toscano's avatar
Pino Toscano committed
462
            Action,      ///< An action
463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489
            Image,       ///< An image
            OAnnotation, ///< An annotation
            SourceRef    ///< A source reference
        };

        /**
         * Creates a new object rectangle.
         *
         * @param left The left coordinate of the rectangle.
         * @param top The top coordinate of the rectangle.
         * @param right The right coordinate of the rectangle.
         * @param bottom The bottom coordinate of the rectangle.
         * @param ellipse If true the rectangle describes an ellipse.
         * @param type The type of the storable object @see ObjectType.
         * @param object The pointer to the storable object.
         */
        ObjectRect( double left, double top, double right, double bottom, bool ellipse, ObjectType type, void *object );

        /**
         * This is an overloaded member function, provided for convenience.
         */
        ObjectRect( const NormalizedRect &rect, bool ellipse, ObjectType type, void *object );

        /**
         * This is an overloaded member function, provided for convenience.
         */
        ObjectRect( const QPolygonF &poly, ObjectType type, void *object );
490

491 492 493
        /**
         * Destroys the object rectangle.
         */
494
        virtual ~ObjectRect();
495

496 497 498 499 500 501 502 503 504 505 506 507 508 509
        /**
         * Returns the object type of the object rectangle.
         * @see ObjectType
         */
        ObjectType objectType() const;

        /**
         * Returns the storable object of the object rectangle.
         */
        const void *object() const;

        /**
         * Returns the region that is covered by the object rectangle.
         */
510
        const QPainterPath &region() const;
511 512 513 514 515

        /**
         * Returns the bounding rect of the object rectangle for the
         * scaling factor @p xScale and @p yScale.
         */
516
        virtual QRect boundingRect( double xScale, double yScale ) const;
517 518

        /**
519 520
         * Returns whether the object rectangle contains the point with absolute coordinates
         * (@p x, @p y) at a page size of @p xScale x @p yScale.
521 522 523 524 525 526
         */
        virtual bool contains( double x, double y, double xScale, double yScale ) const;

        /**
         * Transforms the object rectangle with the operations defined by @p matrix.
         */
527
        virtual void transform( const QTransform &matrix );
528

529
        /**
530 531 532 533
         * Returns the squared distance between the object
         * and the point with
         * normalized coordinates (@p x, @p y)
         * at a page size of @p xScale x @p yScale.
534 535 536 537 538 539
         *
         * @since 0.8.2 (KDE 4.2.2)
         */
        // FIXME this should most probably be a virtual method
        double distanceSqr( double x, double y, double xScale, double yScale ) const;

540
    protected:
541
        ObjectType m_objectType;
542
        void * m_object;
543
        QPainterPath m_path;
544
        QPainterPath m_transformedPath;
545 546
};

547 548 549
/**
 * This class describes the object rectangle for an annotation.
 */
550
class OKULARCORE_EXPORT AnnotationObjectRect : public ObjectRect
551 552
{
    public:
553 554 555 556
        /**
         * Creates a new annotation object rectangle with the
         * given @p annotation.
         */
Yuri Chornoivan's avatar
Yuri Chornoivan committed
557
        explicit AnnotationObjectRect( Annotation *annotation );
558 559 560 561

        /**
         * Destroys the annotation object rectangle.
         */
562 563
        virtual ~AnnotationObjectRect();

564 565 566 567 568 569 570 571 572
        /**
         * Returns the annotation object of the annotation object rectangle.
         */
        Annotation *annotation() const;

        /**
         * Returns the bounding rect of the annotation object rectangle for the
         * scaling factor @p xScale and @p yScale.
         */
Albert Astals Cid's avatar
Albert Astals Cid committed
573
        QRect boundingRect( double xScale, double yScale ) const override;
574 575 576 577 578

        /**
         * Returns whether the annotation object rectangle contains the point @p x, @p y for the
         * scaling factor @p xScale and @p yScale.
         */
Albert Astals Cid's avatar
Albert Astals Cid committed
579
        bool contains( double x, double y, double xScale, double yScale ) const override;
580 581 582 583

        /**
         * Transforms the annotation object rectangle with the operations defined by @p matrix.
         */
Albert Astals Cid's avatar
Albert Astals Cid committed
584
        void transform( const QTransform &matrix ) override;
585 586

    private:
587
        Annotation * m_annotation;
588 589
};

590 591 592
/**
 * This class describes the object rectangle for a source reference.
 */
593
class OKULARCORE_EXPORT SourceRefObjectRect : public ObjectRect
594
{
595 596
    friend class ObjectRect;

597
    public:
598 599 600 601 602 603 604
        /**
         * Creates a new source reference object rectangle.
         *
         * @param point The point of the source reference.
         * @param reference The storable source reference object.
         */
        SourceRefObjectRect( const NormalizedPoint& point, void *reference );
605

606 607 608 609
        /**
         * Returns the bounding rect of the source reference object rectangle for the
         * scaling factor @p xScale and @p yScale.
         */
Albert Astals Cid's avatar
Albert Astals Cid committed
610
        QRect boundingRect( double xScale, double yScale ) const override;
611 612 613 614 615

        /**
         * Returns whether the source reference object rectangle contains the point @p x, @p y for the
         * scaling factor @p xScale and @p yScale.
         */
Albert Astals Cid's avatar
Albert Astals Cid committed
616
        bool contains( double x, double y, double xScale, double yScale ) const override;
617 618 619 620 621

    private:
        NormalizedPoint m_point;
};

622 623 624 625 626 627 628 629 630 631 632
/**
 * This class is an object rect that doesn't own the given pointer, i.e. won't delete it on destruction
 * @since 1.7
 */
class OKULARCORE_EXPORT NonOwningObjectRect : public ObjectRect
{
    public:
        NonOwningObjectRect( double left, double top, double right, double bottom, bool ellipse, ObjectType type, void *object );
        ~NonOwningObjectRect();
};

633
/// @cond PRIVATE
634 635 636
/** @internal */
/** @internal */
template <typename T>
637
T* givePtr( T& t )
638
{
639 640
    return &t;
}
641

642 643 644 645 646 647
/** @internal */
template <typename T>
T& deref( T& t )
{
    return t;
}
648
/// @endcond
649

650
/**
651 652 653 654 655 656
 * @short An area with normalized coordinates, consisting of NormalizedShape objects.
 *
 * This is a template class to describe an area which consists of
 * multiple shapes of the same type, intersecting or non-intersecting.
 * The coordinates are normalized, and can be mapped to a reference area of defined size.
 * For more information about the normalized coordinate system, see NormalizedPoint.
Pino Toscano's avatar
Pino Toscano committed
657 658
 *
 * Class NormalizedShape \b must have the following functions/operators defined:
659
 * - bool contains( double, double ), whether it contains the given NormalizedPoint
Pino Toscano's avatar
Pino Toscano committed
660 661
 * - bool intersects( NormalizedShape )
 * - bool isNull()
662 663 664 665
 * - Shape geometry( int, int ), which maps to the reference area
 * - operator|=( NormalizedShape ), which unites two NormalizedShape's
 *
 * @see RegularAreaRect, NormalizedPoint
666
 */
667
template <class NormalizedShape, class Shape> class RegularArea : public  QList<NormalizedShape>
668
{
669
    public:
670
        /**
671
         * Returns whether this area contains the normalized point (@p x, @p y).
672
         */
673
        bool contains( double x, double y ) const;
674 675

        /**
676 677 678 679
         * Returns whether this area contains a NormalizedShape object that equals @p shape.
         *
         * @note
         * The original NormalizedShape objects can be lost if simplify() was called.
680
         */
681
        bool contains( const NormalizedShape& shape ) const;
682 683

        /**
684
         * Returns whether this area intersects with the given @p area.
685
         */
686
        bool intersects( const RegularArea<NormalizedShape,Shape> *area ) const;
687 688 689 690

        /**
         * Returns whether the regular area intersects with the given @p shape.
         */
691
        bool intersects( const NormalizedShape& shape ) const;
692 693

        /**
694
         * Appends the given @p area to this area.
695
         */
696
        void appendArea( const RegularArea<NormalizedShape,Shape> *area );
697 698

        /**
699
         * Appends the given @p shape to this area.
700
         */
701
        void appendShape( const NormalizedShape& shape, MergeSide side = MergeAll );
702 703

        /**
704 705
         * Simplifies this regular area by merging its intersecting subareas.
         * This might change the effective geometry of this area.
706
         */
707
        void simplify();
708 709 710 711

        /**
         * Returns whether the regular area is a null area.
         */
712
        bool isNull() const;
713 714

        /**
715 716 717
         * Returns the subareas of this regular area
         * mapped to a reference area of size @p xScale x @p yScale,
         * then translated by @p dx and @p dy.
718 719 720 721 722 723
         */
        QList<Shape> geometry( int xScale, int yScale, int dx = 0, int dy = 0 ) const;

        /**
         * Transforms the regular area with the operations defined by @p matrix.
         */
724
        void transform( const QTransform &matrix );
725 726
};

727 728 729
template <class NormalizedShape, class Shape>
void RegularArea<NormalizedShape, Shape>::simplify()
{
730 731 732 733 734
#ifdef DEBUG_REGULARAREA
            int prev_end = this->count();
#endif
            int end = this->count() - 1, x = 0;
            for ( int i = 0; i < end; ++i )
735
            {
736
                    if ( givePtr( (*this)[x] )->intersects( deref( (*this)[i+1] ) ) )
737
                    {
738
                        deref((*this)[x]) |= deref((*this)[i+1]);
739 740 741
                        this->removeAt( i + 1 );
                        --end;
                        --i;
742 743 744 745 746 747
                    }
                    else
                    {
                        x=i+1;
                   }
            }
Pino Toscano's avatar
Pino Toscano committed
748
#ifdef DEBUG_REGULARAREA
749
    qCDebug(OkularCoreDebug) << "from" << prev_end << "to" << this->count();
Pino Toscano's avatar
Pino Toscano committed
750
#endif
751 752
}

753 754 755
template <class NormalizedShape, class Shape>
bool RegularArea<NormalizedShape, Shape>::isNull() const
{
756
    if ( this->isEmpty() )
757
        return true;
758

759 760 761
    typename QList<NormalizedShape>::const_iterator it = this->begin(), itEnd = this->end();
    for ( ; it != itEnd; ++it )
        if ( !givePtr( *it )->isNull() )
762
            return false;
763

764
    return true;
765 766 767
}

template <class NormalizedShape, class Shape>
768
bool RegularArea<NormalizedShape, Shape>::intersects( const NormalizedShape& rect ) const
769
{
770 771 772
    if ( this->isEmpty() )
        return false;

773 774 775
    typename QList<NormalizedShape>::const_iterator it = this->begin(), itEnd = this->end();
    for ( ; it != itEnd; ++it )
        if ( !givePtr( *it )->isNull() && givePtr( *it )->intersects( rect ) )
776 777 778
            return true;

    return false;
779 780 781
}

template <class NormalizedShape, class Shape>
782
bool RegularArea<NormalizedShape, Shape>::intersects( const RegularArea<NormalizedShape,Shape> *area ) const
783
{
784 785 786
    if ( this->isEmpty() )
        return false;

787 788
    typename QList<NormalizedShape>::const_iterator it = this->begin(), itEnd = this->end();
    for ( ; it != itEnd; ++it )
789
    {
790 791
        typename QList<NormalizedShape>::const_iterator areaIt = area->begin(), areaItEnd = area->end();
        for ( ; areaIt != areaItEnd; ++areaIt )
792
        {
Christian Ehrlicher's avatar
Christian Ehrlicher committed
793
            if ( !( *it ).isNull() && ( *it ).intersects( *areaIt ) )
794 795 796 797 798
                return true;
        }
    }

    return false;
799 800 801
}

template <class NormalizedShape, class Shape>
802
void RegularArea<NormalizedShape, Shape>::appendArea( const RegularArea<NormalizedShape, Shape> *area )
803
{
804 805 806
    typename QList<NormalizedShape>::const_iterator areaIt = area->begin(), areaItEnd = area->end();
    for ( ; areaIt != areaItEnd; ++areaIt )
        this->append( *areaIt );
807 808 809
}


810
template <class NormalizedShape, class Shape>
811
void RegularArea<NormalizedShape, Shape>::appendShape( const NormalizedShape& shape, MergeSide side )
812 813 814 815 816 817 818 819 820
{
    int size = this->count();
    // if the list is empty, adds the shape normally
    if ( size == 0 )
    {
        this->append( shape );
    }
    else
    {
821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868
        bool intersection = false;
        NormalizedShape& last = (*this)[size - 1];
#define O_LAST givePtr( last )
#  define O_LAST_R O_LAST->right
#  define O_LAST_L O_LAST->left
#  define O_LAST_T O_LAST->top
#  define O_LAST_B O_LAST->bottom
#define O_NEW givePtr( shape )
#  define O_NEW_R O_NEW->right
#  define O_NEW_L O_NEW->left
#  define O_NEW_T O_NEW->top
#  define O_NEW_B O_NEW->bottom
        switch ( side )
        {
            case MergeRight:
                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 ) );
                break;
            case MergeBottom:
                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 ) );
                break;
            case MergeLeft:
                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 ) );
                break;
            case MergeTop:
                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 ) );
                break;
            case MergeAll:
                intersection = O_LAST->intersects( shape );
                break;
        }
#undef O_LAST
#  undef O_LAST_R
#  undef O_LAST_L
#  undef O_LAST_T
#  undef O_LAST_B
#undef O_NEW
#  undef O_NEW_R
#  undef O_NEW_L
#  undef O_NEW_T
#  undef O_NEW_B
869 870
        // if the new shape intersects with the last shape in the list, then
        // merge it with that and delete the shape
871
        if ( intersection )
872
        {
873
            deref((*this)[size - 1]) |= deref( shape );
874 875 876 877 878 879 880
        }
        else
            this->append( shape );
    }
}


881
template <class NormalizedShape, class Shape>
882
bool RegularArea<NormalizedShape, Shape>::contains( double x, double y ) const
883
{
884 885 886
    if ( this->isEmpty() )
        return false;

887 888
    typename QList<NormalizedShape>::const_iterator it = this->begin(), itEnd = this->end();
    for ( ; it != itEnd; ++it )
Christian Ehrlicher's avatar
Christian Ehrlicher committed
889
        if ( ( *it ).contains( x, y ) )
890 891 892
            return true;

    return false;
893 894
}

895
template <class NormalizedShape, class Shape>
896
bool RegularArea<NormalizedShape, Shape>::contains( const NormalizedShape& shape ) const
897
{
898 899 900
    if ( this->isEmpty() )
        return false;

Pino Toscano's avatar
Pino Toscano committed
901
    return QList<NormalizedShape>::contains( shape );
902 903
}

904
template <class NormalizedShape, class Shape>
905
QList<Shape> RegularArea<NormalizedShape, Shape>::geometry( int xScale, int yScale, int dx, int dy ) const
906
{
907
    if ( this->isEmpty() )
908
        return QList<Shape>();
909

910
    QList<Shape> ret;
911
    Shape t;
912 913
    typename QList<NormalizedShape>::const_iterator it = this->begin(), itEnd = this->end();
    for ( ; it != itEnd; ++it )
914
    {
915
        t = givePtr( *it )->geometry( xScale, yScale );
916
        t.translate( dx, dy );
917
        ret.append( t );
918 919 920
    }

    return ret;
921 922
}

923
template <class NormalizedShape, class Shape>
924
void RegularArea<NormalizedShape, Shape>::transform( const QTransform &matrix )
925 926 927 928 929 930 931 932
{
    if ( this->isEmpty() )
        return;

    for ( int i = 0; i < this->count(); ++i )
        givePtr( (*this)[i] )->transform( matrix );
}

933 934 935 936 937 938 939 940 941 942 943 944
/**
 * This is a list of NormalizedRect, to describe an area consisting of
 * multiple rectangles using normalized coordinates.
 *
 * This area can be mapped to a reference area, resulting in a list of QRects.
 * For more information about the normalized coordinate system, see NormalizedPoint.
 *
 * Okular uses this area e. g. to describe a text highlight area,
 * which consists of multiple, intersecting or non-intersecting rectangles.
 *
 * @see NormalizedRect, NormalizedPoint
 */
945
class OKULARCORE_EXPORT RegularAreaRect : public RegularArea< NormalizedRect, QRect >
946 947 948 949 950 951 952 953 954 955 956 957
{
    public:
        RegularAreaRect();
        RegularAreaRect( const RegularAreaRect& rar );
        ~RegularAreaRect();

        RegularAreaRect& operator=( const RegularAreaRect& rar );

    private:
        class Private;
        Private * const d;
};
958

959
/**
960 961
 * This class stores the geometry of a highlighting area in normalized coordinates,
 * together with highlighting specific information.
962
 */
963 964 965
class HighlightAreaRect : public RegularAreaRect
{
    public:
966 967 968 969
        /**
         * Creates a new highlight area rect with the coordinates of
         * the given @p area.
         */
Yuri Chornoivan's avatar
Yuri Chornoivan committed
970
        explicit HighlightAreaRect( const RegularAreaRect *area = nullptr );
971

972 973 974
        /**
         * The search ID of the highlight owner.
         */
975
        int s_id;
976 977 978 979

        /**
         * The color of the highlight.
         */
980
        QColor color;
981 982
};

983 984
}

985 986
uint qHash(const Okular::NormalizedRect& r, uint seed = 0);

987
#ifndef QT_NO_DEBUG_STREAM
988 989 990
/**
 * Debug operator for normalized @p point.
 */
991
OKULARCORE_EXPORT QDebug operator<<( QDebug str, const Okular::NormalizedPoint &point );
992 993 994 995

/**
 * Debug operator for normalized @p rect.
 */
996
OKULARCORE_EXPORT QDebug operator<<( QDebug str, const Okular::NormalizedRect &rect );
997
#endif
998

999
#endif