from copy import deepcopy
from types import DictType, FileType, ListType, StringTypes
from DateTime import DateTime

from Products.CMFCore.utils import getToolByName
from Products.CMFCore.Expression import Expression
from Products.CMFCore.Expression import createExprContext

from Products.Archetypes.utils import className
from Products.Archetypes.utils import unique
from Products.Archetypes.utils import capitalize
from Products.Archetypes.generator import macrowidget
from Products.Archetypes.debug import log
from Products.Archetypes.Registry import registerPropertyType
from Products.Archetypes.Registry import registerWidget

from ExtensionClass import Base
from AccessControl import ClassSecurityInfo
from Globals import InitializeClass
from Acquisition import aq_base
from Acquisition import Implicit

_marker = []

class TypesWidget(macrowidget, Base):
    _properties = macrowidget._properties.copy()
    _properties.update({
        'modes' : ('view', 'edit'),
        'populate' : True,  # should this field be populated in edit and view?
        'postback' : True,  # should this field be repopulated with POSTed
                         # value when an error occurs?
        'show_content_type' : False,
        'helper_js': (),
        'helper_css': (),
        })

    security = ClassSecurityInfo()

    security.declarePublic('getName')
    def getName(self):
        return self.__class__.__name__

    security.declarePublic('getType')
    def getType(self):
        """Return the type of this field as a string"""
        return className(self)

    security.declarePublic('bootstrap')
    def bootstrap(self, instance):
        """Override if your widget needs data from the instance."""
        return

    security.declarePublic('populateProps')
    def populateProps(self, field):
        """This is called when the field is created."""
        name = field.getName()
        if not self.label:
            self.label = capitalize(name)

    security.declarePublic('isVisible')
    def isVisible(self, instance, mode='view'):
        """decide if a field is visible in a given mode -> 'state'

        Return values are visible, hidden, invisible

        The value for the attribute on the field may either be a dict with a
        mapping for edit and view::

            visible = { 'edit' :'hidden', 'view' : 'invisible' }

        Or a single value for all modes::

            True/1:  'visible'
            False/0: 'invisible'
            -1:      'hidden'
            
        visible: The field is shown in the view/edit screen
        invisible: The field is skipped when rendering the view/edit screen
        hidden: The field is added as <input type="hidden" />

        The default state is 'visible'.
        """
        vis_dic = getattr(aq_base(self), 'visible', _marker)
        state = 'visible'
        if vis_dic is _marker:
            return state
        if type(vis_dic) is DictType:
            state = vis_dic.get(mode, state)
        elif not vis_dic:
            state = 'invisible'
        elif vis_dic < 0:
            state = 'hidden'
        #assert(state in ('visible', 'hidden', 'invisible',),
        #      'Invalid view state %s' % state
        #      )
        return state

    # XXX
    security.declarePublic('setCondition')
    def setCondition(self, condition):
        """Set the widget expression condition."""
        self.condition = condition

    security.declarePublic('getCondition')
    def getCondition(self):
        """Return the widget text condition."""
        return self.condition

    security.declarePublic('testCondition')
    def testCondition(self, folder, portal, object):
        """Test the widget condition."""
        try:
            if self.condition:
                __traceback_info__ = (folder, portal, object, self.condition)
                ec = createExprContext(folder, portal, object)
                return Expression(self.condition)(ec)
            else:
                return True
        except AttributeError:
            return True

    # XXX
    security.declarePublic('process_form')
    def process_form(self, instance, field, form, empty_marker=None,
                     emptyReturnsMarker=False):
        """Basic impl for form processing in a widget"""
        value = form.get(field.getName(), empty_marker)
        if value is empty_marker:
            return empty_marker
        if emptyReturnsMarker and value == '':
            return empty_marker
        return value, {}

    security.declarePublic('copy')
    def copy(self):
        """
        Return a copy of widget instance, consisting of field name and
        properties dictionary.
        """
        cdict = dict(vars(self))
        properties = deepcopy(cdict)
        return self.__class__(**properties)

