Tempo
Tempo is an app that allows users to create and manage timers, stopwatches, and alarms.
Menu.cpp
Go to the documentation of this file.
1 #include "../include/Menu.hpp"
2 
3 using namespace std;
4 
5 const double LOOPTIME = 0.01;
6 
7 const int NUM_FONTS = 4;
8 
9 /* =========================================================
10 CONSTRUCTOR
11 ========================================================= */
12 
16 Menu::Menu(bool testing){
17  srand(static_cast<unsigned int>(chrono::high_resolution_clock::now().time_since_epoch().count()));
18  _display = Display();
19  _run = true;
20  _testing = testing;
21  _menuFormats = { Display::BOLD_WHITE, Display::BOLD_WHITE, Display::BOLD_WHITE, Display::BOLD_WHITE, Display::BOLD_WHITE };
22  _settingsFormats = { Display::BOLD_WHITE, Display::BOLD_WHITE, Display::BOLD_WHITE, Display::BOLD_WHITE};
23 
24  _notiSetting = true;
25  _fontSetting = 1;
26 
27  loadSettings();
28 
29  _display.setFont(_fontSetting);
30 
31  _display.clearScreen();
32  mainMenu(0);
33 }
34 
35 /* =========================================================
36 HELPER FUNCTIONS
37 ========================================================= */
38 
45 void Menu::printArt(vector<string> art, string formatting){
46  _display.setFormat(formatting);
47 
48  for (string line : art){
49  cout << line << endl;
50  }
51 
52  _display.clearFormat();
53 }
54 
59  _display.clearScreen();
60 
61  string lBuffer = " ";
62 
63  printArt(_logoArt, Display::BOLD_CYAN);
64 
65  _display.setFormat(Display::WHITE);
66  cout << endl;
67  for (string line : _credits){
68  cout << lBuffer << line << endl;
69  }
70  _display.clearFormat();
71 
72  _display.setCursor(1,1);
73 
74  waitForInput();
75 
76  _display.clearScreen();
77 }
78 
86  ifstream settingsFile("settings.txt");
87 
88  if (settingsFile.is_open()) {
89  settingsFile >> _fontSetting;
90  settingsFile >> _notiSetting;
91  settingsFile.close();
92  } else {
93  // Default settings if the file doesn't exist
94  _fontSetting = 1;
95  _notiSetting = true;
96  }
97 }
98 
106  ofstream settingsFile("settings.txt");
107 
108  if (settingsFile.is_open()) {
109  settingsFile << _fontSetting << endl;
110  settingsFile << _notiSetting << endl;
111  settingsFile.close();
112  }
113 }
114 
115 /* =========================================================
116 MAIN MENU SEQUENCE
117 ========================================================= */
118 
127 void Menu::mainMenu(int selected) {
128 
129  for (int i = 0; i < _menuFormats.size(); ++i) {
130  if (i == selected) {
131  _menuFormats.at(i) = Display::SELECTED;
132  } else {
133  _menuFormats.at(i) = Display::BOLD_WHITE;
134  }
135  }
136 
137  _display.setCursor(1,1);
138 
139  printArt(_menuArt, Display::BOLD_CYAN);
140 
141  printArt(_menuTimer, _menuFormats.at(0));
142 
143  printArt(_menuStopwatch, _menuFormats.at(1));
144 
145  printArt(_menuAlarm, _menuFormats.at(2));
146 
147  printArt(_menuSettings, _menuFormats.at(3));
148 
149  printArt(_menuQuit, _menuFormats.at(4));
150 
151  _display.setCursor(1,1);
152 
153  char in = getMenuInput(selected);
154 
155  if(_testing){ in = '1'; }
156  // ENTER TIMER SEQUENCE
157  if(in == '1'){
158  timerSequence();
159  _display.clearScreen();
160  }
161 
162  if(_testing){ in = '2'; }
163  // ENTER STOPWATCH SEQUENCE
164  if(in == '2'){
165  stopwatchSequence();
166  _display.clearScreen();
167  }
168 
169  if(_testing){ in = '3'; }
170  // ENTER ALARM SEQUENCE
171  if(in == '3'){
172  alarmSequence();
173  _display.clearScreen();
174  }
175 
176  if(_testing){ in = '4'; }
177  // ENTER SETTINGS SEQUENCE
178  if(in == 's' || in == '4'){
179  _display.clearScreen();
180  settingsMenu(0);
181  _display.clearScreen();
182  }
183 
184  if(_testing){ in = '5'; }
185  // QUIT PROGRAM
186  if(in == 'q' || in == '5'){
187  _display.clearScreen();
188  return;
189  }
190 
191  // Restart menu loop
192  mainMenu(selected);
193 }
194 
202 void Menu::settingsMenu(int selected) {
203 
204  for (int i = 0; i < _settingsFormats.size(); ++i) {
205  if (i == selected) {
206  _settingsFormats.at(i) = Display::SELECTED;
207  } else {
208  _settingsFormats.at(i) = Display::BOLD_WHITE;
209  }
210  }
211 
212  _display.setCursor(1,1);
213 
214  printArt(_settingsArt, Display::BOLD_CYAN);
215 
216  switch(_fontSetting){
217  case 1:
218  printArt(_settingsFont1, _settingsFormats.at(0));
219  break;
220  case 2:
221  printArt(_settingsFont2, _settingsFormats.at(0));
222  break;
223  case 3:
224  printArt(_settingsFont3, _settingsFormats.at(0));
225  break;
226  case 4:
227  printArt(_settingsFont4, _settingsFormats.at(0));
228  break;
229  }
230 
231  if(_notiSetting){
232  printArt(_settingsNotiOn, _settingsFormats.at(1));
233  }
234  else{
235  printArt(_settingsNotiOff, _settingsFormats.at(1));
236  }
237 
238  printArt(_settingsCredits, _settingsFormats.at(2));
239 
240  printArt(_settingsBack, _settingsFormats.at(3));
241 
242  _display.setCursor(1,1);
243 
244  char in = getSettingsInput(selected);
245 
246  if(_testing){ in = '1'; }
247  if(in == '1'){
248  //Rotate fonts
249  if(_fontSetting < NUM_FONTS){
250  _fontSetting++;
251  }
252  else{
253  _fontSetting = 1;
254  }
255  saveSettings();
256  }
257 
258  if(_testing){ in = '2'; }
259  if(in == '2'){
260  //Toggle notifications
261  _notiSetting = !_notiSetting;
262  saveSettings();
263  }
264 
265  if(_testing){ in = '3'; }
266  if(in == '3'){
267  credits();
268  }
269 
270  if(_testing){ in = '4'; }
271  if(in == 'q' || in == '4' ){
272  saveSettings();
273  _display.setFont(_fontSetting);
274  return;
275  }
276 
277  //printArt(_settingsBlank, "");
278  settingsMenu(selected);
279 }
280 
285  _display.clearScreen();
286 
287  bool run = true;
288  bool notified = false;
289 
290  Timer timer = createTimer(run);
291 
292  _display.clearScreen();
293 
294  while(run){
295  // Display a message when the timer finishes
296  if(timer.remainingMilliseconds() == 0) {
297  _display.setSplash("TIMER FINISHED");
298  if(_notiSetting && !notified){
299  Notification noti("Tempo", "Your timer is finished!");
300  notified = true;
301  }
302  }
303 
304  _display.tickTimer(timer);
305 
306  checkTimerInput(timer, run);
307 
308  wait(LOOPTIME);
309 
310  if(_testing){
311  run = false;
312  }
313  }
314 }
315 
320 
321  _display.clearScreen();
322 
323  Stopwatch stopwatch;
324 
325  bool run = true;
326 
327  _display.setSplash("PRESS 'S' TO START");
328 
329  _display.tickStopwatch(stopwatch);
330 
331  //_display.clearScreen();
332 
333  while(run){
334  _display.tickStopwatch(stopwatch);
335 
336  checkStopwatchInput(stopwatch, run);
337 
338  wait(LOOPTIME);
339 
340  if(_testing){
341  run = false;
342  }
343  }
344 }
345 
350  _display.clearScreen();
351 
352  bool run = true;
353  bool notified = false;
354 
355  Alarm alarm = createAlarm(run);
356 
357  _display.clearScreen();
358 
359  while(run){
360  // Display a message when the alarm finishes
361  if(alarm.isDone()) {
362  _display.setSplash("ALARM FINISHED");
363  if(_notiSetting && !notified){
364  Notification noti("Tempo", "Your alarm is finished!");
365  notified = true;
366  }
367  }
368 
369  _display.tickAlarm(alarm);
370 
371  checkAlarmInput(alarm, run);
372 
373  wait(LOOPTIME);
374 
375  if(_testing){
376  run = false;
377  }
378  }
379 }
380 
387  int h = 0;
388  int m = 0;
389  int s = 0;
390  int countdownSeconds = 0;
391 
392  string input = "000000";
393 
394  string to_print = "00:00:00";
395 
396  _display.tickTimerSetup(to_print);
397 
398  int index = 5;
399 
400  while (true) {
401  char ch;
402  if(_testing){
403  ch = '1';
404  }
405  else if(_kbhit()){
406  ch = _getch();
407  }
408  else{
409  ch = 0;
410  }
411 
412  if (ch == 13 && (h || m || s)) {
413  break;
414  }
415  else if (ch >= '0' && ch <= '9') {
416  input = input.substr(1) + ch;
417  }
418  else if (ch == 8) {
419  input = '0' + input.substr(0, 5);
420  }
421 
422  if(ch == 'q' || ch == 'Q' || ch == 27){
423  run = false;
424  return Timer(0,0,0);
425  }
426 
427  h = stoi(input.substr(0, 2));
428  m = stoi(input.substr(2, 2));
429  s = stoi(input.substr(4, 2));
430 
431  to_print = (h < 10 ? "0" : "") + to_string(h) + ":"
432  + (m < 10 ? "0" : "") + to_string(m) + ":"
433  + (s < 10 ? "0" : "") + to_string(s);
434 
435  _display.tickTimerSetup(to_print);
436 
437  wait(LOOPTIME);
438 
439  if(_testing){
440  break;
441  }
442  }
443  _display.clearScreen();
444  Timer timer(h,m,s);
445  return timer;
446 }
447 
454  int h = 0;
455  int m = 0;
456 
457  string input = "0000";
458 
459  string to_print = "00:00";
460 
461  bool isAM = true;
462 
463  _display.tickAlarmSetup(to_print, isAM);
464 
465  int index = 3;
466 
467 
468  while (true) {
469  char ch;
470 
471  if(_testing){
472  ch = '1';
473  }
474 
475  else if(_kbhit()){
476  ch = _getch();
477 
478  if(ch == -32 || ch == 224 || ch == 0){
479  ch = getch();
480  switch(ch){
481  case 75:
482  isAM = 1;
483  break;
484  case 77:
485  isAM = 0;
486  break;
487  default:
488  break;
489  }
490  }
491  }
492 
493  else{
494  ch = 0;
495  }
496 
497  if (ch == 13 && h) {
498  break;
499  }
500  else if (ch >= '0' && ch <= '9') {
501  input = input.substr(1) + ch;
502  }
503  else if (ch == 8) {
504  input = '0' + input.substr(0, 3);
505  }
506 
507  if(ch == 'q' || ch == 'Q' || ch == 27){
508  run = false;
509  return Alarm(0,0);
510  }
511 
512  h = stoi(input.substr(0, 2));
513  m = stoi(input.substr(2, 2));
514 
515  if(h > 12) {
516  h = 12;
517  }
518 
519  if(h && m > 59) {
520  m = 59;
521  }
522 
523  to_print = (h < 10 ? "0" : "") + to_string(h) + ":"
524  + (m < 10 ? "0" : "") + to_string(m);
525 
526  input = to_print.substr(0, 2) + to_print.substr(3, 2);
527 
528  _display.tickAlarmSetup(to_print, isAM);
529 
530  wait(LOOPTIME);
531 
532  if(_testing){
533  break;
534  }
535  }
536  _display.clearScreen();
537  Alarm alarm(0,0);
538 
539  if(isAM){
540  if(h == 12){
541  alarm = Alarm(0,m);
542  }
543  else{
544  alarm = Alarm(h,m);
545  }
546  }
547  else{
548  if(h == 12){
549  alarm = Alarm(12,m);
550  }
551  else{
552  alarm = Alarm(h+12,m);
553  }
554  }
555  return alarm;
556 }
557 
566 void Menu::wait(double seconds) {
567  int milli = 1000 * seconds;
568  this_thread::sleep_for(chrono::milliseconds(milli));
569 }
570 
571 /* =========================================================
572 WAIT FOR ANY USER INPUT
573 ========================================================= */
574 
581  if(_testing){
582  return;
583  }
584  _getch();
585 }
586 
587 /* =========================================================
588 HANDLE KEYBOARD INPUT FOR TIMER
589 ========================================================= */
590 
599 void Menu::checkTimerInput(Timer& timer, bool& run){
600  if(_kbhit() || _testing){
601  char ch;
602  if(!_testing) { ch = _getch(); }
603  if(_testing) { ch = 's'; }
604  if(ch == 's' || ch == 'S'){
605  if(timer.isRunning()){
606  timer.pause();
607  _display.setSplash("TIMER PAUSED");
608  }
609  else if(timer.remainingMilliseconds() > 0){
610  _display.clearSplash();
611  timer.resume();
612  }
613  }
614  if(_testing) { ch = 'r'; }
615  if(ch == 'R' || ch == 'r'){
616  timer.reset();
617  _display.clearScreen();
618  _display.setSplash("TIMER RESET, PRESS 'S' TO START");
619  }
620  if(_testing) { ch = 'a'; }
621  if(ch == 'A' || ch == 'a'){
622  _display.clearScreen();
623  _display.clearSplash();
624  timer = createTimer(run);
625  }
626  if(_testing) { ch = 'q'; }
627  if(ch == 'Q' || ch == 'q' || ch == 27){
628  _display.clearSplash();
629  run = false;
630  return;
631  }
632  }
633 }
634 
635 /* =========================================================
636 HANDLE KEYBOARD INPUT FOR STOPWATCH
637 ========================================================= */
638 
647 void Menu::checkStopwatchInput(Stopwatch& stopwatch, bool& run){
648  if(_kbhit() || _testing){
649  char ch;
650  if(!_testing) { ch = _getch(); }
651  if(_testing) { ch = 's'; }
652  if(ch == 's' || ch == 'S'){
653  if(stopwatch.isRunning()){
654  stopwatch.pause();
655  if ((stopwatch.currentMilliseconds() / 10) % 100 == 0){
656  int i = rand() % _stopMessages.size();
657  _display.setSplash(_stopMessages[i]);
658  }
659  else{
660  _display.setSplash("STOPWATCH PAUSED");
661  }
662  }
663  else{
664  _display.clearSplash();
665  stopwatch.resume();
666  }
667  }
668  if(_testing) { ch = 'r'; }
669  if(ch == 'R' || ch == 'r'){
670  stopwatch.reset();
671  _display.clearScreen();
672  _display.setSplash("STOPWATCH RESET, PRESS 'S' TO START");
673  }
674  if(_testing) { ch = 'a'; }
675  if(ch == 'A' || ch == 'a'){
676  stopwatch.addSplit();
677  _display.setSplash("SPLIT CREATED");
678  }
679  if(_testing) { ch = 'q'; }
680  if(ch == 'Q' || ch == 'q' || ch == 27){
681  _display.clearSplash();
682  run = false;
683  return;
684  }
685  return;
686  }
687 }
688 
689 /* =========================================================
690 HANDLE KEYBOARD INPUT FOR ALARM
691 ========================================================= */
692 
701 void Menu::checkAlarmInput(Alarm& alarm, bool& run){
702  if(_kbhit() || _testing){
703  char ch;
704  if(!_testing) { ch = _getch(); }
705  if(_testing) { ch = 'a'; }
706  if(ch == 'A' || ch == 'a'){
707  _display.clearScreen();
708  _display.clearSplash();
709  alarm = createAlarm(run);
710  }
711  if(_testing) { ch = 'q'; }
712  if(ch == 'Q' || ch == 'q' || ch == 27){
713  _display.clearSplash();
714  run = false;
715  return;
716  }
717  return;
718  }
719 }
720 /* =========================================================
721 GET USER INPUT FOR MENU SELECTION
722 ========================================================= */
723 
732 char Menu::getMenuInput(int& selected){
733  while(true){
734  if(_kbhit() || _testing){
735  char ch = -1;
736  if(!_testing) { ch = _getch(); }
737 
738  if(ch == 13){
739  return selected + '1';
740  }
741 
742  if(ch == -32 || ch == 224 || ch == 0){
743  ch = getch();
744  switch(ch){
745  case 72:
746  selected = max(0,selected-1);
747  break;
748  case 80:
749  selected = min(4,selected+1);
750  break;
751  default:
752  break;
753  }
754  return '0';
755  }
756 
757  else{
758  if(_testing) { ch = 27; }
759  switch(ch){
760  case '1':
761  case '2':
762  case '3':
763  selected = ch - '1';
764  return ch;
765  case 'S':
766  case 's':
767  selected = 3;
768  return 's';
769  case 'Q':
770  case 'q':
771  case 27:
772  selected = 4;
773  return 'q';
774  default:
775  break;
776  }
777  }
778 
779  }
780  wait(LOOPTIME); // Slight delay before checking input again
781  }
782 }
783 
789 char Menu::getSettingsInput(int& selected){
790  while(true){
791  if(_kbhit() || _testing){
792  char ch = -1;
793  if(!_testing) { ch = _getch(); }
794 
795  if(ch == 13){
796  printArt(_settingsBlank, "");
797  return selected + '1';
798  }
799 
800  if(ch == -32 || ch == 224 || ch == 0){
801  ch = getch();
802  switch(ch){
803  case 72:
804  selected = max(0,selected-1);
805  break;
806  case 80:
807  selected = min(3,selected+1);
808  break;
809  default:
810  break;
811  }
812  return '0';
813  }
814  else{
815  if(_testing) { ch = 27; }
816  switch(ch){
817  case '1':
818  case '2':
819  case '3':
820  selected = ch - '1';
821  return ch;
822  case 'Q':
823  case 'q':
824  case 27:
825  selected = 3;
826  return 'q';
827  default:
828  break;
829  }
830  }
831  }
832  wait(LOOPTIME); // Slight delay before checking input again
833  }
834 }
A class to manage alarms with time-based triggers.
Definition: Alarm.hpp:13
Menu(bool testing)
Constructs a Menu object.
Definition: Menu.cpp:16
void checkTimerInput(Timer &timer, bool &run)
Checks and handles user input for the Timer.
Definition: Menu.cpp:599
The Timer class provides a countdown timer with start, pause, resume, reset, and time adjustment func...
Definition: Timer.hpp:12
void addSplit()
Records and prints the current split time in milliseconds.
Definition: Stopwatch.cpp:64
void resume()
Resumes the timer from where it was paused by recalculating the end time.
Definition: Timer.cpp:65
The Stopwatch class provides a simple timer with start, pause, resume, and reset functionalities.
Definition: Stopwatch.hpp:13
char getMenuInput(int &selected)
Waits for the user to press a valid key (1 2 3 or Q) for menu selection.
Definition: Menu.cpp:732
int remainingMilliseconds()
Returns the remaining time in milliseconds.
Definition: Timer.cpp:118
void credits()
Displays the credits for the Timer Stopwatch and Alarm.
Definition: Menu.cpp:58
void checkAlarmInput(Alarm &alarm, bool &run)
Checks and handles user input for the Alarm.
Definition: Menu.cpp:701
void waitForInput()
Waits for any key press from the user.
Definition: Menu.cpp:580
bool isDone()
Checks if the alarm has finished.
Definition: Alarm.cpp:134
void mainMenu(int selected)
Starts the main menu loop allowing the user to choose between Timer Stopwatch and Alarm...
Definition: Menu.cpp:127
static const string BOLD_WHITE
Definition: Display.hpp:47
STL namespace.
void wait(double seconds)
Waits for a specific duration using busy-waiting.
Definition: Menu.cpp:566
void checkStopwatchInput(Stopwatch &stopwatch, bool &run)
Checks and handles user input for the Stopwatch.
Definition: Menu.cpp:647
const int NUM_FONTS
Definition: Menu.cpp:7
Timer createTimer(bool &run)
Creates a Timer object with user input for the duration.
Definition: Menu.cpp:386
void stopwatchSequence()
Displays the help menu for the Timer Stopwatch and Alarm.
Definition: Menu.cpp:319
void saveSettings()
Saves the notification and font settings to a file.
Definition: Menu.cpp:105
void pause()
Pauses the timer and calculates the remaining milliseconds.
Definition: Timer.cpp:50
static const string SELECTED
Definition: Display.hpp:27
void loadSettings()
Loads the notification and font settings from a file.
Definition: Menu.cpp:85
void reset()
Resets the stopwatch to 0 milliseconds and stops it.
Definition: Stopwatch.cpp:148
static const string WHITE
Definition: Display.hpp:38
void resume()
Resumes the stopwatch from the last paused time.
Definition: Stopwatch.cpp:134
void timerSequence()
Displays the help menu for the Timer Stopwatch and Alarm.
Definition: Menu.cpp:284
A class to handle system notifications using PowerShell scripts.
Alarm createAlarm(bool &run)
Creates a Alarm object with user input for the duration.
Definition: Menu.cpp:453
static const string BOLD_CYAN
Definition: Display.hpp:46
bool isRunning()
Checks if the timer is currently running.
Definition: Timer.cpp:143
int currentMilliseconds()
Returns the current elapsed time in milliseconds.
Definition: Stopwatch.cpp:44
const double LOOPTIME
Definition: Menu.cpp:5
void alarmSequence()
Displays the help menu for the Timer Stopwatch and Alarm.
Definition: Menu.cpp:349
void pause()
Pauses the stopwatch.
Definition: Stopwatch.cpp:119
void printArt(vector< string > art, string formatting)
Prints the Alarm&#39;s input duration to the console.
Definition: Menu.cpp:45
char getSettingsInput(int &selected)
Waits for the user to press a valid key (1 2 3 4 or Q) for settings selection.
Definition: Menu.cpp:789
void reset()
Resets the timer to its original duration and pauses it.
Definition: Timer.cpp:78
void settingsMenu(int selected)
Starts the settings menu loop allowing the user to change the notification and font settings...
Definition: Menu.cpp:202
The Display class manages the visual representation and interaction logic for timers, stopwatches, and alarms.
Definition: Display.hpp:23
bool isRunning()
Checks if the stopwatch is currently running.
Definition: Stopwatch.cpp:163