diff options
58 files changed, 5653 insertions, 0 deletions
diff --git a/MANIFEST b/MANIFEST new file mode 100644 index 0000000..82cff9f --- /dev/null +++ b/MANIFEST @@ -0,0 +1,43 @@ +_camera.c +logic.py +server.py +setup.py +idlethread.py +webviewer.py +NEWS +instance.py +constants.py +tray.py +_camera.so +recorded.py +model.py +gplay.py +savedmap.py +button.py +utils.py +p5.py +filepicker.py +result.py +color.py +serialize.py +map.py +_camera.o +photocanvas.py +activity/activity.info +activity/activity-map.svg +icons/map-icon-zoomIn.svg +icons/map-icon-croseS.svg +icons/map-icon-croseE.svg +icons/map-icon-croseW.svg +icons/add-icon.svg +icons/delete-icon.svg +icons/corner-info.svg +icons/map-icon-croseN.svg +icons/topo-icon.svg +icons/map-icon-zoomOut.svg +icons/save-search.svg +icons/web-icon.svg +icons/measure-icon.svg +icons/static-icon.svg +po/map.pot +Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3cd0564 --- /dev/null +++ b/Makefile @@ -0,0 +1,35 @@ +PYVER=`python -c "import sys; print '%s.%s' % (sys.version_info[0], sys.version_info[1])"` +PYTHON=python$(PYVER) + +GLIB_INCLUDES=`pkg-config --cflags glib-2.0` +GLIB_LIBS=`pkg-config --libs glib-2.0` + +GTK_INCLUDES=`pkg-config --cflags gtk+-2.0` +GTK_LIBS=`pkg-config --libs gtk+-2.0` + +PYGTK_INCLUDES=`pkg-config --cflags pygtk-2.0` +PYGTK_LIBS=`pkg-config --libs pygtk-2.0` + +CAIRO_INCLUDES=`pkg-config --cflags cairo` +CAIRO_LIBS=`pkg-config --libs cairo` + +PYCAIRO_INCLUDES=`pkg-config --cflags pycairo` +PYCAIRO_LIBS=`pkg-config --libs pycairo` + +INCLUDES=-I. -I/usr/include/${PYTHON} ${GLIB_INCLUDES} ${PYGTK_INCLUDES} ${CAIRO_INCLUDES} ${PYCAIRO_INCLUDES} ${GTK_INCLUDES} +ARCHFLAGS=-m32 -march=i386 -mtune=generic +OPTFLAGS=-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -fasynchronous-unwind-tables +CFLAGS=-g -fPIC -DPIC +LDFLAGS=-shared -nostdlib -Wl,--export-dynamic -pthread + +all: build link + +build: + gcc ${INCLUDES} ${ARCHFLAGS} ${OPTFLAGS} ${CFLAGS} -c _camera.c -o _camera.o + +link: + g++ ${LDFLAGS} _camera.o ${GLIB_LIBS} ${PYGTK_LIBS} ${CAIRO_LIBS} ${PYCAIRO_LIBS} ${GTK_LIBS} -Wl,-soname -Wl,_camera.so -o _camera.so + +clean: + @find -name "*.o" -exec rm {} \; + @find -name "*.so" -exec rm {} \; @@ -0,0 +1,5 @@ +1 +* init (jedierikb) + +2 +* 8.2 re-release( jedierikb)
\ No newline at end of file diff --git a/_camera.c b/_camera.c new file mode 100644 index 0000000..694f0a4 --- /dev/null +++ b/_camera.c @@ -0,0 +1,276 @@ +#include <Python.h> + +#include "pycairo.h" +#include <glib.h> + +#include <pygobject.h> + +static PyTypeObject *_PyGObject_Type; +#define PyGObject_Type (*_PyGObject_Type) +Pycairo_CAPI_t *Pycairo_CAPI; +static PyTypeObject *_PyGdkPixbuf_Type; +#define PyGdkPixbuf_Type (*_PyGdkPixbuf_Type) + +#include <cairo.h> +#include <gdk/gdkpixbuf.h> +#include <gdk/gdkpixmap.h> +#include <cairo-xlib.h> +#include <gdk/gdkcairo.h> + +static cairo_surface_t * +_cairo_surface_from_pixbuf (GdkPixbuf *pixbuf) +{ +/* Ripped from GooCanvas */ + gint width = gdk_pixbuf_get_width (pixbuf); + gint height = gdk_pixbuf_get_height (pixbuf); + guchar *gdk_pixels = gdk_pixbuf_get_pixels (pixbuf); + int gdk_rowstride = gdk_pixbuf_get_rowstride (pixbuf); + int n_channels = gdk_pixbuf_get_n_channels (pixbuf); + guchar *cairo_pixels; + cairo_format_t format; + cairo_surface_t *surface; + static const cairo_user_data_key_t key; + int j; + + if (n_channels == 3) + format = CAIRO_FORMAT_RGB24; + else + format = CAIRO_FORMAT_ARGB32; + + cairo_pixels = g_malloc (4 * width * height); + surface = cairo_image_surface_create_for_data ((unsigned char *)cairo_pixels, + format, + width, height, 4 * width); + cairo_surface_set_user_data (surface, &key, + cairo_pixels, (cairo_destroy_func_t)g_free); + + for (j = height; j; j--) + { + guchar *p = gdk_pixels; + guchar *q = cairo_pixels; + + if (n_channels == 3) + { + guchar *end = p + 3 * width; + + while (p < end) + { +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + q[0] = p[2]; + q[1] = p[1]; + q[2] = p[0]; +#else + q[1] = p[0]; + q[2] = p[1]; + q[3] = p[2]; +#endif + p += 3; + q += 4; + } + } + else + { + guchar *end = p + 4 * width; + guint t1,t2,t3; + +#define MULT(d,c,a,t) G_STMT_START { t = c * a; d = ((t >> 8) + t) >> 8; } G_STMT_END + + while (p < end) + { +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + MULT(q[0], p[2], p[3], t1); + MULT(q[1], p[1], p[3], t2); + MULT(q[2], p[0], p[3], t3); + q[3] = p[3]; +#else + q[0] = p[3]; + MULT(q[1], p[0], p[3], t1); + MULT(q[2], p[1], p[3], t2); + MULT(q[3], p[2], p[3], t3); +#endif + + p += 4; + q += 4; + } + +#undef MULT + } + + gdk_pixels += gdk_rowstride; + cairo_pixels += 4 * width; + } + + return surface; +} + +static PyObject* +_wrap_camera_cairo_surface_from_gdk_pixbuf(PyGObject *self, PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = { "pixbuf", NULL }; + PyGObject *child; + cairo_surface_t *surface; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs,"O!:camera.cairo_surface_from_gdk_pixbuf", kwlist, &PyGdkPixbuf_Type, &child)) + return NULL; + + surface = _cairo_surface_from_pixbuf(GDK_PIXBUF (child->obj)); + if (surface == NULL) { + PyErr_SetString(PyExc_RuntimeError, "surface could not be converted"); + return NULL; + } + + return PycairoSurface_FromSurface(surface, NULL); +} + +static GdkPixbuf * +_pixbuf_from_cairo_surface (cairo_surface_t * sf) +{ + GdkPixmap * pixmap; + GdkPixbuf * pixbuf = NULL; + cairo_surface_type_t type; + gint width = 0, height = 0, depth = 0; + int format; + cairo_t * cr; + GdkColormap * cm; + + type = cairo_surface_get_type (sf); + switch (type) { + case CAIRO_SURFACE_TYPE_IMAGE: + width = cairo_image_surface_get_width (sf); + height = cairo_image_surface_get_height (sf); + format = cairo_image_surface_get_format (sf); + if (format == CAIRO_FORMAT_ARGB32) + depth = 32; + else if (format == CAIRO_FORMAT_RGB24) + depth = 24; + else if (format == CAIRO_FORMAT_A8) + depth = 8; + else if (format == CAIRO_FORMAT_A1) + depth = 1; + else if (format == CAIRO_FORMAT_RGB16_565) + depth = 16; + break; + case CAIRO_SURFACE_TYPE_XLIB: + width = cairo_xlib_surface_get_width (sf); + height = cairo_xlib_surface_get_height (sf); + depth = cairo_xlib_surface_get_depth (sf); + break; + default: + break; + } + if (!depth) + return NULL; + + pixmap = gdk_pixmap_new (NULL, width, height, depth); + if (!pixmap) + return NULL; + + cr = gdk_cairo_create (pixmap); + if (!cr) + goto release_pixmap; + + cairo_set_source_surface (cr, sf, 0, 0); + cairo_paint (cr); + cairo_destroy (cr); + + cm = gdk_colormap_get_system (); + pixbuf = gdk_pixbuf_get_from_drawable (NULL, pixmap, cm, 0, 0, 0, 0, -1, -1); + +release_pixmap: + gdk_pixmap_unref (pixmap); + + return pixbuf; +} + + +static PyObject* +_wrap_camera_gdk_pixbuf_from_cairo_surface(PyGObject *self, PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = { "surface", NULL }; + PyGObject *child; + GdkPixbuf * pixbuf; + PyTypeObject *type = &PycairoSurface_Type; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs,"O!:camera.gdk_pixbuf_from_cairo_surface", kwlist, type, &child)) + return NULL; + + pixbuf = _pixbuf_from_cairo_surface((cairo_surface_t *)(child->obj)); + if (pixbuf == NULL) { + PyErr_SetString(PyExc_RuntimeError, "pixbuf could not be converted"); + return NULL; + } + + return pygobject_new ((GObject *) pixbuf); +} + +const PyMethodDef py_camera_functions[] = { + { "cairo_surface_from_gdk_pixbuf", (PyCFunction)_wrap_camera_cairo_surface_from_gdk_pixbuf, + METH_VARARGS|METH_KEYWORDS, NULL }, + { "gdk_pixbuf_from_cairo_surface", (PyCFunction)_wrap_camera_gdk_pixbuf_from_cairo_surface, + METH_VARARGS|METH_KEYWORDS, NULL }, + { NULL, NULL, 0, NULL } +}; + + +/* ----------- enums and flags ----------- */ + +void +py_sugar_add_constants(PyObject *module, const gchar *strip_prefix) +{ +} + +/* initialise stuff extension classes */ +void +py_camera_register_classes(PyObject *d) +{ + PyObject *module; + + if ((module = PyImport_ImportModule("gobject")) != NULL) { + _PyGObject_Type = (PyTypeObject *)PyObject_GetAttrString(module, "GObject"); + if (_PyGObject_Type == NULL) { + PyErr_SetString(PyExc_ImportError, + "cannot import name GObject from gobject"); + return ; + } + _PyGObject_Type = (PyTypeObject *)PyObject_GetAttrString(module, "GObject"); + if (_PyGObject_Type == NULL) { + PyErr_SetString(PyExc_ImportError, + "cannot import name GObject from gobject"); + return ; + } + } else { + PyErr_SetString(PyExc_ImportError, + "could not import gobject"); + return ; + } + if ((module = PyImport_ImportModule("gtk.gdk")) != NULL) { + _PyGdkPixbuf_Type = (PyTypeObject *)PyObject_GetAttrString(module, "Pixbuf"); + if (_PyGdkPixbuf_Type == NULL) { + PyErr_SetString(PyExc_ImportError, + "cannot import name Pixbuf from gtk.gdk"); + return ; + } + } else { + PyErr_SetString(PyExc_ImportError, + "could not import gtk.gdk"); + return ; + } + + Pycairo_IMPORT; +} + +DL_EXPORT(void) +init_camera(void) +{ + PyObject *m, *d; + + Pycairo_IMPORT; + + m = Py_InitModule ("_camera", py_camera_functions); + d = PyModule_GetDict (m); + + py_camera_register_classes (d); + if (PyErr_Occurred ()) { + Py_FatalError ("can't initialise module _camera"); + } +} diff --git a/_camera.o b/_camera.o Binary files differnew file mode 100644 index 0000000..e126c76 --- /dev/null +++ b/_camera.o diff --git a/_camera.so b/_camera.so Binary files differnew file mode 100644 index 0000000..c57a12b --- /dev/null +++ b/_camera.so diff --git a/activity/activity-map.svg b/activity/activity-map.svg new file mode 100644 index 0000000..a768956 --- /dev/null +++ b/activity/activity-map.svg @@ -0,0 +1,20 @@ +<?xml version="1.0"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"[ + <!ENTITY stroke_color "#010101"> + <!ENTITY fill_color "#FFFFFF"> +]> +<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + width="74.5px" height="74px" viewBox="0 0 74.5 74" enable-background="new 0 0 74.5 74" xml:space="preserve"> +<g> + <path fill="&fill_color;" d="M74.563,55.001c0,10.692-8.748,19.44-19.44,19.44H18.67c-10.692,0-19.44-8.748-19.44-19.44V18.882 + c0-10.692,8.748-19.44,19.44-19.44h36.454c10.692,0,19.44,8.748,19.44,19.44V55.001z"/> +</g> +<g> + <line fill="none" stroke="&stroke_color;" stroke-width="3.5" x1="37.124" y1="13.641" x2="37.124" y2="23.035"/> + <path fill="none" stroke="&stroke_color;" stroke-width="3.5" d="M43.213,29.176c0,3.406-2.762,6.168-6.166,6.168 + c-3.406,0-6.167-2.762-6.167-6.168c0-3.404,2.761-6.141,6.167-6.141C40.451,23.035,43.213,25.771,43.213,29.176z M21.735,59.275 + L34.298,34.5"/> + <line fill="none" stroke="&stroke_color;" stroke-width="3.5" x1="27.88" y1="47.344" x2="54.881" y2="47.344"/> + <line fill="none" stroke="&stroke_color;" stroke-width="3.5" x1="39.949" y1="34.434" x2="52.546" y2="59.275"/> +</g> +</svg> diff --git a/activity/activity.info b/activity/activity.info new file mode 100644 index 0000000..b25f509 --- /dev/null +++ b/activity/activity.info @@ -0,0 +1,9 @@ +[Activity] +name = OfflineMaps +activity_version = 2 +host_version = 1 +license = MIT +icon = activity-map +service_name = org.laptop.community.OfflineMaps +class = map.Map +show_launcher = 1
\ No newline at end of file diff --git a/button.py b/button.py new file mode 100644 index 0000000..d09791d --- /dev/null +++ b/button.py @@ -0,0 +1,79 @@ +import gtk +import os +import gobject +import rsvg +import gc + +from sugar.graphics.palette import Palette +from sugar.graphics.tray import TrayButton +from sugar.graphics.icon import Icon +from sugar.graphics import style +from constants import Constants +import utils + +class SavedButton(TrayButton, gobject.GObject): + def __init__(self, ui, savedmapData): + TrayButton.__init__(self) + self.ui = ui + self.data = savedmapData + + img = self.getImg() + self.set_icon_widget( img ) + + self.setup_rollover_options() + + + def getImg( self ): + pb = gtk.gdk.pixbuf_new_from_file(self.data.thumbPath) + + img = gtk.Image() + img.set_from_pixbuf(pb) + img.show() + + return img + + + def setButtClickedId( self, id ): + self.BUTT_CLICKED_ID = id + + + def getButtClickedId( self ): + return self.BUTT_CLICKED_ID + + + def setup_rollover_options( self ): + palette = Palette( Constants.istrSavedMap ) + self.set_palette(palette) + + self.tag_menu_item = gtk.MenuItem( Constants.istrTagMap ) + self.ACTIVATE_TAG_ID = self.tag_menu_item.connect('activate', self._tagCb) + palette.menu.append(self.tag_menu_item) + self.tag_menu_item.show() + + self.rem_menu_item = gtk.MenuItem( Constants.istrRemove ) + self.ACTIVATE_REMOVE_ID = self.rem_menu_item.connect('activate', self._itemRemoveCb) + palette.menu.append(self.rem_menu_item) + self.rem_menu_item.show() + + self.copy_menu_item = gtk.MenuItem( Constants.istrCopyToClipboard ) + self.ACTIVATE_COPY_ID = self.copy_menu_item.connect('activate', self._itemCopyToClipboardCb) + self.get_palette().menu.append(self.copy_menu_item) + self.copy_menu_item.show() + + + def cleanUp( self ): + self.rem_menu_item.disconnect( self.ACTIVATE_REMOVE_ID ) + self.copy_menu_item.disconnect( self.ACTIVATE_COPY_ID ) + self.tag_menu_item.disconnect( self.ACTIVATE_TAG_ID) + + + def _tagCb(self, widget): + self.ui.showSearchResultTags( self.data ) + + + def _itemRemoveCb(self, widget): + self.ui.removeThumb( self.data ) + + + def _itemCopyToClipboardCb(self, widget): + self.ui.copyToClipboard( self.data ) diff --git a/color.py b/color.py new file mode 100644 index 0000000..8c8903e --- /dev/null +++ b/color.py @@ -0,0 +1,75 @@ +# Copyright (c) 2008, Media Modifications Ltd. + +#Permission is hereby granted, free of charge, to any person obtaining a copy +#of this software and associated documentation files (the "Software"), to deal +#in the Software without restriction, including without limitation the rights +#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +#copies of the Software, and to permit persons to whom the Software is +#furnished to do so, subject to the following conditions: + +#The above copyright notice and this permission notice shall be included in +#all copies or substantial portions of the Software. + +#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +#THE SOFTWARE. + +import gtk + +class Color: + + def __init__(self): + pass + + + def init_rgba(self, r, g, b, a): + self._ro = r + self._go = g + self._bo = b + self._ao = a; + self._r = self._ro / 255.0 + self._g = self._go / 255.0 + self._b = self._bo / 255.0 + self._a = self._ao / 255.0 + + self._opaque = False + if (self._a == 1): + self.opaque = True + + rgb_tup = ( self._ro, self._go, self._bo ) + self.hex = self.rgb_to_hex( rgb_tup ) + self.gColor = gtk.gdk.color_parse( self.hex ) + + + def init_gdk(self, col): + self.init_hex( col.get_html() ) + + + def init_hex(self, hex): + cTup = self.hex_to_rgb( hex ) + self.init_rgba( cTup[0], cTup[1], cTup[2], 255 ) + + + def get_int(self): + return int(self._a * 255) + (int(self._b * 255) << 8) + (int(self._g * 255) << 16) + (int(self._r * 255) << 24) + + + def rgb_to_hex(self, rgb_tup): + hexcolor = '#%02x%02x%02x' % rgb_tup + return hexcolor + + + def hex_to_rgb(self, h): + c = eval('0x' + h[1:]) + r = (c >> 16) & 0xFF + g = (c >> 8) & 0xFF + b = c & 0xFF + return (int(r), int(g), int(b)) + + + def getCanvasColor(self): + return str(self._ro) + "," + str(self._go) + "," + str(self._bo)
\ No newline at end of file diff --git a/constants.py b/constants.py new file mode 100644 index 0000000..37e07ba --- /dev/null +++ b/constants.py @@ -0,0 +1,127 @@ +import os +import gtk +from gettext import gettext as _ + +import sugar.graphics.style +from sugar.activity import activity +from sugar import profile + +from instance import Instance +from color import Color +import utils + +class Constants: + + VERSION = 2 + + SERVICE = "org.laptop.Map" + IFACE = SERVICE + PATH = "/org/laptop/Map" + activityId = None + + gfxPath = os.path.join(activity.get_bundle_path(), "gfx") + htmlPath = os.path.join(activity.get_bundle_path(), "html") + iconsPath = os.path.join(activity.get_bundle_path(), "icons") + + istrAnnotate = _("Edit") + istrSearch = _("Search") + istrSearchAddress = _('Find:') + istrSearchMedia = _("Tags:") + istrSaveSearch = _("Save Search") + istrConnecting = _("Connecting to Map Server") + istrZoomIn = _("Zoom In") + istrZoomOut = _("Zoom Out") + istrSaveSearch = _("Save") + istrDensity = _("Density") + istrSavedMap = _("Saved Map") + istrTagMap = _("Describe Map") + istrRemove = _("Remove Map") + istrCopyToClipboard = _("Copy to Clipboard") + istrAddMedia = _("Add Media") + istrAddInfo = _("Add Info") + istrDeleteMedia = _("Delete Media") + istrWebMedia = _("OSM import") + istrMeasure = _("Measure") + istrStaticMaps = _("Google import") + LineButton = _("Add Line") + PolyButton = _("Add Shape") + istrLatitude = _("Latitude:") + istrLongitude = ("Longitude:") + istrTags = ("Description:") + + TYPE_PHOTO = 0 + TYPE_VIDEO = 1 + + ui_dim_INSET = 4 + + recdAlbum = "map" + recdLat = "lat" + recdLng = "lng" + recdDatastoreId = "datastore" + recdInfo = "info" + recdMapItem = "mapItem" + recdSavedMapItem = "savedMap" + recdInfoMarker = "infoMarker" + recdIcon = "icon" + recdZoom = "zoom" + recdNotes = "notes" + recdMapImg = "mapImg" + recdTags = "tags" + recdMapThumbImg = "mapThumbImg" + recdRecdId = "recdId" + recdRecdLat = "recdLat" + recdRecdLng = "recdLng" + recdDensity = "density" + recdLine = "line" + lineID = "lid" + lineColor = "lcolor" + lineThick = "lthickness" + linePts = "lpts" + mapLat="lat" + mapLng="lng" + mapZoom="zoom" + + colorBlack = Color() + colorBlack.init_rgba( 0, 0, 0, 255 ) + colorWhite = Color() + colorWhite.init_rgba( 255, 255, 255, 255 ) + colorRed = Color() + colorRed.init_rgba( 255, 0, 0, 255) + colorGreen = Color() + colorGreen.init_rgba( 0, 255, 0, 255) + colorBlue = Color() + colorBlue.init_rgba( 0, 0, 255, 255) + colorGrey = Color() + colorGrey.init_gdk( sugar.graphics.style.COLOR_BUTTON_GREY ) + colorBg = colorBlack + + def __init__( self, ca ): + self.__class__.activityId = ca._activity_id + self.__class__.northImgClr, self.__class__.northImgBw = self.loadSvgImg('map-icon-croseN.svg') + self.__class__.southImgClr, self.__class__.southImgBw = self.loadSvgImg('map-icon-croseS.svg') + self.__class__.eastImgClr, self.__class__.eastImgBw = self.loadSvgImg('map-icon-croseE.svg') + self.__class__.westImgClr, self.__class__.westImgBw = self.loadSvgImg('map-icon-croseW.svg') + + infoOnSvgPath = os.path.join(self.__class__.iconsPath, 'corner-info.svg') + infoOnSvgFile = open(infoOnSvgPath, 'r') + infoOnSvgData = infoOnSvgFile.read() + self.__class__.infoOnSvg = utils.loadSvg(infoOnSvgData, None, None ) + infoOnSvgFile.close() + + def loadSvgImg(self, fileName): + SvgPath = os.path.join(self.__class__.iconsPath, fileName) + SvgFile = open(SvgPath, 'r') + SvgData = SvgFile.read() + SvgFile.close() + + ColorSvg = utils.loadSvg(SvgData, Instance.colorStroke.hex, Instance.colorFill.hex) + ColorPixBuf = ColorSvg.get_pixbuf() + ColorImg = gtk.Image() + ColorImg.set_from_pixbuf(ColorPixBuf) + + MonoSvg = utils.loadSvg(SvgData, self.__class__.colorGrey.hex, self.__class__.colorWhite.hex) + MonoPixBuf = MonoSvg.get_pixbuf() + MonoImg = gtk.Image() + MonoImg.set_from_pixbuf(MonoPixBuf) + + return [ColorImg, MonoImg] diff --git a/filepicker.py b/filepicker.py new file mode 100644 index 0000000..cabc295 --- /dev/null +++ b/filepicker.py @@ -0,0 +1,46 @@ +# Copyright (C) 2007, One Laptop Per Child +# Copyright (c) 2008, Media Modifications Ltd. +# +# 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 2 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, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +import gtk + +from sugar.graphics.objectchooser import ObjectChooser + +class FilePicker: + + def __init__(self): + pass + + + def show(self): + title = None + parent = None + file = None + job = None + chooser = ObjectChooser() + + try: + result = chooser.run() + if result == gtk.RESPONSE_ACCEPT: + jobject = chooser.get_selected_object() + if (jobject and jobject.file_path): + job = jobject + + finally: + chooser.destroy() + del chooser + + return job diff --git a/gplay.py b/gplay.py new file mode 100644 index 0000000..3ceb353 --- /dev/null +++ b/gplay.py @@ -0,0 +1,154 @@ +# Copyright (c) 2008, Media Modifications Ltd. + +#Permission is hereby granted, free of charge, to any person obtaining a copy +#of this software and associated documentation files (the "Software"), to deal +#in the Software without restriction, including without limitation the rights +#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +#copies of the Software, and to permit persons to whom the Software is +#furnished to do so, subject to the following conditions: + +#The above copyright notice and this permission notice shall be included in +#all copies or substantial portions of the Software. + +#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +#THE SOFTWARE. + +#look at jukeboxactivity.py + +import gtk +import pygtk +pygtk.require('2.0') +import sys +import pygst +pygst.require('0.10') +import gst +import gst.interfaces +import gobject +import time +gobject.threads_init() + +class Gplay: + + def __init__(self): + self.window = None + self.players = [] + self.playing = False + self.nextMovie() + + def nextMovie(self): + if ( len(self.players) > 0 ): + self.playing = False + self.getPlayer().set_property("video-sink", None) + self.getPlayer().get_bus().disconnect(self.SYNC_ID) + self.getPlayer().get_bus().remove_signal_watch() + self.getPlayer().get_bus().disable_sync_message_emission() + + player = gst.element_factory_make("playbin", "playbin") + xis = gst.element_factory_make("xvimagesink", "xvimagesink") + player.set_property("video-sink", xis) + bus = player.get_bus() + bus.enable_sync_message_emission() + bus.add_signal_watch() + self.SYNC_ID = bus.connect('sync-message::element', self.onSyncMessage) + self.players.append(player) + + + def getPlayer(self): + return self.players[len(self.players)-1] + + + def onSyncMessage(self, bus, message): + if message.structure is None: + return True + if message.structure.get_name() == 'prepare-xwindow-id': + self.window.set_sink(message.src) + message.src.set_property('force-aspect-ratio', True) + return True + + + def setLocation(self, location): + print("setLocation: ", location ) + if (self.getPlayer().get_property('uri') == location): + self.seek(gst.SECOND*0) + return + + self.getPlayer().set_state(gst.STATE_READY) + self.getPlayer().set_property('uri', location) + ext = location[len(location)-3:] + if (ext == "jpg"): + self.pause() + print("all played?") + + + def queryPosition(self): + "Returns a (position, duration) tuple" + try: + position, format = self.getPlayer().query_position(gst.FORMAT_TIME) + except: + position = gst.CLOCK_TIME_NONE + + try: + duration, format = self.getPlayer().query_duration(gst.FORMAT_TIME) + except: + duration = gst.CLOCK_TIME_NONE + + return (position, duration) + + + def seek(self, location): + event = gst.event_new_seek(1.0, gst.FORMAT_TIME, gst.SEEK_FLAG_FLUSH | gst.SEEK_FLAG_ACCURATE, gst.SEEK_TYPE_SET, location, gst.SEEK_TYPE_NONE, 0) + res = self.getPlayer().send_event(event) + if res: + self.getPlayer().set_new_stream_time(0L) + + + def pause(self): + self.playing = False + self.getPlayer().set_state(gst.STATE_PAUSED) + + + def play(self): + self.playing = True + self.getPlayer().set_state(gst.STATE_PLAYING) + + + def stop(self): + self.playing = False + self.getPlayer().set_state(gst.STATE_NULL) + self.nextMovie() + + + def get_state(self, timeout=1): + return self.getPlayer().get_state(timeout=timeout) + + + def is_playing(self): + return self.playing + + + +class PlayVideoWindow(gtk.EventBox): + def __init__(self, bgd): + gtk.EventBox.__init__(self) + + self.imagesink = None + + self.modify_bg( gtk.STATE_NORMAL, bgd ) + self.modify_bg( gtk.STATE_INSENSITIVE, bgd ) + self.unset_flags(gtk.DOUBLE_BUFFERED) + self.set_flags(gtk.APP_PAINTABLE) + + + def set_sink(self, sink): + if (self.imagesink != None): + assert self.window.xid + self.imagesink = None + del self.imagesink + + self.imagesink = sink + self.imagesink.set_xwindow_id(self.window.xid)
\ No newline at end of file diff --git a/icons/add-icon.svg b/icons/add-icon.svg new file mode 100644 index 0000000..81e7c87 --- /dev/null +++ b/icons/add-icon.svg @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14948) --> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg version="1.1" id="Layer_5" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + width="74.5px" height="74px" viewBox="0 0 74.5 74" enable-background="new 0 0 74.5 74" xml:space="preserve"> +<g> + <path d="M75.084,54.702c0,10.692-8.748,19.44-19.439,19.44H19.19c-10.692,0-19.44-8.748-19.44-19.44V18.583 + c0-10.692,8.748-19.44,19.44-19.44h36.454c10.691,0,19.439,8.748,19.439,19.44V54.702z"/> +</g> +<g> + <g> + <g> + <path fill="none" stroke="#FFFFFF" stroke-width="3" stroke-linecap="round" d="M62.587,37.583 + c0-13.909-11.256-25.165-25.167-25.165S12.254,23.674,12.254,37.583c0,13.911,11.256,25.167,25.167,25.167 + S62.587,51.494,62.587,37.583z"/> + <path fill="none" stroke="#FFFFFF" stroke-width="3" stroke-linecap="round" d="M37.42,54.825 + c-9.537,0-17.241-7.705-17.241-17.242c0-9.535,7.704-17.24,17.241-17.24s17.241,7.705,17.241,17.24 + C54.661,47.12,46.957,54.825,37.42,54.825z"/> + </g> + </g> + <g> + <line fill="none" stroke="#FFFFFF" stroke-width="4" stroke-linecap="round" x1="26.448" y1="37.638" x2="48.613" y2="37.638"/> + <line fill="none" stroke="#FFFFFF" stroke-width="4" stroke-linecap="round" x1="37.532" y1="26.555" x2="37.532" y2="48.721"/> + </g> +</g> +</svg> diff --git a/icons/corner-info.svg b/icons/corner-info.svg new file mode 100644 index 0000000..9a22582 --- /dev/null +++ b/icons/corner-info.svg @@ -0,0 +1,15 @@ +<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd' [ + <!ENTITY stroke_color "#010101"> + <!ENTITY fill_color "#CDCCCC"> +]><svg enable-background="new 0 0 75 75" height="75px" version="1.1" viewBox="0 0 75 75" width="75px" x="0px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" y="0px"><g display="block" id="corner-info"> + <g display="inline"> + <rect height="75" width="75"/> + <g> + <polygon fill="&fill_color;" points="0,0 75,0 0,75 "/> + </g> + <g> + <path d="M22.34,35.03h2.534l-0.383,2.017h-8.436l3.051-15.704h-2.518l0.384-2.018h8.419L22.34,35.03z M19.923,14.308 c0.177-0.9,0.636-1.659,1.375-2.275c0.739-0.617,1.559-0.925,2.459-0.925c0.877,0,1.567,0.311,2.067,0.934 c0.367,0.456,0.55,0.99,0.55,1.601c0,0.21-0.022,0.433-0.066,0.667c-0.167,0.878-0.62,1.625-1.359,2.243 c-0.74,0.617-1.548,0.925-2.426,0.925c-0.9,0-1.6-0.306-2.101-0.917c-0.377-0.456-0.567-0.99-0.567-1.601 C19.856,14.747,19.879,14.53,19.923,14.308z"/> + </g> + <polyline fill="none" points="1,75 1,1 75,1 " stroke="#808080" stroke-width="2"/> + </g> +</g></svg>
\ No newline at end of file diff --git a/icons/delete-icon.svg b/icons/delete-icon.svg new file mode 100644 index 0000000..3c33ea9 --- /dev/null +++ b/icons/delete-icon.svg @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14948) --> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg version="1.1" id="Layer_5" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + width="74.5px" height="74px" viewBox="0 0 74.5 74" enable-background="new 0 0 74.5 74" xml:space="preserve"> +<g> + <path d="M75.084,54.702c0,10.692-8.748,19.44-19.439,19.44H19.19c-10.692,0-19.44-8.748-19.44-19.44V18.583 + c0-10.692,8.748-19.44,19.44-19.44h36.454c10.691,0,19.439,8.748,19.439,19.44V54.702z"/> +</g> +<g> + <g> + <g> + <path fill="none" stroke="#FFFFFF" stroke-width="3" stroke-linecap="round" d="M62.587,37.583 + c0-13.909-11.256-25.165-25.167-25.165S12.254,23.674,12.254,37.583c0,13.911,11.256,25.167,25.167,25.167 + S62.587,51.494,62.587,37.583z"/> + <path fill="none" stroke="#FFFFFF" stroke-width="3" stroke-linecap="round" d="M37.42,54.825 + c-9.537,0-17.241-7.705-17.241-17.242c0-9.535,7.704-17.24,17.241-17.24s17.241,7.705,17.241,17.24 + C54.661,47.12,46.957,54.825,37.42,54.825z"/> + </g> + </g> + <g> + <line fill="none" stroke="#FFFFFF" stroke-width="4" stroke-linecap="round" x1="26.448" y1="37.638" x2="48.613" y2="37.638"/> + </g> +</g> +</svg> diff --git a/icons/map-icon-croseE.svg b/icons/map-icon-croseE.svg new file mode 100644 index 0000000..907247e --- /dev/null +++ b/icons/map-icon-croseE.svg @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [ + <!ENTITY stroke_color "#231F20"> + <!ENTITY fill_color "#FFFFFF"> +]> +<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + width="28px" height="84.5px" viewBox="0 0 28 84.5" enable-background="new 0 0 28 84.5" xml:space="preserve"> + +<g> + <polygon fill="&fill_color;" stroke="&stroke_color;" stroke-width="3.5" points="25.8,42.5 1.8,78.9 1.8,42.5 1.8,5.9 "/> + <polygon fill="&stroke_color;" stroke="&stroke_color;" stroke-width="3.5" points="1.8,44.3 1.8,77.9 21.8,44.3 "/> +</g> +</svg> diff --git a/icons/map-icon-croseN.svg b/icons/map-icon-croseN.svg new file mode 100644 index 0000000..3b2bbaf --- /dev/null +++ b/icons/map-icon-croseN.svg @@ -0,0 +1,12 @@ +<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd' [ + <!ENTITY stroke_color "#010101"> + <!ENTITY fill_color "#FFFFFF"> +]> +<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + width="84.5px" height="28px" viewBox="0 0 84.5 28" enable-background="new 0 0 84.5 28" xml:space="preserve"> + +<g> + <polygon fill="&fill_color;" stroke="&stroke_color;" stroke-width="3.5" points="42.5,2.1 78.9,26.1 42.5,26.1 5.9,26.1 "/> + <polygon fill="&stroke_color;" stroke="&stroke_color;" stroke-width="3.5" points="44.3,26.1 77.9,26.1 44.3,6.1 "/> +</g> +</svg> diff --git a/icons/map-icon-croseS.svg b/icons/map-icon-croseS.svg new file mode 100644 index 0000000..3d9354d --- /dev/null +++ b/icons/map-icon-croseS.svg @@ -0,0 +1,13 @@ +<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd' [ + <!ENTITY stroke_color "#010101"> + <!ENTITY fill_color "#FFFFFF"> +]> + +<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + width="84.5px" height="28px" viewBox="0 0 84.5 28" enable-background="new 0 0 84.5 28" xml:space="preserve"> + +<g> + <polygon fill="&fill_color;" stroke="&stroke_color;" stroke-width="3.5" points="42.2,25.8 5.8,1.8 42.2,1.8 78.8,1.8 "/> + <polygon fill="&stroke_color;" stroke="&stroke_color;" stroke-width="3.5" points="40.4,1.8 6.8,1.8 40.4,21.8 "/> +</g> +</svg> diff --git a/icons/map-icon-croseW.svg b/icons/map-icon-croseW.svg new file mode 100644 index 0000000..714497a --- /dev/null +++ b/icons/map-icon-croseW.svg @@ -0,0 +1,12 @@ +<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd' [ + <!ENTITY stroke_color "#010101"> + <!ENTITY fill_color "#FFFFFF"> +]> +<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + width="28px" height="84.5px" viewBox="0 0 28 84.5" enable-background="new 0 0 28 84.5" xml:space="preserve"> + +<g> + <polygon fill="&fill_color;" stroke="&stroke_color;" stroke-width="3.5" points="2.1,42.2 26.1,5.8 26.1,42.2 26.1,78.8 "/> + <polygon fill="&stroke_color;" stroke="&stroke_color;" stroke-width="3.5" points="26.1,40.4 26.1,6.8 6,40.4 "/> +</g> +</svg> diff --git a/icons/map-icon-zoomIn.svg b/icons/map-icon-zoomIn.svg new file mode 100644 index 0000000..6c139f4 --- /dev/null +++ b/icons/map-icon-zoomIn.svg @@ -0,0 +1,95 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14948) --> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + version="1.1" + id="Layer_5" + x="0px" + y="0px" + width="74.5px" + height="74px" + viewBox="0 0 74.5 74" + enable-background="new 0 0 74.5 74" + xml:space="preserve" + sodipodi:version="0.32" + inkscape:version="0.46" + sodipodi:docname="map-icon-zoomIn.svg" + inkscape:output_extension="org.inkscape.output.svg.inkscape"><metadata + id="metadata16"><rdf:RDF><cc:Work + rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs + id="defs14"><inkscape:perspective + sodipodi:type="inkscape:persp3d" + inkscape:vp_x="0 : 37 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_z="74.5 : 37 : 1" + inkscape:persp3d-origin="37.25 : 24.666667 : 1" + id="perspective18" /> + + + + + + </defs><sodipodi:namedview + inkscape:window-height="718" + inkscape:window-width="1366" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + guidetolerance="10.0" + gridtolerance="10.0" + objecttolerance="10.0" + borderopacity="1.0" + bordercolor="#666666" + pagecolor="#ffffff" + id="base" + showgrid="false" + inkscape:zoom="6.2432432" + inkscape:cx="37.25" + inkscape:cy="24.186147" + inkscape:window-x="-8" + inkscape:window-y="-8" + inkscape:current-layer="Layer_5" /> +<circle + sodipodi:ry="30" + sodipodi:rx="30" + sodipodi:cy="37.18" + sodipodi:cx="37.026001" + id="circle5" + r="30" + cy="37.18" + cx="37.026001" /> +<path + sodipodi:type="arc" + style="fill:none;stroke:#0000ff;stroke-width:6.53190851;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path2399" + sodipodi:cx="42.365803" + sodipodi:cy="28.190475" + sodipodi:rx="15.937229" + sodipodi:ry="17.619047" + d="M 58.303032,28.190475 A 15.937229,17.619047 0 1 1 26.428574,28.190475 A 15.937229,17.619047 0 1 1 58.303032,28.190475 z" + transform="matrix(1.2877593,0,0,1.1648369,-13.31237,-0.2420693)" /><rect + style="fill:#0000ff;fill-opacity:1;stroke:#0000ff;stroke-width:10.37449551;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="rect3175" + width="1.2975725" + height="9.29566" + x="54.576096" + y="29.742977" + transform="matrix(0.8590348,0.5119173,-0.6668522,0.74519,0,0)" /><line + x1="41.510841" + y1="14.695152" + x2="41.510841" + y2="50.695152" + id="line9" + style="fill:none;stroke:#ffffff;stroke-width:4" /><line + x1="58.645912" + y1="33.015495" + x2="24.375782" + y2="33.015495" + id="line11" + style="fill:none;stroke:#ffffff;stroke-width:3.90271306" /></svg>
\ No newline at end of file diff --git a/icons/map-icon-zoomOut.svg b/icons/map-icon-zoomOut.svg new file mode 100644 index 0000000..712f350 --- /dev/null +++ b/icons/map-icon-zoomOut.svg @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14948) --> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg version="1.1" id="Layer_5" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + width="74.5px" height="74px" viewBox="0 0 74.5 74" enable-background="new 0 0 74.5 74" xml:space="preserve"> +<g> + <circle cx="37.026" cy="37.18" r="30"/> + <g> + <line fill="none" stroke="#FFFFFF" stroke-width="4" x1="55.026" y1="37.18" x2="19.026" y2="37.18"/> + </g> +</g> +</svg> diff --git a/icons/measure-icon.svg b/icons/measure-icon.svg new file mode 100644 index 0000000..cd21013 --- /dev/null +++ b/icons/measure-icon.svg @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" + "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [ + <!ENTITY fill_color "#AAAAAA"> + <!ENTITY stroke_color "#000000"> +]> +<svg xmlns="http://www.w3.org/2000/svg" width="45" height="45"> + <rect x="13" y="3" width="20" height="40" style="fill:&fill_color;;stroke:&stroke_color;;stroke-width:3.0"/> + <line y1="13" y2="13" x1="25" x2="33" style="fill:&fill_color;;stroke:&stroke_color;;stroke-width:3.0"/> + <line y1="23" y2="23" x1="25" x2="33" style="fill:&fill_color;;stroke:&stroke_color;;stroke-width:3.0"/> + <line y1="33" y2="33" x1="25" x2="33" style="fill:&fill_color;;stroke:&stroke_color;;stroke-width:3.0"/> +</svg> diff --git a/icons/save-search.svg b/icons/save-search.svg new file mode 100644 index 0000000..22b18bf --- /dev/null +++ b/icons/save-search.svg @@ -0,0 +1,63 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14948) --> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://web.resource.org/cc/" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + version="1.1" + id="Layer_1" + x="0px" + y="0px" + width="76px" + height="76px" + viewBox="0 0 76 76" + enable-background="new 0 0 76 76" + xml:space="preserve" + sodipodi:version="0.32" + inkscape:version="0.45.1" + sodipodi:docname="save-search.svg" + sodipodi:docbase="/root/Desktop" + inkscape:output_extension="org.inkscape.output.svg.inkscape"><metadata + id="metadata2230"><rdf:RDF><cc:Work + rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs + id="defs2228" /><sodipodi:namedview + inkscape:window-height="622" + inkscape:window-width="872" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + guidetolerance="10.0" + gridtolerance="10.0" + objecttolerance="10.0" + borderopacity="1.0" + bordercolor="#666666" + pagecolor="#ffffff" + id="base" + inkscape:zoom="5.3552632" + inkscape:cx="38" + inkscape:cy="38" + inkscape:window-x="127" + inkscape:window-y="103" + inkscape:current-layer="Layer_1" /> +<g + id="g2221"> + <path + d="M75.946,56.313c0,10.692-8.748,19.44-19.44,19.44H20.053c-10.692,0-19.44-8.748-19.44-19.44v-36.12 c0-10.692,8.748-19.44,19.44-19.44h36.453c10.692,0,19.44,8.748,19.44,19.44V56.313z" + id="path2223" /> +</g> + +<g + display="block" + id="share-link" + transform="matrix(1.2,0,0,1.2,5.6048,4.5697942)" + style="display:block"> + <polygon + display="inline" + points="27.5,7.266 34.074,20.588 48.774,22.723 38.138,33.092 40.647,47.734 27.5,40.82 14.353,47.734 16.862,33.092 6.226,22.723 20.926,20.588 27.5,7.266 " + id="polygon5" + style="fill:#ffffff;stroke:#010101;stroke-width:3.5;display:inline" /> +</g></svg>
\ No newline at end of file diff --git a/icons/static-icon.svg b/icons/static-icon.svg new file mode 100644 index 0000000..2bf143b --- /dev/null +++ b/icons/static-icon.svg @@ -0,0 +1,16 @@ +<?xml version="1.0"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"[ + <!ENTITY stroke_color "#FFFFFF"> + <!ENTITY fill_color "#010101"> +]> +<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + width="74.5px" height="74px" viewBox="0 0 74.5 74" enable-background="new 0 0 74.5 74" xml:space="preserve"> +<g> + <line fill="none" stroke="&stroke_color;" stroke-width="3.5" x1="37.124" y1="13.641" x2="37.124" y2="23.035"/> + <path fill="none" stroke="&stroke_color;" stroke-width="3.5" d="M43.213,29.176c0,3.406-2.762,6.168-6.166,6.168 + c-3.406,0-6.167-2.762-6.167-6.168c0-3.404,2.761-6.141,6.167-6.141C40.451,23.035,43.213,25.771,43.213,29.176z M21.735,59.275 + L34.298,34.5"/> + <line fill="none" stroke="&stroke_color;" stroke-width="3.5" x1="27.88" y1="47.344" x2="54.881" y2="47.344"/> + <line fill="none" stroke="&stroke_color;" stroke-width="3.5" x1="39.949" y1="34.434" x2="52.546" y2="59.275"/> +</g> +</svg> diff --git a/icons/tool-marquee-freeform.svg b/icons/tool-marquee-freeform.svg new file mode 100644 index 0000000..2e02329 --- /dev/null +++ b/icons/tool-marquee-freeform.svg @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14948) --> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="55px" + height="55px" viewBox="0 0 55 55" enable-background="new 0 0 55 55" xml:space="preserve"> + +<g id="Freeform_Marquee" > + <path display="inline" fill="none" stroke="#FFFFFF" stroke-width="2.25" stroke-dasharray="6" d="M4.434,48 + c0,0,3.453-38.583,19-41.833s28.334,18.5,28.334,18.5L34.434,33"/> + <g display="inline"> + <path fill="#FFFFFF" d="M45.368,43.971l-5.382-5.383h3.642c0.458,0,0.918-0.174,1.267-0.524c0.701-0.7,0.7-1.836,0-2.535 + c-0.351-0.351-0.811-0.525-1.267-0.526h-9.766l0.001,9.765c0,0.458,0.176,0.916,0.525,1.267c0.7,0.701,1.836,0.701,2.535,0 + c0.351-0.35,0.524-0.809,0.524-1.267v-3.644l5.385,5.384c0.324,0.323,0.772,0.524,1.268,0.525 + c0.99-0.001,1.793-0.804,1.793-1.794C45.894,44.742,45.691,44.296,45.368,43.971z"/> + </g> +</g> + +</svg> diff --git a/icons/tool-polygon.svg b/icons/tool-polygon.svg new file mode 100644 index 0000000..0c8fee6 --- /dev/null +++ b/icons/tool-polygon.svg @@ -0,0 +1,59 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14948) --> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://web.resource.org/cc/" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + version="1.1" + x="0px" + y="0px" + width="55px" + height="55px" + viewBox="0 0 55 55" + enable-background="new 0 0 55 55" + xml:space="preserve" + id="svg2" + sodipodi:version="0.32" + inkscape:version="0.45" + sodipodi:docname="tool-shape-polygon.svg" + sodipodi:docbase="/home/joy/oficina_sugar/oficina/icons" + inkscape:output_extension="org.inkscape.output.svg.inkscape" + sodipodi:modified="true"><metadata + id="metadata10"><rdf:RDF><cc:Work + rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs + id="defs8" /><sodipodi:namedview + inkscape:window-height="941" + inkscape:window-width="1269" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + guidetolerance="10.0" + gridtolerance="10.0" + objecttolerance="10.0" + borderopacity="1.0" + bordercolor="#666666" + pagecolor="#ffffff" + id="base" + inkscape:zoom="6.8909091" + inkscape:cx="27.5" + inkscape:cy="27.5" + inkscape:window-x="0" + inkscape:window-y="27" + inkscape:current-layer="svg2" /> + +<g + id="Polygon" + style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-opacity:1"> + <polygon + display="inline" + fill="#FFFFFF" + points="16.834,46.58 5.83,27.522 16.834,8.464 38.841,8.464 49.844,27.522 38.841,46.58 " + id="polygon5" + style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-opacity:1" /> +</g> + +</svg>
\ No newline at end of file diff --git a/icons/tool-shape-line.svg b/icons/tool-shape-line.svg new file mode 100644 index 0000000..0bf4d8e --- /dev/null +++ b/icons/tool-shape-line.svg @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14948) --> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="55px" + height="55px" viewBox="0 0 55 55" enable-background="new 0 0 55 55" xml:space="preserve"> + +<g id="Line" > + <line display="inline" fill="none" stroke="#FFFFFF" stroke-width="2.25" x1="8.708" y1="47.167" x2="48.042" y2="7.833"/> +</g> + +</svg> diff --git a/icons/topo-icon.svg b/icons/topo-icon.svg new file mode 100644 index 0000000..18a25d6 --- /dev/null +++ b/icons/topo-icon.svg @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14948) --> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + width="75.667px" height="75.333px" viewBox="0 0 75.667 75.333" enable-background="new 0 0 75.667 75.333" xml:space="preserve"> +<g> + <path d="M75.828,55.925c0,10.692-8.748,19.44-19.439,19.44H19.935c-10.692,0-19.44-8.748-19.44-19.44V19.806 + c0-10.692,8.748-19.44,19.44-19.44h36.454c10.691,0,19.439,8.748,19.439,19.44V55.925z"/> +</g> +<g> + <path fill="none" stroke="#FFFFFF" stroke-width="3" stroke-linecap="round" d="M44.936,43.372 + c-2.121,4.811-6.641,7.477-10.094,5.953c-3.454-1.522,1.952-3.797,4.073-8.608c2.121-4.811,0.156-10.337,3.609-8.813 + C45.979,33.427,47.058,38.562,44.936,43.372z"/> + <path fill="none" stroke="#FFFFFF" stroke-width="3" stroke-linecap="round" d="M50.594,45.6 + c-3.481,7.896-13.633,11.064-22.676,7.077c-9.041-3.987,3.43-6.133,6.911-14.028c3.481-7.896-3.344-18.551,5.696-14.563 + C49.568,28.072,54.075,37.705,50.594,45.6z"/> + <path fill="none" stroke="#FFFFFF" stroke-width="3" stroke-linecap="round" d="M58.834,49.5 + c-4.625,10.486-21.741,13.091-38.235,5.818c-16.491-7.272,4.856-8.013,9.479-18.498s-9.226-26.746,7.265-19.474 + C53.837,24.619,63.457,39.015,58.834,49.5z"/> +</g> +</svg> diff --git a/icons/web-icon.svg b/icons/web-icon.svg new file mode 100644 index 0000000..d5e7e59 --- /dev/null +++ b/icons/web-icon.svg @@ -0,0 +1,14 @@ +<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd' [ + <!ENTITY stroke_color "#010101"> + <!ENTITY fill_color "#FFFFFF"> +]><svg enable-background="new 0 0 55 55" height="55px" version="1.1" viewBox="0 0 55 55" width="55px" x="0px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" y="0px"><g display="block" id="activity-browse"> + <circle cx="27.375" cy="27.5" display="inline" fill="&fill_color;" r="19.903" stroke="&stroke_color;" stroke-width="3.5"/> + <g display="inline"> + <path d="M27.376,7.598c0,0-11.205,8.394-11.205,19.976 c0,11.583,11.205,19.829,11.205,19.829" fill="&fill_color;" stroke="&stroke_color;" stroke-width="3.5"/> + <path d="M27.376,7.598c0,0,11.066,9.141,11.066,19.976 c0,10.839-11.066,19.829-11.066,19.829" fill="&fill_color;" stroke="&stroke_color;" stroke-width="3.5"/> + <line fill="&fill_color;" stroke="&stroke_color;" stroke-width="3.5" x1="27.376" x2="27.376" y1="7.598" y2="47.402"/> + <line fill="&fill_color;" stroke="&stroke_color;" stroke-width="3.5" x1="27.376" x2="27.376" y1="7.598" y2="47.402"/> + <line fill="&fill_color;" stroke="&stroke_color;" stroke-width="3.5" x1="27.376" x2="27.376" y1="7.598" y2="47.402"/> + <line fill="&fill_color;" stroke="&stroke_color;" stroke-width="3.5" x1="7.472" x2="47.278" y1="27.5" y2="27.5"/> + </g> +</g></svg>
\ No newline at end of file diff --git a/idlethread.py b/idlethread.py new file mode 100644 index 0000000..7dc424e --- /dev/null +++ b/idlethread.py @@ -0,0 +1,175 @@ +from __future__ import generators + +import gobject +import time +import traceback + +class GIdleThread(object): + """This is a pseudo-"thread" for use with the GTK+ main loop. + + This class does act a bit like a thread, all code is executed in + the callers thread though. The provided function should be a generator + (or iterator). + + It can be started with start(). While the "thread" is running is_alive() + can be called to see if it's alive. wait([timeout]) will wait till the + generator is finished, or timeout seconds. + + If an exception is raised from within the generator, it is stored in + the error property. Execution of the generator is finished. + + Note that this routine runs in the current thread, so there is no need + for nasty locking schemes. + + Example (runs a counter through the GLib main loop routine): + >>> def counter(max): for x in xrange(max): yield x + >>> t = GIdleThread(counter(123)) + >>> t.start() + >>> while gen.is_alive(): + ... main.iteration(False) + """ + + def __init__(self, generator, queue=None): + assert hasattr(generator, 'next'), 'The generator should be an iterator' + self._generator = generator + self._queue = queue + self._idle_id = 0 + self._error = None + + def start(self, priority=gobject.PRIORITY_LOW): + """Start the generator. Default priority is low, so screen updates + will be allowed to happen. + """ + idle_id = gobject.idle_add(self.__generator_executer, + priority=priority) + self._idle_id = idle_id + return idle_id + + def wait(self, timeout=0): + """Wait until the corouine is finished or return after timeout seconds. + This is achieved by running the GTK+ main loop. + """ + clock = time.clock + start_time = clock() + main = gobject.main_context_default() + while self.is_alive(): + main.iteration(False) + if timeout and (clock() - start_time >= timeout): + return + + def interrupt(self): + """Force the generator to stop running. + """ + if self.is_alive(): + gobject.source_remove(self._idle_id) + self._idle_id = 0 + + def is_alive(self): + """Returns True if the generator is still running. + """ + return self._idle_id != 0 + + error = property(lambda self: self._error, + doc="Return a possible exception that had occured "\ + "during execution of the generator") + + def __generator_executer(self): + try: + result = self._generator.next() + if self._queue: + try: + self._queue.put(result) + except QueueFull: + self.wait(0.5) + # If this doesn't work... + self._queue.put(result) + return True + except StopIteration: + self._idle_id = 0 + return False + except Exception, e: + self._error = e + traceback.print_exc() + self._idle_id = 0 + return False + + +class QueueEmpty(Exception): + """Exception raised whenever the queue is empty and someone tries to fetch + a value. + """ + pass + + +class QueueFull(Exception): + """Exception raised when the queue is full and the oldest item may not be + disposed. + """ + pass + + +class Queue(object): + """A FIFO queue. If the queue has a max size, the oldest item on the + queue is dropped if that size id exceeded. + """ + + def __init__(self, size=0, dispose_oldest=True): + self._queue = [] + self._size = size + self._dispose_oldest = dispose_oldest + + def put(self, item): + """Put item on the queue. If the queue size is limited ... + """ + if self._size > 0 and len(self._queue) >= self._size: + if self._dispose_oldest: + self.get() + else: + raise QueueFull + + self._queue.insert(0, item) + + def get(self): + """Get the oldest item off the queue. + QueueEmpty is raised if no items are left on the queue. + """ + try: + return self._queue.pop() + except IndexError: + raise QueueEmpty + + +if __name__ == '__main__': + def counter(max): + for i in range(max): + yield i + + def shower(queue): + # Never stop reading the queue: + while True: + try: + cnt = queue.get() + print 'cnt =', cnt + except QueueEmpty: + pass + yield None + + print 'Test 1: (should print range 0..22)' + queue = Queue() + c = GIdleThread(counter(23), queue) + s = GIdleThread(shower(queue)) + + main = gobject.main_context_default() + c.start() + s.start() + s.wait(2) + + print 'Test 2: (should only print 22)' + queue = Queue(size=1) + c = GIdleThread(counter(23), queue) + s = GIdleThread(shower(queue)) + + main = gobject.main_context_default() + c.start(priority=gobject.PRIORITY_DEFAULT) + s.start() + s.wait(3) diff --git a/instance.py b/instance.py new file mode 100644 index 0000000..95fb932 --- /dev/null +++ b/instance.py @@ -0,0 +1,45 @@ +import os + +from sugar import profile +from sugar import util +from sugar.activity import activity +import shutil + +from color import Color + +class Instance: + key = profile.get_pubkey() + #joyride ... + #keyHash = util.sha_data(key) + #8.2... + #keyHash = util._sha_data(key) + #keyHashPrintable = util.printable_hash(keyHash) + nickName = profile.get_nick_name() + + colorFill = Color() + colorFill.init_hex( profile.get_color().get_fill_color() ) + colorStroke = Color() + colorStroke.init_hex( profile.get_color().get_stroke_color() ) + + instanceId = None + instancePath = None + dataPath = None + + def __init__(self, ca): + self.__class__.instanceId = ca._activity_id + + self.__class__.instancePath = os.path.join(ca.get_activity_root(), "instance") + recreateTmp() + + self.__class__.dataPath = os.path.join(ca.get_activity_root(), "data") + recreateData() + + +def recreateTmp(): + if (not os.path.exists(Instance.instancePath)): + os.makedirs(Instance.instancePath) + + +def recreateData(): + if (not os.path.exists(Instance.dataPath)): + os.makedirs(Instance.dataPath)
\ No newline at end of file diff --git a/locale/es/LC_MESSAGES/org.laptop.map.mo b/locale/es/LC_MESSAGES/org.laptop.map.mo Binary files differnew file mode 100644 index 0000000..ea90e33 --- /dev/null +++ b/locale/es/LC_MESSAGES/org.laptop.map.mo diff --git a/locale/es/activity.linfo b/locale/es/activity.linfo new file mode 100644 index 0000000..b8f1dd2 --- /dev/null +++ b/locale/es/activity.linfo @@ -0,0 +1,2 @@ +[Activity] +name = Mapa diff --git a/locale/mn/LC_MESSAGES/org.laptop.map.mo b/locale/mn/LC_MESSAGES/org.laptop.map.mo Binary files differnew file mode 100644 index 0000000..2ef674c --- /dev/null +++ b/locale/mn/LC_MESSAGES/org.laptop.map.mo diff --git a/locale/mn/activity.linfo b/locale/mn/activity.linfo new file mode 100644 index 0000000..eadffb2 --- /dev/null +++ b/locale/mn/activity.linfo @@ -0,0 +1,2 @@ +[Activity] +name = Map diff --git a/logic.py b/logic.py new file mode 100644 index 0000000..6d135a5 --- /dev/null +++ b/logic.py @@ -0,0 +1,272 @@ +# Copyright (c) 2008, Media Modifications Ltd. + +#Permission is hereby granted, free of charge, to any person obtaining a copy +#of this software and associated documentation files (the "Software"), to deal +#in the Software without restriction, including without limitation the rights +#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +#copies of the Software, and to permit persons to whom the Software is +#furnished to do so, subject to the following conditions: + +#The above copyright notice and this permission notice shall be included in +#all copies or substantial portions of the Software. + +#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +#THE SOFTWARE. + + +from result import ServerResult +from constants import Constants +from instance import Instance + +from threading import Thread +import threading +import os +import gobject +import time +import gtk +import urllib + +class ServerLogic: + def __init__(self, ca): + self.ca = ca + self.proceedTxt = "" + self.proceedHeaders = [] + self.cond = ca.cond + self.addKMLSet=0 + + def doServerLogic(self, url, path, params): + self.ca.remoteServerActive( True ) + r = ServerResult() + fileName = path[len(path)-1] + + if (fileName == "comet.js"): + + #clear... + self.proceedHeaders = [] + self.proceedTxt = "" + + #wait... + self.cond.acquire() + self.cond.wait() + self.cond.release() + + #prep response... + for h in range( len(self.proceedHeaders) ): + r.headers.append( self.proceedHeaders[h] ) + r.txt = ""+self.proceedTxt + + else: + kickThroughComet = True + + if (fileName =="mediaQuery.js"): + self.proceedHeaders.append( ("Content-type", "text/javascript") ) + self.proceedTxt = self.ca.m.getMediaResponse( params[0][1], params[1][1], params[2][1], params[3][1] ) + + elif (fileName == "showMedia.js"): + id = params[0][1] + locX = params[1][1] + locY = params[2][1] + up = params[3][1] + rt = params[4][1] + gobject.idle_add(self.ca.showMedia, id, locX, locY, up=='true', rt=='true') + self.proceedHeaders.append( ("Content-type", "text/javascript") ) + + elif (fileName == "placeAddMedia.js"): + lat = params[0][1] + lng = params[1][1] + gobject.idle_add(self.ca.placeAddMedia, lat, lng) + self.proceedHeaders.append( ("Content-type", "text/javascript") ) + kickThroughComet = False + + elif (fileName == "hideMedia.js"): + gobject.idle_add(self.ca.hideMedia) + + elif (fileName == "getImage.js"): + localfile = open(os.path.join(Instance.instancePath, params[0][1]), 'r') + localdata = localfile.read() + localfile.close() + + #one day we might need to kick you through comet as a base64'd image. + r.txt = localdata + r.headers.append( ("Content-type", "image/jpeg") ) + kickThroughComet = False + + elif (fileName == "updateLocation.js"): + lat = params[0][1] + lng = params[1][1] + zoom = params[2][1] + x = params[3][1] + y = params[4][1] + gobject.idle_add(self.ca.updateMapMetaData,lat,lng,zoom,x,y) + + elif (fileName == "addSavedMap.js"): + # allow internet to send an array of SavedMaps back to map.py + latitudes = params[0][1] + longitudes = params[1][1] + zooms = params[2][1] + notes = params[3][1] + gobject.idle_add(self.ca.addSavedMap,latitudes,longitudes,zooms,urllib.unquote(notes),True) + + elif (fileName == "addInfoMarker.js"): + lat = params[0][1] + lng = params[1][1] + info = params[2][1] + icon = params[3][1] + if(params[4][1] == "True"): + isNew = True + else: + isNew = False + gobject.idle_add(self.ca.addInfoMarker,lat,lng,info,icon,isNew) + + elif (fileName == "addLine.js"): + id = params[0][1] + color = params[1][1] + thickness = params[2][1] + pts = params[3][1] # send pts separated with | instead of , + gobject.idle_add(self.ca.addLine,id,color,thickness,pts) + + elif (fileName == "joinGroup.js"): + groupName = params[0][1] + groupFile = urllib.urlopen("http://bluebird-science.appspot.com/group/data?name=" + groupName) + self.ca.preComet() + self.handleGroupData(groupFile.readline()) + self.ca.postComet() + + elif (fileName == "promptSearch.js"): + address = params[0][1] + time.sleep(0.5) + self.ca.preComet() + self.handleAddressUpdate(address+"+") + self.ca.postComet() + + #elif (fileName == "gotoMapV3.js"): + # button on static maps links to mapv3 + #self.ca.loadMapV3() + + if (kickThroughComet): + #not sure how & why this goes out, but it does. + self.cond.acquire() + self.cond.notifyAll() + self.cond.release() + time.sleep(.1) + + return r + + def handleAddressUpdate( self, address ): + self.proceedHeaders.append( ("Content-type", "text/javascript") ) + self.proceedTxt = "moveTo(" + address + ");" + self.ca.browser.load_uri("javascript:"+self.proceedTxt+"void(0);") + + def handleGroupData( self, groupData ): + self.proceedHeaders.append( ("Content-type", "text/javascript") ) + self.proceedTxt = "joinThisGroup(" + groupData + ");" + self.ca.browser.load_uri("javascript:"+self.proceedTxt+"void(0);") + + def handleCompassUpdate( self, dir ): + self.proceedHeaders.append( ("Content-type", "text/javascript") ) + + if (dir == "e"): + self.proceedTxt = "dirEast();" + elif (dir == "w"): + self.proceedTxt = "dirWest();" + elif (dir == "n"): + self.proceedTxt = "dirNorth();" + elif (dir == "s"): + self.proceedTxt = "dirSouth();" + else: + # use this as a print warning window + self.proceedTxt = 'showInfo("' + dir + '");' + self.ca.browser.load_uri("javascript:"+self.proceedTxt+"void(0);") + + def handleZoomUpdate( self, dir ): + self.proceedHeaders.append( ("Content-type", "text/javascript") ) + if (dir == "+"): + self.proceedTxt = "zoomIn();" + elif (dir == "-"): + self.proceedTxt = "zoomOut();" + self.ca.browser.load_uri("javascript:"+self.proceedTxt+"void(0);") + + def handleClear( self ): + self.proceedHeaders.append( ("Content-type", "text/javascript") ) + self.proceedTxt = "clear();" + self.ca.browser.load_uri("javascript:"+self.proceedTxt+"void(0);") + + def handlePreAdd( self ): + self.proceedHeaders.append( ("Content-type", "text/javascript") ) + self.proceedTxt = "preAddMedia();" + self.ca.browser.load_uri("javascript:"+self.proceedTxt+"void(0);") + + def handlePreAddInfo( self ): + self.proceedHeaders.append( ("Content-type", "text/javascript") ) + self.proceedTxt = "preAddInfo();" + self.ca.browser.load_uri("javascript:"+self.proceedTxt+"void(0);") + + def handlePostAdd( self, rec ): + self.proceedHeaders.append( ("Content-type", "text/javascript") ) + self.proceedTxt = "postAddMedia(" + rec.latitude + ", " + rec.longitude + ", '" + rec.getThumbUrl() + "', '" + rec.getThumbBasename() + "', '" + rec.tags + "');" + self.ca.browser.load_uri("javascript:"+self.proceedTxt+"void(0);") + + def handleDelete( self ): + self.proceedHeaders.append( ("Content-type", "text/javascript") ) + self.proceedTxt = "deleteMedia();" + self.ca.browser.load_uri("javascript:"+self.proceedTxt+"void(0);") + + # handle a map that was sent to us + def handleReceivedMap( self, lat, lng, zoom): + self.proceedHeaders.append( ("Content-type", "text/javascript") ) + self.proceedTxt = "setMap(" + lat + "," + lng + "," + zoom + ");" + self.ca.browser.load_uri("javascript:"+self.proceedTxt+"void(0);") + + def handleSavedMap( self, lat, lng, zoom, info ): + self.proceedHeaders.append( ("Content-type", "text/javascript") ) + if(info.find("Describe the map") != 0): + self.proceedTxt = "setMap2(" + lat + "," + lng + "," + zoom + ",'" + urllib.quote(info) + "');" + else: + self.proceedTxt = "setMap2(" + lat + "," + lng + "," + zoom + ",'');" + self.ca.browser.load_uri("javascript:"+self.proceedTxt+"void(0);") + + # handle a marker that was sent to us + def handleAddMarker( self, lat, lng, pixString, icon ): + if(self.addKMLSet == 0): + self.proceedHeaders.append( ("Content-type", "text/javascript") ) + self.proceedTxt = "" + self.addKMLSet = 1 + self.proceedTxt = self.proceedTxt + "addPt(" + lat + ", " + lng + ", '" + pixString + "', '" + icon + "',false);" + self.ca.browser.load_uri("javascript:"+self.proceedTxt+"void(0);") + + def mapPaste( self, type ): + self.proceedHeaders.append( ("Content-type", "text/javascript") ) + self.proceedTxt = "mapPaste('" + type + "');" + self.ca.browser.load_uri("javascript:"+self.proceedTxt+"void(0);") + + def handleEndKML( self ): + self.addKMLSet = 0 + + def lineMode(self, type): + self.proceedHeaders.append( ("Content-type", "text/javascript") ) + self.proceedTxt = "lineMode('" + type + "');" + self.ca.browser.load_uri("javascript:"+self.proceedTxt+"void(0);") + + def handleLine(self,id,color,thickness,pts): + if(self.addKMLSet == 0): + self.proceedHeaders.append( ("Content-type", "text/javascript") ) + self.proceedTxt = "" + self.addKMLSet = 1 + self.proceedTxt = self.proceedTxt + "addLine('" + id + "','" + color + "','" + thickness + "','" + pts + "');" + self.ca.browser.load_uri("javascript:"+self.proceedTxt+"void(0);") + + # handle start of measure tool + def handleMeasure(self): + self.proceedHeaders.append( ("Content-type", "text/javascript") ) + self.proceedTxt = "measure();" + self.ca.browser.load_uri("javascript:"+self.proceedTxt+"void(0);") + + def handleTagSearch( self, tags ): + self.proceedHeaders.append( ("Content-type", "text/javascript") ) + self.proceedTxt = "filterTags('" + tags + "');" + self.ca.browser.load_uri("javascript:"+self.proceedTxt+"void(0);")
\ No newline at end of file @@ -0,0 +1,1675 @@ +#!/usr/bin/env python + +# Offline Map Activity : http://wiki.laptop.org/go/User:Ndoiron/OfflineMap +# All code open-source under the MIT license +# +# based on Map Version 3 +# Original Map code copyright (c) 2008, Media Modifications Ltd. + +#Permission is hereby granted, free of charge, to any person obtaining a copy +#of this software and associated documentation files (the "Software"), to deal +#in the Software without restriction, including without limitation the rights +#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +#copies of the Software, and to permit persons to whom the Software is +#furnished to do so, subject to the following conditions: + +#The above copyright notice and this permission notice shall be included in +#all copies or substantial portions of the Software. + +#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +#THE SOFTWARE. + +import gtk +import gobject +import os +import threading +from threading import * +import time +import hippo +import shutil +import urllib +import random + +from sugar.graphics.toggletoolbutton import ToggleToolButton +from sugar.graphics.toolbutton import ToolButton +from sugar.activity import activity +from sugar.graphics import style + +from filepicker import FilePicker + +from constants import Constants +from webviewer import WebViewer +from server import Server +from logic import ServerLogic +from result import ServerResult +from instance import Instance +from constants import Constants +from model import Model +from tray import HTray +from photocanvas import PhotoCanvas +from p5 import P5 +from gplay import Gplay +from gplay import PlayVideoWindow +import serialize +import utils +from button import SavedButton +from savedmap import SavedMap +import _camera + +# sharing +import telepathy +from dbus.service import method, signal +from dbus.gobject_service import ExportedGObject +from sugar.graphics.alert import NotifyAlert +from sugar.presence import presenceservice +from sugar.presence.tubeconn import TubeConnection + +SERVICE = "org.laptop.map" +IFACE = SERVICE +PATH = "/org/laptop/map" + +imgWidth = 180 +imgHeight = 135 +webWidth = imgWidth*5 +webHeight = imgHeight*4 + +class Map(activity.Activity): + + #temp values until they get assigned + cometPort = 8889 + ajaxPort = 8890 + + initLat = '42.9' + initLng = '-71.0' + initZoom = '6' + + popW = 500 + popH = 375 + popB = 5 + + def __init__(self, handle): + activity.Activity.__init__(self, handle) + Instance(self) + Constants(self) + self.modify_bg( gtk.STATE_NORMAL, Constants.colorBg.gColor ) + gobject.idle_add(self._initme, None) + + def _initme( self, userdata=None ): + self.basePath = activity.get_bundle_path() + self.htmlPath = os.path.join(self.basePath, "mapviewer") + self.libPath = "/home/olpc/Library/MapPack/" + self.m = Model(self) + self.cond = Condition() + + self.SAVING_SEARCH = False + self.shownSave = None + self.shownRecd = None + #these get updated whenever we hear back from the server + self.NOW_MAP_CENTER_LAT = self.__class__.initLat + self.NOW_MAP_CENTER_LNG = self.__class__.initLng + self.NOW_MAP_ZOOM = self.__class__.initZoom + self.NOW_MAP_TAGS = "" + + #calc unique ports + h = hash(Instance.instanceId) + self.__class__.cometPort = 1024 + (h%32255) * 2 + self.__class__.ajaxPort = self.__class__.cometPort + 1 + + #ui + self.windowStack = [] + + self.toolbox = activity.ActivityToolbox(self) + self.set_toolbox(self.toolbox) + self.searchToolbar = SearchToolbar(self) + self.searchToolbar.connect("address-update", self._addressUpdateCb) + self.searchToolbar.connect("zoom-in", self._zoomInCb) + self.searchToolbar.connect("zoom-out", self._zoomOutCb) + self.searchToolbar.connect("save-search", self._saveSearchCb) + self.searchToolbar.connect("search-update", self._searchUpdateCb) + self.searchToolbar.set_sensitive(False) + self.toolbox.add_toolbar( Constants.istrSearch, self.searchToolbar ) + self.addToolbar = AddToolbar(self) + self.toolbox.add_toolbar( Constants.istrAnnotate, self.addToolbar ) + self.addToolbar.connect("add-media", self._addMediaCb) + self.addToolbar.connect("add-kml", self._addKMLCb) + self.addToolbar.connect("add-info", self._addInfoCb) + self.addToolbar.connect("delete-media", self._deleteMediaCb) + self.addToolbar.connect("measure", self._measureCb) + self.addToolbar.set_sensitive(False) + self.firstSearch = True + self.toolbox.set_current_toolbar(1) + + self.toolbox.remove(self.toolbox._separator) + #taken directly from toolbox.py b/c I don't know how to mod the hongry hippo + separator = hippo.Canvas() + box = hippo.CanvasBox( + border_color=Constants.colorBg.get_int(), + background_color=Constants.colorBg.get_int(), + box_height=style.TOOLBOX_SEPARATOR_HEIGHT, + border_bottom=style.LINE_WIDTH) + separator.set_root(box) + self.toolbox.separator = separator + self.toolbox._notebook.set_property("can-focus", False) + self.toolbox.show() + + #add components + vbox = gtk.VBox() + + self.tray = HTray() + self.tray.set_size_request(-1, 130) + self.tray.connect("enter-notify-event", self._trayMouseCb) + self.tray.viewport.connect("enter-notify-event", self._trayMouseCb) + self.tray.viewport.traybar.connect("enter-notify-event", self._trayMouseCb) + self.tray.scroll_left.connect("enter-notify-event", self._trayMouseCb) + self.tray.scroll_right.connect("enter-notify-event", self._trayMouseCb) + self.tray.scroll_left_event.connect("enter-notify-event", self._trayMouseCb) + self.tray.scroll_right_event.connect("enter-notify-event", self._trayMouseCb) + fakeTray = gtk.VBox() + fakeTray.set_size_request(-1, 130) + fakeTray.modify_bg( gtk.STATE_NORMAL, Constants.colorBg.gColor ) + fakeTrayEvent = gtk.EventBox() + fakeTrayEvent.modify_bg( gtk.STATE_NORMAL, Constants.colorBg.gColor ) + fakeTrayEvent.set_size_request(-1, 130) + fakeTrayEvent.add( fakeTray ) + self.trayBox = gtk.Notebook() + self.trayBox.set_size_request(-1, 130) + self.trayBox.set_show_tabs(False) + #vbox.pack_start(self.trayBox, expand=False) + self.trayBox.append_page(self.tray) + self.trayBox.append_page(fakeTrayEvent) + self.realTrayIndex = self.trayBox.page_num(self.tray) + self.fakeTrayIndex = self.trayBox.page_num(fakeTrayEvent) + self.trayBox.set_current_page(self.realTrayIndex) + fakeTrayEvent.add_events(gtk.gdk.VISIBILITY_NOTIFY_MASK) + fakeTrayEvent.connect_after("visibility-notify-event", self._fakeTrayVisibleNotifyCb) + + self.browseBox = gtk.VBox() + self.browser = WebViewer() + self.htmlScale = 1.43 + self.browser.set_size_request(int(webWidth*self.htmlScale), int(webHeight*self.htmlScale)) + self.browseBox.pack_start(self.browser, expand=False) + self.browseBox.set_size_request(int(webWidth*self.htmlScale), int(webHeight*self.htmlScale)) + vbox.pack_start(self.browseBox) + #vbox.pack_start(self.trayBox, expand=False) + + #important to call these in this order to ensure they show up + self.set_canvas(vbox) + self.browser.show() + + #fire up the web engine, spiderman! + self.cometLogic = ServerLogic(self) + + ajaxServer = ServerThread(self.__class__.ajaxPort, self.cometLogic) + ajaxServer.start() + + cometServer = ServerThread(self.__class__.cometPort, self.cometLogic) + cometServer.start() + + self.browser.load_uri("file://" + self.htmlPath + "/mapCenter.html?ajaxPort=" + str(self.__class__.ajaxPort) + "&cometPort=" + str(self.__class__.cometPort)) + + self.loaded = True + self.loadingWindow = gtk.Window() + self.addToWindowStack( self.loadingWindow, self ) + self.loadingWindow.modify_bg( gtk.STATE_NORMAL, Constants.colorBlack.gColor ) + loadingVBox = gtk.VBox() + self.loadingWindow.add(loadingVBox) + + loadingTopEventBox = gtk.EventBox() + loadingTopEventBox.modify_bg( gtk.STATE_NORMAL, Constants.colorBlack.gColor ) + loadingVBox.pack_start(loadingTopEventBox, expand=True) + + self.loadingCanvas = PhotoCanvas() + self.loadingCanvas.modify_bg( gtk.STATE_NORMAL, Constants.colorBlack.gColor ) + self.loadingCanvas.set_size_request( 1200, 600 ) + loadingVBox.pack_start(self.loadingCanvas, expand=True) + + loadingInfo = gtk.Label() + loadingInfo.set_alignment( .5, 0 ) + loadingInfo.set_text( "<b><span foreground='white' size='xx-large'>" + Constants.istrConnecting + "</span></b>") + loadingInfo.set_use_markup( True ) + loadingVBox.pack_start(loadingInfo, expand=False) + + loadingBotEventBox = gtk.EventBox() + loadingBotEventBox.modify_bg( gtk.STATE_NORMAL, Constants.colorBlack.gColor ) + loadingVBox.pack_start(loadingBotEventBox, expand=True) + self.loadingWindow.resize( gtk.gdk.screen_width(), gtk.gdk.screen_height()-(self.toolbox.allocation.height) ) + self.moveWinOffscreen(self.loadingWindow) + + self.popWindow = gtk.Window() + self.addToWindowStack( self.popWindow, self.loadingWindow ) + self.popWindow.modify_bg( gtk.STATE_NORMAL, Constants.colorBlack.gColor ) + self.popWindow.resize( self.popW+(2*self.popB), self.popH+(2*self.popB) ) + self.popUpBg = PopUpP5() + self.popWindow.add( self.popUpBg ) + self.moveWinOffscreen(self.popWindow) + self.popWindow.show_all() + + self.mediaWindow = gtk.Window() + self.addToWindowStack(self.mediaWindow, self.popWindow) + self.moveWinOffscreen(self.mediaWindow) + self.mediaWindow.show_all() + + self.infoWindow = gtk.Window() + self._fillInInfoWindow() + self.addToWindowStack(self.infoWindow, self.mediaWindow) + self.moveWinOffscreen(self.infoWindow) + self.infoWindow.show_all() + + # GTK science form window, based on infoWindow + #self.scienceWindow = gtk.Window() + #self._fillInScienceWindow() + #self.moveWinOffscreen(self.scienceWindow) + #self.scienceWindow.show_all() + + self.selectWindow = gtk.Window() + self.selectWindow.modify_bg( gtk.STATE_NORMAL, Constants.colorBlack.gColor ) + self.addToWindowStack(self.selectWindow, self.infoWindow) + self.moveWinOffscreen(self.selectWindow) + self.selectWindow.show_all() + + self.photoCanvas = PhotoCanvas() + + self.gplayWin = PlayVideoWindow( Constants.colorBlack.gColor ) + self.gplay = Gplay() + self.gplay.window = self.gplayWin + + self.connect("destroy", self.destroy) + self.TOOLBOX_SIZE_ALLOCATE_ID = self.toolbox.connect_after("size-allocate", self._sizeAllocateCb) + self.show_all() + + # break loading screen + self.loaded = True + self.enableNavigation() + + # add sharing + self.maptube = None # Shared session + self.initiating = False + self.pservice = presenceservice.get_instance() + owner = self.pservice.get_owner() + self.owner = owner + self.connect('shared', self._shared_cb) + self.connect('joined', self._joined_cb) + self.mediaType = "photo" + + return False + + def _received_cb(self, text): + if(text.find(",") != -1): + params = text.split(",") + if(params[0] == "loc"): + # other XO's map location has moved [lat,lng,zoom] + self.preComet() + self.cometLogic.handleReceivedMap(params[1],params[2],params[3]) + self.postComet() + elif(params[0] == "mar"): + # other XO has added or modded info marker [lat,lng,infoText,iconImg] + self.preComet() + self.cometLogic.handleAddMarker(params[1], params[2], params[3], params[4]) + self.cometLogic.handleEndKML() + self.postComet() + elif(params[0] == "del"): + # delete a marker (not working) + self.preComet() + self.cometLogic.handleDelete() + self.postComet() + elif(params[0] == "smap"): + # add SavedMap [lat,lng,zoom,info] + not-new reminder + self.addSavedMap(params[1],params[2],params[3],params[4],False) + elif(params[0] == "line"): + # add a line + self.preComet() + self.cometLogic.handleLine(params[1],params[2],params[3],params[4]) + self.cometLogic.handleEndKML() + self.postComet() + + def updateMapMetaData( self, ctrlat, ctrlng, zoom, showx, showy ): + self.NOW_MAP_CENTER_LAT = ctrlat + self.NOW_MAP_CENTER_LNG = ctrlng + self.NOW_MAP_ZOOM = zoom + # calculate where to put media overlay + showx = int(showx) + 60 + showy = int(showy) + 425 + if(showx > 60) and (showx < 700) and (showy > 425) and (showy < 600): + # open media overlay + if(self.mediaType == "photo"): + # mediaWindow contains a photo + self.mediaWindow.show() + self.smartMove(self.mediaWindow, self.htmlScale * showx, self.htmlScale * showy ) + else: + # popWindow contains a video player + self.popWindow.show() + self.smartMove(self.popWindow, self.htmlScale * showx, self.htmlScale * showy ) + self.popWindow.hide() + self.mediaWindow.show_all() + else: + self.mediaWindow.hide() + self.popWindow.hide() + if(self.maptube is not None): + # update XO group location + self.maptube.SendText("loc," + ctrlat + "," + ctrlng + "," + zoom) + + def _trayMouseCb( self, events, args): + if (self.shownRecd != None): + self.hideMedia() + self.preComet() + self.cometLogic.handleClear( ) + self.postComet() + + return True + + def _tagsBufferEditedCb(self, widget): + if (self.shownSave != None): + txt = self.tagsBuffer.get_text( self.tagsBuffer.get_start_iter(), self.tagsBuffer.get_end_iter() ) + if (txt != self.shownSave.notes): + self.shownSave.notes = txt + + def _hideInfoCb( self, butt ): + self.hideSearchResult() + + def _hideScienceCb( self, button ): + #self.shownSave = None + self.scienceWindow.set_property("accept-focus", False) + self.moveWinOffscreen( self.scienceWindow ) + self.enableNavigation() + + def hideSearchResult(self): + #self.shownSave = None + self.infoWindow.set_property("accept-focus", False) + self.moveWinOffscreen( self.infoWindow ) + self.enableNavigation() + + def showFileLoadBlocker( self, show ): + if (show): + self.smartMove( self.selectWindow, 0, 0 ) + self.smartResize( self.selectWindow, gtk.gdk.screen_width(), gtk.gdk.screen_height() ) + else: + self.moveWinOffscreen( self.selectWindow ) + + + def _sizeAllocateCb( self, widget, event ): + self.toolbox.disconnect( self.TOOLBOX_SIZE_ALLOCATE_ID) + + self.smartMove(self.loadingWindow, 0, self.toolbox.allocation.height) + self.loadingWindow.resize( gtk.gdk.screen_width(), gtk.gdk.screen_height()-(self.toolbox.allocation.height) ) + return False + + + def remoteServerActive(self, loaded): + if (self.loaded): + return + else: + self.loaded = loaded + #add search results from load_file + for i in range(0, len(self.m.savedMaps)): + smap = self.m.savedMaps[i] + self.addThumb( smap, False ) + self.searchToolbar.set_sensitive(True) + self.addToolbar.set_sensitive(True) + browserW = self.browseBox.allocation.width + browserH = self.browseBox.allocation.height + self.smartResize( self.infoWindow, browserW, browserH ) + self.moveWinOffscreen(self.loadingWindow) + + + def addToWindowStack( self, win, parent ): + self.windowStack.append( win ) + win.set_transient_for( parent ) + win.set_type_hint( gtk.gdk.WINDOW_TYPE_HINT_DIALOG ) + win.set_decorated( False ) + win.set_focus_on_map( False ) + win.set_property("accept-focus", False) + + + def moveWinOffscreen( self, win ): + #we move offscreen to resize or else we get flashes on screen, and setting hide() doesn't allow resize & moves + offW = (gtk.gdk.screen_width() + 100) + offH = (gtk.gdk.screen_height() + 100) + self.smartMove(win, offW, offH) + + + def smartMove( self, win, x, y ): + winLoc = win.get_position() + if ( (winLoc[0] != x) or (winLoc[1] != y) ): + win.move( int(x), int(y) ) + return True + else: + return False + + + def smartResize( self, win, w, h ): + winSize = win.get_size() + if ( (winSize[0] != w) or (winSize[1] != h) ): + win.resize( w, h ) + return True + else: + return False + + + def preComet(self): + self.cond.acquire() + + + def postComet(self): + self.cond.notifyAll() + self.cond.release() + time.sleep(.1) + + + def read_file(self, file): + serialize.fillMediaHash(file, self.m) + + + def close( self ): + self.hide() + activity.Activity.close( self ) + + + def write_file(self, file): + dom = serialize.saveMediaHash(self.m) + xmlFile = open( file, "w" ) + dom.writexml(xmlFile) + xmlFile.close() + + def mapPaste(self): + self.preComet() + self.cometLogic.mapPaste('osm') + self.postComet() + + def googlePaste(self): + self.preComet() + self.cometLogic.mapPaste('google') + self.postComet() + + def _addressUpdateCb( self, otets, address ): + #Special to OfflineMaps - decide the latlng of the given address + if(address.find(',') != -1): + if(len(address.split(',')) == 3): + addSplit = address.split(',') + if(addSplit[2] == 'm'): + # add a marker at this lat,lng + self.preComet() + self.cometLogic.handleAddMarker(addSplit[0],addSplit[1],addSplit[0]+","+addSplit[1],"magenta") + self.cometLogic.handleEndKML() + self.postComet() + self.addInfoMarker(addSplit[0],addSplit[1],addSplit[0]+","+addSplit[1],"magenta",True) + return + latlng = address + else: + address = address.lower().replace(' ','').replace('-','') + zoom="16" + lat="-190" + lng="-190" + places = open(self.libPath + "Places.csv", 'r') + for place in places: + pData = place.split(',') + pData[0] = pData[0].lower().replace(' ','').replace('-','') + if(pData[0] == address): + lat = pData[1] + lng = pData[2] + if(pData[3] != "-1"): + zoom = pData[3] + break + if(lat=="-190"): + return + latlng = lat + "," + lng + "," + zoom + self.preComet() + self.cometLogic.handleAddressUpdate(latlng) + self.postComet() + + def _searchUpdateCb( self, otets, tags ): + self.NOW_MAP_TAGS = tags + self.preComet() + self.cometLogic.handleTagSearch(tags) + self.postComet() + + def _zoomInCb( self, butt ): + self.hideMedia() + self.preComet() + self.cometLogic.handleZoomUpdate("+") + self.postComet() + + def _zoomOutCb( self, butt ): + self.hideMedia() + self.preComet() + self.cometLogic.handleZoomUpdate("-") + self.postComet() + + def _saveSearchCb( self, otets ): + self._saveSearch() + + def _saveSearch( self ): + #1st: cover up the tray when grabbing a screenshot + #this will take a moment, so disable all buttons while we wait + self.SAVING_SEARCH = False + #self.SAVING_SEARCH = True + #self.disableNavigation() + #self.savingSearchMediaLoc = self.mediaWindow.get_position() + #self.moveWinOffscreen( self.mediaWindow ) + #self.hideTray(True) + + + def hideTray( self, hide ): + if (hide): + self.trayBox.set_current_page(self.fakeTrayIndex) + else: + self.trayBox.set_current_page(self.realTrayIndex) + + + def _fakeTrayVisibleNotifyCb( self, widget, event ): + #if (event.state == gtk.gdk.VISIBILITY_UNOBSCURED): #this is not used, bc a popup might be over it + if (self.SAVING_SEARCH): + gobject.idle_add( self._saveSearch2 ) + + def _saveSearch2( self ): + # add SavedMap + window = self.canvas.window + width, height = window.get_size() + screenshot = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, has_alpha=False, bits_per_sample=8, width=width, height=height) + screenshot.get_from_drawable(window, window.get_colormap(), 0, 0, 0, 0, width, height) + + popW, popH = [self.popW+(2*self.popB), self.popH+(2*self.popB) ] + popScreenshot = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, has_alpha=False, bits_per_sample=8, width=width, height=height) + popScreenshot.get_from_drawable(self.popWindow.window, self.popWindow.window.get_colormap(), 0, 0, 0, 0, popW, popH) + + popLoc = self.popWindow.get_position() + popLocX = popLoc[0] + popLocY = popLoc[1] - self.toolbox.allocation.height + popScreenshot.composite(screenshot, + popLocX, popLocY, #move the actual mini-canvas (but not its internals (?)) + popW, popH, #actual size of the mini-canvas + popLocX, popLocY, #offx, y of the painting (irrespective of the mini-canvas) + 1, 1, #scalex,y + gtk.gdk.INTERP_BILINEAR, 255) + + screenshotPath = os.path.join(Instance.instancePath, "savedMap.jpg") + screenshotPath = utils.getUniqueFilepath(screenshotPath, 0) + screenshot.save( screenshotPath, "jpeg", {} ) + + smallHeight = 120 + smallWidth = (width*smallHeight) / height + screenshotSmall = screenshot.scale_simple(smallWidth, smallHeight, gtk.gdk.INTERP_NEAREST) + screenshotSmallPath = os.path.join(Instance.instancePath, "savedMapThumb.jpg") + screenshotSmallPath = utils.getUniqueFilepath(screenshotSmallPath, 0) + screenshotSmall.save( screenshotSmallPath, "jpeg", {} ) + + sm = SavedMap( self.NOW_MAP_CENTER_LAT, self.NOW_MAP_CENTER_LNG, self.NOW_MAP_ZOOM, screenshotPath, screenshotSmallPath, "Describe the map", self.NOW_MAP_TAGS ) + if (self.shownRecd != None): + sm.addViewedRecd(self.shownRecd.datastoreId, self.shownRecd.latitude, self.shownRecd.longitude) + + self.m.addSavedMap( sm ) + self.addThumb( sm, True ) + + self.hideTray(False) + self.enableNavigation() + self.smartMove( self.mediaWindow, self.savingSearchMediaLoc[0], self.savingSearchMediaLoc[1] ) + + if(self.maptube is not None): + self.maptube.SendText("smap," + str(self.NOW_MAP_CENTER_LAT) + "," + str(self.NOW_MAP_CENTER_LNG) + "," + str(self.NOW_MAP_ZOOM) + ",Describe this map") + + def addLine(self,id,color,thickness,pts): + self.m.setLine(id,color,thickness,pts) + if(self.maptube is not None): + self.maptube.SendText("line," + id + "," + color + "," + thickness + "," + pts) + + def lineMode(self,type): + self.preComet() + self.cometLogic.lineMode(type) + self.postComet() + + def addSavedMap(self,lat,lng,zoom,info,isNew): + # add SavedMap received from internet or other XO, use Google Static Maps + window = self.canvas.window + width, height = window.get_size() + screenshotPath = os.path.join(Instance.instancePath, "savedMap.jpg") + urllib.urlretrieve("http://maps.google.com/staticmap?key=ABQIAAAAxkKtrWN5q-vPTLRVmO_r6RRFDCLHCbUG3VrjXnZmMRXvQdFL3RS-b-ld9hTrkIgQlYsxPQ1kYq6y9A&size=200x120&format=jpg¢er=" + str(lat) + "," + str(lng) + "&zoom=" + str(int(zoom)-1), screenshotPath) + smallHeight = 120 + smallWidth = 200 + sm = SavedMap( lat, lng, zoom, screenshotPath, screenshotPath, info, "" ) + self.m.addSavedMap( sm ) + self.addThumb( sm, True ) + if(isNew == True): + if(self.maptube is not None): + # user created SaveMap, send to others + self.maptube.SendText("smap," + lat + "," + lng + "," + zoom + "," + info) + + def addInfoMarker(self,lat,lng,info,icon,sendUpdate): + # add/update InfoMarker in model + self.m.setInfo(lat, lng, info, icon) + if(sendUpdate == True): + if(self.maptube is not None): + # send added/updated info marker + self.maptube.SendText("mar," + lat + "," + lng + "," + info + "," + icon) + else: + # put InfoMarker on own map + self.preComet() + self.cometLogic.handleAddMarker(lat,lng,info,icon) + self.cometLogic.handleEndKML() + self.postComet() + + def joinGroup(self,groupName): + #groupFile = urllib.urlopen("http://bluebird-science.appspot.com/group/data?name=" + groupName) + #readError = 0 + groupData = '{}' + #while readError == 0: + # try: + # groupData = groupData + groupFile.readline() + # except: + # readError = 1 + self.preComet() + self.cometLogic.handleGroupData(groupData) + self.postComet() + + def removeThumb( self, sm ): + kids = self.tray.get_children() + for i in range (0, len(kids)): + if (kids[i].data == sm): + self.tray.remove_item(kids[i]) + kids[i].cleanUp() + kids[i].disconnect( kids[i].getButtClickedId() ) + kids[i].setButtClickedId(0) + if (sm == self.shownSave): + self.hideSearchResult() + self.m.savedMaps.remove(sm) + + def addThumb( self, sm, forceScroll ): + butt = SavedButton( self, sm ) + BUTT_CLICKED_ID = butt.connect( "clicked", self._thumbClickedCb, sm ) + butt.setButtClickedId(BUTT_CLICKED_ID) + self.tray.add_item( butt, len(self.tray.get_children()) ) + butt.show() + if (forceScroll): + self.tray.scroll_to_end() + + #1 way to see a sr + def _thumbClickedCb( self, button, smap ): + self.showSearchResult(smap) + self.preComet() + self.cometLogic.handleSavedMap(smap.lat,smap.lng,smap.zoom,smap.notes) + self.postComet() + + def showSearchResult( self, smap ): + self.shownSave = smap + self.updateSearchResultTags(smap) + self.searchToolbar._setTagsString( smap.tags ) + + def updateSearchResultTags( self, smap ): + self.shownSave = smap + self.lngValueLabel.set_text( str(smap.lng) ) + self.latValueLabel.set_text( str(smap.lat) ) + self.tagsBuffer.set_text( smap.notes ) + + def _fillInInfoWindow( self ): + hbox = gtk.HBox() + self.infoWindow.add(hbox) + clr = Constants.colorGrey + inset = 10 + self.infoWindow.modify_bg( gtk.STATE_NORMAL, clr.gColor ) + ltBox = gtk.VBox() + ltBox.set_border_width(inset) + rtBox = gtk.VBox() + hbox.pack_start(ltBox) + hbox.pack_start(rtBox, expand=False) + latHBox = gtk.HBox() + ltBox.pack_start(latHBox, expand=False) + latLabel = gtk.Label("<b>" + Constants.istrLatitude + "</b> ") + latLabel.set_use_markup(True) + latLabel.set_alignment(0, .5) + latHBox.pack_start(latLabel, expand=False) + self.latValueLabel = gtk.Label() + self.latValueLabel.set_alignment(0, .5) + latHBox.pack_start(self.latValueLabel) + fillb1 = gtk.HBox() + fillb1.set_size_request(inset,inset) + ltBox.pack_start(fillb1, expand=False) + lngHBox = gtk.HBox() + ltBox.pack_start(lngHBox, expand=False) + lngLabel = gtk.Label("<b>" + Constants.istrLongitude + "</b> ") + lngLabel.set_use_markup(True) + lngLabel.set_alignment(0, .5) + lngHBox.pack_start(lngLabel, expand=False) + self.lngValueLabel = gtk.Label() + self.lngValueLabel.set_alignment(0, .5) + lngHBox.pack_start(self.lngValueLabel) + fillb2 = gtk.HBox() + fillb2.set_size_request(inset,inset) + ltBox.pack_start(fillb2, expand=False) + tagLabelBox = gtk.HBox() + ltBox.pack_start(tagLabelBox, expand=False) + tagsLabel = gtk.Label("<b>" + Constants.istrTags + "</b>") + tagsLabel.set_use_markup(True) + tagsLabel.set_alignment(0, .5) + tagLabelBox.pack_start(tagsLabel, expand=False) + tagBox = gtk.HBox() + self.tagsBuffer = gtk.TextBuffer() + self.tagsBuffer.set_text("please edit me!") + self.tagsBuffer.connect('changed', self._tagsBufferEditedCb) + self.tagsField = gtk.TextView(self.tagsBuffer) + ltBox.pack_start(self.tagsField, expand=True) + rtFill = gtk.VBox() + rtFill.set_spacing( 0 ) + rtFill.set_border_width( 0 ) + rtBox.pack_start(rtFill, expand=True) + buttImg = gtk.Image() + buttPixb = Constants.infoOnSvg.get_pixbuf() + buttImg.set_from_pixbuf( buttPixb ) + buttImg.show() + rtButt = gtk.Button() + rtButt.set_image(buttImg) + rtButt.modify_bg( gtk.STATE_NORMAL, Constants.colorBlack.gColor ) + rtButt.modify_bg( gtk.STATE_ACTIVE, Constants.colorBlack.gColor ) + rtButt.modify_bg( gtk.STATE_INSENSITIVE, Constants.colorBlack.gColor ) + rtButt.set_relief(gtk.RELIEF_NONE) + rtButt.set_property('can-default', True) + rtButt.set_property('can-focus', False) + rtButt.set_property('yalign', 1) + rtButt.set_size_request( 75, 75 ) + rtButt.connect("clicked", self._hideInfoCb) + grr = gtk.EventBox() + grr.modify_bg( gtk.STATE_NORMAL, Constants.colorBlack.gColor ) + grr.add(rtButt) + rtBox.pack_start(grr, expand=False) + hbox.show_all() + + def _fillInScienceWindow( self ): + hbox = gtk.HBox() + self.scienceWindow.add(hbox) + clr = Constants.colorGrey + inset = 10 + self.scienceWindow.modify_bg( gtk.STATE_NORMAL, clr.gColor ) + ltBox = gtk.VBox() + ltBox.set_border_width(inset) + rtBox = gtk.VBox() + hbox.pack_start(ltBox) + hbox.pack_start(rtBox, expand=False) + latHBox = gtk.HBox() + ltBox.pack_start(latHBox, expand=False) + latLabel = gtk.Label("<b>" + Constants.istrLatitude + "</b> ") + latLabel.set_use_markup(True) + latLabel.set_alignment(0, .5) + latHBox.pack_start(latLabel, expand=False) + self.latValueLabel = gtk.Label() + self.latValueLabel.set_alignment(0, .5) + latHBox.pack_start(self.latValueLabel) + fillb1 = gtk.HBox() + fillb1.set_size_request(inset,inset) + ltBox.pack_start(fillb1, expand=False) + lngHBox = gtk.HBox() + ltBox.pack_start(lngHBox, expand=False) + lngLabel = gtk.Label("<b>" + Constants.istrLongitude + "</b> ") + lngLabel.set_use_markup(True) + lngLabel.set_alignment(0, .5) + lngHBox.pack_start(lngLabel, expand=False) + self.lngValueLabel = gtk.Label() + self.lngValueLabel.set_alignment(0, .5) + lngHBox.pack_start(self.lngValueLabel) + fillb2 = gtk.HBox() + fillb2.set_size_request(inset,inset) + ltBox.pack_start(fillb2, expand=False) + tagLabelBox = gtk.HBox() + ltBox.pack_start(tagLabelBox, expand=False) + tagsLabel = gtk.Label("<b>" + Constants.istrTags + "</b>") + tagsLabel.set_use_markup(True) + tagsLabel.set_alignment(0, .5) + tagLabelBox.pack_start(tagsLabel, expand=False) + tagBox = gtk.HBox() + self.tagsBuffer = gtk.TextBuffer() + self.tagsBuffer.set_text("please edit me!") + self.tagsBuffer.connect('changed', self._tagsBufferEditedCb) + self.tagsField = gtk.TextView(self.tagsBuffer) + ltBox.pack_start(self.tagsField, expand=True) + rtFill = gtk.VBox() + rtFill.set_spacing( 0 ) + rtFill.set_border_width( 0 ) + rtBox.pack_start(rtFill, expand=True) + buttImg = gtk.Image() + buttPixb = Constants.infoOnSvg.get_pixbuf() + buttImg.set_from_pixbuf( buttPixb ) + buttImg.show() + rtButt = gtk.Button() + rtButt.set_image(buttImg) + rtButt.modify_bg( gtk.STATE_NORMAL, Constants.colorBlack.gColor ) + rtButt.modify_bg( gtk.STATE_ACTIVE, Constants.colorBlack.gColor ) + rtButt.modify_bg( gtk.STATE_INSENSITIVE, Constants.colorBlack.gColor ) + rtButt.set_relief(gtk.RELIEF_NONE) + rtButt.set_property('can-default', True) + rtButt.set_property('can-focus', False) + rtButt.set_property('yalign', 1) + rtButt.set_size_request( 75, 75 ) + rtButt.connect("clicked", self._hideScienceCb) + grr = gtk.EventBox() + grr.modify_bg( gtk.STATE_NORMAL, Constants.colorBlack.gColor ) + grr.add(rtButt) + rtBox.pack_start(grr, expand=False) + hbox.show_all() + + def showSearchResultTags( self, smap ): + self.updateSearchResultTags( smap ) + + #avoid popups lingering + self.hideMedia() + self.preComet() + self.cometLogic.handleClear() + self.postComet() + self.disableNavigation() + browserLoc = self.browseBox.translate_coordinates(self, 0, 0) + self.infoWindow.show_all() + self.smartMove(self.infoWindow, browserLoc[0], browserLoc[1]) + browserW = self.browseBox.allocation.width + browserH = self.browseBox.allocation.height + self.smartResize(self.infoWindow, browserW, browserH) + self.infoWindow.set_property("accept-focus", True) + + def copyToClipboard( self, smap ): + tmpImgPath = self.doClipboardCopyStart( smap ) + gtk.Clipboard().set_with_data( [('text/uri-list', 0, 0)], self._clipboardGetFuncCb, self._clipboardClearFuncCb, tmpImgPath ) + return True + + def doClipboardCopyStart( self, smap ): + tmpImgPath = utils.getUniqueFilepath(smap.imgPath, 0) + shutil.copyfile( smap.imgPath, tmpImgPath ) + return tmpImgPath + + def doClipboardCopyCopy( self, tmpImgPath, selection_data ): + tmpImgUri = "file://" + tmpImgPath + selection_data.set( "text/uri-list", 8, tmpImgUri ) + + def doClipboardCopyFinish( self, tmpImgPath ): + if (tmpImgPath != None): + if (os.path.exists(tmpImgPath)): + os.remove( tmpImgPath ) + tmpImgPath = None + + def _clipboardGetFuncCb( self, clipboard, selection_data, info, data): + self.doClipboardCopyCopy( data, selection_data ) + + def _clipboardClearFuncCb( self, clipboard, data): + self.doClipboardCopyFinish( data ) + + def _addMediaCb( self, ot, datastoreOb ): + self.addMeToMapObj = datastoreOb + self.hideMedia() + self.preComet() + self.cometLogic.handlePreAdd() + self.postComet() + + def _addKMLCb( self, ot, datastoreOb ): + kml = open(datastoreOb.file_path, 'r') + firstLine = kml.readline() + if(firstLine.find('<?xml version="1.0" ?><map')==0): + # Offline Map or regular Map file without meta-data + self.preComet() + xmlarray = firstLine.split('>') + for node in xmlarray: + #if(node.find('<mapItem') == 0): + # picture marker + if(node.find('<infoMarker') == 0): + # info marker + lat = node[node.find('lat="')+5:len(node)] + lat = lat[0:lat.find('"')] + lng = node[node.find('lng="')+5:len(node)] + lng = lng[0:lng.find('"')] + description = node[node.find('info="')+6:len(node)] + description = description[0:description.find('"')] + icon = node[node.find('icon="')+6:len(node)] + icon = icon[0:icon.find('"')] + self.cometLogic.handleAddMarker(lat,lng,description,icon) + self.addInfoMarker(lat,lng,description,icon,True) + elif(node.find('<line') == 0): + # line + lineColor = node[node.find('lcolor="')+8:len(node)] + lineColor = lineColor[0:lineColor.find('"')] + lineID = node[node.find('lid="')+5:len(node)] + lineID = lineID[0:lineID.find('"')] + ptStr = node[node.find('lpts="')+6:len(node)] + ptStr = ptStr[0:ptStr.find('"')] + lineThick = node[node.find('lthickness="')+12:len(node)] + lineThick = lineThick[0:lineThick.find('"')] + self.cometLogic.handleLine(lineID,lineColor,lineThick,ptStr) + elif(node.find('<savedMap') == 0): + # saved map + lat = node[node.find('lat="')+5:len(node)] + lat = lat[0:lat.find('"')] + lng = node[node.find('lng="')+5:len(node)] + lng = lng[0:lng.find('"')] + notes = node[node.find('notes="')+7:len(node)] + notes = notes[0:notes.find('"')] + zoom = node[node.find('zoom="')+6:len(node)] + zoom = zoom[0:zoom.find('"')] + self.addSavedMap(lat,lng,zoom,notes,True) + self.cometLogic.handleEndKML() + self.postComet() + return + placename = None + description = None + lat = None + lng = None + coordList = None + placemarkType = None + lineType = None + stillDescribing = False + morePts = False + dataTable = None + isOSM = False + isGeoRSS = False + self.preComet() + for kmlline in kml: + if((kmlline.find('<osm') != -1) and (lat == None) and (lng == None) and (coordList == None)): + # jump to osm + isOSM = True + break + elif((kmlline.find('georss') != -1) and (lat == None) and (lng == None) and (coordList == None)): + # jump to GeoRSS + isGeoRSS = True + break + if(kmlline.find('<Point') != -1): + placemarkType = 'info' + elif(kmlline.find('<LineString') != -1): + placemarkType = 'line' + lineType = 'line' + elif(kmlline.find('<Polygon') != -1): + placemarkType = 'poly' + lineType = 'poly' + #elif(kmlline.find('<GroundOverlay') != -1): + # placemarkType = 'overlay' + if(kmlline.find('<ExtendedData') != -1): + dataTable = '<table>' + elif(kmlline.find('</ExtendedData>') != -1): + dataTable = dataTable + '</table>' + if(dataTable is not None): + if(kmlline.find('<Data') != -1): + dataTable = dataTable + '<tr>' + if(kmlline.find('<Data name="') != -1): + dataTable = dataTable + "<td><b>" + kmlline[kmlline.find('<Data name="')+12:kmlline.rfind('"')] + "</b></td>" + if(kmlline.find('<value>') != -1): + dataTable = dataTable + "<td>" + kmlline[kmlline.find('<value>')+7:kmlline.find('</value>')] + "</td>" + elif(kmlline.find('</Data>') != -1): + dataTable = dataTable + '</tr>' + + if(kmlline.find('<coordinates>') != -1): + if(placemarkType == 'info'): + lng = kmlline[kmlline.find('<coordinates>')+13:kmlline.find(',')] + lat = kmlline[kmlline.find(',')+1:kmlline.rfind(',')] + else: + if(kmlline.find('</coordinates>') == -1): + morePts = True + kmlline = kmlline.replace('<coordinates>','').replace('</coordinates>','') + coordList = kmlline.split(' ') + elif(morePts == True): + if(kmlline.find('</coordinates>') != -1): + morePts = False + kmlline = kmlline.replace('</coordinates>','') + coordList.extend(kmlline.split(' ')) + + if(kmlline.find('<name>') != -1): + placename = kmlline[kmlline.find('<name>')+6:kmlline.find('</name>')] + if(kmlline.find('<description>') != -1): + description = kmlline[kmlline.find('<description>')+13:len(kmlline)] + if(kmlline.find('</description>') == -1): + # need to read in more lines + stillDescribing = True + else: + description = description[0:description.find('</description')] + if(description.find('<![CDATA[') != -1): + description = description.replace('<![CDATA[','').replace(']]>','') + elif(stillDescribing == True): + description = description + kmlline + if(kmlline.find('</description>') != -1): + stillDescribing = False + description = description[0:description.find('</description')] + if(description.find('<![CDATA[') != -1): + description = description.replace('<![CDATA[','').replace(']]>','') + + if((kmlline.find('</Placemark>') != -1) or (kmlline.find('<Placemark>') != -1)): + # finish this placemark + if(lat is not None): + if(description is None): + description = "" + if(dataTable is not None): + description = dataTable + description + if(placename is not None): + description = '<h3>' + placename + '</h3>' + description + self.cometLogic.handleAddMarker(lat,lng,description,"magenta") + self.addInfoMarker(lat,lng,description,"magenta",True) + if(coordList is not None): + ptList = [] + maxInterval = 1 + if(len(coordList) > 50): + maxInterval = int(len(coordList) / 50) + interval = 1 + for pt in coordList: + ptloc = pt.split(',') + interval = interval - 1 + if((len(ptloc) > 1) and (interval < 2)): + ptList.append(ptloc[1]+"|"+ptloc[0]) + interval = maxInterval + if(lineType == 'line'): + self.cometLogic.handleLine(str(random.getrandbits(128)),"B22222","5",'|'.join(ptList)) + elif(lineType == 'poly'): + self.cometLogic.handleLine("poly-" + str(random.getrandbits(128)),"7CFC00","5",'|'.join(ptList)) + #elif(placemarkType == 'overlay'): + lat = None + lng = None + description = None + placename = None + placemarkType = None + coordList = None + dataTable = None + if(isOSM == True): + # read in an OSM style file + osmNodes = {} + readingInNodeID = None + readingInWayID = None + for osm in kml: + if(osm.find('<node') != -1): + # point, which may be part of a line + nodeID = osm[osm.find('id=')+4:len(osm)] + nodeID = nodeID[0:nodeID.find('"')] + nodeID = nodeID[0:nodeID.find("'")] + lat = osm[osm.find('lat=')+5:len(osm)] + lat = lat[0:lat.find("'")] + lat = lat[0:lat.find('"')] + lng = osm[osm.find('lon=')+5:len(osm)] + lng = lng[0:lng.find("'")] + lng = lng[0:lng.find('"')] + osmNodes[nodeID] = {"latitude":lat,"longitude":lng,"inWay":False,"isWay":False} + if(osm.find('/>') == -1): + readingInNodeID = nodeID + elif(readingInNodeID is not None): + # reading in tag-values for the point + if(osm.find('</node>') != -1): + readingInNodeID = None + else: + if(osm.find('<tag') != -1): + tName = osm[osm.find('k=')+3:len(osm)] + tName = tName[0:tName.find('"')] + tName = tName[0:tName.find("'")] + vName = osm[osm.find('v=')+3:len(osm)] + vName = vName[0:vName.find('"')] + vName = vName[0:vName.find("'")] + osmNodes[readingInNodeID][tName] = vName + elif(osm.find('<way') != -1): + # line, composed of multiple points + readingInWayID = osm[osm.find('id=')+4:len(osm)] + readingInWayID = readingInWayID[0:readingInWayID.find('"')] + readingInWayID = readingInWayID[0:readingInWayID.find("'")] + osmNodes[readingInWayID] = {"isWay":True,"pts":[]} + elif(readingInWayID is not None): + # reading in node ids composing the line + if(osm.find('</way>') != -1): + readingInWayID = None + else: + if(osm.find('<nd') != -1): + nodeRef = osm[osm.find('ref=')+5:len(osm)] + if(nodeRef.find('"') != -1): + nodeRef = nodeRef[0:nodeRef.find('"')] + else: + nodeRef = nodeRef[0:nodeRef.find("'")] + osmNodes[readingInWayID]["pts"].append(nodeRef) + osmNodes[nodeRef]["inWay"] = True + # decide how to publish nodes + for node in osmNodes: + if(osmNodes[node]["isWay"] == True): + # line composed of points + ptList=[] + for pID in osmNodes[node]["pts"]: + ptList.append(osmNodes[pID]["latitude"]+"|"+osmNodes[pID]["longitude"]) + self.cometLogic.handleLine(str(random.getrandbits(128)),"B22222","5",'|'.join(ptList)) + elif(osmNodes[node]["inWay"] == False): + # independent point + description="<table>" + for tag in osmNodes[node]: + if(tag=="latitude"): + continue + elif(tag=="longitude"): + continue + elif(tag=="inWay"): + continue + elif(tag=="isWay"): + continue + else: + value = osmNodes[node][tag] + description=description+"<tr><td>"+tag+"</td><td>"+str(value)+"</td></tr>" + description=description+"</table>" + self.cometLogic.handleAddMarker(osmNodes[node]["latitude"],osmNodes[node]["longitude"],description,"magenta") + self.addInfoMarker(osmNodes[node]["latitude"],osmNodes[node]["longitude"],description,"magenta",True) + elif(isGeoRSS == True): + # scan in GeoRSS + placeLink = None + lat = None + lng = None + description = None + placename = None + stillDescribing = False + #self.preComet() + for rss in kml: + if(rss.find('<title') != -1): + placename = rss[rss.find('>')+1:rss.find('</title>')] + if(rss.find('<link') != -1): + placeLink = rss[rss.find('>')+1:rss.find('</link>')] + if(rss.find('<georss:point') != -1): + latlng = rss[rss.find('>')+1:rss.find('</georss:point>')].split(' ') + lat = latlng[0] + lng = latlng[1] + if(rss.find('<description>') != -1): + description = rss[rss.find('<description>')+13:len(rss)] + if(rss.find('</description>') == -1): + # need to read in more lines + stillDescribing = True + else: + description = description[0:description.find('</description')] + elif(stillDescribing == True): + description = description + rss + if(rss.find('</description>') != -1): + stillDescribing = False + description = description[0:description.find('</description')] + if(rss.find('</item>') != -1): + # publish the point + if(lat is not None): + if(description == None): + description = "" + if(placeLink is not None): + description = placeLink + "<br/>" + description + if(placename is not None): + description = '<h3>' + placename + '</h3>' + description + description=description.replace("'","\\'").replace('"','\\"') + description=description.replace("\n","<br/>").replace("\r","<br/>") + description=description.replace("<![CDATA[","").replace("]]>","") + self.cometLogic.handleAddMarker(lat,lng,description,"magenta") + self.addInfoMarker(lat,lng,description,"magenta",True) + placeLink = None + lat = None + lng = None + placename = None + description = None + stillDescribing = False + self.cometLogic.handleEndKML() + self.postComet() + + def _addInfoCb(self,ot): + self.preComet() + self.cometLogic.handlePreAddInfo() + self.postComet() + + def _measureCb(self,ot): + self.preComet() + self.cometLogic.handleMeasure() + self.postComet() + + def disableNavigation( self ): + #disable the toolbar buttons + self.addToolbar.set_sensitive( False ) + self.searchToolbar.set_sensitive( False ) + + def enableNavigation( self ): + #enable the toolbar buttons + self.addToolbar.set_sensitive( True ) + self.searchToolbar.set_sensitive( True ) + + def placeAddMedia( self, lat, lng ): + self.enableNavigation() + #append to the map + recd = self.m.addMedia( lat, lng, self.addMeToMapObj ) + #now show the new addition to the map + self.preComet() + self.cometLogic.handlePostAdd( recd ) + self.postComet() + # send image to others + #if(self.maptube is not None): + # self.maptube.SendText("mar," + lat + "," + lng + "," + utils.getStringFromPixbuf(recd.getPixBuf()) ) + + def _deleteMediaCb( self, ot ): + self.preComet() + self.cometLogic.handleDelete() + self.postComet() + + def showMedia(self, id, x, y, up, rt): + recd = self.m.getMediaByThumbFilename(id) + if (recd == None): + return + self.shownRecd = recd + thumbPath = recd.getThumbPath() + thumbPixbuf = gtk.gdk.pixbuf_new_from_file(thumbPath) + px, py = [self.popW, self.popH] + scl = (self.popW+0.0) / (thumbPixbuf.get_width()+0.0) + thumbCairo = utils.generateThumbnail( thumbPixbuf, scl, px, py ) + self.popUpBg.updatePopInfo( recd.colorStroke, recd.colorFill, thumbCairo, up, rt, self.popB, self.popB, self.htmlScale ) + browserLoc = self.browseBox.translate_coordinates( self, 0, 0 ) + x = int(x) * self.htmlScale + y = int(y) * self.htmlScale + x = browserLoc[0]+x + y = browserLoc[1]+y + if (not rt): + x = x - (self.popW + (2*self.popB)) + if (not up): + y = y - (self.popH + (2*self.popB)) + self.smartMove( self.popWindow, x, y ) + kid = self.mediaWindow.get_child() + if (kid != None): + self.gplay.stop() + self.mediaWindow.remove( kid ) + self.mediaWindow.modify_bg(gtk.STATE_NORMAL, recd.colorFill.gColor) + if (recd.type == Constants.TYPE_PHOTO): + self.mediaType = 'photo' + self.popWindow.hide() + self.mediaWindow.show() + self.smartResize( self.mediaWindow, self.popW, self.popH ) + self.smartMove( self.mediaWindow, x+self.popB, y+self.popB) + pbuf = recd.getPixBuf() + img = _camera.cairo_surface_from_gdk_pixbuf(pbuf) + self.photoCanvas.modify_bg(gtk.STATE_NORMAL, recd.colorFill.gColor) + self.photoCanvas.set_size_request(self.popW, self.popH) + self.photoCanvas.setImage(img) + self.mediaWindow.add(self.photoCanvas) + self.photoCanvas.show_all() + + elif (recd.type == Constants.TYPE_VIDEO): + self.mediaType = 'video' + self.popWindow.show() + self.mediaWindow.show() + self.smartResize( self.mediaWindow, self.popW, self.popH ) + self.smartMove( self.mediaWindow, x+self.popB, y+self.popB) + self.smartMove( self.popWindow, x+self.popB, y+self.popB) + self.mediaWindow.add(self.gplayWin) + self.gplayWin.modify_bg(gtk.STATE_NORMAL, recd.colorFill.gColor) + self.popWindow.hide() + self.mediaWindow.show() + self.gplayWin.show_all() + videoUrl = "file://" + str( recd.getFilepath() ) + self.gplay.setLocation( videoUrl ) + self.gplay.play() + + def hideMedia( self ): + self.shownRecd = None + self.moveWinOffscreen( self.popWindow ) + self.moveWinOffscreen( self.mediaWindow ) + self.gplay.pause() + + def destroy(self, *args): + self.hide_all() + os._exit(0) #needed to kill all threads + gtk.main_quit() + + def _shared_cb(self, activity): + self.initiating = True + self._sharing_setup() + id = self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].OfferDBusTube(SERVICE, {}) + + def _sharing_setup(self): + if self._shared_activity is None: + return + self.conn = self._shared_activity.telepathy_conn + self.tubes_chan = self._shared_activity.telepathy_tubes_chan + self.text_chan = self._shared_activity.telepathy_text_chan + self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal('NewTube', self._new_tube_cb) + + def _list_tubes_reply_cb(self, tubes): + for tube_info in tubes: + self._new_tube_cb(*tube_info) + + # joining a pre-existing activity + def _joined_cb(self, activity): + if not self._shared_activity: + return + self.initiating = False + self._sharing_setup() + self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].ListTubes(reply_handler=self._list_tubes_reply_cb,error_handler=self.tubes_error) + + # display error raised by XO-XO connection on map + def tubes_error(self,e): + self.preComet() + self.cometLogic.handleCompassUpdate("Error: %s",e) + self.postComet() + + def _new_tube_cb(self, id, initiator, type, service, params, state): + if (type == telepathy.TUBE_TYPE_DBUS and service == SERVICE): + if state == telepathy.TUBE_STATE_LOCAL_PENDING: + self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].AcceptDBusTube(id) + tube_conn = TubeConnection(self.conn, self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES], id, group_iface=self.text_chan[telepathy.CHANNEL_INTERFACE_GROUP]) + self.maptube = TextSync(tube_conn, self.initiating, self._received_cb, self.tubes_error, self._get_buddy) + + # _get_buddy may not be necessary. Borrowed from HelloMesh + def _get_buddy(self, cs_handle): + group = self.text_chan[telepathy.CHANNEL_INTERFACE_GROUP] + my_csh = group.GetSelfHandle() + if my_csh == cs_handle: + handle = self.conn.GetSelfHandle() + elif group.GetGroupFlags() & telepathy.CHANNEL_GROUP_FLAG_CHANNEL_SPECIFIC_HANDLES: + handle = group.GetHandleOwners([cs_handle])[0] + else: + handle = cs_handle + return self.pservice.get_buddy_by_telepathy_handle(self.conn.service_name, self.conn.object_path, handle) + +class TextSync(ExportedGObject): + def __init__(self, tube, is_initiator, text_received_cb, alert, get_buddy): + super(TextSync, self).__init__(tube, PATH) + self.tube = tube + self.is_initiator = is_initiator + self.text_received_cb = text_received_cb + self._alert = alert + self.entered = False # Have we set up the tube? + self.text = '' # State that gets sent or received + self._get_buddy = get_buddy # Converts handle to Buddy object + self.tube.watch_participants(self.participant_change_cb) + + def participant_change_cb(self, added, removed): + for handle, bus_name in added: + buddy = self._get_buddy(handle) + for handle in removed: + buddy = self._get_buddy(handle) + if not self.entered: + if self.is_initiator: + self.add_hello_handler() + else: + self.Hello() + self.entered = True + + @signal(dbus_interface=IFACE, signature='') + def Hello(self): + self.bogus = 1 + + @method(dbus_interface=IFACE, in_signature='s', out_signature='') + def World(self, text): + if not self.text: + self.text = text + self.text_received_cb(text) + self.add_hello_handler() + + def add_hello_handler(self): + self.tube.add_signal_receiver(self.hello_cb, 'Hello', IFACE, + path=PATH, sender_keyword='sender') + self.tube.add_signal_receiver(self.sendtext_cb, 'SendText', IFACE, + path=PATH, sender_keyword='sender') + + def hello_cb(self, sender=None): + if sender == self.tube.get_unique_name(): + return + self.tube.get_object(sender, PATH).World(self.text, + dbus_interface=IFACE) + + def sendtext_cb(self, text, sender=None): + if sender == self.tube.get_unique_name(): + return + self.text = text + self.text_received_cb(text) + + @signal(dbus_interface=IFACE, signature='s') + def SendText(self, text): + self.text = text + +class SearchToolbar(gtk.Toolbar): + + __gsignals__ = { + 'address-update': (gobject.SIGNAL_RUN_FIRST, None, [object]), + 'search': (gobject.SIGNAL_RUN_FIRST, None, [object]), + 'zoom-in': (gobject.SIGNAL_RUN_FIRST, None, []), + 'zoom-out': (gobject.SIGNAL_RUN_FIRST, None, []), + 'save-search': (gobject.SIGNAL_RUN_FIRST, None, []), + 'search-update': (gobject.SIGNAL_RUN_FIRST, None, [object]) + } + + def __init__(self, pc): + gtk.Toolbar.__init__(self) + self.ca = pc + + addressLabel = gtk.Label( Constants.istrSearchAddress ) + addressLabel.show() + toolAddressLabel = gtk.ToolItem() + toolAddressLabel.add(addressLabel) + self.insert(toolAddressLabel, -1) + toolAddressLabel.show() + + self._insertSep() + + self.addressField = gtk.Entry() + self.addressField.connect('activate', self._addressActivateCb) + addressItem = gtk.ToolItem() + addressItem.set_expand(True) + addressItem.add(self.addressField) + self.addressField.show() + self.insert( addressItem, -1 ) + addressItem.show() + + self._insertSep() + + searchLabel = gtk.Label( Constants.istrSearchMedia ) + searchLabel.show() + toolSearchLabel = gtk.ToolItem() + toolSearchLabel.add(searchLabel) + self.insert(toolSearchLabel, -1) + toolSearchLabel.show() + + self._insertSep() + + self.searchField = gtk.Entry() + self.searchField.connect('activate', self._searchActivateCb) + searchItem = gtk.ToolItem() + searchItem.set_expand(True) + searchItem.add(self.searchField) + self.searchField.show() + self.insert( searchItem, -1 ) + searchItem.show() + + self._insertSep() + + saveButt = ToolButton("save-search") + saveButt.set_tooltip(Constants.istrSaveSearch) + saveButt.connect('clicked', self._saveCb) + self.insert(saveButt, -1) + saveButt.show() + + def _setTagsString( self, tags ): + self.searchField.set_text(tags) + + def _insertSep( self ): + separator = gtk.SeparatorToolItem() + separator.set_draw(False) + separator.set_expand(False) + separator.set_size_request(Constants.ui_dim_INSET, -1) + self.insert( separator, -1 ) + + + def _addressActivateCb(self, widget): + address = widget.props.text + self.emit('address-update', address) + + + def _saveCb(self, widget): + self.emit('save-search') + + + def _searchActivateCb(self, widget): + search = widget.props.text + self.emit('search-update', search) + + + def _zoomInCb(self, button): + self.emit('zoom-in') + + def _zoomOutCb(self, button): + self.emit('zoom-out') + +class PopUpP5(P5): + def __init__(self): + P5.__init__(self) + self.fill = None + self.stroke = None + self.previewCairoImg = None + self.up = None + self.rt = None + self.scale = None + self.insetX = 0 + self.insetY = 0 + + def updatePopInfo(self, stroke, fill, cairoThumb, up, rt, insetX, insetY, scale): + self.fill = fill + self.stroke = stroke + self.previewCairoImg = cairoThumb + self.up = up + self.rt = rt + self.scale = scale + self.insetX = insetX + self.insetY = insetY + self.queue_draw() + + + def draw(self, ctx, w, h): + #for stroking + self.fillRect( ctx, self.fill, w, h ) + + lw = 5 + hlw = lw/2 + ctx.set_line_width( lw*self.scale ) + ctx.rectangle(0, 0, w, h) + self.setColor(ctx, self.stroke) + ctx.stroke() + + #for the popup bubble's handle + if (not self.up): + ctx.translate(0, h-(3*self.scale)) + if (not self.rt): + ctx.translate(w-((hlw+21)*self.scale), 0) + else: + ctx.translate(hlw*self.scale, 0) + + ctx.rectangle(0, 0, 21*self.scale, 3*self.scale) + self.setColor(ctx, self.fill) + ctx.fill() + + ctx.identity_matrix() + ctx.set_source_surface(self.previewCairoImg, self.insetX, self.insetY) + ctx.paint() + + +class AddToolbar(gtk.Toolbar): + __gsignals__ = { + 'add-media': (gobject.SIGNAL_RUN_FIRST, None, [object]), + 'add-kml': (gobject.SIGNAL_RUN_FIRST, None, [object]), + 'web-media': (gobject.SIGNAL_RUN_FIRST, None, []), + 'delete-media': (gobject.SIGNAL_RUN_FIRST, None, []), + 'measure': (gobject.SIGNAL_RUN_FIRST, None, []), + 'add-info': (gobject.SIGNAL_RUN_FIRST, None, []) + } + + def __init__(self, pc): + gtk.Toolbar.__init__(self) + self.ca = pc + + self.addButt = ToolButton('add-icon') + self.addButt.set_tooltip(Constants.istrAddMedia) + self.insert(self.addButt, -1) + self.addButt.connect('clicked', self._addCb) + + infoButt = ToolButton('corner-info') + infoButt.set_tooltip(Constants.istrAddInfo) + self.insert(infoButt, -1) + infoButt.connect('clicked', self._addInfoCb) + + self._insertSep() + + delButt = ToolButton('delete-icon') + delButt.set_tooltip(Constants.istrDeleteMedia) + self.insert(delButt, -1) + delButt.connect('clicked', self._delCb) + + self._insertSep() + + # add line tools + lineButt = ToolButton('tool-shape-line') + lineButt.set_tooltip(Constants.LineButton) + self.insert(lineButt, -1) + lineButt.connect('clicked', self._lineCb) + polyButt = ToolButton('tool-polygon') + polyButt.set_tooltip(Constants.PolyButton) + self.insert(polyButt,-1) + polyButt.connect('clicked', self._polyCb) + + self._insertSep() + + + #measButt = ToolButton('measure-icon') + #measButt.set_tooltip(Constants.istrMeasure) + #self.insert(measButt, -1) + #measButt.connect('clicked', self._measCb) + + #self._insertSep() + + # OpenStreetMap paste + webButt = ToolButton('static-icon') + webButt.set_tooltip(Constants.istrWebMedia) + self.insert(webButt, -1) + webButt.connect('clicked', self._webCb) + + # Google Paste + staticButt = ToolButton('web-icon') + staticButt.set_tooltip(Constants.istrStaticMaps) + self.insert(staticButt, -1) + staticButt.connect('clicked', self._toStaticCb) + + def _insertSep( self ): + separator = gtk.SeparatorToolItem() + separator.set_draw(False) + separator.set_expand(False) + separator.set_size_request(25 + Constants.ui_dim_INSET, -1) + self.insert( separator, -1 ) + + def _addCb(self, button): + self.ca.showFileLoadBlocker(True) + + fp = FilePicker() + dOb = fp.show() + if (dOb != None): + if (dOb.file_path != None): + if(dOb.metadata['mime_type']=="video/ogg") or (dOb.metadata['mime_type']=="image/jpeg"): + self.emit("add-media", dOb) + elif(dOb.metadata['mime_type']=="audio/ogg"): + self.emit("add-media", dOb) + else: + self.emit("add-kml", dOb) + #else: + # self.emit("add-info") + + self.ca.showFileLoadBlocker(False) + + def _addInfoCb(self, button): + self.emit("add-info") + + def _delCb(self, button): + self.emit("delete-media") + + def _webCb(self, button): + #self.ca.browser.load_uri("http://maptonomy.appspot.com/maps4xo?ajaxPort=" + str(self.ca.__class__.ajaxPort) + "&cometPort=" + str(self.ca.__class__.cometPort) + "&xo=true&lat=" + str(self.ca.NOW_MAP_CENTER_LAT) + "&lng=" + str(self.ca.NOW_MAP_CENTER_LNG) + "&z=" + str(self.ca.NOW_MAP_ZOOM) ) + self.ca.mapPaste() + + def _measCb(self, button): + # start a measure tool (rect area or polyline) - calculation handled in HTML/JavaScript + self.emit("measure") + + def _toStaticCb(self, button): + #self.ca.browser.load_uri("http://maptonomy.appspot.com/ourmap.html?ajaxPort=" + str(self.ca.__class__.ajaxPort) + "&cometPort=" + str(self.ca.__class__.cometPort) + "&xo=true&lat=" + str(self.ca.NOW_MAP_CENTER_LAT) + "&lng=" + str(self.ca.NOW_MAP_CENTER_LNG) + "&zoom=" + str(self.ca.NOW_MAP_ZOOM) ) + self.ca.googlePaste() + + def _lineCb(self, button): + self.ca.lineMode('line') + + def _polyCb(self, button): + self.ca.lineMode('polygon') + +class ServerThread(threading.Thread): + def __init__(self, port, logic): + threading.Thread.__init__(self) + self.server = Server( ("127.0.0.1", port), logic) + + def run(self): + try: + self.server.serve_forever() + except: + self.run() + + def stop(self): + #self.server.shutdown() + r = 2 diff --git a/mapviewer/MediaMarker.js b/mapviewer/MediaMarker.js new file mode 100644 index 0000000..a47b00b --- /dev/null +++ b/mapviewer/MediaMarker.js @@ -0,0 +1,21 @@ +function MediaMarker(){ +return{ + initialize:function(pt, markerServer, markerId, tags, info, icon) + {this.markerServer=markerServer; + this.markerId=markerId; + this.pt=pt; + this.tags=tags; + this.info=info; + if(icon==null){icon="null";} + this.icon=icon; + this.iconDiv=null; + if((this.tags == undefined)||(this.tags==null)) + {this.tags = "";} + }, + getLatLng:function(){return this.pt;}, + get_position:function(){return this.pt;}, + get_info:function(){return this.info;}, + getMarkerServer:function(){return this.markerServer;}, + getMarkerId:function(){return this.markerId;} +}; +}
\ No newline at end of file diff --git a/mapviewer/blue_map_dot.png b/mapviewer/blue_map_dot.png Binary files differnew file mode 100644 index 0000000..196ecc2 --- /dev/null +++ b/mapviewer/blue_map_dot.png diff --git a/mapviewer/magenta_map_dot.png b/mapviewer/magenta_map_dot.png Binary files differnew file mode 100644 index 0000000..4c79ea9 --- /dev/null +++ b/mapviewer/magenta_map_dot.png diff --git a/mapviewer/mapCenter.html b/mapviewer/mapCenter.html new file mode 100644 index 0000000..41d5a9c --- /dev/null +++ b/mapviewer/mapCenter.html @@ -0,0 +1,631 @@ +<html> +<head id="head"> + <title>Offline Map</title> +<script src="MediaMarker.js" type="text/javascript"></script> +<script src="file:///home/olpc/Library/MapPack/Maps.js" type="text/javascript"></script> +<script type="text/javascript"> +var zm=10; +var realZm=800; +var xOff=0; +var yOff=0; +var factor=0.75; +var westBound=-74.44; +var northBound=20.14; +var portMapOn=false; +var markerList=[]; +var lineList=[]; +var editTxt="Edit"; +var addTxt="Add"; +var describeTxt="Describe this place"; +var ajaxPort,cometPort,mID,canvas; +var isMedia=false; +var addingInfo=false; +var iMarker = null; +var addingMedia=false; +var addingLines=false; +var currentLine=-1; +var addingPasteMap=false; +var pasteType=""; +var mediaWin=null; +var isOffXO=false; +function gup(nm){nm=nm.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");var rxS="[\\?&]"+nm+"=([^&#]*)";var rx=new RegExp(rxS);var rs=rx.exec(window.location.href);if(!rs){return null;}else{return rs[1];}} +function init(){ + ajaxPort = gup("ajaxPort"); + cometPort = gup("cometPort"); + lhCometInit(); + canvas = $("mapCover").getContext("2d"); + try{ + //fix all image links + for(var m=0;m<maps.length;m++){ + if(isOffXO){ + maps[m].link="../../MapPack/" + maps[m].link; + } + else{ + maps[m].link="file:///home/olpc/Library/MapPack/" + maps[m].link; + } + if(m>0){ + maps[m].mapdiv=$("overlay-"+maps[m].mapCode); + maps[m].isOn=false; + } + else{ + // set base map + $("mapItself").src=maps[0].link; + } + } + } + catch(e){ + // testing without XO Laptop, assume it's in a neighboring folder + isOffXO=true; + var newScript = document.createElement('script'); + newScript.src = "../../MapPack/Maps.js" + newScript.type = "text/javascript"; + newScript.onload = init; + $("head").appendChild(newScript); + return; + } + /* + var grandG={x:0.5904,y:0.5987,width:0.0168,zoom:18,isOn:false,src:"GrandGoave.jpg"}; + grandG.mapdiv=$("overlay-5"); + var sofMira={x:0.4894,y:0.598,width:0.015,zoom:16,isOn:false,src:"SofMiragoane.jpg"}; + sofMira.mapdiv=$("overlay-3"); + */ + var southWest = toLatLng(0,600); + var northEast = toLatLng(800,0); + var newScript = document.createElement('script'); + newScript.src = "http://127.0.0.1:" + ajaxPort + "/mediaQuery.js?s=" + southWest[0] + "&w=" + southWest[1] + "&n=" + northEast[0] + "&e=" + northEast[1]; + newScript.type = "text/javascript"; + newScript.onload = lhAjax; + $("head").appendChild(newScript); + //addPt(-1.95575,30.437354,"blue crates thing","blue",false); + //addPt(0.021026,32.441184,"blue",false); + //moveTo(18.52876,-73.512198,20); + //addLine("x00x2","#00f",8,[[19,-73],[18.9,-71]]); + //addLine("poly-x00x3","#f00",8,[[19.7,-72.79],[20.1,-73.173],[19.79,-73.31]]); +} +function lineMode(type){ + if(addingLines){ + addingLines = false; + $("mapCover").className = "-moz-grab"; + var newScript = document.createElement('script'); + var dLine = lineList[currentLine]; + var ptOutput = []; + for(var p=0;p<dLine.pts.length;p++){ + ptOutput.push(dLine.pts[p][0] + "|" + dLine.pts[p][1]); + } + ptOutput = ptOutput.join("|"); + newScript.src = "http://127.0.0.1:" + ajaxPort + "/addLine.js?id=" + dLine.id + "&col=" + dLine.color.replace("#","") + "&thick=4&pts=" + ptOutput; + newScript.type = "text/javascript"; + newScript.onload = lhAjax; + $("head").appendChild(newScript); + //showInfo(newScript.src); + currentLine = -1; + updateLines(); + } + else{ + addingLines = true; + $("mapCover").className = "crosshairMap"; + var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz"; + var colorchars = "0123456789abcdef"; + var string_length = 8; + var lineID = ''; + for(var i=0; i<string_length; i++) { + var rnum = Math.floor(Math.random() * chars.length); + lineID += chars.substring(rnum,rnum+1); + } + if(type.indexOf("poly") != -1){ + lineID = "poly-" + lineID; + } + var randColor = ""; + for(var i=0; i<3; i++){ + var rnum = Math.floor(Math.random() * colorchars.length); + randColor += colorchars.substring(rnum,rnum+1); + } + currentLine = lineList.length; + addLine(lineID,randColor,4,[]); + } +} +function placeVertex(event){ + var vert = toLatLng(event.clientX,event.clientY); + lineList[currentLine].pts.push(vert) + updateLines(); +} +function addLine(line_id,line_color,line_thick,line_pts){ + try{ + if(line_pts.indexOf("|") != -1){ + //in |-separated format from Python + line_pts_b = line_pts.split("|"); + line_pts=[]; + for(var lpi=0;lpi<line_pts_b.length;lpi+=2){ + line_pts.push([line_pts_b[lpi],line_pts_b[lpi+1]]); + } + } + } + catch(e){} + for(var l=0;l<lineList.length;l++){ + if(lineList[l].id==line_id){ + lineList[l].color="#"+line_color; + lineList[l].thickness=line_thick; + lineList[l].pts=line_pts; + updateLines(); + return; + } + } + lineList.push({id:line_id,color:"#"+line_color,thickness:line_thick,pts:line_pts}); + if(!addingLines){ + currentLine=lineList.length-1; + addingLines=true; + lineMode('new'); + } +} +function updateLines(){ + if(lineList.length == 0){ return } + $("mapCover").width=$("mapCover").width; + for(var l=0;l<lineList.length;l++){ + var dLine=lineList[l]; + if(dLine.pts.length < 2){continue} + var lastPt=dLine.pts[0]; + var drewLast=false; + var firstPt=lastPt; + if(dLine.id.indexOf("poly")==-1){ + canvas.strokeStyle=dLine.color; + canvas.lineWidth=dLine.thickness; + } + else{ + canvas.fillStyle=dLine.color; + canvas.lineWidth=0; + //canvas.strokeStyle="#000"; + drewLast=true; + } + if(inBounds(pixel(lastPt[0],lastPt[1]))||drewLast){ + drewLast = true; + canvas.beginPath(); + canvas.moveTo(parseInt(pixel(lastPt[0],lastPt[1])[0]),parseInt(pixel(lastPt[0],lastPt[1])[1])); + } + for(var p=1;p<dLine.pts.length;p++){ + var thisPt=dLine.pts[p]; + if(drewLast){ + canvas.lineTo(parseInt(pixel(thisPt[0],thisPt[1])[0]),parseInt(pixel(thisPt[0],thisPt[1])[1])); + } + else{ + if(inBounds(pixel(lastPt[0],lastPt[1]))){ + drewLast=true; + canvas.beginPath(); + canvas.moveTo(parseInt(pixel(lastPt[0],lastPt[1])[0]),parseInt(pixel(lastPt[0],lastPt[1])[1])); + canvas.lineTo(parseInt(pixel(thisPt[0],thisPt[1])[0]),parseInt(pixel(thisPt[0],thisPt[1])[1])); + } + } + lastPt=thisPt; + } + if(drewLast){ + if(dLine.id.indexOf("poly")==-1){ + canvas.stroke(); + } + else{ + canvas.closePath(); + canvas.fill(); + } + } + } + if(currentLine>=0){ + //draw a square at each point in the line or polygon + for(var p=0;p<lineList[currentLine].pts.length;p++){ + var thisPt=lineList[currentLine].pts[p]; + canvas.fillStyle="#000"; + canvas.fillRect(parseInt(pixel(thisPt[0],thisPt[1])[0])-6,parseInt(pixel(thisPt[0],thisPt[1])[1])-6,12,12); + } + } +} +function inBounds(px){ + if(px[0]<0){return false;} + if(px[0]>820){return false;} + if(px[1]<0){return false;} + if(px[1]>520){return false;} + return true; +} +function mapPaste(type){ + addingPasteMap=true; + pasteType=type; + $("mapCover").className = "crosshairMap"; +} +function placeOverlay(event){ + addingPasteMap=false; + $("mapCover").className="-moz-grab"; + var ctr = toLatLng(event.clientX,event.clientY); + var standardZoom = 17; + if(zm < 13){ + standardZoom = 10; + } + else if(zm < 16){ + standardZoom = 12; + } + else if(zm < 18){ + standardZoom = 14; + } + else{ + standardZoom = 15; + } + addPt(toLatLng(304,152)[0],toLatLng(304,152)[1],"","orange",false); + var minimap = markerList[markerList.length-1].iconDiv; + minimap.onclick=""; + if(pasteType=='osm'){ + minimap.src="http://pafciu17.dev.openstreetmap.org/?module=map&width=640&height=404¢er="+ctr[1]+","+ctr[0]+"&zoom="+standardZoom; + } + else{ + minimap.src="http://maps.google.com/maps/api/staticmap?sensor=false&key=ABQIAAAAxkKtrWN5q-vPTLRVmO_r6RRFDCLHCbUG3VrjXnZmMRXvQdFL3RS-b-ld9hTrkIgQlYsxPQ1kYq6y9A&size=640x404&format=jpg¢er="+ctr[0]+","+ctr[1]+"&zoom="+standardZoom+"&maptype=hybrid"; + } + minimap.onclick=""; + minimap.style.zIndex=-1; + var bds = getStandardBounds(ctr,standardZoom); // returns [ sw=[lat,lng] , ne=[lat,lng] ] + var osmMap={north:bds[1][0],east:bds[1][1],west:bds[0][1],mapCode:-1,showZoom:zm-1,isOn:true}; + osmMap.mapdiv=minimap; + maps.push(osmMap); + markerList.pop(); + portMapGo(); +} +function filterTags(sTerms){ + sTerms = unescape(sTerms).toLowerCase(); + sTerms = replaceAll(sTerms," ",""); + for(var m=0;m<markerList.length;m++){ + var formattedInfo = replaceAll(markerList[m].info.toLowerCase()," ",""); + if(formattedInfo.indexOf(sTerms) != -1){ + moveTo(markerList[m].pt[0],markerList[m].pt[1]); + closeiWin(); + isMedia=false; + showInfo(markerList[m].info); + var bigMarker = markerList[m].iconDiv; + bigMarker.width=parseInt(1.8*bigMarker.width); + setTimeout(function(){bigMarker.width/=1.8},5000); + return; + } + } +} +function replaceAll(src,old,fix){ + while(src.indexOf(old) != -1){ + src = src.replace(old,fix); + } + return src; +} +function lhCometInit() +{ var newScript = document.createElement('script'); + var d = new Date(); + newScript.src = "http://127.0.0.1:" + cometPort + "/comet.js?t="+d.getTime() + newScript.type = "text/javascript"; + newScript.onload = lhCometResponse; + $("head").appendChild(newScript); +} +function lhCometResponse(){ lhCometInit(); } +function moveTo(lat,lng){moveTo(lat,lng,zm)} +function moveTo(lat,lng,zoom){ + while(zoom > zm){zoomIn()} + while(zoom < zm){zoomOut()} + var currentOffset = pixel(lat,lng); + xOff+=(410-1*currentOffset[0]); + yOff+=(260-1*currentOffset[1]); + $("mapItself").style.left=xOff; + $("mapItself").style.top=yOff; + portMapGo(); +} +function preAddInfo(){ + closeiWin(); + addingInfo=true; + $("mapCover").className = "crosshairMap"; +} +function placeInfoMarker(event){ + addingInfo = false; + var loc = toLatLng(event.clientX,event.clientY); + addPt(loc[0],loc[1],describeTxt,"magenta",true); + addedMedia(); +} +function addPt(lat,lng,text,color,showNow){ + for(var i=0;i<markerList.length;i++){ + if(markerList[i].pt[0] == lat){ + if(markerList[i].pt[1] == lng){ + markerList[i].info = unescape(text); + return; + } + } + } + var mk = MediaMarker(); + mID="info-"+markerList.length; + mk.initialize([lat,lng],null,mID,null,unescape(text),color+"_map_dot.png"); + mk.iconDiv=document.createElement("img"); + mk.iconDiv.className="marker"; + mk.iconDiv.src=mk.icon; + var px=pixel(mk.pt[0],mk.pt[1]); + mk.iconDiv.style.left=parseInt(px[0]-mk.iconDiv.width/2)+"px"; + mk.iconDiv.style.top=parseInt(px[1]-mk.iconDiv.height/2)+"px"; + mk.iconDiv.style.position="absolute"; + mk.iconDiv.style.display="block"; + mk.iconDiv.onclick=function(event){mID=mk.markerId;isMedia=false;showInfo(mk.get_info());} + document.body.appendChild(mk.iconDiv); + markerList.push(mk); + if(showNow){ + isMedia = false; + mID=mk.getMarkerId(); + showInfo(text); + } +} +function pixel(lat,lng){ + //top left is estimated to be 20.18025 N, -74.62984 W + var latdiff = (maps[0].north - 1.00*lat)*latFactor*Math.pow(1.35,zm-10.0); + var lngdiff = (1.00*lng - maps[0].west)*lngFactor*Math.pow(1.35,zm-10.0); + var x = lngdiff*1.00 + xOff*1.00; + var y = latdiff*1.00 + yOff*1.00; + return [x,y]; +} +function zoomIn(){ + var centerLL = toLatLng(410,260); + zm+=1; + realZm*=1.35; + $("mapItself").style.width=parseInt(realZm)+"px"; + xOff=1*$("mapItself").style.left.replace("px","").replace("pt",""); + yOff=1*$("mapItself").style.top.replace("px","").replace("pt",""); + moveTo(centerLL[0],centerLL[1]); +} +function zoomOut(){ + var centerLL = toLatLng(410,260); + zm-=1; + realZm/=1.35; + $("mapItself").style.width=parseInt(realZm); + xOff=1*$("mapItself").style.left.replace("px","").replace("pt",""); + yOff=1*$("mapItself").style.top.replace("px","").replace("pt",""); + moveTo(centerLL[0],centerLL[1]); +} +function moveLeft(){ + xOff+=2000/zm; + $("mapItself").style.left=parseInt(xOff); + portMapGo(); +} +function moveRight(){ + xOff-=2000/zm; + $("mapItself").style.left=parseInt(xOff); + portMapGo(); +} +function moveUp(){ + yOff+=2000/zm; + $("mapItself").style.top=parseInt(yOff); + portMapGo(); +} +function moveDown(){ + yOff-=2000/zm; + $("mapItself").style.top=parseInt(yOff); + portMapGo(); +} +function mrkWithId(id){ + for(var i=0;i<markerList.length;i++){ + if(markerList[i].getMarkerId() == id){ + return markerList[i]; + } + } +} +function editInfoMarker(id){ + iMarker = mrkWithId(id); + mID = id; + isMedia = true; + showInfo("<textarea id='infoText' style='height:400;width:350;'>" + iMarker.get_info() + "</textarea><br/><input type='button' class='centerButton' value='" + addTxt + "' onclick='updateInfo(\"" + iMarker.getMarkerId() + "\")'/>"); +} +function updateInfo(id){ + closeiWin(); + var iMarker = mrkWithId(id); + iMarker.info = $("infoText").value; + var loc = iMarker.getLatLng(); + var newScript = document.createElement('script'); + newScript.src = "http://127.0.0.1:" + ajaxPort + "/addInfoMarker.js?lat=" + loc[0] + "&lng=" + loc[1] + "&info=" + iMarker.info + "&icon=" + iMarker.icon + "&isNew=True"; + newScript.type = "text/javascript"; + newScript.onload = lhAjax; + $("head").appendChild(newScript); +} +function lhAjax(){} +function updateMarkers(){ + for(var m=0;m<markerList.length;m++){ + var lat=markerList[m].pt[0]; + var lng=markerList[m].pt[1]; + var px=pixel(lat,lng); + markerList[m].iconDiv.style.left=parseInt(px[0]-markerList[m].iconDiv.width/2); + markerList[m].iconDiv.style.top=parseInt(px[1]-markerList[m].iconDiv.height/2); + } +} +function getStandardBounds(centerLL,zoom){ + var sw=[1*centerLL[0]-0.55618*Math.pow(2,(9-1*zoom)),centerLL[1]*1 - 0.98877*Math.pow(2,(9-1*zoom))]; + var ne=[1*centerLL[0]+0.55618*Math.pow(2,(9-1*zoom)),centerLL[1]*1 + 0.98877*Math.pow(2,(9-1*zoom))]; + return [sw,ne]; +} +function portMapGo(){ + //skip m=0, that's the base map + for(var m=1;m<maps.length;m++){ + if(zm>=maps[m].showZoom){ + if((pixel(maps[m].north,maps[m].east)[0]<0)||(pixel(maps[m].north,maps[m].west)[0]>820)){ + maps[m].isOn=false; + if(maps[m].mapCode > 0){ + if(maps[m].mapdiv.src.indexOf(maps[m].link.replace("../../",""))!=-1){ + maps[m].mapdiv.style.display="none"; + } + } + else{ + maps[m].mapdiv.style.display="none"; + } + } + else if((pixel(maps[m].north,maps[m].west)[1]+1*maps[m].mapdiv.height<0)||(pixel(maps[m].north,maps[m].west)[1]>620)){ + maps[m].isOn=false; + if(maps[m].mapCode > 0){ + if(maps[m].mapdiv.src.indexOf(maps[m].link.replace("../../",""))!=-1){ + maps[m].mapdiv.style.display="none"; + } + } + else{ + maps[m].mapdiv.style.display="none"; + } + } + else{ + maps[m].isOn=true; + if(maps[m].mapCode > 0){ + //shared image-div + if(maps[m].mapdiv.src.indexOf(maps[m].link.replace("../../",""))==-1){ + //swap between major maps + maps[m].mapdiv.src=maps[m].link; + } + } + var topleft = pixel(maps[m].north,maps[m].west); + var mWidth = pixel(maps[m].north,maps[m].east)[0]-topleft[0]; + maps[m].mapdiv.style.width = parseInt(mWidth); + maps[m].mapdiv.style.left=parseInt(topleft[0]); + maps[m].mapdiv.style.top=parseInt(topleft[1]); + maps[m].mapdiv.style.display="block"; + } + } + else{ + if(maps[m].mapdiv.src.indexOf(maps[m].src)!=-1){ + maps[m].mapdiv.style.display="none"; + } + maps[m].isOn=false; + } + } + updateMarkers(); + updateLines(); +} +function showInfo(er){ + //if(er==''){return} + if(!isMedia){ + if(er.indexOf("::") != -1){ + if(er.indexOf('wiki::') != -1){ er = '<b>' + er.replace('wiki::','</b><iframe height="400" width="350" src="http://simple.m.wikipedia.org/wiki/') + '"></iframe>'; } + else if(er.indexOf('img::') != -1){ er = '<b>' + er.replace('img::','</b><br/><img style="max-height:400;max-width:350;" src="') + '"/>'; } + else if(er.indexOf('pic::') != -1){ er = '<b>' + er.replace('pic::','</b><br/><img style="max-height:400;max-width:350;" src="') + '"/>'; } + else if(er.indexOf('google::') != -1){ er = er.replace('google::','<iframe height="400" width="350" src="http://google.com/search?q=') + '"></iframe>'; } + else if(er.indexOf('vid::') != -1){ er = '<b>' + er.replace('vid::','</b><br/><object width="400" height="350" type="application/ogg" data="') + '"></object>'; } + else if((er.indexOf('wiki') != -1)&&(er.indexOf('::') > er.indexOf('wiki'))){ + var erBegin = er.substring(0,er.lastIndexOf('wiki')); + er = er.substring(er.lastIndexOf('wiki')); + var wikiCode = er.substring(4,er.indexOf('::')); + er = er.substring(er.indexOf('::') + 2); + er = '<b>' + erBegin + '</b><iframe height="400" width="350" src="http://' + wikiCode + '.m.wikipedia.org/wiki/' + er + '"></iframe>'; + } + } + er+="<br/><input type='button' class='centerButton' value='" + editTxt + "' onclick='editInfoMarker(" + '"' + mID + '"' + ")'/>"; + } + $("iWin").innerHTML = er; + $("iEnc").style.display = "block"; + $("iWin").style.display = "block"; +} +function closeiWin(){ + $("iWin").style.display = "none"; + $("iEnc").style.display = "none"; + mediaWin=null; + var newScript = document.createElement('script'); + var d = new Date(); + newScript.src = "http://127.0.0.1:" + ajaxPort + "/hideMedia.js?t="+d.getTime() + newScript.type = "text/javascript"; + $("head").appendChild(newScript); +} +function toLatLng(x,y){ + x+=".000000"; + y+=".000000"; + x-=xOff; + y-=yOff; + var latdiff = y / latFactor / Math.pow(1.35,zm-10); + var lngdiff = x / lngFactor / Math.pow(1.35,zm-10); + var mapLat = maps[0].north - latdiff; + var mapLng = maps[0].west + lngdiff; + return [mapLat.toFixed(5),mapLng.toFixed(5)]; +} +function updateLoc(){ + var ctr = toLatLng(410,260); + var newScript = document.createElement('script'); + if(mediaWin != null){ + newScript.src = "http://127.0.0.1:" + ajaxPort + "/updateLocation.js?lat=" + ctr[0] + "&lng=" + ctr[1] + "&z=" + zm + "&x=" + ($("iEnc").offsetLeft*1 + 25) + "&y=" + ($("iEnc").offsetTop*1 + 30); + } + else{ + newScript.src = "http://127.0.0.1:" + ajaxPort + "/updateLocation.js?lat=" + ctr[0] + "&lng=" + ctr[1] + "&z=" + zm + "&x=-1&y=-1"; + } + newScript.type = "text/javascript"; + newScript.onload = lhAjax; + $("head").appendChild(newScript); +} +function infoImg(id){ + showMedia('<div style="color:#fff;font-size:45pt;width:200;">a<br/>b<br/>a<br/>b<br/><Media>a</div>'); + var newScript=document.createElement('script'); + newScript.src="http://127.0.0.1:" + ajaxPort + "/showMedia.js?id=" + id + "&x=" + ($("iEnc").offsetLeft*1 + 25) + "&y=" + ($("iEnc").offsetTop*1 + 10) + "&up=true&rt=true"; + newScript.type="text/javascript"; + newScript.onload=lhAjax; + $("head").appendChild(newScript); +} +function showMedia(er){ + mediaWin = $("iWin"); + $("iWin").innerHTML = er; + $("iEnc").style.display = "block"; + $("iWin").style.display = "block"; +} +function preAddMedia() +{ if(!mediaWin){closeiWin();} + addingMedia = true; + updateLoc(); + $("mapCover").className="crosshairMap"; +} +function placeAddMedia(event){ + var pt=toLatLng(event.clientX, event.clientY); + var newScript=document.createElement('script'); + newScript.src="http://127.0.0.1:" + ajaxPort + "/placeAddMedia.js?lat=" + pt[0] + "&lng=" + pt[1]; + newScript.type="text/javascript"; + newScript.onload=lhAjax; + $("head").appendChild(newScript); + addedMedia(); +} +function postAddMedia( lat, lng, url, basename, tags ){ + var mk=MediaMarker(); + mk.initialize([lat,lng],url,basename,tags); + mk.iconDiv=document.createElement("img"); + mk.iconDiv.className="marker"; + mk.iconDiv.src="orange_map_dot.png"; + var px=pixel(mk.pt[0],mk.pt[1]); + mk.iconDiv.style.left=parseInt(px[0]-mk.iconDiv.width/2)+"px"; + mk.iconDiv.style.top=parseInt(px[1]-mk.iconDiv.height/2)+"px"; + mk.iconDiv.style.position="absolute"; + mk.iconDiv.style.display="block"; + mk.iconDiv.onclick=function(event){mID=mk.markerId;infoImg(mID);} + document.body.appendChild(mk.iconDiv); + markerList.push(mk); + hidePics(); + addedMedia(); + infoImg(mk.markerId); + updateLoc(); +} +function addedMedia() +{ addingMedia=false; + $("mapCover").className="-moz-grab"; +} +function clickToZoom(event){ + var ctr = toLatLng(event.clientX,event.clientY); + moveTo(ctr[0],ctr[1],zm+1); +} +function $(id){return document.getElementById(id)} +</script> +<style type="text/css"> +body{font-family:arial} +#iEnc{position:absolute;left:150;top:30;-moz-border-radius:10;border:2px solid #000;background:#fff;width:390;padding:3;padding-left:10;display:none;z-index:10} +input.cBtn{float:right} +input:hover{color:#000;cursor:pointer} +canvas.crosshairMap{cursor:crosshair} +img.marker{cursor:pointer} +</style> +</head> +<body onload="init()"> + <img id="mapItself" src="" style="position:absolute;left:0px;top:0px;width:800px;z-Index:-5"/> + <div id="miniMaps"> + <img id="overlay-1" src="orange_map_dot.png" style="position:absolute;display:none;z-index:-4;"/> + <img id="overlay-2" src="orange_map_dot.png" style="position:absolute;display:none;z-index:-4;"/> + <img id="overlay-3" src="orange_map_dot.png" style="position:absolute;display:none;z-index:-4;"/> + <img id="overlay-4" src="orange_map_dot.png" style="position:absolute;display:none;z-index:-4;"/> + <img id="overlay-5" src="orange_map_dot.png" style="position:absolute;display:none;z-index:-4;"/> + </div> + <canvas id="mapCover" style="position:absolute;left:0px;top:0px;" width="820" height="520" onclick="if(addingMedia){placeAddMedia(event)}if(addingInfo){placeInfoMarker(event)}if(addingLines){placeVertex(event)}if(addingPasteMap){placeOverlay(event)}" ondblclick="clickToZoom(event)"></canvas> + <div style="position:absolute;left:20px;top:80px;background-color:#fff;border:1px solid #000;z-index:100;"> + <input type="button" value="+" style="font-size:12pt;z-index:101;" onclick="zoomIn()"/> + <input type="button" value="-" style="font-size:12pt;z-index:101;" onclick="zoomOut()"/> + <table> + <tr><td></td><td><input type="button" value="↑" style="font-size:12pt;z-index:101;" onclick="moveUp()"/></td><td></td></tr> + <tr><td><input type="button" value="←" style="font-size:12pt;z-index-101;" onclick="moveLeft()"/></td><td></td><td><input type="button" value="→" style="font-size:12pt;" onclick="moveRight()"/></td></tr> + <tr><td></td><td><input type="button" value="↓" style="font-size:12pt;z-index:101;" onclick="moveDown()"/></td><td></td></tr> + </table> + </div> + <div id="iEnc"><input type="button" class="cBtn" onclick="closeiWin()" value="x"/><div id="iWin"></div></div> +</body> +</html> diff --git a/mapviewer/orange_map_dot.png b/mapviewer/orange_map_dot.png Binary files differnew file mode 100644 index 0000000..b3fa7d4 --- /dev/null +++ b/mapviewer/orange_map_dot.png diff --git a/model.py b/model.py new file mode 100644 index 0000000..2a2cf14 --- /dev/null +++ b/model.py @@ -0,0 +1,149 @@ +# Copyright (c) 2008, Media Modifications Ltd. + +#Permission is hereby granted, free of charge, to any person obtaining a copy +#of this software and associated documentation files (the "Software"), to deal +#in the Software without restriction, including without limitation the rights +#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +#copies of the Software, and to permit persons to whom the Software is +#furnished to do so, subject to the following conditions: + +#The above copyright notice and this permission notice shall be included in +#all copies or substantial portions of the Software. + +#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +#THE SOFTWARE. + +from constants import Constants +from recorded import Recorded +from color import Color +from instance import Instance +import utils +from recorded import Recorded + +import gtk +import os +import shutil +import StringIO + +class Model: + def __init__(self, ca): + self.ca = ca + + self.recs = [] + self.savedMaps = [] + self.infoMarkers = {} + self.lineData = {} + + def getMediaByThumbFilename(self, id): + for i in range(len(self.recs)): + if (self.recs[i].thumbFilename == id): + return self.recs[i] + + return None + + + def getMediaByDatastoreId(self, id): + for i in range(len(self.recs)): + if (self.recs[i].datastoreId == id): + return self.recs[i] + + return None + + + def addMedia(self, lat, lon, datastoreOb): + rec = Recorded() + + #default photo + rec.type = Constants.TYPE_PHOTO + if((datastoreOb.metadata['mime_type'] == "video/ogg") or (datastoreOb.metadata['mime_type'] == "audio/ogg")): + rec.type = Constants.TYPE_VIDEO + + rec.source = Recorded.SOURCE_DATASTORE + rec.datastoreOb = datastoreOb + rec.datastoreId = datastoreOb.object_id + + rec.tags = "" + if (datastoreOb.metadata.has_key("tags")): + rec.tags = datastoreOb.metadata['tags'] + + rec.latitude = lat + rec.longitude = lon + + + colors = datastoreOb.metadata['icon-color'] + colorStroke, colorFill = colors.split(",") + rec.colorStroke = Color() + rec.colorStroke.init_hex(colorStroke) + rec.colorFill = Color() + rec.colorFill.init_hex(colorFill) + + thumbPixbuf = None + if (datastoreOb.metadata.has_key("preview")): + try: + thumb = datastoreOb.metadata['preview'] + if thumb[1:4] == 'PNG': + pbl = gtk.gdk.PixbufLoader() + pbl.write(thumb) + pbl.close() + thumbPixbuf = pbl.get_pixbuf() + else: + thumbPixbuf = utils.getPixbufFromString(thumb) + except: + pass + + if (thumbPixbuf == None): + #try to create the thumbnail yourself... + if (rec.type == Constants.TYPE_PHOTO): + try: + #load in the image, make a thumbnail + thumbPixbuf = gtk.gdk.pixbuf_new_from_file_at_size(rec.getFilepath(), 320, 240) + except: + pass + + if (thumbPixbuf == None): + #i give up. load in a blank image from the activity itself. + thumbPath = os.path.join(self.ca.htmlPath, "1.jpg") + thumbPixbuf = gtk.gdk.pixbuf_new_from_file(thumbPath) + + + thumbFilepath = os.path.join(Instance.instancePath, "thumb.png") + thumbFilepath = utils.getUniqueFilepath(thumbFilepath, 0) + thumbPixbuf.save( thumbFilepath, "png", {} ) + rec.thumbFilename = os.path.basename(thumbFilepath) + + self.recs.append(rec) + + return rec + + def setInfo( self, lat, lng, info, icon ): + self.infoMarkers[lat+','+lng] = lat + ";~" + lng + ";~" + info + ";~" + icon + + def setLine( self, id, color, thickness, pts ): + self.lineData[id] = id + ";~" + color + ";~" + thickness + ";~" + pts + + def addSavedMap( self, smap ): + self.savedMaps.append(smap) + + def getMediaResponse(self, s, w, n, e): + r = "" + + for i in range( len(self.recs) ): + rec = self.recs[i] + #r = r + "var m" + str(i) + " = new MediaMarker( [" + str(rec.latitude) + "," + str(rec.longitude) + "], '" + rec.getThumbUrl() + "', '" + rec.getThumbBasename() + "', null, null, 'blue_map_dot.png');" + #r = r + "markerList.push(m" + str(i) + ");" + r = r + "postAddMedia(" + str(rec.latitude) + "," + str(rec.longitude) + ",'" + rec.getThumbUrl() + "','" + rec.getThumbBasename() + "','');" + + for k, i in self.infoMarkers.iteritems(): + iMarker = i.split(";~") + r = r + "addPt(" + iMarker[0] + "," + iMarker[1] + ",'" + iMarker[2] + "','blue',false);" + + for k, i in self.lineData.iteritems(): + iLine = i.split(";~") + r = r + "addLine('" + iLine[0] + "','" + iLine[1] + "','" + iLine[2] + "','" + iLine[3] + "');" + + return r @@ -0,0 +1,86 @@ +# Copyright (c) 2008, Media Modifications Ltd. + +#Permission is hereby granted, free of charge, to any person obtaining a copy +#of this software and associated documentation files (the "Software"), to deal +#in the Software without restriction, including without limitation the rights +#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +#copies of the Software, and to permit persons to whom the Software is +#furnished to do so, subject to the following conditions: + +#The above copyright notice and this permission notice shall be included in +#all copies or substantial portions of the Software. + +#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +#THE SOFTWARE. + +import gtk +from gtk import gdk +import gobject +import cairo +import math + + +class P5(gtk.DrawingArea): + def __init__(self, button=False): + super(P5, self).__init__() + self.connect("expose_event", self.expose) + if (button): + self.add_events(gdk.BUTTON_PRESS_MASK | gdk.BUTTON_RELEASE_MASK | gdk.POINTER_MOTION_MASK) + self.connect("button_press_event", self._buttonPress) + + + def expose(self, widget, event): + ctx = widget.window.cairo_create() + + # set a clip region for the expose event + ctx.rectangle(event.area.x, event.area.y, event.area.width, event.area.height) + ctx.clip() + + rect = widget.allocation + self.draw( ctx, rect.width, rect.height ) + + + def redraw_canvas(self): + #called from update + if self.window: + alloc = self.get_allocation() + self.queue_draw_area(0, 0, alloc.width, alloc.height) + self.window.process_updates(True) + + + def update(self): + #paint thread -- call redraw_canvas, which calls expose + self.redraw_canvas() + + + def fillRect( self, ctx, col, w, h ): + self.setColor( ctx, col ) + + ctx.line_to(0, 0) + ctx.line_to(w, 0) + ctx.line_to(w, h) + ctx.line_to(0, h) + ctx.close_path() + + ctx.fill() + + + def setColor( self, ctx, col ): + if (not col._opaque): + ctx.set_source_rgba( col._r, col._g, col._b, col._a ) + else: + ctx.set_source_rgb( col._r, col._g, col._b ) + + + def _buttonPress(self, widget, event): + self.fireButton() + + + def fireButton( self ): + #for extending + pass
\ No newline at end of file diff --git a/photocanvas.py b/photocanvas.py new file mode 100644 index 0000000..9346066 --- /dev/null +++ b/photocanvas.py @@ -0,0 +1,65 @@ +import cairo +import gtk +import gobject +from gtk import gdk + +from p5 import P5 +from constants import Constants + +class PhotoCanvas(P5): + + def __init__(self): + P5.__init__(self) + self.img = None + self.drawImg = None + self.SCALING_IMG_ID = 0 + self.cacheWid = -1 + self.modify_bg( gtk.STATE_NORMAL, Constants.colorBlack.gColor ) + self.modify_bg( gtk.STATE_INSENSITIVE, Constants.colorBlack.gColor ) + + + def draw(self, ctx, w, h): + self.fillRect( ctx, Constants.colorBlack, w, h ) + + if (self.img != None): + + if (w == self.img.get_width()): + self.cacheWid == w + self.drawImg = self.img + + #only scale images when you need to, otherwise you're wasting cycles, fool! + if (self.cacheWid != w): + if (self.SCALING_IMG_ID == 0): + self.drawImg = None + self.SCALING_IMG_ID = gobject.idle_add( self.resizeImage, w, h ) + + if (self.drawImg != None): + #center the image based on the image size, and w & h + ctx.set_source_surface(self.drawImg, (w/2)-(self.drawImg.get_width()/2), (h/2)-(self.drawImg.get_height()/2)) + ctx.paint() + + self.cacheWid = w + + + def setImage(self, img): + self.cacheWid = -1 + self.img = img + self.drawImg = None + self.queue_draw() + + + def resizeImage(self, w, h): + self.SCALING_IMG_ID = 0 + if (self.img == None): + return + + #use image size in case 640 no more + scaleImg = cairo.ImageSurface(cairo.FORMAT_ARGB32, w, h) + sCtx = cairo.Context(scaleImg) + sScl = (w+0.0)/(self.img.get_width()+0.0) + sCtx.scale( sScl, sScl ) + sCtx.set_source_surface( self.img, 0, 0 ) + sCtx.paint() + self.drawImg = scaleImg + self.cacheWid = w + self.queue_draw() diff --git a/po/es.po b/po/es.po new file mode 100644 index 0000000..f01d56c --- /dev/null +++ b/po/es.po @@ -0,0 +1,99 @@ +#: activity/activity.info:2 +msgid "Map" +msgstr "Mapa" + +#: constants.py:26 +msgid "Edit" +msgstr "Editar" + +#: constants.py:27 +msgid "Search" +msgstr "Buscar" + +#: constants.py:28 +msgid "Find:" +msgstr "Busca:" + +#: constants.py:29 +msgid "Tags:" +msgstr "Palabras:" + +#: constants.py:30 +msgid "Save Search" +msgstr "Recordar Mapa" + +#: constants.py:31 +msgid "Connecting to Map Server" +msgstr "Está Connectando" + +#: constants.py:32 +msgid "Zoom In" +msgstr "Magnificar" + +#: constants.py:33 +msgid "Zoom Out" +msgstr "Magnificar Menos" + +#: constants.py:34 +msgid "Save" +msgstr "Recordar" + +#: constants.py:35 +msgid "Density" +msgstr "Density" + +#: constants.py:36 +msgid "Saved Map" +msgstr "Mapa recordado" + +#: constants.py:37 +msgid "Describe Map" +msgstr "Escribe sobre esto" + +#: constants.py:38 +msgid "Remove Map" +msgstr "Cancelación" + +#: constants.py:39 +msgid "Copy to Clipboard" +msgstr "Copiar" + +#: constants.py:40 +msgid "Add Media" +msgstr "Foto nuevo" + +#: constants.py:41 +msgid "Add Info" +msgstr "Escribir" + +#: constants.py:42 +msgid "Delete" +msgstr "Cancelar" + +#: constants.py:43 +msgid "Library" +msgstr "Biblioteca" + +#: constants.py:44 +msgid "Measure" +msgstr "Medición" + +#: constants.py:45 +msgid "OurMaps" +msgstr "NuestrosMapas" + +#: constants.py:46 +msgid "Latitude:" +msgstr "Latitud:" + +#: constants.py:47 +msgid "Longitude:" +msgstr "Longitud:" + +#: constants.py:48 +msgid "Description:" +msgstr "Descripción:" + +#: constants.py:49 +msgid "lang=en" +msgstr "lang=es" diff --git a/po/map.pot b/po/map.pot new file mode 100644 index 0000000..8b191ef --- /dev/null +++ b/po/map.pot @@ -0,0 +1,113 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2008-03-10 11:08-0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +#: activity/activity.info:2 +msgid "Map" +msgstr "" + +#: constants.py:26 +msgid "Edit" +msgstr "" + +#: constants.py:27 +msgid "Search" +msgstr "" + +#: constants.py:28 +msgid "Find:" +msgstr "" + +#: constants.py:29 +msgid "Tags:" +msgstr "" + +#: constants.py:30 +msgid "Save Search" +msgstr "" + +#: constants.py:31 +msgid "Connecting to Map Server" +msgstr "" + +#: constants.py:32 +msgid "Zoom In" +msgstr "" + +#: constants.py:33 +msgid "Zoom Out" +msgstr "" + +#: constants.py:34 +msgid "Save" +msgstr "" + +#: constants.py:35 +msgid "Density" +msgstr "" + +#: constants.py:36 +msgid "Saved Map" +msgstr "" + +#: constants.py:37 +msgid "Describe Map" +msgstr "" + +#: constants.py:38 +msgid "Remove Map" +msgstr "" + +#: constants.py:39 +msgid "Copy to Clipboard" +msgstr "" + +#: constants.py:40 +msgid "Add Media" +msgstr "" + +#: constants.py:41 +msgid "Add Info" +msgstr "" + +#: constants.py:42 +msgid "Delete" +msgstr "" + +#: constants.py:43 +msgid "Library" +msgstr "" + +#: constants.py:44 +msgid "Measure" +msgstr "" + +#: constants.py:45 +msgid "OurMaps" +msgstr "" + +#: constants.py:46 +msgid "Latitude:" +msgstr "" + +#: constants.py:47 +msgid "Longitude:" +msgstr "" + +#: constants.py:48 +msgid "Description:" +msgstr ""
\ No newline at end of file diff --git a/po/mn.po b/po/mn.po new file mode 100644 index 0000000..433bdce --- /dev/null +++ b/po/mn.po @@ -0,0 +1,99 @@ +#: activity/activity.info:2 +msgid "Map" +msgstr "Газрын зураг" + +#: constants.py:26 +msgid "Edit" +msgstr "хийх" + +#: constants.py:27 +msgid "Search" +msgstr "хайлт" + +#: constants.py:28 +msgid "Find:" +msgstr "олох:" + +#: constants.py:29 +msgid "Tags:" +msgstr "--" + +#: constants.py:30 +msgid "Save Search" +msgstr "хадгалах" + +#: constants.py:31 +msgid "Connecting to Map Server" +msgstr "Connecting" + +#: constants.py:32 +msgid "Zoom In" +msgstr "ойрхон" + +#: constants.py:33 +msgid "Zoom Out" +msgstr "алс" + +#: constants.py:34 +msgid "Save" +msgstr "хадгалах" + +#: constants.py:35 +msgid "Density" +msgstr "Density" + +#: constants.py:36 +msgid "Saved Map" +msgstr "газрын зураг" + +#: constants.py:37 +msgid "Describe Map" +msgstr "тодорхойлох" + +#: constants.py:38 +msgid "Remove Map" +msgstr "арилгах" + +#: constants.py:39 +msgid "Copy to Clipboard" +msgstr "хувъ" + +#: constants.py:40 +msgid "Add Media" +msgstr "фото нэмэх" + +#: constants.py:41 +msgid "Add Info" +msgstr "бичих" + +#: constants.py:42 +msgid "Delete" +msgstr "хадгалах" + +#: constants.py:43 +msgid "Library" +msgstr "номын сан" + +#: constants.py:44 +msgid "Measure" +msgstr "зай | орон зай" + +#: constants.py:45 +msgid "OurMaps" +msgstr "хат газрын зураг" + +#: constants.py:46 +msgid "Latitude:" +msgstr "широта:" + +#: constants.py:47 +msgid "Longitude:" +msgstr "долгота:" + +#: constants.py:48 +msgid "Description:" +msgstr "тодорхойлох:" + +#: constants.py:49 +msgid "lang=en" +msgstr "lang=mn" diff --git a/recorded.py b/recorded.py new file mode 100644 index 0000000..2378f1d --- /dev/null +++ b/recorded.py @@ -0,0 +1,82 @@ +# Copyright (c) 2008, Media Modifications Ltd. + +#Permission is hereby granted, free of charge, to any person obtaining a copy +#of this software and associated documentation files (the "Software"), to deal +#in the Software without restriction, including without limitation the rights +#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +#copies of the Software, and to permit persons to whom the Software is +#furnished to do so, subject to the following conditions: + +#The above copyright notice and this permission notice shall be included in +#all copies or substantial portions of the Software. + +#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +#THE SOFTWARE. + +import os +import map +import gtk + +from constants import Constants +from instance import Instance + +class Recorded: + SOURCE_DATASTORE = 0 + SOURCE_LOCAL = 1 + SOURCE_BUNDLE = 2 + + def __init__(self): + self.name = None + self.recorded = None + self.colorStroke = None + self.colorFill = None + self.type = None + + self.latitude = None + self.longitude = None + + self.thumbFilename = None + self.fileName = None + self.datastoreOb = None + self.datastoreId = None + self.tags = "" + + self.source = 0 + + + def getFilepath(self): + if (self.source == self.__class__.SOURCE_BUNDLE): + path = os.path.join( Constants.htmlPath, self.fileName ) + return path + elif (self.source == self.__class__.SOURCE_DATASTORE): + return self.datastoreOb.file_path + else: + return None + + + def getPixBuf(self): + pb = None + + fp = self.getFilepath() + if (fp != None): + pb = gtk.gdk.pixbuf_new_from_file(fp) + + return pb + + + def getThumbUrl(self): + return "http://127.0.0.1:" + str(map.Map.ajaxPort) + "/getImage.js?id=" + + + def getThumbBasename(self): + return str(self.thumbFilename) + + + def getThumbPath(self): + thumbPath = os.path.join( Instance.instancePath, self.getThumbBasename() ) + return thumbPath
\ No newline at end of file diff --git a/result.py b/result.py new file mode 100644 index 0000000..a43b67a --- /dev/null +++ b/result.py @@ -0,0 +1,25 @@ +# Copyright (c) 2008, Media Modifications Ltd. + +#Permission is hereby granted, free of charge, to any person obtaining a copy +#of this software and associated documentation files (the "Software"), to deal +#in the Software without restriction, including without limitation the rights +#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +#copies of the Software, and to permit persons to whom the Software is +#furnished to do so, subject to the following conditions: + +#The above copyright notice and this permission notice shall be included in +#all copies or substantial portions of the Software. + +#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +#THE SOFTWARE. + + +class ServerResult: + def __init__(self): + self.txt = "" + self.headers = []
\ No newline at end of file diff --git a/savedmap.py b/savedmap.py new file mode 100644 index 0000000..ca890c0 --- /dev/null +++ b/savedmap.py @@ -0,0 +1,41 @@ +# Copyright (c) 2008, Media Modifications Ltd. + +#Permission is hereby granted, free of charge, to any person obtaining a copy +#of this software and associated documentation files (the "Software"), to deal +#in the Software without restriction, including without limitation the rights +#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +#copies of the Software, and to permit persons to whom the Software is +#furnished to do so, subject to the following conditions: + +#The above copyright notice and this permission notice shall be included in +#all copies or substantial portions of the Software. + +#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +#THE SOFTWARE. + +class SavedMap: + + def __init__(self, lat, lng, zoom, imgFile, thumbFile, notes, tags): + self.lat = lat + self.lng = lng + self.zoom = zoom + self.imgPath = imgFile + self.thumbPath = thumbFile + self.notes = notes + self.tags = tags + self.density = " " + + self.recdDatastoreId = None + self.recdLat = None + self.recdLng = None + + + def addViewedRecd(self, id, lat, lng): + self.recdDatastoreId = id + self.recdLat = lat + self.recdLng = lng diff --git a/serialize.py b/serialize.py new file mode 100644 index 0000000..3da2df0 --- /dev/null +++ b/serialize.py @@ -0,0 +1,206 @@ +import xml.dom.minidom +from xml.dom.minidom import getDOMImplementation +from xml.dom.minidom import parse +import cStringIO +import os +import gtk + +from sugar.datastore import datastore + +from constants import Constants +from instance import Instance +import utils +from recorded import Recorded +import recorded +from savedmap import SavedMap + +def fillMediaHash( index, m ): + doc = None + if (os.path.exists(index)): + try: + doc = parse( os.path.abspath(index) ) + except: + doc = None + if (doc == None): + return + + try: + lastLat = doc.documentElement.getAttributeNode(Constants.mapLat).nodeValue + lastLng = doc.documentElement.getAttributeNode(Constants.mapLng).nodeValue + lastZoom = doc.documentElement.getAttributeNode(Constants.mapZoom).nodeValue + m.ca.preComet() + m.ca.cometLogic.handleReceivedMap(lastLat,lastLng,lastZoom) + m.ca.postComet() + except: + # likely an old saved map + lastLat = 0 + + recdElements = doc.documentElement.getElementsByTagName(Constants.recdMapItem) + for el in recdElements: + _loadMediaIntoHash( el, m ) + + saveElements = doc.documentElement.getElementsByTagName(Constants.recdSavedMapItem) + for el in saveElements: + _loadSavedMap( el, m ) + + saveElements = doc.documentElement.getElementsByTagName(Constants.recdInfoMarker) + for el in saveElements: + _loadInfoMarker( el, m ) + + saveElements = doc.documentElement.getElementsByTagName(Constants.recdLine) + for el in saveElements: + _loadLine( el, m ) + +def _loadSavedMap( el, m ): + latNode = el.getAttributeNode(Constants.recdLat) + lat = latNode.nodeValue + lngNode = el.getAttributeNode(Constants.recdLng) + lng = lngNode.nodeValue + + zoomNode = el.getAttributeNode(Constants.recdZoom) + zoom = zoomNode.nodeValue + notesNode = el.getAttributeNode(Constants.recdNotes) + notes = notesNode.nodeValue + tagsNode = el.getAttributeNode(Constants.recdTags) + tags = tagsNode.nodeValue + + m.ca.addSavedMap(lat,lng,zoom,notes,False) + +def _loadInfoMarker( el, m ): + latNode = el.getAttributeNode(Constants.recdLat) + lat = latNode.nodeValue + lngNode = el.getAttributeNode(Constants.recdLng) + lng = lngNode.nodeValue + infoNode = el.getAttributeNode(Constants.recdInfo) + info = infoNode.nodeValue + iconNode = el.getAttributeNode(Constants.recdIcon) + icon = iconNode.nodeValue + m.setInfo(lat,lng,info,icon) + +def _loadLine( el, m ): + idNode = el.getAttributeNode(Constants.lineID) + id = idNode.nodeValue + colorNode = el.getAttributeNode(Constants.lineColor) + color = colorNode.nodeValue + thickNode = el.getAttributeNode(Constants.lineThick) + thick = thickNode.nodeValue + ptsNode = el.getAttributeNode(Constants.linePts) + pts = ptsNode.nodeValue + m.setLine(id,color,thick,pts) + +def _loadMediaIntoHash( el, m ): + if (el.getAttributeNode(Constants.recdDatastoreId) == None): + return + + datastoreNode = el.getAttributeNode(Constants.recdDatastoreId) + if (datastoreNode != None): + datastoreId = datastoreNode.nodeValue + if (datastoreId != None): + #quickly check: if you have a datastoreId that the file hasn't been deleted, + #cause if you do, we need to flag your removal + #2904 trac + datastoreOb = getMediaFromDatastore(datastoreId) + if (datastoreOb != None): + + lat = 0 + lng = 0 + + if (el.getAttributeNode(Constants.recdLat) != None): + latNode = el.getAttributeNode(Constants.recdLat) + lat = latNode.nodeValue + else: + return + + if (el.getAttributeNode(Constants.recdLng) != None): + lngNode = el.getAttributeNode(Constants.recdLng) + lng = lngNode.nodeValue + else: + return + + m.addMedia( lat, lng, datastoreOb ) + + +def getMediaFromDatastore( id ): + mediaObject = None + try: + mediaObject = datastore.get( id ) + finally: + if (mediaObject == None): + return None + + return mediaObject + + +def fillRecdFromNode( recd, el ): + latNode = el.getAttributeNode(Constants.recdLat) + if (latNode != None): + recd.latitude = latNode.nodeValue + lngNode = el.getAttributeNode(Constants.recdLng) + if (lngNode != None): + recd.longitude = lngNode.nodeValue + + return recd + + +def _addRecdXmlAttrs( el, recd ): + el.setAttribute(Constants.recdDatastoreId, str(recd.datastoreId)) + el.setAttribute(Constants.recdLat, str(recd.latitude)) + el.setAttribute(Constants.recdLng, str(recd.longitude)) + +def _addInfoXmlAttrs( el, recd ): + el.setAttribute(Constants.recdInfo, recd[2]) + el.setAttribute(Constants.recdIcon, recd[3]) + el.setAttribute(Constants.recdLat, str(recd[0])) + el.setAttribute(Constants.recdLng, str(recd[1])) + +def _addLineXmlAttrs( el, recd ): + el.setAttribute(Constants.lineID, recd[0]) + el.setAttribute(Constants.lineColor, recd[1]) + el.setAttribute(Constants.lineThick, recd[2]) + el.setAttribute(Constants.linePts, recd[3]) + +def _addSaveXmlAttrs( el, smap ): + el.setAttribute(Constants.recdLat, str(smap.lat)) + el.setAttribute(Constants.recdLng, str(smap.lng)) + el.setAttribute(Constants.recdZoom, str(smap.zoom)) + el.setAttribute(Constants.recdNotes, str(smap.notes)) + el.setAttribute(Constants.recdTags, smap.tags) + #el.setAttribute(Constants.recdDensity, str(smap.density)) + + if (smap.recdDatastoreId != None): + el.setAttribute(Constants.recdRecdId, smap.recdDatastoreId) + el.setAttribute(Constants.recdRecdLat, smap.recdLat) + el.setAttribute(Constants.recdRecdLng, smap.recdLng) + + +def saveMediaHash( m ): + impl = getDOMImplementation() + album = impl.createDocument(None, Constants.recdAlbum, None) + root = album.documentElement + root.setAttribute(Constants.mapLat, m.ca.NOW_MAP_CENTER_LAT) + root.setAttribute(Constants.mapLng, m.ca.NOW_MAP_CENTER_LNG) + root.setAttribute(Constants.mapZoom, m.ca.NOW_MAP_ZOOM) + + for i in range (0, len(m.recs)): + recd = m.recs[i] + mediaEl = album.createElement(Constants.recdMapItem) + root.appendChild( mediaEl ) + _addRecdXmlAttrs( mediaEl, recd ) + + for k, iMarker in m.infoMarkers.iteritems(): + mediaEl = album.createElement(Constants.recdInfoMarker) + root.appendChild( mediaEl ) + _addInfoXmlAttrs( mediaEl, iMarker.split(";~") ) + + for k, iMarker in m.lineData.iteritems(): + mediaEl = album.createElement(Constants.recdLine) + root.appendChild( mediaEl ) + _addLineXmlAttrs( mediaEl, iMarker.split(";~") ) + + for i in range (0, len(m.savedMaps)): + smap = m.savedMaps[i] + saveEl = album.createElement(Constants.recdSavedMapItem) + root.appendChild( saveEl ) + _addSaveXmlAttrs( saveEl, smap ) + + return album diff --git a/server.py b/server.py new file mode 100644 index 0000000..9a91ad4 --- /dev/null +++ b/server.py @@ -0,0 +1,69 @@ +# Copyright (c) 2008, Media Modifications Ltd. + +#Permission is hereby granted, free of charge, to any person obtaining a copy +#of this software and associated documentation files (the "Software"), to deal +#in the Software without restriction, including without limitation the rights +#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +#copies of the Software, and to permit persons to whom the Software is +#furnished to do so, subject to the following conditions: + +#The above copyright notice and this permission notice shall be included in +#all copies or substantial portions of the Software. + +#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +#THE SOFTWARE. + + +import urlparse +import urllib +import posixpath +import SimpleHTTPServer +import BaseHTTPServer + + +class Server(BaseHTTPServer.HTTPServer): + def __init__(self, server_address, logic): + BaseHTTPServer.HTTPServer.__init__(self, server_address, RegHandler) + self.logic = logic + + +#RegHandler extends SimpleHTTPServer.py (in python 2.4) +class RegHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): + def do_POST( self ): + self.translate_path() + + + def do_GET( self ): + self.translate_path() + + + def do_HEAD( self ): + self.translate_path() + + + def translate_path(self): + #todo: compare with send_head in parent + urlp = urlparse.urlparse(self.path) + + urls = urlp[2] + urls = posixpath.normpath(urllib.unquote(urls)) + urlPath = urls.split('/') + urlPath = filter(None, urlPath) + + params = urlp[4] + parama = [] + allParams = params.split('&') + for i in range (0, len(allParams)): + parama.append(allParams[i].split('=')) + + result = self.server.logic.doServerLogic(self.path, urlPath, parama) + self.send_response(200) + for i in range (0, len(result.headers)): + self.send_header( result.headers[i][0], result.headers[i][1] ) + self.end_headers() + self.wfile.write( result.txt )
\ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..235a560 --- /dev/null +++ b/setup.py @@ -0,0 +1,25 @@ +#!/bin/env python + +# Copyright (C) 2006, Red Hat, Inc. +# +# 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 2 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, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +from sugar.activity import bundlebuilder + +import os + +os.system("make") + +bundlebuilder.start("map")
\ No newline at end of file @@ -0,0 +1,207 @@ +# Copyright (C) 2007, One Laptop Per Child +# Copyright (c) 2008, Media Modifications Ltd. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +import gobject +import gtk +import hippo + +import sugar +from sugar.graphics import style +from sugar.graphics.palette import Palette, ToolInvoker +from sugar.graphics.toolbutton import ToolButton +from sugar.graphics.icon import Icon + +from constants import Constants + +_PREVIOUS_PAGE = 0 +_NEXT_PAGE = 1 + +class _TrayViewport(gtk.Viewport): + __gproperties__ = { + 'can-scroll' : (bool, None, None, False, + gobject.PARAM_READABLE), + } + + def __init__(self, orientation): + self.orientation = orientation + self._can_scroll = False + + gobject.GObject.__init__(self) + + self.set_shadow_type(gtk.SHADOW_NONE) + + self.traybar = gtk.Toolbar() + self.traybar.set_orientation(orientation) + self.traybar.set_show_arrow(False) + self.add(self.traybar) + self.traybar.show() + + self.connect('size_allocate', self._size_allocate_cb) + + def scroll(self, direction): + if direction == _PREVIOUS_PAGE: + self._scroll_previous() + elif direction == _NEXT_PAGE: + self._scroll_next() + + def _scroll_next(self): + if self.orientation == gtk.ORIENTATION_HORIZONTAL: + adj = self.get_hadjustment() + new_value = adj.value + self.allocation.width + adj.value = min(new_value, adj.upper - self.allocation.width) + else: + adj = self.get_vadjustment() + new_value = adj.value + self.allocation.height + adj.value = min(new_value, adj.upper - self.allocation.height) + + def _scroll_to_end(self): + if self.orientation == gtk.ORIENTATION_HORIZONTAL: + adj = self.get_hadjustment() + adj.value = adj.upper# - self.allocation.width + else: + adj = self.get_vadjustment() + adj.value = adj.upper - self.allocation.height + + def _scroll_previous(self): + if self.orientation == gtk.ORIENTATION_HORIZONTAL: + adj = self.get_hadjustment() + new_value = adj.value - self.allocation.width + adj.value = max(adj.lower, new_value) + else: + adj = self.get_vadjustment() + new_value = adj.value - self.allocation.height + adj.value = max(adj.lower, new_value) + + def do_size_request(self, requisition): + child_requisition = self.child.size_request() + if self.orientation == gtk.ORIENTATION_HORIZONTAL: + requisition[0] = 0 + requisition[1] = child_requisition[1] + else: + requisition[0] = child_requisition[0] + requisition[1] = 0 + + def do_get_property(self, pspec): + if pspec.name == 'can-scroll': + return self._can_scroll + + def _size_allocate_cb(self, viewport, allocation): + bar_requisition = self.traybar.get_child_requisition() + if self.orientation == gtk.ORIENTATION_HORIZONTAL: + can_scroll = bar_requisition[0] > allocation.width + else: + can_scroll = bar_requisition[1] > allocation.height + + if can_scroll != self._can_scroll: + self._can_scroll = can_scroll + self.notify('can-scroll') + +class _TrayScrollButton(gtk.Button): + def __init__(self, icon_name, scroll_direction): + gobject.GObject.__init__(self) + + self._viewport = None + + self._scroll_direction = scroll_direction + + self.set_relief(gtk.RELIEF_NONE) + self.set_size_request(style.GRID_CELL_SIZE, style.GRID_CELL_SIZE) + + icon = Icon(icon_name = icon_name, + icon_size=gtk.ICON_SIZE_SMALL_TOOLBAR) + self.set_image(icon) + icon.show() + + self.connect('clicked', self._clicked_cb) + + def set_viewport(self, viewport): + self._viewport = viewport + self._viewport.connect('notify::can-scroll', + self._viewport_can_scroll_changed_cb) + + def _viewport_can_scroll_changed_cb(self, viewport, pspec): + #self.props.visible = self._viewport.props.can_scroll + self.set_sensitive(self._viewport.props.can_scroll) + + def _clicked_cb(self, button): + self._viewport.scroll(self._scroll_direction) + + viewport = property(fset=set_viewport) + +class HTray(gtk.VBox): + def __init__(self, **kwargs): + gobject.GObject.__init__(self, **kwargs) + + separator = hippo.Canvas() + box = hippo.CanvasBox( + border_color=Constants.colorWhite.get_int(), + background_color=Constants.colorWhite.get_int(), + box_height=1, + border_bottom=1) + separator.set_root(box) + self.pack_start(separator, False) + + hbox = gtk.HBox() + self.pack_start(hbox) + + self.scroll_left = _TrayScrollButton('go-left', _PREVIOUS_PAGE) + self.scroll_left_event = gtk.EventBox() + self.scroll_left_event.add(self.scroll_left) + self.scroll_left_event.set_size_request(55, -1) + hbox.pack_start(self.scroll_left_event, False) + + self.viewport = _TrayViewport(gtk.ORIENTATION_HORIZONTAL) + hbox.pack_start(self.viewport) + self.viewport.show() + + self.scroll_right = _TrayScrollButton('go-right', _NEXT_PAGE) + self.scroll_right_event = gtk.EventBox() + self.scroll_right_event.add(self.scroll_right) + self.scroll_right_event.set_size_request(55, -1) + hbox.pack_start(self.scroll_right_event, False) + + self.scroll_left.set_focus_on_click(False) + self.scroll_left_event.modify_bg(gtk.STATE_NORMAL, sugar.graphics.style.COLOR_TOOLBAR_GREY.get_gdk_color()) + self.scroll_left.modify_bg(gtk.STATE_ACTIVE, sugar.graphics.style.COLOR_BUTTON_GREY.get_gdk_color()) + + self.scroll_right.set_focus_on_click(False) + self.scroll_right_event.modify_bg(gtk.STATE_NORMAL, sugar.graphics.style.COLOR_TOOLBAR_GREY.get_gdk_color()) + self.scroll_right.modify_bg(gtk.STATE_ACTIVE, sugar.graphics.style.COLOR_BUTTON_GREY.get_gdk_color()) + + self.scroll_left.viewport = self.viewport + self.scroll_right.viewport = self.viewport + + self.connect_after("size-allocate", self._sizeAllocateCb) + + def _sizeAllocateCb(self, widget, event ): + self.viewport.notify('can-scroll') + + def get_children(self): + return self.viewport.traybar.get_children() + + def add_item(self, item, index=-1): + self.viewport.traybar.insert(item, index) + + def remove_item(self, item): + self.viewport.traybar.remove(item) + + def get_item_index(self, item): + return self.viewport.traybar.get_item_index(item) + + def scroll_to_end(self): + self.viewport._scroll_to_end()
\ No newline at end of file diff --git a/utils.py b/utils.py new file mode 100644 index 0000000..5085d66 --- /dev/null +++ b/utils.py @@ -0,0 +1,110 @@ +import base64 +import rsvg +import re +import os +import cairo +import gc +import gtk +from hashlib import md5 +import time +from time import strftime + +from sugar import util +import _camera + +def getStringFromPixbuf(pixbuf): + data = [""] + pixbuf.save_to_callback(_saveDataToBufferCb, "png", {}, data) + return base64.b64encode(str(data[0])) + + +def _saveDataToBufferCb(buf, data): + data[0] += buf + return True + + +def getPixbufFromString(str): + pbl = gtk.gdk.PixbufLoader() + data = base64.b64decode( str ) + pbl.write(data) + pbl.close() + return pbl.get_pixbuf() + + +def loadSvg( data, stroke, fill ): + if ((stroke == None) or (fill == None)): + return rsvg.Handle( data=data ) + + entity = '<!ENTITY fill_color "%s">' % fill + data = re.sub('<!ENTITY fill_color .*>', entity, data) + + entity = '<!ENTITY stroke_color "%s">' % stroke + data = re.sub('<!ENTITY stroke_color .*>', entity, data) + + return rsvg.Handle( data=data ) + + +def getUniqueFilepath( path, i ): + pathOb = os.path.abspath( path ) + newPath = os.path.join( os.path.dirname(pathOb), str( str(i) + os.path.basename(pathOb) ) ) + if (os.path.exists(newPath)): + i = i + 1 + return getUniqueFilepath( pathOb, i ) + else: + return os.path.abspath( newPath ) + + +def md5File( filepath ): + md = md5() + f = file( filepath, 'rb' ) + md.update( f.read() ) + digest = md.hexdigest() + hash = util.printable_hash(digest) + return hash + + +def generateThumbnail( pixbuf, scale, thumbw, thumbh ): + #need to generate thumbnail version here + thumbImg = cairo.ImageSurface(cairo.FORMAT_ARGB32, thumbw, thumbh) + tctx = cairo.Context(thumbImg) + img = _camera.cairo_surface_from_gdk_pixbuf(pixbuf) + tctx.scale(scale, scale) + tctx.set_source_surface(img, 0, 0) + tctx.paint() + gc.collect() + return thumbImg + + +def scaleSvgToDim( handle, dim ): + #todo... + scale = 1.0 + + svgDim = handle.get_dimension_data() + if (svgDim[0] > dim[0]): + pass + + return scale + + +def getDateString( when ): + #todo: internationalize the date + return strftime( "%a, %b %d, %I:%M:%S %p", time.localtime(when) ) + + +def grayScalePixBuf2( pb, copy ): + arr = pb.get_pixels_array() + if (copy): + arr = arr.copy() + for row in arr: + for pxl in row: + y = 0.3*pxl[0][0]+0.59*pxl[1][0]+0.11*pxl[2][0] + pxl[0][0] = y + pxl[1][0] = y + pxl[2][0] = y + return gtk.gdk.pixbuf_new_from_array(arr, pb.get_colorspace(), pb.get_bits_per_sample()) + + +def grayScalePixBuf( pb, copy ): + pb2 = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8, pb.get_width(), pb.get_height()) + pb.saturate_and_pixelate(pb2, 0, 0) + return pb2
\ No newline at end of file diff --git a/webviewer.py b/webviewer.py new file mode 100644 index 0000000..8ce8201 --- /dev/null +++ b/webviewer.py @@ -0,0 +1,148 @@ +# Copyright (C) 2006, Red Hat, Inc. +# Copyright (C) 2007, One Laptop Per Child +# Copyright (c) 2008, Media Modifications Ltd. +# +# 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 2 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, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +from sugar import env + +import logging +import gobject +import gtk +import os +import hulahop +hulahop.startup(os.path.join(env.get_profile_path(), 'gecko')) +import xpcom +from xpcom.nsError import * +from xpcom import components +from xpcom.components import interfaces +from hulahop.webview import WebView + +#import sessionstore +#from dnd import DragDropHooks + +class WebViewer(WebView): + def __init__(self): + WebView.__init__(self) + + window_creator = WindowCreator(self) + cls = components.classes['@mozilla.org/embedcomp/window-watcher;1'] + window_watcher = cls.getService(interfaces.nsIWindowWatcher) + window_watcher.setWindowCreator(window_creator) + + self.connect('realize', self._realize_cb) + + def _realize_cb(self, widget): + #drag_drop_hooks = DragDropHooks(self) + + cls = components.classes['@mozilla.org/embedcomp/command-params;1'] + cmd_params = cls.createInstance('nsICommandParams') + #cmd_params.setISupportsValue('addhook', drag_drop_hooks) + + requestor = self.browser.queryInterface(interfaces.nsIInterfaceRequestor) + command_manager = requestor.getInterface(interfaces.nsICommandManager) + command_manager.doCommand('cmd_clipboardDragDropHook', cmd_params, self.dom_window) + + def get_session(self): + return sessionstore.get_session(self) + + def set_session(self, session_data): + return sessionstore.set_session(self, session_data) + +class WindowCreator: + _com_interfaces_ = interfaces.nsIWindowCreator + + def __init__(self, browser): + self._popup_creators = [] + self._browser = browser + + def createChromeWindow(self, parent, chrome_flags): + logging.debug('createChromeWindow: %r %r' % (parent, chrome_flags)) + + popup_creator = _PopupCreator(self._browser.get_toplevel()) + popup_creator.connect('popup-created', self._popup_created_cb) + + self._popup_creators.append(popup_creator) + + browser = popup_creator.get_embed() + + if chrome_flags & interfaces.nsIWebBrowserChrome.CHROME_OPENAS_CHROME: + logging.debug('Creating chrome window.') + browser.is_chrome = True + item = browser.browser.queryInterface(interfaces.nsIDocShellTreeItem) + item.itemType = interfaces.nsIDocShellTreeItem.typeChromeWrapper + else: + logging.debug('Creating browser window.') + item = browser.browser.queryInterface(interfaces.nsIDocShellTreeItem) + item.itemType = interfaces.nsIDocShellTreeItem.typeContentWrapper + + browser.realize() + + return browser.browser.containerWindow + + def _popup_created_cb(self, creator): + self._popup_creators.remove(creator) + +class _PopupCreator(gobject.GObject): + __gsignals__ = { + 'popup-created': (gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, ([])), + } + + def __init__(self, parent_window): + gobject.GObject.__init__(self) + + logging.debug('Creating the popup widget') + + self._parent_window = parent_window + + self._dialog = gtk.Window() + self._dialog.set_resizable(True) + + self._dialog.realize() + self._dialog.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG) + + self._embed = Browser() + self._vis_sid = self._embed.connect('notify::visible', self._notify_visible_cb) + self._dialog.add(self._embed) + + def _notify_visible_cb(self, embed, param): + self._embed.disconnect(self._vis_sid) + + if self._embed.type == Browser.TYPE_POPUP or self._embed.is_chrome: + logging.debug('Show the popup') + self._embed.show() + self._dialog.set_transient_for(self._parent_window) + self._dialog.show() + else: + logging.debug('Open a new activity for the popup') + self._dialog.remove(self._embed) + self._dialog.destroy() + self._dialog = None + + # FIXME We need a better way to handle this. + # It seem like a pretty special case though, I doubt + # other activities will need something similar. + from webactivity import WebActivity + from sugar.activity import activityfactory + from sugar.activity.activityhandle import ActivityHandle + handle = ActivityHandle(activityfactory.create_activity_id()) + activity = WebActivity(handle, self._embed) + activity.show() + + self.emit('popup-created') + + def get_embed(self): + return self._embed
\ No newline at end of file |