summaryrefslogtreecommitdiffstats
path: root/xbmc/interfaces/info
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 18:07:22 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 18:07:22 +0000
commitc04dcc2e7d834218ef2d4194331e383402495ae1 (patch)
tree7333e38d10d75386e60f336b80c2443c1166031d /xbmc/interfaces/info
parentInitial commit. (diff)
downloadkodi-c04dcc2e7d834218ef2d4194331e383402495ae1.tar.xz
kodi-c04dcc2e7d834218ef2d4194331e383402495ae1.zip
Adding upstream version 2:20.4+dfsg.upstream/2%20.4+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'xbmc/interfaces/info')
-rw-r--r--xbmc/interfaces/info/CMakeLists.txt10
-rw-r--r--xbmc/interfaces/info/Info.h19
-rw-r--r--xbmc/interfaces/info/InfoBool.cpp25
-rw-r--r--xbmc/interfaces/info/InfoBool.h83
-rw-r--r--xbmc/interfaces/info/InfoExpression.cpp311
-rw-r--r--xbmc/interfaces/info/InfoExpression.h117
-rw-r--r--xbmc/interfaces/info/SkinVariable.cpp82
-rw-r--r--xbmc/interfaces/info/SkinVariable.h56
8 files changed, 703 insertions, 0 deletions
diff --git a/xbmc/interfaces/info/CMakeLists.txt b/xbmc/interfaces/info/CMakeLists.txt
new file mode 100644
index 0000000..621ecfc
--- /dev/null
+++ b/xbmc/interfaces/info/CMakeLists.txt
@@ -0,0 +1,10 @@
+set(SOURCES InfoBool.cpp
+ InfoExpression.cpp
+ SkinVariable.cpp)
+
+set(HEADERS Info.h
+ InfoBool.h
+ InfoExpression.h
+ SkinVariable.h)
+
+core_add_library(info_interface)
diff --git a/xbmc/interfaces/info/Info.h b/xbmc/interfaces/info/Info.h
new file mode 100644
index 0000000..e454e3c
--- /dev/null
+++ b/xbmc/interfaces/info/Info.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2022 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+namespace INFO
+{
+/*! Default context of the INFO interface
+ @note when info conditions are evaluated different contexts can be passed. Context usually refers to the window ids where
+ the conditions are being evaluated. By default conditions, skin variables and labels are initialized using the DEFAULT_CONTEXT
+ value unless specifically bound to a given window.
+ */
+constexpr int DEFAULT_CONTEXT = 0;
+} // namespace INFO
diff --git a/xbmc/interfaces/info/InfoBool.cpp b/xbmc/interfaces/info/InfoBool.cpp
new file mode 100644
index 0000000..65b1f44
--- /dev/null
+++ b/xbmc/interfaces/info/InfoBool.cpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2013-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "InfoBool.h"
+
+#include "utils/StringUtils.h"
+
+namespace INFO
+{
+ InfoBool::InfoBool(const std::string &expression, int context, unsigned int &refreshCounter)
+ : m_value(false),
+ m_context(context),
+ m_listItemDependent(false),
+ m_expression(expression),
+ m_refreshCounter(0),
+ m_parentRefreshCounter(refreshCounter)
+ {
+ StringUtils::ToLower(m_expression);
+ }
+}
diff --git a/xbmc/interfaces/info/InfoBool.h b/xbmc/interfaces/info/InfoBool.h
new file mode 100644
index 0000000..6906276
--- /dev/null
+++ b/xbmc/interfaces/info/InfoBool.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include <memory>
+#include <string>
+
+class CGUIListItem;
+
+namespace INFO
+{
+/*!
+ \ingroup info
+ \brief Base class, wrapping boolean conditions and expressions
+ */
+class InfoBool
+{
+public:
+ InfoBool(const std::string &expression, int context, unsigned int &refreshCounter);
+ virtual ~InfoBool() = default;
+
+ virtual void Initialize() {}
+
+ /*! \brief Get the value of this info bool
+ This is called to update (if dirty) and fetch the value of the info bool
+ \param contextWindow the context (window id) where this condition is being evaluated
+ \param item the item used to evaluate the bool
+ */
+ inline bool Get(int contextWindow, const CGUIListItem* item = nullptr)
+ {
+ if (item && m_listItemDependent)
+ Update(contextWindow, item);
+ else if (m_refreshCounter != m_parentRefreshCounter || m_refreshCounter == 0)
+ {
+ Update(contextWindow, nullptr);
+ m_refreshCounter = m_parentRefreshCounter;
+ }
+ return m_value;
+ }
+
+ bool operator==(const InfoBool &right) const
+ {
+ return (m_context == right.m_context &&
+ m_expression == right.m_expression);
+ }
+
+ bool operator<(const InfoBool &right) const
+ {
+ if (m_context < right.m_context)
+ return true;
+ else if (m_context == right.m_context)
+ return m_expression < right.m_expression;
+ else
+ return false;
+ }
+
+ /*! \brief Update the value of this info bool
+ This is called if and only if the info bool is dirty, allowing it to update it's current value
+ */
+ virtual void Update(int contextWindow, const CGUIListItem* item) {}
+
+ const std::string &GetExpression() const { return m_expression; }
+ bool ListItemDependent() const { return m_listItemDependent; }
+protected:
+
+ bool m_value; ///< current value
+ int m_context; ///< contextual information to go with the condition
+ bool m_listItemDependent; ///< do not cache if a listitem pointer is given
+ std::string m_expression; ///< original expression
+
+private:
+ unsigned int m_refreshCounter;
+ unsigned int &m_parentRefreshCounter;
+};
+
+typedef std::shared_ptr<InfoBool> InfoPtr;
+};
diff --git a/xbmc/interfaces/info/InfoExpression.cpp b/xbmc/interfaces/info/InfoExpression.cpp
new file mode 100644
index 0000000..dc9aa63
--- /dev/null
+++ b/xbmc/interfaces/info/InfoExpression.cpp
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "InfoExpression.h"
+
+#include "GUIInfoManager.h"
+#include "ServiceBroker.h"
+#include "guilib/GUIComponent.h"
+#include "utils/log.h"
+
+#include <list>
+#include <memory>
+#include <stack>
+
+using namespace INFO;
+
+void InfoSingle::Initialize()
+{
+ m_condition = CServiceBroker::GetGUI()->GetInfoManager().TranslateSingleString(m_expression, m_listItemDependent);
+}
+
+void InfoSingle::Update(int contextWindow, const CGUIListItem* item)
+{
+ // use propagated context in case this info has the default context (i.e. if not tied to a specific window)
+ // its value might depend on the context in which the evaluation was called
+ int context = m_context == DEFAULT_CONTEXT ? contextWindow : m_context;
+ m_value = CServiceBroker::GetGUI()->GetInfoManager().GetBool(m_condition, context, item);
+}
+
+void InfoExpression::Initialize()
+{
+ if (!Parse(m_expression))
+ {
+ CLog::Log(LOGERROR, "Error parsing boolean expression {}", m_expression);
+ m_expression_tree = std::make_shared<InfoLeaf>(CServiceBroker::GetGUI()->GetInfoManager().Register("false", 0), false);
+ }
+}
+
+void InfoExpression::Update(int contextWindow, const CGUIListItem* item)
+{
+ // use propagated context in case this info expression has the default context (i.e. if not tied to a specific window)
+ // its value might depend on the context in which the evaluation was called
+ int context = m_context == DEFAULT_CONTEXT ? contextWindow : m_context;
+ m_value = m_expression_tree->Evaluate(context, item);
+}
+
+/* Expressions are rewritten at parse time into a form which favours the
+ * formation of groups of associative nodes. These groups are then reordered at
+ * evaluation time such that nodes whose value renders the evaluation of the
+ * remainder of the group unnecessary tend to be evaluated first (these are
+ * true nodes for OR subexpressions, or false nodes for AND subexpressions).
+ * The end effect is to minimise the number of leaf nodes that need to be
+ * evaluated in order to determine the value of the expression. The runtime
+ * adaptability has the advantage of not being customised for any particular skin.
+ *
+ * The modifications to the expression at parse time fall into two groups:
+ * 1) Moving logical NOTs so that they are only applied to leaf nodes.
+ * For example, rewriting ![A+B]|C as !A|!B|C allows reordering such that
+ * any of the three leaves can be evaluated first.
+ * 2) Combining adjacent AND or OR operations such that each path from the root
+ * to a leaf encounters a strictly alternating pattern of AND and OR
+ * operations. So [A|B]|[C|D+[[E|F]|G] becomes A|B|C|[D+[E|F|G]].
+ */
+
+bool InfoExpression::InfoLeaf::Evaluate(int contextWindow, const CGUIListItem* item)
+{
+ return m_invert ^ m_info->Get(contextWindow, item);
+}
+
+InfoExpression::InfoAssociativeGroup::InfoAssociativeGroup(
+ node_type_t type,
+ const InfoSubexpressionPtr &left,
+ const InfoSubexpressionPtr &right)
+ : m_type(type)
+{
+ AddChild(right);
+ AddChild(left);
+}
+
+void InfoExpression::InfoAssociativeGroup::AddChild(const InfoSubexpressionPtr &child)
+{
+ m_children.push_front(child); // largely undoes the effect of parsing right-associative
+}
+
+void InfoExpression::InfoAssociativeGroup::Merge(const std::shared_ptr<InfoAssociativeGroup>& other)
+{
+ m_children.splice(m_children.end(), other->m_children);
+}
+
+bool InfoExpression::InfoAssociativeGroup::Evaluate(int contextWindow, const CGUIListItem* item)
+{
+ /* Handle either AND or OR by using the relation
+ * A AND B == !(!A OR !B)
+ * to convert ANDs into ORs
+ */
+ std::list<InfoSubexpressionPtr>::iterator last = m_children.end();
+ std::list<InfoSubexpressionPtr>::iterator it = m_children.begin();
+ bool use_and = (m_type == NODE_AND);
+ bool result = use_and ^ (*it)->Evaluate(contextWindow, item);
+ while (!result && ++it != last)
+ {
+ result = use_and ^ (*it)->Evaluate(contextWindow, item);
+ if (result)
+ {
+ /* Move this child to the head of the list so we evaluate faster next time */
+ m_children.push_front(*it);
+ m_children.erase(it);
+ }
+ }
+ return use_and ^ result;
+}
+
+/* Expressions are parsed using the shunting-yard algorithm. Binary operators
+ * (AND/OR) are treated as right-associative so that we don't need to make a
+ * special case for the unary NOT operator. This has no effect upon the answers
+ * generated, though the initial sequence of evaluation of leaves may be
+ * different from what you might expect.
+ */
+
+InfoExpression::operator_t InfoExpression::GetOperator(char ch)
+{
+ if (ch == '[')
+ return OPERATOR_LB;
+ else if (ch == ']')
+ return OPERATOR_RB;
+ else if (ch == '!')
+ return OPERATOR_NOT;
+ else if (ch == '+')
+ return OPERATOR_AND;
+ else if (ch == '|')
+ return OPERATOR_OR;
+ else
+ return OPERATOR_NONE;
+}
+
+void InfoExpression::OperatorPop(std::stack<operator_t> &operator_stack, bool &invert, std::stack<InfoSubexpressionPtr> &nodes)
+{
+ operator_t op2 = operator_stack.top();
+ operator_stack.pop();
+ if (op2 == OPERATOR_NOT)
+ {
+ invert = !invert;
+ }
+ else
+ {
+ // At this point, it can only be OPERATOR_AND or OPERATOR_OR
+ if (invert)
+ op2 = (operator_t) (OPERATOR_AND ^ OPERATOR_OR ^ op2);
+ node_type_t new_type = op2 == OPERATOR_AND ? NODE_AND : NODE_OR;
+
+ InfoSubexpressionPtr right = nodes.top();
+ nodes.pop();
+ InfoSubexpressionPtr left = nodes.top();
+
+ node_type_t right_type = right->Type();
+ node_type_t left_type = left->Type();
+
+ // Combine associative operations into the same node where possible
+ if (left_type == new_type && right_type == new_type)
+ /* For example: AND
+ * / \ ____ AND ____
+ * AND AND -> / / \ \
+ * / \ / \ leaf leaf leaf leaf
+ * leaf leaf leaf leaf
+ */
+ std::static_pointer_cast<InfoAssociativeGroup>(left)->Merge(std::static_pointer_cast<InfoAssociativeGroup>(right));
+ else if (left_type == new_type)
+ /* For example: AND AND
+ * / \ / | \
+ * AND OR -> leaf leaf OR
+ * / \ / \ / \
+ * leaf leaf leaf leaf leaf leaf
+ */
+ std::static_pointer_cast<InfoAssociativeGroup>(left)->AddChild(right);
+ else
+ {
+ nodes.pop();
+ if (right_type == new_type)
+ {
+ /* For example: AND AND
+ * / \ / | \
+ * OR AND -> OR leaf leaf
+ * / \ / \ / \
+ * leaf leaf leaf leaf leaf leaf
+ */
+ std::static_pointer_cast<InfoAssociativeGroup>(right)->AddChild(left);
+ nodes.push(right);
+ }
+ else
+ /* For example: AND which can't be simplified, and
+ * / \ requires a new AND node to be
+ * OR OR created with the two OR nodes
+ * / \ / \ as children
+ * leaf leaf leaf leaf
+ */
+ nodes.push(std::make_shared<InfoAssociativeGroup>(new_type, left, right));
+ }
+ }
+}
+
+bool InfoExpression::Parse(const std::string &expression)
+{
+ const char *s = expression.c_str();
+ std::string operand;
+ std::stack<operator_t> operator_stack;
+ bool invert = false;
+ std::stack<InfoSubexpressionPtr> nodes;
+ // The next two are for syntax-checking purposes
+ bool after_binaryoperator = true;
+ int bracket_count = 0;
+
+ CGUIInfoManager& infoMgr = CServiceBroker::GetGUI()->GetInfoManager();
+
+ char c;
+ // Skip leading whitespace - don't want it to count as an operand if that's all there is
+ while (isspace((unsigned char)(c=*s)))
+ s++;
+
+ while ((c = *s++) != '\0')
+ {
+ operator_t op;
+ if ((op = GetOperator(c)) != OPERATOR_NONE)
+ {
+ // Character is an operator
+ if ((!after_binaryoperator && (c == '!' || c == '[')) ||
+ (after_binaryoperator && (c == ']' || c == '+' || c == '|')))
+ {
+ CLog::Log(LOGERROR, "Misplaced {}", c);
+ return false;
+ }
+ if (c == '[')
+ bracket_count++;
+ else if (c == ']' && bracket_count-- == 0)
+ {
+ CLog::Log(LOGERROR, "Unmatched ]");
+ return false;
+ }
+ if (!operand.empty())
+ {
+ InfoPtr info = infoMgr.Register(operand, m_context);
+ if (!info)
+ {
+ CLog::Log(LOGERROR, "Bad operand '{}'", operand);
+ return false;
+ }
+ /* Propagate any listItem dependency from the operand to the expression */
+ m_listItemDependent |= info->ListItemDependent();
+ nodes.push(std::make_shared<InfoLeaf>(info, invert));
+ /* Reuse operand string for next operand */
+ operand.clear();
+ }
+
+ // Handle any higher-priority stacked operators, except when the new operator is left-bracket.
+ // For a right-bracket, this will stop with the matching left-bracket at the top of the operator stack.
+ if (op != OPERATOR_LB)
+ {
+ while (!operator_stack.empty() && operator_stack.top() > op)
+ OperatorPop(operator_stack, invert, nodes);
+ }
+ if (op == OPERATOR_RB)
+ operator_stack.pop(); // remove the matching left-bracket
+ else
+ operator_stack.push(op);
+ if (op == OPERATOR_NOT)
+ invert = !invert;
+
+ if (c == '+' || c == '|')
+ after_binaryoperator = true;
+ // Skip trailing whitespace - don't want it to count as an operand if that's all there is
+ while (isspace((unsigned char)(c=*s))) s++;
+ }
+ else
+ {
+ // Character is part of operand
+ operand += c;
+ after_binaryoperator = false;
+ }
+ }
+ if (bracket_count > 0)
+ {
+ CLog::Log(LOGERROR, "Unmatched [");
+ return false;
+ }
+ if (after_binaryoperator)
+ {
+ CLog::Log(LOGERROR, "Missing operand");
+ return false;
+ }
+ if (!operand.empty())
+ {
+ InfoPtr info = infoMgr.Register(operand, m_context);
+ if (!info)
+ {
+ CLog::Log(LOGERROR, "Bad operand '{}'", operand);
+ return false;
+ }
+ /* Propagate any listItem dependency from the operand to the expression */
+ m_listItemDependent |= info->ListItemDependent();
+ nodes.push(std::make_shared<InfoLeaf>(info, invert));
+ }
+ while (!operator_stack.empty())
+ OperatorPop(operator_stack, invert, nodes);
+
+ m_expression_tree = nodes.top();
+ return true;
+}
diff --git a/xbmc/interfaces/info/InfoExpression.h b/xbmc/interfaces/info/InfoExpression.h
new file mode 100644
index 0000000..1a0877c
--- /dev/null
+++ b/xbmc/interfaces/info/InfoExpression.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "InfoBool.h"
+
+#include <list>
+#include <stack>
+#include <utility>
+#include <vector>
+
+class CGUIListItem;
+
+namespace INFO
+{
+/*! \brief Class to wrap active boolean conditions
+ */
+class InfoSingle : public InfoBool
+{
+public:
+ InfoSingle(const std::string& expression, int context, unsigned int& refreshCounter)
+ : InfoBool(expression, context, refreshCounter)
+ {
+ }
+ void Initialize() override;
+
+ void Update(int contextWindow, const CGUIListItem* item) override;
+
+private:
+ int m_condition; ///< actual condition this represents
+};
+
+/*! \brief Class to wrap active boolean expressions
+ */
+class InfoExpression : public InfoBool
+{
+public:
+ InfoExpression(const std::string& expression, int context, unsigned int& refreshCounter)
+ : InfoBool(expression, context, refreshCounter)
+ {
+ }
+ ~InfoExpression() override = default;
+
+ void Initialize() override;
+
+ void Update(int contextWindow, const CGUIListItem* item) override;
+
+private:
+ typedef enum
+ {
+ OPERATOR_NONE = 0,
+ OPERATOR_LB, // 1
+ OPERATOR_RB, // 2
+ OPERATOR_OR, // 3
+ OPERATOR_AND, // 4
+ OPERATOR_NOT, // 5
+ } operator_t;
+
+ typedef enum
+ {
+ NODE_LEAF,
+ NODE_AND,
+ NODE_OR,
+ } node_type_t;
+
+ // An abstract base class for nodes in the expression tree
+ class InfoSubexpression
+ {
+ public:
+ virtual ~InfoSubexpression(void) = default; // so we can destruct derived classes using a pointer to their base class
+ virtual bool Evaluate(int contextWindow, const CGUIListItem* item) = 0;
+ virtual node_type_t Type() const=0;
+ };
+
+ typedef std::shared_ptr<InfoSubexpression> InfoSubexpressionPtr;
+
+ // A leaf node in the expression tree
+ class InfoLeaf : public InfoSubexpression
+ {
+ public:
+ InfoLeaf(InfoPtr info, bool invert) : m_info(std::move(info)), m_invert(invert) {}
+ bool Evaluate(int contextWindow, const CGUIListItem* item) override;
+ node_type_t Type() const override { return NODE_LEAF; }
+
+ private:
+ InfoPtr m_info;
+ bool m_invert;
+ };
+
+ // A branch node in the expression tree
+ class InfoAssociativeGroup : public InfoSubexpression
+ {
+ public:
+ InfoAssociativeGroup(node_type_t type, const InfoSubexpressionPtr &left, const InfoSubexpressionPtr &right);
+ void AddChild(const InfoSubexpressionPtr &child);
+ void Merge(const std::shared_ptr<InfoAssociativeGroup>& other);
+ bool Evaluate(int contextWindow, const CGUIListItem* item) override;
+ node_type_t Type() const override { return m_type; }
+
+ private:
+ node_type_t m_type;
+ std::list<InfoSubexpressionPtr> m_children;
+ };
+
+ static operator_t GetOperator(char ch);
+ static void OperatorPop(std::stack<operator_t> &operator_stack, bool &invert, std::stack<InfoSubexpressionPtr> &nodes);
+ bool Parse(const std::string &expression);
+ InfoSubexpressionPtr m_expression_tree;
+};
+
+};
diff --git a/xbmc/interfaces/info/SkinVariable.cpp b/xbmc/interfaces/info/SkinVariable.cpp
new file mode 100644
index 0000000..53980b9
--- /dev/null
+++ b/xbmc/interfaces/info/SkinVariable.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "SkinVariable.h"
+
+#include "GUIInfoManager.h"
+#include "ServiceBroker.h"
+#include "guilib/GUIComponent.h"
+#include "utils/XBMCTinyXML.h"
+
+using namespace INFO;
+using namespace KODI;
+
+const CSkinVariableString* CSkinVariable::CreateFromXML(const TiXmlElement& node, int context)
+{
+ const char* name = node.Attribute("name");
+ if (name)
+ {
+ CSkinVariableString* tmp = new CSkinVariableString;
+ tmp->m_name = name;
+ tmp->m_context = context;
+ const TiXmlElement* valuenode = node.FirstChildElement("value");
+ while (valuenode)
+ {
+ CSkinVariableString::ConditionLabelPair pair;
+ const char *condition = valuenode->Attribute("condition");
+ if (condition)
+ pair.m_condition = CServiceBroker::GetGUI()->GetInfoManager().Register(condition, context);
+
+ auto label = valuenode->FirstChild() ? valuenode->FirstChild()->ValueStr() : "";
+ pair.m_label = GUILIB::GUIINFO::CGUIInfoLabel(label);
+ tmp->m_conditionLabelPairs.push_back(pair);
+ if (!pair.m_condition)
+ break; // once we reach default value (without condition) break iterating
+
+ valuenode = valuenode->NextSiblingElement("value");
+ }
+ if (!tmp->m_conditionLabelPairs.empty())
+ return tmp;
+ delete tmp;
+ }
+ return NULL;
+}
+
+CSkinVariableString::CSkinVariableString() = default;
+
+int CSkinVariableString::GetContext() const
+{
+ return m_context;
+}
+
+const std::string& CSkinVariableString::GetName() const
+{
+ return m_name;
+}
+
+std::string CSkinVariableString::GetValue(int contextWindow,
+ bool preferImage /* = false */,
+ const CGUIListItem* item /* = nullptr */) const
+{
+ for (const auto& it : m_conditionLabelPairs)
+ {
+ // use propagated context in case this skin variable has the default context (i.e. if not tied to a specific window)
+ // nested skin variables are supported
+ int context = m_context == INFO::DEFAULT_CONTEXT ? contextWindow : m_context;
+ if (!it.m_condition || it.m_condition->Get(context, item))
+ {
+ if (item)
+ return it.m_label.GetItemLabel(item, preferImage);
+ else
+ {
+ return it.m_label.GetLabel(context, preferImage);
+ }
+ }
+ }
+ return "";
+}
diff --git a/xbmc/interfaces/info/SkinVariable.h b/xbmc/interfaces/info/SkinVariable.h
new file mode 100644
index 0000000..f00f96f
--- /dev/null
+++ b/xbmc/interfaces/info/SkinVariable.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "guilib/guiinfo/GUIInfoLabel.h"
+#include "interfaces/info/InfoBool.h"
+
+#include <string>
+#include <vector>
+
+class TiXmlElement;
+
+namespace INFO
+{
+class CSkinVariableString;
+
+class CSkinVariable
+{
+public:
+ static const CSkinVariableString* CreateFromXML(const TiXmlElement& node, int context);
+};
+
+class CSkinVariableString
+{
+public:
+ const std::string& GetName() const;
+ int GetContext() const;
+ std::string GetValue(int contextWindow,
+ bool preferImage = false,
+ const CGUIListItem* item = nullptr) const;
+
+private:
+ CSkinVariableString();
+
+ std::string m_name;
+ int m_context;
+
+ struct ConditionLabelPair
+ {
+ INFO::InfoPtr m_condition;
+ KODI::GUILIB::GUIINFO::CGUIInfoLabel m_label;
+ };
+
+ typedef std::vector<ConditionLabelPair> VECCONDITIONLABELPAIR;
+ VECCONDITIONLABELPAIR m_conditionLabelPairs;
+
+ friend class CSkinVariable;
+};
+
+}