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