1 /*
2     SPDX-FileCopyrightText: 2006 Tobias Koenig <tokoe@kde.org>
3 
4     SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #include "annotationpopup.h"
8 
9 #include <KLocalizedString>
10 #include <QIcon>
11 #include <QMenu>
12 
13 #include "annotationpropertiesdialog.h"
14 
15 #include "core/annotations.h"
16 #include "core/document.h"
17 #include "guiutils.h"
18 #include "okmenutitle.h"
19 
20 Q_DECLARE_METATYPE(AnnotationPopup::AnnotPagePair)
21 
22 namespace
23 {
annotationHasFileAttachment(Okular::Annotation * annotation)24 bool annotationHasFileAttachment(Okular::Annotation *annotation)
25 {
26     return (annotation->subType() == Okular::Annotation::AFileAttachment || annotation->subType() == Okular::Annotation::ARichMedia);
27 }
28 
embeddedFileFromAnnotation(Okular::Annotation * annotation)29 Okular::EmbeddedFile *embeddedFileFromAnnotation(Okular::Annotation *annotation)
30 {
31     if (annotation->subType() == Okular::Annotation::AFileAttachment) {
32         const Okular::FileAttachmentAnnotation *fileAttachAnnot = static_cast<Okular::FileAttachmentAnnotation *>(annotation);
33         return fileAttachAnnot->embeddedFile();
34     } else if (annotation->subType() == Okular::Annotation::ARichMedia) {
35         const Okular::RichMediaAnnotation *richMediaAnnot = static_cast<Okular::RichMediaAnnotation *>(annotation);
36         return richMediaAnnot->embeddedFile();
37     } else {
38         return nullptr;
39     }
40 }
41 
42 }
43 
AnnotationPopup(Okular::Document * document,MenuMode mode,QWidget * parent)44 AnnotationPopup::AnnotationPopup(Okular::Document *document, MenuMode mode, QWidget *parent)
45     : mParent(parent)
46     , mDocument(document)
47     , mMenuMode(mode)
48 {
49 }
50 
addAnnotation(Okular::Annotation * annotation,int pageNumber)51 void AnnotationPopup::addAnnotation(Okular::Annotation *annotation, int pageNumber)
52 {
53     AnnotPagePair pair(annotation, pageNumber);
54     if (!mAnnotations.contains(pair))
55         mAnnotations.append(pair);
56 }
57 
exec(const QPoint point)58 void AnnotationPopup::exec(const QPoint point)
59 {
60     if (mAnnotations.isEmpty())
61         return;
62 
63     QMenu menu(mParent);
64 
65     addActionsToMenu(&menu);
66 
67     menu.exec(point.isNull() ? QCursor::pos() : point);
68 }
69 
addActionsToMenu(QMenu * menu)70 void AnnotationPopup::addActionsToMenu(QMenu *menu)
71 {
72     QAction *action = nullptr;
73 
74     if (mMenuMode == SingleAnnotationMode) {
75         const bool onlyOne = (mAnnotations.count() == 1);
76 
77         const AnnotPagePair &pair = mAnnotations.at(0);
78 
79         menu->addAction(new OKMenuTitle(menu, i18np("Annotation", "%1 Annotations", mAnnotations.count())));
80 
81         action = menu->addAction(QIcon::fromTheme(QStringLiteral("comment")), i18n("&Open Pop-up Note"));
82         action->setEnabled(onlyOne);
83         connect(action, &QAction::triggered, menu, [this, pair] { doOpenAnnotationWindow(pair); });
84 
85         action = menu->addAction(QIcon::fromTheme(QStringLiteral("list-remove")), i18n("&Delete"));
86         action->setEnabled(mDocument->isAllowed(Okular::AllowNotes));
87         connect(action, &QAction::triggered, menu, [this] {
88             for (const AnnotPagePair &pair : qAsConst(mAnnotations)) {
89                 doRemovePageAnnotation(pair);
90             }
91         });
92 
93         for (const AnnotPagePair &pair : qAsConst(mAnnotations)) {
94             if (!mDocument->canRemovePageAnnotation(pair.annotation))
95                 action->setEnabled(false);
96         }
97 
98         action = menu->addAction(QIcon::fromTheme(QStringLiteral("configure")), i18n("&Properties"));
99         action->setEnabled(onlyOne);
100         connect(action, &QAction::triggered, menu, [this, pair] { doOpenPropertiesDialog(pair); });
101 
102         if (onlyOne && annotationHasFileAttachment(pair.annotation)) {
103             const Okular::EmbeddedFile *embeddedFile = embeddedFileFromAnnotation(pair.annotation);
104             if (embeddedFile) {
105                 const QString saveText = i18nc("%1 is the name of the file to save", "&Save '%1'...", embeddedFile->name());
106 
107                 menu->addSeparator();
108                 action = menu->addAction(QIcon::fromTheme(QStringLiteral("document-save")), saveText);
109                 connect(action, &QAction::triggered, menu, [this, pair] { doSaveEmbeddedFile(pair); });
110             }
111         }
112     } else {
113         for (const AnnotPagePair &pair : qAsConst(mAnnotations)) {
114             menu->addAction(new OKMenuTitle(menu, GuiUtils::captionForAnnotation(pair.annotation)));
115 
116             action = menu->addAction(QIcon::fromTheme(QStringLiteral("comment")), i18n("&Open Pop-up Note"));
117             connect(action, &QAction::triggered, menu, [this, pair] { doOpenAnnotationWindow(pair); });
118 
119             action = menu->addAction(QIcon::fromTheme(QStringLiteral("list-remove")), i18n("&Delete"));
120             action->setEnabled(mDocument->isAllowed(Okular::AllowNotes) && mDocument->canRemovePageAnnotation(pair.annotation));
121             connect(action, &QAction::triggered, menu, [this, pair] { doRemovePageAnnotation(pair); });
122 
123             action = menu->addAction(QIcon::fromTheme(QStringLiteral("configure")), i18n("&Properties"));
124             connect(action, &QAction::triggered, menu, [this, pair] { doOpenPropertiesDialog(pair); });
125 
126             if (annotationHasFileAttachment(pair.annotation)) {
127                 const Okular::EmbeddedFile *embeddedFile = embeddedFileFromAnnotation(pair.annotation);
128                 if (embeddedFile) {
129                     const QString saveText = i18nc("%1 is the name of the file to save", "&Save '%1'...", embeddedFile->name());
130 
131                     menu->addSeparator();
132                     action = menu->addAction(QIcon::fromTheme(QStringLiteral("document-save")), saveText);
133                     connect(action, &QAction::triggered, menu, [this, pair] { doSaveEmbeddedFile(pair); });
134                 }
135             }
136         }
137     }
138 }
139 
doRemovePageAnnotation(AnnotPagePair pair)140 void AnnotationPopup::doRemovePageAnnotation(AnnotPagePair pair)
141 {
142     if (pair.pageNumber != -1) {
143         mDocument->removePageAnnotation(pair.pageNumber, pair.annotation);
144     }
145 }
146 
doOpenAnnotationWindow(AnnotPagePair pair)147 void AnnotationPopup::doOpenAnnotationWindow(AnnotPagePair pair)
148 {
149     emit openAnnotationWindow(pair.annotation, pair.pageNumber);
150 }
151 
doOpenPropertiesDialog(AnnotPagePair pair)152 void AnnotationPopup::doOpenPropertiesDialog(AnnotPagePair pair)
153 {
154     if (pair.pageNumber != -1) {
155         AnnotsPropertiesDialog propdialog(mParent, mDocument, pair.pageNumber, pair.annotation);
156         propdialog.exec();
157     }
158 }
159 
doSaveEmbeddedFile(AnnotPagePair pair)160 void AnnotationPopup::doSaveEmbeddedFile(AnnotPagePair pair)
161 {
162     Okular::EmbeddedFile *embeddedFile = embeddedFileFromAnnotation(pair.annotation);
163     GuiUtils::saveEmbeddedFile(embeddedFile, mParent);
164 }
165