File indexing completed on 2024-04-28 04:33:05
0001 /* 0002 SPDX-FileCopyrightText: 2006-2007 Pino Toscano <pino@kde.org> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "guiutils.h" 0008 0009 // qt/kde includes 0010 #include <KLocalizedString> 0011 #include <KMessageBox> 0012 #include <QApplication> 0013 #include <QFileDialog> 0014 #include <QPainter> 0015 #include <QStandardPaths> 0016 #include <QTextDocument> 0017 0018 // local includes 0019 #include "core/action.h" 0020 #include "core/annotations.h" 0021 #include "core/document.h" 0022 0023 #include <memory> 0024 0025 namespace GuiUtils 0026 { 0027 QString captionForAnnotation(const Okular::Annotation *ann) 0028 { 0029 Q_ASSERT(ann); 0030 0031 const bool hasComment = !ann->contents().isEmpty(); 0032 0033 QString ret; 0034 switch (ann->subType()) { 0035 case Okular::Annotation::AText: 0036 if (((Okular::TextAnnotation *)ann)->textType() == Okular::TextAnnotation::Linked) { 0037 ret = i18n("Pop-up Note"); 0038 } else { 0039 if (((Okular::TextAnnotation *)ann)->inplaceIntent() == Okular::TextAnnotation::TypeWriter) { 0040 ret = i18n("Typewriter"); 0041 } else { 0042 ret = i18n("Inline Note"); 0043 } 0044 } 0045 break; 0046 case Okular::Annotation::ALine: 0047 if (((Okular::LineAnnotation *)ann)->linePoints().count() == 2) { 0048 ret = hasComment ? i18n("Straight Line with Comment") : i18n("Straight Line"); 0049 } else { 0050 ret = hasComment ? i18n("Polygon with Comment") : i18n("Polygon"); 0051 } 0052 break; 0053 case Okular::Annotation::AGeom: 0054 ret = hasComment ? i18n("Geometry with Comment") : i18n("Geometry"); 0055 break; 0056 case Okular::Annotation::AHighlight: 0057 switch (((Okular::HighlightAnnotation *)ann)->highlightType()) { 0058 case Okular::HighlightAnnotation::Highlight: 0059 ret = hasComment ? i18n("Highlight with Comment") : i18n("Highlight"); 0060 break; 0061 case Okular::HighlightAnnotation::Squiggly: 0062 ret = hasComment ? i18n("Squiggle with Comment") : i18n("Squiggle"); 0063 break; 0064 case Okular::HighlightAnnotation::Underline: 0065 ret = hasComment ? i18n("Underline with Comment") : i18n("Underline"); 0066 break; 0067 case Okular::HighlightAnnotation::StrikeOut: 0068 ret = hasComment ? i18n("Strike Out with Comment") : i18n("Strike Out"); 0069 break; 0070 } 0071 break; 0072 case Okular::Annotation::AStamp: 0073 ret = hasComment ? i18n("Stamp with Comment") : i18n("Stamp"); 0074 break; 0075 case Okular::Annotation::AInk: 0076 ret = hasComment ? i18n("Freehand Line with Comment") : i18n("Freehand Line"); 0077 break; 0078 case Okular::Annotation::ACaret: 0079 ret = i18n("Caret"); 0080 break; 0081 case Okular::Annotation::AFileAttachment: 0082 ret = i18n("File Attachment"); 0083 break; 0084 case Okular::Annotation::ASound: 0085 ret = i18n("Sound"); 0086 break; 0087 case Okular::Annotation::AMovie: 0088 ret = i18n("Movie"); 0089 break; 0090 case Okular::Annotation::AScreen: 0091 ret = i18nc("Caption for a screen annotation", "Screen"); 0092 break; 0093 case Okular::Annotation::AWidget: 0094 ret = i18nc("Caption for a widget annotation", "Widget"); 0095 break; 0096 case Okular::Annotation::ARichMedia: 0097 ret = i18nc("Caption for a rich media annotation", "Rich Media"); 0098 break; 0099 case Okular::Annotation::A_BASE: 0100 break; 0101 } 0102 return ret; 0103 } 0104 0105 QString authorForAnnotation(const Okular::Annotation *ann) 0106 { 0107 Q_ASSERT(ann); 0108 0109 return !ann->author().isEmpty() ? ann->author() : i18nc("Unknown author", "Unknown"); 0110 } 0111 0112 QString contentsHtml(const Okular::Annotation *ann) 0113 { 0114 QString text = ann->contents().toHtmlEscaped(); 0115 text.replace(QLatin1Char('\n'), QLatin1String("<br>")); 0116 return text; 0117 } 0118 0119 QString prettyToolTip(const Okular::Annotation *ann) 0120 { 0121 Q_ASSERT(ann); 0122 0123 QString author = authorForAnnotation(ann); 0124 QString contents = contentsHtml(ann); 0125 0126 QString tooltip = QStringLiteral("<qt><b>") + i18n("Author: %1", author) + QStringLiteral("</b>"); 0127 if (!contents.isEmpty()) { 0128 tooltip += QStringLiteral("<div style=\"font-size: 4px;\"><hr /></div>") + contents; 0129 } 0130 0131 tooltip += QLatin1String("</qt>"); 0132 0133 return tooltip; 0134 } 0135 0136 void saveEmbeddedFile(Okular::EmbeddedFile *ef, QWidget *parent) 0137 { 0138 const QString caption = i18n("Where do you want to save %1?", ef->name()); 0139 const QString path = QFileDialog::getSaveFileName(parent, caption, ef->name()); 0140 if (path.isEmpty()) { 0141 return; 0142 } 0143 QFile targetFile(path); 0144 writeEmbeddedFile(ef, parent, targetFile); 0145 } 0146 0147 void writeEmbeddedFile(Okular::EmbeddedFile *ef, QWidget *parent, QFile &target) 0148 { 0149 if (!target.open(QIODevice::WriteOnly)) { 0150 KMessageBox::error(parent, i18n("Could not open \"%1\" for writing. File was not saved.", target.fileName())); 0151 return; 0152 } 0153 target.write(ef->data()); 0154 target.close(); 0155 } 0156 0157 Okular::Movie *renditionMovieFromScreenAnnotation(const Okular::ScreenAnnotation *annotation) 0158 { 0159 if (!annotation) { 0160 return nullptr; 0161 } 0162 0163 if (annotation->action() && annotation->action()->actionType() == Okular::Action::Rendition) { 0164 Okular::RenditionAction *renditionAction = static_cast<Okular::RenditionAction *>(annotation->action()); 0165 return renditionAction->movie(); 0166 } 0167 0168 return nullptr; 0169 } 0170 0171 // from Arthur - qt4 0172 static inline int qt_div_255(int x) 0173 { 0174 return (x + (x >> 8) + 0x80) >> 8; 0175 } 0176 0177 void colorizeImage(QImage &grayImage, const QColor &color, unsigned int destAlpha) 0178 { 0179 // Make sure that the image is Format_ARGB32_Premultiplied 0180 if (grayImage.format() != QImage::Format_ARGB32_Premultiplied) { 0181 grayImage = grayImage.convertToFormat(QImage::Format_ARGB32_Premultiplied); 0182 } 0183 0184 // iterate over all pixels changing the alpha component value 0185 unsigned int *data = reinterpret_cast<unsigned int *>(grayImage.bits()); 0186 unsigned int pixels = grayImage.width() * grayImage.height(); 0187 int red = color.red(), green = color.green(), blue = color.blue(); 0188 0189 for (unsigned int i = 0; i < pixels; ++i) { // optimize this loop keeping byte order into account 0190 int source = data[i]; 0191 int sourceSat = qRed(source); 0192 int newR = qt_div_255(sourceSat * red), newG = qt_div_255(sourceSat * green), newB = qt_div_255(sourceSat * blue); 0193 if (int sourceAlpha = qAlpha(source); sourceAlpha == 255) { 0194 // use destAlpha 0195 data[i] = qRgba(newR, newG, newB, destAlpha); 0196 } else { 0197 // use destAlpha * sourceAlpha product 0198 if (destAlpha < 255) { 0199 sourceAlpha = qt_div_255(destAlpha * sourceAlpha); 0200 } 0201 data[i] = qRgba(newR, newG, newB, sourceAlpha); 0202 } 0203 } 0204 } 0205 0206 QIcon createColorIcon(const QList<QColor> &colors, const QIcon &background, ColorIconFlags flags) 0207 { 0208 QIcon colorIcon; 0209 0210 // Create a pixmap for each common icon size. 0211 for (int size : {16, 22, 24, 32, 48}) { 0212 QPixmap pixmap(QSize(size, size) * qApp->devicePixelRatio()); 0213 pixmap.setDevicePixelRatio(qApp->devicePixelRatio()); 0214 pixmap.fill(Qt::transparent); 0215 QPainter painter(&pixmap); 0216 // Configure hairlines for visualization of outline or transparency (visualizeTransparent): 0217 painter.setPen(QPen(qApp->palette().color(QPalette::Active, QPalette::WindowText), 0)); 0218 painter.setBrush(Qt::NoBrush); 0219 0220 if (background.isNull()) { 0221 // Full-size color rectangles. 0222 // Draw rectangles left to right: 0223 for (int i = 0; i < colors.count(); ++i) { 0224 if (!colors.at(i).isValid()) { 0225 continue; 0226 } 0227 QRect rect(QPoint(size * i / colors.count(), 0), QPoint(size * (i + 1) / colors.count(), size)); 0228 if ((flags & VisualizeTransparent) && (colors.at(i) == Qt::transparent)) { 0229 painter.drawLine(rect.topLeft(), rect.bottomRight()); 0230 painter.drawLine(rect.bottomLeft(), rect.topRight()); 0231 } else { 0232 painter.fillRect(rect, colors.at(i)); 0233 } 0234 } 0235 0236 // Draw hairline outline: 0237 // To get the hairline on the outermost pixels, we shrink the rectangle by a half pixel on each edge. 0238 const qreal halfPixelWidth = 0.5 / pixmap.devicePixelRatio(); 0239 painter.drawRect(QRectF(QPointF(halfPixelWidth, halfPixelWidth), QPointF(qreal(size) - halfPixelWidth, qreal(size) - halfPixelWidth))); 0240 } else { 0241 // Lower 25% color rectangles. 0242 // Draw background icon: 0243 background.paint(&painter, QRect(QPoint(0, 0), QSize(size, size))); 0244 0245 // Draw rectangles left to right: 0246 for (int i = 0; i < colors.count(); ++i) { 0247 if (!colors.at(i).isValid()) { 0248 continue; 0249 } 0250 QRect rect(QPoint(size * i / colors.count(), size * 3 / 4), QPoint(size * (i + 1) / colors.count(), size)); 0251 if ((flags & VisualizeTransparent) && (colors.at(i) == Qt::transparent)) { 0252 painter.drawLine(rect.topLeft(), rect.bottomRight()); 0253 painter.drawLine(rect.bottomLeft(), rect.topRight()); 0254 } else { 0255 painter.fillRect(rect, colors.at(i)); 0256 } 0257 } 0258 } 0259 0260 painter.end(); 0261 colorIcon.addPixmap(pixmap); 0262 } 0263 0264 return colorIcon; 0265 } 0266 0267 QIcon createOpacityIcon(qreal opacity) 0268 { 0269 QIcon opacityIcon; 0270 0271 // Create a pixmap for each common icon size. 0272 for (int size : {16, 22, 24, 32, 48}) { 0273 QPixmap pixmap(QSize(size, size) * qApp->devicePixelRatio()); 0274 pixmap.setDevicePixelRatio(qApp->devicePixelRatio()); 0275 pixmap.fill(Qt::transparent); 0276 QPainter painter(&pixmap); 0277 painter.setPen(Qt::NoPen); 0278 painter.setBrush(qApp->palette().color(QPalette::Active, QPalette::WindowText)); 0279 0280 // Checkerboard pattern 0281 painter.drawRect(QRectF(QPoint(0, 0), QPoint(size, size) / 2)); 0282 painter.drawRect(QRectF(QPoint(size, size) / 2, QPoint(size, size))); 0283 0284 // Opacity 0285 painter.setOpacity(opacity); 0286 painter.drawRect(QRect(QPoint(0, 0), QPoint(size, size))); 0287 0288 painter.end(); 0289 opacityIcon.addPixmap(pixmap); 0290 } 0291 0292 return opacityIcon; 0293 } 0294 0295 }