MythTV  master
progfind.cpp
Go to the documentation of this file.
1 
2 #include "progfind.h"
3 
4 // Qt
5 #include <QDateTime>
6 #include <QCoreApplication>
7 #include <QStringList>
8 #include <QEvent>
9 #include <QList> // for QList
10 #include <QRect> // for QRect
11 
12 // MythTV
13 #include "mythdb.h"
14 #include "mythdbcon.h"
15 #include "mythcorecontext.h"
16 #include "programtypes.h" // for RecStatus
17 #include "tv_play.h"
18 #include "tv_actions.h" // for ACTION_CHANNELSEARCH
19 
20 // MythUI
21 #include "mythuitext.h"
22 #include "mythuibuttonlist.h"
23 #include "mythuibutton.h"
24 #include "mythscreenstack.h"
25 #include "mythmainwindow.h"
26 #include "mythuiutils.h" // for UIUtilE, UIUtilW
27 
28 // mythfrontend
29 #include "guidegrid.h"
30 
31 #define LOC QString("ProgFinder: ")
32 #define LOC_ERR QString("ProgFinder, Error: ")
33 #define LOC_WARN QString("ProgFinder, Warning: ")
34 
35 void RunProgramFinder(TV *player, bool embedVideo, bool allowEPG)
36 {
37  // Language specific progfinder, if needed
39  ProgFinder *programFind = nullptr;
40  if (gCoreContext->GetLanguage() == "ja")
41  programFind = new JaProgFinder(mainStack, allowEPG, player, embedVideo);
42  else if (gCoreContext->GetLanguage() == "he")
43  programFind = new HeProgFinder(mainStack, allowEPG, player, embedVideo);
44  else if (gCoreContext->GetLanguage() == "ru")
45  programFind = new RuProgFinder(mainStack, allowEPG, player, embedVideo);
46  else // default
47  programFind = new ProgFinder(mainStack, allowEPG, player, embedVideo);
48 
49  if (programFind->Create())
50  mainStack->AddScreen(programFind, (player == nullptr));
51  else
52  delete programFind;
53 }
54 
56 {
57  if (!LoadWindowFromXML("schedule-ui.xml", "programfind", this))
58  return false;
59 
60  bool err = false;
61  UIUtilE::Assign(this, m_alphabetList, "alphabet", &err);
62  UIUtilE::Assign(this, m_showList, "shows", &err);
63  UIUtilE::Assign(this, m_timesList, "times", &err);
64 
65  UIUtilW::Assign(this, m_help1Text, "help1text");
66  UIUtilW::Assign(this, m_help2Text, "help2text");
67  UIUtilW::Assign(this, m_searchText, "search");
68 
69  if (err)
70  {
71  LOG(VB_GENERAL, LOG_ERR, "Cannot load screen 'programfind'");
72  return false;
73  }
74 
75  m_alphabetList->SetLCDTitles(tr("Starts With"), "");
76  m_showList->SetLCDTitles(tr("Programs"), "");
77  m_timesList->SetLCDTitles(tr("Times"), "buttontext");
78 
81 
82  if (m_player)
83  m_player->StartEmbedding(QRect());
84 
85  return true;
86 }
87 
88 void ProgFinder::Init(void)
89 {
90  m_allowKeypress = true;
91 
93 
95 
96  connect(m_timesList, SIGNAL(itemSelected(MythUIButtonListItem*)),
97  SLOT(updateInfo()));
98  connect(m_timesList, SIGNAL(itemClicked(MythUIButtonListItem*)),
99  SLOT(select()));
100  connect(m_timesList, SIGNAL(LosingFocus()), SLOT(timesListLosingFocus()));
101  connect(m_timesList, SIGNAL(TakingFocus()), SLOT(timesListTakeFocus()));
102 
103  connect(m_alphabetList, SIGNAL(itemSelected(MythUIButtonListItem*)),
105  connect(m_alphabetList, SIGNAL(itemClicked(MythUIButtonListItem*)),
106  SLOT(select()));
107 
108  connect(m_showList, SIGNAL(TakingFocus()), SLOT(showListTakeFocus()));
109  connect(m_showList, SIGNAL(itemClicked(MythUIButtonListItem*)),
110  SLOT(select()));
111 
113 }
114 
116 {
118 
119  // if we have a player and we are returning to it we need
120  // to tell it to stop embedding and return to fullscreen
121  if (m_player && m_allowEPG)
122  {
123  QString message = QString("PROGFINDER_EXITING");
124  qApp->postEvent(m_player, new MythEvent(message));
125  }
126 }
127 
129 {
130  if (!item || (m_currentLetter == item->GetText()))
131  return;
132 
133  m_currentLetter = item->GetText();
134 
135  updateShowList();
136  updateInfo();
137 }
138 
140 {
141  m_timesList->Reset();
142 }
143 
145 {
146  updateInfo();
147 }
148 
150 {
151  selectShowData("", 0);
152  updateInfo();
153 }
154 
155 bool ProgFinder::keyPressEvent(QKeyEvent *event)
156 {
157  if (!m_allowKeypress)
158  return true;
159 
160  m_allowKeypress = false;
161 
162  if (GetFocusWidget() && GetFocusWidget()->keyPressEvent(event))
163  {
164  m_allowKeypress = true;
165  return true;
166  }
167 
168  QStringList actions;
169  bool handled = GetMythMainWindow()->TranslateKeyPress("TV Frontend", event, actions);
170 
171  for (int i = 0; i < actions.size() && !handled; ++i)
172  {
173  QString action = actions[i];
174  handled = true;
175 
176  if (action == "EDIT")
177  EditScheduled();
178  else if (action == "CUSTOMEDIT")
179  EditCustom();
180  else if (action == "UPCOMING")
181  ShowUpcoming();
182  else if (action == "PREVRECORDED")
183  ShowPrevious();
184  else if (action == "DETAILS" || action == "INFO")
185  ShowDetails();
186  else if (action == "TOGGLERECORD")
187  QuickRecord();
188  else if (action == "GUIDE" || action == "4")
189  ShowGuide();
190  else if (action == ACTION_CHANNELSEARCH)
192  else if (action == "ESCAPE")
193  {
194  // don't fade the screen if we are returning to the player
195  if (m_player && m_allowEPG)
196  GetScreenStack()->PopScreen(this, false);
197  else
198  GetScreenStack()->PopScreen(this, true);
199  }
200  else
201  handled = false;
202  }
203 
204  if (!handled && MythScreenType::keyPressEvent(event))
205  handled = true;
206 
207  m_allowKeypress = true;
208 
209  return handled;
210 }
211 
213 {
214  QString label = tr("Options");
215 
216  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
217  MythDialogBox *menuPopup = new MythDialogBox(label, popupStack, "menuPopup");
218 
219  if (menuPopup->Create())
220  {
221  menuPopup->SetReturnEvent(this, "menu");
222 
223  if (!m_searchStr.isEmpty())
224  menuPopup->AddButton(tr("Clear Search"));
225  menuPopup->AddButton(tr("Edit Search"));
226  if (GetFocusWidget() == m_timesList && m_timesList->GetCount() > 0)
227  {
228  menuPopup->AddButton(tr("Toggle Record"));
229  menuPopup->AddButton(tr("Program Details"));
230  menuPopup->AddButton(tr("Upcoming"));
231  menuPopup->AddButton(tr("Previously Recorded"));
232  menuPopup->AddButton(tr("Custom Edit"));
233  menuPopup->AddButton(tr("Program Guide"));
234  menuPopup->AddButton(tr("Channel Search"));
235  }
236 
237  popupStack->AddScreen(menuPopup);
238  }
239  else
240  {
241  delete menuPopup;
242  }
243 }
244 
245 void ProgFinder::customEvent(QEvent *event)
246 {
247  if (event->type() == MythEvent::MythEventMessage)
248  {
249  MythEvent *me = static_cast<MythEvent *>(event);
250  const QString& message = me->Message();
251 
252  if (message == "SCHEDULE_CHANGE")
253  {
254  if (GetFocusWidget() == m_timesList)
255  {
257  if (curPick)
258  selectShowData(curPick->GetTitle(),
260  }
261  }
262  }
263  else if (event->type() == DialogCompletionEvent::kEventType)
264  {
266 
267  QString resultid = dce->GetId();
268  QString resulttext = dce->GetResultText();
269 
270  if (resultid == "menu")
271  {
272  if (resulttext == tr("Clear Search"))
273  {
274  m_searchStr.clear();
275  if (m_searchText)
277  updateShowList();
279  }
280  else if (resulttext == tr("Edit Search"))
281  {
282  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
283  SearchInputDialog *textInput =
284  new SearchInputDialog(popupStack, m_searchStr);
285 
286  if (textInput->Create())
287  {
288  textInput->SetReturnEvent(this, "searchtext");
289  popupStack->AddScreen(textInput);
290  }
291  }
292  else if (resulttext == tr("Toggle Record"))
293  {
294  QuickRecord();
295  }
296  else if (resulttext == tr("Program Details"))
297  {
298  ShowDetails();
299  }
300  else if (resulttext == tr("Upcoming"))
301  {
302  ShowUpcoming();
303  }
304  else if (resulttext == tr("Previously Recorded"))
305  {
306  ShowPrevious();
307  }
308  else if (resulttext == tr("Custom Edit"))
309  {
310  EditCustom();
311  }
312  else if (resulttext == tr("Program Guide"))
313  {
314  ShowGuide();
315  }
316  else if (resulttext == tr("Channel Search"))
317  {
319  }
320  }
321  else if (resultid == "searchtext")
322  {
323  m_searchStr = resulttext;
324  if (m_searchText)
326  updateShowList();
328  }
329  else
331  }
332 }
333 
335 {
336  if (m_help1Text)
337  m_help1Text->Reset();
338  if (m_help2Text)
339  m_help2Text->Reset();
340 
342  {
343  QString title;
344  QString description;
345 
346  if (m_showList->GetCount() == 0)
347  {
348  if (m_help1Text)
349  m_help1Text->SetText(tr("No Programs"));
350  if (m_help2Text)
351  m_help2Text->SetText(tr("There are no available programs under this search. "
352  "Please select another search."));
353  }
354  else
355  {
356  if (m_help1Text)
357  m_help1Text->SetText(tr("Select a letter..."));
358  if (m_help2Text)
359  m_help2Text->SetText(tr("Pick the first letter of the program name, "
360  "then press SELECT or the right arrow."));
361  }
362 
364  }
365  else if (GetFocusWidget() == m_showList)
366  {
367  if (m_help1Text)
368  m_help1Text->SetText(tr("Select a program..."));
369  if (m_help2Text)
370  m_help2Text->SetText(tr("Select the title of the program you wish to find. "
371  "When finished return with the left arrow key. "
372  "Press SELECT to schedule a recording."));
373 
375  }
376  else if (GetFocusWidget() == m_timesList)
377  {
378  if (m_showData.empty())
379  {
381  if (m_help1Text)
382  m_help1Text->SetText(tr("No Programs"));
383  if (m_help2Text)
384  m_help2Text->SetText(tr("There are no available programs under "
385  "this search. Please select another "
386  "search."));
387  }
388  else
389  {
390  InfoMap infoMap;
391  m_showData[m_timesList->GetCurrentPos()]->ToMap(infoMap);
392  SetTextFromMap(infoMap);
393  m_infoMap = infoMap;
394  }
395  }
396 }
397 
399 {
400  if (m_allowEPG)
401  {
402  QString startchannel = gCoreContext->GetSetting("DefaultTVChannel");
403  uint startchanid = 0;
404  QDateTime starttime;
405 
406  if (GetFocusWidget() == m_timesList)
407  {
409  startchannel = pginfo->GetChanNum();
410  startchanid = pginfo->GetChanID();
411  starttime = pginfo->GetScheduledStartTime();
412  }
413  GuideGrid::RunProgramGuide(startchanid, startchannel, starttime,
414  m_player, m_embedVideo, false, -2);
415  }
416 }
417 
419 {
420  if (GetFocusWidget() == m_timesList)
421  EditRecording();
422  else if (GetFocusWidget() == m_alphabetList && m_showList->GetCount())
424  else if (GetFocusWidget() == m_showList)
426 }
427 
429 {
430  InfoMap infoMap;
431 
432  m_timesList->Reset();
433 
434  if (!m_showData.empty())
435  {
436  QString itemText;
437  QDateTime starttime;
438  for (size_t i = 0; i < m_showData.size(); ++i)
439  {
440  starttime = m_showData[i]->GetScheduledStartTime();
441  itemText = MythDate::toString(starttime,
443 
444  MythUIButtonListItem *item =
446 
447  QString state = RecStatus::toUIState(m_showData[i]->GetRecordingStatus());
448  item->SetText(itemText, "buttontext", state);
449  item->DisplayState(state, "status");
450 
451  m_showData[i]->ToMap(infoMap);
452  item->SetTextFromMap(infoMap, state);
453  }
454  }
455 }
456 
458 {
459  m_showNames.clear();
460 
461  QString thequery;
462  MSqlBindings bindings;
463 
464  MSqlQuery query(MSqlQuery::InitCon());
465  whereClauseGetSearchData(thequery, bindings);
466 
467  query.prepare(thequery);
468  query.bindValues(bindings);
469  if (!query.exec())
470  {
471  MythDB::DBError("getShowNames", query);
472  return;
473  }
474 
475  QString data;
476  while (query.next())
477  {
478  data = query.value(0).toString();
479 
480  if (formatSelectedData(data))
481  m_showNames[data.toLower()] = data;
482  }
483 }
484 
486 {
487  m_showList->Reset();
488 
489  if (m_showNames.isEmpty())
490  getShowNames();
491 
492  ShowName::Iterator it;
493  for (it = m_showNames.begin(); it != m_showNames.end(); ++it)
494  {
495  QString tmpProgData = *it;
496  restoreSelectedData(tmpProgData);
497  new MythUIButtonListItem(m_showList, tmpProgData);
498  }
499 
500  m_showNames.clear();
501 }
502 
503 void ProgFinder::selectShowData(QString progTitle, int newCurShow)
504 {
505  progTitle = m_showList->GetValue();
506 
507  QDateTime progStart = MythDate::current();
508 
509  MSqlBindings bindings;
510  QString querystr = "WHERE program.title = :TITLE "
511  " AND program.endtime > :ENDTIME "
512  " AND channel.visible = 1 ";
513  bindings[":TITLE"] = progTitle;
514  bindings[":ENDTIME"] = progStart.addSecs(50 - progStart.time().second());
515 
517  LoadFromProgram(m_showData, querystr, bindings, m_schedList);
518 
519  updateTimesList();
520 
521  m_timesList->SetItemCurrent(newCurShow);
522 }
523 
525 {
526  for (int charNum = 48; charNum < 91; ++charNum)
527  {
528  if (charNum == 58)
529  charNum = 65;
530 
531  new MythUIButtonListItem(m_alphabetList, QString((char)charNum));
532  }
533 
534  new MythUIButtonListItem(m_alphabetList, QString('@'));
535 }
536 
537 void ProgFinder::whereClauseGetSearchData(QString &where, MSqlBindings &bindings)
538 {
539  QDateTime progStart = MythDate::current();
540  QString searchChar = m_alphabetList->GetValue();
541 
542  if (searchChar.isEmpty())
543  searchChar = "A";
544 
545  if (searchChar.contains('@'))
546  {
547  where = "SELECT DISTINCT title FROM program "
548  "LEFT JOIN channel ON program.chanid = channel.chanid "
549  "WHERE channel.visible = 1 AND "
550  "( title NOT REGEXP '^[A-Z0-9]' AND "
551  " title NOT REGEXP '^The [A-Z0-9]' AND "
552  " title NOT REGEXP '^A [A-Z0-9]' AND "
553  " title NOT REGEXP '^An [A-Z0-9]' AND "
554  " starttime > :STARTTIME ) ";
555  if (!m_searchStr.isEmpty())
556  {
557  where += "AND title LIKE :SEARCH ";
558  bindings[":SEARCH"] = '%' + m_searchStr + '%';
559  }
560 
561  where += "ORDER BY title;";
562 
563  bindings[":STARTTIME"] =
564  progStart.addSecs(50 - progStart.time().second());
565  }
566  else
567  {
568  QString one = searchChar + '%';
569  QString two = QString("The ") + one;
570  QString three = QString("A ") + one;
571  QString four = QString("An ") + one;
572 
573  where = "SELECT DISTINCT title FROM program "
574  "LEFT JOIN channel ON program.chanid = channel.chanid "
575  "WHERE channel.visible = 1 "
576  "AND ( title LIKE :ONE OR title LIKE :TWO "
577  " OR title LIKE :THREE "
578  " OR title LIKE :FOUR ) "
579  "AND starttime > :STARTTIME ";
580  if (!m_searchStr.isEmpty())
581  where += "AND title LIKE :SEARCH ";
582 
583  where += "ORDER BY title;";
584 
585  bindings[":ONE"] = one;
586  bindings[":TWO"] = two;
587  bindings[":THREE"] = three;
588  bindings[":FOUR"] = four;
589  bindings[":STARTTIME"] =
590  progStart.addSecs(50 - progStart.time().second());
591 
592  if (!m_searchStr.isEmpty())
593  bindings[":SEARCH"] = '%' + m_searchStr + '%';
594  }
595 }
596 
598 {
599  bool retval = true;
600  QString searchChar = m_alphabetList->GetValue();
601 
602  if (searchChar == "T")
603  {
604  if (!data.startsWith("The ") && !data.startsWith("A "))
605  {
606  // nothing, use as is
607  }
608  else if (data.startsWith("The T"))
609  data = data.mid(4) + ", The";
610  else if (data.startsWith("A T"))
611  data = data.mid(2) + ", A";
612  else
613  {
614  // don't add
615  retval = false;
616  }
617  }
618  else if (searchChar == "A")
619  {
620  if (!data.startsWith("The ") && !data.startsWith("A "))
621  {
622  // nothing, use as is
623  }
624  else if (data.startsWith("The A"))
625  data = data.mid(4) + ", The";
626  else if (data.startsWith("A A"))
627  data = data.mid(2) + ", A";
628  else if (data.startsWith("An A"))
629  data = data.mid(3) + ", An";
630  else
631  {
632  // don't add
633  retval = false;
634  }
635  }
636  else
637  {
638  if (data.startsWith("The "))
639  data = data.mid(4) + ", The";
640  else if (data.startsWith("A "))
641  data = data.mid(2) + ", A";
642  else if (data.startsWith("An "))
643  data = data.mid(3) + ", An";
644  }
645 
646  return retval;
647 }
648 
649 bool ProgFinder::formatSelectedData(QString& data, int charNum)
650 {
651  bool retval = true;
652 
653  if (charNum == 29 || charNum == 10)
654  {
655  if (data.startsWith("The T") && charNum == 29)
656  data = data.mid(4) + ", The";
657  else if (data.startsWith("The A") && charNum == 10)
658  data = data.mid(4) + ", The";
659  else if (data.startsWith("A T") && charNum == 29)
660  data = data.mid(2) + ", A";
661  else if (data.startsWith("A A") && charNum == 10)
662  data = data.mid(2) + ", A";
663  else if (data.startsWith("An A") && charNum == 10)
664  data = data.mid(3) + ", An";
665  else if (!data.startsWith("The ") && !data.startsWith("A "))
666  {
667  // use as is
668  }
669  else
670  {
671  // don't add
672  retval = false;
673  }
674  }
675  else
676  {
677  if (data.startsWith("The "))
678  data = data.mid(4) + ", The";
679  if (data.startsWith("A "))
680  data = data.mid(2) + ", A";
681  if (data.startsWith("An "))
682  data = data.mid(3) + ", An";
683  }
684 
685  return retval;
686 }
687 
689 {
690  if (data.endsWith(", The"))
691  data = "The " + data.left(data.length() - 5);
692  if (data.endsWith(", A"))
693  data = "A " + data.left(data.length() - 3);
694 }
695 
697 // Japanese specific program finder
698 
699 // japanese HIRAGANA list and more
700 const QChar JaProgFinder::s_searchChars[] =
701 {
702  // "あ", "か", "さ", "た",
703  QChar(0x3042), QChar(0x304b), QChar(0x3055), QChar(0x305f),
704  // "な", "は", "ま", "や",
705  QChar(0x306a), QChar(0x306f), QChar(0x307e), QChar(0x3084),
706  // "ら", "わ", "英", "数",
707  QChar(0x3089), QChar(0x308f), QChar(0x82f1), QChar(0x6570),
708  0,
709 };
710 
712  TV *player, bool embedVideo)
713  : ProgFinder(parentStack, gg, player, embedVideo)
714 {
717  ;
718 }
719 
721 {
722  for (int charNum = 0; charNum < m_numberOfSearchChars; ++charNum)
723  {
724  new MythUIButtonListItem(m_alphabetList, QString(s_searchChars[charNum]));
725  }
726 }
727 
728 // search title_pronounce
729 // we hope japanese HIRAGANA and alphabets, numerics is inserted
730 // these character must required ZENKAKU
731 // because query work not fine, if mysql's default charset latin1
733 {
734  QDateTime progStart = MythDate::current();
735  int charNum = m_alphabetList->GetCurrentPos();
736 
737  where = "SELECT DISTINCT title FROM program "
738  "LEFT JOIN channel ON program.chanid = channel.chanid "
739  "WHERE channel.visible = 1 ";
740 
741  switch (charNum) {
742  case 0:
743  where += "AND ( title_pronounce >= 'あ' AND title_pronounce <= 'お') ";
744  break;
745  case 1:
746  where += "AND ( title_pronounce >= 'か' AND title_pronounce <= 'ご') ";
747  break;
748  case 2:
749  where += "AND ( title_pronounce >= 'さ' AND title_pronounce <= 'そ') ";
750  break;
751  case 3:
752  where += "AND ( title_pronounce >= 'た' AND title_pronounce <= 'ど') ";
753  break;
754  case 4:
755  where += "AND ( title_pronounce >= 'な' AND title_pronounce <= 'の') ";
756  break;
757  case 5:
758  where += "AND ( title_pronounce >= 'は' AND title_pronounce <= 'ぽ') ";
759  break;
760  case 6:
761  where += "AND ( title_pronounce >= 'ま' AND title_pronounce <= 'も') ";
762  break;
763  case 7:
764  where += "AND ( title_pronounce >= 'や' AND title_pronounce <= 'よ') ";
765  break;
766  case 8:
767  where += "AND ( title_pronounce >= 'ら' AND title_pronounce <= 'ろ') ";
768  break;
769  case 9:
770  where += "AND ( title_pronounce >= 'わ' AND title_pronounce <= 'ん') ";
771  break;
772  case 10:
773  where += "AND ( title_pronounce >= 'A' AND title_pronounce <= 'z') ";
774  break;
775  case 11:
776  where += "AND ( title_pronounce >= '0' AND title_pronounce <= '9') ";
777  break;
778  }
779 
780  where += "AND starttime > :STARTTIME ";
781 
782  if (!m_searchStr.isEmpty())
783  {
784  where += "AND title_pronounce LIKE :SEARCH ";
785  bindings[":SEARCH"] = '%' + m_searchStr + '%';
786  }
787 
788  where += "ORDER BY title_pronounce;";
789 
790  bindings[":STARTTIME"] = progStart.addSecs(50 - progStart.time().second());
791 }
792 
793 
795 {
796  (void)data;
797  return true;
798 }
799 
800 bool JaProgFinder::formatSelectedData(QString& data, int charNum)
801 {
802  (void)data;
803  (void)charNum;
804  return true;
805 }
806 
808 {
809  (void)data;
810 }
811 
812 // Hebrew specific program finder
813 
814 // Hebrew alphabet list and more
815 const QChar HeProgFinder::s_searchChars[] =
816 {
817  // "א", "ב", "ג", "ד",
818  QChar(0x5d0), QChar(0x5d1), QChar(0x5d2), QChar(0x5d3),
819  // "ה", "ו", "ז", "ח",
820  QChar(0x5d4), QChar(0x5d5), QChar(0x5d6), QChar(0x5d7),
821  // "ט", "י", "כ", "ל",
822  QChar(0x5d8), QChar(0x5d9), QChar(0x5db), QChar(0x5dc),
823  // "מ", "נ", "ס", "ע",
824  QChar(0x5de), QChar(0x5e0), QChar(0x5e1), QChar(0x5e2),
825  // "פ", "צ", "ק", "ר",
826  QChar(0x5e4), QChar(0x5e6), QChar(0x5e7), QChar(0x5e8),
827  // "ש", "ת", "E", "#",
828  QChar(0x5e9), QChar(0x5ea), QChar('E'), QChar('#'),
829  QChar(0),
830 };
831 
833  TV *player, bool embedVideo)
834  : ProgFinder(parentStack, gg, player, embedVideo)
835 {
838  ;
839 }
840 
842 {
843  for (int charNum = 0; charNum < m_numberOfSearchChars; ++charNum)
844  {
845  new MythUIButtonListItem(m_alphabetList, QString(s_searchChars[charNum]));
846  }
847 }
848 
849 // search by hebrew aleph-bet
850 // # for all numbers, E for all latin
852 {
853  QDateTime progStart = MythDate::current();
854  QString searchChar = m_alphabetList->GetValue();
855 
856  if (searchChar.isEmpty())
857  searchChar = s_searchChars[0];
858 
859  where = "SELECT DISTINCT title FROM program "
860  "LEFT JOIN channel ON program.chanid = channel.chanid "
861  "WHERE channel.visible = 1 ";
862 
863  if (searchChar.contains('E'))
864  {
865  where += "AND ( title REGEXP '^[A-Z]') ";
866  }
867  else if (searchChar.contains('#'))
868  {
869  where += "AND ( title REGEXP '^[0-9]') ";
870  }
871  else
872  {
873  QString one = searchChar + '%';
874  bindings[":ONE"] = one;
875  where += "AND ( title LIKE :ONE ) ";
876  }
877 
878  where += "AND starttime > :STARTTIME ";
879 
880  if (!m_searchStr.isEmpty())
881  {
882  where += "AND title LIKE :SEARCH ";
883  bindings[":SEARCH"] = '%' + m_searchStr + '%';
884  }
885 
886  where += "ORDER BY title;";
887 
888  bindings[":STARTTIME"] = progStart.addSecs(50 - progStart.time().second());
889 }
890 
892 {
893  (void)data;
894  return true;
895 }
896 
897 bool HeProgFinder::formatSelectedData(QString& data, int charNum)
898 {
899  (void)data;
900  (void)charNum;
901  return true;
902 }
903 
905 {
906  (void)data;
907 }
908 
910 
911 // Cyrrilic specific program finder
912 // Cyrrilic alphabet list and more
913 const QChar RuProgFinder::s_searchChars[] =
914 {
915  // "А", "Б", "В", "Г",
916  QChar(0x410), QChar(0x411), QChar(0x412), QChar(0x413),
917  // "Д", "Е", "Ё", "Ж",
918  QChar(0x414), QChar(0x415), QChar(0x401), QChar(0x416),
919  // "З", "И", "Й", "К",
920  QChar(0x417), QChar(0x418), QChar(0x419), QChar(0x41a),
921  // "Л", "М", "Н", "О",
922  QChar(0x41b), QChar(0x41c), QChar(0x41d), QChar(0x41e),
923  // "П", "Р", "С", "Т",
924  QChar(0x41f), QChar(0x420), QChar(0x421), QChar(0x422),
925  // "У", "Ф", "Х", "Ц",
926  QChar(0x423), QChar(0x424), QChar(0x425), QChar(0x426),
927  // "Ч", "Ш", "Щ", "Ъ",
928  QChar(0x427), QChar(0x428), QChar(0x429), QChar(0x42a),
929  // "Ы", "ь", "Э", "Ю",
930  QChar(0x42b), QChar(0x44c), QChar(0x42d), QChar(0x42e),
931  // "Я", "0", "1", "2",
932  QChar(0x42f), QChar('0'), QChar('1'), QChar('2'),
933  QChar('3'), QChar('4'), QChar('5'), QChar('6'),
934  QChar('7'), QChar('8'), QChar('9'), QChar('@'),
935  QChar('A'), QChar('B'), QChar('C'), QChar('D'),
936  QChar('E'), QChar('F'), QChar('G'), QChar('H'),
937  QChar('I'), QChar('J'), QChar('K'), QChar('L'),
938  QChar('M'), QChar('N'), QChar('O'), QChar('P'),
939  QChar('Q'), QChar('R'), QChar('S'), QChar('T'),
940  QChar('U'), QChar('V'), QChar('W'), QChar('X'),
941  QChar('Y'), QChar('Z'), 0
942 };
943 
945  TV *player, bool embedVideo)
946  : ProgFinder(parentStack, gg, player, embedVideo)
947 {
950  ;
951 }
952 
954 {
955  for (int charNum = 0; charNum < m_numberOfSearchChars; ++charNum)
956  {
958  }
959 }
960 
961 // search by cyrillic and latin alphabet
962 // @ all programm
964 &bindings)
965 {
966  QDateTime progStart = MythDate::current();
967  QString searchChar = m_alphabetList->GetValue();
968 
969  if (searchChar.isEmpty())
970  searchChar = s_searchChars[0];
971 
972 
973  if (searchChar.contains('@'))
974  {
975  where = "SELECT DISTINCT title FROM program "
976  "LEFT JOIN channel ON program.chanid = channel.chanid "
977  "WHERE channel.visible = 1 AND "
978  "( "
979  "title NOT REGEXP '^[A-Z0-9]' AND "
980  "title NOT REGEXP '^The [A-Z0-9]' AND "
981  "title NOT REGEXP '^A [A-Z0-9]' AND "
982  "title NOT REGEXP '^[0-9]' AND "
983  "starttime > :STARTTIME ) ";
984  if (!m_searchStr.isEmpty())
985  {
986  where += "AND title LIKE :SEARCH ";
987  bindings[":SEARCH"] = '%' + m_searchStr + '%';
988  }
989 
990  where += "ORDER BY title;";
991 
992  bindings[":STARTTIME"] =
993  progStart.addSecs(50 - progStart.time().second());
994  }
995  else
996  {
997  QString one = searchChar + '%';
998  QString two = QString("The ") + one;
999  QString three = QString("A ") + one;
1000  QString four = QString("An ") + one;
1001  QString five = QString("\"") + one;
1002 
1003  where = "SELECT DISTINCT title FROM program "
1004  "LEFT JOIN channel ON program.chanid = channel.chanid "
1005  "WHERE channel.visible = 1 "
1006  "AND ( title LIKE :ONE OR title LIKE :TWO "
1007  " OR title LIKE :THREE "
1008  " OR title LIKE :FOUR "
1009  " OR title LIKE :FIVE )"
1010  "AND starttime > :STARTTIME ";
1011  if (!m_searchStr.isEmpty())
1012  where += "AND title LIKE :SEARCH ";
1013 
1014  where += "ORDER BY title;";
1015 
1016  bindings[":ONE"] = one;
1017  bindings[":TWO"] = two;
1018  bindings[":THREE"] = three;
1019  bindings[":FOUR"] = four;
1020  bindings[":FIVE"] = five;
1021  bindings[":STARTTIME"] =
1022  progStart.addSecs(50 - progStart.time().second());
1023 
1024  if (!m_searchStr.isEmpty())
1025  bindings[":SEARCH"] = '%' + m_searchStr + '%';
1026  }
1027 }
1028 
1030 {
1031  (void)data;
1032  return true;
1033 }
1034 
1035 bool RuProgFinder::formatSelectedData(QString& data, int charNum)
1036 {
1037  (void)data;
1038  (void)charNum;
1039  return true;
1040 }
1041 
1043 {
1044  (void)data;
1045 }
1046 
1048 {
1049  return (GetFocusWidget() == m_timesList) ?
1050  m_showData[m_timesList->GetCurrentPos()] : nullptr;
1051 };
1052 
1054 
1056 {
1057  if (!LoadWindowFromXML("schedule-ui.xml", "searchpopup", this))
1058  return false;
1059 
1060  MythUIText *messageText = nullptr;
1061  MythUIButton *okButton = nullptr;
1062  MythUIButton *cancelButton = nullptr;
1063 
1064  bool err = false;
1065  UIUtilE::Assign(this, m_textEdit, "input", &err);
1066  UIUtilE::Assign(this, messageText, "message", &err);
1067  UIUtilE::Assign(this, okButton, "ok", &err);
1068  UIUtilW::Assign(this, cancelButton, "cancel");
1069 
1070  if (err)
1071  {
1072  LOG(VB_GENERAL, LOG_ERR, "Cannot load screen 'searchpopup'");
1073  return false;
1074  }
1075 
1076  if (cancelButton)
1077  connect(cancelButton, SIGNAL(Clicked()), SLOT(Close()));
1078  connect(okButton, SIGNAL(Clicked()), SLOT(sendResult()));
1079 
1083  connect(m_textEdit, SIGNAL(valueChanged()), SLOT(editChanged()));
1084 
1085  BuildFocusList();
1086 
1087  return true;
1088 }
1089 
1091 {
1092  QString inputString = m_textEdit->GetText();
1093  emit valueChanged(inputString);
1094 
1095  if (m_retObject)
1096  {
1097  //FIXME: add a new event type for this?
1098  DialogCompletionEvent *dce =
1099  new DialogCompletionEvent(m_id, 0, inputString, "");
1100  QCoreApplication::postEvent(m_retObject, dce);
1101  }
1102 }
1103 
1104 /* vim: set expandtab tabstop=4 shiftwidth=4: */
ProgramInfo * GetCurrentProgram(void) const override
Definition: progfind.cpp:1047
InputFilter m_filter
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:782
void restoreSelectedData(QString &data) override
Definition: progfind.cpp:904
void initAlphabetList() override
Definition: progfind.cpp:841
MythScreenStack * GetScreenStack() const
bool Create(void) override
Definition: progfind.cpp:1055
void alphabetListItemSelected(MythUIButtonListItem *item)
Definition: progfind.cpp:128
void updateShowList()
Definition: progfind.cpp:485
bool StartEmbedding(const QRect &)
Definition: tv_play.cpp:8534
QString m_searchStr
Definition: progfind.h:70
bool TranslateKeyPress(const QString &context, QKeyEvent *e, QStringList &actions, bool allowJumps=true)
Get a list of actions for a keypress in the given context.
void LosingFocus()
void restoreSelectedData(QString &data) override
Definition: progfind.cpp:1042
void Init(void) override
Used after calling Load() to assign data to widgets and other UI initilisation which is prohibited in...
Definition: progfind.cpp:88
All purpose text widget, displays a text string.
Definition: mythuitext.h:28
virtual void ShowUpcoming(void) const
Show the upcoming recordings for this title.
static const QChar s_searchChars[]
Definition: progfind.h:106
static Type MythEventMessage
Definition: mythevent.h:66
void showListTakeFocus(void)
Definition: progfind.cpp:144
void removeListener(QObject *listener)
Remove a listener to the observable.
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:125
void ShowMenu(void) override
Definition: progfind.cpp:212
Basic menu dialog, message and a list of options.
bool formatSelectedData(QString &data) override
Definition: progfind.cpp:1029
QString GetTitle(void) const
Definition: programinfo.h:353
void getShowNames(void)
Definition: progfind.cpp:457
void ShowGuide() const override
Definition: progfind.cpp:398
void SetFilter(InputFilter filter)
virtual void SetText(const QString &text)
Definition: mythuitext.cpp:136
virtual void restoreSelectedData(QString &data)
Definition: progfind.cpp:688
void initAlphabetList() override
Definition: progfind.cpp:953
MythScreenStack * GetStack(const QString &stackname)
MythUIText * m_help2Text
Definition: progfind.h:89
unsigned int uint
Definition: compat.h:140
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
size_t size(void) const
MythScreenStack * GetMainStack()
virtual void ShowDetails(void) const
Show the Program Details screen.
void addListener(QObject *listener)
Add a listener to the observable.
QString GetChanNum(void) const
This is the channel "number", in the form 1, 1_2, 1-2, 1#1, etc.
Definition: programinfo.h:368
static QString toUIState(Type)
Definition: recStatus.cpp:4
TV * m_player
Definition: progfind.h:73
JaProgFinder(MythScreenStack *parentStack, bool gg=false, TV *player=nullptr, bool embedVideo=false)
Definition: progfind.cpp:711
virtual bool formatSelectedData(QString &data)
Definition: progfind.cpp:597
void BuildFocusList(void)
static Type kEventType
Definition: mythdialogbox.h:50
void selectShowData(QString, int)
Definition: progfind.cpp:503
void updateTimesList()
Definition: progfind.cpp:428
virtual void initAlphabetList(void)
Definition: progfind.cpp:524
int m_numberOfSearchChars
Definition: progfind.h:107
bool m_embedVideo
Definition: progfind.h:74
MythUIText * m_help1Text
Definition: progfind.h:88
bool formatSelectedData(QString &data) override
Definition: progfind.cpp:891
bool MoveToNamedPosition(const QString &position_name)
virtual ~ProgFinder()
Definition: progfind.cpp:115
QDateTime GetScheduledStartTime(void) const
The scheduled start time of program.
Definition: programinfo.h:382
QVariant value(int i) const
Definition: mythdbcon.h:198
virtual void Close()
void AddButton(const QString &title, QVariant data=0, bool newMenu=false, bool setCurrent=false)
Holds information on recordings and videos.
Definition: programinfo.h:66
MythUIButtonList * m_timesList
Definition: progfind.h:85
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
static const QChar s_searchChars[]
Definition: progfind.h:124
void TakingFocus()
This class is used as a container for messages.
Definition: mythevent.h:16
virtual void ShowPrevious(void) const
Show the previous recordings for this recording rule.
bool m_allowEPG
Definition: progfind.h:75
QString m_currentLetter
Definition: progfind.h:71
bool empty(void) const
MythUIButtonList * m_showList
Definition: progfind.h:84
void valueChanged(QString)
QHash< QString, QString > InfoMap
Definition: mythtypes.h:15
InfoMap m_infoMap
Definition: progfind.h:81
RuProgFinder(MythScreenStack *parentStack, bool gg=false, TV *player=nullptr, bool embedVideo=false)
Definition: progfind.cpp:944
void editChanged(void)
Definition: progfind.cpp:1090
int m_numberOfSearchChars
Definition: progfind.h:125
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
QString GetSetting(const QString &key, const QString &defaultval="")
void SetReturnEvent(QObject *retobject, const QString &resultid)
MythUIButtonList * m_alphabetList
Definition: progfind.h:83
ProgramList m_schedList
Definition: progfind.h:79
void Reset(void) override
Reset the widget to it's original state, should not reset changes made by the theme.
Definition: mythuitext.cpp:84
virtual void SetTextFromMap(const InfoMap &infoMap)
Control TV playback.
Definition: tv_play.h:284
static bool Assign(ContainerType *container, UIType *&item, const QString &name, bool *err=nullptr)
Definition: mythuiutils.h:27
void SetText(const QString &text, const QString &name="", const QString &state="")
static MSqlQueryInfo InitCon(ConnectionReuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:535
Do Today/Yesterday/Tomorrow transform.
Definition: mythdate.h:23
void restoreSelectedData(QString &data) override
Definition: progfind.cpp:807
bool Create(void) override
Definition: progfind.cpp:55
virtual void EditCustom(void)
Creates a dialog for creating a custom recording rule.
bool formatSelectedData(QString &data) override
Definition: progfind.cpp:794
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
Definition: mythdate.cpp:101
void customEvent(QEvent *) override
static bool LoadWindowFromXML(const QString &xmlfile, const QString &windowname, MythUIType *parent)
virtual void whereClauseGetSearchData(QString &where, MSqlBindings &bindings)
Definition: progfind.cpp:537
void timesListTakeFocus(void)
Definition: progfind.cpp:149
void RunProgramFinder(TV *player, bool embedVideo, bool allowEPG)
Definition: progfind.cpp:35
void SetText(const QString &text, bool moveCursor=true)
MythUIType * GetFocusWidget(void) const
A single button widget.
Definition: mythuibutton.h:21
void Reset() override
Reset the widget to it's original state, should not reset changes made by the theme.
MythMainWindow * GetMythMainWindow(void)
QString GetText(const QString &name="") const
virtual QString GetValue() const
virtual void ShowChannelSearch(void) const
Show the channel search.
MythUIText * m_searchText
Definition: progfind.h:87
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:807
bool keyPressEvent(QKeyEvent *) override
Key event handler.
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
void customEvent(QEvent *e) override
Definition: progfind.cpp:245
void select()
Definition: progfind.cpp:418
void SetLCDTitles(const QString &title, const QString &columnList="")
void updateInfo(void)
Definition: progfind.cpp:334
virtual void PopScreen(MythScreenType *screen=nullptr, bool allowFade=true, bool deleteScreen=true)
void whereClauseGetSearchData(QString &where, MSqlBindings &bindings) override
Definition: progfind.cpp:851
bool LoadFromProgram(ProgramList &destination, const QString &where, const QString &groupBy, const QString &orderBy, const MSqlBindings &bindings, const ProgramList &schedList)
virtual void QuickRecord(void)
Create a kSingleRecord or bring up recording dialog.
virtual void EditScheduled(void)
Creates a dialog for editing the recording schedule.
static void RunProgramGuide(uint startChanId, const QString &startChanNum, const QDateTime &startTime, TV *player=nullptr, bool embedVideo=false, bool allowFinder=true, int changrpid=-1)
Definition: guidegrid.cpp:418
uint GetChanID(void) const
This is the unique key used in the database to locate tuning information.
Definition: programinfo.h:364
void SetReturnEvent(QObject *retobject, const QString &resultid)
void initAlphabetList() override
Definition: progfind.cpp:720
void bindValues(const MSqlBindings &bindings)
Add all the bindings in the passed in bindings.
Definition: mythdbcon.cpp:878
bool LoadFromScheduler(AutoDeleteDeque< TYPE * > &destination, bool &hasConflicts, QString altTable="", int recordid=-1)
Definition: programinfo.h:876
Default local time.
Definition: mythdate.h:20
void SetPassword(bool isPassword)
void SetItemCurrent(MythUIButtonListItem *item)
bool SetFocusWidget(MythUIType *widget=nullptr)
#define ACTION_CHANNELSEARCH
Definition: tv_actions.h:28
ShowName m_showNames
Definition: progfind.h:68
QMap< QString, QVariant > MSqlBindings
typedef for a map of string -> string bindings for generic queries.
Definition: mythdbcon.h:98
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:603
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:179
int GetCurrentPos() const
const QString & Message() const
Definition: mythevent.h:58
HeProgFinder(MythScreenStack *parentStack, bool gg=false, TV *player=nullptr, bool embedVideo=false)
Definition: progfind.cpp:832
int m_numberOfSearchChars
Definition: progfind.h:143
QString GetLanguage(void)
Returns two character ISO-639 language descriptor for UI language.
virtual void ResetMap(const InfoMap &infoMap)
Event dispatched from MythUI modal dialogs to a listening class containing a result of some form.
Definition: mythdialogbox.h:37
void whereClauseGetSearchData(QString &where, MSqlBindings &bindings) override
Definition: progfind.cpp:963
void DisplayState(const QString &state, const QString &name)
void SetTextFromMap(const InfoMap &infoMap, const QString &state="")
ProgramList m_showData
Definition: progfind.h:78
bool m_allowKeypress
Definition: progfind.h:76
static const QChar s_searchChars[]
Definition: progfind.h:142
void timesListLosingFocus(void)
Definition: progfind.cpp:139
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
Definition: progfind.cpp:155
virtual void EditRecording(bool may_watch_now=false)
Creates a dialog for editing the recording status, blocking until user leaves dialog.
QString GetText(void) const
void whereClauseGetSearchData(QString &where, MSqlBindings &bindings) override
Definition: progfind.cpp:732
void LoadInBackground(QString message="")
bool Create(void) override
MythUITextEdit * m_textEdit