PageRenderTime 103ms CodeModel.GetById 47ms app.highlight 51ms RepoModel.GetById 1ms app.codeStats 1ms

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