File indexing completed on 2024-04-28 04:32:36
0001 /* 0002 SPDX-FileCopyrightText: 2005 Enrico Ros <eros.kde@email.it> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "annotations.h" 0008 #include "annotations_p.h" 0009 0010 // qt/kde includes 0011 #include <QApplication> 0012 #include <QColor> 0013 #include <QFile> 0014 #include <QIcon> 0015 #include <QPainter> 0016 #include <QStandardPaths> 0017 #include <QSvgRenderer> 0018 0019 // DBL_MAX 0020 #include <float.h> 0021 0022 // local includes 0023 #include "action.h" 0024 #include "document.h" 0025 #include "document_p.h" 0026 #include "movie.h" 0027 #include "page_p.h" 0028 #include "sound.h" 0029 0030 using namespace Okular; 0031 0032 /** 0033 * True, if point @p c lies to the left of the vector from @p a to @p b 0034 * @internal 0035 */ 0036 static bool isLeftOfVector(const NormalizedPoint &a, const NormalizedPoint &b, const NormalizedPoint &c) 0037 { 0038 // cross product 0039 return ((b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x)) > 0; 0040 } 0041 0042 /** 0043 * @brief Calculates distance of the given point @p x @p y @p xScale @p yScale to the @p path 0044 * 0045 * Does piecewise comparison and selects the distance to the closest segment 0046 */ 0047 static double distanceSqr(double x, double y, double xScale, double yScale, const QList<NormalizedPoint> &path) 0048 { 0049 double distance = DBL_MAX; 0050 double thisDistance; 0051 QList<NormalizedPoint>::const_iterator i = path.constBegin(); 0052 NormalizedPoint lastPoint = *i; 0053 0054 for (++i; i != path.constEnd(); ++i) { 0055 thisDistance = NormalizedPoint::distanceSqr(x, y, xScale, yScale, lastPoint, (*i)); 0056 0057 if (thisDistance < distance) { 0058 distance = thisDistance; 0059 } 0060 0061 lastPoint = *i; 0062 } 0063 return distance; 0064 } 0065 0066 /** 0067 * Given the squared @p distance from the idealized 0-width line and a pen width @p penWidth, 0068 * (not squared!), returns the final distance 0069 * 0070 * @warning The returned distance is not exact: 0071 * We calculate an (exact) squared distance to the ideal (centered) line, and then subtract 0072 * the squared width of the pen: 0073 * a^2 - b^2 where a = "distance from idealized 0-width line" b = "pen width" 0074 * For an exact result, we would want to calculate "(a - b)^2" but that would require 0075 * a square root operation because we only know the squared distance a^2. 0076 * 0077 * However, the approximation is feasible, because: 0078 * error = (a-b)^2 - (a^2 - b^2) = -2ab + 2b^2 = 2b(b - a) 0079 * Therefore: 0080 * lim_{a->b} a^2 - b^2 - a^2 + 2ab - b^2 --> 0 0081 * 0082 * In other words, this approximation will estimate the distance to be slightly more than it actually is 0083 * for as long as we are far "outside" the line, becoming more accurate the closer we get to the line 0084 * boundary. Trivially, it also fulfils (a1 < a2) => ((a1^2 - b^2) < (a2^2 - b^2)) making it monotonic. 0085 * "Inside" of the drawn line, the distance is 0 anyway. 0086 */ 0087 static double strokeDistance(double distance, double penWidth) 0088 { 0089 return fmax(distance - pow(penWidth, 2), 0); 0090 } 0091 0092 // BEGIN AnnotationUtils implementation 0093 Annotation *AnnotationUtils::createAnnotation(const QDomElement &annElement) 0094 { 0095 // safety check on annotation element 0096 if (!annElement.hasAttribute(QStringLiteral("type"))) { 0097 return nullptr; 0098 } 0099 0100 // build annotation of given type 0101 Annotation *annotation = nullptr; 0102 int typeNumber = annElement.attribute(QStringLiteral("type")).toInt(); 0103 switch (typeNumber) { 0104 case Annotation::AText: 0105 annotation = new TextAnnotation(annElement); 0106 break; 0107 case Annotation::ALine: 0108 annotation = new LineAnnotation(annElement); 0109 break; 0110 case Annotation::AGeom: 0111 annotation = new GeomAnnotation(annElement); 0112 break; 0113 case Annotation::AHighlight: 0114 annotation = new HighlightAnnotation(annElement); 0115 break; 0116 case Annotation::AStamp: 0117 annotation = new StampAnnotation(annElement); 0118 break; 0119 case Annotation::AInk: 0120 annotation = new InkAnnotation(annElement); 0121 break; 0122 case Annotation::ACaret: 0123 annotation = new CaretAnnotation(annElement); 0124 break; 0125 } 0126 0127 // return created annotation 0128 return annotation; 0129 } 0130 0131 void AnnotationUtils::storeAnnotation(const Annotation *ann, QDomElement &annElement, QDomDocument &document) 0132 { 0133 // save annotation's type as element's attribute 0134 annElement.setAttribute(QStringLiteral("type"), (uint)ann->subType()); 0135 0136 // append all annotation data as children of this node 0137 ann->store(annElement, document); 0138 } 0139 0140 QDomElement AnnotationUtils::findChildElement(const QDomNode &parentNode, const QString &name) 0141 { 0142 // loop through the whole children and return a 'name' named element 0143 QDomNode subNode = parentNode.firstChild(); 0144 while (subNode.isElement()) { 0145 QDomElement element = subNode.toElement(); 0146 if (element.tagName() == name) { 0147 return element; 0148 } 0149 subNode = subNode.nextSibling(); 0150 } 0151 // if the name can't be found, return a dummy null element 0152 return QDomElement(); 0153 } 0154 0155 QRect AnnotationUtils::annotationGeometry(const Annotation *annotation, double scaleX, double scaleY) 0156 { 0157 const QRect rect = annotation->transformedBoundingRectangle().geometry((int)scaleX, (int)scaleY); 0158 if (annotation->subType() == Annotation::AText && (((TextAnnotation *)annotation)->textType() == TextAnnotation::Linked)) { 0159 // To be honest i have no clue of why the 24,24 is here, maybe to make sure it's not too small? 0160 // But why only for linked text? 0161 const QRect rect24 = QRect((int)(annotation->transformedBoundingRectangle().left * scaleX), (int)(annotation->transformedBoundingRectangle().top * scaleY), 24, 24); 0162 return rect24.united(rect); 0163 } 0164 0165 return rect; 0166 } 0167 0168 QPixmap AnnotationUtils::loadStamp(const QString &nameOrPath, int size, bool keepAspectRatio) 0169 { 0170 const QString name = nameOrPath.toLower(); 0171 0172 static std::unique_ptr<QSvgRenderer> svgStampFile; 0173 if (!svgStampFile.get()) { 0174 const QString stampFile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("okular/pics/stamps.svg")); 0175 if (!stampFile.isEmpty()) { 0176 svgStampFile = std::make_unique<QSvgRenderer>(stampFile); 0177 if (!svgStampFile->isValid()) { 0178 svgStampFile.reset(); 0179 } 0180 } 0181 } 0182 0183 QSvgRenderer *r = svgStampFile.get(); 0184 if (r && r->isValid() && r->elementExists(name)) { 0185 const QSize stampSize = r->boundsOnElement(name).size().toSize(); 0186 const QSize pixmapSize = stampSize.scaled(size, size, keepAspectRatio ? Qt::KeepAspectRatioByExpanding : Qt::IgnoreAspectRatio); 0187 QPixmap pixmap(pixmapSize); 0188 pixmap.fill(Qt::transparent); 0189 QPainter p(&pixmap); 0190 r->render(&p, name); 0191 p.end(); 0192 return pixmap; 0193 } 0194 0195 // _name is a path (do this before loading as icon name to avoid some rare weirdness ) 0196 // Check that it exists up front. While pixmap.load() fails, if it is 0197 // actually an icon from theme, the loader will try all supported 0198 // extensions in current workdir before failing 0199 if (QFile::exists(nameOrPath)) { 0200 QPixmap pixmap; 0201 pixmap.load(nameOrPath); 0202 if (!pixmap.isNull()) { 0203 pixmap = pixmap.scaled(size, size, keepAspectRatio ? Qt::KeepAspectRatioByExpanding : Qt::IgnoreAspectRatio, Qt::SmoothTransformation); 0204 return pixmap; 0205 } 0206 } 0207 0208 // _name is an icon name 0209 return QIcon::fromTheme(name).pixmap(size); 0210 } 0211 // END AnnotationUtils implementation 0212 0213 AnnotationProxy::AnnotationProxy() 0214 { 0215 } 0216 0217 AnnotationProxy::~AnnotationProxy() 0218 { 0219 } 0220 0221 // BEGIN Annotation implementation 0222 0223 class Annotation::Style::Private 0224 { 0225 public: 0226 Private() 0227 : m_opacity(1.0) 0228 , m_width(1.0) 0229 , m_style(Solid) 0230 , m_xCorners(0.0) 0231 , m_yCorners(0.0) 0232 , m_marks(3) 0233 , m_spaces(0) 0234 , m_effect(NoEffect) 0235 , m_effectIntensity(1.0) 0236 { 0237 } 0238 0239 QColor m_color; 0240 double m_opacity; 0241 double m_width; 0242 LineStyle m_style; 0243 double m_xCorners; 0244 double m_yCorners; 0245 int m_marks; 0246 int m_spaces; 0247 LineEffect m_effect; 0248 double m_effectIntensity; 0249 }; 0250 0251 Annotation::Style::Style() 0252 : d(new Private) 0253 { 0254 } 0255 0256 Annotation::Style::~Style() 0257 { 0258 delete d; 0259 } 0260 0261 Annotation::Style::Style(const Style &other) 0262 : d(new Private) 0263 { 0264 *d = *other.d; 0265 } 0266 0267 Annotation::Style &Annotation::Style::operator=(const Style &other) 0268 { 0269 if (this != &other) { 0270 *d = *other.d; 0271 } 0272 0273 return *this; 0274 } 0275 0276 void Annotation::Style::setColor(const QColor &color) 0277 { 0278 d->m_color = color; 0279 } 0280 0281 QColor Annotation::Style::color() const 0282 { 0283 return d->m_color; 0284 } 0285 0286 void Annotation::Style::setOpacity(double opacity) 0287 { 0288 d->m_opacity = opacity; 0289 } 0290 0291 double Annotation::Style::opacity() const 0292 { 0293 return d->m_opacity; 0294 } 0295 0296 void Annotation::Style::setWidth(double width) 0297 { 0298 d->m_width = width; 0299 } 0300 0301 double Annotation::Style::width() const 0302 { 0303 return d->m_width; 0304 } 0305 0306 void Annotation::Style::setLineStyle(LineStyle style) 0307 { 0308 d->m_style = style; 0309 } 0310 0311 Annotation::LineStyle Annotation::Style::lineStyle() const 0312 { 0313 return d->m_style; 0314 } 0315 0316 void Annotation::Style::setXCorners(double xCorners) 0317 { 0318 d->m_xCorners = xCorners; 0319 } 0320 0321 double Annotation::Style::xCorners() const 0322 { 0323 return d->m_xCorners; 0324 } 0325 0326 void Annotation::Style::setYCorners(double yCorners) 0327 { 0328 d->m_yCorners = yCorners; 0329 } 0330 0331 double Annotation::Style::yCorners() const 0332 { 0333 return d->m_yCorners; 0334 } 0335 0336 void Annotation::Style::setMarks(int marks) 0337 { 0338 d->m_marks = marks; 0339 } 0340 0341 int Annotation::Style::marks() const 0342 { 0343 return d->m_marks; 0344 } 0345 0346 void Annotation::Style::setSpaces(int spaces) 0347 { 0348 d->m_spaces = spaces; 0349 } 0350 0351 int Annotation::Style::spaces() const 0352 { 0353 return d->m_spaces; 0354 } 0355 0356 void Annotation::Style::setLineEffect(LineEffect effect) 0357 { 0358 d->m_effect = effect; 0359 } 0360 0361 Annotation::LineEffect Annotation::Style::lineEffect() const 0362 { 0363 return d->m_effect; 0364 } 0365 0366 void Annotation::Style::setEffectIntensity(double intensity) 0367 { 0368 d->m_effectIntensity = intensity; 0369 } 0370 0371 double Annotation::Style::effectIntensity() const 0372 { 0373 return d->m_effectIntensity; 0374 } 0375 0376 class Annotation::Window::Private 0377 { 0378 public: 0379 Private() 0380 : m_flags(-1) 0381 , m_width(0) 0382 , m_height(0) 0383 { 0384 } 0385 0386 int m_flags; 0387 NormalizedPoint m_topLeft; 0388 int m_width; 0389 int m_height; 0390 QString m_title; 0391 QString m_summary; 0392 }; 0393 0394 Annotation::Window::Window() 0395 : d(new Private) 0396 { 0397 } 0398 0399 Annotation::Window::~Window() 0400 { 0401 delete d; 0402 } 0403 0404 Annotation::Window::Window(const Window &other) 0405 : d(new Private) 0406 { 0407 *d = *other.d; 0408 } 0409 0410 Annotation::Window &Annotation::Window::operator=(const Window &other) 0411 { 0412 if (this != &other) { 0413 *d = *other.d; 0414 } 0415 0416 return *this; 0417 } 0418 0419 void Annotation::Window::setFlags(int flags) 0420 { 0421 d->m_flags = flags; 0422 } 0423 0424 int Annotation::Window::flags() const 0425 { 0426 return d->m_flags; 0427 } 0428 0429 void Annotation::Window::setTopLeft(const NormalizedPoint &point) 0430 { 0431 d->m_topLeft = point; 0432 } 0433 0434 NormalizedPoint Annotation::Window::topLeft() const 0435 { 0436 return d->m_topLeft; 0437 } 0438 0439 void Annotation::Window::setWidth(int width) 0440 { 0441 d->m_width = width; 0442 } 0443 0444 int Annotation::Window::width() const 0445 { 0446 return d->m_width; 0447 } 0448 0449 void Annotation::Window::setHeight(int height) 0450 { 0451 d->m_height = height; 0452 } 0453 0454 int Annotation::Window::height() const 0455 { 0456 return d->m_height; 0457 } 0458 0459 void Annotation::Window::setTitle(const QString &title) 0460 { 0461 d->m_title = title; 0462 } 0463 0464 QString Annotation::Window::title() const 0465 { 0466 return d->m_title; 0467 } 0468 0469 void Annotation::Window::setSummary(const QString &summary) 0470 { 0471 d->m_summary = summary; 0472 } 0473 0474 QString Annotation::Window::summary() const 0475 { 0476 return d->m_summary; 0477 } 0478 0479 class Annotation::Revision::Private 0480 { 0481 public: 0482 Private() 0483 : m_annotation(nullptr) 0484 , m_scope(Reply) 0485 , m_type(None) 0486 { 0487 } 0488 0489 Annotation *m_annotation; 0490 RevisionScope m_scope; 0491 RevisionType m_type; 0492 }; 0493 0494 Annotation::Revision::Revision() 0495 : d(new Private) 0496 { 0497 } 0498 0499 Annotation::Revision::~Revision() 0500 { 0501 delete d; 0502 } 0503 0504 Annotation::Revision::Revision(const Revision &other) 0505 : d(new Private) 0506 { 0507 *d = *other.d; 0508 } 0509 0510 Annotation::Revision &Annotation::Revision::operator=(const Revision &other) 0511 { 0512 if (this != &other) { 0513 *d = *other.d; 0514 } 0515 0516 return *this; 0517 } 0518 0519 void Annotation::Revision::setAnnotation(Annotation *annotation) 0520 { 0521 d->m_annotation = annotation; 0522 } 0523 0524 Annotation *Annotation::Revision::annotation() const 0525 { 0526 return d->m_annotation; 0527 } 0528 0529 void Annotation::Revision::setScope(RevisionScope scope) 0530 { 0531 d->m_scope = scope; 0532 } 0533 0534 Annotation::RevisionScope Annotation::Revision::scope() const 0535 { 0536 return d->m_scope; 0537 } 0538 0539 void Annotation::Revision::setType(RevisionType type) 0540 { 0541 d->m_type = type; 0542 } 0543 0544 Annotation::RevisionType Annotation::Revision::type() const 0545 { 0546 return d->m_type; 0547 } 0548 0549 AnnotationPrivate::AnnotationPrivate() 0550 : m_page(nullptr) 0551 , m_flags(0) 0552 , m_disposeFunc(nullptr) 0553 { 0554 } 0555 0556 AnnotationPrivate::~AnnotationPrivate() 0557 { 0558 // delete all children revisions 0559 if (m_revisions.isEmpty()) { 0560 return; 0561 } 0562 0563 for (const Annotation::Revision &revision : std::as_const(m_revisions)) { 0564 delete revision.annotation(); 0565 } 0566 } 0567 0568 AnnotationPrivate *AnnotationPrivate::get(Annotation *a) 0569 { 0570 return a ? a->d_ptr : nullptr; 0571 } 0572 0573 Annotation::Annotation(AnnotationPrivate &dd) 0574 : d_ptr(&dd) 0575 { 0576 } 0577 0578 Annotation::Annotation(AnnotationPrivate &dd, const QDomNode &description) 0579 : d_ptr(&dd) 0580 { 0581 d_ptr->setAnnotationProperties(description); 0582 } 0583 0584 Annotation::~Annotation() 0585 { 0586 if (d_ptr->m_disposeFunc) { 0587 d_ptr->m_disposeFunc(this); 0588 } 0589 0590 delete d_ptr; 0591 } 0592 0593 void Annotation::setAuthor(const QString &author) 0594 { 0595 Q_D(Annotation); 0596 d->m_author = author; 0597 } 0598 0599 QString Annotation::author() const 0600 { 0601 Q_D(const Annotation); 0602 return d->m_author; 0603 } 0604 0605 void Annotation::setContents(const QString &contents) 0606 { 0607 Q_D(Annotation); 0608 d->m_contents = contents; 0609 } 0610 0611 QString Annotation::contents() const 0612 { 0613 Q_D(const Annotation); 0614 return d->m_contents; 0615 } 0616 0617 void Annotation::setUniqueName(const QString &name) 0618 { 0619 Q_D(Annotation); 0620 d->m_uniqueName = name; 0621 } 0622 0623 QString Annotation::uniqueName() const 0624 { 0625 Q_D(const Annotation); 0626 return d->m_uniqueName; 0627 } 0628 0629 void Annotation::setModificationDate(const QDateTime &date) 0630 { 0631 Q_D(Annotation); 0632 d->m_modifyDate = date; 0633 } 0634 0635 QDateTime Annotation::modificationDate() const 0636 { 0637 Q_D(const Annotation); 0638 return d->m_modifyDate; 0639 } 0640 0641 void Annotation::setCreationDate(const QDateTime &date) 0642 { 0643 Q_D(Annotation); 0644 d->m_creationDate = date; 0645 } 0646 0647 QDateTime Annotation::creationDate() const 0648 { 0649 Q_D(const Annotation); 0650 return d->m_creationDate; 0651 } 0652 0653 void Annotation::setFlags(int flags) 0654 { 0655 Q_D(Annotation); 0656 d->m_flags = flags; 0657 } 0658 0659 int Annotation::flags() const 0660 { 0661 Q_D(const Annotation); 0662 return d->m_flags; 0663 } 0664 0665 void Annotation::setBoundingRectangle(const NormalizedRect &rectangle) 0666 { 0667 Q_D(Annotation); 0668 d->m_boundary = rectangle; 0669 d->resetTransformation(); 0670 if (d->m_page) { 0671 d->transform(d->m_page->rotationMatrix()); 0672 } 0673 } 0674 0675 NormalizedRect Annotation::boundingRectangle() const 0676 { 0677 Q_D(const Annotation); 0678 return d->m_boundary; 0679 } 0680 0681 NormalizedRect Annotation::transformedBoundingRectangle() const 0682 { 0683 Q_D(const Annotation); 0684 return d->m_transformedBoundary; 0685 } 0686 0687 void Annotation::translate(const NormalizedPoint &coord) 0688 { 0689 Q_D(Annotation); 0690 d->translate(coord); 0691 d->resetTransformation(); 0692 if (d->m_page) { 0693 d->transform(d->m_page->rotationMatrix()); 0694 } 0695 } 0696 0697 void Annotation::adjust(const NormalizedPoint &deltaCoord1, const NormalizedPoint &deltaCoord2) 0698 { 0699 Q_D(Annotation); 0700 d->adjust(deltaCoord1, deltaCoord2); 0701 d->resetTransformation(); 0702 if (d->m_page) { 0703 d->transform(d->m_page->rotationMatrix()); 0704 } 0705 } 0706 0707 bool Annotation::openDialogAfterCreation() const 0708 { 0709 Q_D(const Annotation); 0710 return d->openDialogAfterCreation(); 0711 } 0712 0713 Annotation::Style &Annotation::style() 0714 { 0715 Q_D(Annotation); 0716 return d->m_style; 0717 } 0718 0719 const Annotation::Style &Annotation::style() const 0720 { 0721 Q_D(const Annotation); 0722 return d->m_style; 0723 } 0724 0725 Annotation::Window &Annotation::window() 0726 { 0727 Q_D(Annotation); 0728 return d->m_window; 0729 } 0730 0731 const Annotation::Window &Annotation::window() const 0732 { 0733 Q_D(const Annotation); 0734 return d->m_window; 0735 } 0736 0737 QList<Annotation::Revision> &Annotation::revisions() 0738 { 0739 Q_D(Annotation); 0740 return d->m_revisions; 0741 } 0742 0743 const QList<Annotation::Revision> &Annotation::revisions() const 0744 { 0745 Q_D(const Annotation); 0746 return d->m_revisions; 0747 } 0748 0749 void Annotation::setNativeId(const QVariant &id) 0750 { 0751 Q_D(Annotation); 0752 d->m_nativeId = id; 0753 } 0754 0755 QVariant Annotation::nativeId() const 0756 { 0757 Q_D(const Annotation); 0758 return d->m_nativeId; 0759 } 0760 0761 void Annotation::setDisposeDataFunction(DisposeDataFunction func) 0762 { 0763 Q_D(Annotation); 0764 d->m_disposeFunc = func; 0765 } 0766 0767 bool Annotation::canBeMoved() const 0768 { 0769 Q_D(const Annotation); 0770 0771 // Don't move annotations if they cannot be modified 0772 if (!d->m_page || !d->m_page->m_doc->m_parent->canModifyPageAnnotation(this)) { 0773 return false; 0774 } 0775 0776 // highlight "requires" to be "bounded" to text, and that's tricky for now 0777 if (subType() == AHighlight) { 0778 return false; 0779 } 0780 0781 return true; 0782 } 0783 0784 bool Annotation::canBeResized() const 0785 { 0786 Q_D(const Annotation); 0787 0788 // Don't resize annotations if they cannot be modified 0789 if (!d->m_page || !d->m_page->m_doc->m_parent->canModifyPageAnnotation(this)) { 0790 return false; 0791 } 0792 0793 return d->canBeResized(); 0794 } 0795 0796 void Annotation::store(QDomNode &annNode, QDomDocument &document) const 0797 { 0798 Q_D(const Annotation); 0799 // create [base] element of the annotation node 0800 QDomElement e = document.createElement(QStringLiteral("base")); 0801 annNode.appendChild(e); 0802 0803 // store -contents- attributes 0804 if (!d->m_author.isEmpty()) { 0805 e.setAttribute(QStringLiteral("author"), d->m_author); 0806 } 0807 if (!d->m_contents.isEmpty()) { 0808 e.setAttribute(QStringLiteral("contents"), d->m_contents); 0809 } 0810 if (!d->m_uniqueName.isEmpty()) { 0811 e.setAttribute(QStringLiteral("uniqueName"), d->m_uniqueName); 0812 } 0813 if (d->m_modifyDate.isValid()) { 0814 e.setAttribute(QStringLiteral("modifyDate"), d->m_modifyDate.toString(Qt::ISODate)); 0815 } 0816 if (d->m_creationDate.isValid()) { 0817 e.setAttribute(QStringLiteral("creationDate"), d->m_creationDate.toString(Qt::ISODate)); 0818 } 0819 0820 // store -other- attributes 0821 if (d->m_flags) { // Strip internal flags 0822 e.setAttribute(QStringLiteral("flags"), d->m_flags & ~(External | ExternallyDrawn | BeingMoved | BeingResized)); 0823 } 0824 if (d->m_style.color().isValid()) { 0825 e.setAttribute(QStringLiteral("color"), d->m_style.color().name(QColor::HexArgb)); 0826 } 0827 if (d->m_style.opacity() != 1.0) { 0828 e.setAttribute(QStringLiteral("opacity"), QString::number(d->m_style.opacity())); 0829 } 0830 0831 // Sub-Node-1 - boundary 0832 QDomElement bE = document.createElement(QStringLiteral("boundary")); 0833 e.appendChild(bE); 0834 bE.setAttribute(QStringLiteral("l"), QString::number(d->m_boundary.left)); 0835 bE.setAttribute(QStringLiteral("t"), QString::number(d->m_boundary.top)); 0836 bE.setAttribute(QStringLiteral("r"), QString::number(d->m_boundary.right)); 0837 bE.setAttribute(QStringLiteral("b"), QString::number(d->m_boundary.bottom)); 0838 0839 // Sub-Node-2 - penStyle 0840 if (d->m_style.width() != 1 || d->m_style.lineStyle() != Solid || d->m_style.xCorners() != 0 || d->m_style.yCorners() != 0.0 || d->m_style.marks() != 3 || d->m_style.spaces() != 0) { 0841 QDomElement psE = document.createElement(QStringLiteral("penStyle")); 0842 e.appendChild(psE); 0843 psE.setAttribute(QStringLiteral("width"), QString::number(d->m_style.width())); 0844 psE.setAttribute(QStringLiteral("style"), (int)d->m_style.lineStyle()); 0845 psE.setAttribute(QStringLiteral("xcr"), QString::number(d->m_style.xCorners())); 0846 psE.setAttribute(QStringLiteral("ycr"), QString::number(d->m_style.yCorners())); 0847 psE.setAttribute(QStringLiteral("marks"), d->m_style.marks()); 0848 psE.setAttribute(QStringLiteral("spaces"), d->m_style.spaces()); 0849 } 0850 0851 // Sub-Node-3 - penEffect 0852 if (d->m_style.lineEffect() != NoEffect || d->m_style.effectIntensity() != 1.0) { 0853 QDomElement peE = document.createElement(QStringLiteral("penEffect")); 0854 e.appendChild(peE); 0855 peE.setAttribute(QStringLiteral("effect"), (int)d->m_style.lineEffect()); 0856 peE.setAttribute(QStringLiteral("intensity"), QString::number(d->m_style.effectIntensity())); 0857 } 0858 0859 // Sub-Node-4 - window 0860 if (d->m_window.flags() != -1 || !d->m_window.title().isEmpty() || !d->m_window.summary().isEmpty()) { 0861 QDomElement wE = document.createElement(QStringLiteral("window")); 0862 e.appendChild(wE); 0863 wE.setAttribute(QStringLiteral("flags"), d->m_window.flags()); 0864 wE.setAttribute(QStringLiteral("top"), QString::number(d->m_window.topLeft().x)); 0865 wE.setAttribute(QStringLiteral("left"), QString::number(d->m_window.topLeft().y)); 0866 wE.setAttribute(QStringLiteral("width"), d->m_window.width()); 0867 wE.setAttribute(QStringLiteral("height"), d->m_window.height()); 0868 wE.setAttribute(QStringLiteral("title"), d->m_window.title()); 0869 wE.setAttribute(QStringLiteral("summary"), d->m_window.summary()); 0870 } 0871 0872 // create [revision] element of the annotation node (if any) 0873 if (d->m_revisions.isEmpty()) { 0874 return; 0875 } 0876 0877 // add all revisions as children of revisions element 0878 for (const Revision &revision : std::as_const(d->m_revisions)) { 0879 // create revision element 0880 QDomElement r = document.createElement(QStringLiteral("revision")); 0881 annNode.appendChild(r); 0882 // set element attributes 0883 r.setAttribute(QStringLiteral("revScope"), (int)revision.scope()); 0884 r.setAttribute(QStringLiteral("revType"), (int)revision.type()); 0885 // use revision as the annotation element, so fill it up 0886 AnnotationUtils::storeAnnotation(revision.annotation(), r, document); 0887 } 0888 } 0889 0890 QDomNode Annotation::getAnnotationPropertiesDomNode() const 0891 { 0892 QDomDocument doc(QStringLiteral("documentInfo")); 0893 QDomElement node = doc.createElement(QStringLiteral("annotation")); 0894 0895 store(node, doc); 0896 return node; 0897 } 0898 0899 void Annotation::setAnnotationProperties(const QDomNode &node) 0900 { 0901 // Save off internal properties that aren't contained in node 0902 Okular::PagePrivate *p = d_ptr->m_page; 0903 QVariant nativeID = d_ptr->m_nativeId; 0904 const int internalFlags = d_ptr->m_flags & (External | ExternallyDrawn | BeingMoved | BeingResized); 0905 Annotation::DisposeDataFunction disposeFunc = d_ptr->m_disposeFunc; 0906 0907 // Replace AnnotationPrivate object with a fresh copy 0908 AnnotationPrivate *new_d_ptr = d_ptr->getNewAnnotationPrivate(); 0909 delete (d_ptr); 0910 d_ptr = new_d_ptr; 0911 0912 // Set the annotations properties from node 0913 d_ptr->setAnnotationProperties(node); 0914 0915 // Restore internal properties 0916 d_ptr->m_page = p; 0917 d_ptr->m_nativeId = nativeID; 0918 d_ptr->m_flags = d_ptr->m_flags | internalFlags; 0919 d_ptr->m_disposeFunc = disposeFunc; 0920 0921 // Transform annotation to current page rotation 0922 d_ptr->transform(d_ptr->m_page->rotationMatrix()); 0923 } 0924 0925 double AnnotationPrivate::distanceSqr(double x, double y, double xScale, double yScale) const 0926 { 0927 return m_transformedBoundary.distanceSqr(x, y, xScale, yScale); 0928 } 0929 0930 void AnnotationPrivate::annotationTransform(const QTransform &matrix) 0931 { 0932 resetTransformation(); 0933 transform(matrix); 0934 } 0935 0936 void AnnotationPrivate::transform(const QTransform &matrix) 0937 { 0938 m_transformedBoundary.transform(matrix); 0939 } 0940 0941 void AnnotationPrivate::baseTransform(const QTransform &matrix) 0942 { 0943 m_boundary.transform(matrix); 0944 } 0945 0946 void AnnotationPrivate::resetTransformation() 0947 { 0948 m_transformedBoundary = m_boundary; 0949 } 0950 0951 void AnnotationPrivate::translate(const NormalizedPoint &coord) 0952 { 0953 m_boundary.left = m_boundary.left + coord.x; 0954 m_boundary.right = m_boundary.right + coord.x; 0955 m_boundary.top = m_boundary.top + coord.y; 0956 m_boundary.bottom = m_boundary.bottom + coord.y; 0957 } 0958 0959 void AnnotationPrivate::adjust(const NormalizedPoint &deltaCoord1, const NormalizedPoint &deltaCoord2) 0960 { 0961 m_boundary.left = m_boundary.left + qBound(-m_boundary.left, deltaCoord1.x, m_boundary.right - m_boundary.left); 0962 m_boundary.top = m_boundary.top + qBound(-m_boundary.top, deltaCoord1.y, m_boundary.bottom - m_boundary.top); 0963 ; 0964 m_boundary.right = m_boundary.right + qBound(m_boundary.left - m_boundary.right, deltaCoord2.x, 1. - m_boundary.right); 0965 m_boundary.bottom = m_boundary.bottom + qBound(m_boundary.top - m_boundary.bottom, deltaCoord2.y, 1. - m_boundary.bottom); 0966 } 0967 0968 bool AnnotationPrivate::openDialogAfterCreation() const 0969 { 0970 return false; 0971 } 0972 0973 void AnnotationPrivate::setAnnotationProperties(const QDomNode &node) 0974 { 0975 // get the [base] element of the annotation node 0976 QDomElement e = AnnotationUtils::findChildElement(node, QStringLiteral("base")); 0977 if (e.isNull()) { 0978 return; 0979 } 0980 0981 // parse -contents- attributes 0982 if (e.hasAttribute(QStringLiteral("author"))) { 0983 m_author = e.attribute(QStringLiteral("author")); 0984 } 0985 if (e.hasAttribute(QStringLiteral("contents"))) { 0986 m_contents = e.attribute(QStringLiteral("contents")); 0987 } 0988 if (e.hasAttribute(QStringLiteral("uniqueName"))) { 0989 m_uniqueName = e.attribute(QStringLiteral("uniqueName")); 0990 } 0991 if (e.hasAttribute(QStringLiteral("modifyDate"))) { 0992 m_modifyDate = QDateTime::fromString(e.attribute(QStringLiteral("modifyDate")), Qt::ISODate); 0993 } 0994 if (e.hasAttribute(QStringLiteral("creationDate"))) { 0995 m_creationDate = QDateTime::fromString(e.attribute(QStringLiteral("creationDate")), Qt::ISODate); 0996 } 0997 0998 // parse -other- attributes 0999 if (e.hasAttribute(QStringLiteral("flags"))) { 1000 m_flags = e.attribute(QStringLiteral("flags")).toInt(); 1001 } 1002 if (e.hasAttribute(QStringLiteral("color"))) { 1003 m_style.setColor(QColor(e.attribute(QStringLiteral("color")))); 1004 } 1005 if (e.hasAttribute(QStringLiteral("opacity"))) { 1006 m_style.setOpacity(e.attribute(QStringLiteral("opacity")).toDouble()); 1007 } 1008 1009 // parse -the-subnodes- (describing Style, Window, Revision(s) structures) 1010 // Note: all subnodes if present must be 'attributes complete' 1011 QDomNode eSubNode = e.firstChild(); 1012 while (eSubNode.isElement()) { 1013 QDomElement ee = eSubNode.toElement(); 1014 eSubNode = eSubNode.nextSibling(); 1015 1016 // parse boundary 1017 if (ee.tagName() == QLatin1String("boundary")) { 1018 m_boundary = NormalizedRect(ee.attribute(QStringLiteral("l")).toDouble(), ee.attribute(QStringLiteral("t")).toDouble(), ee.attribute(QStringLiteral("r")).toDouble(), ee.attribute(QStringLiteral("b")).toDouble()); 1019 } 1020 // parse penStyle if not default 1021 else if (ee.tagName() == QLatin1String("penStyle")) { 1022 m_style.setWidth(ee.attribute(QStringLiteral("width")).toDouble()); 1023 m_style.setLineStyle((Annotation::LineStyle)ee.attribute(QStringLiteral("style")).toInt()); 1024 m_style.setXCorners(ee.attribute(QStringLiteral("xcr")).toDouble()); 1025 m_style.setYCorners(ee.attribute(QStringLiteral("ycr")).toDouble()); 1026 m_style.setMarks(ee.attribute(QStringLiteral("marks")).toInt()); 1027 m_style.setSpaces(ee.attribute(QStringLiteral("spaces")).toInt()); 1028 } 1029 // parse effectStyle if not default 1030 else if (ee.tagName() == QLatin1String("penEffect")) { 1031 m_style.setLineEffect((Annotation::LineEffect)ee.attribute(QStringLiteral("effect")).toInt()); 1032 m_style.setEffectIntensity(ee.attribute(QStringLiteral("intensity")).toDouble()); 1033 } 1034 // parse window if present 1035 else if (ee.tagName() == QLatin1String("window")) { 1036 m_window.setFlags(ee.attribute(QStringLiteral("flags")).toInt()); 1037 m_window.setTopLeft(NormalizedPoint(ee.attribute(QStringLiteral("top")).toDouble(), ee.attribute(QStringLiteral("left")).toDouble())); 1038 m_window.setWidth(ee.attribute(QStringLiteral("width")).toInt()); 1039 m_window.setHeight(ee.attribute(QStringLiteral("height")).toInt()); 1040 m_window.setTitle(ee.attribute(QStringLiteral("title"))); 1041 m_window.setSummary(ee.attribute(QStringLiteral("summary"))); 1042 } 1043 } 1044 1045 // get the [revisions] element of the annotation node 1046 QDomNode revNode = node.firstChild(); 1047 for (; revNode.isElement(); revNode = revNode.nextSibling()) { 1048 QDomElement revElement = revNode.toElement(); 1049 if (revElement.tagName() != QLatin1String("revision")) { 1050 continue; 1051 } 1052 1053 // compile the Revision structure crating annotation 1054 Annotation::Revision revision; 1055 revision.setScope((Annotation::RevisionScope)revElement.attribute(QStringLiteral("revScope")).toInt()); 1056 revision.setType((Annotation::RevisionType)revElement.attribute(QStringLiteral("revType")).toInt()); 1057 revision.setAnnotation(AnnotationUtils::createAnnotation(revElement)); 1058 1059 // if annotation is valid, add revision to internal list 1060 if (revision.annotation()) { 1061 m_revisions.append(revision); 1062 } 1063 } 1064 1065 m_transformedBoundary = m_boundary; 1066 } 1067 1068 bool AnnotationPrivate::canBeResized() const 1069 { 1070 return false; 1071 } 1072 1073 // END Annotation implementation 1074 1075 /** TextAnnotation [Annotation] */ 1076 1077 class Okular::TextAnnotationPrivate : public Okular::AnnotationPrivate 1078 { 1079 public: 1080 TextAnnotationPrivate() 1081 : AnnotationPrivate() 1082 , m_textType(TextAnnotation::Linked) 1083 , m_textIcon(QStringLiteral("Comment")) 1084 , m_inplaceAlign(0) 1085 , m_inplaceIntent(TextAnnotation::Unknown) 1086 { 1087 } 1088 1089 void transform(const QTransform &matrix) override; 1090 void baseTransform(const QTransform &matrix) override; 1091 void resetTransformation() override; 1092 void translate(const NormalizedPoint &coord) override; 1093 bool openDialogAfterCreation() const override; 1094 void setAnnotationProperties(const QDomNode &node) override; 1095 bool canBeResized() const override; 1096 AnnotationPrivate *getNewAnnotationPrivate() override; 1097 1098 TextAnnotation::TextType m_textType; 1099 QString m_textIcon; 1100 QFont m_textFont; 1101 QColor m_textColor; 1102 int m_inplaceAlign; 1103 NormalizedPoint m_inplaceCallout[3]; 1104 NormalizedPoint m_transformedInplaceCallout[3]; 1105 TextAnnotation::InplaceIntent m_inplaceIntent; 1106 }; 1107 1108 /* 1109 The default textIcon for text annotation is Note as the PDF Reference says 1110 */ 1111 TextAnnotation::TextAnnotation() 1112 : Annotation(*new TextAnnotationPrivate()) 1113 { 1114 } 1115 1116 TextAnnotation::TextAnnotation(const QDomNode &description) 1117 : Annotation(*new TextAnnotationPrivate(), description) 1118 { 1119 } 1120 1121 TextAnnotation::~TextAnnotation() 1122 { 1123 } 1124 1125 void TextAnnotation::setTextType(TextType textType) 1126 { 1127 Q_D(TextAnnotation); 1128 d->m_textType = textType; 1129 } 1130 1131 TextAnnotation::TextType TextAnnotation::textType() const 1132 { 1133 Q_D(const TextAnnotation); 1134 return d->m_textType; 1135 } 1136 1137 void TextAnnotation::setTextIcon(const QString &icon) 1138 { 1139 Q_D(TextAnnotation); 1140 d->m_textIcon = icon; 1141 } 1142 1143 QString TextAnnotation::textIcon() const 1144 { 1145 Q_D(const TextAnnotation); 1146 return d->m_textIcon; 1147 } 1148 1149 void TextAnnotation::setTextFont(const QFont &font) 1150 { 1151 Q_D(TextAnnotation); 1152 d->m_textFont = font; 1153 } 1154 1155 QFont TextAnnotation::textFont() const 1156 { 1157 Q_D(const TextAnnotation); 1158 return d->m_textFont; 1159 } 1160 1161 void TextAnnotation::setTextColor(const QColor &color) 1162 { 1163 Q_D(TextAnnotation); 1164 d->m_textColor = color; 1165 } 1166 1167 QColor TextAnnotation::textColor() const 1168 { 1169 Q_D(const TextAnnotation); 1170 return d->m_textColor; 1171 } 1172 1173 void TextAnnotation::setInplaceAlignment(int alignment) 1174 { 1175 Q_D(TextAnnotation); 1176 d->m_inplaceAlign = alignment; 1177 } 1178 1179 int TextAnnotation::inplaceAlignment() const 1180 { 1181 Q_D(const TextAnnotation); 1182 return d->m_inplaceAlign; 1183 } 1184 1185 void TextAnnotation::setInplaceCallout(const NormalizedPoint &point, int index) 1186 { 1187 if (index < 0 || index > 2) { 1188 return; 1189 } 1190 1191 Q_D(TextAnnotation); 1192 d->m_inplaceCallout[index] = point; 1193 } 1194 1195 NormalizedPoint TextAnnotation::inplaceCallout(int index) const 1196 { 1197 if (index < 0 || index > 2) { 1198 return NormalizedPoint(); 1199 } 1200 1201 Q_D(const TextAnnotation); 1202 return d->m_inplaceCallout[index]; 1203 } 1204 1205 NormalizedPoint TextAnnotation::transformedInplaceCallout(int index) const 1206 { 1207 if (index < 0 || index > 2) { 1208 return NormalizedPoint(); 1209 } 1210 1211 Q_D(const TextAnnotation); 1212 return d->m_transformedInplaceCallout[index]; 1213 } 1214 1215 void TextAnnotation::setInplaceIntent(InplaceIntent intent) 1216 { 1217 Q_D(TextAnnotation); 1218 d->m_inplaceIntent = intent; 1219 } 1220 1221 TextAnnotation::InplaceIntent TextAnnotation::inplaceIntent() const 1222 { 1223 Q_D(const TextAnnotation); 1224 return d->m_inplaceIntent; 1225 } 1226 1227 Annotation::SubType TextAnnotation::subType() const 1228 { 1229 return AText; 1230 } 1231 1232 void TextAnnotation::store(QDomNode &node, QDomDocument &document) const 1233 { 1234 Q_D(const TextAnnotation); 1235 // recurse to parent objects storing properties 1236 Annotation::store(node, document); 1237 1238 // create [text] element 1239 QDomElement textElement = document.createElement(QStringLiteral("text")); 1240 node.appendChild(textElement); 1241 1242 // store the optional attributes 1243 if (d->m_textType != Linked) { 1244 textElement.setAttribute(QStringLiteral("type"), (int)d->m_textType); 1245 } 1246 if (!d->m_textIcon.isEmpty()) { 1247 textElement.setAttribute(QStringLiteral("icon"), d->m_textIcon); 1248 } 1249 if (d->m_textFont != QApplication::font()) { 1250 textElement.setAttribute(QStringLiteral("font"), d->m_textFont.toString()); 1251 } 1252 if (d->m_textColor.isValid()) { 1253 textElement.setAttribute(QStringLiteral("fontColor"), d->m_textColor.name()); 1254 } 1255 if (d->m_inplaceAlign) { 1256 textElement.setAttribute(QStringLiteral("align"), d->m_inplaceAlign); 1257 } 1258 if (d->m_inplaceIntent != Unknown) { 1259 textElement.setAttribute(QStringLiteral("intent"), (int)d->m_inplaceIntent); 1260 } 1261 1262 // Sub-Node - callout 1263 if (d->m_inplaceCallout[0].x != 0.0) { 1264 QDomElement calloutElement = document.createElement(QStringLiteral("callout")); 1265 textElement.appendChild(calloutElement); 1266 calloutElement.setAttribute(QStringLiteral("ax"), QString::number(d->m_inplaceCallout[0].x)); 1267 calloutElement.setAttribute(QStringLiteral("ay"), QString::number(d->m_inplaceCallout[0].y)); 1268 calloutElement.setAttribute(QStringLiteral("bx"), QString::number(d->m_inplaceCallout[1].x)); 1269 calloutElement.setAttribute(QStringLiteral("by"), QString::number(d->m_inplaceCallout[1].y)); 1270 calloutElement.setAttribute(QStringLiteral("cx"), QString::number(d->m_inplaceCallout[2].x)); 1271 calloutElement.setAttribute(QStringLiteral("cy"), QString::number(d->m_inplaceCallout[2].y)); 1272 } 1273 } 1274 1275 void TextAnnotationPrivate::transform(const QTransform &matrix) 1276 { 1277 AnnotationPrivate::transform(matrix); 1278 1279 for (NormalizedPoint &np : m_transformedInplaceCallout) { 1280 np.transform(matrix); 1281 } 1282 } 1283 1284 void TextAnnotationPrivate::baseTransform(const QTransform &matrix) 1285 { 1286 AnnotationPrivate::baseTransform(matrix); 1287 1288 for (NormalizedPoint &np : m_inplaceCallout) { 1289 np.transform(matrix); 1290 } 1291 } 1292 1293 void TextAnnotationPrivate::resetTransformation() 1294 { 1295 AnnotationPrivate::resetTransformation(); 1296 1297 for (int i = 0; i < 3; ++i) { 1298 m_transformedInplaceCallout[i] = m_inplaceCallout[i]; 1299 } 1300 } 1301 1302 void TextAnnotationPrivate::translate(const NormalizedPoint &coord) 1303 { 1304 AnnotationPrivate::translate(coord); 1305 1306 #define ADD_COORD(c1, c2) \ 1307 { \ 1308 c1.x = c1.x + c2.x; \ 1309 c1.y = c1.y + c2.y; \ 1310 } 1311 ADD_COORD(m_inplaceCallout[0], coord) 1312 ADD_COORD(m_inplaceCallout[1], coord) 1313 ADD_COORD(m_inplaceCallout[2], coord) 1314 #undef ADD_COORD 1315 } 1316 1317 bool TextAnnotationPrivate::openDialogAfterCreation() const 1318 { 1319 return (m_textType == Okular::TextAnnotation::Linked) || (m_inplaceIntent == TextAnnotation::InplaceIntent::Unknown); 1320 } 1321 1322 void TextAnnotationPrivate::setAnnotationProperties(const QDomNode &node) 1323 { 1324 Okular::AnnotationPrivate::setAnnotationProperties(node); 1325 1326 // loop through the whole children looking for a 'text' element 1327 QDomNode subNode = node.firstChild(); 1328 while (subNode.isElement()) { 1329 QDomElement e = subNode.toElement(); 1330 subNode = subNode.nextSibling(); 1331 if (e.tagName() != QLatin1String("text")) { 1332 continue; 1333 } 1334 1335 // parse the attributes 1336 if (e.hasAttribute(QStringLiteral("type"))) { 1337 m_textType = (TextAnnotation::TextType)e.attribute(QStringLiteral("type")).toInt(); 1338 } 1339 if (e.hasAttribute(QStringLiteral("icon"))) { 1340 m_textIcon = e.attribute(QStringLiteral("icon")); 1341 } 1342 if (e.hasAttribute(QStringLiteral("font"))) { 1343 m_textFont.fromString(e.attribute(QStringLiteral("font"))); 1344 } 1345 if (e.hasAttribute(QStringLiteral("fontColor"))) { 1346 m_textColor = QColor(e.attribute(QStringLiteral("fontColor"))); 1347 } 1348 if (e.hasAttribute(QStringLiteral("align"))) { 1349 m_inplaceAlign = e.attribute(QStringLiteral("align")).toInt(); 1350 } 1351 if (e.hasAttribute(QStringLiteral("intent"))) { 1352 m_inplaceIntent = (TextAnnotation::InplaceIntent)e.attribute(QStringLiteral("intent")).toInt(); 1353 } 1354 1355 // parse the subnodes 1356 QDomNode eSubNode = e.firstChild(); 1357 while (eSubNode.isElement()) { 1358 QDomElement ee = eSubNode.toElement(); 1359 eSubNode = eSubNode.nextSibling(); 1360 1361 if (ee.tagName() == QLatin1String("escapedText")) { 1362 m_contents = ee.firstChild().toCDATASection().data(); 1363 } else if (ee.tagName() == QLatin1String("callout")) { 1364 m_inplaceCallout[0].x = ee.attribute(QStringLiteral("ax")).toDouble(); 1365 m_inplaceCallout[0].y = ee.attribute(QStringLiteral("ay")).toDouble(); 1366 m_inplaceCallout[1].x = ee.attribute(QStringLiteral("bx")).toDouble(); 1367 m_inplaceCallout[1].y = ee.attribute(QStringLiteral("by")).toDouble(); 1368 m_inplaceCallout[2].x = ee.attribute(QStringLiteral("cx")).toDouble(); 1369 m_inplaceCallout[2].y = ee.attribute(QStringLiteral("cy")).toDouble(); 1370 } 1371 } 1372 1373 // loading complete 1374 break; 1375 } 1376 1377 for (int i = 0; i < 3; ++i) { 1378 m_transformedInplaceCallout[i] = m_inplaceCallout[i]; 1379 } 1380 } 1381 1382 bool TextAnnotationPrivate::canBeResized() const 1383 { 1384 if (m_textType != TextAnnotation::Linked) { 1385 return true; 1386 } 1387 return false; 1388 } 1389 1390 AnnotationPrivate *TextAnnotationPrivate::getNewAnnotationPrivate() 1391 { 1392 return new TextAnnotationPrivate(); 1393 } 1394 1395 /** LineAnnotation [Annotation] */ 1396 1397 class Okular::LineAnnotationPrivate : public Okular::AnnotationPrivate 1398 { 1399 public: 1400 LineAnnotationPrivate() 1401 : AnnotationPrivate() 1402 , m_lineStartStyle(LineAnnotation::None) 1403 , m_lineEndStyle(LineAnnotation::None) 1404 , m_lineClosed(false) 1405 , m_lineShowCaption(false) 1406 , m_lineLeadingFwdPt(0) 1407 , m_lineLeadingBackPt(0) 1408 , m_lineIntent(LineAnnotation::Unknown) 1409 { 1410 } 1411 1412 void transform(const QTransform &matrix) override; 1413 void baseTransform(const QTransform &matrix) override; 1414 void resetTransformation() override; 1415 void translate(const NormalizedPoint &coord) override; 1416 double distanceSqr(double x, double y, double xScale, double yScale) const override; 1417 void setAnnotationProperties(const QDomNode &node) override; 1418 AnnotationPrivate *getNewAnnotationPrivate() override; 1419 1420 QList<NormalizedPoint> m_linePoints; 1421 QList<NormalizedPoint> m_transformedLinePoints; 1422 LineAnnotation::TermStyle m_lineStartStyle; 1423 LineAnnotation::TermStyle m_lineEndStyle; 1424 bool m_lineClosed : 1; 1425 bool m_lineShowCaption : 1; 1426 QColor m_lineInnerColor; 1427 double m_lineLeadingFwdPt; 1428 double m_lineLeadingBackPt; 1429 LineAnnotation::LineIntent m_lineIntent; 1430 }; 1431 1432 LineAnnotation::LineAnnotation() 1433 : Annotation(*new LineAnnotationPrivate()) 1434 { 1435 } 1436 1437 LineAnnotation::LineAnnotation(const QDomNode &description) 1438 : Annotation(*new LineAnnotationPrivate(), description) 1439 { 1440 } 1441 1442 LineAnnotation::~LineAnnotation() 1443 { 1444 } 1445 1446 void LineAnnotation::setLinePoints(const QList<NormalizedPoint> &points) 1447 { 1448 Q_D(LineAnnotation); 1449 d->m_linePoints = points; 1450 } 1451 1452 QList<NormalizedPoint> LineAnnotation::linePoints() const 1453 { 1454 Q_D(const LineAnnotation); 1455 return d->m_linePoints; 1456 } 1457 1458 QList<NormalizedPoint> LineAnnotation::transformedLinePoints() const 1459 { 1460 Q_D(const LineAnnotation); 1461 return d->m_transformedLinePoints; 1462 } 1463 1464 void LineAnnotation::setLineStartStyle(TermStyle style) 1465 { 1466 Q_D(LineAnnotation); 1467 d->m_lineStartStyle = style; 1468 } 1469 1470 LineAnnotation::TermStyle LineAnnotation::lineStartStyle() const 1471 { 1472 Q_D(const LineAnnotation); 1473 return d->m_lineStartStyle; 1474 } 1475 1476 void LineAnnotation::setLineEndStyle(TermStyle style) 1477 { 1478 Q_D(LineAnnotation); 1479 d->m_lineEndStyle = style; 1480 } 1481 1482 LineAnnotation::TermStyle LineAnnotation::lineEndStyle() const 1483 { 1484 Q_D(const LineAnnotation); 1485 return d->m_lineEndStyle; 1486 } 1487 1488 void LineAnnotation::setLineClosed(bool closed) 1489 { 1490 Q_D(LineAnnotation); 1491 d->m_lineClosed = closed; 1492 } 1493 1494 bool LineAnnotation::lineClosed() const 1495 { 1496 Q_D(const LineAnnotation); 1497 return d->m_lineClosed; 1498 } 1499 1500 void LineAnnotation::setLineInnerColor(const QColor &color) 1501 { 1502 Q_D(LineAnnotation); 1503 d->m_lineInnerColor = color; 1504 } 1505 1506 QColor LineAnnotation::lineInnerColor() const 1507 { 1508 Q_D(const LineAnnotation); 1509 return d->m_lineInnerColor; 1510 } 1511 1512 void LineAnnotation::setLineLeadingForwardPoint(double point) 1513 { 1514 Q_D(LineAnnotation); 1515 d->m_lineLeadingFwdPt = point; 1516 } 1517 1518 double LineAnnotation::lineLeadingForwardPoint() const 1519 { 1520 Q_D(const LineAnnotation); 1521 return d->m_lineLeadingFwdPt; 1522 } 1523 1524 void LineAnnotation::setLineLeadingBackwardPoint(double point) 1525 { 1526 Q_D(LineAnnotation); 1527 d->m_lineLeadingBackPt = point; 1528 } 1529 1530 double LineAnnotation::lineLeadingBackwardPoint() const 1531 { 1532 Q_D(const LineAnnotation); 1533 return d->m_lineLeadingBackPt; 1534 } 1535 1536 void LineAnnotation::setShowCaption(bool show) 1537 { 1538 Q_D(LineAnnotation); 1539 d->m_lineShowCaption = show; 1540 } 1541 1542 bool LineAnnotation::showCaption() const 1543 { 1544 Q_D(const LineAnnotation); 1545 return d->m_lineShowCaption; 1546 } 1547 1548 void LineAnnotation::setLineIntent(LineIntent intent) 1549 { 1550 Q_D(LineAnnotation); 1551 d->m_lineIntent = intent; 1552 } 1553 1554 LineAnnotation::LineIntent LineAnnotation::lineIntent() const 1555 { 1556 Q_D(const LineAnnotation); 1557 return d->m_lineIntent; 1558 } 1559 1560 Annotation::SubType LineAnnotation::subType() const 1561 { 1562 return ALine; 1563 } 1564 1565 void LineAnnotation::store(QDomNode &node, QDomDocument &document) const 1566 { 1567 Q_D(const LineAnnotation); 1568 // recurse to parent objects storing properties 1569 Annotation::store(node, document); 1570 1571 // create [line] element 1572 QDomElement lineElement = document.createElement(QStringLiteral("line")); 1573 node.appendChild(lineElement); 1574 1575 // store the attributes 1576 if (d->m_lineStartStyle != None) { 1577 lineElement.setAttribute(QStringLiteral("startStyle"), (int)d->m_lineStartStyle); 1578 } 1579 if (d->m_lineEndStyle != None) { 1580 lineElement.setAttribute(QStringLiteral("endStyle"), (int)d->m_lineEndStyle); 1581 } 1582 if (d->m_lineClosed) { 1583 lineElement.setAttribute(QStringLiteral("closed"), d->m_lineClosed); 1584 } 1585 if (d->m_lineInnerColor.isValid()) { 1586 lineElement.setAttribute(QStringLiteral("innerColor"), d->m_lineInnerColor.name()); 1587 } 1588 if (d->m_lineLeadingFwdPt != 0.0) { 1589 lineElement.setAttribute(QStringLiteral("leadFwd"), QString::number(d->m_lineLeadingFwdPt)); 1590 } 1591 if (d->m_lineLeadingBackPt != 0.0) { 1592 lineElement.setAttribute(QStringLiteral("leadBack"), QString::number(d->m_lineLeadingBackPt)); 1593 } 1594 if (d->m_lineShowCaption) { 1595 lineElement.setAttribute(QStringLiteral("showCaption"), d->m_lineShowCaption); 1596 } 1597 if (d->m_lineIntent != Unknown) { 1598 lineElement.setAttribute(QStringLiteral("intent"), d->m_lineIntent); 1599 } 1600 1601 // append the list of points 1602 int points = d->m_linePoints.count(); 1603 if (points > 1) { 1604 QList<NormalizedPoint>::const_iterator it = d->m_linePoints.begin(), end = d->m_linePoints.end(); 1605 while (it != end) { 1606 const NormalizedPoint &p = *it; 1607 QDomElement pElement = document.createElement(QStringLiteral("point")); 1608 lineElement.appendChild(pElement); 1609 pElement.setAttribute(QStringLiteral("x"), QString::number(p.x)); 1610 pElement.setAttribute(QStringLiteral("y"), QString::number(p.y)); 1611 it++; // to avoid loop 1612 } 1613 } 1614 } 1615 1616 void LineAnnotationPrivate::transform(const QTransform &matrix) 1617 { 1618 AnnotationPrivate::transform(matrix); 1619 1620 QMutableListIterator<NormalizedPoint> it(m_transformedLinePoints); 1621 while (it.hasNext()) { 1622 it.next().transform(matrix); 1623 } 1624 } 1625 1626 void LineAnnotationPrivate::baseTransform(const QTransform &matrix) 1627 { 1628 AnnotationPrivate::baseTransform(matrix); 1629 1630 QMutableListIterator<NormalizedPoint> it(m_linePoints); 1631 while (it.hasNext()) { 1632 it.next().transform(matrix); 1633 } 1634 } 1635 1636 void LineAnnotationPrivate::resetTransformation() 1637 { 1638 AnnotationPrivate::resetTransformation(); 1639 1640 m_transformedLinePoints = m_linePoints; 1641 } 1642 1643 void LineAnnotationPrivate::translate(const NormalizedPoint &coord) 1644 { 1645 AnnotationPrivate::translate(coord); 1646 1647 QMutableListIterator<NormalizedPoint> it(m_linePoints); 1648 while (it.hasNext()) { 1649 NormalizedPoint &p = it.next(); 1650 p.x = p.x + coord.x; 1651 p.y = p.y + coord.y; 1652 } 1653 } 1654 1655 void LineAnnotationPrivate::setAnnotationProperties(const QDomNode &node) 1656 { 1657 Okular::AnnotationPrivate::setAnnotationProperties(node); 1658 1659 // loop through the whole children looking for a 'line' element 1660 QDomNode subNode = node.firstChild(); 1661 while (subNode.isElement()) { 1662 QDomElement e = subNode.toElement(); 1663 subNode = subNode.nextSibling(); 1664 if (e.tagName() != QLatin1String("line")) { 1665 continue; 1666 } 1667 1668 // parse the attributes 1669 if (e.hasAttribute(QStringLiteral("startStyle"))) { 1670 m_lineStartStyle = (LineAnnotation::TermStyle)e.attribute(QStringLiteral("startStyle")).toInt(); 1671 } 1672 if (e.hasAttribute(QStringLiteral("endStyle"))) { 1673 m_lineEndStyle = (LineAnnotation::TermStyle)e.attribute(QStringLiteral("endStyle")).toInt(); 1674 } 1675 if (e.hasAttribute(QStringLiteral("closed"))) { 1676 m_lineClosed = e.attribute(QStringLiteral("closed")).toInt(); 1677 } 1678 if (e.hasAttribute(QStringLiteral("innerColor"))) { 1679 m_lineInnerColor = QColor(e.attribute(QStringLiteral("innerColor"))); 1680 } 1681 if (e.hasAttribute(QStringLiteral("leadFwd"))) { 1682 m_lineLeadingFwdPt = e.attribute(QStringLiteral("leadFwd")).toDouble(); 1683 } 1684 if (e.hasAttribute(QStringLiteral("leadBack"))) { 1685 m_lineLeadingBackPt = e.attribute(QStringLiteral("leadBack")).toDouble(); 1686 } 1687 if (e.hasAttribute(QStringLiteral("showCaption"))) { 1688 m_lineShowCaption = e.attribute(QStringLiteral("showCaption")).toInt(); 1689 } 1690 if (e.hasAttribute(QStringLiteral("intent"))) { 1691 m_lineIntent = (LineAnnotation::LineIntent)e.attribute(QStringLiteral("intent")).toInt(); 1692 } 1693 1694 // parse all 'point' subnodes 1695 QDomNode pointNode = e.firstChild(); 1696 while (pointNode.isElement()) { 1697 QDomElement pe = pointNode.toElement(); 1698 pointNode = pointNode.nextSibling(); 1699 1700 if (pe.tagName() != QLatin1String("point")) { 1701 continue; 1702 } 1703 1704 NormalizedPoint p; 1705 p.x = pe.attribute(QStringLiteral("x"), QStringLiteral("0.0")).toDouble(); 1706 p.y = pe.attribute(QStringLiteral("y"), QStringLiteral("0.0")).toDouble(); 1707 m_linePoints.append(p); 1708 } 1709 1710 // loading complete 1711 break; 1712 } 1713 1714 m_transformedLinePoints = m_linePoints; 1715 } 1716 1717 AnnotationPrivate *LineAnnotationPrivate::getNewAnnotationPrivate() 1718 { 1719 return new LineAnnotationPrivate(); 1720 } 1721 1722 double LineAnnotationPrivate::distanceSqr(double x, double y, double xScale, double yScale) const 1723 { 1724 QList<NormalizedPoint> transformedLinePoints = m_transformedLinePoints; 1725 1726 if (m_lineClosed) { // Close the path 1727 transformedLinePoints.append(transformedLinePoints.first()); 1728 } 1729 1730 if (m_lineInnerColor.isValid()) { 1731 QPolygonF polygon; 1732 for (const NormalizedPoint &p : std::as_const(transformedLinePoints)) { 1733 polygon.append(QPointF(p.x, p.y)); 1734 } 1735 1736 if (polygon.containsPoint(QPointF(x, y), Qt::WindingFill)) { 1737 return 0; 1738 } 1739 } 1740 1741 return strokeDistance(::distanceSqr(x, y, xScale, yScale, transformedLinePoints), m_style.width() * xScale / (m_page->m_width * 2)); 1742 } 1743 1744 /** GeomAnnotation [Annotation] */ 1745 1746 class Okular::GeomAnnotationPrivate : public Okular::AnnotationPrivate 1747 { 1748 public: 1749 GeomAnnotationPrivate() 1750 : AnnotationPrivate() 1751 , m_geomType(GeomAnnotation::InscribedSquare) 1752 { 1753 } 1754 void setAnnotationProperties(const QDomNode &node) override; 1755 bool canBeResized() const override; 1756 AnnotationPrivate *getNewAnnotationPrivate() override; 1757 double distanceSqr(double x, double y, double xScale, double yScale) const override; 1758 1759 GeomAnnotation::GeomType m_geomType; 1760 QColor m_geomInnerColor; 1761 }; 1762 1763 GeomAnnotation::GeomAnnotation() 1764 : Annotation(*new GeomAnnotationPrivate()) 1765 { 1766 } 1767 1768 GeomAnnotation::GeomAnnotation(const QDomNode &description) 1769 : Annotation(*new GeomAnnotationPrivate(), description) 1770 { 1771 } 1772 1773 GeomAnnotation::~GeomAnnotation() 1774 { 1775 } 1776 1777 void GeomAnnotation::setGeometricalType(GeomType type) 1778 { 1779 Q_D(GeomAnnotation); 1780 d->m_geomType = type; 1781 } 1782 1783 GeomAnnotation::GeomType GeomAnnotation::geometricalType() const 1784 { 1785 Q_D(const GeomAnnotation); 1786 return d->m_geomType; 1787 } 1788 1789 void GeomAnnotation::setGeometricalInnerColor(const QColor &color) 1790 { 1791 Q_D(GeomAnnotation); 1792 d->m_geomInnerColor = color; 1793 } 1794 1795 QColor GeomAnnotation::geometricalInnerColor() const 1796 { 1797 Q_D(const GeomAnnotation); 1798 return d->m_geomInnerColor; 1799 } 1800 1801 Annotation::SubType GeomAnnotation::subType() const 1802 { 1803 return AGeom; 1804 } 1805 1806 void GeomAnnotation::store(QDomNode &node, QDomDocument &document) const 1807 { 1808 Q_D(const GeomAnnotation); 1809 // recurse to parent objects storing properties 1810 Annotation::store(node, document); 1811 1812 // create [geom] element 1813 QDomElement geomElement = document.createElement(QStringLiteral("geom")); 1814 node.appendChild(geomElement); 1815 1816 // append the optional attributes 1817 if (d->m_geomType != InscribedSquare) { 1818 geomElement.setAttribute(QStringLiteral("type"), (int)d->m_geomType); 1819 } 1820 if (d->m_geomInnerColor.isValid()) { 1821 geomElement.setAttribute(QStringLiteral("color"), d->m_geomInnerColor.name()); 1822 } 1823 } 1824 1825 void GeomAnnotationPrivate::setAnnotationProperties(const QDomNode &node) 1826 { 1827 Okular::AnnotationPrivate::setAnnotationProperties(node); 1828 // loop through the whole children looking for a 'geom' element 1829 QDomNode subNode = node.firstChild(); 1830 while (subNode.isElement()) { 1831 QDomElement e = subNode.toElement(); 1832 subNode = subNode.nextSibling(); 1833 if (e.tagName() != QLatin1String("geom")) { 1834 continue; 1835 } 1836 1837 // parse the attributes 1838 if (e.hasAttribute(QStringLiteral("type"))) { 1839 m_geomType = (GeomAnnotation::GeomType)e.attribute(QStringLiteral("type")).toInt(); 1840 } 1841 if (e.hasAttribute(QStringLiteral("color"))) { 1842 m_geomInnerColor = QColor(e.attribute(QStringLiteral("color"))); 1843 } 1844 // compatibility 1845 if (e.hasAttribute(QStringLiteral("width"))) { 1846 m_style.setWidth(e.attribute(QStringLiteral("width")).toInt()); 1847 } 1848 1849 // loading complete 1850 break; 1851 } 1852 } 1853 1854 bool GeomAnnotationPrivate::canBeResized() const 1855 { 1856 return true; 1857 } 1858 1859 AnnotationPrivate *GeomAnnotationPrivate::getNewAnnotationPrivate() 1860 { 1861 return new GeomAnnotationPrivate(); 1862 } 1863 1864 double GeomAnnotationPrivate::distanceSqr(double x, double y, double xScale, double yScale) const 1865 { 1866 double distance = 0; 1867 // the line thickness is applied unevenly (only on the "inside") - account for this 1868 bool withinShape = false; 1869 switch (m_geomType) { 1870 case GeomAnnotation::InscribedCircle: { 1871 // calculate the center point and focus lengths of the ellipse 1872 const double centerX = (m_transformedBoundary.left + m_transformedBoundary.right) / 2.0; 1873 const double centerY = (m_transformedBoundary.top + m_transformedBoundary.bottom) / 2.0; 1874 const double focusX = (m_transformedBoundary.right - centerX); 1875 const double focusY = (m_transformedBoundary.bottom - centerY); 1876 1877 const double focusXSqr = pow(focusX, 2); 1878 const double focusYSqr = pow(focusY, 2); 1879 1880 // to calculate the distance from the ellipse, we will first find the point "projection" 1881 // that lies on the ellipse and is closest to the point (x,y) 1882 // This point can obviously be written as "center + lambda(inputPoint - center)". 1883 // Because the point lies on the ellipse, we know that: 1884 // 1 = ((center.x - projection.x)/focusX)^2 + ((center.y - projection.y)/focusY)^2 1885 // After filling in projection.x = center.x + lambda * (inputPoint.x - center.x) 1886 // and its y-equivalent, we can solve for lambda: 1887 const double lambda = sqrt(focusXSqr * focusYSqr / (focusYSqr * pow(x - centerX, 2) + focusXSqr * pow(y - centerY, 2))); 1888 1889 // if the ellipse is filled, we treat all points within as "on" it 1890 if (lambda > 1) { 1891 if (m_geomInnerColor.isValid()) { 1892 return 0; 1893 } else { 1894 withinShape = true; 1895 } 1896 } 1897 1898 // otherwise we calculate the squared distance from the projected point on the ellipse 1899 NormalizedPoint projection(centerX, centerY); 1900 projection.x += lambda * (x - centerX); 1901 projection.y += lambda * (y - centerY); 1902 1903 distance = projection.distanceSqr(x, y, xScale, yScale); 1904 break; 1905 } 1906 1907 case GeomAnnotation::InscribedSquare: 1908 // if the square is filled, only check the bounding box 1909 if (m_geomInnerColor.isValid()) { 1910 return AnnotationPrivate::distanceSqr(x, y, xScale, yScale); 1911 } 1912 1913 const QList<NormalizedPoint> edges = {NormalizedPoint(m_transformedBoundary.left, m_transformedBoundary.top), 1914 NormalizedPoint(m_transformedBoundary.right, m_transformedBoundary.top), 1915 NormalizedPoint(m_transformedBoundary.right, m_transformedBoundary.bottom), 1916 NormalizedPoint(m_transformedBoundary.left, m_transformedBoundary.bottom), 1917 NormalizedPoint(m_transformedBoundary.left, m_transformedBoundary.top)}; 1918 distance = ::distanceSqr(x, y, xScale, yScale, edges); 1919 1920 if (m_transformedBoundary.contains(x, y)) { 1921 withinShape = true; 1922 } 1923 1924 break; 1925 } 1926 if (withinShape) { 1927 distance = strokeDistance(distance, m_style.width() * xScale / m_page->m_width); 1928 } 1929 1930 return distance; 1931 } 1932 1933 /** HighlightAnnotation [Annotation] */ 1934 1935 class HighlightAnnotation::Quad::Private 1936 { 1937 public: 1938 Private() 1939 : m_capStart(false) 1940 , m_capEnd(false) 1941 , m_feather(0.0) 1942 { 1943 } 1944 1945 NormalizedPoint m_points[4]; 1946 NormalizedPoint m_transformedPoints[4]; 1947 bool m_capStart : 1; 1948 bool m_capEnd : 1; 1949 double m_feather; 1950 }; 1951 1952 HighlightAnnotation::Quad::Quad() 1953 : d(new Private) 1954 { 1955 } 1956 1957 HighlightAnnotation::Quad::~Quad() 1958 { 1959 delete d; 1960 } 1961 1962 HighlightAnnotation::Quad::Quad(const Quad &other) 1963 : d(new Private) 1964 { 1965 *d = *other.d; 1966 } 1967 1968 HighlightAnnotation::Quad &HighlightAnnotation::Quad::operator=(const Quad &other) 1969 { 1970 if (this != &other) { 1971 *d = *other.d; 1972 } 1973 1974 return *this; 1975 } 1976 1977 void HighlightAnnotation::Quad::setPoint(const NormalizedPoint &point, int index) 1978 { 1979 if (index < 0 || index > 3) { 1980 return; 1981 } 1982 1983 d->m_points[index] = point; 1984 } 1985 1986 NormalizedPoint HighlightAnnotation::Quad::point(int index) const 1987 { 1988 if (index < 0 || index > 3) { 1989 return NormalizedPoint(); 1990 } 1991 1992 return d->m_points[index]; 1993 } 1994 1995 NormalizedPoint HighlightAnnotation::Quad::transformedPoint(int index) const 1996 { 1997 if (index < 0 || index > 3) { 1998 return NormalizedPoint(); 1999 } 2000 2001 return d->m_transformedPoints[index]; 2002 } 2003 2004 void HighlightAnnotation::Quad::setCapStart(bool value) 2005 { 2006 d->m_capStart = value; 2007 } 2008 2009 bool HighlightAnnotation::Quad::capStart() const 2010 { 2011 return d->m_capStart; 2012 } 2013 2014 void HighlightAnnotation::Quad::setCapEnd(bool value) 2015 { 2016 d->m_capEnd = value; 2017 } 2018 2019 bool HighlightAnnotation::Quad::capEnd() const 2020 { 2021 return d->m_capEnd; 2022 } 2023 2024 void HighlightAnnotation::Quad::setFeather(double width) 2025 { 2026 d->m_feather = width; 2027 } 2028 2029 double HighlightAnnotation::Quad::feather() const 2030 { 2031 return d->m_feather; 2032 } 2033 2034 void HighlightAnnotation::Quad::transform(const QTransform &matrix) 2035 { 2036 for (int i = 0; i < 4; ++i) { 2037 d->m_transformedPoints[i] = d->m_points[i]; 2038 d->m_transformedPoints[i].transform(matrix); 2039 } 2040 } 2041 2042 class Okular::HighlightAnnotationPrivate : public Okular::AnnotationPrivate 2043 { 2044 public: 2045 HighlightAnnotationPrivate() 2046 : AnnotationPrivate() 2047 , m_highlightType(HighlightAnnotation::Highlight) 2048 { 2049 } 2050 2051 void transform(const QTransform &matrix) override; 2052 void baseTransform(const QTransform &matrix) override; 2053 double distanceSqr(double x, double y, double xScale, double yScale) const override; 2054 void setAnnotationProperties(const QDomNode &node) override; 2055 AnnotationPrivate *getNewAnnotationPrivate() override; 2056 2057 HighlightAnnotation::HighlightType m_highlightType; 2058 QList<HighlightAnnotation::Quad> m_highlightQuads; 2059 }; 2060 2061 HighlightAnnotation::HighlightAnnotation() 2062 : Annotation(*new HighlightAnnotationPrivate()) 2063 { 2064 } 2065 2066 HighlightAnnotation::HighlightAnnotation(const QDomNode &description) 2067 : Annotation(*new HighlightAnnotationPrivate(), description) 2068 { 2069 } 2070 2071 HighlightAnnotation::~HighlightAnnotation() 2072 { 2073 } 2074 2075 void HighlightAnnotation::setHighlightType(HighlightType type) 2076 { 2077 Q_D(HighlightAnnotation); 2078 d->m_highlightType = type; 2079 } 2080 2081 HighlightAnnotation::HighlightType HighlightAnnotation::highlightType() const 2082 { 2083 Q_D(const HighlightAnnotation); 2084 return d->m_highlightType; 2085 } 2086 2087 QList<HighlightAnnotation::Quad> &HighlightAnnotation::highlightQuads() 2088 { 2089 Q_D(HighlightAnnotation); 2090 return d->m_highlightQuads; 2091 } 2092 2093 const QList<HighlightAnnotation::Quad> &HighlightAnnotation::highlightQuads() const 2094 { 2095 Q_D(const HighlightAnnotation); 2096 return d->m_highlightQuads; 2097 } 2098 2099 void HighlightAnnotation::store(QDomNode &node, QDomDocument &document) const 2100 { 2101 Q_D(const HighlightAnnotation); 2102 // recurse to parent objects storing properties 2103 Annotation::store(node, document); 2104 2105 // create [hl] element 2106 QDomElement hlElement = document.createElement(QStringLiteral("hl")); 2107 node.appendChild(hlElement); 2108 2109 // append the optional attributes 2110 if (d->m_highlightType != Highlight) { 2111 hlElement.setAttribute(QStringLiteral("type"), (int)d->m_highlightType); 2112 } 2113 if (d->m_highlightQuads.count() < 1) { 2114 return; 2115 } 2116 // append highlight quads, all children describe quads 2117 QList<Quad>::const_iterator it = d->m_highlightQuads.begin(), end = d->m_highlightQuads.end(); 2118 for (; it != end; ++it) { 2119 QDomElement quadElement = document.createElement(QStringLiteral("quad")); 2120 hlElement.appendChild(quadElement); 2121 const Quad &q = *it; 2122 quadElement.setAttribute(QStringLiteral("ax"), QString::number(q.point(0).x)); 2123 quadElement.setAttribute(QStringLiteral("ay"), QString::number(q.point(0).y)); 2124 quadElement.setAttribute(QStringLiteral("bx"), QString::number(q.point(1).x)); 2125 quadElement.setAttribute(QStringLiteral("by"), QString::number(q.point(1).y)); 2126 quadElement.setAttribute(QStringLiteral("cx"), QString::number(q.point(2).x)); 2127 quadElement.setAttribute(QStringLiteral("cy"), QString::number(q.point(2).y)); 2128 quadElement.setAttribute(QStringLiteral("dx"), QString::number(q.point(3).x)); 2129 quadElement.setAttribute(QStringLiteral("dy"), QString::number(q.point(3).y)); 2130 if (q.capStart()) { 2131 quadElement.setAttribute(QStringLiteral("start"), 1); 2132 } 2133 if (q.capEnd()) { 2134 quadElement.setAttribute(QStringLiteral("end"), 1); 2135 } 2136 quadElement.setAttribute(QStringLiteral("feather"), QString::number(q.feather())); 2137 } 2138 } 2139 2140 Annotation::SubType HighlightAnnotation::subType() const 2141 { 2142 return AHighlight; 2143 } 2144 2145 void HighlightAnnotationPrivate::transform(const QTransform &matrix) 2146 { 2147 AnnotationPrivate::transform(matrix); 2148 2149 QMutableListIterator<HighlightAnnotation::Quad> it(m_highlightQuads); 2150 while (it.hasNext()) { 2151 it.next().transform(matrix); 2152 } 2153 } 2154 2155 void HighlightAnnotationPrivate::baseTransform(const QTransform &matrix) 2156 { 2157 AnnotationPrivate::baseTransform(matrix); 2158 2159 QMutableListIterator<HighlightAnnotation::Quad> it(m_highlightQuads); 2160 while (it.hasNext()) { 2161 it.next().transform(matrix); 2162 } 2163 } 2164 2165 void HighlightAnnotationPrivate::setAnnotationProperties(const QDomNode &node) 2166 { 2167 Okular::AnnotationPrivate::setAnnotationProperties(node); 2168 m_highlightQuads.clear(); 2169 2170 // loop through the whole children looking for a 'hl' element 2171 QDomNode subNode = node.firstChild(); 2172 while (subNode.isElement()) { 2173 QDomElement e = subNode.toElement(); 2174 subNode = subNode.nextSibling(); 2175 if (e.tagName() != QLatin1String("hl")) { 2176 continue; 2177 } 2178 2179 // parse the attributes 2180 if (e.hasAttribute(QStringLiteral("type"))) { 2181 m_highlightType = (HighlightAnnotation::HighlightType)e.attribute(QStringLiteral("type")).toInt(); 2182 } 2183 2184 // parse all 'quad' subnodes 2185 QDomNode quadNode = e.firstChild(); 2186 for (; quadNode.isElement(); quadNode = quadNode.nextSibling()) { 2187 QDomElement qe = quadNode.toElement(); 2188 if (qe.tagName() != QLatin1String("quad")) { 2189 continue; 2190 } 2191 2192 HighlightAnnotation::Quad q; 2193 q.setPoint(NormalizedPoint(qe.attribute(QStringLiteral("ax"), QStringLiteral("0.0")).toDouble(), qe.attribute(QStringLiteral("ay"), QStringLiteral("0.0")).toDouble()), 0); 2194 q.setPoint(NormalizedPoint(qe.attribute(QStringLiteral("bx"), QStringLiteral("0.0")).toDouble(), qe.attribute(QStringLiteral("by"), QStringLiteral("0.0")).toDouble()), 1); 2195 q.setPoint(NormalizedPoint(qe.attribute(QStringLiteral("cx"), QStringLiteral("0.0")).toDouble(), qe.attribute(QStringLiteral("cy"), QStringLiteral("0.0")).toDouble()), 2); 2196 q.setPoint(NormalizedPoint(qe.attribute(QStringLiteral("dx"), QStringLiteral("0.0")).toDouble(), qe.attribute(QStringLiteral("dy"), QStringLiteral("0.0")).toDouble()), 3); 2197 q.setCapStart(qe.hasAttribute(QStringLiteral("start"))); 2198 q.setCapEnd(qe.hasAttribute(QStringLiteral("end"))); 2199 q.setFeather(qe.attribute(QStringLiteral("feather"), QStringLiteral("0.1")).toDouble()); 2200 2201 q.transform(QTransform()); 2202 2203 m_highlightQuads.append(q); 2204 } 2205 2206 // loading complete 2207 break; 2208 } 2209 } 2210 2211 AnnotationPrivate *HighlightAnnotationPrivate::getNewAnnotationPrivate() 2212 { 2213 return new HighlightAnnotationPrivate(); 2214 } 2215 2216 double HighlightAnnotationPrivate::distanceSqr(double x, double y, double xScale, double yScale) const 2217 { 2218 NormalizedPoint point(x, y); 2219 double outsideDistance = DBL_MAX; 2220 for (const HighlightAnnotation::Quad &quad : m_highlightQuads) { 2221 QList<NormalizedPoint> pathPoints; 2222 2223 // first, we check if the point is within the area described by the 4 quads 2224 // this is the case, if the point is always on one side of each segments delimiting the polygon: 2225 pathPoints << quad.transformedPoint(0); 2226 int directionVote = 0; 2227 for (int i = 1; i < 5; ++i) { 2228 NormalizedPoint thisPoint = quad.transformedPoint(i % 4); 2229 directionVote += (isLeftOfVector(pathPoints.back(), thisPoint, point)) ? 1 : -1; 2230 pathPoints << thisPoint; 2231 } 2232 if (abs(directionVote) == 4) { 2233 return 0; 2234 } 2235 2236 // if that's not the case, we treat the outline as path and simply determine 2237 // the distance from the path to the point 2238 const double thisOutsideDistance = ::distanceSqr(x, y, xScale, yScale, pathPoints); 2239 if (thisOutsideDistance < outsideDistance) { 2240 outsideDistance = thisOutsideDistance; 2241 } 2242 } 2243 2244 return outsideDistance; 2245 } 2246 2247 /** StampAnnotation [Annotation] */ 2248 2249 class Okular::StampAnnotationPrivate : public Okular::AnnotationPrivate 2250 { 2251 public: 2252 StampAnnotationPrivate() 2253 : AnnotationPrivate() 2254 , m_stampIconName(QStringLiteral("Draft")) 2255 { 2256 } 2257 void setAnnotationProperties(const QDomNode &node) override; 2258 bool canBeResized() const override; 2259 AnnotationPrivate *getNewAnnotationPrivate() override; 2260 2261 QString m_stampIconName; 2262 }; 2263 2264 StampAnnotation::StampAnnotation() 2265 : Annotation(*new StampAnnotationPrivate()) 2266 { 2267 } 2268 2269 StampAnnotation::StampAnnotation(const QDomNode &description) 2270 : Annotation(*new StampAnnotationPrivate(), description) 2271 { 2272 } 2273 2274 StampAnnotation::~StampAnnotation() 2275 { 2276 } 2277 2278 void StampAnnotation::setStampIconName(const QString &name) 2279 { 2280 Q_D(StampAnnotation); 2281 d->m_stampIconName = name; 2282 } 2283 2284 QString StampAnnotation::stampIconName() const 2285 { 2286 Q_D(const StampAnnotation); 2287 return d->m_stampIconName; 2288 } 2289 2290 Annotation::SubType StampAnnotation::subType() const 2291 { 2292 return AStamp; 2293 } 2294 2295 void StampAnnotation::store(QDomNode &node, QDomDocument &document) const 2296 { 2297 Q_D(const StampAnnotation); 2298 // recurse to parent objects storing properties 2299 Annotation::store(node, document); 2300 2301 // create [stamp] element 2302 QDomElement stampElement = document.createElement(QStringLiteral("stamp")); 2303 node.appendChild(stampElement); 2304 2305 // append the optional attributes 2306 if (d->m_stampIconName != QLatin1String("Draft")) { 2307 stampElement.setAttribute(QStringLiteral("icon"), d->m_stampIconName); 2308 } 2309 } 2310 2311 void StampAnnotationPrivate::setAnnotationProperties(const QDomNode &node) 2312 { 2313 Okular::AnnotationPrivate::setAnnotationProperties(node); 2314 2315 // loop through the whole children looking for a 'stamp' element 2316 QDomNode subNode = node.firstChild(); 2317 while (subNode.isElement()) { 2318 QDomElement e = subNode.toElement(); 2319 subNode = subNode.nextSibling(); 2320 if (e.tagName() != QLatin1String("stamp")) { 2321 continue; 2322 } 2323 2324 // parse the attributes 2325 if (e.hasAttribute(QStringLiteral("icon"))) { 2326 m_stampIconName = e.attribute(QStringLiteral("icon")); 2327 } 2328 2329 // loading complete 2330 break; 2331 } 2332 } 2333 2334 bool StampAnnotationPrivate::canBeResized() const 2335 { 2336 return true; 2337 } 2338 2339 AnnotationPrivate *StampAnnotationPrivate::getNewAnnotationPrivate() 2340 { 2341 return new StampAnnotationPrivate(); 2342 } 2343 2344 /** InkAnnotation [Annotation] */ 2345 2346 class Okular::InkAnnotationPrivate : public Okular::AnnotationPrivate 2347 { 2348 public: 2349 InkAnnotationPrivate() 2350 : AnnotationPrivate() 2351 { 2352 } 2353 2354 void transform(const QTransform &matrix) override; 2355 void baseTransform(const QTransform &matrix) override; 2356 void resetTransformation() override; 2357 double distanceSqr(double x, double y, double xScale, double yScale) const override; 2358 void translate(const NormalizedPoint &coord) override; 2359 void setAnnotationProperties(const QDomNode &node) override; 2360 AnnotationPrivate *getNewAnnotationPrivate() override; 2361 2362 QList<QList<NormalizedPoint>> m_inkPaths; 2363 QList<QList<NormalizedPoint>> m_transformedInkPaths; 2364 }; 2365 2366 InkAnnotation::InkAnnotation() 2367 : Annotation(*new InkAnnotationPrivate()) 2368 { 2369 } 2370 2371 InkAnnotation::InkAnnotation(const QDomNode &description) 2372 : Annotation(*new InkAnnotationPrivate(), description) 2373 { 2374 } 2375 2376 InkAnnotation::~InkAnnotation() 2377 { 2378 } 2379 2380 void InkAnnotation::setInkPaths(const QList<QList<NormalizedPoint>> &paths) 2381 { 2382 Q_D(InkAnnotation); 2383 d->m_inkPaths = paths; 2384 } 2385 2386 QList<QList<NormalizedPoint>> InkAnnotation::inkPaths() const 2387 { 2388 Q_D(const InkAnnotation); 2389 return d->m_inkPaths; 2390 } 2391 2392 QList<QList<NormalizedPoint>> InkAnnotation::transformedInkPaths() const 2393 { 2394 Q_D(const InkAnnotation); 2395 return d->m_transformedInkPaths; 2396 } 2397 2398 Annotation::SubType InkAnnotation::subType() const 2399 { 2400 return AInk; 2401 } 2402 2403 void InkAnnotation::store(QDomNode &node, QDomDocument &document) const 2404 { 2405 Q_D(const InkAnnotation); 2406 // recurse to parent objects storing properties 2407 Annotation::store(node, document); 2408 2409 // create [ink] element 2410 QDomElement inkElement = document.createElement(QStringLiteral("ink")); 2411 node.appendChild(inkElement); 2412 2413 // append the optional attributes 2414 if (d->m_inkPaths.count() < 1) { 2415 return; 2416 } 2417 2418 QList<QList<NormalizedPoint>>::const_iterator pIt = d->m_inkPaths.begin(), pEnd = d->m_inkPaths.end(); 2419 for (; pIt != pEnd; ++pIt) { 2420 QDomElement pathElement = document.createElement(QStringLiteral("path")); 2421 inkElement.appendChild(pathElement); 2422 const QList<NormalizedPoint> &path = *pIt; 2423 for (const NormalizedPoint &point : path) { 2424 QDomElement pointElement = document.createElement(QStringLiteral("point")); 2425 pathElement.appendChild(pointElement); 2426 pointElement.setAttribute(QStringLiteral("x"), QString::number(point.x)); 2427 pointElement.setAttribute(QStringLiteral("y"), QString::number(point.y)); 2428 } 2429 } 2430 } 2431 2432 double InkAnnotationPrivate::distanceSqr(double x, double y, double xScale, double yScale) const 2433 { 2434 double distance = DBL_MAX; 2435 for (const QList<NormalizedPoint> &path : m_transformedInkPaths) { 2436 const double thisDistance = ::distanceSqr(x, y, xScale, yScale, path); 2437 if (thisDistance < distance) { 2438 distance = thisDistance; 2439 } 2440 } 2441 return strokeDistance(distance, m_style.width() * xScale / (m_page->m_width * 2)); 2442 } 2443 2444 void InkAnnotationPrivate::transform(const QTransform &matrix) 2445 { 2446 AnnotationPrivate::transform(matrix); 2447 2448 for (int i = 0; i < m_transformedInkPaths.count(); ++i) { 2449 QMutableListIterator<NormalizedPoint> it(m_transformedInkPaths[i]); 2450 while (it.hasNext()) { 2451 it.next().transform(matrix); 2452 } 2453 } 2454 } 2455 2456 void InkAnnotationPrivate::baseTransform(const QTransform &matrix) 2457 { 2458 AnnotationPrivate::baseTransform(matrix); 2459 2460 for (int i = 0; i < m_inkPaths.count(); ++i) { 2461 QMutableListIterator<NormalizedPoint> it(m_inkPaths[i]); 2462 while (it.hasNext()) { 2463 it.next().transform(matrix); 2464 } 2465 } 2466 } 2467 2468 void InkAnnotationPrivate::resetTransformation() 2469 { 2470 AnnotationPrivate::resetTransformation(); 2471 2472 m_transformedInkPaths = m_inkPaths; 2473 } 2474 2475 void InkAnnotationPrivate::translate(const NormalizedPoint &coord) 2476 { 2477 AnnotationPrivate::translate(coord); 2478 2479 for (int i = 0; i < m_inkPaths.count(); ++i) { 2480 QMutableListIterator<NormalizedPoint> it(m_inkPaths[i]); 2481 while (it.hasNext()) { 2482 NormalizedPoint &p = it.next(); 2483 p.x = p.x + coord.x; 2484 p.y = p.y + coord.y; 2485 } 2486 } 2487 } 2488 2489 void InkAnnotationPrivate::setAnnotationProperties(const QDomNode &node) 2490 { 2491 Okular::AnnotationPrivate::setAnnotationProperties(node); 2492 m_inkPaths.clear(); 2493 2494 // loop through the whole children looking for a 'ink' element 2495 QDomNode subNode = node.firstChild(); 2496 while (subNode.isElement()) { 2497 QDomElement e = subNode.toElement(); 2498 subNode = subNode.nextSibling(); 2499 if (e.tagName() != QLatin1String("ink")) { 2500 continue; 2501 } 2502 2503 // parse the 'path' subnodes 2504 QDomNode pathNode = e.firstChild(); 2505 while (pathNode.isElement()) { 2506 QDomElement pathElement = pathNode.toElement(); 2507 pathNode = pathNode.nextSibling(); 2508 2509 if (pathElement.tagName() != QLatin1String("path")) { 2510 continue; 2511 } 2512 2513 // build each path parsing 'point' subnodes 2514 QList<NormalizedPoint> path; 2515 QDomNode pointNode = pathElement.firstChild(); 2516 while (pointNode.isElement()) { 2517 QDomElement pointElement = pointNode.toElement(); 2518 pointNode = pointNode.nextSibling(); 2519 2520 if (pointElement.tagName() != QLatin1String("point")) { 2521 continue; 2522 } 2523 2524 NormalizedPoint p; 2525 p.x = pointElement.attribute(QStringLiteral("x"), QStringLiteral("0.0")).toDouble(); 2526 p.y = pointElement.attribute(QStringLiteral("y"), QStringLiteral("0.0")).toDouble(); 2527 path.append(p); 2528 } 2529 2530 // add the path to the path list if it contains at least 2 nodes 2531 if (path.count() >= 2) { 2532 m_inkPaths.append(path); 2533 } 2534 } 2535 2536 // loading complete 2537 break; 2538 } 2539 2540 m_transformedInkPaths = m_inkPaths; 2541 } 2542 2543 AnnotationPrivate *InkAnnotationPrivate::getNewAnnotationPrivate() 2544 { 2545 return new InkAnnotationPrivate(); 2546 } 2547 2548 /** CaretAnnotation [Annotation] */ 2549 2550 class Okular::CaretAnnotationPrivate : public Okular::AnnotationPrivate 2551 { 2552 public: 2553 CaretAnnotationPrivate() 2554 : AnnotationPrivate() 2555 , m_symbol(CaretAnnotation::None) 2556 { 2557 } 2558 2559 void setAnnotationProperties(const QDomNode &node) override; 2560 AnnotationPrivate *getNewAnnotationPrivate() override; 2561 2562 CaretAnnotation::CaretSymbol m_symbol; 2563 }; 2564 2565 static QString caretSymbolToString(CaretAnnotation::CaretSymbol symbol) 2566 { 2567 switch (symbol) { 2568 case CaretAnnotation::None: 2569 return QStringLiteral("None"); 2570 case CaretAnnotation::P: 2571 return QStringLiteral("P"); 2572 } 2573 return QString(); 2574 } 2575 2576 static CaretAnnotation::CaretSymbol caretSymbolFromString(const QString &symbol) 2577 { 2578 if (symbol == QLatin1String("None")) { 2579 return CaretAnnotation::None; 2580 } else if (symbol == QLatin1String("P")) { 2581 return CaretAnnotation::P; 2582 } 2583 return CaretAnnotation::None; 2584 } 2585 2586 void CaretAnnotationPrivate::setAnnotationProperties(const QDomNode &node) 2587 { 2588 Okular::AnnotationPrivate::setAnnotationProperties(node); 2589 2590 // loop through the whole children looking for a 'caret' element 2591 QDomNode subNode = node.firstChild(); 2592 while (subNode.isElement()) { 2593 QDomElement e = subNode.toElement(); 2594 subNode = subNode.nextSibling(); 2595 if (e.tagName() != QLatin1String("caret")) { 2596 continue; 2597 } 2598 2599 // parse the attributes 2600 if (e.hasAttribute(QStringLiteral("symbol"))) { 2601 m_symbol = caretSymbolFromString(e.attribute(QStringLiteral("symbol"))); 2602 } 2603 2604 // loading complete 2605 break; 2606 } 2607 } 2608 2609 AnnotationPrivate *CaretAnnotationPrivate::getNewAnnotationPrivate() 2610 { 2611 return new CaretAnnotationPrivate(); 2612 } 2613 2614 CaretAnnotation::CaretAnnotation() 2615 : Annotation(*new CaretAnnotationPrivate()) 2616 { 2617 } 2618 2619 CaretAnnotation::CaretAnnotation(const QDomNode &description) 2620 : Annotation(*new CaretAnnotationPrivate(), description) 2621 { 2622 } 2623 2624 CaretAnnotation::~CaretAnnotation() 2625 { 2626 } 2627 2628 void CaretAnnotation::setCaretSymbol(CaretAnnotation::CaretSymbol symbol) 2629 { 2630 Q_D(CaretAnnotation); 2631 d->m_symbol = symbol; 2632 } 2633 2634 CaretAnnotation::CaretSymbol CaretAnnotation::caretSymbol() const 2635 { 2636 Q_D(const CaretAnnotation); 2637 return d->m_symbol; 2638 } 2639 2640 Annotation::SubType CaretAnnotation::subType() const 2641 { 2642 return ACaret; 2643 } 2644 2645 void CaretAnnotation::store(QDomNode &node, QDomDocument &document) const 2646 { 2647 Q_D(const CaretAnnotation); 2648 // recurse to parent objects storing properties 2649 Annotation::store(node, document); 2650 2651 // create [caret] element 2652 QDomElement caretElement = document.createElement(QStringLiteral("caret")); 2653 node.appendChild(caretElement); 2654 2655 // append the optional attributes 2656 if (d->m_symbol != None) { 2657 caretElement.setAttribute(QStringLiteral("symbol"), caretSymbolToString(d->m_symbol)); 2658 } 2659 } 2660 2661 /** FileAttachmentAnnotation [Annotation] */ 2662 2663 class Okular::FileAttachmentAnnotationPrivate : public Okular::AnnotationPrivate 2664 { 2665 public: 2666 FileAttachmentAnnotationPrivate() 2667 : AnnotationPrivate() 2668 , icon(QStringLiteral("PushPin")) 2669 , embfile(nullptr) 2670 { 2671 } 2672 ~FileAttachmentAnnotationPrivate() override 2673 { 2674 delete embfile; 2675 } 2676 2677 void setAnnotationProperties(const QDomNode &node) override; 2678 AnnotationPrivate *getNewAnnotationPrivate() override; 2679 2680 // data fields 2681 QString icon; 2682 EmbeddedFile *embfile; 2683 }; 2684 2685 void FileAttachmentAnnotationPrivate::setAnnotationProperties(const QDomNode &node) 2686 { 2687 Okular::AnnotationPrivate::setAnnotationProperties(node); 2688 2689 // loop through the whole children looking for a 'fileattachment' element 2690 QDomNode subNode = node.firstChild(); 2691 while (subNode.isElement()) { 2692 QDomElement e = subNode.toElement(); 2693 subNode = subNode.nextSibling(); 2694 if (e.tagName() != QLatin1String("fileattachment")) { 2695 continue; 2696 } 2697 2698 // loading complete 2699 break; 2700 } 2701 } 2702 2703 AnnotationPrivate *FileAttachmentAnnotationPrivate::getNewAnnotationPrivate() 2704 { 2705 return new FileAttachmentAnnotationPrivate(); 2706 } 2707 2708 FileAttachmentAnnotation::FileAttachmentAnnotation() 2709 : Annotation(*new FileAttachmentAnnotationPrivate()) 2710 { 2711 } 2712 2713 FileAttachmentAnnotation::FileAttachmentAnnotation(const QDomNode &description) 2714 : Annotation(*new FileAttachmentAnnotationPrivate(), description) 2715 { 2716 } 2717 2718 FileAttachmentAnnotation::~FileAttachmentAnnotation() 2719 { 2720 } 2721 2722 void FileAttachmentAnnotation::store(QDomNode &node, QDomDocument &document) const 2723 { 2724 // recurse to parent objects storing properties 2725 Annotation::store(node, document); 2726 2727 // create [fileattachment] element 2728 QDomElement fileAttachmentElement = document.createElement(QStringLiteral("fileattachment")); 2729 node.appendChild(fileAttachmentElement); 2730 } 2731 2732 Annotation::SubType FileAttachmentAnnotation::subType() const 2733 { 2734 return AFileAttachment; 2735 } 2736 2737 QString FileAttachmentAnnotation::fileIconName() const 2738 { 2739 Q_D(const FileAttachmentAnnotation); 2740 return d->icon; 2741 } 2742 2743 void FileAttachmentAnnotation::setFileIconName(const QString &iconName) 2744 { 2745 Q_D(FileAttachmentAnnotation); 2746 d->icon = iconName; 2747 } 2748 2749 EmbeddedFile *FileAttachmentAnnotation::embeddedFile() const 2750 { 2751 Q_D(const FileAttachmentAnnotation); 2752 return d->embfile; 2753 } 2754 2755 void FileAttachmentAnnotation::setEmbeddedFile(EmbeddedFile *ef) 2756 { 2757 Q_D(FileAttachmentAnnotation); 2758 d->embfile = ef; 2759 } 2760 2761 /** SoundAnnotation [Annotation] */ 2762 2763 class Okular::SoundAnnotationPrivate : public Okular::AnnotationPrivate 2764 { 2765 public: 2766 SoundAnnotationPrivate() 2767 : AnnotationPrivate() 2768 , icon(QStringLiteral("Speaker")) 2769 , sound(nullptr) 2770 { 2771 } 2772 ~SoundAnnotationPrivate() override 2773 { 2774 delete sound; 2775 } 2776 2777 void setAnnotationProperties(const QDomNode &node) override; 2778 AnnotationPrivate *getNewAnnotationPrivate() override; 2779 2780 // data fields 2781 QString icon; 2782 Sound *sound; 2783 }; 2784 2785 void SoundAnnotationPrivate::setAnnotationProperties(const QDomNode &node) 2786 { 2787 Okular::AnnotationPrivate::setAnnotationProperties(node); 2788 2789 // loop through the whole children looking for a 'sound' element 2790 QDomNode subNode = node.firstChild(); 2791 while (subNode.isElement()) { 2792 QDomElement e = subNode.toElement(); 2793 subNode = subNode.nextSibling(); 2794 if (e.tagName() != QLatin1String("sound")) { 2795 continue; 2796 } 2797 2798 // loading complete 2799 break; 2800 } 2801 } 2802 2803 AnnotationPrivate *SoundAnnotationPrivate::getNewAnnotationPrivate() 2804 { 2805 return new SoundAnnotationPrivate(); 2806 } 2807 2808 SoundAnnotation::SoundAnnotation() 2809 : Annotation(*new SoundAnnotationPrivate()) 2810 { 2811 } 2812 2813 SoundAnnotation::SoundAnnotation(const QDomNode &description) 2814 : Annotation(*new SoundAnnotationPrivate(), description) 2815 { 2816 } 2817 2818 SoundAnnotation::~SoundAnnotation() 2819 { 2820 } 2821 2822 void SoundAnnotation::store(QDomNode &node, QDomDocument &document) const 2823 { 2824 // recurse to parent objects storing properties 2825 Annotation::store(node, document); 2826 2827 // create [sound] element 2828 QDomElement soundElement = document.createElement(QStringLiteral("sound")); 2829 node.appendChild(soundElement); 2830 } 2831 2832 Annotation::SubType SoundAnnotation::subType() const 2833 { 2834 return ASound; 2835 } 2836 2837 QString SoundAnnotation::soundIconName() const 2838 { 2839 Q_D(const SoundAnnotation); 2840 return d->icon; 2841 } 2842 2843 void SoundAnnotation::setSoundIconName(const QString &iconName) 2844 { 2845 Q_D(SoundAnnotation); 2846 d->icon = iconName; 2847 } 2848 2849 Sound *SoundAnnotation::sound() const 2850 { 2851 Q_D(const SoundAnnotation); 2852 return d->sound; 2853 } 2854 2855 void SoundAnnotation::setSound(Sound *s) 2856 { 2857 Q_D(SoundAnnotation); 2858 d->sound = s; 2859 } 2860 2861 /** MovieAnnotation [Annotation] */ 2862 2863 class Okular::MovieAnnotationPrivate : public Okular::AnnotationPrivate 2864 { 2865 public: 2866 MovieAnnotationPrivate() 2867 : AnnotationPrivate() 2868 , movie(nullptr) 2869 { 2870 } 2871 ~MovieAnnotationPrivate() override 2872 { 2873 delete movie; 2874 } 2875 2876 void setAnnotationProperties(const QDomNode &node) override; 2877 AnnotationPrivate *getNewAnnotationPrivate() override; 2878 2879 // data fields 2880 Movie *movie; 2881 }; 2882 2883 void MovieAnnotationPrivate::setAnnotationProperties(const QDomNode &node) 2884 { 2885 Okular::AnnotationPrivate::setAnnotationProperties(node); 2886 2887 // loop through the whole children looking for a 'movie' element 2888 QDomNode subNode = node.firstChild(); 2889 while (subNode.isElement()) { 2890 QDomElement e = subNode.toElement(); 2891 subNode = subNode.nextSibling(); 2892 if (e.tagName() != QLatin1String("movie")) { 2893 continue; 2894 } 2895 2896 // loading complete 2897 break; 2898 } 2899 } 2900 2901 AnnotationPrivate *MovieAnnotationPrivate::getNewAnnotationPrivate() 2902 { 2903 return new MovieAnnotationPrivate(); 2904 } 2905 2906 MovieAnnotation::MovieAnnotation() 2907 : Annotation(*new MovieAnnotationPrivate()) 2908 { 2909 } 2910 2911 MovieAnnotation::MovieAnnotation(const QDomNode &description) 2912 : Annotation(*new MovieAnnotationPrivate(), description) 2913 { 2914 } 2915 2916 MovieAnnotation::~MovieAnnotation() 2917 { 2918 } 2919 2920 void MovieAnnotation::store(QDomNode &node, QDomDocument &document) const 2921 { 2922 // recurse to parent objects storing properties 2923 Annotation::store(node, document); 2924 2925 // create [movie] element 2926 QDomElement movieElement = document.createElement(QStringLiteral("movie")); 2927 node.appendChild(movieElement); 2928 } 2929 2930 Annotation::SubType MovieAnnotation::subType() const 2931 { 2932 return AMovie; 2933 } 2934 2935 Movie *MovieAnnotation::movie() const 2936 { 2937 Q_D(const MovieAnnotation); 2938 return d->movie; 2939 } 2940 2941 void MovieAnnotation::setMovie(Movie *movie) 2942 { 2943 Q_D(MovieAnnotation); 2944 d->movie = movie; 2945 } 2946 2947 /** ScreenAnnotation [Annotation] */ 2948 2949 class Okular::ScreenAnnotationPrivate : public Okular::AnnotationPrivate 2950 { 2951 public: 2952 ScreenAnnotationPrivate(); 2953 ~ScreenAnnotationPrivate() override; 2954 2955 void setAnnotationProperties(const QDomNode &node) override; 2956 AnnotationPrivate *getNewAnnotationPrivate() override; 2957 2958 Okular::Action *m_action; 2959 QMap<Okular::Annotation::AdditionalActionType, Okular::Action *> m_additionalActions; 2960 }; 2961 2962 ScreenAnnotationPrivate::ScreenAnnotationPrivate() 2963 : m_action(nullptr) 2964 { 2965 } 2966 2967 ScreenAnnotationPrivate::~ScreenAnnotationPrivate() 2968 { 2969 delete m_action; 2970 qDeleteAll(m_additionalActions); 2971 } 2972 2973 void ScreenAnnotationPrivate::setAnnotationProperties(const QDomNode &node) 2974 { 2975 Okular::AnnotationPrivate::setAnnotationProperties(node); 2976 2977 // loop through the whole children looking for a 'screen' element 2978 QDomNode subNode = node.firstChild(); 2979 while (subNode.isElement()) { 2980 QDomElement e = subNode.toElement(); 2981 subNode = subNode.nextSibling(); 2982 if (e.tagName() != QLatin1String("screen")) { 2983 continue; 2984 } 2985 2986 // loading complete 2987 break; 2988 } 2989 } 2990 2991 AnnotationPrivate *ScreenAnnotationPrivate::getNewAnnotationPrivate() 2992 { 2993 return new ScreenAnnotationPrivate(); 2994 } 2995 2996 ScreenAnnotation::ScreenAnnotation() 2997 : Annotation(*new ScreenAnnotationPrivate()) 2998 { 2999 } 3000 3001 ScreenAnnotation::ScreenAnnotation(const QDomNode &description) 3002 : Annotation(*new ScreenAnnotationPrivate(), description) 3003 { 3004 } 3005 3006 ScreenAnnotation::~ScreenAnnotation() 3007 { 3008 } 3009 3010 void ScreenAnnotation::store(QDomNode &node, QDomDocument &document) const 3011 { 3012 // recurse to parent objects storing properties 3013 Annotation::store(node, document); 3014 3015 // create [screen] element 3016 QDomElement movieElement = document.createElement(QStringLiteral("screen")); 3017 node.appendChild(movieElement); 3018 } 3019 3020 Annotation::SubType ScreenAnnotation::subType() const 3021 { 3022 return AScreen; 3023 } 3024 3025 void ScreenAnnotation::setAdditionalAction(AdditionalActionType type, Action *action) 3026 { 3027 Q_D(ScreenAnnotation); 3028 if (d->m_additionalActions.contains(type)) { 3029 delete d->m_additionalActions.value(type); 3030 } 3031 3032 d->m_additionalActions.insert(type, action); 3033 } 3034 3035 Action *ScreenAnnotation::additionalAction(AdditionalActionType type) const 3036 { 3037 Q_D(const ScreenAnnotation); 3038 if (!d->m_additionalActions.contains(type)) { 3039 return nullptr; 3040 } else { 3041 return d->m_additionalActions.value(type); 3042 } 3043 } 3044 3045 void ScreenAnnotation::setAction(Action *action) 3046 { 3047 Q_D(ScreenAnnotation); 3048 3049 delete d->m_action; 3050 d->m_action = action; 3051 } 3052 3053 Action *ScreenAnnotation::action() const 3054 { 3055 Q_D(const ScreenAnnotation); 3056 return d->m_action; 3057 } 3058 3059 /** WidgetAnnotation [Annotation] */ 3060 3061 class Okular::WidgetAnnotationPrivate : public Okular::AnnotationPrivate 3062 { 3063 public: 3064 ~WidgetAnnotationPrivate() override; 3065 void setAnnotationProperties(const QDomNode &node) override; 3066 AnnotationPrivate *getNewAnnotationPrivate() override; 3067 3068 QMap<Okular::Annotation::AdditionalActionType, Okular::Action *> m_additionalActions; 3069 }; 3070 3071 WidgetAnnotationPrivate::~WidgetAnnotationPrivate() 3072 { 3073 qDeleteAll(m_additionalActions); 3074 } 3075 3076 void WidgetAnnotationPrivate::setAnnotationProperties(const QDomNode &node) 3077 { 3078 Okular::AnnotationPrivate::setAnnotationProperties(node); 3079 3080 // loop through the whole children looking for a 'widget' element 3081 QDomNode subNode = node.firstChild(); 3082 while (subNode.isElement()) { 3083 QDomElement e = subNode.toElement(); 3084 subNode = subNode.nextSibling(); 3085 if (e.tagName() != QLatin1String("widget")) { 3086 continue; 3087 } 3088 3089 // loading complete 3090 break; 3091 } 3092 } 3093 3094 AnnotationPrivate *WidgetAnnotationPrivate::getNewAnnotationPrivate() 3095 { 3096 return new WidgetAnnotationPrivate(); 3097 } 3098 3099 WidgetAnnotation::WidgetAnnotation() 3100 : Annotation(*new WidgetAnnotationPrivate()) 3101 { 3102 } 3103 3104 WidgetAnnotation::WidgetAnnotation(const QDomNode &description) 3105 : Annotation(*new WidgetAnnotationPrivate, description) 3106 { 3107 } 3108 3109 WidgetAnnotation::~WidgetAnnotation() 3110 { 3111 } 3112 3113 void WidgetAnnotation::store(QDomNode &node, QDomDocument &document) const 3114 { 3115 // recurse to parent objects storing properties 3116 Annotation::store(node, document); 3117 3118 // create [widget] element 3119 QDomElement movieElement = document.createElement(QStringLiteral("widget")); 3120 node.appendChild(movieElement); 3121 } 3122 3123 Annotation::SubType WidgetAnnotation::subType() const 3124 { 3125 return AWidget; 3126 } 3127 3128 void WidgetAnnotation::setAdditionalAction(AdditionalActionType type, Action *action) 3129 { 3130 Q_D(WidgetAnnotation); 3131 if (d->m_additionalActions.contains(type)) { 3132 delete d->m_additionalActions.value(type); 3133 } 3134 3135 d->m_additionalActions.insert(type, action); 3136 } 3137 3138 Action *WidgetAnnotation::additionalAction(AdditionalActionType type) const 3139 { 3140 Q_D(const WidgetAnnotation); 3141 if (!d->m_additionalActions.contains(type)) { 3142 return nullptr; 3143 } else { 3144 return d->m_additionalActions.value(type); 3145 } 3146 } 3147 3148 /** RichMediaAnnotation [Annotation] */ 3149 3150 class Okular::RichMediaAnnotationPrivate : public Okular::AnnotationPrivate 3151 { 3152 public: 3153 RichMediaAnnotationPrivate(); 3154 ~RichMediaAnnotationPrivate() override; 3155 void setAnnotationProperties(const QDomNode &node) override; 3156 AnnotationPrivate *getNewAnnotationPrivate() override; 3157 3158 // data fields 3159 Movie *movie; 3160 EmbeddedFile *embeddedFile; 3161 }; 3162 3163 RichMediaAnnotationPrivate::RichMediaAnnotationPrivate() 3164 : movie(nullptr) 3165 , embeddedFile(nullptr) 3166 { 3167 } 3168 3169 RichMediaAnnotationPrivate::~RichMediaAnnotationPrivate() 3170 { 3171 delete movie; 3172 delete embeddedFile; 3173 } 3174 3175 void RichMediaAnnotationPrivate::setAnnotationProperties(const QDomNode &node) 3176 { 3177 Okular::AnnotationPrivate::setAnnotationProperties(node); 3178 3179 // loop through the whole children looking for a 'richMedia' element 3180 QDomNode subNode = node.firstChild(); 3181 while (subNode.isElement()) { 3182 QDomElement e = subNode.toElement(); 3183 subNode = subNode.nextSibling(); 3184 if (e.tagName() != QLatin1String("richMedia")) { 3185 continue; 3186 } 3187 3188 // loading complete 3189 break; 3190 } 3191 } 3192 3193 AnnotationPrivate *RichMediaAnnotationPrivate::getNewAnnotationPrivate() 3194 { 3195 return new RichMediaAnnotationPrivate(); 3196 } 3197 3198 RichMediaAnnotation::RichMediaAnnotation() 3199 : Annotation(*new RichMediaAnnotationPrivate()) 3200 { 3201 } 3202 3203 RichMediaAnnotation::RichMediaAnnotation(const QDomNode &description) 3204 : Annotation(*new RichMediaAnnotationPrivate, description) 3205 { 3206 } 3207 3208 RichMediaAnnotation::~RichMediaAnnotation() 3209 { 3210 } 3211 3212 void RichMediaAnnotation::store(QDomNode &node, QDomDocument &document) const 3213 { 3214 // recurse to parent objects storing properties 3215 Annotation::store(node, document); 3216 3217 // create [richMedia] element 3218 QDomElement movieElement = document.createElement(QStringLiteral("richMedia")); 3219 node.appendChild(movieElement); 3220 } 3221 3222 Annotation::SubType RichMediaAnnotation::subType() const 3223 { 3224 return ARichMedia; 3225 } 3226 3227 void RichMediaAnnotation::setMovie(Movie *movie) 3228 { 3229 Q_D(RichMediaAnnotation); 3230 3231 delete d->movie; 3232 d->movie = movie; 3233 } 3234 3235 Movie *RichMediaAnnotation::movie() const 3236 { 3237 Q_D(const RichMediaAnnotation); 3238 3239 return d->movie; 3240 } 3241 3242 EmbeddedFile *RichMediaAnnotation::embeddedFile() const 3243 { 3244 Q_D(const RichMediaAnnotation); 3245 3246 return d->embeddedFile; 3247 } 3248 3249 void RichMediaAnnotation::setEmbeddedFile(EmbeddedFile *embeddedFile) 3250 { 3251 Q_D(RichMediaAnnotation); 3252 3253 delete d->embeddedFile; 3254 d->embeddedFile = embeddedFile; 3255 }