|
1 """html widgets |
|
2 |
|
3 those are in cubicweb.common since we need to know available widgets at schema |
|
4 serialization time |
|
5 |
|
6 :organization: Logilab |
|
7 :copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved. |
|
8 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr |
|
9 """ |
|
10 |
|
11 from logilab.mtconverter import html_escape |
|
12 |
|
13 from cubicweb.common.utils import UStringIO |
|
14 from cubicweb.common.uilib import toggle_action |
|
15 |
|
16 class HTMLWidget(object): |
|
17 |
|
18 def _initialize_stream(self, w=None): |
|
19 if w: |
|
20 self.w = w |
|
21 else: |
|
22 self._stream = UStringIO() |
|
23 self.w = self._stream.write |
|
24 |
|
25 def _render(self): |
|
26 raise NotImplementedError |
|
27 |
|
28 def render(self, w=None): |
|
29 self._initialize_stream(w) |
|
30 self._render() |
|
31 if w is None: |
|
32 return self._stream.getvalue() |
|
33 |
|
34 def is_empty(self): |
|
35 return False |
|
36 |
|
37 |
|
38 class BoxWidget(HTMLWidget): |
|
39 def __init__(self, title, id, items=None, _class="boxFrame", |
|
40 islist=True, shadow=True, escape=True): |
|
41 self.title = title |
|
42 self.id = id |
|
43 self.items = items or [] |
|
44 self._class = _class |
|
45 self.islist = islist |
|
46 self.shadow = shadow |
|
47 self.escape = escape |
|
48 |
|
49 def __len__(self): |
|
50 return len(self.items) |
|
51 |
|
52 def is_empty(self): |
|
53 return len(self) == 0 |
|
54 |
|
55 def append(self, item): |
|
56 self.items.append(item) |
|
57 |
|
58 main_div_class = 'boxContent' |
|
59 listing_class = 'boxListing' |
|
60 |
|
61 def box_begin_content(self): |
|
62 self.w(u'<div class="%s">\n' % self.main_div_class) |
|
63 if self.islist: |
|
64 self.w(u'<ul class="%s">' % self.listing_class) |
|
65 |
|
66 def box_end_content(self): |
|
67 if self.islist: |
|
68 self.w(u'</ul>\n') |
|
69 self.w(u'</div>\n') |
|
70 if self.shadow: |
|
71 self.w(u'<div class="shadow"> </div>') |
|
72 |
|
73 def _render(self): |
|
74 if self.id: |
|
75 self.w(u'<div class="%s" id="%s">' % (self._class, self.id)) |
|
76 else: |
|
77 self.w(u'<div class="%s">' % self._class) |
|
78 if self.title: |
|
79 if self.escape: |
|
80 title = '<span>%s</span>' % html_escape(self.title) |
|
81 else: |
|
82 title = '<span>%s</span>' % self.title |
|
83 self.w(u'<div class="boxTitle">%s</div>' % title) |
|
84 if self.items: |
|
85 self.box_begin_content() |
|
86 for item in self.items: |
|
87 item.render(self.w) |
|
88 self.box_end_content() |
|
89 self.w(u'</div>') |
|
90 |
|
91 |
|
92 class SideBoxWidget(BoxWidget): |
|
93 """default CubicWeb's sidebox widget""" |
|
94 main_div_class = u'sideBoxBody' |
|
95 listing_class = '' |
|
96 |
|
97 def __init__(self, title, id=None): |
|
98 super(SideBoxWidget, self).__init__(title, id=id, _class='sideBox', |
|
99 shadow=False) |
|
100 |
|
101 |
|
102 class MenuWidget(BoxWidget): |
|
103 main_div_class = 'menuContent' |
|
104 listing_class = 'menuListing' |
|
105 |
|
106 def box_end_content(self): |
|
107 if self.islist: |
|
108 self.w(u'</ul>\n') |
|
109 self.w(u'</div>\n') |
|
110 |
|
111 |
|
112 class RawBoxItem(HTMLWidget): |
|
113 """a simpe box item displaying raw data""" |
|
114 def __init__(self, label, liclass=None): |
|
115 self.label = label |
|
116 self.liclass = liclass |
|
117 |
|
118 def _start_li(self): |
|
119 if self.liclass is None: |
|
120 return u'<li>' |
|
121 else: |
|
122 return u'<li class="%s">' % self.liclass |
|
123 |
|
124 return self.label |
|
125 |
|
126 def _render(self): |
|
127 self.w(u'%s%s</li>' % (self._start_li(), self.label)) |
|
128 |
|
129 |
|
130 class BoxMenu(RawBoxItem): |
|
131 """a menu in a box""" |
|
132 link_class = 'boxMenu' |
|
133 |
|
134 def __init__(self, label, items=None, isitem=True, liclass=None, ident=None, |
|
135 link_class=None): |
|
136 super(BoxMenu, self).__init__(label, liclass) |
|
137 self.items = items or [] |
|
138 self.isitem = isitem |
|
139 self.ident = ident or u'boxmenu_%s' % label.replace(' ', '_').replace("'", '') |
|
140 if link_class: |
|
141 self.link_class = link_class |
|
142 |
|
143 def append(self, item): |
|
144 self.items.append(item) |
|
145 |
|
146 def _begin_menu(self, ident): |
|
147 self.w(u'<ul id="%s" class="hidden">' % ident) |
|
148 |
|
149 def _end_menu(self): |
|
150 self.w(u'</ul>') |
|
151 |
|
152 def _render(self): |
|
153 if self.isitem: |
|
154 self.w(self._start_li()) |
|
155 ident = self.ident |
|
156 self.w(u'<a href="%s" class="%s">%s</a>' % ( |
|
157 toggle_action(ident), self.link_class, self.label)) |
|
158 self._begin_menu(ident) |
|
159 for item in self.items: |
|
160 item.render(self.w) |
|
161 self._end_menu() |
|
162 if self.isitem: |
|
163 self.w(u'</li>') |
|
164 |
|
165 |
|
166 class PopupBoxMenu(BoxMenu): |
|
167 """like BoxMenu but uses div and specific css class |
|
168 in order to behave like a popup menu |
|
169 """ |
|
170 link_class = 'popupMenu' |
|
171 |
|
172 def _begin_menu(self, ident): |
|
173 self.w(u'<div class="popupWrapper"><div id="%s" class="hidden popup"><ul>' % ident) |
|
174 |
|
175 def _end_menu(self): |
|
176 self.w(u'</ul></div></div>') |
|
177 |
|
178 |
|
179 class BoxField(HTMLWidget): |
|
180 """couples label / value meant to be displayed in a box""" |
|
181 def __init__(self, label, value): |
|
182 self.label = label |
|
183 self.value = value |
|
184 |
|
185 def _render(self): |
|
186 self.w(u'<li><div><span class="label">%s</span> ' |
|
187 u'<span class="value">%s</span></div></li>' |
|
188 % (self.label, self.value)) |
|
189 |
|
190 class BoxSeparator(HTMLWidget): |
|
191 """a menu separator""" |
|
192 |
|
193 def _render(self): |
|
194 self.w(u'</ul><hr class="boxSeparator"/><ul>') |
|
195 |
|
196 |
|
197 class BoxLink(HTMLWidget): |
|
198 """a link in a box""" |
|
199 def __init__(self, href, label, _class='', title='', ident='', escape=False): |
|
200 self.href = href |
|
201 if escape: |
|
202 self.label = html_escape(label) |
|
203 else: |
|
204 self.label = label |
|
205 self._class = _class or '' |
|
206 self.title = title |
|
207 self.ident = ident |
|
208 |
|
209 def _render(self): |
|
210 link = u'<a href="%s" title="%s">%s</a>' % ( |
|
211 html_escape(self.href), html_escape(self.title), self.label) |
|
212 if self.ident: |
|
213 self.w(u'<li id="%s" class="%s">%s</li>\n' % (self.ident, self._class, link)) |
|
214 else: |
|
215 self.w(u'<li class="%s">%s</li>\n' % (self._class, link)) |
|
216 |
|
217 |
|
218 class BoxHtml(HTMLWidget): |
|
219 """a form in a box""" |
|
220 def __init__(self, rawhtml): |
|
221 self.rawhtml = rawhtml |
|
222 |
|
223 def _render(self): |
|
224 self.w(self.rawhtml) |
|
225 |
|
226 |
|
227 class TableColumn(object): |
|
228 def __init__(self, name, rset_sortcol): |
|
229 """ |
|
230 :param name: the column's name |
|
231 :param rset_sortcol: the model's column used to sort this column view |
|
232 """ |
|
233 self.name = name |
|
234 self.cellrenderers = [] |
|
235 self.rset_sortcol = rset_sortcol |
|
236 self.cell_attrs = {} |
|
237 |
|
238 def append_renderer(self, cellvid, colindex): |
|
239 # XXX (adim) : why do we need colindex here ? |
|
240 self.cellrenderers.append( (cellvid, colindex) ) |
|
241 |
|
242 def add_attr(self, attr, value): |
|
243 self.cell_attrs[attr] = value |
|
244 |
|
245 |
|
246 class TableWidget(HTMLWidget): |
|
247 |
|
248 highlight = "onmouseover=\"addElementClass(this, 'highlighted');\" " \ |
|
249 "onmouseout=\"removeElementClass(this, 'highlighted');\"" |
|
250 |
|
251 def __init__(self, model): |
|
252 self.model = model |
|
253 self.columns = [] |
|
254 |
|
255 def append_column(self, column): |
|
256 """ |
|
257 :type column: TableColumn |
|
258 """ |
|
259 self.columns.append(column) |
|
260 |
|
261 def _render(self): |
|
262 self.w(u'<table class="listing">') |
|
263 self.w(u'<thead>') |
|
264 self.w(u'<tr class="header">') |
|
265 for column in self.columns: |
|
266 attrs = ('%s="%s"' % (name, value) for name, value in column.cell_attrs.iteritems()) |
|
267 self.w(u'<th %s>%s</th>' % (' '.join(attrs), column.name)) |
|
268 self.w(u'</tr>') |
|
269 self.w(u'</thead><tbody>') |
|
270 for rowindex, row in enumerate(self.model.get_rows()): |
|
271 klass = (rowindex%2==1) and 'odd' or 'even' |
|
272 self.w(u'<tr class="%s" %s>' % (klass, self.highlight)) |
|
273 for column, sortvalue in self.itercols(rowindex): |
|
274 attrs = dict(column.cell_attrs) |
|
275 attrs["cubicweb:sortvalue"] = 'json:' + sortvalue |
|
276 attrs = ('%s="%s"' % (name, value) for name, value in attrs.iteritems()) |
|
277 self.w(u'<td %s>' % (' '.join(attrs))) |
|
278 for cellvid, colindex in column.cellrenderers: |
|
279 self.model.render(cellvid, rowindex, colindex, w=self.w) |
|
280 self.w(u'</td>') |
|
281 self.w(u'</tr>') |
|
282 self.w(u'</tbody>') |
|
283 self.w(u'</table>') |
|
284 |
|
285 def itercols(self, rowindex): |
|
286 for column in self.columns: |
|
287 yield column, self.model.sortvalue(rowindex, column.rset_sortcol) |
|
288 |
|
289 |
|
290 class ProgressBarWidget(HTMLWidget): |
|
291 """display a progress bar widget""" |
|
292 def __init__(self, done, todo, total): |
|
293 self.done = done |
|
294 self.todo = todo |
|
295 self.total = total |
|
296 |
|
297 def _render(self): |
|
298 try: |
|
299 pourcent = self.done*100./self.total |
|
300 except ZeroDivisionError: |
|
301 pourcent = 0 |
|
302 real_pourcent = pourcent |
|
303 if pourcent > 100 : |
|
304 color = 'done' |
|
305 pourcent = 100 |
|
306 elif self.todo + self.done > self.total : |
|
307 color = 'overpassed' |
|
308 else: |
|
309 color = 'inprogress' |
|
310 if pourcent < 0: |
|
311 pourcent = 0 |
|
312 self.w(u'<div class="progressbarback" title="%i %%">' % real_pourcent) |
|
313 self.w(u'<div class="progressbar %s" style="width: %spx; align: left;" ></div>' % (color, pourcent)) |
|
314 self.w(u'</div>') |