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