[Zope3-checkins] CVS: Zope3/src/zope/app/browser/component - interfacewidget.py:1.3 configure.zcml:1.4
Steve Alexander
steve@cat-box.net
Sun, 5 Jan 2003 13:56:55 -0500
Update of /cvs-repository/Zope3/src/zope/app/browser/component
In directory cvs.zope.org:/tmp/cvs-serv19046/src/zope/app/browser/component
Modified Files:
interfacewidget.py configure.zcml
Log Message:
Added a widget for choosing a tuple of interfaces, and displaying them.
=== Zope3/src/zope/app/browser/component/interfacewidget.py 1.2 => 1.3 ===
--- Zope3/src/zope/app/browser/component/interfacewidget.py:1.2 Wed Dec 25 09:12:28 2002
+++ Zope3/src/zope/app/browser/component/interfacewidget.py Sun Jan 5 13:56:52 2003
@@ -18,58 +18,260 @@
from zope.interface import Interface
from zope.app.interfaces.browser.form import IBrowserWidget
+from zope.app.interfaces.forms import WidgetInputError
+from zope.app.form.widget import Widget
+from zope.publisher.browser import BrowserView
from zope.app.browser.form.widget import BrowserWidget, DisplayWidget
from zope.component import getService
from zope.exceptions import NotFoundError
from zope.schema.interfaces import ValidationError
+from zope.component.exceptions import ComponentLookupError
-class BaseWidget:
+class BaseSingleWidget:
def _convert(self, name):
- if not name:
- return None
- service = getService(self.context.context, "Interfaces")
- return service.getInterface(name)
+ return nameToInterface(self.context.context, name)
def _unconvert(self, interface):
- if interface is None:
- return interface
- return interface.__module__ + '.' + interface.__name__
+ return interfaceToName(interface)
-class SingleInterfaceWidget(BaseWidget, BrowserWidget):
+class InterfaceWidget(BaseSingleWidget, BrowserWidget):
def __call__(self):
- search_name = self.name + ".search"
+ name = self.name
+ search_name = name + ".search"
search_string = self.request.form.get(search_name, '')
field = self.context
service = getService(field.context, "Interfaces")
base = field.type
if base == Interface:
- base=None
+ base = None
interfaces = list(service.searchInterface(search_string, base=base))
interfaces.sort()
interfaces = map(self._unconvert, interfaces)
- select_name = self.name
selected = self._showData()
- options = ['<option value=\"\">---select interface---</option>']
- for interface in interfaces:
- options.append('<option value="%s"%s>%s</option>'
- % (interface,
- interface == selected and ' selected' or '',
- interface)
- )
+ return renderInterfaceSelect(
+ interfaces, selected, search_name, search_string, name)
- search_field = '<input type="text" name="%s" value=\"%s\">' % (
- search_name, search_string)
- select_field = '<select name="%s">%s</select>' % (
- select_name, ''.join(options))
+class MultiInterfaceWidget(Widget, BrowserView):
- HTML = search_field + select_field
+ __implements__ = IBrowserWidget
+
+ # Names used:
+ #
+ # name.i0, name.i1, ... the value of the interfaces
+ # name.search.i0, ... the search box for that interface
+ #
+ def haveData(self):
+ name_i = self.name+'.i'
+ for k,v in self.request.form.iteritems():
+ if k.startswith(name_i):
+ if nameToInterface(v) is not None:
+ return True
+ return False
+
+ def getData(self, optional=0):
+ field = self.context
+ name_i = self.name+'.i'
+ items_sorted = self.request.form.items()
+ items_sorted.sort()
+ # values will be sorted in key order
+ values = [v
+ for k,v in items_sorted
+ if k.startswith(name_i)]
+ if not values:
+ # No user input
+ if field.required and not optional:
+ raise MissingInputError(field.__name__, field.title,
+ 'the field is required')
+ return field.default
+
+ try:
+ values = tuple([nameToInterface(field, value) for value in values])
+ except ComponentLookupError:
+ # Convert to conversion error
+ exc = ConversionError(sys.exc_info()[1])
+ raise ConversionError, exc, sys.exc_info()[2]
+
+ if not optional:
+ try:
+ field.validate(values)
+ except ValidationError, v:
+ raise WidgetInputError(self.context.__name__,
+ self.title, str(v))
+ return values
+
+ def __call__(self):
+ 'See IBrowserWidget'
+ field = self.context
+ form = self.request.form
+ name = self.name
+ name_i = name+'.i'
+ name_search_i = name+'.search.i'
+
+ service = getService(field.context, "Interfaces")
+ base = field.value_type
+ if base == Interface:
+ base = None
+
+ if self._data is None: # no data has been set with Widget.setData(),
+ # so use the data in the form
+
+ # If a search term is entered, that interface selection remains.
+ # If an interface is selected, that interface selection remains.
+ # Remove all others.
+ # Add one empty one at the end.
+
+ selections = {} # index:[search, value]
+ for k,v in form.iteritems():
+ if k.startswith(name_i):
+ index = int(k[len(name_i):])
+ selection = selections.setdefault(index, ['', ''])
+ selection[1] = v
+ elif k.startswith(name_search_i):
+ index = int(k[len(name_search_i):])
+ selection = selections.setdefault(index, ['', ''])
+ selection[0] = v.strip()
+
+ first_is_blank = False
+ # remove all of the selections that have no search and no value
+ for k,(s,v) in selections.items():
+ if s == v == '':
+ del selections[k]
+
+ if selections:
+ selections = selections.items()
+ selections.sort()
+
+ # If the first selection really was blank, then remember this
+ # fact. We'll use it later if we need to add in an extra
+ # selection box: we can add it at the beginning to preserve
+ # the order as the user might expect.
+ if selections[0][0] != 0:
+ first_is_blank = True
+
+ # get just [search, value], and discard the keys
+ selections = [v for k,v in selections]
+ # XXX is validation here really needed?
+ field.validate(tuple([nameToInterface(field, v)
+ for s,v in selections
+ if v != '']))
+ else: # otherwise, use the default
+ selections = [('', interfaceToName(interface))
+ for interface in field.default]
+ else:
+ # data has been set with Widget.setData()
+ selections = [('', interfaceToName(interface))
+ for interface in self._data]
+
+ # If there are no empty values, add one extra empty selection
+ if not [1 for s,v in selections if v == '']:
+ # if first_is_blank, put the empty selection at the start
+ if first_is_blank:
+ selections = [['', None]] + selections
+ else:
+ selections.append(['', None])
+ # If there is only one value, add another one. We want at least
+ # two values so that it is obvious this is a multi-value selection.
+ if len(selections) == 1:
+ selections.append(['', None])
+ rendered_selections = []
+ count = 0
+ for search, value in selections:
+ interfaces = list(service.searchInterface(search, base=base))
+ interfaces.sort()
+ interfaces = map(interfaceToName, interfaces)
+ search_name = '%s.search.i%s' % (name, count)
+ rendered_selections.append(
+ renderInterfaceSelect(interfaces, value, search_name,
+ search, '%s.i%s' % (name, count))
+ )
+ count += 1
+
+ HTML = ('Use refresh to enter more interfaces<br>' +
+ '<br>'.join(rendered_selections))
return HTML
-class DisplayWidget(BaseWidget, DisplayWidget):
+ def hidden(self):
+ 'See IBrowserWidget'
+ if self._data is None:
+ data = self.getData(1)
+ else:
+ data = self._data
+ name = self.name
+ elements = ['<input type="hidden" name="%s" value="%s" />'
+ % (name, interfaceToName(interface))
+ for interface in data]
+ return ''.join(elements)
+
+
+ # --- deprecated methods of IBrowserWidget
+ #def label(self):
+ # 'See IBrowserWidget'
+ # raise NotImplementedError
+
+ def label(self):
+ return '<label for="%s">%s</label>' % (
+ self.name,
+ self.title,
+ )
+
+ #def row(self):
+ # 'See IBrowserWidget'
+ # raise NotImplementedError
+
+ def row(self):
+ return "<td>%s</td><td>%s</td>" % (self.label(), self())
+
+ def renderHidden(self, value):
+ 'See IBrowserWidget'
+ raise NotImplementedError
+
+ def render(self, value):
+ 'See IBrowserWidget'
+ raise NotImplementedError
+
+
+class InterfaceDisplayWidget(BaseSingleWidget, DisplayWidget):
pass
+
+class MultiInterfaceDisplayWidget(MultiInterfaceWidget):
+ def __call__(self):
+ if self._data is None:
+ data = self.getData(1)
+ else:
+ data = self._data
+ return ', '.join([interfaceToName(interface) for interface in data])
+
+def renderInterfaceSelect(
+ interfaces, selected, search_name, search_string, select_name):
+ options = ['<option value="">---select interface---</option>']
+ for interface in interfaces:
+ options.append('<option value="%s"%s>%s</option>'
+ % (interface,
+ interface == selected and ' selected' or '',
+ interface)
+ )
+
+ search_field = '<input type="text" name="%s" value="%s">' % (
+ search_name, search_string)
+ select_field = '<select name="%s">%s</select>' % (
+ select_name, ''.join(options))
+
+ HTML = search_field + select_field
+ return HTML
+
+def nameToInterface(context, name):
+ if not name:
+ return None
+ service = getService(context, "Interfaces")
+ return service.getInterface(name)
+
+def interfaceToName(interface):
+ if interface is None:
+ return None
+ return interface.__module__ + '.' + interface.__name__
=== Zope3/src/zope/app/browser/component/configure.zcml 1.3 => 1.4 ===
--- Zope3/src/zope/app/browser/component/configure.zcml:1.3 Mon Dec 30 18:50:23 2002
+++ Zope3/src/zope/app/browser/component/configure.zcml Sun Jan 5 13:56:52 2003
@@ -4,17 +4,30 @@
>
<browser:page
- class = ".interfacewidget.SingleInterfaceWidget"
+ class = ".interfacewidget.InterfaceWidget"
for = "zope.app.interfaces.component.interfacefield.IInterfaceField"
name = "edit"
- permission="zope.Public"
- />
+ permission="zope.Public"
+ />
<browser:page
- class = ".interfacewidget.DisplayWidget"
+ class = ".interfacewidget.InterfaceDisplayWidget"
for = "zope.app.interfaces.component.interfacefield.IInterfaceField"
name = "display"
- permission="zope.Public"
- />
+ permission="zope.Public"
+ />
+<browser:page
+ class = ".interfacewidget.MultiInterfaceWidget"
+ for = "zope.app.interfaces.component.interfacefield.IInterfacesField"
+ name = "edit"
+ permission='zope.Public'
+ />
+
+<browser:page
+ class = ".interfacewidget.MultiInterfaceDisplayWidget"
+ for = "zope.app.interfaces.component.interfacefield.IInterfacesField"
+ name = "display"
+ permission = 'zope.Public'
+ />
</zopeConfigure>