Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Added optgroup widget (based on code from Max Brauer, Inqbus).

  • Loading branch information...
commit 9b62fc3c164ec7d0a671e58849fe78c1ef770c93 1 parent d87fd06
@tmassman authored
View
1  src/z3c/formwidget/optgroup/__init__.py
@@ -0,0 +1 @@
+"""An optgroup widget for z3c.form."""
View
28 src/z3c/formwidget/optgroup/configure.zcml
@@ -0,0 +1,28 @@
+<configure
+ xmlns="http://namespaces.zope.org/zope"
+ xmlns:z3c="http://namespaces.zope.org/z3c">
+
+ <class class=".widget.OptgroupWidget">
+ <require
+ interface=".interfaces.IOptgroupWidget"
+ permission="zope.Public"
+ />
+ </class>
+
+
+ <!-- This widget is not configured for any field by default. -->
+ <z3c:widgetTemplate
+ layer="z3c.form.interfaces.IFormLayer"
+ mode="display"
+ template="display.pt"
+ widget=".interfaces.IOptgroupWidget"
+ />
+
+ <z3c:widgetTemplate
+ layer="z3c.form.interfaces.IFormLayer"
+ mode="input"
+ template="input.pt"
+ widget=".interfaces.IOptgroupWidget"
+ />
+
+</configure>
View
29 src/z3c/formwidget/optgroup/display.pt
@@ -0,0 +1,29 @@
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:tal="http://xml.zope.org/namespaces/tal"
+ tal:omit-tag="">
+ <span id="" class=""
+ tal:attributes="id view/id;
+ class view/klass;
+ style view/style;
+ title view/title;
+ lang view/lang;
+ onclick view/onclick;
+ ondblclick view/ondblclick;
+ onmousedown view/onmousedown;
+ onmouseup view/onmouseup;
+ onmouseover view/onmouseover;
+ onmousemove view/onmousemove;
+ onmouseout view/onmouseout;
+ onkeypress view/onkeypress;
+ onkeydown view/onkeydown;
+ onkeyup view/onkeyup">
+ <tal:groups tal:repeat="group python: view.displayValue().keys()">
+ <p tal:content="group">[group]</p>
+ <ul>
+ <tal:items tal:repeat="item python:view.displayValue()[group]">
+ <li tal:content="item">[item]</li>
+ </tal:items>
+ </ul>
+ </tal:groups>
+ </span>
+</htlml>
View
62 src/z3c/formwidget/optgroup/input.pt
@@ -0,0 +1,62 @@
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:tal="http://xml.zope.org/namespaces/tal"
+ tal:omit-tag="">
+
+ <select id="" name="" class="" tabindex="" disabled="" multiple="" size=""
+ tal:attributes="id view/id;
+ name string:${view/name}:list;
+ class view/klass;
+ style view/style;
+ title view/title;
+ lang view/lang;
+ onclick view/onclick;
+ ondblclick view/ondblclick;
+ onmousedown view/onmousedown;
+ onmouseup view/onmouseup;
+ onmouseover view/onmouseover;
+ onmousemove view/onmousemove;
+ onmouseout view/onmouseout;
+ onkeypress view/onkeypress;
+ onkeydown view/onkeydown;
+ onkeyup view/onkeyup;
+ disabled view/disabled;
+ tabindex view/tabindex;
+ onfocus view/onfocus;
+ onblur view/onblur;
+ onchange view/onchange;
+ multiple view/multiple;
+ size view/size">
+ <tal:block repeat="item view/items">
+ <tal:nogroup tal:condition="not: item/title | nothing">
+ <option id="" value="" selected="selected"
+ tal:condition="item/selected"
+ tal:attributes="id item/id;
+ value item/value"
+ tal:content="item/content">[label]</option>
+ <option id="" value=""
+ tal:condition="not:item/selected"
+ tal:attributes="id item/id;
+ value item/value"
+ tal:content="item/content">[label]</option>
+ </tal:nogroup>
+ <optgroup label=""
+ tal:condition="item/title | nothing"
+ tal:attributes="label item/title">
+ <tal:options repeat="option item/member">
+ <option id="" value="" selected="selected"
+ tal:condition="option/selected"
+ tal:attributes="id option/id;
+ value option/value"
+ tal:content="option/content">[label]</option>
+ <option id="" value=""
+ tal:condition="not:option/selected"
+ tal:attributes="id option/id;
+ value option/value"
+ tal:content="option/content">[label]</option>
+ </tal:options>
+ </optgroup>
+ </tal:block>
+ </select>
+ <input name="field-empty-marker" type="hidden" value="1"
+ tal:attributes="name string:${view/name}-empty-marker" />
+</html>
View
23 src/z3c/formwidget/optgroup/interfaces.py
@@ -0,0 +1,23 @@
+# -*- coding: utf-8 -*-
+
+##############################################################################
+#
+# Copyright (c) 2012 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE
+#
+##############################################################################
+"""Interface definitions."""
+
+# zope imports
+from z3c.form.interfaces import ITextWidget
+
+
+class IOptgroupWidget(ITextWidget):
+ """Widget Interface."""
View
154 src/z3c/formwidget/optgroup/widget.py
@@ -0,0 +1,154 @@
+# -*- coding: utf-8 -*-
+
+##############################################################################
+#
+# Copyright (c) 2012 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE
+#
+##############################################################################
+"""An optgroup widget for z3c.form."""
+
+# zope imports
+from z3c.form import converter, interfaces
+from z3c.form.browser import widget
+from z3c.form.browser.select import SelectWidget
+from z3c.form.widget import FieldWidget
+from zope.component import adapter, getMultiAdapter
+from zope.schema.interfaces import (IFromUnicode, ITextLine,
+ ITitledTokenizedTerm, ITokenizedTerm)
+from zope.i18n import translate
+from zope.interface import implementer, implements
+from zope.interface.declarations import directlyProvides
+
+# local imports
+from z3c.formwidget.optgroup.interfaces import IOptgroupWidget
+
+
+class OptgroupTerm(object):
+ """A SimpleTerm with an aditional attribute "optgroup".
+
+ It's also a tokenized term used by SimpleVocabulary.
+ """
+ implements(ITokenizedTerm)
+
+ def __init__(self, value, token=None, title=None, optgroup=None):
+ """Create a term for value and token.
+
+ If token is omitted, str(value) is used for the token. If title is
+ provided, term implements ITitledTokenizedTerm.
+ """
+ self.optgroup = optgroup
+ self.value = value
+ if token is None:
+ token = value
+ self.token = str(token)
+ self.title = title
+ if title is not None:
+ directlyProvides(self, ITitledTokenizedTerm)
+
+
+class OptgroupWidget(SelectWidget):
+ """Optgroup widget based on SelectWidget."""
+ implements(IOptgroupWidget, IFromUnicode)
+
+ klass = u'optgroup-widget'
+ items = ()
+
+ def getSize(self):
+ """Get a dynamic size for the widget."""
+ if hasattr(self.field, 'max_length'):
+ self.size = self.field.max_length
+ return
+ if not self.multiple:
+ self.size = 1
+ return
+ amount = len(self.items)
+ for optgroup in range(0, len(self.items)):
+ amount = amount + len(self.items[optgroup]['member'])
+ if amount <= 10:
+ self.size = amount
+ else:
+ self.size = 10
+
+ def displayValue(self):
+ value = {}
+ for token in self.value:
+ # Ignore no value entries. They are in the request only.
+ if token == self.noValueToken:
+ continue
+ term = self.terms.getTermByToken(token)
+ if ITitledTokenizedTerm.providedBy(term):
+ content = self.getContent(term)
+ else:
+ value.append(term.value)
+ if not term.optgroup in value:
+ value[term.optgroup] = []
+ value[term.optgroup].append(content)
+ return value
+
+ def getContent(self, term):
+ return translate(term.title, context=self.request, default=term.title)
+
+ def update(self):
+ """See z3c.form.interfaces.IWidget."""
+ super(OptgroupWidget, self).update()
+ widget.addFieldClass(self)
+ self.items = []
+ if (not self.required or self.prompt) and self.multiple is None:
+ message = self.noValueMessage
+ if self.prompt:
+ message = self.promptMessage
+ self.items.append({
+ 'id': self.id + '-novalue',
+ 'value': self.noValueToken,
+ 'content': message,
+ 'selected': self.value == []
+ })
+ unordered_items = {}
+ optgroups = []
+ for count, term in enumerate(self.terms):
+ # Exctract optgroups in an ordered list, so we can use this to
+ # preserve the main order of optgroups.
+ if not term.optgroup in optgroups:
+ optgroups.append(term.optgroup)
+ selected = self.isSelected(term)
+ id = '%s-%i' % (self.id, count)
+ content = term.title
+ if ITitledTokenizedTerm.providedBy(term):
+ content = self.getContent(term)
+ if not term.optgroup in unordered_items:
+ unordered_items[term.optgroup] = []
+ unordered_items[term.optgroup].append({
+ 'id': id,
+ 'value': term.token,
+ 'content': content,
+ 'selected': selected,
+ })
+ for group in optgroups:
+ item = {}
+ item['title'] = group
+ item['member'] = unordered_items[group]
+ self.items.append(item)
+ self.getSize()
+
+
+@adapter(converter.FieldDataConverter)
+@adapter(ITextLine, interfaces.IFormLayer)
+@implementer(interfaces.IFieldWidget)
+def OptgroupFieldWidget(field, request):
+ """Factory for OptgroupWidget."""
+ widget = FieldWidget(field, OptgroupWidget(request))
+ if hasattr(field, 'value_type'):
+ widget.multiple = getMultiAdapter((field,
+ field.value_type, request), interfaces.IFieldWidget).multiple
+ else:
+ widget.multiple = getMultiAdapter((field, request),
+ interfaces.IFieldWidget).multiple
+ return widget
Please sign in to comment.
Something went wrong with that request. Please try again.