1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
|
# Copyright 2009 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Widgets dealing with a choice of options."""
__metaclass__ = type
__all__ = [
'CheckBoxMatrixWidget',
'LabeledMultiCheckBoxWidget',
'LaunchpadBooleanRadioWidget',
'LaunchpadDropdownWidget',
'LaunchpadRadioWidget',
'LaunchpadRadioWidgetWithDescription',
'PlainMultiCheckBoxWidget',
]
import math
from lazr.enum import IEnumeratedType
from zope.app.form.browser import MultiCheckBoxWidget
from zope.app.form.browser.itemswidgets import (
DropdownWidget,
RadioWidget,
)
from zope.app.form.browser.widget import renderElement
from zope.schema.interfaces import IChoice
from zope.schema.vocabulary import SimpleVocabulary
from lp.services.webapp.menu import escape
class LaunchpadDropdownWidget(DropdownWidget):
"""A Choice widget that doesn't encloses itself in <div> tags."""
def _div(self, cssClass, contents, **kw):
return contents
class PlainMultiCheckBoxWidget(MultiCheckBoxWidget):
"""MultiCheckBoxWidget that copes with CustomWidgetFacotry."""
_joinButtonToMessageTemplate = u'%s %s '
def __init__(self, field, vocabulary, request):
# XXX flacoste 2006-07-23 Workaround Zope3 bug #545:
# CustomWidgetFactory passes wrong arguments to a MultiCheckBoxWidget
if IChoice.providedBy(vocabulary):
vocabulary = vocabulary.vocabulary
MultiCheckBoxWidget.__init__(self, field, vocabulary, request)
def _renderItem(self, index, text, value, name, cssClass, checked=False):
"""Render a checkbox and text without without label."""
kw = {}
if checked:
kw['checked'] = 'checked'
value = escape(value)
text = escape(text)
id = '%s.%s' % (name, index)
element = renderElement(
u'input', value=value, name=name, id=id,
cssClass=cssClass, type='checkbox', **kw)
return self._joinButtonToMessageTemplate % (element, text)
class LabeledMultiCheckBoxWidget(PlainMultiCheckBoxWidget):
"""MultiCheckBoxWidget which wraps option labels with proper
<label> elements.
"""
_joinButtonToMessageTemplate = (
u'<label for="%s" style="font-weight: normal">%s %s</label> ')
def _renderItem(self, index, text, value, name, cssClass, checked=False):
"""Render a checkbox and text in a label with a style attribute."""
kw = {}
if checked:
kw['checked'] = 'checked'
value = escape(value)
text = escape(text)
id = '%s.%s' % (name, index)
elem = renderElement(u'input',
value=value,
name=name,
id=id,
cssClass=cssClass,
type='checkbox',
**kw)
option_id = '%s.%s' % (self.name, index)
return self._joinButtonToMessageTemplate % (option_id, elem, text)
# XXX Brad Bollenbach 2006-08-10 bugs=56062: This is a hack to
# workaround Zope's RadioWidget not properly selecting the default value.
class LaunchpadRadioWidget(RadioWidget):
"""A widget to work around a bug in RadioWidget."""
def _renderItem(self, index, text, value, name, cssClass, checked=False):
# This is an almost-complete copy of the method in Zope. We need it
# to inject the style in the label, and we omit the "for" in the label
# because it is redundant (and not used in legacy tests).
kw = {}
if checked:
kw['checked'] = 'checked'
value = escape(value)
text = escape(text)
id = '%s.%s' % (name, index)
elem = renderElement(u'input',
value=value,
name=name,
id=id,
cssClass=cssClass,
type='radio',
**kw)
if '<label' in text:
return '%s %s' % (elem, text)
else:
return renderElement(u'label',
contents='%s %s' % (elem, text),
**{'style': 'font-weight: normal'})
def _div(self, cssClass, contents, **kw):
return contents
class LaunchpadRadioWidgetWithDescription(LaunchpadRadioWidget):
"""Display the enumerated type description after the label.
If the value of the vocabulary terms have a description this
is shown as text on a line under the label.
"""
_labelWithDescriptionTemplate = (
u'''<tr>
<td rowspan="2">%s</td>
<td><label for="%s">%s</label></td>
</tr>
<tr>
<td class="formHelp">%s</td>
</tr>
''')
_labelWithoutDescriptionTemplate = (
u'''<tr>
<td>%s</td>
<td><label for="%s">%s</label></td>
</tr>
''')
def __init__(self, field, vocabulary, request):
"""Initialize the widget."""
assert IEnumeratedType.providedBy(vocabulary), (
'The vocabulary must implement IEnumeratedType')
super(LaunchpadRadioWidgetWithDescription, self).__init__(
field, vocabulary, request)
def _renderRow(self, text, form_value, id, elem):
"""Render the table row for the widget depending on description."""
if form_value != self._missing:
vocab_term = self.vocabulary.getTermByToken(form_value)
description = vocab_term.value.description
else:
description = None
if description is None:
return self._labelWithoutDescriptionTemplate % (elem, id, text)
else:
return self._labelWithDescriptionTemplate % (
elem, id, text, escape(description))
def renderItem(self, index, text, value, name, cssClass):
"""Render an item of the list."""
text = escape(text)
id = '%s.%s' % (name, index)
elem = renderElement(u'input',
value=value,
name=name,
id=id,
cssClass=cssClass,
type='radio')
return self._renderRow(text, value, id, elem)
def renderSelectedItem(self, index, text, value, name, cssClass):
"""Render a selected item of the list."""
text = escape(text)
id = '%s.%s' % (name, index)
elem = renderElement(u'input',
value=value,
name=name,
id=id,
cssClass=cssClass,
checked="checked",
type='radio')
return self._renderRow(text, value, id, elem)
def renderValue(self, value):
# Render the items in a table to align the descriptions.
rendered_items = self.renderItems(value)
return (
'<table class="radio-button-widget">%s</table>'
% ''.join(rendered_items))
class LaunchpadBooleanRadioWidget(LaunchpadRadioWidget):
"""Render a Bool field as radio widget.
The `LaunchpadRadioWidget` does the rendering. Only the True-False values
are rendered; a missing value item is not rendered. The default labels
are rendered as 'yes' and 'no', but can be changed by setting the widget's
true_label and false_label attributes.
"""
TRUE = 'yes'
FALSE = 'no'
def __init__(self, field, request):
"""Initialize the widget."""
vocabulary = SimpleVocabulary.fromItems(
((self.TRUE, True), (self.FALSE, False)))
super(LaunchpadBooleanRadioWidget, self).__init__(
field, vocabulary, request)
# Suppress the missing value behaviour; this is a boolean field.
self.required = True
self._displayItemForMissingValue = False
# Set the default labels for true and false values.
self.true_label = 'yes'
self.false_label = 'no'
def _renderItem(self, index, text, value, name, cssClass, checked=False):
"""Render the item with the preferred true and false labels."""
if value == self.TRUE:
text = self.true_label
else:
# value == self.FALSE.
text = self.false_label
return super(LaunchpadBooleanRadioWidget, self)._renderItem(
index, text, value, name, cssClass, checked=checked)
class CheckBoxMatrixWidget(LabeledMultiCheckBoxWidget):
"""A CheckBox widget which organizes the inputs in a grid.
The column_count attribute can be set in the view to change
the number of columns in the matrix.
"""
column_count = 1
def renderValue(self, value):
"""Render the checkboxes inside a <table>."""
rendered_items = self.renderItems(value)
html = ['<table>']
if self.orientation == 'horizontal':
for i in range(0, len(rendered_items), self.column_count):
html.append('<tr>')
for j in range(0, self.column_count):
index = i + j
if index >= len(rendered_items):
break
html.append('<td>%s</td>' % rendered_items[index])
html.append('</tr>')
else:
row_count = int(math.ceil(
len(rendered_items) / float(self.column_count)))
for i in range(0, row_count):
html.append('<tr>')
for j in range(0, self.column_count):
index = i + (j * row_count)
if index >= len(rendered_items):
break
html.append('<td>%s</td>' % rendered_items[index])
html.append('</tr>')
html.append('</table>')
return '\n'.join(html)
|