diff --git a/pvr.mythtv/resources/language/English/strings.po b/pvr.mythtv/resources/language/English/strings.po index 2b7b602b..aaf809ae 100644 --- a/pvr.mythtv/resources/language/English/strings.po +++ b/pvr.mythtv/resources/language/English/strings.po @@ -371,7 +371,11 @@ msgctxt "#30466" msgid "Record series (EPG based)" msgstr "" -# empty strings from id 30467 to 30500 +msgctxt "#30467" +msgid "Text Search" +msgstr "" + +# empty strings from id 30468 to 30500 msgctxt "#30501" msgid "Don't match duplicates" diff --git a/src/cppmyth/MythScheduleHelper75.cpp b/src/cppmyth/MythScheduleHelper75.cpp index 2a12759f..5605df66 100644 --- a/src/cppmyth/MythScheduleHelper75.cpp +++ b/src/cppmyth/MythScheduleHelper75.cpp @@ -178,6 +178,27 @@ const std::vector& MythScheduleHelper75::GetTime GetRuleRecordingGroupList(), GetRuleRecordingGroupDefault())); + typeList.push_back(MythScheduleManager::TimerType(TIMER_TYPE_TEXT_SEARCH, + PVR_TIMER_TYPE_IS_REPEATING | + PVR_TIMER_TYPE_SUPPORTS_ENABLE_DISABLE | + PVR_TIMER_TYPE_SUPPORTS_CHANNELS | + PVR_TIMER_TYPE_SUPPORTS_START_END_MARGIN | + PVR_TIMER_TYPE_SUPPORTS_PRIORITY | + PVR_TIMER_TYPE_SUPPORTS_LIFETIME | + PVR_TIMER_TYPE_SUPPORTS_RECORDING_GROUP | + PVR_TIMER_TYPE_SUPPORTS_RECORD_ONLY_NEW_EPISODES | + PVR_TIMER_TYPE_SUPPORTS_TITLE_EPG_MATCH | + PVR_TIMER_TYPE_SUPPORTS_FULLTEXT_EPG_MATCH, + XBMC->GetLocalizedString(30467), + GetRulePriorityList(), + GetRulePriorityDefault(), + GetRuleDupMethodList(), + GetRuleDupMethodDefault(), + GetRuleExpirationList(), + GetRuleExpirationDefault(), + GetRuleRecordingGroupList(), + GetRuleRecordingGroupDefault())); + /////////////////////////////////////////////////////////////////////////// //// KEEP LAST /////////////////////////////////////////////////////////////////////////// @@ -424,7 +445,7 @@ bool MythScheduleHelper75::FillTimerEntry(MythTimerEntry& entry, const MythRecor // that which is applied in function 'NewFromTimer' MythRecordingRule rule = node.GetRule(); - + XBMC->Log(LOG_DEBUG, "75::%s: Rule %u", __FUNCTION__, rule.RecordID()); switch (rule.Type()) { case Myth::RT_SingleRecord: @@ -479,7 +500,12 @@ bool MythScheduleHelper75::FillTimerEntry(MythTimerEntry& entry, const MythRecor break; case Myth::RT_AllRecord: - entry.timerType = TIMER_TYPE_RECORD_ALL; + if (rule.SearchType() == Myth::ST_NoSearch) + entry.timerType = TIMER_TYPE_RECORD_ALL; + else if (rule.SearchType() == Myth::ST_TitleSearch) + entry.timerType = TIMER_TYPE_TEXT_SEARCH; + else if (rule.SearchType() == Myth::ST_KeywordSearch) + entry.timerType = TIMER_TYPE_TEXT_SEARCH; break; case Myth::RT_OverrideRecord: @@ -505,8 +531,12 @@ bool MythScheduleHelper75::FillTimerEntry(MythTimerEntry& entry, const MythRecor { case Myth::ST_TitleSearch: entry.epgSearch = rule.Description(); + entry.isFullTextSearch = false; break; case Myth::ST_KeywordSearch: + entry.epgSearch = rule.Description(); + entry.isFullTextSearch = true; + break; case Myth::ST_PeopleSearch: case Myth::ST_PowerSearch: entry.epgSearch = rule.Description(); @@ -534,6 +564,7 @@ bool MythScheduleHelper75::FillTimerEntry(MythTimerEntry& entry, const MythRecor case TIMER_TYPE_RECORD_DAILY: case TIMER_TYPE_RECORD_ALL: case TIMER_TYPE_RECORD_SERIES: + case TIMER_TYPE_TEXT_SEARCH: case TIMER_TYPE_UNHANDLED: if (difftime(rule.NextRecording(), 0) > 0) { @@ -547,6 +578,11 @@ bool MythScheduleHelper75::FillTimerEntry(MythTimerEntry& entry, const MythRecor entry.startTime = entry.endTime = rule.LastRecorded(); timeadd(&entry.endTime, difftime(rule.EndTime(), rule.StartTime())); } + else + { + entry.startTime = rule.StartTime(); + entry.endTime = rule.EndTime(); + } break; default: entry.startTime = rule.StartTime(); @@ -584,9 +620,9 @@ bool MythScheduleHelper75::FillTimerEntry(MythTimerEntry& entry, const MythProgr case Myth::RS_PREVIOUS_RECORDING: //Previoulsy recorded but no longer in the library if (true /*!m_showNotRecording*/) { - XBMC->Log(LOG_DEBUG, "%s: Skipping %s:%s on %s because status %d and m_showNotRecording=%d", __FUNCTION__, + XBMC->Log(LOG_DEBUG, "75::%s: Skipping %s:%s on %s because status %d", __FUNCTION__, recording.Title().c_str(), recording.Subtitle().c_str(), recording.ChannelName().c_str(), - recording.Status(), 0 /*m_showNotRecording*/); + recording.Status()); return false; } default: @@ -690,7 +726,7 @@ MythRecordingRule MythScheduleHelper75::NewFromTemplate(const MythEPGInfo& epgIn } if (tplIt != templates.end()) { - XBMC->Log(LOG_INFO, "Overriding the rule with template %u '%s'", (unsigned)tplIt->RecordID(), tplIt->Title().c_str()); + XBMC->Log(LOG_INFO, "75::%s: Overriding the rule with template %u '%s'", __FUNCTION__, (unsigned)tplIt->RecordID(), tplIt->Title().c_str()); rule.SetPriority(tplIt->Priority()); rule.SetStartOffset(tplIt->StartOffset()); rule.SetEndOffset(tplIt->EndOffset()); @@ -715,7 +751,7 @@ MythRecordingRule MythScheduleHelper75::NewFromTemplate(const MythEPGInfo& epgIn rule.SetCategory(tplIt->Category()); } else - XBMC->Log(LOG_INFO, "No template found for the category '%s'", epgInfo.Category().c_str()); + XBMC->Log(LOG_INFO, "75::%s: No template found for the category '%s'", __FUNCTION__, epgInfo.Category().c_str()); } break; case 0: // Template provider is 'Internal', then set rule with settings @@ -745,7 +781,7 @@ MythRecordingRule MythScheduleHelper75::NewFromTemplate(const MythEPGInfo& epgIn Myth::SettingPtr categoryOverTime = m_control->GetSetting("CategoryOverTime", false); if (categoryOverTime) { - XBMC->Log(LOG_DEBUG, "Overriding end offset for category %s: +%s", overTimeCategory->value.c_str(), categoryOverTime->value.c_str()); + XBMC->Log(LOG_DEBUG, "75::%s: Overriding end offset for category %s: +%s", __FUNCTION__, overTimeCategory->value.c_str(), categoryOverTime->value.c_str()); rule.SetEndOffset(atoi(categoryOverTime->value.c_str())); } } @@ -759,7 +795,7 @@ MythRecordingRule MythScheduleHelper75::NewFromTimer(const MythTimerEntry& entry // that which is applied in function 'FillTimerEntry' MythRecordingRule rule; - + XBMC->Log(LOG_DEBUG, "75::%s", __FUNCTION__); if (withTemplate) { // Base on template @@ -1046,6 +1082,37 @@ MythRecordingRule MythScheduleHelper75::NewFromTimer(const MythTimerEntry& entry break; } + case TIMER_TYPE_TEXT_SEARCH: + { + if (entry.HasChannel()) + { + rule.SetType(Myth::RT_ChannelRecord); + rule.SetChannelID(entry.chanid); + rule.SetCallsign(entry.callsign); + } + else + rule.SetType(Myth::RT_AllRecord); + rule.SetInactive(entry.isInactive); + rule.SetTitle(entry.title); + if (!entry.epgInfo.IsNull()) + { + rule.SetStartTime(entry.epgInfo.StartTime()); + rule.SetEndTime(entry.epgInfo.EndTime()); + rule.SetCategory(entry.epgInfo.Category()); + } + if (!entry.epgSearch.empty()) + { + if (entry.isFullTextSearch) + rule.SetSearchType(Myth::ST_KeywordSearch); + else + rule.SetSearchType(Myth::ST_TitleSearch); + rule.SetSubtitle(""); // Backend uses Subtitle as table join SQL for power searches (not needed for keyword or title) + rule.SetDescription(entry.epgSearch); // Backend uses description to find program by keywords or title and SQL for power searches + return rule; + } + break; + } + case TIMER_TYPE_DONT_RECORD: case TIMER_TYPE_OVERRIDE: case TIMER_TYPE_UPCOMING: @@ -1066,7 +1133,7 @@ MythRecordingRule MythScheduleHelper75::NewFromTimer(const MythTimerEntry& entry break; } rule.SetType(Myth::RT_UNKNOWN); - XBMC->Log(LOG_ERROR, "%s - Invalid timer %u: TYPE=%d CHANID=%u SIGN=%s ST=%u ET=%u", __FUNCTION__, entry.entryIndex, + XBMC->Log(LOG_ERROR, "75::%s: Invalid timer %u: TYPE=%d CHANID=%u SIGN=%s ST=%u ET=%u", __FUNCTION__, entry.entryIndex, entry.timerType, entry.chanid, entry.callsign.c_str(), (unsigned)entry.startTime, (unsigned)entry.endTime); return rule; } diff --git a/src/cppmyth/MythScheduleHelper76.cpp b/src/cppmyth/MythScheduleHelper76.cpp index db6609a6..a34d6e3a 100644 --- a/src/cppmyth/MythScheduleHelper76.cpp +++ b/src/cppmyth/MythScheduleHelper76.cpp @@ -55,6 +55,7 @@ bool MythScheduleHelper76::FillTimerEntry(MythTimerEntry& entry, const MythRecor // that which is applied in function 'NewFromTimer' MythRecordingRule rule = node.GetRule(); + XBMC->Log(LOG_DEBUG, "76::%s: RecordID %u", __FUNCTION__, rule.RecordID()); switch (rule.Type()) { @@ -111,7 +112,14 @@ bool MythScheduleHelper76::FillTimerEntry(MythTimerEntry& entry, const MythRecor else if ((rule.Filter() & Myth::FM_ThisSeries)) entry.timerType = TIMER_TYPE_RECORD_SERIES; else - entry.timerType = TIMER_TYPE_RECORD_ALL; + { + if (rule.SearchType() == Myth::ST_NoSearch) + entry.timerType = TIMER_TYPE_RECORD_ALL; + else if (rule.SearchType() == Myth::ST_TitleSearch) + entry.timerType = TIMER_TYPE_TEXT_SEARCH; + else if (rule.SearchType() == Myth::ST_KeywordSearch) + entry.timerType = TIMER_TYPE_TEXT_SEARCH; + } if (rule.Filter() & Myth::FM_ThisChannel) { entry.chanid = rule.ChannelID(); @@ -142,8 +150,12 @@ bool MythScheduleHelper76::FillTimerEntry(MythTimerEntry& entry, const MythRecor { case Myth::ST_TitleSearch: entry.epgSearch = rule.Description(); + entry.isFullTextSearch = false; break; case Myth::ST_KeywordSearch: + entry.epgSearch = rule.Description(); + entry.isFullTextSearch = true; + break; case Myth::ST_PeopleSearch: case Myth::ST_PowerSearch: entry.epgSearch = rule.Description(); @@ -171,6 +183,7 @@ bool MythScheduleHelper76::FillTimerEntry(MythTimerEntry& entry, const MythRecor case TIMER_TYPE_RECORD_DAILY: case TIMER_TYPE_RECORD_ALL: case TIMER_TYPE_RECORD_SERIES: + case TIMER_TYPE_TEXT_SEARCH: case TIMER_TYPE_UNHANDLED: if (difftime(rule.NextRecording(), 0) > 0) { @@ -184,6 +197,11 @@ bool MythScheduleHelper76::FillTimerEntry(MythTimerEntry& entry, const MythRecor entry.startTime = entry.endTime = rule.LastRecorded(); timeadd(&entry.endTime, difftime(rule.EndTime(), rule.StartTime())); } + else + { + entry.startTime = rule.StartTime(); + entry.endTime = rule.EndTime(); + } break; default: entry.startTime = rule.StartTime(); @@ -215,7 +233,7 @@ MythRecordingRule MythScheduleHelper76::NewFromTimer(const MythTimerEntry& entry // that which is applied in function 'FillTimerEntry' MythRecordingRule rule; - + XBMC->Log(LOG_DEBUG, "76::%s", __FUNCTION__); if (withTemplate) { // Base on template @@ -480,7 +498,7 @@ MythRecordingRule MythScheduleHelper76::NewFromTimer(const MythTimerEntry& entry case TIMER_TYPE_RECORD_SERIES: { - if (!entry.epgInfo.IsNull()) + if (!entry.epgInfo.IsNull()) //Question - how does epgInfo get to be populated if this is an 'old' timer. { rule.SetType(Myth::RT_AllRecord); rule.SetFilter(Myth::FM_ThisChannel | Myth::FM_ThisSeries); @@ -501,6 +519,36 @@ MythRecordingRule MythScheduleHelper76::NewFromTimer(const MythTimerEntry& entry break; } + case TIMER_TYPE_TEXT_SEARCH: + { + if (entry.HasChannel()) + { + rule.SetFilter(Myth::FM_ThisChannel); + rule.SetChannelID(entry.chanid); + rule.SetCallsign(entry.callsign); + } + rule.SetType(Myth::RT_AllRecord); + rule.SetInactive(entry.isInactive); + rule.SetTitle(entry.title); + if (!entry.epgInfo.IsNull()) + { + rule.SetStartTime(entry.epgInfo.StartTime()); + rule.SetEndTime(entry.epgInfo.EndTime()); + rule.SetCategory(entry.epgInfo.Category()); + } + if (!entry.epgSearch.empty()) + { + if (entry.isFullTextSearch) + rule.SetSearchType(Myth::ST_KeywordSearch); + else + rule.SetSearchType(Myth::ST_TitleSearch); + rule.SetSubtitle(""); // Backend uses Subtitle as table join SQL for power searches (not needed for keyword or title) + rule.SetDescription(entry.epgSearch); // Backend uses description to find program by keywords or title and SQL for power searches + return rule; + } + break; + } + case TIMER_TYPE_DONT_RECORD: case TIMER_TYPE_OVERRIDE: case TIMER_TYPE_UPCOMING: @@ -521,13 +569,14 @@ MythRecordingRule MythScheduleHelper76::NewFromTimer(const MythTimerEntry& entry break; } rule.SetType(Myth::RT_UNKNOWN); - XBMC->Log(LOG_ERROR, "%s - Invalid timer %u: TYPE=%d CHANID=%u SIGN=%s ST=%u ET=%u", __FUNCTION__, entry.entryIndex, + XBMC->Log(LOG_ERROR, "76::%s - Invalid timer %u: TYPE=%d CHANID=%u SIGN=%s ST=%u ET=%u", __FUNCTION__, entry.entryIndex, entry.timerType, entry.chanid, entry.callsign.c_str(), (unsigned)entry.startTime, (unsigned)entry.endTime); return rule; } MythScheduleManager::RuleSummaryInfo MythScheduleHelper76::GetSummaryInfo(const MythRecordingRule &rule) const { + XBMC->Log(LOG_DEBUG, "76::%s:", __FUNCTION__); MythScheduleManager::RuleSummaryInfo meta; time_t st = rule.StartTime(); meta.isRepeating = false; @@ -593,6 +642,7 @@ MythScheduleManager::RuleSummaryInfo MythScheduleHelper76::GetSummaryInfo(const MythRecordingRule MythScheduleHelper76::NewDailyRecord(const MythEPGInfo& epgInfo) { + XBMC->Log(LOG_DEBUG, "76::%s:", __FUNCTION__); unsigned int filter; MythRecordingRule rule = this->NewFromTemplate(epgInfo); @@ -630,6 +680,7 @@ MythRecordingRule MythScheduleHelper76::NewDailyRecord(const MythEPGInfo& epgInf MythRecordingRule MythScheduleHelper76::NewWeeklyRecord(const MythEPGInfo& epgInfo) { + XBMC->Log(LOG_DEBUG, "76::%s:", __FUNCTION__); unsigned int filter; MythRecordingRule rule = this->NewFromTemplate(epgInfo); @@ -667,6 +718,7 @@ MythRecordingRule MythScheduleHelper76::NewWeeklyRecord(const MythEPGInfo& epgIn MythRecordingRule MythScheduleHelper76::NewChannelRecord(const MythEPGInfo& epgInfo) { + XBMC->Log(LOG_DEBUG, "76::%s:", __FUNCTION__); unsigned int filter; MythRecordingRule rule = this->NewFromTemplate(epgInfo); @@ -701,6 +753,7 @@ MythRecordingRule MythScheduleHelper76::NewChannelRecord(const MythEPGInfo& epgI MythRecordingRule MythScheduleHelper76::NewOneRecord(const MythEPGInfo& epgInfo) { + XBMC->Log(LOG_DEBUG, "76::%s:", __FUNCTION__); unsigned int filter; MythRecordingRule rule = this->NewFromTemplate(epgInfo); diff --git a/src/cppmyth/MythScheduleHelper85.cpp b/src/cppmyth/MythScheduleHelper85.cpp index 8be0900b..8c4245c7 100644 --- a/src/cppmyth/MythScheduleHelper85.cpp +++ b/src/cppmyth/MythScheduleHelper85.cpp @@ -44,9 +44,9 @@ bool MythScheduleHelper85::FillTimerEntry(MythTimerEntry& entry, const MythProgr case Myth::RS_PREVIOUS_RECORDING: //Previoulsy recorded but no longer in the library if (true /*!m_showNotRecording*/) { - XBMC->Log(LOG_DEBUG, "%s: Skipping %s:%s on %s because status %d and m_showNotRecording=%d", __FUNCTION__, + XBMC->Log(LOG_DEBUG, "85::%s: Skipping %s:%s on %s because status %d", __FUNCTION__, recording.Title().c_str(), recording.Subtitle().c_str(), recording.ChannelName().c_str(), - recording.Status(), 0 /*m_showNotRecording*/); + recording.Status()); return false; } default: diff --git a/src/cppmyth/MythScheduleManager.cpp b/src/cppmyth/MythScheduleManager.cpp index c861e270..cb160a75 100644 --- a/src/cppmyth/MythScheduleManager.cpp +++ b/src/cppmyth/MythScheduleManager.cpp @@ -138,13 +138,25 @@ void MythScheduleManager::Setup() { SAFE_DELETE(m_versionHelper); if (m_protoVersion >= 85) + { m_versionHelper = new MythScheduleHelper85(this, m_control); + XBMC->Log(LOG_DEBUG, "Using MythScheduleHelper85 and inherited functions"); + } else if (m_protoVersion >= 76) + { m_versionHelper = new MythScheduleHelper76(this, m_control); + XBMC->Log(LOG_DEBUG, "Using MythScheduleHelper76 and inherited functions"); + } else if (m_protoVersion >= 75) + { m_versionHelper = new MythScheduleHelper75(this, m_control); + XBMC->Log(LOG_DEBUG, "Using MythScheduleHelper75 and inherited functions"); + } else + { m_versionHelper = new MythScheduleHelperNoHelper(); + XBMC->Log(LOG_DEBUG, "Using MythScheduleHelperNoHelper"); + } } } @@ -204,11 +216,14 @@ MythScheduleManager::MSM_ERROR MythScheduleManager::SubmitTimer(const MythTimerE case TIMER_TYPE_RECORD_DAILY: case TIMER_TYPE_RECORD_ALL: case TIMER_TYPE_RECORD_SERIES: + case TIMER_TYPE_TEXT_SEARCH: break; default: return MSM_ERROR_NOT_IMPLEMENTED; } MythRecordingRule rule = m_versionHelper->NewFromTimer(entry, true); + XBMC->Log(LOG_DEBUG, "%s: Channel: %s(%u), Filter: %u, Type: %u, Search: %u",__FUNCTION__, + rule.Callsign().c_str(), rule.ChannelID(), rule.Filter(), rule.Type(), rule.SearchType()); MSM_ERROR ret = AddRecordingRule(rule); return ret; } @@ -231,13 +246,11 @@ MythScheduleManager::MSM_ERROR MythScheduleManager::UpdateTimer(const MythTimerE case TIMER_TYPE_RECORD_DAILY: case TIMER_TYPE_RECORD_ALL: case TIMER_TYPE_RECORD_SERIES: + case TIMER_TYPE_TEXT_SEARCH: { - if (entry.epgCheck && entry.epgInfo.IsNull()) - { - XBMC->Log(LOG_ERROR, "%s - index %u requires valid EPG info", __FUNCTION__, entry.entryIndex); - return MSM_ERROR_NOT_IMPLEMENTED; - } MythRecordingRule newrule = m_versionHelper->NewFromTimer(entry, false); + XBMC->Log(LOG_DEBUG, "%s: Channel: %s(%u), Filter: %u, Type: %u, Search: %u",__FUNCTION__, + newrule.Callsign().c_str(), newrule.ChannelID(), newrule.Filter(), newrule.Type(), newrule.SearchType()); return UpdateRecordingRule(entry.entryIndex, newrule); } default: @@ -263,6 +276,7 @@ MythScheduleManager::MSM_ERROR MythScheduleManager::DeleteTimer(const MythTimerE case TIMER_TYPE_RECORD_DAILY: case TIMER_TYPE_RECORD_ALL: case TIMER_TYPE_RECORD_SERIES: + case TIMER_TYPE_TEXT_SEARCH: if (force) return DeleteRecordingRule(entry.entryIndex); return MSM_ERROR_SUCCESS; @@ -689,6 +703,10 @@ MythScheduleManager::MSM_ERROR MythScheduleManager::UpdateRecordingRule(uint32_t handle.SetRecordingGroup(newrule.RecordingGroup()); handle.SetCheckDuplicatesInType(newrule.CheckDuplicatesInType()); handle.SetDuplicateControlMethod(newrule.DuplicateControlMethod()); + handle.SetChannelID(newrule.ChannelID()); + handle.SetCallsign(newrule.Callsign()); + handle.SetFilter(newrule.Filter()); + handle.SetSearchType(newrule.SearchType()); } XBMC->Log(LOG_DEBUG, "%s - Dealing with the problem using method %d", __FUNCTION__, method); diff --git a/src/cppmyth/MythScheduleManager.h b/src/cppmyth/MythScheduleManager.h index 3ce28d4b..900dc106 100644 --- a/src/cppmyth/MythScheduleManager.h +++ b/src/cppmyth/MythScheduleManager.h @@ -41,6 +41,7 @@ typedef enum TIMER_TYPE_RECORD_DAILY, // Record one showing every day TIMER_TYPE_RECORD_ALL, // Record all showings TIMER_TYPE_RECORD_SERIES, // Record series + TIMER_TYPE_TEXT_SEARCH, // Text Search // Keep last TIMER_TYPE_UPCOMING, // Upcoming TIMER_TYPE_OVERRIDE, // Override @@ -61,6 +62,7 @@ struct MythTimerEntry time_t startTime; time_t endTime; std::string epgSearch; + bool isFullTextSearch; std::string title; std::string description; std::string category; diff --git a/src/pvrclient-mythtv.cpp b/src/pvrclient-mythtv.cpp index 635b1529..32e191da 100644 --- a/src/pvrclient-mythtv.cpp +++ b/src/pvrclient-mythtv.cpp @@ -1492,7 +1492,7 @@ PVR_ERROR PVRClientMythTV::GetTimers(ADDON_HANDLE handle) tag.iTimerType = static_cast((*it)->timerType); PVR_STRCPY(tag.strTitle, (*it)->title.c_str()); PVR_STRCPY(tag.strEpgSearchString, (*it)->epgSearch.c_str()); - tag.bFullTextEpgSearch = false; + tag.bFullTextEpgSearch = (*it)->isFullTextSearch; PVR_STRCPY(tag.strDirectory, ""); // not implemented PVR_STRCPY(tag.strSummary, (*it)->description.c_str()); tag.iPriority = (*it)->priority; @@ -1727,6 +1727,7 @@ MythTimerEntry PVRClientMythTV::PVRtoTimerEntry(const PVR_TIMER& timer, bool che } if (hasEpgSearch) { + entry.isFullTextSearch = timer.bFullTextEpgSearch; unsigned i = 0; while (timer.strEpgSearchString[i] && isspace(timer.strEpgSearchString[i] != 0)) ++i; if (timer.strEpgSearchString[i])