InitializeClass(TypesWidget)

class StringWidget(TypesWidget):
    _properties = TypesWidget._properties.copy()
    _properties.update({
        'macro' : "widgets/string",
        'size' : '30',
        'maxlength' : '255',
        })

    security = ClassSecurityInfo()

class DecimalWidget(TypesWidget):
    _properties = TypesWidget._properties.copy()
    _properties.update({
        'macro' : "widgets/decimal",
        'size' : '5',
        'maxlength' : '255',
        'dollars_and_cents' : False,
        'whole_dollars' : False,
        'thousands_commas' : False,
        })

    security = ClassSecurityInfo()

class IntegerWidget(TypesWidget):
    _properties = TypesWidget._properties.copy()
    _properties.update({
        'macro' : "widgets/integer",
        'size' : '5',
        'maxlength' : '255',
        })

    security = ClassSecurityInfo()

class ReferenceWidget(TypesWidget):
    _properties = TypesWidget._properties.copy()
    _properties.update({
        'macro' : "widgets/reference",
        'checkbox_bound': 5,

        'addable' : False, # create createObject link for every addable type
        'destination_types' : None,
        'destination' : None, # may be:
                              # - ".", context object;
                              # - None, any place where
                              #   Field.allowed_types can be added;
                              # - string path;
                              # - name of method on instance
                              #   (it can be a combination list);
                              # - a list, combining all item above;
                              # - a dict, where
                              #   {portal_type:<combination of the items above>}
                              # destination is relative to portal root
        'helper_css' : ('content_types.css',),
        })

    security = ClassSecurityInfo()

    def lookupDestinationsFor(self, typeinfo, tool, purl, destination_types=None):
        """
        search where the user can add a typeid instance
        """
        searchFor = []

        # first, discover who can contain the type
        if destination_types is not None:
            if type(destination_types) in (type(()), type([])):
                searchFor += list(destination_types[:])
            else:
                searchFor.append(destination_types)
        else:
            for regType in tool.listTypeInfo():
                if typeinfo.globalAllow():
                    searchFor.append(regType.getId())
                elif regType.filter_content_types and regType.allowed_content_types:
                    act_dict = dict([ (act, 0) for act in regType.allowed_content_types ])
                    if act_dict.has_key(typeinfo.getId()):
                        searchFor.append(regType.getId())

        catalog = getToolByName(purl, 'portal_catalog')
        containers = []
        portal_path = "/".join(purl.getPortalObject().getPhysicalPath())
        for wanted in searchFor:
            for brain in catalog(portal_type=wanted):
                relative_path = brain.getPath().replace(portal_path + '/', '')
                containers.append(relative_path)

        return containers

    security.declarePublic('addableTypes')
    def addableTypes(self, instance, field):
        """ Returns a list of dictionaries which maps portal_type
            to a human readable form.
        """
        tool = getToolByName(instance, 'portal_types')
        purl = getToolByName(instance, 'portal_url')

        lookupDestinationsFor = self.lookupDestinationsFor
        getRelativeContentURL = purl.getRelativeContentURL

        # if destination_types is None (by default) it will do
        # N-portal_types queries to the catalog which is horribly inefficient

        destination_types = getattr(self, 'destination_types', None)
        destination = self.destination
        types = []

        options = {}
        for typeid in field.allowed_types:
            _info = tool.getTypeInfo(typeid)
            if _info is None:
                # The portal_type asked for was not
                # installed/has been removed.
                log("Warning: in Archetypes.Widget.lookupDestinationsFor: " \
                    "portal type %s not found" % typeid )
                continue

            if destination == None:
                options[typeid]=[None]
            elif isinstance(destination, DictType):
                options[typeid]=destination.get(typeid, [None])
                if not isinstance(options[typeid], ListType):
                    options[typeid] = [options[typeid]]
            elif isinstance(destination, ListType):
                options[typeid]=destination
            else:
                place = getattr(aq_base(instance), destination, destination)
                if callable(place):
                    #restore acq.wrapper
                    place = getattr(instance, destination)
                    place = place()
                if isinstance(place, ListType):
                    options[typeid] = place
                else:
                    options[typeid] = [place]

            value = {}
            value['id'] = typeid
            value['name'] = _info.Title()
            value['destinations'] = []

            for option in options.get(typeid):
                if option == None:
                    value['destinations'] = value['destinations'] + \
                        lookupDestinationsFor(_info, tool, purl,
                                          destination_types=destination_types)
                elif option == '.':
                    value['destinations'].append(getRelativeContentURL(instance))
                else:
                    try:
                        place = getattr(aq_base(instance), option, option)
                    except TypeError:
                        place = option
                    if callable(place):
                        #restore acq.wrapper
                        place = getattr(instance, option)
                        place = place()
                    if isinstance(place, ListType):
                        value['destinations'] = place + value['destinations']
                    else:
                        #XXX Might as well check for type, doing it everywhere else
                        value['destinations'].append(place)

            if value['destinations']:
                types.append(value)

        return types

