From 6f64fe14ea678edba5b7ed3ea1a97aac3ffe20b1 Mon Sep 17 00:00:00 2001 From: Nick Doiron Date: Tue, 10 May 2011 20:07:11 +0000 Subject: Uploaded Map Activity code to Gitorious --- 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 {} \; 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 + +#include "pycairo.h" +#include + +#include + +static PyTypeObject *_PyGObject_Type; +#define PyGObject_Type (*_PyGObject_Type) +Pycairo_CAPI_t *Pycairo_CAPI; +static PyTypeObject *_PyGdkPixbuf_Type; +#define PyGdkPixbuf_Type (*_PyGdkPixbuf_Type) + +#include +#include +#include +#include +#include + +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 new file mode 100644 index 0000000..e126c76 --- /dev/null +++ b/_camera.o Binary files differ diff --git a/_camera.so b/_camera.so new file mode 100644 index 0000000..c57a12b --- /dev/null +++ b/_camera.so Binary files differ 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 @@ + + + +]> + + + + + + + + + + + diff --git a/activity/activity.info b/activity/activity.info new file mode 100644 index 0000000..127473f --- /dev/null +++ b/activity/activity.info @@ -0,0 +1,9 @@ +[Activity] +name = Map +activity_version = 12 +host_version = 1 +license = MIT +icon = activity-map +service_name = org.laptop.map +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..9ae1ecf --- /dev/null +++ b/constants.py @@ -0,0 +1,131 @@ +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 = 8 + + 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") + istrWebMedia = _("Library") + istrMeasure = _("Measure") + istrStaticMaps = _("olpcMAP.net") + istrPanoramio = _("Panoramio") + istrLocalWiki = _("LocationWiki") + istrWikiMapia = _("WikiMapia") + istrLatitude = _("Latitude:") + istrLongitude = _("Longitude:") + istrTags = _("Description:") + istrLang = _("lang=en") + LineButton = _("Add Line") + PolyButton = _("Add Shape") + + 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/html/MediaMarker.js b/html/MediaMarker.js new file mode 100644 index 0000000..a47b00b --- /dev/null +++ b/html/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/html/staticMapCanvas.js b/html/staticMapCanvas.js new file mode 100644 index 0000000..422c373 --- /dev/null +++ b/html/staticMapCanvas.js @@ -0,0 +1,148 @@ +var GMapCanvas2={ + initialize: function(prefLat, prefLng, prefZoom, prefType) + { this.map = document.getElementById("map"); + this.center = [prefLat*1,prefLng*1]; + this.zoom=prefZoom; + this.resolution="480x303"; + this.factor=0.75; + this.yfactor=0.75; + switch(prefType){ + case "terrain": + this.mapType = "terrain";break; + case "sat": + this.mapType = "satellite";break; + case "terr": + this.mapType = "hybrid";break; + case "hyb": + this.mapType = "hybrid";break; + case "osm": + this.mapType = "osm";break; + default: + this.mapType = "roadmap";break; + } + document.getElementById(this.mapType).className="selectmaptype"; + this.markerList=[]; + this.lineColor="ff0000"; + this.updateImg(); + return this; + }, + get_center:function(){return this.center;}, + get_zoom:function(){return this.zoom;}, + get_type:function(){return this.mapType;}, + get_bounds:function(){ + var sw=[1*this.center[0]-0.55618*this.factor*Math.pow(2,(9-1*this.zoom)),this.center[1]*1 - 0.98877*this.factor*Math.pow(2,(9-1*this.zoom))]; + var ne=[1*this.center[0]+0.55618*this.factor*Math.pow(2,(9-1*this.zoom)),this.center[1]*1 + 0.98877*this.factor*Math.pow(2,(9-1*this.zoom))]; + return [sw,ne]; + }, + set_center:function(latlng){ + if(latlng[0]>85){latlng=[85,latlng[1]]} + if(latlng[0]<-85){latlng=[-85,latlng[1]]} + this.center = latlng; + this.updateImg(); + updateLoc() + }, + set_zoom:function(z){this.zoom = z;this.updateImg();}, + set_type:function(type){ + if(type==this.mapType){return;} + try{document.getElementById(this.mapType).className = "maptype";document.getElementById(type).className = "selectmaptype";} + catch(e){} + this.mapType=type; + this.updateImg(); + }, + fitBounds:function(bounds){ + this.bounds=bounds; + this.center=[(bounds[0][0]*1.0+1.0*bounds[1][0])/2.0,(bounds[0][1]*1.0+1.0*bounds[1][1])/2.0]; + var latDiff=Math.abs(bounds[0][0]-bounds[1][0]); + var lngDiff=Math.abs(bounds[0][1]-bounds[1][1]); + if(latDiff/lngDiff>0.628){this.zoom=Math.floor(10-Math.log(latDiff/0.556)/0.301);} + else{this.zoom=Math.floor(10-Math.log(lngDiff/0.8789)/0.301);} + this.zoom=Math.min(Math.max(this.zoom,3),15); + this.updateImg(); + }, + setLineLayer:function(data){ + if((this.lineLayer)&&(mapData)){this.lineLayer+="&path=color:0x"+this.lineColor+"|weight:5|"+data;} + else{this.lineLayer = data;} + }, + setLineColor:function(color){this.lineColor = color;}, + setResolution:function(res){this.resolution = res;}, + updateImg:function(){ + params=[]; + if(this.mapType!="osm"){ + params.push("center="+this.center[0].toFixed(6)+","+this.center[1].toFixed(6)); + params.push("zoom="+this.zoom); + params.push("maptype="+this.mapType); + if(this.markerList.length>0){ + var lls=""; + var bounds=this.get_bounds(); + for(var m=0;mbounds[0][0])&&(mar.pt[1]>bounds[0][1])&&(mar.pt[1]-1){params.push("polygons="+linefill.join(",")+",thickness:5,transparency:50")} + else{params.push("paths="+linefill.join(",")+",thickness:5,transparency:50")} + } + this.map.src="http://dev.openstreetmap.org/~pafciu17/?module=map&width=480&height=303&imgType=jpg&"+params.join("&"); + } + } +} diff --git a/html/staticmap2.html b/html/staticmap2.html new file mode 100644 index 0000000..960e1c5 --- /dev/null +++ b/html/staticmap2.html @@ -0,0 +1,734 @@ + + + + + + + + + + +Connecting to Google Maps... + +
+ diff --git a/html/zMinus.png b/html/zMinus.png new file mode 100644 index 0000000..01b4447 --- /dev/null +++ b/html/zMinus.png Binary files differ diff --git a/html/zPlus.png b/html/zPlus.png new file mode 100644 index 0000000..cb00699 --- /dev/null +++ b/html/zPlus.png Binary files differ 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 @@ + + + + + + + + + + + + + + + + + + + + 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 @@ + + +]> + + + + + + + + + + + \ 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 @@ + + + + + + + + + + + + + + + + + + + diff --git a/icons/info-marker.svg b/icons/info-marker.svg new file mode 100644 index 0000000..329063f --- /dev/null +++ b/icons/info-marker.svg @@ -0,0 +1,74 @@ + +image/svg+xml + + + + + + + + + + + \ No newline at end of file diff --git a/icons/localwiki.svg b/icons/localwiki.svg new file mode 100644 index 0000000..245f888 --- /dev/null +++ b/icons/localwiki.svg @@ -0,0 +1,327 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + Jakub Steiner + + + http://jimmac.musichall.cz + + Read Only Emblem + + + emblem + read-only + nowrite + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + +]> + + + + + + + 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 @@ + + +]> + + + + + + + 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 @@ + + +]> + + + + + + + + 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 @@ + + +]> + + + + + + + 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 @@ + + +image/svg+xml + + + + + + + + \ 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 @@ + + + + + + + + + + + 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 @@ + + + +]> + + + + + + diff --git a/icons/panoramio.svg b/icons/panoramio.svg new file mode 100644 index 0000000..3d46175 --- /dev/null +++ b/icons/panoramio.svg @@ -0,0 +1,25 @@ + + + + +]> + + + + + + + + + + + + + + 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 @@ + + +image/svg+xml + + + + + + + \ 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 @@ + + + +]> + + + + + + + + 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 @@ + + +image/svg+xml + + + + + + \ 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 @@ + + + + + + + + + + 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 @@ + + + + + + + + + + + + + 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 @@ + + +]> + + + + + + + + + + \ No newline at end of file diff --git a/icons/wikimapia.svg b/icons/wikimapia.svg new file mode 100644 index 0000000..0101988 --- /dev/null +++ b/icons/wikimapia.svg @@ -0,0 +1,100 @@ + + +image/svg+xml + + + + + + \ 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 new file mode 100644 index 0000000..ea90e33 --- /dev/null +++ b/locale/es/LC_MESSAGES/org.laptop.map.mo Binary files differ 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 new file mode 100644 index 0000000..2ef674c --- /dev/null +++ b/locale/mn/LC_MESSAGES/org.laptop.map.mo Binary files differ 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..820e306 --- /dev/null +++ b/logic.py @@ -0,0 +1,359 @@ +# 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 + #self.ca.browser.load_uri("javascript:"+r.txt+"void(0);") + + 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 + gobject.idle_add(self.ca.cometLogic.forceupdate) + 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,1) + + 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 ): + findsrc="http://maps.googleapis.com/maps/api/geocode/json?sensor=false&address=" + urllib.quote(address) + findAddress = urllib.urlopen(findsrc).read() + + longname=findAddress[findAddress.find('long_name')+13:len(findAddress)] + longname=longname[0:longname.find('"')] + + findSW=findAddress[findAddress.find('southwest'):findAddress.find('northeast')].replace(" ","").replace('\n','') + swlat=findSW[findSW.find('lat')+5:findSW.find(',')] + swlng=findSW[findSW.find('lng')+5:findSW.find('}')] + + findNE=findAddress[findAddress.find('northeast'):len(findAddress)].replace(" ","").replace('\n','') + nelat=findNE[findNE.find('lat')+5:findNE.find(',')] + nelng=findNE[findNE.find('lng')+5:findNE.find('}')] + + self.proceedHeaders.append( ("Content-type", "text/javascript") ) + #self.proceedTxt = "showInfo('" + swlat + "," + swlng + "," + nelat + "," + nelng + "');" + self.proceedTxt = "moveToAddress(" + swlat + "," + swlng + "," + nelat + "," + nelng + ",'" + longname + "');" + self.ca.ajaxServer.stop() + self.ca.cometServer.stop() + self.ca.browser.load_uri("javascript:"+self.proceedTxt+";void(0);") + + def forceupdate(self): + #self.ca.preComet() + + self.proceedHeaders.append( ("Content-type", "text/javascript") ) + self.proceedTxt = "canvas.updateImg();" + self.ca.ajaxServer.stop() + self.ca.cometServer.stop() + self.ca.browser.load_uri("javascript:"+self.proceedTxt+";void(0);") + #self.ca.postComet() + + 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.ajaxServer.stop() + self.ca.cometServer.stop() + self.ca.browser.load_uri("javascript:"+self.proceedTxt+";void(0);") + + def handlePanoramio(self): + self.proceedHeaders.append( ("Content-type", "text/javascript") ) + self.proceedTxt = 'panoramio();' + self.ca.ajaxServer.stop() + self.ca.cometServer.stop() + self.ca.browser.load_uri("javascript:"+self.proceedTxt+";void(0);") + + def handleLocalWiki(self): + self.proceedHeaders.append( ("Content-type", "text/javascript") ) + self.proceedTxt = 'wikiloc();' + self.ca.ajaxServer.stop() + self.ca.cometServer.stop() + self.ca.browser.load_uri("javascript:"+self.proceedTxt+";void(0);") + + def handleWikiMapia(self): + self.proceedHeaders.append( ("Content-type", "text/javascript") ) + self.proceedTxt = 'wikimapia();' + self.ca.ajaxServer.stop() + self.ca.cometServer.stop() + self.ca.browser.load_uri("javascript:"+self.proceedTxt+";void(0);") + + def handleOlpcMAP(self): + self.proceedHeaders.append( ("Content-type", "text/javascript") ) + llc=[float(self.ca.NOW_MAP_CENTER_LAT),float(self.ca.NOW_MAP_CENTER_LNG)] + llz=float(self.ca.NOW_MAP_ZOOM) + lln=llc[0]+0.55618*0.75*(2**(9-llz)) + lle=llc[1]+0.98877*0.75*(2**(9-llz)) + lls=llc[0]-0.55618*0.75*(2**(9-llz)) + llw=llc[1]-0.98877*0.75*(2**(9-llz)) + findsrc="http://mapmeld.appspot.com/olpcMAP/kml?llregion="+str(lln)+","+str(lle)+","+str(lls)+","+str(llw) + self.ca.readKML(urllib.urlopen(findsrc)) + + def handleZoomUpdate( self, dir ): + self.proceedHeaders.append( ("Content-type", "text/javascript") ) + if (dir == "+"): + self.proceedTxt = "zoomIn();" + elif (dir == "-"): + self.proceedTxt = "zoomOut();" + self.ca.ajaxServer.stop() + self.ca.cometServer.stop() + 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.ajaxServer.stop() + self.ca.cometServer.stop() + 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.ajaxServer.stop() + self.ca.cometServer.stop() + 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.ajaxServer.stop() + self.ca.cometServer.stop() + 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.ajaxServer.stop() + self.ca.cometServer.stop() + 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.ajaxServer.stop() + self.ca.cometServer.stop() + 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.ajaxServer.stop() + self.ca.cometServer.stop() + 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.ajaxServer.stop() + self.ca.cometServer.stop() + 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 < 1): + self.proceedHeaders.append( ("Content-type", "text/javascript") ) + self.proceedTxt = "" + if(self.addKMLSet == -1): + self.addKMLSet = 1 + self.proceedTxt = self.proceedTxt + "addInfoMarker(" + lat + ", " + lng + ", '" + pixString.replace("'",'"') + "', '" + icon + "',false);" + #self.proceedTxt = self.proceedTxt + "addInfoMarker(" + lat + ", " + lng + ", '"+pixString.replace("'",'"')+"', 'http://mapmeld.appspot.com/xo-red.png',false);" + self.ca.ajaxServer.stop() + self.ca.cometServer.stop() + self.ca.browser.load_uri("javascript:"+self.proceedTxt+";void(0);") + + def startKML( self, tellOthers ): + self.addKMLSet = -1 + if((self.ca.maptube is not None) and (tellOthers == 1)): + self.ca.sendStartKML() + + def handleEndKML( self, tellOthers ): + self.proceedHeaders.append( ("Content-type", "text/javascript") ) + self.proceedTxt = self.proceedTxt + "canvas.updateImg();" + self.addKMLSet = 0 + if((self.ca.maptube is not None) and (tellOthers == 1)): + self.ca.sendEndKML() + self.ca.ajaxServer.stop() + self.ca.cometServer.stop() + self.ca.browser.load_uri("javascript:"+self.proceedTxt+";void(0);") + + def lineMode(self, type): + self.proceedHeaders.append( ("Content-type", "text/javascript") ) + self.proceedTxt = "lineMode('" + type + "');" + self.ca.ajaxServer.stop() + self.ca.cometServer.stop() + self.ca.browser.load_uri("javascript:"+self.proceedTxt+";void(0);") + + def handleLine(self,id,color,thickness,pts): + if(self.addKMLSet < 1): + self.proceedHeaders.append( ("Content-type", "text/javascript") ) + self.proceedTxt = "" + if(self.addKMLSet == -1): + self.addKMLSet = 1 + self.proceedTxt = self.proceedTxt + "addLine('" + id + "','" + color + "','" + thickness + "','" + pts + "');" + self.ca.ajaxServer.stop() + self.ca.cometServer.stop() + 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.ajaxServer.stop() + self.ca.cometServer.stop() + 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.ajaxServer.stop() + self.ca.cometServer.stop() + self.ca.browser.load_uri("javascript:"+self.proceedTxt+";void(0);") 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/map-activity.iml b/map-activity.iml new file mode 100644 index 0000000..4e07241 --- /dev/null +++ b/map-activity.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/map-activity.ipr b/map-activity.ipr new file mode 100644 index 0000000..3e24b75 --- /dev/null +++ b/map-activity.ipr @@ -0,0 +1,286 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/map-activity.iws b/map-activity.iws new file mode 100644 index 0000000..76651af --- /dev/null +++ b/map-activity.iws @@ -0,0 +1,694 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + localhost + 5050 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/map.py b/map.py new file mode 100644 index 0000000..82a8216 --- /dev/null +++ b/map.py @@ -0,0 +1,1799 @@ +#!/usr/bin/env python +# 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*4 +webHeight = imgHeight*3 + +class Map(activity.Activity): + + #temp values until they get assigned + cometPort = 8889 + ajaxPort = 8890 + + initLat = '48.224' + initLng = '-11.07' + initZoom = '3' + initType = 'terr' + + popW = 320 + popH = 240 + popB = 10 + + 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, "html") + 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.connect("olpcmap", self._olpcmapCb) + self.addToolbar.connect("panoramio", self._panoramioCb) + self.addToolbar.connect("local-wiki", self._localwikiCb) + self.addToolbar.connect("wikimapia",self._wikimapiaCb) + 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() + + vboxNorth = gtk.VBox() + vbox.pack_start(vboxNorth) + buttNorthFiller = gtk.EventBox() + buttNorthFiller.modify_bg( gtk.STATE_NORMAL, Constants.colorBg.gColor ) + buttNorthFiller.modify_bg( gtk.STATE_ACTIVE, Constants.colorBg.gColor ) + buttNorthFiller.modify_bg( gtk.STATE_INSENSITIVE, Constants.colorBg.gColor ) + vboxNorth.pack_start(buttNorthFiller, expand=True) + buttNorthEventBox = gtk.EventBox() + buttNorthEventBox.modify_bg( gtk.STATE_NORMAL, Constants.colorBg.gColor ) + buttNorthEventBox.modify_bg( gtk.STATE_ACTIVE, Constants.colorBg.gColor ) + buttNorthEventBox.modify_bg( gtk.STATE_INSENSITIVE, Constants.colorBg.gColor ) + self.buttNorth = gtk.Button() + self.buttNorth.set_property("can-focus", False) + self.buttNorth.modify_bg( gtk.STATE_NORMAL, Constants.colorBg.gColor ) + self.buttNorth.modify_bg( gtk.STATE_ACTIVE, Constants.colorBg.gColor ) + self.buttNorth.modify_bg( gtk.STATE_INSENSITIVE, Constants.colorBg.gColor ) + buttNorthEventBox.add(self.buttNorth) + self.buttNorth.set_image( Constants.northImgClr ) + self.buttNorth.connect('clicked', self._buttNorthCb) + vboxNorth.pack_start(buttNorthEventBox, expand=False) + self.buttNorth.set_relief(gtk.RELIEF_NONE) + + hbox = gtk.HBox() + self.htmlScale = 1.43 + hbox.set_size_request(-1, int(webHeight*self.htmlScale)) + vbox.pack_start(hbox, expand=False) + + vboxSouth = gtk.VBox() + vbox.pack_start(vboxSouth) + buttSouthEventBox = gtk.EventBox() + buttSouthEventBox.modify_bg( gtk.STATE_ACTIVE, Constants.colorBg.gColor ) + buttSouthEventBox.modify_bg( gtk.STATE_NORMAL, Constants.colorBg.gColor ) + buttSouthEventBox.modify_bg( gtk.STATE_INSENSITIVE, Constants.colorBg.gColor ) + vboxSouth.pack_start(buttSouthEventBox, expand=False) + self.buttSouth = gtk.Button() + self.buttSouth.set_property("can-focus", False) + self.buttSouth.modify_bg( gtk.STATE_NORMAL, Constants.colorBg.gColor ) + self.buttSouth.modify_bg( gtk.STATE_ACTIVE, Constants.colorBg.gColor ) + self.buttSouth.modify_bg( gtk.STATE_INSENSITIVE, Constants.colorBg.gColor ) + buttSouthEventBox.add(self.buttSouth) + self.buttSouth.set_image(Constants.southImgClr) + self.buttSouth.connect('clicked', self._buttSouthCb) + buttSouthFiller = gtk.EventBox() + buttSouthFiller.modify_bg( gtk.STATE_NORMAL, Constants.colorBg.gColor ) + buttSouthFiller.modify_bg( gtk.STATE_ACTIVE, Constants.colorBg.gColor ) + buttSouthFiller.modify_bg( gtk.STATE_INSENSITIVE, Constants.colorBg.gColor ) + vboxSouth.pack_start(buttSouthFiller, expand=True) + self.buttSouth.set_relief(gtk.RELIEF_NONE) + + 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) + + hboxWest = gtk.HBox() + hbox.pack_start(hboxWest) + buttWestFiller = gtk.EventBox() + buttWestFiller.modify_bg( gtk.STATE_ACTIVE, Constants.colorBg.gColor ) + buttWestFiller.modify_bg( gtk.STATE_NORMAL, Constants.colorBg.gColor ) + buttWestFiller.modify_bg( gtk.STATE_INSENSITIVE, Constants.colorBg.gColor ) + hboxWest.pack_start(buttWestFiller, expand=True) + buttWestEventBox = gtk.EventBox() + hboxWest.pack_start(buttWestEventBox, expand=False) + buttWestEventBox.modify_bg( gtk.STATE_NORMAL, Constants.colorBg.gColor ) + buttWestEventBox.modify_bg( gtk.STATE_ACTIVE, Constants.colorBg.gColor ) + buttWestEventBox.modify_bg( gtk.STATE_INSENSITIVE, Constants.colorBg.gColor ) + self.buttWest = gtk.Button() + self.buttWest.set_property("can-focus", False) + buttWestEventBox.add(self.buttWest) + self.buttWest.modify_bg( gtk.STATE_NORMAL, Constants.colorBg.gColor ) + self.buttWest.modify_bg( gtk.STATE_ACTIVE, Constants.colorBg.gColor ) + self.buttWest.modify_bg( gtk.STATE_INSENSITIVE, Constants.colorBg.gColor ) + self.buttWest.set_image( Constants.westImgClr ) + self.buttWest.connect('clicked', self._buttWestCb) + self.buttWest.set_relief(gtk.RELIEF_NONE) + + self.browseBox = gtk.VBox() + self.browser = WebViewer() + self.browser.set_size_request(int(webWidth*self.htmlScale), int(webHeight*self.htmlScale)) + self.browseBox.pack_start(self.browser) + self.browseBox.set_size_request(int(webWidth*self.htmlScale), int(webHeight*self.htmlScale)) + hbox.pack_start(self.browseBox, expand=False) + + hboxEast = gtk.HBox() + hbox.pack_start(hboxEast) + buttEastEventBox = gtk.EventBox() + hboxEast.pack_start(buttEastEventBox, expand=False) + buttEastEventBox.modify_bg( gtk.STATE_ACTIVE, Constants.colorBg.gColor ) + buttEastEventBox.modify_bg( gtk.STATE_NORMAL, Constants.colorBg.gColor ) + buttEastEventBox.modify_bg( gtk.STATE_INSENSITIVE, Constants.colorBg.gColor ) + self.buttEast = gtk.Button() + self.buttEast.set_property("can-focus", False) + self.buttEast.modify_bg( gtk.STATE_ACTIVE, Constants.colorBg.gColor ) + self.buttEast.modify_bg( gtk.STATE_NORMAL, Constants.colorBg.gColor ) + self.buttEast.modify_bg( gtk.STATE_INSENSITIVE, Constants.colorBg.gColor ) + buttEastEventBox.add(self.buttEast) + self.buttEast.set_image(Constants.eastImgClr) + buttEastFiller = gtk.EventBox() + hboxEast.pack_start(buttEastFiller, expand=True) + buttEastFiller.modify_bg( gtk.STATE_ACTIVE, Constants.colorBg.gColor ) + buttEastFiller.modify_bg( gtk.STATE_NORMAL, Constants.colorBg.gColor ) + buttEastFiller.modify_bg( gtk.STATE_INSENSITIVE, Constants.colorBg.gColor ) + self.buttEast.connect('clicked', self._buttEastCb) + self.buttEast.set_relief(gtk.RELIEF_NONE) + + #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) + + self.ajaxServer = ServerThread(self.__class__.ajaxPort, self.cometLogic) + self.ajaxServer.start() + + self.cometServer = ServerThread(self.__class__.cometPort, self.cometLogic) + self.cometServer.start() + + self.browser.load_uri("file://" + self.htmlPath + "/staticmap2.html?ajaxPort=" + str(self.__class__.ajaxPort) + "&cometPort=" + str(self.__class__.cometPort) + "&xo=true" + "&lat=" + str(self.NOW_MAP_CENTER_LAT) + "&lng=" + str(self.NOW_MAP_CENTER_LNG) + "&zoom=" + str(self.NOW_MAP_ZOOM) + "&type=" + self.__class__.initType + "&" + Constants.istrLang) + + 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( "" + Constants.istrConnecting + "") + 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() + + 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] + if(self.cometLogic.addKMLSet == 0): + self.preComet() + self.cometLogic.handleAddMarker(params[1], params[2], params[3], params[4]) + self.m.setInfo(params[1],params[2],params[3],params[4]) + #self.cometLogic.handleEndKML() + if(self.cometLogic.addKMLSet == 0): + 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 + if(self.cometLogic.addKMLSet == 0): + self.preComet() + self.cometLogic.handleLine(params[1],params[2],params[3],params[4]) + self.addLine(params[1],params[2],params[3],params[4],0) + #self.cometLogic.handleEndKML() + if(self.cometLogic.addKMLSet == 0): + self.postComet() + elif(params[0] == "endkml"): + self.cometLogic.handleEndKML(0) + self.postComet() + elif(params[0] == "startkml"): + self.preComet() + self.cometLogic.startKML(0) + + 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) + 125 + if(showx > 60) and (showx < 700) and (showy > 125) and (showy < 400): + # 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 loadMapV3( self ): + # self.browser.load_uri("file://" + self.htmlPath + "/map3.html?ajaxPort=" + str(self.__class__.ajaxPort) + "&cometPort=" + str(self.__class__.cometPort) + "&xo=true" + "&lat=" + str(self.NOW_MAP_CENTER_LAT) + "&lng=" + str(self.NOW_MAP_CENTER_LNG) + "&zoom=" + str(self.NOW_MAP_ZOOM) + "&type=" + self.__class__.initType) + + 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 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 _addressUpdateCb( self, otets, 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 + elif(os.path.exists(self.libPath)): + mp_address = address.lower().replace(' ','').replace('-','') + zoom="13" + 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] == mp_address): + lat = pData[1] + lng = pData[2] + if(pData[3] != "-1"): + zoom = pData[3] + self.preComet() + self.cometLogic.handleReceivedMap(lat,lng,zoom) + self.postComet() + return + self.preComet() + self.cometLogic.handleAddressUpdate(address) + self.postComet() + + def _searchUpdateCb( self, otets, tags ): + self.NOW_MAP_TAGS = tags + self.preComet() + self.cometLogic.handleTagSearch(tags) + self.postComet() + + def _buttEastCb( self, butt ): + self.hideMedia() + self.preComet() + self.cometLogic.handleCompassUpdate("e") + self.postComet() + + + def _buttWestCb( self, butt ): + self.hideMedia() + self.preComet() + self.cometLogic.handleCompassUpdate("w") + self.postComet() + + def _buttNorthCb( self, butt ): + self.hideMedia() + self.preComet() + self.cometLogic.handleCompassUpdate("n") + self.postComet() + + def _buttSouthCb( self, butt ): + self.hideMedia() + self.preComet() + self.cometLogic.handleCompassUpdate("s") + 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 = 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,isNew): + self.m.setLine(id,color,thickness,pts) + if((self.maptube is not None) and (isNew == 1)): + 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.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("" + Constants.istrLatitude + " ") + 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("" + Constants.istrLongitude + " ") + 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("" + Constants.istrTags + "") + 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 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 ): + self.readKML(open(datastoreOb.file_path, 'r')) + + def readKML(self, kml): + firstLine = kml.readline() + if(firstLine.find('') + for node in xmlarray: + #if(node.find('') != -1): + dataTable = dataTable + '' + if(dataTable is not None): + if(kmlline.find('' + if(kmlline.find('" + kmlline[kmlline.find('" + if(kmlline.find('') != -1): + dataTable = dataTable + "" + kmlline[kmlline.find('')+7:kmlline.find('')] + "" + elif(kmlline.find('') != -1): + dataTable = dataTable + '' + + if(kmlline.find('') != -1): + if(placemarkType == 'info'): + lng = kmlline[kmlline.find('')+13:kmlline.find(',')] + lat = kmlline[kmlline.find(',')+1:kmlline.rfind(',')] + else: + if(kmlline.find('') == -1): + morePts = True + kmlline = kmlline.replace('','').replace('','') + coordList = kmlline.split(' ') + elif(morePts == True): + if(kmlline.find('') != -1): + morePts = False + kmlline = kmlline.replace('','') + coordList.extend(kmlline.split(' ')) + + if(kmlline.find('') != -1): + placename = kmlline[kmlline.find('')+6:kmlline.find('')] + if(kmlline.find('') != -1): + description = kmlline[kmlline.find('')+13:len(kmlline)] + if(kmlline.find('') == -1): + # need to read in more lines + stillDescribing = True + else: + description = description[0:description.find('','') + elif(stillDescribing == True): + description = description + kmlline + if(kmlline.find('') != -1): + stillDescribing = False + description = description[0:description.find('','') + if(kmlline.find('')!=-1): + icon = kmlline[kmlline.find('')+12:kmlline.find('')] + if((kmlline.find('') != -1) or (kmlline.find('') != -1)): + # finish this placemark + if(lat is not None): + if(description is None): + description = "" + else: + description=description.replace("width:300","width:60").replace("max-height:400","max-height:80") + if(dataTable is not None): + description = dataTable + description + if(placename is not None): + description = '

