mirror of
https://invent.kde.org/graphics/okular
synced 2024-11-05 18:34:53 +00:00
88fc4032ac
- missing consts - shadowing of variable names - superflous code - tighter scoping of some variables and other such bits
295 lines
10 KiB
C++
295 lines
10 KiB
C++
/*
|
|
SPDX-FileCopyrightText: 2006-2007 Pino Toscano <pino@kde.org>
|
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later
|
|
*/
|
|
|
|
#include "guiutils.h"
|
|
|
|
// qt/kde includes
|
|
#include <KLocalizedString>
|
|
#include <KMessageBox>
|
|
#include <QApplication>
|
|
#include <QFileDialog>
|
|
#include <QPainter>
|
|
#include <QStandardPaths>
|
|
#include <QTextDocument>
|
|
|
|
// local includes
|
|
#include "core/action.h"
|
|
#include "core/annotations.h"
|
|
#include "core/document.h"
|
|
|
|
#include <memory>
|
|
|
|
namespace GuiUtils
|
|
{
|
|
QString captionForAnnotation(const Okular::Annotation *ann)
|
|
{
|
|
Q_ASSERT(ann);
|
|
|
|
const bool hasComment = !ann->contents().isEmpty();
|
|
|
|
QString ret;
|
|
switch (ann->subType()) {
|
|
case Okular::Annotation::AText:
|
|
if (((Okular::TextAnnotation *)ann)->textType() == Okular::TextAnnotation::Linked) {
|
|
ret = i18n("Pop-up Note");
|
|
} else {
|
|
if (((Okular::TextAnnotation *)ann)->inplaceIntent() == Okular::TextAnnotation::TypeWriter) {
|
|
ret = i18n("Typewriter");
|
|
} else {
|
|
ret = i18n("Inline Note");
|
|
}
|
|
}
|
|
break;
|
|
case Okular::Annotation::ALine:
|
|
if (((Okular::LineAnnotation *)ann)->linePoints().count() == 2) {
|
|
ret = hasComment ? i18n("Straight Line with Comment") : i18n("Straight Line");
|
|
} else {
|
|
ret = hasComment ? i18n("Polygon with Comment") : i18n("Polygon");
|
|
}
|
|
break;
|
|
case Okular::Annotation::AGeom:
|
|
ret = hasComment ? i18n("Geometry with Comment") : i18n("Geometry");
|
|
break;
|
|
case Okular::Annotation::AHighlight:
|
|
switch (((Okular::HighlightAnnotation *)ann)->highlightType()) {
|
|
case Okular::HighlightAnnotation::Highlight:
|
|
ret = hasComment ? i18n("Highlight with Comment") : i18n("Highlight");
|
|
break;
|
|
case Okular::HighlightAnnotation::Squiggly:
|
|
ret = hasComment ? i18n("Squiggle with Comment") : i18n("Squiggle");
|
|
break;
|
|
case Okular::HighlightAnnotation::Underline:
|
|
ret = hasComment ? i18n("Underline with Comment") : i18n("Underline");
|
|
break;
|
|
case Okular::HighlightAnnotation::StrikeOut:
|
|
ret = hasComment ? i18n("Strike Out with Comment") : i18n("Strike Out");
|
|
break;
|
|
}
|
|
break;
|
|
case Okular::Annotation::AStamp:
|
|
ret = hasComment ? i18n("Stamp with Comment") : i18n("Stamp");
|
|
break;
|
|
case Okular::Annotation::AInk:
|
|
ret = hasComment ? i18n("Freehand Line with Comment") : i18n("Freehand Line");
|
|
break;
|
|
case Okular::Annotation::ACaret:
|
|
ret = i18n("Caret");
|
|
break;
|
|
case Okular::Annotation::AFileAttachment:
|
|
ret = i18n("File Attachment");
|
|
break;
|
|
case Okular::Annotation::ASound:
|
|
ret = i18n("Sound");
|
|
break;
|
|
case Okular::Annotation::AMovie:
|
|
ret = i18n("Movie");
|
|
break;
|
|
case Okular::Annotation::AScreen:
|
|
ret = i18nc("Caption for a screen annotation", "Screen");
|
|
break;
|
|
case Okular::Annotation::AWidget:
|
|
ret = i18nc("Caption for a widget annotation", "Widget");
|
|
break;
|
|
case Okular::Annotation::ARichMedia:
|
|
ret = i18nc("Caption for a rich media annotation", "Rich Media");
|
|
break;
|
|
case Okular::Annotation::A_BASE:
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
QString authorForAnnotation(const Okular::Annotation *ann)
|
|
{
|
|
Q_ASSERT(ann);
|
|
|
|
return !ann->author().isEmpty() ? ann->author() : i18nc("Unknown author", "Unknown");
|
|
}
|
|
|
|
QString contentsHtml(const Okular::Annotation *ann)
|
|
{
|
|
QString text = ann->contents().toHtmlEscaped();
|
|
text.replace(QLatin1Char('\n'), QLatin1String("<br>"));
|
|
return text;
|
|
}
|
|
|
|
QString prettyToolTip(const Okular::Annotation *ann)
|
|
{
|
|
Q_ASSERT(ann);
|
|
|
|
QString author = authorForAnnotation(ann);
|
|
QString contents = contentsHtml(ann);
|
|
|
|
QString tooltip = QStringLiteral("<qt><b>") + i18n("Author: %1", author) + QStringLiteral("</b>");
|
|
if (!contents.isEmpty()) {
|
|
tooltip += QStringLiteral("<div style=\"font-size: 4px;\"><hr /></div>") + contents;
|
|
}
|
|
|
|
tooltip += QLatin1String("</qt>");
|
|
|
|
return tooltip;
|
|
}
|
|
|
|
void saveEmbeddedFile(Okular::EmbeddedFile *ef, QWidget *parent)
|
|
{
|
|
const QString caption = i18n("Where do you want to save %1?", ef->name());
|
|
const QString path = QFileDialog::getSaveFileName(parent, caption, ef->name());
|
|
if (path.isEmpty()) {
|
|
return;
|
|
}
|
|
QFile targetFile(path);
|
|
writeEmbeddedFile(ef, parent, targetFile);
|
|
}
|
|
|
|
void writeEmbeddedFile(Okular::EmbeddedFile *ef, QWidget *parent, QFile &target)
|
|
{
|
|
if (!target.open(QIODevice::WriteOnly)) {
|
|
KMessageBox::error(parent, i18n("Could not open \"%1\" for writing. File was not saved.", target.fileName()));
|
|
return;
|
|
}
|
|
target.write(ef->data());
|
|
target.close();
|
|
}
|
|
|
|
Okular::Movie *renditionMovieFromScreenAnnotation(const Okular::ScreenAnnotation *annotation)
|
|
{
|
|
if (!annotation) {
|
|
return nullptr;
|
|
}
|
|
|
|
if (annotation->action() && annotation->action()->actionType() == Okular::Action::Rendition) {
|
|
Okular::RenditionAction *renditionAction = static_cast<Okular::RenditionAction *>(annotation->action());
|
|
return renditionAction->movie();
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
// from Arthur - qt4
|
|
static inline int qt_div_255(int x)
|
|
{
|
|
return (x + (x >> 8) + 0x80) >> 8;
|
|
}
|
|
|
|
void colorizeImage(QImage &grayImage, const QColor &color, unsigned int destAlpha)
|
|
{
|
|
// Make sure that the image is Format_ARGB32_Premultiplied
|
|
if (grayImage.format() != QImage::Format_ARGB32_Premultiplied) {
|
|
grayImage = grayImage.convertToFormat(QImage::Format_ARGB32_Premultiplied);
|
|
}
|
|
|
|
// iterate over all pixels changing the alpha component value
|
|
unsigned int *data = reinterpret_cast<unsigned int *>(grayImage.bits());
|
|
unsigned int pixels = grayImage.width() * grayImage.height();
|
|
int red = color.red(), green = color.green(), blue = color.blue();
|
|
|
|
for (unsigned int i = 0; i < pixels; ++i) { // optimize this loop keeping byte order into account
|
|
int source = data[i];
|
|
int sourceSat = qRed(source);
|
|
int newR = qt_div_255(sourceSat * red), newG = qt_div_255(sourceSat * green), newB = qt_div_255(sourceSat * blue);
|
|
if (int sourceAlpha = qAlpha(source); sourceAlpha == 255) {
|
|
// use destAlpha
|
|
data[i] = qRgba(newR, newG, newB, destAlpha);
|
|
} else {
|
|
// use destAlpha * sourceAlpha product
|
|
if (destAlpha < 255) {
|
|
sourceAlpha = qt_div_255(destAlpha * sourceAlpha);
|
|
}
|
|
data[i] = qRgba(newR, newG, newB, sourceAlpha);
|
|
}
|
|
}
|
|
}
|
|
|
|
QIcon createColorIcon(const QList<QColor> &colors, const QIcon &background, ColorIconFlags flags)
|
|
{
|
|
QIcon colorIcon;
|
|
|
|
// Create a pixmap for each common icon size.
|
|
for (int size : {16, 22, 24, 32, 48}) {
|
|
QPixmap pixmap(QSize(size, size) * qApp->devicePixelRatio());
|
|
pixmap.setDevicePixelRatio(qApp->devicePixelRatio());
|
|
pixmap.fill(Qt::transparent);
|
|
QPainter painter(&pixmap);
|
|
// Configure hairlines for visualization of outline or transparency (visualizeTransparent):
|
|
painter.setPen(QPen(qApp->palette().color(QPalette::Active, QPalette::WindowText), 0));
|
|
painter.setBrush(Qt::NoBrush);
|
|
|
|
if (background.isNull()) {
|
|
// Full-size color rectangles.
|
|
// Draw rectangles left to right:
|
|
for (int i = 0; i < colors.count(); ++i) {
|
|
if (!colors.at(i).isValid()) {
|
|
continue;
|
|
}
|
|
QRect rect(QPoint(size * i / colors.count(), 0), QPoint(size * (i + 1) / colors.count(), size));
|
|
if ((flags & VisualizeTransparent) && (colors.at(i) == Qt::transparent)) {
|
|
painter.drawLine(rect.topLeft(), rect.bottomRight());
|
|
painter.drawLine(rect.bottomLeft(), rect.topRight());
|
|
} else {
|
|
painter.fillRect(rect, colors.at(i));
|
|
}
|
|
}
|
|
|
|
// Draw hairline outline:
|
|
// To get the hairline on the outermost pixels, we shrink the rectangle by a half pixel on each edge.
|
|
const qreal halfPixelWidth = 0.5 / pixmap.devicePixelRatio();
|
|
painter.drawRect(QRectF(QPointF(halfPixelWidth, halfPixelWidth), QPointF(qreal(size) - halfPixelWidth, qreal(size) - halfPixelWidth)));
|
|
} else {
|
|
// Lower 25% color rectangles.
|
|
// Draw background icon:
|
|
background.paint(&painter, QRect(QPoint(0, 0), QSize(size, size)));
|
|
|
|
// Draw rectangles left to right:
|
|
for (int i = 0; i < colors.count(); ++i) {
|
|
if (!colors.at(i).isValid()) {
|
|
continue;
|
|
}
|
|
QRect rect(QPoint(size * i / colors.count(), size * 3 / 4), QPoint(size * (i + 1) / colors.count(), size));
|
|
if ((flags & VisualizeTransparent) && (colors.at(i) == Qt::transparent)) {
|
|
painter.drawLine(rect.topLeft(), rect.bottomRight());
|
|
painter.drawLine(rect.bottomLeft(), rect.topRight());
|
|
} else {
|
|
painter.fillRect(rect, colors.at(i));
|
|
}
|
|
}
|
|
}
|
|
|
|
painter.end();
|
|
colorIcon.addPixmap(pixmap);
|
|
}
|
|
|
|
return colorIcon;
|
|
}
|
|
|
|
QIcon createOpacityIcon(qreal opacity)
|
|
{
|
|
QIcon opacityIcon;
|
|
|
|
// Create a pixmap for each common icon size.
|
|
for (int size : {16, 22, 24, 32, 48}) {
|
|
QPixmap pixmap(QSize(size, size) * qApp->devicePixelRatio());
|
|
pixmap.setDevicePixelRatio(qApp->devicePixelRatio());
|
|
pixmap.fill(Qt::transparent);
|
|
QPainter painter(&pixmap);
|
|
painter.setPen(Qt::NoPen);
|
|
painter.setBrush(qApp->palette().color(QPalette::Active, QPalette::WindowText));
|
|
|
|
// Checkerboard pattern
|
|
painter.drawRect(QRectF(QPoint(0, 0), QPoint(size, size) / 2));
|
|
painter.drawRect(QRectF(QPoint(size, size) / 2, QPoint(size, size)));
|
|
|
|
// Opacity
|
|
painter.setOpacity(opacity);
|
|
painter.drawRect(QRect(QPoint(0, 0), QPoint(size, size)));
|
|
|
|
painter.end();
|
|
opacityIcon.addPixmap(pixmap);
|
|
}
|
|
|
|
return opacityIcon;
|
|
}
|
|
|
|
}
|