class ComputedWidget(TypesWidget):
    _properties = TypesWidget._properties.copy()
    _properties.update({
        'macro' : "widgets/computed",
        })

    security = ClassSecurityInfo()

class TextAreaWidget(TypesWidget):
    _properties = TypesWidget._properties.copy()
    _properties.update({
        'macro' : "widgets/textarea",
        'rows'  : 5,
        'cols'  : 40,
        'format': 0,
        'append_only': False,
        'divider':"\n\n========================\n\n",
        'maxlength' : False,
        'helper_js': ('widgets/js/textcount.js',),        
        })

    security = ClassSecurityInfo()

    # XXX
    security.declarePublic('process_form')
    def process_form(self, instance, field, form, empty_marker=None,
                     emptyReturnsMarker=False):
        """handle text formatting"""
        text_format = None
        value = None
        # text field with formatting
        value = form.get(field.getName(), empty_marker)

        if value is empty_marker:
            return empty_marker

        if emptyReturnsMarker and value == '':
            return empty_marker

        if hasattr(field, 'allowable_content_types') and \
               field.allowable_content_types:
            format_field = "%s_text_format" % field.getName()
            text_format = form.get(format_field, empty_marker)
        kwargs = {}

        if text_format is not empty_marker and text_format:
            kwargs['mimetype'] = text_format

        """ handle append_only  """
        # Don't append if the existing data is empty or nothing was passed in
        if getattr(field.widget, 'append_only', None):
            if field.getEditAccessor(instance)():
                if (value and not value.isspace()):
                    
                    divider = field.widget.divider
                    
                    # Add a datestamp along with divider if desired.
                    if getattr(field.widget, 'timestamp', None):

                        divider = "\n\n" + str(DateTime()) + divider
                        
                    # using default_output_type caused a recursive transformation
                    # that sucked, thus mimetype= here to keep it in line
                    value = value + divider + \
                            field.getEditAccessor(instance)()
                else:
                    # keep historical entries
                    value = field.getEditAccessor(instance)()
        return value, kwargs

class LinesWidget(TypesWidget):
    _properties = TypesWidget._properties.copy()
    _properties.update({
        'macro' : "widgets/lines",
        'rows'  : 5,
        'cols'  : 40,
        })

    security = ClassSecurityInfo()

class BooleanWidget(TypesWidget):
    _properties = TypesWidget._properties.copy()
    _properties.update({
        'macro' : "widgets/boolean",
        })

    security = ClassSecurityInfo()

