Advanced Settings Panel
[pierogi] / mainwindow.cpp
1 // mainwindow.cpp
2 //
3 // For the Pierogi IR remote control app.
4 //
5 // Copyright (C) 2012 by John Pietrzak  (john@pietrzak.org)
6 //
7 // Licensed under the GNU GPL version 2.0 or later.
8
9 #include "mainwindow.h"
10 #include "ui_mainwindow.h"
11
12 #include <QtCore/QCoreApplication>
13 #include <QMutex>
14 #include <QtGui/QMessageBox>
15 #include <QPushButton>
16 #include <QDialog>
17 #include <QDialogButtonBox>
18 #include <QScrollArea>
19 #include <QSettings>
20 #include <QKeyEvent>
21
22 //#include "pirtabwidget.h"
23
24 #include "pirkeysetmetadata.h"
25
26 #include "pirkeysetwidgetitem.h"
27 #include "pirselectkeysetform.h"
28 #include "pirselectdeviceform.h"
29 #include "pirpreferencesform.h"
30 #include "pirdocumentationform.h"
31 #include "piraboutform.h"
32 #include "dialogs/pirtabschoicedialog.h"
33 #include "dialogs/pirfavoritesdialog.h"
34
35 #include "pirkeysetmanager.h"
36 #include "pirpanelmanager.h"
37 #include "macros/pirmacromanager.h"
38
39 //#define DEBUGGING
40 #include <iostream>
41
42 // Some ugly globals used for thread communications:
43
44 // A global to show that a command is being processed:
45 bool commandInFlight = false;
46 QMutex commandIFMutex;
47
48 // The stopRepeatingFlag boolean is the method used to tell running commands
49 // in the worker thread to stop:
50 bool stopRepeatingFlag = false;
51 QMutex stopRepeatingMutex;
52
53
54 extern PIRMakeMgr makeManager;
55
56
57 MainWindow::MainWindow(QWidget *parent)
58   : QMainWindow(parent),
59     ui(new Ui::MainWindow),
60     selectKeysetForm(0),
61     selectDeviceForm(0),
62     preferencesForm(0),
63     documentationForm(0),
64     aboutForm(0),
65     favoritesDialog(0),
66     myKeysets(0),
67     myPanels(0),
68     myMacros(0),
69     currentKeyset(1) // Zero is not a valid keyset any more
70 {
71   // Create the tab widget:
72 //  myTabWidget = new PIRTabWidget(ui->centralWidget, this);
73
74   ui->setupUi(this);
75
76   // Make this a Maemo 5 stacked widget:
77   setAttribute(Qt::WA_Maemo5StackedWindow);
78
79   // Create the managers:
80   myKeysets = new PIRKeysetManager();
81   myMacros = new PIRMacroManager(this);
82   myPanels = new PIRPanelManager(this);
83
84   // Display the panels:
85   myPanels->updateTabSet();
86
87   // Construct the rest of the forms:
88   selectKeysetForm = new PIRSelectKeysetForm(this);
89   favoritesDialog = new PIRFavoritesDialog(this);
90   myKeysets->populateListWidgets(selectKeysetForm, favoritesDialog);
91   selectKeysetForm->populateKeysetComboBox(myPanels->getKeysetComboBox());
92
93   selectDeviceForm = new PIRSelectDeviceForm(this);
94   PIRKeysetMetaData::populateDevices(selectDeviceForm);
95
96   preferencesForm = new PIRPreferencesForm(this, myKeysets);
97
98   // Retrieve the user's preferences:
99   QSettings settings("pietrzak.org", "Pierogi");
100   if (settings.contains("currentKeysetName"))
101   {
102     myKeysets->findKeysetID(
103       settings.value("currentKeysetMake").toString(),
104       settings.value("currentKeysetName").toString(),
105       currentKeyset);
106   }
107
108   selectKeysetForm->selectKeyset(currentKeyset);
109
110   // Add the corner buttons:
111   insertCornerButtons();
112
113   // Set up all the buttons:
114   enableButtons();
115   myPanels->updateUserButtons();
116
117   // Make sure the three selection lists don't show different selections:
118   QListWidget *klw = selectKeysetForm->getKeysetListWidget();
119   QListWidget *dlw = selectDeviceForm->getDeviceListWidget();
120
121   // keyset name -> device name
122   connect(
123     klw,
124     SIGNAL(itemActivated(QListWidgetItem *)),
125     dlw,
126     SLOT(clearSelection()),
127     Qt::QueuedConnection);
128
129   // device name -> keyset name
130   connect(
131     dlw,
132     SIGNAL(itemActivated(QListWidgetItem *)),
133     klw,
134     SLOT(clearSelection()),
135     Qt::QueuedConnection);
136
137 #ifndef DEBUGGING
138   // The PIRModprobe object should take care of setting up and shutting down
139   // the lirc_rx51 kernel module, if necessary:
140  
141   if (modprobeObj.loadRX51Module() != 0)
142   {
143     // Couldn't load module, quit:
144     QMessageBox errBox;
145     errBox.setText("Couldn't load lirc_rx51 kernel module!");
146     errBox.setIcon(QMessageBox::Warning);
147     errBox.exec();
148 //    throw; // Need a clean way to exit here!!!
149   }
150 #endif
151 }
152
153
154 MainWindow::~MainWindow()
155 {
156   if (aboutForm) delete aboutForm;
157   if (documentationForm) delete documentationForm;
158   if (preferencesForm) delete preferencesForm;
159   if (selectDeviceForm) delete selectDeviceForm;
160   if (favoritesDialog) delete favoritesDialog;
161   if (selectKeysetForm) delete selectKeysetForm;
162
163   if (myPanels) delete myPanels;
164   if (myMacros) delete myMacros;
165   if (myKeysets) delete myKeysets;
166   delete ui;
167 }
168
169
170 void MainWindow::setOrientation(ScreenOrientation orientation)
171 {
172 #if defined(Q_OS_SYMBIAN)
173     // If the version of Qt on the device is < 4.7.2, that attribute won't work
174     if (orientation != ScreenOrientationAuto) {
175         const QStringList v = QString::fromAscii(qVersion()).split(QLatin1Char('.'));
176         if (v.count() == 3 && (v.at(0).toInt() << 16 | v.at(1).toInt() << 8 | v.at(2).toInt()) < 0x040702) {
177             qWarning("Screen orientation locking only supported with Qt 4.7.2 and above");
178             return;
179         }
180     }
181 #endif // Q_OS_SYMBIAN
182
183     Qt::WidgetAttribute attribute;
184     switch (orientation) {
185 #if QT_VERSION < 0x040702
186     // Qt < 4.7.2 does not yet have the Qt::WA_*Orientation attributes
187     case ScreenOrientationLockPortrait:
188         attribute = static_cast<Qt::WidgetAttribute>(128);
189         break;
190     case ScreenOrientationLockLandscape:
191         attribute = static_cast<Qt::WidgetAttribute>(129);
192         break;
193     default:
194     case ScreenOrientationAuto:
195         attribute = static_cast<Qt::WidgetAttribute>(130);
196         break;
197 #else // QT_VERSION < 0x040702
198     case ScreenOrientationLockPortrait:
199         attribute = Qt::WA_LockPortraitOrientation;
200         break;
201     case ScreenOrientationLockLandscape:
202         attribute = Qt::WA_LockLandscapeOrientation;
203         break;
204     default:
205     case ScreenOrientationAuto:
206         attribute = Qt::WA_AutoOrientation;
207         break;
208 #endif // QT_VERSION < 0x040702
209     };
210     setAttribute(attribute, true);
211 }
212
213 void MainWindow::showExpanded()
214 {
215 #if defined(Q_OS_SYMBIAN) || defined(Q_WS_SIMULATOR)
216     showFullScreen();
217 #elif defined(Q_WS_MAEMO_5)
218     showMaximized();
219 #else
220     show();
221 #endif
222 }
223
224
225 void MainWindow::enableButtons()
226 {
227   // Just to be sure, check to see if the keyset has been populated:
228   myKeysets->populateKeyset(this, currentKeyset);
229
230   if (preferencesForm)
231   {
232     unsigned int dk = preferencesForm->getDefaultKeyset();
233     if (preferencesForm->defaultControlsVolume() && dk)
234     {
235       myKeysets->populateKeyset(this, dk);
236       myPanels->enableButtons(myKeysets, currentKeyset, dk);
237     }
238     else
239     {
240       myPanels->enableButtons(myKeysets, currentKeyset);
241     }
242   }
243   else
244   {
245     myPanels->enableButtons(myKeysets, currentKeyset);
246   }
247 }
248
249
250 void MainWindow::useMainPanel()
251 {
252   myPanels->useMainPanel();
253 }
254
255
256 void MainWindow::useAltMainPanel()
257 {
258   myPanels->useAltMainPanel();
259 }
260
261
262 QString MainWindow::getCurrentMake()
263 {
264   return makeManager.getMakeString(myKeysets->getMake(currentKeyset));
265 }
266
267
268 QString MainWindow::getCurrentName()
269 {
270   return myKeysets->getDisplayName(currentKeyset);
271 }
272
273
274 QString MainWindow::getCurrentFullName()
275 {
276   return selectKeysetForm->getCurrentKeysetName();
277 }
278
279
280 QString MainWindow::getKeysetMake(
281   unsigned int id)
282 {
283   return makeManager.getMakeString(myKeysets->getMake(id));
284 }
285
286
287 QString MainWindow::getKeysetName(
288   unsigned int id)
289 {
290   return myKeysets->getDisplayName(id);
291 }
292
293
294 QString MainWindow::getFullKeysetName(
295   unsigned int id)
296 {
297   return selectKeysetForm->getKeysetName(id);
298 }
299
300
301 bool MainWindow::findKeysetID(
302   QString make,
303   QString name,
304   unsigned int &id)
305 {
306   return myKeysets->findKeysetID(make, name, id);
307 }
308
309
310 void MainWindow::receivedExternalWarning(
311   const char *warning)
312 {
313   QMessageBox errBox;
314   errBox.setText(warning);
315   errBox.setIcon(QMessageBox::Warning);
316   errBox.exec();
317 }
318
319
320 // Menu actions:
321
322 void MainWindow::on_actionSelectKeyset_triggered()
323 {
324   selectKeysetForm->show();
325 }
326
327 void MainWindow::on_actionBrowse_Device_List_triggered()
328 {
329   selectDeviceForm->show();
330 }
331
332 void MainWindow::on_actionPreferences_triggered()
333 {
334   preferencesForm->show();
335 }
336
337 void MainWindow::on_actionAbout_triggered()
338 {
339   if (!aboutForm)
340   {
341     aboutForm = new PIRAboutForm(this);
342   }
343
344   aboutForm->show();
345 }
346
347 void MainWindow::on_actionDocumentation_triggered()
348 {
349   if (!documentationForm)
350   {
351     documentationForm = new PIRDocumentationForm(this);
352   }
353
354   documentationForm->show();
355 }
356
357
358 // Other actions:
359
360 void MainWindow::keysetSelectionChanged(
361   QListWidgetItem *item)
362 {
363   if (!item) return;  // Should probably display error message here!
364
365   PIRKeysetWidgetItem *kwi = dynamic_cast<PIRKeysetWidgetItem *>(item);
366
367   if (!kwi) return; // Also need to say something here
368
369   if (currentKeyset == kwi->getID())
370   {
371     // We're already on that keyset, so nothing to do:
372     return;
373   }
374
375   // Clean up and remove the current keyset:
376   myKeysets->clearKeyset(currentKeyset);
377   
378   currentKeyset = kwi->getID();
379
380   QSettings settings("pietrzak.org", "Pierogi");
381
382   settings.setValue(
383     "currentKeysetMake",
384     makeManager.getMakeString(kwi->getMake()));
385
386   settings.setValue(
387     "currentKeysetName",
388     myKeysets->getDisplayName(currentKeyset));
389
390   enableButtons();
391 }
392
393
394 void MainWindow::finalCleanup()
395 {
396   // Perform any necessary cleanup work here.
397
398   // Make certain that the thread stops repeating:
399   stopRepeating();
400 }
401
402
403 void MainWindow::addToFavorites(
404   PIRKeysetWidgetItem *kwi)
405 {
406   //Add keyset to the favorites:
407   favoritesDialog->addItem(new PIRKeysetWidgetItem(kwi));
408
409   // And, add the keyset id to the persistent list:
410   QSettings settings("pietrzak.org", "Pierogi");
411
412   int favSettingsSize = settings.beginReadArray("favorites");
413   settings.endArray();
414
415   settings.beginWriteArray("favorites");
416   settings.setArrayIndex(favSettingsSize);
417
418   settings.setValue(
419     "keysetMake",
420     makeManager.getMakeString(kwi->getMake()));
421
422   settings.setValue("keysetName", kwi->getInternalName());
423
424   settings.endArray();
425 }
426
427
428 void MainWindow::removeFromFavorites(
429   unsigned int keysetID)
430 {
431   favoritesDialog->removeItem(keysetID);
432
433   // Remove this item from the persistent list.  Well, actually, it seems a
434   // little more convenient to just blow away the existing list of favorites
435   // and rewrite it, as modifying an existing QSettings array in the middle
436   // seems a bit hard...
437   QSettings settings("pietrzak.org", "Pierogi");
438
439   settings.remove("favorites");
440
441   int count = favoritesDialog->getCount();
442
443   // If the count is empty, we can stop right here:
444   if (count == 0) return;
445
446   int index = 0;
447   unsigned int id;
448   PIRKeysetWidgetItem *kwi = NULL;
449   settings.beginWriteArray("favorites");
450   while (index < count)
451   {
452     kwi = favoritesDialog->getItem(index);
453
454     settings.setArrayIndex(index);
455     id = kwi->getID();
456
457     settings.setValue(
458       "keysetMake",
459       makeManager.getMakeString(myKeysets->getMake(id)));
460
461     settings.setValue("keysetName", myKeysets->getDisplayName(id));
462
463     ++index;
464   }
465   settings.endArray();
466 }
467
468
469 /*
470 void MainWindow::populateFavorites()
471 {
472   QSettings settings("pietrzak.org", "Pierogi");
473
474   int size = settings.beginReadArray("favorites");
475   int index = 0;
476   QString make;
477   QString name;
478   PIRKeysetWidgetItem *kwi;
479
480   while (index < size)
481   {
482     settings.setArrayIndex(index);
483     make = settings.value("keysetMake").toString();
484     name = settings.value("keysetName").toString();
485
486     kwi = myKeysets->makeKeysetItem(make, name);
487
488     // Did the item creation work?
489     if (kwi)
490     {
491       // Keyset does exist, so continue:
492       favoritesDialog->addItem(kwi);
493     }
494
495     ++index;
496   }
497
498   settings.endArray();
499 }
500 */
501
502
503 bool MainWindow::startRepeating(
504   PIRKeyName name)
505 {
506   QMutexLocker locker(&commandIFMutex);
507   if (!commandInFlight)
508   {
509     commandInFlight = true;
510     emit buttonPressed(currentKeyset, name);
511     return true;
512   }
513   else
514   {
515     return false;
516   }
517 }
518
519
520 bool MainWindow::startRepeating(
521   PIRKeyName name,
522   unsigned int keysetID)
523 {
524   QMutexLocker locker(&commandIFMutex);
525   if (!commandInFlight)
526   {
527     commandInFlight = true;
528     emit buttonPressed(keysetID, name);
529     return true;
530   }
531   else
532   {
533     return false;
534   }
535 }
536
537
538 void MainWindow::stopRepeating()
539 {
540   QMutexLocker locker(&stopRepeatingMutex);
541   stopRepeatingFlag = true;
542 }
543
544
545 void MainWindow::selectPrevFavKeyset()
546 {
547   favoritesDialog->selectPrevFavKeyset();
548 }
549
550
551 void MainWindow::selectNextFavKeyset()
552 {
553   favoritesDialog->selectNextFavKeyset();
554 }
555
556
557 void MainWindow::insertCornerButtons()
558 {
559   // Set up the dialog boxes:
560   PIRTabsChoiceDialog *tcd = new PIRTabsChoiceDialog(this);
561 //  favoritesDialog = new PIRFavoritesDialog(this);
562
563   // Next, set up the corner buttons:
564   QPushButton *button =
565     new QPushButton(QIcon(":/icons/folder_plus_icon&32.png"), "");
566
567   button->setFlat(true);
568
569   connect(
570     button,
571     SIGNAL(clicked()),
572     tcd,
573     SLOT(exec()),
574     Qt::QueuedConnection);
575
576   ui->mainTabWidget->setCornerWidget(button, Qt::TopRightCorner);
577 //  myTabWidget->setCornerWidget(button, Qt::TopRightCorner);
578
579   button =
580     new QPushButton(QIcon(":/icons/align_just_icon&32.png"), "");
581
582   button->setFlat(true);
583
584   connect(
585     button,
586     SIGNAL(clicked()),
587     favoritesDialog,
588     SLOT(exec()),
589     Qt::QueuedConnection);
590
591   ui->mainTabWidget->setCornerWidget(button, Qt::TopLeftCorner);
592 //  myTabWidget->setCornerWidget(button, Qt::TopLeftCorner);
593 }
594
595
596 void MainWindow::disableUpdates()
597 {
598   ui->mainTabWidget->setUpdatesEnabled(false);
599 //  myTabWidget->setUpdatesEnabled(false);
600 }
601
602
603 void MainWindow::enableUpdates()
604 {
605   ui->mainTabWidget->setUpdatesEnabled(true);
606 //  myTabWidget->setUpdatesEnabled(true);
607 }
608
609
610 void MainWindow::clearTabs()
611 {
612   ui->mainTabWidget->clear();
613 //  myTabWidget->clear();
614 }
615
616
617 void MainWindow::addTab(
618   QWidget *page,
619   QString label)
620 {
621   ui->mainTabWidget->addTab(page, label);
622 //  myTabWidget->addTab(page, label);
623 }
624
625 void MainWindow::setupTabs(
626   PIRTabBarName name)
627 {
628   myPanels->setupTabs(name);
629 }
630
631
632 bool MainWindow::selectNextKeyset()
633 {
634   return selectKeysetForm->selectNextKeyset();
635 }
636
637
638 bool MainWindow::selectPrevKeyset()
639 {
640   return selectKeysetForm->selectPrevKeyset();
641 }
642
643
644 bool MainWindow::selectFirstKeyset()
645 {
646   return selectKeysetForm->selectFirstKeyset();
647 }
648
649
650 void MainWindow::openCurrentKeysetDialog()
651 {
652   selectKeysetForm->openCurrentKeysetDialog();
653 }
654
655
656 void MainWindow::updateKeysetSelection(
657   unsigned int targetID)
658 {
659   selectKeysetForm->selectKeyset(targetID);
660 }
661
662
663 PIRMacroPack *MainWindow::getUserPack()
664 {
665   return myMacros->getUserPack();
666 }
667
668
669 PIRMacroPack *MainWindow::getMultitapPack()
670 {
671   return myMacros->getMultitapPack();
672 }
673
674
675 void MainWindow::handleKeypress(
676   char key)
677 {
678   myMacros->handleKeypress(key);
679 }
680
681
682 /*
683 void MainWindow::handleKeyRelease(
684   char key)
685 {
686 }
687 */
688
689
690 void MainWindow::gotoPreviousTabs()
691 {
692   myPanels->gotoPreviousTabs();
693 }
694
695
696 void MainWindow::gotoNextTabs()
697 {
698   myPanels->gotoNextTabs();
699 }
700
701  
702 /*
703 void MainWindow::keyPressEvent(
704   QKeyEvent *event)
705 {
706   switch(event->key())
707   {
708   case Qt::Key_A:
709     myMacros->handleKeypress('A');
710     break;
711   case Qt::Key_B:
712     myMacros->handleKeypress('B');
713     break;
714   case Qt::Key_C:
715     myMacros->handleKeypress('C');
716     break;
717   case Qt::Key_D:
718     myMacros->handleKeypress('D');
719     break;
720   case Qt::Key_E:
721     myMacros->handleKeypress('E');
722     break;
723   case Qt::Key_F:
724     myMacros->handleKeypress('F');
725     break;
726   case Qt::Key_G:
727     myMacros->handleKeypress('G');
728     break;
729   case Qt::Key_H:
730     myMacros->handleKeypress('H');
731     break;
732   case Qt::Key_I:
733     myMacros->handleKeypress('I');
734     break;
735   case Qt::Key_J:
736     myMacros->handleKeypress('J');
737     break;
738   case Qt::Key_K:
739     myMacros->handleKeypress('K');
740     break;
741   case Qt::Key_L:
742     myMacros->handleKeypress('L');
743     break;
744   case Qt::Key_M:
745     myMacros->handleKeypress('M');
746     break;
747   case Qt::Key_N:
748     myMacros->handleKeypress('N');
749     break;
750   case Qt::Key_O:
751     myMacros->handleKeypress('O');
752     break;
753   case Qt::Key_P:
754     myMacros->handleKeypress('P');
755     break;
756   case Qt::Key_Q:
757     myMacros->handleKeypress('Q');
758     break;
759   case Qt::Key_R:
760     myMacros->handleKeypress('R');
761     break;
762   case Qt::Key_S:
763     myMacros->handleKeypress('S');
764     break;
765   case Qt::Key_T:
766     myMacros->handleKeypress('T');
767     break;
768   case Qt::Key_U:
769     myMacros->handleKeypress('U');
770     break;
771   case Qt::Key_V:
772     myMacros->handleKeypress('V');
773     break;
774   case Qt::Key_W:
775     myMacros->handleKeypress('W');
776     break;
777   case Qt::Key_X:
778     myMacros->handleKeypress('X');
779     break;
780   case Qt::Key_Y:
781     myMacros->handleKeypress('Y');
782     break;
783   case Qt::Key_Z:
784     myMacros->handleKeypress('Z');
785     break;
786   case Qt::Key_Space:
787     myMacros->handleKeypress(' ');
788     break;
789
790   case Qt::Key_Up:
791     myPanels->gotoPreviousTabs();
792     break;
793   case Qt::Key_Down:
794     myPanels->gotoNextTabs();
795     break;
796
797
798   case Qt::Key_Left:
799     startRepeating(VolumeDown_Key);
800     break;
801   case Qt::Key_Right:
802     startRepeating(VolumeUp_Key);
803     break;
804
805   case Qt::Key_Backspace:
806     startRepeating(Power_Key);
807     break;
808   case Qt::Key_Return:
809     startRepeating(Mute_Key);
810     break;
811
812   default:
813     QMainWindow::keyPressEvent(event);
814     break;
815   }
816 }
817 */
818
819
820 /*
821 void MainWindow::keyReleaseEvent(
822   QKeyEvent *event)
823 {
824   switch(event->key())
825   {
826
827   case Qt::Key_Up:
828   case Qt::Key_Down:
829   case Qt::Key_Left:
830   case Qt::Key_Right:
831
832   case Qt::Key_Backspace:
833   case Qt::Key_Return:
834     stopRepeating();
835     break;
836
837   default:
838     QMainWindow::keyReleaseEvent(event);
839     break;
840   }
841 }
842 */
843
844
845 bool MainWindow::hasMacroButton(
846   unsigned int buttonID)
847 {
848   return myMacros->hasMacroButton(buttonID);
849 }
850
851
852 QString MainWindow::getMacroButtonText(
853   unsigned int buttonID)
854 {
855   return myMacros->getMacroButtonText(buttonID);
856 }
857
858
859 void MainWindow::executeMacroButton(
860   unsigned int buttonID)
861 {
862   myMacros->executeMacroButton(buttonID);
863 }
864
865
866 void MainWindow::updateUserButtons()
867 {
868   myPanels->updateUserButtons();
869 }
870
871
872 void MainWindow::storeMacros()
873 {
874   myMacros->storeSettings();
875 }
876
877
878 void MainWindow::setMacroKbdFocus(
879   int index)
880 {
881   myMacros->setKbdFocus(index);
882 }
883
884
885 void MainWindow::setMacroBtnFocus(
886   int index)
887 {
888   myMacros->setBtnFocus(index);
889 }
890
891
892 void MainWindow::switchToTab(
893   int tabNumber)
894 {
895   int count = ui->mainTabWidget->count();
896
897   if (tabNumber < 0)
898   {
899     ui->mainTabWidget->setCurrentIndex(0);
900   }
901   else if (tabNumber >= count)
902   {
903     ui->mainTabWidget->setCurrentIndex(count-1);
904   }
905   else
906   {
907     ui->mainTabWidget->setCurrentIndex(tabNumber);
908   }
909 }