1#include "PomodoroButton.h"
2
3#include <GitQlientSettings.h>
4#include <GitBase.h>
5#include <PomodoroConfigDlg.h>
6
7#include <QTime>
8#include <QToolButton>
9#include <QLabel>
10#include <QMenu>
11#include <QVBoxLayout>
12#include <QMouseEvent>
13#include <QTimer>
14#include <QMessageBox>
15#include <QStyle>
16
17PomodoroButton::PomodoroButton(const QSharedPointer<GitBase> &git, QWidget *parent)
18 : QFrame(parent)
19 , mGit(git)
20 , mButton(new QToolButton())
21 , mArrow(new QToolButton())
22 , mCounter(new QLabel())
23 , mTimer(new QTimer(this))
24{
25 setContentsMargins(0, 0, 0, 0);
26 setToolTip(tr("Pomodoro"));
27
28 const auto menu = new QMenu(mButton);
29 menu->installEventFilter(this);
30 mConfigAction = menu->addAction(tr("Options..."));
31 connect(mConfigAction, &QAction::triggered, this, &PomodoroButton::showConfig);
32
33 mButton->setIcon(QIcon(":/icons/pomodoro"));
34 mButton->setIconSize(QSize(22, 22));
35 mButton->setToolButtonStyle(Qt::ToolButtonIconOnly);
36 mButton->setObjectName("ToolButtonAboveMenu");
37 connect(mButton, &QToolButton::clicked, this, &PomodoroButton::onClick);
38
39 mArrow->setObjectName("Arrow");
40 mArrow->setIcon(QIcon(":/icons/arrow_down"));
41 mArrow->setIconSize(QSize(10, 10));
42 mArrow->setToolButtonStyle(Qt::ToolButtonIconOnly);
43 mArrow->setToolTip(tr("Options"));
44 mArrow->setPopupMode(QToolButton::InstantPopup);
45 mArrow->setMenu(menu);
46 mArrow->setFixedWidth(10);
47 mArrow->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
48
49 GitQlientSettings settings(mGit->getGitDir());
50 const auto durationMins = settings.localValue("Pomodoro/Duration", 25).toInt();
51 mDurationTime = QTime(0, durationMins, 0);
52 mCounter->setText(mDurationTime.toString("mm:ss"));
53
54 const auto breakMins = settings.localValue("Pomodoro/Break", 5).toInt();
55 mBreakTime = QTime(0, breakMins, 0);
56
57 const auto longBreakMins = settings.localValue("Pomodoro/LongBreak", 15).toInt();
58 mLongBreakTime = QTime(0, longBreakMins, 0);
59
60 mBigBreakCount = settings.localValue("Pomodoro/LongBreakTrigger", 4).toInt();
61
62 mStopResets = settings.localValue("Pomodoro/StopResets", true).toBool();
63
64 mTimer->setInterval(1000);
65 connect(mTimer, &QTimer::timeout, this, &PomodoroButton::onTimeout);
66
67 const auto layout = new QGridLayout(this);
68 layout->setContentsMargins(QMargins());
69 layout->setSpacing(0);
70 layout->addWidget(mButton, 0, 0);
71 layout->addWidget(mCounter, 1, 0);
72 layout->addWidget(mArrow, 0, 1, 2, 1);
73}
74
75void PomodoroButton::setText(const QString &text)
76{
77 mCounter->setText(text);
78}
79
80void PomodoroButton::updateCounters()
81{
82 mState = State::OnHold;
83 mTimer->stop();
84 mButton->setIcon(QIcon(":/icons/pomodoro"));
85
86 GitQlientSettings settings(mGit->getGitDir());
87
88 const auto durationMins = settings.localValue("Pomodoro/Duration", 25).toInt();
89 mDurationTime = QTime(0, durationMins, 0);
90 mCounter->setText(mDurationTime.toString("mm:ss"));
91
92 const auto breakMins = settings.localValue("Pomodoro/Break", 5).toInt();
93 mBreakTime = QTime(0, breakMins, 0);
94
95 const auto longBreakMins = settings.localValue("Pomodoro/LongBreak", 15).toInt();
96 mLongBreakTime = QTime(0, longBreakMins, 0);
97
98 const auto longBreakTriggerCount = settings.localValue("Pomodoro/LongBreakTrigger", 4).toInt();
99
100 mStopResets = settings.localValue("Pomodoro/StopResets", true).toBool();
101
102 if (longBreakTriggerCount < mBigBreakOriginalValue)
103 {
104 mBigBreakOriginalValue = longBreakTriggerCount;
105 mBigBreakCount = longBreakTriggerCount;
106 }
107 else
108 {
109 mBigBreakCount = longBreakTriggerCount - (mBigBreakOriginalValue - mBigBreakCount);
110 mBigBreakOriginalValue = longBreakTriggerCount;
111 }
112}
113
114void PomodoroButton::setRunningMode()
115{
116 style()->unpolish(this);
117 setProperty("checked", true);
118 style()->polish(this);
119 mState = State::Running;
120 mTimer->start();
121 mButton->setIcon(QIcon(":/icons/pomodoro_running"));
122 --mBigBreakCount;
123}
124
125void PomodoroButton::mousePressEvent(QMouseEvent *e)
126{
127 if (isEnabled())
128 mPressed = true;
129
130 QFrame::mousePressEvent(e);
131}
132
133void PomodoroButton::mouseReleaseEvent(QMouseEvent *e)
134{
135 if (isEnabled() && mPressed)
136 {
137 onClick();
138
139 emit clicked();
140 }
141
142 QFrame::mouseReleaseEvent(e);
143}
144
145bool PomodoroButton::eventFilter(QObject *obj, QEvent *event)
146{
147 if (const auto menu = qobject_cast<QMenu *>(obj); menu && event->type() == QEvent::Show)
148 {
149 auto localPos = mButton->pos();
150 localPos.setX(localPos.x());
151 auto pos = mapToGlobal(localPos);
152 menu->show();
153 pos.setY(pos.y() + height());
154 menu->move(pos);
155
156 return true;
157 }
158
159 return false;
160}
161
162void PomodoroButton::onTimeout()
163{
164 if (mState == State::Running)
165 onRunningMode();
166 else if (mState == State::InBreakRunning)
167 onBreakingMode();
168 else if (mState == State::InLongBreakRunning)
169 onLongBreakingMode();
170}
171
172void PomodoroButton::onClick()
173{
174 switch (mState)
175 {
176 case State::OnHold:
177 case State::Finished:
178 setRunningMode();
179 break;
180 case State::InBreak:
181 style()->unpolish(this);
182 setProperty("checked", true);
183 style()->polish(this);
184 mState = State::InBreakRunning;
185 mTimer->start();
186 break;
187 case State::InLongBreak:
188 style()->unpolish(this);
189 setProperty("checked", true);
190 style()->polish(this);
191 mState = State::InLongBreakRunning;
192 mTimer->start();
193 break;
194 case State::InBreakRunning:
195 case State::InLongBreakRunning:
196 setRunningMode();
197 break;
198 case State::Running:
199 style()->unpolish(this);
200 setProperty("checked", false);
201 style()->polish(this);
202 mState = State::OnHold;
203 mTimer->stop();
204 mButton->setIcon(QIcon(":/icons/pomodoro"));
205
206 if (mStopResets)
207 {
208 GitQlientSettings settings(mGit->getGitDir());
209 const auto durationMins = settings.localValue("Pomodoro/Duration", 25).toInt();
210 mDurationTime = QTime(0, durationMins, 0);
211 mCounter->setText(mDurationTime.toString("mm:ss"));
212 }
213
214 ++mBigBreakCount;
215 break;
216 }
217}
218
219void PomodoroButton::onRunningMode()
220{
221 mDurationTime = mDurationTime.addSecs(-1);
222 mCounter->setText(mDurationTime.toString("mm:ss"));
223
224 if (mDurationTime == QTime(0, 0, 0))
225 {
226 mTimer->stop();
227
228 GitQlientSettings settings(mGit->getGitDir());
229 const auto durationMins = settings.localValue("Pomodoro/Duration", 25).toInt();
230 mDurationTime = QTime(0, durationMins, 0);
231
232 mButton->setIcon(QIcon(":/icons/pomodoro_timeout"));
233
234 const auto breakMins = settings.localValue("Pomodoro/Break", 5).toInt();
235 mBreakTime = QTime(0, breakMins, 0);
236
237 const auto longBreakMins = settings.localValue("Pomodoro/LongBreak", 15).toInt();
238 mLongBreakTime = QTime(0, longBreakMins, 0);
239
240 if (mBigBreakCount <= 0)
241 {
242 mBigBreakCount = settings.localValue("Pomodoro/LongBreakTrigger", 4).toInt();
243
244 mCounter->setText(mLongBreakTime.toString("mm:ss"));
245 }
246 else
247 mCounter->setText(mBreakTime.toString("mm:ss"));
248
249 if (QMessageBox::question(this, tr("Time for a break!"), tr("It's time to do a break. Are you ready?"))
250 == QMessageBox::Yes)
251 {
252 if (mBigBreakCount == 0)
253 mState = State::InLongBreakRunning;
254 else
255 mState = State::InBreakRunning;
256
257 mTimer->start();
258 }
259 else
260 {
261 style()->unpolish(this);
262 setProperty("checked", false);
263 style()->polish(this);
264
265 if (mBigBreakCount == 0)
266 mState = State::InLongBreak;
267 else
268 mState = State::InBreak;
269 }
270 }
271}
272
273void PomodoroButton::onBreakingMode()
274{
275 mBreakTime = mBreakTime.addSecs(-1);
276 mCounter->setText(mBreakTime.toString("mm:ss"));
277
278 if (mBreakTime == QTime(0, 0, 0))
279 {
280 mTimer->stop();
281
282 GitQlientSettings settings(mGit->getGitDir());
283 const auto breakMins = settings.localValue("Pomodoro/Break", 5).toInt();
284 mBreakTime = QTime(0, breakMins, 0);
285
286 mState = State::Finished;
287
288 const auto answer
289 = QMessageBox::question(this, tr("Time to work!"), tr("It's time to go back to work. Are you ready?"));
290
291 if (answer == QMessageBox::Yes)
292 {
293 mCounter->setText(mDurationTime.toString("mm:ss"));
294 setRunningMode();
295 }
296 else
297 {
298 style()->unpolish(this);
299 setProperty("checked", false);
300 style()->polish(this);
301 }
302 }
303}
304
305void PomodoroButton::onLongBreakingMode()
306{
307 mLongBreakTime = mLongBreakTime.addSecs(-1);
308 mCounter->setText(mLongBreakTime.toString("mm:ss"));
309
310 if (mLongBreakTime == QTime(0, 0, 0))
311 {
312 mTimer->stop();
313
314 GitQlientSettings settings(mGit->getGitDir());
315 const auto breakMins = settings.localValue("Pomodoro/LongBreak", 15).toInt();
316 mLongBreakTime = QTime(0, breakMins, 0);
317
318 mState = State::Finished;
319
320 const auto answer
321 = QMessageBox::question(this, tr("Time to work!"), tr("It's time to go back to work. Are you ready?"));
322
323 if (answer == QMessageBox::Yes)
324 {
325 mCounter->setText(mDurationTime.toString("mm:ss"));
326 setRunningMode();
327 }
328 else
329 {
330 style()->unpolish(this);
331 setProperty("checked", false);
332 style()->polish(this);
333 }
334 }
335}
336
337void PomodoroButton::showConfig()
338{
339 const auto dlg = new PomodoroConfigDlg(mGit, this);
340
341 connect(dlg, &PomodoroConfigDlg::finished, this, [this](int result) {
342 if (result == QDialog::Accepted)
343 updateCounters();
344 });
345
346 dlg->open();
347}
348