#!/usr/bin/env python # -*- coding: utf-8 -*- # Copyright (C) 2008-2011 Étienne Loks # 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 . # See the file COPYING for details. """ Extra widgets and fields """ from django import conf from django import forms from django.core.exceptions import ObjectDoesNotExist 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 _ 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')"\ u";\n" % settings.CHIMERE_EPSG_DISPLAY_PROJECTION js += u"OpenLayers.ImgPath = '%schimere/img/';\n" % settings.STATIC_URL js += u"var epsg_projection = new OpenLayers.Projection('EPSG:%d');\n" % \ settings.CHIMERE_EPSG_PROJECTION js += u"var centerLonLat = new OpenLayers.LonLat(%f,"\ u"%f).transform(epsg_display_projection, epsg_projection);\n" % \ settings.CHIMERE_DEFAULT_CENTER js += u"var media_path = '%s';\n" % settings.MEDIA_URL js += u"var static_path = '%s';\n" % settings.STATIC_URL js += u"var map_layer = %s;\n" % settings.CHIMERE_DEFAULT_MAP_LAYER js += u"var restricted_extent;\n" if area_name: js += u"var area_name='%s';\n" % area_name js = u"\n" % js return js def get_map_layers(area_name=''): from chimere.models import Area area = None if area_name: try: area = Area.objects.get(urn=area_name) except ObjectDoesNotExist: pass else: try: area = Area.objects.get(default=True) except ObjectDoesNotExist: pass map_layers, default = [], None if area and area.layers.count(): map_layers = [layer.layer_code for layer in area.layers.order_by('arealayers__order').all()] def_layer = area.layers.filter(arealayers__default=True) if def_layer.count(): def_layer = def_layer.all()[0] for order, map_layer in enumerate(map_layers): if map_layer == def_layer.layer_code: default = order elif settings.CHIMERE_DEFAULT_MAP_LAYER: map_layers = [settings.CHIMERE_DEFAULT_MAP_LAYER] else: map_layers = ["new OpenLayers.Layer.OSM.Mapnik('Mapnik')"] return map_layers, default class ChosenSelectWidget(forms.Select): """ Chosen select widget. """ class Media: js = ["%schosen/chosen/chosen.jquery.min.js" % settings.STATIC_URL,] css = {'all': ["%schosen/chosen/chosen.css" % settings.STATIC_URL,] } def render(self, *args, **kwargs): if 'attrs' not in kwargs: kwargs['attrs'] = {} kwargs['attrs'].update({'class': 'chzn-select'}) rendered = super(ChosenSelectWidget, self).render(*args, **kwargs) rendered += u"\n\n" % kwargs['attrs']['id'] return mark_safe(rendered) class TextareaWidget(forms.Textarea): """ Manage the edition of a text using TinyMCE """ class Media: js = ["%stiny_mce.js" % settings.TINYMCE_URL, "%schimere/js/textareas.js" % settings.STATIC_URL,] class DatePickerWidget(forms.TextInput): """ Manage the edition of dates. JQuery and Jquery-UI are already loaded by default so don't include them in Media files. """ def render(self, *args, **kwargs): rendered = super(DatePickerWidget, self).render(*args, **kwargs) rendered += u"\n\n" % kwargs['attrs']['id'] return mark_safe(rendered) class PointChooserWidget(forms.TextInput): """ Manage the edition of point on a map """ class Media: css = { "all": settings.OSM_CSS_URLS + \ ["%schimere/css/forms.css" % settings.STATIC_URL,] } js = settings.OSM_JS_URLS + list(settings.JQUERY_JS_URLS) + \ ["%schimere/js/jquery.chimere.js" % settings.STATIC_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 map_layers, default_area = get_map_layers(area_name) js = """ OpenLayers.ImgPath = '%schimere/img/'; var EPSG_DISPLAY_PROJECTION = epsg_display_projection = new OpenLayers.Projection('EPSG:%s'); var EPSG_PROJECTION = epsg_projection = new OpenLayers.Projection('EPSG:%s'); var CENTER_LONLAT = centerLonLat = new OpenLayers.LonLat%s.transform(epsg_display_projection, epsg_projection); var DEFAULT_ZOOM = %s; var chimere_init_options = {}; chimere_init_options["default_icon"] = new OpenLayers.Icon('%schimere/img/marker-green.png', new OpenLayers.Size(21, 25), new OpenLayers.Pixel(-(21/2), -25)); chimere_init_options["map_layers"] = [%s]; chimere_init_options['dynamic_categories'] = false; chimere_init_options['edition'] = true; chimere_init_options["checked_categories"] = []; """ % (settings.STATIC_URL, settings.CHIMERE_EPSG_DISPLAY_PROJECTION, settings.CHIMERE_EPSG_PROJECTION, settings.CHIMERE_DEFAULT_CENTER, settings.CHIMERE_DEFAULT_ZOOM, settings.STATIC_URL, ", ".join(map_layers)) if default_area: js += "chimere_init_options['selected_map_layer'] = %d;\n" % \ default_area #TODO: manage area tpl = u"\n" % js tpl += u"
"\ u"
"\ u"

