Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RStyle.cxx
Go to the documentation of this file.
1/*************************************************************************
2 * Copyright (C) 1995-2017, Rene Brun and Fons Rademakers. *
3 * All rights reserved. *
4 * *
5 * For the licensing terms see $ROOTSYS/LICENSE. *
6 * For the list of contributors see $ROOTSYS/README/CREDITS. *
7 *************************************************************************/
8
9#include <ROOT/RStyle.hxx>
10
11#include "ROOT/RAttrBase.hxx" // for GPadLog()
12#include <ROOT/RDrawable.hxx>
13#include <ROOT/RLogger.hxx>
14
15using namespace std::string_literals;
16
17///////////////////////////////////////////////////////////////////////////////
18/// Evaluate attribute value for provided RDrawable
19
20const ROOT::Experimental::RAttrMap::Value_t *ROOT::Experimental::RStyle::Eval(const std::string &field, const RDrawable &drawable) const
21{
22 for (const auto &block : fBlocks) {
23 if (drawable.MatchSelector(block.selector)) {
24 auto res = block.map.Find(field);
25 if (res)
26 return res;
27 }
28 }
29
30 return nullptr;
31}
32
33///////////////////////////////////////////////////////////////////////////////
34/// Evaluate attribute value for provided selector - exact match is expected
35
36const ROOT::Experimental::RAttrMap::Value_t *ROOT::Experimental::RStyle::Eval(const std::string &field, const std::string &selector) const
37{
38 for (const auto &block : fBlocks) {
39 if (block.selector == selector) {
40 auto res = block.map.Find(field);
41 if (res)
42 return res;
43 }
44 }
45
46 return nullptr;
47}
48
49
50///////////////////////////////////////////////////////////////////////////////
51/// Parse string with CSS code inside
52
54{
55 fBlocks.clear();
56}
57
58///////////////////////////////////////////////////////////////////////////////
59/// Parse string with CSS code inside
60/// All data will be append to existing style records
61
62bool ROOT::Experimental::RStyle::ParseString(const std::string &css_code)
63{
64 if (css_code.empty())
65 return true;
66
67 struct RParser {
68 int pos{0};
69 int nline{1};
70 int linebeg{0};
71 int len{0};
72 const std::string &css_code;
73
74 RParser(const std::string &_code) : css_code(_code)
75 {
76 len = css_code.length();
77 }
78
79 bool more_data() const { return pos < len; }
80
81 char current() const { return css_code[pos]; }
82
83 void shift() { ++pos; }
84
85 bool check_symbol(bool isfirst = false)
86 {
87 auto symbol = current();
88 if (((symbol >= 'a') && (symbol <= 'z')) ||
89 ((symbol >= 'A') && (symbol <= 'Z')) || (symbol == '_')) return true;
90 return (!isfirst && (symbol>='0') && (symbol<='9'));
91 }
92
93 std::string error_position() const
94 {
95 std::string res = "\nLine "s + std::to_string(nline) + ": "s;
96
97 int p = linebeg;
98 while ((p<len) && (p < linebeg+100) && (css_code[p] != '\n')) ++p;
99
100 return res + css_code.substr(linebeg, p-linebeg);
101 }
102
103 bool skip_empty()
104 {
105 bool skip_until_newline = false, skip_until_endblock = false;
106
107 while (pos < len) {
108 if (current() == '\n') {
109 skip_until_newline = false;
110 linebeg = ++pos;
111 ++nline;
112 continue;
113 }
114
115 if (skip_until_endblock && (current() == '*') && (pos+1 < len) && (css_code[pos+1] == '/')) {
116 pos+=2;
117 skip_until_endblock = false;
118 continue;
119 }
120
121 if (skip_until_newline || skip_until_endblock || (current() == ' ') || (current() == '\t')) {
122 shift();
123 continue;
124 }
125
126 if ((current() == '/') && (pos+1 < len)) {
127 if (css_code[pos+1] == '/') {
128 pos+=2;
129 skip_until_newline = true;
130 continue;
131 } else if (css_code[pos+1] == '*') {
132 pos+=2;
133 skip_until_endblock = true;
134 continue;
135 }
136 }
137
138 return true;
139 }
140
141 return false;
142 }
143
144 std::string scan_identifier(bool selector = false)
145 {
146 if (pos >= len) return ""s;
147
148 int pos0 = pos;
149
150 // start symbols of selector
151 if (selector && ((current() == '.') || (current() == '#'))) shift();
152
153 bool is_first = true;
154
155 while ((pos < len) && check_symbol(is_first)) { shift(); is_first = false; }
156
157 return css_code.substr(pos0, pos-pos0);
158 }
159
160 std::string scan_value()
161 {
162 if (pos >= len) return ""s;
163
164 int pos0 = pos;
165
166 while ((pos < len) && (current() != ';') && current() != '\n') shift();
167
168 if (pos >= len)
169 return ""s;
170
171 shift();
172
173 return css_code.substr(pos0, pos - pos0 - 1);
174 }
175
176 };
177
178 RParser parser(css_code);
179
180 RStyle newstyle;
181
182 while (parser.more_data()) {
183
184 if (!parser.skip_empty())
185 return false;
186
187 auto sel = parser.scan_identifier(true);
188 if (sel.empty()) {
189 R__LOG_ERROR(GPadLog()) << "Fail to find selector" << parser.error_position();
190 return false;
191 }
192
193 if (!parser.skip_empty())
194 return false;
195
196 if (parser.current() != '{') {
197 R__LOG_ERROR(GPadLog()) << "Fail to find starting {" << parser.error_position();
198 return false;
199 }
200
201 parser.shift();
202
203 if (!parser.skip_empty())
204 return false;
205
206 auto &map = newstyle.AddBlock(sel);
207
208 while (parser.current() != '}') {
209 auto name = parser.scan_identifier();
210 if (name.empty()) {
211 R__LOG_ERROR(GPadLog()) << "not able to extract identifier" << parser.error_position();
212 return false;
213 }
214
215 if (!parser.skip_empty())
216 return false;
217
218 if (parser.current() != ':') {
219 R__LOG_ERROR(GPadLog()) << "not able to find separator :" << parser.error_position();
220 return false;
221 }
222
223 parser.shift();
224
225 if (!parser.skip_empty())
226 return false;
227
228 if (parser.current() == ';') {
229 parser.shift();
230 map.AddNoValue(name);
231 } else {
232 auto value = parser.scan_value();
233 if (value.empty()) {
234 R__LOG_ERROR(GPadLog()) << "not able to find value" << parser.error_position();
235 return false;
236 }
237
238 map.AddBestMatch(name, value);
239 }
240
241 if (!parser.skip_empty())
242 return false;
243 }
244
245 parser.shift();
246
247 parser.skip_empty(); // after closing } end of file is possible
248 }
249
250 // finally move all read blocks to this
251 fBlocks.splice(fBlocks.end(), newstyle.fBlocks);
252
253 return true;
254}
255
256
257///////////////////////////////////////////////////////////////////////////////
258/// Parse CSS code and returns std::shared_ptr<RStyle> when successful
259
260std::shared_ptr<ROOT::Experimental::RStyle> ROOT::Experimental::RStyle::Parse(const std::string &css_code)
261{
262 auto style = std::make_shared<RStyle>();
263 if (!style->ParseString(css_code)) return nullptr;
264 return style;
265}
#define R__LOG_ERROR(...)
Definition RLogger.hxx:362
char name[80]
Definition TGX11.cxx:110
Base class for drawable entities: objects that can be painted on a RPad.
bool MatchSelector(const std::string &selector) const
Preliminary method which checks if drawable matches with given selector Following selector are allowe...
Definition RDrawable.cxx:59
A set of defaults for graphics attributes, e.g.
Definition RStyle.hxx:32
std::list< Block_t > fBlocks
Definition RStyle.hxx:64
const RAttrMap::Value_t * Eval(const std::string &field, const RDrawable &drawable) const
Evaluate attribute value for provided RDrawable.
Definition RStyle.cxx:20
void Clear()
Parse string with CSS code inside.
Definition RStyle.cxx:53
bool ParseString(const std::string &css_code)
Parse string with CSS code inside All data will be append to existing style records.
Definition RStyle.cxx:62
RAttrMap & AddBlock(const std::string &selector)
Definition RStyle.hxx:50
static std::shared_ptr< RStyle > Parse(const std::string &css_code)
Parse CSS code and returns std::shared_ptr<RStyle> when successful.
Definition RStyle.cxx:260
RLogChannel & GPadLog()
Log channel for GPad diagnostics.
Definition RAttrBase.cxx:19
TCanvas * style()
Definition style.C:1