Branch data Line data Source code
1 : : #include "PdfViewerWidget.h"
2 : :
3 : : #include <QDir>
4 : : #include <QEvent>
5 : : #include <QFileDialog>
6 : : #include <QHBoxLayout>
7 : : #include <QLabel>
8 : : #include <QLoggingCategory>
9 : : #include <QMessageBox>
10 : : #include <QPdfDocument>
11 : : #include <QPdfView>
12 : : #include <QSaveFile>
13 : : #include <QScrollArea>
14 : : #include <QTemporaryFile>
15 : : #include <QToolButton>
16 : : #include <QVBoxLayout>
17 : :
18 [ # # # # : 0 : Q_LOGGING_CATEGORY(lcPdfViewer, "mailjd.pdfviewer")
# # # # ]
19 : :
20 [ + - ]: 86 : PdfViewerWidget::PdfViewerWidget(QWidget *parent) : QWidget(parent) {
21 [ + - + - : 86 : auto *layout = new QVBoxLayout(this);
- + - - ]
22 [ + - ]: 86 : layout->setContentsMargins(0, 0, 0, 0);
23 [ + - ]: 86 : layout->setSpacing(0);
24 : :
25 : : // --- Toolbar ---
26 [ + - + - : 86 : auto *toolbar = new QHBoxLayout();
- + - - ]
27 [ + - ]: 86 : toolbar->setContentsMargins(6, 2, 6, 2);
28 [ + - ]: 86 : toolbar->setSpacing(4);
29 : :
30 [ + - + - : 86 : auto *backBtn = m_backBtn = new QToolButton(this);
- + - - ]
31 [ + - + - ]: 86 : backBtn->setText(tr("← Back"));
32 [ + - + - ]: 86 : backBtn->setToolTip(tr("Back to message"));
33 [ + - ]: 86 : backBtn->setAutoRaise(true);
34 [ + - ]: 86 : connect(backBtn, &QToolButton::clicked, this, &PdfViewerWidget::backRequested);
35 [ + - ]: 86 : toolbar->addWidget(backBtn);
36 : :
37 [ + - + - : 86 : auto *saveBtn = m_saveBtn = new QToolButton(this);
- + - - ]
38 [ + - + - ]: 86 : saveBtn->setText(tr("Save…"));
39 [ + - + - ]: 86 : saveBtn->setToolTip(tr("Save PDF to disk"));
40 [ + - ]: 86 : saveBtn->setAutoRaise(true);
41 [ + - ]: 86 : connect(saveBtn, &QToolButton::clicked, this, [this]() {
42 [ # # ]: 0 : if (m_currentData.isEmpty()) return;
43 : : const QString suggested =
44 [ # # ]: 0 : m_currentFilename.isEmpty() ? QStringLiteral("document.pdf")
45 [ # # ]: 0 : : m_currentFilename;
46 : 0 : const QString path = QFileDialog::getSaveFileName(
47 [ # # # # ]: 0 : this, tr("Save Attachment"), suggested);
48 [ # # ]: 0 : if (path.isEmpty()) return;
49 [ # # ]: 0 : QSaveFile file(path);
50 [ # # # # ]: 0 : if (!file.open(QIODevice::WriteOnly)) {
51 [ # # # # ]: 0 : QMessageBox::warning(this, tr("Save Error"),
52 [ # # # # ]: 0 : tr("Could not write to %1.").arg(path));
53 : 0 : return;
54 : : }
55 [ # # ]: 0 : file.write(m_currentData);
56 [ # # # # ]: 0 : if (!file.commit()) {
57 [ # # # # ]: 0 : QMessageBox::warning(this, tr("Save Error"),
58 [ # # # # ]: 0 : tr("Could not save %1.").arg(path));
59 : : }
60 [ # # # # : 0 : });
# # ]
61 [ + - ]: 86 : toolbar->addWidget(saveBtn);
62 : :
63 [ + - ]: 86 : toolbar->addStretch();
64 : :
65 : : // Zoom controls.
66 [ + - + - : 86 : auto *zoomOutBtn = m_zoomOutBtn = new QToolButton(this);
- + - - ]
67 [ + - ]: 172 : zoomOutBtn->setText(QStringLiteral("−"));
68 [ + - + - ]: 86 : zoomOutBtn->setToolTip(tr("Zoom out"));
69 [ + - ]: 86 : zoomOutBtn->setAutoRaise(true);
70 : 86 : connect(zoomOutBtn, &QToolButton::clicked, this,
71 [ + - ]: 86 : [this]() { changeZoom(-25); });
72 [ + - ]: 86 : toolbar->addWidget(zoomOutBtn);
73 : :
74 [ + - + - : 172 : m_zoomLabel = new QLabel(QStringLiteral("100%"), this);
- + - - ]
75 [ + - ]: 86 : m_zoomLabel->setMinimumWidth(48);
76 [ + - ]: 86 : m_zoomLabel->setAlignment(Qt::AlignCenter);
77 [ + - ]: 86 : toolbar->addWidget(m_zoomLabel);
78 : :
79 [ + - + - : 86 : auto *zoomInBtn = m_zoomInBtn = new QToolButton(this);
- + - - ]
80 [ + - ]: 172 : zoomInBtn->setText(QStringLiteral("+"));
81 [ + - + - ]: 86 : zoomInBtn->setToolTip(tr("Zoom in"));
82 [ + - ]: 86 : zoomInBtn->setAutoRaise(true);
83 : 86 : connect(zoomInBtn, &QToolButton::clicked, this,
84 [ + - ]: 86 : [this]() { changeZoom(25); });
85 [ + - ]: 86 : toolbar->addWidget(zoomInBtn);
86 : :
87 [ + - ]: 86 : layout->addLayout(toolbar);
88 : :
89 : : // T-71.6: QPdfView/QPdfDocument are created lazily in ensureView() on the
90 : : // first load() call. This keeps MailView construction (and thus every mail
91 : : // open) fast — no PDF engine init until a PDF is actually viewed.
92 : 86 : }
93 : :
94 : 68 : PdfViewerWidget::~PdfViewerWidget() = default;
95 : :
96 : 1765 : void PdfViewerWidget::changeEvent(QEvent *event) {
97 [ + + ]: 1765 : if (event->type() == QEvent::LanguageChange)
98 : 90 : retranslateUi();
99 : 1765 : QWidget::changeEvent(event);
100 : 1765 : }
101 : :
102 : 90 : void PdfViewerWidget::retranslateUi() {
103 [ + - + - ]: 90 : m_backBtn->setText(tr("← Back"));
104 [ + - + - ]: 90 : m_backBtn->setToolTip(tr("Back to message"));
105 [ + - + - ]: 90 : m_saveBtn->setText(tr("Save…"));
106 [ + - + - ]: 90 : m_saveBtn->setToolTip(tr("Save PDF to disk"));
107 [ + - + - ]: 90 : m_zoomOutBtn->setToolTip(tr("Zoom out"));
108 [ + - + - ]: 90 : m_zoomInBtn->setToolTip(tr("Zoom in"));
109 : 90 : }
110 : :
111 : 0 : void PdfViewerWidget::ensureView() {
112 [ # # ]: 0 : if (m_pdfView) return;
113 : :
114 [ # # # # : 0 : m_document = new QPdfDocument(this);
# # ]
115 [ # # # # : 0 : m_pdfView = new QPdfView(this);
# # ]
116 : 0 : m_pdfView->setDocument(m_document);
117 : 0 : m_pdfView->setZoomMode(QPdfView::ZoomMode::Custom);
118 : 0 : m_pdfView->setZoomFactor(1.0);
119 [ # # ]: 0 : qobject_cast<QVBoxLayout *>(layout())->addWidget(m_pdfView, 1);
120 : : }
121 : :
122 : 0 : void PdfViewerWidget::load(const QByteArray &data,
123 : : const QString &filename) {
124 : 0 : ensureView();
125 : 0 : m_currentData = data;
126 : 0 : m_currentFilename = filename;
127 : 0 : setZoomDisplay(100);
128 : 0 : m_pdfView->setZoomFactor(1.0);
129 : :
130 : : // Write to a temp file with .pdf extension (QPdfDocument::load takes a
131 : : // file path, not a byte array).
132 [ # # ]: 0 : delete m_tempFile;
133 [ # # ]: 0 : m_tempFile = new QTemporaryFile(
134 [ # # # # ]: 0 : QDir::temp().absoluteFilePath(QStringLiteral("mailjd_pdf_XXXXXX.pdf")),
135 [ # # # # ]: 0 : this);
136 [ # # ]: 0 : if (!m_tempFile->open()) {
137 [ # # # # : 0 : qCWarning(lcPdfViewer) << "cannot create temp file";
# # # # ]
138 : 0 : return;
139 : : }
140 : 0 : m_tempFile->write(data);
141 : 0 : m_tempFile->close();
142 : :
143 [ # # # # ]: 0 : m_document->load(m_tempFile->fileName());
144 : : }
145 : :
146 : 0 : void PdfViewerWidget::clear() {
147 : 0 : m_currentData.clear();
148 : 0 : m_currentFilename.clear();
149 : 0 : setZoomDisplay(100);
150 [ # # ]: 0 : if (m_pdfView) m_pdfView->setZoomFactor(1.0);
151 [ # # ]: 0 : if (m_document) m_document->close();
152 [ # # ]: 0 : delete m_tempFile;
153 : 0 : m_tempFile = nullptr;
154 : 0 : }
155 : :
156 : 0 : void PdfViewerWidget::setZoomDisplay(int percent) {
157 : 0 : m_zoomPercent = percent;
158 [ # # # # ]: 0 : m_zoomLabel->setText(QStringLiteral("%1%").arg(percent));
159 : 0 : }
160 : :
161 : 0 : void PdfViewerWidget::changeZoom(int deltaPercent) {
162 : 0 : int next = m_zoomPercent + deltaPercent;
163 [ # # ]: 0 : next = qBound(25, next, 500);
164 [ # # ]: 0 : if (next == m_zoomPercent) return;
165 [ # # ]: 0 : setZoomDisplay(next);
166 [ # # ]: 0 : m_pdfView->setZoomFactor(next / 100.0);
167 : : }
|