MythTV  0.27pre
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Groups Pages
proglist.cpp
Go to the documentation of this file.
1 // C/C++
2 #include <vector>
3 #include <algorithm>
4 #include <functional>
5 using namespace std;
6 
7 // Qt
8 #include <QCoreApplication>
9 #include <QRegExp>
10 #include <QLocale>
11 
12 // MythTV
13 #include "scheduledrecording.h"
14 #include "mythuibuttonlist.h"
15 #include "mythcorecontext.h"
16 #include "mythdialogbox.h"
17 #include "recordinginfo.h"
18 #include "recordingrule.h"
19 #include "channelinfo.h"
20 #include "channelutil.h"
21 #include "proglist.h"
22 #include "mythdb.h"
23 #include "mythdate.h"
24 #include "guidegrid.h"
25 
26 #define LOC QString("ProgLister: ")
27 #define LOC_WARN QString("ProgLister, Warning: ")
28 #define LOC_ERR QString("ProgLister, Error: ")
29 
31  const QString &view, const QString &extraArg) :
32  ScheduleCommon(parent, "ProgLister"),
33  m_type(pltype),
34  m_recid(0),
35  m_title(),
36  m_extraArg(extraArg),
37  m_startTime(MythDate::current()),
38  m_searchTime(m_startTime),
39  m_channelOrdering(gCoreContext->GetSetting("ChannelOrdering", "channum")),
40 
41  m_searchType(kNoSearch),
42 
43  m_view(view),
44  m_curView(-1),
45  m_viewList(),
46  m_viewTextList(),
47 
48  m_itemList(),
49  m_itemListSave(),
50  m_schedList(),
51 
52  m_typeList(),
53  m_genreList(),
54  m_stationList(),
55 
56  m_allowEvents(true),
57  m_titleSort(false),
58  m_reverseSort(false),
59  m_useGenres(false),
60 
61  m_schedText(NULL),
62  m_curviewText(NULL),
63  m_positionText(NULL),
64  m_progList(NULL),
65  m_messageText(NULL),
66 
67  m_allowViewDialog(true)
68 {
69  if (pltype == plMovies)
70  {
72  query.prepare("SELECT COUNT(*) FROM program WHERE stars > 0");
73 
74  if (query.exec() && query.next())
75  {
76  if (query.value(0).toInt() == 0) // No ratings in database
77  {
78  m_curView = 0; // Show All
79  m_allowViewDialog = false;
80  }
81  }
82  }
83 
84  switch (pltype)
85  {
90  case plSQLSearch: m_searchType = kPowerSearch; break;
92  default: m_searchType = kNoSearch; break;
93  }
94 }
95 
96 // previously recorded ctor
98  MythScreenStack *parent, uint recid, const QString &title) :
99  ScheduleCommon(parent, "PreviousList"),
100  m_type(plPreviouslyRecorded),
101  m_recid(recid),
102  m_title(title),
103  m_extraArg(),
104  m_startTime(MythDate::current()),
105  m_searchTime(m_startTime),
106  m_channelOrdering(gCoreContext->GetSetting("ChannelOrdering", "channum")),
107 
108  m_searchType(kNoSearch),
109 
110  m_view("reverse time"),
111  m_curView(-1),
112  m_viewList(),
113  m_viewTextList(),
114 
115  m_itemList(),
116  m_itemListSave(),
117  m_schedList(),
118 
119  m_typeList(),
120  m_genreList(),
121  m_stationList(),
122 
123  m_allowEvents(true),
124  m_titleSort(false),
125  m_reverseSort(true),
126  m_useGenres(false),
127 
128  m_schedText(NULL),
129  m_curviewText(NULL),
130  m_positionText(NULL),
131  m_progList(NULL),
132  m_messageText(NULL),
133 
134  m_allowViewDialog(true)
135 {
136 }
137 
139 {
140  m_itemList.clear();
143 }
144 
146 {
147  if (!LoadWindowFromXML("schedule-ui.xml", "programlist", this))
148  return false;
149 
150  bool err = false;
151  UIUtilW::Assign(this, m_curviewText, "curview", &err);
152  UIUtilE::Assign(this, m_progList, "proglist", &err);
153  UIUtilW::Assign(this, m_schedText, "sched", &err);
154  UIUtilW::Assign(this, m_messageText, "msg", &err);
155  UIUtilW::Assign(this, m_positionText, "position", &err);
156 
157  if (err)
158  {
159  LOG(VB_GENERAL, LOG_ERR, "Cannot load screen 'programlist'");
160  return false;
161  }
162 
163  connect(m_progList, SIGNAL(itemSelected(MythUIButtonListItem*)),
164  this, SLOT( HandleSelected( MythUIButtonListItem*)));
165 
166  connect(m_progList, SIGNAL(itemVisible(MythUIButtonListItem*)),
167  this, SLOT( HandleVisible( MythUIButtonListItem*)));
168 
169  connect(m_progList, SIGNAL(itemLoaded(MythUIButtonListItem*)),
170  this, SLOT( HandleVisible( MythUIButtonListItem*)));
171 
172  connect(m_progList, SIGNAL(itemClicked(MythUIButtonListItem*)),
173  this, SLOT( HandleClicked()));
174 
175  m_progList->SetLCDTitles(tr("Program List"), "title|channel|shortstarttimedate");
176  m_progList->SetSearchFields("titlesubtitle");
177 
178  BuildFocusList();
179 
180  QString value;
181  switch (m_type)
182  {
183  case plTitle: value = tr("Program Listings"); break;
184  case plNewListings: value = tr("New Title Search"); break;
185  case plTitleSearch: value = tr("Title Search"); break;
186  case plKeywordSearch: value = tr("Keyword Search"); break;
187  case plPeopleSearch: value = tr("People Search"); break;
188  case plStoredSearch: value = tr("Stored Search"); break;
189  case plPowerSearch: value = tr("Power Search"); break;
190  case plSQLSearch: value = tr("Power Search"); break;
191  case plRecordid: value = tr("Rule Search"); break;
192  case plCategory: value = tr("Category Search"); break;
193  case plChannel: value = tr("Channel Search"); break;
194  case plMovies: value = tr("Movie Search"); break;
195  case plTime: value = tr("Time Search"); break;
196  case plPreviouslyRecorded: value = tr("Previously Recorded"); break;
197  default: value = tr("Unknown Search"); break;
198  }
199 
200  if (m_schedText)
201  m_schedText->SetText(value);
202 
203  gCoreContext->addListener(this);
204 
206 
207  return true;
208 }
209 
211 {
212  if (m_viewList.isEmpty() || m_curView < 0)
214 
215  FillItemList(false, false);
216 
218  new ScreenLoadCompletionEvent(objectName());
219  QCoreApplication::postEvent(this, slce);
220 }
221 
222 bool ProgLister::keyPressEvent(QKeyEvent *e)
223 {
224  if (!m_allowEvents)
225  return true;
226 
228  {
229  m_allowEvents = true;
230  return true;
231  }
232 
233  m_allowEvents = false;
234 
235  QStringList actions;
236  bool handled = GetMythMainWindow()->TranslateKeyPress(
237  "TV Frontend", e, actions);
238 
239  bool needUpdate = false;
240  for (uint i = 0; i < uint(actions.size()) && !handled; ++i)
241  {
242  QString action = actions[i];
243  handled = true;
244 
245  if (action == "PREVVIEW")
247  else if (action == "NEXTVIEW")
249  else if (action == "CUSTOMEDIT")
250  {
251  if (GetCurrent())
253  }
254  else if (action == "EDIT")
255  {
256  if (GetCurrent())
258  }
259  else if (action == "DELETE")
261  else if (action == "UPCOMING")
262  ShowUpcoming();
263  else if (action == "DETAILS" || action == "INFO")
264  ShowDetails();
265  else if (action == "GUIDE")
266  ShowGuide();
267  else if (action == "TOGGLERECORD")
268  RecordSelected();
269  else if (action == "1")
270  {
271  if (m_titleSort == true)
272  {
273  m_titleSort = false;
275  }
276  else
277  {
279  }
280  needUpdate = true;
281  }
282  else if (action == "2")
283  {
284  if (m_titleSort == false)
285  {
286  m_titleSort = true;
287  m_reverseSort = false;
288  }
289  else
290  {
292  }
293  needUpdate = true;
294  }
295  else
296  {
297  handled = false;
298  }
299  }
300 
301  if (!handled && MythScreenType::keyPressEvent(e))
302  handled = true;
303 
304  if (needUpdate)
306 
307  m_allowEvents = true;
308 
309  return handled;
310 }
311 
313 {
314  MythMenu *sortMenu = new MythMenu(tr("Sort Options"), this, "sortmenu");
315  sortMenu->AddItem(tr("Reverse Sort Order"));
316  sortMenu->AddItem(tr("Sort By Title"));
317  sortMenu->AddItem(tr("Sort By Time"));
318 
319  MythMenu *menu = new MythMenu(tr("Options"), this, "menu");
320 
322  {
323  menu->AddItem(tr("Choose Search Phrase..."), SLOT(ShowChooseViewMenu()));
324  }
325 
326  menu->AddItem(tr("Sort"), NULL, sortMenu);
327 
329  menu->AddItem(tr("Record"), SLOT(RecordSelected()));
330 
331  menu->AddItem(tr("Edit Schedule"), SLOT(EditScheduled()));
332  menu->AddItem(tr("Program Details"), SLOT(ShowDetails()));
333  menu->AddItem(tr("Program Guide"), SLOT(ShowGuide()));
334  menu->AddItem(tr("Upcoming"), SLOT(ShowUpcoming()));
335  menu->AddItem(tr("Custom Edit"), SLOT(EditCustom()));
336 
339  {
340  if (pi && pi->GetRecordingRuleID())
341  menu->AddItem(tr("Delete Rule"), SLOT(ShowDeleteRuleMenu()));
342  }
343  else
344  {
345  menu->AddItem(
346  tr("Delete Episode"), SLOT(ShowDeleteOldEpisodeMenu()));
347  }
348 
349  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
350  MythDialogBox *menuPopup = new MythDialogBox(menu, popupStack, "menuPopup");
351 
352  if (!menuPopup->Create())
353  {
354  delete menuPopup;
355  return;
356  }
357 
358  popupStack->AddScreen(menuPopup);
359 }
360 
362 {
363  if (m_type == plTime && !m_viewList.empty() && !m_viewTextList.empty())
364  {
365  m_searchTime = m_searchTime.addSecs(-3600);
366  m_curView = 0;
371  return;
372  }
373 
374  if (m_viewList.size() <= 1)
375  return;
376 
377  m_curView--;
378  if (m_curView < 0)
379  m_curView = m_viewList.size() - 1;
380 
382 }
383 
385 {
386  if (m_type == plTime && !m_viewList.empty() && !m_viewTextList.empty())
387  {
388  m_searchTime = m_searchTime.addSecs(3600);
389  m_curView = 0;
394 
395  return;
396  }
397 
398  if (m_viewList.size() <= 1)
399  return;
400 
401  m_curView++;
402  if (m_curView >= (int)m_viewList.size())
403  m_curView = 0;
404 
406 }
407 
408 void ProgLister::UpdateKeywordInDB(const QString &text, const QString &oldValue)
409 {
410  int oldview = m_viewList.indexOf(oldValue);
411  int newview = m_viewList.indexOf(text);
412 
413  if (newview >= 0 && newview == oldview)
414  return;
415 
416  if (oldview >= 0)
417  {
418  QString qphrase = m_viewList[oldview];
419 
420  MSqlQuery query(MSqlQuery::InitCon());
421  query.prepare("DELETE FROM keyword "
422  "WHERE phrase = :PHRASE AND searchtype = :TYPE;");
423  query.bindValue(":PHRASE", qphrase);
424  query.bindValue(":TYPE", m_searchType);
425  if (!query.exec())
426  {
428  "ProgLister::updateKeywordInDB -- delete", query);
429  }
430  m_viewList.removeAll(qphrase);
431  m_viewTextList.removeAll(qphrase);
432  }
433 
434  if (newview < 0)
435  {
436  QString qphrase = text;
437 
438  MSqlQuery query(MSqlQuery::InitCon());
439  query.prepare("REPLACE INTO keyword (phrase, searchtype)"
440  "VALUES(:PHRASE, :TYPE );");
441  query.bindValue(":PHRASE", qphrase);
442  query.bindValue(":TYPE", m_searchType);
443  if (!query.exec())
444  {
446  "ProgLister::updateKeywordInDB -- replace", query);
447  }
448  m_viewList.push_back(qphrase);
449  m_viewTextList.push_back(qphrase);
450  }
451 }
452 
454 {
455  MythScreenStack *popupStack =
456  GetMythMainWindow()->GetStack("popup stack");
457  MythScreenType *screen = NULL;
458  bool connect_string = true;
459 
460  switch (m_type)
461  {
462  case plChannel:
463  case plCategory:
464  case plMovies:
465  case plNewListings:
466  case plStoredSearch:
467  {
468  if (m_viewList.empty())
469  return;
470 
471  QString msg;
472  switch (m_type)
473  {
474  case plMovies: msg = tr("Select Rating"); break;
475  case plChannel: msg = tr("Select Channel"); break;
476  case plCategory: msg = tr("Select Category"); break;
477  case plNewListings: msg = tr("Select List"); break;
478  case plStoredSearch: msg = QString("%1\n%2")
479  .arg(tr("Select a search stored from"))
480  .arg(tr("Custom Record")); break;
481  default: // silence warning
482  break;
483  }
484 
485  screen = new MythUISearchDialog(
486  popupStack, msg, m_viewTextList, true, "");
487 
488  break;
489  }
490  case plTitleSearch:
491  case plKeywordSearch:
492  case plPeopleSearch:
493  screen = new PhrasePopup(
494  popupStack, this, m_searchType, m_viewTextList,
495  (m_curView >= 0) ? m_viewList[m_curView] : QString());
496  break;
497  case plPowerSearch:
498  screen = new PowerSearchPopup(
499  popupStack, this, m_searchType, m_viewTextList,
500  (m_curView >= 0) ? m_viewList[m_curView] : QString());
501  break;
502  case plTime:
503  {
504  QString message = tr("Start search from date and time");
505  int flags = (MythTimeInputDialog::kDay |
508  screen = new MythTimeInputDialog(popupStack, message, flags);
509  connect_string = false;
510  break;
511  }
512  case plRecordid:
514  case plUnknown:
515  case plTitle:
516  case plSQLSearch:
517  break;
518  }
519 
520  if (!screen)
521  return;
522 
523  if (!screen->Create())
524  {
525  delete screen;
526  return;
527  }
528 
529  if (connect_string)
530  {
531  connect(screen, SIGNAL(haveResult( QString)),
532  this, SLOT( SetViewFromList(QString)));
533  }
534  else
535  {
536  connect(screen, SIGNAL(haveResult( QDateTime)),
537  this, SLOT( SetViewFromTime(QDateTime)));
538  }
539 
540  popupStack->AddScreen(screen);
541 }
542 
543 void ProgLister::SetViewFromTime(QDateTime searchTime)
544 {
545  if (m_viewList.empty() || m_viewTextList.empty())
546  return;
547 
548  m_searchTime = searchTime;
549  m_curView = 0;
553 
555 }
556 
557 void ProgLister::SetViewFromList(QString item)
558 {
559  m_curView = m_viewTextList.indexOf(item);
560  if (m_curView >= 0)
562 }
563 
565  const QString &qphrase, QString &output, MSqlBindings &bindings) const
566 {
567  output.clear();
568 
569  QStringList field = qphrase.split(':');
570  if (field.size() != 6)
571  {
572  LOG(VB_GENERAL, LOG_ERR, LOC + "Power search should have 6 fields," +
573  QString("\n\t\t\tnot %1 (%2)") .arg(field.size()).arg(qphrase));
574  return false;
575  };
576 
577  static const QString bindinglist[6] =
578  {
579  ":POWERTITLE",
580  ":POWERSUB",
581  ":POWERDESC",
582  ":POWERCATTYPE",
583  ":POWERGENRE",
584  ":POWERCALLSIGN",
585  };
586 
587  static const QString outputlist[6] =
588  {
589  "program.title LIKE :POWERTITLE ",
590  "program.subtitle LIKE :POWERSUB ",
591  "program.description LIKE :POWERDESC ",
592  "program.category_type = :POWERCATTYPE ",
593  "programgenres.genre = :POWERGENRE ",
594  "channel.callsign = :POWERCALLSIGN ",
595  };
596 
597  for (uint i = 0; i < (uint) field.size(); i++)
598  {
599  if (field[i].isEmpty())
600  continue;
601 
602  if (!output.isEmpty())
603  output += "\nAND ";
604 
605  output += outputlist[i];
606  bindings[bindinglist[i]] =
607  (!outputlist[i].contains("=")) ?
608  QString('%') + field[i] + QString('%') : field[i];
609  }
610 
611  return output.contains("programgenres");
612 }
613 
615 {
616  int pos = m_progList->GetCurrentPos();
617  if (pos >= 0 && pos < (int) m_itemList.size())
618  return m_itemList[pos];
619  return NULL;
620 }
621 
623 {
624  int pos = m_progList->GetCurrentPos();
625  if (pos >= 0 && pos < (int) m_itemList.size())
626  return m_itemList[pos];
627  return NULL;
628 }
629 
631 {
632  ProgramInfo *pi = GetCurrent();
633  if (pi)
634  QuickRecord(pi);
635 }
636 
638 {
639  ProgramInfo *pi = GetCurrent();
640  if (pi)
641  {
644  else
645  EditRecording(pi);
646  }
647 }
648 
650 {
653  else
655 }
656 
658 {
659  ProgramInfo *pi = GetCurrent();
660 
661  if (!pi || !pi->GetRecordingRuleID())
662  return;
663 
664  RecordingRule *record = new RecordingRule();
665  if (!record->LoadByProgram(pi))
666  {
667  delete record;
668  return;
669  }
670 
671  QString message = tr("Delete '%1' %2 rule?").arg(record->m_title)
672  .arg(toString(pi->GetRecordingRuleType()));
673 
674  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
675 
677  popupStack, message, true);
678 
679  okPopup->SetReturnEvent(this, "deleterule");
680  okPopup->SetData(qVariantFromValue(record));
681 
682  if (okPopup->Create())
683  popupStack->AddScreen(okPopup);
684  else
685  delete okPopup;
686 }
687 
689 {
690  ProgramInfo *pi = GetCurrent();
691 
692  if (!pi)
693  return;
694 
695  QString message = tr("Delete this episode of '%1'?").arg(pi->GetTitle());
696 
697  ShowOkPopup(message, this, SLOT(DeleteOldEpisode(bool)), true);
698 }
699 
701 {
702  ProgramInfo *pi = GetCurrent();
703  if (!ok || !pi)
704  return;
705 
706  MSqlQuery query(MSqlQuery::InitCon());
707  query.prepare(
708  "DELETE FROM oldrecorded "
709  "WHERE chanid = :CHANID AND "
710  " starttime = :STARTTIME");
711  query.bindValue(":CHANID", pi->GetChanID());
712  query.bindValue(":STARTTIME", pi->GetScheduledStartTime());
713 
714  if (!query.exec())
715  MythDB::DBError("ProgLister::DeleteOldEpisode", query);
716 
717  ScheduledRecording::RescheduleCheck(*pi, "DeleteOldEpisode");
718  FillItemList(true);
719 }
720 
722 {
723  ProgramInfo *pi = GetCurrent();
724 
725  if (!pi)
726  return;
727 
728  QString message = tr("Delete all episodes of '%1'?").arg(pi->GetTitle());
729 
730  ShowOkPopup(message, this, SLOT(DeleteOldSeries(bool)), true);
731 }
732 
734 {
735  ProgramInfo *pi = GetCurrent();
736  if (!ok || !pi)
737  return;
738 
739  MSqlQuery query(MSqlQuery::InitCon());
740  query.prepare("DELETE FROM oldrecorded "
741  "WHERE title = :TITLE AND future = 0");
742  query.bindValue(":TITLE", pi->GetTitle());
743  if (!query.exec())
744  MythDB::DBError("ProgLister::DeleteOldSeries -- delete", query);
745 
746  // Set the programid to the special value of "**any**" which the
747  // scheduler recognizes to mean the entire series was deleted.
748  RecordingInfo tempri(*pi);
749  tempri.SetProgramID("**any**");
750  ScheduledRecording::RescheduleCheck(tempri, "DeleteOldSeries");
751  FillItemList(true);
752 }
753 
755 {
756  ProgramInfo *pi = GetCurrent();
757 
758  if (!pi)
759  return;
760 
761  QString message = pi->toString(ProgramInfo::kTitleSubtitle, " - ");
762 
763  if (!pi->GetDescription().isEmpty())
764  message += "\n\n" + pi->GetDescription();
765 
766  message += "\n\n\n" + tr("NOTE: removing items from this list will not "
767  "delete any recordings.");
768 
769  QString title = tr("Previously Recorded");
770 
771  MythMenu *menu = new MythMenu(title, message, this, "deletemenu");
772  if (pi->IsDuplicate())
773  menu->AddItem(tr("Allow this episode to re-record"));
774  else
775  menu->AddItem(tr("Never record this episode"));
776  menu->AddItem(tr("Remove this episode from the list"));
777  menu->AddItem(tr("Remove all episodes for this title"));
778  menu->AddItem(tr("Cancel"));
779 
781  MythDialogBox *menuPopup = new MythDialogBox(menu, mainStack, "deletepopup", true);
782 
783  if (menuPopup->Create())
784  mainStack->AddScreen(menuPopup);
785  else
786  delete menuPopup;
787 }
788 
790 {
791  ProgramInfo *pi = GetCurrent();
792  if (pi)
793  {
795  pi->GetScheduledStartTime(),
796  NULL, this, -2);
797  }
798 }
799 
801 {
802  ProgramInfo *pi = GetCurrent();
803  if (pi && m_type != plTitle)
805 }
806 
807 void ProgLister::FillViewList(const QString &view)
808 {
809  m_viewList.clear();
810  m_viewTextList.clear();
811 
812  if (m_type == plChannel) // list by channel
813  {
815  0, true, "channum, chanid");
817 
818  for (uint i = 0; i < channels.size(); ++i)
819  {
820  QString chantext = channels[i].GetFormatted(ChannelInfo::kChannelShort);
821 
822  m_viewList.push_back(QString::number(channels[i].chanid));
823  m_viewTextList.push_back(chantext);
824  }
825 
826  if (!view.isEmpty())
827  m_curView = m_viewList.indexOf(view);
828  }
829  else if (m_type == plCategory) // list by category
830  {
831  QDateTime query_starttime = m_startTime.addSecs(50 -
832  m_startTime.time().second());
833  MSqlQuery query(MSqlQuery::InitCon());
834  query.prepare("SELECT g1.genre, g2.genre "
835  "FROM program "
836  "JOIN programgenres g1 ON "
837  " program.chanid = g1.chanid AND "
838  " program.starttime = g1.starttime "
839  "LEFT JOIN programgenres g2 ON "
840  " g1.chanid = g2.chanid AND "
841  " g1.starttime = g2.starttime "
842  "WHERE program.endtime > :PGILSTART "
843  "GROUP BY g1.genre, g2.genre;");
844  query.bindValue(":PGILSTART", query_starttime);
845 
846  m_useGenres = false;
847 
848  if (query.exec())
849  {
850  QString lastGenre1;
851 
852  while (query.next())
853  {
854  m_useGenres = true;
855 
856  QString genre1 = query.value(0).toString();
857  if (genre1.isEmpty())
858  continue;
859 
860  if (genre1 != lastGenre1)
861  {
862  m_viewList.push_back(genre1);
863  m_viewTextList.push_back(genre1);
864  lastGenre1 = genre1;
865  }
866 
867  QString genre2 = query.value(1).toString();
868  if (genre2.isEmpty() || genre2 == genre1)
869  continue;
870 
871  m_viewList.push_back(genre1 + ":/:" + genre2);
872  m_viewTextList.push_back(" " + genre1 + " / " + genre2);
873  }
874  }
875 
876  if (!m_useGenres)
877  {
878  query.prepare("SELECT category "
879  "FROM program "
880  "WHERE program.endtime > :PGILSTART "
881  "GROUP BY category");
882  query.bindValue(":PGILSTART", query_starttime);
883 
884  if (query.exec())
885  {
886  while (query.next())
887  {
888  QString category = query.value(0).toString();
889  if (category.isEmpty())
890  continue;
891  category = query.value(0).toString();
892  m_viewList.push_back(category);
893  m_viewTextList.push_back(category);
894  }
895  }
896  }
897 
898  if (!view.isEmpty())
899  m_curView = m_viewList.indexOf(view);
900  }
901  else if (m_type == plTitleSearch || m_type == plKeywordSearch ||
903  {
904  MSqlQuery query(MSqlQuery::InitCon());
905  query.prepare("SELECT phrase FROM keyword "
906  "WHERE searchtype = :SEARCHTYPE;");
907  query.bindValue(":SEARCHTYPE", m_searchType);
908 
909  if (query.exec())
910  {
911  while (query.next())
912  {
913  /* The keyword.phrase column uses utf8_bin collation, so
914  * Qt uses QString::fromAscii() for toString(). Explicitly
915  * convert the value using QString::fromUtf8() to prevent
916  * corruption. */
917  QString phrase = QString::fromUtf8(query.value(0)
918  .toByteArray().constData());
919  if (phrase.isEmpty())
920  continue;
921  m_viewList.push_back(phrase);
922  m_viewTextList.push_back(phrase);
923  }
924  }
925 
926  if (!view.isEmpty())
927  {
928  m_curView = m_viewList.indexOf(view);
929 
930  if (m_curView < 0)
931  {
932  QString qphrase = view;
933 
934  MSqlQuery query(MSqlQuery::InitCon());
935  query.prepare("REPLACE INTO keyword (phrase, searchtype)"
936  "VALUES(:VIEW, :SEARCHTYPE );");
937  query.bindValue(":VIEW", qphrase);
938  query.bindValue(":SEARCHTYPE", m_searchType);
939  if (!query.exec())
940  MythDB::DBError("ProgLister::FillViewList -- "
941  "replace keyword", query);
942 
943  m_viewList.push_back(qphrase);
944  m_viewTextList.push_back(qphrase);
945 
946  m_curView = m_viewList.size() - 1;
947  }
948  }
949  else
950  {
951  m_curView = -1;
952  }
953  }
954  else if (m_type == plTitle)
955  {
956  if (!view.isEmpty())
957  {
958  m_viewList.push_back(view);
959  m_viewTextList.push_back(view);
960  m_curView = 0;
961  }
962  else
963  {
964  m_curView = -1;
965  }
966  }
967  else if (m_type == plNewListings)
968  {
969  m_viewList.push_back("all");
970  m_viewTextList.push_back(tr("All"));
971 
972  m_viewList.push_back("premieres");
973  m_viewTextList.push_back(tr("Premieres"));
974 
975  m_viewList.push_back("movies");
976  m_viewTextList.push_back(tr("Movies"));
977 
978  m_viewList.push_back("series");
979  m_viewTextList.push_back(tr("Series"));
980 
981  m_viewList.push_back("specials");
982  m_viewTextList.push_back(tr("Specials"));
983 
984  if (!view.isEmpty())
985  m_curView = m_viewList.indexOf(view);
986  }
987  else if (m_type == plMovies)
988  {
989  m_viewList.push_back(">= 0.0");
990  m_viewTextList.push_back(tr("All"));
991  m_viewList.push_back("= 0.0");
992  m_viewTextList.push_back(tr("Unrated"));
993  m_viewList.push_back(QString("= 10.0"));
994  m_viewTextList.push_back(tr("%n star(s)", "", 10));
995  for (int i = 9; i > 0; i--)
996  {
997  float stars = i / 10.0;
998  m_viewList.push_back(QString(">= %1").arg(stars));
999  m_viewTextList.push_back(tr("%n star(s) and above", "", i));
1000  }
1001 
1002  if (!view.isEmpty())
1003  m_curView = m_viewList.indexOf(view);
1004  }
1005  else if (m_type == plTime)
1006  {
1007  m_curView = 0;
1010  m_viewTextList.push_back(m_viewList[m_curView]);
1011  }
1012  else if (m_type == plSQLSearch)
1013  {
1014  m_curView = 0;
1015  m_viewList.push_back(view);
1016  m_viewTextList.push_back(tr("Power Recording Rule"));
1017  }
1018  else if (m_type == plRecordid)
1019  {
1020  m_curView = 0;
1021 
1022  MSqlQuery query(MSqlQuery::InitCon());
1023  query.prepare("SELECT title FROM record "
1024  "WHERE recordid = :RECORDID");
1025  query.bindValue(":RECORDID", view);
1026 
1027  if (query.exec() && query.next())
1028  {
1029  QString title = query.value(0).toString();
1030  title = query.value(0).toString();
1031  m_viewList.push_back(view);
1032  m_viewTextList.push_back(title);
1033  }
1034  }
1035  else if (m_type == plStoredSearch) // stored searches
1036  {
1037  MSqlQuery query(MSqlQuery::InitCon());
1038  query.prepare("SELECT rulename FROM customexample "
1039  "WHERE search > 0 ORDER BY rulename;");
1040 
1041  if (query.exec())
1042  {
1043  while (query.next())
1044  {
1045  QString rulename = query.value(0).toString();
1046  if (rulename.isEmpty() || rulename.trimmed().isEmpty())
1047  continue;
1048  rulename = query.value(0).toString();
1049  m_viewList.push_back(rulename);
1050  m_viewTextList.push_back(rulename);
1051  }
1052  }
1053  if (!view.isEmpty())
1054  m_curView = m_viewList.indexOf(view);
1055  }
1056  else if (m_type == plPreviouslyRecorded) // previously recorded
1057  {
1058  m_viewList.push_back("sort by time");
1059  m_viewTextList.push_back(tr("Time"));
1060 
1061  m_viewList.push_back("reverse time");
1062  m_viewTextList.push_back(tr("Reverse Time"));
1063 
1064  m_viewList.push_back("sort by title");
1065  m_viewTextList.push_back(tr("Title"));
1066 
1067  m_viewList.push_back("reverse title");
1068  m_viewTextList.push_back(tr("Reverse Title"));
1069 
1070  if (!view.isEmpty())
1071  m_curView = m_viewList.indexOf(view);
1072  }
1073 
1074  if (m_curView >= (int)m_viewList.size())
1075  m_curView = m_viewList.size() - 1;
1076 }
1077 
1078 class plCompare : binary_function<const ProgramInfo*, const ProgramInfo*, bool>
1079 {
1080  public:
1081  virtual bool operator()(const ProgramInfo*, const ProgramInfo*) = 0;
1082  virtual ~plCompare() {}
1083 };
1084 
1085 class plTitleSort : public plCompare
1086 {
1087  public:
1088  bool operator()(const ProgramInfo *a, const ProgramInfo *b)
1089  {
1090  if (a->sortTitle != b->sortTitle)
1091  return (a->sortTitle < b->sortTitle);
1092 
1093  if (a->GetRecordingStatus() == b->GetRecordingStatus())
1094  return a->GetScheduledStartTime() < b->GetScheduledStartTime();
1095 
1096  if (a->GetRecordingStatus() == rsRecording)
1097  return true;
1098  if (b->GetRecordingStatus() == rsRecording)
1099  return false;
1100 
1101  if (a->GetRecordingStatus() == rsWillRecord)
1102  return true;
1103  if (b->GetRecordingStatus() == rsWillRecord)
1104  return false;
1105 
1106  return a->GetScheduledStartTime() < b->GetScheduledStartTime();
1107  }
1108 };
1109 
1111 {
1112  public:
1114 
1115  bool operator()(const ProgramInfo *a, const ProgramInfo *b)
1116  {
1117  if (a->sortTitle != b->sortTitle)
1118  return (a->sortTitle < b->sortTitle);
1119 
1120  if (a->GetProgramID() != b->GetProgramID())
1121  return a->GetProgramID() < b->GetProgramID();
1122 
1123  return a->GetScheduledStartTime() < b->GetScheduledStartTime();
1124  }
1125 };
1126 
1127 class plTimeSort : public plCompare
1128 {
1129  public:
1130  plTimeSort(void) : plCompare() {;}
1131 
1132  bool operator()(const ProgramInfo *a, const ProgramInfo *b)
1133  {
1135  return (a->GetChanID() < b->GetChanID());
1136 
1137  return (a->GetScheduledStartTime() < b->GetScheduledStartTime());
1138  }
1139 };
1140 
1141 void ProgLister::FillItemList(bool restorePosition, bool updateDisp)
1142 {
1144  {
1145  if (!m_titleSort)
1146  {
1147  if (!m_reverseSort)
1148  m_curView = 0;
1149  else
1150  m_curView = 1;
1151  }
1152  else
1153  {
1154  if (!m_reverseSort)
1155  m_curView = 2;
1156  else
1157  m_curView = 3;
1158  }
1159  }
1160 
1161  if (m_curView < 0)
1162  return;
1163 
1164  QString where;
1165  QString qphrase = m_viewList[m_curView];
1166 
1167  MSqlBindings bindings;
1168 
1170  bindings[":PGILSTART"] =
1171  m_startTime.addSecs(50 - m_startTime.time().second());
1172 
1173  if (m_type == plTitle) // per title listings
1174  {
1175  where = "WHERE channel.visible = 1 "
1176  " AND program.endtime > :PGILSTART "
1177  " AND (program.title = :PGILPHRASE0 OR "
1178  " (program.seriesid <> '' AND "
1179  " program.seriesid = :PGILPHRASE1)) ";
1180  bindings[":PGILPHRASE0"] = qphrase;
1181  bindings[":PGILPHRASE1"] = m_extraArg;
1182  }
1183  else if (m_type == plNewListings) // what's new list
1184  {
1185  where = "LEFT JOIN oldprogram ON "
1186  " oldprogram.oldtitle = program.title "
1187  "WHERE channel.visible = 1 "
1188  " AND program.endtime > :PGILSTART "
1189  " AND oldprogram.oldtitle IS NULL "
1190  " AND program.manualid = 0 ";
1191 
1192  if (qphrase == "premieres")
1193  {
1194  where += " AND ( ";
1195  where += " ( program.originalairdate = DATE(";
1196  where += " CONVERT_TZ(program.starttime, 'UTC', 'SYSTEM'))";
1197  where += " AND (program.category = 'Special' ";
1198  where += " OR program.programid LIKE 'EP%0001')) ";
1199  where += " OR (program.category_type='movie' ";
1200  where += " AND program.stars > 0.5 ";
1201  where += " AND program.airdate >= YEAR(NOW()) - 2) ";
1202  where += " ) ";
1203  }
1204  else if (qphrase == "movies")
1205  {
1206  where += " AND program.category_type = 'movie' ";
1207  }
1208  else if (qphrase == "series")
1209  {
1210  where += " AND program.category_type = 'series' ";
1211  }
1212  else if (qphrase == "specials")
1213  {
1214  where += " AND program.category_type = 'tvshow' ";
1215  }
1216  else
1217  {
1218  where += " AND (program.category_type <> 'movie' ";
1219  where += " OR program.airdate >= YEAR(NOW()) - 3) ";
1220  }
1221  }
1222  else if (m_type == plTitleSearch) // keyword search
1223  {
1224  where = "WHERE channel.visible = 1 "
1225  " AND program.endtime > :PGILSTART "
1226  " AND program.title LIKE :PGILLIKEPHRASE0 ";
1227  bindings[":PGILLIKEPHRASE0"] = QString("%") + qphrase + '%';
1228  }
1229  else if (m_type == plKeywordSearch) // keyword search
1230  {
1231  where = "WHERE channel.visible = 1 "
1232  " AND program.endtime > :PGILSTART "
1233  " AND (program.title LIKE :PGILLIKEPHRASE1 "
1234  " OR program.subtitle LIKE :PGILLIKEPHRASE2 "
1235  " OR program.description LIKE :PGILLIKEPHRASE3 ) ";
1236  bindings[":PGILLIKEPHRASE1"] = QString("%") + qphrase + '%';
1237  bindings[":PGILLIKEPHRASE2"] = QString("%") + qphrase + '%';
1238  bindings[":PGILLIKEPHRASE3"] = QString("%") + qphrase + '%';
1239  }
1240  else if (m_type == plPeopleSearch) // people search
1241  {
1242  where = ", people, credits WHERE channel.visible = 1 "
1243  " AND program.endtime > :PGILSTART "
1244  " AND people.name LIKE :PGILPHRASE1 "
1245  " AND credits.person = people.person "
1246  " AND program.chanid = credits.chanid "
1247  " AND program.starttime = credits.starttime";
1248  bindings[":PGILPHRASE1"] = qphrase;
1249  }
1250  else if (m_type == plPowerSearch) // complex search
1251  {
1252  QString powerWhere;
1253  MSqlBindings powerBindings;
1254 
1255  bool genreflag = PowerStringToSQL(qphrase, powerWhere, powerBindings);
1256 
1257  if (!powerWhere.isEmpty())
1258  {
1259  if (genreflag)
1260  where = QString("LEFT JOIN programgenres ON "
1261  "program.chanid = programgenres.chanid AND "
1262  "program.starttime = programgenres.starttime ");
1263 
1264  where += QString("WHERE channel.visible = 1 "
1265  " AND program.endtime > :PGILSTART "
1266  " AND ( ") + powerWhere + " ) ";
1267  MSqlAddMoreBindings(bindings, powerBindings);
1268  }
1269  }
1270  else if (m_type == plSQLSearch) // complex search
1271  {
1272  qphrase.remove(QRegExp("^\\s*AND\\s+", Qt::CaseInsensitive));
1273  where = QString("WHERE channel.visible = 1 "
1274  " AND program.endtime > :PGILSTART "
1275  " AND ( %1 ) ").arg(qphrase);
1276  if (!m_extraArg.isEmpty())
1277  where = m_extraArg + ' ' + where;
1278  }
1279  else if (m_type == plChannel) // list by channel
1280  {
1281  where = "WHERE channel.visible = 1 "
1282  " AND program.endtime > :PGILSTART "
1283  " AND channel.chanid = :PGILPHRASE2 ";
1284  bindings[":PGILPHRASE2"] = qphrase;
1285  }
1286  else if (m_type == plCategory) // list by category
1287  {
1288  if (!m_useGenres)
1289  {
1290  where = "WHERE channel.visible = 1 "
1291  " AND program.endtime > :PGILSTART "
1292  " AND program.category = :PGILPHRASE3 ";
1293  bindings[":PGILPHRASE3"] = qphrase;
1294  }
1295  else if (m_viewList[m_curView].indexOf(":/:") < 0)
1296  {
1297  where = "JOIN programgenres g ON "
1298  " program.chanid = g.chanid AND "
1299  " program.starttime = g.starttime AND "
1300  " genre = :PGILPHRASE4 "
1301  "WHERE channel.visible = 1 "
1302  " AND program.endtime > :PGILSTART ";
1303  bindings[":PGILPHRASE4"] = qphrase;
1304  }
1305  else
1306  {
1307  where = "JOIN programgenres g1 ON "
1308  " program.chanid = g1.chanid AND "
1309  " program.starttime = g1.starttime AND "
1310  " g1.genre = :GENRE1 "
1311  "JOIN programgenres g2 ON "
1312  " program.chanid = g2.chanid AND "
1313  " program.starttime = g2.starttime AND "
1314  " g2.genre = :GENRE2 "
1315  "WHERE channel.visible = 1 "
1316  " AND program.endtime > :PGILSTART ";
1317  bindings[":GENRE1"] = m_viewList[m_curView].section(":/:", 0, 0);
1318  bindings[":GENRE2"] = m_viewList[m_curView].section(":/:", 1, 1);
1319  }
1320  }
1321  else if (m_type == plMovies) // list movies
1322  {
1323  where = "WHERE channel.visible = 1 "
1324  " AND program.endtime > :PGILSTART "
1325  " AND program.category_type = 'movie' "
1326  " AND program.stars " + qphrase + ' ';
1327  }
1328  else if (m_type == plTime) // list by time
1329  {
1330  QDateTime searchTime(m_searchTime);
1331  searchTime.setTime(QTime(searchTime.time().hour(), 0, 0));
1332  bindings[":PGILSEARCHTIME1"] = searchTime;
1333  where = "WHERE channel.visible = 1 "
1334  " AND program.starttime >= :PGILSEARCHTIME1 ";
1335  if (m_titleSort)
1336  {
1337  where += " AND program.starttime < DATE_ADD(:PGILSEARCHTIME2, "
1338  "INTERVAL '1' HOUR) ";
1339  bindings[":PGILSEARCHTIME2"] = bindings[":PGILSEARCHTIME1"];
1340  }
1341  }
1342  else if (m_type == plRecordid) // list by recordid
1343  {
1344  where = "JOIN recordmatch ON "
1345  " (program.starttime = recordmatch.starttime "
1346  " AND program.chanid = recordmatch.chanid) "
1347  "WHERE channel.visible = 1 "
1348  " AND program.endtime > :PGILSTART "
1349  " AND recordmatch.recordid = :PGILPHRASE5 ";
1350  bindings[":PGILPHRASE5"] = qphrase;
1351  }
1352  else if (m_type == plStoredSearch) // stored search
1353  {
1354  MSqlQuery query(MSqlQuery::InitCon());
1355  query.prepare("SELECT fromclause, whereclause FROM customexample "
1356  "WHERE rulename = :RULENAME;");
1357  query.bindValue(":RULENAME", qphrase);
1358 
1359  if (query.exec() && query.next())
1360  {
1361  QString fromc = query.value(0).toString();
1362  QString wherec = query.value(1).toString();
1363 
1364  where = QString("WHERE channel.visible = 1 "
1365  " AND program.endtime > :PGILSTART "
1366  " AND ( %1 ) ").arg(wherec);
1367  if (!fromc.isEmpty())
1368  where = fromc + ' ' + where;
1369  }
1370  }
1371  else if (m_type == plPreviouslyRecorded)
1372  {
1373  if (m_recid && !m_title.isEmpty())
1374  {
1375  where = QString("AND ( recordid = %1 OR title = :MTITLE )")
1376  .arg(m_recid);
1377  bindings[":MTITLE"] = m_title;
1378  }
1379  else if (!m_title.isEmpty())
1380  {
1381  where = QString("AND title = :MTITLE ");
1382  bindings[":MTITLE"] = m_title;
1383  }
1384  else if (m_recid)
1385  {
1386  where = QString("AND recordid = %1 ").arg(m_recid);
1387  }
1388  }
1389 
1390  ProgramInfo selected;
1391  const ProgramInfo *selectedP = (restorePosition) ? GetCurrent() : NULL;
1392  if (selectedP)
1393  {
1394  selected = *selectedP;
1395  selectedP = &selected;
1396  }
1397 
1398  // Save a copy of m_itemList so that deletion of the ProgramInfo
1399  // objects can be deferred until background processing of old
1400  // ProgramInfo objects has completed.
1403  m_itemList.setAutoDelete(false);
1404  m_itemList.clear();
1405  m_itemList.setAutoDelete(true);
1406 
1408  {
1409  LoadFromOldRecorded(m_itemList, where, bindings);
1410  }
1411  else
1412  {
1414  LoadFromProgram(m_itemList, where, bindings, m_schedList);
1415  }
1416 
1417  const QRegExp prefixes(
1418  tr("^(The |A |An )",
1419  "Regular Expression for what to ignore when sorting"));
1420  for (uint i = 0; i < m_itemList.size(); i++)
1421  {
1422  ProgramInfo *s = m_itemList[i];
1423  s->sortTitle = (m_type == plTitle) ? s->GetSubtitle() : s->GetTitle();
1424  s->sortTitle.remove(prefixes);
1425  }
1426 
1427  if (m_type == plNewListings || m_titleSort)
1428  {
1431  {
1432  // Prune to one per title
1433  QString curtitle;
1435  while (it != m_itemList.end())
1436  {
1437  if ((*it)->sortTitle != curtitle)
1438  {
1439  curtitle = (*it)->sortTitle;
1440  ++it;
1441  }
1442  else
1443  {
1444  it = m_itemList.erase(it);
1445  }
1446  }
1447  }
1448  }
1449 
1450  if (!m_titleSort)
1452 
1453  if (updateDisp)
1454  UpdateDisplay(selectedP);
1455 }
1456 
1458 {
1459  if (!m_titleSort)
1460  return kTimeSort;
1462  return kPrevTitleSort;
1463  return kTitleSort;
1464 }
1465 
1466 void ProgLister::SortList(SortBy sortby, bool reverseSort)
1467 {
1468  if (reverseSort)
1469  {
1470  if (kTimeSort == sortby)
1471  stable_sort(m_itemList.rbegin(), m_itemList.rend(), plTimeSort());
1472  else if (kPrevTitleSort == sortby)
1473  stable_sort(m_itemList.rbegin(), m_itemList.rend(),
1474  plPrevTitleSort());
1475  else
1476  stable_sort(m_itemList.rbegin(), m_itemList.rend(), plTitleSort());
1477  }
1478  else
1479  {
1480  if (kTimeSort == sortby)
1481  stable_sort(m_itemList.begin(), m_itemList.end(), plTimeSort());
1482  else if (kPrevTitleSort == sortby)
1483  stable_sort(m_itemList.begin(), m_itemList.end(),
1484  plPrevTitleSort());
1485  else
1486  stable_sort(m_itemList.begin(), m_itemList.end(), plTitleSort());
1487  }
1488 }
1489 
1491 {
1492  InfoMap infoMap;
1493  ProgramInfo pginfo;
1494  pginfo.ToMap(infoMap);
1495  ResetMap(infoMap);
1496 
1497  if (m_positionText)
1498  m_positionText->Reset();
1499 }
1500 
1502 {
1503  int offset = 0;
1504 
1505  if (selected)
1507 
1508  m_progList->Reset();
1509 
1510  if (m_messageText)
1512 
1514 
1515  if (m_curviewText && m_curView >= 0)
1517 
1518  UpdateButtonList();
1519 
1520  if (selected)
1521  RestoreSelection(selected, offset);
1522 }
1523 
1525  int selectedOffset)
1526 {
1527  plCompare *comp;
1528  if (!m_titleSort)
1529  comp = new plTimeSort();
1530  else if (m_type == plPreviouslyRecorded)
1531  comp = new plPrevTitleSort();
1532  else
1533  comp = new plTitleSort();
1534 
1535  int i;
1536  for (i = m_itemList.size() - 2; i >= 0; i--)
1537  {
1538  bool dobreak;
1539  if (m_reverseSort)
1540  dobreak = comp->operator()(selected, m_itemList[i]);
1541  else
1542  dobreak = comp->operator()(m_itemList[i], selected);
1543  if (dobreak)
1544  break;
1545  }
1546 
1547  delete comp;
1548 
1549  m_progList->SetItemCurrent(i + 1, i + 1 - selectedOffset);
1550 }
1551 
1553 {
1554  ProgramInfo *pginfo = qVariantValue<ProgramInfo*>(item->GetData());
1555 
1556  if (item->GetText("is_item_initialized").isNull())
1557  {
1558  InfoMap infoMap;
1559  pginfo->ToMap(infoMap);
1560 
1561  QString state = toUIState(pginfo->GetRecordingStatus());
1562  if ((state == "warning") && (plPreviouslyRecorded == m_type))
1563  state = "disabled";
1564 
1565  item->SetTextFromMap(infoMap, state);
1566 
1567  if (m_type == plTitle)
1568  {
1569  QString tempSubTitle = pginfo->GetSubtitle();
1570  if (tempSubTitle.trimmed().isEmpty())
1571  tempSubTitle = pginfo->GetTitle();
1572  item->SetText(tempSubTitle, "titlesubtitle", state);
1573  }
1574 
1575  item->DisplayState(QString::number(pginfo->GetStars(10)),
1576  "ratingstate");
1577 
1578  item->DisplayState(state, "status");
1579 
1580  // Mark this button list item as initialized.
1581  item->SetText("yes", "is_item_initialized");
1582  }
1583 }
1584 
1586 {
1588  for (; it != m_itemList.end(); ++it)
1589  new MythUIButtonListItem(m_progList, "", qVariantFromValue(*it));
1591 
1592  if (m_positionText)
1593  {
1595  tr("%1 of %2", "Current position in list where %1 is the "
1596  "position, %2 is the total count")
1597  .arg(QLocale::system().toString(m_progList->IsEmpty() ? 0 : m_progList->GetCurrentPos() + 1))
1598  .arg(QLocale::system().toString(m_progList->GetCount())));
1599  }
1600 }
1601 
1603 {
1604  if (!item)
1605  {
1607  return;
1608  }
1609 
1610  ProgramInfo *pginfo = qVariantValue<ProgramInfo*> (item->GetData());
1611  if (!pginfo)
1612  {
1614  return;
1615  }
1616 
1617  InfoMap infoMap;
1618  pginfo->ToMap(infoMap);
1619  SetTextFromMap(infoMap);
1620 
1621  if (m_positionText)
1622  {
1624  tr("%1 of %2", "Current position in list where %1 is the "
1625  "position, %2 is the total count")
1626  .arg(QLocale::system().toString(m_progList->IsEmpty() ? 0 : m_progList->GetCurrentPos() + 1))
1627  .arg(QLocale::system().toString(m_progList->GetCount())));
1628  }
1629 
1630  MythUIStateType *ratingState = dynamic_cast<MythUIStateType*>
1631  (GetChild("ratingstate"));
1632 
1633  if (ratingState)
1634  {
1635  QString rating = QString::number(pginfo->GetStars(10));
1636  ratingState->DisplayState(rating);
1637  }
1638 }
1639 
1640 void ProgLister::customEvent(QEvent *event)
1641 {
1642  bool needUpdate = false;
1643 
1644  if (event->type() == DialogCompletionEvent::kEventType)
1645  {
1647 
1648  QString resultid = dce->GetId();
1649  QString resulttext = dce->GetResultText();
1650  int buttonnum = dce->GetResult();
1651 
1652  if (resultid == "sortmenu")
1653  {
1654  switch (buttonnum)
1655  {
1656  case 0:
1658  needUpdate = true;
1659  break;
1660  case 1:
1661  m_titleSort = true;
1662  m_reverseSort = false;
1663  needUpdate = true;
1664  break;
1665  case 2:
1666  m_titleSort = false;
1668  needUpdate = true;
1669  break;
1670  }
1671  }
1672  else if (resultid == "deletemenu")
1673  {
1674  switch (buttonnum)
1675  {
1676  case 0:
1677  {
1678  ProgramInfo *pi = GetCurrent();
1679  if (pi)
1680  {
1681  RecordingInfo ri(*pi);
1682  if (ri.IsDuplicate())
1683  ri.ForgetHistory();
1684  else
1685  ri.SetDupHistory();
1686  *pi = ri;
1687  }
1688  break;
1689  }
1690  case 1:
1692  break;
1693  case 2:
1695  break;
1696  }
1697  }
1698  else if (resultid == "deleterule")
1699  {
1700  RecordingRule *record =
1701  qVariantValue<RecordingRule *>(dce->GetData());
1702  if (record && buttonnum > 0 && !record->Delete())
1703  {
1704  LOG(VB_GENERAL, LOG_ERR, LOC +
1705  "Failed to delete recording rule");
1706  }
1707  if (record)
1708  delete record;
1709  }
1710  else
1711  {
1713  }
1714  }
1715  else if (event->type() == ScreenLoadCompletionEvent::kEventType)
1716  {
1718  QString id = slce->GetId();
1719 
1720  if (id == objectName())
1721  {
1722  CloseBusyPopup(); // opened by LoadInBackground()
1723  UpdateDisplay();
1724 
1725  if (m_curView < 0 && m_type != plPreviouslyRecorded)
1727  }
1728  }
1729  else if ((MythEvent::Type)(event->type()) == MythEvent::MythEventMessage)
1730  {
1731  MythEvent *me = (MythEvent *)event;
1732  QString message = me->Message();
1733 
1734  if (m_allowViewDialog && message == "CHOOSE_VIEW")
1736  else if (message == "SCHEDULE_CHANGE")
1737  needUpdate = true;
1738  }
1739 
1740  if (needUpdate)
1741  FillItemList(true);
1742 }