diff options
| author | Étienne Loks <etienne.loks@peacefrogs.net> | 2012-02-15 16:59:28 +0100 | 
|---|---|---|
| committer | Étienne Loks <etienne.loks@peacefrogs.net> | 2012-02-15 16:59:28 +0100 | 
| commit | f88541bedcffdfaff485ef71287be88a58c745c2 (patch) | |
| tree | 87e9fcd59da5d687d2954ae99d9f511df55058f2 /chimere/widgets.py | |
| parent | 8ccdaf23128fbe563658ca0d9d74d2ffd831b68d (diff) | |
| download | Chimère-f88541bedcffdfaff485ef71287be88a58c745c2.tar.bz2 Chimère-f88541bedcffdfaff485ef71287be88a58c745c2.zip | |
Large reorganization (refs #316), south migration script to new model names (refs #319)
Diffstat (limited to 'chimere/widgets.py')
| -rw-r--r-- | chimere/widgets.py | 357 | 
1 files changed, 357 insertions, 0 deletions
| diff --git a/chimere/widgets.py b/chimere/widgets.py new file mode 100644 index 0000000..ec63ea3 --- /dev/null +++ b/chimere/widgets.py @@ -0,0 +1,357 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (C) 2008-2011 Étienne Loks  <etienne.loks_AT_peacefrogsDOTnet> + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program.  If not, see <http://www.gnu.org/licenses/>. + +# See the file COPYING for details. + +""" +Extra widgets and fields +""" + +from django import forms +from django.conf import settings +from django.contrib.gis.db import models +from django.contrib.gis.geos import fromstr +from django.utils.safestring import mark_safe +from django.utils.translation import ugettext as _ + +URL_OSM_CSS = ["http://www.openlayers.org/api/theme/default/style.css"] +URL_OSM_JS = [settings.MEDIA_URL+"OpenLayers.js", +          "http://www.openstreetmap.org/openlayers/OpenStreetMap.js"] + +def getMapJS(area_name=''): +    '''Variable initialization for drawing the map +    ''' +    # projection, center and bounds definitions +    js = u"var epsg_display_projection = new OpenLayers.Projection('EPSG:%d')\ +;\n" % settings.EPSG_DISPLAY_PROJECTION +    js += u"var epsg_projection = new OpenLayers.Projection('EPSG:%d');\n" % \ +                                                        settings.EPSG_PROJECTION +    js += u"var centerLonLat = new OpenLayers.LonLat(%f,\ +%f).transform(epsg_display_projection, epsg_projection);\n" % \ +                                                         settings.DEFAULT_CENTER +    js += u"var media_path = '%s';\n" % settings.MEDIA_URL +    js += u"var map_layer = %s;\n" % settings.MAP_LAYER +    js += u"var restricted_extent;\n" + +    if area_name: +        js += u"var area_name='%s';\n" % area_name +    if settings.RESTRICTED_EXTENT: +        restricted_extent_str = [str(coord) \ +                                 for coord in settings.RESTRICTED_EXTENT] +        js += u"restricted_extent = new OpenLayers.Bounds(%s);\n" %\ +                                           ", ".join(restricted_extent_str) +    js = u"""<script type="text/javascript"><!-- +%s// !--></script> +""" % js +    return js + +class TextareaWidget(forms.Textarea): +    """ +    Manage the edition of a text using TinyMCE +    """ +    class Media: +        js = ["%stiny_mce.js" % settings.TINYMCE_URL, +              "%stextareas.js" % settings.MEDIA_URL,] + +class PointChooserWidget(forms.TextInput): +    """ +    Manage the edition of point on a map +    """ +    class Media: +        css = { +            "all": URL_OSM_CSS + ["%sforms.css" % settings.MEDIA_URL,] +        } +        js = URL_OSM_JS + ["%sedit_map.js" % settings.MEDIA_URL, +                           "%sbase.js" % settings.MEDIA_URL,] + +    def render(self, name, value, attrs=None, area_name=''): +        ''' +        Render a map and latitude, longitude information field +        ''' +        val = '0' +        value_x, value_y = 0, 0 +        if value: +            val = str(value) +            if hasattr(value, 'x') and hasattr(value, 'y'): +                value_x, value_y = value.x, value.y +            elif isinstance(value, unicode) and value.startswith('POINT('): +                try: +                    value_x, value_y = value.split('(')[1][:-1].split(' ') +                    value_x, value_y = float(value_x), float(value_y) +                except: +                    value = None +            else: +                value = None +        tpl = getMapJS(area_name) +        tpl += u'<script src="%sedit_map.js"></script>\n' % settings.MEDIA_URL +        tpl += u"""<div id='map_edit'></div> +<div id='live_lonlat'> +<p><label for='live_latitude'>%s</label>\ +<input type='texte' name='live_latitude' id='live_latitude' size='8' \ +disabled='true' value='%f'/></p> +<p><label for='live_longitude'>%s</label><input type='texte' \ +name='live_longitude' id='live_longitude' size='8' disabled='true' \ +value='%f'/></p> +</div> +<input type='hidden' name='%s' id='id_%s' value="%s"/> +""" % (_("Latitude"), value_y, _("Longitude"), value_x, name, name, val) +        tpl += "<script type='text/javascript'><!--\n" +        tpl += "init();\n" +        if value: +            tpl += '''var mylonlat = new OpenLayers.LonLat(%f,%f); +putMarker(mylonlat.transform(epsg_display_projection, +                             map.getProjectionObject()).clone(), true); +''' % (value_x, value_y) +        tpl += """// --></script> +<hr class='spacer'/> +""" +        return mark_safe(tpl) + +class PointField(models.PointField): +    ''' +    Set the widget for the form field +    ''' +    def formfield(self, **keys): +        defaults = {'widget': PointChooserWidget} +        keys.update(defaults) +        return super(PointField, self).formfield(**keys) + +    def clean(self, value, instance=None): +        if len(value) != 2 and self.required: +            raise ValidationError(_("Invalid point")) +        return value + +class RouteChooserWidget(forms.TextInput): +    """ +    Manage the edition of route on a map +    """ +    class Media: +        css = { +            "all": URL_OSM_CSS + ["%sforms.css" % settings.MEDIA_URL,] +        } +        js = ["%sedit_route_map.js" % settings.MEDIA_URL, +              "%sbase.js" % settings.MEDIA_URL,] + URL_OSM_JS + +    def render(self, name, value, attrs=None, area_name='', routefile_id=None): +        ''' +        Render a map and latitude, longitude information field +        ''' +        tpl = getMapJS(area_name) +        help_create = '' +        if not value: +            help_create = """<h3>%s</h3> +<p>%s</p> +<p>%s</p> +<p>%s</p> +<p>%s</p> +<p>%s</p>""" % (_(u"Creation mode"), +_(u"To start drawing the route click on the toggle button: \"Draw\"."), +_(u"Then click on the map to begin the drawing."), +_(u"You can add points by clicking again."), +_(u"To finish the drawing double click. When the drawing is finished you can \ +edit it."), +_(u"While creating to undo a drawing click again on the toggle button \"Stop \ +drawing\".")) +        help_modify = """<h3>%s</h3> +<p>%s</p> +<p>%s</p> +<p>%s</p>""" % (_(u"Modification mode"), +_(u"To move a point click on it and drag it to the desired position."), +_(u"To delete a point move the mouse cursor over it and press the \"d\" or \ +\"Del\" key."), +_(u"To add a point click in the middle of a segment and drag the new point to \ +the desired position")) +        tpl += u'<script src="%sedit_route_map.js"></script>\n' % \ +                                                            settings.MEDIA_URL +        if not value: +            # upload a file +            tpl += u"""<script type='text/javascript'><!-- +    var error_msg = "%s"; +// --></script> +""" % _(u"Give a name and set category before uploading a file.") +            tpl += u'<div id="upload"><a href="#" class="upload-button" '\ +               u'onclick="uploadFile(error_msg);return false;">%s</a></div>' % ( +                    _(u"Upload a route file (GPX or KML)")) +            tpl += u"""\n<p id='draw-or'>%s</p>\n""" % _(u"or") +            tpl += u"""<div id='draw-label'><div id='draw-toggle-off' \ +onclick='toggleDraw();'> +<a href='#' onclick='return false;'>%s</a></div> +</div> +<hr class='spacer'/>""" % (_(u"Start \"hand\" drawing")) +        if value: +            tpl += """ +<div id='map_edit'></div>""" +        else: +            tpl += """ +<div id='map_edit'> +  <div class='map_button'> +  <a href='#' id='button-move-map' class='toggle-button toggle-button-active' onclick='toggleDrawOff();return false;'>%s</a> +  <a href='#' id='button-draw-map' class='toggle-button toggle-button-inactive' onclick='toggleDrawOn();return false;'>%s</a></div> +  </div>""" % (_(u"Move on the map"), _(u"Draw")) +            tpl += ''' +<div class='help-route' id='help-route-create'>%s</div>''' % help_create +        style = '' +        if value: +            style = " style='display:block'" +        tpl += """ +<div class='help-route' id='help-route-modify'%s>%s</div> +<hr class='spacer'/> +<input type='hidden' name='%s' id='id_%s' value="%s"/> +<input type='hidden' name='associated_file_id' id='id_associated_file_id' \ +value="%s"/> +""" % (style, help_modify, name, name, value, routefile_id) +        tpl += "<script type='text/javascript'><!--\n" +        if not value: +            tpl += "document.getElementById('map_edit').style.display = 'None';" +        if value: +            tpl += "init();\n" +            val = value +            if type(value) == unicode: +                try: +                    val = fromstr(value) +                except: +                    pass +            if hasattr(val, 'json'): +                tpl += """ +var geometry='%s'; +initFeature(geometry);""" % val.json +        tpl += """ +// --></script> +""" +        return mark_safe(tpl) + +class RouteField(models.LineStringField): +    ''' +    Set the widget for the form field +    ''' +    def formfield(self, **keys): +        defaults = {'widget': RouteChooserWidget} +        keys.update(defaults) +        return super(RouteField, self).formfield(**keys) + +class AreaWidget(forms.TextInput): +    """ +    Manage the edition of an area on the map +    """ +    class Media: +        css = { +            "all": URL_OSM_CSS + ["%sforms.css" % settings.MEDIA_URL,] +        } +        js = URL_OSM_JS + ["%sedit_area.js" % settings.MEDIA_URL, +              "%sbase.js" % settings.MEDIA_URL,] + +    def render(self, name, value, attrs=None): +        """ +        Render a map +        """ +        upper_left_lat, upper_left_lon = 0, 0 +        lower_right_lat, lower_right_lon = 0, 0 +        if value: +            if len(value) == 2: +                upper_left = value[0] +                lower_right = value[1] +                if hasattr(upper_left, 'x') and hasattr(upper_left, 'y'): +                    upper_left_lon, upper_left_lat = upper_left.x, upper_left.y +                if hasattr(lower_right, 'x') and hasattr(lower_right, 'y'): +                    lower_right_lon, lower_right_lat = lower_right.x, \ +                                                       lower_right.y +        tpl = getMapJS() +        tpl += u"""<div id='map_edit'></div> +<input type='hidden' name='upper_left_lat' id='upper_left_lat' value='%f'/> +<input type='hidden' name='upper_left_lon' id='upper_left_lon' value='%f'/> +<input type='hidden' name='lower_right_lat' id='lower_right_lat' value='%f'/> +<input type='hidden' name='lower_right_lon' id='lower_right_lon' value='%f'/> +""" % (upper_left_lat, upper_left_lon, lower_right_lat, lower_right_lon) +        tpl += """<script type='text/javascript'><!-- +init();""" +        if value: +            tpl += """var extent = new OpenLayers.Bounds(%f, %f, %f, %f); +extent.transform(epsg_display_projection, epsg_projection); +map.zoomToExtent(extent, true);""" % (upper_left_lon, upper_left_lat, +                                      lower_right_lon, lower_right_lat) +        tpl += """// --></script> +<hr class='spacer'/> +""" +        return mark_safe(tpl) + +    def value_from_datadict(self, data, files, name): +        """ +        Return the appropriate values +        """ +        values = [] +        for keys in (('upper_left_lon', 'upper_left_lat',), +                     ('lower_right_lon', 'lower_right_lat')): +            value = [] +            for key in keys: +                val = data.get(key, None) +                if not val: +                    return [] +                value.append(val) +            values.append(value) +        return values + +class AreaField(forms.MultiValueField): +    ''' +    Set the widget for the form field +    ''' +    widget = AreaWidget + +    def compress(self, data_list): +        if not data_list: +            return None +        return data_list + +class MultiSelectWidget(forms.SelectMultiple): +    class Media: +        css = {'all': ( +            settings.MEDIA_URL + 'jquery/bsmSelect/css/jquery.bsmselect.css', +            settings.MEDIA_URL + 'jquery/css/jquery.bsmselect.custom.css', +            ) +        } +        js = ( +            settings.MEDIA_URL + 'jquery/bsmSelect/js/jquery.bsmselect.js', +            settings.MEDIA_URL + 'jquery/bsmSelect/js/jquery.bsmselect.compatibility.js', +            ) + +    def render(self, name, value, attrs=None): +        rendered = super(MultiSelectWidget, self).render(name, value, attrs) +        return mark_safe(rendered + u'''<hr class='spacer'/><script type="text/javascript"> +$.bsmSelect.conf['title'] = "%(title)s"; +$("#id_%(name)s").bsmSelect({ +    removeLabel: '<strong>X</strong>', +    containerClass: 'bsmContainer', +    listClass: 'bsmList-custom', +    listItemClass: 'bsmListItem-custom', +    listItemLabelClass: 'bsmListItemLabel-custom', +    removeClass: 'bsmListItemRemove-custom' +}); +</script>''' % {'name':name, 'title':_("Select...")}) + +class SelectMultipleField(models.ManyToManyField): +    ''' +    Set the widget for the category field +    ''' +    def formfield(self, **keys): +        self.help_text = "" +        defaults = {'widget': MultiSelectWidget} +        keys.update(defaults) +        return super(SelectMultipleField, self).formfield(**keys) + +from south.modelsinspector import add_introspection_rules +add_introspection_rules([], ["^chimere\.widgets\.PointField"]) +add_introspection_rules([], ["^chimere\.widgets\.SelectMultipleField"]) +add_introspection_rules([], ["^chimere\.widgets\.RouteField"]) | 
