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  QCoreApplication::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  auto *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  auto *me = dynamic_cast<MythEvent *>(event);
250  if (me == nullptr)
251  return;
252 
253  const QString& message = me->Message();
254 
255  if (message == "SCHEDULE_CHANGE")
256  {
257  if (GetFocusWidget() == m_timesList)
258  {
260  if (curPick)
261  selectShowData(curPick->GetTitle(),
263  }
264  }
265  }
266  else if (event->type() == DialogCompletionEvent::kEventType)
267  {
268  auto *dce = (DialogCompletionEvent*)(event);
269 
270  QString resultid = dce->GetId();
271  QString resulttext = dce->GetResultText();
272 
273  if (resultid == "menu")
274  {
275  if (resulttext == tr("Clear Search"))
276  {
277  m_searchStr.clear();
278  if (m_searchText)
280  updateShowList();
282  }
283  else if (resulttext == tr("Edit Search"))
284  {
285  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
286  auto *textInput = new SearchInputDialog(popupStack, m_searchStr);
287 
288  if (textInput->Create())
289  {
290  textInput->SetReturnEvent(this, "searchtext");
291  popupStack->AddScreen(textInput);
292  }
293  }
294  else if (resulttext == tr("Toggle Record"))
295  {
296  QuickRecord();
297  }
298  else if (resulttext == tr("Program Details"))
299  {
300  ShowDetails();
301  }
302  else if (resulttext == tr("Upcoming"))
303  {
304  ShowUpcoming();
305  }
306  else if (resulttext == tr("Previously Recorded"))
307  {
308  ShowPrevious();
309  }
310  else if (resulttext == tr("Custom Edit"))
311  {
312  EditCustom();
313  }
314  else if (resulttext == tr("Program Guide"))
315  {
316  ShowGuide();
317  }
318  else if (resulttext == tr("Channel Search"))
319  {
321  }
322  }
323  else if (resultid == "searchtext")
324  {
325  m_searchStr = resulttext;
326  if (m_searchText)
328  updateShowList();
330  }
331  else
333  }
334 }
335 
337 {
338  if (m_help1Text)
339  m_help1Text->Reset();
340  if (m_help2Text)
341  m_help2Text->Reset();
342 
344  {
345  QString title;
346  QString description;
347 
348  if (m_showList->GetCount() == 0)
349  {
350  if (m_help1Text)
351  m_help1Text->SetText(tr("No Programs"));
352  if (m_help2Text)
353  m_help2Text->SetText(tr("There are no available programs under this search. "
354  "Please select another search."));
355  }
356  else
357  {
358  if (m_help1Text)
359  m_help1Text->SetText(tr("Select a letter..."));
360  if (m_help2Text)
361  m_help2Text->SetText(tr("Pick the first letter of the program name, "
362  "then press SELECT or the right arrow."));
363  }
364 
366  }
367  else if (GetFocusWidget() == m_showList)
368  {
369  if (m_help1Text)
370  m_help1Text->SetText(tr("Select a program..."));
371  if (m_help2Text)
372  {
373  m_help2Text->SetText(tr("Select the title of the program you wish to find. "
374  "When finished return with the left arrow key. "
375  "Press SELECT to schedule a recording."));
376  }
377 
379  }
380  else if (GetFocusWidget() == m_timesList)
381  {
382  if (m_showData.empty())
383  {
385  if (m_help1Text)
386  m_help1Text->SetText(tr("No Programs"));
387  if (m_help2Text)
388  {
389  m_help2Text->SetText(tr("There are no available programs under "
390  "this search. Please select another "
391  "search."));
392  }
393  }
394  else
395  {
396  InfoMap infoMap;
397  m_showData[m_timesList->GetCurrentPos()]->ToMap(infoMap);
398  SetTextFromMap(infoMap);
399  m_infoMap = infoMap;
400  }
401  }
402 }
403 
405 {
406  if (m_allowEPG)
407  {
408  QString startchannel = gCoreContext->GetSetting("DefaultTVChannel");
409  uint startchanid = 0;
410  QDateTime starttime;
411 
412  if (GetFocusWidget() == m_timesList)
413  {
415  startchannel = pginfo->GetChanNum();
416  startchanid = pginfo->GetChanID();
417  starttime = pginfo->GetScheduledStartTime();
418  }
419  GuideGrid::RunProgramGuide(startchanid, startchannel, starttime,
420  m_player, m_embedVideo, false, -2);
421  }
422 }
423 
425 {
426  if (GetFocusWidget() == m_timesList)
427  EditRecording();
428  else if (GetFocusWidget() == m_alphabetList && m_showList->GetCount())
430  else if (GetFocusWidget() == m_showList)
432 }
433 
435 {
436  InfoMap infoMap;
437 
438  m_timesList->Reset();
439 
440  if (!m_showData.empty())
441  {
442  QString itemText;
443  QDateTime starttime;
444  for (auto *show : m_showData)
445  {
446  starttime = show->GetScheduledStartTime();
447  itemText = MythDate::toString(starttime,
449 
450  auto *item = new MythUIButtonListItem(m_timesList, "");
451 
452  QString state = RecStatus::toUIState(show->GetRecordingStatus());
453  item->SetText(itemText, "buttontext", state);
454  item->DisplayState(state, "status");
455 
456  show->ToMap(infoMap);
457  item->SetTextFromMap(infoMap, state);
458  }
459  }
460 }
461 
463 {
464  m_showNames.clear();
465 
466  QString thequery;
467  MSqlBindings bindings;
468 
470  whereClauseGetSearchData(thequery, bindings);
471 
472  query.prepare(thequery);
473  query.bindValues(bindings);
474  if (!query.exec())
475  {
476  MythDB::DBError("getShowNames", query);
477  return;
478  }
479 
480  QString data;
481  while (query.next())
482  {
483  data = query.value(0).toString();
484 
485  if (formatSelectedData(data))
486  m_showNames[data.toLower()] = data;
487  }
488 }
489 
491 {
492  m_showList->Reset();
493 
494  if (m_showNames.isEmpty())
495  getShowNames();
496 
497  ShowName::Iterator it;
498  for (it = m_showNames.begin(); it != m_showNames.end(); ++it)
499  {
500  QString tmpProgData = *it;
501  restoreSelectedData(tmpProgData);
502  new MythUIButtonListItem(m_showList, tmpProgData);
503  }
504 
505  m_showNames.clear();
506 }
507 
508 void ProgFinder::selectShowData(QString progTitle, int newCurShow)
509 {
510  progTitle = m_showList->GetValue();
511 
512  QDateTime progStart = MythDate::current();
513 
514  MSqlBindings bindings;
515  QString querystr = "WHERE program.title = :TITLE "
516  " AND program.endtime > :ENDTIME "
517  " AND channel.deleted IS NULL "
518  " AND channel.visible > 0 ";
519  bindings[":TITLE"] = progTitle;
520  bindings[":ENDTIME"] = progStart.addSecs(50 - progStart.time().second());
521 
523  LoadFromProgram(m_showData, querystr, bindings, m_schedList);
524 
525  updateTimesList();
526 
527  m_timesList->SetItemCurrent(newCurShow);
528 }
529 
531 {
532  for (int charNum = 48; charNum < 91; ++charNum)
533  {
534  if (charNum == 58)
535  charNum = 65;
536 
537  new MythUIButtonListItem(m_alphabetList, QString((char)charNum));
538  }
539 
540  new MythUIButtonListItem(m_alphabetList, QString('@'));
541 }
542 
543 void ProgFinder::whereClauseGetSearchData(QString &where, MSqlBindings &bindings)
544 {
545  QDateTime progStart = MythDate::current();
546  QString searchChar = m_alphabetList->GetValue();
547 
548  if (searchChar.isEmpty())
549  searchChar = "A";
550 
551  if (searchChar.contains('@'))
552  {
553  where = "SELECT DISTINCT title FROM program "
554  "LEFT JOIN channel ON program.chanid = channel.chanid "
555  "WHERE channel.deleted IS NULL AND "
556  " channel.visible > 0 AND "
557  "( title NOT REGEXP '^[A-Z0-9]' AND "
558  " title NOT REGEXP '^The [A-Z0-9]' AND "
559  " title NOT REGEXP '^A [A-Z0-9]' AND "
560  " title NOT REGEXP '^An [A-Z0-9]' AND "
561  " starttime > :STARTTIME ) ";
562  if (!m_searchStr.isEmpty())
563  {
564  where += "AND title LIKE :SEARCH ";
565  bindings[":SEARCH"] = '%' + m_searchStr + '%';
566  }
567 
568  where += "ORDER BY title;";
569 
570  bindings[":STARTTIME"] =
571  progStart.addSecs(50 - progStart.time().second());
572  }
573  else
574  {
575  QString one = searchChar + '%';
576  QString two = QString("The ") + one;
577  QString three = QString("A ") + one;
578  QString four = QString("An ") + one;
579 
580  where = "SELECT DISTINCT title FROM program "
581  "LEFT JOIN channel ON program.chanid = channel.chanid "
582  "WHERE channel.deleted IS NULL "
583  "AND channel.visible > 0 "
584  "AND ( title LIKE :ONE OR title LIKE :TWO "
585  " OR title LIKE :THREE "
586  " OR title LIKE :FOUR ) "
587  "AND starttime > :STARTTIME ";
588  if (!m_searchStr.isEmpty())
589  where += "AND title LIKE :SEARCH ";
590 
591  where += "ORDER BY title;";
592 
593  bindings[":ONE"] = one;
594  bindings[":TWO"] = two;
595  bindings[":THREE"] = three;
596  bindings[":FOUR"] = four;
597  bindings[":STARTTIME"] =
598  progStart.addSecs(50 - progStart.time().second());
599 
600  if (!m_searchStr.isEmpty())
601  bindings[":SEARCH"] = '%' + m_searchStr + '%';
602  }
603 }
604 
606 {
607  bool retval = true;
608  QString searchChar = m_alphabetList->GetValue();
609 
610  if (searchChar == "T")
611  {
612  if (!data.startsWith("The ") && !data.startsWith("A "))
613  {
614  // nothing, use as is
615  }
616  else if (data.startsWith("The T"))
617  data = data.mid(4) + ", The";
618  else if (data.startsWith("A T"))
619  data = data.mid(2) + ", A";
620  else
621  {
622  // don't add
623  retval = false;
624  }
625  }
626  else if (searchChar == "A")
627  {
628  if (!data.startsWith("The ") && !data.startsWith("A "))
629  {
630  // nothing, use as is
631  }
632  else if (data.startsWith("The A"))
633  data = data.mid(4) + ", The";
634  else if (data.startsWith("A A"))
635  data = data.mid(2) + ", A";
636  else if (data.startsWith("An A"))
637  data = data.mid(3) + ", An";
638  else
639  {
640  // don't add
641  retval = false;
642  }
643  }
644  else
645  {
646  if (data.startsWith("The "))
647  data = data.mid(4) + ", The";
648  else if (data.startsWith("A "))
649  data = data.mid(2) + ", A";
650  else if (data.startsWith("An "))
651  data = data.mid(3) + ", An";
652  }
653 
654  return retval;
655 }
656 
657 bool ProgFinder::formatSelectedData(QString& data, int charNum)
658 {
659  bool retval = true;
660 
661  if (charNum == 29 || charNum == 10)
662  {
663  if ((data.startsWith("The T") && charNum == 29) ||
664  (data.startsWith("The A") && charNum == 10))
665  data = data.mid(4) + ", The";
666  else if ((data.startsWith("A T") && charNum == 29) ||
667  (data.startsWith("A A") && charNum == 10))
668  data = data.mid(2) + ", A";
669  else if (data.startsWith("An A") && charNum == 10)
670  data = data.mid(3) + ", An";
671  else if (!data.startsWith("The ") && !data.startsWith("A "))
672  {
673  // use as is
674  }
675  else
676  {
677  // don't add
678  retval = false;
679  }
680  }
681  else
682  {
683  if (data.startsWith("The "))
684  data = data.mid(4) + ", The";
685  if (data.startsWith("A "))
686  data = data.mid(2) + ", A";
687  if (data.startsWith("An "))
688  data = data.mid(3) + ", An";
689  }
690 
691  return retval;
692 }
693 
695 {
696  if (data.endsWith(", The"))
697  data = "The " + data.left(data.length() - 5);
698  if (data.endsWith(", A"))
699  data = "A " + data.left(data.length() - 3);
700 }
701 
703 // Japanese specific program finder
704 
705 // japanese HIRAGANA list and more
706 const QChar JaProgFinder::kSearchChars[] =
707 {
708  // "あ", "か", "さ", "た",
709  QChar(0x3042), QChar(0x304b), QChar(0x3055), QChar(0x305f),
710  // "な", "は", "ま", "や",
711  QChar(0x306a), QChar(0x306f), QChar(0x307e), QChar(0x3084),
712  // "ら", "わ", "英", "数",
713  QChar(0x3089), QChar(0x308f), QChar(0x82f1), QChar(0x6570),
714  0,
715 };
716 
718  TV *player, bool embedVideo)
719  : ProgFinder(parentStack, gg, player, embedVideo)
720 {
723  ;
724 }
725 
727 {
728  for (int charNum = 0; charNum < m_numberOfSearchChars; ++charNum)
729  {
730  new MythUIButtonListItem(m_alphabetList, QString(kSearchChars[charNum]));
731  }
732 }
733 
734 // search title_pronounce
735 // we hope japanese HIRAGANA and alphabets, numerics is inserted
736 // these character must required ZENKAKU
737 // because query work not fine, if mysql's default charset latin1
739 {
740  QDateTime progStart = MythDate::current();
741  int charNum = m_alphabetList->GetCurrentPos();
742 
743  where = "SELECT DISTINCT title FROM program "
744  "LEFT JOIN channel ON program.chanid = channel.chanid "
745  "WHERE channel.deleted IS NULL AND channel.visible > 0 ";
746 
747  switch (charNum) {
748  case 0:
749  where += "AND ( title_pronounce >= 'あ' AND title_pronounce <= 'お') ";
750  break;
751  case 1:
752  where += "AND ( title_pronounce >= 'か' AND title_pronounce <= 'ご') ";
753  break;
754  case 2:
755  where += "AND ( title_pronounce >= 'さ' AND title_pronounce <= 'そ') ";
756  break;
757  case 3:
758  where += "AND ( title_pronounce >= 'た' AND title_pronounce <= 'ど') ";
759  break;
760  case 4:
761  where += "AND ( title_pronounce >= 'な' AND title_pronounce <= 'の') ";
762  break;
763  case 5:
764  where += "AND ( title_pronounce >= 'は' AND title_pronounce <= 'ぽ') ";
765  break;
766  case 6:
767  where += "AND ( title_pronounce >= 'ま' AND title_pronounce <= 'も') ";
768  break;
769  case 7:
770  where += "AND ( title_pronounce >= 'や' AND title_pronounce <= 'よ') ";
771  break;
772  case 8:
773  where += "AND ( title_pronounce >= 'ら' AND title_pronounce <= 'ろ') ";
774  break;
775  case 9:
776  where += "AND ( title_pronounce >= 'わ' AND title_pronounce <= 'ん') ";
777  break;
778  case 10:
779  where += "AND ( title_pronounce >= 'A' AND title_pronounce <= 'z') ";
780  break;
781  case 11:
782  where += "AND ( title_pronounce >= '0' AND title_pronounce <= '9') ";
783  break;
784  }
785 
786  where += "AND starttime > :STARTTIME ";
787 
788  if (!m_searchStr.isEmpty())
789  {
790  where += "AND title_pronounce LIKE :SEARCH ";
791  bindings[":SEARCH"] = '%' + m_searchStr + '%';
792  }
793 
794  where += "ORDER BY title_pronounce;";
795 
796  bindings[":STARTTIME"] = progStart.addSecs(50 - progStart.time().second());
797 }
798 
799 
801 {
802  (void)data;
803  return true;
804 }
805 
806 bool JaProgFinder::formatSelectedData(QString& data, int charNum)
807 {
808  (void)data;
809  (void)charNum;
810  return true;
811 }
812 
814 {
815  (void)data;
816 }
817 
818 // Hebrew specific program finder
819 
820 // Hebrew alphabet list and more
821 const QChar HeProgFinder::kSearchChars[] =
822 {
823  // "א", "ב", "ג", "ד",
824  QChar(0x5d0), QChar(0x5d1), QChar(0x5d2), QChar(0x5d3),
825  // "ה", "ו", "ז", "ח",
826  QChar(0x5d4), QChar(0x5d5), QChar(0x5d6), QChar(0x5d7),
827  // "ט", "י", "כ", "ל",
828  QChar(0x5d8), QChar(0x5d9), QChar(0x5db), QChar(0x5dc),
829  // "מ", "נ", "ס", "ע",
830  QChar(0x5de), QChar(0x5e0), QChar(0x5e1), QChar(0x5e2),
831  // "פ", "צ", "ק", "ר",
832  QChar(0x5e4), QChar(0x5e6), QChar(0x5e7), QChar(0x5e8),
833  // "ש", "ת", "E", "#",
834  QChar(0x5e9), QChar(0x5ea), QChar('E'), QChar('#'),
835  QChar(0),
836 };
837 
839  TV *player, bool embedVideo)
840  : ProgFinder(parentStack, gg, player, embedVideo)
841 {
844  ;
845 }
846 
848 {
849  for (int charNum = 0; charNum < m_numberOfSearchChars; ++charNum)
850  {
851  new MythUIButtonListItem(m_alphabetList, QString(kSearchChars[charNum]));
852  }
853 }
854 
855 // search by hebrew aleph-bet
856 // # for all numbers, E for all latin
858 {
859  QDateTime progStart = MythDate::current();
860  QString searchChar = m_alphabetList->GetValue();
861 
862  if (searchChar.isEmpty())
863  searchChar = kSearchChars[0];
864 
865  where = "SELECT DISTINCT title FROM program "
866  "LEFT JOIN channel ON program.chanid = channel.chanid "
867  "WHERE channel.deleted IS NULL AND channel.visible > 0 ";
868 
869  if (searchChar.contains('E'))
870  {
871  where += "AND ( title REGEXP '^[A-Z]') ";
872  }
873  else if (searchChar.contains('#'))
874  {
875  where += "AND ( title REGEXP '^[0-9]') ";
876  }
877  else
878  {
879  QString one = searchChar + '%';
880  bindings[":ONE"] = one;
881  where += "AND ( title LIKE :ONE ) ";
882  }
883 
884  where += "AND starttime > :STARTTIME ";
885 
886  if (!m_searchStr.isEmpty())
887  {
888  where += "AND title LIKE :SEARCH ";
889  bindings[":SEARCH"] = '%' + m_searchStr + '%';
890  }
891 
892  where += "ORDER BY title;";
893 
894  bindings[":STARTTIME"] = progStart.addSecs(50 - progStart.time().second());
895 }
896 
898 {
899  (void)data;
900  return true;
901 }
902 
903 bool HeProgFinder::formatSelectedData(QString& data, int charNum)
904 {
905  (void)data;
906  (void)charNum;
907  return true;
908 }
909 
911 {
912  (void)data;
913 }
914 
916 
917 // Cyrrilic specific program finder
918 // Cyrrilic alphabet list and more
919 const QChar RuProgFinder::kSearchChars[] =
920 {
921  // "А", "Б", "В", "Г",
922  QChar(0x410), QChar(0x411), QChar(0x412), QChar(0x413),
923  // "Д", "Е", "Ё", "Ж",
924  QChar(0x414), QChar(0x415), QChar(0x401), QChar(0x416),
925  // "З", "И", "Й", "К",
926  QChar(0x417), QChar(0x418), QChar(0x419), QChar(0x41a),
927  // "Л", "М", "Н", "О",
928  QChar(0x41b), QChar(0x41c), QChar(0x41d), QChar(0x41e),
929  // "П", "Р", "С", "Т",
930  QChar(0x41f), QChar(0x420), QChar(0x421), QChar(0x422),
931  // "У", "Ф", "Х", "Ц",
932  QChar(0x423), QChar(0x424), QChar(0x425), QChar(0x426),
933  // "Ч", "Ш", "Щ", "Ъ",
934  QChar(0x427), QChar(0x428), QChar(0x429), QChar(0x42a),
935  // "Ы", "ь", "Э", "Ю",
936  QChar(0x42b), QChar(0x44c), QChar(0x42d), QChar(0x42e),
937  // "Я", "0", "1", "2",
938  QChar(0x42f), QChar('0'), QChar('1'), QChar('2'),
939  QChar('3'), QChar('4'), QChar('5'), QChar('6'),
940  QChar('7'), QChar('8'), QChar('9'), QChar('@'),
941  QChar('A'), QChar('B'), QChar('C'), QChar('D'),
942  QChar('E'), QChar('F'), QChar('G'), QChar('H'),
943  QChar('I'), QChar('J'), QChar('K'), QChar('L'),
944  QChar('M'), QChar('N'), QChar('O'), QChar('P'),
945  QChar('Q'), QChar('R'), QChar('S'), QChar('T'),
946  QChar('U'), QChar('V'), QChar('W'), QChar('X'),
947  QChar('Y'), QChar('Z'), 0
948 };
949 
951  TV *player, bool embedVideo)
952  : ProgFinder(parentStack, gg, player, embedVideo)
953 {
956  ;
957 }
958 
960 {
961  for (int charNum = 0; charNum < m_numberOfSearchChars; ++charNum)
962  {
964  }
965 }
966 
967 // search by cyrillic and latin alphabet
968 // @ all programm
970 &bindings)
971 {
972  QDateTime progStart = MythDate::current();
973  QString searchChar = m_alphabetList->GetValue();
974 
975  if (searchChar.isEmpty())
976  searchChar = kSearchChars[0];
977 
978 
979  if (searchChar.contains('@'))
980  {
981  where = "SELECT DISTINCT title FROM program "
982  "LEFT JOIN channel ON program.chanid = channel.chanid "
983  "WHERE channel.deleted IS NULL AND "
984  " channel.visible > 0 AND "
985  "( "
986  "title NOT REGEXP '^[A-Z0-9]' AND "
987  "title NOT REGEXP '^The [A-Z0-9]' AND "
988  "title NOT REGEXP '^A [A-Z0-9]' AND "
989  "title NOT REGEXP '^[0-9]' AND "
990  "starttime > :STARTTIME ) ";
991  if (!m_searchStr.isEmpty())
992  {
993  where += "AND title LIKE :SEARCH ";
994  bindings[":SEARCH"] = '%' + m_searchStr + '%';
995  }
996 
997  where += "ORDER BY title;";
998 
999  bindings[":STARTTIME"] =
1000  progStart.addSecs(50 - progStart.time().second());
1001  }
1002  else
1003  {
1004  QString one = searchChar + '%';
1005  QString two = QString("The ") + one;
1006  QString three = QString("A ") + one;
1007  QString four = QString("An ") + one;
1008  QString five = QString("\"") + one;
1009 
1010  where = "SELECT DISTINCT title FROM program "
1011  "LEFT JOIN channel ON program.chanid = channel.chanid "
1012  "WHERE channel.deleted IS NULL "
1013  "AND channel.visible > 0 "
1014  "AND ( title LIKE :ONE OR title LIKE :TWO "
1015  " OR title LIKE :THREE "
1016  " OR title LIKE :FOUR "
1017  " OR title LIKE :FIVE )"
1018  "AND starttime > :STARTTIME ";
1019  if (!m_searchStr.isEmpty())
1020  where += "AND title LIKE :SEARCH ";
1021 
1022  where += "ORDER BY title;";
1023 
1024  bindings[":ONE"] = one;
1025  bindings[":TWO"] = two;
1026  bindings[":THREE"] = three;
1027  bindings[":FOUR"] = four;
1028  bindings[":FIVE"] = five;
1029  bindings[":STARTTIME"] =
1030  progStart.addSecs(50 - progStart.time().second());
1031 
1032  if (!m_searchStr.isEmpty())
1033  bindings[":SEARCH"] = '%' + m_searchStr + '%';
1034  }
1035 }
1036 
1038 {
1039  (void)data;
1040  return true;
1041 }
1042 
1043 bool RuProgFinder::formatSelectedData(QString& data, int charNum)
1044 {
1045  (void)data;
1046  (void)charNum;
1047  return true;
1048 }
1049 
1051 {
1052  (void)data;
1053 }
1054 
1056 {
1057  return (GetFocusWidget() == m_timesList) ?
1058  m_showData[m_timesList->GetCurrentPos()] : nullptr;
1059 };
1060 
1062 
1064 {
1065  if (!LoadWindowFromXML("schedule-ui.xml", "searchpopup", this))
1066  return false;
1067 
1068  MythUIText *messageText = nullptr;
1069  MythUIButton *okButton = nullptr;
1070  MythUIButton *cancelButton = nullptr;
1071 
1072  bool err = false;
1073  UIUtilE::Assign(this, m_textEdit, "input", &err);
1074  UIUtilE::Assign(this, messageText, "message", &err);
1075  UIUtilE::Assign(this, okButton, "ok", &err);
1076  UIUtilW::Assign(this, cancelButton, "cancel");
1077 
1078  if (err)
1079  {
1080  LOG(VB_GENERAL, LOG_ERR, "Cannot load screen 'searchpopup'");
1081  return false;
1082  }
1083 
1084  if (cancelButton)
1085  connect(cancelButton, SIGNAL(Clicked()), SLOT(Close()));
1086  connect(okButton, SIGNAL(Clicked()), SLOT(sendResult()));
1087 
1091  connect(m_textEdit, SIGNAL(valueChanged()), SLOT(editChanged()));
1092 
1093  BuildFocusList();
1094 
1095  return true;
1096 }
1097 
1099 {
1100  QString inputString = m_textEdit->GetText();
1101  emit valueChanged(inputString);
1102 
1103  if (m_retObject)
1104  {
1105  //FIXME: add a new event type for this?
1106  auto *dce = new DialogCompletionEvent(m_id, 0, inputString, "");
1107  QCoreApplication::postEvent(m_retObject, dce);
1108  }
1109 }
1110 
1111 /* vim: set expandtab tabstop=4 shiftwidth=4: */
ProgramInfo * GetCurrentProgram(void) const override
Definition: progfind.cpp:1055
InputFilter m_filter
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:783
static const QChar kSearchChars[]
Definition: progfind.h:106
void restoreSelectedData(QString &data) override
Definition: progfind.cpp:910
void initAlphabetList() override
Definition: progfind.cpp:847
MythScreenStack * GetScreenStack() const
bool Create(void) override
Definition: progfind.cpp:1063
void alphabetListItemSelected(MythUIButtonListItem *item)
Definition: progfind.cpp:128
void updateShowList()
Definition: progfind.cpp:490
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:1050
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.
QHash< QString, QString > InfoMap
Definition: mythtypes.h:15
static Type MythEventMessage
Definition: mythevent.h:73
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:1037
QString GetTitle(void) const
Definition: programinfo.h:355
void getShowNames(void)
Definition: progfind.cpp:462
void ShowGuide() const override
Definition: progfind.cpp:404
static const QChar kSearchChars[]
Definition: progfind.h:124
void SetFilter(InputFilter filter)
virtual void SetText(const QString &text)
Definition: mythuitext.cpp:135
virtual void restoreSelectedData(QString &data)
Definition: progfind.cpp:694
void initAlphabetList() override
Definition: progfind.cpp:959
MythScreenStack * GetStack(const QString &stackname)
MythUIText * m_help2Text
Definition: progfind.h:89
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
~ProgFinder() override
Definition: progfind.cpp:115
MythScreenStack * GetMainStack()
virtual void ShowDetails(void) const
Show the Program Details screen.
void addListener(QObject *listener)
Add a listener to the observable.
Do Today/Yesterday/Tomorrow transform.
Definition: mythdate.h:23
QString GetChanNum(void) const
This is the channel "number", in the form 1, 1_2, 1-2, 1#1, etc.
Definition: programinfo.h:370
TV * m_player
Definition: progfind.h:73
JaProgFinder(MythScreenStack *parentStack, bool gg=false, TV *player=nullptr, bool embedVideo=false)
Definition: progfind.cpp:717
virtual bool formatSelectedData(QString &data)
Definition: progfind.cpp:605
void BuildFocusList(void)
static Type kEventType
Definition: mythdialogbox.h:57
bool StartEmbedding(const QRect &embedRect)
Definition: tv_play.cpp:8469
void updateTimesList()
Definition: progfind.cpp:434
virtual void initAlphabetList(void)
Definition: progfind.cpp:530
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:897
bool MoveToNamedPosition(const QString &position_name)
QDateTime GetScheduledStartTime(void) const
The scheduled start time of program.
Definition: programinfo.h:384
QVariant value(int i) const
Definition: mythdbcon.h:198
virtual void Close()
Default local time.
Definition: mythdate.h:20
Holds information on recordings and videos.
Definition: programinfo.h:67
MythUIButtonList * m_timesList
Definition: progfind.h:85
void LoadInBackground(const QString &message="")
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
QMap< QString, QVariant > MSqlBindings
typedef for a map of string -> string bindings for generic queries.
Definition: mythdbcon.h:98
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.
MSqlQuery query(MSqlQuery::InitCon())
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)
InfoMap m_infoMap
Definition: progfind.h:81
RuProgFinder(MythScreenStack *parentStack, bool gg=false, TV *player=nullptr, bool embedVideo=false)
Definition: progfind.cpp:950
void editChanged(void)
Definition: progfind.cpp:1098
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="")
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:83
virtual void SetTextFromMap(const InfoMap &infoMap)
Control TV playback.
Definition: tv_play.h:280
static bool Assign(ContainerType *container, UIType *&item, const QString &name, bool *err=nullptr)
Definition: mythuiutils.h:27
unsigned int uint
Definition: compat.h:140
void restoreSelectedData(QString &data) override
Definition: progfind.cpp:813
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:535
void selectShowData(QString progTitle, int newCurShow)
Definition: progfind.cpp:508
void customEvent(QEvent *event) override
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:800
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
Definition: mythdate.cpp:101
static bool LoadWindowFromXML(const QString &xmlfile, const QString &windowname, MythUIType *parent)
virtual void whereClauseGetSearchData(QString &where, MSqlBindings &bindings)
Definition: progfind.cpp:543
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 keyPressEvent(QKeyEvent *event) override
Key event handler.
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:808
void customEvent(QEvent *e) override
Definition: progfind.cpp:245
void select()
Definition: progfind.cpp:424
static void show(uint8_t *buf, int length)
Definition: ringbuffer.cpp:319
void SetLCDTitles(const QString &title, const QString &columnList="")
void updateInfo(void)
Definition: progfind.cpp:336
virtual void PopScreen(MythScreenType *screen=nullptr, bool allowFade=true, bool deleteScreen=true)
void whereClauseGetSearchData(QString &where, MSqlBindings &bindings) override
Definition: progfind.cpp:857
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:415
uint GetChanID(void) const
This is the unique key used in the database to locate tuning information.
Definition: programinfo.h:366
static QString toUIState(Type recstatus)
Definition: recStatus.cpp:4
void initAlphabetList() override
Definition: progfind.cpp:726
void bindValues(const MSqlBindings &bindings)
Add all the bindings in the passed in bindings.
Definition: mythdbcon.cpp:879
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
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
bool LoadFromScheduler(AutoDeleteDeque< TYPE * > &destination, bool &hasConflicts, const QString &altTable="", int recordid=-1)
Definition: programinfo.h:877
HeProgFinder(MythScreenStack *parentStack, bool gg=false, TV *player=nullptr, bool embedVideo=false)
Definition: progfind.cpp:838
static const QChar kSearchChars[]
Definition: progfind.h:142
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:41
void whereClauseGetSearchData(QString &where, MSqlBindings &bindings) override
Definition: progfind.cpp:969
ProgramList m_showData
Definition: progfind.h:78
bool m_allowKeypress
Definition: progfind.h:76
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:738
MythUITextEdit * m_textEdit
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:23