class CalendarWidget(TypesWidget):
    _properties = TypesWidget._properties.copy()
    _properties.update({
        'macro' : "widgets/calendar",
        'format' : '', # time.strftime string
        'show_hm' : True, 
        'show_ymd' : True, # False not supported by the plone templates yet
        'starting_year' : 1999, # not supported by the plone templates yet
        'ending_year' : None, # not supported by the plone templates yet
        'future_years' : 5, # not supported by the plone templates yet
        'helper_js': ('jscalendar/calendar_stripped.js',
                      'jscalendar/calendar-en.js'),
        'helper_css': ('jscalendar/calendar-system.css',),
        })

    security = ClassSecurityInfo()

class SelectionWidget(TypesWidget):
    _properties = TypesWidget._properties.copy()
    _properties.update({
        'format': "flex", # possible values: flex, select, radio
        'macro' : "widgets/selection",
        })

    security = ClassSecurityInfo()

class MultiSelectionWidget(TypesWidget):
    _properties = TypesWidget._properties.copy()
    _properties.update({
        'format': "select", # possible values: select, checkbox
        'macro' : "widgets/multiselection",
        'size'  : 5,
        })

    security = ClassSecurityInfo()

    security.declarePublic('process_form')
    def process_form(self, instance, field, form, empty_marker=None,
                     emptyReturnsMarker=False):
        """Basic impl for form processing in a widget"""
        value = form.get(field.getName(), empty_marker)
        if value is empty_marker:
            return empty_marker
        if emptyReturnsMarker and value == '':
            return empty_marker
        if isinstance(value, StringTypes):
            values = [v.strip() for v in value.split('\n')]
        elif isinstance(value, ListType):
            values = value
        else:
            values = []
        return values, {}

class KeywordWidget(TypesWidget):
    _properties = TypesWidget._properties.copy()
    _properties.update({
        'macro' : "widgets/keyword",
        'size'  : 5,
        'vocab_source' : 'portal_catalog',
        'roleBasedAdd' : True,
        })

    security = ClassSecurityInfo()

    # XXX
    security.declarePublic('process_form')
    def process_form(self, instance, field, form, empty_marker=None,
                     emptyReturnsMarker=False):
        """process keywords from form where this widget has a list of
        available keywords and any new ones"""
        name = field.getName()
        existing_keywords = form.get('%s_existing_keywords' % name, empty_marker)
        if existing_keywords is empty_marker:
            existing_keywords = []
        new_keywords = form.get('%s_keywords' % name, empty_marker)
        if new_keywords is empty_marker:
            new_keywords = []

        value = existing_keywords + new_keywords
        value = [k for k in list(unique(value)) if k]

        if not value and emptyReturnsMarker: return empty_marker

        return value, {}


class FileWidget(TypesWidget):
    _properties = TypesWidget._properties.copy()
    _properties.update({
        'macro' : "widgets/file",
        'show_content_type' : True,
        })

    security = ClassSecurityInfo()

    # XXX
    security.declarePublic('process_form')
    def process_form(self, instance, field, form, empty_marker=None,
                     emptyReturnsMarker=False):
        """form processing that deals with binary data"""

        delete = form.get('%s_delete' % field.getName(), empty_marker)
        if delete=='delete': return "DELETE_FILE", {}
        if delete=='nochange' : return empty_marker

        value = None

        fileobj = form.get('%s_file' % field.getName(), empty_marker)

        if fileobj is empty_marker: return empty_marker

        filename = getattr(fileobj, 'filename', '')
        if not filename:
            filename = getattr(fileobj, 'name', '')

        if filename:
            value = fileobj

        if not value: return None

        return value, {}


