1 // For license of this file, see <project-root-folder>/LICENSE.md.
2
3 #include "gui/reusable/labelsmenu.h"
4
5 #include "3rd-party/boolinq/boolinq.h"
6 #include "database/databasequeries.h"
7 #include "miscellaneous/application.h"
8 #include "miscellaneous/iconfactory.h"
9
10 #include <QCheckBox>
11 #include <QKeyEvent>
12 #include <QPainter>
13
LabelsMenu(const QList<Message> & messages,const QList<Label * > & labels,QWidget * parent)14 LabelsMenu::LabelsMenu(const QList<Message>& messages, const QList<Label*>& labels, QWidget* parent)
15 : NonClosableMenu(tr("Labels"), parent), m_messages(messages) {
16 setIcon(qApp->icons()->fromTheme(QSL("tag-folder")));
17
18 if (labels.isEmpty()) {
19 QAction* act_not_labels = new QAction(tr("No labels found"));
20
21 act_not_labels->setEnabled(false);
22 addAction(act_not_labels);
23 }
24 else {
25 QSqlDatabase db = qApp->database()->driver()->connection(QSL("LabelsMenu"));
26
27 for (Label* label: boolinq::from(labels).orderBy([](const Label* label) {
28 return label->title().toLower();
29 }).toStdList()) {
30
31 auto count = boolinq::from(messages).count([&db, label](const Message& msg) {
32 return DatabaseQueries::isLabelAssignedToMessage(db, label, msg);
33 });
34
35 Qt::CheckState state = Qt::CheckState::Unchecked;
36
37 if (count == messages.size()) {
38 state = Qt::CheckState::Checked;
39 }
40 else if (count > 0) {
41 state = Qt::CheckState::PartiallyChecked;
42 }
43
44 addLabelAction(label, state);
45 }
46 }
47 }
48
keyPressEvent(QKeyEvent * event)49 void LabelsMenu::keyPressEvent(QKeyEvent* event) {
50 LabelAction* act = qobject_cast<LabelAction*>(activeAction());
51
52 if (act != nullptr && event->key() == Qt::Key::Key_Space) {
53 act->toggleCheckState();
54 }
55
56 NonClosableMenu::keyPressEvent(event);
57 }
58
mousePressEvent(QMouseEvent * event)59 void LabelsMenu::mousePressEvent(QMouseEvent* event) {
60 LabelAction* act = qobject_cast<LabelAction*>(activeAction());
61
62 if (act != nullptr) {
63 act->toggleCheckState();
64 }
65 else {
66 NonClosableMenu::mousePressEvent(event);
67 }
68 }
69
changeLabelAssignment(Qt::CheckState state)70 void LabelsMenu::changeLabelAssignment(Qt::CheckState state) {
71 LabelAction* origin = qobject_cast<LabelAction*>(sender());
72
73 if (origin != nullptr) {
74 if (state == Qt::CheckState::Checked) {
75 // Assign this label to selected messages.
76 for (const auto& msg : qAsConst(m_messages)) {
77 origin->label()->assignToMessage(msg);
78 }
79 }
80 else if (state == Qt::CheckState::Unchecked) {
81 // Remove label from selected messages.
82 for (const auto& msg : qAsConst(m_messages)) {
83 origin->label()->deassignFromMessage(msg);
84 }
85 }
86 }
87
88 emit labelsChanged();
89 }
90
addLabelAction(Label * label,Qt::CheckState state)91 void LabelsMenu::addLabelAction(Label* label, Qt::CheckState state) {
92 auto* act = new LabelAction(label, this, this);
93
94 act->setCheckState(state);
95 addAction(act);
96
97 connect(act, &LabelAction::checkStateChanged, this, &LabelsMenu::changeLabelAssignment);
98 }
99
LabelAction(Label * label,QWidget * parent_widget,QObject * parent)100 LabelAction::LabelAction(Label* label, QWidget* parent_widget, QObject* parent)
101 : QAction(parent), m_label(label), m_parentWidget(parent_widget), m_checkState(Qt::CheckState::Unchecked) {
102 setText(label->title());
103 setIconVisibleInMenu(true);
104 setIcon(label->icon());
105
106 connect(this, &LabelAction::checkStateChanged, this, &LabelAction::updateActionForState);
107 updateActionForState();
108 }
109
checkState() const110 Qt::CheckState LabelAction::checkState() const {
111 return m_checkState;
112 }
113
setCheckState(Qt::CheckState state)114 void LabelAction::setCheckState(Qt::CheckState state) {
115 if (state != m_checkState) {
116 m_checkState = state;
117 emit checkStateChanged(m_checkState);
118 }
119 }
120
label() const121 Label* LabelAction::label() const {
122 return m_label;
123 }
124
toggleCheckState()125 void LabelAction::toggleCheckState() {
126 if (m_checkState == Qt::CheckState::Unchecked) {
127 setCheckState(Qt::CheckState::Checked);
128 }
129 else {
130 setCheckState(Qt::CheckState::Unchecked);
131 }
132 }
133
updateActionForState()134 void LabelAction::updateActionForState() {
135 QColor highlight;
136
137 switch (m_checkState) {
138 case Qt::CheckState::Checked:
139 highlight = Qt::GlobalColor::green;
140 break;
141
142 case Qt::CheckState::PartiallyChecked:
143 highlight = QColor(100, 50, 0);
144 break;
145
146 case Qt::CheckState::Unchecked:
147 default:
148 highlight = Qt::GlobalColor::transparent;
149 break;
150 }
151
152 QPixmap copy_icon(m_label->icon().pixmap(48, 48));
153
154 if (m_checkState != Qt::CheckState::Unchecked) {
155 QPainter paint(©_icon);
156
157 paint.setPen(QPen(Qt::GlobalColor::black, 4.0f));
158 paint.setBrush(highlight);
159 paint.drawRect(0, 0, 22, 22);
160 }
161
162 setIcon(copy_icon);
163 }
164