File indexing completed on 2024-04-28 15:51:40

0001 /*
0002     SPDX-FileCopyrightText: 2006 Pino Toscano <pino@kde.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "annotationwidgets.h"
0008 
0009 // qt/kde includes
0010 #include <KColorButton>
0011 #include <KComboBox>
0012 #include <KFontRequester>
0013 #include <KFormat>
0014 #include <KLocalizedString>
0015 #include <KMessageBox>
0016 #include <KMessageWidget>
0017 #include <QCheckBox>
0018 #include <QDebug>
0019 #include <QFileDialog>
0020 #include <QFormLayout>
0021 #include <QGroupBox>
0022 #include <QGuiApplication>
0023 #include <QIcon>
0024 #include <QLabel>
0025 #include <QLayout>
0026 #include <QList>
0027 #include <QMimeDatabase>
0028 #include <QPair>
0029 #include <QSize>
0030 #include <QSpinBox>
0031 #include <QVariant>
0032 
0033 #include "core/annotations.h"
0034 #include "core/annotations_p.h"
0035 #include "core/document.h"
0036 #include "core/document_p.h"
0037 #include "core/page_p.h"
0038 #include "gui/guiutils.h"
0039 #include "gui/pagepainter.h"
0040 
0041 #define FILEATTACH_ICONSIZE 48
0042 
0043 PixmapPreviewSelector::PixmapPreviewSelector(QWidget *parent, PreviewPosition position)
0044     : QWidget(parent)
0045     , m_previewPosition(position)
0046 {
0047     QVBoxLayout *mainlay = new QVBoxLayout(this);
0048     mainlay->setContentsMargins(0, 0, 0, 0);
0049     QHBoxLayout *toplay = new QHBoxLayout;
0050     toplay->setContentsMargins(0, 0, 0, 0);
0051     mainlay->addLayout(toplay);
0052     m_comboItems = new KComboBox(this);
0053     toplay->addWidget(m_comboItems);
0054     m_stampPushButton = new QPushButton(QIcon::fromTheme(QStringLiteral("document-open")), QString(), this);
0055     m_stampPushButton->setVisible(false);
0056     m_stampPushButton->setToolTip(i18nc("@info:tooltip", "Select a custom stamp symbol from file"));
0057     toplay->addWidget(m_stampPushButton);
0058     m_iconLabel = new QLabel(this);
0059     switch (m_previewPosition) {
0060     case Side:
0061         toplay->addWidget(m_iconLabel);
0062         break;
0063     case Below:
0064         mainlay->addWidget(m_iconLabel);
0065         mainlay->setAlignment(m_iconLabel, Qt::AlignHCenter);
0066         break;
0067     }
0068     m_iconLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
0069     m_iconLabel->setAlignment(Qt::AlignCenter);
0070     m_iconLabel->setFrameStyle(QFrame::StyledPanel);
0071     setPreviewSize(32);
0072 
0073     setFocusPolicy(Qt::TabFocus);
0074     setFocusProxy(m_comboItems);
0075 
0076     connect(m_comboItems, &QComboBox::currentIndexChanged, this, [this](int index) { iconComboChanged(m_comboItems->itemText(index)); });
0077     connect(m_comboItems, &QComboBox::editTextChanged, this, &PixmapPreviewSelector::iconComboChanged);
0078     connect(m_stampPushButton, &QPushButton::clicked, this, &PixmapPreviewSelector::selectCustomStamp);
0079 }
0080 
0081 PixmapPreviewSelector::~PixmapPreviewSelector()
0082 {
0083 }
0084 
0085 void PixmapPreviewSelector::setIcon(const QString &icon)
0086 {
0087     int id = m_comboItems->findData(QVariant(icon), Qt::UserRole, Qt::MatchFixedString);
0088     if (id == -1) {
0089         id = m_comboItems->findText(icon, Qt::MatchFixedString);
0090     }
0091     if (id > -1) {
0092         m_comboItems->setCurrentIndex(id);
0093     } else if (m_comboItems->isEditable()) {
0094         m_comboItems->addItem(icon, QVariant(icon));
0095         m_comboItems->setCurrentIndex(m_comboItems->findText(icon, Qt::MatchFixedString));
0096     }
0097 }
0098 
0099 QString PixmapPreviewSelector::icon() const
0100 {
0101     return m_icon;
0102 }
0103 
0104 void PixmapPreviewSelector::addItem(const QString &item, const QString &id)
0105 {
0106     m_comboItems->addItem(item, QVariant(id));
0107     setIcon(m_icon);
0108 }
0109 
0110 void PixmapPreviewSelector::setPreviewSize(int size)
0111 {
0112     m_previewSize = size;
0113     switch (m_previewPosition) {
0114     case Side:
0115         m_iconLabel->setFixedSize(m_previewSize + 8, m_previewSize + 8);
0116         break;
0117     case Below:
0118         m_iconLabel->setFixedSize(3 * m_previewSize + 8, m_previewSize + 8);
0119         break;
0120     }
0121     iconComboChanged(m_icon);
0122 }
0123 
0124 int PixmapPreviewSelector::previewSize() const
0125 {
0126     return m_previewSize;
0127 }
0128 
0129 void PixmapPreviewSelector::setEditable(bool editable)
0130 {
0131     m_comboItems->setEditable(editable);
0132     m_stampPushButton->setVisible(editable);
0133 }
0134 
0135 void PixmapPreviewSelector::iconComboChanged(const QString &icon)
0136 {
0137     int id = m_comboItems->findText(icon, Qt::MatchFixedString);
0138     if (id >= 0) {
0139         m_icon = m_comboItems->itemData(id).toString();
0140     } else {
0141         m_icon = icon;
0142     }
0143 
0144     QPixmap pixmap = Okular::AnnotationUtils::loadStamp(m_icon, m_previewSize);
0145     const QRect cr = m_iconLabel->contentsRect();
0146     if (pixmap.width() > cr.width() || pixmap.height() > cr.height()) {
0147         pixmap = pixmap.scaled(cr.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
0148     }
0149     m_iconLabel->setPixmap(pixmap);
0150 
0151     Q_EMIT iconChanged(m_icon);
0152 }
0153 
0154 void PixmapPreviewSelector::selectCustomStamp()
0155 {
0156     const QString customStampFile = QFileDialog::getOpenFileName(this, i18nc("@title:window file chooser", "Select custom stamp symbol"), QString(), i18n("*.ico *.png *.xpm *.svg *.svgz | Icon Files (*.ico *.png *.xpm *.svg *.svgz)"));
0157     if (!customStampFile.isEmpty()) {
0158         QPixmap pixmap = Okular::AnnotationUtils::loadStamp(customStampFile, m_previewSize);
0159         if (pixmap.isNull()) {
0160             KMessageBox::error(this, xi18nc("@info", "Could not load the file <filename>%1</filename>", customStampFile), i18nc("@title:window", "Invalid file"));
0161         } else {
0162             m_comboItems->setEditText(customStampFile);
0163         }
0164     }
0165 }
0166 
0167 AnnotationWidget *AnnotationWidgetFactory::widgetFor(Okular::Annotation *ann)
0168 {
0169     switch (ann->subType()) {
0170     case Okular::Annotation::AStamp:
0171         return new StampAnnotationWidget(ann);
0172         break;
0173     case Okular::Annotation::AText:
0174         return new TextAnnotationWidget(ann);
0175         break;
0176     case Okular::Annotation::ALine:
0177         return new LineAnnotationWidget(ann);
0178         break;
0179     case Okular::Annotation::AHighlight:
0180         return new HighlightAnnotationWidget(ann);
0181         break;
0182     case Okular::Annotation::AInk:
0183         return new InkAnnotationWidget(ann);
0184         break;
0185     case Okular::Annotation::AGeom:
0186         return new GeomAnnotationWidget(ann);
0187         break;
0188     case Okular::Annotation::AFileAttachment:
0189         return new FileAttachmentAnnotationWidget(ann);
0190         break;
0191     case Okular::Annotation::ACaret:
0192         return new CaretAnnotationWidget(ann);
0193         break;
0194     // shut up gcc
0195     default:;
0196     }
0197     // cases not covered yet: return a generic widget
0198     return new AnnotationWidget(ann);
0199 }
0200 
0201 AnnotationWidget::AnnotationWidget(Okular::Annotation *ann)
0202     : m_typeEditable(true)
0203     , m_ann(ann)
0204 {
0205 }
0206 
0207 AnnotationWidget::~AnnotationWidget()
0208 {
0209 }
0210 
0211 Okular::Annotation::SubType AnnotationWidget::annotationType() const
0212 {
0213     return m_ann->subType();
0214 }
0215 
0216 QWidget *AnnotationWidget::appearanceWidget()
0217 {
0218     if (m_appearanceWidget) {
0219         return m_appearanceWidget;
0220     }
0221 
0222     m_appearanceWidget = createAppearanceWidget();
0223     return m_appearanceWidget;
0224 }
0225 
0226 QWidget *AnnotationWidget::extraWidget()
0227 {
0228     if (m_extraWidget) {
0229         return m_extraWidget;
0230     }
0231 
0232     m_extraWidget = createExtraWidget();
0233     return m_extraWidget;
0234 }
0235 
0236 void AnnotationWidget::applyChanges()
0237 {
0238     if (m_colorBn) {
0239         m_ann->style().setColor(m_colorBn->color());
0240     }
0241     if (m_opacity) {
0242         m_ann->style().setOpacity((double)m_opacity->value() / 100.0);
0243     }
0244 }
0245 
0246 QWidget *AnnotationWidget::createAppearanceWidget()
0247 {
0248     QWidget *widget = new QWidget();
0249     QFormLayout *formlayout = new QFormLayout(widget);
0250     formlayout->setLabelAlignment(Qt::AlignRight);
0251     formlayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
0252 
0253     createStyleWidget(formlayout);
0254 
0255     return widget;
0256 }
0257 
0258 void AnnotationWidget::createStyleWidget(QFormLayout *formlayout)
0259 {
0260     Q_UNUSED(formlayout);
0261 }
0262 
0263 void AnnotationWidget::addColorButton(QWidget *widget, QFormLayout *formlayout)
0264 {
0265     m_colorBn = new KColorButton(widget);
0266     m_colorBn->setColor(m_ann->style().color());
0267     formlayout->addRow(i18n("&Color:"), m_colorBn);
0268     connect(m_colorBn, &KColorButton::changed, this, &AnnotationWidget::dataChanged);
0269 }
0270 
0271 void AnnotationWidget::addOpacitySpinBox(QWidget *widget, QFormLayout *formlayout)
0272 {
0273     m_opacity = new QSpinBox(widget);
0274     m_opacity->setRange(0, 100);
0275     m_opacity->setValue((int)(m_ann->style().opacity() * 100));
0276     m_opacity->setSuffix(i18nc("Suffix for the opacity level, eg '80%'", "%"));
0277     formlayout->addRow(i18n("&Opacity:"), m_opacity);
0278     connect(m_opacity, QOverload<int>::of(&QSpinBox::valueChanged), this, &AnnotationWidget::dataChanged);
0279 }
0280 
0281 void AnnotationWidget::addVerticalSpacer(QFormLayout *formlayout)
0282 {
0283     formlayout->addItem(new QSpacerItem(0, 5, QSizePolicy::Fixed, QSizePolicy::Fixed));
0284 }
0285 
0286 QWidget *AnnotationWidget::createExtraWidget()
0287 {
0288     return nullptr;
0289 }
0290 
0291 TextAnnotationWidget::TextAnnotationWidget(Okular::Annotation *ann)
0292     : AnnotationWidget(ann)
0293 {
0294     m_textAnn = static_cast<Okular::TextAnnotation *>(ann);
0295 }
0296 
0297 void TextAnnotationWidget::createStyleWidget(QFormLayout *formlayout)
0298 {
0299     QWidget *widget = qobject_cast<QWidget *>(formlayout->parent());
0300 
0301     if (m_textAnn->textType() == Okular::TextAnnotation::Linked) {
0302         createPopupNoteStyleUi(widget, formlayout);
0303     } else if (m_textAnn->textType() == Okular::TextAnnotation::InPlace) {
0304         if (isTypewriter()) {
0305             createTypewriterStyleUi(widget, formlayout);
0306         } else {
0307             createInlineNoteStyleUi(widget, formlayout);
0308         }
0309     }
0310 }
0311 
0312 void TextAnnotationWidget::applyChanges()
0313 {
0314     AnnotationWidget::applyChanges();
0315     if (m_textAnn->textType() == Okular::TextAnnotation::Linked) {
0316         Q_ASSERT(m_pixmapSelector);
0317         m_textAnn->setTextIcon(m_pixmapSelector->icon());
0318     } else if (m_textAnn->textType() == Okular::TextAnnotation::InPlace) {
0319         Q_ASSERT(m_fontReq);
0320         m_textAnn->setTextFont(m_fontReq->font());
0321         if (!isTypewriter()) {
0322             Q_ASSERT(m_textAlign && m_spinWidth);
0323             m_textAnn->setInplaceAlignment(m_textAlign->currentIndex());
0324             m_textAnn->style().setWidth(m_spinWidth->value());
0325         } else {
0326             Q_ASSERT(m_textColorBn);
0327             m_textAnn->setTextColor(m_textColorBn->color());
0328         }
0329     }
0330 }
0331 
0332 void AnnotationWidget::setAnnotTypeEditable(bool editable)
0333 {
0334     m_typeEditable = editable;
0335 }
0336 
0337 void TextAnnotationWidget::createPopupNoteStyleUi(QWidget *widget, QFormLayout *formlayout)
0338 {
0339     addColorButton(widget, formlayout);
0340     addOpacitySpinBox(widget, formlayout);
0341     addVerticalSpacer(formlayout);
0342     addPixmapSelector(widget, formlayout);
0343 }
0344 
0345 void TextAnnotationWidget::createInlineNoteStyleUi(QWidget *widget, QFormLayout *formlayout)
0346 {
0347     addColorButton(widget, formlayout);
0348     addOpacitySpinBox(widget, formlayout);
0349     addVerticalSpacer(formlayout);
0350     addFontRequester(widget, formlayout);
0351     addTextAlignComboBox(widget, formlayout);
0352     addVerticalSpacer(formlayout);
0353     addWidthSpinBox(widget, formlayout);
0354 }
0355 
0356 void TextAnnotationWidget::createTypewriterStyleUi(QWidget *widget, QFormLayout *formlayout)
0357 {
0358     addFontRequester(widget, formlayout);
0359     addTextColorButton(widget, formlayout);
0360 }
0361 
0362 void TextAnnotationWidget::addPixmapSelector(QWidget *widget, QFormLayout *formlayout)
0363 {
0364     m_pixmapSelector = new PixmapPreviewSelector(widget);
0365     formlayout->addRow(i18n("Icon:"), m_pixmapSelector);
0366     m_pixmapSelector->addItem(i18n("Comment"), QStringLiteral("Comment"));
0367     m_pixmapSelector->addItem(i18n("Help"), QStringLiteral("Help"));
0368     m_pixmapSelector->addItem(i18n("Insert"), QStringLiteral("Insert"));
0369     m_pixmapSelector->addItem(i18n("Key"), QStringLiteral("Key"));
0370     m_pixmapSelector->addItem(i18n("New paragraph"), QStringLiteral("NewParagraph"));
0371     m_pixmapSelector->addItem(i18n("Note"), QStringLiteral("Note"));
0372     m_pixmapSelector->addItem(i18n("Paragraph"), QStringLiteral("Paragraph"));
0373     m_pixmapSelector->setIcon(m_textAnn->textIcon());
0374     connect(m_pixmapSelector, &PixmapPreviewSelector::iconChanged, this, &AnnotationWidget::dataChanged);
0375 }
0376 
0377 void TextAnnotationWidget::addFontRequester(QWidget *widget, QFormLayout *formlayout)
0378 {
0379     m_fontReq = new KFontRequester(widget);
0380     formlayout->addRow(i18n("Font:"), m_fontReq);
0381     m_fontReq->setFont(m_textAnn->textFont());
0382     connect(m_fontReq, &KFontRequester::fontSelected, this, &AnnotationWidget::dataChanged);
0383 }
0384 
0385 void TextAnnotationWidget::addTextColorButton(QWidget *widget, QFormLayout *formlayout)
0386 {
0387     m_textColorBn = new KColorButton(widget);
0388     m_textColorBn->setColor(m_textAnn->textColor());
0389     formlayout->addRow(i18n("Text &color:"), m_textColorBn);
0390     connect(m_textColorBn, &KColorButton::changed, this, &AnnotationWidget::dataChanged);
0391 }
0392 
0393 void TextAnnotationWidget::addTextAlignComboBox(QWidget *widget, QFormLayout *formlayout)
0394 {
0395     m_textAlign = new KComboBox(widget);
0396     formlayout->addRow(i18n("&Align:"), m_textAlign);
0397     m_textAlign->addItem(i18n("Left"));
0398     m_textAlign->addItem(i18n("Center"));
0399     m_textAlign->addItem(i18n("Right"));
0400     m_textAlign->setCurrentIndex(m_textAnn->inplaceAlignment());
0401     connect(m_textAlign, QOverload<int>::of(&KComboBox::currentIndexChanged), this, &AnnotationWidget::dataChanged);
0402 }
0403 
0404 void TextAnnotationWidget::addWidthSpinBox(QWidget *widget, QFormLayout *formlayout)
0405 {
0406     m_spinWidth = new QDoubleSpinBox(widget);
0407     formlayout->addRow(i18n("Border &width:"), m_spinWidth);
0408     m_spinWidth->setRange(0, 100);
0409     m_spinWidth->setValue(m_textAnn->style().width());
0410     m_spinWidth->setSingleStep(0.1);
0411     connect(m_spinWidth, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &AnnotationWidget::dataChanged);
0412 }
0413 
0414 const QList<QPair<QString, QString>> &StampAnnotationWidget::defaultStamps()
0415 {
0416     static const QList<QPair<QString, QString>> defaultStampsList = {{i18n("Approved"), QStringLiteral("Approved")},
0417                                                                      {i18n("As Is"), QStringLiteral("AsIs")},
0418                                                                      {i18n("Confidential"), QStringLiteral("Confidential")},
0419                                                                      {i18n("Departmental"), QStringLiteral("Departmental")},
0420                                                                      {i18n("Draft"), QStringLiteral("Draft")},
0421                                                                      {i18n("Experimental"), QStringLiteral("Experimental")},
0422                                                                      {i18n("Expired"), QStringLiteral("Expired")},
0423                                                                      {i18n("Final"), QStringLiteral("Final")},
0424                                                                      {i18n("For Comment"), QStringLiteral("ForComment")},
0425                                                                      {i18n("For Public Release"), QStringLiteral("ForPublicRelease")},
0426                                                                      {i18n("Not Approved"), QStringLiteral("NotApproved")},
0427                                                                      {i18n("Not For Public Release"), QStringLiteral("NotForPublicRelease")},
0428                                                                      {i18n("Sold"), QStringLiteral("Sold")},
0429                                                                      {i18n("Top Secret"), QStringLiteral("TopSecret")},
0430                                                                      {i18n("Bookmark"), QStringLiteral("bookmark-new")},
0431                                                                      {i18n("Information"), QStringLiteral("help-about")},
0432                                                                      {i18n("KDE"), QStringLiteral("kde")},
0433                                                                      {i18n("Okular"), QStringLiteral("okular")}};
0434 
0435     return defaultStampsList;
0436 }
0437 
0438 StampAnnotationWidget::StampAnnotationWidget(Okular::Annotation *ann)
0439     : AnnotationWidget(ann)
0440     , m_pixmapSelector(nullptr)
0441 {
0442     m_stampAnn = static_cast<Okular::StampAnnotation *>(ann);
0443 }
0444 
0445 void StampAnnotationWidget::createStyleWidget(QFormLayout *formlayout)
0446 {
0447     QWidget *widget = qobject_cast<QWidget *>(formlayout->parent());
0448 
0449     addOpacitySpinBox(widget, formlayout);
0450     addVerticalSpacer(formlayout);
0451 
0452     m_pixmapSelector = new PixmapPreviewSelector(widget, PixmapPreviewSelector::Below);
0453     formlayout->addRow(i18n("Stamp symbol:"), m_pixmapSelector);
0454     m_pixmapSelector->setEditable(true);
0455 
0456     for (const QPair<QString, QString> &pair : defaultStamps()) {
0457         m_pixmapSelector->addItem(pair.first, pair.second);
0458     }
0459 
0460     m_pixmapSelector->setIcon(m_stampAnn->stampIconName());
0461     m_pixmapSelector->setPreviewSize(64);
0462 
0463     connect(m_pixmapSelector, &PixmapPreviewSelector::iconChanged, this, &AnnotationWidget::dataChanged);
0464 }
0465 
0466 void StampAnnotationWidget::applyChanges()
0467 {
0468     AnnotationWidget::applyChanges();
0469     m_stampAnn->setStampIconName(m_pixmapSelector->icon());
0470 }
0471 
0472 LineAnnotationWidget::LineAnnotationWidget(Okular::Annotation *ann)
0473     : AnnotationWidget(ann)
0474 {
0475     m_lineAnn = static_cast<Okular::LineAnnotation *>(ann);
0476     if (m_lineAnn->linePoints().count() == 2) {
0477         m_lineType = 0; // line
0478     } else if (m_lineAnn->lineClosed()) {
0479         m_lineType = 1; // polygon
0480     } else {
0481         m_lineType = 2; // polyline
0482     }
0483 }
0484 
0485 void LineAnnotationWidget::createStyleWidget(QFormLayout *formlayout)
0486 {
0487     QWidget *widget = qobject_cast<QWidget *>(formlayout->parent());
0488 
0489     addColorButton(widget, formlayout);
0490     addOpacitySpinBox(widget, formlayout);
0491 
0492     m_spinSize = new QDoubleSpinBox(widget);
0493     m_spinSize->setRange(1, 100);
0494     m_spinSize->setValue(m_lineAnn->style().width());
0495 
0496     connect(m_spinSize, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &LineAnnotationWidget::dataChanged);
0497 
0498     // Straight line
0499     if (m_lineType == 0) {
0500         addVerticalSpacer(formlayout);
0501         formlayout->addRow(i18n("&Width:"), m_spinSize);
0502 
0503         // Line Term Styles
0504         addVerticalSpacer(formlayout);
0505         m_startStyleCombo = new QComboBox(widget);
0506         formlayout->addRow(i18n("Line start:"), m_startStyleCombo);
0507         m_endStyleCombo = new QComboBox(widget);
0508         formlayout->addRow(i18n("Line end:"), m_endStyleCombo);
0509         // FIXME: Where does the tooltip goes??
0510 
0511         const QList<QPair<Okular::LineAnnotation::TermStyle, QString>> termStyles {{Okular::LineAnnotation::Square, i18n("Square")},
0512                                                                                    {Okular::LineAnnotation::Circle, i18n("Circle")},
0513                                                                                    {Okular::LineAnnotation::Diamond, i18n("Diamond")},
0514                                                                                    {Okular::LineAnnotation::OpenArrow, i18n("Open Arrow")},
0515                                                                                    {Okular::LineAnnotation::ClosedArrow, i18n("Closed Arrow")},
0516                                                                                    {Okular::LineAnnotation::None, i18n("None")},
0517                                                                                    {Okular::LineAnnotation::Butt, i18n("Butt")},
0518                                                                                    {Okular::LineAnnotation::ROpenArrow, i18n("Right Open Arrow")},
0519                                                                                    {Okular::LineAnnotation::RClosedArrow, i18n("Right Closed Arrow")},
0520                                                                                    {Okular::LineAnnotation::Slash, i18n("Slash")}};
0521         for (const auto &item : termStyles) {
0522             const QIcon icon = endStyleIcon(item.first, QGuiApplication::palette().color(QPalette::WindowText));
0523             m_startStyleCombo->addItem(icon, item.second);
0524             m_endStyleCombo->addItem(icon, item.second);
0525         }
0526 
0527         m_startStyleCombo->setCurrentIndex(m_lineAnn->lineStartStyle());
0528         m_endStyleCombo->setCurrentIndex(m_lineAnn->lineEndStyle());
0529 
0530         // Leaders lengths
0531         addVerticalSpacer(formlayout);
0532         m_spinLL = new QDoubleSpinBox(widget);
0533         formlayout->addRow(i18n("Leader line length:"), m_spinLL);
0534         m_spinLLE = new QDoubleSpinBox(widget);
0535         formlayout->addRow(i18n("Leader line extensions length:"), m_spinLLE);
0536 
0537         m_spinLL->setRange(-500, 500);
0538         m_spinLL->setValue(m_lineAnn->lineLeadingForwardPoint());
0539         m_spinLLE->setRange(0, 500);
0540         m_spinLLE->setValue(m_lineAnn->lineLeadingBackwardPoint());
0541 
0542         connect(m_startStyleCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &LineAnnotationWidget::dataChanged);
0543         connect(m_endStyleCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &LineAnnotationWidget::dataChanged);
0544         connect(m_spinLL, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &LineAnnotationWidget::dataChanged);
0545         connect(m_spinLLE, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &LineAnnotationWidget::dataChanged);
0546     } else if (m_lineType == 1) // Polygon
0547     {
0548         QHBoxLayout *colorlay = new QHBoxLayout();
0549         m_useColor = new QCheckBox(i18n("Enabled"), widget);
0550         colorlay->addWidget(m_useColor);
0551         m_innerColor = new KColorButton(widget);
0552         colorlay->addWidget(m_innerColor);
0553         formlayout->addRow(i18n("Shape fill:"), colorlay);
0554 
0555         m_innerColor->setColor(m_lineAnn->lineInnerColor());
0556         if (m_lineAnn->lineInnerColor().isValid()) {
0557             m_useColor->setChecked(true);
0558         } else {
0559             m_innerColor->setEnabled(false);
0560         }
0561 
0562         addVerticalSpacer(formlayout);
0563         formlayout->addRow(i18n("&Width:"), m_spinSize);
0564 
0565         connect(m_innerColor, &KColorButton::changed, this, &AnnotationWidget::dataChanged);
0566         connect(m_useColor, &QAbstractButton::toggled, this, &AnnotationWidget::dataChanged);
0567         connect(m_useColor, &QCheckBox::toggled, m_innerColor, &KColorButton::setEnabled);
0568     }
0569 }
0570 
0571 void LineAnnotationWidget::applyChanges()
0572 {
0573     AnnotationWidget::applyChanges();
0574     if (m_lineType == 0) {
0575         Q_ASSERT(m_spinLL && m_spinLLE && m_startStyleCombo && m_endStyleCombo);
0576         m_lineAnn->setLineLeadingForwardPoint(m_spinLL->value());
0577         m_lineAnn->setLineLeadingBackwardPoint(m_spinLLE->value());
0578         m_lineAnn->setLineStartStyle((Okular::LineAnnotation::TermStyle)m_startStyleCombo->currentIndex());
0579         m_lineAnn->setLineEndStyle((Okular::LineAnnotation::TermStyle)m_endStyleCombo->currentIndex());
0580     } else if (m_lineType == 1) {
0581         Q_ASSERT(m_useColor && m_innerColor);
0582         if (!m_useColor->isChecked()) {
0583             m_lineAnn->setLineInnerColor(QColor());
0584         } else {
0585             m_lineAnn->setLineInnerColor(m_innerColor->color());
0586         }
0587     }
0588     Q_ASSERT(m_spinSize);
0589     m_lineAnn->style().setWidth(m_spinSize->value());
0590 }
0591 
0592 QIcon LineAnnotationWidget::endStyleIcon(Okular::LineAnnotation::TermStyle endStyle, const QColor &lineColor)
0593 {
0594     const int iconSize {48};
0595     QImage image {iconSize, iconSize, QImage::Format_ARGB32};
0596     image.fill(qRgba(0, 0, 0, 0));
0597     Okular::LineAnnotation prototype;
0598     prototype.setLinePoints({{0, 0.5}, {0.65, 0.5}});
0599     prototype.setLineStartStyle(Okular::LineAnnotation::TermStyle::None);
0600     prototype.setLineEndStyle(endStyle);
0601     prototype.style().setWidth(4);
0602     prototype.style().setColor(lineColor);
0603     prototype.style().setLineStyle(Okular::Annotation::LineStyle::Solid);
0604     prototype.setBoundingRectangle({0, 0, 1, 1});
0605     LineAnnotPainter linepainter {&prototype, QSize {iconSize, iconSize}, 1, QTransform()};
0606     linepainter.draw(image);
0607     return QIcon(QPixmap::fromImage(image));
0608 }
0609 
0610 InkAnnotationWidget::InkAnnotationWidget(Okular::Annotation *ann)
0611     : AnnotationWidget(ann)
0612 {
0613     m_inkAnn = static_cast<Okular::InkAnnotation *>(ann);
0614 }
0615 
0616 void InkAnnotationWidget::createStyleWidget(QFormLayout *formlayout)
0617 {
0618     QWidget *widget = qobject_cast<QWidget *>(formlayout->parent());
0619 
0620     addColorButton(widget, formlayout);
0621     addOpacitySpinBox(widget, formlayout);
0622 
0623     addVerticalSpacer(formlayout);
0624 
0625     m_spinSize = new QDoubleSpinBox(widget);
0626     formlayout->addRow(i18n("&Width:"), m_spinSize);
0627 
0628     m_spinSize->setRange(1, 100);
0629     m_spinSize->setValue(m_inkAnn->style().width());
0630 
0631     connect(m_spinSize, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &AnnotationWidget::dataChanged);
0632 }
0633 
0634 void InkAnnotationWidget::applyChanges()
0635 {
0636     AnnotationWidget::applyChanges();
0637     m_inkAnn->style().setWidth(m_spinSize->value());
0638 }
0639 
0640 HighlightAnnotationWidget::HighlightAnnotationWidget(Okular::Annotation *ann)
0641     : AnnotationWidget(ann)
0642 {
0643     m_hlAnn = static_cast<Okular::HighlightAnnotation *>(ann);
0644 }
0645 
0646 void HighlightAnnotationWidget::createStyleWidget(QFormLayout *formlayout)
0647 {
0648     QWidget *widget = qobject_cast<QWidget *>(formlayout->parent());
0649 
0650     m_typeCombo = new KComboBox(widget);
0651     m_typeCombo->setVisible(m_typeEditable);
0652     if (m_typeEditable) {
0653         formlayout->addRow(i18n("Type:"), m_typeCombo);
0654     }
0655     m_typeCombo->addItem(i18n("Highlight"));
0656     m_typeCombo->addItem(i18n("Squiggle"));
0657     m_typeCombo->addItem(i18n("Underline"));
0658     m_typeCombo->addItem(i18n("Strike out"));
0659     m_typeCombo->setCurrentIndex(m_hlAnn->highlightType());
0660 
0661     addVerticalSpacer(formlayout);
0662     addColorButton(widget, formlayout);
0663     addOpacitySpinBox(widget, formlayout);
0664 
0665     connect(m_typeCombo, QOverload<int>::of(&KComboBox::currentIndexChanged), this, &AnnotationWidget::dataChanged);
0666 }
0667 
0668 void HighlightAnnotationWidget::applyChanges()
0669 {
0670     AnnotationWidget::applyChanges();
0671     m_hlAnn->setHighlightType((Okular::HighlightAnnotation::HighlightType)m_typeCombo->currentIndex());
0672 }
0673 
0674 GeomAnnotationWidget::GeomAnnotationWidget(Okular::Annotation *ann)
0675     : AnnotationWidget(ann)
0676 {
0677     m_geomAnn = static_cast<Okular::GeomAnnotation *>(ann);
0678 }
0679 
0680 void GeomAnnotationWidget::createStyleWidget(QFormLayout *formlayout)
0681 {
0682     QWidget *widget = qobject_cast<QWidget *>(formlayout->parent());
0683 
0684     m_typeCombo = new KComboBox(widget);
0685     m_typeCombo->setVisible(m_typeEditable);
0686     if (m_typeEditable) {
0687         formlayout->addRow(i18n("Type:"), m_typeCombo);
0688     }
0689     addVerticalSpacer(formlayout);
0690     addColorButton(widget, formlayout);
0691     addOpacitySpinBox(widget, formlayout);
0692     QHBoxLayout *colorlay = new QHBoxLayout();
0693     m_useColor = new QCheckBox(i18n("Enabled"), widget);
0694     colorlay->addWidget(m_useColor);
0695     m_innerColor = new KColorButton(widget);
0696     colorlay->addWidget(m_innerColor);
0697     formlayout->addRow(i18n("Shape fill:"), colorlay);
0698     addVerticalSpacer(formlayout);
0699     m_spinSize = new QDoubleSpinBox(widget);
0700     formlayout->addRow(i18n("&Width:"), m_spinSize);
0701 
0702     m_typeCombo->addItem(i18n("Rectangle"));
0703     m_typeCombo->addItem(i18n("Ellipse"));
0704     m_typeCombo->setCurrentIndex(m_geomAnn->geometricalType());
0705     m_innerColor->setColor(m_geomAnn->geometricalInnerColor());
0706     if (m_geomAnn->geometricalInnerColor().isValid()) {
0707         m_useColor->setChecked(true);
0708     } else {
0709         m_innerColor->setEnabled(false);
0710     }
0711     m_spinSize->setRange(0, 100);
0712     m_spinSize->setValue(m_geomAnn->style().width());
0713 
0714     connect(m_typeCombo, QOverload<int>::of(&KComboBox::currentIndexChanged), this, &AnnotationWidget::dataChanged);
0715     connect(m_innerColor, &KColorButton::changed, this, &AnnotationWidget::dataChanged);
0716     connect(m_useColor, &QAbstractButton::toggled, this, &AnnotationWidget::dataChanged);
0717     connect(m_useColor, &QCheckBox::toggled, m_innerColor, &KColorButton::setEnabled);
0718     connect(m_spinSize, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &AnnotationWidget::dataChanged);
0719 }
0720 
0721 void GeomAnnotationWidget::applyChanges()
0722 {
0723     AnnotationWidget::applyChanges();
0724     m_geomAnn->setGeometricalType((Okular::GeomAnnotation::GeomType)m_typeCombo->currentIndex());
0725     if (!m_useColor->isChecked()) {
0726         m_geomAnn->setGeometricalInnerColor(QColor());
0727     } else {
0728         m_geomAnn->setGeometricalInnerColor(m_innerColor->color());
0729     }
0730     m_geomAnn->style().setWidth(m_spinSize->value());
0731 }
0732 
0733 FileAttachmentAnnotationWidget::FileAttachmentAnnotationWidget(Okular::Annotation *ann)
0734     : AnnotationWidget(ann)
0735     , m_pixmapSelector(nullptr)
0736 {
0737     m_attachAnn = static_cast<Okular::FileAttachmentAnnotation *>(ann);
0738 }
0739 
0740 void FileAttachmentAnnotationWidget::createStyleWidget(QFormLayout *formlayout)
0741 {
0742     QWidget *widget = qobject_cast<QWidget *>(formlayout->parent());
0743 
0744     addOpacitySpinBox(widget, formlayout);
0745 
0746     m_pixmapSelector = new PixmapPreviewSelector(widget);
0747     formlayout->addRow(i18n("File attachment symbol:"), m_pixmapSelector);
0748     m_pixmapSelector->setEditable(true);
0749 
0750     m_pixmapSelector->addItem(i18nc("Symbol for file attachment annotations", "Graph"), QStringLiteral("graph"));
0751     m_pixmapSelector->addItem(i18nc("Symbol for file attachment annotations", "Push Pin"), QStringLiteral("pushpin"));
0752     m_pixmapSelector->addItem(i18nc("Symbol for file attachment annotations", "Paperclip"), QStringLiteral("paperclip"));
0753     m_pixmapSelector->addItem(i18nc("Symbol for file attachment annotations", "Tag"), QStringLiteral("tag"));
0754     m_pixmapSelector->setIcon(m_attachAnn->fileIconName());
0755 
0756     connect(m_pixmapSelector, &PixmapPreviewSelector::iconChanged, this, &AnnotationWidget::dataChanged);
0757 }
0758 
0759 QWidget *FileAttachmentAnnotationWidget::createExtraWidget()
0760 {
0761     QWidget *widget = new QWidget();
0762     widget->setWindowTitle(i18nc("'File' as normal file, that can be opened, saved, etc..", "File"));
0763 
0764     Okular::EmbeddedFile *ef = m_attachAnn->embeddedFile();
0765     const int size = ef->size();
0766     const QString sizeString = size <= 0 ? i18nc("Not available size", "N/A") : KFormat().formatByteSize(size);
0767     const QString descString = ef->description().isEmpty() ? i18n("No description available.") : ef->description();
0768 
0769     QHBoxLayout *mainLay = new QHBoxLayout(widget);
0770     QFormLayout *lay = new QFormLayout();
0771     mainLay->addLayout(lay);
0772 
0773     QLabel *tmplabel = new QLabel(ef->name(), widget);
0774     tmplabel->setTextInteractionFlags(Qt::TextSelectableByMouse);
0775     lay->addRow(i18n("Name:"), tmplabel);
0776 
0777     tmplabel = new QLabel(sizeString, widget);
0778     tmplabel->setTextInteractionFlags(Qt::TextSelectableByMouse);
0779     lay->addRow(i18n("&Width:"), tmplabel);
0780 
0781     tmplabel = new QLabel(widget);
0782     tmplabel->setTextFormat(Qt::PlainText);
0783     tmplabel->setWordWrap(true);
0784     tmplabel->setText(descString);
0785     tmplabel->setTextInteractionFlags(Qt::TextSelectableByMouse);
0786     lay->addRow(i18n("Description:"), tmplabel);
0787 
0788     QMimeDatabase db;
0789     QMimeType mime = db.mimeTypeForFile(ef->name(), QMimeDatabase::MatchExtension);
0790     if (mime.isValid()) {
0791         tmplabel = new QLabel(widget);
0792         tmplabel->setPixmap(QIcon::fromTheme(mime.iconName()).pixmap(FILEATTACH_ICONSIZE, FILEATTACH_ICONSIZE));
0793         tmplabel->setFixedSize(FILEATTACH_ICONSIZE, FILEATTACH_ICONSIZE);
0794         QVBoxLayout *tmpLayout = new QVBoxLayout(widget);
0795         tmpLayout->setAlignment(Qt::AlignTop);
0796         mainLay->addLayout(tmpLayout);
0797         tmpLayout->addWidget(tmplabel);
0798     }
0799 
0800     return widget;
0801 }
0802 
0803 void FileAttachmentAnnotationWidget::applyChanges()
0804 {
0805     AnnotationWidget::applyChanges();
0806     m_attachAnn->setFileIconName(m_pixmapSelector->icon());
0807 }
0808 
0809 static QString caretSymbolToIcon(Okular::CaretAnnotation::CaretSymbol symbol)
0810 {
0811     switch (symbol) {
0812     case Okular::CaretAnnotation::None:
0813         return QStringLiteral("caret-none");
0814     case Okular::CaretAnnotation::P:
0815         return QStringLiteral("caret-p");
0816     }
0817     return QString();
0818 }
0819 
0820 static Okular::CaretAnnotation::CaretSymbol caretSymbolFromIcon(const QString &icon)
0821 {
0822     if (icon == QLatin1String("caret-none")) {
0823         return Okular::CaretAnnotation::None;
0824     } else if (icon == QLatin1String("caret-p")) {
0825         return Okular::CaretAnnotation::P;
0826     }
0827     return Okular::CaretAnnotation::None;
0828 }
0829 
0830 CaretAnnotationWidget::CaretAnnotationWidget(Okular::Annotation *ann)
0831     : AnnotationWidget(ann)
0832     , m_pixmapSelector(nullptr)
0833 {
0834     m_caretAnn = static_cast<Okular::CaretAnnotation *>(ann);
0835 }
0836 
0837 void CaretAnnotationWidget::createStyleWidget(QFormLayout *formlayout)
0838 {
0839     QWidget *widget = qobject_cast<QWidget *>(formlayout->parent());
0840 
0841     addColorButton(widget, formlayout);
0842     addOpacitySpinBox(widget, formlayout);
0843 
0844     m_pixmapSelector = new PixmapPreviewSelector(widget);
0845     formlayout->addRow(i18n("Caret symbol:"), m_pixmapSelector);
0846 
0847     m_pixmapSelector->addItem(i18nc("Symbol for caret annotations", "None"), QStringLiteral("caret-none"));
0848     m_pixmapSelector->addItem(i18nc("Symbol for caret annotations", "P"), QStringLiteral("caret-p"));
0849     m_pixmapSelector->setIcon(caretSymbolToIcon(m_caretAnn->caretSymbol()));
0850 
0851     connect(m_pixmapSelector, &PixmapPreviewSelector::iconChanged, this, &AnnotationWidget::dataChanged);
0852 }
0853 
0854 void CaretAnnotationWidget::applyChanges()
0855 {
0856     AnnotationWidget::applyChanges();
0857     m_caretAnn->setCaretSymbol(caretSymbolFromIcon(m_pixmapSelector->icon()));
0858 }
0859 
0860 #include "moc_annotationwidgets.cpp"