1 /*
2  *  Copyright (C) 2012-2018 Team Kodi
3  *  This file is part of Kodi - https://kodi.tv
4  *
5  *  SPDX-License-Identifier: GPL-2.0-or-later
6  *  See LICENSES/README.md for more information.
7  */
8 
9 #include "PVRChannelNumberInputHandler.h"
10 
11 #include "ServiceBroker.h"
12 #include "settings/AdvancedSettings.h"
13 #include "settings/SettingsComponent.h"
14 #include "threads/SingleLock.h"
15 #include "utils/StringUtils.h"
16 
17 #include <algorithm>
18 #include <cstdlib>
19 #include <string>
20 
21 namespace PVR
22 {
23 
CPVRChannelNumberInputHandler()24 CPVRChannelNumberInputHandler::CPVRChannelNumberInputHandler()
25 : CPVRChannelNumberInputHandler(CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_iPVRNumericChannelSwitchTimeout, CHANNEL_NUMBER_INPUT_MAX_DIGITS)
26 {
27 }
28 
CPVRChannelNumberInputHandler(int iDelay,int iMaxDigits)29 CPVRChannelNumberInputHandler::CPVRChannelNumberInputHandler(int iDelay, int iMaxDigits /* = CHANNEL_NUMBER_INPUT_MAX_DIGITS */)
30 : m_iDelay(iDelay),
31   m_iMaxDigits(iMaxDigits),
32   m_timer(this)
33 {
34 }
35 
OnTimeout()36 void CPVRChannelNumberInputHandler::OnTimeout()
37 {
38   if (m_inputBuffer.empty())
39   {
40     CSingleLock lock(m_mutex);
41     m_label.erase();
42   }
43   else
44   {
45     // call the overridden worker method
46     OnInputDone();
47 
48     CSingleLock lock(m_mutex);
49 
50     // erase input buffer immediately , but...
51     m_inputBuffer.erase();
52 
53     // ... display the label for another .5 secs if we stopped the timer before regular timeout.
54     if (m_timer.IsRunning())
55       m_label.erase();
56     else
57       m_timer.Start(500);
58   }
59 }
60 
ExecuteAction()61 void CPVRChannelNumberInputHandler::ExecuteAction()
62 {
63   m_timer.Stop();
64   OnTimeout();
65 }
66 
CheckInputAndExecuteAction()67 bool CPVRChannelNumberInputHandler::CheckInputAndExecuteAction()
68 {
69   const CPVRChannelNumber channelNumber = GetChannelNumber();
70   if (channelNumber.IsValid())
71   {
72     // we have a valid channel number; execute the associated action now.
73     ExecuteAction();
74     return true;
75   }
76   return false;
77 }
78 
AppendChannelNumberCharacter(char cCharacter)79 void CPVRChannelNumberInputHandler::AppendChannelNumberCharacter(char cCharacter)
80 {
81   if (cCharacter != CPVRChannelNumber::SEPARATOR && (cCharacter < '0' || cCharacter > '9'))
82     return;
83 
84   CSingleLock lock(m_mutex);
85 
86   if (cCharacter == CPVRChannelNumber::SEPARATOR)
87   {
88     // no leading separator
89     if (m_inputBuffer.empty())
90       return;
91 
92     // max one separator
93     if (m_inputBuffer.find(CPVRChannelNumber::SEPARATOR) != std::string::npos)
94       return;
95   }
96 
97   if (m_inputBuffer.size() == static_cast<size_t>(m_iMaxDigits))
98   {
99     m_inputBuffer.erase(m_inputBuffer.begin());
100     m_label = m_inputBuffer;
101   }
102   else if (m_inputBuffer.empty())
103   {
104     m_sortedChannelNumbers.clear();
105     GetChannelNumbers(m_sortedChannelNumbers);
106 
107     std::sort(m_sortedChannelNumbers.begin(), m_sortedChannelNumbers.end());
108   }
109 
110   m_inputBuffer.append(&cCharacter, 1);
111   m_label = m_inputBuffer;
112 
113   for (auto it = m_sortedChannelNumbers.begin(); it != m_sortedChannelNumbers.end();)
114   {
115     const std::string channel = *it;
116     ++it;
117 
118     if (StringUtils::StartsWith(channel, m_inputBuffer))
119     {
120       if (it != m_sortedChannelNumbers.end() && StringUtils::StartsWith(*it, m_inputBuffer))
121       {
122         // there are alternative numbers; wait for more input
123         break;
124       }
125 
126       // no alternatives; complete the number and fire immediately
127       m_inputBuffer = channel;
128       m_label = m_inputBuffer;
129       ExecuteAction();
130       return;
131     }
132   }
133 
134   if (!m_timer.IsRunning())
135     m_timer.Start(m_iDelay);
136   else
137     m_timer.Restart();
138 }
139 
GetChannelNumber() const140 CPVRChannelNumber CPVRChannelNumberInputHandler::GetChannelNumber() const
141 {
142   int iChannelNumber = 0;
143   int iSubChannelNumber = 0;
144 
145   CSingleLock lock(m_mutex);
146 
147   size_t pos = m_inputBuffer.find(CPVRChannelNumber::SEPARATOR);
148   if (pos != std::string::npos)
149   {
150     // main + sub
151     if (pos != 0)
152     {
153       iChannelNumber = std::atoi(m_inputBuffer.substr(0, pos).c_str());
154       if (pos != m_inputBuffer.size() - 1)
155         iSubChannelNumber = std::atoi(m_inputBuffer.substr(pos + 1).c_str());
156     }
157   }
158   else
159   {
160     // only main
161     iChannelNumber = std::atoi(m_inputBuffer.c_str());
162   }
163 
164   return CPVRChannelNumber(iChannelNumber, iSubChannelNumber);
165 }
166 
HasChannelNumber() const167 bool CPVRChannelNumberInputHandler::HasChannelNumber() const
168 {
169   return !m_inputBuffer.empty();
170 }
171 
GetChannelNumberLabel() const172 std::string CPVRChannelNumberInputHandler::GetChannelNumberLabel() const
173 {
174   CSingleLock lock(m_mutex);
175   return m_label;
176 }
177 
178 } // namespace PVR
179