Updated web pages due to 0.4.0 being released
[kitchenalert] / src / kitchenalertmainwindow.cpp
1 /**************************************************************************
2
3         KitchenAlert
4
5         Copyright (C) 2010-2011  Heli Hyvättinen
6
7         This file is part of KitchenAlert.
8
9         Kitchen Alert is free software: you can redistribute it and/or modify
10         it under the terms of the GNU General Public License as published by
11         the Free Software Foundation, either version 3 of the License, or
12         (at your option) any later version.
13
14         This program is distributed in the hope that it will be useful,
15         but WITHOUT ANY WARRANTY; without even the implied warranty of
16         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17         GNU General Public License for more details.
18
19         You should have received a copy of the GNU General Public License
20         along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
22 **************************************************************************/
23
24
25
26
27
28 #include "kitchenalertmainwindow.h"
29 #include "ui_kitchenalertmainwindow.h"
30
31 #include <QString>
32 #include <QList>
33
34
35 #include "createtimersequencedialog.h"
36 #include "selectsounddialog.h"
37
38 #include <QDebug>
39
40 #include <QAction>
41 #include <QMenuBar>
42 #include <QMessageBox>
43 #include <QSettings>
44 #include <QFileDialog>
45
46 #include <QStringListModel>
47 #include <QListView>
48 #include "stickydialog.h"
49
50
51 KitchenAlertMainWindow::KitchenAlertMainWindow(QWidget *parent) :
52     QMainWindow(parent),
53     ui(new Ui::KitchenAlertMainWindow)
54     {
55       
56   defaultSaveDirectory_ = "/home/user/KitchenAlert";    
57
58   ui->setupUi(this);
59
60   setWindowIcon(QIcon(":/kitchenalert.png"));
61
62   //alerts' tableview setup
63
64   ui->ComingAlertsTableView->setModel(&model_);
65   ui->ComingAlertsTableView->setSelectionMode(QAbstractItemView::SingleSelection);
66   ui->ComingAlertsTableView->setSelectionBehavior(QAbstractItemView::SelectRows);
67
68   ui->ComingAlertsTableView->horizontalHeader()->hide();
69 //  ui->ComingAlertsTableView->verticalHeader()->setVisible(true);
70
71   ui->ComingAlertsTableView->horizontalHeader()->setResizeMode(QHeaderView::Fixed);
72   ui->ComingAlertsTableView->horizontalHeader()->resizeSection(0,535);
73   ui->ComingAlertsTableView->horizontalHeader()->resizeSection(1,140);
74   ui->ComingAlertsTableView->horizontalHeader()->resizeSection(2,100);
75
76   ui->ComingAlertsTableView->verticalHeader()->setResizeMode(QHeaderView::ResizeToContents);
77
78
79   //Buttons used to reacting to an alarm are hidden by default
80
81   disableSelectionDependentButtons();
82
83
84   connect(ui->ComingAlertsTableView->selectionModel(),SIGNAL(selectionChanged(QItemSelection,QItemSelection)),this,SLOT(timerSelected(QItemSelection,QItemSelection)));
85   connect(ui->CreateNewScheduleButton, SIGNAL (clicked()), this, SLOT (newTimerSequence()));
86   connect(ui->DoneButton,SIGNAL(clicked()),this,SLOT(stop()));
87   connect(ui->RestartButton,SIGNAL(clicked()),this,SLOT(restart()));
88   connect(ui->SnoozeButton,SIGNAL(clicked()),this, SLOT(snooze()));
89   connect(ui->RemoveButton,SIGNAL(clicked()),this,SLOT(remove()));
90   connect(ui->SaveButton,SIGNAL(clicked()),this,SLOT(saveTimer()));
91   connect(ui->OpenButton,SIGNAL(clicked()),this,SLOT(loadTimer()));
92
93   // menu setup
94
95   QAction * p_SelectSoundAction = new QAction(tr("Select alert sound"),this);
96   connect(p_SelectSoundAction, SIGNAL(triggered()), this, SLOT(openSelectSoundDialog()));
97   menuBar()->addAction(p_SelectSoundAction);
98
99   QAction * p_AboutAction = new QAction(tr("About"),this);
100   connect(p_AboutAction,SIGNAL(triggered()),this,SLOT(openAbout()));
101   menuBar()->addAction(p_AboutAction);
102
103   QAction * p_StickyAction = new QAction(tr("Edit sticky timers"),this);
104   connect(p_StickyAction, SIGNAL(triggered()),this,SLOT(openStickyDialog()));
105   menuBar()->addAction(p_StickyAction);
106
107   QTimer::singleShot(0,this,SLOT(loadStickies())); //Load sticky timers after construction to speed the application showing up
108
109     }
110
111 KitchenAlertMainWindow::~KitchenAlertMainWindow()
112 {
113     delete ui;
114 }
115
116 void KitchenAlertMainWindow::changeEvent(QEvent *e)
117 {
118     QMainWindow::changeEvent(e);
119     switch (e->type()) {
120     case QEvent::LanguageChange:
121         ui->retranslateUi(this);
122         break;
123     default:
124         break;
125
126     }
127
128 }
129
130
131 void KitchenAlertMainWindow::newTimerSequence()
132 {
133     CreateTimerSequenceDialog createdialog;
134
135
136     if (createdialog.exec() == QDialog::Accepted) //if user pressed OK
137     {
138
139
140        QList<Timer *>  alltimers = createdialog.getTimers();  //get user input from the dialog
141
142        Timer* timer1 = alltimers.at(0); // take first timer (currently the only one!)
143
144
145
146        connect(timer1,SIGNAL(alert(QModelIndex)),this,SLOT(alert(QModelIndex)));
147
148
149
150        connect(this,SIGNAL(defaultSoundEnabled()),timer1,SLOT(enableDefaultSound()));
151        connect(this,SIGNAL(soundChanged(QString)),timer1,SLOT(changeAlertSound(QString)));
152
153
154
155         model_.addTimers(alltimers); // give timers to the model, they are started automatically by default
156
157  //       ui->ComingAlertsTableView->resizeColumnsToContents();
158
159
160         //Disable buttons, as selection is cleared when view is refreshed to show the new timer
161         //But only if the timer has not already alerted and thus been selected
162
163         if (!selectedRow().isValid())
164             disableSelectionDependentButtons();
165
166
167
168     }
169     // if cancelled, do nothing
170
171
172
173 }
174
175
176
177
178
179 void KitchenAlertMainWindow::alert(QModelIndex indexOfAlerter)
180 {
181
182     // The program is brought to front and activated when alerted
183
184
185     activateWindow();
186
187 // removing everything below does not solve the bug #6752!
188
189     raise();  //this may be unnecessary
190
191     // The alerting timer is selected
192     ui->ComingAlertsTableView->selectionModel()->select(QItemSelection(indexOfAlerter,indexOfAlerter),QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows );
193
194     //Scrolls the view so that the alerting timer is visible
195     ui->ComingAlertsTableView->scrollTo(indexOfAlerter);
196
197    // qDebug() << "Should be selected now";
198
199
200     //Snooze button is enabled
201
202
203     ui->SnoozeButton->setEnabled(true);
204 //qDebug ("Snooze on when alerting");
205
206 }
207
208
209 void KitchenAlertMainWindow::timerSelected(QItemSelection selected,QItemSelection)
210 {
211     ui->DoneButton->setEnabled(true);
212     ui->RestartButton->setEnabled(true);
213     ui->RemoveButton->setEnabled(true);
214     ui->SaveButton->setEnabled(true);
215
216
217     //snooze button enabled only when alerting
218     QModelIndexList indexes = selected.indexes();
219
220     //the selection model only allows selecting one row at the time & we only need to know the row, so we can just take the first one
221     QModelIndex index = indexes.value(0);
222     if (index.isValid())
223     {
224         if (model_.isThisTimerAlerting(index) == true)
225         {
226              ui->SnoozeButton->setEnabled(true);
227 //qDebug() << "Snooze on";
228         }
229         else
230         {
231             ui->SnoozeButton->setDisabled(true);
232 //qDebug() << "Snooze off";
233         }
234     }
235
236 }
237
238 void KitchenAlertMainWindow::snooze()
239 {
240     QModelIndex row = selectedRow();
241     if (row.isValid()) //If there was no row selected invalid row was returned
242     {
243         model_.snoozeTimer(row);
244     }
245     ui->SnoozeButton->setDisabled(true);
246
247
248 }
249
250 void KitchenAlertMainWindow::restart()
251 {
252     QModelIndex row = selectedRow(); //If there was no row selected invalid row was returned
253     if (row.isValid())
254     {
255
256         model_.startTimer(row);
257     }
258
259
260    if (model_.isThisTimerAlerting(row) == false) //This has to be checked, because 00:00:00 alerts alert *before* the program execution reaches here
261     {
262         ui->SnoozeButton->setDisabled(true);
263     }
264  //   qDebug () << "disabled snooze because of restart";
265
266
267 }
268
269 void KitchenAlertMainWindow::stop()
270 {
271     QModelIndex row = selectedRow();
272     if (row.isValid()) //If there was no row selected invalid row was returned
273     {
274         model_.stopTimer(row);
275     }
276     ui->SnoozeButton->setDisabled(true);
277
278 }
279
280 QModelIndex KitchenAlertMainWindow::selectedRow()
281 {
282     //Returns the cells in row 0 that have the whole row selected (the selection mode used allows only selecting whole rows
283
284     QModelIndexList chosenRows = ui->ComingAlertsTableView->selectionModel()->selectedRows();
285
286     //The selection mode used allows only one row to be selected at time, so we just take the first
287
288
289     return chosenRows.value(0); //gives an invalid QModelIndex if the list is empty
290 }
291
292 void KitchenAlertMainWindow::openSelectSoundDialog()
293 {
294
295     SelectSoundDialog dialog;
296    if ( dialog.exec() == QDialog::Accepted) //if user pressed OK
297     {
298        QSettings settings ("KitchenAlert","KitchenAlert");
299       
300        if (dialog.isDefaultSoundChecked() == true)
301        { 
302          
303            settings.setValue("UseDefaultSound",true); 
304            emit defaultSoundEnabled();
305        }   
306       else
307        {
308            QString filename = dialog.getSoundFileName();
309            settings.setValue("UseDefaultSound",false);
310            settings.setValue("soundfile",filename);
311            emit soundChanged(filename);
312        }
313
314     }
315
316 }
317
318 void KitchenAlertMainWindow::openAbout()
319 {
320     QMessageBox::about(this,tr("About KitchenAlert"),tr("<p>Version %1"
321                                                         "<p>Copyright &copy; Heli Hyv&auml;ttinen 2010-2011"
322                                                          "<p>License: General Public License v3"
323                                                          "<p>Web page: http://kitchenalert.garage.maemo.org/"
324                                                          "<p>Bugtracker: https://garage.maemo.org/projects/kitchenalert/").arg(QApplication::applicationVersion()));
325 }
326
327 bool KitchenAlertMainWindow::event(QEvent *event)
328 {
329
330
331     switch (event->type())
332     {
333         case QEvent::WindowActivate:
334
335             model_.setUpdateViewOnChanges(true);
336 //            ui->debugLabel->setText("Returned to the application!");
337             break;
338
339        case QEvent::WindowDeactivate:
340             model_.setUpdateViewOnChanges(false);
341 //            ui->debugLabel->setText("");
342             break;
343
344        default:
345             break;
346
347     }
348
349     return QMainWindow::event(event); // Send the event to the base class implementation (also when handling the event in this function): necessary for the program to work!
350 }
351
352 void KitchenAlertMainWindow::disableSelectionDependentButtons()
353 {
354     ui->DoneButton->setDisabled(true);
355     ui->SnoozeButton->setDisabled(true);
356     ui->RestartButton->setDisabled(true);
357     ui->RemoveButton->setDisabled(true);
358     ui->SaveButton->setDisabled(true);
359
360
361 }
362
363 void KitchenAlertMainWindow::initializeAlertSound()
364 {
365     QSettings settings;
366
367     bool useDefaultSound = settings.value("UseDefaultSound",true).toBool();
368     QString filename = settings.value("soundfile","").toString();
369
370     if (useDefaultSound == true)
371     {
372         openSelectSoundDialog();
373     }
374     else if (filename.isEmpty())
375     {
376         openSelectSoundDialog();
377     }
378
379    QString currentFilename = settings.value("soundfile","").toString();
380
381    if (currentFilename.isEmpty())
382    {
383         ui->debugLabel->setText("<FONT color=red>No alert sound file set. Alert sound will not be played!</FONT>");
384
385    }
386
387 }
388
389 void KitchenAlertMainWindow::remove()
390 {
391     QModelIndex row = selectedRow();
392     if (row.isValid()) //If there was no row selected invalid row was returned
393     {
394         QString text = tr("Are you sure you want to remove this timer from the list:\n");
395         text.append((row.data().toString()));
396         if (QMessageBox::question(this,tr("Confirm timer removal"),text,QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes)
397         {
398             model_.removeTimer(row);
399             ui->ComingAlertsTableView->clearSelection();
400             disableSelectionDependentButtons();
401
402         }
403     }
404
405 }
406
407 void KitchenAlertMainWindow::saveTimer()
408 {
409
410     QModelIndex row = selectedRow();
411
412     if (row.isValid() == false) //If there was no row selected invalid row was returned
413         return;
414
415
416     //file name is asked. As the filename will be appended, there's no point in confirming owerwrite here
417     QString filename = QFileDialog::getSaveFileName(this, "", defaultSaveDirectory_, "*.kitchenalert",NULL,QFileDialog::DontConfirmOverwrite);
418
419
420
421 //    qDebug() << filename;
422
423     if (filename.isEmpty()) //user cancelled the dialog (or gave an empty name)
424     {
425         return;
426     }
427
428     if (!filename.endsWith(".kitchenalert"))
429     {
430         filename.append(".kitchenalert");
431
432     }
433
434   //  qDebug() << "filename appended to " << filename;
435
436
437     //MANUAL CONFIRMATION OF OWERWRITE
438
439     if ( QFile::exists(filename))
440     {
441          //ASK FOR CONFIRMATION
442
443         QString overwriteQuestion ("File ");
444         overwriteQuestion.append(filename);
445         overwriteQuestion.append(" already exists. Do you want to overwrite it?");
446         if (QMessageBox::question(this,"Confirm overwrite?", overwriteQuestion,QMessageBox::Yes | QMessageBox::No,QMessageBox::No) != QMessageBox::Yes)
447         {
448             return;
449         }
450     }
451
452
453
454
455     QString errorMessage(tr("Cannot write to file "));
456     errorMessage.append(filename);
457
458     if (!model_.saveTimer(row,filename)) //Save the file, if not successful give an error message
459     {
460         QMessageBox::critical(this,tr("Save timer failed!"), errorMessage);
461     }
462
463
464 }
465
466 void KitchenAlertMainWindow::loadTimer()
467 {
468
469 // If the default save directory does not exist use /home/user instead (as that's what the save dialog will use)
470 // This avoids a situation where save directs to a folder that cannot be accessed with open...
471
472     QString startDirectory;
473
474     if (QFile(defaultSaveDirectory_).exists())
475     {
476         startDirectory = defaultSaveDirectory_;
477     }
478     else
479     {
480         startDirectory = "/home/user/";
481         qDebug () << "default save directory not found";
482     }
483
484
485  //Get the filename to open with a dialog
486
487     QString filename = QFileDialog::getOpenFileName(this,"",startDirectory,tr("KitchenAlert timer files (*.kitchenalert)"));
488     if (!filename.isEmpty())
489     {
490
491 //        if (!filename.endsWith(".kitchenalert"))      //not needed, the dialog won't let the user to select files not ending with ".kitchenalert"
492 //        {
493 //            filename.append(".kitchenalert");
494 //        }
495
496         loadTimer(filename,true); //timer gets started
497
498     }
499 }
500
501 void KitchenAlertMainWindow::initializeTimer(Timer *p_timer, bool startImmediately)
502 {
503
504 //connect alert
505
506
507 connect(p_timer,SIGNAL(alert(QModelIndex)),this,SLOT(alert(QModelIndex)));
508
509
510 //connect change sound functions
511
512 connect(this,SIGNAL(defaultSoundEnabled()),p_timer,SLOT(enableDefaultSound()));
513 connect(this,SIGNAL(soundChanged(QString)),p_timer,SLOT(changeAlertSound(QString)));
514
515
516 //Disable buttons, as selection is cleared when view is refreshed to show the new timer
517
518 disableSelectionDependentButtons();
519
520
521 // give timers to the model (model wants list of timers now..)
522
523 QList<Timer *> timerList;
524
525 timerList.append(p_timer);
526 model_.addTimers(timerList,startImmediately); //timer gets started in the model if startImmediately is true (default)
527
528 }
529
530 void KitchenAlertMainWindow::openStickyDialog()
531 {
532     StickyDialog stickyDialog(defaultSaveDirectory_);
533
534
535     if (stickyDialog.exec()== QDialog::Accepted)
536     {
537         QSettings settings;
538         settings.setValue("sticky",stickyDialog.getStickyList());
539     }
540 }
541
542 bool KitchenAlertMainWindow::loadStickies()
543 {
544     bool allSuccess = true;
545
546     QSettings settings;
547
548     QStringList stickies = settings.value("sticky").toStringList();
549
550
551     foreach (QString stickyFileName, stickies)
552     {
553        if (!loadTimer(stickyFileName,false)) //initializes the timer without starting it
554             allSuccess = false;
555     }
556
557     return allSuccess;
558 }
559
560 bool KitchenAlertMainWindow::loadTimer(QString filename, bool startImmediately)
561 {
562     QString errorTitle(tr("Failed to load file "));
563     errorTitle.append(filename);
564
565     Timer * p_timer = new Timer();
566     if (!p_timer->load(filename))
567     {
568         QMessageBox::critical(this,errorTitle,tr("Unable to open file or not a valid KitchenAlert timer file."));
569         delete p_timer;
570         return false;
571     }
572
573     initializeTimer(p_timer,startImmediately);
574     return true;
575 }