"\ u"

"\ u"

"\ u"
"\ u"" % ( _("Latitude"), value_y, _("Longitude"), value_x, name, name, val) tpl += "\n"\ u"
\n" 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": settings.OSM_CSS_URLS + \ ["%schimere/css/forms.css" % settings.STATIC_URL,] } js = settings.OSM_JS_URLS + list(settings.JQUERY_JS_URLS) + \ ["%schimere/js/jquery.chimere.js" % settings.STATIC_URL, "%schimere/js/edit_route_map.js" % settings.STATIC_URL, "%schimere/js/base.js" % settings.STATIC_URL,] def render(self, name, value, attrs=None, area_name='', routefile_id=None): ''' Render a map and latitude, longitude information field ''' tpl = getMapJS(area_name) map_layers, default_area = get_map_layers(area_name) js = """ OpenLayers.ImgPath = '%schimere/img/'; var EPSG_DISPLAY_PROJECTION = epsg_display_projection = new OpenLayers.Projection('EPSG:%s'); var EPSG_PROJECTION = epsg_projection = new OpenLayers.Projection('EPSG:%s'); var CENTER_LONLAT = centerLonLat = new OpenLayers.LonLat%s.transform(epsg_display_projection, epsg_projection); var DEFAULT_ZOOM = %s; var chimere_init_options = {}; chimere_init_options["map_layers"] = [%s]; chimere_init_options['dynamic_categories'] = false; chimere_init_options['edition'] = true; chimere_init_options['edition_type_is_route'] = true; chimere_init_options["checked_categories"] = []; """ % (settings.STATIC_URL, settings.CHIMERE_EPSG_DISPLAY_PROJECTION, settings.CHIMERE_EPSG_PROJECTION, settings.CHIMERE_DEFAULT_CENTER, settings.CHIMERE_DEFAULT_ZOOM, ", ".join(map_layers)) if default_area: js += "chimere_init_options['selected_map_layer'] = %d;\n" % \ default_area tpl = u"\n" % js #TODO: manage area help_create = '' if not value: help_create = u"

%s

\n"\ u"

%s

\n"\ u"

%s

\n"\ u"

%s

\n"\ u"

%s

\n"\ u"

%s

\n" % (_(u"Creation mode"), _(u"To start drawing the route click on the toggle button: "\ u"\"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 "\ u"finished you can edit it."), _(u"While creating to undo a drawing click again on the toggle "\ u"button \"Stop drawing\".")) help_modify = u"

%s

\n"\ u"

%s

\n"\ u"

%s

\n"\ u"

%s

\n" % (_(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 "\ u"\"d\" or \"Del\" key."), _(u"To add a point click in the middle of a segment and drag the new "\ u"point to the desired position")) if not value: # upload a file tpl += u"" % ( _(u"Give a name and set category before uploading a file.")) tpl += u'' % ( _(u"Upload a route file (GPX or KML)")) tpl += u"\n

%s

\n" % _(u"or") tpl += u"
\n"\ u"%s
"\ u"
\n"\ u"
" % (_(u"Start \"hand\" drawing")) style = '' if value: style = " style='display:block'" tpl += u"\n
%s
"\ u"\n
\n"\ u"\n"\ u"\n" % ( style, help_modify, name, name, value, routefile_id) if value: tpl += u"\n
" else: tpl += "\n
%s
"\ % help_create tpl += u"\n
\n"\ u"
\n"\ u" %s"\ u"\n"\ u""\ u"%s
\n"\ u"
" % (_(u"Move on the map"), _(u"Draw")) tpl += u"\n" 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": settings.OSM_CSS_URLS + ["%schimere/css/forms.css" % settings.STATIC_URL,] } js = settings.OSM_JS_URLS + ["%schimere/js/edit_area.js" % settings.STATIC_URL, "%schimere/js/base.js" % settings.STATIC_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"
\n"\ u"\n"\ u"\n"\ u"\n"\ u"\n" % ( upper_left_lat, upper_left_lon, lower_right_lat, lower_right_lon) tpl += u"\n
\n" 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': list(settings.JQUERY_CSS_URLS) + [ settings.STATIC_URL + 'chimere/js/jquery/bsmSelect/css/jquery.bsmselect.css', settings.STATIC_URL + 'chimere/js/jquery/bsmSelect/css/jquery.bsmselect.custom.css', ] } js = list(settings.JQUERY_JS_URLS) + [ settings.STATIC_URL + 'chimere/js/jquery/bsmSelect/js/jquery.bsmselect.js', settings.STATIC_URL + 'chimere/js/jquery/bsmSelect/js/jquery.bsmselect.compatibility.js', ] def render(self, name, value, attrs=None): rendered = super(MultiSelectWidget, self).render(name, value, attrs) rendered += u"
\n" % {'name':name, 'title':_("Select...")} return mark_safe(rendered) 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"])