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