You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3082 lines
86 KiB
3082 lines
86 KiB
/* |
|
SPDX-FileCopyrightText: 2005 Enrico Ros <eros.kde@email.it> |
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later |
|
*/ |
|
|
|
#include "annotations.h" |
|
#include "annotations_p.h" |
|
|
|
// qt/kde includes |
|
#include <QApplication> |
|
#include <QColor> |
|
|
|
// DBL_MAX |
|
#include <float.h> |
|
|
|
// local includes |
|
#include "action.h" |
|
#include "document.h" |
|
#include "document_p.h" |
|
#include "movie.h" |
|
#include "page_p.h" |
|
#include "sound.h" |
|
|
|
using namespace Okular; |
|
|
|
/** |
|
* True, if point @p c lies to the left of the vector from @p a to @p b |
|
* @internal |
|
*/ |
|
static bool isLeftOfVector(const NormalizedPoint &a, const NormalizedPoint &b, const NormalizedPoint &c) |
|
{ |
|
// cross product |
|
return ((b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x)) > 0; |
|
} |
|
|
|
/** |
|
* @brief Calculates distance of the given point @p x @p y @p xScale @p yScale to the @p path |
|
* |
|
* Does piecewise comparison and selects the distance to the closest segment |
|
*/ |
|
static double distanceSqr(double x, double y, double xScale, double yScale, const QLinkedList<NormalizedPoint> &path) |
|
{ |
|
double distance = DBL_MAX; |
|
double thisDistance; |
|
QLinkedList<NormalizedPoint>::const_iterator i = path.constBegin(); |
|
NormalizedPoint lastPoint = *i; |
|
|
|
for (++i; i != path.constEnd(); ++i) { |
|
thisDistance = NormalizedPoint::distanceSqr(x, y, xScale, yScale, lastPoint, (*i)); |
|
|
|
if (thisDistance < distance) |
|
distance = thisDistance; |
|
|
|
lastPoint = *i; |
|
} |
|
return distance; |
|
} |
|
|
|
/** |
|
* Given the squared @p distance from the idealized 0-width line and a pen width @p penWidth, |
|
* (not squared!), returns the final distance |
|
* |
|
* @warning The returned distance is not exact: |
|
* We calculate an (exact) squared distance to the ideal (centered) line, and then subtract |
|
* the squared width of the pen: |
|
* a^2 - b^2 where a = "distance from idealized 0-width line" b = "pen width" |
|
* For an exact result, we would want to calculate "(a - b)^2" but that would require |
|
* a square root operation because we only know the squared distance a^2. |
|
* |
|
* However, the approximation is feasible, because: |
|
* error = (a-b)^2 - (a^2 - b^2) = -2ab + 2b^2 = 2b(b - a) |
|
* Therefore: |
|
* lim_{a->b} a^2 - b^2 - a^2 + 2ab - b^2 --> 0 |
|
* |
|
* In other words, this approximation will estimate the distance to be slightly more than it actually is |
|
* for as long as we are far "outside" the line, becoming more accurate the closer we get to the line |
|
* boundary. Trivially, it also fulfils (a1 < a2) => ((a1^2 - b^2) < (a2^2 - b^2)) making it monotonic. |
|
* "Inside" of the drawn line, the distance is 0 anyway. |
|
*/ |
|
static double strokeDistance(double distance, double penWidth) |
|
{ |
|
return fmax(distance - pow(penWidth, 2), 0); |
|
} |
|
|
|
// BEGIN AnnotationUtils implementation |
|
Annotation *AnnotationUtils::createAnnotation(const QDomElement &annElement) |
|
{ |
|
// safety check on annotation element |
|
if (!annElement.hasAttribute(QStringLiteral("type"))) |
|
return nullptr; |
|
|
|
// build annotation of given type |
|
Annotation *annotation = nullptr; |
|
int typeNumber = annElement.attribute(QStringLiteral("type")).toInt(); |
|
switch (typeNumber) { |
|
case Annotation::AText: |
|
annotation = new TextAnnotation(annElement); |
|
break; |
|
case Annotation::ALine: |
|
annotation = new LineAnnotation(annElement); |
|
break; |
|
case Annotation::AGeom: |
|
annotation = new GeomAnnotation(annElement); |
|
break; |
|
case Annotation::AHighlight: |
|
annotation = new HighlightAnnotation(annElement); |
|
break; |
|
case Annotation::AStamp: |
|
annotation = new StampAnnotation(annElement); |
|
break; |
|
case Annotation::AInk: |
|
annotation = new InkAnnotation(annElement); |
|
break; |
|
case Annotation::ACaret: |
|
annotation = new CaretAnnotation(annElement); |
|
break; |
|
} |
|
|
|
// return created annotation |
|
return annotation; |
|
} |
|
|
|
void AnnotationUtils::storeAnnotation(const Annotation *ann, QDomElement &annElement, QDomDocument &document) |
|
{ |
|
// save annotation's type as element's attribute |
|
annElement.setAttribute(QStringLiteral("type"), (uint)ann->subType()); |
|
|
|
// append all annotation data as children of this node |
|
ann->store(annElement, document); |
|
} |
|
|
|
QDomElement AnnotationUtils::findChildElement(const QDomNode &parentNode, const QString &name) |
|
{ |
|
// loop through the whole children and return a 'name' named element |
|
QDomNode subNode = parentNode.firstChild(); |
|
while (subNode.isElement()) { |
|
QDomElement element = subNode.toElement(); |
|
if (element.tagName() == name) |
|
return element; |
|
subNode = subNode.nextSibling(); |
|
} |
|
// if the name can't be found, return a dummy null element |
|
return QDomElement(); |
|
} |
|
|
|
QRect AnnotationUtils::annotationGeometry(const Annotation *annotation, double scaleX, double scaleY) |
|
{ |
|
const QRect rect = annotation->transformedBoundingRectangle().geometry((int)scaleX, (int)scaleY); |
|
if (annotation->subType() == Annotation::AText && (((TextAnnotation *)annotation)->textType() == TextAnnotation::Linked)) { |
|
// To be honest i have no clue of why the 24,24 is here, maybe to make sure it's not too small? |
|
// But why only for linked text? |
|
const QRect rect24 = QRect((int)(annotation->transformedBoundingRectangle().left * scaleX), (int)(annotation->transformedBoundingRectangle().top * scaleY), 24, 24); |
|
return rect24.united(rect); |
|
} |
|
|
|
return rect; |
|
} |
|
// END AnnotationUtils implementation |
|
|
|
AnnotationProxy::AnnotationProxy() |
|
{ |
|
} |
|
|
|
AnnotationProxy::~AnnotationProxy() |
|
{ |
|
} |
|
|
|
// BEGIN Annotation implementation |
|
|
|
class Annotation::Style::Private |
|
{ |
|
public: |
|
Private() |
|
: m_opacity(1.0) |
|
, m_width(1.0) |
|
, m_style(Solid) |
|
, m_xCorners(0.0) |
|
, m_yCorners(0.0) |
|
, m_marks(3) |
|
, m_spaces(0) |
|
, m_effect(NoEffect) |
|
, m_effectIntensity(1.0) |
|
{ |
|
} |
|
|
|
QColor m_color; |
|
double m_opacity; |
|
double m_width; |
|
LineStyle m_style; |
|
double m_xCorners; |
|
double m_yCorners; |
|
int m_marks; |
|
int m_spaces; |
|
LineEffect m_effect; |
|
double m_effectIntensity; |
|
}; |
|
|
|
Annotation::Style::Style() |
|
: d(new Private) |
|
{ |
|
} |
|
|
|
Annotation::Style::~Style() |
|
{ |
|
delete d; |
|
} |
|
|
|
Annotation::Style::Style(const Style &other) |
|
: d(new Private) |
|
{ |
|
*d = *other.d; |
|
} |
|
|
|
Annotation::Style &Annotation::Style::operator=(const Style &other) |
|
{ |
|
if (this != &other) |
|
*d = *other.d; |
|
|
|
return *this; |
|
} |
|
|
|
void Annotation::Style::setColor(const QColor &color) |
|
{ |
|
d->m_color = color; |
|
} |
|
|
|
QColor Annotation::Style::color() const |
|
{ |
|
return d->m_color; |
|
} |
|
|
|
void Annotation::Style::setOpacity(double opacity) |
|
{ |
|
d->m_opacity = opacity; |
|
} |
|
|
|
double Annotation::Style::opacity() const |
|
{ |
|
return d->m_opacity; |
|
} |
|
|
|
void Annotation::Style::setWidth(double width) |
|
{ |
|
d->m_width = width; |
|
} |
|
|
|
double Annotation::Style::width() const |
|
{ |
|
return d->m_width; |
|
} |
|
|
|
void Annotation::Style::setLineStyle(LineStyle style) |
|
{ |
|
d->m_style = style; |
|
} |
|
|
|
Annotation::LineStyle Annotation::Style::lineStyle() const |
|
{ |
|
return d->m_style; |
|
} |
|
|
|
void Annotation::Style::setXCorners(double xCorners) |
|
{ |
|
d->m_xCorners = xCorners; |
|
} |
|
|
|
double Annotation::Style::xCorners() const |
|
{ |
|
return d->m_xCorners; |
|
} |
|
|
|
void Annotation::Style::setYCorners(double yCorners) |
|
{ |
|
d->m_yCorners = yCorners; |
|
} |
|
|
|
double Annotation::Style::yCorners() const |
|
{ |
|
return d->m_yCorners; |
|
} |
|
|
|
void Annotation::Style::setMarks(int marks) |
|
{ |
|
d->m_marks = marks; |
|
} |
|
|
|
int Annotation::Style::marks() const |
|
{ |
|
return d->m_marks; |
|
} |
|
|
|
void Annotation::Style::setSpaces(int spaces) |
|
{ |
|
d->m_spaces = spaces; |
|
} |
|
|
|
int Annotation::Style::spaces() const |
|
{ |
|
return d->m_spaces; |
|
} |
|
|
|
void Annotation::Style::setLineEffect(LineEffect effect) |
|
{ |
|
d->m_effect = effect; |
|
} |
|
|
|
Annotation::LineEffect Annotation::Style::lineEffect() const |
|
{ |
|
return d->m_effect; |
|
} |
|
|
|
void Annotation::Style::setEffectIntensity(double intensity) |
|
{ |
|
d->m_effectIntensity = intensity; |
|
} |
|
|
|
double Annotation::Style::effectIntensity() const |
|
{ |
|
return d->m_effectIntensity; |
|
} |
|
|
|
class Annotation::Window::Private |
|
{ |
|
public: |
|
Private() |
|
: m_flags(-1) |
|
, m_width(0) |
|
, m_height(0) |
|
{ |
|
} |
|
|
|
int m_flags; |
|
NormalizedPoint m_topLeft; |
|
int m_width; |
|
int m_height; |
|
QString m_title; |
|
QString m_summary; |
|
}; |
|
|
|
Annotation::Window::Window() |
|
: d(new Private) |
|
{ |
|
} |
|
|
|
Annotation::Window::~Window() |
|
{ |
|
delete d; |
|
} |
|
|
|
Annotation::Window::Window(const Window &other) |
|
: d(new Private) |
|
{ |
|
*d = *other.d; |
|
} |
|
|
|
Annotation::Window &Annotation::Window::operator=(const Window &other) |
|
{ |
|
if (this != &other) |
|
*d = *other.d; |
|
|
|
return *this; |
|
} |
|
|
|
void Annotation::Window::setFlags(int flags) |
|
{ |
|
d->m_flags = flags; |
|
} |
|
|
|
int Annotation::Window::flags() const |
|
{ |
|
return d->m_flags; |
|
} |
|
|
|
void Annotation::Window::setTopLeft(const NormalizedPoint &point) |
|
{ |
|
d->m_topLeft = point; |
|
} |
|
|
|
NormalizedPoint Annotation::Window::topLeft() const |
|
{ |
|
return d->m_topLeft; |
|
} |
|
|
|
void Annotation::Window::setWidth(int width) |
|
{ |
|
d->m_width = width; |
|
} |
|
|
|
int Annotation::Window::width() const |
|
{ |
|
return d->m_width; |
|
} |
|
|
|
void Annotation::Window::setHeight(int height) |
|
{ |
|
d->m_height = height; |
|
} |
|
|
|
int Annotation::Window::height() const |
|
{ |
|
return d->m_height; |
|
} |
|
|
|
void Annotation::Window::setTitle(const QString &title) |
|
{ |
|
d->m_title = title; |
|
} |
|
|
|
QString Annotation::Window::title() const |
|
{ |
|
return d->m_title; |
|
} |
|
|
|
void Annotation::Window::setSummary(const QString &summary) |
|
{ |
|
d->m_summary = summary; |
|
} |
|
|
|
QString Annotation::Window::summary() const |
|
{ |
|
return d->m_summary; |
|
} |
|
|
|
class Annotation::Revision::Private |
|
{ |
|
public: |
|
Private() |
|
: m_annotation(nullptr) |
|
, m_scope(Reply) |
|
, m_type(None) |
|
{ |
|
} |
|
|
|
Annotation *m_annotation; |
|
RevisionScope m_scope; |
|
RevisionType m_type; |
|
}; |
|
|
|
Annotation::Revision::Revision() |
|
: d(new Private) |
|
{ |
|
} |
|
|
|
Annotation::Revision::~Revision() |
|
{ |
|
delete d; |
|
} |
|
|
|
Annotation::Revision::Revision(const Revision &other) |
|
: d(new Private) |
|
{ |
|
*d = *other.d; |
|
} |
|
|
|
Annotation::Revision &Annotation::Revision::operator=(const Revision &other) |
|
{ |
|
if (this != &other) |
|
*d = *other.d; |
|
|
|
return *this; |
|
} |
|
|
|
void Annotation::Revision::setAnnotation(Annotation *annotation) |
|
{ |
|
d->m_annotation = annotation; |
|
} |
|
|
|
Annotation *Annotation::Revision::annotation() const |
|
{ |
|
return d->m_annotation; |
|
} |
|
|
|
void Annotation::Revision::setScope(RevisionScope scope) |
|
{ |
|
d->m_scope = scope; |
|
} |
|
|
|
Annotation::RevisionScope Annotation::Revision::scope() const |
|
{ |
|
return d->m_scope; |
|
} |
|
|
|
void Annotation::Revision::setType(RevisionType type) |
|
{ |
|
d->m_type = type; |
|
} |
|
|
|
Annotation::RevisionType Annotation::Revision::type() const |
|
{ |
|
return d->m_type; |
|
} |
|
|
|
AnnotationPrivate::AnnotationPrivate() |
|
: m_page(nullptr) |
|
, m_flags(0) |
|
, m_disposeFunc(nullptr) |
|
{ |
|
} |
|
|
|
AnnotationPrivate::~AnnotationPrivate() |
|
{ |
|
// delete all children revisions |
|
if (m_revisions.isEmpty()) |
|
return; |
|
|
|
QLinkedList<Annotation::Revision>::iterator it = m_revisions.begin(), end = m_revisions.end(); |
|
for (; it != end; ++it) |
|
delete (*it).annotation(); |
|
} |
|
|
|
Annotation::Annotation(AnnotationPrivate &dd) |
|
: d_ptr(&dd) |
|
{ |
|
} |
|
|
|
Annotation::Annotation(AnnotationPrivate &dd, const QDomNode &description) |
|
: d_ptr(&dd) |
|
{ |
|
d_ptr->setAnnotationProperties(description); |
|
} |
|
|
|
Annotation::~Annotation() |
|
{ |
|
if (d_ptr->m_disposeFunc) |
|
d_ptr->m_disposeFunc(this); |
|
|
|
delete d_ptr; |
|
} |
|
|
|
void Annotation::setAuthor(const QString &author) |
|
{ |
|
Q_D(Annotation); |
|
d->m_author = author; |
|
} |
|
|
|
QString Annotation::author() const |
|
{ |
|
Q_D(const Annotation); |
|
return d->m_author; |
|
} |
|
|
|
void Annotation::setContents(const QString &contents) |
|
{ |
|
Q_D(Annotation); |
|
d->m_contents = contents; |
|
} |
|
|
|
QString Annotation::contents() const |
|
{ |
|
Q_D(const Annotation); |
|
return d->m_contents; |
|
} |
|
|
|
void Annotation::setUniqueName(const QString &name) |
|
{ |
|
Q_D(Annotation); |
|
d->m_uniqueName = name; |
|
} |
|
|
|
QString Annotation::uniqueName() const |
|
{ |
|
Q_D(const Annotation); |
|
return d->m_uniqueName; |
|
} |
|
|
|
void Annotation::setModificationDate(const QDateTime &date) |
|
{ |
|
Q_D(Annotation); |
|
d->m_modifyDate = date; |
|
} |
|
|
|
QDateTime Annotation::modificationDate() const |
|
{ |
|
Q_D(const Annotation); |
|
return d->m_modifyDate; |
|
} |
|
|
|
void Annotation::setCreationDate(const QDateTime &date) |
|
{ |
|
Q_D(Annotation); |
|
d->m_creationDate = date; |
|
} |
|
|
|
QDateTime Annotation::creationDate() const |
|
{ |
|
Q_D(const Annotation); |
|
return d->m_creationDate; |
|
} |
|
|
|
void Annotation::setFlags(int flags) |
|
{ |
|
Q_D(Annotation); |
|
d->m_flags = flags; |
|
} |
|
|
|
int Annotation::flags() const |
|
{ |
|
Q_D(const Annotation); |
|
return d->m_flags; |
|
} |
|
|
|
void Annotation::setBoundingRectangle(const NormalizedRect &rectangle) |
|
{ |
|
Q_D(Annotation); |
|
d->m_boundary = rectangle; |
|
d->resetTransformation(); |
|
if (d->m_page) { |
|
d->transform(d->m_page->rotationMatrix()); |
|
} |
|
} |
|
|
|
NormalizedRect Annotation::boundingRectangle() const |
|
{ |
|
Q_D(const Annotation); |
|
return d->m_boundary; |
|
} |
|
|
|
NormalizedRect Annotation::transformedBoundingRectangle() const |
|
{ |
|
Q_D(const Annotation); |
|
return d->m_transformedBoundary; |
|
} |
|
|
|
void Annotation::translate(const NormalizedPoint &coord) |
|
{ |
|
Q_D(Annotation); |
|
d->translate(coord); |
|
d->resetTransformation(); |
|
if (d->m_page) { |
|
d->transform(d->m_page->rotationMatrix()); |
|
} |
|
} |
|
|
|
void Annotation::adjust(const NormalizedPoint &deltaCoord1, const NormalizedPoint &deltaCoord2) |
|
{ |
|
Q_D(Annotation); |
|
d->adjust(deltaCoord1, deltaCoord2); |
|
d->resetTransformation(); |
|
if (d->m_page) { |
|
d->transform(d->m_page->rotationMatrix()); |
|
} |
|
} |
|
|
|
bool Annotation::openDialogAfterCreation() const |
|
{ |
|
Q_D(const Annotation); |
|
return d->openDialogAfterCreation(); |
|
} |
|
|
|
Annotation::Style &Annotation::style() |
|
{ |
|
Q_D(Annotation); |
|
return d->m_style; |
|
} |
|
|
|
const Annotation::Style &Annotation::style() const |
|
{ |
|
Q_D(const Annotation); |
|
return d->m_style; |
|
} |
|
|
|
Annotation::Window &Annotation::window() |
|
{ |
|
Q_D(Annotation); |
|
return d->m_window; |
|
} |
|
|
|
const Annotation::Window &Annotation::window() const |
|
{ |
|
Q_D(const Annotation); |
|
return d->m_window; |
|
} |
|
|
|
QLinkedList<Annotation::Revision> &Annotation::revisions() |
|
{ |
|
Q_D(Annotation); |
|
return d->m_revisions; |
|
} |
|
|
|
const QLinkedList<Annotation::Revision> &Annotation::revisions() const |
|
{ |
|
Q_D(const Annotation); |
|
return d->m_revisions; |
|
} |
|
|
|
void Annotation::setNativeId(const QVariant &id) |
|
{ |
|
Q_D(Annotation); |
|
d->m_nativeId = id; |
|
} |
|
|
|
QVariant Annotation::nativeId() const |
|
{ |
|
Q_D(const Annotation); |
|
return d->m_nativeId; |
|
} |
|
|
|
void Annotation::setDisposeDataFunction(DisposeDataFunction func) |
|
{ |
|
Q_D(Annotation); |
|
d->m_disposeFunc = func; |
|
} |
|
|
|
bool Annotation::canBeMoved() const |
|
{ |
|
Q_D(const Annotation); |
|
|
|
// Don't move annotations if they cannot be modified |
|
if (!d->m_page || !d->m_page->m_doc->m_parent->canModifyPageAnnotation(this)) |
|
return false; |
|
|
|
// highlight "requires" to be "bounded" to text, and that's tricky for now |
|
if (subType() == AHighlight) |
|
return false; |
|
|
|
return true; |
|
} |
|
|
|
bool Annotation::canBeResized() const |
|
{ |
|
Q_D(const Annotation); |
|
|
|
// Don't resize annotations if they cannot be modified |
|
if (!d->m_page || !d->m_page->m_doc->m_parent->canModifyPageAnnotation(this)) |
|
return false; |
|
|
|
return d->canBeResized(); |
|
} |
|
|
|
void Annotation::store(QDomNode &annNode, QDomDocument &document) const |
|
{ |
|
Q_D(const Annotation); |
|
// create [base] element of the annotation node |
|
QDomElement e = document.createElement(QStringLiteral("base")); |
|
annNode.appendChild(e); |
|
|
|
// store -contents- attributes |
|
if (!d->m_author.isEmpty()) |
|
e.setAttribute(QStringLiteral("author"), d->m_author); |
|
if (!d->m_contents.isEmpty()) |
|
e.setAttribute(QStringLiteral("contents"), d->m_contents); |
|
if (!d->m_uniqueName.isEmpty()) |
|
e.setAttribute(QStringLiteral("uniqueName"), d->m_uniqueName); |
|
if (d->m_modifyDate.isValid()) |
|
e.setAttribute(QStringLiteral("modifyDate"), d->m_modifyDate.toString(Qt::ISODate)); |
|
if (d->m_creationDate.isValid()) |
|
e.setAttribute(QStringLiteral("creationDate"), d->m_creationDate.toString(Qt::ISODate)); |
|
|
|
// store -other- attributes |
|
if (d->m_flags) // Strip internal flags |
|
e.setAttribute(QStringLiteral("flags"), d->m_flags & ~(External | ExternallyDrawn | BeingMoved | BeingResized)); |
|
if (d->m_style.color().isValid()) |
|
e.setAttribute(QStringLiteral("color"), d->m_style.color().name(QColor::HexArgb)); |
|
if (d->m_style.opacity() != 1.0) |
|
e.setAttribute(QStringLiteral("opacity"), QString::number(d->m_style.opacity())); |
|
|
|
// Sub-Node-1 - boundary |
|
QDomElement bE = document.createElement(QStringLiteral("boundary")); |
|
e.appendChild(bE); |
|
bE.setAttribute(QStringLiteral("l"), QString::number(d->m_boundary.left)); |
|
bE.setAttribute(QStringLiteral("t"), QString::number(d->m_boundary.top)); |
|
bE.setAttribute(QStringLiteral("r"), QString::number(d->m_boundary.right)); |
|
bE.setAttribute(QStringLiteral("b"), QString::number(d->m_boundary.bottom)); |
|
|
|
// Sub-Node-2 - penStyle |
|
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) { |
|
QDomElement psE = document.createElement(QStringLiteral("penStyle")); |
|
e.appendChild(psE); |
|
psE.setAttribute(QStringLiteral("width"), QString::number(d->m_style.width())); |
|
psE.setAttribute(QStringLiteral("style"), (int)d->m_style.lineStyle()); |
|
psE.setAttribute(QStringLiteral("xcr"), QString::number(d->m_style.xCorners())); |
|
psE.setAttribute(QStringLiteral("ycr"), QString::number(d->m_style.yCorners())); |
|
psE.setAttribute(QStringLiteral("marks"), d->m_style.marks()); |
|
psE.setAttribute(QStringLiteral("spaces"), d->m_style.spaces()); |
|
} |
|
|
|
// Sub-Node-3 - penEffect |
|
if (d->m_style.lineEffect() != NoEffect || d->m_style.effectIntensity() != 1.0) { |
|
QDomElement peE = document.createElement(QStringLiteral("penEffect")); |
|
e.appendChild(peE); |
|
peE.setAttribute(QStringLiteral("effect"), (int)d->m_style.lineEffect()); |
|
peE.setAttribute(QStringLiteral("intensity"), QString::number(d->m_style.effectIntensity())); |
|
} |
|
|
|
// Sub-Node-4 - window |
|
if (d->m_window.flags() != -1 || !d->m_window.title().isEmpty() || !d->m_window.summary().isEmpty()) { |
|
QDomElement wE = document.createElement(QStringLiteral("window")); |
|
e.appendChild(wE); |
|
wE.setAttribute(QStringLiteral("flags"), d->m_window.flags()); |
|
wE.setAttribute(QStringLiteral("top"), QString::number(d->m_window.topLeft().x)); |
|
wE.setAttribute(QStringLiteral("left"), QString::number(d->m_window.topLeft().y)); |
|
wE.setAttribute(QStringLiteral("width"), d->m_window.width()); |
|
wE.setAttribute(QStringLiteral("height"), d->m_window.height()); |
|
wE.setAttribute(QStringLiteral("title"), d->m_window.title()); |
|
wE.setAttribute(QStringLiteral("summary"), d->m_window.summary()); |
|
} |
|
|
|
// create [revision] element of the annotation node (if any) |
|
if (d->m_revisions.isEmpty()) |
|
return; |
|
|
|
// add all revisions as children of revisions element |
|
QLinkedList<Revision>::const_iterator it = d->m_revisions.begin(), end = d->m_revisions.end(); |
|
for (; it != end; ++it) { |
|
// create revision element |
|
const Revision &revision = *it; |
|
QDomElement r = document.createElement(QStringLiteral("revision")); |
|
annNode.appendChild(r); |
|
// set element attributes |
|
r.setAttribute(QStringLiteral("revScope"), (int)revision.scope()); |
|
r.setAttribute(QStringLiteral("revType"), (int)revision.type()); |
|
// use revision as the annotation element, so fill it up |
|
AnnotationUtils::storeAnnotation(revision.annotation(), r, document); |
|
} |
|
} |
|
|
|
QDomNode Annotation::getAnnotationPropertiesDomNode() const |
|
{ |
|
QDomDocument doc(QStringLiteral("documentInfo")); |
|
QDomElement node = doc.createElement(QStringLiteral("annotation")); |
|
|
|
store(node, doc); |
|
return node; |
|
} |
|
|
|
void Annotation::setAnnotationProperties(const QDomNode &node) |
|
{ |
|
// Save off internal properties that aren't contained in node |
|
Okular::PagePrivate *p = d_ptr->m_page; |
|
QVariant nativeID = d_ptr->m_nativeId; |
|
const int internalFlags = d_ptr->m_flags & (External | ExternallyDrawn | BeingMoved | BeingResized); |
|
Annotation::DisposeDataFunction disposeFunc = d_ptr->m_disposeFunc; |
|
|
|
// Replace AnnotationPrivate object with a fresh copy |
|
AnnotationPrivate *new_d_ptr = d_ptr->getNewAnnotationPrivate(); |
|
delete (d_ptr); |
|
d_ptr = new_d_ptr; |
|
|
|
// Set the annotations properties from node |
|
d_ptr->setAnnotationProperties(node); |
|
|
|
// Restore internal properties |
|
d_ptr->m_page = p; |
|
d_ptr->m_nativeId = nativeID; |
|
d_ptr->m_flags = d_ptr->m_flags | internalFlags; |
|
d_ptr->m_disposeFunc = disposeFunc; |
|
|
|
// Transform annotation to current page rotation |
|
d_ptr->transform(d_ptr->m_page->rotationMatrix()); |
|
} |
|
|
|
double AnnotationPrivate::distanceSqr(double x, double y, double xScale, double yScale) const |
|
{ |
|
return m_transformedBoundary.distanceSqr(x, y, xScale, yScale); |
|
} |
|
|
|
void AnnotationPrivate::annotationTransform(const QTransform &matrix) |
|
{ |
|
resetTransformation(); |
|
transform(matrix); |
|
} |
|
|
|
void AnnotationPrivate::transform(const QTransform &matrix) |
|
{ |
|
m_transformedBoundary.transform(matrix); |
|
} |
|
|
|
void AnnotationPrivate::baseTransform(const QTransform &matrix) |
|
{ |
|
m_boundary.transform(matrix); |
|
} |
|
|
|
void AnnotationPrivate::resetTransformation() |
|
{ |
|
m_transformedBoundary = m_boundary; |
|
} |
|
|
|
void AnnotationPrivate::translate(const NormalizedPoint &coord) |
|
{ |
|
m_boundary.left = m_boundary.left + coord.x; |
|
m_boundary.right = m_boundary.right + coord.x; |
|
m_boundary.top = m_boundary.top + coord.y; |
|
m_boundary.bottom = m_boundary.bottom + coord.y; |
|
} |
|
|
|
void AnnotationPrivate::adjust(const NormalizedPoint &deltaCoord1, const NormalizedPoint &deltaCoord2) |
|
{ |
|
m_boundary.left = m_boundary.left + qBound(-m_boundary.left, deltaCoord1.x, m_boundary.right - m_boundary.left); |
|
m_boundary.top = m_boundary.top + qBound(-m_boundary.top, deltaCoord1.y, m_boundary.bottom - m_boundary.top); |
|
; |
|
m_boundary.right = m_boundary.right + qBound(m_boundary.left - m_boundary.right, deltaCoord2.x, 1. - m_boundary.right); |
|
m_boundary.bottom = m_boundary.bottom + qBound(m_boundary.top - m_boundary.bottom, deltaCoord2.y, 1. - m_boundary.bottom); |
|
} |
|
|
|
bool AnnotationPrivate::openDialogAfterCreation() const |
|
{ |
|
return false; |
|
} |
|
|
|
void AnnotationPrivate::setAnnotationProperties(const QDomNode &node) |
|
{ |
|
// get the [base] element of the annotation node |
|
QDomElement e = AnnotationUtils::findChildElement(node, QStringLiteral("base")); |
|
if (e.isNull()) |
|
return; |
|
|
|
// parse -contents- attributes |
|
if (e.hasAttribute(QStringLiteral("author"))) |
|
m_author = e.attribute(QStringLiteral("author")); |
|
if (e.hasAttribute(QStringLiteral("contents"))) |
|
m_contents = e.attribute(QStringLiteral("contents")); |
|
if (e.hasAttribute(QStringLiteral("uniqueName"))) |
|
m_uniqueName = e.attribute(QStringLiteral("uniqueName")); |
|
if (e.hasAttribute(QStringLiteral("modifyDate"))) |
|
m_modifyDate = QDateTime::fromString(e.attribute(QStringLiteral("modifyDate")), Qt::ISODate); |
|
if (e.hasAttribute(QStringLiteral("creationDate"))) |
|
m_creationDate = QDateTime::fromString(e.attribute(QStringLiteral("creationDate")), Qt::ISODate); |
|
|
|
// parse -other- attributes |
|
if (e.hasAttribute(QStringLiteral("flags"))) |
|
m_flags = e.attribute(QStringLiteral("flags")).toInt(); |
|
if (e.hasAttribute(QStringLiteral("color"))) |
|
m_style.setColor(QColor(e.attribute(QStringLiteral("color")))); |
|
if (e.hasAttribute(QStringLiteral("opacity"))) |
|
m_style.setOpacity(e.attribute(QStringLiteral("opacity")).toDouble()); |
|
|
|
// parse -the-subnodes- (describing Style, Window, Revision(s) structures) |
|
// Note: all subnodes if present must be 'attributes complete' |
|
QDomNode eSubNode = e.firstChild(); |
|
while (eSubNode.isElement()) { |
|
QDomElement ee = eSubNode.toElement(); |
|
eSubNode = eSubNode.nextSibling(); |
|
|
|
// parse boundary |
|
if (ee.tagName() == QLatin1String("boundary")) { |
|
m_boundary = NormalizedRect(ee.attribute(QStringLiteral("l")).toDouble(), ee.attribute(QStringLiteral("t")).toDouble(), ee.attribute(QStringLiteral("r")).toDouble(), ee.attribute(QStringLiteral("b")).toDouble()); |
|
} |
|
// parse penStyle if not default |
|
else if (ee.tagName() == QLatin1String("penStyle")) { |
|
m_style.setWidth(ee.attribute(QStringLiteral("width")).toDouble()); |
|
m_style.setLineStyle((Annotation::LineStyle)ee.attribute(QStringLiteral("style")).toInt()); |
|
m_style.setXCorners(ee.attribute(QStringLiteral("xcr")).toDouble()); |
|
m_style.setYCorners(ee.attribute(QStringLiteral("ycr")).toDouble()); |
|
m_style.setMarks(ee.attribute(QStringLiteral("marks")).toInt()); |
|
m_style.setSpaces(ee.attribute(QStringLiteral("spaces")).toInt()); |
|
} |
|
// parse effectStyle if not default |
|
else if (ee.tagName() == QLatin1String("penEffect")) { |
|
m_style.setLineEffect((Annotation::LineEffect)ee.attribute(QStringLiteral("effect")).toInt()); |
|
m_style.setEffectIntensity(ee.attribute(QStringLiteral("intensity")).toDouble()); |
|
} |
|
// parse window if present |
|
else if (ee.tagName() == QLatin1String("window")) { |
|
m_window.setFlags(ee.attribute(QStringLiteral("flags")).toInt()); |
|
m_window.setTopLeft(NormalizedPoint(ee.attribute(QStringLiteral("top")).toDouble(), ee.attribute(QStringLiteral("left")).toDouble())); |
|
m_window.setWidth(ee.attribute(QStringLiteral("width")).toInt()); |
|
m_window.setHeight(ee.attribute(QStringLiteral("height")).toInt()); |
|
m_window.setTitle(ee.attribute(QStringLiteral("title"))); |
|
m_window.setSummary(ee.attribute(QStringLiteral("summary"))); |
|
} |
|
} |
|
|
|
// get the [revisions] element of the annotation node |
|
QDomNode revNode = node.firstChild(); |
|
for (; revNode.isElement(); revNode = revNode.nextSibling()) { |
|
QDomElement revElement = revNode.toElement(); |
|
if (revElement.tagName() != QLatin1String("revision")) |
|
continue; |
|
|
|
// compile the Revision structure crating annotation |
|
Annotation::Revision revision; |
|
revision.setScope((Annotation::RevisionScope)revElement.attribute(QStringLiteral("revScope")).toInt()); |
|
revision.setType((Annotation::RevisionType)revElement.attribute(QStringLiteral("revType")).toInt()); |
|
revision.setAnnotation(AnnotationUtils::createAnnotation(revElement)); |
|
|
|
// if annotation is valid, add revision to internal list |
|
if (revision.annotation()) |
|
m_revisions.append(revision); |
|
} |
|
|
|
m_transformedBoundary = m_boundary; |
|
} |
|
|
|
bool AnnotationPrivate::canBeResized() const |
|
{ |
|
return false; |
|
} |
|
|
|
// END Annotation implementation |
|
|
|
/** TextAnnotation [Annotation] */ |
|
|
|
class Okular::TextAnnotationPrivate : public Okular::AnnotationPrivate |
|
{ |
|
public: |
|
TextAnnotationPrivate() |
|
: AnnotationPrivate() |
|
, m_textType(TextAnnotation::Linked) |
|
, m_textIcon(QStringLiteral("Comment")) |
|
, m_inplaceAlign(0) |
|
, m_inplaceIntent(TextAnnotation::Unknown) |
|
{ |
|
} |
|
|
|
void transform(const QTransform &matrix) override; |
|
void baseTransform(const QTransform &matrix) override; |
|
void resetTransformation() override; |
|
void translate(const NormalizedPoint &coord) override; |
|
bool openDialogAfterCreation() const override; |
|
void setAnnotationProperties(const QDomNode &node) override; |
|
bool canBeResized() const override; |
|
AnnotationPrivate *getNewAnnotationPrivate() override; |
|
|
|
TextAnnotation::TextType m_textType; |
|
QString m_textIcon; |
|
QFont m_textFont; |
|
QColor m_textColor; |
|
int m_inplaceAlign; |
|
NormalizedPoint m_inplaceCallout[3]; |
|
NormalizedPoint m_transformedInplaceCallout[3]; |
|
TextAnnotation::InplaceIntent m_inplaceIntent; |
|
}; |
|
|
|
/* |
|
The default textIcon for text annotation is Note as the PDF Reference says |
|
*/ |
|
TextAnnotation::TextAnnotation() |
|
: Annotation(*new TextAnnotationPrivate()) |
|
{ |
|
} |
|
|
|
TextAnnotation::TextAnnotation(const QDomNode &description) |
|
: Annotation(*new TextAnnotationPrivate(), description) |
|
{ |
|
} |
|
|
|
TextAnnotation::~TextAnnotation() |
|
{ |
|
} |
|
|
|
void TextAnnotation::setTextType(TextType textType) |
|
{ |
|
Q_D(TextAnnotation); |
|
d->m_textType = textType; |
|
} |
|
|
|
TextAnnotation::TextType TextAnnotation::textType() const |
|
{ |
|
Q_D(const TextAnnotation); |
|
return d->m_textType; |
|
} |
|
|
|
void TextAnnotation::setTextIcon(const QString &icon) |
|
{ |
|
Q_D(TextAnnotation); |
|
d->m_textIcon = icon; |
|
} |
|
|
|
QString TextAnnotation::textIcon() const |
|
{ |
|
Q_D(const TextAnnotation); |
|
return d->m_textIcon; |
|
} |
|
|
|
void TextAnnotation::setTextFont(const QFont &font) |
|
{ |
|
Q_D(TextAnnotation); |
|
d->m_textFont = font; |
|
} |
|
|
|
QFont TextAnnotation::textFont() const |
|
{ |
|
Q_D(const TextAnnotation); |
|
return d->m_textFont; |
|
} |
|
|
|
void TextAnnotation::setTextColor(const QColor &color) |
|
{ |
|
Q_D(TextAnnotation); |
|
d->m_textColor = color; |
|
} |
|
|
|
QColor TextAnnotation::textColor() const |
|
{ |
|
Q_D(const TextAnnotation); |
|
return d->m_textColor; |
|
} |
|
|
|
void TextAnnotation::setInplaceAlignment(int alignment) |
|
{ |
|
Q_D(TextAnnotation); |
|
d->m_inplaceAlign = alignment; |
|
} |
|
|
|
int TextAnnotation::inplaceAlignment() const |
|
{ |
|
Q_D(const TextAnnotation); |
|
return d->m_inplaceAlign; |
|
} |
|
|
|
void TextAnnotation::setInplaceCallout(const NormalizedPoint &point, int index) |
|
{ |
|
if (index < 0 || index > 2) |
|
return; |
|
|
|
Q_D(TextAnnotation); |
|
d->m_inplaceCallout[index] = point; |
|
} |
|
|
|
NormalizedPoint TextAnnotation::inplaceCallout(int index) const |
|
{ |
|
if (index < 0 || index > 2) |
|
return NormalizedPoint(); |
|
|
|
Q_D(const TextAnnotation); |
|
return d->m_inplaceCallout[index]; |
|
} |
|
|
|
NormalizedPoint TextAnnotation::transformedInplaceCallout(int index) const |
|
{ |
|
if (index < 0 || index > 2) |
|
return NormalizedPoint(); |
|
|
|
Q_D(const TextAnnotation); |
|
return d->m_transformedInplaceCallout[index]; |
|
} |
|
|
|
void TextAnnotation::setInplaceIntent(InplaceIntent intent) |
|
{ |
|
Q_D(TextAnnotation); |
|
d->m_inplaceIntent = intent; |
|
} |
|
|
|
TextAnnotation::InplaceIntent TextAnnotation::inplaceIntent() const |
|
{ |
|
Q_D(const TextAnnotation); |
|
return d->m_inplaceIntent; |
|
} |
|
|
|
Annotation::SubType TextAnnotation::subType() const |
|
{ |
|
return AText; |
|
} |
|
|
|
void TextAnnotation::store(QDomNode &node, QDomDocument &document) const |
|
{ |
|
Q_D(const TextAnnotation); |
|
// recurse to parent objects storing properties |
|
Annotation::store(node, document); |
|
|
|
// create [text] element |
|
QDomElement textElement = document.createElement(QStringLiteral("text")); |
|
node.appendChild(textElement); |
|
|
|
// store the optional attributes |
|
if (d->m_textType != Linked) |
|
textElement.setAttribute(QStringLiteral("type"), (int)d->m_textType); |
|
if (!d->m_textIcon.isEmpty()) |
|
textElement.setAttribute(QStringLiteral("icon"), d->m_textIcon); |
|
if (d->m_textFont != QApplication::font()) |
|
textElement.setAttribute(QStringLiteral("font"), d->m_textFont.toString()); |
|
if (d->m_textColor.isValid()) |
|
textElement.setAttribute(QStringLiteral("fontColor"), d->m_textColor.name()); |
|
if (d->m_inplaceAlign) |
|
textElement.setAttribute(QStringLiteral("align"), d->m_inplaceAlign); |
|
if (d->m_inplaceIntent != Unknown) |
|
textElement.setAttribute(QStringLiteral("intent"), (int)d->m_inplaceIntent); |
|
|
|
// Sub-Node - callout |
|
if (d->m_inplaceCallout[0].x != 0.0) { |
|
QDomElement calloutElement = document.createElement(QStringLiteral("callout")); |
|
textElement.appendChild(calloutElement); |
|
calloutElement.setAttribute(QStringLiteral("ax"), QString::number(d->m_inplaceCallout[0].x)); |
|
calloutElement.setAttribute(QStringLiteral("ay"), QString::number(d->m_inplaceCallout[0].y)); |
|
calloutElement.setAttribute(QStringLiteral("bx"), QString::number(d->m_inplaceCallout[1].x)); |
|
calloutElement.setAttribute(QStringLiteral("by"), QString::number(d->m_inplaceCallout[1].y)); |
|
calloutElement.setAttribute(QStringLiteral("cx"), QString::number(d->m_inplaceCallout[2].x)); |
|
calloutElement.setAttribute(QStringLiteral("cy"), QString::number(d->m_inplaceCallout[2].y)); |
|
} |
|
} |
|
|
|
void TextAnnotationPrivate::transform(const QTransform &matrix) |
|
{ |
|
AnnotationPrivate::transform(matrix); |
|
|
|
for (NormalizedPoint &np : m_transformedInplaceCallout) { |
|
np.transform(matrix); |
|
} |
|
} |
|
|
|
void TextAnnotationPrivate::baseTransform(const QTransform &matrix) |
|
{ |
|
AnnotationPrivate::baseTransform(matrix); |
|
|
|
for (NormalizedPoint &np : m_inplaceCallout) { |
|
np.transform(matrix); |
|
} |
|
} |
|
|
|
void TextAnnotationPrivate::resetTransformation() |
|
{ |
|
AnnotationPrivate::resetTransformation(); |
|
|
|
for (int i = 0; i < 3; ++i) { |
|
m_transformedInplaceCallout[i] = m_inplaceCallout[i]; |
|
} |
|
} |
|
|
|
void TextAnnotationPrivate::translate(const NormalizedPoint &coord) |
|
{ |
|
AnnotationPrivate::translate(coord); |
|
|
|
#define ADD_COORD(c1, c2) \ |
|
{ \ |
|
c1.x = c1.x + c2.x; \ |
|
c1.y = c1.y + c2.y; \ |
|
} |
|
ADD_COORD(m_inplaceCallout[0], coord) |
|
ADD_COORD(m_inplaceCallout[1], coord) |
|
ADD_COORD(m_inplaceCallout[2], coord) |
|
#undef ADD_COORD |
|
} |
|
|
|
bool TextAnnotationPrivate::openDialogAfterCreation() const |
|
{ |
|
return (m_textType == Okular::TextAnnotation::Linked); |
|
} |
|
|
|
void TextAnnotationPrivate::setAnnotationProperties(const QDomNode &node) |
|
{ |
|
Okular::AnnotationPrivate::setAnnotationProperties(node); |
|
|
|
// loop through the whole children looking for a 'text' element |
|
QDomNode subNode = node.firstChild(); |
|
while (subNode.isElement()) { |
|
QDomElement e = subNode.toElement(); |
|
subNode = subNode.nextSibling(); |
|
if (e.tagName() != QLatin1String("text")) |
|
continue; |
|
|
|
// parse the attributes |
|
if (e.hasAttribute(QStringLiteral("type"))) |
|
m_textType = (TextAnnotation::TextType)e.attribute(QStringLiteral("type")).toInt(); |
|
if (e.hasAttribute(QStringLiteral("icon"))) |
|
m_textIcon = e.attribute(QStringLiteral("icon")); |
|
if (e.hasAttribute(QStringLiteral("font"))) |
|
m_textFont.fromString(e.attribute(QStringLiteral("font"))); |
|
if (e.hasAttribute(QStringLiteral("fontColor"))) |
|
m_textColor = QColor(e.attribute(QStringLiteral("fontColor"))); |
|
if (e.hasAttribute(QStringLiteral("align"))) |
|
m_inplaceAlign = e.attribute(QStringLiteral("align")).toInt(); |
|
if (e.hasAttribute(QStringLiteral("intent"))) |
|
m_inplaceIntent = (TextAnnotation::InplaceIntent)e.attribute(QStringLiteral("intent")).toInt(); |
|
|
|
// parse the subnodes |
|
QDomNode eSubNode = e.firstChild(); |
|
while (eSubNode.isElement()) { |
|
QDomElement ee = eSubNode.toElement(); |
|
eSubNode = eSubNode.nextSibling(); |
|
|
|
if (ee.tagName() == QLatin1String("escapedText")) { |
|
m_contents = ee.firstChild().toCDATASection().data(); |
|
} else if (ee.tagName() == QLatin1String("callout")) { |
|
m_inplaceCallout[0].x = ee.attribute(QStringLiteral("ax")).toDouble(); |
|
m_inplaceCallout[0].y = ee.attribute(QStringLiteral("ay")).toDouble(); |
|
m_inplaceCallout[1].x = ee.attribute(QStringLiteral("bx")).toDouble(); |
|
m_inplaceCallout[1].y = ee.attribute(QStringLiteral("by")).toDouble(); |
|
m_inplaceCallout[2].x = ee.attribute(QStringLiteral("cx")).toDouble(); |
|
m_inplaceCallout[2].y = ee.attribute(QStringLiteral("cy")).toDouble(); |
|
} |
|
} |
|
|
|
// loading complete |
|
break; |
|
} |
|
|
|
for (int i = 0; i < 3; ++i) |
|
m_transformedInplaceCallout[i] = m_inplaceCallout[i]; |
|
} |
|
|
|
bool TextAnnotationPrivate::canBeResized() const |
|
{ |
|
if (m_textType != TextAnnotation::Linked) { |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
AnnotationPrivate *TextAnnotationPrivate::getNewAnnotationPrivate() |
|
{ |
|
return new TextAnnotationPrivate(); |
|
} |
|
|
|
/** LineAnnotation [Annotation] */ |
|
|
|
class Okular::LineAnnotationPrivate : public Okular::AnnotationPrivate |
|
{ |
|
public: |
|
LineAnnotationPrivate() |
|
: AnnotationPrivate() |
|
, m_lineStartStyle(LineAnnotation::None) |
|
, m_lineEndStyle(LineAnnotation::None) |
|
, m_lineClosed(false) |
|
, m_lineShowCaption(false) |
|
, m_lineLeadingFwdPt(0) |
|
, m_lineLeadingBackPt(0) |
|
, m_lineIntent(LineAnnotation::Unknown) |
|
{ |
|
} |
|
|
|
void transform(const QTransform &matrix) override; |
|
void baseTransform(const QTransform &matrix) override; |
|
void resetTransformation() override; |
|
void translate(const NormalizedPoint &coord) override; |
|
double distanceSqr(double x, double y, double xScale, double yScale) const override; |
|
void setAnnotationProperties(const QDomNode &node) override; |
|
AnnotationPrivate *getNewAnnotationPrivate() override; |
|
|
|
QLinkedList<NormalizedPoint> m_linePoints; |
|
QLinkedList<NormalizedPoint> m_transformedLinePoints; |
|
LineAnnotation::TermStyle m_lineStartStyle; |
|
LineAnnotation::TermStyle m_lineEndStyle; |
|
bool m_lineClosed : 1; |
|
bool m_lineShowCaption : 1; |
|
QColor m_lineInnerColor; |
|
double m_lineLeadingFwdPt; |
|
double m_lineLeadingBackPt; |
|
LineAnnotation::LineIntent m_lineIntent; |
|
}; |
|
|
|
LineAnnotation::LineAnnotation() |
|
: Annotation(*new LineAnnotationPrivate()) |
|
{ |
|
} |
|
|
|
LineAnnotation::LineAnnotation(const QDomNode &description) |
|
: Annotation(*new LineAnnotationPrivate(), description) |
|
{ |
|
} |
|
|
|
LineAnnotation::~LineAnnotation() |
|
{ |
|
} |
|
|
|
void LineAnnotation::setLinePoints(const QLinkedList<NormalizedPoint> &points) |
|
{ |
|
Q_D(LineAnnotation); |
|
d->m_linePoints = points; |
|
} |
|
|
|
QLinkedList<NormalizedPoint> LineAnnotation::linePoints() const |
|
{ |
|
Q_D(const LineAnnotation); |
|
return d->m_linePoints; |
|
} |
|
|
|
QLinkedList<NormalizedPoint> LineAnnotation::transformedLinePoints() const |
|
{ |
|
Q_D(const LineAnnotation); |
|
return d->m_transformedLinePoints; |
|
} |
|
|
|
void LineAnnotation::setLineStartStyle(TermStyle style) |
|
{ |
|
Q_D(LineAnnotation); |
|
d->m_lineStartStyle = style; |
|
} |
|
|
|
LineAnnotation::TermStyle LineAnnotation::lineStartStyle() const |
|
{ |
|
Q_D(const LineAnnotation); |
|
return d->m_lineStartStyle; |
|
} |
|
|
|
void LineAnnotation::setLineEndStyle(TermStyle style) |
|
{ |
|
Q_D(LineAnnotation); |
|
d->m_lineEndStyle = style; |
|
} |
|
|
|
LineAnnotation::TermStyle LineAnnotation::lineEndStyle() const |
|
{ |
|
Q_D(const LineAnnotation); |
|
return d->m_lineEndStyle; |
|
} |
|
|
|
void LineAnnotation::setLineClosed(bool closed) |
|
{ |
|
Q_D(LineAnnotation); |
|
d->m_lineClosed = closed; |
|
} |
|
|
|
bool LineAnnotation::lineClosed() const |
|
{ |
|
Q_D(const LineAnnotation); |
|
return d->m_lineClosed; |
|
} |
|
|
|
void LineAnnotation::setLineInnerColor(const QColor &color) |
|
{ |
|
Q_D(LineAnnotation); |
|
d->m_lineInnerColor = color; |
|
} |
|
|
|
QColor LineAnnotation::lineInnerColor() const |
|
{ |
|
Q_D(const LineAnnotation); |
|
return d->m_lineInnerColor; |
|
} |
|
|
|
void LineAnnotation::setLineLeadingForwardPoint(double point) |
|
{ |
|
Q_D(LineAnnotation); |
|
d->m_lineLeadingFwdPt = point; |
|
} |
|
|
|
double LineAnnotation::lineLeadingForwardPoint() const |
|
{ |
|
Q_D(const LineAnnotation); |
|
return d->m_lineLeadingFwdPt; |
|
} |
|
|
|
void LineAnnotation::setLineLeadingBackwardPoint(double point) |
|
{ |
|
Q_D(LineAnnotation); |
|
d->m_lineLeadingBackPt = point; |
|
} |
|
|
|
double LineAnnotation::lineLeadingBackwardPoint() const |
|
{ |
|
Q_D(const LineAnnotation); |
|
return d->m_lineLeadingBackPt; |
|
} |
|
|
|
void LineAnnotation::setShowCaption(bool show) |
|
{ |
|
Q_D(LineAnnotation); |
|
d->m_lineShowCaption = show; |
|
} |
|
|
|
bool LineAnnotation::showCaption() const |
|
{ |
|
Q_D(const LineAnnotation); |
|
return d->m_lineShowCaption; |
|
} |
|
|
|
void LineAnnotation::setLineIntent(LineIntent intent) |
|
{ |
|
Q_D(LineAnnotation); |
|
d->m_lineIntent = intent; |
|
} |
|
|
|
LineAnnotation::LineIntent LineAnnotation::lineIntent() const |
|
{ |
|
Q_D(const LineAnnotation); |
|
return d->m_lineIntent; |
|
} |
|
|
|
Annotation::SubType LineAnnotation::subType() const |
|
{ |
|
return ALine; |
|
} |
|
|
|
void LineAnnotation::store(QDomNode &node, QDomDocument &document) const |
|
{ |
|
Q_D(const LineAnnotation); |
|
// recurse to parent objects storing properties |
|
Annotation::store(node, document); |
|
|
|
// create [line] element |
|
QDomElement lineElement = document.createElement(QStringLiteral("line")); |
|
node.appendChild(lineElement); |
|
|
|
// store the attributes |
|
if (d->m_lineStartStyle != None) |
|
lineElement.setAttribute(QStringLiteral("startStyle"), (int)d->m_lineStartStyle); |
|
if (d->m_lineEndStyle != None) |
|
lineElement.setAttribute(QStringLiteral("endStyle"), (int)d->m_lineEndStyle); |
|
if (d->m_lineClosed) |
|
lineElement.setAttribute(QStringLiteral("closed"), d->m_lineClosed); |
|
if (d->m_lineInnerColor.isValid()) |
|
lineElement.setAttribute(QStringLiteral("innerColor"), d->m_lineInnerColor.name()); |
|
if (d->m_lineLeadingFwdPt != 0.0) |
|
lineElement.setAttribute(QStringLiteral("leadFwd"), QString::number(d->m_lineLeadingFwdPt)); |
|
if (d->m_lineLeadingBackPt != 0.0) |
|
lineElement.setAttribute(QStringLiteral("leadBack"), QString::number(d->m_lineLeadingBackPt)); |
|
if (d->m_lineShowCaption) |
|
lineElement.setAttribute(QStringLiteral("showCaption"), d->m_lineShowCaption); |
|
if (d->m_lineIntent != Unknown) |
|
lineElement.setAttribute(QStringLiteral("intent"), d->m_lineIntent); |
|
|
|
// append the list of points |
|
int points = d->m_linePoints.count(); |
|
if (points > 1) { |
|
QLinkedList<NormalizedPoint>::const_iterator it = d->m_linePoints.begin(), end = d->m_linePoints.end(); |
|
while (it != end) { |
|
const NormalizedPoint &p = *it; |
|
QDomElement pElement = document.createElement(QStringLiteral("point")); |
|
lineElement.appendChild(pElement); |
|
pElement.setAttribute(QStringLiteral("x"), QString::number(p.x)); |
|
pElement.setAttribute(QStringLiteral("y"), QString::number(p.y)); |
|
it++; // to avoid loop |
|
} |
|
} |
|
} |
|
|
|
void LineAnnotationPrivate::transform(const QTransform &matrix) |
|
{ |
|
AnnotationPrivate::transform(matrix); |
|
|
|
QMutableLinkedListIterator<NormalizedPoint> it(m_transformedLinePoints); |
|
while (it.hasNext()) |
|
it.next().transform(matrix); |
|
} |
|
|
|
void LineAnnotationPrivate::baseTransform(const QTransform &matrix) |
|
{ |
|
AnnotationPrivate::baseTransform(matrix); |
|
|
|
QMutableLinkedListIterator<NormalizedPoint> it(m_linePoints); |
|
while (it.hasNext()) |
|
it.next().transform(matrix); |
|
} |
|
|
|
void LineAnnotationPrivate::resetTransformation() |
|
{ |
|
AnnotationPrivate::resetTransformation(); |
|
|
|
m_transformedLinePoints = m_linePoints; |
|
} |
|
|
|
void LineAnnotationPrivate::translate(const NormalizedPoint &coord) |
|
{ |
|
AnnotationPrivate::translate(coord); |
|
|
|
QMutableLinkedListIterator<NormalizedPoint> it(m_linePoints); |
|
while (it.hasNext()) { |
|
NormalizedPoint &p = it.next(); |
|
p.x = p.x + coord.x; |
|
p.y = p.y + coord.y; |
|
} |
|
} |
|
|
|
void LineAnnotationPrivate::setAnnotationProperties(const QDomNode &node) |
|
{ |
|
Okular::AnnotationPrivate::setAnnotationProperties(node); |
|
|
|
// loop through the whole children looking for a 'line' element |
|
QDomNode subNode = node.firstChild(); |
|
while (subNode.isElement()) { |
|
QDomElement e = subNode.toElement(); |
|
subNode = subNode.nextSibling(); |
|
if (e.tagName() != QLatin1String("line")) |
|
continue; |
|
|
|
// parse the attributes |
|
if (e.hasAttribute(QStringLiteral("startStyle"))) |
|
m_lineStartStyle = (LineAnnotation::TermStyle)e.attribute(QStringLiteral("startStyle")).toInt(); |
|
if (e.hasAttribute(QStringLiteral("endStyle"))) |
|
m_lineEndStyle = (LineAnnotation::TermStyle)e.attribute(QStringLiteral("endStyle")).toInt(); |
|
if (e.hasAttribute(QStringLiteral("closed"))) |
|
m_lineClosed = e.attribute(QStringLiteral("closed")).toInt(); |
|
if (e.hasAttribute(QStringLiteral("innerColor"))) |
|
m_lineInnerColor = QColor(e.attribute(QStringLiteral("innerColor"))); |
|
if (e.hasAttribute(QStringLiteral("leadFwd"))) |
|
m_lineLeadingFwdPt = e.attribute(QStringLiteral("leadFwd")).toDouble(); |
|
if (e.hasAttribute(QStringLiteral("leadBack"))) |
|
m_lineLeadingBackPt = e.attribute(QStringLiteral("leadBack")).toDouble(); |
|
if (e.hasAttribute(QStringLiteral("showCaption"))) |
|
m_lineShowCaption = e.attribute(QStringLiteral("showCaption")).toInt(); |
|
if (e.hasAttribute(QStringLiteral("intent"))) |
|
m_lineIntent = (LineAnnotation::LineIntent)e.attribute(QStringLiteral("intent")).toInt(); |
|
|
|
// parse all 'point' subnodes |
|
QDomNode pointNode = e.firstChild(); |
|
while (pointNode.isElement()) { |
|
QDomElement pe = pointNode.toElement(); |
|
pointNode = pointNode.nextSibling(); |
|
|
|
if (pe.tagName() != QLatin1String("point")) |
|
continue; |
|
|
|
NormalizedPoint p; |
|
p.x = pe.attribute(QStringLiteral("x"), QStringLiteral("0.0")).toDouble(); |
|
p.y = pe.attribute(QStringLiteral("y"), QStringLiteral("0.0")).toDouble(); |
|
m_linePoints.append(p); |
|
} |
|
|
|
// loading complete |
|
break; |
|
} |
|
|
|
m_transformedLinePoints = m_linePoints; |
|
} |
|
|
|
AnnotationPrivate *LineAnnotationPrivate::getNewAnnotationPrivate() |
|
{ |
|
return new LineAnnotationPrivate(); |
|
} |
|
|
|
double LineAnnotationPrivate::distanceSqr(double x, double y, double xScale, double yScale) const |
|
{ |
|
QLinkedList<NormalizedPoint> transformedLinePoints = m_transformedLinePoints; |
|
|
|
if (m_lineClosed) // Close the path |
|
transformedLinePoints.append(transformedLinePoints.first()); |
|
|
|
if (m_lineInnerColor.isValid()) { |
|
QPolygonF polygon; |
|
for (const NormalizedPoint &p : qAsConst(transformedLinePoints)) |
|
polygon.append(QPointF(p.x, p.y)); |
|
|
|
if (polygon.containsPoint(QPointF(x, y), Qt::WindingFill)) |
|
return 0; |
|
} |
|
|
|
return strokeDistance(::distanceSqr(x, y, xScale, yScale, transformedLinePoints), m_style.width() * xScale / (m_page->m_width * 2)); |
|
} |
|
|
|
/** GeomAnnotation [Annotation] */ |
|
|
|
class Okular::GeomAnnotationPrivate : public Okular::AnnotationPrivate |
|
{ |
|
public: |
|
GeomAnnotationPrivate() |
|
: AnnotationPrivate() |
|
, m_geomType(GeomAnnotation::InscribedSquare) |
|
{ |
|
} |
|
void setAnnotationProperties(const QDomNode &node) override; |
|
bool canBeResized() const override; |
|
AnnotationPrivate *getNewAnnotationPrivate() override; |
|
double distanceSqr(double x, double y, double xScale, double yScale) const override; |
|
|
|
GeomAnnotation::GeomType m_geomType; |
|
QColor m_geomInnerColor; |
|
}; |
|
|
|
GeomAnnotation::GeomAnnotation() |
|
: Annotation(*new GeomAnnotationPrivate()) |
|
{ |
|
} |
|
|
|
GeomAnnotation::GeomAnnotation(const QDomNode &description) |
|
: Annotation(*new GeomAnnotationPrivate(), description) |
|
{ |
|
} |
|
|
|
GeomAnnotation::~GeomAnnotation() |
|
{ |
|
} |
|
|
|
void GeomAnnotation::setGeometricalType(GeomType type) |
|
{ |
|
Q_D(GeomAnnotation); |
|
d->m_geomType = type; |
|
} |
|
|
|
GeomAnnotation::GeomType GeomAnnotation::geometricalType() const |
|
{ |
|
Q_D(const GeomAnnotation); |
|
return d->m_geomType; |
|
} |
|
|
|
void GeomAnnotation::setGeometricalInnerColor(const QColor &color) |
|
{ |
|
Q_D(GeomAnnotation); |
|
d->m_geomInnerColor = color; |
|
} |
|
|
|
QColor GeomAnnotation::geometricalInnerColor() const |
|
{ |
|
Q_D(const GeomAnnotation); |
|
return d->m_geomInnerColor; |
|
} |
|
|
|
Annotation::SubType GeomAnnotation::subType() const |
|
{ |
|
return AGeom; |
|
} |
|
|
|
void GeomAnnotation::store(QDomNode &node, QDomDocument &document) const |
|
{ |
|
Q_D(const GeomAnnotation); |
|
// recurse to parent objects storing properties |
|
Annotation::store(node, document); |
|
|
|
// create [geom] element |
|
QDomElement geomElement = document.createElement(QStringLiteral("geom")); |
|
node.appendChild(geomElement); |
|
|
|
// append the optional attributes |
|
if (d->m_geomType != InscribedSquare) |
|
geomElement.setAttribute(QStringLiteral("type"), (int)d->m_geomType); |
|
if (d->m_geomInnerColor.isValid()) |
|
geomElement.setAttribute(QStringLiteral("color"), d->m_geomInnerColor.name()); |
|
} |
|
|
|
void GeomAnnotationPrivate::setAnnotationProperties(const QDomNode &node) |
|
{ |
|
Okular::AnnotationPrivate::setAnnotationProperties(node); |
|
// loop through the whole children looking for a 'geom' element |
|
QDomNode subNode = node.firstChild(); |
|
while (subNode.isElement()) { |
|
QDomElement e = subNode.toElement(); |
|
subNode = subNode.nextSibling(); |
|
if (e.tagName() != QLatin1String("geom")) |
|
continue; |
|
|
|
// parse the attributes |
|
if (e.hasAttribute(QStringLiteral("type"))) |
|
m_geomType = (GeomAnnotation::GeomType)e.attribute(QStringLiteral("type")).toInt(); |
|
if (e.hasAttribute(QStringLiteral("color"))) |
|
m_geomInnerColor = QColor(e.attribute(QStringLiteral("color"))); |
|
// compatibility |
|
if (e.hasAttribute(QStringLiteral("width"))) |
|
m_style.setWidth(e.attribute(QStringLiteral("width")).toInt()); |
|
|
|
// loading complete |
|
break; |
|
} |
|
} |
|
|
|
bool GeomAnnotationPrivate::canBeResized() const |
|
{ |
|
return true; |
|
} |
|
|
|
AnnotationPrivate *GeomAnnotationPrivate::getNewAnnotationPrivate() |
|
{ |
|
return new GeomAnnotationPrivate(); |
|
} |
|
|
|
double GeomAnnotationPrivate::distanceSqr(double x, double y, double xScale, double yScale) const |
|
{ |
|
double distance = 0; |
|
// the line thickness is applied unevenly (only on the "inside") - account for this |
|
bool withinShape = false; |
|
switch (m_geomType) { |
|
case GeomAnnotation::InscribedCircle: { |
|
// calculate the center point and focus lengths of the ellipse |
|
const double centerX = (m_transformedBoundary.left + m_transformedBoundary.right) / 2.0; |
|
const double centerY = (m_transformedBoundary.top + m_transformedBoundary.bottom) / 2.0; |
|
const double focusX = (m_transformedBoundary.right - centerX); |
|
const double focusY = (m_transformedBoundary.bottom - centerY); |
|
|
|
const double focusXSqr = pow(focusX, 2); |
|
const double focusYSqr = pow(focusY, 2); |
|
|
|
// to calculate the distance from the ellipse, we will first find the point "projection" |
|
// that lies on the ellipse and is closest to the point (x,y) |
|
// This point can obviously be written as "center + lambda(inputPoint - center)". |
|
// Because the point lies on the ellipse, we know that: |
|
// 1 = ((center.x - projection.x)/focusX)^2 + ((center.y - projection.y)/focusY)^2 |
|
// After filling in projection.x = center.x + lambda * (inputPoint.x - center.x) |
|
// and its y-equivalent, we can solve for lambda: |
|
const double lambda = sqrt(focusXSqr * focusYSqr / (focusYSqr * pow(x - centerX, 2) + focusXSqr * pow(y - centerY, 2))); |
|
|
|
// if the ellipse is filled, we treat all points within as "on" it |
|
if (lambda > 1) { |
|
if (m_geomInnerColor.isValid()) |
|
return 0; |
|
else |
|
withinShape = true; |
|
} |
|
|
|
// otherwise we calculate the squared distance from the projected point on the ellipse |
|
NormalizedPoint projection(centerX, centerY); |
|
projection.x += lambda * (x - centerX); |
|
projection.y += lambda * (y - centerY); |
|
|
|
distance = projection.distanceSqr(x, y, xScale, yScale); |
|
break; |
|
} |
|
|
|
case GeomAnnotation::InscribedSquare: |
|
// if the square is filled, only check the bounding box |
|
if (m_geomInnerColor.isValid()) |
|
return AnnotationPrivate::distanceSqr(x, y, xScale, yScale); |
|
|
|
QLinkedList<NormalizedPoint> edges; |
|
edges << NormalizedPoint(m_transformedBoundary.left, m_transformedBoundary.top); |
|
edges << NormalizedPoint(m_transformedBoundary.right, m_transformedBoundary.top); |
|
edges << NormalizedPoint(m_transformedBoundary.right, m_transformedBoundary.bottom); |
|
edges << NormalizedPoint(m_transformedBoundary.left, m_transformedBoundary.bottom); |
|
edges << NormalizedPoint(m_transformedBoundary.left, m_transformedBoundary.top); |
|
distance = ::distanceSqr(x, y, xScale, yScale, edges); |
|
|
|
if (m_transformedBoundary.contains(x, y)) |
|
withinShape = true; |
|
|
|
break; |
|
} |
|
if (withinShape) |
|
distance = strokeDistance(distance, m_style.width() * xScale / m_page->m_width); |
|
|
|
return distance; |
|
} |
|
|
|
/** HighlightAnnotation [Annotation] */ |
|
|
|
class HighlightAnnotation::Quad::Private |
|
{ |
|
public: |
|
Private() |
|
{ |
|
} |
|
|
|
NormalizedPoint m_points[4]; |
|
NormalizedPoint m_transformedPoints[4]; |
|
bool m_capStart : 1; |
|
bool m_capEnd : 1; |
|
double m_feather; |
|
}; |
|
|
|
HighlightAnnotation::Quad::Quad() |
|
: d(new Private) |
|
{ |
|
} |
|
|
|
HighlightAnnotation::Quad::~Quad() |
|
{ |
|
delete d; |
|
} |
|
|
|
HighlightAnnotation::Quad::Quad(const Quad &other) |
|
: d(new Private) |
|
{ |
|
*d = *other.d; |
|
} |
|
|
|
HighlightAnnotation::Quad &HighlightAnnotation::Quad::operator=(const Quad &other) |
|
{ |
|
if (this != &other) |
|
*d = *other.d; |
|
|
|
return *this; |
|
} |
|
|
|
void HighlightAnnotation::Quad::setPoint(const NormalizedPoint &point, int index) |
|
{ |
|
if (index < 0 || index > 3) |
|
return; |
|
|
|
d->m_points[index] = point; |
|
} |
|
|
|
NormalizedPoint HighlightAnnotation::Quad::point(int index) const |
|
{ |
|
if (index < 0 || index > 3) |
|
return NormalizedPoint(); |
|
|
|
return d->m_points[index]; |
|
} |
|
|
|
NormalizedPoint HighlightAnnotation::Quad::transformedPoint(int index) const |
|
{ |
|
if (index < 0 || index > 3) |
|
return NormalizedPoint(); |
|
|
|
return d->m_transformedPoints[index]; |
|
} |
|
|
|
void HighlightAnnotation::Quad::setCapStart(bool value) |
|
{ |
|
d->m_capStart = value; |
|
} |
|
|
|
bool HighlightAnnotation::Quad::capStart() const |
|
{ |
|
return d->m_capStart; |
|
} |
|
|
|
void HighlightAnnotation::Quad::setCapEnd(bool value) |
|
{ |
|
d->m_capEnd = value; |
|
} |
|
|
|
bool HighlightAnnotation::Quad::capEnd() const |
|
{ |
|
return d->m_capEnd; |
|
} |
|
|
|
void HighlightAnnotation::Quad::setFeather(double width) |
|
{ |
|
d->m_feather = width; |
|
} |
|
|
|
double HighlightAnnotation::Quad::feather() const |
|
{ |
|
return d->m_feather; |
|
} |
|
|
|
void HighlightAnnotation::Quad::transform(const QTransform &matrix) |
|
{ |
|
for (int i = 0; i < 4; ++i) { |
|
d->m_transformedPoints[i] = d->m_points[i]; |
|
d->m_transformedPoints[i].transform(matrix); |
|
} |
|
} |
|
|
|
class Okular::HighlightAnnotationPrivate : public Okular::AnnotationPrivate |
|
{ |
|
public: |
|
HighlightAnnotationPrivate() |
|
: AnnotationPrivate() |
|
, m_highlightType(HighlightAnnotation::Highlight) |
|
{ |
|
} |
|
|
|
void transform(const QTransform &matrix) override; |
|
void baseTransform(const QTransform &matrix) override; |
|
double distanceSqr(double x, double y, double xScale, double yScale) const override; |
|
void setAnnotationProperties(const QDomNode &node) override; |
|
AnnotationPrivate *getNewAnnotationPrivate() override; |
|
|
|
HighlightAnnotation::HighlightType m_highlightType; |
|
QList<HighlightAnnotation::Quad> m_highlightQuads; |
|
}; |
|
|
|
HighlightAnnotation::HighlightAnnotation() |
|
: Annotation(*new HighlightAnnotationPrivate()) |
|
{ |
|
} |
|
|
|
HighlightAnnotation::HighlightAnnotation(const QDomNode &description) |
|
: Annotation(*new HighlightAnnotationPrivate(), description) |
|
{ |
|
} |
|
|
|
HighlightAnnotation::~HighlightAnnotation() |
|
{ |
|
} |
|
|
|
void HighlightAnnotation::setHighlightType(HighlightType type) |
|
{ |
|
Q_D(HighlightAnnotation); |
|
d->m_highlightType = type; |
|
} |
|
|
|
HighlightAnnotation::HighlightType HighlightAnnotation::highlightType() const |
|
{ |
|
Q_D(const HighlightAnnotation); |
|
return d->m_highlightType; |
|
} |
|
|
|
QList<HighlightAnnotation::Quad> &HighlightAnnotation::highlightQuads() |
|
{ |
|
Q_D(HighlightAnnotation); |
|
return d->m_highlightQuads; |
|
} |
|
|
|
const QList<HighlightAnnotation::Quad> &HighlightAnnotation::highlightQuads() const |
|
{ |
|
Q_D(const HighlightAnnotation); |
|
return d->m_highlightQuads; |
|
} |
|
|
|
void HighlightAnnotation::store(QDomNode &node, QDomDocument &document) const |
|
{ |
|
Q_D(const HighlightAnnotation); |
|
// recurse to parent objects storing properties |
|
Annotation::store(node, document); |
|
|
|
// create [hl] element |
|
QDomElement hlElement = document.createElement(QStringLiteral("hl")); |
|
node.appendChild(hlElement); |
|
|
|
// append the optional attributes |
|
if (d->m_highlightType != Highlight) |
|
hlElement.setAttribute(QStringLiteral("type"), (int)d->m_highlightType); |
|
if (d->m_highlightQuads.count() < 1) |
|
return; |
|
// append highlight quads, all children describe quads |
|
QList<Quad>::const_iterator it = d->m_highlightQuads.begin(), end = d->m_highlightQuads.end(); |
|
for (; it != end; ++it) { |
|
QDomElement quadElement = document.createElement(QStringLiteral("quad")); |
|
hlElement.appendChild(quadElement); |
|
const Quad &q = *it; |
|
quadElement.setAttribute(QStringLiteral("ax"), QString::number(q.point(0).x)); |
|
quadElement.setAttribute(QStringLiteral("ay"), QString::number(q.point(0).y)); |
|
quadElement.setAttribute(QStringLiteral("bx"), QString::number(q.point(1).x)); |
|
quadElement.setAttribute(QStringLiteral("by"), QString::number(q.point(1).y)); |
|
quadElement.setAttribute(QStringLiteral("cx"), QString::number(q.point(2).x)); |
|
quadElement.setAttribute(QStringLiteral("cy"), QString::number(q.point(2).y)); |
|
quadElement.setAttribute(QStringLiteral("dx"), QString::number(q.point(3).x)); |
|
quadElement.setAttribute(QStringLiteral("dy"), QString::number(q.point(3).y)); |
|
if (q.capStart()) |
|
quadElement.setAttribute(QStringLiteral("start"), 1); |
|
if (q.capEnd()) |
|
quadElement.setAttribute(QStringLiteral("end"), 1); |
|
quadElement.setAttribute(QStringLiteral("feather"), QString::number(q.feather())); |
|
} |
|
} |
|
|
|
Annotation::SubType HighlightAnnotation::subType() const |
|
{ |
|
return AHighlight; |
|
} |
|
|
|
void HighlightAnnotationPrivate::transform(const QTransform &matrix) |
|
{ |
|
AnnotationPrivate::transform(matrix); |
|
|
|
QMutableListIterator<HighlightAnnotation::Quad> it(m_highlightQuads); |
|
while (it.hasNext()) |
|
it.next().transform(matrix); |
|
} |
|
|
|
void HighlightAnnotationPrivate::baseTransform(const QTransform &matrix) |
|
{ |
|
AnnotationPrivate::baseTransform(matrix); |
|
|
|
QMutableListIterator<HighlightAnnotation::Quad> it(m_highlightQuads); |
|
while (it.hasNext()) |
|
it.next().transform(matrix); |
|
} |
|
|
|
void HighlightAnnotationPrivate::setAnnotationProperties(const QDomNode &node) |
|
{ |
|
Okular::AnnotationPrivate::setAnnotationProperties(node); |
|
m_highlightQuads.clear(); |
|
|
|
// loop through the whole children looking for a 'hl' element |
|
QDomNode subNode = node.firstChild(); |
|
while (subNode.isElement()) { |
|
QDomElement e = subNode.toElement(); |
|
subNode = subNode.nextSibling(); |
|
if (e.tagName() != QLatin1String("hl")) |
|
continue; |
|
|
|
// parse the attributes |
|
if (e.hasAttribute(QStringLiteral("type"))) |
|
m_highlightType = (HighlightAnnotation::HighlightType)e.attribute(QStringLiteral("type")).toInt(); |
|
|
|
// parse all 'quad' subnodes |
|
QDomNode quadNode = e.firstChild(); |
|
for (; quadNode.isElement(); quadNode = quadNode.nextSibling()) { |
|
QDomElement qe = quadNode.toElement(); |
|
if (qe.tagName() != QLatin1String("quad")) |
|
continue; |
|
|
|
HighlightAnnotation::Quad q; |
|
q.setPoint(NormalizedPoint(qe.attribute(QStringLiteral("ax"), QStringLiteral("0.0")).toDouble(), qe.attribute(QStringLiteral("ay"), QStringLiteral("0.0")).toDouble()), 0); |
|
q.setPoint(NormalizedPoint(qe.attribute(QStringLiteral("bx"), QStringLiteral("0.0")).toDouble(), qe.attribute(QStringLiteral("by"), QStringLiteral("0.0")).toDouble()), 1); |
|
q.setPoint(NormalizedPoint(qe.attribute(QStringLiteral("cx"), QStringLiteral("0.0")).toDouble(), qe.attribute(QStringLiteral("cy"), QStringLiteral("0.0")).toDouble()), 2); |
|
q.setPoint(NormalizedPoint(qe.attribute(QStringLiteral("dx"), QStringLiteral("0.0")).toDouble(), qe.attribute(QStringLiteral("dy"), QStringLiteral("0.0")).toDouble()), 3); |
|
q.setCapStart(qe.hasAttribute(QStringLiteral("start"))); |
|
q.setCapEnd(qe.hasAttribute(QStringLiteral("end"))); |
|
q.setFeather(qe.attribute(QStringLiteral("feather"), QStringLiteral("0.1")).toDouble()); |
|
|
|
q.transform(QTransform()); |
|
|
|
m_highlightQuads.append(q); |
|
} |
|
|
|
// loading complete |
|
break; |
|
} |
|
} |
|
|
|
AnnotationPrivate *HighlightAnnotationPrivate::getNewAnnotationPrivate() |
|
{ |
|
return new HighlightAnnotationPrivate(); |
|
} |
|
|
|
double HighlightAnnotationPrivate::distanceSqr(double x, double y, double xScale, double yScale) const |
|
{ |
|
NormalizedPoint point(x, y); |
|
double outsideDistance = DBL_MAX; |
|
for (const HighlightAnnotation::Quad &quad : m_highlightQuads) { |
|
QLinkedList<NormalizedPoint> pathPoints; |
|
|
|
// first, we check if the point is within the area described by the 4 quads |
|
// this is the case, if the point is always on one side of each segments delimiting the polygon: |
|
pathPoints << quad.transformedPoint(0); |
|
int directionVote = 0; |
|
for (int i = 1; i < 5; ++i) { |
|
NormalizedPoint thisPoint = quad.transformedPoint(i % 4); |
|
directionVote += (isLeftOfVector(pathPoints.back(), thisPoint, point)) ? 1 : -1; |
|
pathPoints << thisPoint; |
|
} |
|
if (abs(directionVote) == 4) |
|
return 0; |
|
|
|
// if that's not the case, we treat the outline as path and simply determine |
|
// the distance from the path to the point |
|
const double thisOutsideDistance = ::distanceSqr(x, y, xScale, yScale, pathPoints); |
|
if (thisOutsideDistance < outsideDistance) |
|
outsideDistance = thisOutsideDistance; |
|
} |
|
|
|
return outsideDistance; |
|
} |
|
|
|
/** StampAnnotation [Annotation] */ |
|
|
|
class Okular::StampAnnotationPrivate : public Okular::AnnotationPrivate |
|
{ |
|
public: |
|
StampAnnotationPrivate() |
|
: AnnotationPrivate() |
|
, m_stampIconName(QStringLiteral("Draft")) |
|
{ |
|
} |
|
void setAnnotationProperties(const QDomNode &node) override; |
|
bool canBeResized() const override; |
|
AnnotationPrivate *getNewAnnotationPrivate() override; |
|
|
|
QString m_stampIconName; |
|
}; |
|
|
|
StampAnnotation::StampAnnotation() |
|
: Annotation(*new StampAnnotationPrivate()) |
|
{ |
|
} |
|
|
|
StampAnnotation::StampAnnotation(const QDomNode &description) |
|
: Annotation(*new StampAnnotationPrivate(), description) |
|
{ |
|
} |
|
|
|
StampAnnotation::~StampAnnotation() |
|
{ |
|
} |
|
|
|
void StampAnnotation::setStampIconName(const QString &name) |
|
{ |
|
Q_D(StampAnnotation); |
|
d->m_stampIconName = name; |
|
} |
|
|
|
QString StampAnnotation::stampIconName() const |
|
{ |
|
Q_D(const StampAnnotation); |
|
return d->m_stampIconName; |
|
} |
|
|
|
Annotation::SubType StampAnnotation::subType() const |
|
{ |
|
return AStamp; |
|
} |
|
|
|
void StampAnnotation::store(QDomNode &node, QDomDocument &document) const |
|
{ |
|
Q_D(const StampAnnotation); |
|
// recurse to parent objects storing properties |
|
Annotation::store(node, document); |
|
|
|
// create [stamp] element |
|
QDomElement stampElement = document.createElement(QStringLiteral("stamp")); |
|
node.appendChild(stampElement); |
|
|
|
// append the optional attributes |
|
if (d->m_stampIconName != QLatin1String("Draft")) |
|
stampElement.setAttribute(QStringLiteral("icon"), d->m_stampIconName); |
|
} |
|
|
|
void StampAnnotationPrivate::setAnnotationProperties(const QDomNode &node) |
|
{ |
|
Okular::AnnotationPrivate::setAnnotationProperties(node); |
|
|
|
// loop through the whole children looking for a 'stamp' element |
|
QDomNode subNode = node.firstChild(); |
|
while (subNode.isElement()) { |
|
QDomElement e = subNode.toElement(); |
|
subNode = subNode.nextSibling(); |
|
if (e.tagName() != QLatin1String("stamp")) |
|
continue; |
|
|
|
// parse the attributes |
|
if (e.hasAttribute(QStringLiteral("icon"))) |
|
m_stampIconName = e.attribute(QStringLiteral("icon")); |
|
|
|
// loading complete |
|
break; |
|
} |
|
} |
|
|
|
bool StampAnnotationPrivate::canBeResized() const |
|
{ |
|
return true; |
|
} |
|
|
|
AnnotationPrivate *StampAnnotationPrivate::getNewAnnotationPrivate() |
|
{ |
|
return new StampAnnotationPrivate(); |
|
} |
|
|
|
/** InkAnnotation [Annotation] */ |
|
|
|
class Okular::InkAnnotationPrivate : public Okular::AnnotationPrivate |
|
{ |
|
public: |
|
InkAnnotationPrivate() |
|
: AnnotationPrivate() |
|
{ |
|
} |
|
|
|
void transform(const QTransform &matrix) override; |
|
void baseTransform(const QTransform &matrix) override; |
|
void resetTransformation() override; |
|
double distanceSqr(double x, double y, double xScale, double yScale) const override; |
|
void translate(const NormalizedPoint &coord) override; |
|
void setAnnotationProperties(const QDomNode &node) override; |
|
AnnotationPrivate *getNewAnnotationPrivate() override; |
|
|
|
QList<QLinkedList<NormalizedPoint>> m_inkPaths; |
|
QList<QLinkedList<NormalizedPoint>> m_transformedInkPaths; |
|
}; |
|
|
|
InkAnnotation::InkAnnotation() |
|
: Annotation(*new InkAnnotationPrivate()) |
|
{ |
|
} |
|
|
|
InkAnnotation::InkAnnotation(const QDomNode &description) |
|
: Annotation(*new InkAnnotationPrivate(), description) |
|
{ |
|
} |
|
|
|
InkAnnotation::~InkAnnotation() |
|
{ |
|
} |
|
|
|
void InkAnnotation::setInkPaths(const QList<QLinkedList<NormalizedPoint>> &paths) |
|
{ |
|
Q_D(InkAnnotation); |
|
d->m_inkPaths = paths; |
|
} |
|
|
|
QList<QLinkedList<NormalizedPoint>> InkAnnotation::inkPaths() const |
|
{ |
|
Q_D(const InkAnnotation); |
|
return d->m_inkPaths; |
|
} |
|
|
|
QList<QLinkedList<NormalizedPoint>> InkAnnotation::transformedInkPaths() const |
|
{ |
|
Q_D(const InkAnnotation); |
|
return d->m_transformedInkPaths; |
|
} |
|
|
|
Annotation::SubType InkAnnotation::subType() const |
|
{ |
|
return AInk; |
|
} |
|
|
|
void InkAnnotation::store(QDomNode &node, QDomDocument &document) const |
|
{ |
|
Q_D(const InkAnnotation); |
|
// recurse to parent objects storing properties |
|
Annotation::store(node, document); |
|
|
|
// create [ink] element |
|
QDomElement inkElement = document.createElement(QStringLiteral("ink")); |
|
node.appendChild(inkElement); |
|
|
|
// append the optional attributes |
|
if (d->m_inkPaths.count() < 1) |
|
return; |
|
|
|
QList<QLinkedList<NormalizedPoint>>::const_iterator pIt = d->m_inkPaths.begin(), pEnd = d->m_inkPaths.end(); |
|
for (; pIt != pEnd; ++pIt) { |
|
QDomElement pathElement = document.createElement(QStringLiteral("path")); |
|
inkElement.appendChild(pathElement); |
|
const QLinkedList<NormalizedPoint> &path = *pIt; |
|
QLinkedList<NormalizedPoint>::const_iterator iIt = path.begin(), iEnd = path.end(); |
|
for (; iIt != iEnd; ++iIt) { |
|
const NormalizedPoint &point = *iIt; |
|
QDomElement pointElement = document.createElement(QStringLiteral("point")); |
|
pathElement.appendChild(pointElement); |
|
pointElement.setAttribute(QStringLiteral("x"), QString::number(point.x)); |
|
pointElement.setAttribute(QStringLiteral("y"), QString::number(point.y)); |
|
} |
|
} |
|
} |
|
|
|
double InkAnnotationPrivate::distanceSqr(double x, double y, double xScale, double yScale) const |
|
{ |
|
double distance = DBL_MAX; |
|
for (const QLinkedList<NormalizedPoint> &path : m_transformedInkPaths) { |
|
const double thisDistance = ::distanceSqr(x, y, xScale, yScale, path); |
|
if (thisDistance < distance) |
|
distance = thisDistance; |
|
} |
|
return strokeDistance(distance, m_style.width() * xScale / (m_page->m_width * 2)); |
|
} |
|
|
|
void InkAnnotationPrivate::transform(const QTransform &matrix) |
|
{ |
|
AnnotationPrivate::transform(matrix); |
|
|
|
for (int i = 0; i < m_transformedInkPaths.count(); ++i) { |
|
QMutableLinkedListIterator<NormalizedPoint> it(m_transformedInkPaths[i]); |
|
while (it.hasNext()) |
|
it.next().transform(matrix); |
|
} |
|
} |
|
|
|
void InkAnnotationPrivate::baseTransform(const QTransform &matrix) |
|
{ |
|
AnnotationPrivate::baseTransform(matrix); |
|
|
|
for (int i = 0; i < m_inkPaths.count(); ++i) { |
|
QMutableLinkedListIterator<NormalizedPoint> it(m_inkPaths[i]); |
|
while (it.hasNext()) |
|
it.next().transform(matrix); |
|
} |
|
} |
|
|
|
void InkAnnotationPrivate::resetTransformation() |
|
{ |
|
AnnotationPrivate::resetTransformation(); |
|
|
|
m_transformedInkPaths = m_inkPaths; |
|
} |
|
|
|
void InkAnnotationPrivate::translate(const NormalizedPoint &coord) |
|
{ |
|
AnnotationPrivate::translate(coord); |
|
|
|
for (int i = 0; i < m_inkPaths.count(); ++i) { |
|
QMutableLinkedListIterator<NormalizedPoint> it(m_inkPaths[i]); |
|
while (it.hasNext()) { |
|
NormalizedPoint &p = it.next(); |
|
p.x = p.x + coord.x; |
|
p.y = p.y + coord.y; |
|
} |
|
} |
|
} |
|
|
|
void InkAnnotationPrivate::setAnnotationProperties(const QDomNode &node) |
|
{ |
|
Okular::AnnotationPrivate::setAnnotationProperties(node); |
|
m_inkPaths.clear(); |
|
|
|
// loop through the whole children looking for a 'ink' element |
|
QDomNode subNode = node.firstChild(); |
|
while (subNode.isElement()) { |
|
QDomElement e = subNode.toElement(); |
|
subNode = subNode.nextSibling(); |
|
if (e.tagName() != QLatin1String("ink")) |
|
continue; |
|
|
|
// parse the 'path' subnodes |
|
QDomNode pathNode = e.firstChild(); |
|
while (pathNode.isElement()) { |
|
QDomElement pathElement = pathNode.toElement(); |
|
pathNode = pathNode.nextSibling(); |
|
|
|
if (pathElement.tagName() != QLatin1String("path")) |
|
continue; |
|
|
|
// build each path parsing 'point' subnodes |
|
QLinkedList<NormalizedPoint> path; |
|
QDomNode pointNode = pathElement.firstChild(); |
|
while (pointNode.isElement()) { |
|
QDomElement pointElement = pointNode.toElement(); |
|
pointNode = pointNode.nextSibling(); |
|
|
|
if (pointElement.tagName() != QLatin1String("point")) |
|
continue; |
|
|
|
NormalizedPoint p; |
|
p.x = pointElement.attribute(QStringLiteral("x"), QStringLiteral("0.0")).toDouble(); |
|
p.y = pointElement.attribute(QStringLiteral("y"), QStringLiteral("0.0")).toDouble(); |
|
path.append(p); |
|
} |
|
|
|
// add the path to the path list if it contains at least 2 nodes |
|
if (path.count() >= 2) |
|
m_inkPaths.append(path); |
|
} |
|
|
|
// loading complete |
|
break; |
|
} |
|
|
|
m_transformedInkPaths = m_inkPaths; |
|
} |
|
|
|
AnnotationPrivate *InkAnnotationPrivate::getNewAnnotationPrivate() |
|
{ |
|
return new InkAnnotationPrivate(); |
|
} |
|
|
|
/** CaretAnnotation [Annotation] */ |
|
|
|
class Okular::CaretAnnotationPrivate : public Okular::AnnotationPrivate |
|
{ |
|
public: |
|
CaretAnnotationPrivate() |
|
: AnnotationPrivate() |
|
, m_symbol(CaretAnnotation::None) |
|
{ |
|
} |
|
|
|
void setAnnotationProperties(const QDomNode &node) override; |
|
AnnotationPrivate *getNewAnnotationPrivate() override; |
|
|
|
CaretAnnotation::CaretSymbol m_symbol; |
|
}; |
|
|
|
static QString caretSymbolToString(CaretAnnotation::CaretSymbol symbol) |
|
{ |
|
switch (symbol) { |
|
case CaretAnnotation::None: |
|
return QStringLiteral("None"); |
|
case CaretAnnotation::P: |
|
return QStringLiteral("P"); |
|
} |
|
return QString(); |
|
} |
|
|
|
static CaretAnnotation::CaretSymbol caretSymbolFromString(const QString &symbol) |
|
{ |
|
if (symbol == QLatin1String("None")) |
|
return CaretAnnotation::None; |
|
else if (symbol == QLatin1String("P")) |
|
return CaretAnnotation::P; |
|
return CaretAnnotation::None; |
|
} |
|
|
|
void CaretAnnotationPrivate::setAnnotationProperties(const QDomNode &node) |
|
{ |
|
Okular::AnnotationPrivate::setAnnotationProperties(node); |
|
|
|
// loop through the whole children looking for a 'caret' element |
|
QDomNode subNode = node.firstChild(); |
|
while (subNode.isElement()) { |
|
QDomElement e = subNode.toElement(); |
|
subNode = subNode.nextSibling(); |
|
if (e.tagName() != QLatin1String("caret")) |
|
continue; |
|
|
|
// parse the attributes |
|
if (e.hasAttribute(QStringLiteral("symbol"))) |
|
m_symbol = caretSymbolFromString(e.attribute(QStringLiteral("symbol"))); |
|
|
|
// loading complete |
|
break; |
|
} |
|
} |
|
|
|
AnnotationPrivate *CaretAnnotationPrivate::getNewAnnotationPrivate() |
|
{ |
|
return new CaretAnnotationPrivate(); |
|
} |
|
|
|
CaretAnnotation::CaretAnnotation() |
|
: Annotation(*new CaretAnnotationPrivate()) |
|
{ |
|
} |
|
|
|
CaretAnnotation::CaretAnnotation(const QDomNode &description) |
|
: Annotation(*new CaretAnnotationPrivate(), description) |
|
{ |
|
} |
|
|
|
CaretAnnotation::~CaretAnnotation() |
|
{ |
|
} |
|
|
|
void CaretAnnotation::setCaretSymbol(CaretAnnotation::CaretSymbol symbol) |
|
{ |
|
Q_D(CaretAnnotation); |
|
d->m_symbol = symbol; |
|
} |
|
|
|
CaretAnnotation::CaretSymbol CaretAnnotation::caretSymbol() const |
|
{ |
|
Q_D(const CaretAnnotation); |
|
return d->m_symbol; |
|
} |
|
|
|
Annotation::SubType CaretAnnotation::subType() const |
|
{ |
|
return ACaret; |
|
} |
|
|
|
void CaretAnnotation::store(QDomNode &node, QDomDocument &document) const |
|
{ |
|
Q_D(const CaretAnnotation); |
|
// recurse to parent objects storing properties |
|
Annotation::store(node, document); |
|
|
|
// create [caret] element |
|
QDomElement caretElement = document.createElement(QStringLiteral("caret")); |
|
node.appendChild(caretElement); |
|
|
|
// append the optional attributes |
|
if (d->m_symbol != None) |
|
caretElement.setAttribute(QStringLiteral("symbol"), caretSymbolToString(d->m_symbol)); |
|
} |
|
|
|
/** FileAttachmentAnnotation [Annotation] */ |
|
|
|
class Okular::FileAttachmentAnnotationPrivate : public Okular::AnnotationPrivate |
|
{ |
|
public: |
|
FileAttachmentAnnotationPrivate() |
|
: AnnotationPrivate() |
|
, icon(QStringLiteral("PushPin")) |
|
, embfile(nullptr) |
|
{ |
|
} |
|
~FileAttachmentAnnotationPrivate() override |
|
{ |
|
delete embfile; |
|
} |
|
|
|
void setAnnotationProperties(const QDomNode &node) override; |
|
AnnotationPrivate *getNewAnnotationPrivate() override; |
|
|
|
// data fields |
|
QString icon; |
|
EmbeddedFile *embfile; |
|
}; |
|
|
|
void FileAttachmentAnnotationPrivate::setAnnotationProperties(const QDomNode &node) |
|
{ |
|
Okular::AnnotationPrivate::setAnnotationProperties(node); |
|
|
|
// loop through the whole children looking for a 'fileattachment' element |
|
QDomNode subNode = node.firstChild(); |
|
while (subNode.isElement()) { |
|
QDomElement e = subNode.toElement(); |
|
subNode = subNode.nextSibling(); |
|
if (e.tagName() != QLatin1String("fileattachment")) |
|
continue; |
|
|
|
// loading complete |
|
break; |
|
} |
|
} |
|
|
|
AnnotationPrivate *FileAttachmentAnnotationPrivate::getNewAnnotationPrivate() |
|
{ |
|
return new FileAttachmentAnnotationPrivate(); |
|
} |
|
|
|
FileAttachmentAnnotation::FileAttachmentAnnotation() |
|
: Annotation(*new FileAttachmentAnnotationPrivate()) |
|
{ |
|
} |
|
|
|
FileAttachmentAnnotation::FileAttachmentAnnotation(const QDomNode &description) |
|
: Annotation(*new FileAttachmentAnnotationPrivate(), description) |
|
{ |
|
} |
|
|
|
FileAttachmentAnnotation::~FileAttachmentAnnotation() |
|
{ |
|
} |
|
|
|
void FileAttachmentAnnotation::store(QDomNode &node, QDomDocument &document) const |
|
{ |
|
// recurse to parent objects storing properties |
|
Annotation::store(node, document); |
|
|
|
// create [fileattachment] element |
|
QDomElement fileAttachmentElement = document.createElement(QStringLiteral("fileattachment")); |
|
node.appendChild(fileAttachmentElement); |
|
} |
|
|
|
Annotation::SubType FileAttachmentAnnotation::subType() const |
|
{ |
|
return AFileAttachment; |
|
} |
|
|
|
QString FileAttachmentAnnotation::fileIconName() const |
|
{ |
|
Q_D(const FileAttachmentAnnotation); |
|
return d->icon; |
|
} |
|
|
|
void FileAttachmentAnnotation::setFileIconName(const QString &iconName) |
|
{ |
|
Q_D(FileAttachmentAnnotation); |
|
d->icon = iconName; |
|
} |
|
|
|
EmbeddedFile *FileAttachmentAnnotation::embeddedFile() const |
|
{ |
|
Q_D(const FileAttachmentAnnotation); |
|
return d->embfile; |
|
} |
|
|
|
void FileAttachmentAnnotation::setEmbeddedFile(EmbeddedFile *ef) |
|
{ |
|
Q_D(FileAttachmentAnnotation); |
|
d->embfile = ef; |
|
} |
|
|
|
/** SoundAnnotation [Annotation] */ |
|
|
|
class Okular::SoundAnnotationPrivate : public Okular::AnnotationPrivate |
|
{ |
|
public: |
|
SoundAnnotationPrivate() |
|
: AnnotationPrivate() |
|
, icon(QStringLiteral("Speaker")) |
|
, sound(nullptr) |
|
{ |
|
} |
|
~SoundAnnotationPrivate() override |
|
{ |
|
delete sound; |
|
} |
|
|
|
void setAnnotationProperties(const QDomNode &node) override; |
|
AnnotationPrivate *getNewAnnotationPrivate() override; |
|
|
|
// data fields |
|
QString icon; |
|
Sound *sound; |
|
}; |
|
|
|
void SoundAnnotationPrivate::setAnnotationProperties(const QDomNode &node) |
|
{ |
|
Okular::AnnotationPrivate::setAnnotationProperties(node); |
|
|
|
// loop through the whole children looking for a 'sound' element |
|
QDomNode subNode = node.firstChild(); |
|
while (subNode.isElement()) { |
|
QDomElement e = subNode.toElement(); |
|
subNode = subNode.nextSibling(); |
|
if (e.tagName() != QLatin1String("sound")) |
|
continue; |
|
|
|
// loading complete |
|
break; |
|
} |
|
} |
|
|
|
AnnotationPrivate *SoundAnnotationPrivate::getNewAnnotationPrivate() |
|
{ |
|
return new SoundAnnotationPrivate(); |
|
} |
|
|
|
SoundAnnotation::SoundAnnotation() |
|
: Annotation(*new SoundAnnotationPrivate()) |
|
{ |
|
} |
|
|
|
SoundAnnotation::SoundAnnotation(const QDomNode &description) |
|
: Annotation(*new SoundAnnotationPrivate(), description) |
|
{ |
|
} |
|
|
|
SoundAnnotation::~SoundAnnotation() |
|
{ |
|
} |
|
|
|
void SoundAnnotation::store(QDomNode &node, QDomDocument &document) const |
|
{ |
|
// recurse to parent objects storing properties |
|
Annotation::store(node, document); |
|
|
|
// create [sound] element |
|
QDomElement soundElement = document.createElement(QStringLiteral("sound")); |
|
node.appendChild(soundElement); |
|
} |
|
|
|
Annotation::SubType SoundAnnotation::subType() const |
|
{ |
|
return ASound; |
|
} |
|
|
|
QString SoundAnnotation::soundIconName() const |
|
{ |
|
Q_D(const SoundAnnotation); |
|
return d->icon; |
|
} |
|
|
|
void SoundAnnotation::setSoundIconName(const QString &iconName) |
|
{ |
|
Q_D(SoundAnnotation); |
|
d->icon = iconName; |
|
} |
|
|
|
Sound *SoundAnnotation::sound() const |
|
{ |
|
Q_D(const SoundAnnotation); |
|
return d->sound; |
|
} |
|
|
|
void SoundAnnotation::setSound(Sound *s) |
|
{ |
|
Q_D(SoundAnnotation); |
|
d->sound = s; |
|
} |
|
|
|
/** MovieAnnotation [Annotation] */ |
|
|
|
class Okular::MovieAnnotationPrivate : public Okular::AnnotationPrivate |
|
{ |
|
public: |
|
MovieAnnotationPrivate() |
|
: AnnotationPrivate() |
|
, movie(nullptr) |
|
{ |
|
} |
|
~MovieAnnotationPrivate() override |
|
{ |
|
delete movie; |
|
} |
|
|
|
void setAnnotationProperties(const QDomNode &node) override; |
|
AnnotationPrivate *getNewAnnotationPrivate() override; |
|
|
|
// data fields |
|
Movie *movie; |
|
}; |
|
|
|
void MovieAnnotationPrivate::setAnnotationProperties(const QDomNode &node) |
|
{ |
|
Okular::AnnotationPrivate::setAnnotationProperties(node); |
|
|
|
// loop through the whole children looking for a 'movie' element |
|
QDomNode subNode = node.firstChild(); |
|
while (subNode.isElement()) { |
|
QDomElement e = subNode.toElement(); |
|
subNode = subNode.nextSibling(); |
|
if (e.tagName() != QLatin1String("movie")) |
|
continue; |
|
|
|
// loading complete |
|
break; |
|
} |
|
} |
|
|
|
AnnotationPrivate *MovieAnnotationPrivate::getNewAnnotationPrivate() |
|
{ |
|
return new MovieAnnotationPrivate(); |
|
} |
|
|
|
MovieAnnotation::MovieAnnotation() |
|
: Annotation(*new MovieAnnotationPrivate()) |
|
{ |
|
} |
|
|
|
MovieAnnotation::MovieAnnotation(const QDomNode &description) |
|
: Annotation(*new MovieAnnotationPrivate(), description) |
|
{ |
|
} |
|
|
|
MovieAnnotation::~MovieAnnotation() |
|
{ |
|
} |
|
|
|
void MovieAnnotation::store(QDomNode &node, QDomDocument &document) const |
|
{ |
|
// recurse to parent objects storing properties |
|
Annotation::store(node, document); |
|
|
|
// create [movie] element |
|
QDomElement movieElement = document.createElement(QStringLiteral("movie")); |
|
node.appendChild(movieElement); |
|
} |
|
|
|
Annotation::SubType MovieAnnotation::subType() const |
|
{ |
|
return AMovie; |
|
} |
|
|
|
Movie *MovieAnnotation::movie() const |
|
{ |
|
Q_D(const MovieAnnotation); |
|
return d->movie; |
|
} |
|
|
|
void MovieAnnotation::setMovie(Movie *movie) |
|
{ |
|
Q_D(MovieAnnotation); |
|
d->movie = movie; |
|
} |
|
|
|
/** ScreenAnnotation [Annotation] */ |
|
|
|
class Okular::ScreenAnnotationPrivate : public Okular::AnnotationPrivate |
|
{ |
|
public: |
|
ScreenAnnotationPrivate(); |
|
~ScreenAnnotationPrivate() override; |
|
|
|
void setAnnotationProperties(const QDomNode &node) override; |
|
AnnotationPrivate *getNewAnnotationPrivate() override; |
|
|
|
Okular::Action *m_action; |
|
QMap<Okular::Annotation::AdditionalActionType, Okular::Action *> m_additionalActions; |
|
}; |
|
|
|
ScreenAnnotationPrivate::ScreenAnnotationPrivate() |
|
: m_action(nullptr) |
|
{ |
|
} |
|
|
|
ScreenAnnotationPrivate::~ScreenAnnotationPrivate() |
|
{ |
|
delete m_action; |
|
qDeleteAll(m_additionalActions); |
|
} |
|
|
|
void ScreenAnnotationPrivate::setAnnotationProperties(const QDomNode &node) |
|
{ |
|
Okular::AnnotationPrivate::setAnnotationProperties(node); |
|
|
|
// loop through the whole children looking for a 'screen' element |
|
QDomNode subNode = node.firstChild(); |
|
while (subNode.isElement()) { |
|
QDomElement e = subNode.toElement(); |
|
subNode = subNode.nextSibling(); |
|
if (e.tagName() != QLatin1String("screen")) |
|
continue; |
|
|
|
// loading complete |
|
break; |
|
} |
|
} |
|
|
|
AnnotationPrivate *ScreenAnnotationPrivate::getNewAnnotationPrivate() |
|
{ |
|
return new ScreenAnnotationPrivate(); |
|
} |
|
|
|
ScreenAnnotation::ScreenAnnotation() |
|
: Annotation(*new ScreenAnnotationPrivate()) |
|
{ |
|
} |
|
|
|
ScreenAnnotation::ScreenAnnotation(const QDomNode &description) |
|
: Annotation(*new ScreenAnnotationPrivate(), description) |
|
{ |
|
} |
|
|
|
ScreenAnnotation::~ScreenAnnotation() |
|
{ |
|
} |
|
|
|
void ScreenAnnotation::store(QDomNode &node, QDomDocument &document) const |
|
{ |
|
// recurse to parent objects storing properties |
|
Annotation::store(node, document); |
|
|
|
// create [screen] element |
|
QDomElement movieElement = document.createElement(QStringLiteral("screen")); |
|
node.appendChild(movieElement); |
|
} |
|
|
|
Annotation::SubType ScreenAnnotation::subType() const |
|
{ |
|
return AScreen; |
|
} |
|
|
|
void ScreenAnnotation::setAdditionalAction(AdditionalActionType type, Action *action) |
|
{ |
|
Q_D(ScreenAnnotation); |
|
if (d->m_additionalActions.contains(type)) |
|
delete d->m_additionalActions.value(type); |
|
|
|
d->m_additionalActions.insert(type, action); |
|
} |
|
|
|
Action *ScreenAnnotation::additionalAction(AdditionalActionType type) const |
|
{ |
|
Q_D(const ScreenAnnotation); |
|
if (!d->m_additionalActions.contains(type)) |
|
return nullptr; |
|
else |
|
return d->m_additionalActions.value(type); |
|
} |
|
|
|
void ScreenAnnotation::setAction(Action *action) |
|
{ |
|
Q_D(ScreenAnnotation); |
|
|
|
delete d->m_action; |
|
d->m_action = action; |
|
} |
|
|
|
Action *ScreenAnnotation::action() const |
|
{ |
|
Q_D(const ScreenAnnotation); |
|
return d->m_action; |
|
} |
|
|
|
/** WidgetAnnotation [Annotation] */ |
|
|
|
class Okular::WidgetAnnotationPrivate : public Okular::AnnotationPrivate |
|
{ |
|
public: |
|
~WidgetAnnotationPrivate() override; |
|
void setAnnotationProperties(const QDomNode &node) override; |
|
AnnotationPrivate *getNewAnnotationPrivate() override; |
|
|
|
QMap<Okular::Annotation::AdditionalActionType, Okular::Action *> m_additionalActions; |
|
}; |
|
|
|
WidgetAnnotationPrivate::~WidgetAnnotationPrivate() |
|
{ |
|
qDeleteAll(m_additionalActions); |
|
} |
|
|
|
void WidgetAnnotationPrivate::setAnnotationProperties(const QDomNode &node) |
|
{ |
|
Okular::AnnotationPrivate::setAnnotationProperties(node); |
|
|
|
// loop through the whole children looking for a 'widget' element |
|
QDomNode subNode = node.firstChild(); |
|
while (subNode.isElement()) { |
|
QDomElement e = subNode.toElement(); |
|
subNode = subNode.nextSibling(); |
|
if (e.tagName() != QLatin1String("widget")) |
|
continue; |
|
|
|
// loading complete |
|
break; |
|
} |
|
} |
|
|
|
AnnotationPrivate *WidgetAnnotationPrivate::getNewAnnotationPrivate() |
|
{ |
|
return new WidgetAnnotationPrivate(); |
|
} |
|
|
|
WidgetAnnotation::WidgetAnnotation() |
|
: Annotation(*new WidgetAnnotationPrivate()) |
|
{ |
|
} |
|
|
|
WidgetAnnotation::WidgetAnnotation(const QDomNode &description) |
|
: Annotation(*new WidgetAnnotationPrivate, description) |
|
{ |
|
} |
|
|
|
WidgetAnnotation::~WidgetAnnotation() |
|
{ |
|
} |
|
|
|
void WidgetAnnotation::store(QDomNode &node, QDomDocument &document) const |
|
{ |
|
// recurse to parent objects storing properties |
|
Annotation::store(node, document); |
|
|
|
// create [widget] element |
|
QDomElement movieElement = document.createElement(QStringLiteral("widget")); |
|
node.appendChild(movieElement); |
|
} |
|
|
|
Annotation::SubType WidgetAnnotation::subType() const |
|
{ |
|
return AWidget; |
|
} |
|
|
|
void WidgetAnnotation::setAdditionalAction(AdditionalActionType type, Action *action) |
|
{ |
|
Q_D(WidgetAnnotation); |
|
if (d->m_additionalActions.contains(type)) |
|
delete d->m_additionalActions.value(type); |
|
|
|
d->m_additionalActions.insert(type, action); |
|
} |
|
|
|
Action *WidgetAnnotation::additionalAction(AdditionalActionType type) const |
|
{ |
|
Q_D(const WidgetAnnotation); |
|
if (!d->m_additionalActions.contains(type)) |
|
return nullptr; |
|
else |
|
return d->m_additionalActions.value(type); |
|
} |
|
|
|
/** RichMediaAnnotation [Annotation] */ |
|
|
|
class Okular::RichMediaAnnotationPrivate : public Okular::AnnotationPrivate |
|
{ |
|
public: |
|
RichMediaAnnotationPrivate(); |
|
~RichMediaAnnotationPrivate() override; |
|
void setAnnotationProperties(const QDomNode &node) override; |
|
AnnotationPrivate *getNewAnnotationPrivate() override; |
|
|
|
// data fields |
|
Movie *movie; |
|
EmbeddedFile *embeddedFile; |
|
}; |
|
|
|
RichMediaAnnotationPrivate::RichMediaAnnotationPrivate() |
|
: movie(nullptr) |
|
, embeddedFile(nullptr) |
|
{ |
|
} |
|
|
|
RichMediaAnnotationPrivate::~RichMediaAnnotationPrivate() |
|
{ |
|
delete movie; |
|
delete embeddedFile; |
|
} |
|
|
|
void RichMediaAnnotationPrivate::setAnnotationProperties(const QDomNode &node) |
|
{ |
|
Okular::AnnotationPrivate::setAnnotationProperties(node); |
|
|
|
// loop through the whole children looking for a 'richMedia' element |
|
QDomNode subNode = node.firstChild(); |
|
while (subNode.isElement()) { |
|
QDomElement e = subNode.toElement(); |
|
subNode = subNode.nextSibling(); |
|
if (e.tagName() != QLatin1String("richMedia")) |
|
continue; |
|
|
|
// loading complete |
|
break; |
|
} |
|
} |
|
|
|
AnnotationPrivate *RichMediaAnnotationPrivate::getNewAnnotationPrivate() |
|
{ |
|
return new RichMediaAnnotationPrivate(); |
|
} |
|
|
|
RichMediaAnnotation::RichMediaAnnotation() |
|
: Annotation(*new RichMediaAnnotationPrivate()) |
|
{ |
|
} |
|
|
|
RichMediaAnnotation::RichMediaAnnotation(const QDomNode &description) |
|
: Annotation(*new RichMediaAnnotationPrivate, description) |
|
{ |
|
} |
|
|
|
RichMediaAnnotation::~RichMediaAnnotation() |
|
{ |
|
} |
|
|
|
void RichMediaAnnotation::store(QDomNode &node, QDomDocument &document) const |
|
{ |
|
// recurse to parent objects storing properties |
|
Annotation::store(node, document); |
|
|
|
// create [richMedia] element |
|
QDomElement movieElement = document.createElement(QStringLiteral("richMedia")); |
|
node.appendChild(movieElement); |
|
} |
|
|
|
Annotation::SubType RichMediaAnnotation::subType() const |
|
{ |
|
return ARichMedia; |
|
} |
|
|
|
void RichMediaAnnotation::setMovie(Movie *movie) |
|
{ |
|
Q_D(RichMediaAnnotation); |
|
|
|
delete d->movie; |
|
d->movie = movie; |
|
} |
|
|
|
Movie *RichMediaAnnotation::movie() const |
|
{ |
|
Q_D(const RichMediaAnnotation); |
|
|
|
return d->movie; |
|
} |
|
|
|
EmbeddedFile *RichMediaAnnotation::embeddedFile() const |
|
{ |
|
Q_D(const RichMediaAnnotation); |
|
|
|
return d->embeddedFile; |
|
} |
|
|
|
void RichMediaAnnotation::setEmbeddedFile(EmbeddedFile *embeddedFile) |
|
{ |
|
Q_D(RichMediaAnnotation); |
|
|
|
delete d->embeddedFile; |
|
d->embeddedFile = embeddedFile; |
|
}
|
|
|