' + placename + '

' + description + if(icon is None): + icon="null" + self.cometLogic.handleAddMarker(lat,lng,description,icon) + self.addInfoMarker(lat,lng,description,icon,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'): + lineID = str(random.getrandbits(128)) + self.cometLogic.handleLine(lineID,"B22222","5",'|'.join(ptList)) + self.addLine(lineID,"B22222","5",'|'.join(ptList),1) + elif(lineType == 'poly'): + lineID = "poly-" + str(random.getrandbits(128)) + self.cometLogic.handleLine(lineID,"7CFC00","5",'|'.join(ptList)) + self.addLine(lineID,"7CFC00","5",'|'.join(ptList),1) + #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('') == -1): + readingInNodeID = nodeID + elif(readingInNodeID is not None): + # reading in tag-values for the point + if(osm.find('') != -1): + readingInNodeID = None + else: + if(osm.find('') != -1): + readingInWayID = None + else: + if(osm.find('"+tag+""+str(value)+"" + description=description+"" + self.cometLogic.handleAddMarker(osmNodes[node]["latitude"],osmNodes[node]["longitude"],description,"null") + self.addInfoMarker(osmNodes[node]["latitude"],osmNodes[node]["longitude"],description,"null",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('')+1:rss.find('')] + if(rss.find('')+1:rss.find('')] + if(rss.find('')+1:rss.find('')].split(' ') + lat = latlng[0] + lng = latlng[1] + if(rss.find('') != -1): + description = rss[rss.find('')+13:len(rss)] + if(rss.find('') == -1): + # need to read in more lines + stillDescribing = True + else: + description = description[0:description.find('') != -1): + stillDescribing = False + description = description[0:description.find('') != -1): + # publish the point + if(lat is not None): + if(description == None): + description = "" + if(placeLink is not None): + description = placeLink + "
" + description + if(placename is not None): + description = '