class RichWidget(TypesWidget):
    _properties = TypesWidget._properties.copy()
    _properties.update({
        'macro' : "widgets/rich",
        'rows'  : 5,
        'cols'  : 40,
        'format': 1,
        'allow_file_upload': True,
        })

    security = ClassSecurityInfo()

    # XXX
    security.declarePublic('process_form')
    def process_form(self, instance, field, form, empty_marker=None,
                     emptyReturnsMarker=False):
        """complex form processing, includes handling for text
        formatting and file objects"""
        # This is basically the old processing chain from base object
        text_format = None
        isFile = False
        value = None

        # text field with formatting
        if hasattr(field, 'allowable_content_types') and \
           field.allowable_content_types:
            # was a mimetype specified
            format_field = "%s_text_format" % field.getName()
            text_format = form.get(format_field, empty_marker)

        # or a file?
        fileobj = form.get('%s_file' % field.getName(), empty_marker)

        if fileobj is not empty_marker:

            filename = getattr(fileobj, 'filename', '') or \
                       (isinstance(fileobj, FileType) and \
                        getattr(fileobj, 'name', ''))

            if filename:
                value = fileobj
                isFile = True

        kwargs = {}
        if not value:
            value = form.get(field.getName(), empty_marker)
            if text_format is not empty_marker and text_format:
                kwargs['mimetype'] = text_format

        if value is empty_marker: return empty_marker

        if value and not isFile:
            # Value filled, no file uploaded
            if kwargs.get('mimetype') == str(field.getContentType(instance)) \
                   and instance.isBinary(field.getName()):
                # Was an uploaded file, same content type
                del kwargs['mimetype']

        return value, kwargs


class IdWidget(TypesWidget):
    _properties = TypesWidget._properties.copy()
    _properties.update({
        'macro' : "widgets/zid",
         # show IDs in edit boxes when they are autogenerated?
        'display_autogenerated' : True,
        # script used to determine if an ID is autogenerated
        'is_autogenerated' : 'isIDAutoGenerated',
        # ignore global or by-member setting for visible ids?
        'ignore_visible_ids': False,
        })

    security = ClassSecurityInfo()

    # XXX
    security.declarePublic('process_form')
    def process_form(self, instance, field, form, empty_marker=None,
                     emptyReturnsMarker=False):
        """the id might be hidden by the widget and not submitted"""
        value = form.get('id', empty_marker)
        if not value or value is empty_marker or not value.strip():
            value = instance.getId()
        return value,  {}

class RequiredIdWidget(IdWidget):
    _properties = IdWidget._properties.copy()
    _properties.update({
        })

    security = ClassSecurityInfo()

    # XXX
    security.declarePublic('process_form')
    def process_form(self, instance, field, form, empty_marker=None):
        """Override IdWidget.process_form to require id."""
        return TypesWidget.process_form(self, instance, field, form, empty_marker)


class ImageWidget(FileWidget):
    _properties = FileWidget._properties.copy()
    _properties.update({
        'macro' : "widgets/image",
        # only display if size <= threshold, otherwise show link
        'display_threshold': 102400,
        })

    security = ClassSecurityInfo()

    # XXX
    security.declarePublic('process_form')
    def process_form(self, instance, field, form, empty_marker=None,
                     emptyReturnsMarker=False):
        """form processing that deals with image data (and its delete case)"""
        value = None
        ## check to see if the delete hidden was selected
        delete = form.get('%s_delete' % field.getName(), empty_marker)
        if delete=='delete': return "DELETE_IMAGE", {}
        if delete=='nochange' : return empty_marker
        

        fileobj = form.get('%s_file' % field.getName(), empty_marker)

        if fileobj is empty_marker: return empty_marker

        filename = getattr(fileobj, 'filename', '') or \
                   (isinstance(fileobj, FileType) and \
                    getattr(fileobj, 'name', ''))

        if filename:
            value = fileobj

        if not value: return None
        return value, {}


# LabelWidgets are used to display instructions on a form.  The widget only
# displays the label for a value -- no values and no form elements.
class LabelWidget(TypesWidget):
    _properties = TypesWidget._properties.copy()
    _properties.update({
        'macro' : "widgets/label",
        })

    security = ClassSecurityInfo()

