Branch data Line data Source code
1 : : #include "AttachmentBar.h"
2 : :
3 : : #include <QEvent>
4 : : #include <QHBoxLayout>
5 : : #include <QIcon>
6 : : #include <QToolButton>
7 : :
8 [ + - ]: 98 : AttachmentBar::AttachmentBar(QWidget *parent) : QFrame(parent) {
9 [ + - ]: 196 : setObjectName(QStringLiteral("attachmentBar"));
10 [ + - + - : 98 : m_layout = new QHBoxLayout(this);
- + - - ]
11 [ + - ]: 98 : m_layout->setContentsMargins(4, 2, 4, 2);
12 [ + - ]: 98 : m_layout->setSpacing(4);
13 [ + - ]: 98 : setFrameShape(QFrame::NoFrame);
14 [ + - ]: 98 : setVisible(false);
15 : 98 : }
16 : :
17 : 86 : void AttachmentBar::setAttachments(const QList<Attachment> &attachments) {
18 : 86 : clear();
19 : 86 : m_attachments = attachments;
20 : :
21 [ + + ]: 86 : if (attachments.isEmpty()) {
22 : 72 : return;
23 : : }
24 : :
25 [ + + ]: 33 : for (const auto &att : attachments) {
26 [ + - + - : 19 : auto *btn = new QToolButton(this);
- + - - ]
27 : : QString label =
28 [ - + - - ]: 19 : att.filename.isEmpty() ? tr("(unnamed)") : att.filename;
29 [ + - + - : 38 : label += QStringLiteral(" (%1)").arg(formatSize(att.size));
+ - ]
30 [ + - ]: 19 : btn->setText(label);
31 [ + - ]: 19 : btn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
32 [ + - + - ]: 38 : btn->setIcon(QIcon::fromTheme(QStringLiteral("mail-attachment")));
33 [ + - ]: 19 : btn->setAutoRaise(true);
34 : :
35 : 19 : const qint64 id = att.id;
36 : 19 : const QString filename = att.filename;
37 : : // T-71.6: PDFs open inline on click; everything else downloads.
38 : : // Detection covers both a proper MIME type and a .pdf extension so
39 : : // mis-typed attachments still work.
40 : : const bool isPdf =
41 [ + - - - : 38 : att.contentType.startsWith(QStringLiteral("application/pdf"),
- - ]
42 [ + - + + ]: 51 : Qt::CaseInsensitive) ||
43 [ + - + + : 45 : att.filename.endsWith(QStringLiteral(".pdf"), Qt::CaseInsensitive);
+ + + + +
- - - -
- ]
44 : :
45 [ + + ]: 19 : if (isPdf) {
46 : 8 : connect(btn, &QToolButton::clicked, this,
47 [ + - ]: 19 : [this, id]() { emit viewInlineRequested(id); });
48 : : } else {
49 [ + - ]: 11 : connect(btn, &QToolButton::clicked, this,
50 : 22 : [this, id, filename]() {
51 : 1 : emit downloadRequested(id, filename);
52 : 1 : });
53 : : }
54 : :
55 [ + - ]: 19 : m_layout->addWidget(btn);
56 : :
57 : : // T-71.6: PDF chips get a small dedicated download button so users can
58 : : // still save the file (the main chip now opens the inline viewer).
59 [ + + ]: 19 : if (isPdf) {
60 [ + - + - : 8 : auto *dlBtn = new QToolButton(this);
- + - - ]
61 [ + - + - ]: 8 : dlBtn->setText(tr("Save"));
62 [ + - + - : 16 : dlBtn->setToolTip(tr("Save %1 to disk").arg(
+ - ]
63 [ - + - - ]: 16 : (att.filename.isEmpty() ? tr("attachment") : att.filename)
64 [ + - ]: 24 : .toHtmlEscaped()));
65 [ + - ]: 8 : dlBtn->setAutoRaise(true);
66 [ + - ]: 8 : connect(dlBtn, &QToolButton::clicked, this,
67 : 16 : [this, id, filename]() {
68 : 0 : emit downloadRequested(id, filename);
69 : 0 : });
70 [ + - ]: 8 : m_layout->addWidget(dlBtn);
71 : : }
72 : 19 : }
73 : :
74 : : // "Save All" button if multiple attachments
75 [ + + ]: 14 : if (attachments.size() > 1) {
76 : 3 : m_layout->addStretch();
77 [ + - - + : 3 : auto *allBtn = new QToolButton(this);
- - ]
78 [ + - + - ]: 3 : allBtn->setText(tr("Save All"));
79 [ + - + - ]: 6 : allBtn->setIcon(QIcon::fromTheme(QStringLiteral("document-save-all")));
80 : 3 : allBtn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
81 : 3 : allBtn->setAutoRaise(true);
82 : 3 : connect(allBtn, &QToolButton::clicked, this,
83 [ + - ]: 3 : &AttachmentBar::downloadAllRequested);
84 [ + - ]: 3 : m_layout->addWidget(allBtn);
85 : : } else {
86 : 11 : m_layout->addStretch();
87 : : }
88 : :
89 : 14 : setVisible(true);
90 : : }
91 : :
92 : 122 : void AttachmentBar::clear() {
93 : 122 : m_attachments.clear();
94 : :
95 : : // Remove all widgets from layout
96 : : QLayoutItem *item;
97 [ + + ]: 150 : while ((item = m_layout->takeAt(0)) != nullptr) {
98 [ + + ]: 28 : delete item->widget();
99 [ + - ]: 28 : delete item;
100 : : }
101 : 122 : setVisible(false);
102 : 122 : }
103 : :
104 : 19 : QString AttachmentBar::formatSize(qint64 bytes) {
105 [ + + ]: 19 : if (bytes < 1024) {
106 [ + - + - ]: 22 : return QString("%1 B").arg(bytes);
107 : : }
108 [ + + ]: 8 : if (bytes < 1024 * 1024) {
109 [ + - + - ]: 12 : return QString("%1 KB").arg(bytes / 1024.0, 0, 'f', 1);
110 : : }
111 [ + - ]: 2 : if (bytes < 1024LL * 1024 * 1024) {
112 [ + - + - ]: 4 : return QString("%1 MB").arg(bytes / (1024.0 * 1024.0), 0, 'f', 1);
113 : : }
114 [ # # # # ]: 0 : return QString("%1 GB").arg(bytes / (1024.0 * 1024.0 * 1024.0), 0, 'f', 1);
115 : : }
116 : :
117 : : // T-76.B3: Runtime language switching.
118 : : // The chips are built dynamically in setAttachments() (one Save button per
119 : : // PDF, a Save All button, "(unnamed)" fallback), so re-applying tr() means
120 : : // rebuilding. A local copy is made first — setAttachments() takes a const
121 : : // ref and clear() empties m_attachments, which would otherwise dangle it.
122 : 1578 : void AttachmentBar::changeEvent(QEvent *event) {
123 [ + + ]: 1578 : if (event->type() == QEvent::LanguageChange)
124 : 8 : retranslateUi();
125 : 1578 : QFrame::changeEvent(event);
126 : 1578 : }
127 : :
128 : 8 : void AttachmentBar::retranslateUi() {
129 : : // T-76.B3: setAttachments() rebuilds the widget tree (deletes/creates child
130 : : // widgets). Calling it from within changeEvent can re-enter if a child
131 : : // removal posts further events; guard against that.
132 [ + - + - : 8 : if (m_retranslating || m_attachments.isEmpty())
+ - ]
133 : 8 : return;
134 : 0 : m_retranslating = true;
135 : 0 : QList<Attachment> copy = m_attachments;
136 [ # # ]: 0 : setAttachments(copy);
137 : 0 : m_retranslating = false;
138 : 0 : }
|