' + placename + '

' + description + description=description.replace("'","\\'").replace('"','\\"') + description=description.replace("\n","
").replace("\r","
") + description=description.replace("","") + self.cometLogic.handleAddMarker(lat,lng,description,"null") + self.addInfoMarker(lat,lng,description,"null",True) + placeLink = None + lat = None + lng = None + placename = None + description = None + stillDescribing = False + self.cometLogic.handleEndKML(1) + 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 _olpcmapCb(self,ot): + self.preComet() + self.cometLogic.handleOlpcMAP() + self.postComet() + + def _panoramioCb(self,ot): + self.preComet() + self.cometLogic.handlePanoramio() + self.postComet() + + def _localwikiCb(self,ot): + self.preComet() + self.cometLogic.handleLocalWiki() + self.postComet() + + def _wikimapiaCb(self,ot): + self.preComet() + self.cometLogic.handleWikiMapia() + self.postComet() + + def disableNavigation( self ): + #disable the nav buttons + self.buttEast.set_image(Constants.eastImgBw) + self.buttEast.set_sensitive(False) + self.buttWest.set_image(Constants.westImgBw) + self.buttWest.set_sensitive(False) + self.buttNorth.set_image(Constants.northImgBw) + self.buttNorth.set_sensitive(False) + self.buttSouth.set_image(Constants.southImgBw) + self.buttSouth.set_sensitive(False) + #disable the toolbar buttons + self.addToolbar.set_sensitive( False ) + self.searchToolbar.set_sensitive( False ) + + def enableNavigation( self ): + #enable the nav buttons + self.buttEast.set_image(Constants.eastImgClr) + self.buttEast.set_sensitive(True) + self.buttWest.set_image(Constants.westImgClr) + self.buttWest.set_sensitive(True) + self.buttNorth.set_image(Constants.northImgClr) + self.buttNorth.set_sensitive(True) + self.buttSouth.set_image(Constants.southImgClr) + self.buttSouth.set_sensitive(True) + #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 sendEndKML(self): + self.maptube.SendText("endkml,now") + + def sendStartKML(self): + self.maptube.SendText("startkml,now") + + 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() + self._insertSep() + self._insertSep() + 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() + + self._insertSep() + + zout = ToolButton('map-icon-zoomOut') + zout.set_tooltip(Constants.istrZoomOut) + self.insert( zout, -1 ) + zout.connect('clicked', self._zoomOutCb) + + zin = ToolButton('map-icon-zoomIn') + zin.set_tooltip(Constants.istrZoomIn) + self.insert( zin, -1 ) + zin.connect('clicked', self._zoomInCb) + + 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, []), + 'olpcmap': (gobject.SIGNAL_RUN_FIRST, None, []), + 'panoramio':(gobject.SIGNAL_RUN_FIRST, None, []), + 'local-wiki':(gobject.SIGNAL_RUN_FIRST, None, []), + 'wikimapia':(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('info-marker') + infoButt.set_tooltip(Constants.istrAddInfo) + self.insert(infoButt, -1) + infoButt.connect('clicked', self._addInfoCb) + + 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() + + # Maps4xo library + webButt = ToolButton('web-icon') + webButt.set_tooltip(Constants.istrWebMedia) + self.insert(webButt, -1) + webButt.connect('clicked', self._webCb) + + # OurMaps Wiki + staticButt = ToolButton('static-icon') + staticButt.set_tooltip(Constants.istrStaticMaps) + self.insert(staticButt, -1) + staticButt.connect('clicked', self._toStaticCb) + + panorButt = ToolButton('panoramio') + panorButt.set_tooltip(Constants.istrPanoramio) + self.insert(panorButt,-1) + panorButt.connect('clicked', self._toPanoramioCb) + + lwButt = ToolButton('localwiki') + lwButt.set_tooltip(Constants.istrLocalWiki) + self.insert(lwButt,-1) + lwButt.connect('clicked', self._toLocalWikiCb) + + mapiaButt = ToolButton('wikimapia') + mapiaButt.set_tooltip(Constants.istrWikiMapia) + self.insert(mapiaButt,-1) + mapiaButt.connect('clicked', self._toWikiMapiaCb) + + 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) + "&" + Constants.istrLang ) + + def _measCb(self, button): + # start a measure tool (rect area or polyline) - calculation handled in HTML/JavaScript + self.emit("measure") + + def _toStaticCb(self, button): + # Map wiki through maptonomy.appspot.com/ourmap.html + #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) + "&" + Constants.istrLang ) + self.emit("olpcmap") + #self.ca.preComet() + #self.ca.cometLogic.handleOlpcMAP() + #self.ca.postComet() + + def _toPanoramioCb(self, button): + self.emit("panoramio") + + def _toLocalWikiCb(self, button): + self.emit("local-wiki") + + def _toWikiMapiaCb(self, button): + self.emit("wikimapia") + + 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/model.py b/model.py new file mode 100644 index 0000000..c97f3fe --- /dev/null +++ b/model.py @@ -0,0 +1,147 @@ +# 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( new google.maps.LatLng(" + str(rec.latitude) + "," + str(rec.longitude) + "), '" + rec.getThumbUrl() + "', '" + rec.getThumbBasename() + "', null, null, 'null');" + r = r + "canvas.markerList.addMarker(m" + str(i) + ");" + + for k, i in self.infoMarkers.iteritems(): + iMarker = i.split(";~") + r = r + "addInfoMarker(" + iMarker[0] + "," + iMarker[1] + ",'" + iMarker[2] + "','" + iMarker[3] + "');" + + for k, i in self.lineData.iteritems(): + iLine = i.split(";~") + r = r + "addLine('" + iLine[0] + "','" + iLine[1] + "','" + iLine[2] + "','" + iLine[3] + "');" + return r diff --git a/news b/news new file mode 100644 index 0000000..8ebe0c3 --- /dev/null +++ b/news @@ -0,0 +1,5 @@ +1 +* init (jedierikb) + +2 +* 8.2 re-release( jedierikb) \ No newline at end of file diff --git a/p5.py b/p5.py new file mode 100644 index 0000000..8a92c15 --- /dev/null +++ b/p5.py @@ -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 , 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 \n" +"Language-Team: LANGUAGE \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..4415f3a --- /dev/null +++ b/serialize.py @@ -0,0 +1,203 @@ +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 + + 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 diff --git a/tray.py b/tray.py new file mode 100644 index 0000000..d82e442 --- /dev/null +++ b/tray.py @@ -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 = '' % fill + data = re.sub('', entity, data) + + entity = '' % stroke + data = re.sub('', 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 -- cgit v0.9.1