class PasswordWidget(TypesWidget):
    _properties = TypesWidget._properties.copy()
    _properties.update({
        'macro' : 'widgets/password',
        'modes' : ('edit',),
        'populate' : False,
        'postback' : False,
        'size' : 20,
        'maxlength' : '255',
        })

    security = ClassSecurityInfo()

class VisualWidget(TextAreaWidget):
    _properties = TextAreaWidget._properties.copy()
    _properties.update({
        'macro' : "widgets/visual",
        'rows'  : 25,      #rows of TextArea if VE is not available
        'cols'  : 80,      #same for cols
        'width' : '507px', #width of VE frame (if VE is avalilable)
        'height': '400px', #same for height
        'format': 0,
        'append_only': False, #creates a textarea you can only add to, not edit
        'divider': '\n\n<hr />\n\n', # default divider for append only divider
        })

    security = ClassSecurityInfo()

class EpozWidget(TextAreaWidget):
    _properties = TextAreaWidget._properties.copy()
    _properties.update({
        'macro' : "widgets/epoz",
        })

    security = ClassSecurityInfo()

class InAndOutWidget(ReferenceWidget):
    _properties = ReferenceWidget._properties.copy()
    _properties.update({
        'macro' : "widgets/inandout",
        'size' : '6',
        'helper_js': ('widgets/js/inandout.js',),
        })

    security = ClassSecurityInfo()

class PicklistWidget(TypesWidget):
    _properties = TypesWidget._properties.copy()
    _properties.update({
        'macro' : "widgets/picklist",
        'size' : '6',
        'helper_js': ('widgets/js/picklist.js',),
        })

    security = ClassSecurityInfo()

__all__ = ('StringWidget', 'DecimalWidget', 'IntegerWidget',
           'ReferenceWidget', 'ComputedWidget', 'TextAreaWidget',
           'LinesWidget', 'BooleanWidget', 'CalendarWidget',
           'SelectionWidget', 'MultiSelectionWidget', 'KeywordWidget',
           'RichWidget', 'FileWidget', 'IdWidget', 'ImageWidget',
           'LabelWidget', 'PasswordWidget', 'VisualWidget', 'EpozWidget',
           'InAndOutWidget', 'PicklistWidget', 'RequiredIdWidget',
           )

registerWidget(StringWidget,
               title='String',
               description=('Renders a HTML text input box which '
                            'accepts a single line of text'),
               used_for=('Products.Archetypes.Field.StringField',)
               )

registerWidget(DecimalWidget,
               title='Decimal',
               description=('Renders a HTML text input box which '
                            'accepts a fixed point value'),
               used_for=('Products.Archetypes.Field.FixedPointField',)
               )

registerWidget(IntegerWidget,
               title='Integer',
               description=('Renders a HTML text input box which '
                            'accepts a integer value'),
               used_for=('Products.Archetypes.Field.IntegerField',)
               )

registerWidget(ReferenceWidget,
               title='Reference',
               description=('Renders a HTML text input box which '
                            'accepts a reference value'),
               used_for=('Products.Archetypes.Field.ReferenceField',)
               )

registerWidget(ComputedWidget,
               title='Computed',
               description='Renders the computed value as HTML',
               used_for=('Products.Archetypes.Field.ComputedField',)
               )

registerWidget(TextAreaWidget,
               title='Text Area',
               description=('Renders a HTML Text Area for typing '
                            'a few lines of text'),
               used_for=('Products.Archetypes.Field.StringField',
                         'Products.Archetypes.Field.TextField')
               )

registerWidget(LinesWidget,
               title='Lines',
               description=('Renders a HTML textarea for a list '
                            'of values, one per line'),
               used_for=('Products.Archetypes.Field.LinesField',)
               )

registerWidget(BooleanWidget,
               title='Boolean',
               description='Renders a HTML checkbox',
               used_for=('Products.Archetypes.Field.BooleanField',)
               )

