File indexing completed on 2024-11-24 04:15:41
0001 /* 0002 SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "mapcsselementstate.h" 0008 #include "mapcssselector_p.h" 0009 #include "mapcsscondition_p.h" 0010 #include "mapcssresult.h" 0011 #include "mapcssstate_p.h" 0012 0013 #include <QDebug> 0014 #include <QIODevice> 0015 0016 #include <cmath> 0017 #include <cstring> 0018 0019 using namespace KOSMIndoorMap; 0020 0021 MapCSSSelector::MapCSSSelector() = default; 0022 MapCSSSelector::~MapCSSSelector() = default; 0023 0024 MapCSSBasicSelector::MapCSSBasicSelector() = default; 0025 MapCSSBasicSelector::~MapCSSBasicSelector() = default; 0026 0027 void MapCSSBasicSelector::compile(const OSM::DataSet &dataSet) 0028 { 0029 for (const auto &c : conditions) { 0030 c->compile(dataSet); 0031 } 0032 } 0033 0034 bool MapCSSBasicSelector::matches(const MapCSSState &state, MapCSSResult &result, const std::vector<std::unique_ptr<MapCSSDeclaration>> &declarations) const 0035 { 0036 // check object type 0037 switch (m_objectType) { 0038 case MapCSSObjectType::Node: if (state.element.type() != OSM::Type::Node) return false; break; 0039 case MapCSSObjectType::Way: if (state.element.type() != OSM::Type::Way) return false; break; 0040 case MapCSSObjectType::Relation: if (state.element.type() != OSM::Type::Relation) return false; break; 0041 case MapCSSObjectType::Area: if (state.objectType !=MapCSSObjectType::Area && state.objectType != MapCSSObjectType::LineOrArea) return false; break; 0042 case MapCSSObjectType::Line: if (state.objectType !=MapCSSObjectType::Line && state.objectType != MapCSSObjectType::LineOrArea) return false; break; 0043 case MapCSSObjectType::Canvas: return false; 0044 case MapCSSObjectType::Any: break; 0045 case MapCSSObjectType::LineOrArea: Q_UNREACHABLE(); 0046 } 0047 0048 // check zoom level 0049 if (m_zoomLow > 0 && state.zoomLevel < m_zoomLow) { 0050 return false; 0051 } 0052 if (m_zoomHigh > 0 && state.zoomLevel >= m_zoomHigh) { 0053 return false; 0054 } 0055 0056 if (!m_class.isNull() && !result[m_layer].hasClass(m_class)) { 0057 return false; 0058 } 0059 0060 if (m_elementState && (state.state & m_elementState) == 0) { 0061 return false; 0062 } 0063 0064 if (std::all_of(conditions.begin(), conditions.end(), [&state](const auto &cond) { return cond->matches(state); })) { 0065 result.applyDeclarations(m_layer, declarations); 0066 return true; 0067 } 0068 return false; 0069 } 0070 0071 bool MapCSSBasicSelector::matchesCanvas(const MapCSSState &state) const 0072 { 0073 if (m_objectType != MapCSSObjectType::Canvas) { 0074 return false; 0075 } 0076 0077 if (m_zoomLow > 0 && state.zoomLevel < m_zoomLow) { 0078 return false; 0079 } 0080 if (m_zoomHigh > 0 && state.zoomLevel >= m_zoomHigh) { 0081 return false; 0082 } 0083 0084 return std::all_of(conditions.begin(), conditions.end(), [&state](const auto &cond) { return cond->matchesCanvas(state); }); 0085 } 0086 0087 LayerSelectorKey MapCSSBasicSelector::layerSelector() const 0088 { 0089 return m_layer; 0090 } 0091 0092 struct { 0093 const char *name; 0094 MapCSSObjectType type; 0095 } static constexpr const object_type_map[] = { 0096 { "node", MapCSSObjectType::Node }, 0097 { "way", MapCSSObjectType::Way }, 0098 { "relation", MapCSSObjectType::Relation }, 0099 { "area", MapCSSObjectType::Area }, 0100 { "line", MapCSSObjectType::Line }, 0101 { "canvas", MapCSSObjectType::Canvas }, 0102 { "*", MapCSSObjectType::Any }, 0103 }; 0104 0105 void MapCSSBasicSelector::write(QIODevice *out) const 0106 { 0107 for (const auto &t : object_type_map) { 0108 if (m_objectType == t.type) { 0109 out->write(t.name); 0110 break; 0111 } 0112 } 0113 0114 if (!m_class.isNull()) { 0115 out->write("."); 0116 out->write(m_class.name()); 0117 } 0118 0119 if (m_zoomLow > 0 || m_zoomHigh > 0) { 0120 out->write("|z"); 0121 if (m_zoomLow == m_zoomHigh) { 0122 out->write(QByteArray::number(m_zoomLow)); 0123 } else { 0124 if (m_zoomLow > 0) { 0125 out->write(QByteArray::number(m_zoomLow)); 0126 } 0127 out->write("-"); 0128 if (m_zoomHigh > 0) { 0129 out->write(QByteArray::number(m_zoomHigh)); 0130 } 0131 } 0132 } 0133 0134 for (const auto &cond : conditions) { 0135 cond->write(out); 0136 } 0137 0138 if (m_elementState & MapCSSElementState::Active) { 0139 out->write(":active"); 0140 } 0141 if (m_elementState & MapCSSElementState::Hovered) { 0142 out->write(":hovered"); 0143 } 0144 0145 if (!m_layer.isNull()) { 0146 out->write("::"); 0147 out->write(m_layer.name()); 0148 } 0149 } 0150 0151 void MapCSSBasicSelector::setObjectType(const char *str, std::size_t len) 0152 { 0153 for (const auto &t : object_type_map) { 0154 if (std::strncmp(t.name, str, std::max(std::strlen(t.name), len)) == 0) { 0155 m_objectType = t.type; 0156 return; 0157 } 0158 } 0159 } 0160 0161 void MapCSSBasicSelector::setZoomRange(int low, int high) 0162 { 0163 m_zoomLow = low; 0164 m_zoomHigh = high; 0165 } 0166 0167 void MapCSSBasicSelector::setConditions(MapCSSConditionHolder *conds) 0168 { 0169 if (!conds) { 0170 return; 0171 } 0172 conditions = std::move(conds->conditions); 0173 delete conds; 0174 } 0175 0176 void MapCSSBasicSelector::setClass(ClassSelectorKey key) 0177 { 0178 m_class = key; 0179 } 0180 0181 void MapCSSBasicSelector::setPseudoClass(const char *str, std::size_t len) 0182 { 0183 if (std::strncmp(str, "active", len) == 0) { 0184 m_elementState |= MapCSSElementState::Active; 0185 } else if (std::strncmp(str, "hovered", len) == 0) { 0186 m_elementState |= MapCSSElementState::Hovered; 0187 } else { 0188 qWarning() << "Unhandled pseudo class:" << QByteArrayView(str, (qsizetype)len); 0189 } 0190 } 0191 0192 void MapCSSBasicSelector::setLayer(LayerSelectorKey key) 0193 { 0194 m_layer = key; 0195 } 0196 0197 0198 void MapCSSChainedSelector::compile(const OSM::DataSet &dataSet) 0199 { 0200 for (const auto &s : selectors) { 0201 s->compile(dataSet); 0202 } 0203 } 0204 0205 bool MapCSSChainedSelector::matches([[maybe_unused]] const MapCSSState &state, [[maybe_unused]] MapCSSResult &result, 0206 [[maybe_unused]] const std::vector<std::unique_ptr<MapCSSDeclaration>> &declarations) const 0207 { 0208 // TODO 0209 return false; 0210 } 0211 0212 bool MapCSSChainedSelector::matchesCanvas(const MapCSSState &state) const 0213 { 0214 Q_UNUSED(state); 0215 // canvas cannot be chained 0216 return false; 0217 } 0218 0219 LayerSelectorKey MapCSSChainedSelector::layerSelector() const 0220 { 0221 return selectors.back()->layerSelector(); 0222 } 0223 0224 void MapCSSChainedSelector::write(QIODevice *out) const 0225 { 0226 assert(selectors.size() > 1); 0227 selectors[0]->write(out); 0228 for (auto it = std::next(selectors.begin()); it != selectors.end(); ++it) { 0229 out->write(" "); 0230 (*it)->write(out); 0231 } 0232 } 0233 0234 0235 MapCSSUnionSelector::MapCSSUnionSelector() = default; 0236 MapCSSUnionSelector::~MapCSSUnionSelector() = default; 0237 0238 void MapCSSUnionSelector::compile(const OSM::DataSet &dataSet) 0239 { 0240 for (const auto &ls : m_selectors) { 0241 for (const auto &s : ls.selectors) { 0242 s->compile(dataSet); 0243 } 0244 } 0245 } 0246 0247 bool MapCSSUnionSelector::matches(const MapCSSState &state, MapCSSResult &result, const std::vector<std::unique_ptr<MapCSSDeclaration>> &declarations) const 0248 { 0249 bool ret = false; 0250 for (const auto &ls : m_selectors) { 0251 if (std::any_of(ls.selectors.begin(), ls.selectors.end(), [&state, &result, &declarations](const auto &selector) { 0252 return selector->matches(state, result, declarations); 0253 0254 })) { 0255 // no short-circuit evaluation, we want the matchCallback to be called once per matching layer! 0256 ret = true; 0257 } 0258 } 0259 return ret; 0260 } 0261 0262 bool MapCSSUnionSelector::matchesCanvas(const MapCSSState &state) const 0263 { 0264 for (const auto &ls : m_selectors) { 0265 if (ls.layer.isNull()) { 0266 return std::any_of(ls.selectors.begin(), ls.selectors.end(), [&state](const auto &selector) { return selector->matchesCanvas(state); }); 0267 } 0268 } 0269 return false; 0270 } 0271 0272 LayerSelectorKey MapCSSUnionSelector::layerSelector() const 0273 { 0274 return {}; 0275 } 0276 0277 void MapCSSUnionSelector::write(QIODevice *out) const 0278 { 0279 for (std::size_t i = 0; i < m_selectors.size(); ++i) { 0280 for (std::size_t j = 0; j < m_selectors[i].selectors.size(); ++j) { 0281 if (i != 0 || j != 0) { 0282 out->write(",\n"); 0283 } 0284 m_selectors[i].selectors[j]->write(out); 0285 } 0286 } 0287 } 0288 0289 void MapCSSUnionSelector::addSelector(std::unique_ptr<MapCSSSelector> &&selector) 0290 { 0291 auto it = std::find_if(m_selectors.begin(), m_selectors.end(), [&selector](const auto &ls) { 0292 return ls.layer == selector->layerSelector(); 0293 }); 0294 if (it != m_selectors.end()) { 0295 (*it).selectors.push_back(std::move(selector)); 0296 } else { 0297 SelectorMap ls; 0298 ls.layer = selector->layerSelector(); 0299 ls.selectors.push_back(std::move(selector)); 0300 m_selectors.push_back(std::move(ls)); 0301 } 0302 }