1 // This may look like C code, but it's really -*- C++ -*-
2 /*
3  * Copyright (C) 2008 Emweb bv, Herent, Belgium.
4  *
5  * See the LICENSE file for terms of use.
6  */
7 #ifndef WCSS_STYLE_SHEET_H_
8 #define WCSS_STYLE_SHEET_H_
9 
10 #include <vector>
11 #include <set>
12 #include <string>
13 
14 #include <Wt/WBreak.h>
15 #include <Wt/WCssDecorationStyle.h>
16 
17 namespace Wt {
18 
19 class WApplication;
20 class WCssStyleSheet;
21 class WCssTemplateWidget;
22 
23 /*! \class WCssRule Wt/WCssStyleSheet.h Wt/WCssStyleSheet.h
24  *  \brief Abstract rule in a CSS style sheet.
25  *
26  * A rule presents CSS style properties that are applied to a selected
27  * set of elements.
28  *
29  * Use WCssTemplateRule if you would like to use a widget as a
30  * template for specifying (<i>and</i> updating) a style rule, using
31  * the widgets style properties, or WCssTextRule if you wish to
32  * directly specify the CSS declarations.
33  *
34  * \sa WCssStyleSheet
35  *
36  * \ingroup style
37  */
38 class WT_API WCssRule : public WObject
39 {
40 public:
41   /*! \brief Destructor.
42    */
43   virtual ~WCssRule();
44 
45   /*! \brief Sets the selector.
46    *
47    * \note The selector can only be changed as long as the rule hasn't
48    *       been rendered.
49    */
setSelector(const std::string & selector)50   void setSelector(const std::string& selector) { selector_ = selector; }
51 
52   /*! \brief Returns the selector.
53    */
selector()54   virtual std::string selector() const { return selector_; }
55 
56   /*! \brief Returns the style sheet to which this rule belongs.
57    */
sheet()58   WCssStyleSheet *sheet() const { return sheet_; }
59 
60   /*! \brief Indicates that the rule has changed and needs updating
61    */
62   void modified();
63 
64   /*! \brief Returns the declarations.
65    *
66    * This is a semi-colon separated list of CSS declarations.
67    */
68   virtual std::string declarations() = 0;
69 
70   virtual bool updateDomElement(DomElement& cssRuleElement, bool all);
71 
72 protected:
73   /*! \brief Creates a new CSS rule with given selector.
74    */
75   WCssRule(const std::string& selector);
76 
77 private:
78   std::string selector_;
79   WCssStyleSheet *sheet_;
80 
81   friend class WCssStyleSheet;
82 };
83 
84 /*! \class WCssTemplateRule Wt/WCssStyleSheet.h Wt/WCssStyleSheet.h
85  *  \brief A CSS rule based on a template widget.
86  *
87  * This is a CSS rule whose CSS style properties are defined based on
88  * properties of a template widget. When modifying the template
89  * widget, these changes are reflected on the CSS rule and thus all
90  * widgets that have this CSS rule.
91  *
92  * \if cpp
93  * Usage example:
94  * \code
95  * Wt::WCssTemplateRule *styleRule =
96  *   Wt::WApplication::instance()->styleSheet().addRule(
97  *     std::make_unique<Wt::WCssTemplateRule>("#" + id() + " .item"));
98  *
99  * styleRule->templateWidget()->resize(100, WLength::Auto);
100  * styleRule->templateWidget()->decorationStyle().setCursor(Cursor::PointingHand);
101  * \endcode
102  * \endif
103  *
104  * \sa Wt::WCssStyleSheet
105  *
106  * \ingroup style
107  */
108 class WT_API WCssTemplateRule : public WCssRule
109 {
110 public:
111   /*! \brief Creates a CSS rule with a given selector.
112    *
113    * The selector should be a valid CSS selector.
114    *
115    * \note If you want to update the rule, then the selector should be
116    * unique and not contain commas, since this is not supported by
117    * Microsoft Internet Explorer.
118    */
119   WCssTemplateRule(const std::string& selector);
120 
121   ~WCssTemplateRule();
122 
123   /*! \brief Returns the widget that is used as a template.
124    *
125    * Various properties of the widget are reflected in the CSS style:
126    * - size and dimensions: WWidget::resize(), WWidget::setMinimumSize(),
127    * and WWidget::setMaximumSize()
128    * - its position: WWidget::setPositionScheme(), WWidget::setOffsets(),
129    * WWidget::setFloatSide(), WWidget::setClearSides()
130    * - visibility: WWidget::hide(), WWidget::show() and WWidget::setHidden()
131    * - margins: WWidget::setMargin()
132    * - line height: WWidget::setLineHeight()
133    * - all decoration style properties: WWidget::decorationStyle()
134    *
135    * When modifying one of these properties of the returned widget, the
136    * rule will be updated accordingly.
137    */
138   WWidget *templateWidget();
139 
140   virtual std::string declarations() override;
141 
142   virtual bool updateDomElement(DomElement& cssRuleElement, bool all) override;
143 
144 private:
145   std::unique_ptr<WCssTemplateWidget> widget_;
146 };
147 
148 /*! \class WCssTextRule Wt/WCssStyleSheet.h Wt/WCssStyleSheet.h
149  *  \brief A CSS rule specified directly using CSS declarations
150  *
151  * \if cpp
152  * Usage example:
153  * \code
154  * auto styleRule = std::make_unique<Wt::WCssTextRule>(".MyWidget .item", "width: 100px; cursor: pointer;");
155  * Wt::WApplication::instance()->styleSheet().addRule(std::move(styleRule));
156  * \endcode
157  * \endif
158  *
159  * \sa WCssStyleSheet
160  *
161  * \ingroup style
162  */
163 class WT_API WCssTextRule : public WCssRule
164 {
165 public:
166   /*! \brief Creates a CSS rule with a given selector and declarations.
167    */
168   WCssTextRule(const std::string& selector,
169 	       const WT_USTRING& declarations);
170 
171   virtual std::string declarations() override;
172 
173 private:
174   WT_USTRING declarations_;
175 };
176 
177 /*! \class WCssStyleSheet Wt/WCssStyleSheet.h Wt/WCssStyleSheet.h
178  *  \brief A CSS style sheet.
179  *
180  * \if cpp
181  * Usage example:
182  * \code
183  * Wt::WApplication::instance()->styleSheet().addRule(".MyWidget .item", "width: 100px; cursor: pointer;");
184  * \endcode
185  * \endif
186  *
187  * \sa WApplication::styleSheet()
188  *
189  * \ingroup style
190  */
191 class WT_API WCssStyleSheet
192 {
193 public:
194   /*! \brief Creates a new (internal) style sheet.
195    */
196   WCssStyleSheet();
197 
198   WCssStyleSheet(const WCssStyleSheet &) = delete;
199   WCssStyleSheet& operator=(const WCssStyleSheet &) = delete;
200 
201   /*! \brief Destroys a style sheet, and all rules in it.
202    */
203   ~WCssStyleSheet();
204 
205 
206   /*! \brief Adds a CSS rule.
207    *
208    * Add a rule using the CSS selector \p selector, with CSS
209    * declarations in \p declarations. These declarations must be a
210    * list separated by semi-colons (;).
211    *
212    * Optionally, you may give a \p ruleName, which may later be
213    * used to check if the rule was already defined.
214    *
215    * \sa isDefined()
216    */
217   WCssTextRule *addRule(const std::string& selector,
218 			const WT_USTRING& declarations,
219 			const std::string& ruleName = std::string());
220 
221 #ifndef WT_TARGET_JAVA
222   /* Interprets as UTF-8 */
223   WCssTextRule *addRule(const std::string& selector,
224 			const std::string& declarations,
225 			const std::string& ruleName = std::string());
226 
227   /* Interprets as UTF-8 */
228   WCssTextRule *addRule(const std::string& selector,
229 			const char *declarations,
230 			const std::string& ruleName = std::string());
231 #endif
232 
233   /*! \brief Adds a CSS rule.
234    *
235    * Add a rule using the CSS selector \p selector, with styles specified
236    * in \p style.
237    *
238    * Optionally, you may give a \p ruleName, which may later be
239    * used to check if the rule was already defined.
240    *
241    * \sa isDefined()
242    */
243   WCssTemplateRule *addRule(const std::string& selector,
244 			    const WCssDecorationStyle& style,
245 			    const std::string& ruleName = std::string());
246 
247   /*! \brief Adds a CSS rule.
248    *
249    * Optionally, you may give a \p ruleName, which may later be
250    * used to check if the rule was already defined.
251    * Note: you may not pass the same rule to 2 diffrent applications.
252    *
253    * \sa isDefined()
254    */
255   WCssRule *addRule(std::unique_ptr<WCssRule> rule,
256 		    const std::string& ruleName = std::string());
257 
258   template <typename Rule>
addRule(std::unique_ptr<Rule> rule)259   Rule *addRule(std::unique_ptr<Rule> rule)
260 #ifndef WT_TARGET_JAVA
261   {
262     Rule *result = rule.get();
263     addRule(std::unique_ptr<WCssRule>(std::move(rule)));
264     return result;
265   }
266 #else // WT_TARGET_JAVA
267   ;
268 #endif // WT_TARGET_JAVA
269 
270   /*! \brief Returns if a rule was already defined in this style sheet.
271    *
272    * Returns whether a rule was added with the given \p ruleName.
273    *
274    * \sa addRule()
275    */
276   bool isDefined(const std::string& ruleName) const;
277 
278   /*! \brief Removes a rule.
279    */
280   std::unique_ptr<WCssRule> removeRule(WCssRule *rule);
281 
282   void ruleModified(WCssRule *rule);
283 
284   void cssText(WStringStream& out, bool all);
285 
286   void javaScriptUpdate(WApplication *app, WStringStream& js, bool all);
287 
288 private:
289   typedef std::vector<std::unique_ptr<WCssRule> > RuleList;
290   typedef std::set<WCssRule *> RuleSet;
291 
292   RuleList rules_;
293   std::vector<WCssRule *> rulesAdded_;
294 
295   RuleSet rulesModified_;
296   std::vector<std::string> rulesRemoved_;
297 
298   std::set<std::string> defined_;
299 
300   bool isDirty();
301 
302   friend class WebRenderer;
303 };
304 
305 }
306 
307 #endif // WCSS_STYLE_SHEET_H_
308