registerWidget(CalendarWidget,
               title='Calendar',
               description=('Renders a HTML input box with a helper '
                            'popup box for choosing dates'),
               used_for=('Products.Archetypes.Field.DateTimeField',)
               )

registerWidget(SelectionWidget,
               title='Selection',
               description=('Renders a HTML selection widget, which '
                            'can be represented as a dropdown, or as '
                            'a group of radio buttons'),
               used_for=('Products.Archetypes.Field.StringField',
                         'Products.Archetypes.Field.LinesField',)
               )

registerWidget(MultiSelectionWidget,
               title='Multi Selection',
               description=('Renders a HTML selection widget, where '
                            'you can be choose more than one value'),
               used_for=('Products.Archetypes.Field.LinesField',)
               )

registerWidget(KeywordWidget,
               title='Keyword',
               description='Renders a HTML widget for choosing keywords',
               used_for=('Products.Archetypes.Field.LinesField',)
               )

registerWidget(RichWidget,
               title='Rich Widget',
               description=('Renders a HTML widget that allows you to '
                            'type some content, choose formatting '
                            'and/or upload a file'),
               used_for=('Products.Archetypes.Field.TextField',)
               )

registerWidget(FileWidget,
               title='File',
               description='Renders a HTML widget upload a file',
               used_for=('Products.Archetypes.Field.FileField',)
               )

registerWidget(IdWidget,
               title='ID',
               description='Renders a HTML widget for typing an Id',
               used_for=('Products.Archetypes.Field.StringField',)
               )

registerWidget(RequiredIdWidget,
               title='ID',
               description='Renders a HTML widget for typing an required Id',
               used_for=('Products.Archetypes.Field.StringField',)
               )

registerWidget(ImageWidget,
               title='Image',
               description=('Renders a HTML widget for '
                            'uploading/displaying an image'),
               used_for=('Products.Archetypes.Field.ImageField',)
               )

registerWidget(LabelWidget,
               title='Label',
               description=('Renders a HTML widget that only '
                            'displays the label'),
               used_for=None
               )

registerWidget(PasswordWidget,
               title='Password',
               description='Renders a HTML password widget',
               used_for=('Products.Archetypes.Field.StringField',)
               )

registerWidget(VisualWidget,
               title='Visual',
               description='Renders a HTML visual editing widget widget',
               used_for=('Products.Archetypes.Field.StringField',)
               )

registerWidget(EpozWidget,
               title='Epoz',
               description='Renders a HTML Epoz widget',
               used_for=('Products.Archetypes.Field.StringField',)
               )

registerWidget(InAndOutWidget,
               title='In & Out',
               description=('Renders a widget for moving items '
                            'from one list to another. Items are '
                            'removed from the first list.'),
               used_for=('Products.Archetypes.Field.LinesField',
                         'Products.Archetypes.Field.ReferenceField',)
               )

registerWidget(PicklistWidget,
               title='Picklist',
               description=('Render a widget to pick from one '
                            'list to populate another.  Items '
                            'stay in the first list.'),
               used_for=('Products.Archetypes.Field.LinesField',)
               )

registerPropertyType('maxlength', 'integer', StringWidget)
registerPropertyType('populate', 'boolean')
registerPropertyType('postback', 'boolean')
registerPropertyType('rows', 'integer', RichWidget)
registerPropertyType('cols', 'integer', RichWidget)
registerPropertyType('rows', 'integer', TextAreaWidget)
registerPropertyType('cols', 'integer', TextAreaWidget)
registerPropertyType('append_only', 'boolean', TextAreaWidget)
registerPropertyType('divider', 'string', TextAreaWidget)
registerPropertyType('rows', 'integer', LinesWidget)
registerPropertyType('cols', 'integer', LinesWidget)
registerPropertyType('rows', 'integer', VisualWidget)
registerPropertyType('cols', 'integer', VisualWidget)
