/xbmc/guilib/GUIInfoTypes.cpp

http://github.com/xbmc/xbmc · C++ · 440 lines · 359 code · 51 blank · 30 comment · 77 complexity · 345eb6a5a9e73e120aebe2bd7adf22e9 MD5 · raw file

  1. /*
  2. * Copyright (C) 2005-2015 Team Kodi
  3. * http://kodi.tv
  4. *
  5. * This Program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2, or (at your option)
  8. * any later version.
  9. *
  10. * This Program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with Kodi; see the file COPYING. If not, see
  17. * <http://www.gnu.org/licenses/>.
  18. *
  19. */
  20. #include "GUIInfoTypes.h"
  21. #include "GUIInfoManager.h"
  22. #include "addons/AddonManager.h"
  23. #include "utils/log.h"
  24. #include "LocalizeStrings.h"
  25. #include "GUIColorManager.h"
  26. #include "GUIListItem.h"
  27. #include "utils/StringUtils.h"
  28. #include "addons/Skin.h"
  29. using ADDON::CAddonMgr;
  30. CGUIInfoBool::CGUIInfoBool(bool value)
  31. {
  32. m_value = value;
  33. }
  34. CGUIInfoBool::~CGUIInfoBool()
  35. {
  36. }
  37. void CGUIInfoBool::Parse(const std::string &expression, int context)
  38. {
  39. if (expression == "true")
  40. m_value = true;
  41. else if (expression == "false")
  42. m_value = false;
  43. else
  44. {
  45. m_info = g_infoManager.Register(expression, context);
  46. Update();
  47. }
  48. }
  49. void CGUIInfoBool::Update(const CGUIListItem *item /*= NULL*/)
  50. {
  51. if (m_info)
  52. m_value = m_info->Get(item);
  53. }
  54. CGUIInfoColor::CGUIInfoColor(uint32_t color)
  55. {
  56. m_color = color;
  57. m_info = 0;
  58. }
  59. CGUIInfoColor &CGUIInfoColor::operator=(color_t color)
  60. {
  61. m_color = color;
  62. m_info = 0;
  63. return *this;
  64. }
  65. CGUIInfoColor &CGUIInfoColor::operator=(const CGUIInfoColor &color)
  66. {
  67. m_color = color.m_color;
  68. m_info = color.m_info;
  69. return *this;
  70. }
  71. bool CGUIInfoColor::Update()
  72. {
  73. if (!m_info)
  74. return false; // no infolabel
  75. // Expand the infolabel, and then convert it to a color
  76. std::string infoLabel(g_infoManager.GetLabel(m_info));
  77. color_t color = !infoLabel.empty() ? g_colorManager.GetColor(infoLabel.c_str()) : 0;
  78. if (m_color != color)
  79. {
  80. m_color = color;
  81. return true;
  82. }
  83. else
  84. return false;
  85. }
  86. void CGUIInfoColor::Parse(const std::string &label, int context)
  87. {
  88. // Check for the standard $INFO[] block layout, and strip it if present
  89. std::string label2 = label;
  90. if (label == "-")
  91. return;
  92. if (StringUtils::StartsWithNoCase(label, "$var["))
  93. {
  94. label2 = label.substr(5, label.length() - 6);
  95. m_info = g_infoManager.TranslateSkinVariableString(label2, context);
  96. if (!m_info)
  97. m_info = g_infoManager.RegisterSkinVariableString(g_SkinInfo->CreateSkinVariable(label2, context));
  98. return;
  99. }
  100. if (StringUtils::StartsWithNoCase(label, "$info["))
  101. label2 = label.substr(6, label.length()-7);
  102. m_info = g_infoManager.TranslateString(label2);
  103. if (!m_info)
  104. m_color = g_colorManager.GetColor(label);
  105. }
  106. CGUIInfoLabel::CGUIInfoLabel() : m_dirty(false)
  107. {
  108. }
  109. CGUIInfoLabel::CGUIInfoLabel(const std::string &label, const std::string &fallback /*= ""*/, int context /*= 0*/) : m_dirty(false)
  110. {
  111. SetLabel(label, fallback, context);
  112. }
  113. int CGUIInfoLabel::GetIntValue(int contextWindow) const
  114. {
  115. std::string label = GetLabel(contextWindow);
  116. if (!label.empty())
  117. return strtol(label.c_str(), NULL, 10);
  118. return 0;
  119. }
  120. void CGUIInfoLabel::SetLabel(const std::string &label, const std::string &fallback, int context /*= 0*/)
  121. {
  122. m_fallback = fallback;
  123. Parse(label, context);
  124. }
  125. const std::string &CGUIInfoLabel::GetLabel(int contextWindow, bool preferImage, std::string *fallback /*= NULL*/) const
  126. {
  127. bool needsUpdate = m_dirty;
  128. if (!m_info.empty())
  129. {
  130. for (std::vector<CInfoPortion>::const_iterator portion = m_info.begin(); portion != m_info.end(); ++portion)
  131. {
  132. if (portion->m_info)
  133. {
  134. std::string infoLabel;
  135. if (preferImage)
  136. infoLabel = g_infoManager.GetImage(portion->m_info, contextWindow, fallback);
  137. if (infoLabel.empty())
  138. infoLabel = g_infoManager.GetLabel(portion->m_info, contextWindow, fallback);
  139. needsUpdate |= portion->NeedsUpdate(infoLabel);
  140. }
  141. }
  142. }
  143. else
  144. needsUpdate = !m_label.empty();
  145. return CacheLabel(needsUpdate);
  146. }
  147. const std::string &CGUIInfoLabel::GetItemLabel(const CGUIListItem *item, bool preferImages, std::string *fallback /*= NULL*/) const
  148. {
  149. bool needsUpdate = m_dirty;
  150. if (item->IsFileItem() && !m_info.empty())
  151. {
  152. for (std::vector<CInfoPortion>::const_iterator portion = m_info.begin(); portion != m_info.end(); ++portion)
  153. {
  154. if (portion->m_info)
  155. {
  156. std::string infoLabel;
  157. if (preferImages)
  158. infoLabel = g_infoManager.GetItemImage((const CFileItem *)item, portion->m_info, fallback);
  159. else
  160. infoLabel = g_infoManager.GetItemLabel((const CFileItem *)item, portion->m_info, fallback);
  161. needsUpdate |= portion->NeedsUpdate(infoLabel);
  162. }
  163. }
  164. }
  165. else
  166. needsUpdate = !m_label.empty();
  167. return CacheLabel(needsUpdate);
  168. }
  169. const std::string &CGUIInfoLabel::CacheLabel(bool rebuild) const
  170. {
  171. if (rebuild)
  172. {
  173. m_label.clear();
  174. for (std::vector<CInfoPortion>::const_iterator portion = m_info.begin(); portion != m_info.end(); ++portion)
  175. m_label += portion->Get();
  176. m_dirty = false;
  177. }
  178. if (m_label.empty()) // empty label, use the fallback
  179. return m_fallback;
  180. return m_label;
  181. }
  182. bool CGUIInfoLabel::IsEmpty() const
  183. {
  184. return m_info.empty();
  185. }
  186. bool CGUIInfoLabel::IsConstant() const
  187. {
  188. return m_info.empty() || (m_info.size() == 1 && m_info[0].m_info == 0);
  189. }
  190. bool CGUIInfoLabel::ReplaceSpecialKeywordReferences(const std::string &strInput, const std::string &strKeyword, const StringReplacerFunc &func, std::string &strOutput)
  191. {
  192. // replace all $strKeyword[value] with resolved strings
  193. std::string dollarStrPrefix = "$" + strKeyword + "[";
  194. size_t index = 0;
  195. size_t startPos;
  196. while ((startPos = strInput.find(dollarStrPrefix, index)) != std::string::npos)
  197. {
  198. size_t valuePos = startPos + dollarStrPrefix.size();
  199. size_t endPos = StringUtils::FindEndBracket(strInput, '[', ']', valuePos);
  200. if (endPos != std::string::npos)
  201. {
  202. if (index == 0) // first occurrence?
  203. strOutput.clear();
  204. strOutput += strInput.substr(index, startPos - index); // append part from the left side
  205. strOutput += func(strInput.substr(valuePos, endPos - valuePos)); // resolve and append value part
  206. index = endPos + 1;
  207. }
  208. else
  209. {
  210. // if closing bracket is missing, report error and leave incomplete reference in
  211. CLog::Log(LOGERROR, "Error parsing value - missing ']' in \"%s\"", strInput.c_str());
  212. break;
  213. }
  214. }
  215. if (index) // if we replaced anything
  216. {
  217. strOutput += strInput.substr(index); // append leftover from the right side
  218. return true;
  219. }
  220. return false;
  221. }
  222. bool CGUIInfoLabel::ReplaceSpecialKeywordReferences(std::string &work, const std::string &strKeyword, const StringReplacerFunc &func)
  223. {
  224. std::string output;
  225. if (ReplaceSpecialKeywordReferences(work, strKeyword, func, output))
  226. {
  227. work = output;
  228. return true;
  229. }
  230. return false;
  231. }
  232. std::string LocalizeReplacer(const std::string &str)
  233. {
  234. std::string replace = g_localizeStringsTemp.Get(atoi(str.c_str()));
  235. if (replace.empty())
  236. replace = g_localizeStrings.Get(atoi(str.c_str()));
  237. return replace;
  238. }
  239. std::string AddonReplacer(const std::string &str)
  240. {
  241. // assumes "addon.id #####"
  242. size_t length = str.find(" ");
  243. std::string addonid = str.substr(0, length);
  244. int stringid = atoi(str.substr(length + 1).c_str());
  245. return g_localizeStrings.GetAddonString(addonid, stringid);
  246. }
  247. std::string NumberReplacer(const std::string &str)
  248. {
  249. return str;
  250. }
  251. std::string CGUIInfoLabel::ReplaceLocalize(const std::string &label)
  252. {
  253. std::string work(label);
  254. ReplaceSpecialKeywordReferences(work, "LOCALIZE", LocalizeReplacer);
  255. ReplaceSpecialKeywordReferences(work, "NUMBER", NumberReplacer);
  256. return work;
  257. }
  258. std::string CGUIInfoLabel::ReplaceAddonStrings(const std::string &label)
  259. {
  260. std::string work(label);
  261. ReplaceSpecialKeywordReferences(work, "ADDON", AddonReplacer);
  262. return work;
  263. }
  264. enum EINFOFORMAT { NONE = 0, FORMATINFO, FORMATESCINFO, FORMATVAR, FORMATESCVAR };
  265. typedef struct
  266. {
  267. const char *str;
  268. EINFOFORMAT val;
  269. } infoformat;
  270. const static infoformat infoformatmap[] = {{ "$INFO[", FORMATINFO},
  271. { "$ESCINFO[", FORMATESCINFO},
  272. { "$VAR[", FORMATVAR},
  273. { "$ESCVAR[", FORMATESCVAR}};
  274. void CGUIInfoLabel::Parse(const std::string &label, int context)
  275. {
  276. m_info.clear();
  277. m_dirty = true;
  278. // Step 1: Replace all $LOCALIZE[number] with the real string
  279. std::string work = ReplaceLocalize(label);
  280. // Step 2: Replace all $ADDON[id number] with the real string
  281. work = ReplaceAddonStrings(work);
  282. // Step 3: Find all $INFO[info,prefix,postfix] blocks
  283. EINFOFORMAT format;
  284. do
  285. {
  286. format = NONE;
  287. size_t pos1 = work.size();
  288. size_t pos2;
  289. size_t len = 0;
  290. for (size_t i = 0; i < sizeof(infoformatmap) / sizeof(infoformat); i++)
  291. {
  292. pos2 = work.find(infoformatmap[i].str);
  293. if (pos2 != std::string::npos && pos2 < pos1)
  294. {
  295. pos1 = pos2;
  296. len = strlen(infoformatmap[i].str);
  297. format = infoformatmap[i].val;
  298. }
  299. }
  300. if (format != NONE)
  301. {
  302. if (pos1 > 0)
  303. m_info.push_back(CInfoPortion(0, work.substr(0, pos1), ""));
  304. pos2 = StringUtils::FindEndBracket(work, '[', ']', pos1 + len);
  305. if (pos2 != std::string::npos)
  306. {
  307. // decipher the block
  308. std::string block = work.substr(pos1 + len, pos2 - pos1 - len);
  309. std::vector<std::string> params = StringUtils::Split(block, ",");
  310. if (!params.empty())
  311. {
  312. int info;
  313. if (format == FORMATVAR || format == FORMATESCVAR)
  314. {
  315. info = g_infoManager.TranslateSkinVariableString(params[0], context);
  316. if (info == 0)
  317. info = g_infoManager.RegisterSkinVariableString(g_SkinInfo->CreateSkinVariable(params[0], context));
  318. if (info == 0) // skinner didn't define this conditional label!
  319. CLog::Log(LOGWARNING, "Label Formating: $VAR[%s] is not defined", params[0].c_str());
  320. }
  321. else
  322. info = g_infoManager.TranslateString(params[0]);
  323. std::string prefix, postfix;
  324. if (params.size() > 1)
  325. prefix = params[1];
  326. if (params.size() > 2)
  327. postfix = params[2];
  328. m_info.push_back(CInfoPortion(info, prefix, postfix, format == FORMATESCINFO || format == FORMATESCVAR));
  329. }
  330. // and delete it from our work string
  331. work = work.substr(pos2 + 1);
  332. }
  333. else
  334. {
  335. CLog::Log(LOGERROR, "Error parsing label - missing ']' in \"%s\"", label.c_str());
  336. return;
  337. }
  338. }
  339. }
  340. while (format != NONE);
  341. if (!work.empty())
  342. m_info.push_back(CInfoPortion(0, work, ""));
  343. }
  344. CGUIInfoLabel::CInfoPortion::CInfoPortion(int info, const std::string &prefix, const std::string &postfix, bool escaped /*= false */):
  345. m_prefix(prefix),
  346. m_postfix(postfix)
  347. {
  348. m_info = info;
  349. m_escaped = escaped;
  350. // filter our prefix and postfix for comma's
  351. StringUtils::Replace(m_prefix, "$COMMA", ",");
  352. StringUtils::Replace(m_postfix, "$COMMA", ",");
  353. StringUtils::Replace(m_prefix, "$LBRACKET", "["); StringUtils::Replace(m_prefix, "$RBRACKET", "]");
  354. StringUtils::Replace(m_postfix, "$LBRACKET", "["); StringUtils::Replace(m_postfix, "$RBRACKET", "]");
  355. }
  356. bool CGUIInfoLabel::CInfoPortion::NeedsUpdate(const std::string &label) const
  357. {
  358. if (m_label != label)
  359. {
  360. m_label = label;
  361. return true;
  362. }
  363. return false;
  364. }
  365. std::string CGUIInfoLabel::CInfoPortion::Get() const
  366. {
  367. if (!m_info)
  368. return m_prefix;
  369. else if (m_label.empty())
  370. return "";
  371. std::string label = m_prefix + m_label + m_postfix;
  372. if (m_escaped) // escape all quotes and backslashes, then quote
  373. {
  374. StringUtils::Replace(label, "\\", "\\\\");
  375. StringUtils::Replace(label, "\"", "\\\"");
  376. return "\"" + label + "\"";
  377. }
  378. return label;
  379. }
  380. std::string CGUIInfoLabel::GetLabel(const std::string &label, int contextWindow /*= 0*/, bool preferImage /*= false */)
  381. { // translate the label
  382. CGUIInfoLabel info(label, "", contextWindow);
  383. return info.GetLabel(contextWindow, preferImage);
  384. }
  385. std::string CGUIInfoLabel::GetItemLabel(const std::string &label, const CGUIListItem *item, bool preferImage /*= false */)
  386. { // translate the label
  387. CGUIInfoLabel info(label);
  388. return info.GetItemLabel(item, preferImage);
  389. }