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

0001 /*
0002     SPDX-FileCopyrightText: 2015 Laurent Montel <montel@kde.org>
0003     SPDX-FileCopyrightText: 2015 Albert Astals Cid <aacid@kde.org>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #include "drawingtoolactions.h"
0009 
0010 #include "gui/debug_ui.h"
0011 #include "settings.h"
0012 
0013 #include <KActionCollection>
0014 #include <KLocalizedString>
0015 
0016 #include <QAction>
0017 #include <QIconEngine>
0018 #include <QPainter>
0019 
0020 class ColorAction : public QAction
0021 {
0022     Q_OBJECT
0023 
0024 public:
0025     explicit ColorAction(KActionCollection *parent)
0026         : QAction(parent)
0027     {
0028     }
0029 
0030     void setColor(const QColor &color)
0031     {
0032         setIcon(QIcon(new ColorActionIconEngine(color)));
0033     }
0034 
0035 protected:
0036     class ColorActionIconEngine : public QIconEngine
0037     {
0038     public:
0039         explicit ColorActionIconEngine(const QColor &color)
0040             : m_color(color)
0041         {
0042         }
0043 
0044         ColorActionIconEngine(const ColorActionIconEngine &) = delete;
0045         ColorActionIconEngine &operator=(const ColorActionIconEngine &) = delete;
0046 
0047         // No one needs clone(), but it’s pure virtual
0048         QIconEngine *clone() const override
0049         {
0050             return nullptr;
0051         }
0052 
0053         QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) override
0054         {
0055             QPixmap pixmap(size);
0056             pixmap.fill(Qt::transparent);
0057             Q_ASSERT(pixmap.hasAlphaChannel());
0058 
0059             QPainter painter(&pixmap);
0060             paint(&painter, QRect(QPoint(0, 0), size), mode, state);
0061             return pixmap;
0062         }
0063 
0064         void paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state) override
0065         {
0066             Q_UNUSED(mode)
0067 
0068             // Assume that rect is square and at position (0, 0)
0069             int squareSize = rect.height() * 0.8;
0070             int squareOffset = (rect.height() - squareSize) / 2;
0071 
0072             painter->fillRect(squareOffset, squareOffset, squareSize, squareSize, m_color);
0073 
0074             if (state == QIcon::On) {
0075                 QFont checkmarkFont = painter->font();
0076                 checkmarkFont.setPixelSize(squareSize * 0.9);
0077                 painter->setFont(checkmarkFont);
0078 
0079                 const int lightness = ((m_color.red() * 299) + (m_color.green() * 587) + (m_color.blue() * 114)) / 1000;
0080                 painter->setPen(lightness < 128 ? Qt::white : Qt::black);
0081 
0082                 painter->drawText(QRect(squareOffset, squareOffset, squareSize, squareSize), Qt::AlignCenter, QStringLiteral("\u2713"));
0083             }
0084         }
0085 
0086     protected:
0087         QColor m_color;
0088     };
0089 };
0090 
0091 DrawingToolActions::DrawingToolActions(KActionCollection *parent)
0092     : QObject(parent)
0093 {
0094     loadTools();
0095 }
0096 
0097 DrawingToolActions::~DrawingToolActions()
0098 {
0099 }
0100 
0101 QList<QAction *> DrawingToolActions::actions() const
0102 {
0103     return m_actions;
0104 }
0105 
0106 void DrawingToolActions::reparseConfig()
0107 {
0108     qDeleteAll(m_actions);
0109     m_actions.clear();
0110     loadTools();
0111 }
0112 
0113 void DrawingToolActions::actionTriggered()
0114 {
0115     QAction *action = qobject_cast<QAction *>(sender());
0116 
0117     if (action) {
0118         if (action->isChecked()) {
0119             for (QAction *btn : std::as_const(m_actions)) {
0120                 if (action != btn) {
0121                     btn->setChecked(false);
0122                 }
0123             }
0124 
0125             Q_EMIT changeEngine(action->property("__document").value<QDomElement>());
0126         } else {
0127             Q_EMIT changeEngine(QDomElement());
0128         }
0129     }
0130 }
0131 
0132 void DrawingToolActions::loadTools()
0133 {
0134     const QStringList drawingTools = Okular::Settings::drawingTools();
0135 
0136     QDomDocument main;
0137     QDomElement drawingDefinition = main.createElement(QStringLiteral("drawingTools"));
0138     for (const QString &drawingXml : drawingTools) {
0139         QDomDocument entryParser;
0140         if (entryParser.setContent(drawingXml)) {
0141             drawingDefinition.appendChild(main.importNode(entryParser.documentElement(), true));
0142         } else {
0143             qCWarning(OkularUiDebug) << "Skipping malformed quick selection XML in QuickSelectionTools setting";
0144         }
0145     }
0146 
0147     // Create the AnnotationToolItems from the XML dom tree
0148     QDomNode drawingDescription = drawingDefinition.firstChild();
0149     while (drawingDescription.isElement()) {
0150         const QDomElement toolElement = drawingDescription.toElement();
0151         if (toolElement.tagName() == QLatin1String("tool")) {
0152             QString tooltip;
0153             QString width;
0154             QString colorStr;
0155             QString opacity;
0156 
0157             const QString name = toolElement.attribute(QStringLiteral("name"));
0158             if (toolElement.attribute(QStringLiteral("default"), QStringLiteral("false")) == QLatin1String("true")) {
0159                 tooltip = i18n(name.toLatin1().constData());
0160             } else {
0161                 tooltip = name;
0162             }
0163 
0164             const QDomNodeList engineNodeList = toolElement.elementsByTagName(QStringLiteral("engine"));
0165             if (engineNodeList.size() > 0) {
0166                 const QDomElement engineEl = engineNodeList.item(0).toElement();
0167                 if (engineEl.hasAttribute(QStringLiteral("color"))) {
0168                     colorStr = engineEl.attribute(QStringLiteral("color"));
0169                 }
0170 
0171                 const QDomNodeList annotationList = engineEl.elementsByTagName(QStringLiteral("annotation"));
0172                 if (annotationList.size() > 0) {
0173                     const QDomElement annotationEl = annotationList.item(0).toElement();
0174                     if (annotationEl.hasAttribute(QStringLiteral("width"))) {
0175                         width = annotationEl.attribute(QStringLiteral("width"));
0176                         opacity = annotationEl.attribute(QStringLiteral("opacity"), QStringLiteral("1.0"));
0177                     }
0178                 }
0179             }
0180 
0181             QDomDocument engine(QStringLiteral("engine"));
0182             QDomElement root = engine.createElement(QStringLiteral("engine"));
0183             root.setAttribute(QStringLiteral("color"), colorStr);
0184             engine.appendChild(root);
0185             QDomElement annElem = engine.createElement(QStringLiteral("annotation"));
0186             root.appendChild(annElem);
0187             annElem.setAttribute(QStringLiteral("type"), QStringLiteral("Ink"));
0188             annElem.setAttribute(QStringLiteral("color"), colorStr);
0189             annElem.setAttribute(QStringLiteral("width"), width);
0190             annElem.setAttribute(QStringLiteral("opacity"), opacity);
0191 
0192             const QString text = i18n("Drawing Tool: %1", tooltip);
0193             createToolAction(text, tooltip, colorStr, root);
0194         }
0195 
0196         drawingDescription = drawingDescription.nextSibling();
0197     }
0198 
0199     // add erasure action
0200     {
0201         QDomDocument engine(QStringLiteral("engine"));
0202         QDomElement root = engine.createElement(QStringLiteral("engine"));
0203         root.setAttribute(QStringLiteral("color"), QStringLiteral("transparent"));
0204         root.setAttribute(QStringLiteral("compositionMode"), QStringLiteral("clear"));
0205         engine.appendChild(root);
0206         QDomElement annElem = engine.createElement(QStringLiteral("annotation"));
0207         root.appendChild(annElem);
0208         annElem.setAttribute(QStringLiteral("type"), QStringLiteral("Ink"));
0209         annElem.setAttribute(QStringLiteral("color"), QStringLiteral("transparent"));
0210         annElem.setAttribute(QStringLiteral("width"), 20);
0211 
0212         KActionCollection *ac = static_cast<KActionCollection *>(parent());
0213         QAction *action = new QAction(ac);
0214         action->setText(i18n("Eraser"));
0215         action->setToolTip(i18n("Eraser"));
0216         action->setCheckable(true);
0217         action->setIcon(QIcon::fromTheme(QStringLiteral("draw-eraser")));
0218         action->setProperty("__document", QVariant::fromValue<QDomElement>(root));
0219 
0220         m_actions.append(action);
0221 
0222         ac->addAction(QStringLiteral("presentation_drawing_eraser"), action);
0223 
0224         connect(action, &QAction::triggered, this, &DrawingToolActions::actionTriggered);
0225     }
0226 }
0227 
0228 void DrawingToolActions::createToolAction(const QString &text, const QString &toolName, const QString &colorName, const QDomElement &root)
0229 {
0230     KActionCollection *ac = static_cast<KActionCollection *>(parent());
0231     ColorAction *action = new ColorAction(ac);
0232     action->setText(text);
0233     action->setToolTip(toolName);
0234     action->setCheckable(true);
0235     action->setColor(QColor(colorName));
0236     action->setEnabled(false);
0237 
0238     action->setProperty("__document", QVariant::fromValue<QDomElement>(root));
0239 
0240     m_actions.append(action);
0241 
0242     ac->addAction(QStringLiteral("presentation_drawing_%1").arg(toolName), action);
0243 
0244     connect(action, &QAction::triggered, this, &DrawingToolActions::actionTriggered);
0245 }
0246 
0247 #include "drawingtoolactions.moc"