pax_global_header00006660000000000000000000000064115156474770014533gustar00rootroot0000000000000052 comment=adcbe4f5c31cdb00d84b0368978f67ddd726faee agtl-0.8.0.3/000077500000000000000000000000001151564747700126305ustar00rootroot00000000000000agtl-0.8.0.3/.gitignore000066400000000000000000000001761151564747700146240ustar00rootroot00000000000000*.ipk *.pyo *.pyc *.tar.gz *.dsc *.changes *~ *.egg* files/dist/ files/build/ files/advancedcaching/build/ /*.zip freerunner/ agtl-0.8.0.3/build-qt.sh000077500000000000000000000012511151564747700147070ustar00rootroot00000000000000set -o verbose pyuic4 files/qt/MainWindow.ui > files/advancedcaching/qt/ui_mainwindow.py pyuic4 files/qt/SearchDialog.ui > files/advancedcaching/qt/ui_searchdialog.py pyuic4 files/qt/ShowImageDialog.ui > files/advancedcaching/qt/ui_showimagedialog.py pyuic4 files/qt/GeocacheDetailsWindow.ui > files/advancedcaching/qt/ui_geocachedetailswindow.py pyuic4 files/qt/SearchGeocachesDialog.ui > files/advancedcaching/qt/ui_searchgeocachesdialog.py pyuic4 files/qt/SearchResultsDialog.ui > files/advancedcaching/qt/ui_searchresultsdialog.py pyuic4 files/qt/OptionsDialog.ui > files/advancedcaching/qt/ui_optionsdialog.py pyrcc4 files/qt/icons.qrc > files/advancedcaching/qt/icons_rc.py agtl-0.8.0.3/cp2freerunner000077500000000000000000000003131151564747700153330ustar00rootroot00000000000000#!/bin/bash if [ "" == "$1" ] ; then echo "Bitte Versionsnummer angeben." exit fi scp advancedcaching_$1_all.ipk root@192.168.0.202: ssh root@192.168.0.202 "opkg-cl install advancedcaching_$1_all.ipk" agtl-0.8.0.3/files/000077500000000000000000000000001151564747700137325ustar00rootroot00000000000000agtl-0.8.0.3/files/MANIFEST.in000066400000000000000000000000751151564747700154720ustar00rootroot00000000000000include agtl README recursive-include advancedcaching/data * agtl-0.8.0.3/files/advancedcaching-48.png000066400000000000000000000047741151564747700177670ustar00rootroot00000000000000PNG  IHDR00WsRGB pHYs B(xtIME 3h IDAThYYl3]%KH,bύEjPїZ ARQSB K!B}h+!Z$mH0I & l}9{|I"'#߱dϜ9p]N9 XWX!8)7͛ g{= Qv S{يkUy]Ȟ<@<ֆ 7XbbO~,;ۅe%&b"ƌd:]G0#cNgdh`e u -wLppyLy _rXImu/; aƓҀb@i@2 5i @XZ.Ţ9K`GĴq`S\G WpXh}`d^w4+πTG D*l4 lmh u,Tl[ ؂` Tr O_,B21(dQ2998n*@0R-+{^vsy4TB *zRއK%o J9)u]W')NLh}jl.MKaOJKG!y,׽yUA`[ M,,N?6G9i/t }+Ѳ],> s$@,,YK.%?4x 3`L8#%?gc& |&Ckb@"1o!ㅔ T,NKzϾƅV/\ln\嚧U F'9qR" ݯRmAT=]FOEDȌZHWV%]YЅJfO2nu<_{A I=O}C!S:>wQxrFNkSJȌMw+=q8Sdm )hN.&gh]ݽ%e16`{CθkTiLPqha'xh4UwY P]`t6i9x$H0/w"͐(?Dc[M p-YueXb›s4 z/lw<,ēן߸"&(-cMY5y&<4:72K7n,?r| 0sU\곜, ()\>zf>sQIڻM#u MfE*b˚^ d~d3ce68aLʐR7^yk4{sŸ^-k;q$bZ@EI(+2s>rDri.[}&`wV{@4phׯ'>]`8*pLePLuLZ/;o?mxmީ'm"s:Ǚ@sj>ƻyB;ĽdSh Tlp/ 㮖ԝb˳Gz0NiڰE-ҙr +=X-`ێ[s `dlhizJ>|ӮU9+I\Ÿ8+ٝ"Ԣ~71)3y؏*g? `{y|_]~\7F^<D-ϟ5ɟ[|C_8.u`tGvF۷_)>x'Qfev~عz= ٙ_c]JMcӲC |+tnr]`?Nu24/7`ˊkr3462cQ0%up9L>vbJٖf䌶}p{v S 6߮?Poyo֭S~[䈋"IENDB`agtl-0.8.0.3/files/advancedcaching-debian.desktop000066400000000000000000000005671151564747700216370ustar00rootroot00000000000000 [Desktop Entry] Name=AGTL Geocaching Tool Comment=Advanced Geocaching Tool For Linux Encoding=UTF-8 Version=0.0 Type=Application Exec=/usr/lib/site-python/advancedcaching/core.py --simple Icon=advancedcaching Terminal=false Categories=Office;Geography; SingleInstance=true StartupNotify=true Comment[de_DE.UTF-8]=Ein Geocaching-Werkzeug Name[de_DE]=AGTL Geocaching-Werkzeug agtl-0.8.0.3/files/advancedcaching-maemo.desktop000066400000000000000000000003701151564747700215030ustar00rootroot00000000000000[Desktop Entry] Name=AGTL Comment=Advanced Geocaching Tool For Linux Encoding=UTF-8 Version=1.0.0 Type=Application Exec=python2.5 /opt/advancedcaching/core.py --hildon Icon=advancedcaching X-Icon-path=/usr/share/icons X-Window-Icon=advancedcaching agtl-0.8.0.3/files/advancedcaching.desktop000066400000000000000000000005611151564747700204110ustar00rootroot00000000000000 [Desktop Entry] Name=AdvancedCaching Comment=Advanced Geocaching Tool For Linux Encoding=UTF-8 Version=0.0 Type=Application Exec=/usr/lib/site-python/advancedcaching/core.py --simple Icon=advancedcaching Terminal=false Categories=Office; SingleInstance=true StartupNotify=true Comment[de_DE.UTF-8]=An Advanced Linux Geocaching tool Name[de_DE]=advancedcaching.desktop agtl-0.8.0.3/files/advancedcaching.png000066400000000000000000000100561151564747700175240ustar00rootroot00000000000000PNG  IHDRPQEM!sBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<IDATx[yɽJU,ˎ,[eMeǮIIm4p캎Ӈl^7@۠H ~A4@IQ$}{dٺ{wjwr̐KE\rhf\9?\4`fԲ=c'nK"V*@"Z!e zeYnSPn@!_#jF(~UYш<ߡqwd%͖e]wۼeE!_3&XF| *+@"ҐgOF7&J0(]ƝţY }eYsn8/,KJJZS+e  7&J„fjьc~VV,sYsH2D>)JCvˊ1R,Sw$"԰jU[sG3e 2Q *P> H'֪-M,CJC>ʊ0,ūw$+2uj3 u 2zeYa_-0Bz. aR \ (B^̍ܞU8м}KX;(YOZ@!#0u 'zkmDfBT 療D+nІcgk'%[ӖeeՇ(ėI2ժz*1}B $$od# >U Hg/8fя(322T(\ ~U6mF0o;}1R$afÛm8XxMT-/1P?2IGsIQmtY0e0ėҕ^HD!!m#*4VZJN.?7PD8r>OU&JT-)!@"26jÕhOs]-,`"sb^,Ы>@G‘hcmq"ȫG2 !兡_g ﵲι^˅ H̉^>eFp$֔w]`<9UzM"q:H )/ Y?5my@u\V HgO9#[+bhj׶\W3̓D9/*b/cn`>uv`t$llYITG zføeA \yUZLmZgP+tBhXU$Cx7h 55!y^A ZTT'p`a(nXxCAjl6-b+Z[%@zo$rTV8s_S[  ]T&`=gבF:d 5u#h uj #ql0o1ȠPryXVxON$:4lꯧ!zj΅6#jGt 5SDt iC6h זw83APvAl'{ 5u ͢ieX(fEAv+TH3pRͅ=HDj-ر +k @څZ{ЗrlNeX:' ـpׁK (kfTLX74ga}KyF^ ${ \gs =lK^?l;/`nnMuU>PvqڄRw 톹Nrante_@=ùMZ͵Kr-Vgc-=u%V,O*^m<|#qP@ `zg&ȅ$bۉ򈼮diu7cB9;OP`c=5%V/G+? /u-B; hm[ `Рv[$@"CGUԼ xw0u<]  \ n%.gEvOD,u=;?90U¹. Qml>?v@eL˕4ϖ%Xy1"R@9ȼ?䷳ɏ'N>XQ>:<bY~pC5ߚyU<$TZHkf?ۅ.zAB ta,zA?q?5r۶n~pQ寞wS e|t``rE~, }(D4j c1tm,q0;F㕵3IUyв,̌}WGtHJ`}4Gfrwů {a,:8lz<՟,nٵ:kr-uY?>zq׶/hyjǩ<6~fYo/{g!{ Zo߻8ͯ. # # Author: Daniel Fett agtl@danielfett.de # Jabber: fett.daniel@jaber.ccc.de # Bugtracker and GIT Repository: http://github.com/webhamster/advancedcaching # import openstreetmap import logging logger = logging.getLogger('abstractmap') import geo import math class AbstractMap(): MAP_FACTOR = 0 RADIUS_EARTH = 6371000.0 CLICK_MAX_RADIUS = 7 CLICK_CHECK_RADIUS = 17 @classmethod def set_config(Map, map_providers, map_path, placeholder_cantload, placeholder_loading): Map.noimage_cantload = Map._load_tile(placeholder_cantload) Map.noimage_loading = Map._load_tile(placeholder_loading) Map.tile_loaders = [] for name, params in map_providers: tl = openstreetmap.get_tile_loader( ** dict([(str(a), b) for a, b in params.items()])) tl.noimage_loading = Map.noimage_loading tl.noimage_cantload = Map.noimage_cantload tl.base_dir = map_path #tl.gui = self Map.tile_loaders.append((name, tl)) def __init__(self, center, zoom, tile_loader = None): self.active_tile_loaders = [] self.double_size = False self.layers = [] self.osd_message = None if tile_loader == None: self.tile_loader = self.tile_loaders[0][1] else: self.tile_loader = tile_loader self.dragging = False self.drag_offset_x = 0 self.drag_offset_y = 0 self.zoom = zoom self.total_map_width = 256 * 2 ** zoom self.set_center(center, False) #self.set_zoom(zoom) ############################################## # # Controlling the layers # ############################################## def add_layer(self, layer): self.layers.append(layer) layer.attach(self) def set_osd_message(self, message): self.osd_message = message ############################################## # # Controlling the map view # ############################################## def set_center(self, coord, update = True): if self.dragging: return self.map_center_x, self.map_center_y = self.deg2num(coord) self.center_latlon = coord self.draw_at_x = 0 self.draw_at_y = 0 if update: self._draw_map() def set_center_lazy(self, coord): if self.dragging: return old_center_x, old_center_y = self.coord2point(self.center_latlon) new_center_x, new_center_y = self.coord2point(coord) if abs(old_center_x - new_center_x) > \ self.map_width * self.LAZY_SET_CENTER_DIFFERENCE or \ abs(old_center_y - new_center_y) > \ self.map_height * self.LAZY_SET_CENTER_DIFFERENCE: self.set_center(coord) logger.debug('Not lazy!') return True logger.debug('Lazy!') return False def get_center(self): return self.center_latlon def relative_zoom(self, direction=None, update=True): if direction != None: self.set_zoom(self.zoom + direction, update) def relative_zoom_preserve_center_at(self, screenpoint, direction): offs = screenpoint[0] - self.map_width/2.0, screenpoint[1] - self.map_height/2.0 self.set_center(self.screenpoint2coord(screenpoint), False) self.relative_zoom(direction, False) self._move_map_relative(-offs[0], -offs[1]) def set_zoom(self, newzoom, update = True): if newzoom < 0 or newzoom > self.tile_loader.MAX_ZOOM: return logger.debug('New zoom level: %d' % newzoom) self.zoom = newzoom self.total_map_width = (256 * 2**self.zoom) self.set_center(self.center_latlon, update) def get_zoom(self): return self.zoom def get_max_zoom(self): return self.tile_loader.MAX_ZOOM def get_min_zoom(self): return 0 def _move_map_relative(self, offset_x, offset_y, update = True): self.map_center_x += (float(offset_x) / self.tile_loader.TILE_SIZE) self.map_center_y += (float(offset_y) / self.tile_loader.TILE_SIZE) self.map_center_x, self.map_center_y = self.check_bounds(self.map_center_x, self.map_center_y) self.center_latlon = self.num2deg(self.map_center_x, self.map_center_y) if update: self._draw_map() def fit_to_bounds(self, minlat, maxlat, minlon, maxlon): if minlat == maxlat and minlon == maxlon: self.set_center(geo.Coordinate(minlat, minlon)) self.set_zoom(self.get_max_zoom()) return logger.debug("Settings Bounds: lat(%f, %f) lon(%f, %f)" % (minlat, maxlat, minlon, maxlon)) req_deg_per_pix_lat = (maxlat - minlat) / self.map_height prop_zoom_lat = math.log(((180.0/req_deg_per_pix_lat) / self.tile_loader.TILE_SIZE), 2) req_deg_per_pix_lon = (maxlon - minlon) / self.map_width prop_zoom_lon = math.log(((360.0/req_deg_per_pix_lon) / self.tile_loader.TILE_SIZE), 2) target_zoom = math.floor(min(prop_zoom_lat, prop_zoom_lon)) logger.debug("Proposed zoom lat: %f, proposed zoom lon: %f, target: %f" %(prop_zoom_lat, prop_zoom_lon, target_zoom)) center = geo.Coordinate((maxlat + minlat) / 2.0, (maxlon + minlon) / 2.0) logger.debug("New Center: %s" % center) self.set_center(center, False) self.set_zoom(max(min(target_zoom, self.get_max_zoom()), self.get_min_zoom())) ############################################## # # Configuration # ############################################## def set_double_size(self, ds): self.double_size = ds def get_double_size(self): return self.double_size def set_tile_loader(self, loader): self.tile_loader = loader self.emit('tile-loader-changed', loader) self.relative_zoom(0) def set_placeholder_images(self, cantload, loading): self.noimage_cantload = self._load_tile(cantload) self.noimage_loading = self._load_tile(loading) ############################################## # # Coordinate Conversion and Checking # ############################################## def point_in_screen(self, point): a = (point[0] >= 0 and point[1] >= 0 and point[0] < self.map_width and point[1] < self.map_height) return a def coord2point(self, coord): point = self.deg2num(coord) size = self.tile_loader.TILE_SIZE p_x = int(point[0] * size + self.map_width / 2) - self.map_center_x * size p_y = int(point[1] * size + self.map_height / 2) - self.map_center_y * size return (p_x % self.total_map_width , p_y) def coord2point_float(self, coord): point = self.deg2num(coord) size = self.tile_loader.TILE_SIZE p_x = point[0] * size + self.map_width / 2 - self.map_center_x * size p_y = point[1] * size + self.map_height / 2 - self.map_center_y * size return (p_x % self.total_map_width , p_y) def screenpoint2coord(self, point): size = self.tile_loader.TILE_SIZE coord = self.num2deg(\ ((point[0] - self.draw_at_x) + self.map_center_x * size - self.map_width / 2) / size, \ ((point[1] - self.draw_at_y) + self.map_center_y * size - self.map_height / 2) / size \ ) return coord def get_visible_area(self): a = self.screenpoint2coord((0, 0)) b = self.screenpoint2coord((self.map_width, self.map_height)) return geo.Coordinate(min(a.lat, b.lat), min(a.lon, b.lon)), geo.Coordinate(max(a.lat, b.lat), max(a.lon, b.lon)) @staticmethod def in_area(coord, area): return (coord.lat > area[0].lat and coord.lat < area[1].lat and coord.lon > area[0].lon and coord.lon < area[1].lon) def _check_click(self, offset_x, offset_y, ev_x, ev_y): if offset_x ** 2 + offset_y ** 2 < self.CLICK_MAX_RADIUS ** 2: c = self.screenpoint2coord((ev_x, ev_y)) c1 = self.screenpoint2coord([ev_x - self.CLICK_CHECK_RADIUS, ev_y - self.CLICK_CHECK_RADIUS]) c2 = self.screenpoint2coord([ev_x + self.CLICK_CHECK_RADIUS, ev_y + self.CLICK_CHECK_RADIUS]) for l in reversed(self.layers): if l.clicked_screen((ev_x, ev_y)) == False: break if l.clicked_coordinate(c, c1, c2) == False: break return True return False ############################################## # # Tile Number calculations # ############################################## def tile_size(self): return self.tile_loader.TILE_SIZE def get_meters_per_pixel(self, lat): return math.cos(lat * math.pi / 180.0) * 2.0 * math.pi * self.RADIUS_EARTH / self.total_map_width def deg2tilenum(self, lat_deg, lon_deg): lat_rad = math.radians(lat_deg) n = 2 ** self.zoom xtile = int((lon_deg + 180) / 360 * n) ytile = int((1.0 - math.log(math.tan(lat_rad) + (1.0 / math.cos(lat_rad))) / math.pi) / 2.0 * n) return(xtile, ytile) def deg2num(self, coord): lat_rad = math.radians(coord.lat) n = 2 ** self.zoom xtile = (coord.lon + 180.0) / 360 * n ytile = (1.0 - math.log(math.tan(lat_rad) + (1.0 / math.cos(lat_rad))) / math.pi) / 2.0 * n return(xtile, ytile) def num2deg(self, xtile, ytile): n = 2 ** self.zoom lon_deg = xtile / n * 360.0 - 180.0 lat_rad = math.atan(math.sinh(math.pi * (1 - 2 * ytile / n))) lat_deg = lat_rad * 180.0 / math.pi return geo.Coordinate(lat_deg, lon_deg) def check_bounds(self, xtile, ytile): max_x = 2**self.zoom max_y = 2**self.zoom return ( xtile % max_x, ytile % max_y ) class AbstractMapLayer(): def __init__(self): self.result = None def draw(self): pass def clicked_screen(self, screenpoint): pass def clicked_coordinate(self, center, topleft, bottomright): pass def resize(self): pass def attach(self, map): self.map = map def refresh(self): self.draw() self.map.refresh() logger = logging.getLogger('abstractmarkslayer') class AbstractMarksLayer(AbstractMapLayer): ARROW_OFFSET = 1.0 / 3.0 # Offset to center of arrow, calculated as 2-x = sqrt(1^2+(x+1)^2) ARROW_SHAPE = [(0, -2 + ARROW_OFFSET), (1, + 1 + ARROW_OFFSET), (0, 0 + ARROW_OFFSET), (-1, 1 + ARROW_OFFSET), (0, -2 + ARROW_OFFSET)] def __init__(self): AbstractMapLayer.__init__(self) self.current_target = None self.gps_target_distance = None self.gps_target_bearing = None self.gps_data = None self.gps_last_good_fix = None self.gps_has_fix = None self.follow_position = False def set_follow_position(self, value): logger.info('Setting "Follow position" to :' + repr(value)) if value and not self.follow_position and self.gps_last_good_fix != None: self.map.set_center(self.gps_last_good_fix.position) self.follow_position = value def get_follow_position(self): return self.follow_position def on_target_changed(self, caller, cache, distance, bearing): self.current_target = cache self.gps_target_distance = distance self.gps_target_bearing = bearing def on_good_fix(self, core, gps_data, distance, bearing): self.gps_data = gps_data self.gps_last_good_fix = gps_data self.gps_has_fix = True self.gps_target_distance = distance self.gps_target_bearing = bearing if self.map.dragging: return if (self.follow_position and not self.map.set_center_lazy(self.gps_data.position)) or not self.follow_position: self.draw() self.map.refresh() def on_no_fix(self, caller, gps_data, status): self.gps_data = gps_data self.gps_has_fix = False @staticmethod def _get_arrow_transformed(root_x, root_y, width, height, angle): multiply = height / (2 * (2-AbstractMarksLayer.ARROW_OFFSET)) offset_x = width / 2 offset_y = height / 2 s = multiply * math.sin(math.radians(angle)) c = multiply * math.cos(math.radians(angle)) arrow_transformed = [(int(x * c + offset_x - y * s) + root_x, int(y * c + offset_y + x * s) + root_y) for x, y in AbstractMarksLayer.ARROW_SHAPE] return arrow_transformed class AbstractGeocacheLayer(AbstractMapLayer): CACHE_SIZE = 20 TOO_MANY_POINTS = 30 CACHES_ZOOM_LOWER_BOUND = 9 CACHE_DRAW_SIZE = 10 MAX_NUM_RESULTS_SHOW = 100 def __init__(self, get_geocaches_callback, show_cache_callback): AbstractMapLayer.__init__(self) #self.show_found = False self.show_name = False self.get_geocaches_callback = get_geocaches_callback self.visualized_geocaches = [] self.show_cache_callback = show_cache_callback self.current_cache = None self.select_found = None ''' def set_show_found(self, show_found): if show_found: self.select_found = None else: self.select_found = False ''' def set_show_name(self, show_name): self.show_name = show_name def set_current_cache(self, cache): self.current_cache = cache def clicked_coordinate(self, center, topleft, bottomright): mindistance = (center.lat - topleft.lat) ** 2 + (center.lon - topleft.lon) ** 2 mincache = None for c in self.visualized_geocaches: dist = (c.lat - center.lat) ** 2 + (c.lon - center.lon) ** 2 if dist < mindistance: mindistance = dist mincache = c if mincache != None: self.show_cache_callback(mincache) return False @staticmethod def shorten_name(s, chars): max_pos = chars min_pos = chars - 10 NOT_FOUND = -1 suffix = '…' # Case 1: Return string if it is shorter (or equal to) than the limit length = len(s) if length <= max_pos: return s else: # Case 2: Return it to nearest period if possible try: end = s.rindex('.', min_pos, max_pos) except ValueError: # Case 3: Return string to nearest space end = s.rfind(' ', min_pos, max_pos) if end == NOT_FOUND: end = max_pos return s[0:end] + suffix agtl-0.8.0.3/files/advancedcaching/actors/000077500000000000000000000000001151564747700203075ustar00rootroot00000000000000agtl-0.8.0.3/files/advancedcaching/actors/__init__.py000066400000000000000000000000001151564747700224060ustar00rootroot00000000000000agtl-0.8.0.3/files/advancedcaching/actors/notify.py000066400000000000000000000013651151564747700221760ustar00rootroot00000000000000import gobject class Notify(gobject.GObject): def __init__(self, core): gobject.GObject.__init__(self) self.gps_target_bearing_abs = None self.gps_target_distance = None #core.connect('good-fix', self.__on_good_fix) #core.connect('no-fix', self.__on_no_fix) #core.connect('settings-changed', self.__on_settings_changed) def __on_settings_changed(self, caller, settings, source): pass def __on_good_fix(self, caller, gps_data, distance, bearing): self.gps_target_distance = distance self.gps_target_bearing_abs = bearing - gps_data.bearing def __on_no_fix(self, caller, fix, msg): self.gps_target_distance = None self.gps_target_bearing_abs = None agtl-0.8.0.3/files/advancedcaching/actors/tts.py000066400000000000000000000111221151564747700214700ustar00rootroot00000000000000import subprocess import logging import gobject logger = logging.getLogger('tts') class TTS(gobject.GObject): MIN_DISTANCE = 20 MAX_INTERVAL_DISTANCE = 1000 MIN_INTERVAL = 5 MAX_INTERVAL = 50 DEFAULT_INTERVAL = 10 __gsignals__ = { 'error' : (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)), } def __init__(self, core): gobject.GObject.__init__(self) self.gps_target_bearing_abs = None self.gps_target_distance = None self.gps_data = None self.proc = None self.timeout_event_id = None self.automatic_interval = False core.connect('good-fix', self.__on_good_fix) core.connect('no-fix', self.__on_no_fix) core.connect('settings-changed', self.__on_settings_changed) def __on_settings_changed(self, caller, settings, source): if 'tts_interval' in settings: self.__set_enabled(settings['tts_interval']) def __set_enabled(self, interval): if interval == -1: interval = self.__calculate_automatic_interval(self.gps_target_distance) self.automatic_interval = True logger.info("Setting interval to %d" % interval) else: self.automatic_interval = False if self.timeout_event_id != None: gobject.source_remove(self.timeout_event_id) self.timeout_event_id = None if interval > 0: self.timeout_event_id = gobject.timeout_add_seconds(interval, self.__tell) self.__connect() else: self.__disconnect() @staticmethod def __calculate_automatic_interval(d): if d == None: return TTS.DEFAULT_INTERVAL if d > TTS.MAX_INTERVAL_DISTANCE: return TTS.MAX_INTERVAL if d <= TTS.MIN_DISTANCE: return TTS.MIN_INTERVAL else: return int(round(TTS.MAX_INTERVAL + (TTS.MAX_INTERVAL_DISTANCE - d) * (float(TTS.MIN_INTERVAL - TTS.MAX_INTERVAL) / float(TTS.MAX_INTERVAL_DISTANCE - TTS.MIN_DISTANCE)))) def __connect(self): if self.proc != None: return logger.info("Starting espeak...") try: self.proc = subprocess.Popen("espeak", stdin=subprocess.PIPE) self.proc.stdin.write("Espeak ready.\n") except: self.proc = None self.emit('error', Exception("Please install the 'espeak' package from the package manager to get text-to-speech functionality.")) def __disconnect(self): logger.info("Stopping espeak...") if self.proc != None: self.proc.stdin.write("Stopping espeak.\n") #self.proc.terminate() self.proc = None def __on_good_fix(self, caller, gps_data, distance, bearing): self.gps_target_distance = distance self.gps_target_bearing_abs = bearing - gps_data.bearing self.gps_data = gps_data def __tell(self): output = "%s\n%s\n" % (self.__format_distance(), self.__format_bearing()) logger.info("Espeak: %s" % repr(output.strip())) self.proc.stdin.write(output) if self.automatic_interval: self.__set_enabled(-1) return False return True def __format_distance(self): distance = self.gps_target_distance if distance == None: return 'No Fix. ' if distance >= 1000: return '%d kilometers. ' % round(distance / 1000.0) elif distance >= 10: return '%d meters. ' % round(distance) else: return '%.1f meters. ' % round(distance, 1) def __format_bearing(self): if self.gps_target_bearing_abs == None: return '' if self.gps_data == None or self.gps_data.error_bearing > 179: return '' return "%d o'clock." % self.__degree_to_hour(self.gps_target_bearing_abs) def __on_no_fix(self, caller, fix, msg): self.gps_target_distance = None self.gps_target_bearing_abs = None self.gps_data = None @staticmethod def __degree_to_hour(degree): ret = round((degree % 360) / 30.0) return ret if ret != 0 else 12 @staticmethod def test(): print "Testing of coordinate-to-hour conversion:" for i in xrange(360): print "%3d -> %d" % (i, TTS.__degree_to_hour(i)) print print "Testing of interval calculation:" for z in xrange(1050, 0, -3): print "%4d m -> %d seconds" % (z, TTS.__calculate_automatic_interval(z)) if __name__ == '__main__': TTS.test() agtl-0.8.0.3/files/advancedcaching/astral.py000066400000000000000000000474721151564747700206720ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # Copyright 2009-2010, Simon Kennedy, python@sffjunkie.co.uk # Distributed under the terms of the MIT License. # # Shortened for AGTL by Daniel Fett import datetime from math import cos, sin, tan, acos, asin, atan2, floor, radians, degrees SUN_POSITION_CACHE_DURATION = 3600 # seconds class Astral(object): sun_cache_result = None sun_cache_time = None instance = None def __init__(self): """Initialise the list of cities. """ self._depression = 6 # Set default depression in degrees def solar_depression(): doc = """The number of degrees the sun must be below the horizon for the dawn/dusk calc. Can either be set as a number of degrees below the horizon or as one of the following strings ============= ======= String Degrees ============= ======= civil 6.0 nautical 12.0 astronomical 18.0 ============= ======= """ def fget(self): return self._depression def fset(self, depression): if isinstance(depression, basestring): try: self._depression = {'civil': 6, 'nautical': 12, 'astronomical': 18}[depression] except: raise KeyError("solar_depression must be either a number or one of 'civil', 'nautical' or 'astronomical'") else: self._depression = float(depression) return locals() solar_depression = property(**solar_depression()) def sun_utc(self, date, latitude, longitude): """Returns dawn, sunrise, noon, sunset and dusk times as a dictionary. """ dawn = self.dawn_utc(date, latitude, longitude) sunrise = self.sunrise_utc(date, latitude, longitude) noon = self.solar_noon_utc(date, longitude) sunset = self.sunset_utc(date, latitude, longitude) dusk = self.dusk_utc(date, latitude, longitude) return {'dawn': dawn, 'sunrise': sunrise, 'noon': noon, 'sunset': sunset, 'dusk': dusk} def dawn_utc(self, date, latitude, longitude): """Calculate dawn time for a specific date at a particular position. Returns date/time in UTC """ julianday = self._julianday(date.day, date.month, date.year) if latitude > 89.8: latitude = 89.8 if latitude < -89.8: latitude = -89.8 t = self._jday_to_jcentury(julianday) eqtime = self._eq_of_time(t) solarDec = self._sun_declination(t) try: hourangle = self._hour_angle_sunrise(latitude, solarDec) except: raise AstralError('Sun remains below horizon on this day, at this location.') delta = longitude - degrees(hourangle) timeDiff = 4.0 * delta timeUTC = 720.0 + timeDiff - eqtime newt = self._jday_to_jcentury(self._jcentury_to_jday(t) + timeUTC / 1440.0) eqtime = self._eq_of_time(newt) solarDec = self._sun_declination(newt) hourangle = self._hour_angle_dawn(latitude, solarDec, self._depression) delta = longitude - degrees(hourangle) timeDiff = 4 * delta timeUTC = 720 + timeDiff - eqtime timeUTC = timeUTC/60.0 hour = int(timeUTC) minute = int((timeUTC - hour) * 60) second = int((((timeUTC - hour) * 60) - minute) * 60) if hour > 23.0: hour -= 24 date += datetime.timedelta(days=1) dawn = datetime.datetime(date.year, date.month, date.day, hour, minute, second) return dawn def sunrise_utc(self, date, latitude, longitude): """Calculate sunrise time for a specific date at a particular position. Returns date/time in UTC """ julianday = self._julianday(date.day, date.month, date.year) t = self._jday_to_jcentury(julianday) eqtime = self._eq_of_time(t) solarDec = self._sun_declination(t) try: hourangle = self._hour_angle_sunrise(latitude, solarDec) except: raise AstralError('Sun remains below horizon on this day, at this location.') delta = longitude - degrees(hourangle) timeDiff = 4.0 * delta timeUTC = 720.0 + timeDiff - eqtime newt = self._jday_to_jcentury(self._jcentury_to_jday(t) + timeUTC / 1440.0) eqtime = self._eq_of_time(newt) solarDec = self._sun_declination(newt) hourangle = self._hour_angle_sunrise(latitude, solarDec) delta = longitude - degrees(hourangle) timeDiff = 4 * delta timeUTC = 720 + timeDiff - eqtime timeUTC = timeUTC/60.0 hour = int(timeUTC) minute = int((timeUTC - hour) * 60) second = int((((timeUTC - hour) * 60) - minute) * 60) if hour > 23: hour -= 24 date += datetime.timedelta(days=1) sunrise = datetime.datetime(date.year, date.month, date.day, hour, minute, second) return sunrise def solar_noon_utc(self, date, longitude): """Calculate solar noon time for a specific date at a particular position. Returns date/time in UTC """ julianday = self._julianday(date.day, date.month, date.year) newt = self._jday_to_jcentury(julianday + 0.5 + longitude / 360.0) eqtime = self._eq_of_time(newt) solarNoonDec = self._sun_declination(newt) timeUTC = 720.0 + (longitude * 4.0) - eqtime timeUTC = timeUTC/60.0 hour = int(timeUTC) minute = int((timeUTC - hour) * 60) second = int((((timeUTC - hour) * 60) - minute) * 60) if hour > 23: hour -= 24 date += datetime.timedelta(days=1) noon = datetime.datetime(date.year, date.month, date.day, hour, minute, second) return noon def sunset_utc(self, date, latitude, longitude): """Calculate sunset time for a specific date at a particular position. Returns date/time in UTC """ julianday = self._julianday(date.day, date.month, date.year) t = self._jday_to_jcentury(julianday) eqtime = self._eq_of_time(t) solarDec = self._sun_declination(t) try: hourangle = self._hour_angle_sunset(latitude, solarDec) except: raise AstralError('Sun remains below horizon on this day, at this location.') delta = longitude - degrees(hourangle) timeDiff = 4.0 * delta timeUTC = 720.0 + timeDiff - eqtime newt = self._jday_to_jcentury(self._jcentury_to_jday(t) + timeUTC / 1440.0) eqtime = self._eq_of_time(newt) solarDec = self._sun_declination(newt) hourangle = self._hour_angle_sunset(latitude, solarDec) delta = longitude - degrees(hourangle) timeDiff = 4 * delta timeUTC = 720 + timeDiff - eqtime timeUTC = timeUTC/60.0 hour = int(timeUTC) minute = int((timeUTC - hour) * 60) second = int((((timeUTC - hour) * 60) - minute) * 60) if hour > 23: hour -= 24 date += datetime.timedelta(days=1) sunset = datetime.datetime(date.year, date.month, date.day, hour, minute, second) return sunset def dusk_utc(self, date, latitude, longitude): """Calculate dusk time for a specific date at a particular position. Returns date/time in UTC """ julianday = self._julianday(date.day, date.month, date.year) if latitude > 89.8: latitude = 89.8 if latitude < -89.8: latitude = -89.8 t = self._jday_to_jcentury(julianday) eqtime = self._eq_of_time(t) solarDec = self._sun_declination(t) try: hourangle = self._hour_angle_sunset(latitude, solarDec) except: raise AstralError('Sun remains below horizon on this day, at this location.') delta = longitude - degrees(hourangle) timeDiff = 4.0 * delta timeUTC = 720.0 + timeDiff - eqtime newt = self._jday_to_jcentury(self._jcentury_to_jday(t) + timeUTC / 1440.0) eqtime = self._eq_of_time(newt) solarDec = self._sun_declination(newt) hourangle = self._hour_angle_dusk(latitude, solarDec, self._depression) delta = longitude - degrees(hourangle) timeDiff = 4 * delta timeUTC = 720 + timeDiff - eqtime timeUTC = timeUTC/60.0 hour = int(timeUTC) minute = int((timeUTC - hour) * 60) second = int((((timeUTC - hour) * 60) - minute) * 60) if hour > 23: hour -= 24 date += datetime.timedelta(days=1) dusk = datetime.datetime(date.year, date.month, date.day, hour, minute, second) return dusk def solar_azimuth(self, dateandtime, latitude, longitude): """Calculate the azimuth of the sun as a specific date/time and location. """ if latitude > 89.8: latitude = 89.8 if latitude < -89.8: latitude = -89.8 zone = 0#-dateandtime.utcoffset().seconds / 3600.0 utc_datetime = dateandtime #dateandtime.astimezone(pytz.utc) timenow = utc_datetime.hour + (utc_datetime.minute / 60.0) + (utc_datetime.second / 3600) JD = self._julianday(dateandtime.day, dateandtime.month, dateandtime.year) t = self._jday_to_jcentury(JD + timenow / 24.0) R = self._sun_rad_vector(t) alpha = self._sun_rt_ascension(t) theta = self._sun_declination(t) Etime = self._eq_of_time(t) eqtime = Etime solarDec = theta # in degrees earthRadVec = R solarTimeFix = eqtime - (4.0 * longitude) + (60 * zone) trueSolarTime = dateandtime.hour * 60.0 + dateandtime.minute + dateandtime.second / 60.0 + solarTimeFix # in minutes while trueSolarTime > 1440: trueSolarTime = trueSolarTime - 1440 hourangle = trueSolarTime / 4.0 - 180.0 # Thanks to Louis Schwarzmayr for the next line: if hourangle < -180: hourangle = hourangle + 360.0 harad = radians(hourangle) csz = sin(radians(latitude)) * sin(radians(solarDec)) + \ cos(radians(latitude)) * cos(radians(solarDec)) * cos(harad) if csz > 1.0: csz = 1.0 elif csz < -1.0: csz = -1.0 zenith = degrees(acos(csz)) azDenom = (cos(radians(latitude)) * sin(radians(zenith))) if (abs(azDenom) > 0.001): azRad = ((sin(radians(latitude)) * cos(radians(zenith))) - sin(radians(solarDec))) / azDenom if abs(azRad) > 1.0: if azRad < 0: azRad = -1.0 else: azRad = 1.0 azimuth = 180.0 - degrees(acos(azRad)) if hourangle > 0.0: azimuth = -azimuth else: if latitude > 0.0: azimuth = 180.0 else: azimuth = 0# if azimuth < 0.0: azimuth = azimuth + 360.0 return azimuth def solar_elevation(self, dateandtime, latitude, longitude): """Calculate the elevation of the sun as a specific date/time and location. """ if latitude > 89.8: latitude = 89.8 if latitude < -89.8: latitude = -89.8 zone = -dateandtime.utcoffset().seconds / 3600.0 utc_datetime = dateandtime.astimezone(pytz.utc) timenow = utc_datetime.hour + (utc_datetime.minute / 60.0) + (utc_datetime.second / 3600) JD = self._julianday(dateandtime.day, dateandtime.month, dateandtime.year) t = self._jday_to_jcentury(JD + timenow / 24.0) R = self._sun_rad_vector(t) alpha = self._sun_rt_ascension(t) theta = self._sun_declination(t) Etime = self._eq_of_time(t) eqtime = Etime solarDec = theta # in degrees earthRadVec = R solarTimeFix = eqtime - (4.0 * longitude) + (60 * zone) trueSolarTime = dateandtime.hour * 60.0 + dateandtime.minute + dateandtime.second / 60.0 + solarTimeFix # in minutes while trueSolarTime > 1440: trueSolarTime = trueSolarTime - 1440 hourangle = trueSolarTime / 4.0 - 180.0 # Thanks to Louis Schwarzmayr for the next line: if hourangle < -180: hourangle = hourangle + 360.0 harad = radians(hourangle) csz = sin(radians(latitude)) * sin(radians(solarDec)) + \ cos(radians(latitude)) * cos(radians(solarDec)) * cos(harad) if csz > 1.0: csz = 1.0 elif csz < -1.0: csz = -1.0 zenith = degrees(acos(csz)) azDenom = (cos(radians(latitude)) * sin(radians(zenith))) if (abs(azDenom) > 0.001): azRad = ((sin(radians(latitude)) * cos(radians(zenith))) - sin(radians(solarDec))) / azDenom if abs(azRad) > 1.0: if azRad < 0: azRad = -1.0 else: azRad = 1.0 azimuth = 180.0 - degrees(acos(azRad)) if hourangle > 0.0: azimuth = -azimuth else: if latitude > 0.0: azimuth = 180.0 else: azimuth = 0# if azimuth < 0.0: azimuth = azimuth + 360.0 exoatmElevation = 90.0 - zenith if exoatmElevation > 85.0: refractionCorrection = 0.0 else: te = tan(radians(exoatmElevation)) if exoatmElevation > 5.0: refractionCorrection = 58.1 / te - 0.07 / (te * te * te) + 0.000086 / (te * te * te * te * te) elif exoatmElevation > -0.575: step1 = (-12.79 + exoatmElevation * 0.711) step2 = (103.4 + exoatmElevation * (step1)) step3 = (-518.2 + exoatmElevation * (step2)) refractionCorrection = 1735.0 + exoatmElevation * (step3) else: refractionCorrection = -20.774 / te refractionCorrection = refractionCorrection / 3600.0 solarzen = zenith - refractionCorrection solarelevation = 90.0 - solarzen return solarelevation def _julianday(self, day, month, year): if month <= 2: year = year - 1 month = month + 12 A = floor(year / 100.0) B = 2 - A + floor(A / 4.0) return floor(365.25 * (year + 4716)) + floor(30.6001 * (month + 1)) + day + B - 1524.5 def _jday_to_jcentury(self, julianday): return (julianday - 2451545.0) / 36525.0 def _jcentury_to_jday(self, juliancentury): return (juliancentury * 36525.0) + 2451545.0 def _mean_obliquity_of_ecliptic(self, juliancentury): seconds = 21.448 - juliancentury * (46.815 + juliancentury * (0.00059 - juliancentury * (0.001813))) return 23.0 + (26.0 + (seconds / 60.0)) / 60.0 def _obliquity_correction(self, juliancentury): e0 = self._mean_obliquity_of_ecliptic(juliancentury) omega = 125.04 - 1934.136 * juliancentury return e0 + 0.00256 * cos(radians(omega)) def _geom_mean_long_sun(self, juliancentury): l0 = 280.46646 + juliancentury * (36000.76983 + 0.0003032 * juliancentury) return l0 % 360.0 def _eccentricity_earth_orbit(self, juliancentury): return 0.016708634 - juliancentury * (0.000042037 + 0.0000001267 * juliancentury) def _geom_mean_anomaly_sun(self, juliancentury): return 357.52911 + juliancentury * (35999.05029 - 0.0001537 * juliancentury) def _eq_of_time(self, juliancentury): epsilon = self._obliquity_correction(juliancentury) l0 = self._geom_mean_long_sun(juliancentury) e = self._eccentricity_earth_orbit(juliancentury) m = self._geom_mean_anomaly_sun(juliancentury) y = tan(radians(epsilon) / 2.0) y = y * y sin2l0 = sin(2.0 * radians(l0)) sinm = sin(radians(m)) cos2l0 = cos(2.0 * radians(l0)) sin4l0 = sin(4.0 * radians(l0)) sin2m = sin(2.0 * radians(m)) Etime = y * sin2l0 - 2.0 * e * sinm + 4.0 * e * y * sinm * cos2l0 - \ 0.5 * y * y * sin4l0 - 1.25 * e * e * sin2m return degrees(Etime) * 4.0 def _sun_eq_of_center(self, juliancentury): m = self._geom_mean_anomaly_sun(juliancentury) mrad = radians(m) sinm = sin(mrad) sin2m = sin(mrad + mrad) sin3m = sin(mrad + mrad + mrad) c = sinm * (1.914602 - juliancentury * (0.004817 + 0.000014 * juliancentury)) + \ sin2m * (0.019993 - 0.000101 * juliancentury) + sin3m * 0.000289 return c def _sun_true_long(self, juliancentury): l0 = self._geom_mean_long_sun(juliancentury) c = self._sun_eq_of_center(juliancentury) return l0 + c def _sun_apparent_long(self, juliancentury): O = self._sun_true_long(juliancentury) omega = 125.04 - 1934.136 * juliancentury return O - 0.00569 - 0.00478 * sin(radians(omega)) def _sun_declination(self, juliancentury): e = self._obliquity_correction(juliancentury) lambd = self._sun_apparent_long(juliancentury) sint = sin(radians(e)) * sin(radians(lambd)) return degrees(asin(sint)) def _hour_angle(self, latitude, solar_dec, solar_depression): latRad = radians(latitude) sdRad = radians(solar_dec) HA = (acos(cos(radians(90 + solar_depression)) / (cos(latRad) * cos(sdRad)) - tan(latRad) * tan(sdRad))) return HA def _hour_angle_sunrise(self, latitude, solar_dec): return self._hour_angle(latitude, solar_dec, 0.833) def _hour_angle_sunset(self, latitude, solar_dec): return -self._hour_angle(latitude, solar_dec, 0.833) def _hour_angle_dawn(self, latitude, solar_dec, solar_depression): return self._hour_angle(latitude, solar_dec, solar_depression) def _hour_angle_dusk(self, latitude, solar_dec, solar_depression): return -self._hour_angle(latitude, solar_dec, solar_depression) def _sun_true_anomoly(self, juliancentury): m = self._geom_mean_anomaly_sun(juliancentury) c = self._sun_eq_of_center(juliancentury) return m + c def _sun_rad_vector(self, juliancentury): v = self._sun_true_anomoly(juliancentury) e = self._eccentricity_earth_orbit(juliancentury) return (1.000001018 * (1 - e * e)) / (1 + e * cos(radians(v))) def _sun_rt_ascension(self, juliancentury): e = self._obliquity_correction(juliancentury) lambd = self._sun_apparent_long(juliancentury) tananum = (cos(radians(e)) * sin(radians(lambd))) tanadenom = (cos(radians(lambd))) return degrees(atan2(tananum, tanadenom)) def get_sun_azimuth_from_fix(self, fix): if self.sun_cache_time == None or abs((fix.timestamp - self.sun_cache_time).seconds) > SUN_POSITION_CACHE_DURATION: self.sun_cache_time = fix.timestamp sunrise = self.sunrise_utc(fix.timestamp, fix.position.lat, -fix.position.lon) sunset = self.sunset_utc(fix.timestamp, fix.position.lat, -fix.position.lon) if fix.timestamp > sunrise and fix.timestamp < sunset: self.sun_cache_result = self.solar_azimuth(fix.timestamp, fix.position.lat, -fix.position.lon) else: self.sun_cache_result = None return self.sun_cache_result agtl-0.8.0.3/files/advancedcaching/biggui.py000066400000000000000000000022111151564747700206300ustar00rootroot00000000000000#!/usr/bin/python # -*- coding: utf-8 -*- # Copyright (C) 2010 Daniel Fett # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # Author: Daniel Fett agtl@danielfett.de # Jabber: fett.daniel@jaber.ccc.de # Bugtracker and GIT Repository: http://github.com/webhamster/advancedcaching # import simplegui class BigGui(simplegui.SimpleGui): USES = ['gpsprovider'] #XMLFILE = "desktop.glade" XMLFILE = "freerunner.glade" TOO_MUCH_POINTS = 100 CACHES_ZOOM_LOWER_BOUND = 3 MAX_NUM_RESULTS = 500 MAX_NUM_RESULTS_SHOW = 500 agtl-0.8.0.3/files/advancedcaching/cachedownloader.py000066400000000000000000000624771151564747700225300ustar00rootroot00000000000000#!/usr/bin/python # -*- coding: utf-8 -*- # Copyright (C) 2010 Daniel Fett # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # Author: Daniel Fett agtl@danielfett.de # Jabber: fett.daniel@jaber.ccc.de # Bugtracker and GIT Repository: http://github.com/webhamster/advancedcaching # VERSION = 8 VERSION_DATE = '2011-01-09' try: import json json.dumps except (ImportError, AttributeError): import simplejson as json from geocaching import GeocacheCoordinate import datetime import geo import os import threading import logging logger = logging.getLogger('cachedownloader') global Image try: import Image except: Image = None logger.info("Not using image resize feature") import re import gobject class HTMLManipulations(object): COMMENT_REGEX = re.compile('', re.DOTALL) @staticmethod def _strip_html(text, soft = False): if not soft: return re.sub(r'<[^>]*?>', '', text) else: return re.sub(r'<[^>]*?>', ' ', text) @staticmethod def strip_html_visual(text, image_replace_callback): text = text.replace("\n", " ") text = re.sub(r"""(?i)]+alt=["']?([^'"> ]+)[^>]+>""", image_replace_callback, text) text = re.sub(r'(?i)<(br|p)[^>]*?>', "\n", text) text = re.sub(r'<[^>]*?>', '', text) text = HTMLManipulations._decode_htmlentities(text) text = re.sub(r'[\n\r]+\s*[\n\r]+', '\n', text) return text.strip() @staticmethod def _replace_br(text): return re.sub('<[bB][rR]\s*/?>|', '\n', text) @staticmethod def _decode_htmlentities(string): def substitute_entity(match): from htmlentitydefs import name2codepoint as n2cp ent = match.group(3) if match.group(1) == "#": # decoding by number if match.group(2) == '': # number is in decimal return unichr(int(ent)) elif match.group(2) == 'x': # number is in hex return unichr(int('0x' + ent, 16)) else: # they were using a name cp = n2cp.get(ent) if cp: return unichr(cp) else: return match.group() entity_re = re.compile(r'&(#?)(x?)(\w+);') return entity_re.subn(substitute_entity, string)[0] class CacheDownloader(gobject.GObject): __gsignals__ = { 'finished-overview': (gobject.SIGNAL_RUN_FIRST,\ gobject.TYPE_NONE,\ (gobject.TYPE_PYOBJECT,)), 'progress' : (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (str, int, int, )), 'download-error' : (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)), 'already-downloading-error' : (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)), 'finished-single' : (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)), 'finished-multiple' : (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)), } lock = threading.Lock() @staticmethod def _rot13(text): import string trans = string.maketrans( 'nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM', 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') return text.translate(trans) def __init__(self, downloader, path, download_images, resize = None): gobject.GObject.__init__(self) self.downloader = downloader self.path = path self.download_images = download_images self.resize = resize if not os.path.exists(path): try: os.mkdir(path) except: raise Exception("Path does not exist: %s" % path) def _init_images(self): self.downloaded_images = {} self.current_image = 0 self.images = {} def _download_image(self, url): logger.info("Checking download for %s" % url) if url in self.downloaded_images: return self.downloaded_images[url] ext = url.rsplit('.', 1)[1] if not re.match('^[a-zA-Z0-9]+$', ext): ext = 'img' filename = '' id = "%s-image%d.%s" % (self.current_cache.name, self.current_image, ext) if self.download_images: try: filename = os.path.join(self.path, id) logger.info("Downloading %s to %s" % (url, filename)) f = open(filename, 'wb') f.write(self.downloader.get_reader(url).read()) f.close() if Image != None and self.resize != None and self.resize > 0: im = Image.open(filename) im.thumbnail((self.resize, self.resize), Image.ANTIALIAS) im.save(filename) except Exception, e: logger.exception("Could not download %s: %s" % (url, e)) return None self.downloaded_images[url] = id self.current_image += 1 return id def update_coordinates(self, coordinates): i = 0 c = [] if len(coordinates) > self.MAX_DOWNLOAD_NUM: self.emit("download-error", Exception("Downloading of more than %d descriptions is not supported. (We do not want to knock out our beloved geocaching website.)" % self.MAX_DOWNLOAD_NUM)) return for cache in coordinates: self.emit("progress", cache.name, i, len(coordinates)) u = self.update_coordinate(cache) c.append(u) i += 1 self.emit("finished-multiple", c) return c def update_coordinate(self, coordinate, outfile = None): if not CacheDownloader.lock.acquire(False): self.emit('already-downloading-error', Exception("There's a download in progress. Please wait.")) return self._init_images() coordinate = coordinate.clone() self.current_cache = coordinate try: logger.info("Downloading %s..." % (coordinate.name)) response = self._get_cache_page(coordinate.name) if outfile != None: f = open(outfile, 'w') f.write(response.read()) f.close() response = open(outfile) u = self._parse_cache_page(response, coordinate) except Exception, e: CacheDownloader.lock.release() self.emit('download-error', e) return self.current_cache CacheDownloader.lock.release() self.emit('finished-single', u) return u def get_geocaches(self, location, rec_depth = 0): if not CacheDownloader.lock.acquire(False): self.emit('already-downloading-error', Exception("There's a download in progress. Please wait.")) logger.warning("Download in progress") return # don't recurse indefinitely if rec_depth > self.MAX_REC_DEPTH: raise Exception("Please select a smaller part of the map.") CacheDownloader.lock.release() return [] try: content = self._get_overview(location) except Exception, e: self.emit('download-error', e) CacheDownloader.lock.release() return [] if content == None: return [] try: points = self._parse_overview(content, location, rec_depth) except Exception, e: if rec_depth == 0: self.emit('download-error', e) CacheDownloader.lock.release() return [] else: raise e self.emit('finished-overview', points) CacheDownloader.lock.release() return points class GeocachingComCacheDownloader(CacheDownloader): MAX_REC_DEPTH = 2 MAX_DOWNLOAD_NUM = 20 user_token = None CTIDS = { 2:GeocacheCoordinate.TYPE_REGULAR, 3:GeocacheCoordinate.TYPE_MULTI, 4:GeocacheCoordinate.TYPE_VIRTUAL, 6:GeocacheCoordinate.TYPE_EVENT, 8:GeocacheCoordinate.TYPE_MYSTERY, 11:GeocacheCoordinate.TYPE_WEBCAM, 137:GeocacheCoordinate.TYPE_EARTH } @staticmethod def login_callback(username, password): url = 'http://www.geocaching.com/Default.aspx' values = {'ctl00$MiniProfile$loginUsername': username, 'ctl00$MiniProfile$loginPassword': password, 'ctl00$MiniProfile$uxRememberMe': 'on', 'ctl00$MiniProfile$LoginBtn': 'Go', '__EVENTTARGET': '', '__EVENTARGUMENT': '' } return url, values def _get_overview(self, location): if self.user_token == None: self._get_user_token() c1, c2 = location url = 'http://www.geocaching.com/map/default.aspx/MapAction?lat=9&lng=6' '''values = {'eo_cb_id':'ctl00_ContentBody_cbAjax', 'eo_cb_param':'{"c": 1, "m": "", "d": "%f|%f|%f|%f"}' % (max(c1.lat, c2.lat), min(c1.lat, c2.lat), max(c1.lon, c2.lon), min(c1.lon, c2.lon)), 'eo_version':'5.0.51.2' }''' data = ('application/json', '{"dto":{"data":{"c":1,"m":"","d":"%f|%f|%f|%f"},"ut":"%s"}}' % (max(c1.lat, c2.lat), min(c1.lat, c2.lat), max(c1.lon, c2.lon), min(c1.lon, c2.lon), self.user_token)) try: response = self.downloader.get_reader(url, data = data) the_page = response.read() #extractor = re.compile('', re.DOTALL) #match = extractor.search(the_page) #if match == None: # logger.debug(the_page) # raise Exception('Could not load map of geocaches') except Exception, e: CacheDownloader.lock.release() self.emit('download-error', e) return None return the_page def _get_user_token(self): page = self.downloader.get_reader('http://www.geocaching.com/map/default.aspx?lat=6&lng=9') for line in page: if line.startswith('var uvtoken'): self.user_token = re.compile("userToken[ =]+'([^']+)'").search(line).group(1) page.close() return raise Exception("Website contents unexpected. Please check connection.") def _parse_overview(self, content, location, rec_depth = 0): c1, c2 = location #text = content.replace('\\"', '"') #text = text.replace('\t', ' ') full = json.loads(content) a = json.loads(full['d']) points = [] if not 'cc' in a['cs']: if 'count' in a['cs'] and 'count' != 0: # let's try to download one half of the geocaches first mlat = (c1.lat + c2.lat)/2 nc1 = geo.Coordinate(min(c1.lat, c2.lat), min(c1.lon, c2.lon)) mc1 = geo.Coordinate(mlat, max(c1.lon, c2.lon)) mc2 = geo.Coordinate(mlat, min(c1.lon, c2.lon)) nc2 = geo.Coordinate(max(c1.lat, c2.lat), max(c1.lon, c2.lon)) #print "recursing..." CacheDownloader.lock.release() points += self.get_geocaches((nc1, mc1), rec_depth + 1) points += self.get_geocaches((mc2, nc2), rec_depth + 1) CacheDownloader.lock.acquire(False) return points for b in a['cs']['cc']: c = GeocacheCoordinate(b['lat'], b['lon'], b['gc']) c.title = b['nn'] if b['ctid'] in self.CTIDS: c.type = self.CTIDS[b['ctid']] else: c.type = GeocacheCoordinate.TYPE_UNKNOWN c.found = b['f'] if not b['ia']: c.status = GeocacheCoordinate.STATUS_DISABLED points.append(c) return points def _get_cache_page(self, cacheid): return self.downloader.get_reader('http://www.geocaching.com/seek/cache_details.aspx?wp=%s' % cacheid) def _parse_cache_page(self, cache_page, coordinate): section = '' prev_section = '' shortdesc = desc = hints = waypoints = images = logs = owner = head = '' logger.debug("Start parsing...") for line in cache_page: line = line.strip() if section == '' and line.startswith('
'): section = 'head' elif section == 'head' and line.startswith(''): section = 'shortdesc' elif (section == 'head' or section == 'shortdesc') and line.startswith(''): section = 'desc' elif (section == 'desc' or section == 'shortdesc') and line.startswith('
'): section = 'after-desc' elif section == 'after-desc' and line.startswith('
'): section = 'hints' elif section == 'hints' and line.startswith('
'): section = 'after-hints' elif (section == 'after-hints' or section == 'after-desc') and line.startswith('
'): section = 'pre-waypoints' elif (section == 'after-hints' or section == 'pre-waypoints') and line.startswith(' '): section = 'after-waypoints' elif (section == 'pre-waypoints' or section == 'after-waypoints') and line.startswith(''): section = 'images' elif section == 'images' and line.startswith('

'): section = 'after-images' elif section == 'after-images' and line.startswith(''): section = 'logs' if section != prev_section: logger.debug("Now in Section '%s', with line %s" % (section, line[:20:])) prev_section = section if section == 'head': head = "%s%s\n" % (head, line) elif section == 'shortdesc': shortdesc = "%s%s\n" % (shortdesc, line) elif section == 'desc': desc = "%s%s\n" % (desc, line) elif section == 'hints': hints = "%s%s " % (hints, line) elif section == 'waypoints': waypoints = "%s%s " % (waypoints, line) elif section == 'images': images = "%s%s " % (images, line) elif section == 'logs': logs = "%s%s" % (logs, line) if line.endswith('
'): break logger.debug('finished parsing') coordinate.size, coordinate.difficulty, coordinate.terrain, coordinate.owner, coordinate.lat, coordinate.lon = self.__parse_head(head) coordinate.shortdesc = self.__treat_shortdesc(shortdesc) coordinate.desc = self.__treat_desc(desc) coordinate.hints = self.__treat_hints(hints) coordinate.set_waypoints(self.__treat_waypoints(waypoints)) coordinate.set_logs(self.__treat_logs(logs)) self.__treat_images(images) coordinate.set_images(self.images) return coordinate def __parse_head(self, head): sizestring = re.compile('Size:').search(head).group(1)
        if sizestring == 'micro':
            size = 1
        elif sizestring == 'small':
            size = 2
        elif sizestring == 'regular':
            size = 3
        elif sizestring == 'large' or sizestring == 'big':
            size = 4
        elif sizestring == 'not_chosen' or sizestring == 'other':
            size = 5
        else:
            logger.warning(.*?([0-9.]+) out').search(head).group(1))*10
        terr = float(re.compile('(?s)Terrain:</strong>.*?<img src=]+>([^<]+)", re.MULTILINE).search(head).group(1)) coords = re.compile('lat=([0-9.-]+)&lon=([0-9.-]+)&').search(head) lat = float(coords.group(1)) lon = float(coords.group(2)) return size, diff, terr, owner, lat, lon def __treat_hints(self, hints): hints = HTMLManipulations._strip_html(HTMLManipulations._replace_br(hints)).strip() hints = self._rot13(hints) hints = re.sub(r'\[([^\]]+)\]', lambda match: self._rot13(match.group(0)), hints) return hints def __treat_desc(self, desc): desc = self.__treat_html(desc.rsplit('\n', 5)[0]) return desc.strip() def __treat_shortdesc(self, desc): if desc.strip() == '': return '' desc = self.__treat_html(desc.rsplit('\n', 3)[0]) return desc def __treat_html(self, html): html = HTMLManipulations.COMMENT_REGEX.sub('', html) html = self.__replace_images(html) return html @staticmethod def __from_dm(direction, decimal, minutes): if direction == None or decimal == None or minutes == None: return -1 if direction in "SsWw": sign = -1 else: sign = 1 return (float(decimal) + (float(minutes) / 60.0)) * sign def __treat_waypoints(self, data): data.replace('', '') data.replace('', '') lines = data.split(']*>', line) if len(tds) <= 1: continue elif len(tds) > 4: id = ''.join(HTMLManipulations._strip_html(x).strip() for x in tds[4:6]) name = HTMLManipulations._decode_htmlentities(HTMLManipulations._strip_html(tds[6])).strip() m = re.compile(r'''(\?\?\?|(?PN|S) (?P\d+)° (?P[0-9\.]+) (?PE|W) (?P\d+)° (?P[0-9\.]+))''').search(tds[7]) lat = self.__from_dm(m.group('lat_sign'), m.group('lat_d'), m.group('lat_m')) lon = self.__from_dm(m.group('lon_sign'), m.group('lon_d'), m.group('lon_m')) else: comment = HTMLManipulations._decode_htmlentities(HTMLManipulations._strip_html(HTMLManipulations._replace_br(tds[3]))).strip() waypoints.append({'lat':lat, 'lon':lon, 'id':id, 'name':name, 'comment':comment}) return waypoints def __treat_images(self, data): finder = re.finditer('(.+?)

', data) for m in finder: if m.group(1) == None: continue id = self._download_image(url = m.group(1)) if id != None: self.__add_image(id, HTMLManipulations._decode_htmlentities(HTMLManipulations._strip_html(m.group(2)))) def __treat_logs(self, logs): lines = logs.split('') # lines 0 and 1 are useless! output = [] for l in lines: #lines = [re.sub("\w+", ' ', HTMLManipulations._decode_htmlentities(HTMLManipulations._strip_html(x, True)), '').sub('[ view this log ]') for x in lines[2:]] m = re.match(r"""]+>]*/>""" + r""" (?P[^ ]+) (?P\d+)(, (?P\d+))? by ]+>(?P[^<]+) \(\d+ found\)

(?P.+)""" + r"""

""", l, re.DOTALL) if m == None: #print "Could not parse Log-Line:\nBEGIN\n%s\nEND\n\n This can be normal." % l logger.debug("Ignoring following log line:-----\n%s\n------" % l) pass else: type = m.group('icon') if m.group('icon') != None else m.group('icon2') month = self.__month_to_number(m.group('month')) day = m.group('day') year = m.group('year') if year == '' or year == None: year = datetime.datetime.now().year finder = m.group('finder') text = HTMLManipulations._decode_htmlentities(HTMLManipulations._strip_html(HTMLManipulations._replace_br(m.group('text')), True)) output.append(dict(type=type, month=month, day=day, year=year, finder=finder, text=text)) return output def __replace_images(self, data): return re.sub(r'''(?is)(]+src=\n?["']?)([^ >"']+)([^>]+?/?>)''', self.__replace_image_callback, data) def __replace_image_callback(self, m): url = m.group(2) if not url.startswith('http://'): return m.group(0) id = self._download_image(url) if id == None: return m.group(0) else: self.__add_image(id) return "[[img:%s]]" % id def __add_image(self, id, description = ''): if ((id in self.images and len(description) > len(self.images[id])) or id not in self.images): self.images[id] = description @staticmethod def __month_to_number(text): months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'] if text in months: return months.index(text) + 1 logger.warning("Unknown month: " + text) return 0 if __name__ == '__main__': import sys import downloader outfile = None if len(sys.argv) == 2: # cachedownloder.py filename print "Reading from file %s" % sys.argv[1] inp = open(sys.argv[1]) m = GeocacheCoordinate(0, 0, 'GC1N8G6') a = GeocachingComCacheDownloader(downloader.FileDownloader('dummy', 'dummy', '/tmp/cookies'), '/tmp/', True) else: # cachedownloader.py username password name, password = sys.argv[1:3] a = GeocachingComCacheDownloader(downloader.FileDownloader(name, password, '/tmp/cookies', GeocachingComCacheDownloader.login_callback(name, password)), '/tmp/', True) print "Using Username %s" % name def pcache(c): print "--------------------\nName: '%s'\nTitle: '%s'\nType: %s" % (c.name, c.title, c.type) coords = a.get_geocaches((geo.Coordinate(49.3513,6.583), geo.Coordinate(49.352,6.584))) print "# Found %d coordinates" % len(coords) for x in coords: pcache(x) if x.name == 'GC1N8G6': if x.type != GeocacheCoordinate.TYPE_REGULAR or x.title != 'Druidenpfad': print "# Wrong type or title" sys.exit() m = x break else: print "# Didn't find my own geocache :-(" if len(sys.argv) == 4: print "Writing to File %s" % sys.argv[3] outfile = sys.argv[3] res = a.update_coordinate(m, outfile) print res c = res if c.owner != 'webhamster': print "Owner doesn't match ('%s', expected webhamster)" % c.owner if c.title != 'Druidenpfad': print "Title doesn't match ('%s', expected 'Druidenpfad')" % c.title if c.get_terrain() != '3.0': print "Terrain doesn't match (%s, expected 3.0) " % c.get_terrain() if c.get_difficulty() != '2.0': print "Diff. doesn't match (%s, expected 2.0)" % c.get_difficulty() if len(c.desc) != 1980: print "Length of text doesn't match (%d, expected %d" % (len(c.desc), 1980) if len(c.shortdesc) != 238: print "Length of short description doesn't match (%d, expected %d" % (len(c.shortdesc), 238) if len(c.get_waypoints()) != 4: print "Expected 4 waypoints, got %d" % len(c.get_waypoints()) if len(c.hints) != 83: print "Expected 83 characters of hints, got %d" % len(c.hints) if len(c.get_logs()) < 2: print "Expected at least 2 logs, got %d" % len(c.get_logs()) print c.get_logs() print "Owner:%s\nTitle:%s\nTerrain:%s\nDifficulty:%s\nDescription:%s\nShortdesc:%s\nHints:%s" % (c.owner, c.title, c.get_terrain(), c.get_difficulty(), c.desc, c.shortdesc, c.hints) print c.get_waypoints() agtl-0.8.0.3/files/advancedcaching/cli.py000066400000000000000000000542201151564747700201400ustar00rootroot00000000000000#!/usr/bin/python # -*- coding: utf-8 -*- # Copyright (C) 2010 Daniel Fett # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # Author: Daniel Fett agtl@danielfett.de # Jabber: fett.daniel@jaber.ccc.de # Bugtracker and GIT Repository: http://github.com/webhamster/advancedcaching # import geocaching import sys import geo import math import os usage = r'''Here's how to use this app: If you want to use the gui: %(name)s --simple Simple User Interface, for mobile devices such as the Openmoko Freerunner %(name)s --desktop Full User Interface, for desktop usage (not implemented yet) If you don't like your mouse: %(name)s set [options] Change the configuration. %(name)s import [importactions] Fetch geocaches from geocaching.com and write to the internal database. %(name)s import [importactions] do [actions] Fetch geocaches from geocaching.com, put them into the internal database and do whatever actions are listed. %(name)s filter [filter-options] do [actions] Query the internal database for geocaches and do the desired actions. %(name)s import [importactions] filter [filter-options] do [actions] Import geocaches, put them into the internal database, filter the imported geocaches and run the actions. %(name)s sql "SELECT * FROM geocaches WHERE ... ORDER BY ... LIMIT ..." do [actions] Select geocaches from local database and run the actions afterwards. Additional use of the filter is also supported. To get more information, run "%(name)s sql". options: --user(name) username --pass(word) password Your geocaching.com login data. importactions: --in coord1 coord2 Fetches the index of geocaches between the given coordinates. These are interpreted as the corners of a rectangle. All caches within the rectangle are retrieved. No details are retrieved. --around coord radius-in-km Fetches the index of geocaches at the given coordinate and radius kilometers around it. No details are retrieved. --at-route coord1 coord2 radius-in-km Find caches along the route from coord1 to coord2. Uses OpenRouteService and is not available for routes outside of europe. filter-options: --in coord1 coord2 --around coord1 radius-in-km See import actions. -f|--found -F|--not-found Filter out geocaches which have (not) been found by the user. -w|--was-downloaded caches which have full detail information available -s|--size (min|max) 1..4|micro|small|regular|huge|other Specify a minimum or maximum size. If min/max is not given, show only geocaches with the given size. -d|--difficulty (min|max) 1.0..5.0 -t|--terrain (min|max) 1.0..5.0 Filter out geocaches by difficulty or terrain. -T|--type type,type,... type: virtual|regular|unknown|multi|event Only show geocaches of the given type(s) -o|--owner owner-search-string -n|--name name-search-string -i|--id id-search-string Search owner, name (title) or id of the geocaches. --new Caches which were downloaded in current session. Useful to get alerted when new caches arrive. actions: --print Default action, prints tab-separated list of geocaches --fetch-details Downloads Descriptions etc. for selected geocaches --export-html folder Dumps HTML pages to given folder --command command Runs command if more than one geocache has survived the filtering. The placeholder %%s is replaced by a shell-escaped list of geocaches. Not implemented yet: --export-gpx folder Dumps geocaches into separate GPX files --export-single-gpx file Dumps selected geocaches into a single GPX file --draw-map zoom file Draws one big JPEG file with the positions of the selected geocaches --draw-maps zoom folder [tiles] Draws a small JPEG image for every geocache. Preferred format for coordinates: 'N49 44.111 E6 29.123' or 'N49.123456 E6.043212' Instead of a coordinate, you may also query geonames.com for a place name. Just start the string with 'q:': q:London 'q:Brisbane, Australia' ''' class ParseError(Exception): def __init__(self, errormsg, token = None): self.msg = errormsg self.token = token def __str__(self): return repr(self.msg) class RunError(Exception): def __init__(self, errormsg): self.msg = errormsg def __str__(self): return repr(self.msg) class Cli(): USES = ['geonames'] # operators EQ = 0 MIN = 1 MAX = 2 def __init__(self, core, dataroot): self.nt = 1 self.core = core self.caches = None self.new_caches = [] self.pointprovider = core.pointprovider pass def write_settings(self, settings): self.settings = settings def show(self): print "$ The command line interface is not fully implemented yet, feel" print "$ free to contribute at git://github.com/webhamster/advancedcaching.git" try: self.parse_input() except ParseError, e: if e.token == None: print "# Parse Error at token '%s': " % sys.argv[self.nt - 1] else: print "# Parse Error after Token '%s':" % sys.argv[e.token] print "# %s" % e.msg except RunError, e: print "# Execution Error at token '%s': " % sys.argv[self.nt - 1] print "# %s" % e.msg def check_caches_retrieved(self): if self.caches == None: self.caches = self.pointprovider.get_all() print "* retrieved all caches (%d) from database" % len(self.caches) def parse_input (self): while self.has_next(): if sys.argv[self.nt] == 'set': self.parse_set() if sys.argv[self.nt] == 'import': self.parse_import() elif sys.argv[self.nt] == 'sql': self.parse_sql() elif sys.argv[self.nt] == 'filter': self.parse_filter() elif sys.argv[self.nt] == 'do': self.parse_actions() else: raise ParseError("Expected 'import', 'sql', 'filter' or 'do'", self.nt - 1) def parse_set(self): self.nt += 1 if not self.has_next(): raise ParseError("Expected some options.") while self.has_next(): token = sys.argv[self.nt] self.nt += 1 if token == '--pass' or token == '--password': password = self.parse_string() self.set_password(password) elif token == '--user' or token == '--username': username = self.parse_string() self.set_username(username) else: raise ParseError("I don't understand '%s'" % token) print "* Finished setting options. Exiting." sys.exit() def parse_import(self): self.nt += 1 if not self.has_next(): raise ParseError("Expected import actions.") token = sys.argv[self.nt] self.nt += 1 if token == '--in': coord1 = self.parse_coord() coord2 = self.parse_coord() self.import_points(coord1, coord2) elif token == '--around': coord1 = self.parse_coord() radius = self.parse_int() self.import_points(coord1, radius) elif token == '--at-route': coord1 = self.parse_coord() coord2 = self.parse_coord() radius = self.parse_int() self.import_points_route(coord1, coord2, radius) else: # undo what we did. self.nt -= 1 return def parse_sql(self): self.nt += 1 if not self.has_next(): print "Table structure for geocaches:" info = self.pointprovider.get_table_info() for row in info: print "\t".join([str(x) for x in row]) print "Example SQL-Query:" print "SELECT * FROM geocaches WHERE type = 'multi' AND name LIKE 'GC1X%' AND found = 0 ORDER BY title DESC LIMIT 5" raise ParseError("Expected sql string.") text = self.parse_string() self.caches = self.pointprovider.get_by_query(text) def parse_filter(self): self.check_caches_retrieved() self.nt += 1 if not self.has_next(): raise ParseError("Expected filter options.") while self.has_next(): token = sys.argv[self.nt] self.nt += 1 if token == '--in': coord1 = self.parse_coord() coord2 = self.parse_coord() self.add_filter_in(coord1, coord2) elif token == '--around': coord1 = self.parse_coord() radius = self.parse_int() self.add_filter_in(coord1, radius) elif token == '--found' or token == '-f': self.add_filter_found(True) elif token == '--not-found' or token == '-F': self.add_filter_found(False) elif token == '-w' or token == '--was-downloaded': self.add_filter_has_details(True) elif token == '-s' or token == '--size': op = self.parse_operator() size = self.parse_size() self.add_filter_size(op, size) elif token == '-d' or token == '--difficulty': op = self.parse_operator() diff = self.parse_decimal() self.add_filter_difficulty(op, diff) elif token == '-t' or token == '--terrain': op = self.parse_operator() terr = self.parse_decimal() self.add_filter_terrain(op, terr) elif token == '-T' or token == '--type': types = self.parse_types() self.add_filter_types(types) elif token == '-o' or token == '--owner': owner = self.parse_string() self.add_filter_owner(owner) elif token == '-n' or token == '--name': name = self.parse_string() self.add_filter_name (name) elif token == '-i' or token == '--id': id = self.parse_string() self.add_filter_id (id) elif token == '--new': self.caches = self.new_caches else: # undo what we did. self.nt -= 1 return def parse_actions(self): self.check_caches_retrieved() self.nt += 1 if not self.has_next(): raise ParseError("Expected actions.") while self.has_next(): token = sys.argv[self.nt] self.nt += 1 if token == '--print': self.action_print() elif token == '--fetch-details': self.action_fetch_details() elif token == '--export-html': folder = self.parse_string() self.action_export('html', folder) elif token == '--export-gpx': folder = self.parse_string() self.action_export('gpx', folder) elif token == '--export-single-gpx': raise ParseError("Exporting to a single gpx file is currently not supported, sorry.") filename = self.parse_string() self.action_export_single_gpx(filename) elif token == '--draw-map': zoom = self.parse_integer() filename = self.parse_string() self.action_draw_map(zoom, filename) elif token == '--draw-maps': zoom = self.parse_integer() folder = self.parse_string() self.action_draw_maps(zoom, folder) elif token == '--command': cmd = self.parse_string() self.action_command(cmd) else: raise ParseError("Unknown export action: %s" % token) def has_next(self): # if we have 5 tokens # then 1..4 are valid tokens (0 is command) # "5" is len(tokens) # so we have a next token, if nt < len(tokens)-1 return (self.nt < len(sys.argv)) def parse_coord(self): if not self.has_next(): raise ParseError("Expected Coordinate but there was none.", self.nt-1) text = sys.argv[self.nt] self.nt += 1 if text.startswith('q:'): query = text[2:] try: c = self.core.get_coord_by_name(query) except Exception, e: raise ParseError(e) else: try: c = geo.try_parse_coordinate(text) except Exception, e: raise ParseError(e) return c def parse_string(self): if not self.has_next(): raise ParseError("Expected some Input, found nothing", self.nt-1) text = sys.argv[self.nt] self.nt += 1 return text.strip() def parse_int(self): if not self.has_next(): raise ParseError("Expected a number, found nothing.", self.nt-1) text = sys.argv[self.nt] self.nt += 1 return int(text) def parse_size(self): if not self.has_next(): raise ParseError("Expected a size (1..4/micro/small/regular/huge/other), found nothing.", self.nt-1) text = sys.argv[self.nt].lower() self.nt += 1 if text in ['1', '2', '3', '4', '5']: return int(text) elif text == 'micro': return 1 elif text == 'small': return 2 elif text in ['normal', 'regular']: return 3 elif text in ['huge', 'big']: return 4 elif text == 'other': return 5 else: raise ParseError('Unknown size: %s' % text) def parse_types(self): if not self.has_next(): raise ParseError("Expected geocache type, found not even an electronic sausage.", self.nt-1) text = sys.argv[self.nt].lower() self.nt += 1 types = text.split(',') output = [] for i in types: if i in geocaching.GeocacheCoordinate.TYPES: output.append(i) else: raise ParseError("Unknown Type: %s (expected one of: %s)" % (i, ', '.join(geocaching.GeocacheCoordinate.TYPES))) return output def parse_operator(self): text = sys.argv[self.nt] if text == 'min': self.nt += 1 return self.MIN elif text == 'max': self.nt += 1 return self.MAX else: return self.EQ def parse_decimal(self): if not self.has_next(): raise ParseError("Expected a number", self.nt - 1) text = sys.argv[self.nt] try: return 10 * float(text) except: raise ParseError("Could not parse '%s' as a valid number." % text) def set_username(self, string): self.settings['options_username'] = string self.core.on_config_changed(self.settings) def set_password(self, string): self.settings['options_password'] = string self.core.on_config_changed(self.settings) def import_points(self, c1, c2): if isinstance(c2, geo.Coordinate): print "* Downloading Caches between %s and %s" % (c1, c2) self.caches, self.new_caches = self.core.on_download((c1, c2)) else: # try to calculate some points northwest and southeast to the # given point with approximately correct distances new_c1 = c1.transform(-45, c2 * 1000 * math.sqrt(2)) new_c2 = c1.transform(-45 + 180, c2 * 1000 * math.sqrt(2)) print "* Downloading Caches in %d km distance to %s" % (c2, c1) print "* Approximation: Caches between %s and %s" % (new_c1, new_c2) self.caches, self.new_caches = self.core.on_download((new_c1, new_c2)) def import_points_route(self, c1, c2, r): print "* Querying OpenRouteService for route from startpoint to endpoint" points = self.core.get_route(c1, c2, r) print "* Found route, now retrieving partial cache overviews" for p in points: self.import_points(p[0], p[1]) #pass print "* Done." def add_filter_in(self, coord1, coord2): if isinstance(coord2, geo.Coordinate): self.caches = filter(lambda x: self.filter_in(coord1, coord2, x), self.caches) else: self.caches = filter(lambda x: self.filter_in_radius(coord1, coord2, x), self.caches) print "* filter in radius/coordinates: %d left" % len(self.caches) def filter_in(self, c1, c2, check): return (check.lat > min(c1.lat, c2.lat) and check.lat < max(c1.lat, c2.lat) and check.lon > min(c1.lon, c2.lon) and check.lon < max(c1.lon, c2.lon)) def filter_in_radius(self, coord1, radius, check): return check.distance_to(coord1) <= radius * 1000 def add_filter_found(self, found): self.caches = filter(lambda x: x.found == found, self.caches) print "* filter width found: %d left" % len(self.caches) def add_filter_has_details(self, has_details): self.caches = filter(lambda x: x.was_downloaded() == has_details, self.caches) print "* filter with 'has details': %d left" % len(self.caches) def add_filter_size(self, op, size): if op == self.EQ: self.caches = filter(lambda x: x.size == size, self.caches) elif op == self.MIN: self.caches = filter(lambda x: x.size >= size, self.caches) elif op == self.MAX: self.caches = filter(lambda x: x.size <= size, self.caches) else: raise RunError("What Happen? Somebody set us up the geocache.") print "* filter with size: %d left" % len(self.caches) def add_filter_difficulty(self, op, diff): if op == self.EQ: self.caches = filter(lambda x: x.diff == diff, self.caches) elif op == self.MIN: self.caches = filter(lambda x: x.diff >= diff, self.caches) elif op == self.MAX: self.caches = filter(lambda x: x.diff <= diff, self.caches) else: raise RunError("What Happen? Somebody set us up the geocache.") print "* filter with difficulty: %d left" % len(self.caches) def add_filter_terrain(self, op, terr): if op == self.EQ: self.caches = filter(lambda x: x.terr == terr, self.caches) elif op == self.MIN: self.caches = filter(lambda x: x.terr >= terr, self.caches) elif op == self.MAX: self.caches = filter(lambda x: x.terr <= terr, self.caches) else: raise RunError("What Happen? Somebody set us up the geocache.") print "* filter with terrain: %d left" % len(self.caches) def add_filter_types(self, types): self.caches = filter(lambda x: x.type in types, self.caches) print "* filter with types: %d left" % len(self.caches) def add_filter_owner(self, owner): self.caches = filter(lambda x: owner.lower() in x.owner.lower(), self.caches) print "* filter with owner: %d left" % len(self.caches) def add_filter_name (self, name): self.caches = filter(lambda x: name.lower() in x.title.lower(), self.caches) print "* filter with name: %d left" % len(self.caches) def add_filter_id (self, idstring): self.caches = filter(lambda x: idstring.lower() in x.name.lower(), self.caches) print "* filter with id: %d left" % len(self.caches) def action_print (self): print "Found %d Caches:" % len(self.caches) for c in self.caches: print "%s\t%s (%s)" % (c.name, c.title, c.type) def action_fetch_details(self): i = 1 for c in self.caches: print "* (%d of %d)\tDownloading '%s'" % (i, len(self.caches), c.title) self.core.on_download_cache(c) i += 1 def action_export(self, format, folder): i = 1 for c in self.caches: print "* (%d of %d)\tExporting to %s: '%s'" % (i, len(self.caches), format, c.title) self.core.on_export_cache(c, format, folder) i += 1 def action_command(self, commandline): import unicodedata if len(self.caches) == 0: print "* Not running command (no geocaches left)" return list = " -- ".join(["%s (%s)" % (a.title, a.type) for a in self.caches]) if not isinstance(list, str): list = unicodedata.normalize('NFKD', list).encode('ascii','ignore') os.system(commandline % ('"%s"' % list.encode('string-escape'))) def set_download_progress(self, some, thing): pass def hide_progress(self): pass def show_error(self, message): raise RunError(message) agtl-0.8.0.3/files/advancedcaching/colorer.py000066400000000000000000000073201151564747700210350ustar00rootroot00000000000000#!/usr/bin/env python # source: http://stackoverflow.com/questions/384076/how-can-i-make-the-python-logging-output-to-be-colored # encoding: utf-8 import logging # now we patch Python code to add color support to logging.StreamHandler def add_coloring_to_emit_windows(fn): # add methods we need to the class def _out_handle(self): import ctypes return ctypes.windll.kernel32.GetStdHandle(self.STD_OUTPUT_HANDLE) out_handle = property(_out_handle) def _set_color(self, code): import ctypes # Constants from the Windows API self.STD_OUTPUT_HANDLE = -11 hdl = ctypes.windll.kernel32.GetStdHandle(self.STD_OUTPUT_HANDLE) ctypes.windll.kernel32.SetConsoleTextAttribute(hdl, code) setattr(logging.StreamHandler, '_set_color', _set_color) def new(*args): FOREGROUND_BLUE = 0x0001 # text color contains blue. FOREGROUND_GREEN = 0x0002 # text color contains green. FOREGROUND_RED = 0x0004 # text color contains red. FOREGROUND_INTENSITY = 0x0008 # text color is intensified. FOREGROUND_WHITE = FOREGROUND_BLUE|FOREGROUND_GREEN |FOREGROUND_RED # winbase.h STD_INPUT_HANDLE = -10 STD_OUTPUT_HANDLE = -11 STD_ERROR_HANDLE = -12 # wincon.h FOREGROUND_BLACK = 0x0000 FOREGROUND_BLUE = 0x0001 FOREGROUND_GREEN = 0x0002 FOREGROUND_CYAN = 0x0003 FOREGROUND_RED = 0x0004 FOREGROUND_MAGENTA = 0x0005 FOREGROUND_YELLOW = 0x0006 FOREGROUND_GREY = 0x0007 FOREGROUND_INTENSITY = 0x0008 # foreground color is intensified. BACKGROUND_BLACK = 0x0000 BACKGROUND_BLUE = 0x0010 BACKGROUND_GREEN = 0x0020 BACKGROUND_CYAN = 0x0030 BACKGROUND_RED = 0x0040 BACKGROUND_MAGENTA = 0x0050 BACKGROUND_YELLOW = 0x0060 BACKGROUND_GREY = 0x0070 BACKGROUND_INTENSITY = 0x0080 # background color is intensified. levelno = args[1].levelno if(levelno>=50): color = BACKGROUND_YELLOW | FOREGROUND_RED | FOREGROUND_INTENSITY | BACKGROUND_INTENSITY elif(levelno>=40): color = FOREGROUND_RED | FOREGROUND_INTENSITY elif(levelno>=30): color = FOREGROUND_YELLOW | FOREGROUND_INTENSITY elif(levelno>=20): color = FOREGROUND_GREEN elif(levelno>=10): color = FOREGROUND_BLACK else: color = FOREGROUND_WHITE args[0]._set_color(color) ret = fn(*args) args[0]._set_color( FOREGROUND_WHITE ) #print "after" return ret return new def add_coloring_to_emit_ansi(fn): # add methods we need to the class def new(*args): levelno = args[1].levelno if(levelno>=50): color = '\x1b[31m' # red elif(levelno>=40): color = '\x1b[31m' # red elif(levelno>=30): color = '\x1b[33m' # yellow elif(levelno>=20): color = '\x1b[32m' # green elif(levelno>=10): color = '\x1b[39m' # pink else: color = '\x1b[0m' # normal args[1].msg = '%s%s%s' % (color, args[1].msg, '\x1b[0m') # normal #print "after" return fn(*args) return new import platform if platform.system()=='Windows': # Windows does not support ANSI escapes and we are using API calls to set the console color logging.StreamHandler.emit = add_coloring_to_emit_windows(logging.StreamHandler.emit) else: # all non-Windows platforms are supporting ANSI escapes so we use them logging.StreamHandler.emit = add_coloring_to_emit_ansi(logging.StreamHandler.emit)agtl-0.8.0.3/files/advancedcaching/coordfinder.py000066400000000000000000000235451151564747700216750ustar00rootroot00000000000000#!/usr/bin/python # -*- coding: utf-8 -*- # Copyright (C) 2010 Daniel Fett # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # Author: Daniel Fett agtl@danielfett.de # Jabber: fett.daniel@jaber.ccc.de # Bugtracker and GIT Repository: http://github.com/webhamster/advancedcaching # from __future__ import division TEST = (''' ''', {'A': 2, 'D': 4, 'G': 3,'T': 1, 'R': 2, 'S': 1, 'H': 4, 'B': 2, 'C': 9, 'E': 0, 'F': 1}) HTML = '''

Final:

N 49° (B-C+A+0,5*D).(F+D)(F-G)(C-2*A)

E 6° (2*A+C).(G-E)(B-C+0,5*D)(F-D)

Es müssen keine Zäune oder Mauern überwunden werden.

N 49° (B-C+A+0,5*D).(F+D)(F-G)(D-2*B)



''' import geo import re import logging logger = logging.getLogger('coordfinder') class CalcCoordinateManager(object): def __init__(self, vars): self.__vars = vars self.__known_signatures = [] self.__filtered_signatures = [] self.requires = set() self.coords = [] logger.debug("New coordfinder started") def add_text(self, text, source): logger.debug("Adding Text with length %d from source %s" % (len(text), source)) self.__add_coords(CalcCoordinate.find(text, source)) def __add_coords(self, coords, apply_filter = True): for x in coords: logger.debug("Adding: %s, apply_filter = %s" % (x, apply_filter)) if x.signature in self.__known_signatures: continue if apply_filter and x.signature in self.__filtered_signatures: continue self.__known_signatures.append(x.signature) self.requires |= x.requires self.coords.append(x) logger.debug("Now having %d coords, %d requires" % (len(self.coords), len(self.requires))) def __remove_coord(self, signature): self.__filtered_signatures.append(signature) self.__known_signatures = [] self.requires = set() logger.debug("Removing: %s" % signature) new_coords = [] for x in self.coords: if x.signature != signature: self.requires |= x.requires new_coords.append(x) self.__known_signatures.append(x.signature) self.coords = new_coords logger.debug("Now having %d coords, %d requires" % (len(self.coords), len(self.requires))) def add_replacement(self, signature, replacement_text, source): self.__remove_coord(signature) self.__add_coords(CalcCoordinate.find(replacement_text, source), False) def set_var(self, char, value): if value != '': self.__vars[char] = value else: del self.__vars[char] self.update() def update(self): logger.debug("updating...") for c in self.coords: c.set_vars(self.__vars) if c.has_requires(): c.try_get_solution() def get_solutions(self): return [(c.result, c.source) for c in self.coords if c.has_requires() and len(c.requires) > 0] def get_plain_coordinates(self): return [(c.result, c.source) for c in self.coords if len(c.requires) == 0] def get_vars(self): return self.__vars class CalcCoordinate(): WARNING_NEGATIVE = "Negative intermediate result (%d)." WARNING_VERY_HIGH = "Very high intermediate result (%d)." WARNING_FLOAT = "Intermediate result with decimal point ('%s')." WARNING_WRONG_LENGTH = "%d digits where %s digits were expected (%s)." WARNING_CANNOT_PARSE = "Cannot parse result: %s." WARNING_SYNTAX = "Could not parse formula." HIGH_RESULT_THRESHOLD = 1000 EXPECTED_LENGTHS = [(1,2), (1,2), (3,), (1,2,3), (1,2), (3,)] def __init__(self, ns, lat_deg, lat_min, lat_min_2, ew, lon_deg, lon_min, lon_min_2, source): self.ns = ns self.ew = ew self.lat_deg = self.__prepare(lat_deg) self.lat_min = self.__prepare(lat_min) self.lat_min_2 = self.__prepare(lat_min_2) self.lon_deg = self.__prepare(lon_deg) self.lon_min = self.__prepare(lon_min) self.lon_min_2 = self.__prepare(lon_min_2) self.orig = "%s%s %s.%s %s%s %s.%s" % (self.ns, self.lat_deg, self.lat_min, self.lat_min_2, self.ew, self.lon_deg, self.lon_min, self.lon_min_2) self.requires = set(x for i in [self.lat_deg, self.lat_min, self.lat_min_2, self.lon_deg, self.lon_min, self.lon_min_2] for x in re.sub('[^A-Za-z]', '', i)) self.warnings = [] self.vars = {} self.signature = "|".join([ns, self.lat_deg, self.lat_min, self.lat_min_2, ew, self.lon_deg, self.lon_min, self.lon_min_2]) self.source = source def __prepare(self, text): return (re.sub('[^A-Za-z()+*/0-9-.,]', '', text)).replace(',', '.') def set_vars(self, var): self.warnings = [] self.vars = var def has_requires(self): for i in self.requires: if not i in self.vars: return False return True def try_get_solution(self): replaced = [self.__replace(x) for x in [self.lat_deg, self.lat_min, self.lat_min_2, self.lon_deg, self.lon_min, self.lon_min_2]] self.replaced_result = ("%%s%s %s.%s %%s%s %s.%s" % tuple(replaced)) % (self.ns, self.ew) results = [self.resolve(x) for x in replaced] for i in range(len(results)): if len(results[i]) not in self.EXPECTED_LENGTHS[i]: self.warnings.append(self.WARNING_WRONG_LENGTH % (len(results[i]), " or ".join([str(x) for x in self.EXPECTED_LENGTHS[i]]), results[i])) result = ("%%s%s %s.%s %%s%s %s.%s" % tuple(results)) % (self.ns, self.ew) #print self.replaced_result try: self.result = geo.try_parse_coordinate(result) self.result.name = self.orig except (Exception): self.warnings.append(self.WARNING_CANNOT_PARSE % result) self.result = False def __replace(self, text): for char, value in self.vars.items(): text = text.replace(char, str(value)) return text def resolve(self, text): c = 1 while c > 0: text, c = re.subn('\([^()]+\)', lambda match: self.__safe_eval(match.group(0)), text) if re.match('^[0-9]+$', text) == None: # determine number of leading zeros #lz = len(text) - len(str(int(text))) text = self.__safe_eval(text) try: text = "%03d" % int(text) except Exception: text = '?' return text def __safe_eval(self, text): try: tmp = eval(text,{"__builtins__":None},{}) except (SyntaxError, Exception): self.warnings.append(self.WARNING_SYNTAX) return '?' if round(tmp) != round(tmp, 1): self.warnings.append(self.WARNING_FLOAT % text) tmp = int(tmp) if tmp < 0: self.warnings.append(self.WARNING_NEGATIVE % tmp) if tmp > self.HIGH_RESULT_THRESHOLD: self.warnings.append(self.WARNING_VERY_HIGH % tmp) return str(tmp) def __str__(self): return "<%s> from %s" % (self.orig, self.source) SINGLE_CALC_PART = ur'''((?:\([A-Za-z +*/0-9-.,]+\)|[A-Za-z ()+*/0-9-])+)''' @staticmethod def find(text, source): text = re.sub(ur'''(?u)\s[^\W\d_]{2,}\s''', ' | ', text) text = re.sub(ur'''(?u)\b[^\W\d_]{4,}\b''', ' | ', text) text = text.replace('°', '|') text = text.decode('utf-8', 'replace').encode('utf-8') text = text.replace(unichr(160), ' ') text = re.sub(ur''' +''', ' ', text) matches = re.findall(ur'''(? %s" % (ord(x), x) print '---------------------------------------------------------' for instance in CalcCoordinate.find(h)[0]: print "Found: %s" % (instance.orig) agtl-0.8.0.3/files/advancedcaching/core.py000077500000000000000000001015241151564747700203240ustar00rootroot00000000000000#!/usr/bin/python # -*- coding: utf-8 -*- # Copyright (C) 2010 Daniel Fett # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # Author: Daniel Fett agtl@danielfett.de # Jabber: fett.daniel@jaber.ccc.de # Bugtracker and GIT Repository: http://github.com/webhamster/advancedcaching # from __future__ import with_statement VERSION = "0.8.0.3" import logging logging.basicConfig(level=logging.WARNING, format='%(relativeCreated)6d %(levelname)10s %(name)-20s %(message)s', ) from geo import Coordinate try: from json import loads, dumps except (ImportError, AttributeError): from simplejson import loads, dumps from sys import argv, exit from sys import path as sys_path import downloader import geocaching import gobject import gpsreader from os import path, mkdir, extsep, remove, walk import provider from threading import Thread import cachedownloader import fieldnotesuploader from actors.tts import TTS #from actors.notify import Notify if len(argv) == 1: import cli print cli.usage % ({'name': argv[0]}) exit() if '-v' in argv: import colorer import logging.handlers logging.getLogger('').setLevel(logging.DEBUG) logging.debug("Set log level to DEBUG") if '--simple' in argv: import simplegui gui = simplegui.SimpleGui elif '--desktop' in argv: import biggui gui = biggui.BigGui elif '--qt' in argv: import qtgui gui = qtgui.QtGui elif '--hildon' in argv: import hildongui gui = hildongui.HildonGui else: import cli gui = cli.Cli logger = logging.getLogger('core') class Core(gobject.GObject): __gsignals__ = { 'map-marks-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ()), 'cache-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT, )), 'fieldnotes-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ()), 'good-fix': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT)), 'no-fix': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT)), 'target-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT)), 'settings-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT, )), 'save-settings': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ()), 'error': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT, )), 'progress': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT, )), 'hide-progress': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ()), } SETTINGS_DIR = path.expanduser(path.join('~', '.agtl')) CACHES_DB = path.join(SETTINGS_DIR, "caches.db") COOKIE_FILE = path.join(SETTINGS_DIR, "cookies.lwp") UPDATE_DIR = path.join(SETTINGS_DIR, 'updates') MAEMO_HOME = path.expanduser(path.join('~', 'MyDocs', '.')) MAPS_DIR = path.join('Maps', '') DATA_DIR = path.expanduser(path.join('~', '')) if not path.exists(MAEMO_HOME) else MAEMO_HOME UPDATE_MODULES = [cachedownloader, fieldnotesuploader] DEFAULT_SETTINGS = { 'download_visible': True, 'download_notfound': True, 'download_new': True, 'download_nothing': False, 'download_create_index': False, 'download_run_after': False, 'download_run_after_string': '', 'download_output_dir': path.expanduser(path.join(DATA_DIR, 'geocaches', '')), 'map_position_lat': 49.7540, 'map_position_lon': 6.66135, 'map_follow_position': True, 'map_zoom': 7, 'download_resize': True, 'download_resize_pixel': 400, 'options_show_name': True, 'options_username': "", 'options_password': "", 'last_target_lat': 50, 'last_target_lon': 10, 'last_target_name': 'default', 'last_selected_geocache': '', 'download_noimages': False, 'download_map_path': DATA_DIR + MAPS_DIR, 'options_hide_found': False, 'options_show_error': True, 'options_show_html_description': False, 'map_providers': [ ('OpenStreetMaps', {'remote_url': "http://tile.openstreetmap.org/mapnik/%(zoom)d/%(x)d/%(y)d.png", 'prefix': 'OpenStreetMap I'}), ('OpenCycleMaps', {'remote_url': 'http://andy.sandbox.cloudmade.com/tiles/cycle/%(zoom)d/%(x)d/%(y)d.png', 'prefix': 'OpenCycleMap'}) ], 'options_map_double_size': False, 'options_rotate_screen': 0, 'tts_interval': 0, 'options_default_log_text' : 'TFTC!\n\nLogged at %X from my %(machine)s using AGTL.', } def __init__(self, guitype, root): gobject.GObject.__init__(self) self.current_target = None self.current_position = None self.create_recursive(self.SETTINGS_DIR) self.dataroot = path.join(root, 'data') self._install_updates() self.__read_config() self.connect('settings-changed', self.__on_settings_changed) self.connect('save-settings', self.__on_save_settings) self.create_recursive(self.settings['download_output_dir']) self.create_recursive(self.settings['download_map_path']) self.downloader = downloader.FileDownloader(self.settings['options_username'], self.settings['options_password'], self.COOKIE_FILE, cachedownloader.GeocachingComCacheDownloader.login_callback) self.pointprovider = provider.PointProvider(self.CACHES_DB, geocaching.GeocacheCoordinate, 'geocaches') self.gui = guitype(self, self.dataroot) actor_tts = TTS(self) actor_tts.connect('error', lambda caller, msg: self.emit('error', msg)) #actor_notify = Notify(self) self.emit('settings-changed', self.settings, self) self.emit('fieldnotes-changed') if '--sim' in argv: self.gps_thread = gpsreader.FakeGpsReader(self) gobject.timeout_add(1000, self.__read_gps) self.set_target(gpsreader.FakeGpsReader.get_target()) elif 'gpsprovider' in self.gui.USES: self.gps_thread = gpsreader.GpsReader() #self.gps_thread = gpsreader.FakeGpsReader(self) gobject.timeout_add(1000, self.__read_gps) elif 'locationgpsprovider' in self.gui.USES: self.gps_thread = gpsreader.LocationGpsReader(self.__read_gps_cb_error, self.__read_gps_cb_changed) gobject.idle_add(self.gps_thread.start) if 'geonames' in self.gui.USES: import geonames self.geonames = geonames.Geonames(self.downloader) if '--startup-only' in argv: return self.gui.show() if not '--profile' in argv: exit() ############################################## # # Misc # ############################################## @staticmethod def create_recursive(dpath): if dpath != '/': if not path.exists(dpath): head, tail = path.split(dpath) Core.create_recursive(head) try: mkdir(dpath) except Exception, e: logging.info("Could not create directory; %s" % e) pass def optimize_data(self): self.pointprovider.push_filter() self.pointprovider.set_filter(found=True) old_geocaches = self.pointprovider.get_points_filter() self.pointprovider.pop_filter() for x in old_geocaches: images = x.get_images() if len(images) == 0: continue for filename, caption in images.items(): fullpath = path.join(self.settings['download_output_dir'], filename) try: remove(fullpath) except Exception: logging.warning("Could not remove " + fullpath) self.pointprovider.remove_geocaches(old_geocaches) self.pointprovider.optimize() def get_file_sizes(self): folder = self.settings['download_output_dir'] folder_size = 0 for (p, dirs, files) in walk(folder): for file in files: filename = path.join(p, file) folder_size += path.getsize(filename) sqlite_size = path.getsize(self.CACHES_DB) return {'images': folder_size, 'sqlite': sqlite_size} @staticmethod def format_file_size(size): if size < 1024: return "%d B" % size elif size < 1024 * 1024: return "%d KiB" % (size / 1024) elif size < 1024 * 1024 * 1024: return "%d MiB" % (size / (1024 * 1024)) else: return "%d GiB" % (size / (1024 * 1024 * 1024)) ############################################## # # Handling Updates # ############################################## def _install_updates(self): updated_modules = 0 if path.exists(self.UPDATE_DIR): sys_path.insert(0, self.UPDATE_DIR) for m in self.UPDATE_MODULES: modulefile = path.join(self.UPDATE_DIR, "%s%spy" % (m.__name__, extsep)) if path.exists(modulefile): v_dict = {'VERSION': -1} with open(modulefile) as f: for line in f: if line.startswith('VERSION'): exec line in v_dict break if v_dict['VERSION'] > m.VERSION: pass#logging.info("Reloading Module '%s', current version number: %d, new version number: %d" % (m.__name__, v_dict['VERSION'], m.VERSION)) reload(m) updated_modules += 1 else: logging.info("Not reloading Module '%s', current version number: %d, version number of update file: %d" % (m.__name__, v_dict['VERSION'], m.VERSION)) else: logging.info("Skipping nonexistant update from" + path.join(self.UPDATE_DIR, "%s%spy" % (m.__name__, extsep))) return updated_modules def try_update(self): from urllib import urlretrieve from urllib2 import HTTPError import tempfile import hashlib from shutil import copyfile self.create_recursive(self.UPDATE_DIR) baseurl = 'http://www.danielfett.de/files/agtl-updates/%s' % VERSION url = "%s/updates" % baseurl self.emit('progress', 0.5, "Checking for updates...") try: try: reader = self.downloader.get_reader(url, login=False) except HTTPError, e: logging.exception(e) raise Exception("No updates available.") except Exception, e: logging.exception(e) raise Exception("Could not connect to update server.") try: files = [] for line in reader: md5, name = line.strip().split(' ') handle, temp = tempfile.mkstemp() files.append((md5, name, temp)) except Exception, e: logging.exception(e) raise Exception("No updates were found. (Could not process index file.)") if len(files) == 0: raise Exception("No updates available.") for md5sum, name, temp in files: url = '%s/%s' % (baseurl, name) try: urlretrieve(url, temp) except Exception, e: logging.exception(e) raise Exception("Could not download file '%s'" % name) hash = hashlib.md5(open(temp).read()).hexdigest() if hash != md5sum: raise Exception("There was an error downloading the file. (MD5 sum mismatch in %s)" % name) for md5sum, name, temp in files: file = path.join(self.UPDATE_DIR, name) try: copyfile(temp, file) except Exception, e: logging.exception(e) raise Exception("The update process was stopped while copying files. AGTL may run or not. If not, delete all *.py files in %s." % self.UPDATE_DIR) finally: try: remove(tmpfile) except Exception: pass except Exception, e: logging.exception(e) def same_thread(error): self.emit('hide-progress') self.emit('error', error) return False gobject.idle_add(same_thread, e) return None def same_thread(): self.emit('hide-progress') return False gobject.idle_add(same_thread) return self._install_updates() ############################################## # # Settings # ############################################## ''' This should be called to update the settings throughout all components. ''' def save_settings(self, settings, source): logger.debug("Got settings update from %s" % source) self.settings.update(settings) self.emit('settings-changed', settings, source) ''' This is called when settings have changed. ''' def __on_settings_changed(self, caller, settings, source): logger.debug("Settings where changed by %s." % source) if source == self: return if 'options_username' in settings and 'options_password' in settings: self.downloader.update_userdata(settings['options_username'], settings['options_password']) ''' This is called when settings shall be saved, calling save_settings afterwards. ''' def __on_save_settings(self, caller): logger.debug("Assembling update for settings, on behalf of %s" % caller) settings = { 'last_target_lat': self.current_target.lat, 'last_target_lon': self.current_target.lon } caller.save_settings(settings, self) def __del__(self): logger.debug("Somebody is trying to kill me, saving the settings.") self.emit('save-settings') self.__write_config() # called by gui def on_destroy(self): logger.debug("Somebody is being killed, saving the settings.") self.emit('save-settings') self.__write_config() # called by gui #def on_config_changed(self, new_settings): # self.settings = new_settings # self.downloader.update_userdata(self.settings['options_username'], self.settings['options_password']) # self.__write_config() def __read_config(self): filename = path.join(self.SETTINGS_DIR, 'config') logger.debug("Loading config from %s" % filename) if not path.exists(filename): self.settings = self.DEFAULT_SETTINGS return f = file(filename, 'r') string = f.read() self.settings = {} if string != '': tmp_settings = loads(string) for k, v in self.DEFAULT_SETTINGS.items(): if k in tmp_settings != None: self.settings[k] = tmp_settings[k] else: self.settings[k] = v else: self.settings = self.DEFAULT_SETTINGS def __write_config(self): filename = path.join(self.SETTINGS_DIR, 'config') logger.debug("Writing settings to %s" % filename) f = file(filename, 'w') config = dumps(self.settings, sort_keys=True, indent=4) f.write(config) ############################################## # # Target & GPS # ############################################## def set_target(self, coordinate): self.current_target = coordinate distance, bearing = self.__get_target_distance_bearing() self.emit('target-changed', coordinate, distance, bearing) self.emit('map-marks-changed') def __get_target_distance_bearing(self): if self.current_position != None and self.current_target != None: distance = self.current_position.distance_to(self.current_target) bearing = self.current_position.bearing_to(self.current_target) else: distance = None bearing = None return distance, bearing def __read_gps(self): fix = self.gps_thread.get_data() if fix.position != None: self.current_position = fix.position distance, bearing = self.__get_target_distance_bearing() self.emit('good-fix', fix, distance, bearing) else: self.emit('no-fix', fix, self.gps_thread.status) return True def __read_gps_cb_error(self, control, error): fix = gpsreader.Fix() msg = gpsreader.LocationGpsReader.get_error_from_code(error) self.emit('no-fix', fix, msg) return True def __read_gps_cb_changed(self, device): fix = self.gps_thread.fix_from_tuple(device.fix, device) # @type fix gpsreader.Fix if fix.position != None: self.current_position = fix.position distance, bearing = self.__get_target_distance_bearing() self.emit('good-fix', fix, distance, bearing) else: self.emit('no-fix', fix, 'No Fix') return True ############################################## # # Geonames & Routing # ############################################## def get_coord_by_name(self, query): return self.geonames.search(query) def search_place(self, search): return self.geonames.search_all(search) def get_route(self, c1, c2, r): c1 = self.geonames.find_nearest_intersection(c1) c2 = self.geonames.find_nearest_intersection(c2) route = self.geonames.find_route(c1, c2, r) out = [] together = [] TOL = 15 MAX_TOGETHER = 20 for i in range(len(route)): if len(together) == 0: together = [route[i]] if (i < len(route) - 1): brg = route[i].bearing_to(route[i + 1]) if len(together) < MAX_TOGETHER \ and (i < len(route) - 1) \ and (abs(brg - 90) < TOL or abs(brg + 90) < TOL or abs(brg) < TOL or abs (brg - 180) < TOL) \ and route[i].distance_to(route[i + 1]) < (r * 1000 * 2): together.append(route[i + 1]) else: from math import sqrt min_lat = min([x.lat for x in together]) min_lon = min([x.lon for x in together]) max_lat = max([x.lat for x in together]) max_lon = max([x.lon for x in together]) c1 = Coordinate(min_lat, min_lon) c2 = Coordinate(max_lat, max_lon) new_c1 = c1.transform(-45, r * 1000 * sqrt(2)) new_c2 = c2.transform(-45 + 180, r * 1000 * sqrt(2)) out.append((new_c1, new_c2)) together = [] logging.info("Needing %d unique queries" % len(out)) return out ############################################## # # Deprecated # ############################################## # called by gui def on_cache_selected(self, cache): self.gui.show_cache(cache) ############################################## # # Filters, Searching & Pointprovider # ############################################## # called by gui def on_start_search_simple(self, text): #m = re.search(r'/([NS]?)\s*(\d{1,2})\.(\d{1,2})\D+(\d+)\s*([WE]?)\s*(\d{1,3})\.(\d{1,2})\D+(\d+)', text, re.I) #if m != None: self.__try_show_cache_by_search('%' + text + '%') # called by gui def set_filter(self, found=None, owner_search='', name_search='', size=None, terrain=None, diff=None, ctype=None, location=None, marked=None): self.pointprovider.set_filter(found=found, owner_search=owner_search, name_search=name_search, size=size, terrain=terrain, diff=diff, ctype=ctype, marked=marked) self.emit('map-marks-changed') # called by gui def reset_filter(self): self.pointprovider.set_filter() self.emit('map-marks-changed') # called by gui def on_start_search_advanced(self, found=None, owner_search='', name_search='', size=None, terrain=None, diff=None, ctype=None, location=None, marked=None): self.pointprovider.set_filter(found=found, owner_search=owner_search, name_search=name_search, size=size, terrain=terrain, diff=diff, ctype=ctype, marked=marked) points = self.pointprovider.get_points_filter(location) self.gui.display_results_advanced(points) def get_points_filter(self, found=None, owner_search='', name_search='', size=None, terrain=None, diff=None, ctype=None, location=None, marked=None): self.pointprovider.push_filter() self.pointprovider.set_filter(found=found, owner_search=owner_search, name_search=name_search, size=size, terrain=terrain, diff=diff, ctype=ctype, marked=marked) points = self.pointprovider.get_points_filter(location) truncated = (len(points) >= self.pointprovider.MAX_RESULTS) self.pointprovider.pop_filter() return (points, truncated) def get_geocache_by_name(self, name): return self.pointprovider.get_by_name(name) def __try_show_cache_by_search(self, idstring): cache = self.pointprovider.find_by_string(idstring) if cache != None: self.gui.show_cache(cache) self.gui.set_center(cache) return True return False ############################################## # # Downloading # ############################################## # called by gui def on_download(self, location, sync=False): self.emit('progress', 0.5, "Downloading...") cd = cachedownloader.GeocachingComCacheDownloader(self.downloader, self.settings['download_output_dir'], not self.settings['download_noimages']) cd.connect("download-error", self.on_download_error) cd.connect("already-downloading-error", self.on_already_downloading_error) if not sync: def same_thread(arg1, arg2): gobject.idle_add(self.on_download_complete, arg1, arg2) return False cd.connect("finished-overview", same_thread) t = Thread(target=cd.get_geocaches, args=[location]) t.daemon = True t.start() return False else: return self.on_download_complete(None, cd.get_geocaches(location)) # called on signal by downloading thread def on_download_complete(self, something, caches, sync=False): new_caches = [] for c in caches: point_new = self.pointprovider.add_point(c) if point_new: new_caches.append(c) self.pointprovider.save() self.emit('hide-progress') self.emit('map-marks-changed') if sync: return (caches, new_caches) else: return False # called on signal by downloading thread def on_already_downloading_error(self, something, error): def same_thread(error): self.emit('error', error) return False gobject.idle_add(same_thread, error) #self.emit('error', error) # called on signal by downloading thread def on_download_error(self, something, error): logging.exception(error) def same_thread(error): self.emit('hide-progress') self.emit('error', error) return False gobject.idle_add(same_thread, error) # called by gui def on_download_cache(self, cache, sync=False): # self.emit('progress', 0.5, "Downloading %s..." % cache.name) cd = cachedownloader.GeocachingComCacheDownloader(self.downloader, self.settings['download_output_dir'], not self.settings['download_noimages']) cd.connect("download-error", self.on_download_error) cd.connect("already-downloading-error", self.on_already_downloading_error) if not sync: def same_thread(arg1, arg2): gobject.idle_add(self.on_download_cache_complete, arg1, arg2) return False cd.connect("finished-single", same_thread) t = Thread(target=cd.update_coordinate, args=[cache]) t.daemon = True t.start() #t.join() return False else: full = cd.update_coordinate(cache) return full # called on signal by downloading thread def on_download_cache_complete(self, something, cache): self.pointprovider.add_point(cache, True) self.pointprovider.save() self.emit('hide-progress') self.emit('cache-changed', cache) return False # called by gui def on_download_descriptions(self, location, visibleonly=False): #exporter = geocaching.HTMLExporter(self.downloader, self.settings['download_output_dir']) self.pointprovider.push_filter() if self.settings['download_notfound'] or visibleonly: found = False else: found = None if self.settings['download_new'] or visibleonly: has_details = False elif self.settings['download_nothing']: has_details = True else: has_details = None if self.settings['download_visible'] or visibleonly: self.pointprovider.set_filter(found=found, has_details=has_details, adapt_filter=True) caches = self.pointprovider.get_points_filter(location) else: self.pointprovider.set_filter(found=found, has_details=has_details, adapt_filter=False) caches = self.pointprovider.get_points_filter() self.pointprovider.pop_filter() self.update_coordinates(caches) def update_coordinates(self, caches): cd = cachedownloader.GeocachingComCacheDownloader(self.downloader, self.settings['download_output_dir'], not self.settings['download_noimages']) cd.connect("download-error", self.on_download_error) cd.connect("already-downloading-error", self.on_already_downloading_error) def same_thread(arg1, arg2): gobject.idle_add(self.on_download_descriptions_complete, arg1, arg2) return False def same_thread_progress (arg1, arg2, arg3, arg4): gobject.idle_add(self.on_download_progress, arg1, arg2, arg3, arg4) return False cd.connect('progress', self.on_download_progress) cd.connect('finished-multiple', same_thread) t = Thread(target=cd.update_coordinates, args=[caches]) t.daemon = True t.start() # called on signal by downloading thread def on_download_descriptions_complete(self, something, caches): for c in caches: self.pointprovider.add_point(c, True) self.pointprovider.save() self.emit('hide-progress') for c in caches: self.emit('cache-changed', c) self.emit('map-marks-changed') return False # called on signal by downloading thread def on_download_progress(self, something, cache_name, i, max_i): self.emit('progress', float(i) / float(max_i), "Downloading %s (%d of %d)..." % (cache_name, i, max_i)) return False ############################################## # # Exporting # ############################################## def on_export_cache(self, cache, format, folder): from exporter import GpxExporter if (format == 'gpx'): exporter = GpxExporter() else: raise Exception("Format currently not supported: %s" % format) self.emit('progress', 0.5, "Exporting %s..." % cache.name) try: exporter.export(cache, folder) except Exception, e: self.emit('error', e) finally: self.emit('hide-progress') ############################################## # # Fieldnotes # ############################################## def save_fieldnote(self, cache): if cache.logas == geocaching.GeocacheCoordinate.LOG_AS_FOUND: cache.found = 1 elif cache.logas == geocaching.GeocacheCoordinate.LOG_AS_NOTFOUND: cache.found = 0 self.save_cache_attribute(cache, ('logas', 'logdate', 'fieldnotes')) self.emit('fieldnotes-changed') def on_upload_fieldnotes(self): self.emit('progress', 0.5, "Uploading Fieldnotes...") caches = self.pointprovider.get_new_fieldnotes() fn = fieldnotesuploader.FieldnotesUploader(self.downloader) fn.connect("upload-error", self.on_download_error) def same_thread(arg1): gobject.idle_add(self.on_upload_fieldnotes_finished, arg1, caches) return False fn.connect('finished-uploading', same_thread) for c in caches: fn.add_fieldnote(c) t = Thread(target=fn.upload) t.daemon = True t.start() def on_upload_fieldnotes_finished(self, widget, caches): for c in caches: c.logas = geocaching.GeocacheCoordinate.LOG_NO_LOG self.save_cache_attribute(c, 'logas') self.emit('hide-progress') self.emit('fieldnotes-changed') def get_new_fieldnotes_count(self): return self.pointprovider.get_new_fieldnotes_count() ############################################## # # Geocache Handling # ############################################## def save_cache_attribute(self, cache, attribute): if type(attribute) == tuple: for a in attribute: self.pointprovider.update_field(cache, a, cache.serialize_one(a), save=False) self.pointprovider.save() else: self.pointprovider.update_field(cache, attribute, cache.serialize_one(attribute)) def set_alternative_position(self, cache, ap): cache.set_alternative_position(ap) self.save_cache_attribute(cache, ('alter_lat', 'alter_lon')) self.emit('map-marks-changed') def determine_path (): """Borrowed from wxglade.py""" try: root = __file__ if path.islink (root): root = path.realpath (root) return path.dirname(path.abspath (root)) except: logging.error("I'm sorry, but something is wrong.") logging.error("There is no __file__ variable. Please contact the author.") exit() def start(): gobject.threads_init() Core(gui, determine_path()) def start_profile(what): import cProfile p = cProfile.Profile() p.run(what) stats = p.getstats() print "BY CALLS:\n------------------------------------------------------------" def c(x, y): if x.callcount < y.callcount: return 1 elif x.callcount == y.callcount: return 0 else: return -1 stats.sort(cmp=c) for line in stats[:100]: print "%d %4f %s" % (line.callcount, line.totaltime, line.code) if line.calls == None: continue line.calls.sort(cmp=c) for line in line.calls[:10]: print "-- %d %4f %s" % (line.callcount, line.totaltime, line.code) print "BY TOTALTIME:\n------------------------------------------------------------" def c(x, y): if x.totaltime < y.totaltime: return 1 elif x.totaltime == y.totaltime: return 0 else: return -1 stats.sort(cmp=c) for line in stats[:30]: print "%d %4f %s" % (line.callcount, line.totaltime, line.code) if line.calls == None: continue line.calls.sort(cmp=c) for line in line.calls[:10]: print "-- %d %4f %s" % (line.callcount, line.totaltime, line.code) if __name__ == "__main__": if '--profile' in argv: start_profile('start()') else: start() agtl-0.8.0.3/files/advancedcaching/data/000077500000000000000000000000001151564747700177255ustar00rootroot00000000000000agtl-0.8.0.3/files/advancedcaching/data/accept.png000066400000000000000000000014151151564747700216730ustar00rootroot00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8˥KSa;vvl dD!P{$; ż,Kݽ6cL2r^H)-jsNm֔2qQB̽BatoL#z {q' r=)La8,u%2Rg>ݾW ϛJ߸Pd makD|=G Vn6[Įd桚(Pm.0Q`'Fb#&ܧ6aP׏Q12[+zi; ]C17оpI9̾jD}›?7ayze,hXAK^3*bk @+wQ=!}uXzq:g쯺n= :d+_GTA;Ր Jƣ.!P)5!H:epր"݂"Kyw|{H2!i~3z_X;okBZK* ^R:O(jF*^ȰS诿_ gЬycIENDB`agtl-0.8.0.3/files/advancedcaching/data/asterisk_yellow.png000066400000000000000000000013471151564747700236600ustar00rootroot00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<yIDAT8˥KTaƟa-j9ijD ETQ@1 I&V);D-sD"̏QgRk9w5Z<9/Hb! ʼp,Ͼ3N?j5G+C.f%L*o3M`)8D{F[l9Z͹U}sud43bQ[RoIvr7ޤ q5c\|z*a$][ ,[RI"l12x'|FjaY/r A*f?LE ELwChP_ c5Pp)9fA7s 9/L~>7dS!{/V?@l*ydLކ ]Gp*{" 3tRI}/7K VtV.ۥPc >o"4TSH{Z6,<9 F}pL*f<҆-B]+,I+.~'sl#!lZuZѺl+wx !BNJH/:ePm%𴸗 L:%IENDB`agtl-0.8.0.3/files/advancedcaching/data/calendar_edit.png000066400000000000000000000014111151564747700232060ustar00rootroot00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8˥KlQߥkgHMDUb6E"h*4BbX! Xt%JK AS7j+.A%.c3Ue,~F#'rټ󽆈?з_JtHkiROQP~tdܰ Evso 9;AZT$]p]j(_>@{~S$ nzJ|NhDkD3ȧyyXùGVD7}ؕ ""R+%&k$ZG:kE"JZNhl [Xql jZ$.fz^)'L'k̉- 5 և>CEL*\C2zgWɞEi`P$AkqB:<&)c$r>pw 6}G5"BKCl>spI}YNLr>X,^G*V|`I ?gx{`Œ:r'Ɠ gtͣ%檓rZL~oa,:+|c>6򺦉[<POjh-z IKoJz^ˬE+BW7a: }#?i%p,$#<Ol+=oiv AdIENDB`agtl-0.8.0.3/files/advancedcaching/data/comment.png000066400000000000000000000006351151564747700221010ustar00rootroot00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe</IDAT8c?%jD@XW-' lp ߿ӿ>Ϳ_A! 3XP?j~P_ĀUgAxc5)PwPͯ P' {@[&P_jdN  C5, 7`>P2hĆ <;{gn IJ@ J@|S"j IENDB`agtl-0.8.0.3/files/advancedcaching/data/cross.png000066400000000000000000000012171151564747700215650ustar00rootroot00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<!IDAT8˕NQɉϠVȑB[( &^M6M| Dްҙv:官J-%Nd LB>w_3:*WrlNC/-좕B'{ u_a46ҽbߡE%D47;ٻƩ;8ˣ}>6[ӕS@*Z Qk>~͵hB\9uxZvYb J Cيٽ?BYvn&kft$,d9Zap\^ Y7 QJF 9=Q4 ؜Io SBpsI) Fv(@yՎވc\@ %% Z2h'@d(<|áaJuM@O⤁LGjd!X8Af 5J i K->w62ƾWH}:mP]XB0QX=ib_g=!Ftt…clrIENDB`agtl-0.8.0.3/files/advancedcaching/data/delete.png000066400000000000000000000013131151564747700216730ustar00rootroot00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<]IDAT8˥KSa[nQP2wܦγL[,biaA\Cv_2MlZFjסNMjmkʷ`&.#z<ϓ bVPT3%I{GqRivȅ tz#E6EddJ`DR2<]N ;4Ѿ;m>78ɀQe6LIt殷cq!z |v j/Xi@ %1|hl !|! Y#uUNw]˼ H3u t]E>k%IfoRD:0`~ | (r on3oG0!$V *[W0_-+ dW&2ZfMFVJpiF&B > Rg- ~ CmڴER ឫ p5ްy+21Kawh` #aZ񽞆TZoLѓ`"(?'ˎJvKކ|:G9[aw82 Jw f'ymzsӘTsw__ιIrIENDB`agtl-0.8.0.3/files/advancedcaching/data/desktop.glade000066400000000000000000003437341151564747700224120ustar00rootroot00000000000000 480 500 True True True True True vertical True <b>Geocache</b> (none selected) True True end False 0 True False 1 True 2 4 True True <b>size</b> True 2 3 True ? 3 4 True <b>terrain</b> True 1 2 True ? 1 2 1 2 True <b>diff.</b> True 2 3 1 2 True ? 3 4 1 2 True <b>type</b> True True ? 1 2 False 2 True False 3 True True 8 True True True never automatic True queue True True 5 False word-char False Please select a geocache by clicking it on the map or double clicking it on the "cache search" results table. True desc. False tab True 0.10000000149011612 0.10000000149011612 15 True True word-char True 35 1 True hints 1 False tab True vertical True double click a coordinate to set it as the target False False 0 True True automatic automatic 1 2 True coords 2 False tab True vertical True True automatic automatic True queue none True gtk-missing-image 0 True click "next" button to see image True False 1 50 True change zoom True True True 0 gtk-go-forward True True True True 1 False end 2 3 True images 3 False tab True vertical True True automatic automatic True queue True True 0 True fieldnote date: not set False 1 True 2 2 don't post notes True True False True True post as 'found' True True False True radiobutton_cache_log_no 1 2 post as 'not found' True True False True radiobutton_cache_log_no 1 2 1 2 post note True True False True radiobutton_cache_log_no 1 2 False 2 True to upload these notes, go to sync tab False 3 4 True field note 4 False tab True vertical True True automatic automatic True queue True True 0 5 True notes 5 False tab 4 50 True download details 50 True False True True 0 True True False True True <span bgcolor="yellow">marked</span> True False 1 set as target 50 True True True 2 False 5 False True True 3 4 True 4 1 2 GTK_EXPAND | GTK_SHRINK | GTK_FILL True filter is active - reset at "cache search" 4 GTK_FILL True 20 start + 50 True True True False False 0 - 50 True True True False False 1 track 50 True True True False False 2 actions 50 True True True False False 3 4 2 3 GTK_FILL GTK_FILL False True False True True vertical 480 600 True True 6 True True 4 6 True True 6 1 2 change 50 True True True 5 6 3 4 GTK_FILL GTK_FILL True No Target set True True 3 6 2 3 GTK_FILL GTK_FILL True No Position True center 3 2 3 GTK_FILL GTK_FILL True No satellites in sight. 5 3 4 GTK_FILL GTK_FILL True 1 <span size="x-large"><b>- m</b></span> True 2 GTK_FILL GTK_FILL True 343° 2 4 GTK_FILL GTK_FILL True alt 12 m 4 6 GTK_FILL GTK_FILL True GPS data False tab True True True vertical True 10 3 True <b>Status</b> True 3 4 True <b>Size</b> True 4 5 True <b>Difficulty</b> True 6 7 True <b>Type</b> True traditional True True False True True 1 2 multi True True False True True 2 3 all True True False True 2 3 1 2 mystery True True False True True 1 2 1 2 virtual True True False True 1 2 micro True True False True 1 2 4 5 small True True False True 2 3 4 5 other True True False True 2 3 5 6 huge True True False True 1 2 5 6 regular True True False True 5 6 True <b>Terrain</b> True 7 8 True all not found found marked & new 1 3 3 4 True all min max = 1 2 6 7 True 1..1.5 2..2.5 3..3.5 4..4.5 5 2 3 6 7 True all min max = 1 2 7 8 True 1..1.5 2..2.5 3..3.5 4..4.5 5 2 3 7 8 True 3 8 9 in current map view True True False True 3 9 10 True True gtk-find suchen... 1 3 2 3 True <b>Name/ID</b> True 2 3 False 6 0 True True gtk-revert-to-saved 50 True True True True 0 True True True True show <span bgcolor="yellow" fgcolor="black">marked</span> True 1 search 50 True True True 2 False 1 True search False tab True vertical True Too much results, not showing all. False False 0 True True automatic automatic 1 True view on map True True True 0 view details True True True 1 True True False True True <span bgcolor="yellow">marked</span> True False 2 set as target True True True 3 False 2 1 True result list 1 True False end tab 1 True search 1 False tab True vertical True 0 none True 12 True vertical True 0 you have not created any new field notes yet False 4 0 gtk-ok True True True True False 1 True False True False word False After uploading, fieldnotes can be accessed through your profile page. They are private and can be used as a template for a log entry. 2 True <b>upload field notes</b> True label_item 0 True 0 none True 12 True vertical only visible caches True True False True False 0 only not-found caches True True False True True False 1 don't update existing descriptions True True False True False 2 True 2 resize images (not working) False True False True True False True True 0 False True px True True False 6 1 1 2 False 3 gtk-ok True True True True False 4 True <b>download cache descriptions</b> True label_item 1 True 0 none True 12 True vertical True 0 file format: HTML-pages with images False 4 0 True True 1 gtk-ok True True True True False 2 True <b>export geocaches</b> True label_item 2 2 True sync 2 False tab True True automatic automatic True queue True 11 2 True 0.10000000149011612 user name 1 2 GTK_FILL True 0.10000000149011612 password 2 3 GTK_FILL GTK_FILL 150 True True 1 2 1 2 GTK_FILL 150 True True False 1 2 2 3 GTK_FILL True 0.10000000149011612 OSM map tile path (restart after changing) 4 5 GTK_FILL GTK_FILL 150 True True 1 2 4 5 GTK_FILL True 0.10000000149011612 output directory (don't change after first download) 6 7 GTK_FILL 150 True True 1 2 6 7 GTK_FILL don't download images True True False True 2 7 8 True 0.05000000074505806 <b>login data</b> True 2 GTK_FILL 10 True 0.05000000074505806 <b>map tiles</b> True 2 3 4 GTK_FILL 10 True 0.05000000074505806 <b>cache download</b> True 2 5 6 GTK_FILL 10 True 0.05000000074505806 <b>display</b> True 2 8 9 GTK_FILL 10 show cache id on map True True False True 2 9 10 GTK_FILL hide found caches on map True True False True 2 10 11 GTK_FILL 3 True options 3 False tab 0 True True False 1 True True True gtk-save True True set center as target True True True download geocaches True True True fetch details for visible True True True go to target True agtl-0.8.0.3/files/advancedcaching/data/emoticon_grin.png000066400000000000000000000013121151564747700232640ustar00rootroot00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<\IDAT8˥KQqQB$ȐJVaIEiUZ n}(* e(0`T3k0A?lt7sZVHžxzzL$F]˱X[vfQ Qŭ&܋xz*Bi[em5_QE k!XS5 TY_x *"D~^Ics!FeYƝ%,%LRS]n1IENDB`agtl-0.8.0.3/files/advancedcaching/data/error.png000066400000000000000000000012321151564747700215620ustar00rootroot00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<,IDAT8˥SKHkTC[RL5 o= ck)c.$BDP,,NY(FFfkaf>o1Hb=fU~Ϣ=USl.ZkP(9X(>H3kR x_Oqg覆8t]tiXas_ j'{Љ_袺I~}^OTj5՟羇}vM悥]b7(Vl9o XoCn%M+ciѐ+Ci@IzW^5 @Ee6dVK>@dW2U/zW ѳ'BOlYxoT3# Yd(,aPG+_Rr\:m;gS o*0>N @aΣ5;50?kkz65ß} /qoI0-  R`ZUކ1̌ U rj1B CeqƊ@޳H \):xu4E3'ǃxǃ8>!@}X;Lc٧S/IENDB`agtl-0.8.0.3/files/advancedcaching/data/freerunner.glade000066400000000000000000003364501151564747700231110ustar00rootroot00000000000000 480 500 True 480 600 True True 6 True True 4 6 True True 6 1 2 change 50 True True True 5 6 3 4 GTK_FILL GTK_FILL True No Target set True True 3 6 2 3 GTK_FILL GTK_FILL True No Position True center 3 2 3 GTK_FILL GTK_FILL True No satellites in sight. 5 3 4 GTK_FILL GTK_FILL True 1 <span size="x-large"><b>- m</b></span> True 2 GTK_FILL GTK_FILL True 343° 2 4 GTK_FILL GTK_FILL True alt 12 m 4 6 GTK_FILL GTK_FILL True GPS False tab True 3 4 True filter is active - reset at "cache search" 4 GTK_FILL actions 50 True True True 3 4 2 3 GTK_FILL track 50 True True True 2 3 2 3 GTK_FILL - 50 True True True 1 2 2 3 GTK_FILL + 50 True True True 2 3 GTK_FILL 1 True map view 1 False tab True True <b>Geocache</b> (none selected) True True end False 0 True False 1 True 2 4 True True <b>size</b> True 2 3 True ? 3 4 True <b>terrain</b> True 1 2 True ? 1 2 1 2 True <b>diff.</b> True 2 3 1 2 True ? 3 4 1 2 True <b>type</b> True True ? 1 2 False 2 True False 3 True True 8 True True True never automatic True queue True True 5 False word-char False Please select a geocache by clicking it on the map or double clicking it on the "cache search" results table. True desc. False tab True True automatic automatic True queue True True 5 False word-char False 1 True logs& hints 1 False tab True True double click a coordinate to set it as the target False False 0 True True automatic automatic 1 2 True coords 2 False tab True True True automatic automatic True queue none True gtk-missing-image 0 True click "next" button to see image True False 1 50 True change zoom True True True 0 gtk-go-forward True True True True 1 False end 2 3 True images 3 False tab True True True automatic automatic True queue True True 0 True fieldnote date: not set False 1 True 2 2 don't post notes True True False True True post as 'found' True True False True True radiobutton_cache_log_no 1 2 post as 'not found' True True False True True radiobutton_cache_log_no 1 2 1 2 post note True True False True True radiobutton_cache_log_no 1 2 False 2 True to upload these notes, go to sync tab False 3 4 True field note 4 False tab True True True automatic automatic True queue True True 0 5 True notes 5 False tab 4 50 True download details 50 True False True True 0 True True False True True <span bgcolor="yellow">marked</span> True False 1 set as target 50 True True True 2 False 5 2 30 True details 2 False tab True True True True 10 3 True <b>Status</b> True 3 4 True <b>Size</b> True 4 5 True <b>Difficulty</b> True 6 7 True <b>Type</b> True traditional True True False True True 1 2 multi True True False True True 2 3 all True True False True 2 3 1 2 mystery True True False True True 1 2 1 2 virtual True True False True 1 2 micro True True False True 1 2 4 5 small True True False True 2 3 4 5 other True True False True 2 3 5 6 huge True True False True 1 2 5 6 regular True True False True 5 6 True <b>Terrain</b> True 7 8 True all not found found marked & new 1 3 3 4 True all min max = 1 2 6 7 True 1..1.5 2..2.5 3..3.5 4..4.5 5 2 3 6 7 True all min max = 1 2 7 8 True 1..1.5 2..2.5 3..3.5 4..4.5 5 2 3 7 8 True 3 8 9 in current map view True True False True 3 9 10 True True gtk-find suchen... 1 3 2 3 True <b>Name/ID</b> True 2 3 False 6 0 True True gtk-revert-to-saved 50 True True True True 0 True True True True show <span bgcolor="yellow" fgcolor="black">marked</span> True 1 search 50 True True True 2 False 1 True search False tab True True Too much results, not showing all. False False 0 True True automatic automatic 1 True view on map True True True 0 view details True True True 1 True True False True True <span bgcolor="yellow">marked</span> True False 2 set as target True True True 3 False 2 1 True result list 1 True False end tab 3 True search 3 False tab True True 0 none True 12 True True 0 you have not created any new field notes yet False 4 0 gtk-ok True True True True False 1 True False True False word False After uploading, fieldnotes can be accessed through your profile page. They are private and can be used as a template for a log entry. 2 True <b>upload field notes</b> True label_item 0 True 0 none True 12 True only visible caches True True False True False 0 only not-found caches True True False True True False 1 don't update existing descriptions True True False True False 2 True 2 resize images (not working) False True False True True False True True 0 False True px True True False 6 1 1 2 False 3 gtk-ok True True True True False 4 True <b>download cache descriptions</b> True label_item 1 True 0 none True 12 True True 0 file format: HTML-pages with images False 4 0 True True 1 gtk-ok True True True True False 2 True <b>export geocaches</b> True label_item 2 4 True sync 4 False tab True True automatic automatic True queue True 14 2 True 0.10000000149011612 user name 1 2 GTK_FILL True 0.10000000149011612 password 2 3 GTK_FILL GTK_FILL 150 True True 1 2 1 2 GTK_FILL 150 True True False 1 2 2 3 GTK_FILL True 0.10000000149011612 OSM map tile path (restart after changing) 4 5 GTK_FILL GTK_FILL 150 True True 1 2 4 5 GTK_FILL True 0.10000000149011612 output directory (don't change after first download) 6 7 GTK_FILL 150 True True 1 2 6 7 GTK_FILL don't download images True True False True 2 7 8 True 0.05000000074505806 <b>login data</b> True 2 GTK_FILL 10 True 0.05000000074505806 <b>map tiles</b> True 2 3 4 GTK_FILL 10 True 0.05000000074505806 <b>cache download</b> True 2 5 6 GTK_FILL 10 True 0.05000000074505806 <b>display</b> True 2 8 9 GTK_FILL 10 show geocache name on map True True False True 2 9 10 GTK_FILL hide found caches on map True True False True 2 10 11 GTK_FILL show map in double size True True False True 2 11 12 GTK_FILL True 0.05000000074505806 <b>check for updates</b> True 2 12 13 GTK_FILL 10 check for website parser update True True True 2 13 14 GTK_FILL 5 True opt 5 False tab 0 True True False 1 True gtk-save True True set center as target True True True download geocaches True True True fetch details for visible True True True go to target True agtl-0.8.0.3/files/advancedcaching/data/group.png000066400000000000000000000013611151564747700215700ustar00rootroot00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8ˍMLqpЖ:ud֋9X@BLɡ"d[?Z)R m{XkC ݚ9k2<<߾={~Qv{X-8&P{K+D d{0IT~ |꿋#-7p:3I bg7SB U:w(Άj$hOZ(NDoWWu .5:<9l2y RԺ %b&O8Dmd0u un%t%=Q"k{ݒ {u3pv JŠߡ4-aJ5髣hO>6@ bj 600 500 True True vertical True True True 3 3 True both-horiz True Zoom In True zoom-in False True True Zoom Out True zoom-out False True True Geocaches Herunterladen document-save False True 2 GTK_FILL True True True vertical True True vertical button True True True none http://glade.gnome.org 0 button False True True True 1 0 True 0.10000000149011612 0.10000000149011612 Geocache mit einem doch schon ganz schön langen Namen True 25 1 False 0 True False 1 True 2 4 True True Größe 2 3 True klein 3 4 True Terrain 1 2 True 4/5 1 2 1 2 True Diff. 2 3 1 2 True 2.5/5 3 4 1 2 True Typ True multi 1 2 False 2 True False 3 True True automatic automatic True True True 0.10000000149011612 0.10000000149011612 15 Dies ist eine Beispiel Cachebeschreibung, die nur als Beispiel dient. Sie enthält auch Absätze, wie diesen. Beispiel Cachebeschreibung, die nur als Beispiel dient. Und noch Text. Hier gehts weiter. Sie enthält auch Absätze, wie diesen. Beispiel Cachebeschreibung, die nur als Beispiel dient. Und noch Text. True word-char True 30 True Kurz False True 0.10000000149011612 0.10000000149011612 15 Dies ist eine Beispiel Cachebeschreibung, die nur als Beispiel dient. Sie enthält auch Absätze, wie diesen. Beispiel Cachebeschreibung, die nur als Beispiel dient. Und noch Text. Hier gehts weiter. Sie enthält auch Absätze, wie diesen. Beispiel Cachebeschreibung, die nur als Beispiel dient. Und noch Text. True word-char True 30 1 True Lang 1 False True 0.10000000149011612 0.10000000149011612 15 Dies ist eine Beispiel Cachebeschreibung, die nur als Beispiel dient. Sie enthält auch Absätze, wie diesen. Beispiel Cachebeschreibung, die nur als Beispiel dient. Und noch Text. Hier gehts weiter. Sie enthält auch Absätze, wie diesen. Beispiel Cachebeschreibung, die nur als Beispiel dient. Und noch Text. True word-char True 30 2 True Hints 2 False True 0.10000000149011612 0.10000000149011612 15 Dies ist eine Beispiel Cachebeschreibung, die nur als Beispiel dient. Sie enthält auch Absätze, wie diesen. Beispiel Cachebeschreibung, die nur als Beispiel dient. Und noch Text. Hier gehts weiter. Sie enthält auch Absätze, wie diesen. Beispiel Cachebeschreibung, die nur als Beispiel dient. Und noch Text. True True word-char True 30 3 True Koordinaten 3 False 4 gefunden True True False True False 5 True geocaching.com-Seite True True True none http://glade.gnome.org False 0 loggen True True True none http://glade.gnome.org 1 False 6 False True True queue True True True 3 1 3 True 7 True Zoom-Stufe: 0 True True adjustment1 True 1 True Suchen: 2 True True 3 OK True True True 4 2 3 GTK_FILL True Kartenansicht False True vertical True 5 10 True True Name oder ID: 0 True True 1 5 True True Besitzer: 0 True True 1 5 10 True 0 none True 12 True vertical traditional True True False True True 0 multi True True False True True 1 unknown True True False True True 2 virtual True True False True 3 alle True True False True 4 True <b>Typ:</b> True 2 4 2 3 True 0 none True 12 True vertical micro True True False True True 0 small True True False True True 1 regular True True False True True 2 big True True False True True 3 andere True True False True True 4 True <b>Größe:</b> True 4 6 2 3 True 0 none True 12 True True True vertical adjustment2 False 0 0 bottom False 0 True True vertical adjustment3 on on 5 0 bottom False 1 True <b>Terrain:</b> True 6 8 2 3 True 0 none True 12 True True True vertical adjustment4 False 1 0 bottom False 0 True True vertical adjustment5 5 0 bottom False 1 True <b>Schwierigkeit:</b> True 8 10 2 3 True 10 1 2 True 10 3 4 True 14 True end gtk-revert-to-saved True True True True False False 0 gtk-find True True True True False False 1 10 4 5 True vertical True 0 none True vertical egal True True False True True False False 0 ja True True False True radio_search_found False False 1 nein True True False True radio_search_found False False 2 True <b>gefunden?</b> True 0 auch Caches ohne Details finden True True False True 1 2 2 3 False 6 0 True True automatic automatic 1 True 15 end Filter für Kartenansicht True True True False False 0 markierte Herunterladen True True True False False 1 False 2 1 True Filtern und Suchen 1 False True vertical Nur sichtbare Caches herunterladen True True False True 0 Nur nicht-gefundene Caches herunterladen True True False True True 1 Nur neue Cache-Beschreibungen herunterladen True True False True 2 Nichts herunterladen, nur Seiten aus Datenbank schreiben True True False True 3 HTML-Index-Seite anlegen True True False True 4 True 3 2 True Ausgabeverzeichnis True True 1 2 True True 1 2 1 2 danach ausführen: True True False True 1 2 Bilder verkleinern auf True True False True 2 3 True True True 0 True Pixel True False 6 1 1 2 2 3 5 Herunterladen starten! True True True image1 False False 6 True 0.10000000000000001 7 2 False True Cache-Download 2 False True vertical True True Position: 0 True True 1 True Kommentar: 2 True True 3 False 0 True 16 True end gtk-new True True True True False False 0 gtk-save True True True True bottom False False 1 False 1 True True automatic automatic 2 3 True Eigene Wegpunkte 3 False True vertical True 0 none True 12 True vertical Name neben Cache-Symbol anzeigen True True False True 0 True <b>Darstellung</b> True False 0 True 0 none True 12 True 2 2 True 0 Benutzername True 0 Passwort 1 2 GTK_FILL True True 1 2 True True False 1 2 1 2 True <b>Zugangsdaten</b> True False 1 4 False True Optionen 4 False 0 True 2 False 1 1 1 18 1 1 1 1 1 6 1 1 1 5 1 6 1 1 1 1 1 6 1 1 1 1 1 6 1 1 1 True gtk-save True gtk-apply agtl-0.8.0.3/files/advancedcaching/data/noimage-cantload.png000066400000000000000000000233001151564747700236330ustar00rootroot00000000000000PNG  IHDR\rfsRGB pHYs  tIME "*4%tEXtCommentCreated with GIMPW IDATx{xǿ;b[h@R/U\BE$$$=rIr%"(@ % 4BPAԢKQTw339i&WVuhp8 ] GW9ph3kWd: |[QQQaAtn1hԩ?N-77%%%={s~o>|,\0"@8Ƙ1c bv֘ph3>}: 6 Z ݻw{7XۗG)ul(,,m_0#ޜ_,-Z|0"Ȣ4 `Xv6sCG1]_nSklq}1ZF]v>IqEEE;wmf]'@7:{f2nbx1gΜS1quQOe@(Byy9x sk())1bn 2yd]4qȎP^^]_sij|9r'aw[SL]Mh7uF۷cHnʣչ0k,>uˍezc?x`1?%hرc:S4k [FΝ[oڵ+Bтd_TT'nhuDoѢIIIK 1{qe+Ab#O3ZA0n݊o6^ydM6a̘1fmv6***BaaoĿSL1;Ϟ=L={>(Əoj4 V\h]KB!<3Ȉaţ.9pZ?'$$Du:/sn8p {hf;pAbĉr|8HVV.]j%eeexѵkW}8ƺuн{wB!$$$ /Dq~{ň#:h-ZSll} Km۶!55_|1ZlĈJ,EÒ%Kp"..[F~pSg!jK/K.5!;e]f\W[Mt~3՟aӦMH3QF{>Ř8q)-뚦ON ҥ zꅓ'OUUU1cfϞm2KٶmRRRGkMӰb s=Fڷo7Æ ùxk׮7ߌ^î]P[[;vSN+V{t N2tRdff@ii)1-t~Dɨ5k]~+|ػw/zAcCa֬Y]u"oXf Ӎryy9233o>t58"h|>?=zJJJ0p@3gqr]lE\g?*++`3aׯG0ih׮vhaO]-[4:}GƌK_:e RbQ'MGy3gD(2n49(PTTP(,nK/cǎE0DQQQ]رcMqq1B1p@;vԩSyN3XB!|>L6 {nMd{k;w61x{3*B(BEEnFRLކ͑ߥj*cJ|{ 2t6]tСC GiL%k.c,,1dȐ?W8N)+0f_}! {p ҥ&pس=ʐji1~jtxc&PYYlӁ!1=޴}/7nȧ~/iz_|FMulذC5}1}5(Yy~{Djm۶N/de8x;:zC/#==ѣ]۷k׮_|Evm^y.W_E޽yxoNyD0``ʕ馛LZbZ طo btI'[EERRR :۶m-b|>444xszvA?7a=fl߃u,B_>|pnǎCqqƳ_۷oiի|z*//9 m۶!!!dž ld!!rg+߇Uʍ#ߺ'_^Gkf͚!77sӧM7G}\̉'cƍظqqƍzjL0TDc=flCx71{;MObͦ:TUUaժUYf]ǪU<ۣ8pccљ=Teo= $?x1l0c^VV>#4k hӦ t]7+paB!>| ?v/ 36o={믿_|'سg.,Yر)))x1sLouQ\\lDyyy9s>h{ⵕ+WzGuu5ŋ#//͛7Grr2em.A 0$$^;@ǁ(5aqoشi;fnӦ :wA4 7oF~iߏK_g}8tM;w4x5 36D; d]`9rPHKK3}?n8ܘM#>ի!8)z-rXܤׯLj# ȥ)))C r>Ԥ$d-v$&^.lf 6n3v mE7/((>Sz_.piii298Etݺu1b1*IS-(?6y:B!#f7z< vNN,YbܴnSd"_ߩqs;x:y ~v17R-?lq.zmYYYXjU+GݜBQVy$%nѣG׏&-;S$~^;m`M~ъ#Q:/o)((8#P>/ ***fq*h r뭷矏:J:~'{^xxa?cKj~k neuD9f ^ÇƍRwYvM٨l333\^,"J>5AXgJ7>?lrhg֜%%%=u&MBYYe6ZR䘦^҄ϟ~Hm-ӦMCIIIDP4Z4!wkOGrrrPRRz=X*7>77״wٶ:+Vz='1nݺ)0(j㳲"\kqe#rO`޼y|z͉;uT^}/ڦc1?@~~>MQc=i_<eZZ:d>1=e˖2{gbȑXn]D3`Ν1bN>\s Kڵ ~-ƍ tٔ1H>'rƠ@Sk|@}}=RRRbN:Ξ=R#o(gΜwމ L2ƴԚm۶F7b̘11ڱcU5.Q]]. | pBu|w߿?ߏ{sE8ٳg/#)) >馛\믣G|(--E >={͚v3g`͚50`}ׯN|)3"uWǎѥKsjχ!C੧Zw!)t]K/>}^O|>=b=3?FEEE6 ۦҕ9p 2Juѣ1}t1vXlٲO> /t:^Rwlr*rJcȑύ[jqķYfo֨_c޽1.]i'kILeG_ythk=kuݺu;w.lق yyyҥ Zl;P\p1XI!''{mT}>LSMnÏ~#_|a\C.n')UtJMMZڶm444/6vYXtW"hܹIPDzr^-ullq!Km6khh@MM F!]v8y$f̘:`޽Fխp*]wrF0z9z'X}g֭[GZ=Η`6nS.q$\-DBBBLmmWEEѣG#akMn׫W/=zT_GOt-Uaa!***"X]y.gڳg,X|c1*++;\}r1% [n1YHWV\\^x^x\ɓѢE ,ZoI7A/8~8nPuO2T5՝KK]qua5joѐ~nj亮cƌ oƸq㐒T|:G(Ñl$''cȑ7n\IJ\+X_fQoߎaÆa޼y2d[nC w 8L%\|4o&L@qaРAݻ/c322\_oLBbƌ(,,04V͛7cCFffuz饗ЩS;ڤAy7׫0j(3ym5]Yuu5,Y#G  k]w݅tmڴ' ]vw"ɓo III6l.҈?'NDaaX7jCq())zlHwe+wN1 ۵i֭O9U|0+ܹMuD;'+Ϸz SLA~PVVP(R1"b_S 9~`Pc:4A=4Wr[O>&1kZ/O!IDAT8fwht_|g0tPۼ[Aם@ӕ͛ 8p;v,`6l)vzrΜ9e@,i &vYgM%"v;.:nt_KN<{}ٲe4i-vV8] ѹsg_^{ 0n8b۷^RkAA̙c,#a@̵m]lN^%H(҅k,+5um wqiv}q~7+Ç# !..{رc1p@޽zP*C6oIENDB`agtl-0.8.0.3/files/advancedcaching/data/noimage-loading.png000066400000000000000000000213021151564747700234630ustar00rootroot00000000000000PNG  IHDR\rfsRGB pHYs  tIME !+FtEXtCommentCreated with GIMPW IDATx}tǿ;jZ Т T/A @_ WrA0mJ:y_]oE.]\u^sU[e(,,== jjjЮ];:U8厩U@E痳,]4$He i0`>\];իæN+_#́ ==g϶ NU9r$֬Y;~mN%l$lYΓ`0uT,Z~555tƋw~i<|IY~̶ѿP+Vرc3%'[`ʔ)n*nݺY|0qD,Xz<nL\'X{~nJ0?fyM'%%YfK]Fb 50ˁTӴ[lvE~Ν;^z_WtZx˗;k؋/ƌ3]_~aWDg "x<tUUUz0qcAt q%;;cǎ56Vu***h򇛈5/5Rf~xڵ8.m@}J{:vAAx W﫲cGli zZ9|$ܹSwX^oH*!7޽{p:NS[j4K\Ys 6 ֭$''4d"Էƛ;u]ף3t#F`ٲe9l @~eI#5E=z4y睐E|͔+YF~^Ry)QF7pUIIIAnnE{ |c̘1G~^/z)9VA@t{ k׮ (Q^|E&1DhN6{up#^I47]3;Ä iQ 55˖-LiL̛7SN qǣg3b5`x#]EEE0:gR ??vM)..F>}_̧ȣ]s=^d x饗\Ⴘ|EW'U :wOzOKK~mա1~xӉ`u-+)..СC-)3bݺuxGlGp%??IIIs& Kqq1~a,`ʔ)6m^n39r{z64DW&mzГ'OFFFS'ܺu+ bHEt+**ЧOCF!/|>L\l5+W ) ?#===͝ϵgDZLJ@M0n8̝;7ĩğaɒ%(..ƿ/={ 4ߎΝ;#&&6Lhm/\Rl۶ ?GԩSh֬Zn]G~3X\C,//Gll,ߏ_7o_DEE[oE^0l0lHHH07KV7+PUUϣI&{0tP$&& o)S29sz[ɓhР>ĸn3?<_o i_m'ŋL8q".\`/\cXp!ʐ(c%K 555Z|gPVV ;v)쥼]vEff&MfHq/^3g`޽7o͛ĈWKܔGbGQN:`XlZ]Ç/; /,Cpx"N>*deea^AW/vER4MCrr2,Yn@2-[ٳ;n^^9իWRcykÇ ߼ysDGGqƸx":SN.]I&믿Fjj*^QTx۶mѣ^yyme˖8s q >_}D%n^>S?'Nj 7|3 ۷OqFL<III7==G:t(rss-7hRnػw/ 1k,ఢwy'wOR555ԩ6nܨ;W ܹs |wǃoٲ%6l؀z555曱d 8_|e;~~>bccC̙3HHHooІ k Qq|{ҤI~zm`f͚^رck\ ͛Nr]m|rr2fΜ1Ow q~a f̘f͚˗/Gll`Qff&na-Zdn @MM PXX:YQO޿_TT;HAAǃ TUU233ӟTvΚ5+TY./FRR5lgWA6DGG{>i&?~\7g4n555zHKK-RΏx#'M X?v@ +V !!Axa7oLaaad]PP޽{3F|rz})=z[lAbb~kٻwoҡ} V\Vw؁ݻ<*^o>@ wy 0s4mTXz5sCYY>C|m{1}=zŅӋYӧ#rT贑&&&N?62Xu`ׯם@kiDв:d3pk.QNDN0#GwcǎիA]X8}݆=zTZjk.ٹDf͚!}] 4!??FrUvoxl馸]*k < bƌxku?938s w697߿IF+aT;=7"zpGYwy߬`0ݺu>awa3Fw bxЪU+h 64;۬ʥK gnӟg/qlU 2220a„˺ŐI+sX5NK+=V>\zn5jJ >w'O?ϟ?|[lA6m ihΝ;ö׳ڙiӦmUDIgΞ=k,9E>}UzW@_͛رc˅ѣk]%%%M6wZ<vjYo;˗g 9cN}3-ZR C~nԣZI[յՠGgKKnpݻw7UVVO\ꔚ<#;py]{1,ɧ[nx:JvBYRR$v|kW w6m۶|Z`bU4/U]b.:uuud.;8fj[ر#~ݺu+>s6UXPP`}>_&{^/VZ}Bvw%%%uNsW׮]F̯];v4 B| |MV005tWJCu$nR^vik/>%%%x oYxW S倢i6l`X&JJJ vr"$&&eee  "!!A(**rlMl~r[u^~i|W=Gח_~ِa9Y}ժUMNNuNzO>au zViݗhٳqqq}M7!99Y… Sj9R{w 11 tYt)*++k!>>^۶mÈ#¦ʁ@M41Yp!/^lzr;v]N~%Jqq!hjfXB߾}w^O>q^C]߯qgݺuoԭ\z鳼3zh=Mtz쉑#Ge˖h۶- n:\wV{zkرcgyγgϢyyyo0`aWK~RQQؐMV زe 9N:?8X[`P"]-WuzO~5k֠_~zذa6l`4hyyyرaÇn׮^z%}vҥK;w? ++1lܸqqq(,,DϞ=qI͛-Ois1+IePqh޼yz6u뮻YYY֭vkQu[p*[l2;5xЧOܹ}5若"ܗm4j)))(,,˖-C=PSShܹz-[vH_.:†ӧOǶm0|pDGGA_)))@RRl(3(/W<ĉ1{l{=ey埽'.\ݻw?ġCp뭷oGNpM7?wƥKp?5BVЩS'4jH… 2x駟b۶mq[oEΝѤIۀrߏ :4l=َ|a7B8$N6l| ~my6zU] [oU6X#0ȑ#tRW?j7r9-uɒV~onгKA`ӦMի°]xꔗ?v,NkLj#0oD0D\\aǑlkݺ5/#--[ՑOVb$+A]>XdK67JW2ruHf"5;+6ȯLx޿?~aT8վoOǚ5k݀,7eʔ)x饗BJ>v^?z@Ztxg}NJy'&_;KeyU#\`^jڍIמ!y/R0ι}_'Kmנ/x,>_-Kmuh<5e^xz=_z>|i^O{=z>|i^WO{=z>|i^O{=z>|x=wzU^O{=z>|i^O{=z^;|i^O{=z>|i^xz=_z>|i^O{=z>|i^WO{=z>|i^O{=z>|x=wzU^O{=z>|i^O{=z^;|i^O{=z>|i^xz=_z>|i^O{=z>|i^WO{=z>|i^O{=z>|x=wzU^O{=z>|i^O{=z^;|i^O{=z>|i^xz=_z>|i^O{=z>|i^WO{=z>|i^O{=z>|x=wzU^O{=z>|iNgbEקIENDB`agtl-0.8.0.3/files/advancedcaching/data/options.glade000066400000000000000000000240711151564747700224220ustar00rootroot00000000000000 5 popup True center-on-parent True applications-system dialog False True vertical 2 True 0 in True 3 4 True Verzeichnis für heruntergeladene Geoaches: 4 True True 4 1 2 heruntergeladene Bilder verkleinern auf True True False True 2 2 3 True True 4 2 3 2 3 GTK_FILL True Pixel 3 4 2 3 True <b>Optionen zum Herunterladen</b> True 1 True 0 in True 12 True 2 2 True Benutzername True Passwort 1 2 True True 1 2 True True True 1 2 1 2 True <b>Zugangsdaten</b> True 2 True end gtk-cancel True True True True False False 0 gtk-apply True True True True False False 1 False end 0 button2 button1 agtl-0.8.0.3/files/advancedcaching/data/simple.glade000066400000000000000000002740011151564747700222200ustar00rootroot00000000000000 600 500 True True vertical True True True 3 3 True both-horiz True Zoom In True zoom-in False True True Zoom Out True zoom-out False True True Geocaches Herunterladen document-save False True 2 GTK_FILL True True True vertical True True vertical button True True True none http://glade.gnome.org 0 button False True True True 1 0 True 0.10000000149011612 0.10000000149011612 Geocache mit einem doch schon ganz schön langen Namen True 25 1 False 0 True False 1 True 2 4 True True Größe 2 3 True klein 3 4 True Terrain 1 2 True 4/5 1 2 1 2 True Diff. 2 3 1 2 True 2.5/5 3 4 1 2 True Typ True multi 1 2 False 2 True False 3 True True automatic automatic True True True 0.10000000149011612 0.10000000149011612 15 Dies ist eine Beispiel Cachebeschreibung, die nur als Beispiel dient. Sie enthält auch Absätze, wie diesen. Beispiel Cachebeschreibung, die nur als Beispiel dient. Und noch Text. Hier gehts weiter. Sie enthält auch Absätze, wie diesen. Beispiel Cachebeschreibung, die nur als Beispiel dient. Und noch Text. True word-char True 30 True Kurz False True 0.10000000149011612 0.10000000149011612 15 Dies ist eine Beispiel Cachebeschreibung, die nur als Beispiel dient. Sie enthält auch Absätze, wie diesen. Beispiel Cachebeschreibung, die nur als Beispiel dient. Und noch Text. Hier gehts weiter. Sie enthält auch Absätze, wie diesen. Beispiel Cachebeschreibung, die nur als Beispiel dient. Und noch Text. True word-char True 30 1 True Lang 1 False True 0.10000000149011612 0.10000000149011612 15 Dies ist eine Beispiel Cachebeschreibung, die nur als Beispiel dient. Sie enthält auch Absätze, wie diesen. Beispiel Cachebeschreibung, die nur als Beispiel dient. Und noch Text. Hier gehts weiter. Sie enthält auch Absätze, wie diesen. Beispiel Cachebeschreibung, die nur als Beispiel dient. Und noch Text. True word-char True 30 2 True Hints 2 False True 0.10000000149011612 0.10000000149011612 15 Dies ist eine Beispiel Cachebeschreibung, die nur als Beispiel dient. Sie enthält auch Absätze, wie diesen. Beispiel Cachebeschreibung, die nur als Beispiel dient. Und noch Text. Hier gehts weiter. Sie enthält auch Absätze, wie diesen. Beispiel Cachebeschreibung, die nur als Beispiel dient. Und noch Text. True True word-char True 30 3 True Koordinaten 3 False 4 gefunden True True False True False 5 True geocaching.com-Seite True True True none http://glade.gnome.org False 0 loggen True True True none http://glade.gnome.org 1 False 6 False True True queue True True True 3 1 3 True 7 True Zoom-Stufe: 0 True True adjustment1 True 1 True Suchen: 2 True True 3 OK True True True 4 2 3 GTK_FILL True Kartenansicht False True vertical True 5 10 True True Name oder ID: 0 True True 1 5 True True Besitzer: 0 True True 1 5 10 True 0 none True 12 True vertical traditional True True False True True 0 multi True True False True True 1 unknown True True False True True 2 virtual True True False True 3 alle True True False True 4 True <b>Typ:</b> True 2 4 2 3 True 0 none True 12 True vertical micro True True False True True 0 small True True False True True 1 regular True True False True True 2 big True True False True True 3 andere True True False True True 4 True <b>Größe:</b> True 4 6 2 3 True 0 none True 12 True True True vertical adjustment2 False 0 0 bottom False 0 True True vertical adjustment3 on on 5 0 bottom False 1 True <b>Terrain:</b> True 6 8 2 3 True 0 none True 12 True True True vertical adjustment4 False 1 0 bottom False 0 True True vertical adjustment5 5 0 bottom False 1 True <b>Schwierigkeit:</b> True 8 10 2 3 True 10 1 2 True 10 3 4 True 14 True end gtk-revert-to-saved True True True True False False 0 gtk-find True True True True False False 1 10 4 5 True vertical True 0 none True vertical egal True True False True True False False 0 ja True True False True radio_search_found False False 1 nein True True False True radio_search_found False False 2 True <b>gefunden?</b> True 0 auch Caches ohne Details finden True True False True 1 2 2 3 False 6 0 True True automatic automatic 1 True 15 end Filter für Kartenansicht True True True False False 0 markierte Herunterladen True True True False False 1 False 2 1 True Filtern und Suchen 1 False True vertical Nur sichtbare Caches herunterladen True True False True 0 Nur nicht-gefundene Caches herunterladen True True False True True 1 Nur neue Cache-Beschreibungen herunterladen True True False True 2 Nichts herunterladen, nur Seiten aus Datenbank schreiben True True False True 3 HTML-Index-Seite anlegen True True False True 4 True 3 2 True Ausgabeverzeichnis True True 1 2 True True 1 2 1 2 danach ausführen: True True False True 1 2 Bilder verkleinern auf True True False True 2 3 True True True 0 True Pixel True False 6 1 1 2 2 3 5 Herunterladen starten! True True True image1 False False 6 True 0.10000000000000001 7 2 False True Cache-Download 2 False True vertical True True Position: 0 True True 1 True Kommentar: 2 True True 3 False 0 True 16 True end gtk-new True True True True False False 0 gtk-save True True True True bottom False False 1 False 1 True True automatic automatic 2 3 True Eigene Wegpunkte 3 False True vertical True 0 none True 12 True vertical Name neben Cache-Symbol anzeigen True True False True 0 True <b>Darstellung</b> True False 0 True 0 none True 12 True 2 2 True 0 Benutzername True 0 Passwort 1 2 GTK_FILL True True 1 2 True True False 1 2 1 2 True <b>Zugangsdaten</b> True False 1 4 False True Optionen 4 False 0 True 2 False 1 1 1 18 1 1 1 1 1 6 1 1 1 5 1 6 1 1 1 1 1 6 1 1 1 1 1 6 1 1 1 True gtk-save True gtk-apply agtl-0.8.0.3/files/advancedcaching/data/wrench.png000066400000000000000000000011421151564747700217170ustar00rootroot00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8˕oQ_[c+W.]хi5  g mJ[Fq#_)qո;,@{;upLTʙH$(Z XMɤni"\nt:}yD 0 \ ږIU4(r HMk̈_4_ziy'"[ n1rM_A`b=$Ik_p-qS~=li~3Bv"qZAԧ̸r[G]<&e!'ڸ67 yq$OX!=_~1Gs~EZQx&qWK3!ޤunkzGrjQnIENDB`agtl-0.8.0.3/files/advancedcaching/downloader.py000066400000000000000000000143711151564747700215320ustar00rootroot00000000000000#!/usr/bin/python # -*- coding: utf-8 -*- # Copyright (C) 2010 Daniel Fett # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # Author: Daniel Fett agtl@danielfett.de # Jabber: fett.daniel@jaber.ccc.de # Bugtracker and GIT Repository: http://github.com/webhamster/advancedcaching # import logging logger = logging.getLogger('downloader') class FileDownloader(): USER_AGENT = 'User-Agent: Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.0.12) Gecko/2009070811 Windows NT Firefox/3.1' opener_installed = False def __init__(self, username, password, cookiefile, login_callback): self.username = username self.password = password self.cookiefile = cookiefile self.logged_in = False from socket import setdefaulttimeout setdefaulttimeout(30) self.opener_installed = False self.login_callback = login_callback def update_userdata(self, username, password): from os import path, remove self.username = username self.password = password self.logged_in = False if path.exists(self.cookiefile): try: remove(self.cookiefile) except: logger.info("Could not remove cookie file?!") pass def login(self): if self.username == '' or self.password == '': raise Exception("Please configure your username/password and restart the application") logger.info("Checking Login status") from cookielib import LWPCookieJar cj = LWPCookieJar(self.cookiefile) if not self.opener_installed: from urllib2 import build_opener, install_opener, HTTPCookieProcessor opener = build_opener(HTTPCookieProcessor(cj)) install_opener(opener) self.opener_installed = True try: cj.load() logger.info("Loaded cookie file") except: logger.info("Couldn't load cookie file") else: logger.info("Checking if still logged in...") url = 'http://www.geocaching.com/seek/nearest.aspx' page = self.get_reader(url, login = False) for line in page: if 'You are logged in as' in line: self.logged_in = True logger.info("Seems as we're still logged in") page.close() return elif 'You are not logged in.' in line: logger.info("Nope, not logged in anymore") page.close() break logger.info("Logging in") url, values = self.login_callback(self.username, self.password) page = self.get_reader(url, values, login = False) for line in page: if 'You are logged in as' in line: break elif 'You are not logged in.' in line or 'combination does not match' in line: raise Exception("Wrong password or username!") else: logger.info("Seems as if the language is set to something other than english") raise Exception("Please go to geocaching.com and set the website language to english!") logger.info("Great success.") self.logged_in = True try: cj.save() except Exception, e: logger.info("Could not save cookies: %s" % e) def get_reader(self, url, values=None, data=None, login = True): from urllib import urlencode from urllib2 import Request, urlopen if login and not self.logged_in: self.login() if values == None and data == None: req = Request(url) self.add_headers(req) return urlopen(req) elif data == None: if (isinstance(values, dict)): values = urlencode( values) req = Request(url, values) self.add_headers(req) return urlopen(req) elif values == None: content_type, body = data req = Request(url) req.add_header('Content-Type', content_type) req.add_header('Content-Length', len(str(body))) self.add_headers(req) req.add_data(body) return urlopen(req) def encode_multipart_formdata(self, fields, files): """ fields is a sequence of (name, value) elements for regular form fields. files is a sequence of (name, filename, value) elements for data to be uploaded as files Return (content_type, body) ready for httplib.HTTP instance """ BOUNDARY = '----------ThIs_Is_tHe_bouNdaRY_$' CRLF = '\r\n' L = [] for (key, value) in fields: L.append('--' + BOUNDARY) L.append('Content-Disposition: form-data; name="%s"' % key) L.append('') L.append(value) for (key, filename, value) in files: L.append('--' + BOUNDARY) L.append('Content-Disposition: form-data; name="%s"; filename="%s"' % (key, filename)) L.append('Content-Type: %s' % self.get_content_type(filename)) L.append('') L.append(value) L.append('--' + BOUNDARY + '--') L.append('') body = CRLF.join(L) content_type = 'multipart/form-data; boundary=%s' % BOUNDARY return content_type, body @staticmethod def get_content_type(filename): import mimetypes return mimetypes.guess_type(filename)[0] or 'application/octet-stream' def add_headers(self, req): req.add_header('User-Agent', self.USER_AGENT) req.add_header('Cache-Control', 'no-cache') req.add_header('Pragma', 'no-cache') agtl-0.8.0.3/files/advancedcaching/exporter.py000066400000000000000000000272411151564747700212440ustar00rootroot00000000000000#!/usr/bin/python # -*- coding: utf-8 -*- # Copyright (C) 2010 Daniel Fett # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # Author: Daniel Fett agtl@danielfett.de # Jabber: fett.daniel@jaber.ccc.de # Bugtracker and GIT Repository: http://github.com/webhamster/advancedcaching # from pyfo import pyfo import os from datetime import datetime from geocaching import GeocacheCoordinate class Exporter(): def export(self, coordinate, folder = None): if coordinate.name == '': raise Exception('Koordinate hat keinen Namen') if folder == None: folder = self.path filename = self.__get_uri(coordinate, folder) f = open(filename, 'w') f.write(self.get_text(coordinate)) f.close() def __get_uri(self, coordinate, folder): return os.path.join(folder, "%s%s%s" % (coordinate.name, os.extsep, self.EXTENSION)) class GpxExporter(Exporter): EXTENSION = 'gpx' def get_text(self, c): result = pyfo(self.__build_gpx(c), pretty=True, prolog=True, encoding='utf-8') return result.encode('utf8', 'xmlcharrefreplace') def __build_gpx(self, c): return ('gpx', self.__build_intro(c) + self.__build_main_wp(c) + self.__build_wps(c.get_waypoints()), { 'xmlns:xsi' : "http://www.w3.org/2001/XMLSchema-instance", 'xmlns:xsd' : 'http://www.w3.org/2001/XMLSchema', 'version' : '1.0', 'creator' : 'AGTL Geocaching Tool', 'xsi:schemaLocation' : "http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd http://www.groundspeak.com/cache/1/0 http://www.groundspeak.com/cache/1/0/cache.xsd", 'xmlns' : "http://www.topografix.com/GPX/1/0" }) def __build_intro(self, c): return [ ('name', 'AGTL Geocache Listing'), ('desc', ' '), ('email', 'nothing@example.com'), ('url', 'http://www.geocaching.com'), ('urlname', 'Geocaching - High Tech Treasure Hunting'), ('time', '2010-02-27T18:31:24.4812526Z'), ('keywords', 'cache, geocache'), ('author', c.owner), ('bounds', None, c.get_bounds()), ] def __build_main_wp(self, c): # prepare some variables... available = archived = 'True' if c.status & GeocacheCoordinate.STATUS_DISABLED: available = 'False' if not (c.status & GeocacheCoordinate.STATUS_ARCHIVED): archived = 'False' return [('wpt', [ ('time', '2010-02-27T18:31:24.4812526Z'), ('name', c.name), ('desc', "%s D%s T%s: %s" % (c.type, c.get_difficulty(), c.get_terrain(), c.title)), ('url', 'http://coord.info/%s' % c.name), ('urlname', c.name), ('sym', 'Geocache'), ('type', 'Geocache|%s' % c.get_gs_type()), ('groundspeak:cache', self.__build_cache_info(c), { 'id' : 42, 'available' : available, 'archived' : archived, 'xmlns:groundspeak' : "http://www.groundspeak.com/cache/1/0" }) ], { 'lat' : "%.5f" % c.lat, 'lon' : "%.5f" % c.lon }) ] def __build_cache_info(self, c): if c.size == 0 or c.size == 5: cs = 'Not Chosen' elif c.size == 1: cs = 'Micro' elif c.size == 2: cs = 'Small' elif c.size == 3: cs = 'Regular' elif c.size == 4: cs = 'Large' else: cs = 'Not Chosen' return [ ('groundspeak:name', c.title), ('groundspeak:placed_by', c.owner), ('groundspeak:owner', c.owner, {'id' : '42'}), ('groundspeak:type', c.get_gs_type()), ('groundspeak:container', cs), ('groundspeak:difficulty', c.get_difficulty()), ('groundspeak:terrain', c.get_terrain()), ('groundspeak:country', 'unknown'), ('groundspeak:state', 'unknown'), ('groundspeak:short_description', c.shortdesc, {'html' : 'True'}), ('groundspeak:long_description', c.desc, {'html' : 'True'}), ('groundspeak:encoded_hints', c.hints), ] def __build_wps(self, wps): out = [] for wp in wps: if wp['lat'] == -1 and wp['lon'] == -1: continue out += [('wpt', [ ('time', datetime.now().strftime('%Y-%m%dT%H:%M:%S.00')), ('name', wp['id']), ('desc', wp['name']), ('cmt', wp['comment']), ('url', ''), ('urlname', ''), ('sym', 'Trailhead'), ('type', 'Waypoint|Trailhead') ], { 'lat' : "%.5f" % wp['lat'], 'lon' : "%.5f" % wp['lon'] }) ] return out class HTMLExporter(): def __init__(self, downloader, path): self.downloader = downloader self.path = path if not os.path.exists(path): try: os.mkdir(path) except: raise def export(self, coordinate, folder = None): if coordinate.name == '': raise Exception('Koordinate hat keinen Namen') if folder == None: folder = self.path filename = self.__get_uri(coordinate, folder) f = open(filename, 'w') self.__write_html(f, coordinate) f.close() self.__copy_images(coordinate, folder) def __copy_images(self, coordinate, folder): for image, description in coordinate.get_images().items(): src = os.path.realpath(os.path.join(self.path, image)) dst = os.path.realpath(os.path.join(folder, image)) if not src == dst and not os.path.exists(dst) and os.path.exists(src): import shutil shutil.copy(src, dst) def __get_uri(self, coordinate, folder): return os.path.join(folder, "%s%shtml" % (coordinate.name, os.extsep)) def write_index(self, caches): b = [{'n': c.name, 't': c.title} for c in caches] caches = json.dumps(b) f = open(os.path.join(self.path, "index.html"), 'w') f.write('') f.write(' ') f.write('') f.write(' Geocache suchen
Geocache suchen
Ergebnisse
Bitte Suchbegriff eingeben!
""") f.close() def __write_html(self, f, coordinate): f.write('\n') f.write('\n \n') f.write('') self.__write_header(f, coordinate) f.write(' \n \n') self.__write_body(f, coordinate) f.write(' \n\n') def __write_header(self, f, coordinate): f.write(' %s|%s\n' % (coordinate.name, coordinate.title)) def __write_body(self, f, coordinate): f.write('

%s|%s

\n' % (coordinate.name, coordinate.title)) f.write('
Daten\n') f.write('
Size: %s/5

\n' % coordinate.size) f.write('
Difficulty: %.1f/5

\n' % (coordinate.difficulty / 10)) f.write('
Terrain: %.1f/5

\n' % (coordinate.terrain / 10)) f.write('
\n') f.write('
Koordinaten\n') f.write('
MAIN: %s %s

\n' % (coordinate.get_lat(geo.Coordinate.FORMAT_DM), coordinate.get_lon(geo.Coordinate.FORMAT_DM))) if len(coordinate.get_waypoints()) > 0: f.write(' \n') for w in coordinate.get_waypoints(): if not (w['lat'] == -1 and w['lon'] == -1): n = geo.Coordinate(w['lat'], w['lon']) latlon = "%s %s" % (n.get_lat(geo.Coordinate.FORMAT_DM), n.get_lon(geo.Coordinate.FORMAT_DM)) else: latlon = "???" f.write(' \n' % (w['name'], latlon)) f.write(' \n' % w['comment']) f.write('
%s%s
%s
\n') f.write('
') f.write('
Cachebeschreibung\n') f.write(self.__replace_images(coordinate.desc, coordinate)) f.write('
') if len(coordinate.get_images()) > 0: f.write('
Bilder\n') for image, description in coordinate.get_images().items(): f.write(' %s:
\n' % description) f.write(' \n' % image) f.write('
\n') f.write('
') def __replace_images(self, text, coordinate): return re.sub(r'\[\[img:([^\]]+)\]\]', lambda a: self.__replace_image_callback(a, coordinate), text) def __replace_image_callback(self, match, coordinate): if match.group(1) in coordinate.get_images(): return '' % match.group(1) else: return ' [image not found -- please re-download geocache description] ' agtl-0.8.0.3/files/advancedcaching/extListview.py000066400000000000000000000573451151564747700217330ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # Author: Ingelrest François (Francois.Ingelrest@gmail.com) # # 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 Library 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 # # ExtListView v1.6 # # v1.6: # * Added a context menu to column headers allowing users to show/hide columns # * Improved sorting a bit # # v1.5: # * Fixed intermittent improper columns resizing # # v1.4: # * Replaced TYPE_INT by TYPE_PYOBJECT as the fifth parameter type of extListview-dnd # (see http://www.daa.com.au/pipermail/pygtk/2007-October/014311.html) # * Prevent sorting rows when the list is empty # # v1.3: # * Greatly improved speed when sorting a lot of rows # * Added support for gtk.CellRendererToggle # * Improved replaceContent() method # * Added a call to set_cursor() when removing selected row(s) # * Added getFirstSelectedRow(), appendRows(), addColumnAttribute(), unselectAll() and selectAll() methods # * Set expand to False when calling pack_start() # # v1.2: # * Fixed D'n'D reordering bugs # * Improved code for testing the state of the keys for mouse clicks # * Added quite a few new methods (replaceContent, hasMarkAbove, hasMarkUnder, __len__, iterSelectedRows, iterAllRows) # # v1.1: # * Added a call to set_cursor() when unselecting all rows upon clicking on the empty area # * Sort indicators are now displayed whenever needed from gobject import SIGNAL_RUN_LAST from gobject import TYPE_BOOLEAN from gobject import TYPE_INT from gobject import TYPE_NONE from gobject import TYPE_PYOBJECT from gobject import TYPE_STRING from gobject import signal_new import gtk from gtk import gdk # Internal d'n'd (reordering) DND_REORDERING_ID = 1024 DND_INTERNAL_TARGET = ('extListview-internal', gtk.TARGET_SAME_WIDGET, DND_REORDERING_ID) # Custom signals signal_new('extlistview-dnd', gtk.TreeView, SIGNAL_RUN_LAST, TYPE_NONE, (gdk.DragContext, TYPE_INT, TYPE_INT, gtk.SelectionData, TYPE_INT, TYPE_PYOBJECT)) signal_new('extlistview-modified', gtk.TreeView, SIGNAL_RUN_LAST, TYPE_NONE, ()) signal_new('extlistview-button-pressed', gtk.TreeView, SIGNAL_RUN_LAST, TYPE_NONE, (gdk.Event, TYPE_PYOBJECT)) signal_new('extlistview-column-visibility-changed', gtk.TreeView, SIGNAL_RUN_LAST, TYPE_NONE, (TYPE_STRING, TYPE_BOOLEAN)) signal_new('button-press-event', gtk.TreeViewColumn, SIGNAL_RUN_LAST, TYPE_NONE, (gdk.Event, )) class ExtListViewColumn(gtk.TreeViewColumn): """ TreeViewColumn does not signal right-click events, and we need them This subclass is equivalent to TreeViewColumn, but it signals these events Most of the code of this class comes from Quod Libet (http://www.sacredchao.net/quodlibet) """ def __init__(self, title=None, cell_renderer=None, ** args): """ Constructor, see gtk.TreeViewColumn """ gtk.TreeViewColumn.__init__(self, title, cell_renderer, ** args) label = gtk.Label(title) self.set_widget(label) label.show() label.__realize = label.connect('realize', self.onRealize) def onRealize(self, widget): widget.disconnect(widget.__realize) del widget.__realize button = widget.get_ancestor(gtk.Button) if button is not None: button.connect('button-press-event', self.onButtonPressed) def onButtonPressed(self, widget, event): self.emit('button-press-event', event) class ExtListView(gtk.TreeView): def __init__(self, columns, sortable=True, dndTargets=[], useMarkup=False, canShowHideColumns=True): """ If sortable is True, the user can click on headers to sort the contents of the list The d'n'd targets are the targets accepted by the list (e.g., [('text/uri-list', 0, 0)]) Note that for the latter, the identifier 1024 must not be used (internally used for reordering) If useMarkup is True, the 'markup' attributes is used instead of 'text' for CellRendererTexts """ gtk.TreeView.__init__(self) self.selection = self.get_selection() # Sorting rows self.sortLastCol = None # The last column used for sorting (needed to switch between ascending/descending) self.sortAscending = True # Ascending or descending order self.sortColCriteria = {} # For each column, store the tuple of indexes used to sort the rows # Default configuration for this list self.set_rules_hint(True) self.set_headers_visible(True) self.selection.set_mode(gtk.SELECTION_MULTIPLE) # Create the columns nbEntries = 0 dataTypes = [] for (title, renderers, sortIndexes, expandable, visible) in columns: if title is None: nbEntries += len(renderers) dataTypes += [renderer[1] for renderer in renderers] else: column = ExtListViewColumn(title) column.set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE) column.set_expand(expandable) column.set_visible(visible) if canShowHideColumns: column.connect('button-press-event', self.onColumnHeaderClicked) self.append_column(column) if sortable: column.set_clickable(True) column.connect('clicked', self.__sortRows) self.sortColCriteria[column] = sortIndexes for (renderer, type) in renderers: nbEntries += 1 dataTypes.append(type) column.pack_start(renderer, False) if isinstance(renderer, gtk.CellRendererToggle): column.add_attribute(renderer, 'active', nbEntries-1) elif isinstance(renderer, gtk.CellRendererPixbuf): column.add_attribute(renderer, 'pixbuf', nbEntries-1) elif isinstance(renderer, gtk.CellRendererText): if useMarkup: column.add_attribute(renderer, 'markup', nbEntries-1) else: column.add_attribute(renderer, 'text', nbEntries-1) # Mark management self.markedRow = None self.markColumn = len(dataTypes) dataTypes.append(TYPE_BOOLEAN) # When there's no other solution, this additional entry helps in finding the marked row # Create the ListStore associated with this tree self.store = gtk.ListStore(*dataTypes) self.set_model(self.store) # Drag'n'drop management self.dndContext = None self.dndTargets = dndTargets self.motionEvtId = None self.dndStartPos = None self.dndReordering = False if len(dndTargets) != 0: self.enable_model_drag_dest(dndTargets, gdk.ACTION_DEFAULT) self.connect('drag-begin', self.onDragBegin) self.connect('drag-motion', self.onDragMotion) self.connect('button-press-event', self.onButtonPressed) self.connect('drag-data-received', self.onDragDataReceived) self.connect('button-release-event', self.onButtonReleased) # Show the list self.show() # --== Miscellaneous ==-- def __getIterOnSelectedRows(self): """ Return a list of iterators pointing to the selected rows """ return [self.store.get_iter(path) for path in self.selection.get_selected_rows()[1]] def addColumnAttribute(self, colIndex, renderer, attribute, value): """ Add a new attribute to the given column """ self.get_column(colIndex).add_attribute(renderer, attribute, value) # --== Mark management ==-- def hasMark(self): """ True if a mark has been set """ return self.markedRow is not None def hasMarkAbove(self, index): """ True if a mark is set and is above the given index """ return self.markedRow is not None and self.markedRow > index def hasMarkUnder(self, index): """ True if a mark is set and is undex the given index """ return self.markedRow is not None and self.markedRow < index def clearMark(self): """ Remove the mark """ if self.markedRow is not None: self.setItem(self.markedRow, self.markColumn, False) self.markedRow = None def getMark(self): """ Return the index of the marked row """ return self.markedRow def setMark(self, rowIndex): """ Put the mark on the given row, it will move with the row itself (e.g., D'n'D) """ self.clearMark() self.markedRow = rowIndex self.setItem(rowIndex, self.markColumn, True) def __findMark(self): """ Linear search for the marked row -- To be used only when there's no other solution """ iter = self.store.get_iter_first() while iter is not None: if self.store.get_value(iter, self.markColumn) == True: self.markedRow = self.store.get_path(iter)[0] break iter = self.store.iter_next(iter) # --== Sorting content ==-- def __resetSorting(self): """ Reset sorting such that the next column click will result in an ascending sorting """ if self.sortLastCol is not None: self.sortLastCol.set_sort_indicator(False) self.sortLastCol = None def __cmpRows(self, row1, row2, criteria, ascending): """ Compare two rows based on the given criteria, the latter being a tuple of the indexes to use for the comparison """ # Sorting on the first criterion may be done either ascending or descending criterion = criteria[0] result = cmp(row1[criterion], row2[criterion]) if result != 0: if ascending: return result else: return -result # For subsequent criteria, the order is always ascending for criterion in criteria[1:]: result = cmp(row1[criterion], row2[criterion]) if result != 0: return result return 0 def __sortRows(self, column): """ Sort the rows """ if len(self.store) == 0: return if self.sortLastCol is not None: self.sortLastCol.set_sort_indicator(False) # Find how sorting must be performed if self.sortLastCol == column: self.sortAscending = not self.sortAscending else: self.sortLastCol = column self.sortAscending = True # Dump the rows, sort them, and reorder the list rows = [tuple(r) + (i,) for i, r in enumerate(self.store)] criteria = self.sortColCriteria[column] rows.sort(lambda r1, r2: self.__cmpRows(r1, r2, criteria, self.sortAscending)) self.store.reorder([r[-1] for r in rows]) # Move the mark if needed if self.markedRow is not None: self.__findMark() column.set_sort_indicator(True) if self.sortAscending: column.set_sort_order(gtk.SORT_ASCENDING) else: column.set_sort_order(gtk.SORT_DESCENDING) self.emit('extlistview-modified') # --== Selection ==-- def unselectAll(self): """ Unselect all rows """ self.selection.unselect_all() def selectAll(self): """ Select all rows """ self.selection.select_all() def getSelectedRowsCount(self): """ Return how many rows are currently selected """ return self.selection.count_selected_rows() def getSelectedRows(self): """ Return all selected row(s) """ return [tuple(self.store[path])[:-1] for path in self.selection.get_selected_rows()[1]] def getFirstSelectedRow(self): """ Return only the first selected row """ return tuple(self.store[self.selection.get_selected_rows()[1][0]])[:-1] def getFirstSelectedRowIndex(self): """ Return the index of the first selected row """ return self.selection.get_selected_rows()[1][0][0] def iterSelectedRows(self): """ Iterate on all selected row(s) """ for path in self.selection.get_selected_rows()[1]: yield tuple(self.store[path])[:-1] # --== Retrieving content / Iterating on content ==-- def __len__(self): """ Return how many rows are stored in the list """ return len(self.store) def getCount(self): """ Return how many rows are stored in the list """ return len(self.store) def getRow(self, rowIndex): """ Return the given row """ return tuple(self.store[rowIndex])[:-1] def getAllRows(self): """ Return all rows """ return [tuple(row)[:-1] for row in self.store] def iterAllRows(self): """ Iterate on all rows """ for row in self.store: yield tuple(row)[:-1] def getItem(self, rowIndex, colIndex): """ Return the value of the given item """ return self.store.get_value(self.store.get_iter(rowIndex), colIndex) # --== Adding/removing/modifying content ==-- def clear(self): """ Remove all rows from the list """ self.__resetSorting() self.clearMark() self.store.clear() # This fixes the problem of columns sometimes not resizing correctly self.resize_children() def setItem(self, rowIndex, colIndex, value): """ Change the value of the given item """ # Check if changing that item may change the sorting: if so, reset sorting if self.sortLastCol is not None and colIndex in self.sortColCriteria[self.sortLastCol]: self.__resetSorting() self.store.set_value(self.store.get_iter(rowIndex), colIndex, value) def removeSelectedRows(self): """ Remove the selected row(s) """ self.freeze_child_notify() for iter in self.__getIterOnSelectedRows(): # Move the mark if needed if self.markedRow is not None: currentPath = self.store.get_path(iter)[0] if currentPath < self.markedRow: self.markedRow -= 1 elif currentPath == self.markedRow: self.markedRow = None # Remove the current row if self.store.remove(iter): self.set_cursor(self.store.get_path(iter)) elif len(self.store) != 0: self.set_cursor(len(self.store)-1) self.thaw_child_notify() if len(self.store) == 0: self.set_cursor(0) self.__resetSorting() # This fixes the problem of columns sometimes not resizing correctly self.resize_children() self.emit('extlistview-modified') def cropSelectedRows(self): """ Remove all rows but the selected ones """ pathsList = self.selection.get_selected_rows()[1] self.freeze_child_notify() self.selection.select_all() for path in pathsList: self.selection.unselect_path(path) self.removeSelectedRows() self.selection.select_all() self.thaw_child_notify() def insertRows(self, rows, position=None): """ Insert or append (if position is None) some rows to the list """ if len(rows) == 0: return # Insert the additional column used for the mark management if type(rows[0]) is tuple: rows[:] = [row + (False,) for row in rows] else: rows[:] = [row + [False] for row in rows] # Move the mark if needed if self.markedRow is not None and position is not None and position <= self.markedRow: self.markedRow += len(rows) # Insert rows self.freeze_child_notify() if position is None: for row in rows: self.store.append(row) else: for row in rows: self.store.insert(position, row) position += 1 self.thaw_child_notify() self.__resetSorting() self.emit('extlistview-modified') def appendRows(self, rows): """ Helper function, equivalent to insertRows(rows, None) """ self.insertRows(rows, None) def replaceContent(self, rows): """ Replace the content of the list with the given rows """ self.freeze_child_notify() self.set_model(None) self.clear() self.appendRows(rows) self.set_model(self.store) self.thaw_child_notify() def shuffle(self): import random """ Shuffle the content of the list """ order = xrange(len(self.store)) random.shuffle(order) self.store.reorder(order) # Move the mark if needed if self.markedRow is not None: self.__findMark() self.__resetSorting() self.emit('extlistview-modified') # --== D'n'D management ==-- def enableDNDReordering(self): """ Enable the use of Drag'n'Drop to reorder the list """ self.dndReordering = True self.dndTargets.append(DND_INTERNAL_TARGET) self.enable_model_drag_dest(self.dndTargets, gdk.ACTION_DEFAULT) def __isDropAfter(self, pos): """ Helper function, True if pos is gtk.TREE_VIEW_DROP_AFTER or gtk.TREE_VIEW_DROP_INTO_OR_AFTER """ return pos == gtk.TREE_VIEW_DROP_AFTER or pos == gtk.TREE_VIEW_DROP_INTO_OR_AFTER def __moveSelectedRows(self, x, y): """ Internal function used for drag'n'drop """ iterList = self.__getIterOnSelectedRows() dropInfo = self.get_dest_row_at_pos(int(x), int(y)) if dropInfo is None: pos, path = gtk.TREE_VIEW_DROP_INTO_OR_AFTER, len(self.store) - 1 else: pos, path = dropInfo[1], dropInfo[0][0] if self.__isDropAfter(pos) and path < len(self.store)-1: pos = gtk.TREE_VIEW_DROP_INTO_OR_BEFORE path += 1 self.freeze_child_notify() for srcIter in iterList: srcPath = self.store.get_path(srcIter)[0] if self.__isDropAfter(pos): dstIter = self.store.insert_after(self.store.get_iter(path), self.store[srcIter]) else: dstIter = self.store.insert_before(self.store.get_iter(path), self.store[srcIter]) if path == srcPath: path += 1 self.store.remove(srcIter) dstPath = self.store.get_path(dstIter)[0] if srcPath > dstPath: path += 1 if self.markedRow is not None: if srcPath == self.markedRow: self.markedRow = dstPath elif srcPath < self.markedRow and dstPath >= self.markedRow: self.markedRow -= 1 elif srcPath > self.markedRow and dstPath <= self.markedRow: self.markedRow += 1 self.thaw_child_notify() self.__resetSorting() self.emit('extlistview-modified') # --== GTK Handlers ==-- def onButtonPressed(self, tree, event): """ A mouse button has been pressed """ retVal = False pathInfo = self.get_path_at_pos(int(event.x), int(event.y)) if pathInfo is None: path = None else: path = pathInfo[0] if event.button == 1 or event.button == 3: if path is None: self.selection.unselect_all() tree.set_cursor(len(self.store)) else: if self.dndReordering and self.motionEvtId is None and event.button == 1: self.dndStartPos = (int(event.x), int(event.y)) self.motionEvtId = gtk.TreeView.connect(self, 'motion-notify-event', self.onMouseMotion) stateClear = not (event.state & (gdk.SHIFT_MASK | gdk.CONTROL_MASK)) if stateClear and not self.selection.path_is_selected(path): self.selection.unselect_all() self.selection.select_path(path) else: retVal = (stateClear and self.getSelectedRowsCount() > 1 and self.selection.path_is_selected(path)) self.emit('extlistview-button-pressed', event, path) return retVal def onButtonReleased(self, tree, event): """ A mouse button has been released """ if self.motionEvtId is not None: self.disconnect(self.motionEvtId) self.dndContext = None self.motionEvtId = None if len(self.dndTargets) != 0: self.enable_model_drag_dest(self.dndTargets, gdk.ACTION_DEFAULT) stateClear = not (event.state & (gdk.SHIFT_MASK | gdk.CONTROL_MASK)) if stateClear and event.state & gdk.BUTTON1_MASK and self.getSelectedRowsCount() > 1: pathInfo = self.get_path_at_pos(int(event.x), int(event.y)) if pathInfo is not None: self.selection.unselect_all() self.selection.select_path(pathInfo[0][0]) def onMouseMotion(self, tree, event): """ The mouse has been moved """ if self.dndContext is None and self.drag_check_threshold(self.dndStartPos[0], self.dndStartPos[1], int(event.x), int(event.y)): self.dndContext = self.drag_begin([DND_INTERNAL_TARGET], gdk.ACTION_COPY, 1, event) def onDragBegin(self, tree, context): """ A drag'n'drop operation has begun """ if self.getSelectedRowsCount() == 1: context.set_icon_stock(gtk.STOCK_DND, 0, 0) else: context.set_icon_stock(gtk.STOCK_DND_MULTIPLE, 0, 0) def onDragDataReceived(self, tree, context, x, y, selection, dndId, time): """ Some data has been dropped into the list """ if dndId == DND_REORDERING_ID: self.__moveSelectedRows(x, y) else: self.emit('extlistview-dnd', context, int(x), int(y), selection, dndId, time) def onDragMotion(self, tree, context, x, y, time): """ Prevent rows from being dragged *into* other rows (this is a list, not a tree) """ drop = self.get_dest_row_at_pos(int(x), int(y)) if drop is not None and (drop[1] == gtk.TREE_VIEW_DROP_INTO_OR_AFTER or drop[1] == gtk.TREE_VIEW_DROP_INTO_OR_BEFORE): self.enable_model_drag_dest([('invalid-position', 0, -1)], gdk.ACTION_DEFAULT) else: self.enable_model_drag_dest(self.dndTargets, gdk.ACTION_DEFAULT) def onColumnHeaderClicked(self, column, event): """ A column header has been clicked """ if event.button == 3: # Create a menu with a CheckMenuItem per column menu = gtk.Menu() nbVisibleItems = 0 lastVisibleItem = None for column in self.get_columns(): item = gtk.CheckMenuItem(column.get_title()) item.set_active(column.get_visible()) item.connect('toggled', self.onShowHideColumn, column) item.show() menu.append(item) # Count how many columns are visible if item.get_active(): nbVisibleItems += 1 lastVisibleItem = item # Don't allow the user to hide the only visible column left if nbVisibleItems == 1: lastVisibleItem.set_sensitive(False) menu.popup(None, None, None, event.button, event.get_time()) def onShowHideColumn(self, menuItem, column): """ Switch the visibility of the given column """ column.set_visible(not column.get_visible()) self.emit('extlistview-column-visibility-changed', column.get_title(), column.get_visible()) agtl-0.8.0.3/files/advancedcaching/fieldnotesuploader.py000066400000000000000000000070351151564747700232630ustar00rootroot00000000000000#!/usr/bin/python # -*- coding: utf-8 -*- # Copyright (C) 2010 Daniel Fett # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # Author: Daniel Fett agtl@danielfett.de # Jabber: fett.daniel@jaber.ccc.de # Bugtracker and GIT Repository: http://github.com/webhamster/advancedcaching # VERSION = 3 VERSION_DATE = '2010-07-03' import geocaching import re import gobject import logging logger = logging.getLogger('fieldnotesuploader') class FieldnotesUploader(gobject.GObject): __gsignals__ = { 'finished-uploading': (gobject.SIGNAL_RUN_FIRST,\ gobject.TYPE_NONE,\ ()), 'upload-error' : (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)), } #lock = threading.Lock() URL = 'http://www.geocaching.com/my/uploadfieldnotes.aspx' def __init__(self, downloader): gobject.GObject.__init__(self) self.downloader = downloader self.notes = [] def add_fieldnote(self, geocache): if geocache.logdate == '': raise Exception("Illegal Date.") if geocache.logas == geocaching.GeocacheCoordinate.LOG_AS_FOUND: log = "Found it" elif geocache.logas == geocaching.GeocacheCoordinate.LOG_AS_NOTFOUND: log = "Didn't find it" elif geocache.logas == geocaching.GeocacheCoordinate.LOG_AS_NOTE: log = "Write note" else: raise Exception("Illegal status: %s" % geocache.logas) text = geocache.fieldnotes.replace('"', "'") self.notes.append('%s,%sT10:00Z,%s,"%s"' % (geocache.name, geocache.logdate, log, text)) def upload(self): try: logger.info("Uploading fieldnotes...") page = self.downloader.get_reader(self.URL).read() m = re.search('', page) if m == None: raise Exception("Could not download fieldnotes page.") viewstate = m.group(1) text = "\r\n".join(self.notes).encode("UTF-16") response = self.downloader.get_reader(self.URL, data=self.downloader.encode_multipart_formdata( [('ctl00$ContentBody$btnUpload', 'Upload Field Note'), ('ctl00$ContentBody$chkSuppressDate', ''), ('__VIEWSTATE', viewstate)], [('ctl00$ContentBody$FieldNoteLoader', 'geocache_visits.txt', text)] )) res = response.read() if not "successfully uploaded" in res: raise Exception("Something went wrong while uploading the field notes.") else: self.emit('finished-uploading') logger.info("Finished upload") except Exception, e: self.emit('upload-error', e) agtl-0.8.0.3/files/advancedcaching/geo.py000066400000000000000000000251721151564747700201470ustar00rootroot00000000000000#!/usr/bin/python # -*- coding: utf-8 -*- # Copyright (C) 2010 Daniel Fett # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # Author: Daniel Fett agtl@danielfett.de # Jabber: fett.daniel@jaber.ccc.de # Bugtracker and GIT Repository: http://github.com/webhamster/advancedcaching # import math import re try: from location import distance_between def distance_to_liblocation(src, target): return distance_between(src.lat, src.lon, target.lat, target.lon) * 1000 distance_to = distance_to_liblocation except Exception: def distance_to_manual (src, target): dlat = math.pow(math.sin(math.radians(target.lat-src.lat) / 2), 2) dlon = math.pow(math.sin(math.radians(target.lon-src.lon) / 2), 2) a = dlat + math.cos(math.radians(src.lat)) * math.cos(math.radians(target.lat)) * dlon; c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a)); return Coordinate.RADIUS_EARTH * c; distance_to = distance_to_manual def try_parse_coordinate(text): text = text.strip() # got some problems with the degree symbol in regexes. text = text.replace('°', ' ') # 1 2 3 4 5 6 7 8 match = re.match(ur'''(?i)^([NS+-]?)\s?(\d\d?\d?)[ °]{0,2}(\d\d?\d?)[., ](\d+)['\s,]+([EOW+-]?)\s?(\d{1,3})[ °]{0,2}(\d\d?\d?)[., ](\d+)?[\s']*$''', text) if match != None: c = Coordinate(0, 0) if match.group(1) in 'sS-': sign_lat = -1 else: sign_lat = 1 if match.group(5) in 'wW-': sign_lon = -1 else: sign_lon = 1 c.from_dm(sign_lat * int(match.group(2)), sign_lat * float("%s.%s" % (match.group(3), match.group(4))), sign_lon * int(match.group(6)), sign_lon * float("%s.%s" % (match.group(7), match.group(8))) ) return c # 1 2 3 4 5 6 match = re.match(ur'''(?i)^([NS+-]?)\s?(\d\d?)[., ](\d+)[°']?[\s,]+([EOW+-]?)\s?(\d{1,3})[., ](\d+)['°]?\s*$''', text) if match != None: c = Coordinate(0, 0) if match.group(1) in 'sS-': sign_lat = -1 else: sign_lat = 1 if match.group(4) in 'wW-': sign_lon = -1 else: sign_lon = 1 # not using math magic here: this is more error-free :-) c.lat = sign_lat * float("%s.%s" % (match.group(2), match.group(3))) c.lon = sign_lon * float("%s.%s" % (match.group(5), match.group(6))) return c raise Exception("Could not parse this input as a coordinate: '%s'\nExample Input: N49 44.111 E6 12.123" % text) def search_coordinates(text): text = text.strip() # got some problems with the degree symbol in regexes. text = text.replace('°', ' ') # output = [] matches = re.finditer(ur'''(?i)([NS+-]?)\s?(\d\d?\d?)[ °]{1,2}(\d\d?\d?)[., ](\d+)['\s,]+([EOW+-]?)\s?(\d{1,3})[ °]{1,2}(\d\d?\d?)[., ](\d+)?[\s']*''', text) for match in matches: c = Coordinate(0, 0) if match.group(1) in 'sS-': sign_lat = -1 else: sign_lat = 1 if match.group(5) in 'wW-': sign_lon = -1 else: sign_lon = 1 c.from_dm(sign_lat * int(match.group(2)), sign_lat * float("%s.%s" % (match.group(3), match.group(4))), sign_lon * int(match.group(6)), sign_lon * float("%s.%s" % (match.group(7), match.group(8))) ) output.append(c) # 1 2 3 4 5 6 matches = re.finditer(ur'''(?i)([NS+-]?)\s?(\d\d?)[.,](\d+)[°']?[\s,]+([EOW+-]?)\s?(\d{1,3})[.,](\d+)['°]?\s*''', text) for match in matches: c = Coordinate(0, 0) if match.group(1) in 'sS-': sign_lat = -1 else: sign_lat = 1 if match.group(4) in 'wW-': sign_lon = -1 else: sign_lon = 1 # not using math magic here: this is more error-free :-) c.lat = sign_lat * float("%s.%s" % (match.group(2), match.group(3))) c.lon = sign_lon * float("%s.%s" % (match.group(5), match.group(6))) output.append(c) return output class Coordinate(object): SQLROW = {'lat': 'REAL', 'lon': 'REAL', 'name': 'TEXT'} RADIUS_EARTH = 6371000.0 FORMAT_D = 0 FORMAT_DM = 1 re_to_dm_array = re.compile('^(\d?)(\d)(\d) (\d)(\d)\.(\d)(\d)(\d)$') re_to_d_array = re.compile('^(\d?)(\d)(\d).(\d)(\d)(\d)(\d)(\d)$') def __init__(self, lat, lon, name="No Name"): self.lat = lat self.lon = lon self.name = name def from_d(self, lat, lon): self.lat = lat self.lon = lon def from_dm(self, latdd, latmm, londd, lonmm): self.lat = latdd + (latmm / 60) self.lon = londd + (lonmm / 60) def from_dm_array(self, sign_lat, lat, sign_lon, lon): lat += [0, 0, 0, 0, 0, 0] lon += [0, 0, 0, 0, 0, 0, 0] self.from_dm(sign_lat * (lat[0] * 10 + lat[1]), sign_lat * float(str(lat[2]) + str(lat[3]) + "." + str(lat[4]) + str(lat[5]) + str(lat[6])), sign_lon * (lon[0] * 100 + lon[1] * 10 + lon[2]), sign_lon * float(str(lon[3]) + str(lon[4]) + "." + str(lon[5]) + str(lon[6]) + str(lon[7]))) def from_d_array(self, sign_lat, lat, sign_lon, lon): self.lat = int(sign_lat) * float("%d%d.%d%d%d%d%d" % tuple(lat)) self.lon = int(sign_lon) * float("%d%d%d.%d%d%d%d%d" % tuple(lon)) def to_dm_array(self): [[lat_d, lat_m], [lon_d, lon_m]] = self.to_dm() d_lat = self.re_to_dm_array.search("%02d %06.3f" % (abs(lat_d), abs(lat_m))) d_lon = self.re_to_dm_array.search("%03d %06.3f" % (abs(lon_d), abs(lon_m))) return [ [d_lat.group(i) for i in xrange (2, 9)], [d_lon.group(i) for i in xrange (1, 9)] ] def to_d_array(self): d_lat = self.re_to_d_array.search("%08.5f" % abs(self.lat)) d_lon = self.re_to_d_array.search("%09.5f" % abs(self.lon)) return [ [d_lat.group(i) for i in xrange (2, 7)], [d_lon.group(i) for i in xrange (1, 7)] ] def to_dm(self): lat = abs(self.lat) lon = abs(self.lon) return [[int(math.floor(lat)), (lat - math.floor(lat)) * 60], [int(math.floor(lon)), (lon - math.floor(lon)) * 60]] def bearing_to(self, target): lat1 = math.radians(self.lat) lat2 = math.radians(target.lat) #lon1 = math.radians(self.lon) #lon2 = math.radians(target.lon) dlon = math.radians(target.lon - self.lon); y = math.sin(dlon) * math.cos(lat2) x = math.cos(lat1) * math.sin(lat2) - math.sin(lat1) * math.cos(lat2) * math.cos(dlon) bearing = math.degrees(math.atan2(y, x)) return (360 + bearing) % 360 def transform(self, bearing, distance): # expect distance in meters and bearing in degrees rlat1 = math.radians(self.lat) rlon1 = math.radians(self.lon) rbearing = math.radians(bearing) rdistance = distance / self.RADIUS_EARTH # normalize linear distance to radian angle rlat = math.asin( math.sin(rlat1) * math.cos(rdistance) + math.cos(rlat1) * math.sin(rdistance) * math.cos(rbearing) ) if math.cos(rlat) == 0 or abs(math.cos(rlat)) < 0.00001: # Endpoint a pole rlon=rlon1 else: rlon = ( (rlon1 - math.asin( math.sin(rbearing)* math.sin(rdistance) / math.cos(rlat) ) + math.pi ) % (2*math.pi) ) - math.pi lat = math.degrees(rlat) lon = math.degrees(rlon) return Coordinate(lat, lon, self.name) def get_lat(self, format): l = abs(self.lat) if self.lat > 0: c = 'N' else: c = 'S' if format == self.FORMAT_D: return "%s %.5f°" % (c, l) elif format == self.FORMAT_DM: return "%s %d° %06.3f'" % (c, math.floor(l), (l - math.floor(l)) * 60) def get_lon(self, format): l = abs(self.lon) if self.lon > 0: c = 'E' else: c = 'W' if format == self.FORMAT_D: return "%s %.5f°" % (c, l) elif format == self.FORMAT_DM: return "%s %d° %06.3f'" % (c, math.floor(l), (l - math.floor(l)) * 60) def get_latlon(self, format = 1): # that is FORMAT_DM return "%s %s" % (self.get_lat(format), self.get_lon(format)) def __str__(self): return self.get_latlon() def serialize(self): return {'lat': self.lat, 'lon': self.lon, 'name': self.name} def unserialize(self, data): self.lat = data['lat'] self.lon = data['lon'] self.name = data['name'] def distance_to(self, target): return distance_to(self, target) @staticmethod def format_distance(distance): if distance == None: return '?' if distance >= 1000: return "%d km" % round(distance / 1000.0) elif distance >= 100: return "%d m" % round(distance) else: return "%.1f m" % round(distance, 1) @staticmethod def format_direction(angle): directions = ['N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE', 'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW', 'N'] return directions[int(round(((angle+360.0) % 360.0)/22.5))] @staticmethod def get_bounds(coord_list): min_lat = coord_list[0].lat max_lat = coord_list[0].lat min_lon = coord_list[0].lon max_lon = coord_list[0].lon for x in coord_list: min_lat = min(min_lat, x.lat) max_lat = max(max_lat, x.lat) min_lon = min(min_lon, x.lon) max_lon = max(max_lon, x.lon) return min_lat, max_lat, min_lon, max_lon agtl-0.8.0.3/files/advancedcaching/geocaching.py000066400000000000000000000344161151564747700214650ustar00rootroot00000000000000#!/usr/bin/python # -*- coding: utf-8 -*- # Copyright (C) 2010 Daniel Fett # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # Author: Daniel Fett agtl@danielfett.de # Jabber: fett.daniel@jaber.ccc.de # Bugtracker and GIT Repository: http://github.com/webhamster/advancedcaching # try: from simplejson import dumps, loads except (ImportError, AttributeError): from json import loads, dumps from datetime import datetime import logging import time import geo logger = logging.getLogger('geocaching') class GeocacheCoordinate(geo.Coordinate): LOG_NO_LOG = 0 LOG_AS_FOUND = 1 LOG_AS_NOTFOUND = 2 LOG_AS_NOTE = 3 TYPE_REGULAR = 'regular' TYPE_MULTI = 'multi' TYPE_VIRTUAL = 'virtual' TYPE_EVENT = 'event' TYPE_MYSTERY = 'mystery' TYPE_WEBCAM = 'webcam' TYPE_UNKNOWN = 'unknown' TYPE_EARTH = 'earth' TYPES = [ TYPE_REGULAR, TYPE_MULTI, TYPE_VIRTUAL, TYPE_EVENT, TYPE_MYSTERY, TYPE_WEBCAM, TYPE_UNKNOWN, TYPE_EARTH ] STATUS_NORMAL = 0 STATUS_DISABLED = 1 STATUS_ARCHIVED = 2 STATUS_TEXT = ['normal', 'not available!'] LOG_TYPE_FOUND = 'smile' LOG_TYPE_NOTFOUND = 'sad' LOG_TYPE_NOTE = 'note' LOG_TYPE_MAINTENANCE = 'maint' LOG_TYPE_PUBLISHED = 'greenlight' LOG_TYPE_DISABLED = 'disabled' LOG_TYPE_NEEDS_MAINTENANCE = 'needsmaint' LOG_TYPE_WILLATTEND = 'rsvp' LOG_TYPE_ATTENDED = 'attended' LOG_TYPE_UPDATE = 'coord_update' SIZES = ['other', 'micro', 'small', 'regular', 'big', 'other'] TYPE_MAPPING = { TYPE_MULTI: 'Multi-cache', TYPE_REGULAR: 'Traditional Cache', TYPE_EARTH: 'Earthcache', TYPE_UNKNOWN: 'Unknown Cache', TYPE_EVENT: 'Event Cache', TYPE_WEBCAM: 'Webcam Cache', TYPE_VIRTUAL: 'Virtual Cache' } USER_TYPE_COORDINATE = 0 USER_TYPE_CALC_STRING = 1 USER_TYPE_CALC_STRING_OVERRIDE = 2 ATTRS = ('lat', 'lon', 'title', 'name', 'shortdesc', 'desc', 'hints', 'type', \ 'size', 'difficulty', 'terrain', 'owner', 'found', 'waypoints', \ 'images', 'notes', 'fieldnotes', 'logas', 'logdate', 'marked', \ 'logs', 'status', 'vars', 'alter_lat', 'alter_lon', 'updated', 'user_coordinates') SQLROW = { 'lat': 'REAL', 'lon': 'REAL', 'name': 'TEXT PRIMARY KEY', 'title': 'TEXT', 'shortdesc': 'TEXT', 'desc': 'TEXT', 'hints': 'TEXT', 'type': 'TEXT', 'size': 'INTEGER', 'difficulty': 'INTEGER', 'terrain': 'INTEGER', 'owner': 'TEXT', 'found': 'INTEGER', 'waypoints': 'text', 'images': 'text', 'notes': 'TEXT', 'fieldnotes': 'TEXT', 'logas': 'INTEGER', 'logdate': 'TEXT', 'marked': 'INTEGER', 'logs': 'TEXT', 'status': 'INTEGER', 'vars': 'TEXT', 'alter_lat': 'REAL', 'alter_lon': 'REAL', 'updated' : 'INTEGER', 'user_coordinates' : 'TEXT', } def __init__(self, lat, lon=None, name='', data=None): geo.Coordinate.__init__(self, lat, lon, name) if data != None: self.unserialize(data) self.calc = None return self.calc = None # NAME = GC-ID self.title = '' self.shortdesc = '' self.desc = '' self.hints = '' self.type = '' # regular, multi, virtual, webcam,... self.size = -1 self.difficulty = -1 self.terrain = -1 self.owner = '' self.found = False self.waypoints = '' self.images = '' self.notes = '' self.fieldnotes = '' self.logas = self.LOG_NO_LOG self.logdate = '' self.marked = False self.logs = '' self.status = self.STATUS_NORMAL self.vars = '' self.alter_lat = 0 self.alter_lon = 0 self.updated = 0 self.user_coordinates = '' def clone(self): n = GeocacheCoordinate(self.lat, self.lon) for k in self.ATTRS: setattr(n, k, getattr(self, k)) return n def updated(self): self.updated = int(time.mktime(datetime.now().timetuple())) def get_updated(self): return datetime.fromtimestamp(self.updated) def get_difficulty(self): return "%.1f" % (self.difficulty / 10.0) if self.difficulty != -1 else '?' def get_terrain(self): return "%.1f" % (self.terrain / 10.0) if self.difficulty != -1 else '?' def get_status(self): return self.STATUS_TEXT[self.status] if self.status != None else '' def serialize(self): ret = {} for key in self.ATTRS: ret[key] = self.serialize_one(key) return ret def serialize_one(self, attribute): if attribute == 'found': return 1 if self.found else 0 elif attribute == 'marked': return 1 if self.marked else 0 elif attribute == 'vars': return dumps(self.calc.get_vars()) if self.calc != None else '' elif attribute == 'user_coordinates': try: return dumps(self.saved_user_coordinates) except AttributeError: return self.user_coordinates elif attribute == 'waypoints': try: return dumps(self.saved_waypoints) except AttributeError: return self.waypoints else: return getattr(self, attribute) def unserialize(self, data): ret = {} for key in self.ATTRS: ret[key] = data[key] if ret['notes'] == None: self.notes = '' if ret['fieldnotes'] == None: self.fieldnotes = '' if ret['logs'] == None: self.logs = '' if ret['vars'] == None: self.vars = '' ret['found'] = (ret['found'] == 1) self.__dict__ = ret def get_waypoints(self): try: return self.saved_waypoints except (AttributeError): if self.waypoints in (None, '{}', ''): self.saved_waypoints = [] else: self.saved_waypoints = loads(self.waypoints) return self.saved_waypoints def get_user_coordinates(self, ctype): try: self.saved_user_coordinates except (AttributeError): if self.user_coordinates in (None, '{}', ''): self.saved_user_coordinates = [] else: self.saved_user_coordinates = loads(self.user_coordinates) if type(self.saved_user_coordinates) != list: logger.debug('Creating new list!') self.saved_user_coordinates = [] return [(id, point) for id, point in zip(range(len(self.saved_user_coordinates)), self.saved_user_coordinates) if point['type'] == ctype] def get_user_coordinate(self, id): try: return self.saved_user_coordinates[id] except AttributeError: logger.exception("Call get_user_coordinates first!") except KeyError: raise Exception("No user coordinate with id %d" % id) def get_logs(self): if self.logs == None or self.logs == '': return [] return loads(self.logs) def get_images(self): if self.images == None or self.images == '': return [] try: return self.saved_images except (AttributeError): self.saved_images = loads(self.images) return self.saved_images def set_waypoints(self, wps): self.saved_waypoints = wps def set_logs(self, ls): self.logs = dumps(ls) def set_images(self, imgs): self.images = dumps(imgs) try: del self.saved_images except: pass def was_downloaded(self): return (self.shortdesc != '' or self.desc != '') def get_bounds(self): minlat = maxlat = self.lat minlon = maxlon = self.lon for wpt in self.get_waypoints(): if wpt['lat'] != -1 and wpt['lon'] != -1: minlat = min(minlat, wpt['lat']) maxlat = max(maxlat, wpt['lat']) minlon = min(minlon, wpt['lon']) maxlon = max(maxlon, wpt['lon']) return {'minlat': "%.5f" % minlat, 'maxlat': "%.5f" % maxlat, 'minlon': "%.5f" % minlon, 'maxlon': "%.5f" % maxlon} def get_size_string(self): if self.size == -1: return '?' else: return self.SIZES[self.size] def get_gs_type(self): if self.TYPE_MAPPING.has_key(self.type): return self.TYPE_MAPPING[self.type] else: return self.TYPE_MAPPING[self.TYPE_UNKNOWN] def set_alternative_position(self, coord): self.alter_lat = coord.lat self.alter_lon = coord.lon def start_calc(self, stripped_desc): from coordfinder import CalcCoordinateManager if self.vars == None or self.vars == '': vars = {} else: vars = loads(self.vars) self.calc = CalcCoordinateManager(vars) self.calc.add_text(stripped_desc, 'Description') for id, local in self.get_user_coordinates(self.USER_TYPE_CALC_STRING_OVERRIDE): signature, replacement_text = local['value'] self.calc.add_replacement(signature, replacement_text, id) for id, local in self.get_user_coordinates(self.USER_TYPE_CALC_STRING): self.calc.add_text(local['value'], id) for w in self.get_waypoints(): self.calc.add_text(w['comment'], "Waypoint %s" % w['name']) self.calc.update() def set_user_coordinate(self, type, value, name, id = None): d = {'value': value, 'type' : type, 'name' : name} try: if id == None: new_id = len(self.saved_user_coordinates) self.saved_user_coordinates.append(d) return new_id else: self.saved_user_coordinates[id] = d return id except AttributeError: raise Exception("get_user_coordinates has to be called first!") def delete_user_coordinate(self, id): try: del self.saved_user_coordinates[id] except AttributeError: raise Exception("get_user_coordinates has to be called first!") def get_collected_coordinates(self, format, include_unknown = True, htmlcallback = lambda x: x, shorten_callback = lambda x: x): cache = self cache.display_text = "Geocache: %s" % cache.get_latlon(format) cache.comment = "Original coordinate given in the cache description." cache.user_coordinate_id = None clist = {0: cache} i = 1 # waypoints for w in self.get_waypoints(): if not (w['lat'] == -1 and w['lon'] == -1): coord = geo.Coordinate(w['lat'], w['lon'], w['name']) coord.comment = htmlcallback(w['comment']) latlon = coord.get_latlon(format) elif not include_unknown: continue else: coord = geo.Coordinate(None, None, w['name']) coord.comment = htmlcallback(w['comment']) latlon = '???' coord.user_coordinate_id = None coord.display_text = "%s - %s - %s\n%s" % (w['name'], latlon, w['id'], shorten_callback(htmlcallback(w['comment']))) clist[i] = coord i += 1 # read from local user_coordinates for id, local in self.get_user_coordinates(self.USER_TYPE_COORDINATE): coord = geo.Coordinate(* local['value']) text = local['name'] if local['name'] != '' else 'manually entered' coord.display_text = "%s: %s" % (text, coord.get_latlon(format)) coord.comment = "This coordinate was manually entered." coord.user_coordinate_id = id clist[i] = coord i += 1 # cache calc if self.calc != None: for coord, source in self.calc.get_solutions(): if coord == False: continue if type(source) == int: source_string = self.get_user_coordinate(source)['name'] coord.user_coordinate_id = source else: source_string = source coord.user_coordinate_id = None coord.display_text = "%s: %s = %s" % (source_string, coord.name, coord.get_latlon(format)) coord.comment = "From %s:\n%s = %s" % (source_string, coord.name, coord.get_latlon(format)) clist[i] = coord i += 1 for coord, source in self.calc.get_plain_coordinates(): if coord == False: continue if type(source) == int: source_string = self.get_user_coordinate(source)['name'] coord.user_coordinate_id = source else: source_string = source coord.user_coordinate_id = None coord.display_text = "%s: %s" % (source_string, coord.get_latlon(format)) coord.comment = "Found in %s." % source_string clist[i] = coord i += 1 # parsed from notes for coord in geo.search_coordinates(self.notes): coord.display_text = "from notes: %s" % coord.get_latlon(format) coord.comment = "This coordinate was manually entered in the notes field." coord.user_coordinate_id = None clist[i] = coord i += 1 return clist agtl-0.8.0.3/files/advancedcaching/geonames.py000066400000000000000000000122721151564747700211700ustar00rootroot00000000000000#!/usr/bin/python # -*- coding: utf-8 -*- # Copyright (C) 2010 Daniel Fett # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # Author: Daniel Fett agtl@danielfett.de # Jabber: fett.daniel@jaber.ccc.de # Bugtracker and GIT Repository: http://github.com/webhamster/advancedcaching # from urllib import quote import geo try: import json json.dumps except (ImportError, AttributeError): import simplejson as json import logging logger = logging.getLogger('geonames') class Geonames(): URL = '''http://ws.geonames.org/searchJSON?formatted=true&q=%(query)s&maxRows=%(max_rows)d&style=short''' URL_STREETS = 'http://ws.geonames.org/findNearestIntersectionOSMJSON?formatted=true&lat=%f&lng=%f&style=short' ORS_URL = 'http://data.giub.uni-bonn.de/openrouteservice/php/OpenLSRS_DetermineRoute.php' ORS_DATA = 'Start=%f,%f&End=%f,%f&Via=&lang=de&distunit=KM&routepref=Fastest&avoidAreas=&useTMC=false&noMotorways=false&noTollways=false&instructions=true' MAX_NODES = 1000 DIST_FACTOR = 1.3 def __init__(self, downloader): self.downloader = downloader @staticmethod def my_quote(input): if isinstance(input, unicode): return quote(input.encode('utf-8')) return quote(input) def search(self, search, nearest_street=False): logger.info("Trying to search geonames for %s" % search) page = self.downloader.get_reader(url=self.URL % {'query': self.my_quote(search), 'max_rows': 1}, login=False).read() values = json.loads(page) if int(values['totalResultsCount']) == 0: raise Exception('No Record found for query "%s"' % search) res = values['geonames'][0] c = geo.Coordinate(float(res['lat']), float(res['lng']), search) logger.info("Using %s for query '%s'" % (c, search)) return c def search_all(self, search, max_results=15, name_string="%(name)s, %(countryCode)s"): logger.info("Trying to search geonames for %s" % search) page = self.downloader.get_reader(url=self.URL % {'query': self.my_quote(search), 'max_rows': max_results}, login=False).read() logger.debug("Result:\n%s\n" % page) values = json.loads(page) return [geo.Coordinate(float(res['lat']), float(res['lng']), name_string % res) for res in values['geonames'] if 'countryCode' in res] def find_nearest_intersection(self, c): logger.info("trying to find nearest street...") url = self.URL_STREETS % (c.lat, c.lon) page = self.downloader.get_reader(url, login=False).read() values = json.loads(page) if (len(values) == 0): logger.warning("Could NOT find nearest intersection to %s, using this" % c) return c intersection = values['intersection'] c = geo.Coordinate(float(intersection['lat']), float(intersection['lng'])) logger.info("Using nearest intersection at %s" % c) return c def find_route(self, c1, c2, min_distance): page = self.downloader.get_reader(url=self.ORS_URL, values=self.ORS_DATA % (c1.lon, c1.lat, c2.lon, c2.lat), login=False).read() import xml.dom.minidom from xml.dom.minidom import Node doc = xml.dom.minidom.parseString(page) # @type doc xml.dom.minidom.Document errors = doc.getElementsByTagName('xls:Error') if len(errors) > 0: if errors[0].getAttribute('locationPath') == 'PathFinder - getPath()': raise Exception("Could not find route. Please try another street as start or endpoint. The server said: ''%s''\n" % errors[0].getAttribute('message')) raise Exception("Could not find route. The server said: ''%s''\n" % errors[0].getAttribute('message')) segments = doc.getElementsByTagName('gml:LineString') route_points = [] # min_distance is in km, we need m mdist = (min_distance * 1000.0) / self.DIST_FACTOR for s in segments: for p in s.childNodes: if p.nodeType != Node.ELEMENT_NODE: continue lon, tmp, lat = p.childNodes[0].data.partition(' ') c = geo.Coordinate(float(lat), float(lon)) stop = False for o in route_points: if c.distance_to(o) < mdist: stop = True break if not stop: route_points.append(c) if len(route_points) > self.MAX_NODES: raise Exception("Too many waypoints! Try a bigger radius.") logger.info("Using the following Waypoints:") return route_points agtl-0.8.0.3/files/advancedcaching/gpsreader.py000066400000000000000000000337151151564747700213530ustar00rootroot00000000000000#!/usr/bin/python # -*- coding: utf-8 -*- # Copyright (C) 2010 Daniel Fett # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # Author: Daniel Fett agtl@danielfett.de # Jabber: fett.daniel@jaber.ccc.de # Bugtracker and GIT Repository: http://github.com/webhamster/advancedcaching # import geo from socket import socket, AF_INET, SOCK_STREAM from datetime import datetime import logging logger = logging.getLogger('gpsreader') try: import location except (ImportError): logger.warning("If you're on maemo, please install python-location") class Fix(): BEARING_HOLD_EPD = 90 # arbitrary, yet non-random value last_bearing = 0 # tracking the minimum difference between a received fix time and # our current internal time. min_timediff = datetime.utcnow() - datetime.utcfromtimestamp(0) def __init__(self, position = None, altitude = None, bearing = None, speed = None, sats = 0, sats_known = 0, dgps = False, quality = 0, error = 0, error_bearing = 0, timestamp = None): self.position = position self.altitude = altitude self.bearing = bearing self.speed = speed self.sats = sats self.sats_known = sats_known self.dgps = dgps self.quality = quality self.error = error self.error_bearing = error_bearing if timestamp == None: self.timestamp = datetime.utcnow() else: self.timestamp = timestamp class GpsReader(): BEARING_HOLD_SPEED = 0.62 # meters per second. empirical value. QUALITY_LOW_BOUND = 5.0 # meters of HDOP. DGPS_ADVANTAGE = 1 # see below for usage PORT = 2947 HOST = '127.0.0.1' EMPTY = Fix() def __init__(self): logger.info("Using GPSD gps reader on port %d host %s" % (self.PORT, self.HOST)) self.status = "connecting..." self.connected = False self.last_bearing = 0 # enable this to track speeds and see the max speed # self.speeds = [] def connect(self): try: self.gpsd_connection = socket(AF_INET, SOCK_STREAM) self.gpsd_connection.connect((self.HOST, self.PORT)) self.gpsd_connection.setblocking(False) self.status = "connected" self.connected = True except: text = "Could not connect to GPSD!" logger.warning(text) self.status = text self.connected = False def get_data(self): try: if not self.connected: self.connect() if not self.connected: return self.EMPTY self.gpsd_connection.send("%s\r\n" % 'o') data = self.gpsd_connection.recv(512) self.gpsd_connection.send("%s\r\n" % 'y') quality_data = self.gpsd_connection.recv(512) # 1: Parse Quality Data # example output: # GPSD,Y=- 1243847265.000 10:32 3 105 0 0:2 36 303 20 0:16 9 65 26 # 1:13 87 259 35 1:4 60 251 30 1:23 54 60 37 1:25 51 149 24 0:8 2 # 188 0 0:7 33 168 24 1:20 26 110 28 1: if quality_data.strip() == "GPSD,Y=?": sats = 0 sats_known = 0 dgps = False else: sats = 0 dgps = False groups = quality_data.split(':') sats_known = int(groups[0].split(' ')[2]) for i in xrange(1, sats_known): sat_data = groups[i].split(' ') if sat_data[4] == "1": sats = sats + 1 if int(sat_data[0]) > 32: dgps = True if data.strip() == "GPSD,O=?": self.status = "No GPS signal" return Fix(sats = sats, sats_known = sats_known, dgps = dgps) # 2: Get current position, altitude, bearing and speed # example output: # GPSD,O=- 1243530779.000 ? 49.736876 6.686998 271.49 1.20 1.61 49.8566 0.050 -0.175 ? ? ? 3 # GPSD,O=- 1251325613.000 ? 49.734453 6.686360 ? 10.55 ? 180.1476 1.350 ? ? ? ? 2 # that means: # [tag, timestamp, time_error, lat, lon, alt, err_hor, err_vert, track, speed, delta_alt, err_track, err_speed, err_delta_alt, mode] # 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 # or # GPSD,O=? try: splitted = data.split(' ') lat, lon, alt, err_hor = splitted[3:7] track, speed = splitted[8:10] err_track = splitted[11] time = datetime.utcfromtimestamp(int(float(splitted[1]))) except: logger.info("GPSD Output: \n%s\n -- cannot be parsed." % data) self.status = "Could not read GPSD output." return Fix() alt = self.to_float(alt) track = self.to_float(track) speed = self.to_float(speed) err_hor = self.to_float(err_hor) err_track = self.to_float(err_track) # the following is probably wrong: # # it seems that gpsd doesn't take into account that the # receiver may get signals from space base augmentation systems # like egnos. therefore, we estimate that the error is about # self.DGPS_ADVANTAGE meters lower. this is a complete guess. if dgps: err_hor -= self.DGPS_ADVANTAGE if err_hor <= 0: quality = 1 elif err_hor > self.QUALITY_LOW_BOUND: quality = 0 else: quality = 1-err_hor/self.QUALITY_LOW_BOUND # enable this to track speeds and see the max speed #self.speeds.append(speed) #print "Aktuell %f, max: %f" % (speed, max(self.speeds)) return Fix( position =geo.Coordinate(float(lat), float(lon)), altitude = alt, bearing = track, speed = speed, sats = int(sats), sats_known = sats_known, dgps = dgps, quality = quality, error = err_hor, error_bearing = err_track, timestamp = time ) except Exception, e: logger.exception("Fehler beim Auslesen der Daten: %s " % e) return self.EMPTY @staticmethod def to_float(string): try: return float(string) except: return 0.0 class LocationGpsReader(): TIMEOUT = 5 BEARING_HOLD_SPEED = 2.5 # km/h def __init__(self, cb_error, cb_changed): logger.info("Using liblocation GPS device") control = location.GPSDControl.get_default() device = location.GPSDevice() control.set_properties(preferred_method = location.METHOD_CWP | location.METHOD_ACWP | location.METHOD_GNSS | location.METHOD_AGNSS, preferred_interval=location.INTERVAL_1S) control.connect("error-verbose", cb_error) device.connect("changed", cb_changed) self.last_gps_bearing = 0 self.control = control self.device = device def start(self): self.control.start() return False @staticmethod def get_error_from_code(error): if error == location.ERROR_USER_REJECTED_DIALOG: return "Requested GPS method not enabled" elif error == location.ERROR_USER_REJECTED_SETTINGS: return "Location disabled due to change in settings" elif error == location.ERROR_BT_GPS_NOT_AVAILABLE: return "Problems with BT GPS" elif error == location.ERROR_METHOD_NOT_ALLOWED_IN_OFFLINE_MODE: return "Requested method is not allowed in offline mode" elif error == location.ERROR_SYSTEM: return "System error" def fix_from_tuple(self, f, device): a = Fix() # check if this is an actual fix if (not f[1] & (location.GPS_DEVICE_LATLONG_SET | location.GPS_DEVICE_ALTITUDE_SET | location.GPS_DEVICE_TRACK_SET)): return a # location independent data a.sats = device.satellites_in_use a.sats_known = device.satellites_in_view a.dgps = False a.quality = 0 # if this fix is too old, discard it if f[2] == f[2]: # is not NaN a.timestamp = datetime.utcfromtimestamp(f[2]) else: a.timestamp = datetime.utcfromtimestamp(0) Fix.min_timediff = min(Fix.min_timediff, datetime.utcnow() - a.timestamp) # if this fix is too old, discard it if ((datetime.utcnow() - a.timestamp) - Fix.min_timediff).seconds > LocationGpsReader.TIMEOUT: logger.info("Discarding fix: Timestamp diff is %d, should not be > %d" % (((datetime.utcnow() - a.timestamp) - Fix.min_timediff).seconds, LocationGpsReader.TIMEOUT)) return a # now on for location dependent data #if f[10] > Fix.BEARING_HOLD_EPD: # a.bearing = Fix.last_bearing #else: a.altitude = f[7] a.speed = f[11] if a.speed > self.BEARING_HOLD_SPEED: a.bearing = f[9] self.last_gps_bearing = a.bearing else: a.bearing = self.last_gps_bearing # Fix.last_bearing = a.bearing a.position = geo.Coordinate(f[4], f[5]) a.error = f[6]/100.0 a.error_bearing = f[10] return a class FakeGpsReader(): INC = 0.0001 def __init__(self, something): self.status = "Fake GPS reader." self.index = -1 self.data = [x.split('\t') for x in self.TESTDATA.split("\n")] self.lastpos = None @staticmethod def get_target(): return geo.Coordinate(50.0000798795372000, 6.9949468020349700) def get_data(self): import random if self.index < len(self.data) - 1: self.index += 1 if self.data[self.index][0] == '0': return Fix() pos = geo.Coordinate(float(self.data[self.index][0]), float(self.data[self.index][1])) if self.lastpos != None: bearing = self.lastpos.bearing_to(pos) else: bearing = 0 self.lastpos = pos return Fix( position = pos, altitude = 5, bearing = bearing, speed = 4, sats = 12, sats_known = 6, dgps = True, quality = 0, error = random.randrange(10, 100), error_bearing = 10 ) TESTDATA = '''0 0 0 0 0 0 50.0000000000000000 7.0000000000000000 49.9999706633389000 7.0001229625195300 49.9997950624675000 7.0003442447632600 49.9997997563332000 7.0004659499973100 49.9997218046337000 7.0005903374403700 49.9995578546077000 7.0006271339952900 49.9994435254484000 7.0008635874837600 49.9993037991226000 7.0009828619659000 49.9992146994919000 7.0010608136653900 49.9991217441857000 7.0012173876166300 49.9990843608975000 7.0012444611638800 49.9990095943213000 7.0015110895037700 49.9988885596395000 7.0016821641475000 0 0 0 0 0 0 49.9987537786365000 7.0018086470663600 49.9985118769109000 7.0020990800112500 49.9983842205256000 7.0021572504192600 49.9982605036348000 7.0022816378623300 49.9980872496963000 7.0023336894810200 49.9979986529797000 7.0024224538356100 49.9979185219854000 7.0025429017841800 49.9978181067854000 7.0025481823831800 49.9976762011647000 7.0025224499404400 49.9975882750005000 7.0024726614356000 49.9974449444562000 7.0023075379431300 49.9973412603140000 7.0022041890770200 49.9972049705684000 7.0021101441234400 0 0 0 0 0 0 49.9970952514559000 7.0020336173474800 49.9969987757504000 7.0019501335918900 49.9968421179801000 7.0017190445214500 49.9967520125210000 7.0016104150563500 49.9966504238546000 7.0015143584460000 49.9965638387948000 7.0014302041381600 49.9964761640877000 7.0013357400894200 49.9963049218059000 7.0011528469622100 49.9962143134326000 7.0009845383465300 49.9961593281478000 7.0008703768253300 49.9960857350379000 7.0007528625428700 49.9960248824209000 7.0006081908941300 49.9959259759635000 7.0004951190203400 49.9958231300116000 7.0003485195338700 49.9957155063748000 7.0002043507993200 49.9956013448536000 7.0000658817589300 49.9954995047301000 6.9999083019793000 49.9954301863909000 6.9997342936694600 49.9954084772617000 6.9995050486177200 49.9953969940543000 6.9992866162210700 49.9965626653284000 6.9970068223774400 49.9966021440923000 6.9968105182051700 49.9968151282519000 6.9966180697083500 49.9971344787627000 6.9964923411607700 49.9972403421998000 6.9964339192956700 49.9973804876208000 6.9963862262666200 49.9979287479073000 6.9956857506185800 49.9980570748448000 6.9955223873257600 49.9982320051640000 6.9954270012676700 49.9984868150204000 6.9951742030680200 49.9985938519239000 6.9949829280376400 49.9986792635173000 6.9948330596089400 49.9987863004208000 6.9947258550673700 49.9990340694785000 6.9947269447147800 49.9992224946618000 6.9946833588182900 49.9994972534478000 6.9947828520089400 49.9996298551559000 6.9948167987167800 49.9997046217322000 6.9948615580797200 49.9997673183680000 6.9949107598513400 49.9999811407179000 6.9948655813932400 50.0000479444861000 6.9948898889124400 50.0000799633563000 6.9948716163635300 50.0000798795372000 6.9949468020349700''' pass agtl-0.8.0.3/files/advancedcaching/gtkmap.py000066400000000000000000000716601151564747700206630ustar00rootroot00000000000000#!/usr/bin/python # -*- coding: utf-8 -*- # Copyright (C) 2010 Daniel Fett # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # Author: Daniel Fett agtl@danielfett.de # Jabber: fett.daniel@jaber.ccc.de # Bugtracker and GIT Repository: http://github.com/webhamster/advancedcaching # import logging import math from abstractmap import AbstractGeocacheLayer from abstractmap import AbstractMap from abstractmap import AbstractMapLayer from abstractmap import AbstractMarksLayer import cairo import geo import geocaching import gobject import gtk import openstreetmap import pango import threadpool logger = logging.getLogger('gtkmap') class Map(gtk.DrawingArea, AbstractMap): MIN_DRAG_REDRAW_DISTANCE = 5 DRAG_RECHECK_SPEED = 20 LAZY_SET_CENTER_DIFFERENCE = 0.1 # * screen (width|height) def __init__(self, center, zoom, tile_loader=None, draggable=True): gtk.DrawingArea.__init__(self) AbstractMap.__init__(self, center, zoom, tile_loader) self.connect("expose_event", self.__expose_event) self.connect("configure_event", self.__configure_event) self.connect("button_press_event", self.__drag_start) self.connect("scroll_event", self.__scroll) self.connect("button_release_event", self.__drag_end) if draggable: self.connect("motion_notify_event", self.__drag) self.set_events(gtk.gdk.EXPOSURE_MASK | gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.BUTTON_RELEASE_MASK | gtk.gdk.POINTER_MOTION_MASK | gtk.gdk.SCROLL) try: gobject.signal_new('tile-loader-changed', Map, gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT, )) gobject.signal_new('map-dragged', Map, gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ()) gobject.signal_new('draw-marks', Map, gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)) except RuntimeError: pass self.surface_buffer = {} self.delay_expose = False self.tile_loader_threadpool = threadpool.ThreadPool(openstreetmap.CONCURRENT_THREADS * 2) #self.ts = openstreetmap.TileServer(self.tile_loader) self.drawing_area_configured = self.drawing_area_arrow_configured = False self.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse("white")) ############################################## # # Map actions # ############################################## def redraw_layers(self): if self.dragging: return self.__draw_layers() self.refresh() def refresh(self): if self.dragging: return self.queue_draw() ############################################## # # Expose & Configure # ############################################## def __expose_event(self, widget, event): if self.dragging or self.delay_expose: return True x, y, width, height = event.area cr = self.cr_drawing_area #cr.clip() #import time #start = time.time() #for i in xrange(50): cr = self.window.cairo_create() cr.rectangle(x, y, width, height) cr.save() cr.clip() cr.set_source_surface(self.cr_drawing_area_map) cr.paint() for l in self.layers: if l.result == None: continue cr.set_source_surface(l.result) cr.paint() cr.restore() #end = time.time() #print end - start #import sys #sys.exit(0) #widget.window.draw_drawable(self.xgc, self.pixmap, x, y, x, y, width, height) return False def __configure_event(self, widget, event): x, y, width, height = widget.get_allocation() self.map_width = int(width + 2 * width * self.MAP_FACTOR) self.map_height = int(height + 2 * height * self.MAP_FACTOR) self.pixmap = gtk.gdk.Pixmap(widget.window, self.map_width, self.map_height) self.cr_marks = cairo.ImageSurface(cairo.FORMAT_ARGB32, self.map_width, self.map_height) self.cr_osd = cairo.ImageSurface(cairo.FORMAT_ARGB32, self.map_width, self.map_height) self.cr_drawing_area_map = cairo.ImageSurface(cairo.FORMAT_ARGB32, self.map_width, self.map_height) self.cr_drawing_area = self.pixmap.cairo_create()#widget.window.cairo_create() self.cr_window = self.window.cairo_create() self.xgc = widget.window.new_gc() self.drawing_area_configured = True self.draw_at_x = 0 self.draw_at_y = 0 self.draw_root_x = int(-width * self.MAP_FACTOR) self.draw_root_y = int(-height * self.MAP_FACTOR) for l in self.layers: l.resize() gobject.idle_add(self._draw_map) ############################################## # # User Input # ############################################## def __scroll(self, widget, event): if event.direction == gtk.gdk.SCROLL_DOWN: self.relative_zoom(-1) else: self.relative_zoom(+ 1) def __drag_start(self, widget, event): try: while True: x = self.active_tile_loaders.pop() x.halt() except IndexError: pass cr = self.cr_drawing_area cr.set_source_surface(self.cr_drawing_area_map) cr.paint() cr.set_source_surface(self.cr_marks) cr.paint() self.window.draw_drawable(self.xgc, self.pixmap, 0, 0, 0, 0, -1, -1) self.drag_start_x = int(event.x) self.drag_start_y = int(event.y) self.drag_offset_x = 0 self.drag_offset_y = 0 self.last_drag_offset_x = 0 self.last_drag_offset_y = 0 self.dragging = True gobject.timeout_add(self.DRAG_RECHECK_SPEED, self.__drag_draw) def __drag(self, widget, event): if not self.dragging: return True self.drag_offset_x = self.drag_start_x - int(event.x) self.drag_offset_y = self.drag_start_y - int(event.y) return True def __drag_draw(self): if not self.dragging: return False delta = math.sqrt((self.last_drag_offset_x - self.drag_offset_x) ** 2 + (self.last_drag_offset_y - self.drag_offset_y) ** 2) if delta < self.MIN_DRAG_REDRAW_DISTANCE: return True self.last_drag_offset_x = self.drag_offset_x self.last_drag_offset_y = self.drag_offset_y x, y, width, height = self.get_allocation() self.window.clear() self.window.draw_drawable(gc=self.xgc, src=self.pixmap, xsrc=0, ysrc=0, xdest=self.draw_at_x - self.drag_offset_x, ydest=self.draw_at_y - self.drag_offset_y, width=width, height=height) return True def __drag_end(self, widget, event): if not self.dragging: return self.dragging = False offset_x = self.drag_offset_x #(self.drag_start_x - event.x) offset_y = self.drag_offset_y #(self.drag_start_y - event.y) self._move_map_relative(offset_x, offset_y) if self._check_click(offset_x, offset_y, event.x, event.y): self.draw_at_x -= offset_x self.draw_at_y -= offset_y else: self.emit('map-dragged') self.draw_at_x = self.draw_at_y = 0 if offset_x != 0 or offset_y != 0: self._draw_map() ############################################## # # Map Drawing # ############################################## def _draw_map(self): if not self.drawing_area_configured: return False if self.map_width == 0 or self.map_height == 0: return try: while True: x = self.active_tile_loaders.pop() x.halt() except IndexError: pass zoom = self.zoom size = self.tile_loader.TILE_SIZE xi = int(self.map_center_x) yi = int(self.map_center_y) span_x = int(math.ceil(float(self.map_width) / (size * 2.0))) span_y = int(math.ceil(float(self.map_height) / (size * 2.0))) offset_x = int(self.map_width / 2 - (self.map_center_x - int(self.map_center_x)) * size) offset_y = int(self.map_height / 2 -(self.map_center_y - int(self.map_center_y)) * size) undersample = self.double_size requests = [] new_surface_buffer = {} old_surface_buffer = self.surface_buffer tiles = [] for i in xrange(-span_x, span_x + 1, 1): for j in xrange(-span_y, span_y + 1, 1): tile = (xi + i, yi + j) dx = i * size + offset_x dy = j * size + offset_y id_string = self.__get_id_string(tile, zoom, undersample) tile = self.check_bounds(*tile) if tile in tiles: continue if id_string in old_surface_buffer and old_surface_buffer[id_string][0] != self.tile_loader.noimage_cantload and old_surface_buffer[id_string][0] != self.tile_loader.noimage_loading: new_surface_buffer[id_string] = old_surface_buffer[id_string] new_surface_buffer[id_string][1:3] = dx, dy else: requests.append(((id_string, tile, zoom, undersample, dx, dy, self._add_to_buffer), {})) self.surface_buffer = new_surface_buffer reqs = threadpool.makeRequests(self.__run_tile_loader, requests) cr = gtk.gdk.CairoContext(cairo.Context(self.cr_drawing_area_map)) cr.set_source_rgba(0, 0, 0, 1) self.delay_expose = True cr.paint() for r in reqs: self.tile_loader_threadpool.putRequest(r) self.__draw_layers() self.__draw_tiles() def __get_id_string(self, tile, display_zoom, undersample): return (self.tile_loader.PREFIX, tile[0], tile[1], display_zoom, 1 if undersample else 0) @staticmethod def _load_tile(filename): surface = cairo.ImageSurface.create_from_png(filename) if surface.get_width() != surface.get_height(): raise Exception("Image too small, probably corrupted file") return surface def __run_tile_loader(self, id_string, tile, zoom, undersample, x, y, callback_draw): d = self.tile_loader(id_string=id_string, tile=tile, zoom=zoom, undersample=undersample, x=x, y=y, callback_draw=callback_draw, callback_load=self._load_tile) self.active_tile_loaders.append(d) d.run() def _add_to_buffer(self, id_string, surface, x, y, scale_source=None): self.surface_buffer[id_string] = [surface, x, y, scale_source] self.__draw_tiles(which=([surface, x, y, scale_source], )) def __draw_tiles(self, which=None, off_x=0, off_y=0): self.delay_expose = False cr = gtk.gdk.CairoContext(cairo.Context(self.cr_drawing_area_map)) if which == None: which = self.surface_buffer.values() for surface, x, y, scale_source in which: if surface == None: print "pbuf was none!" return size = self.tile_loader.TILE_SIZE if scale_source == None: cr.set_source_surface(surface, x + off_x, y + off_y) else: xs, ys = scale_source imgpat = cairo.SurfacePattern(surface) imgpat.set_filter(cairo.FILTER_BEST) scale = cairo.Matrix() scale.translate(xs, ys) scale.scale(0.5, 0.5) scale.translate(-x + off_x, -y + off_y) imgpat.set_matrix(scale) cr.set_source(imgpat) cr.rectangle(max(0, x + off_x), max(0, y + off_y), min(size + x, size, self.map_width - x + size), min(size + y, size, self.map_height - y + size)) cr.fill() self.queue_draw_area(max(0, x + off_x), max(0, y + off_y), min(size + x, size, self.map_width - x + size), min(size + y, size, self.map_height - y + size)) return False ############################################## # # Drawing marks & osd # ############################################## def __draw_layers(self): if not self.drawing_area_configured: return False for l in self.layers: l.draw() class SingleMarkLayer(AbstractMapLayer): COLOR_TARGET = gtk.gdk.color_parse('darkblue') COLOR_TARGET_SHADOW = gtk.gdk.color_parse('white') def __init__(self, coordinate): AbstractMapLayer.__init__(self) self.coord = coordinate def draw(self): surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, self.map.map_width, self.map.map_height) cr = gtk.gdk.CairoContext(cairo.Context(surface)) self.result = surface t = self.map.coord2point(self.coord) if not self.map.point_in_screen(t): return radius_o = 15 radius_i = 3 radius_c = 10 cr.move_to(t[0] - radius_o, t[1]) cr.line_to(t[0] - radius_i, t[1]) cr.move_to(t[0] + radius_o, t[1]) cr.line_to(t[0] + radius_i, t[1]) cr.move_to(t[0], t[1] + radius_o) cr.line_to(t[0], t[1] + radius_i) cr.move_to(t[0], t[1] - radius_o) cr.line_to(t[0], t[1] - radius_i) cr.new_sub_path() cr.arc(t[0], t[1], radius_c, 0, math.pi * 2) cr.set_source_color(self.COLOR_TARGET_SHADOW) cr.set_line_width(3) cr.stroke_preserve() cr.set_source_color(self.COLOR_TARGET) cr.set_line_width(2) cr.stroke() class OsdLayer(AbstractMapLayer): MESSAGE_DRAW_FONT = pango.FontDescription("Sans 5") MESSAGE_DRAW_COLOR = gtk.gdk.color_parse('black') COLOR_OSD_SECONDARY = gtk.gdk.color_parse("white") COLOR_OSD_MAIN = gtk.gdk.color_parse("black") @staticmethod def set_layout(message_draw_font, message_draw_color): OsdLayer.MESSAGE_DRAW_FONT = message_draw_font OsdLayer.MESSAGE_DRAW_COLOR = message_draw_color def draw(self): surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, self.map.map_width, self.map.map_height) cr = gtk.gdk.CairoContext(cairo.Context(surface)) # message if self.map.osd_message != None: cr.set_source_color(self.MESSAGE_DRAW_COLOR) layout = self.map.create_pango_layout(self.map.osd_message) layout.set_font_description(self.MESSAGE_DRAW_FONT) cr.move_to(20, 20) cr.show_layout(layout) # scale bar position = (20, self.map.map_height - 28) center = self.map.get_center() mpp = self.map.get_meters_per_pixel(center.lat) avglength = self.map.map_width / 5 first_length_meters = mpp * avglength final_length_meters = round(first_length_meters, int(-math.floor(math.log(first_length_meters, 10) + 0.00001))) final_length_pixels = final_length_meters / mpp cr.move_to(position[0], position[1] + 10) cr.line_to(position[0] + final_length_pixels, position[1] + 10) cr.set_line_width(5) cr.set_source_color(self.COLOR_OSD_SECONDARY) cr.stroke_preserve() cr.set_line_width(3) cr.set_source_color(self.COLOR_OSD_MAIN) cr.stroke_preserve() cr.set_source_color(self.MESSAGE_DRAW_COLOR) if final_length_meters < 10000: msg = "%d m" % final_length_meters else: msg = "%d km" % (final_length_meters / 1000) layout = self.map.create_pango_layout(msg) layout.set_font_description(self.MESSAGE_DRAW_FONT) cr.move_to(position[0], position[1] - 15) cr.show_layout(layout) self.result = surface logger = logging.getLogger('geocachelayer') class GeocacheLayer(AbstractGeocacheLayer): CACHE_DRAW_FONT = pango.FontDescription("Sans 10") CACHE_DRAW_FONT_COLOR = gtk.gdk.color_parse('black') # map markers colors COLOR_MARKED = gtk.gdk.color_parse('yellow') COLOR_DEFAULT = gtk.gdk.color_parse('blue') COLOR_FOUND = gtk.gdk.color_parse('grey') COLOR_REGULAR = gtk.gdk.color_parse('green') COLOR_MULTI = gtk.gdk.color_parse('orange') COLOR_CACHE_CENTER = gtk.gdk.color_parse('black') COLOR_CURRENT_CACHE = gtk.gdk.color_parse('red') COLOR_WAYPOINTS = gtk.gdk.color_parse('deeppink') def draw(self): surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, self.map.map_width, self.map.map_height) cr = gtk.gdk.CairoContext(cairo.Context(surface)) coords = self.get_geocaches_callback(self.map.get_visible_area(), self.MAX_NUM_RESULTS_SHOW) if self.map.get_zoom() < self.CACHES_ZOOM_LOWER_BOUND: self.map.set_osd_message('Zoom in to see geocaches.') self.visualized_geocaches = [] self.result = surface return elif len(coords) >= self.MAX_NUM_RESULTS_SHOW: self.map.set_osd_message('Too many geocaches to display.') self.visualized_geocaches = [] self.result = surface return self.map.set_osd_message(None) self.visualized_geocaches = coords draw_short = (len(coords) > self.TOO_MANY_POINTS) default_radius = self.CACHE_DRAW_SIZE found, regular, multi, default = self.COLOR_FOUND, self.COLOR_REGULAR, self.COLOR_MULTI, self.COLOR_DEFAULT for c in coords: # for each geocache radius = default_radius if c.found: color = found elif c.type == geocaching.GeocacheCoordinate.TYPE_REGULAR: color = regular elif c.type == geocaching.GeocacheCoordinate.TYPE_MULTI: color = multi else: color = default cr.set_source_color(color) p = self.map.coord2point(c) if c.alter_lat != None and (c.alter_lat != 0 and c.alter_lon != 0): x = self.map.coord2point(geo.Coordinate(c.alter_lat, c.alter_lon)) if x != p: cr.move_to(p[0], p[1]) cr.line_to(x[0], x[1]) cr.set_line_width(2) cr.stroke() if draw_short: radius = radius / 2.0 if c.marked: cr.set_source_rgba(1, 1, 0, 0.5) cr.rectangle(p[0] - radius, p[1] - radius, radius * 2, radius * 2) cr.fill() cr.set_source_color(color) cr.set_line_width(4) cr.rectangle(p[0] - radius, p[1] - radius, radius * 2, radius * 2) cr.stroke() if draw_short: continue # if we have a description for this cache... if c.was_downloaded(): # draw something like: # ---- # ---- # ---- # besides the icon width = 6 dist = 3 count = 3 pos_x = p[0] + radius + 3 + 1 pos_y = p[1] + radius - (dist * count) + dist cr.set_line_width(1) for i in xrange(count): cr.move_to(pos_x, pos_y + dist * i) cr.line_to(pos_x + width, pos_y + dist * i) cr.set_line_width(2) cr.stroke() # if this cache is the active cache if self.current_cache != None and c.name == self.current_cache.name: cr.set_line_width(1) cr.set_source_color(self.COLOR_CURRENT_CACHE) #radius = 7 radius_outline = radius + 3 cr.rectangle(p[0] - radius_outline, p[1] - radius_outline, radius_outline * 2, radius_outline * 2) cr.stroke() # if this cache is disabled if c.status == geocaching.GeocacheCoordinate.STATUS_DISABLED: cr.set_line_width(3) cr.set_source_color(self.COLOR_CURRENT_CACHE) radius_disabled = 7 cr.move_to(p[0]-radius_disabled, p[1]-radius_disabled) cr.line_to(p[0] + radius_disabled, p[1] + radius_disabled) cr.stroke() # print the name? if self.show_name: layout = self.map.create_pango_layout(AbstractGeocacheLayer.shorten_name(c.title, 20)) layout.set_font_description(self.CACHE_DRAW_FONT) width, height = layout.get_pixel_size() cr.move_to(p[0] + 4 + radius, p[1] - height + 2) #cr.set_line_width(1) cr.set_source_color(self.CACHE_DRAW_FONT_COLOR) cr.show_layout(layout) cr.set_source_color(self.COLOR_CACHE_CENTER) cr.set_line_width(1) cr.move_to(p[0], p[1] - 3) cr.line_to(p[0], p[1] + 3) # | cr.move_to(p[0] - 3, p[1],) cr.line_to(p[0] + 3, p[1]) # --- cr.stroke() # draw additional waypoints # --> print description! if self.current_cache != None and self.current_cache.get_waypoints() != None: cr.set_source_color(self.COLOR_WAYPOINTS) cr.set_line_width(1) radius = 5 num = 0 for w in self.current_cache.get_waypoints(): if w['lat'] != -1 and w['lon'] != -1: num = num + 1 p = self.map.coord2point(geo.Coordinate(w['lat'], w['lon'])) if not self.map.point_in_screen(p): continue cr.move_to(p[0], p[1] - radius) cr.line_to(p[0], p[1] + radius) # | #cr.stroke() cr.move_to(p[0] - radius, p[1]) cr.line_to(p[0] + radius, p[1]) # --- #cr.stroke() cr.arc(p[0], p[1], radius, 0, math.pi * 2) layout = self.map.create_pango_layout('') layout.set_markup('%s' % (w['id'])) layout.set_font_description(self.CACHE_DRAW_FONT) cr.move_to(p[0] + 3 + radius, p[1] - 3 - radius) #cr.set_line_width(1) cr.set_source_color(self.COLOR_WAYPOINTS) cr.show_layout(layout) cr.stroke() self.result = surface logger = logging.getLogger('markslayer') class MarksLayer(AbstractMarksLayer): SIZE_CURRENT_POSITION = 3 COLOR_CURRENT_POSITION = gtk.gdk.color_parse('green') COLOR_CURRENT_POSITION_NO_FIX = gtk.gdk.color_parse('red') COLOR_TARGET = gtk.gdk.color_parse('darkblue') COLOR_TARGET_SHADOW = gtk.gdk.color_parse('white') COLOR_CROSSHAIR = gtk.gdk.color_parse("black") COLOR_LINE_INVERT = gtk.gdk.color_parse("blue") COLOR_ACCURACY = gtk.gdk.color_parse("orange") COLOR_DIRECTION_ARROW = gtk.gdk.color_parse("black") COLOR_DIRECTION_ARROW_SHADOW = gtk.gdk.color_parse("white") DISTANCE_DRAW_FONT = pango.FontDescription("Sans 20") DISTANCE_DRAW_FONT_COLOR = gtk.gdk.color_parse("black") OSD_BORDER_TOPBOTTOM = 25 OSD_BORDER_LEFTRIGHT = 35 def __init__(self): AbstractMarksLayer.__init__(self) def draw(self): surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, self.map.map_width, self.map.map_height) cr = gtk.gdk.CairoContext(cairo.Context(surface)) # if we have a target, draw it if self.current_target != None: t = self.map.coord2point(self.current_target) if t != False and self.map.point_in_screen(t): radius_o = 15 radius_i = 3 radius_c = 10 cr.move_to(t[0] - radius_o, t[1]) cr.line_to(t[0] - radius_i, t[1]) cr.move_to(t[0] + radius_o, t[1]) cr.line_to(t[0] + radius_i, t[1]) cr.move_to(t[0], t[1] + radius_o) cr.line_to(t[0], t[1] + radius_i) cr.move_to(t[0], t[1] - radius_o) cr.line_to(t[0], t[1] - radius_i) cr.new_sub_path() cr.arc(t[0], t[1], radius_c, 0, math.pi * 2) cr.set_source_color(self.COLOR_TARGET_SHADOW) cr.set_line_width(3) cr.stroke_preserve() cr.set_source_color(self.COLOR_TARGET) cr.set_line_width(2) cr.stroke() else: t = False if self.gps_last_good_fix != None and self.gps_last_good_fix.position != None: p = self.map.coord2point(self.gps_last_good_fix.position) else: p = None # a line between target and position if we have both if p != None and t != False: cr.set_line_width(5) cr.set_source_rgba(1, 1, 0, 0.5) if self.map.point_in_screen(t) and self.map.point_in_screen(p): cr.move_to(p[0], p[1]) cr.line_to(t[0], t[1]) cr.stroke() elif self.map.point_in_screen(p): direction = math.radians(self.gps_target_bearing - 180) # correct max length: sqrt(width**2 + height**2) length = self.map.map_width cr.move_to(p[0], p[1]) cr.line_to(int(p[0] - math.sin(direction) * length), int(p[1] + math.cos(direction) * length)) cr.stroke() elif self.map.point_in_screen(t): direction = math.radians(self.gps_target_bearing) length = self.map.map_width + self.map.map_height cr.move_to(t[0], t[1]) cr.line_to(int(t[0] - math.sin(direction) * length), int(t[1] + math.cos(direction) * length)) cr.stroke() if p != None and self.map.point_in_screen(p): cr.set_line_width(2) if self.gps_has_fix: radius = self.gps_data.error radius_pixels = radius / self.map.get_meters_per_pixel(self.gps_last_good_fix.position.lat) else: radius_pixels = 10 radius_o = int((radius_pixels + 8) / math.sqrt(2)) radius_i = int((radius_pixels - 8) / math.sqrt(2)) if radius_i < 2: radius_i = 2 if self.gps_has_fix: cr.set_source_color(self.COLOR_CURRENT_POSITION) else: cr.set_source_color(self.COLOR_CURRENT_POSITION_NO_FIX) # \ / # # / \ cr.arc(p[0], p[1], self.SIZE_CURRENT_POSITION, 0, math.pi * 2) cr.fill() if self.gps_has_fix: cr.set_line_width(1) cr.set_source_color(self.COLOR_ACCURACY) cr.set_dash((5, 3)) cr.new_sub_path() cr.arc(p[0], p[1], radius_pixels, 0, math.pi * 2) cr.stroke() cr.set_dash(()) # draw moving direction, if we're moving if self.gps_data.speed > 2.5: # km/h position = p#(self.map.map_width - self.OSD_BORDER_LEFTRIGHT, self.map.map_height - self.OSD_BORDER_TOPBOTTOM) arrow = self._get_arrow_transformed(position[0] - 15, position[1] - 15, 30, 30, self.gps_data.bearing) cr.move_to(* arrow[0]) for x, y in arrow: cr.line_to(x, y) cr.line_to(* arrow[0]) else: cr.arc(p[0], p[1], self.SIZE_CURRENT_POSITION + 5, 0, math.pi * 2) cr.set_source_color(self.COLOR_DIRECTION_ARROW_SHADOW) cr.set_line_width(2) cr.stroke_preserve() cr.set_source_color(self.COLOR_DIRECTION_ARROW) cr.set_line_width(1) cr.stroke() if self.gps_data != None and self.gps_has_fix: position = (self.map.map_width - self.OSD_BORDER_LEFTRIGHT, self.OSD_BORDER_TOPBOTTOM) layout = self.map.create_pango_layout(geo.Coordinate.format_distance(self.gps_target_distance)) layout.set_font_description(self.DISTANCE_DRAW_FONT) width, height = layout.get_pixel_size() cr.set_source_color(self.DISTANCE_DRAW_FONT_COLOR) cr.move_to(position[0] - width, position[1]) cr.show_layout(layout) self.result = surface agtl-0.8.0.3/files/advancedcaching/gui.py000066400000000000000000000016341151564747700201560ustar00rootroot00000000000000#!/usr/bin/python # -*- coding: utf-8 -*- # Copyright (C) 2010 Daniel Fett # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # Author: Daniel Fett agtl@danielfett.de # Jabber: fett.daniel@jaber.ccc.de # Bugtracker and GIT Repository: http://github.com/webhamster/advancedcaching # class Gui(): USES = []agtl-0.8.0.3/files/advancedcaching/hildon_plugins.py000066400000000000000000000743731151564747700224220ustar00rootroot00000000000000#!/usr/bin/python # -*- coding: utf-8 -*- # Copyright (C) 2010 Daniel Fett # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # Author: Daniel Fett agtl@danielfett.de # Jabber: fett.daniel@jaber.ccc.de # Bugtracker and GIT Repository: http://github.com/webhamster/advancedcaching # import geocaching import gtk import hildon import pango import threadpool import logging import geo logger = logging.getLogger('plugins') class HildonSearchPlace(object): def plugin_init(self): self.last_searched_text = '' logger.info("Using Search Place plugin") def _get_search_place_button(self): button = hildon.Button(gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL) button.set_title("Search Place") button.set_value('requires internet') button.connect('clicked', self._on_show_search_place) return button def _on_show_search_place(self, widget): dialog = gtk.Dialog("Search Place", self.window, gtk.DIALOG_DESTROY_WITH_PARENT, (gtk.STOCK_OK, gtk.RESPONSE_ACCEPT)) search = hildon.Entry(gtk.HILDON_SIZE_AUTO) search.set_text(self.last_searched_text) dialog.vbox.pack_start(search) dialog.show_all() result = dialog.run() dialog.hide() search_text = search.get_text().strip() self.last_searched_text = search_text if result != gtk.RESPONSE_ACCEPT or search_text == '': return try: results = self.core.search_place(search_text) except Exception, e: self.show_error(e) return if len(results) == 0: self.show_error("The search returned no results.") return sel = hildon.TouchSelector(text=True) for x in results: sel.append_text(x.name) dlg = hildon.PickerDialog(self.window) dlg.set_selector(sel) dlg.show_all() res = dlg.run() dlg.hide() if res != gtk.RESPONSE_OK: return self.set_center(results[self._get_selected_pos(sel)]) class HildonFieldnotes(object): def plugin_init(self): #self.update_fieldnotes_display() self.core.connect('fieldnotes-changed', self._on_fieldnotes_changed) logger.info("Using Fieldnotes plugin") button = hildon.Button(gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL) button.set_title("Upload Fieldnote(s)") button.set_value("You have not created any fieldnotes.") button.connect("clicked", self._on_upload_fieldnotes, None) self.button_fieldnotes = button def _get_fieldnotes_button(self): self.update_fieldnotes_display() self.button_fieldnotes.unparent() return self.button_fieldnotes def _get_write_fieldnote_button(self): button = hildon.Button(gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL) button.set_label("Write Fieldnote") button.connect("clicked", self._on_show_log_fieldnote_dialog, None) return button def _on_show_log_fieldnote_dialog(self, widget, data): from time import gmtime from time import localtime from time import strftime if self.current_cache == None: return statuses = [ ("Don't upload a fieldnote", geocaching.GeocacheCoordinate.LOG_NO_LOG), ("Found it", geocaching.GeocacheCoordinate.LOG_AS_FOUND), ("Did not find it", geocaching.GeocacheCoordinate.LOG_AS_NOTFOUND), ("Post a note", geocaching.GeocacheCoordinate.LOG_AS_NOTE) ] cache = self.current_cache dialog = gtk.Dialog("Write Fieldnote", self.window, gtk.DIALOG_DESTROY_WITH_PARENT, (gtk.STOCK_OK, gtk.RESPONSE_ACCEPT)) if cache.logdate == '': try: text = strftime(self.settings['options_default_log_text'], localtime()) % {'machine': 'N900'} except ValueError, e: text = self.settings['options_default_log_text'] else: text = cache.fieldnotes fieldnotes = gtk.TextView() #fieldnotes.set_placeholder("Your fieldnote text...") fieldnotes.get_buffer().set_text(text) fieldnotes_log_as_selector = hildon.TouchSelector(text=True) for text, status in statuses: fieldnotes_log_as_selector.append_text(text) i = 0 for text, status in statuses: if cache.logas == status: fieldnotes_log_as_selector.select_iter(0, fieldnotes_log_as_selector.get_model(0).get_iter(i), False) i += 1 fieldnotes_log_as = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_HORIZONTAL) fieldnotes_log_as.set_title('Log Type') fieldnotes_log_as.set_selector(fieldnotes_log_as_selector) dialog.vbox.pack_start(fieldnotes_log_as, False) dialog.vbox.pack_start(fieldnotes, True) #dialog.vbox.pack_start(hildon.Caption(None, "Text", fieldnotes, None, hildon.CAPTION_OPTIONAL)) dialog.show_all() result = dialog.run() dialog.hide() if result != gtk.RESPONSE_ACCEPT: logger.debug('Not logging this fieldnote') return cache.logas = statuses[fieldnotes_log_as_selector.get_selected_rows(0)[0][0]][1] cache.logdate = strftime('%Y-%m-%d', gmtime()) cache.fieldnotes = fieldnotes.get_buffer().get_text(fieldnotes.get_buffer().get_start_iter(), fieldnotes.get_buffer().get_end_iter()) self.core.save_fieldnote(cache) def _on_upload_fieldnotes(self, some, thing): self.core.on_upload_fieldnotes() #emitted by core def _on_fieldnotes_changed(self, core): self.update_fieldnotes_display() def update_fieldnotes_display(self): count = self.core.get_new_fieldnotes_count() w = self.button_fieldnotes if count == 0: w.set_value("Nothing to upload.") w.set_sensitive(False) else: w.set_value("You have %d fieldnotes." % count) w.set_sensitive(True) class HildonSearchGeocaches(object): def plugin_init(self): self.old_search_window = None self.map_filter_active = False logger.info("Using Search plugin") def _get_search_button(self): button1 = hildon.Button(gtk.HILDON_SIZE_AUTO, hildon.BUTTON_ARRANGEMENT_VERTICAL) button1.set_title("Search Geocaches") button1.set_value("in local database") button1.connect("clicked", self._on_show_search, None) return button1 def _on_show_search(self, widget, data): name = hildon.Entry(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT) name.set_placeholder("search for name...") name_hbox = hildon.Caption(None, "Name", name, None, hildon.CAPTION_OPTIONAL) sel_dist_type = hildon.TouchSelector(text=True) sel_dist_type.append_text('anywhere') sel_dist_type.append_text('around my position') sel_dist_type.append_text('around the current map center') pick_dist_type = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_HORIZONTAL) pick_dist_type.set_selector(sel_dist_type) pick_dist_type.set_title("Search") sel_dist_type.select_iter(0, sel_dist_type.get_model(0).get_iter(1), False) list_dist_radius = (1, 5, 10, 20, 50, 100, 200) sel_dist_radius = hildon.TouchSelector(text=True) for x in list_dist_radius: sel_dist_radius.append_text('%d km' % x) pick_dist_radius = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_HORIZONTAL) pick_dist_radius.set_selector(sel_dist_radius) pick_dist_radius.set_title("Radius") pick_dist_type.connect('value-changed', lambda caller: pick_dist_radius.set_sensitive(sel_dist_type.get_selected_rows(0)[0][0] != 0)) sel_dist_radius.select_iter(0, sel_dist_radius.get_model(0).get_iter(1), False) sel_size = hildon.TouchSelector(text=True) sel_size.append_text('micro') sel_size.append_text('small') sel_size.append_text('regular') sel_size.append_text('huge') sel_size.append_text('other') sel_size.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_MULTIPLE) pick_size = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_HORIZONTAL) pick_size.set_selector(sel_size) pick_size.set_title("Select Size(s)") for i in xrange(5): sel_size.select_iter(0, sel_size.get_model(0).get_iter(i), False) sel_type = hildon.TouchSelector(text=True) sel_type.append_text('traditional') sel_type.append_text('multi-stage') sel_type.append_text('virtual') sel_type.append_text('earth') sel_type.append_text('event') sel_type.append_text('mystery') sel_type.append_text('all') sel_type.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_MULTIPLE) pick_type = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_HORIZONTAL) pick_type.set_selector(sel_type) pick_type.set_title("Select Type(s)") sel_type.unselect_all(0) sel_type.select_iter(0, sel_type.get_model(0).get_iter(6), False) sel_status = hildon.TouchSelector(text=True) sel_status.append_text('any') sel_status.append_text("Geocaches I haven't found") sel_status.append_text("Geocaches I have found") sel_status.append_text("Marked Geocaches") sel_status.append_text("Marked Geocaches I haven't found") pick_status = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_HORIZONTAL) pick_status.set_selector(sel_status) pick_status.set_title("Select Status") sel_status.unselect_all(0) sel_status.select_iter(0, sel_status.get_model(0).get_iter(0), False) sel_diff = hildon.TouchSelector(text=True) sel_diff.append_text('1..2.5') sel_diff.append_text('3..4') sel_diff.append_text('4.5..5') sel_diff.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_MULTIPLE) pick_diff = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_HORIZONTAL) pick_diff.set_selector(sel_diff) pick_diff.set_title("Select Difficulty") for i in xrange(3): sel_diff.select_iter(0, sel_diff.get_model(0).get_iter(i), False) sel_terr = hildon.TouchSelector(text=True) sel_terr.append_text('1..2.5') sel_terr.append_text('3..4') sel_terr.append_text('4.5..5') sel_terr.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_MULTIPLE) pick_terr = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_HORIZONTAL) pick_terr.set_selector(sel_terr) pick_terr.set_title("Select Terrain") for i in xrange(3): sel_terr.select_iter(0, sel_terr.get_model(0).get_iter(i), False) RESPONSE_SHOW_LIST, RESPONSE_RESET, RESPONSE_LAST_RESULTS = range(3) dialog = gtk.Dialog("Search", self.window, gtk.DIALOG_DESTROY_WITH_PARENT, ("OK", RESPONSE_SHOW_LIST)) dialog.add_button("Filter Map", gtk.RESPONSE_ACCEPT) if self.map_filter_active: dialog.add_button("Reset Filter", RESPONSE_RESET) if self.old_search_window != None: dialog.add_button("Last Results", RESPONSE_LAST_RESULTS) dialog.set_size_request(800, 800) pan = hildon.PannableArea() options = gtk.VBox() pan.add_with_viewport(options) dialog.vbox.pack_start(pan) options.pack_start(gtk.Label("Search Geocaches")) options.pack_start(name_hbox) options.pack_start(pick_dist_type) options.pack_start(pick_dist_radius) options.pack_start(pick_type) options.pack_start(pick_status) options.pack_start(gtk.Label("Details...")) w = gtk.Label("If you select something here, only geocaches for which details were downloaded will be shown in the result.") w.set_line_wrap(True) w.set_alignment(0, 0.5) options.pack_start(w) options.pack_start(pick_size) options.pack_start(pick_diff) options.pack_start(pick_terr) while True: dialog.show_all() response = dialog.run() dialog.hide() if response == RESPONSE_RESET: self.core.reset_filter() self.map_filter_active = False self.show_success("Showing all geocaches.") return elif response == RESPONSE_LAST_RESULTS: if self.old_search_window == None: return hildon.WindowStack.get_default().push_1(self.old_search_window) return name_search = name.get_text().strip().lower() sizes = [x + 1 for x, in sel_size.get_selected_rows(0)] if sizes == [1, 2, 3, 4, 5]: sizes = None typelist = [ geocaching.GeocacheCoordinate.TYPE_REGULAR, geocaching.GeocacheCoordinate.TYPE_MULTI, geocaching.GeocacheCoordinate.TYPE_VIRTUAL, geocaching.GeocacheCoordinate.TYPE_EARTH, geocaching.GeocacheCoordinate.TYPE_EVENT, geocaching.GeocacheCoordinate.TYPE_MYSTERY, geocaching.GeocacheCoordinate.TYPE_UNKNOWN ] types = [typelist[x] for x, in sel_type.get_selected_rows(0)] if geocaching.GeocacheCoordinate.TYPE_UNKNOWN in types: types = None # found, marked statuslist = [ (None, None), (False, None), (True, None), (None, True), (False, True), ] found, marked = statuslist[sel_status.get_selected_rows(0)[0][0]] numberlist = [ [1, 1.5, 2, 2.5], [3, 3.5, 4], [4.5, 5] ] difficulties = [] count = 0 for x, in sel_diff.get_selected_rows(0): difficulties += numberlist[x] count += 1 if count == len(numberlist): difficulties = None terrains = [] count = 0 for x, in sel_terr.get_selected_rows(0): terrains += numberlist[x] count += 1 if count == len(numberlist): terrains = None center = None dist_type = sel_dist_type.get_selected_rows(0)[0][0] if dist_type == 1: try: center = self.gps_last_good_fix.position except AttributeError: logger.debug("No current Fix.") pass elif dist_type == 2: center = self.map.get_center() if center != None: radius = list_dist_radius[sel_dist_radius.get_selected_rows(0)[0][0]] sqrt_2 = 1.41421356 c1 = center.transform(-45, radius * 1000 * sqrt_2) c2 = center.transform(-45 + 180, radius * 1000 * sqrt_2) location = (c1, c2) else: location = None if response == RESPONSE_SHOW_LIST: points, truncated = self.core.get_points_filter(found=found, name_search=name_search, size=sizes, terrain=terrains, diff=difficulties, ctype=types, marked=marked, location=location) if len(points) > 0: self._display_results(points, truncated) break else: self.show_error("Search returned no geocaches. Please remember that search works only within the downloaded geocaches.") elif response == gtk.RESPONSE_ACCEPT: self.core.set_filter(found=found, name_search=name_search, size=sizes, terrain=terrains, diff=difficulties, ctype=types, marked=marked) self.show_success("Filter for map activated, ignoring distance restrictions.") self.map_filter_active = True break else: break def _display_results(self, caches, truncated): sortfuncs = [ ('Dist', lambda x, y: cmp(x.prox, y.prox)), ('Name', lambda x, y: cmp(x.title, y.title)), ('Diff', lambda x, y: cmp(x.difficulty if x.difficulty > 0 else 100, y.difficulty if y.difficulty > 0 else 100)), ('Terr', lambda x, y: cmp(x.terrain if x.terrain > 0 else 100, y.terrain if y.terrain > 0 else 100)), ('Size', lambda x, y: cmp(x.size if x.size > 0 else 100, y.size if y.size > 0 else 100)), ('Type', lambda x, y: cmp(x.type, y.type)), ] if self.gps_data != None and self.gps_data.position != None: for c in caches: c.prox = c.distance_to(self.gps_data.position) else: for c in caches: c.prox = None win = hildon.StackableWindow() win.set_title("Search results") ls = gtk.ListStore(str, str, str, str, object) tv = hildon.TouchSelector() col1 = tv.append_column(ls, gtk.CellRendererText()) c1cr = gtk.CellRendererText() c1cr.ellipsize = pango.ELLIPSIZE_MIDDLE c2cr = gtk.CellRendererText() c3cr = gtk.CellRendererText() c4cr = gtk.CellRendererText() col1.pack_start(c1cr, True) col1.pack_end(c2cr, False) col1.pack_start(c3cr, False) col1.pack_end(c4cr, False) col1.set_attributes(c1cr, text=0) col1.set_attributes(c2cr, text=1) col1.set_attributes(c3cr, text=2) col1.set_attributes(c4cr, text=3) def select_cache(widget, data, more): self.show_cache(self._get_selected(tv)[4]) tv.connect("changed", select_cache, None) def on_change_sort(widget, sortfunc): tv.handler_block_by_func(select_cache) ls.clear() caches.sort(cmp=sortfunc) for c in caches: ls.append([self.shorten_name(c.title, 40), " " + c.get_size_string(), ' D%s T%s' % (c.get_difficulty(), c.get_terrain()), " " + geo.Coordinate.format_distance(c.prox), c]) tv.handler_unblock_by_func(select_cache) menu = hildon.AppMenu() button = None for name, function in sortfuncs: button = hildon.GtkRadioButton(gtk.HILDON_SIZE_AUTO, button) button.set_label(name) button.connect("clicked", on_change_sort, function) menu.add_filter(button) button.set_mode(False) def download_geocaches(widget): self.core.update_coordinates(caches) button = hildon.Button(gtk.HILDON_SIZE_AUTO, hildon.BUTTON_ARRANGEMENT_VERTICAL) button.set_title("Download Details") button.set_value("for all Geocaches") button.connect("clicked", download_geocaches) menu.append(button) menu.show_all() win.set_app_menu(menu) win.add(tv) on_change_sort(None, sortfuncs[0][1]) win.show_all() if truncated: hildon.hildon_banner_show_information_with_markup(win, "hu", "Showing only the first %d results." % len(caches)) win.connect('delete_event', self.hide_search_view) def hide_search_view(self, widget, data): self.old_search_window = hildon.WindowStack.get_default().pop_1() return True class HildonAboutDialog(object): def plugin_init(self): logger.info("Using About Dialog plugin") def _get_about_button(self): button = hildon.Button(gtk.HILDON_SIZE_AUTO, hildon.BUTTON_ARRANGEMENT_VERTICAL) button.set_title("About AGTL") button.set_value("and online update") button.connect("clicked", self._on_show_about, None) return button def _on_show_about(self, widget, data): (RESPONSE_UPDATE, RESPONSE_HOMEPAGE, RESPONSE_OPTIMIZE) = range(3) dialog = gtk.Dialog("About AGTL", self.window, gtk.DIALOG_DESTROY_WITH_PARENT, ('Update', RESPONSE_UPDATE, 'Website', RESPONSE_HOMEPAGE, 'Optimize', RESPONSE_OPTIMIZE)) dialog.set_size_request(800, 800) notebook = gtk.Notebook() dialog.vbox.pack_start(notebook) page = gtk.VBox() notebook.append_page(page, gtk.Label('About')) copyright = '''Copyright (C) in most parts 2010 Daniel Fett This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/. Author: Daniel Fett advancedcaching@fragcom.de''' additional = '''Neither the author nor the software is affiliated with or endorsed by any geocaching website.''' text = "%s\n\n%s\n\n" % (copyright, additional) l = gtk.Label('') import core l.set_markup("AGTL version %s" % core.VERSION) l.set_alignment(0, 0) page.pack_start(l, False) l = gtk.Label() l.set_line_wrap(True) l.set_alignment(0, 0) l.set_size_request(self.window.size_request()[0] - 10, -1) l.set_markup(text) p = hildon.PannableArea() p.set_property('mov-mode', hildon.MOVEMENT_MODE_BOTH) p.add_with_viewport(l) page.pack_start(p) page = gtk.VBox() notebook.append_page(page, gtk.Label('Update')) l = gtk.Label('') import cachedownloader l.set_markup("Website parser version %d (from %s)\n\nIf you're having trouble downloading geocaches or uploading fieldnotes, try clicking 'update' to fetch the latest website parser.\n\nAlso check the regular maemo updates from time to time." % (cachedownloader.VERSION, cachedownloader.VERSION_DATE)) l.set_alignment(0, 0) l.set_line_wrap(True) page.pack_start(l, False) page = gtk.VBox() notebook.append_page(page, gtk.Label('Files')) sizes = self.core.get_file_sizes() l = gtk.Label('') import cachedownloader l.set_markup("Database Size: %s\nImage Folder Size: %s\n\nClick 'optimize' to purge found geocaches and their images. Be aware that this includes your notes and calculation values for those geocaches." % (self.core.format_file_size(sizes['sqlite']), self.core.format_file_size(sizes['images']))) l.set_alignment(0, 0) l.set_line_wrap(True) page.pack_start(l, False) dialog.show_all() result = dialog.run() if result == RESPONSE_HOMEPAGE: dialog.hide() self._open_browser(None, 'http://www.danielfett.de/') return elif result == RESPONSE_UPDATE: dialog.hide() self._try_parser_update() self._on_show_about(None, None) elif result == RESPONSE_OPTIMIZE: hildon.hildon_gtk_window_set_progress_indicator(dialog, 1) self.core.optimize_data() hildon.hildon_gtk_window_set_progress_indicator(dialog, 0) dialog.hide() self._on_show_about(None, None) def _try_parser_update(self): updates = self.core.try_update() if updates != None: self.show_success("%d modules upgraded. There's no need to restart AGTL." % updates) logger = logging.getLogger('mapdownloader') class HildonDownloadMap(object): SIZE_PER_TILE = 1200 def plugin_init(self): logger.info("Using Map Download plugin") def _get_download_map_button(self): button = hildon.Button(gtk.HILDON_SIZE_AUTO, hildon.BUTTON_ARRANGEMENT_VERTICAL) button.set_title("Download Map") button.set_value("for offline use") button.connect("clicked", self._on_show_download_map, None) return button def _show_tile_select_dialog(self, zoom_steps): sel_zoom = hildon.TouchSelector(text=True) current_zoom = self.map.get_zoom() for zoom, size, count in zoom_steps: sel_zoom.append_text('Zoom %d (Current+%d) ~%s' % (zoom, zoom - current_zoom, self.core.format_file_size(size))) sel_zoom.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_MULTIPLE) pick_zoom = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_HORIZONTAL) pick_zoom.set_selector(sel_zoom) pick_zoom.set_title("Select Zoom Levels") def print_func(widget): size = sum(zoom_steps[x][1] for x, in sel_zoom.get_selected_rows(0)) pick_zoom.set_value('~' + self.core.format_file_size(size)) pick_zoom.connect('value-changed', print_func) pick_zoom.connect('realize', print_func) dialog = gtk.Dialog("Download Map Tiles", self.window, gtk.DIALOG_DESTROY_WITH_PARENT, (gtk.STOCK_OK, gtk.RESPONSE_ACCEPT)) dialog.vbox.pack_start(pick_zoom) dialog.show_all() res = dialog.run() dialog.hide() if res != gtk.RESPONSE_ACCEPT: return [] steps = [zoom_steps[x] for x, in sel_zoom.get_selected_rows(0)] return steps def _on_show_download_map(self, widget, data): current_visible_tiles = self.map.surface_buffer.keys() if len(current_visible_tiles) == 0: return current_zoom = self.map.get_zoom() if current_zoom == self.map.get_max_zoom(): self.show_error("Please zoom out to download tiles") zoom_steps = [] for zoom in xrange(current_zoom + 1, min(self.map.get_max_zoom() + 1, current_zoom + 7)): count = len(current_visible_tiles) * (4 ** (zoom-current_zoom)) size = (count * HildonDownloadMap.SIZE_PER_TILE) zoom_steps.append((zoom, size, count)) active_zoom_steps = self._show_tile_select_dialog(zoom_steps) for zoom, size, count in active_zoom_steps: logger.info("Requesting zoom %d" % zoom) if len(active_zoom_steps) == 0: return zoom_step_keys = [x[0] for x in active_zoom_steps] max_zoom_step = max(zoom_step_keys) todo = sum(x[2] for x in active_zoom_steps) status = {'finished': 0, 'aborted': 0} tile_loader_threadpool = threadpool.ThreadPool(6) requests = [] dialog = gtk.Dialog("Downloading Map Tiles...", self.window, gtk.DIALOG_DESTROY_WITH_PARENT, (gtk.STOCK_OK, gtk.RESPONSE_CANCEL)) hildon.hildon_gtk_window_set_progress_indicator(dialog, 1) pbar = gtk.ProgressBar() dialog.vbox.pack_start(pbar) dialog.show_all() stopped = [False] def cancel(widget, data): stopped[0] = True dialog.connect('response', cancel) pbar.set_text("Preparing download of %d map tiles..." % todo) while gtk.events_pending(): gtk.main_iteration() if stopped[0]: return def add_tiles(source, zoom): if zoom in zoom_step_keys: requests.append(((source, zoom), {})) if zoom + 1 <= max_zoom_step: for add_x in (0, 1): for add_y in (0, 1): tile = (source[0] * 2 + add_x, source[1] * 2 + add_y) add_tiles(tile, zoom + 1) for prefix, tile_x, tile_y, zoom, undersample in current_visible_tiles: add_tiles((tile_x, tile_y), current_zoom) if len(requests) != todo: raise Exception("Something went wrong while calculating the amount of tiles. (%d vs. %d)" % (len(requests), todo)) def download_tile(tile, zoom): tl = self.map.tile_loader(None, tile = tile, zoom = zoom) res = tl.download_tile_only() if res: status['finished'] += 1 else: status['aborted'] += 1 reqs = threadpool.makeRequests(download_tile, requests) i = 0 count = len(reqs) for r in reqs: i += 1 tile_loader_threadpool.putRequest(r) if i % 100 == 0: pbar.set_text("Starting download...") pbar.set_fraction(i/count) while gtk.events_pending(): gtk.main_iteration() if stopped[0]: return import time import threading try: while True: time.sleep(0.5) while gtk.events_pending(): gtk.main_iteration() tile_loader_threadpool.poll() pbar.set_fraction(sum(status.values())/float(todo)) pbar.set_text("%d of %d downloaded (%d errors)" % (sum(status.values()), todo, status['aborted'])) if stopped[0]: tile_loader_threadpool.dismissWorkers break except threadpool.NoResultsPending: logger.info("Downloading finished") except Exception,e: print e self.show_error(e) finally: logger.info("Closing") dialog.hide() agtl-0.8.0.3/files/advancedcaching/hildongui.py000066400000000000000000002047541151564747700213640ustar00rootroot00000000000000#!/usr/bin/python # -*- coding: utf-8 -*- # Copyright (C) 2010 Daniel Fett # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # Author: Daniel Fett agtl@danielfett.de # Jabber: fett.daniel@jaber.ccc.de # Bugtracker and GIT Repository: http://github.com/webhamster/advancedcaching # # deps: python-html python-image python-netclient python-misc python-pygtk python-mime python-json ### For the gui :-) from math import ceil from os import extsep from os import path from os import system import re from astral import Astral import geo import geocaching import gobject import gtk import hildon from cachedownloader import HTMLManipulations from hildon_plugins import HildonFieldnotes from hildon_plugins import HildonSearchPlace from hildon_plugins import HildonSearchGeocaches from hildon_plugins import HildonAboutDialog from hildon_plugins import HildonDownloadMap import pango from portrait import FremantleRotation from simplegui import SimpleGui from simplegui import UpdownRows from xml.sax.saxutils import escape as my_gtk_label_escape from gtkmap import Map, OsdLayer, SingleMarkLayer from coordfinder import CalcCoordinate import logging logger = logging.getLogger('simplegui') class HildonGui(HildonSearchPlace, HildonFieldnotes, HildonSearchGeocaches, HildonAboutDialog, HildonDownloadMap, SimpleGui): USES = ['locationgpsprovider', 'geonames'] MIN_DRAG_REDRAW_DISTANCE = 2 DRAG_RECHECK_SPEED = 40 # arrow colors and sizes COLOR_ARROW_DISABLED = gtk.gdk.color_parse("red") COLOR_ARROW_CIRCLE = gtk.gdk.color_parse("lightslategrey") COLOR_CROSSHAIR = gtk.gdk.color_parse("deeppink") COLOR_ARROW_OUTER_LINE = gtk.gdk.color_parse("white") COLOR_QUALITY_OUTER = None ARROW_LINE_WIDTH = 2 NORTH_INDICATOR_SIZE = 30 FONT_NORTH_INDICATOR = pango.FontDescription("Sans 19") CLICK_RADIUS = 25 TOO_MANY_POINTS = 80 MAX_NUM_RESULTS_SHOW = 80 CACHES_ZOOM_LOWER_BOUND = 8 ICONS = { geocaching.GeocacheCoordinate.LOG_TYPE_FOUND: 'emoticon_grin', geocaching.GeocacheCoordinate.LOG_TYPE_NOTFOUND: 'cross', geocaching.GeocacheCoordinate.LOG_TYPE_NOTE: 'comment', geocaching.GeocacheCoordinate.LOG_TYPE_MAINTENANCE: 'wrench', geocaching.GeocacheCoordinate.LOG_TYPE_PUBLISHED: 'accept', geocaching.GeocacheCoordinate.LOG_TYPE_DISABLED: 'delete', geocaching.GeocacheCoordinate.LOG_TYPE_NEEDS_MAINTENANCE: 'error', geocaching.GeocacheCoordinate.LOG_TYPE_WILLATTEND: 'calendar_edit', geocaching.GeocacheCoordinate.LOG_TYPE_ATTENDED: 'group', geocaching.GeocacheCoordinate.LOG_TYPE_UPDATE: 'asterisk_yellow', } ICONPATH='/usr/share/icons/hicolor/%(size)dx%(size)d/hildon/%(name)s.png' CACHE_DRAW_SIZE = 13 CACHE_DRAW_FONT = pango.FontDescription("Nokia Sans Maps 10") def __init__(self, core, dataroot): gtk.gdk.threads_init() self._prepare_images(dataroot) self.core = core self.core.connect('map-marks-changed', self._on_map_changed) self.core.connect('cache-changed', self._on_cache_changed) self.core.connect('target-changed', self._on_target_changed) self.core.connect('good-fix', self._on_good_fix) self.core.connect('no-fix', self._on_no_fix) self.core.connect('settings-changed', self._on_settings_changed) self.core.connect('save-settings', self._on_save_settings) self.core.connect('error', lambda caller, message: self.show_error(message)) self.core.connect('progress', lambda caller, fraction, text: self.set_progress(fraction, text)) self.core.connect('hide-progress', lambda caller: self.hide_progress()) self.settings = {} self.format = geo.Coordinate.FORMAT_DM Map.set_config(self.core.settings['map_providers'], self.core.settings['download_map_path'], self.noimage_cantload, self.noimage_loading) OsdLayer.set_layout(pango.FontDescription("Nokia Sans Maps 13"), gtk.gdk.color_parse('black')) self.current_cache = None self.current_cache_window_open = False self.gps_data = None self.gps_has_fix = False self.gps_last_good_fix = None self.gps_last_screen_position = (0, 0) self.banner = None self.old_cache_window = None self.cache_calc_vars = {} self.north_indicator_layout = None self.notes_changed = False gtk.set_application_name("Geocaching Tool") program = hildon.Program.get_instance() self.window = hildon.StackableWindow() program.add_window(self.window) self.window.connect("delete_event", self.on_window_destroy, None) self.window.add(self._create_main_view()) self.window.set_app_menu(self._create_main_menu()) gtk.link_button_set_uri_hook(self._open_browser) self.rotation_manager = FremantleRotation('advancedcaching', main_window = self.window) self.astral = Astral() self.plugin_init() def plugin_init(self): for x in (HildonSearchGeocaches, HildonSearchPlace, HildonFieldnotes): x.plugin_init(self) def on_window_destroy(self, target, more=None, data=None): hildon.hildon_gtk_window_take_screenshot(self.window, True) SimpleGui.on_window_destroy(self, target, more, data) def _prepare_images(self, dataroot): p = "%s%s%%s" % (path.join(dataroot, '%s'), extsep) self.noimage_cantload = p % ('noimage-cantload', 'png') self.noimage_loading = p % ('noimage-loading', 'png') #MarksLayer.load_image_target(p % ('target', 'png')) out = {} for key, name in self.ICONS.items(): out[key] = gtk.gdk.pixbuf_new_from_file(p % (name, 'png')) self.icon_pixbufs = out self.image_icon_add = self.ICONPATH % {'size' : 64, 'name':'general_add'} self.image_zoom_in = gtk.image_new_from_file(self.ICONPATH % {'size' : 48, 'name':'pdf_zoomin'}) self.image_zoom_out = gtk.image_new_from_file(self.ICONPATH % {'size' : 48, 'name':'pdf_zoomout'}) self.image_action = gtk.image_new_from_file(self.ICONPATH % {'size' : 64, 'name':'keyboard_menu'}) self.image_preferences = gtk.image_new_from_file(self.ICONPATH % {'size' : 48, 'name':'camera_camera_setting'}) self.image_info = gtk.image_new_from_file(self.ICONPATH % {'size' : 48, 'name':'general_information'}) self.image_left = self.ICONPATH % {'size' : 48, 'name':'general_back'} self.image_right = self.ICONPATH % {'size' : 48, 'name':'general_forward'} def _open_browser(self, widget, link): system("dbus-send --print-reply --dest=com.nokia.osso_browser /com/nokia/osso_browser/request com.nokia.osso_browser.open_new_window 'string:%s' &" % link) def show_coordinate_input(self, start, none_on_cancel = False, show_name_input = False): udr = UpdownRows(self.format, start, True) dialog = gtk.Dialog("Edit Target", self.window, gtk.DIALOG_MODAL, ("OK", gtk.RESPONSE_ACCEPT)) if show_name_input: e = hildon.Entry(gtk.HILDON_SIZE_AUTO) dialog.vbox.pack_start(e) e.set_text(start.name) dialog.set_size_request(-1, 480) dialog.vbox.pack_start(udr.table_lat) dialog.vbox.pack_start(udr.table_lon) dialog.show_all() res = dialog.run() dialog.destroy() if res == gtk.RESPONSE_ACCEPT: c = udr.get_value() if show_name_input: c.name = e.get_text() else: c.name = 'manual' return c elif none_on_cancel: return None else: return start def _create_main_view(self): root = gtk.VBox() self.main_gpspage = gtk.VBox() self.main_gpspage_table = gtk.Table(7, 3) self.drawing_area_arrow = gtk.DrawingArea() self.label_dist = gtk.Label() self.label_dist.set_markup("") self.label_dist.set_alignment(0, 0) self.label_bearing = gtk.Label() self.label_bearing.set_markup("") self.label_bearing.set_alignment(0, 0) self.label_altitude = gtk.Label() self.label_altitude.set_markup("") self.label_altitude.set_alignment(0, 0) self.label_latlon = gtk.Label() self.label_latlon.set_markup("") self.label_latlon.set_alignment(0, 0) self.label_quality = gtk.Label() self.label_quality.set_markup("") self.label_quality.set_alignment(0, 0) button = hildon.Button(gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL) button.set_title("Target") button.set_value('none set') button.connect('clicked', self._on_show_dialog_change_target, None) button.set_size_request(270, -1) self.label_target = button #buttons = gtk.HBox() button_details = hildon.Button(gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL) button_details.set_title("Details") button_details.set_sensitive(False) button_details.connect("clicked", self._on_show_cache_details, None) #buttons.pack_start(button, True, True) self.button_show_details_small = button_details button_map = hildon.Button(gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL) button_map.set_label("Map") button_map.connect("clicked", self._on_set_active_page, False) #buttons.pack_start(button, True, True) self.main_gpspage_table.attach(self.label_dist, 1, 3, 1, 2, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND) self.main_gpspage_table.attach(self.label_altitude, 1, 2, 2, 3, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND) self.main_gpspage_table.attach(self.label_bearing, 2, 3, 2, 3, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND) self.main_gpspage_table.attach(self.label_latlon, 1, 3, 3, 4, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND) self.main_gpspage_table.attach(self.label_quality, 1, 3, 4, 5, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND) self.main_gpspage_table.attach(self.label_target, 1, 3, 5, 6, gtk.FILL | gtk.EXPAND, 0) self.main_gpspage_table.attach(button_details, 1, 2, 6, 7, gtk.FILL | gtk.EXPAND, 0) self.main_gpspage_table.attach(button_map, 2, 3, 6, 7, gtk.FILL | gtk.EXPAND, 0) self.main_gpspage_table.attach(self.drawing_area_arrow, 0, 1, 0, 7, gtk.EXPAND | gtk.FILL, gtk.EXPAND | gtk.FILL) def reorder_gps(widget, event): widget = self.main_gpspage portrait = (event.width < event.height) x = self.drawing_area_arrow.get_parent() if x != None: x.remove(self.drawing_area_arrow) x = self.main_gpspage_table.get_parent() if x != None: x.remove(self.main_gpspage_table) for x in widget.get_children(): widget.remove(x) if portrait: widget.pack_start(self.drawing_area_arrow, True) widget.pack_start(self.main_gpspage_table, False) else: landscape_hbox = gtk.HBox(True) landscape_hbox.pack_start(self.drawing_area_arrow, True) landscape_hbox.pack_start(self.main_gpspage_table, True) widget.pack_start(landscape_hbox) landscape_hbox.show() self.window.connect('configure-event', reorder_gps) #self.main_gpspage.add_events(gtk.gdk.STRUCTURE_MASK) self.main_gpspage.pack_start(self.main_gpspage_table, False, True) self.main_mappage = gtk.VBox() self._configure_map() buttons = gtk.HBox() button = hildon.GtkButton(gtk.HILDON_SIZE_FINGER_HEIGHT) button.set_image(self.image_action) button.connect("clicked", self._on_show_actions_clicked, None) buttons.pack_start(button, True, True) button = hildon.GtkButton(gtk.HILDON_SIZE_FINGER_HEIGHT) button.set_image(self.image_preferences) button.connect("clicked", self._on_show_preferences, None) buttons.pack_start(button, True, True) button = hildon.GtkButton(gtk.HILDON_SIZE_FINGER_HEIGHT) button.set_image(self.image_zoom_in) button.connect("clicked", self.on_zoomin_clicked, None) self.button_zoom_in = button buttons.pack_start(button, True, True) button = hildon.GtkButton(gtk.HILDON_SIZE_FINGER_HEIGHT) button.set_image(self.image_zoom_out) button.connect("clicked", self.on_zoomout_clicked, None) self.button_zoom_out = button buttons.pack_start(button, True, True) button = hildon.Button(gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL) button.set_title("Show Details") button.set_value('No Cache selected') button.set_sensitive(False) button.connect("clicked", self._on_show_cache_details, None) buttons.pack_start(button, True, True) self.button_show_details = button button_replace = hildon.Button(gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL) button_replace.set_image(self.image_info) button_replace.connect("clicked", self._on_show_cache_details, None) buttons.pack_start(button_replace, True, True) def reorder_main(widget, event): portrait = (event.width < event.height) if not portrait: button_replace.hide() self.button_show_details.show() buttons.set_homogeneous(False) else: button_replace.show() self.button_show_details.hide() buttons.set_homogeneous(True) self.window.connect('configure-event', reorder_main) button = hildon.GtkButton(gtk.HILDON_SIZE_FINGER_HEIGHT) button.set_label("GPS") button.connect("clicked", self._on_set_active_page, True) buttons.pack_start(button, True, True) #self.main_mappage.pack_start(self.drawing_area, True) pan = hildon.PannableArea() pan.add(self.map) self.main_mappage.pack_start(pan, True) self.main_mappage.pack_start(buttons, False) root.pack_start(self.main_gpspage, True) root.pack_start(self.main_mappage, True) # arrow drawing area self.drawing_area_arrow.connect("expose_event", self._expose_event_arrow) self.drawing_area_arrow.connect("configure_event", self._configure_event_arrow) self.drawing_area_arrow.set_events(gtk.gdk.EXPOSURE_MASK) return root def _create_main_menu(self): menu = hildon.AppMenu() sel_tiles = hildon.TouchSelector(text=True) for name, loader in self.map.tile_loaders: sel_tiles.append_text(name) pick_tiles = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL) pick_tiles.set_selector(sel_tiles) pick_tiles.set_title("Map Style") pick_tiles.set_active(0) pick_tiles.connect('value-changed', lambda widget: self.map.set_tile_loader(self.map.tile_loaders[widget.get_active()][1])) menu.append(pick_tiles) menu.append(self._get_search_place_button()) menu.append(self._get_search_button()) menu.append(self._get_about_button()) menu.append(self._get_download_map_button()) menu.show_all() return menu def _on_show_actions_clicked(self, caller, event): dialog = gtk.Dialog("Actions Menu", self.window, gtk.DIALOG_MODAL, ()) buttons = [] if self.core.current_position != None: button = hildon.Button(gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL) button.set_title("Go to my Position") button.set_value(self.core.current_position.get_latlon(self.format)) button.connect("clicked", lambda caller: self.set_center(self.core.current_position)) button.connect("clicked", lambda caller: dialog.hide()) else: button = hildon.Button(gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL) button.set_title("Go to my Position") button.set_value('Not available') button.set_sensitive(False) buttons.append(button) button = hildon.Button(gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL) button.set_title("Go to Target") button.set_value(self.core.current_target.get_latlon(self.format)) button.connect("clicked", self.on_show_target_clicked, None) button.connect("clicked", lambda caller: dialog.hide()) buttons.append(button) c = self.map.get_center() button = hildon.Button(gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL) button.set_title("Use Center as Target") button.set_value(c.get_latlon(self.format)) button.connect("clicked", self.on_set_target_center, None) button.connect("clicked", lambda caller: dialog.hide()) buttons.append(button) self.button_center_as_target = button button = self._get_fieldnotes_button() button.connect("clicked", lambda caller: dialog.hide()) buttons.append(button) button = hildon.Button(gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL) button.set_title("Download Overview") button.set_value("for the visible area") button.connect("clicked", self.on_download_clicked) button.connect("clicked", lambda caller: dialog.hide()) buttons.append(button) button = hildon.Button(gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL) button.set_title("Download Details") button.set_value("for all visible caches") button.connect("clicked", self.on_download_details_map_clicked) button.connect("clicked", lambda caller: dialog.hide()) buttons.append(button) self.make_rearranging_table(buttons, dialog) dialog.show_all() dialog.run() dialog.hide() def _on_show_preferences(self, caller, event): dialog = gtk.Dialog("Quick Settings", self.window, gtk.DIALOG_MODAL, ()) buttons = [] button = hildon.CheckButton(gtk.HILDON_SIZE_FINGER_HEIGHT) button.set_label("Follow Position") button.set_active(self._get_track_mode()) button.connect("clicked", self.on_track_toggled, None) button_track = button buttons.append(button) check_map_double_size = hildon.CheckButton(gtk.HILDON_SIZE_FINGER_HEIGHT) check_map_double_size.set_label("Show Map in double size") check_map_double_size.set_active(self.settings['options_map_double_size']) buttons.append(check_map_double_size) tts_button, tts_get_result = self._get_tts_settings() buttons.append(tts_button) rotate_button, rotate_get_result = self._get_rotate_settings() buttons.append(rotate_button) check_hide_found = hildon.CheckButton(gtk.HILDON_SIZE_FINGER_HEIGHT) check_hide_found.set_label("Hide Found Geocaches") check_hide_found.set_active(self.settings['options_hide_found']) buttons.append(check_hide_found) button = hildon.Button(gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL) button.set_label("more Settings") button.connect("clicked", self._on_show_settings_dialog, None) button.connect("clicked", lambda caller: dialog.hide()) buttons.append(button) self.make_rearranging_table(buttons, dialog) dialog.show_all() dialog.run() dialog.hide() logger.debug("Setting 'Hide Found Geocaches' to %s" % check_hide_found.get_active()) update = { 'tts_interval': tts_get_result(), 'options_rotate_screen': rotate_get_result(), 'options_map_double_size': check_map_double_size.get_active(), 'options_hide_found': check_hide_found.get_active(), } self.settings.update(update) self.core.save_settings(update, self) def make_rearranging_table(self, elements, dialog, columns = 2): count = len(elements) container = gtk.VBox() def reorder_table(widget, event): portrait = (event.width < event.height) if portrait: real_cols = 1 else: real_cols = columns for table in container.get_children(): for x in table.get_children(): table.remove(x) container.remove(table) table = gtk.Table(int(ceil(count/float(real_cols))), real_cols, True) i = 0 for x in elements: table.attach(x, i % real_cols, i % real_cols + 1, i//real_cols, i//real_cols + 1) i += 1 container.pack_start(table, False) container.show_all() id = self.window.connect('configure-event', reorder_table) dialog.connect('hide', lambda widget: self.window.disconnect(id)) dialog.vbox.pack_start(container) reorder_table(None, self.window.get_allocation()) def show(self): self.window.show_all() self.set_active_page(False) gtk.main() def _on_show_settings_dialog(self, widget, data): dialog = gtk.Dialog("Settings", self.window, gtk.DIALOG_DESTROY_WITH_PARENT, (gtk.STOCK_OK, gtk.RESPONSE_ACCEPT)) dialog.set_size_request(800, 480) p = hildon.PannableArea() list = gtk.VBox() p.add_with_viewport(list) dialog.vbox.pack_start(p, True) c_size = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL) username = hildon.Entry(gtk.HILDON_SIZE_AUTO) username.set_text(self.settings['options_username']) list.pack_start(hildon.Caption(c_size, "Username", username, None, hildon.CAPTION_MANDATORY)) password = hildon.Entry(gtk.HILDON_SIZE_AUTO) password.set_visibility(False) password.set_text(self.settings['options_password']) list.pack_start(hildon.Caption(c_size, "Password", password, None, hildon.CAPTION_MANDATORY)) list.pack_start(gtk.Label('Display')) check_show_cache_id = hildon.CheckButton(gtk.HILDON_SIZE_FINGER_HEIGHT) check_show_cache_id.set_label("Show Geocache Name on Map") check_show_cache_id.set_active(self.settings['options_show_name']) list.pack_start(check_show_cache_id) #check_map_double_size = hildon.CheckButton(gtk.HILDON_SIZE_FINGER_HEIGHT) #check_map_double_size.set_label("Show Map in double size") #check_map_double_size.set_active(self.settings['options_map_double_size']) #list.pack_start(check_map_double_size) #check_hide_found = hildon.CheckButton(gtk.HILDON_SIZE_FINGER_HEIGHT) #check_hide_found.set_label("Hide Found Geocaches on Map") #check_hide_found.set_active(self.settings['options_hide_found']) #list.pack_start(check_hide_found) check_show_html_description = hildon.CheckButton(gtk.HILDON_SIZE_FINGER_HEIGHT) check_show_html_description.set_label("Show Cache Description with HTML") check_show_html_description.set_active(self.settings['options_show_html_description']) list.pack_start(check_show_html_description) #rotate_button, rotate_get_result = self._get_rotate_settings() #list.pack_start(rotate_button) #list.pack_start(gtk.Label('TTS Settings')) #tts_button, tts_get_result = self._get_tts_settings() #list.pack_start(tts_button) list.pack_start(gtk.Label('Other')) check_dl_images = hildon.CheckButton(gtk.HILDON_SIZE_FINGER_HEIGHT) check_dl_images.set_label("Don't Download Images") check_dl_images.set_active(self.settings['download_noimages']) list.pack_start(check_dl_images) button = hildon.Button(gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL) button.set_label("Change default fieldnote text") button.connect("clicked", self._show_default_log_text_settings, None) #button.connect("clicked", lambda caller: dialog.hide()) list.pack_start(button) dialog.show_all() result = dialog.run() dialog.hide() if result != gtk.RESPONSE_ACCEPT: return if self.settings['options_show_html_description'] != check_show_html_description.get_active(): self.old_cache_window = None update = { 'options_username': username.get_text(), 'options_password': password.get_text(), 'download_noimages': check_dl_images.get_active(), 'options_show_name': check_show_cache_id.get_active(), #'options_map_double_size': check_map_double_size.get_active(), #'options_hide_found': check_hide_found.get_active(), 'options_show_html_description': check_show_html_description.get_active(), #'options_rotate_screen': rotate_get_result(), #'tts_interval':tts_get_result(), } self.settings.update(update) self.core.save_settings(update, self) #self.core.on_userdata_changed(self.settings['options_username'], self.settings['options_password']) def _show_default_log_text_settings(self, widget, data): dialog = gtk.Dialog("Default Fieldnote Text", None, gtk.DIALOG_DESTROY_WITH_PARENT, (gtk.STOCK_OK, gtk.RESPONSE_ACCEPT)) default_log_text = hildon.TextView() default_log_text.get_buffer().set_text(self.settings['options_default_log_text']) dialog.vbox.pack_start(default_log_text) l = gtk.Label() l.set_alignment(0, 0.5) l.set_markup("Placeholders:\n%(machine)s = device name\n%c = Date and Time, %x = Date, %X = Time\nand more, just search the web for strftime.") dialog.vbox.pack_start(l) dialog.show_all() result = dialog.run() dialog.hide() if result != gtk.RESPONSE_ACCEPT: return text = default_log_text.get_buffer().get_text(default_log_text.get_buffer().get_start_iter(), default_log_text.get_buffer().get_end_iter()) update = {'options_default_log_text' : text} self.settings.update(update) self.core.save_settings(update, self) def _get_tts_settings(self): tts_settings = ( (0, 'Off'), (-1, 'Automatic'), (10, '10 Seconds'), (20, '20 Seconds'), (30, '30 Seconds'), (50, '50 Seconds'), (100, '100 Seconds'), (180, '3 Minutes'), (5 * 60, '5 Minutes'), (10 * 60, '10 Minutes'), ) tts_selector = hildon.TouchSelector(text=True) i = 0 for seconds, text in tts_settings: tts_selector.append_text(text) if self.settings['tts_interval'] == seconds: tts_selector.select_iter(0, tts_selector.get_model(0).get_iter(i), False) i += 1 tts_button = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_HORIZONTAL) tts_button.set_title('TTS interval') tts_button.set_selector(tts_selector) tts_get_result = lambda: tts_settings[tts_selector.get_selected_rows(0)[0][0]][0] return tts_button, tts_get_result def _get_rotate_settings(self): rotate_settings = ( (FremantleRotation.AUTOMATIC, 'Automatic'), (FremantleRotation.NEVER, 'Landscape'), (FremantleRotation.ALWAYS, 'Portrait') ) rotate_selector = hildon.TouchSelector(text=True) i = 0 for status, text in rotate_settings: rotate_selector.append_text(text) if self.settings['options_rotate_screen'] == status: rotate_selector.select_iter(0, rotate_selector.get_model(0).get_iter(i), False) i += 1 rotate_screen = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_HORIZONTAL) rotate_screen.set_title('Screen Rotation') rotate_screen.set_selector(rotate_selector) rotate_get_result = lambda: rotate_settings[rotate_selector.get_selected_rows(0)[0][0]][0] return rotate_screen, rotate_get_result def _on_show_dialog_change_target(self, widget, data): c = self._get_best_coordinate(self.core.current_target) dialog = gtk.Dialog("change target", self.window, gtk.DIALOG_DESTROY_WITH_PARENT, (gtk.STOCK_OK, gtk.RESPONSE_ACCEPT)) bar_label = gtk.Label("Lat/Lon: ") bar_entry = hildon.Entry(gtk.HILDON_SIZE_AUTO) bar_entry.set_property('hildon-input-mode', gtk.HILDON_GTK_INPUT_MODE_ALPHA | gtk.HILDON_GTK_INPUT_MODE_SPECIAL | gtk.HILDON_GTK_INPUT_MODE_TELE) bar_entry.set_text(c.get_latlon(self.format)) def show_coord_input(widget): try: m = geo.try_parse_coordinate(bar_entry.get_text()) except Exception: m = c m_new = self.show_coordinate_input(m) bar_entry.set_text(m_new.get_latlon(self.format)) bar_button = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL) bar_button.set_label(" Edit ") bar_button.connect("clicked", show_coord_input) bar = gtk.HBox() bar.pack_start(bar_label, False) bar.pack_start(bar_entry, True) bar.pack_start(bar_button, False) dialog.vbox.pack_start(bar, False) def sel_coord(widget, data, clist): coord = clist[self._get_selected_pos(widget)] bar_entry.set_text(coord.get_latlon(self.format)) if self.current_cache != None: sel, clist = self._get_coord_selector(self.current_cache, sel_coord, True) sel.set_size_request(-1, 200) dialog.vbox.pack_start(sel) dialog.show_all() result = dialog.run() dialog.hide() if result == gtk.RESPONSE_ACCEPT: self.set_target(geo.try_parse_coordinate(bar_entry.get_text())) def show_cache(self, cache, select=True): if cache == None: return if self.current_cache != None and self.current_cache.name == cache.name and self.old_cache_window != None: hildon.WindowStack.get_default().push_1(self.old_cache_window) self.current_cache_window_open = True return if self.old_cache_window != None: self.old_cache_window.destroy() if select: self.set_current_cache(cache) win = hildon.StackableWindow() win.set_title(cache.title) events = [] notebook = gtk.Notebook() notebook.set_tab_pos(gtk.POS_BOTTOM) # info p = gtk.Table(10, 2) labels = ( ('Full Name', cache.title), ('ID', cache.name), ('Type', cache.type), ('Size', cache.get_size_string()), ('Terrain', cache.get_terrain()), ('Difficulty', cache.get_difficulty()), ('Owner', cache.owner), ('Status', cache.get_status()) ) i = 0 for label, text in labels: l = gtk.Label() l.set_alignment(0, 0.5) l.set_markup("%s" % label) w = gtk.Label(text) w.set_line_wrap(True) w.set_alignment(0, 0.5) p.attach(l, 0, 1, i, i + 1) p.attach(w, 1, 2, i, i + 1) i += 1 # links for listing & log l = gtk.Label() l.set_markup("Open Website") l.set_alignment(0, 0.5) p.attach(l, 0, 1, 8, 9) z = gtk.HBox(True) z.pack_start(gtk.LinkButton("http://www.geocaching.com/seek/cache_details.aspx?wp=%s" % cache.name, 'Listing')) z.pack_start(gtk.LinkButton("http://www.geocaching.com/seek/log.aspx?wp=%s" % cache.name, 'Post Log')) z.pack_start(gtk.LinkButton("http://www.geocaching.com/seek/cache_details.aspx?wp=%s&log=y#ctl00_ContentBody_CacheLogs" % cache.name, 'All Logs')) p.attach(z, 1, 2, 8, 9) # cache-was-not-downloaded-yet-warning if not cache.was_downloaded(): p.attach(gtk.Label("Please download full details to see the description."), 0, 2, 9, 10) notebook.append_page(p, gtk.Label("Info")) if cache.was_downloaded(): # Description p = hildon.PannableArea() notebook.append_page(p, gtk.Label("Description")) text_longdesc = re.sub(r'(?i)]+?>', ' [to get all images, re-download description] ', re.sub(r'\[\[img:([^\]]+)\]\]', lambda a: self._replace_image_callback(a, cache), cache.desc)) if not self.settings['options_show_html_description']: widget_description = gtk.Label() widget_description.set_line_wrap(True) widget_description.set_alignment(0, 0) widget_description.set_size_request(self.window.size_request()[0] - 10, -1) p.add_with_viewport(widget_description) text_shortdesc = my_gtk_label_escape(self._strip_html(cache.shortdesc).strip()) text_longdesc = my_gtk_label_escape(self._strip_html(text_longdesc).strip()) if cache.status == geocaching.GeocacheCoordinate.STATUS_DISABLED: text_shortdesc = 'This Cache is not available!\n%s' % text_shortdesc if text_longdesc != '' and text_shortdesc != '': showdesc = "%s\n\n%s" % (text_shortdesc, text_longdesc) elif text_longdesc == '' and text_shortdesc == '': showdesc = "No description available" elif text_longdesc == '': showdesc = text_shortdesc else: showdesc = text_longdesc widget_description.set_markup(showdesc) events.append(self.window.connect('configure-event', self._on_configure_label, widget_description)) else: text_shortdesc = cache.shortdesc if text_longdesc != '' and text_shortdesc != '': showdesc = "%s
%s" % (text_shortdesc, text_longdesc) elif text_longdesc == '' and text_shortdesc == '': showdesc = "No description available" elif text_longdesc == '': showdesc = text_shortdesc else: showdesc = text_longdesc import gtkhtml2 description = gtkhtml2.Document() description.clear() description.open_stream('text/html') description.write_stream('%s' % showdesc) description.close_stream() description.connect('link-clicked', self._open_browser) widget_description = gtkhtml2.View() widget_description.set_document(description) p.set_property("mov-mode", hildon.MOVEMENT_MODE_BOTH) p.add(widget_description) text_longdesc = self._strip_html(text_longdesc) # logs&hints p = hildon.PannableArea() widget_hints = gtk.VBox() p.add_with_viewport(widget_hints) notebook.append_page(p, gtk.Label("Logs & Hints")) logs = cache.get_logs() for l in logs: try: w_type = gtk.image_new_from_pixbuf(self.icon_pixbufs[l['type']]) except KeyError: w_type = gtk.Label(l['type'].upper()) w_type.set_alignment(0, 0) w_name = gtk.Label() w_name.set_markup(" %s" % my_gtk_label_escape(HTMLManipulations._decode_htmlentities(l['finder']))) w_name.set_alignment(0, 0) w_date = gtk.Label() w_date.set_markup("%4d-%02d-%02d" % (int(l['year']), int(l['month']), int(l['day']))) w_date.set_alignment(0.95, 0) w_text = gtk.Label("%s\n" % l['text'].strip()) w_text.set_line_wrap(True) w_text.set_alignment(0, 0) w_text.set_size_request(self.window.size_request()[0] - 10, -1) events.append(self.window.connect('configure-event', self._on_configure_label, w_text, True)) w_first = gtk.HBox() w_first.pack_start(w_type, False, False) w_first.pack_start(w_name) w_first.pack_start(w_date) widget_hints.pack_start(w_first, False, False) widget_hints.pack_start(w_text, False, False) widget_hints.pack_start(gtk.HSeparator(), False, False) hints = cache.hints.strip() if len(hints) > 0: button_hints = hildon.GtkButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT) button_hints.set_label('Show Hints (%d chars)' % len(hints)) label_hints = gtk.Label() label_hints.set_line_wrap(True) label_hints.set_alignment(0, 0) events.append(self.window.connect('configure-event', self._on_configure_label, label_hints)) label_hints.set_size_request(self.window.size_request()[0] - 10, -1) def show_hints(widget): label_hints.set_text(hints) widget.hide() button_hints.connect('clicked', show_hints) widget_hints.pack_start(button_hints, False, False) widget_hints.pack_start(label_hints, False, False) else: label_hints = gtk.Label() label_hints.set_markup('No hints available') widget_hints.pack_start(label_hints, False, False) # images self.build_cache_images(cache, notebook) # calculated coords cache_calc_box = gtk.VBox() notebook.append_page(cache_calc_box, gtk.Label("Calc")) def rebuild_cache_calc(): cache.start_calc(self._strip_html(text_longdesc)) self.build_cache_calc(cache, cache_calc_box) rebuild_cache_calc() self.rebuild_cache_calc = rebuild_cache_calc # coords p = gtk.VBox() self.cache_coord_page = p coord_page_number = notebook.get_n_pages() notebook.append_page(p, gtk.Label("Coords")) # notes pan = hildon.PannableArea() pan.set_property('mov-mode', hildon.MOVEMENT_MODE_BOTH) self.cache_notes = gtk.TextView() pan.add(self.cache_notes) self.cache_notes.get_buffer().set_text(cache.notes) self.cache_notes.get_buffer().connect('changed', self.on_notes_changed) self.notes_changed = False button = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_HORIZONTAL ) button.set_title("Add Waypoint") button.set_image(gtk.image_new_from_file(self.image_icon_add)) button.connect("clicked", self._on_add_waypoint_clicked) p = gtk.VBox() p.pack_start(button, False) p.pack_start(pan, True) notebook.append_page(p, gtk.Label("Notes")) # portrait mode notebook switcher notebook_switcher = gtk.HBox(True) notebook_switcher.set_no_show_all(True) def switch_nb(widget, forward): if forward: notebook.next_page() else: notebook.prev_page() for label, fwd in ((self.image_left, False), (self.image_right, True)): nb = hildon.GtkButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT) nb.set_image(gtk.image_new_from_file(label)) nb.connect('clicked', switch_nb, fwd) notebook_switcher.pack_start(nb) details = gtk.VBox() details.pack_start(notebook) details.pack_start(notebook_switcher, False) def on_switch_page(caller, page, pageno): if pageno == coord_page_number: self.update_coords() notebook_switcher.get_children()[0].set_sensitive(pageno != 0) notebook_switcher.get_children()[1].set_sensitive(pageno != notebook.get_n_pages() - 1) notebook.connect("switch-page", on_switch_page) def reorder_details(widget, event=None): portrait = (event.width < event.height) notebook.set_property('show-tabs', not portrait) if portrait: notebook_switcher.show_all() else: notebook_switcher.hide() events.append(self.window.connect('configure-event', reorder_details)) win.add(details) # menu menu = hildon.AppMenu() widget_marked = hildon.CheckButton(gtk.HILDON_SIZE_AUTO) widget_marked.set_label("marked") widget_marked.set_active(cache.marked) widget_marked.connect("clicked", self._on_cache_marked_toggle, None) menu.append(widget_marked) ### großschreibung button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO) button.set_label("Download all Details") button.connect("clicked", self._on_download_cache_clicked, None) menu.append(button) button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO) button.set_label("Set as Target") button.connect("clicked", self._on_set_target_clicked, cache) menu.append(button) button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO) button.set_label("Show on Map") button.connect("clicked", self._on_show_on_map, cache) menu.append(button) menu.append(self._get_write_fieldnote_button()) menu.show_all() win.set_app_menu(menu) win.show_all() notebook_switcher.set_no_show_all(False) reorder_details(None, win.get_allocation()) def delete_events(widget): for x in events: self.window.disconnect(x) win.connect('delete_event', self.hide_cache_view) win.connect('unrealize', delete_events) self.current_cache_window_open = True def _on_configure_label(self, source, event, widget, force=False, factor = 1): widget.set_size_request(event.width * factor - 10, -1) if force: widget.realize() def build_coordinates(self, cache, p): button = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_HORIZONTAL) button.set_title("Add Coordinate") button.set_image(gtk.image_new_from_file(self.image_icon_add)) button.connect("clicked", lambda caller, cache: self._add_waypoint_to_user_coordinates_list(cache), cache) p.pack_start(button, False) def show_details(widget_coords, stuff, clist): c = clist[self._get_selected_pos(widget_coords)] if c == None: return self.__show_coordinate_details(c, cache) widget_coords, clist = self._get_coord_selector(cache, show_details) p.pack_start(widget_coords, True, True) p.show_all() def __show_coordinate_details(self, c, cache): RESPONSE_AS_TARGET, RESPONSE_AS_MAIN, RESPONSE_COPY_EDIT, RESPONSE_EDIT, RESPONSE_DELETE = range(5) try: name = c.display_text except AttributeError: name = "Coordinate Details" if (c.name == "") else c.name dialog = gtk.Dialog(self.shorten_name(name, 70), self.window, gtk.DIALOG_DESTROY_WITH_PARENT, (gtk.STOCK_OK, gtk.RESPONSE_ACCEPT)) dialog.set_size_request(800, 800) if c.lat != None: dialog.add_button("as Target", RESPONSE_AS_TARGET) dialog.add_button("as Main Coord.", RESPONSE_AS_MAIN) if c.user_coordinate_id != None: dialog.add_button("edit", RESPONSE_EDIT) dialog.add_button("delete", RESPONSE_DELETE) elif c.lat != None: dialog.add_button("copy & edit", RESPONSE_COPY_EDIT) lbl = gtk.Label() lbl.set_line_wrap(True) lbl.set_alignment(0, 0.5) lbl.set_markup("%s\n%s" % (c.get_latlon(self.format) if c.lat != None else '???', c.comment)) #lbl.set_size_request(dialog.size_request()[0]/2, -1) dialog.connect('configure-event', self._on_configure_label, lbl, True, 2.0/3) dialog.vbox.pack_start(lbl, False) if c.lat != None: map = Map(center = c, zoom = 17, draggable = False) map.add_layer(SingleMarkLayer(c)) dialog.vbox.pack_start(map, True) dialog.show_all() resp = dialog.run() dialog.hide() if resp == RESPONSE_AS_TARGET: self.set_current_cache(cache) self.set_target(c) self.hide_cache_view(go_to_map = True) elif resp == RESPONSE_AS_MAIN: self.core.set_alternative_position(cache, c) elif resp == RESPONSE_COPY_EDIT: self._add_waypoint_to_notes(c) self.update_coords() elif resp == RESPONSE_EDIT: self._on_edit_user_coordinate(None, cache, c.user_coordinate_id) self.update_coords() elif resp == RESPONSE_DELETE: self.__delete_user_coordinate(cache, c.user_coordinate_id) def update_coords(self): self.current_cache.notes = self.get_cache_notes() for x in self.cache_coord_page.get_children(): self.cache_coord_page.remove(x) self.build_coordinates(self.current_cache, self.cache_coord_page) self.cache_coord_page.show() def build_cache_images(self, cache, notebook): selector = hildon.TouchSelector(text=True) selector.get_column(0).get_cells()[0].set_property('xalign', 0) selector.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE) images = self.current_cache.get_images() if len(images) == 0: return imagelist = images.items() imagelist.sort(cmp=lambda x, y: cmp(x[1], y[1])) i = 1 for filename, caption in imagelist: if len(caption) == 0: caption = "(no caption)" text = "#%d: %s" % (i, caption) i += 1 selector.append_text(text) def on_imagelist_clicked(widget, data): path, caption = imagelist[self._get_selected_pos(widget)] self._on_show_image(path, caption) selector.connect("changed", on_imagelist_clicked) notebook.append_page(selector, gtk.Label("Images")) def build_cache_calc(self, cache, p): logger.debug("Cache calc for %s" % p) def input_changed(widget, char): cache.calc.set_var(char, widget.get_text()) self.show_cache_calc_results(cache) for x in p.get_children(): p.remove(x) count = len(cache.calc.requires) # create table with n columns. cols = 7 rows = int(ceil(float(count) / float(cols))) table = gtk.Table(rows, cols) i = 0 requires_sort = list(cache.calc.requires) requires_sort.sort() vars = cache.calc.get_vars() for char in requires_sort: row = i / cols col = i % cols m = gtk.HBox() m.pack_start(gtk.Label(str(char))) e = hildon.Entry(gtk.HILDON_SIZE_AUTO) e.set_property("hildon-input-mode", gtk.HILDON_GTK_INPUT_MODE_NUMERIC) try: e.set_text(vars[char]) except KeyError: pass e.connect('changed', input_changed, str(char)) e.set_size_request(50, -1) m.pack_start(e) table.attach(m, col, col + 1, row, row + 1) i += 1 # add button button = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_HORIZONTAL ) button.set_title("Add Calculation") button.set_image(gtk.image_new_from_file(self.image_icon_add)) button.connect("clicked", self._on_edit_user_coordinate, cache) p.pack_start(button, False) # add contents p.pack_start(table, False) p.pack_start(gtk.HSeparator(), False) # add list vbox a = hildon.PannableArea() self.cache_calc_vbox = gtk.VBox() a.add_with_viewport(self.cache_calc_vbox) p.pack_start(a, True) p.show_all() self.show_cache_calc_results(cache) def show_cache_calc_results(self, cache): for x in self.cache_calc_vbox.get_children(): self.cache_calc_vbox.remove(x) vars = cache.calc.get_vars() for c in cache.calc.coords: if len(c.requires) == 0: continue if type(c.source) == int: source = cache.get_user_coordinate(c.source)['name'] else: source = c.source if c.has_requires(): text_calc = "= %s\n%s%s" % (c.replaced_result, c.result if c.result != False else '', "".join("\n! %s" % warning for warning in c.warnings)) else: text_calc = "Needs %s\n" % (', '.join(("%s" if r in vars else "%s") % r for r in c.requires)) label_text = '%s: %s\n%s' % (source, c.orig, text_calc) #logger.debug("Showing cache calc: %s" % label_text) l = gtk.Label() l.set_alignment(0, 0.5) l.set_markup(label_text) b = gtk.Table(rows = 2, columns = 2) b.attach(l, 0, 1, 0, 2) button = hildon.GtkButton(gtk.HILDON_SIZE_FINGER_HEIGHT) button.set_label("edit") button.connect("clicked", self._on_edit_user_coordinate, cache, c.source, c) b.attach(button, 1, 2, 0, 1, 0, 0) self.cache_calc_vbox.pack_start(b, False) self.cache_calc_vbox.pack_start(gtk.HSeparator(), False) self.cache_calc_vbox.show_all() def _on_edit_user_coordinate(self, caller, cache, source_id = None, coordinate = None): if source_id == None: before_calc = '' before_name = '' ctype = geocaching.GeocacheCoordinate.USER_TYPE_CALC_STRING logger.debug("Adding new calc string") button_text = None elif type(source_id) != int: before_calc = coordinate.orig before_name = '' ctype = geocaching.GeocacheCoordinate.USER_TYPE_CALC_STRING_OVERRIDE logger.debug("Overwriting original coordinate.") button_text = None else: info = cache.get_user_coordinate(source_id) before_name = info['name'] ctype = info['type'] if info['type'] == geocaching.GeocacheCoordinate.USER_TYPE_CALC_STRING: before_calc = info['value'] logger.debug("Editing user supplied calc string.") button_text = 'Delete' elif info['type'] == geocaching.GeocacheCoordinate.USER_TYPE_CALC_STRING_OVERRIDE: before_calc = info['value'][1] logger.debug("Editing user supplied overwrite.") button_text = 'Reset' elif info['type'] == geocaching.GeocacheCoordinate.USER_TYPE_COORDINATE: start = geo.Coordinate(info['value'][0], info['value'][1], info['name']) res = self.show_coordinate_input(start, none_on_cancel = True, show_name_input = True) if res == None: return cache.set_user_coordinate(geocaching.GeocacheCoordinate.USER_TYPE_COORDINATE, (res.lat, res.lon), res.name, source_id) self.core.save_cache_attribute(self.current_cache, 'user_coordinates') self.update_coords() return else: raise Exception("Illegal type.") (res, after_name, after_calc) = self.__show_edit_calculation(before_calc, before_name, button_text) if res not in (gtk.RESPONSE_ACCEPT, gtk.RESPONSE_NO): return if res == gtk.RESPONSE_ACCEPT: try: geo.try_parse_coordinate(after_calc) self.show_success("Coordinate saved.") except Exception: self.show_success("Calc string saved.") if source_id == None: value = after_calc id = None elif type(source_id) != int: value = (coordinate.signature, after_calc) id = None elif info['type'] == geocaching.GeocacheCoordinate.USER_TYPE_CALC_STRING or info['type'] == geocaching.GeocacheCoordinate.USER_TYPE_COORDINATE: value = after_calc id = source_id else: value = (info['value'][0], after_calc) id = source_id cache.set_user_coordinate(ctype, value, after_name, id) elif res == gtk.RESPONSE_NO: cache.delete_user_coordinate(source_id) self.core.save_cache_attribute(self.current_cache, 'user_coordinates') self.rebuild_cache_calc() def __delete_user_coordinate(self, cache, id): cache.delete_user_coordinate(id) self.core.save_cache_attribute(cache, 'user_coordinates') self.rebuild_cache_calc() self.update_coords() def __show_edit_calculation(self, before_calc, before_name, button_text): dialog = gtk.Dialog("Edit Calculation", self.window, gtk.DIALOG_DESTROY_WITH_PARENT, ( gtk.STOCK_OK, gtk.RESPONSE_ACCEPT )) if button_text != None: dialog.add_button(button_text, gtk.RESPONSE_NO) text = "Enter a Coordinate or a calculation string.\n" + \ "Format is: N12 12.123 E123 12.123\n" + \ "Variables: A-Z, Operators: +-*/%()" lbl = gtk.Label() dialog.vbox.pack_start(lbl) lbl.set_line_wrap(True) lbl.set_alignment(0, 0.5) lbl.set_markup(text) grp = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL) entry_name = hildon.Entry(gtk.HILDON_SIZE_AUTO) entry_name.set_text(before_name) dialog.vbox.pack_start(hildon.Caption(grp, "Name", entry_name, None, hildon.CAPTION_MANDATORY)) entry_calc = hildon.Entry(gtk.HILDON_SIZE_AUTO) entry_calc.set_text(before_calc) dialog.vbox.pack_start(hildon.Caption(grp, "Calculation", entry_calc, None, hildon.CAPTION_MANDATORY)) dialog.show_all() while True: res = dialog.run() if res == gtk.RESPONSE_ACCEPT and not CalcCoordinate.is_calc_string(entry_calc.get_text()): self.show_error("Please enter a valid calculation.") else: break dialog.hide() return (res, entry_name.get_text(), entry_calc.get_text()) def _on_add_waypoint_clicked (self, widget): self._add_waypoint_to_notes() def _add_waypoint_to_notes(self, start=None): res = self.show_coordinate_input(self._get_best_coordinate(start), none_on_cancel = True) if res == None: return text = "\n%s\n" % res.get_latlon(self.format) self.cache_notes.get_buffer().insert(self.cache_notes.get_buffer().get_end_iter(), text) def _add_waypoint_to_user_coordinates_list(self, cache, start=None): res = self.show_coordinate_input(self._get_best_coordinate(start), none_on_cancel = True, show_name_input = True) if res == None: return cache.set_user_coordinate(geocaching.GeocacheCoordinate.USER_TYPE_COORDINATE, (res.lat, res.lon), res.name) self.core.save_cache_attribute(self.current_cache, 'user_coordinates') self.update_coords() def _on_show_image(self, dpath, caption): fullpath = path.join(self.settings['download_output_dir'], dpath) if not path.exists(fullpath): print "file does not exist: " + fullpath return win = hildon.StackableWindow() win.set_title(caption) p = hildon.PannableArea() p.set_property('mov-mode', hildon.MOVEMENT_MODE_BOTH) i = gtk.Image() i.set_from_file(fullpath) i.set_pixel_size(3) win.add(p) p.add_with_viewport(i) win.show_all() @staticmethod def wrap(text, width): """ A word-wrap function that preserves existing line breaks and most spaces in the text. Expects that existing line breaks are posix newlines (\n). """ return reduce(lambda line, word, width=width: '%s%s%s' % (line, ' \n'[(len(line)-line.rfind('\n')-1 + len(word.split('\n', 1)[0] ) >= width)], word), text.split(' ') ) def _get_coord_selector(self, cache, callback, no_empty=False): selector = hildon.TouchSelector(text=True) selector.get_column(0).get_cells()[0].set_property('xalign', 0) selector.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE) clist = cache.get_collected_coordinates(include_unknown=not no_empty, format=self.format, htmlcallback=self._strip_html, shorten_callback=lambda text: self.shorten_name(text, 65)) for number, c in clist.items(): selector.append_text(c.display_text) selector.connect('changed', callback, clist) return selector, clist def _on_download_cache_clicked(self, some, thing): self.core.on_download_cache(self.current_cache) def _on_cache_marked_toggle(self, widget, data): if self.current_cache == None: return self._update_mark(self.current_cache, widget.get_active()) def _on_show_cache_details(self, widget, data, touched=None): self.show_cache(self.current_cache) def set_active_page(self, show_gps): self._on_set_active_page(None, show_gps) def _on_set_active_page(self, widget, show_gps): if show_gps: self.main_gpspage.show() self.main_mappage.hide() else: self.main_gpspage.hide() self.main_mappage.show() def _on_show_on_map(self, widget, data): self.set_center(data) self.hide_cache_view(go_to_map = True) self._on_set_active_page(None, False) @staticmethod def _get_selected(widget): ls, iter = widget.get_selected(0) return ls[ls.get_path(iter)[0]] @staticmethod def _get_selected_pos(widget): ls, iter = widget.get_selected(0) return ls.get_path(iter)[0] ############################################## # # Signal Handling from Core # ############################################## def _on_cache_changed(self, something, cache): if self.current_cache != None \ and cache.name == self.current_cache.name \ and self.current_cache_window_open: self.hide_cache_view() self.old_cache_window = None self.show_cache(cache) return False else: return False ############################################## # # Current Cache / Current Target / Setting Center # ############################################## #def set_center(self, coord, noupdate=False, reset_track=True): # SimpleGui.set_center(self, coord, noupdate, reset_track) def set_current_cache(self, cache): self.current_cache = cache self.button_show_details.set_value(self.shorten_name(cache.title, 25)) self.button_show_details.set_sensitive(True) self.button_show_details_small.set_sensitive(True) self.geocache_layer.set_current_cache(cache) gobject.idle_add(self.map.redraw_layers) def _on_set_target_clicked(self, some, cache): self.set_target(cache) self.hide_cache_view(go_to_map = True) self.set_active_page(True) def _on_target_changed(self, caller, cache, distance, bearing): self.gps_target_distance = distance self.gps_target_bearing = bearing coord = cache.get_latlon(self.format) self.label_target.set_value(coord) ############################################## # # Map # ############################################## def _update_zoom_buttons(self): if self.map.get_zoom() == self.map.get_min_zoom(): self.button_zoom_out.set_sensitive(False) else: self.button_zoom_out.set_sensitive(True) if self.map.get_zoom() == self.map.get_max_zoom(): self.button_zoom_in.set_sensitive(False) else: self.button_zoom_in.set_sensitive(True) ############################################## # # Displaying Messages and Window Handling # ############################################## def hide_progress(self): hildon.hildon_gtk_window_set_progress_indicator(self.window, 0) if self.banner != None: self.banner.hide() self.banner = None def hide_cache_view(self, widget=None, data=None, go_to_map = False): if self.current_cache.calc != None: self.core.save_cache_attribute(self.current_cache, 'vars') self.current_cache_window_open = False if self.notes_changed: self.current_cache.notes = self.get_cache_notes() self.core.save_cache_attribute(self.current_cache, 'notes') self.notes_changed = False self.old_cache_window = hildon.WindowStack.get_default().pop_1() while go_to_map and hildon.WindowStack.get_default().size() > 1: hildon.WindowStack.get_default().pop_1() return True def get_cache_notes(self): b = self.cache_notes.get_buffer() return b.get_text(b.get_start_iter(), b.get_end_iter()) def set_progress(self, fraction, text = ''): hildon.hildon_gtk_window_set_progress_indicator(self.window, 1) if text == '': text = 'Please wait...' if self.banner == None: self.banner = hildon.Banner() self.banner.set_text(text) self.banner.show_all() else: self.banner.set_text(text) gtk.gdk.threads_leave() def show_error(self, errormsg): hildon.hildon_banner_show_information(self.window, "", "%s" % errormsg) def show_success(self, message): hildon.hildon_banner_show_information(self.window, "", message) ############################################## # # GPS Display # ############################################## def update_gps_display(self): if self.gps_data == None: #self.osd_string = "No Fix " return if self.gps_data.sats == 0: text = "No sats, error: ±%3.1fm" % self.gps_data.error else: text = "%d/%d sats, error: ±%3.1fm" % (self.gps_data.sats, self.gps_data.sats_known, self.gps_data.error) self.label_quality.set_markup("Accuracy\n%s" % text) if self.gps_data.altitude == None or self.gps_data.bearing == None: return self.label_altitude.set_markup("Altitude\n%d m" % self.gps_data.altitude) self.label_bearing.set_markup("Bearing\n%d°" % self.gps_data.bearing) self.label_latlon.set_markup("Current Position\n%s" % self.gps_data.position.get_latlon(self.format)) if self.gps_has_fix and self.gps_target_distance != None: td_string = geo.Coordinate.format_distance(self.gps_target_distance) self.label_dist.set_markup("%s" % td_string) elif self.gps_target_distance == None: self.label_dist.set_markup("No Target") else: self.label_dist.set_markup("No Fix") def _on_no_fix(self, caller, gps_data, status): self.gps_data = gps_data self.gps_has_fix = False self.label_bearing.set_text("No Fix") self.label_latlon.set_text(status) self.update_gps_display() self._draw_arrow() self.map.redraw_layers() ############################################## # # Settings # ############################################## def _on_settings_changed_gui(self, settings): if 'options_rotate_screen' in settings: self.rotation_manager.set_mode(settings['options_rotate_screen']) def _on_save_settings(self, caller): c = self.map.get_center() settings = {} settings['map_position_lat'] = c.lat settings['map_position_lon'] = c.lon settings['map_zoom'] = self.map.get_zoom() settings['map_follow_position'] = self._get_track_mode() if self.current_cache != None: settings['last_selected_geocache'] = self.current_cache.name for i in ['options_username', 'options_password', 'download_noimages', 'options_show_name', 'options_hide_found', 'options_show_html_description', 'options_map_double_size', 'options_rotate_screen', 'tts_interval']: settings[i] = self.settings[i] caller.save_settings(settings, self) agtl-0.8.0.3/files/advancedcaching/openstreetmap.py000066400000000000000000000205111151564747700222530ustar00rootroot00000000000000#!/usr/bin/python # -*- coding: utf-8 -*- # Copyright (C) 2010 Daniel Fett # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # Author: Daniel Fett agtl@danielfett.de # Jabber: fett.daniel@jaber.ccc.de # Bugtracker and GIT Repository: http://github.com/webhamster/advancedcaching # from __future__ import with_statement import logging logger = logging.getLogger('openstreetmap') from os import path, mkdir, extsep, remove from threading import Semaphore from urllib import urlretrieve from socket import setdefaulttimeout setdefaulttimeout(30) CONCURRENT_THREADS = 10 def get_tile_loader(prefix, remote_url, max_zoom = 18, reverse_zoom = False, file_type = 'png', size = 256): class TileLoader(): downloading = {} semaphore = Semaphore(CONCURRENT_THREADS) noimage_cantload = None noimage_loading = None base_dir = '' PREFIX = prefix MAX_ZOOM = max_zoom FILE_TYPE = file_type REMOTE_URL = remote_url TILE_SIZE = size TPL_LOCAL_PATH = path.join("%s", PREFIX, "%d", "%d") TPL_LOCAL_FILENAME = path.join("%s", "%%d%s%s" % (extsep, FILE_TYPE)) def __init__(self, id_string, tile, zoom, undersample = False, x = 0, y = 0, callback_draw = None, callback_load = None): self.id_string = id_string self.undersample = undersample self.tile = tile #self.download_tile = self.gui.ts.check_bounds(*tile) self.download_tile = tile if not undersample: self.download_zoom = zoom self.display_zoom = zoom else: self.download_zoom = zoom - 1 self.display_zoom = zoom self.download_tile = (int(self.download_tile[0]/2), int(self.download_tile[1]/2)) self.pbuf = None self.callback_draw = callback_draw self.callback_load = callback_load self.my_noimage = None self.stop = False self.x = x self.y = y # setup paths self.local_path = self.TPL_LOCAL_PATH % (self.base_dir, self.download_zoom, self.download_tile[0]) self.local_filename = self.TPL_LOCAL_FILENAME % (self.local_path, self.download_tile[1]) self.remote_filename = self.REMOTE_URL % {'zoom': self.download_zoom, 'x' : self.download_tile[0], 'y' : self.download_tile[1]} def halt(self): self.stop = True @staticmethod def create_recursive(dpath): if dpath != '/': if not path.exists(dpath): head, tail = path.split(dpath) TileLoader.create_recursive(head) try: mkdir(dpath) except Exception: # let others fail here. pass def run(self): answer = True if not path.isfile(self.local_filename): self.create_recursive(self.local_path) self.draw(self.get_no_image(self.noimage_loading)) answer = self.__download(self.remote_filename, self.local_filename) # now the file hopefully exists if answer == True: self.load() self.draw(self.pbuf) #gobject.idle_add(lambda: self.draw(self.pbuf)) elif answer == False: #gobject.idle_add(lambda: self.draw(self.get_no_image(self.noimage_cantload))) self.draw(self.get_no_image(self.noimage_cantload)) else: # Do nothing here, as the thread was told to stop pass def run_again(self): self.load() #gobject.idle_add(lambda: self.draw(self.pbuf)) self.draw(self.pbuf) return False def get_no_image(self, default): return (default, None) ''' if self.my_noimage != None: return self.my_noimage size, tile = self.TILE_SIZE, self.tile # we have no image available. so what do now? # first, check if we've the "supertile" available (zoomed out) supertile_zoom = self.download_zoom - 1 supertile_x = int(tile[0]/2) supertile_y = int(tile[1]/2) supertile_path = self.TPL_LOCAL_PATH % (self.base_dir, supertile_zoom, supertile_x) supertile_name = self.TPL_LOCAL_FILENAME % (supertile_path, supertile_y) #supertile_name = path.join(TileLoader.base_dir, self.PREFIX, str(supertile_zoom), str(supertile_x), "%d%s%s" % (supertile_y, extsep, self.FILE_TYPE)) if not self.undersample and path.exists(supertile_name): off_x = (tile[0]/2.0 - supertile_x) * size off_y = (tile[1]/2.0 - supertile_y) * size #pbuf = gtk.gdk.pixbuf_new_from_file(supertile_name) #dest = gtk.gdk.Pixbuf(pbuf.get_colorspace(), pbuf.get_has_alpha(), pbuf.get_bits_per_sample(), size, size) #pbuf.scale(dest, 0, 0, 256, 256, -off_x*2, -off_y*2, 2, 2, gtk.gdk.INTERP_BILINEAR) self.pbuf = (surface, (off_x, off_y)) self.my_noimage = surface return dest else: self.my_noimage = default return default ''' def load(self, tryno=0): # load the pixbuf to memory if self.stop: return True try: size, tile = self.TILE_SIZE, self.tile if self.undersample: # don't load the tile directly, but load the supertile instead supertile_x = int(tile[0]/2) supertile_y = int(tile[1]/2) off_x = (tile[0]/2.0 - supertile_x) * size off_y = (tile[1]/2.0 - supertile_y) * size surface = self.callback_load(self.local_filename) self.pbuf = (surface, (off_x, off_y)) else: surface = self.callback_load(self.local_filename) self.pbuf = (surface, None) return True except Exception, e: if tryno == 0: return self.recover() else: logger.exception("Exception while loading map tile: %s" % e) self.pbuf = (self.noimage_cantload, None) return True def recover(self): try: remove(self.local_filename) except: pass self.__download(self.remote_filename, self.local_filename) return self.load(1) def draw(self, pbuf): if not self.stop: return self.callback_draw(self.id_string, pbuf[0], self.x, self.y, pbuf[1]) return False def __download(self, remote, local): if path.exists(local): return True #import time #time.sleep(10) #return False with TileLoader.semaphore: try: if self.stop: return None info = urlretrieve(remote, local) if "text/html" in info[1]['Content-Type']: return False return True except Exception, e: print "Download Error", e return False def download_tile_only(self): if not path.isfile(self.local_filename): self.create_recursive(self.local_path) return self.__download(self.remote_filename, self.local_filename) return TileLoader agtl-0.8.0.3/files/advancedcaching/portrait.py000066400000000000000000000200431151564747700212310ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # gPodder - A media aggregator and podcast client # Copyright (c) 2005-2010 Thomas Perl and the gPodder Team # # gPodder is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # gPodder is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # import dbus import dbus.glib import hildon import osso # Replace this with your own gettext() functionality #import gpodder _ = lambda x: x class FremantleRotation(object): """thp's screen rotation for Maemo 5 Simply instantiate an object of this class and let it auto-rotate your StackableWindows depending on the device orientation. If you need to relayout a window, connect to its "configure-event" signal and measure the ratio of width/height and relayout for that. You can set the mode for rotation to AUTOMATIC (default), NEVER or ALWAYS with the set_mode() method. """ AUTOMATIC, NEVER, ALWAYS = range(3) # Human-readable captions for the above constants MODE_CAPTIONS = (_('Automatic'), _('Landscape'), _('Portrait')) # Privately-used constants _PORTRAIT, _LANDSCAPE = ('portrait', 'landscape') _ENABLE_ACCEL = 'req_accelerometer_enable' _DISABLE_ACCEL = 'req_accelerometer_disable' # Defined in mce/dbus-names.h _MCE_SERVICE = 'com.nokia.mce' _MCE_REQUEST_PATH = '/com/nokia/mce/request' _MCE_REQUEST_IF = 'com.nokia.mce.request' # sysfs device name for the keyboard slider switch KBD_SLIDER = '/sys/devices/platform/gpio-switch/slide/state' _KBD_OPEN = 'open' _KBD_CLOSED = 'closed' def __init__(self, app_name, main_window=None, version='1.0', mode=0): """Create a new rotation manager app_name ... The name of your application (for osso.Context) main_window ... The root window (optional, hildon.StackableWindow) version ... The version of your application (optional, string) mode ... Initial mode for this manager (default: AUTOMATIC) """ self._orientation = None self._main_window = main_window self._stack = hildon.WindowStack.get_default() self._mode = -1 self._last_dbus_orientation = None self._keyboard_state = self._get_keyboard_state() app_id = '-'.join((app_name, self.__class__.__name__)) self._osso_context = osso.Context(app_id, version, False) program = hildon.Program.get_instance() program.connect('notify::is-topmost', self._on_topmost_changed) system_bus = dbus.Bus.get_system() system_bus.add_signal_receiver(self._on_orientation_signal, \ signal_name='sig_device_orientation_ind', \ dbus_interface='com.nokia.mce.signal', \ path='/com/nokia/mce/signal') system_bus.add_signal_receiver(self._on_keyboard_signal, \ signal_name='Condition', \ dbus_interface='org.freedesktop.Hal.Device', \ path='/org/freedesktop/Hal/devices/platform_slide') self.set_mode(mode) def get_mode(self): """Get the currently-set rotation mode This will return one of three values: AUTOMATIC, ALWAYS or NEVER. """ return self._mode def set_mode(self, new_mode): """Set the rotation mode You can set the rotation mode to AUTOMATIC (use hardware rotation info), ALWAYS (force portrait) and NEVER (force landscape). """ if new_mode not in (self.AUTOMATIC, self.ALWAYS, self.NEVER): raise ValueError('Unknown rotation mode') if self._mode != new_mode: if self._mode == self.AUTOMATIC: # Remember the current "automatic" orientation for later self._last_dbus_orientation = self._orientation # Tell MCE that we don't need the accelerometer anymore self._send_mce_request(self._DISABLE_ACCEL) if new_mode == self.NEVER: self._orientation_changed(self._LANDSCAPE) elif new_mode == self.ALWAYS and \ self._keyboard_state != self._KBD_OPEN: self._orientation_changed(self._PORTRAIT) elif new_mode == self.AUTOMATIC: # Restore the last-known "automatic" orientation self._orientation_changed(self._last_dbus_orientation) # Tell MCE that we need the accelerometer again self._send_mce_request(self._ENABLE_ACCEL) self._mode = new_mode def _send_mce_request(self, request): rpc = osso.Rpc(self._osso_context) rpc.rpc_run(self._MCE_SERVICE, \ self._MCE_REQUEST_PATH, \ self._MCE_REQUEST_IF, \ request, \ use_system_bus=True) def _on_topmost_changed(self, program, property_spec): # XXX: This seems to never get called on Fremantle(?) if self._mode == self.AUTOMATIC: if program.get_is_topmost(): self._send_mce_request(self._ENABLE_ACCEL) else: self._send_mce_request(self._DISABLE_ACCEL) def _get_main_window(self): if self._main_window: # If we have gotten the main window as parameter, return it and # don't try "harder" to find another window using the stack return self._main_window else: # The main window is at the "bottom" of the window stack, and as # the list we get with get_windows() is sorted "topmost first", we # simply take the last item of the list to get our main window windows = self._stack.get_windows() if windows: return windows[-1] else: return None def _orientation_changed(self, orientation): if self._orientation == orientation: # Ignore repeated requests return flags = 0 if orientation != self._LANDSCAPE: flags |= hildon.PORTRAIT_MODE_SUPPORT if orientation == self._PORTRAIT: flags |= hildon.PORTRAIT_MODE_REQUEST window = self._get_main_window() if window is not None: hildon.hildon_gtk_window_set_portrait_flags(window, flags) self._orientation = orientation def _get_keyboard_state(self): # For sbox, if the device does not exist assume that it's closed try: return open(self.KBD_SLIDER).read().strip() except IOError: return self._KBD_CLOSED def _keyboard_state_changed(self): state = self._get_keyboard_state() if state == self._KBD_OPEN: self._orientation_changed(self._LANDSCAPE) elif state == self._KBD_CLOSED: if self._mode == self.AUTOMATIC: self._orientation_changed(self._last_dbus_orientation) elif self._mode == self.ALWAYS: self._orientation_changed(self._PORTRAIT) self._keyboard_state = state def _on_keyboard_signal(self, condition, button_name): if condition == 'ButtonPressed' and button_name == 'cover': self._keyboard_state_changed() def _on_orientation_signal(self, orientation, stand, face, x, y, z): if orientation in (self._PORTRAIT, self._LANDSCAPE): if self._mode == self.AUTOMATIC and \ self._keyboard_state != self._KBD_OPEN: # Automatically set the rotation based on hardware orientation self._orientation_changed(orientation) # Save the current orientation for "automatic" mode later on self._last_dbus_orientation = orientation agtl-0.8.0.3/files/advancedcaching/provider.py000066400000000000000000000313241151564747700212230ustar00rootroot00000000000000#!/usr/bin/python # -*- coding: utf-8 -*- # Copyright (C) 2010 Daniel Fett # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # Author: Daniel Fett agtl@danielfett.de # Jabber: fett.daniel@jaber.ccc.de # Bugtracker and GIT Repository: http://github.com/webhamster/advancedcaching # from math import sqrt from sqlite3 import connect, Row from copy import copy class PointProvider(): MAX_RESULTS = 1000 def __init__(self, filename, ctype, table): self.filterstack = [] self.conn = connect(filename) self.conn.row_factory = Row self.conn.text_factory = str self.ctype = ctype self.cache_table = table self.filterstring = [] self.filterargs = [] # yes, the synchronous=off setting is a bit dangerous for the database, # but the advantages outbalance unlikely database corruption self.conn.executescript( 'PRAGMA temp_store = MEMORY;' \ 'PRAGMA synchronous=OFF;' \ 'CREATE TABLE IF NOT EXISTS %s (%s);' % (self.cache_table, ', '.join('%s %s' % m for m in self.ctype.SQLROW.items()))) self.check_table() self.conn.executescript( 'CREATE INDEX IF NOT EXISTS %(table)s_latlon ON %(table)s (lat ASC, lon ASC);' \ 'CREATE INDEX IF NOT EXISTS %(table)s_name ON %(table)s (name ASC);' \ 'CREATE INDEX IF NOT EXISTS %(table)s_fieldnote ON %(table)s (logas);' % {'table' : self.cache_table} ) #c.execute('CREATE TABLE IF NOT EXISTS %s (%s)' % (self.cache_table, ', '.join(' '.join(m) for m in self.ctype.SQLROW.items()))) #self.check_table() #c.execute('CREATE INDEX IF NOT EXISTS %s_latlon ON %s (lat ASC, lon ASC)' % (self.cache_table, self.cache_table)) def check_table(self): c = self.conn.cursor() fields = copy(self.ctype.SQLROW) c.execute('PRAGMA TABLE_INFO(%s)' % self.cache_table) for row in c: if row[1] in fields: del fields[row[1]] # add all remaining fields for name, type in fields.items(): cmd = 'ALTER TABLE %s ADD COLUMN %s %s' % (self.cache_table, name, type) print "Updating your Database, adding Column %s to Table %s:\n%s" % (name, self.cache_table, cmd) c.execute(cmd) self.save() def get_table_info(self): c = self.conn.cursor() c.execute('PRAGMA TABLE_INFO(%s)' % self.cache_table) return c.fetchall() def save(self): self.conn.commit() def __del__(self): self.conn.commit() self.conn.close() def add_point(self, p, replace=False): if replace: self.conn.execute("INSERT OR REPLACE INTO %s (`%s`) VALUES (%s)" % (self.cache_table, '`, `'.join(self.ctype.SQLROW.keys()), ', '.join(':%s' % k for k in self.ctype.SQLROW.keys())), p.serialize()) return None else: c = self.conn.cursor() c.execute("SELECT found FROM %s WHERE name = ?" % self.cache_table, (p.name,)) num = len(c.fetchall()) existing = (num == 1) c.close() if existing: self.conn.execute("UPDATE %s SET found = ?, type = ?, lat = ?, lon = ?, status = ? WHERE name = ?" % self.cache_table, (p.found, p.type, p.lat, p.lon, p.status, p.name)) return False else: self.conn.execute("INSERT INTO %s (`%s`) VALUES (%s)" % (self.cache_table, '`, `'.join(self.ctype.SQLROW.keys()), ', '.join(':%s' % k for k in self.ctype.SQLROW.keys())), p.serialize()) return True # should be used with caution :-) def get_all(self): c = self.conn.cursor() c.execute('SELECT * FROM %s' % self.cache_table) return self.pack_result(c) # should never ever be used with anything except a user provided query def get_by_query(self, query): c = self.conn.execute(query) return self.pack_result(c) def get_points(self, c1, c2): c = self.conn.execute('SELECT * FROM %s WHERE (lat BETWEEN ? AND ?) AND (lon BETWEEN ? AND ?)' % self.cache_table, (min(c1.lat, c2.lat), max(c1.lat, c2.lat), min(c1.lon, c2.lon), max(c1.lon, c2.lon))) return self.pack_result(c) def get_titles_and_names(self): c = self.conn.execute('SELECT name, title FROM %s' % self.cache_table) strings = [] for row in c: strings.append(row['name']) strings.append(row['title']) c.close() return strings def get_new_fieldnotes_count(self): c = self.conn.execute('SELECT count(*) AS cnt FROM %s WHERE logas != %d' % (self.cache_table, self.ctype.LOG_NO_LOG)) for row in c: return row['cnt'] return 0 def get_new_fieldnotes(self): c = self.conn.execute('SELECT * FROM %s WHERE logas != %d' % (self.cache_table, self.ctype.LOG_NO_LOG)) return self.pack_result(c) def get_nearest_point_filter(self, center, c1, c2, found): filterstring = copy(self.filterstring) filterargs = copy(self.filterargs) filterstring.append('((lat BETWEEN ? AND ?) AND (lon BETWEEN ? AND ?))') filterargs.append(min(c1.lat, c2.lat)) filterargs.append(max(c1.lat, c2.lat)) filterargs.append(min(c1.lon, c2.lon)) filterargs.append(max(c1.lon, c2.lon)) if found == True: filterstring.append('(found = 1)') elif found == False: filterstring.append('(found = 0)') # we don't have 'power' or other advanced mathematic operators # in sqlite, so doing distance calculation in python query = 'SELECT * FROM %s WHERE %s' % (self.cache_table, " AND ".join(filterstring)) c = self.conn.execute(query, tuple(filterargs)) mindist = () # we use this as positive infinity mindistrow = None for row in c: # we have points very close to each other # for the sake of performance, using simpler # distance calc here dist = sqrt((row['lat'] - center.lat) ** 2 + (row['lon'] - center.lon) ** 2) if dist < mindist: mindistrow = row mindist = dist if mindistrow == None: return None coord = self.ctype(mindistrow['lat'], mindistrow['lon'], '', mindistrow) return coord def set_filter(self, found=None, has_details=None, owner_search='', name_search='', size=None, terrain=None, diff=None, ctype=None, adapt_filter=False, marked=None): # a value "None" means: apply no filtering on this value filter = copy(locals()) del filter['self'] self.filter = filter if adapt_filter: filterstring = copy(self.filterstring) filterargs = copy(self.filterargs) else: filterstring = [] filterargs = [] if found == True: filterstring.append('(found = 1)') elif found == False: filterstring.append('(found = 0)') if marked == True: filterstring.append('(marked = 1)') elif marked == False: filterstring.append('(marked = 0)') if has_details == True: filterstring.append("(desc != '' or shortdesc != '')") elif has_details == False: filterstring.append("NOT (desc != '' or shortdesc != '')") if owner_search != None and len(owner_search) > 2: filterstring.append("(owner LIKE '%%%s%%')" % owner_search) if name_search != None and len(name_search) > 2: filterstring.append("((name LIKE '%%%s%%') OR (title LIKE '%%%s%%'))" % (name_search, name_search)) if size != None: filterstring.append('(size IN (%s))' % (", ".join(str(b) for b in size))) if terrain != None: if type(terrain) == tuple: filterstring.append('(terrain >= ?) AND (terrain <= ?)') filterargs.append(terrain[0] * 10) filterargs.append(terrain[1] * 10) elif type(terrain) == list: filterstring.append('(terrain IN (%s))' % (", ".join('?' for b in terrain))) for b in terrain: filterargs.append(b * 10) if diff != None: if type(diff) == tuple: filterstring.append('(difficulty >= ?) AND (difficulty <= ?)') filterargs.append(diff[0] * 10) filterargs.append(diff[1] * 10) elif type(diff) == list: filterstring.append('(difficulty IN (%s))' % (", ".join('?' for b in diff))) for b in diff: filterargs.append(b * 10) if ctype != None: if len(ctype) > 0: filterstring.append('(type IN (%s))' % (", ".join('?' for b in ctype))) for b in ctype: filterargs.append(b) if len(filterstring) == 0: filterstring.append('1') self.filterstring = filterstring self.filterargs = filterargs def push_filter(self): self.filterstack.append((self.filterstring, self.filterargs)) def pop_filter(self): self.filterstring, self.filterargs = self.filterstack.pop() def get_points_filter(self, location=None, found=None, max_results=None): filterstring = copy(self.filterstring) filterargs = copy(self.filterargs) if max_results == None: max_results = self.MAX_RESULTS if location != None: c1, c2 = location filterstring.append('(lat BETWEEN ? AND ?) AND (lon BETWEEN ? AND ?)') filterargs.append(min(c1.lat, c2.lat)) filterargs.append(max(c1.lat, c2.lat)) filterargs.append(min(c1.lon, c2.lon)) filterargs.append(max(c1.lon, c2.lon)) if found == True: filterstring.append('(found = 1)') elif found == False: filterstring.append('(found = 0)') query = 'SELECT * FROM %s WHERE %s LIMIT %s' % (self.cache_table, " AND ".join(filterstring), max_results) c = self.conn.execute(query, tuple(filterargs)) return self.pack_result(c) def pack_result(self, cursor): points = [self.ctype(None, None, None, row) for row in cursor] cursor.close() return points def find_by_string(self, string): query = 'SELECT * FROM %s WHERE name LIKE ? OR title LIKE ? LIMIT 2' % self.cache_table c = self.conn.execute(query, (string, string)) row = c.fetchone() coord = self.ctype(None, None, None, row) # we cannot reliably determine # of results, so using workaround here if c.fetchone() != None: return None return coord def update_field(self, coordinate, field, newvalue, save = True): query = 'UPDATE %s SET %s = ? WHERE name = ?' % (self.cache_table, field) self.conn.execute(query, (newvalue, coordinate.name)) if save: self.save() def get_by_name(self, gcname): query = 'SELECT * FROM %s WHERE name LIKE ? LIMIT 2' % self.cache_table c = self.conn.execute(query, (gcname,)) row = c.fetchone() if row != None: coord = self.ctype(None, None, None, row) return coord else: return None def remove_geocaches(self, list): names = [x.name for x in list if x.name != ''] query = 'DELETE FROM %s WHERE name IN (%s)' % (self.cache_table, (','.join('?' for x in names))) self.conn.execute(query, tuple(names)) self.save() def optimize(self): self.conn.execute('VACUUM') agtl-0.8.0.3/files/advancedcaching/pyfo.py000066400000000000000000000105251151564747700203460ustar00rootroot00000000000000"""pyfo - Generate XML using native python data structures. Created and maintained by Luke Arno See documentation of pyfo method in this module for details. Copyright (C) 2006-2007 Central Piedmont Community College 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.1 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Central Piedmont Community College 1325 East 7th St. Charlotte, NC 28204, USA Luke Arno can be found at http://lukearno.com/ """ from xml.sax.saxutils import escape def isiterable(it): """return True if 'it' is iterable else return False.""" try: iter(it) except: return False else: return True def make_attributes(dct): """Turn a dict into string of XML attributes.""" return u"".join((' %s="%s"' % (x, escape(unicode(y))) for x, y in dct.iteritems())) def pyfo(node, prolog=False, pretty=False, indent_size=2, encoding='utf-8', collapse=True): """Generate XML using native python data structures. node structure like (name, contents) or (name, contents, attribs) accepts stings, callables, or another node structure. pyfo should be called with a tuple of two or three items like so: (element, contents, attributes) or a string. for a tuple: the first item: is the element name. the second item: if it is callable, it is called and its return value . if it is a list, pyfo is called on all its members and the results are concatenated to become the contents. otherwise it is run through 'unicode' and 'escape'. optional third item: should be a dictionary used as xml attributes for a string: just return it as unicode. """ if callable(node): node = node() if not node: return u"" if pretty and pretty >= 0: if pretty is True: pretty = 1 indent = '\n' + (" " * indent_size * pretty) unindent = '\n' + (" " * indent_size * (pretty-1)) pretty += 1 else: unindent = indent = "" if isinstance(node, basestring): return unicode(node) elif len(node) == 2: name, contents = node dct = {} else: name, contents, dct = node leaf = False if callable(contents): contents = contents() if isinstance(contents, dict): contents = contents.items() if isinstance(contents, tuple): contents = pyfo(contents, pretty=pretty, indent_size=indent_size, collapse=collapse) elif not isinstance(contents, basestring) and isiterable(contents): cgen = (pyfo(c, pretty=pretty, indent_size=indent_size, collapse=collapse) for c in contents) contents = indent.join((c for c in cgen if c)) elif contents not in [None, ""]: contents = escape(unicode(contents)) leaf = True if leaf: indent = unindent = "" if prolog: prolog = u'\n' % encoding else: prolog = u'' if contents or not collapse: return u'%s<%s%s>%s%s%s' % (prolog, name, make_attributes(dct), indent, contents or '', unindent, name) else: return u'%s<%s%s/>' % (prolog, name, make_attributes(dct)) agtl-0.8.0.3/files/advancedcaching/qt/000077500000000000000000000000001151564747700174405ustar00rootroot00000000000000agtl-0.8.0.3/files/advancedcaching/qt/__init__.py000066400000000000000000000014431151564747700215530ustar00rootroot00000000000000#!/usr/bin/python # -*- coding: utf-8 -*- # Copyright (C) 2010 Daniel Fett # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # Author: Daniel Fett advancedcaching@fragcom.de # agtl-0.8.0.3/files/advancedcaching/qt/geocachedetailswindow.py000066400000000000000000000132661151564747700243560ustar00rootroot00000000000000#!/usr/bin/python # -*- coding: utf-8 -*- # Copyright (C) 2010 Daniel Fett # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # Author: Daniel Fett agtl@danielfett.de # Jabber: fett.daniel@jaber.ccc.de # Bugtracker and GIT Repository: http://github.com/webhamster/advancedcaching # import logging import geocaching import re from PyQt4.QtCore import * from PyQt4.QtGui import * from showimagedialog import QtShowImageDialog from ui_geocachedetailswindow import Ui_GeocacheDetailsWindow from os import path, extsep d = lambda x: x.decode('utf-8') logger = logging.getLogger('qtgeocachewindow') class QtGeocacheDetailsWindow(QMainWindow, Ui_GeocacheDetailsWindow): download_details = pyqtSignal() ICONS = { geocaching.GeocacheCoordinate.LOG_TYPE_FOUND: 'emoticon_grin', geocaching.GeocacheCoordinate.LOG_TYPE_NOTFOUND: 'cross', geocaching.GeocacheCoordinate.LOG_TYPE_NOTE: 'comment', geocaching.GeocacheCoordinate.LOG_TYPE_MAINTENANCE: 'wrench', geocaching.GeocacheCoordinate.LOG_TYPE_PUBLISHED: 'accept', geocaching.GeocacheCoordinate.LOG_TYPE_DISABLED: 'delete', geocaching.GeocacheCoordinate.LOG_TYPE_NEEDS_MAINTENANCE: 'error', geocaching.GeocacheCoordinate.LOG_TYPE_WILLATTEND: 'calendar_edit', geocaching.GeocacheCoordinate.LOG_TYPE_ATTENDED: 'group', geocaching.GeocacheCoordinate.LOG_TYPE_UPDATE: 'asterisk_yellow', } def __init__(self, core, parent=None): QMainWindow.__init__(self, parent) self.core = core self.setupUi(self) self.actionDownload_Details.triggered.connect(self.__download_details) self.core.connect('cache-changed', self.__cache_changed) def __download_details(self): self.core.update_coordinates([self.current_geocache]) def __cache_changed(self, caller, geocache): if geocache.name == self.current_geocache.name: self.show_geocache(geocache) def show_geocache(self, geocache): self.current_geocache = geocache # window title self.setWindowTitle("Geocache Details: %s" % d(geocache.title)) # information labels = ( (self.labelFullName, geocache.title), (self.labelID, geocache.name), (self.labelType, geocache.type), (self.labelSize, geocache.get_size_string()), (self.labelTerrain, geocache.get_terrain()), (self.labelDifficulty, geocache.get_difficulty()), (self.labelOwner, geocache.owner), (self.labelStatus, geocache.get_status()) ) for label, text in labels: label.setText(d(text)) if geocache.desc != '' and geocache.shortdesc != '': showdesc = "%s
%s" % (geocache.shortdesc, geocache.desc) elif geocache.desc == '' and geocache.shortdesc == '': showdesc = "No description available" elif geocache.desc == '': showdesc = geocache.shortdesc else: showdesc = geocache.desc showdesc = d(showdesc) showdesc = re.sub(r'\[\[img:([^\]]+)\]\]', lambda a: "" % self.get_path_to_image(a.group(1)), showdesc) self.labelDescription.setText(showdesc) # logs and hints logs = [] for l in geocache.get_logs(): logs.append(self.__get_log_line(l)) self.labelLogs.setText(''.join(logs)) hint = d(geocache.hints).strip() if len(hint) > 0: self.pushButtonShowHint.clicked.connect(lambda: self.__show_hint(hint)) else: self.pushButtonShowHint.hide() # images self.listWidgetImages.clear() images = geocache.get_images() if len(images) > 0: i = 0 for filename, description in images.items(): file = self.get_path_to_image(filename) icon = QIcon(file) m = QListWidgetItem(icon, d(description), self.listWidgetImages) m.setData(Qt.UserRole, QVariant(i)) i += 1 self.listWidgetImages.itemClicked.connect(lambda item: self.__show_image(item.icon().pixmap(QApplication.desktop().size()))) else: self.tabImages.deleteLater() def __get_log_line(self, log): icon = "%s%spng" % (path.join(self.core.dataroot, self.ICONS[log['type']]), extsep) date = "%4d-%02d-%02d" % (int(log['year']), int(log['month']), int(log['day'])) finder = d(log['finder']) line1 = "%s%s" % (icon, finder, date) line2 = "%s" % log['text'].strip() line3 = "td colspan='2'>
" return ''.join((line1, line2, line3)) def __show_hint(self, text): QMessageBox.information(self, "Hint, Hint!", text) def get_path_to_image(self, image): return path.join(self.core.settings['download_output_dir'], image) def __show_image(self, pixmap): m = QtShowImageDialog(self) m.show_image(pixmap) m.show() agtl-0.8.0.3/files/advancedcaching/qt/icons_rc.py000066400000000000000000000752421151564747700216230ustar00rootroot00000000000000# -*- coding: utf-8 -*- # Resource object code # # Created: Mi. Aug 4 14:54:51 2010 # by: The Resource Compiler for PyQt (Qt v4.6.2) # # WARNING! All changes made in this file will be lost! from PyQt4 import QtCore qt_resource_data = "\ \x00\x00\x02\xf1\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ \x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\ \x00\x00\x00\x04\x67\x41\x4d\x41\x00\x00\xaf\xc8\x37\x05\x8a\xe9\ \x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\x74\x77\x61\x72\x65\ \x00\x41\x64\x6f\x62\x65\x20\x49\x6d\x61\x67\x65\x52\x65\x61\x64\ \x79\x71\xc9\x65\x3c\x00\x00\x02\x83\x49\x44\x41\x54\x38\xcb\x8d\ \xd1\x4d\x4c\xd2\x71\x1c\x06\x70\xeb\xd0\x96\x1e\x3a\x75\xe8\xed\ \x80\x64\x1d\xd2\xd6\x8b\xce\x39\xb6\x88\xf0\xd0\x58\xba\xe6\x40\ \x86\xa8\xc8\x42\x4c\xc9\xa1\x22\x19\x06\x64\x82\xa5\x5b\x8a\xfe\ \xc1\x3f\x5a\x29\x94\xe8\xdf\x52\x0c\xcd\x11\xeb\xa2\x07\x6d\xa6\ \xe2\x7b\x58\x6b\x83\x43\x0a\xdd\x9a\x39\x6b\xcb\xf0\x89\xd8\x32\ \x9b\xcc\x3c\x3c\xa7\xdf\xbe\x9f\x3d\x7b\x7e\x51\x00\xa2\x76\x8a\ \x9a\x7b\x58\x3c\x6f\x8e\x5b\x59\xea\x3c\x02\xaf\x95\xb6\x6e\x91\ \xc7\xb6\xa5\x27\xc7\xee\xfb\xf3\xbe\xe3\xb1\x9c\x73\x94\xed\xeb\ \xa0\x05\x97\xbb\x0e\x61\x6b\x5a\x65\xf4\xc6\x5d\x01\x6d\xa2\x83\ \xc4\x72\x5f\x22\xfc\xae\x7c\x7c\x1b\xab\x87\xdf\xce\xc0\x32\x75\ \x0c\x0b\x66\xfa\x97\x5d\x01\x24\xf7\x40\x7d\x60\xc6\x85\x95\x51\ \x12\xeb\x33\x4f\xb0\xfa\x86\xc4\xd2\xb4\x13\x43\xba\xb3\xbe\x5d\ \x01\x19\x9c\x4c\xa9\xc7\x56\xb6\xf1\x75\xb6\x17\xc1\xc5\x3e\xac\ \x2d\x38\x10\x18\x26\x50\x9d\x7b\xd1\x1e\x11\x90\xb2\xe2\xc4\xcd\ \xe2\x4b\x2b\x44\x0e\x0b\x64\x1e\x7b\xbd\x90\x9f\x30\x49\x54\xc8\ \x7e\x04\xec\x0a\x7c\xea\xbf\x8b\xc0\x80\x06\x23\x2d\x37\x70\x3a\ \xff\x94\x33\x49\x19\x1f\xf3\x0f\x20\x62\x1c\x67\x37\x8b\x53\x83\ \x8d\x42\x16\xb6\xa6\xdd\x20\x87\xb1\x55\x8f\x3a\x93\x06\x77\x1e\ \x28\xd1\xff\xce\x86\x9a\xa1\x6a\x24\x68\x4f\x5a\xcf\x28\x4e\x44\ \x6f\x02\x06\xfe\xb9\x91\x87\x85\x57\xd1\x57\x75\x0d\x9e\x2e\x35\ \x3a\x15\x3c\x10\x39\x6c\xb4\x14\xa4\xc2\x32\xa3\x86\x79\xaa\x02\ \x0d\xee\x52\xd4\xba\xcb\xd0\xf3\xbe\x0d\x25\x8e\x62\xa4\x14\x26\ \x91\x9b\x80\x89\x4f\xef\xf9\x38\x44\x6d\x1b\x8c\xaa\x64\xc1\x30\ \x75\x0b\x75\x6e\x25\x74\x13\x25\xb8\x3d\x51\x04\xd5\xdb\x22\x88\ \xc9\x3c\x78\xc9\xf4\xff\x0f\x56\xa7\xe3\xc2\x34\xad\x42\x43\xa8\ \x81\x61\xea\x26\x9a\x42\x29\x1f\x94\x21\xcb\x90\x05\xff\xa3\x2b\ \x7f\x81\xbd\xe7\x9b\x54\x26\x55\xf1\xb6\xc1\xe2\x79\xe5\x90\xd8\ \xb2\x71\xbd\x5b\x80\x02\x4a\x00\x49\x47\x26\x2e\xe8\x19\x3e\xa5\ \xb4\xaa\xca\x6b\x11\x08\xc3\xc7\x7b\x18\xdd\x92\x0c\x85\x7b\x75\ \xf8\x33\x70\xef\xb9\x1d\x9a\x76\x0a\x4a\xc2\x8a\xc7\xe3\xdf\xa1\ \x34\x2d\x61\x7f\x4a\xb7\x35\x86\xd9\x1a\x1d\xe9\xab\xa3\x68\x9c\ \x16\x1e\x4f\x3e\xb8\x36\xb0\x18\x84\xde\x03\xe4\x8f\x03\xb9\xc3\ \x40\xe6\x20\xc0\xa7\x00\x62\x14\x10\x6a\x3c\x88\x63\xd7\x90\x11\ \x81\x39\x7d\xe2\xcf\xc9\x5e\x23\x14\x2f\x02\x10\xb9\x00\x91\x63\ \x03\x42\x6a\x03\x3c\x6b\x10\x69\x66\x20\xcd\x18\xc4\x53\xcb\xcb\ \xf0\x60\x11\x01\x5f\x13\xd3\xdf\x5b\x53\x0a\x81\x76\x0c\xd2\xd7\ \x80\xf8\x55\xa8\x81\x03\xc8\x19\x00\xb2\x7f\xb7\xa8\xf5\x42\xab\ \xb8\x1f\x1e\x2c\x22\xf0\xa1\xa3\x20\xc5\x59\x9d\x51\x99\xcc\xb3\ \x39\x12\x4b\x26\xc0\xd2\xce\x85\x32\x0f\x96\x7a\x16\xcc\x4a\x37\ \xe8\x97\x9f\xf9\x8c\xe5\x92\xf0\x60\x91\x80\x5f\x41\x7f\xb2\x00\ \x90\xb7\x7c\x41\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ \ \x00\x00\x01\x9d\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ \x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\ \x00\x00\x00\x04\x67\x41\x4d\x41\x00\x00\xaf\xc8\x37\x05\x8a\xe9\ \x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\x74\x77\x61\x72\x65\ \x00\x41\x64\x6f\x62\x65\x20\x49\x6d\x61\x67\x65\x52\x65\x61\x64\ \x79\x71\xc9\x65\x3c\x00\x00\x01\x2f\x49\x44\x41\x54\x38\xcb\x63\ \xf8\xff\xff\x3f\x03\x25\x98\x81\x6a\x06\x44\xb4\xef\x10\x02\xe2\ \xa5\x40\xfc\x13\x88\xff\xe3\xc1\xbb\x80\x58\x05\x9b\x01\x1b\x57\ \x1e\xbc\xf5\xff\xf7\x9f\xbf\xff\xf1\x81\x2d\x27\xef\x83\x0c\xb9\ \x0d\xc4\x6c\x70\x03\x80\x1c\x1e\x20\xfe\xf3\xf7\xdf\xbf\xff\x1f\ \xbe\xfd\xfb\xff\xf2\xd3\xbf\xff\xcf\x3e\xfe\xfb\xff\xe4\xfd\xbf\ \xff\x0f\xdf\xfd\xfb\x7f\xef\xcd\xbf\xff\xb7\x5f\xfd\xfd\x7f\xe7\ \x15\xc4\xf0\xea\x05\xc7\x41\x86\x98\x21\x1b\x20\x97\x33\xf5\x00\ \x58\xf2\x05\x50\xe3\xb3\x0f\xff\xfe\x3f\x06\x6a\x7e\xf0\xf6\xdf\ \xff\xbb\x50\xcd\xd7\x5f\xfc\xfd\x7f\xf5\x19\xc4\x80\xce\x55\x67\ \x41\x06\x78\x63\x35\xe0\x29\x50\xf3\xa3\x77\x50\xcd\xaf\xff\xfd\ \xbf\xf5\x12\xa2\xf9\x0a\x50\xf3\xc5\x27\xb8\x0d\x80\x7b\x01\xe4\ \xfc\xfb\x40\x5b\xef\xbc\xfe\xfb\xff\x26\x50\xf3\xb5\xe7\x7f\xff\ \x5f\x06\x6a\xbe\xf0\x04\x64\xc0\x1f\xb0\x01\xb5\x8b\x4e\x80\x0c\ \xb0\xc4\x1a\x88\x20\x43\x90\x01\xc8\x35\x17\x1e\xff\x05\x1b\x8c\ \x16\x88\x9c\xb8\xa2\xf1\x0f\x2c\xca\xe6\xee\xb8\x0a\x37\xe8\xfd\ \xe7\x1f\x60\x3e\x50\xfc\x32\xd6\x68\xc4\x86\x81\x0a\xef\x3c\x7f\ \xf7\x15\xa6\xf1\x3b\x10\xd7\x12\x9d\x12\x81\x8a\x7b\x67\x6e\xbb\ \x02\xd2\xf8\x05\x88\x0b\x49\x4a\xca\x40\x0d\x4a\x40\x7c\x0f\x88\ \x53\xe8\x97\x17\x06\xcc\x00\x00\xf7\x8b\x0f\x22\xbe\xe2\x6a\x0b\ \x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ \x00\x00\x02\xe7\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ \x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\ \x00\x00\x00\x04\x67\x41\x4d\x41\x00\x00\xaf\xc8\x37\x05\x8a\xe9\ \x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\x74\x77\x61\x72\x65\ \x00\x41\x64\x6f\x62\x65\x20\x49\x6d\x61\x67\x65\x52\x65\x61\x64\ \x79\x71\xc9\x65\x3c\x00\x00\x02\x79\x49\x44\x41\x54\x38\xcb\xa5\ \x93\xcd\x4b\x54\x61\x14\xc6\x9f\x7f\x61\x16\x2d\x6a\x39\xb4\x69\ \xd1\x6a\xa0\x85\x44\x10\x0c\xd1\xc6\x45\xc1\x54\x90\xa5\x51\xcd\ \x40\x05\x95\x31\x0c\x1a\x49\x26\x56\xb6\x29\x8b\x88\xa9\xec\x3b\ \x99\x44\x8c\xac\xec\xe3\x2d\x73\x44\xad\xe8\x22\xd7\xcc\x8f\x51\ \x67\x9c\x99\x1a\x52\xd3\x6b\x8e\x8e\x39\x1f\x77\x9e\x8e\x90\x8a\ \x35\xae\x5a\x9c\xc5\xfb\xde\xfb\x3c\xe7\x9c\xdf\x39\x2f\x48\xe2\ \x7f\x62\xd9\x21\xdb\x0a\x9b\xe9\x87\xca\xbc\x03\xd3\xcd\x70\x2c\ \xdc\xcf\xbe\x84\x33\xd1\x04\x4e\x3f\x83\x9a\x6a\x84\x35\xa7\x81\ \x88\x9d\xe6\x47\x2b\xb3\x43\x2e\x66\xc3\x25\x4c\x2a\xa8\x85\x6f\ \x33\x4d\xd0\xcc\x60\x29\x93\x9d\xdb\x38\xf9\x18\xc6\x44\xfd\x92\ \xf9\xa2\x81\xd9\x02\x7b\x46\x5b\xcf\x6c\xec\x1c\x39\x5a\xcd\xb9\ \x96\x55\x9c\x7d\x01\xeb\xcc\x73\xd8\x13\xfe\x75\x64\xe4\x34\x33\ \x81\x62\x8a\xd8\xf8\x51\x07\x5b\xce\x16\x52\x6f\xe1\x49\xeb\x76\ \x72\xa4\x9a\xa9\xee\x9d\x14\xb1\x37\xfe\x14\xde\xa4\xbe\x83\x0c\ \x97\x71\xa6\x35\x8f\x63\x8f\xe0\x5c\x91\xc1\x7c\xfc\x7a\x05\x95\ \xd2\xf3\xa5\x92\x2a\xc6\x1b\x61\x24\xf5\x5d\x92\xbd\x9c\x89\xf7\ \x5b\xe6\xc5\xde\x7f\x20\x0a\x2c\x5b\xf2\xcd\x52\x49\x02\xcb\x22\ \xb0\x8c\x6c\xb4\x82\xd9\xd0\x31\x32\x78\x9c\xe9\x1e\x27\xc7\x7c\ \xd0\x46\x6a\x61\x59\xf8\x2f\x72\x0b\xb6\xe1\x1b\xb0\x41\xc4\x2a\ \xd9\x66\xa5\x90\xa6\x88\x8d\x3f\xa4\xb5\x4c\xc0\x45\x0e\x1e\x20\ \x03\x45\x4c\x77\x17\xf2\xfb\x43\x68\xb1\xfb\x50\x5f\xef\x20\x18\ \xa9\x01\x63\xbe\x35\x1c\xba\x06\x85\xb9\xd7\x50\xe6\x70\x29\x39\ \xfe\x80\x66\xb8\x9c\xe9\x41\x37\xd3\x03\xc5\xd2\x73\x09\x39\xb0\ \x8f\xec\x2f\x10\xa3\xc3\x4c\xf5\x1c\xe5\x9c\x7e\x90\xe9\x3e\x37\ \x19\xaa\x64\xe2\x53\x21\x7b\x2f\x89\x81\x90\x56\x02\x8b\x02\x8b\ \x3f\x9f\x40\x9b\x6c\x80\x12\xd2\x2a\xf5\x79\x8f\x64\x17\x83\xbe\ \xdd\x4c\xe9\x05\x8c\xde\x86\x16\xae\x81\x0a\x5d\x47\x70\xf0\x2a\ \xd8\x7b\x11\xec\xaa\x12\x83\xbf\xa1\x8c\xd7\xc3\x22\xb0\x0c\x33\ \x74\x8a\x99\x9e\xfd\x52\x85\x8b\x49\x7d\x2f\xc3\x37\xa1\x85\xbc\ \x4b\x0c\x56\x9c\x82\x88\xd5\x74\xfb\x56\xa6\xfa\x8b\xf9\xed\x2e\ \x82\xd3\x1d\xdb\xa5\x85\x13\x9c\x50\x9b\x19\xb8\x92\x63\x0a\xcb\ \xc4\x3e\x9c\x9f\xf2\x6f\x22\xa3\x95\x34\x54\x1e\x85\xb4\x53\x48\ \x7b\xa7\x5a\xf3\x85\xc5\x11\xc6\x1a\x36\xb0\xf3\x2c\x3c\x39\x0d\ \x46\x7d\x70\x4c\x2a\x9b\xac\xf1\x19\x66\x06\x3c\x14\xd2\x86\x88\ \x2d\x42\xda\x1e\xad\x5d\x2b\x2c\x0e\x49\x2b\x2e\x7e\xb9\xbc\x9a\ \xed\x27\x73\x6c\xa2\x8c\xc9\x11\xbb\x07\x23\xde\x21\xfb\xde\x6c\ \xa7\xc0\x5a\xcc\xd4\x75\x01\x5a\xbc\xa3\x88\xd1\xba\x8d\x6c\x2b\ \x85\xe1\x77\xe7\x78\x0b\xf3\x21\xa4\xad\x42\xba\x4e\xc4\x4a\x48\ \x2f\xbe\x3a\xad\x02\x8e\x0f\x65\x50\x6d\x25\xf0\xb4\xb8\x97\x83\ \xfc\x0d\x4c\xdb\xc0\x3a\x1b\x25\xf2\x81\x00\x00\x00\x00\x49\x45\ \x4e\x44\xae\x42\x60\x82\ \x00\x00\x02\xcb\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ \x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\ \x00\x00\x00\x04\x67\x41\x4d\x41\x00\x00\xaf\xc8\x37\x05\x8a\xe9\ \x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\x74\x77\x61\x72\x65\ \x00\x41\x64\x6f\x62\x65\x20\x49\x6d\x61\x67\x65\x52\x65\x61\x64\ \x79\x71\xc9\x65\x3c\x00\x00\x02\x5d\x49\x44\x41\x54\x38\xcb\xa5\ \x93\xfb\x4b\x53\x61\x18\xc7\xfd\x5b\xb6\x1f\xa2\x04\x89\x6e\x84\ \x84\x51\x50\x98\xf3\x32\x77\xd6\xda\xdc\xa6\xce\xb3\x4c\x8f\x5b\ \x2c\x62\x69\x8e\x9d\x61\x9a\x41\x8d\xc5\x5c\xf8\x43\xa8\xa5\x76\ \xd5\xca\x5f\x32\x4d\x6c\x94\x5a\x46\x6a\xd7\xa1\xe5\xb1\xf2\xd2\ \x4e\x4d\x6a\x6d\x9e\xb3\x6b\xca\xb7\xb9\x60\x26\x2e\x23\x7a\xe1\ \xfb\xcb\xcb\xfb\xf9\xbc\x3c\x0f\xcf\x93\x02\x20\xe5\x7f\xb2\xe6\ \x62\x56\xaf\x17\xce\x50\x15\xe6\xf7\x54\x19\x33\xa5\x25\xb9\x49\ \xad\x86\x7b\x47\xaa\x99\x71\x52\x69\x76\x95\xc8\x85\xeb\x0a\xe6\ \x74\x7a\xe2\x23\x45\xb1\xdf\x1c\x36\x84\x07\x9d\x88\xbc\x19\x45\ \x64\x64\x08\xfc\xdd\x0e\xcc\x1a\x4a\xf1\xaa\x90\x60\x9f\xab\xc5\ \x44\x52\xc1\x32\x3c\x5d\x4e\xf1\x0b\xb7\x3b\xb0\x34\xf5\x16\xd1\ \xbe\x3b\x88\xb6\xdb\x11\x6d\x3e\x87\x1f\x37\x9b\xb0\x38\xdc\x07\ \x8f\xc9\x80\x51\x65\x36\xff\x4c\x9e\x49\xac\x12\xcc\xe8\x74\x82\ \x18\xec\xe6\xae\xb7\x63\x89\x71\x21\x7a\xf1\x0c\x7c\x76\x0b\xfc\ \xb6\x6a\x84\x2f\x58\x10\x69\xa0\x11\xb6\x9e\x40\xf8\xde\x0d\xcc\ \x1d\x25\x31\x7c\x68\x9f\xfb\xb1\x6c\x8f\x20\x21\x88\xc1\xf4\x7c\ \xad\x19\x8b\xae\xb1\xf8\x8f\x21\x07\x0d\xef\x59\x23\x82\x75\xba\ \x55\xe1\x4e\x92\x08\x77\x5d\xc1\xcb\xbc\x0c\x0c\x48\x33\xe8\x84\ \xe0\x03\x75\x84\x09\x74\x5d\x45\xb4\xb3\x19\x3e\x6b\x25\xbe\x16\ \x49\x93\x66\xa1\x92\x04\x6f\xab\x81\xc7\x52\x85\x87\x44\x3a\x93\ \x10\x30\xe5\xda\x60\xe4\x7e\x17\xa2\x0e\x0b\x7c\xa7\x0d\xf8\xd3\ \xf1\x28\x72\xe0\xa5\x0a\xe1\x6f\x6e\x84\x33\x6f\x47\x30\x21\x98\ \x24\x8b\x82\xa1\xce\x56\x84\xeb\x0d\x08\x9e\x2a\x5b\x57\x30\x5f\ \xaa\x82\xbf\xa9\x11\xfd\xe2\x2d\x2b\x82\x89\x12\x15\xe3\xb5\xd6\ \x20\x64\xa7\xc1\x1d\x57\xc7\x1f\x26\x8d\x32\x0f\xbe\x5a\x13\x66\ \x4d\x46\xf4\x89\xd2\x56\x4a\x70\x15\xcb\x69\x46\x26\x42\xb0\xb3\ \x0d\x3e\xad\x0c\xde\x52\xc9\x1a\x98\x95\x67\x83\x2d\x90\x20\xd0\ \x7e\x09\x43\xe2\x6d\xe8\xcd\xda\xb4\xd2\xc4\xd7\x45\x52\xc1\x0b\ \x0d\xe1\x9e\xab\xd0\x20\x70\xab\x35\xde\xb0\x79\x95\xf8\x17\xa8\ \xc8\x05\x2b\x8b\xc1\x32\x31\xf8\xb6\x16\x8c\x17\x4b\x97\x61\x77\ \xb7\x68\xa3\x60\xd5\x20\x8d\x15\xe4\x10\x23\x8a\x03\xfc\xf4\x61\ \x15\x02\xd7\x5a\xf1\xbd\x9e\x86\x87\x54\xe2\xb3\x5a\x01\x6f\x9d\ \x19\xfc\xe5\x16\x4c\xa8\xf3\xd1\x93\x95\xca\xc7\x60\x22\xe9\x28\ \x3f\x95\xef\x27\x9e\x1c\xdc\xcb\x8e\x4a\x76\xe1\x4b\xb5\x11\xde\ \x86\xf3\xf1\x7c\xaa\x3a\x86\x47\x39\x5b\x97\x61\xf6\x77\x38\xe9\ \x32\x0d\x4a\x77\x0b\x07\xc4\xe9\x66\x27\xb1\x93\x79\x90\xbf\x9d\ \xeb\x17\x6d\xe6\x7a\x73\xd3\x98\x9e\xec\x54\x73\x77\xe6\x06\xe1\ \x5f\xb7\xf1\x5f\xf3\x13\x1d\xd2\xce\xb9\x49\x72\x1b\xfe\x00\x00\ \x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ \x00\x00\x03\x0d\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ \x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\ \x00\x00\x00\x04\x67\x41\x4d\x41\x00\x00\xaf\xc8\x37\x05\x8a\xe9\ \x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\x74\x77\x61\x72\x65\ \x00\x41\x64\x6f\x62\x65\x20\x49\x6d\x61\x67\x65\x52\x65\x61\x64\ \x79\x71\xc9\x65\x3c\x00\x00\x02\x9f\x49\x44\x41\x54\x38\xcb\xa5\ \x93\xeb\x4b\x53\x61\x1c\xc7\xfd\x3b\x76\xce\x76\x6c\x03\x09\x64\ \x44\x21\x82\x84\x50\x7b\x15\x24\x12\x3b\x9a\x0d\xc5\xbc\x2c\x4b\ \xd3\xdd\xbd\xd2\x36\x63\xd8\x4c\x8b\x32\x72\x5e\xc6\x48\x29\x2d\ \xb3\xd4\x6a\x73\x4e\x6d\xea\xd4\xe6\xd6\x94\x32\x71\xd9\x51\x42\ \xcc\xbd\xe9\x42\xb5\x61\x74\xb1\x6f\xe7\xec\xc5\x4c\x12\x23\x7a\ \xe0\x0b\x0f\x0f\xcf\xe7\xf3\x7b\xae\x71\x00\xe2\xfe\x27\x7f\x0c\ \x14\xf8\x0e\x89\x72\xa7\x0f\xea\xb3\x3d\x29\x4c\xc6\xe3\xfd\x61\ \xe9\x88\x38\x2c\x75\x25\x32\x52\x67\xa2\x3e\xdd\xbe\x57\xb4\xab\ \x20\xcf\x9b\x4a\xcb\x3c\xc9\x21\x9d\x47\x86\x9b\x41\x0b\xfa\x96\ \xbb\xa2\xe9\x5c\x6c\x46\x89\xeb\x18\x24\xbd\x54\x48\xd2\x43\xd1\ \x3b\x0a\xd8\xaa\x74\xe6\x78\x52\xe4\xea\x9c\x11\xce\xd5\x7e\xd8\ \x5e\x5e\x83\x69\xae\x32\x1a\xae\xef\x58\xed\x43\xe3\x4c\x15\x0e\ \xd8\xf8\x91\x64\x1b\x9f\xde\x26\xc8\xf1\xa4\x08\x33\xdd\x49\xeb\ \x1c\xcc\x4d\xac\x09\x94\xa1\xc2\x5f\x02\xcd\xcc\x19\xe8\xd8\x94\ \xb3\xa9\xf6\x9d\x85\xfd\xf5\x3d\x5c\x9c\xaa\x80\xd8\x42\xae\x8b\ \xaf\x93\xc2\x98\x40\xe6\x4e\x32\xa8\xc6\xb2\xa2\x95\x39\x98\x03\ \x55\xde\x53\x50\x4c\x17\x42\x31\x55\x00\xf5\x54\x21\xdc\x6b\x83\ \x30\x78\x95\x70\xb0\x92\xdc\x9e\x23\x48\xb8\x42\x1a\x62\x82\x8c\ \x11\x31\xd3\x19\x6c\x86\x35\xd8\x84\x0a\x5f\x31\x94\x4f\xe4\x2c\ \x98\x0f\xe5\x24\x1b\x4f\x3e\xc6\xdf\xb8\xc0\xb5\x50\x64\x0d\x6d\ \xcf\x1b\x61\x9b\x6b\x44\x7c\x3d\xc9\xc4\x04\x47\xed\x09\x1b\x0f\ \x56\x6e\xa3\x36\xa0\x81\xd6\x5b\xc4\xae\x64\x00\x8b\x1f\xe6\xa1\ \x9a\x28\xc4\xd8\xda\x50\x14\xfe\xb1\xf9\x1d\x6d\xcf\x2e\xc1\x30\ \x51\x8c\xbe\x60\x27\x04\x46\x62\x23\x26\x90\xdc\xa7\x36\xfa\x97\ \xbb\x61\xf4\xab\x50\xeb\xd7\xe2\xd3\xd7\x8f\x51\xe8\xfd\x97\xb7\ \x31\xd8\x32\x5b\x0f\xb5\x2b\x1b\x7a\xf7\x69\xf4\x07\x3b\x20\xa8\ \xf9\x5d\xd0\x43\x31\x37\xe6\x9b\xd0\xbe\x70\x19\xba\x49\x39\xcc\ \xbe\x6a\x44\xbe\x7d\x8e\xc2\x9b\x3f\x37\x61\x79\x7a\x01\x65\xce\ \x2c\x68\x58\x41\x4b\xa0\x0e\xed\x5e\x33\xa8\x2a\x62\x6b\x0b\xa9\ \xb7\x04\x06\xf9\x40\x1a\xec\x2b\x77\x51\x3d\x21\x87\xda\x7d\x12\ \x75\xd3\xe5\x58\x7a\xb7\x80\xb6\xd9\x06\x94\x0e\x1e\x87\xc2\x71\ \x02\x3a\x67\x0e\xec\xaf\xba\x91\x6e\x3d\x0c\xaa\x92\xd8\x3a\xc4\ \x64\x2b\x5f\xb8\x8f\xbd\x1a\xb3\x47\x83\x87\xcc\x1d\x54\x8e\xe6\ \x41\x3b\x9c\x03\xd5\x90\x0c\x4a\x07\x17\x0e\xce\xc6\xa3\xa5\x2e\ \x18\x87\x8a\x21\x50\xf3\xd6\x29\x35\x21\xdc\xf6\x90\x12\x9b\x48\ \x3a\xbe\x81\x88\x98\xdc\x65\x70\xb0\x92\xd6\x80\x19\xfa\xd1\x22\ \x9c\x1b\x96\xa3\x95\xdd\x82\x9d\x85\xf5\xce\x22\xf0\x4b\x79\x11\ \x16\xa6\x77\x7c\xca\x7b\x1a\x48\x9a\x32\x11\x21\x69\x87\x04\xed\ \x7e\x33\x7a\x5f\x58\xd1\x3b\x6f\x85\xc5\x6b\x42\x5a\x4b\x2a\x04\ \x0a\x5e\x88\x52\x11\xf4\xae\x9f\x89\x3a\x4f\x8a\x28\x03\xa1\xa7\ \x6a\x08\x46\xa0\xe5\x85\x05\x2a\x5e\x98\xad\xc8\xb0\xd1\x53\xa5\ \x84\xe8\xaf\xbf\xf1\x5f\xf3\x0b\x67\xd0\xac\xe5\x79\xba\xd4\x63\ \x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ \x00\x00\x03\x09\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ \x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\ \x00\x00\x00\x04\x67\x41\x4d\x41\x00\x00\xaf\xc8\x37\x05\x8a\xe9\ \x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\x74\x77\x61\x72\x65\ \x00\x41\x64\x6f\x62\x65\x20\x49\x6d\x61\x67\x65\x52\x65\x61\x64\ \x79\x71\xc9\x65\x3c\x00\x00\x02\x9b\x49\x44\x41\x54\x38\xcb\xa5\ \x93\x4b\x6c\x8c\x51\x18\x86\x9f\xff\xd2\xdf\xa5\xad\x6b\x67\x48\ \x4d\xb5\x8a\x44\x55\x10\x62\x36\x45\x22\x11\x91\x68\x2a\x34\xb1\ \x42\x62\xc1\x82\x58\xb3\x21\x08\x89\x20\xb1\x20\x58\x74\x25\x82\ \x4a\x4b\x1a\x0d\x41\x53\xa1\x37\x8a\x6a\x2b\x2e\x41\x25\x2e\x63\ \xa6\x33\x55\xd5\xea\x65\xe6\xff\xcf\xf9\x2c\x7e\x46\x23\xb1\x10\ \x27\xf9\x72\xce\xd9\xbc\xe7\xf9\xde\xf3\xbd\x86\x88\xf0\x3f\xcb\ \x06\xb8\xd0\xd0\xb7\x5f\x84\xed\x4a\x74\x48\x6b\x8d\xd2\xe0\x69\ \x8d\x52\x1a\xa5\x05\x4f\xe9\x51\xa5\x50\x9e\xa0\xb4\x7e\x74\x64\ \xf3\xdc\xb0\x0d\xa0\x45\x76\x97\x87\xb3\x73\xfe\xe5\xe5\x03\x95\ \x6f\x17\xa6\x09\x94\xd6\x39\x00\x99\xd5\x3b\x41\x04\x94\x07\x5a\ \x81\xeb\xfa\xe7\x54\x12\xdc\x14\x24\x5d\x70\x5d\x06\xf7\xd6\xe3\ \xba\x6a\xcc\x28\x81\x5f\x3e\x08\xe4\x06\x40\x7b\x7e\x89\xf7\x53\ \x24\x09\x6e\x12\xbc\x14\xbc\x88\x02\xe0\x7a\xea\xb7\x07\x4a\xe9\ \x9f\x02\x06\x7c\x4e\x80\x68\x9f\x44\x6b\x9f\x44\xf9\xfb\xa0\x33\ \xc8\xa7\x79\x79\x58\xb7\x8f\xb1\xc3\xb9\x47\xe7\x99\xc8\x56\x44\ \x84\xd3\x37\xa3\xf2\x7d\xd8\x95\x8b\x0d\xdd\x22\x22\x52\xd9\x18\ \x13\x11\x91\x2b\xcd\xfe\xbd\xaa\x25\x26\x83\x9f\x6b\x24\x5a\xbf\ \x47\xfa\x3a\x6b\x45\x06\x22\xf2\xb5\xb3\x4a\x5a\x4e\xac\x8d\xd9\ \x00\xae\xd2\x68\x11\x6c\x0b\xaa\x5b\xe2\x58\x96\xc9\xd5\x07\x71\ \x6c\x0b\x6a\x5a\xe3\x24\x12\xbb\x18\xca\x2e\x66\xca\xdc\x12\x7a\ \xbb\x5e\xe2\x18\x29\xb2\x27\x4c\x27\x6b\xea\xcc\x89\xe9\x16\x04\ \xb0\x2d\x83\x0d\xe1\x00\x35\xad\x09\xd6\x87\x83\xd4\x3e\x8e\xb3\ \xba\xe0\x15\x43\x99\x45\x4c\x2a\x5c\x43\x32\x7a\x19\x67\xbc\xc1\ \xfb\xf6\x57\xf4\x0f\x99\x18\xc9\x9e\x45\x69\x02\xd1\x60\x9b\x50\ \xfb\x24\x41\x6b\xcf\x71\x1e\xde\x10\x42\x3a\xc6\xf0\xd8\x3c\x26\ \xcd\x29\x63\x24\x72\x0e\xd3\xf1\xc8\xc8\xca\xc7\xf1\x3e\x70\xbe\ \x77\x0b\xa7\xf7\x96\xbf\x36\x7d\x47\x35\x22\x42\x86\x05\xa5\x4b\ \x03\x00\x14\xdb\x43\x6c\x9c\x3e\x83\x89\x73\xca\x18\x89\x9c\xc5\ \xcc\x70\x49\xf5\x17\xd0\x7d\xbf\x03\x59\xb6\x8f\xef\x4e\x08\x00\ \x13\xfc\xa9\x03\xb0\x4c\x83\xba\x8e\x1e\x72\xfa\x3e\xb0\x58\xc6\ \x11\x2c\x5e\x47\x2a\x56\x81\xe5\x08\xc9\xfe\x7c\xba\x1b\xda\x60\ \xd5\x49\x02\x05\x0b\xd3\x3f\x67\x03\x78\x9e\xe7\x7b\x60\xc2\x8c\ \x81\x3a\x72\x27\xcf\xc6\x93\x20\xd1\x67\xa7\x98\x1c\x74\x18\xe9\ \xcd\xa3\xa7\xf5\x25\xe6\xaa\x93\x04\x72\xf3\xd1\x5a\xb0\x4c\x7e\ \x13\xb8\xca\x6f\x61\xc1\xcc\x2c\x3a\x1a\x2b\x98\xbf\x7c\x07\x63\ \x3e\x36\xf2\xba\xa6\x89\xf6\x5b\xbd\x3c\xbf\xf3\x14\xa3\xe4\x10\ \xc1\x50\xa1\x4f\x6a\x19\x68\x2d\x7a\xd4\x20\x49\xd3\xc1\x4b\x6f\ \x16\xa7\x94\xce\xcc\xed\x4a\x90\x7a\x5e\xcb\xac\x45\x2b\xf8\x96\ \xf8\x42\x57\xdb\x1b\xee\x86\x0e\xf3\xe5\xda\x37\xe0\x61\x3a\x0b\ \x7d\x03\x23\xd7\x01\x8c\x3f\xe3\xbc\x69\xe5\x94\xe1\x25\x85\xd3\ \xec\x70\xd1\x2c\x24\x23\xd9\x3c\x1c\x4f\x6c\x2b\x3d\xda\xf9\xee\ \x6f\xa1\xfa\x01\x69\x8d\x76\xb8\x0b\xb7\x41\x64\x00\x00\x00\x00\ \x49\x45\x4e\x44\xae\x42\x60\x82\ \x00\x00\x02\x8f\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ \x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\ \x00\x00\x00\x04\x67\x41\x4d\x41\x00\x00\xaf\xc8\x37\x05\x8a\xe9\ \x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\x74\x77\x61\x72\x65\ \x00\x41\x64\x6f\x62\x65\x20\x49\x6d\x61\x67\x65\x52\x65\x61\x64\ \x79\x71\xc9\x65\x3c\x00\x00\x02\x21\x49\x44\x41\x54\x38\xcb\x95\ \x93\xeb\x4e\x13\x51\x14\x85\x89\x89\xc9\x89\xcf\xa0\x56\x89\x86\ \xc8\x91\x80\xc4\x1b\x42\x5b\x06\x28\xad\x0d\x08\x26\xd0\xfb\x85\ \x5e\xa4\x80\xb4\xa5\xed\xa4\x4d\xa1\x36\xea\x0f\x4d\x7c\x12\x9f\ \x0b\x44\xc5\xde\xb0\xd2\x99\x76\x3a\xd3\xe5\xae\x98\x4a\x2d\x25\ \xe1\xc7\x4e\xce\x64\xce\xfa\xf6\xac\xb5\xf7\x0c\x00\x18\xb8\x4c\ \xd5\xd7\x42\xd7\xce\x3e\x77\x5f\x10\x33\x3a\x2a\xde\x57\xec\x0f\ \x72\xd9\xed\x8d\xd7\x6c\x4e\x43\x2f\xa0\x2d\xce\xec\xa2\x95\xce\ \x42\x8b\x27\x7b\x20\x75\x5f\x80\xd7\x03\x61\x34\x36\xb6\xf0\xeb\ \xe5\xca\xe7\xea\xe2\xd2\xbd\x7f\x80\xbf\x62\xe4\xdf\xa1\x45\xa5\ \x25\x44\x34\x37\xb7\x3b\x10\xd9\xbb\xc6\xa9\x3b\x9a\xd1\x38\x90\ \xcb\xa3\x7d\x3e\x36\x5b\xe3\xe5\x19\xd3\x95\x53\x40\x2a\xcd\x5a\ \x09\x51\x6b\xed\xbe\x01\x3e\x7e\x82\x96\xcd\xb5\x01\x68\x04\x42\ \x5c\xf6\xf8\x39\x75\x87\xb2\x1d\x03\xe8\xbd\xec\x0f\xe0\x78\xfe\ \xb9\x5a\x16\xe6\xae\x76\x59\xd0\x62\x09\xa6\xbe\x8e\xa9\x9a\x98\ \x01\xde\x7f\x80\x9a\x4a\xa3\x1e\x0c\x83\xba\x43\xd9\x8a\x02\xd9\ \xbd\x3f\xe7\x8a\xc9\x42\xe2\x59\x76\x6e\x88\xcd\xc8\x26\x6b\x84\ \xd6\xd5\x66\x74\x87\xec\xbc\x05\xf6\xf2\x24\xcc\x01\x99\x2c\x64\ \x8f\x0f\x95\x39\xb3\x5a\x9e\x9e\x61\xfd\xa7\x70\x1a\x16\x93\x5c\ \x5e\x0d\x59\xb2\xe3\xf6\x01\x0e\x37\x20\xa6\x51\x99\x9d\xd7\x4a\ \x46\x81\xfd\x7f\xbf\x07\x20\x39\x3d\xed\x51\x01\x34\x0d\xd8\x9c\ \xc0\x8a\x1d\xd8\x49\x92\x6f\x0b\x0a\x13\x53\xfc\x42\x80\xe4\x70\ \x73\xc9\xe5\x81\x12\x8e\x00\x49\x91\x84\x29\x20\x46\x76\x28\x40\ \x79\xd5\x8e\xb2\xde\x88\xef\x63\xe3\xfc\x5c\x40\xcd\xee\xe2\x92\ \xd3\x0d\x25\xb4\x0e\xd0\x18\x25\x87\x0b\x14\x96\x5a\x9c\x32\x68\ \x27\x8b\xcb\x40\x64\x03\xb5\x17\xcb\x28\x3c\x7c\x8c\xc3\xa1\x61\ \xde\x05\xa0\xcd\xe2\xd4\x1d\x4a\xf0\x15\x75\x4d\x40\xa2\x4f\xa7\ \xb0\xd4\xe2\xa4\x81\x15\x9e\x4c\xb0\xa3\xf1\x47\x6a\xd5\x64\x06\ \x82\x21\x9c\x58\xac\x38\x1a\x19\xc5\xc1\xad\x41\xde\x01\xd0\x66\ \x09\x35\x9b\x03\x4a\x20\x04\x69\xd5\x01\x0a\x4b\x2d\x3e\xd3\x77\ \x02\xfb\x36\x32\xc6\xbe\x0e\xdf\x57\x7f\x1a\x05\x48\xd6\x05\xfc\ \x18\x7d\x80\xfd\x1b\x3a\xa1\xcb\x02\x6d\x96\x50\x5d\x58\x42\xc9\ \x30\xad\x51\x58\x3d\x69\x1f\xde\x1d\x62\x5f\x06\xef\xa8\x67\xc5\ \x3d\x21\x96\xf4\x46\xa1\xf0\x74\x92\xf5\xfb\x99\x0e\x74\xb7\xd9\ \xfe\xf5\x9b\xc2\x85\x63\xbc\x6c\xfd\x06\x72\xbb\xa4\xc7\xdb\xed\ \xbe\x14\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ \x00\x00\x02\x8f\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ \x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\ \x00\x00\x00\x04\x67\x41\x4d\x41\x00\x00\xaf\xc8\x37\x05\x8a\xe9\ \x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\x74\x77\x61\x72\x65\ \x00\x41\x64\x6f\x62\x65\x20\x49\x6d\x61\x67\x65\x52\x65\x61\x64\ \x79\x71\xc9\x65\x3c\x00\x00\x02\x21\x49\x44\x41\x54\x38\xcb\x95\ \x93\xeb\x4e\x13\x51\x14\x85\x89\x89\xc9\x89\xcf\xa0\x56\x89\x86\ \xc8\x91\x80\xc4\x1b\x42\x5b\x06\x28\xad\x0d\x08\x26\xd0\xfb\x85\ \x5e\xa4\x80\xb4\xa5\xed\xa4\x4d\xa1\x36\xea\x0f\x4d\x7c\x12\x9f\ \x0b\x44\xc5\xde\xb0\xd2\x99\x76\x3a\xd3\xe5\xae\x98\x4a\x2d\x25\ \xe1\xc7\x4e\xce\x64\xce\xfa\xf6\xac\xb5\xf7\x0c\x00\x18\xb8\x4c\ \xd5\xd7\x42\xd7\xce\x3e\x77\x5f\x10\x33\x3a\x2a\xde\x57\xec\x0f\ \x72\xd9\xed\x8d\xd7\x6c\x4e\x43\x2f\xa0\x2d\xce\xec\xa2\x95\xce\ \x42\x8b\x27\x7b\x20\x75\x5f\x80\xd7\x03\x61\x34\x36\xb6\xf0\xeb\ \xe5\xca\xe7\xea\xe2\xd2\xbd\x7f\x80\xbf\x62\xe4\xdf\xa1\x45\xa5\ \x25\x44\x34\x37\xb7\x3b\x10\xd9\xbb\xc6\xa9\x3b\x9a\xd1\x38\x90\ \xcb\xa3\x7d\x3e\x36\x5b\xe3\xe5\x19\xd3\x95\x53\x40\x2a\xcd\x5a\ \x09\x51\x6b\xed\xbe\x01\x3e\x7e\x82\x96\xcd\xb5\x01\x68\x04\x42\ \x5c\xf6\xf8\x39\x75\x87\xb2\x1d\x03\xe8\xbd\xec\x0f\xe0\x78\xfe\ \xb9\x5a\x16\xe6\xae\x76\x59\xd0\x62\x09\xa6\xbe\x8e\xa9\x9a\x98\ \x01\xde\x7f\x80\x9a\x4a\xa3\x1e\x0c\x83\xba\x43\xd9\x8a\x02\xd9\ \xbd\x3f\xe7\x8a\xc9\x42\xe2\x59\x76\x6e\x88\xcd\xc8\x26\x6b\x84\ \xd6\xd5\x66\x74\x87\xec\xbc\x05\xf6\xf2\x24\xcc\x01\x99\x2c\x64\ \x8f\x0f\x95\x39\xb3\x5a\x9e\x9e\x61\xfd\xa7\x70\x1a\x16\x93\x5c\ \x5e\x0d\x59\xb2\xe3\xf6\x01\x0e\x37\x20\xa6\x51\x99\x9d\xd7\x4a\ \x46\x81\xfd\x7f\xbf\x07\x20\x39\x3d\xed\x51\x01\x34\x0d\xd8\x9c\ \xc0\x8a\x1d\xd8\x49\x92\x6f\x0b\x0a\x13\x53\xfc\x42\x80\xe4\x70\ \x73\xc9\xe5\x81\x12\x8e\x00\x49\x91\x84\x29\x20\x46\x76\x28\x40\ \x79\xd5\x8e\xb2\xde\x88\xef\x63\xe3\xfc\x5c\x40\xcd\xee\xe2\x92\ \xd3\x0d\x25\xb4\x0e\xd0\x18\x25\x87\x0b\x14\x96\x5a\x9c\x32\x68\ \x27\x8b\xcb\x40\x64\x03\xb5\x17\xcb\x28\x3c\x7c\x8c\xc3\xa1\x61\ \xde\x05\xa0\xcd\xe2\xd4\x1d\x4a\xf0\x15\x75\x4d\x40\xa2\x4f\xa7\ \xb0\xd4\xe2\xa4\x81\x15\x9e\x4c\xb0\xa3\xf1\x47\x6a\xd5\x64\x06\ \x82\x21\x9c\x58\xac\x38\x1a\x19\xc5\xc1\xad\x41\xde\x01\xd0\x66\ \x09\x35\x9b\x03\x4a\x20\x04\x69\xd5\x01\x0a\x4b\x2d\x3e\xd3\x77\ \x02\xfb\x36\x32\xc6\xbe\x0e\xdf\x57\x7f\x1a\x05\x48\xd6\x05\xfc\ \x18\x7d\x80\xfd\x1b\x3a\xa1\xcb\x02\x6d\x96\x50\x5d\x58\x42\xc9\ \x30\xad\x51\x58\x3d\x69\x1f\xde\x1d\x62\x5f\x06\xef\xa8\x67\xc5\ \x3d\x21\x96\xf4\x46\xa1\xf0\x74\x92\xf5\xfb\x99\x0e\x74\xb7\xd9\ \xfe\xf5\x9b\xc2\x85\x63\xbc\x6c\xfd\x06\x72\xbb\xa4\xc7\xdb\xed\ \xbe\x14\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ \x00\x00\x02\xca\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ \x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\ \x00\x00\x00\x04\x67\x41\x4d\x41\x00\x00\xaf\xc8\x37\x05\x8a\xe9\ \x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\x74\x77\x61\x72\x65\ \x00\x41\x64\x6f\x62\x65\x20\x49\x6d\x61\x67\x65\x52\x65\x61\x64\ \x79\x71\xc9\x65\x3c\x00\x00\x02\x5c\x49\x44\x41\x54\x38\xcb\xa5\ \x93\xcd\x4b\xd4\x51\x14\x86\x9f\xfb\x9b\x71\xfc\x9a\xb1\x51\x91\ \x8a\x42\xd1\x24\xc8\x90\x4a\xc3\x56\x61\x1b\x49\x92\x16\x45\xb5\ \x69\x55\xbb\x5a\x0a\x6e\xb2\xfe\x02\xa1\x7d\xb4\x28\x88\x08\x2a\ \x8c\x20\x65\x28\x30\xac\x85\x60\x84\x54\x1b\x33\x6b\x30\x41\x13\ \x3f\xc6\x6c\x74\xe6\x37\xf7\xde\x73\x5a\x04\x93\x56\xb4\xa8\xb3\ \x3c\x9c\xf3\x70\x38\xef\xfb\x1a\x55\xe5\x7f\x2a\xfa\x6b\xc3\x3d\ \x37\x71\x55\x7a\x55\xe8\x16\xa1\x43\x15\x54\x18\x17\x21\xa5\xc2\ \xf5\xc4\x49\xcd\x6e\x9e\x37\x9b\x2f\x70\xcf\x4d\xa7\x0a\xb7\x4c\ \x7d\x5f\xa3\x49\x76\xa0\xb1\x1a\x50\x8f\xe6\x96\xf0\x99\x09\xc2\ \x0f\x03\x69\x11\x2e\x54\x9f\xd2\xd1\xdf\x00\x6e\xc4\x9c\xd2\x64\ \xe7\x60\xb0\xe3\x3c\x5a\xb1\x0d\xbf\x31\x89\x84\xcb\xa8\xcb\x83\ \x29\x23\x28\x6b\xc6\xd8\x80\xf0\xf3\x03\xc2\x85\xd1\xd3\x75\xe7\ \xf4\x51\x11\x60\x47\x4c\x8d\x2a\x33\x91\xd6\x7b\x71\x09\xd6\xf0\ \xf9\x34\xd8\x6f\x88\x0b\x51\x9f\xff\x01\x11\xc5\x94\xd6\x13\x29\ \x6d\xe5\xeb\xd8\xc5\xac\x08\x0d\x3b\xcf\xeb\x4a\x00\xa0\x42\x6f\ \xd0\xd0\x1f\xd7\x18\x48\x98\x06\xb7\xce\xc8\xe3\x25\x90\x02\xf8\ \x02\x2f\x9e\x81\xd8\x2c\x6e\xed\x1d\x3e\xfc\x48\xc5\xbe\xfe\xb8\ \x78\x7a\x01\x02\x00\x11\x7a\x4c\xf5\x11\x24\x9f\x46\x5d\x0e\x15\ \xcb\xb1\x13\xe5\xa8\x58\xd4\x5b\x8e\x76\x66\x51\x09\x51\x9b\xc5\ \xad\xbe\x26\x9a\xdc\x8b\x78\x7a\x8a\x2a\x88\xd0\x42\x69\x15\x92\ \x5b\x65\xfa\x6d\x35\x98\x04\x98\x5f\xe4\x51\x45\xdd\x0a\xbb\x6b\ \x9e\x02\x21\xde\xd3\x58\x04\xa8\x90\x53\x97\x8f\xe1\x1d\x35\x0d\ \xed\x54\xd4\xee\xc1\x04\x91\xad\xfb\xe2\x59\x5f\x78\x83\xae\x0d\ \x81\x2a\x22\x44\x7e\x5e\xe0\x49\x63\x73\x07\x21\x46\x65\x3c\x47\ \x62\x7b\x33\x41\xb4\x64\x0b\xc0\xdb\x10\xbf\x3c\x8c\x09\x2a\x10\ \x1f\xc5\x86\x4c\x6f\xfe\xc1\x90\x5d\x9a\xc0\xc4\xea\x09\xc2\x51\ \xec\xc6\x1c\xb0\xd9\xa1\x8a\x5d\xfb\x08\xcb\xf7\x89\x56\x1d\x20\ \xb7\x34\x83\x2d\x30\x54\x94\x31\x33\x68\xea\x44\xf8\x14\x3f\x72\ \x27\x2e\xf6\x3d\x6e\x7d\x86\x48\xb2\x87\x92\x64\x1b\xa8\x27\x5c\ \x7c\x49\x61\xf6\x26\x91\xf2\x5d\x98\xaa\xe3\xcc\x3c\xb9\x94\x75\ \x96\xa6\x43\x57\x74\xb1\x68\xa4\xc5\xfb\xe6\x4c\xb4\xae\xeb\x41\ \x59\xfd\x69\x88\x7c\xc3\x65\x5e\xe1\x73\x73\xa8\xcb\x63\xa2\x09\ \x22\x89\x83\x78\x9a\xc8\x4c\x0e\x93\x99\x4a\x9d\x6d\xeb\xd7\x87\ \xbf\x59\x79\xfe\xae\xe9\x12\xcf\x8d\xca\x96\xbe\xc6\x58\xed\x7e\ \x82\x92\x00\xc4\xe1\x42\xcf\xfa\xfc\x14\x5f\xc6\x06\xd2\xce\x72\ \xb9\xfd\xaa\xa6\xfe\x98\x05\x80\xd9\xdb\x26\x2e\x9e\x3e\x11\xba\ \xbd\xa7\xc3\x15\xc0\x59\xc6\x9d\x25\xe5\x2c\x03\x87\xaf\xfd\x25\ \x4c\xff\x52\xdf\x01\x53\xad\x5d\x6e\x31\xa5\xb5\xa9\x00\x00\x00\ \x00\x49\x45\x4e\x44\xae\x42\x60\x82\ \x00\x00\x02\x9a\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ \x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\ \x00\x00\x00\x04\x67\x41\x4d\x41\x00\x00\xaf\xc8\x37\x05\x8a\xe9\ \x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\x74\x77\x61\x72\x65\ \x00\x41\x64\x6f\x62\x65\x20\x49\x6d\x61\x67\x65\x52\x65\x61\x64\ \x79\x71\xc9\x65\x3c\x00\x00\x02\x2c\x49\x44\x41\x54\x38\xcb\xa5\ \x53\x4b\x48\x94\x01\x10\xfe\xfe\x87\xab\xae\xeb\x6b\xcb\x54\x02\ \x43\x5b\x52\x90\x4c\xca\xc8\x35\x0d\x6f\x3d\x0c\xea\xd4\x63\xa9\ \x6b\xd1\x29\xa4\x63\x87\x2e\x1d\x24\x08\x42\x8b\xec\x90\x44\x85\ \x50\x87\x8a\x10\x2c\x11\x2c\x89\x88\x4e\x05\x59\x28\x46\x46\x66\ \xae\xbb\xb2\x98\x8a\xfe\x8f\x99\xaf\x83\x1a\x81\x6b\x05\x0e\xcc\ \xcc\x61\x66\x3e\xbe\x6f\x98\x31\x48\x62\x3d\x66\xaf\x55\x98\x7e\ \xb1\xcf\xa2\xf0\x3d\x55\x53\xaa\x6c\x2e\xde\xff\x5a\xd2\xf5\x99\ \x6b\x01\x50\xd8\x1e\x28\x39\x58\x1d\x28\x3e\xd0\x48\xd1\x33\x6b\ \x52\x20\xb9\xca\x13\xfd\x8d\xe1\x78\x5f\xc3\x4f\x71\x67\xe8\xa6\ \x86\x38\xf1\x74\xf7\xf4\xf8\xe3\x5d\xe1\x74\xbd\x69\x19\xa8\xe8\ \xdd\xec\xb2\x58\xae\x61\x9a\xb0\x73\xc2\x08\x96\xc7\xc2\x14\xbd\ \xfc\x5f\x0c\xe2\xcf\x1b\x6a\x27\x7b\xeb\x85\xe2\xd0\x89\x5f\xa7\ \x9b\xe8\xa2\xba\x49\x7e\x7d\xb0\xc3\x1f\xeb\xde\x5e\xfb\x4f\x06\ \x54\xbd\x1f\xaa\x6a\x35\xd5\x9f\xc3\xe7\xbe\x87\x18\x7d\x76\x07\ \xea\x4d\xa2\xa0\xe6\x82\xa5\xa2\x1d\x7f\x5d\x62\xbc\x37\x1a\xcb\ \x28\x8c\x56\xe7\x6c\x39\x8a\x85\x6f\xad\x20\x01\x12\x58\x1c\x6f\ \x43\x6e\xa4\x05\x99\x25\x4d\x8d\xa3\xb7\x2b\x63\x69\x01\xe2\xbd\ \xd1\x90\xaa\xb6\x07\x2b\x8e\x43\xfd\x69\x40\x97\xa7\x49\xa8\x7a\ \xd0\xc5\x11\xe4\x57\x9d\x80\x8a\x5e\x19\xbe\xb9\x35\xb4\x0a\x40\ \x45\xbb\x82\x65\xc7\x36\x64\x16\x56\xc0\x4b\x3e\x02\x40\x64\x15\ \x04\x57\x84\xc1\x99\xbc\x81\xec\xa2\x32\xe4\x55\x9e\xdc\xec\x2f\ \x7a\x17\x57\xe6\x0c\x92\xf8\xd1\xb3\x27\x42\xd5\x4f\xa5\x87\x06\ \x6c\x59\x78\x07\x6f\xea\x16\x54\x01\x33\x23\x0b\xf3\xc9\x59\x64\ \xe6\x28\x2c\x8b\xb0\xf2\x9a\x61\xe5\x1e\xc1\x50\x47\x9d\x2b\x8e\ \x5f\xbd\xf3\x52\x72\xd4\x5c\x3a\x1a\xed\x0e\x6d\x3b\x67\x53\xe7\ \x20\xb3\x6f\x00\x2a\x00\xe2\xcb\xe0\x30\xa6\x3e\x4e\x00\x20\x40\ \x85\x9f\xea\x87\x61\xce\xa3\xa8\xbe\x35\xe0\x3b\xde\x35\x00\x30\ \xbe\x3f\xa9\x6b\xb1\xf2\x6b\x7a\x36\x35\xb5\xc3\x9f\x7d\x09\x2f\ \x71\x6f\x49\x92\x12\xa4\x01\x10\x30\x2d\xc2\xa0\x0b\x52\x60\x5a\ \x1b\x91\x55\xde\x86\x0f\x9d\x31\xcc\x8c\x0c\x1e\xb6\x55\xa4\xb3\ \x20\x72\x6a\xe9\x31\x42\x0d\xb0\x43\xd1\x65\x71\xc6\x8a\xfc\xe5\ \xc0\xdf\xfb\x80\x11\x40\xe9\xde\xb3\x48\x0d\x0d\x5c\xb5\x29\x3a\ \x90\x78\x75\xfe\x34\x45\xa1\xa2\xf8\x33\x8b\x27\x10\xc7\x83\xef\ \x78\x10\xc7\x83\x38\x3e\xc4\xf5\x21\x9e\x40\x7d\x81\xfa\xf2\xd6\ \x58\xef\x3b\xff\x02\x94\x4c\x63\xd9\xa7\xb9\x53\x2f\x00\x00\x00\ \x00\x49\x45\x4e\x44\xae\x42\x60\x82\ " qt_resource_name = "\ \x00\x05\ \x00\x6f\xa6\x53\ \x00\x69\ \x00\x63\x00\x6f\x00\x6e\x00\x73\ \x00\x0f\ \x00\x71\x49\xe7\ \x00\x61\ \x00\x64\x00\x76\x00\x61\x00\x6e\x00\x63\x00\x65\x00\x64\x00\x63\x00\x61\x00\x63\x00\x68\x00\x69\x00\x6e\x00\x67\ \x00\x04\ \x00\x06\xa8\xa1\ \x00\x64\ \x00\x61\x00\x74\x00\x61\ \x00\x09\ \x06\xc3\x8a\x67\ \x00\x67\ \x00\x72\x00\x6f\x00\x75\x00\x70\x00\x2e\x00\x70\x00\x6e\x00\x67\ \x00\x0b\ \x0c\x83\x9f\x27\ \x00\x63\ \x00\x6f\x00\x6d\x00\x6d\x00\x65\x00\x6e\x00\x74\x00\x2e\x00\x70\x00\x6e\x00\x67\ \x00\x13\ \x05\xf0\x80\x47\ \x00\x61\ \x00\x73\x00\x74\x00\x65\x00\x72\x00\x69\x00\x73\x00\x6b\x00\x5f\x00\x79\x00\x65\x00\x6c\x00\x6c\x00\x6f\x00\x77\x00\x2e\x00\x70\ \x00\x6e\x00\x67\ \x00\x0a\ \x0c\xad\x0f\x07\ \x00\x64\ \x00\x65\x00\x6c\x00\x65\x00\x74\x00\x65\x00\x2e\x00\x70\x00\x6e\x00\x67\ \x00\x0a\ \x0c\x7b\xa4\x67\ \x00\x61\ \x00\x63\x00\x63\x00\x65\x00\x70\x00\x74\x00\x2e\x00\x70\x00\x6e\x00\x67\ \x00\x11\ \x03\xf0\x57\x27\ \x00\x63\ \x00\x61\x00\x6c\x00\x65\x00\x6e\x00\x64\x00\x61\x00\x72\x00\x5f\x00\x65\x00\x64\x00\x69\x00\x74\x00\x2e\x00\x70\x00\x6e\x00\x67\ \ \x00\x09\ \x06\xa6\x82\x67\ \x00\x63\ \x00\x72\x00\x6f\x00\x73\x00\x73\x00\x2e\x00\x70\x00\x6e\x00\x67\ \x00\x11\ \x06\x3c\xc6\x27\ \x00\x65\ \x00\x6d\x00\x6f\x00\x74\x00\x69\x00\x63\x00\x6f\x00\x6e\x00\x5f\x00\x67\x00\x72\x00\x69\x00\x6e\x00\x2e\x00\x70\x00\x6e\x00\x67\ \ \x00\x09\ \x09\x65\x8e\x67\ \x00\x65\ \x00\x72\x00\x72\x00\x6f\x00\x72\x00\x2e\x00\x70\x00\x6e\x00\x67\ " qt_resource_struct = "\ \x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\ \x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\ \x00\x00\x00\x10\x00\x02\x00\x00\x00\x01\x00\x00\x00\x03\ \x00\x00\x00\x34\x00\x02\x00\x00\x00\x0a\x00\x00\x00\x04\ \x00\x00\x00\xd6\x00\x00\x00\x00\x00\x01\x00\x00\x0d\x61\ \x00\x00\x00\x76\x00\x00\x00\x00\x00\x01\x00\x00\x04\x96\ \x00\x00\x01\x16\x00\x00\x00\x00\x00\x01\x00\x00\x15\x94\ \x00\x00\x00\xfe\x00\x00\x00\x00\x00\x01\x00\x00\x13\x01\ \x00\x00\x00\xfe\x00\x00\x00\x00\x00\x01\x00\x00\x10\x6e\ \x00\x00\x00\x42\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ \x00\x00\x01\x3e\x00\x00\x00\x00\x00\x01\x00\x00\x18\x62\ \x00\x00\x00\xbc\x00\x00\x00\x00\x00\x01\x00\x00\x0a\x50\ \x00\x00\x00\x5a\x00\x00\x00\x00\x00\x01\x00\x00\x02\xf5\ \x00\x00\x00\xa2\x00\x00\x00\x00\x00\x01\x00\x00\x07\x81\ " def qInitResources(): QtCore.qRegisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data) def qCleanupResources(): QtCore.qUnregisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data) qInitResources() agtl-0.8.0.3/files/advancedcaching/qt/mapwidget.py000066400000000000000000000724051151564747700220030ustar00rootroot00000000000000#!/usr/bin/python # -*- coding: utf-8 -*- # Copyright (C) 2010 Daniel Fett # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # Author: Daniel Fett agtl@danielfett.de # Jabber: fett.daniel@jaber.ccc.de # Bugtracker and GIT Repository: http://github.com/webhamster/advancedcaching # import logging import math from threading import Lock from PyQt4.QtCore import * from PyQt4.QtGui import * from PyQt4.QtOpenGL import * from abstractmap import AbstractGeocacheLayer from abstractmap import AbstractMap from abstractmap import AbstractMapLayer from abstractmap import AbstractMarksLayer import geo import geocaching import threadpool logger = logging.getLogger('qtmap') logger.debug("Using pyqt bindings") class QtMap(QGLWidget, AbstractMap): tileLoaderChanged = pyqtSignal() mapDragged = pyqtSignal() zoomChanged = pyqtSignal() centerChanged = pyqtSignal() tileFinished = pyqtSignal(tuple) def __init__(self, parent, center, zoom, tile_loader=None): QGLWidget.__init__(self, QGLFormat(QGL.SampleBuffers | QGL.DoubleBuffer | QGL.Rgba | QGL.DirectRendering)) AbstractMap.__init__(self, center, zoom, tile_loader) self.buffer = QPixmap(self.size()) self.painter = QPainter() self.surface_buffer = {} self.tile_loader_threadpool = threadpool.ThreadPool(20) self.sem = Lock() self.drag_offset_x = 0 self.drag_offset_y = 0 self.tileFinished.connect(self.__draw_tiles) ############################################## # # Event handling # ############################################## def paintEvent(self, ev): # blit the pixmap p = QPainter(self) if self.dragging: p.drawPixmap(-self.drag_offset_x, -self.drag_offset_y, self.buffer) return else: p.drawPixmap(0, 0, self.buffer) for l in self.layers: if l.result == None: continue p.drawPixmap(0, 0, l.result) def mousePressEvent(self, ev): if self.dragging or ev.button() != Qt.LeftButton: return self.dragging = True self.drag_start = ev.pos() def mouseMoveEvent(self, ev): if not self.dragging: return self.drag_offset_x = self.drag_start.x() - ev.x() self.drag_offset_y = self.drag_start.y() - ev.y() self.repaint() return self.drag_end = ev.pos() offset_x = self.drag_start.x() - self.drag_end.x() offset_y = self.drag_start.y() - self.drag_end.y() self._move_map_relative(offset_x, offset_y) self.drag_offset_x = 0 self.drag_offset_y = 0 self._draw_map() self.drag_start = ev.pos() def mouseReleaseEvent(self, ev): if not self.dragging or ev.button() != Qt.LeftButton: return #self.dragging = False #self.repaint() self.drag_end = ev.pos() offset_x = self.drag_start.x() - self.drag_end.x() offset_y = self.drag_start.y() - self.drag_end.y() self.drag_offset_x = 0 self.drag_offset_y = 0 self.dragging = False if self._check_click(offset_x, offset_y, ev.pos().x(), ev.pos().y()): self.repaint() else: self._move_map_relative(offset_x, offset_y) self.mapDragged.emit() def mouseDoubleClickEvent(self, ev): self.set_center(self.screenpoint2coord((ev.x(), ev.y())), False) self.zoom_in() def resizeEvent(self, ev): s = ev.size() self.buffer = QPixmap(s) self.map_width = s.width() self.map_height = s.height() for l in self.layers: l.resize() self._draw_map() def wheelEvent(self, ev): if ev.delta() == 0: return if ev.delta() > 0: dir = 1 else: dir = -1 self.relative_zoom_preserve_center_at((ev.x(), ev.y()), dir) ############################################## # # Map actions # ############################################## #@pyqtSignature("zoomIn()") def zoom_in(self): self.relative_zoom( + 1) #@pyqtSignature("zoomOut()") def zoom_out(self): self.relative_zoom(-1) def relative_zoom(self, direction=None, update=True): AbstractMap.relative_zoom(self, direction, update) self.zoomChanged.emit() def redraw_layers(self): if self.dragging: return self.__draw_layers() self.repaint() def refresh(self): self.repaint() def set_center(self, coord, update=True): AbstractMap.set_center(self, coord, update) self.centerChanged.emit() def _move_map_relative(self, offset_x, offset_y, update=True): AbstractMap._move_map_relative(self, offset_x, offset_y, update) self.centerChanged.emit() ############################################## # # Drawing marks & osd # ############################################## def __draw_layers(self): for l in self.layers: l.draw() ############################################## # # Map Drawing # ############################################## def _draw_map(self): if self.map_width == 0 or self.map_height == 0: return try: while True: x = self.active_tile_loaders.pop() x.halt() except IndexError: pass zoom = self.zoom size = self.tile_loader.TILE_SIZE xi = int(self.map_center_x) yi = int(self.map_center_y) span_x = int(math.ceil(float(self.map_width) / (size * 2.0))) span_y = int(math.ceil(float(self.map_height) / (size * 2.0))) offset_x = int(self.map_width / 2 - (self.map_center_x - int(self.map_center_x)) * size) offset_y = int(self.map_height / 2 -(self.map_center_y - int(self.map_center_y)) * size) undersample = self.double_size requests = [] new_surface_buffer = {} old_surface_buffer = self.surface_buffer tiles = [] for i in xrange(-span_x, span_x + 1, 1): for j in xrange(-span_y, span_y + 1, 1): tile = (xi + i, yi + j) dx = i * size + offset_x dy = j * size + offset_y id_string = self.__get_id_string(tile, zoom, undersample) tile = self.check_bounds(*tile) if tile in tiles: continue if id_string in old_surface_buffer and old_surface_buffer[id_string][0] != self.tile_loader.noimage_cantload and old_surface_buffer[id_string][0] != self.tile_loader.noimage_loading: new_surface_buffer[id_string] = old_surface_buffer[id_string] new_surface_buffer[id_string][1:3] = dx, dy else: requests.append(((id_string, tile, zoom, undersample, dx, dy, self._add_to_buffer), {})) self.surface_buffer = new_surface_buffer reqs = threadpool.makeRequests(self.__run_tile_loader, requests) #cr = gtk.gdk.CairoContext(cairo.Context(self.cr_drawing_area_map)) #cr.set_source_rgba(0, 0, 0, 1) self.delay_expose = True #cr.paint() for r in reqs: self.tile_loader_threadpool.putRequest(r) self.__draw_layers() self.__draw_tiles() #self.repaint() def __get_id_string(self, tile, display_zoom, undersample): return (self.tile_loader.PREFIX, tile[0], tile[1], display_zoom, 1 if undersample else 0) def __run_tile_loader(self, id_string, tile, zoom, undersample, x, y, callback_draw): #callback_draw = lambda x, y, z: 1 d = self.tile_loader(id_string=id_string, tile=tile, zoom=zoom, undersample=undersample, x=x, y=y, callback_draw=callback_draw, callback_load=self._load_tile) self.active_tile_loaders.append(d) d.run() def _add_to_buffer(self, id_string, surface, x, y, scale_source=None): self.surface_buffer[id_string] = [surface, x, y, scale_source] self.tileFinished.emit(([surface, x, y, scale_source], )) #self.__draw_tiles(which=([surface, x, y, scale_source],)) @staticmethod def _load_tile(filename): return filename def __draw_tiles(self, which=None, off_x=0, off_y=0): self.delay_expose = False #cr = gtk.gdk.CairoContext(cairo.Context(self.cr_drawing_area_map)) p = self.painter if which == None: which = self.surface_buffer.values() for surface, x, y, scale_source in which: if surface == None: logger.info("Surface was none") continue size = self.tile_loader.TILE_SIZE try: pm = self.__load_tile_cached(surface) except Exception: logger.exception("Could not load Pixmap from Filename %s" % surface) try: pm = self.__load_tile_cached(self.noimage_cantload) except Exception: logger.exception("Could not load replacement Pixmap from Filename %s" % self.noimage_cantload) if scale_source == None: self.sem.acquire() p.begin(self.buffer) p.drawPixmap(x + off_x, y + off_y, pm) p.end() else: xs, ys = scale_source target = QRectF(x + off_x, y + off_y, size, size) source = QRectF(xs, ys, size / 2, size / 2) self.sem.acquire() p.begin(self.buffer) p.setRenderHint(QPainter.SmoothPixmapTransform, True) p.drawPixmap(target, pm, source) p.end() self.sem.release() #cr.rectangle(max(0, x + off_x), max(0, y + off_y), min(size + x, size, self.map_width - x + size), min(size + y, size, self.map_height - y + size)) #cr.fill() #self.queue_draw_area(max(0, x + off_x), max(0, y + off_y), min(size + x, size, self.map_width - x + size), min(size + y, size, self.map_height - y + size)) self.repaint() return False def __load_tile_cached(self, filename): pixmap = QPixmapCache.find(filename) if pixmap == None: pixmap = QPixmap(filename) QPixmapCache.insert(filename, pixmap) return pixmap class AbstractQtLayer(AbstractMapLayer): def __init__(self): self.painter = QPainter() def attach(self, map): AbstractMapLayer.attach(self, map) self.result = QPixmap(self.map.size()) self.result.fill(Qt.transparent) def resize(self): self.result = QPixmap(self.map.size()) self.result.fill(Qt.transparent) class QtSingleMarkLayer(AbstractQtLayer): PEN_TARGET = QPen(QColor(0, 0, 200), 1) PEN_SHADOW_TARGET = QPen(QColor(255, 255, 255), 3) def __init__(self, coordinate): AbstractQtLayer.__init__(self) self.coord = coordinate def draw(self): self.result.fill(Qt.transparent) t = self.map.coord2point(self.coord) if not self.map.point_in_screen(t): return p = self.painter p.begin(self.result) p.setRenderHint(QPainter.Antialiasing) radius_o = 15 radius_i = 3 radius_c = 10 p.setPen(QtSingleMarkLayer.PEN_SHADOW_TARGET) p.drawLines( QLineF(t[0] - radius_o, t[1], t[0] - radius_i, t[1]), QLineF(t[0] + radius_o, t[1], t[0] + radius_i, t[1]), QLineF(t[0], t[1] + radius_o, t[0], t[1] + radius_i), QLineF(t[0], t[1] - radius_o, t[0], t[1] - radius_i) ) p.drawArc(QRectF(t[0] - radius_c / 2, t[1] - radius_c / 2, radius_c, radius_c), 0, 16 * 360) p.setPen(QtSingleMarkLayer.PEN_TARGET) p.drawLines( QLineF(t[0] - radius_o, t[1], t[0] - radius_i, t[1]), QLineF(t[0] + radius_o, t[1], t[0] + radius_i, t[1]), QLineF(t[0], t[1] + radius_o, t[0], t[1] + radius_i), QLineF(t[0], t[1] - radius_o, t[0], t[1] - radius_i) ) p.drawArc(QRectF(t[0] - radius_c / 2, t[1] - radius_c / 2, radius_c, radius_c), 0, 16 * 360) p.end() class QtOsdLayer(AbstractQtLayer): MESSAGE_DRAW_FONT = QFont('Sans', 12) MESSAGE_DRAW_PEN = QPen(QColor(0, 0, 0)) OSD_PEN = QPen(QColor(255, 255, 255)) OSD_BRUSH = QBrush(QColor(0, 0, 0)) OSD_BORDER_TOPBOTTOM = 25 OSD_BORDER_LEFTRIGHT = 10 BUTTON_SIZE = 35 BUTTON_DISTANCE = 10 BUTTON_PEN = QPen(QColor(0, 0, 0)) BUTTON_BRUSH = QBrush(QColor(255, 255, 255, 170)) BUTTON_SIGN_PEN = QPen(Qt.transparent) BUTTON_SIGN_BRUSH = QBrush(QColor(100, 100, 100)) BUTTON_SIGN_DISABLED_PEN = QPen(Qt.transparent) BUTTON_SIGN_DISABLED_BRUSH = QBrush(QColor(200, 200, 200)) SIGN_WIDTH = 7 SIGN_SIZE = 0.7 def __init__(self): AbstractQtLayer.__init__(self) self.click_zoom_in = None self.click_zoom_out = None @staticmethod def set_layout(message_draw_font, message_draw_color): QtOsdLayer.MESSAGE_DRAW_FONT = message_draw_font QtOsdLayer.MESSAGE_DRAW_COLOR = message_draw_color def clicked_screen(self, screenpoint): if self.click_zoom_in == None or self.click_zoom_out == None: return if self.click_zoom_in.contains(*screenpoint): self.map.zoom_in() return False elif self.click_zoom_out.contains(*screenpoint): self.map.zoom_out() return False def draw(self): # scale bar position position = (self.OSD_BORDER_LEFTRIGHT, self.map.map_height - 10 - self.OSD_BORDER_TOPBOTTOM) # scale bar calculation center = self.map.get_center() mpp = self.map.get_meters_per_pixel(center.lat) avglength = self.map.map_width / 5 first_length_meters = mpp * avglength final_length_meters = round(first_length_meters, int(-math.floor(math.log(first_length_meters, 10) + 0.00001))) final_length_pixels = final_length_meters / mpp if final_length_meters < 10000: scale_msg = "%d m" % final_length_meters else: scale_msg = "%d km" % (final_length_meters / 1000) # start drawing self.result.fill(Qt.transparent) p = self.painter p.begin(self.result) p.setRenderHint(QPainter.Antialiasing) p.setFont(QtOsdLayer.MESSAGE_DRAW_FONT) p.setPen(QtOsdLayer.MESSAGE_DRAW_PEN) # osd message if self.map.osd_message != None: p.drawText(QRect(self.OSD_BORDER_LEFTRIGHT, self.OSD_BORDER_TOPBOTTOM, 300, 20), Qt.AlignLeft, self.map.osd_message) # scale bar text p.drawText(QRect(self.OSD_BORDER_LEFTRIGHT, self.map.map_height - 10 - self.OSD_BORDER_TOPBOTTOM - 20, 200, 20), Qt.AlignLeft, scale_msg) # scale bar p.setPen(QtOsdLayer.OSD_PEN) p.setBrush(QtOsdLayer.OSD_BRUSH) p.drawRect(position[0], position[1] + 10, final_length_pixels, 3) # zoom buttons bd = self.BUTTON_DISTANCE zoom_in_pos_y = self.map.map_height/2.0 - bd/2.0 - self.BUTTON_SIZE zoom_out_pos_y = self.map.map_height/2.0 + bd/2.0 self.click_zoom_in = self.__draw_zoom_button(self.OSD_BORDER_LEFTRIGHT, zoom_in_pos_y, 1) self.click_zoom_out = self.__draw_zoom_button(self.OSD_BORDER_LEFTRIGHT, zoom_out_pos_y, -1) p.end() def __draw_zoom_button(self, pos_x, pos_y, direction = -1): bs = self.BUTTON_SIZE p = self.painter rect = QRectF(pos_x, pos_y, bs, bs) p.setPen(self.BUTTON_PEN) p.setBrush(self.BUTTON_BRUSH) p.drawRoundedRect(rect, 5, 5) sign = QRectF(pos_x + ((1-self.SIGN_SIZE)*bs)/2.0, pos_y + bs/2.0 - self.SIGN_WIDTH/2.0, bs * self.SIGN_SIZE, self.SIGN_WIDTH) new_zoom = self.map.get_zoom() + direction if new_zoom < self.map.get_min_zoom() or new_zoom > self.map.get_max_zoom(): p.setPen(self.BUTTON_SIGN_DISABLED_PEN) p.setBrush(self.BUTTON_SIGN_DISABLED_BRUSH) else: p.setPen(self.BUTTON_SIGN_PEN) p.setBrush(self.BUTTON_SIGN_BRUSH) p.drawRect(sign) if direction == 1: sign = QRectF(pos_x + bs/2.0 - self.SIGN_WIDTH/2.0, pos_y + ((1-self.SIGN_SIZE)*bs)/2.0, self.SIGN_WIDTH, bs * self.SIGN_SIZE) p.drawRect(sign) return rect logger = logging.getLogger('geocachelayer') class QtGeocacheLayer(AbstractQtLayer, AbstractGeocacheLayer): CACHE_DRAW_FONT = QFont('Sans', 10) CACHE_DRAW_FONT_PEN = QPen(QColor(0, 0, 0)) BRUSH_CACHE = QBrush(QColor(255, 255, 255, 100)) PEN_CURRENT_CACHE = QPen(QColor(200, 0, 0), 1) PEN_CACHE_DISABLED = QPen(QColor(255, 0, 0), 3) PEN_WAYPOINTS = QPen(QColor(200, 0, 200), 1) # map markers colors COLOR_MARKED = QColor(255, 255, 0) COLOR_DEFAULT = QColor(0, 0, 255) COLOR_FOUND = QColor(100, 100, 100) COLOR_REGULAR = QColor(0, 255, 0) COLOR_MULTI = QColor(255, 120, 0) COLOR_CACHE_CENTER = QColor(0, 0, 0) CENTER_CROSS_SIZE = 3 CENTER_CROSS_PEN = QPen(QColor(0, 0, 0), 0.7) def __init__(self, get_geocaches_callback, show_cache_callback): AbstractQtLayer.__init__(self) AbstractGeocacheLayer.__init__(self, get_geocaches_callback, show_cache_callback) def clicked_screen(self, screenpoint): AbstractGeocacheLayer.clicked_screen(self, screenpoint) def clicked_coordinate(self, center, topleft, bottomright): AbstractGeocacheLayer.clicked_coordinate(self, center, topleft, bottomright) def draw(self): coords = self.get_geocaches_callback(self.map.get_visible_area(), self.MAX_NUM_RESULTS_SHOW) self.result.fill(Qt.transparent) if self.map.get_zoom() < self.CACHES_ZOOM_LOWER_BOUND: self.map.set_osd_message('Zoom in to see geocaches.') self.visualized_geocaches = [] return elif len(coords) >= self.MAX_NUM_RESULTS_SHOW: self.map.set_osd_message('Too many geocaches to display.') self.visualized_geocaches = [] return self.map.set_osd_message(None) self.visualized_geocaches = coords draw_short = (len(coords) > self.TOO_MANY_POINTS) default_radius = self.CACHE_DRAW_SIZE found, regular, multi, default = self.COLOR_FOUND, self.COLOR_REGULAR, self.COLOR_MULTI, self.COLOR_DEFAULT p = self.painter p.begin(self.result) p.setRenderHint(QPainter.Antialiasing) p.setFont(self.CACHE_DRAW_FONT) cache_pen = QPen() cache_pen.setWidth(2) #cache_pen.setJoinStyle(Qt.MiterJoin) desc_pen = QPen() desc_pen.setWidth(2) for c in coords: # for each geocache radius = default_radius if c.found: color = found elif c.type == geocaching.GeocacheCoordinate.TYPE_REGULAR: color = regular elif c.type == geocaching.GeocacheCoordinate.TYPE_MULTI: color = multi else: color = default cache_pen.setColor(color) loc = self.map.coord2point(c) if c.alter_lat != None and (c.alter_lat != 0 and c.alter_lon != 0): x = self.map.coord2point(geo.Coordinate(c.alter_lat, c.alter_lon)) if x != loc: p.setPen(QPen(color, 2)) p.drawLine(QPoint(*loc), QPoint(*t)) if draw_short: radius = radius / 2.0 if c.marked: p.setBrush(QBrush(QColor(1, 1, 0, 0.5))) p.setPen(QPen(Qt.transparent)) p.drawRect(loc[0] - radius, loc[1] - radius, radius * 2, radius * 2) p.setBrush(self.BRUSH_CACHE) p.setPen(cache_pen) rect = QRectF(loc[0] - radius, loc[1] - radius, radius * 2, radius * 2) #p.drawRoundedRect(rect, 3.5, 3.5) p.drawRect(rect) p.setBrush(QBrush(Qt.transparent)) if draw_short: continue # + p.setPen(self.CENTER_CROSS_PEN) s = self.CENTER_CROSS_SIZE p.drawLines( QLineF(loc[0] - s, loc[1] - s, loc[0] + s, loc[1] + s), QLineF(loc[0] - s, loc[1] + s, loc[0] + s, loc[1] - s) ) # if we have a description for this cache... if c.was_downloaded(): # draw something like: # ---- # ---- # ---- # besides the icon width = 6 dist = 3 count = 3 pos_x = loc[0] + radius + 3 + 1 pos_y = loc[1] + radius - (dist * count) + dist desc_pen.setColor(color) p.setPen(desc_pen) lines = (QLine(pos_x, pos_y + dist * i, pos_x + width, pos_y + dist * i) for i in xrange(count)) p.drawLines(*lines) # if this cache is the active cache if self.current_cache != None and c.name == self.current_cache.name: p.setPen(self.PEN_CURRENT_CACHE) p.setBrush(QBrush(Qt.transparent)) radius_outline = radius + 3 p.drawRect(loc[0] - radius_outline, loc[1] - radius_outline, radius_outline * 2, radius_outline * 2) # if this cache is disabled if c.status == geocaching.GeocacheCoordinate.STATUS_DISABLED: p.setPen(self.PEN_CACHE_DISABLED) radius_disabled = 7 p.drawLine(loc[0]-radius_disabled, loc[1]-radius_disabled, loc[0] + radius_disabled, loc[1] + radius_disabled) # print the name? if self.show_name: p.setPen(self.CACHE_DRAW_FONT_PEN) p.drawText(loc[0] + 4 + radius, loc[1] - height + 2, AbstractGeocacheLayer.shorten_name(c.title, 20)) # draw additional waypoints # --> print description! if self.current_cache != None and self.current_cache.get_waypoints() != None: p.setPen(self.PEN_WAYPOINTS) radius = 5 num = 0 lines = [] for w in self.current_cache.get_waypoints(): if w['lat'] != -1 and w['lon'] != -1: num = num + 1 loc = self.map.coord2point(geo.Coordinate(w['lat'], w['lon'])) if not self.map.point_in_screen(loc): continue lines.append(QLine(loc[0], loc[1] - radius, loc[0], loc[1] + radius)) lines.append(QLine(loc[0] - radius, loc[1], loc[0] + radius, loc[1])) p.drawArc(QRectF(loc[0] - radius - 1, loc[1] - radius - 1, radius * 2 + 1, radius * 2 + 1), 0, 16 * 360) p.drawText(loc[0] + 3 + radius, loc[1] - 3 - radius, w['id']) p.drawLines(lines) p.end() logger = logging.getLogger('markslayer') class QtMarksLayer(AbstractQtLayer, AbstractMarksLayer): SIZE_CURRENT_POSITION = 5 BRUSH_CURRENT_POSITION = QBrush(QColor(0, 255, 0)) BRUSH_CURRENT_POSITION_NO_FIX = QBrush(QColor(255, 0, 0)) PEN_TARGET = QPen(QColor(0, 0, 200), 1) PEN_SHADOW_TARGET = QPen(QColor(255, 255, 255), 3) PEN_LINE_DIRECT_LINE = QPen(QColor(255, 255, 0, 0.5), 5) PEN_ACCURACY = QPen(QColor(255, 125, 0), 2) PEN_POSITION = QPen(QColor(0, 0, 0), 2) PEN_POSITION_SHADOW = QPen(QColor(255, 255, 255), 4) DISTANCE_DRAW_FONT = QFont('Sans', 12) DISTANCE_DRAW_PEN = QPen(QColor(0, 0, 0)) OSD_BORDER_TOPBOTTOM = 25 OSD_BORDER_LEFTRIGHT = 35 RADIUS_ARROW = 30 RADIUS_STANDING = 5 def __init__(self): AbstractQtLayer.__init__(self) AbstractMarksLayer.__init__(self) self.PEN_ACCURACY.setDashPattern([5, 3]) def draw(self): self.result.fill(Qt.transparent) p = self.painter p.begin(self.result) p.setRenderHint(QPainter.Antialiasing) # if we have a target, draw it if self.current_target != None: t = self.map.coord2point(self.current_target) if t != False and self.map.point_in_screen(t): radius_o = 15 radius_i = 3 radius_c = 10 p.setPen(QtSingleMarkLayer.PEN_SHADOW_TARGET) p.drawLines( QLineF(t[0] - radius_o, t[1], t[0] - radius_i, t[1]), QLineF(t[0] + radius_o, t[1], t[0] + radius_i, t[1]), QLineF(t[0], t[1] + radius_o, t[0], t[1] + radius_i), QLineF(t[0], t[1] - radius_o, t[0], t[1] - radius_i) ) p.drawArc(QRectF(t[0] - radius_c / 2, t[1] - radius_c / 2, radius_c, radius_c), 0, 16 * 360) p.setPen(QtSingleMarkLayer.PEN_TARGET) p.drawLines( QLineF(t[0] - radius_o, t[1], t[0] - radius_i, t[1]), QLineF(t[0] + radius_o, t[1], t[0] + radius_i, t[1]), QLineF(t[0], t[1] + radius_o, t[0], t[1] + radius_i), QLineF(t[0], t[1] - radius_o, t[0], t[1] - radius_i) ) p.drawArc(QRectF(t[0] - radius_c / 2, t[1] - radius_c / 2, radius_c, radius_c), 0, 16 * 360) else: t = False if self.gps_last_good_fix != None and self.gps_last_good_fix.position != None: loc = self.map.coord2point_float(self.gps_last_good_fix.position) else: loc = None # a line between target and position if we have both if loc != None and t != False: p.setPen(self.PEN_LINE_DIRECT_LINE) if self.map.point_in_screen(t) and self.map.point_in_screen(loc): p.drawLine(loc[0], loc[1], t[0], t[1]) elif self.map.point_in_screen(loc): direction = math.radians(self.gps_target_bearing - 180) # correct max length: sqrt(width**2 + height**2) length = self.map.map_width p.drawLine(loc[0], loc[1], loc[0] - math.sin(direction) * length, loc[1] + math.cos(direction) * length) elif self.map.point_in_screen(t): direction = math.radians(self.gps_target_bearing) length = self.map.map_width + self.map.map_height p.drawLine(t[0], t[1], t[0] - math.sin(direction) * length, t[1] + math.cos(direction) * length) if loc != None and self.map.point_in_screen(loc): if self.gps_has_fix: radius = self.gps_data.error radius_pixels = radius / self.map.get_meters_per_pixel(self.gps_last_good_fix.position.lat) else: radius_pixels = 10 if self.gps_has_fix: p.setPen(self.PEN_ACCURACY) p.setBrush(QBrush(Qt.transparent)) p.drawArc(loc[0] - radius_pixels / 2.0 - 1, loc[1] - radius_pixels / 2.0 - 1, radius_pixels, radius_pixels, 0, 16 * 360) # draw moving direction, if we're moving if self.gps_data.speed > 2.5: # km/h arrow = self._get_arrow_transformed(loc[0] - self.RADIUS_ARROW / 2, loc[1] - self.RADIUS_ARROW / 2, self.RADIUS_ARROW, self.RADIUS_ARROW, self.gps_data.bearing) p.setPen(self.PEN_POSITION_SHADOW) p.drawPolyline(*arrow) p.setPen(self.PEN_POSITION) p.drawPolyline(*arrow) else: p.drawArc(loc[0] - self.RADIUS_STANDING / 2.0, loc[1] - self.RADIUS_STANDING / 2.0 - 1, self.RADIUS_STANDING + 1, self.RADIUS_STANDING + 1, 0, 16 * 360) if self.gps_has_fix: p.setBrush(self.BRUSH_CURRENT_POSITION) else: p.setBrush(self.BRUSH_CURRENT_POSITION_NO_FIX) p.setPen(QPen(Qt.transparent)) p.drawPie(loc[0] - self.SIZE_CURRENT_POSITION / 2.0, loc[1] - self.SIZE_CURRENT_POSITION / 2.0, self.SIZE_CURRENT_POSITION + 1, self.SIZE_CURRENT_POSITION + 1, 0, 16 * 360) if self.gps_data != None and self.gps_has_fix: p.setFont(self.DISTANCE_DRAW_FONT) p.setPen(self.DISTANCE_DRAW_PEN) position = QRect(self.map.map_width - self.OSD_BORDER_LEFTRIGHT - 100, self.OSD_BORDER_TOPBOTTOM, 100, 40) text = geo.Coordinate.format_distance(self.gps_target_distance) p.drawText(position, Qt.AlignRight | Qt.TextDontClip, text) p.end()agtl-0.8.0.3/files/advancedcaching/qt/optionsdialog.py000066400000000000000000000063731151564747700226760ustar00rootroot00000000000000#!/usr/bin/python # -*- coding: utf-8 -*- # Copyright (C) 2010 Daniel Fett # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # Author: Daniel Fett agtl@danielfett.de # Jabber: fett.daniel@jaber.ccc.de # Bugtracker and GIT Repository: http://github.com/webhamster/advancedcaching # import logging from PyQt4.QtCore import * from PyQt4.QtGui import * from ui_optionsdialog import Ui_OptionsDialog logger = logging.getLogger('qtoptionsdialog') d = lambda x: x.decode('utf-8') class QtOptionsDialog(Ui_OptionsDialog, QDialog): saveSettings = pyqtSignal() TTS_SETTINGS = ( (0, 'Off'), (-1, 'Automatic'), (10, '10 Seconds'), (20, '20 Seconds'), (30, '30 Seconds'), (50, '50 Seconds'), (100, '100 Seconds'), (180, '3 Minutes'), (5 * 60, '5 Minutes'), (10 * 60, '10 Minutes'), ) def __init__(self, core, settings, parent=None): QDialog.__init__(self, parent) self.setupUi(self) self.core = core self.buttonBox.clicked.connect(self.__button_clicked) self.load_settings(settings) def load_settings(self, settings): self.lineEditUserName.setText(d(settings['options_username'])) self.lineEditPassword.setText(d(settings['options_password'])) self.checkBoxHideFound.setCheckState(Qt.Checked if settings['options_hide_found'] else Qt.Unchecked) self.checkBoxShowName.setCheckState(Qt.Checked if settings['options_show_name'] else Qt.Unchecked) self.checkBoxDoubleSize.setCheckState(Qt.Checked if settings['options_map_double_size'] else Qt.Unchecked) self.comboBoxTTS.clear() i = 0 for time, text in self.TTS_SETTINGS: self.comboBoxTTS.addItem(text) if time == settings['tts_interval']: self.comboBoxTTS.setCurrentIndex(i) i += 1 def get_settings(self): return { 'options_username' : unicode(self.lineEditUserName.text()), 'options_password' : unicode(self.lineEditPassword.text()), 'options_hide_found' : (self.checkBoxHideFound.checkState() == Qt.Checked), 'options_show_name' : (self.checkBoxShowName.checkState() == Qt.Checked), 'options_map_double_size' : (self.checkBoxDoubleSize.checkState() == Qt.Checked), 'tts_interval' : self.TTS_SETTINGS[self.comboBoxTTS.currentIndex()][0] } def __button_clicked(self, button): id = self.buttonBox.standardButton(button) if id == QDialogButtonBox.Ok: self.saveSettings.emit()agtl-0.8.0.3/files/advancedcaching/qt/searchdialog.py000066400000000000000000000057171151564747700224510ustar00rootroot00000000000000#!/usr/bin/python # -*- coding: utf-8 -*- # Copyright (C) 2010 Daniel Fett # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # Author: Daniel Fett agtl@danielfett.de # Jabber: fett.daniel@jaber.ccc.de # Bugtracker and GIT Repository: http://github.com/webhamster/advancedcaching # import logging from PyQt4.QtCore import * from PyQt4.QtGui import * import geo from ui_searchdialog import Ui_SearchDialog d = lambda x: x.decode('utf-8') logger = logging.getLogger('qtsearchdialog') class QtSearchDialog(Ui_SearchDialog, QDialog): locationSelected = pyqtSignal(geo.Coordinate) def __init__(self, core, parent=None): QDialog.__init__(self, parent) self.setupUi(self) self.core = core self.pushButtonSearch.clicked.connect(self.__start_search) #self.lineEditSearch.returnPressed.connect(self.__start_search) self.listWidgetResults.itemClicked.connect(self.__return_location) def __start_search(self): search_text = unicode(self.lineEditSearch.text()).strip() if search_text == '': return try: self.results = self.core.search_place(search_text) except Exception, e: QErrorMessage.qtHandler().showMessage(repr(e)) logger.exception(repr(e)) return self.listWidgetResults.clear() if len(self.results) == 0: QMessageBox.information(self, "Search results", "The search returned no results.") return i = 0 if self.core.current_position == None: for res in self.results: m = QListWidgetItem("res.name", self.listWidgetResults) m.setData(Qt.UserRole, QVariant(i)) i += 1 else: pos = self.core.current_position for res in self.results: distance = geo.Coordinate.format_distance(res.distance_to(pos)) direction = geo.Coordinate.format_direction(pos.bearing_to(res)) text = "%s (%s %s)" % (res.name, distance, direction) m = QListWidgetItem(text, self.listWidgetResults) m.setData(Qt.UserRole, QVariant(i)) i += 1 def __return_location(self, item): res = self.results[item.data(Qt.UserRole).toInt()[0]] logger.debug("Setting center to %s" % res) self.locationSelected.emit(res)agtl-0.8.0.3/files/advancedcaching/qt/searchgeocachesdialog.py000066400000000000000000000141211151564747700243000ustar00rootroot00000000000000#!/usr/bin/python # -*- coding: utf-8 -*- # Copyright (C) 2010 Daniel Fett # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # Author: Daniel Fett agtl@danielfett.de # Jabber: fett.daniel@jaber.ccc.de # Bugtracker and GIT Repository: http://github.com/webhamster/advancedcaching # import logging from PyQt4.QtCore import * from PyQt4.QtGui import * import geocaching from searchresultsdialog import QtSearchResultsDialog from ui_searchgeocachesdialog import Ui_SearchGeocachesDialog d = lambda x: x.decode('utf-8') logger = logging.getLogger('qtsearchgeocachesdialog') class QtSearchGeocachesDialog(Ui_SearchGeocachesDialog, QDialog): RADI = [1, 2, 3, 5, 10, 15, 20, 50, 100] TYPELIST = [ ('traditional', geocaching.GeocacheCoordinate.TYPE_REGULAR), ('multi stage', geocaching.GeocacheCoordinate.TYPE_MULTI), ('virtual', geocaching.GeocacheCoordinate.TYPE_VIRTUAL), ('earth', geocaching.GeocacheCoordinate.TYPE_EARTH), ('event', geocaching.GeocacheCoordinate.TYPE_EVENT), ('mystery', geocaching.GeocacheCoordinate.TYPE_MYSTERY), ('webcam', geocaching.GeocacheCoordinate.TYPE_WEBCAM), ('all/other', geocaching.GeocacheCoordinate.TYPE_UNKNOWN) ] def __init__(self, core, map_position, user_position, parent=None): QDialog.__init__(self, parent) self.core = core self.setupUi(self) self.populateUi() self.setModal(True) self.dialogButtonBox.clicked.connect(self.__button_clicked) self.map_position = map_position self.user_position = user_position def populateUi(self): for name, type in self.TYPELIST: m = QListWidgetItem(name, self.listWidgetType) m.setCheckState(Qt.Unchecked if type == geocaching.GeocacheCoordinate.TYPE_UNKNOWN else Qt.Checked) self.comboBoxLocation.currentIndexChanged.connect(self.__combo_box_changed) def __combo_box_changed(self, index): self.spinBoxRadius.setEnabled(index != 0) if index == 1: text = self.map_position.get_latlon() if self.map_position != None else 'not available' elif index == 2: text = self.user_position.get_latlon() if self.user_position != None else 'not available' else: text = '' self.labelLocation.setText(d(text)) def show(self): QDialog.show(self) def __button_clicked(self, button): id = self.dialogButtonBox.standardButton(button) if id == QDialogButtonBox.Ok: self.__start_search() def __start_search(self): # Name name = str(self.lineEditName.text()).strip().lower() if name == '': name = None logger.debug("Name: %s" % name) # Location & Radius i = self.comboBoxLocation.currentIndex() if i == 1 and self.map_position != None: center = self.map_position elif i == 2 and self.user_position != None: center = self.user_position else: center = None if center != None: radius = self.spinBoxRadius.value() sqrt_2 = 1.41421356 c1 = center.transform(-45, radius * 1000 * sqrt_2) c2 = center.transform(-45 + 180, radius * 1000 * sqrt_2) location = (c2, c1) logger.debug("Location: %s %s" % location) else: location = None logger.debug("Location: None") # Details (1) types = [self.TYPELIST[x][1] for x in range(self.listWidgetType.count()) if self.listWidgetType.item(x).checkState() == Qt.Checked] if geocaching.GeocacheCoordinate.TYPE_UNKNOWN in types: types = None logger.debug("Types: %s" % types) if self.checkBoxHideFound.checkState() == Qt.Checked: found = False else: found = None logger.debug("Found: %s" % found) if self.checkBoxShowOnlyMarked.checkState() == Qt.Checked: marked = True else: marked = False logger.debug("Marked: %s" % marked) # Details (2) sizes = [x + 1 for x in range(self.listWidgetSize.count()) if self.listWidgetSize.item(x).checkState() == Qt.Checked] if sizes == [1, 2, 3, 4, 5]: sizes = None logger.debug("Sizes: %s" % sizes) r = lambda x: int(x / 0.5) * 0.5 diff_min = r(self.doubleSpinBoxDifficultyMin.value()) diff_max = r(self.doubleSpinBoxDifficultyMax.value() + 0.5) if diff_min == 1 and diff_max == 5.5: difficulties = None else: # range doesn't support floats! difficulties = [x / 10.0 for x in range(int(diff_min * 10), int(diff_max * 10), 5)] logger.debug("Difficulties: %s" % difficulties) terr_min = r(self.doubleSpinBoxTerrainMin.value()) terr_max = r(self.doubleSpinBoxTerrainMax.value() + 0.5) if terr_min == 1 and terr_max == 5.5: terrains = None else: # range doesn't support floats! terrains = [x / 10.0 for x in range(int(terr_min * 10), int(terr_max * 10), 5)] logger.debug("Terrains: %s" % terrains) results = self.core.get_points_filter(found=found, name_search=name, size=sizes, terrain=terrains, diff=difficulties, ctype=types, marked=marked, location=location) logger.debug("Found %d results" % len(results[0])) self.__show_results(results) def __show_results(self, results): d = QtSearchResultsDialog(self.core) d.show(results[0]) agtl-0.8.0.3/files/advancedcaching/qt/searchresultsdialog.py000066400000000000000000000111111151564747700240540ustar00rootroot00000000000000#!/usr/bin/python # -*- coding: utf-8 -*- # Copyright (C) 2010 Daniel Fett # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # Author: Daniel Fett agtl@danielfett.de # Jabber: fett.daniel@jaber.ccc.de # Bugtracker and GIT Repository: http://github.com/webhamster/advancedcaching # import logging from PyQt4.QtCore import * from PyQt4.QtGui import * import geo from qt.mapwidget import QtGeocacheLayer from qt.mapwidget import QtMap, QtOsdLayer from ui_searchresultsdialog import Ui_SearchResultsDialog logger = logging.getLogger('qtsearchresultsdialog') d = lambda x: x.decode('utf-8') class QtSearchResultsDialog(Ui_SearchResultsDialog, QDialog): def __init__(self, core, parent=None): QDialog.__init__(self, parent) self.core = core self.setupUi(self) self.setup_ui_custom() self.results = [] self.selected_results = [] def show(self, results): QDialog.show(self) self.results = results self.tableWidgetResults.clearContents() self.tableWidgetResults.setRowCount(len(results)) row = 0 max_size_first_col = 0 for g in results: col = 0 items = self.__make_items(g) # remember size of first col max_size_first_col = max(max_size_first_col, items[0].sizeHint().width()) for item in items: self.tableWidgetResults.setItem(row, col, item) col += 1 row += 1 self.tableWidgetResults.resizeColumnsToContents() self.tableWidgetResults.setColumnWidth(0, 300) self.tableWidgetResults.selectAll() def __make_items(self, cache): # used to identify this cache later. start = QTableWidgetItem(d(cache.title)) if self.core.current_position != None: distance = geo.Coordinate.format_distance(self.core.current_position.distance_to(cache)) direction = geo.Coordinate.format_direction(self.core.current_position.bearing_to(cache)) last = QTableWidgetItem("%s %s" % (distance, direction)) else: last = QTableWidgetItem("?") entries = [ start, QTableWidgetItem(d(cache.get_size_string())), QTableWidgetItem(d(cache.get_terrain())), QTableWidgetItem(d(cache.get_difficulty())), last ] cache.item = start return entries def setup_ui_custom(self): self.map = QtMap(self, geo.Coordinate(0, 0), 1) self.geocacheLayer = QtGeocacheLayer(self.__get_geocaches_callback, self.__show_cache) self.osdLayer = QtOsdLayer() self.map.add_layer(self.geocacheLayer) self.map.add_layer(self.osdLayer) #self.map = QLabel("test") self.layout().insertWidget(1, self.map) self.map.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.map.show() self.centralLayout = QVBoxLayout() self.splitter = QSplitter() self.splitter.setOrientation(Qt.Vertical) self.splitter.addWidget(self.tableWidgetResults) self.splitter.addWidget(self.map) self.centralLayout.addWidget(self.splitter) self.centralLayout.addWidget(self.pushButtonExportSelected) l = self.layout() l.deleteLater() QCoreApplication.sendPostedEvents(l, QEvent.DeferredDelete) self.setLayout(self.centralLayout) self.splitter.setSizes([1000, 1000]) self.tableWidgetResults.itemSelectionChanged.connect(self.__selection_changed) def __selection_changed(self): self.selected_results = [c for c in self.results if self.tableWidgetResults.isItemSelected(c.item)] if len(self.selected_results) > 0: self.map.fit_to_bounds(*geo.Coordinate.get_bounds(self.selected_results)) #self.map.refresh() def __get_geocaches_callback(self, visible_area, maxresults): return [x for x in self.selected_results if self.map.in_area(x, visible_area)] def __show_cache(self, cache): pass agtl-0.8.0.3/files/advancedcaching/qt/showimagedialog.py000066400000000000000000000030611151564747700231550ustar00rootroot00000000000000#!/usr/bin/python # -*- coding: utf-8 -*- # Copyright (C) 2010 Daniel Fett # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # Author: Daniel Fett agtl@danielfett.de # Jabber: fett.daniel@jaber.ccc.de # Bugtracker and GIT Repository: http://github.com/webhamster/advancedcaching # import logging from PyQt4.QtCore import * from PyQt4.QtGui import * from ui_showimagedialog import Ui_ShowImageDialog logger = logging.getLogger('qtshowimagedialog') class QtShowImageDialog(Ui_ShowImageDialog, QDialog): def __init__(self, parent=None): QDialog.__init__(self, parent) self.setupUi(self) self.size_hint = QSize(10, 10) def show_image(self, pixmap): self.labelImage.setPixmap(pixmap) self.size_hint = pixmap.size() self.labelImage.adjustSize() self.scrollAreaWidgetContents.adjustSize() self.scrollArea.adjustSize() self.adjustSize() def sizeHint(self): return self.size_hint agtl-0.8.0.3/files/advancedcaching/qt/ui_geocachedetailswindow.py000066400000000000000000000356071151564747700250560ustar00rootroot00000000000000# -*- coding: utf-8 -*- # Form implementation generated from reading ui file 'files/qt/GeocacheDetailsWindow.ui' # # Created: Wed Aug 4 14:54:49 2010 # by: PyQt4 UI code generator 4.7.2 # # WARNING! All changes made in this file will be lost! from PyQt4 import QtCore, QtGui class Ui_GeocacheDetailsWindow(object): def setupUi(self, GeocacheDetailsWindow): GeocacheDetailsWindow.setObjectName("GeocacheDetailsWindow") GeocacheDetailsWindow.resize(800, 480) self.centralwidget = QtGui.QWidget(GeocacheDetailsWindow) self.centralwidget.setObjectName("centralwidget") self.horizontalLayout = QtGui.QHBoxLayout(self.centralwidget) self.horizontalLayout.setObjectName("horizontalLayout") self.tabWidget = QtGui.QTabWidget(self.centralwidget) self.tabWidget.setObjectName("tabWidget") self.tabInformation = QtGui.QWidget() self.tabInformation.setObjectName("tabInformation") self.gridLayout = QtGui.QGridLayout(self.tabInformation) self.gridLayout.setObjectName("gridLayout") self.label_2 = QtGui.QLabel(self.tabInformation) font = QtGui.QFont() font.setWeight(75) font.setBold(True) self.label_2.setFont(font) self.label_2.setObjectName("label_2") self.gridLayout.addWidget(self.label_2, 0, 0, 1, 1) self.labelFullName = QtGui.QLabel(self.tabInformation) self.labelFullName.setText("") self.labelFullName.setObjectName("labelFullName") self.gridLayout.addWidget(self.labelFullName, 0, 1, 1, 1) self.label_3 = QtGui.QLabel(self.tabInformation) font = QtGui.QFont() font.setWeight(75) font.setBold(True) self.label_3.setFont(font) self.label_3.setObjectName("label_3") self.gridLayout.addWidget(self.label_3, 1, 0, 1, 1) self.label_4 = QtGui.QLabel(self.tabInformation) font = QtGui.QFont() font.setWeight(75) font.setBold(True) self.label_4.setFont(font) self.label_4.setObjectName("label_4") self.gridLayout.addWidget(self.label_4, 2, 0, 1, 1) self.label_5 = QtGui.QLabel(self.tabInformation) font = QtGui.QFont() font.setWeight(75) font.setBold(True) self.label_5.setFont(font) self.label_5.setObjectName("label_5") self.gridLayout.addWidget(self.label_5, 3, 0, 1, 1) self.label_6 = QtGui.QLabel(self.tabInformation) font = QtGui.QFont() font.setWeight(75) font.setBold(True) self.label_6.setFont(font) self.label_6.setObjectName("label_6") self.gridLayout.addWidget(self.label_6, 4, 0, 1, 1) self.labelID = QtGui.QLabel(self.tabInformation) self.labelID.setText("") self.labelID.setObjectName("labelID") self.gridLayout.addWidget(self.labelID, 1, 1, 1, 1) self.labelType = QtGui.QLabel(self.tabInformation) self.labelType.setText("") self.labelType.setObjectName("labelType") self.gridLayout.addWidget(self.labelType, 2, 1, 1, 1) self.labelSize = QtGui.QLabel(self.tabInformation) self.labelSize.setText("") self.labelSize.setObjectName("labelSize") self.gridLayout.addWidget(self.labelSize, 3, 1, 1, 1) self.label_11 = QtGui.QLabel(self.tabInformation) font = QtGui.QFont() font.setWeight(75) font.setBold(True) self.label_11.setFont(font) self.label_11.setObjectName("label_11") self.gridLayout.addWidget(self.label_11, 5, 0, 1, 1) self.label_12 = QtGui.QLabel(self.tabInformation) font = QtGui.QFont() font.setWeight(75) font.setBold(True) self.label_12.setFont(font) self.label_12.setObjectName("label_12") self.gridLayout.addWidget(self.label_12, 6, 0, 1, 1) self.label_13 = QtGui.QLabel(self.tabInformation) font = QtGui.QFont() font.setWeight(75) font.setBold(True) self.label_13.setFont(font) self.label_13.setObjectName("label_13") self.gridLayout.addWidget(self.label_13, 7, 0, 1, 1) self.labelTerrain = QtGui.QLabel(self.tabInformation) self.labelTerrain.setText("") self.labelTerrain.setObjectName("labelTerrain") self.gridLayout.addWidget(self.labelTerrain, 4, 1, 1, 1) self.labelDifficulty = QtGui.QLabel(self.tabInformation) self.labelDifficulty.setText("") self.labelDifficulty.setObjectName("labelDifficulty") self.gridLayout.addWidget(self.labelDifficulty, 5, 1, 1, 1) self.labelOwner = QtGui.QLabel(self.tabInformation) self.labelOwner.setText("") self.labelOwner.setObjectName("labelOwner") self.gridLayout.addWidget(self.labelOwner, 6, 1, 1, 1) self.labelStatus = QtGui.QLabel(self.tabInformation) self.labelStatus.setText("") self.labelStatus.setObjectName("labelStatus") self.gridLayout.addWidget(self.labelStatus, 7, 1, 1, 1) self.tabWidget.addTab(self.tabInformation, "") self.tabDescription = QtGui.QWidget() self.tabDescription.setObjectName("tabDescription") self.horizontalLayout_7 = QtGui.QHBoxLayout(self.tabDescription) self.horizontalLayout_7.setObjectName("horizontalLayout_7") self.scrollArea_2 = QtGui.QScrollArea(self.tabDescription) self.scrollArea_2.setWidgetResizable(True) self.scrollArea_2.setObjectName("scrollArea_2") self.scrollAreaWidgetContents_2 = QtGui.QWidget(self.scrollArea_2) self.scrollAreaWidgetContents_2.setGeometry(QtCore.QRect(0, 0, 770, 387)) self.scrollAreaWidgetContents_2.setObjectName("scrollAreaWidgetContents_2") self.horizontalLayout_6 = QtGui.QHBoxLayout(self.scrollAreaWidgetContents_2) self.horizontalLayout_6.setObjectName("horizontalLayout_6") self.labelDescription = QtGui.QLabel(self.scrollAreaWidgetContents_2) self.labelDescription.setTextFormat(QtCore.Qt.RichText) self.labelDescription.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop) self.labelDescription.setWordWrap(True) self.labelDescription.setObjectName("labelDescription") self.horizontalLayout_6.addWidget(self.labelDescription) self.scrollArea_2.setWidget(self.scrollAreaWidgetContents_2) self.horizontalLayout_7.addWidget(self.scrollArea_2) self.tabWidget.addTab(self.tabDescription, "") self.tabLogsHints = QtGui.QWidget() self.tabLogsHints.setObjectName("tabLogsHints") self.verticalLayout = QtGui.QVBoxLayout(self.tabLogsHints) self.verticalLayout.setObjectName("verticalLayout") self.scrollArea = QtGui.QScrollArea(self.tabLogsHints) self.scrollArea.setWidgetResizable(True) self.scrollArea.setObjectName("scrollArea") self.scrollAreaWidgetContents = QtGui.QWidget(self.scrollArea) self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 770, 359)) self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents") self.horizontalLayout_2 = QtGui.QHBoxLayout(self.scrollAreaWidgetContents) self.horizontalLayout_2.setObjectName("horizontalLayout_2") self.labelLogs = QtGui.QLabel(self.scrollAreaWidgetContents) self.labelLogs.setTextFormat(QtCore.Qt.RichText) self.labelLogs.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop) self.labelLogs.setWordWrap(True) self.labelLogs.setObjectName("labelLogs") self.horizontalLayout_2.addWidget(self.labelLogs) self.scrollArea.setWidget(self.scrollAreaWidgetContents) self.verticalLayout.addWidget(self.scrollArea) self.pushButtonShowHint = QtGui.QPushButton(self.tabLogsHints) self.pushButtonShowHint.setObjectName("pushButtonShowHint") self.verticalLayout.addWidget(self.pushButtonShowHint) self.tabWidget.addTab(self.tabLogsHints, "") self.tabCoordinateCalculation = QtGui.QWidget() self.tabCoordinateCalculation.setObjectName("tabCoordinateCalculation") self.tabWidget.addTab(self.tabCoordinateCalculation, "") self.tabCoordinates = QtGui.QWidget() sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.tabCoordinates.sizePolicy().hasHeightForWidth()) self.tabCoordinates.setSizePolicy(sizePolicy) self.tabCoordinates.setObjectName("tabCoordinates") self.horizontalLayout_3 = QtGui.QHBoxLayout(self.tabCoordinates) self.horizontalLayout_3.setObjectName("horizontalLayout_3") self.tableWidget = QtGui.QTableWidget(self.tabCoordinates) self.tableWidget.setObjectName("tableWidget") self.tableWidget.setColumnCount(0) self.tableWidget.setRowCount(0) self.horizontalLayout_3.addWidget(self.tableWidget) self.tabWidget.addTab(self.tabCoordinates, "") self.tabImages = QtGui.QWidget() self.tabImages.setObjectName("tabImages") self.horizontalLayout_4 = QtGui.QHBoxLayout(self.tabImages) self.horizontalLayout_4.setObjectName("horizontalLayout_4") self.listWidgetImages = QtGui.QListWidget(self.tabImages) self.listWidgetImages.setViewMode(QtGui.QListView.IconMode) self.listWidgetImages.setObjectName("listWidgetImages") self.horizontalLayout_4.addWidget(self.listWidgetImages) self.tabWidget.addTab(self.tabImages, "") self.tabNotes = QtGui.QWidget() self.tabNotes.setObjectName("tabNotes") self.horizontalLayout_5 = QtGui.QHBoxLayout(self.tabNotes) self.horizontalLayout_5.setObjectName("horizontalLayout_5") self.plainTextEdit = QtGui.QPlainTextEdit(self.tabNotes) self.plainTextEdit.setObjectName("plainTextEdit") self.horizontalLayout_5.addWidget(self.plainTextEdit) self.tabWidget.addTab(self.tabNotes, "") self.horizontalLayout.addWidget(self.tabWidget) GeocacheDetailsWindow.setCentralWidget(self.centralwidget) self.menubar = QtGui.QMenuBar(GeocacheDetailsWindow) self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 21)) self.menubar.setObjectName("menubar") self.menuGeocache = QtGui.QMenu(self.menubar) self.menuGeocache.setObjectName("menuGeocache") GeocacheDetailsWindow.setMenuBar(self.menubar) self.statusbar = QtGui.QStatusBar(GeocacheDetailsWindow) self.statusbar.setObjectName("statusbar") GeocacheDetailsWindow.setStatusBar(self.statusbar) self.actionSet_as_Target = QtGui.QAction(GeocacheDetailsWindow) self.actionSet_as_Target.setObjectName("actionSet_as_Target") self.actionWrite_Fieldnote = QtGui.QAction(GeocacheDetailsWindow) self.actionWrite_Fieldnote.setObjectName("actionWrite_Fieldnote") self.actionDownload_Details = QtGui.QAction(GeocacheDetailsWindow) self.actionDownload_Details.setObjectName("actionDownload_Details") self.menuGeocache.addAction(self.actionSet_as_Target) self.menuGeocache.addAction(self.actionWrite_Fieldnote) self.menuGeocache.addAction(self.actionDownload_Details) self.menubar.addAction(self.menuGeocache.menuAction()) self.retranslateUi(GeocacheDetailsWindow) self.tabWidget.setCurrentIndex(0) QtCore.QMetaObject.connectSlotsByName(GeocacheDetailsWindow) def retranslateUi(self, GeocacheDetailsWindow): GeocacheDetailsWindow.setWindowTitle(QtGui.QApplication.translate("GeocacheDetailsWindow", "MainWindow", None, QtGui.QApplication.UnicodeUTF8)) self.label_2.setText(QtGui.QApplication.translate("GeocacheDetailsWindow", "Full Name", None, QtGui.QApplication.UnicodeUTF8)) self.label_3.setText(QtGui.QApplication.translate("GeocacheDetailsWindow", "ID", None, QtGui.QApplication.UnicodeUTF8)) self.label_4.setText(QtGui.QApplication.translate("GeocacheDetailsWindow", "Type", None, QtGui.QApplication.UnicodeUTF8)) self.label_5.setText(QtGui.QApplication.translate("GeocacheDetailsWindow", "Size", None, QtGui.QApplication.UnicodeUTF8)) self.label_6.setText(QtGui.QApplication.translate("GeocacheDetailsWindow", "Terrain", None, QtGui.QApplication.UnicodeUTF8)) self.label_11.setText(QtGui.QApplication.translate("GeocacheDetailsWindow", "Difficulty", None, QtGui.QApplication.UnicodeUTF8)) self.label_12.setText(QtGui.QApplication.translate("GeocacheDetailsWindow", "Owner", None, QtGui.QApplication.UnicodeUTF8)) self.label_13.setText(QtGui.QApplication.translate("GeocacheDetailsWindow", "Status", None, QtGui.QApplication.UnicodeUTF8)) self.tabWidget.setTabText(self.tabWidget.indexOf(self.tabInformation), QtGui.QApplication.translate("GeocacheDetailsWindow", "Information", None, QtGui.QApplication.UnicodeUTF8)) self.labelDescription.setText(QtGui.QApplication.translate("GeocacheDetailsWindow", "Description", None, QtGui.QApplication.UnicodeUTF8)) self.tabWidget.setTabText(self.tabWidget.indexOf(self.tabDescription), QtGui.QApplication.translate("GeocacheDetailsWindow", "Description", None, QtGui.QApplication.UnicodeUTF8)) self.labelLogs.setText(QtGui.QApplication.translate("GeocacheDetailsWindow", "Logs", None, QtGui.QApplication.UnicodeUTF8)) self.pushButtonShowHint.setText(QtGui.QApplication.translate("GeocacheDetailsWindow", "Show Hint", None, QtGui.QApplication.UnicodeUTF8)) self.tabWidget.setTabText(self.tabWidget.indexOf(self.tabLogsHints), QtGui.QApplication.translate("GeocacheDetailsWindow", "Logs && Hints", None, QtGui.QApplication.UnicodeUTF8)) self.tabWidget.setTabText(self.tabWidget.indexOf(self.tabCoordinateCalculation), QtGui.QApplication.translate("GeocacheDetailsWindow", "Coordinate Calculation", None, QtGui.QApplication.UnicodeUTF8)) self.tabWidget.setTabText(self.tabWidget.indexOf(self.tabCoordinates), QtGui.QApplication.translate("GeocacheDetailsWindow", "Coordinates", None, QtGui.QApplication.UnicodeUTF8)) self.tabWidget.setTabText(self.tabWidget.indexOf(self.tabImages), QtGui.QApplication.translate("GeocacheDetailsWindow", "Images", None, QtGui.QApplication.UnicodeUTF8)) self.tabWidget.setTabText(self.tabWidget.indexOf(self.tabNotes), QtGui.QApplication.translate("GeocacheDetailsWindow", "Notes", None, QtGui.QApplication.UnicodeUTF8)) self.menuGeocache.setTitle(QtGui.QApplication.translate("GeocacheDetailsWindow", "Geocache", None, QtGui.QApplication.UnicodeUTF8)) self.actionSet_as_Target.setText(QtGui.QApplication.translate("GeocacheDetailsWindow", "Set as Target", None, QtGui.QApplication.UnicodeUTF8)) self.actionWrite_Fieldnote.setText(QtGui.QApplication.translate("GeocacheDetailsWindow", "Write Fieldnote", None, QtGui.QApplication.UnicodeUTF8)) self.actionDownload_Details.setText(QtGui.QApplication.translate("GeocacheDetailsWindow", "Download Details", None, QtGui.QApplication.UnicodeUTF8)) agtl-0.8.0.3/files/advancedcaching/qt/ui_mainwindow.py000066400000000000000000000221451151564747700226670ustar00rootroot00000000000000# -*- coding: utf-8 -*- # Form implementation generated from reading ui file 'files/qt/MainWindow.ui' # # Created: Wed Aug 4 14:54:48 2010 # by: PyQt4 UI code generator 4.7.2 # # WARNING! All changes made in this file will be lost! from PyQt4 import QtCore, QtGui class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") MainWindow.resize(767, 392) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(MainWindow.sizePolicy().hasHeightForWidth()) MainWindow.setSizePolicy(sizePolicy) MainWindow.setAutoFillBackground(False) MainWindow.setDocumentMode(False) self.centralwidget = QtGui.QWidget(MainWindow) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Maximum, QtGui.QSizePolicy.Maximum) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.centralwidget.sizePolicy().hasHeightForWidth()) self.centralwidget.setSizePolicy(sizePolicy) self.centralwidget.setObjectName("centralwidget") self.horizontalLayout = QtGui.QHBoxLayout(self.centralwidget) self.horizontalLayout.setObjectName("horizontalLayout") MainWindow.setCentralWidget(self.centralwidget) self.menubar = QtGui.QMenuBar(MainWindow) self.menubar.setGeometry(QtCore.QRect(0, 0, 767, 21)) self.menubar.setObjectName("menubar") self.menuMap = QtGui.QMenu(self.menubar) self.menuMap.setObjectName("menuMap") self.menuSearch = QtGui.QMenu(self.menubar) self.menuSearch.setObjectName("menuSearch") self.menuHelp = QtGui.QMenu(self.menubar) self.menuHelp.setObjectName("menuHelp") self.menuAbout_AGTL = QtGui.QMenu(self.menuHelp) self.menuAbout_AGTL.setObjectName("menuAbout_AGTL") self.menuTest = QtGui.QMenu(self.menubar) self.menuTest.setObjectName("menuTest") MainWindow.setMenuBar(self.menubar) self.statusBar = QtGui.QStatusBar(MainWindow) self.statusBar.setObjectName("statusBar") MainWindow.setStatusBar(self.statusBar) self.toolBar = QtGui.QToolBar(MainWindow) self.toolBar.setObjectName("toolBar") MainWindow.addToolBar(QtCore.Qt.ToolBarArea(QtCore.Qt.BottomToolBarArea), self.toolBar) self.actionHallo_Welt = QtGui.QAction(MainWindow) self.actionHallo_Welt.setObjectName("actionHallo_Welt") self.actionTsch_ss_Welt = QtGui.QAction(MainWindow) self.actionTsch_ss_Welt.setObjectName("actionTsch_ss_Welt") self.actionSelect_Map_Style = QtGui.QAction(MainWindow) self.actionSelect_Map_Style.setObjectName("actionSelect_Map_Style") self.actionSearch_Geocaches = QtGui.QAction(MainWindow) self.actionSearch_Geocaches.setObjectName("actionSearch_Geocaches") self.actionDownload_Map_Tiles = QtGui.QAction(MainWindow) self.actionDownload_Map_Tiles.setObjectName("actionDownload_Map_Tiles") self.actionBlub = QtGui.QAction(MainWindow) self.actionBlub.setCheckable(True) self.actionBlub.setObjectName("actionBlub") self.actionBla = QtGui.QAction(MainWindow) self.actionBla.setObjectName("actionBla") self.actionBlub_1 = QtGui.QAction(MainWindow) self.actionBlub_1.setCheckable(True) self.actionBlub_1.setObjectName("actionBlub_1") self.actionBlub_2 = QtGui.QAction(MainWindow) self.actionBlub_2.setCheckable(True) self.actionBlub_2.setObjectName("actionBlub_2") self.actionSearch_Place = QtGui.QAction(MainWindow) self.actionSearch_Place.setObjectName("actionSearch_Place") self.actionUpdate_Geocache_Map = QtGui.QAction(MainWindow) icon = QtGui.QIcon() icon.addPixmap(QtGui.QPixmap(":/icons/advancedcaching/data/emoticon_grin.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.actionUpdate_Geocache_Map.setIcon(icon) self.actionUpdate_Geocache_Map.setObjectName("actionUpdate_Geocache_Map") self.actionDownload_Details_for_all_visible_Geocaches = QtGui.QAction(MainWindow) icon1 = QtGui.QIcon() icon1.addPixmap(QtGui.QPixmap(":/icons/advancedcaching/data/comment.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.actionDownload_Details_for_all_visible_Geocaches.setIcon(icon1) self.actionDownload_Details_for_all_visible_Geocaches.setObjectName("actionDownload_Details_for_all_visible_Geocaches") self.actionOptions = QtGui.QAction(MainWindow) self.actionOptions.setObjectName("actionOptions") self.actionExport_all_visible_Geocaches = QtGui.QAction(MainWindow) self.actionExport_all_visible_Geocaches.setObjectName("actionExport_all_visible_Geocaches") self.menuMap.addAction(self.actionSelect_Map_Style) self.menuMap.addAction(self.actionDownload_Map_Tiles) self.menuMap.addSeparator() self.menuMap.addAction(self.actionUpdate_Geocache_Map) self.menuMap.addAction(self.actionDownload_Details_for_all_visible_Geocaches) self.menuMap.addSeparator() self.menuMap.addAction(self.actionOptions) self.menuSearch.addAction(self.actionSearch_Geocaches) self.menuSearch.addAction(self.actionSearch_Place) self.menuAbout_AGTL.addAction(self.actionBlub_1) self.menuAbout_AGTL.addAction(self.actionBlub_2) self.menuHelp.addAction(self.menuAbout_AGTL.menuAction()) self.menuTest.addAction(self.actionExport_all_visible_Geocaches) self.menubar.addAction(self.menuMap.menuAction()) self.menubar.addAction(self.menuSearch.menuAction()) self.menubar.addAction(self.menuHelp.menuAction()) self.menubar.addAction(self.menuTest.menuAction()) self.toolBar.addSeparator() self.toolBar.addAction(self.actionUpdate_Geocache_Map) self.toolBar.addAction(self.actionDownload_Details_for_all_visible_Geocaches) self.retranslateUi(MainWindow) QtCore.QMetaObject.connectSlotsByName(MainWindow) def retranslateUi(self, MainWindow): MainWindow.setWindowTitle(QtGui.QApplication.translate("MainWindow", "MainWindow", None, QtGui.QApplication.UnicodeUTF8)) self.menuMap.setTitle(QtGui.QApplication.translate("MainWindow", "Map", None, QtGui.QApplication.UnicodeUTF8)) self.menuSearch.setTitle(QtGui.QApplication.translate("MainWindow", "Search", None, QtGui.QApplication.UnicodeUTF8)) self.menuHelp.setTitle(QtGui.QApplication.translate("MainWindow", "Help", None, QtGui.QApplication.UnicodeUTF8)) self.menuAbout_AGTL.setTitle(QtGui.QApplication.translate("MainWindow", "About &AGTL", None, QtGui.QApplication.UnicodeUTF8)) self.menuTest.setTitle(QtGui.QApplication.translate("MainWindow", "Tools", None, QtGui.QApplication.UnicodeUTF8)) self.toolBar.setWindowTitle(QtGui.QApplication.translate("MainWindow", "toolBar", None, QtGui.QApplication.UnicodeUTF8)) self.actionHallo_Welt.setText(QtGui.QApplication.translate("MainWindow", "Hallo Welt!", None, QtGui.QApplication.UnicodeUTF8)) self.actionTsch_ss_Welt.setText(QtGui.QApplication.translate("MainWindow", "Tschüss Welt!", None, QtGui.QApplication.UnicodeUTF8)) self.actionSelect_Map_Style.setText(QtGui.QApplication.translate("MainWindow", "Select Map Style", None, QtGui.QApplication.UnicodeUTF8)) self.actionSearch_Geocaches.setText(QtGui.QApplication.translate("MainWindow", "Search Geocaches", None, QtGui.QApplication.UnicodeUTF8)) self.actionSearch_Geocaches.setStatusTip(QtGui.QApplication.translate("MainWindow", "Test", None, QtGui.QApplication.UnicodeUTF8)) self.actionDownload_Map_Tiles.setText(QtGui.QApplication.translate("MainWindow", "Download Map Tiles", None, QtGui.QApplication.UnicodeUTF8)) self.actionBlub.setText(QtGui.QApplication.translate("MainWindow", "blub", None, QtGui.QApplication.UnicodeUTF8)) self.actionBla.setText(QtGui.QApplication.translate("MainWindow", "bla", None, QtGui.QApplication.UnicodeUTF8)) self.actionBlub_1.setText(QtGui.QApplication.translate("MainWindow", "Blub 1", None, QtGui.QApplication.UnicodeUTF8)) self.actionBlub_2.setText(QtGui.QApplication.translate("MainWindow", "Blub 2", None, QtGui.QApplication.UnicodeUTF8)) self.actionSearch_Place.setText(QtGui.QApplication.translate("MainWindow", "Search Place", None, QtGui.QApplication.UnicodeUTF8)) self.actionUpdate_Geocache_Map.setText(QtGui.QApplication.translate("MainWindow", "Update Geocache Locations", None, QtGui.QApplication.UnicodeUTF8)) self.actionDownload_Details_for_all_visible_Geocaches.setText(QtGui.QApplication.translate("MainWindow", "Download Details for visible Geocaches", None, QtGui.QApplication.UnicodeUTF8)) self.actionOptions.setText(QtGui.QApplication.translate("MainWindow", "Options", None, QtGui.QApplication.UnicodeUTF8)) self.actionExport_all_visible_Geocaches.setText(QtGui.QApplication.translate("MainWindow", "Export all visible Geocaches", None, QtGui.QApplication.UnicodeUTF8)) import icons_rc agtl-0.8.0.3/files/advancedcaching/qt/ui_optionsdialog.py000066400000000000000000000164651151564747700233760ustar00rootroot00000000000000# -*- coding: utf-8 -*- # Form implementation generated from reading ui file 'files/qt/OptionsDialog.ui' # # Created: Wed Aug 4 14:54:51 2010 # by: PyQt4 UI code generator 4.7.2 # # WARNING! All changes made in this file will be lost! from PyQt4 import QtCore, QtGui class Ui_OptionsDialog(object): def setupUi(self, OptionsDialog): OptionsDialog.setObjectName("OptionsDialog") OptionsDialog.resize(412, 384) self.verticalLayout = QtGui.QVBoxLayout(OptionsDialog) self.verticalLayout.setObjectName("verticalLayout") self.groupBox = QtGui.QGroupBox(OptionsDialog) self.groupBox.setObjectName("groupBox") self.gridLayout = QtGui.QGridLayout(self.groupBox) self.gridLayout.setObjectName("gridLayout") self.label = QtGui.QLabel(self.groupBox) self.label.setObjectName("label") self.gridLayout.addWidget(self.label, 1, 0, 1, 1) self.lineEditUserName = QtGui.QLineEdit(self.groupBox) self.lineEditUserName.setObjectName("lineEditUserName") self.gridLayout.addWidget(self.lineEditUserName, 1, 1, 1, 1) self.label_2 = QtGui.QLabel(self.groupBox) self.label_2.setObjectName("label_2") self.gridLayout.addWidget(self.label_2, 2, 0, 1, 1) self.lineEditPassword = QtGui.QLineEdit(self.groupBox) self.lineEditPassword.setInputMethodHints(QtCore.Qt.ImhNone) self.lineEditPassword.setEchoMode(QtGui.QLineEdit.PasswordEchoOnEdit) self.lineEditPassword.setObjectName("lineEditPassword") self.gridLayout.addWidget(self.lineEditPassword, 2, 1, 1, 1) self.label_4 = QtGui.QLabel(self.groupBox) self.label_4.setScaledContents(False) self.label_4.setWordWrap(True) self.label_4.setObjectName("label_4") self.gridLayout.addWidget(self.label_4, 0, 0, 1, 2) self.verticalLayout.addWidget(self.groupBox) self.groupBox_2 = QtGui.QGroupBox(OptionsDialog) self.groupBox_2.setObjectName("groupBox_2") self.gridLayout_2 = QtGui.QGridLayout(self.groupBox_2) self.gridLayout_2.setObjectName("gridLayout_2") self.checkBoxShowName = QtGui.QCheckBox(self.groupBox_2) self.checkBoxShowName.setObjectName("checkBoxShowName") self.gridLayout_2.addWidget(self.checkBoxShowName, 1, 0, 1, 1) self.checkBoxDoubleSize = QtGui.QCheckBox(self.groupBox_2) self.checkBoxDoubleSize.setObjectName("checkBoxDoubleSize") self.gridLayout_2.addWidget(self.checkBoxDoubleSize, 2, 0, 1, 1) self.checkBoxHideFound = QtGui.QCheckBox(self.groupBox_2) self.checkBoxHideFound.setObjectName("checkBoxHideFound") self.gridLayout_2.addWidget(self.checkBoxHideFound, 0, 0, 1, 1) self.verticalLayout.addWidget(self.groupBox_2) self.groupBox_3 = QtGui.QGroupBox(OptionsDialog) self.groupBox_3.setObjectName("groupBox_3") self.gridLayout_3 = QtGui.QGridLayout(self.groupBox_3) self.gridLayout_3.setObjectName("gridLayout_3") self.label_3 = QtGui.QLabel(self.groupBox_3) self.label_3.setObjectName("label_3") self.gridLayout_3.addWidget(self.label_3, 0, 0, 1, 1) self.comboBoxTTS = QtGui.QComboBox(self.groupBox_3) self.comboBoxTTS.setObjectName("comboBoxTTS") self.comboBoxTTS.addItem("") self.comboBoxTTS.addItem("") self.comboBoxTTS.addItem("") self.comboBoxTTS.addItem("") self.comboBoxTTS.addItem("") self.comboBoxTTS.addItem("") self.comboBoxTTS.addItem("") self.comboBoxTTS.addItem("") self.comboBoxTTS.addItem("") self.comboBoxTTS.addItem("") self.gridLayout_3.addWidget(self.comboBoxTTS, 0, 1, 1, 1) self.verticalLayout.addWidget(self.groupBox_3) self.buttonBox = QtGui.QDialogButtonBox(OptionsDialog) self.buttonBox.setOrientation(QtCore.Qt.Horizontal) self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) self.buttonBox.setObjectName("buttonBox") self.verticalLayout.addWidget(self.buttonBox) self.retranslateUi(OptionsDialog) QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), OptionsDialog.accept) QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), OptionsDialog.reject) QtCore.QMetaObject.connectSlotsByName(OptionsDialog) def retranslateUi(self, OptionsDialog): OptionsDialog.setWindowTitle(QtGui.QApplication.translate("OptionsDialog", "Options", None, QtGui.QApplication.UnicodeUTF8)) self.groupBox.setTitle(QtGui.QApplication.translate("OptionsDialog", "Login Data", None, QtGui.QApplication.UnicodeUTF8)) self.label.setText(QtGui.QApplication.translate("OptionsDialog", "Username", None, QtGui.QApplication.UnicodeUTF8)) self.label_2.setText(QtGui.QApplication.translate("OptionsDialog", "Password", None, QtGui.QApplication.UnicodeUTF8)) self.label_4.setText(QtGui.QApplication.translate("OptionsDialog", "Please make sure that the website language ist set to english. Otherwise, downloading of geocaches won\'t work.", None, QtGui.QApplication.UnicodeUTF8)) self.groupBox_2.setTitle(QtGui.QApplication.translate("OptionsDialog", "Map Display", None, QtGui.QApplication.UnicodeUTF8)) self.checkBoxShowName.setText(QtGui.QApplication.translate("OptionsDialog", "Show Geocache Names on Map", None, QtGui.QApplication.UnicodeUTF8)) self.checkBoxDoubleSize.setText(QtGui.QApplication.translate("OptionsDialog", "Show Map in double size (ugly!)", None, QtGui.QApplication.UnicodeUTF8)) self.checkBoxHideFound.setText(QtGui.QApplication.translate("OptionsDialog", "Hide Found Geocaches", None, QtGui.QApplication.UnicodeUTF8)) self.groupBox_3.setTitle(QtGui.QApplication.translate("OptionsDialog", "Text-to-speech settings", None, QtGui.QApplication.UnicodeUTF8)) self.label_3.setText(QtGui.QApplication.translate("OptionsDialog", "Enable Voice Guidance", None, QtGui.QApplication.UnicodeUTF8)) self.comboBoxTTS.setItemText(0, QtGui.QApplication.translate("OptionsDialog", "Off", None, QtGui.QApplication.UnicodeUTF8)) self.comboBoxTTS.setItemText(1, QtGui.QApplication.translate("OptionsDialog", "Automatic", None, QtGui.QApplication.UnicodeUTF8)) self.comboBoxTTS.setItemText(2, QtGui.QApplication.translate("OptionsDialog", "10 Seconds", None, QtGui.QApplication.UnicodeUTF8)) self.comboBoxTTS.setItemText(3, QtGui.QApplication.translate("OptionsDialog", "20 Seconds", None, QtGui.QApplication.UnicodeUTF8)) self.comboBoxTTS.setItemText(4, QtGui.QApplication.translate("OptionsDialog", "30 Seconds", None, QtGui.QApplication.UnicodeUTF8)) self.comboBoxTTS.setItemText(5, QtGui.QApplication.translate("OptionsDialog", "50 Seconds", None, QtGui.QApplication.UnicodeUTF8)) self.comboBoxTTS.setItemText(6, QtGui.QApplication.translate("OptionsDialog", "100 Seconds", None, QtGui.QApplication.UnicodeUTF8)) self.comboBoxTTS.setItemText(7, QtGui.QApplication.translate("OptionsDialog", "3 Minutes", None, QtGui.QApplication.UnicodeUTF8)) self.comboBoxTTS.setItemText(8, QtGui.QApplication.translate("OptionsDialog", "5 Minutes", None, QtGui.QApplication.UnicodeUTF8)) self.comboBoxTTS.setItemText(9, QtGui.QApplication.translate("OptionsDialog", "10 Minutes", None, QtGui.QApplication.UnicodeUTF8)) agtl-0.8.0.3/files/advancedcaching/qt/ui_searchdialog.py000066400000000000000000000032571151564747700231430ustar00rootroot00000000000000# -*- coding: utf-8 -*- # Form implementation generated from reading ui file 'files/qt/SearchDialog.ui' # # Created: Wed Aug 4 14:54:48 2010 # by: PyQt4 UI code generator 4.7.2 # # WARNING! All changes made in this file will be lost! from PyQt4 import QtCore, QtGui class Ui_SearchDialog(object): def setupUi(self, SearchDialog): SearchDialog.setObjectName("SearchDialog") SearchDialog.resize(400, 300) self.verticalLayout = QtGui.QVBoxLayout(SearchDialog) self.verticalLayout.setObjectName("verticalLayout") self.horizontalLayout = QtGui.QHBoxLayout() self.horizontalLayout.setObjectName("horizontalLayout") self.lineEditSearch = QtGui.QLineEdit(SearchDialog) self.lineEditSearch.setObjectName("lineEditSearch") self.horizontalLayout.addWidget(self.lineEditSearch) self.pushButtonSearch = QtGui.QPushButton(SearchDialog) self.pushButtonSearch.setObjectName("pushButtonSearch") self.horizontalLayout.addWidget(self.pushButtonSearch) self.verticalLayout.addLayout(self.horizontalLayout) self.listWidgetResults = QtGui.QListWidget(SearchDialog) self.listWidgetResults.setObjectName("listWidgetResults") self.verticalLayout.addWidget(self.listWidgetResults) self.retranslateUi(SearchDialog) QtCore.QMetaObject.connectSlotsByName(SearchDialog) def retranslateUi(self, SearchDialog): SearchDialog.setWindowTitle(QtGui.QApplication.translate("SearchDialog", "Search Place", None, QtGui.QApplication.UnicodeUTF8)) self.pushButtonSearch.setText(QtGui.QApplication.translate("SearchDialog", "Search", None, QtGui.QApplication.UnicodeUTF8)) agtl-0.8.0.3/files/advancedcaching/qt/ui_searchgeocachesdialog.py000066400000000000000000000356071151564747700250110ustar00rootroot00000000000000# -*- coding: utf-8 -*- # Form implementation generated from reading ui file 'files/qt/SearchGeocachesDialog.ui' # # Created: Wed Aug 4 14:54:50 2010 # by: PyQt4 UI code generator 4.7.2 # # WARNING! All changes made in this file will be lost! from PyQt4 import QtCore, QtGui class Ui_SearchGeocachesDialog(object): def setupUi(self, SearchGeocachesDialog): SearchGeocachesDialog.setObjectName("SearchGeocachesDialog") SearchGeocachesDialog.resize(874, 481) self.gridLayout = QtGui.QGridLayout(SearchGeocachesDialog) self.gridLayout.setObjectName("gridLayout") self.groupBox = QtGui.QGroupBox(SearchGeocachesDialog) self.groupBox.setObjectName("groupBox") self.formLayout_4 = QtGui.QFormLayout(self.groupBox) self.formLayout_4.setObjectName("formLayout_4") self.label = QtGui.QLabel(self.groupBox) self.label.setFrameShape(QtGui.QFrame.NoFrame) self.label.setObjectName("label") self.formLayout_4.setWidget(0, QtGui.QFormLayout.LabelRole, self.label) self.lineEditName = QtGui.QLineEdit(self.groupBox) self.lineEditName.setObjectName("lineEditName") self.formLayout_4.setWidget(0, QtGui.QFormLayout.FieldRole, self.lineEditName) self.gridLayout.addWidget(self.groupBox, 0, 0, 2, 1) self.dialogButtonBox = QtGui.QDialogButtonBox(SearchGeocachesDialog) self.dialogButtonBox.setOrientation(QtCore.Qt.Horizontal) self.dialogButtonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) self.dialogButtonBox.setObjectName("dialogButtonBox") self.gridLayout.addWidget(self.dialogButtonBox, 6, 0, 1, 2) self.groupBox_4 = QtGui.QGroupBox(SearchGeocachesDialog) self.groupBox_4.setObjectName("groupBox_4") self.formLayout_3 = QtGui.QFormLayout(self.groupBox_4) self.formLayout_3.setFieldGrowthPolicy(QtGui.QFormLayout.ExpandingFieldsGrow) self.formLayout_3.setObjectName("formLayout_3") self.label_10 = QtGui.QLabel(self.groupBox_4) self.label_10.setWordWrap(True) self.label_10.setObjectName("label_10") self.formLayout_3.setWidget(0, QtGui.QFormLayout.LabelRole, self.label_10) self.label_4 = QtGui.QLabel(self.groupBox_4) self.label_4.setObjectName("label_4") self.formLayout_3.setWidget(1, QtGui.QFormLayout.LabelRole, self.label_4) self.listWidgetSize = QtGui.QListWidget(self.groupBox_4) self.listWidgetSize.setObjectName("listWidgetSize") item = QtGui.QListWidgetItem(self.listWidgetSize) item.setCheckState(QtCore.Qt.Checked) item = QtGui.QListWidgetItem(self.listWidgetSize) item.setCheckState(QtCore.Qt.Checked) item = QtGui.QListWidgetItem(self.listWidgetSize) item.setCheckState(QtCore.Qt.Checked) item = QtGui.QListWidgetItem(self.listWidgetSize) item.setCheckState(QtCore.Qt.Checked) item = QtGui.QListWidgetItem(self.listWidgetSize) item.setCheckState(QtCore.Qt.Checked) self.formLayout_3.setWidget(1, QtGui.QFormLayout.FieldRole, self.listWidgetSize) self.label_7 = QtGui.QLabel(self.groupBox_4) self.label_7.setObjectName("label_7") self.formLayout_3.setWidget(2, QtGui.QFormLayout.LabelRole, self.label_7) self.horizontalLayout_2 = QtGui.QHBoxLayout() self.horizontalLayout_2.setObjectName("horizontalLayout_2") self.label_12 = QtGui.QLabel(self.groupBox_4) self.label_12.setObjectName("label_12") self.horizontalLayout_2.addWidget(self.label_12) self.doubleSpinBoxDifficultyMin = QtGui.QDoubleSpinBox(self.groupBox_4) self.doubleSpinBoxDifficultyMin.setButtonSymbols(QtGui.QAbstractSpinBox.UpDownArrows) self.doubleSpinBoxDifficultyMin.setCorrectionMode(QtGui.QAbstractSpinBox.CorrectToNearestValue) self.doubleSpinBoxDifficultyMin.setPrefix("") self.doubleSpinBoxDifficultyMin.setDecimals(1) self.doubleSpinBoxDifficultyMin.setMinimum(1.0) self.doubleSpinBoxDifficultyMin.setMaximum(5.0) self.doubleSpinBoxDifficultyMin.setSingleStep(0.5) self.doubleSpinBoxDifficultyMin.setObjectName("doubleSpinBoxDifficultyMin") self.horizontalLayout_2.addWidget(self.doubleSpinBoxDifficultyMin) self.label_11 = QtGui.QLabel(self.groupBox_4) self.label_11.setObjectName("label_11") self.horizontalLayout_2.addWidget(self.label_11) self.doubleSpinBoxDifficultyMax = QtGui.QDoubleSpinBox(self.groupBox_4) self.doubleSpinBoxDifficultyMax.setDecimals(1) self.doubleSpinBoxDifficultyMax.setMinimum(1.0) self.doubleSpinBoxDifficultyMax.setMaximum(5.0) self.doubleSpinBoxDifficultyMax.setSingleStep(0.5) self.doubleSpinBoxDifficultyMax.setProperty("value", 5.0) self.doubleSpinBoxDifficultyMax.setObjectName("doubleSpinBoxDifficultyMax") self.horizontalLayout_2.addWidget(self.doubleSpinBoxDifficultyMax) self.formLayout_3.setLayout(2, QtGui.QFormLayout.FieldRole, self.horizontalLayout_2) self.label_8 = QtGui.QLabel(self.groupBox_4) self.label_8.setObjectName("label_8") self.formLayout_3.setWidget(3, QtGui.QFormLayout.LabelRole, self.label_8) self.horizontalLayout_3 = QtGui.QHBoxLayout() self.horizontalLayout_3.setObjectName("horizontalLayout_3") self.label_13 = QtGui.QLabel(self.groupBox_4) self.label_13.setObjectName("label_13") self.horizontalLayout_3.addWidget(self.label_13) self.doubleSpinBoxTerrainMin = QtGui.QDoubleSpinBox(self.groupBox_4) self.doubleSpinBoxTerrainMin.setDecimals(1) self.doubleSpinBoxTerrainMin.setMinimum(1.0) self.doubleSpinBoxTerrainMin.setMaximum(5.0) self.doubleSpinBoxTerrainMin.setSingleStep(0.5) self.doubleSpinBoxTerrainMin.setObjectName("doubleSpinBoxTerrainMin") self.horizontalLayout_3.addWidget(self.doubleSpinBoxTerrainMin) self.label_14 = QtGui.QLabel(self.groupBox_4) self.label_14.setObjectName("label_14") self.horizontalLayout_3.addWidget(self.label_14) self.doubleSpinBoxTerrainMax = QtGui.QDoubleSpinBox(self.groupBox_4) self.doubleSpinBoxTerrainMax.setDecimals(1) self.doubleSpinBoxTerrainMax.setMinimum(1.0) self.doubleSpinBoxTerrainMax.setMaximum(5.0) self.doubleSpinBoxTerrainMax.setSingleStep(0.5) self.doubleSpinBoxTerrainMax.setProperty("value", 5.0) self.doubleSpinBoxTerrainMax.setObjectName("doubleSpinBoxTerrainMax") self.horizontalLayout_3.addWidget(self.doubleSpinBoxTerrainMax) self.formLayout_3.setLayout(3, QtGui.QFormLayout.FieldRole, self.horizontalLayout_3) self.gridLayout.addWidget(self.groupBox_4, 2, 1, 2, 1) self.groupBox_2 = QtGui.QGroupBox(SearchGeocachesDialog) self.groupBox_2.setObjectName("groupBox_2") self.formLayout = QtGui.QFormLayout(self.groupBox_2) self.formLayout.setObjectName("formLayout") self.label_2 = QtGui.QLabel(self.groupBox_2) self.label_2.setObjectName("label_2") self.formLayout.setWidget(0, QtGui.QFormLayout.LabelRole, self.label_2) self.comboBoxLocation = QtGui.QComboBox(self.groupBox_2) self.comboBoxLocation.setObjectName("comboBoxLocation") self.comboBoxLocation.addItem("") self.comboBoxLocation.addItem("") self.comboBoxLocation.addItem("") self.formLayout.setWidget(0, QtGui.QFormLayout.FieldRole, self.comboBoxLocation) self.label_3 = QtGui.QLabel(self.groupBox_2) self.label_3.setObjectName("label_3") self.formLayout.setWidget(2, QtGui.QFormLayout.LabelRole, self.label_3) self.horizontalLayout_4 = QtGui.QHBoxLayout() self.horizontalLayout_4.setObjectName("horizontalLayout_4") self.spinBoxRadius = QtGui.QSpinBox(self.groupBox_2) self.spinBoxRadius.setEnabled(False) self.spinBoxRadius.setMinimum(1) self.spinBoxRadius.setMaximum(100) self.spinBoxRadius.setSingleStep(2) self.spinBoxRadius.setObjectName("spinBoxRadius") self.horizontalLayout_4.addWidget(self.spinBoxRadius) self.labelLocation = QtGui.QLabel(self.groupBox_2) self.labelLocation.setText("") self.labelLocation.setObjectName("labelLocation") self.horizontalLayout_4.addWidget(self.labelLocation) self.formLayout.setLayout(2, QtGui.QFormLayout.FieldRole, self.horizontalLayout_4) self.gridLayout.addWidget(self.groupBox_2, 0, 1, 1, 1) self.groupBox_3 = QtGui.QGroupBox(SearchGeocachesDialog) self.groupBox_3.setObjectName("groupBox_3") self.formLayout_2 = QtGui.QFormLayout(self.groupBox_3) self.formLayout_2.setFieldGrowthPolicy(QtGui.QFormLayout.ExpandingFieldsGrow) self.formLayout_2.setObjectName("formLayout_2") self.label_5 = QtGui.QLabel(self.groupBox_3) self.label_5.setObjectName("label_5") self.formLayout_2.setWidget(1, QtGui.QFormLayout.LabelRole, self.label_5) self.listWidgetType = QtGui.QListWidget(self.groupBox_3) self.listWidgetType.setObjectName("listWidgetType") self.formLayout_2.setWidget(1, QtGui.QFormLayout.FieldRole, self.listWidgetType) self.label_6 = QtGui.QLabel(self.groupBox_3) self.label_6.setObjectName("label_6") self.formLayout_2.setWidget(3, QtGui.QFormLayout.LabelRole, self.label_6) self.horizontalLayout = QtGui.QHBoxLayout() self.horizontalLayout.setObjectName("horizontalLayout") self.checkBoxHideFound = QtGui.QCheckBox(self.groupBox_3) self.checkBoxHideFound.setChecked(True) self.checkBoxHideFound.setObjectName("checkBoxHideFound") self.horizontalLayout.addWidget(self.checkBoxHideFound) self.formLayout_2.setLayout(3, QtGui.QFormLayout.FieldRole, self.horizontalLayout) self.verticalLayout = QtGui.QVBoxLayout() self.verticalLayout.setObjectName("verticalLayout") self.checkBoxShowOnlyMarked = QtGui.QCheckBox(self.groupBox_3) self.checkBoxShowOnlyMarked.setObjectName("checkBoxShowOnlyMarked") self.verticalLayout.addWidget(self.checkBoxShowOnlyMarked) self.formLayout_2.setLayout(4, QtGui.QFormLayout.FieldRole, self.verticalLayout) self.gridLayout.addWidget(self.groupBox_3, 2, 0, 2, 1) self.retranslateUi(SearchGeocachesDialog) QtCore.QObject.connect(self.dialogButtonBox, QtCore.SIGNAL("accepted()"), SearchGeocachesDialog.accept) QtCore.QObject.connect(self.dialogButtonBox, QtCore.SIGNAL("rejected()"), SearchGeocachesDialog.reject) QtCore.QMetaObject.connectSlotsByName(SearchGeocachesDialog) def retranslateUi(self, SearchGeocachesDialog): SearchGeocachesDialog.setWindowTitle(QtGui.QApplication.translate("SearchGeocachesDialog", "Search Geocaches", None, QtGui.QApplication.UnicodeUTF8)) self.groupBox.setTitle(QtGui.QApplication.translate("SearchGeocachesDialog", "Name", None, QtGui.QApplication.UnicodeUTF8)) self.label.setText(QtGui.QApplication.translate("SearchGeocachesDialog", "Geocache Name", None, QtGui.QApplication.UnicodeUTF8)) self.groupBox_4.setTitle(QtGui.QApplication.translate("SearchGeocachesDialog", "Details (2)", None, QtGui.QApplication.UnicodeUTF8)) self.label_10.setText(QtGui.QApplication.translate("SearchGeocachesDialog", "If you change something here, only geocaches for which details have been downloaded will be shown in the result.", None, QtGui.QApplication.UnicodeUTF8)) self.label_4.setText(QtGui.QApplication.translate("SearchGeocachesDialog", "Size", None, QtGui.QApplication.UnicodeUTF8)) __sortingEnabled = self.listWidgetSize.isSortingEnabled() self.listWidgetSize.setSortingEnabled(False) self.listWidgetSize.item(0).setText(QtGui.QApplication.translate("SearchGeocachesDialog", "micro", None, QtGui.QApplication.UnicodeUTF8)) self.listWidgetSize.item(1).setText(QtGui.QApplication.translate("SearchGeocachesDialog", "small", None, QtGui.QApplication.UnicodeUTF8)) self.listWidgetSize.item(2).setText(QtGui.QApplication.translate("SearchGeocachesDialog", "regular", None, QtGui.QApplication.UnicodeUTF8)) self.listWidgetSize.item(3).setText(QtGui.QApplication.translate("SearchGeocachesDialog", "huge", None, QtGui.QApplication.UnicodeUTF8)) self.listWidgetSize.item(4).setText(QtGui.QApplication.translate("SearchGeocachesDialog", "other", None, QtGui.QApplication.UnicodeUTF8)) self.listWidgetSize.setSortingEnabled(__sortingEnabled) self.label_7.setText(QtGui.QApplication.translate("SearchGeocachesDialog", "Difficulty", None, QtGui.QApplication.UnicodeUTF8)) self.label_12.setText(QtGui.QApplication.translate("SearchGeocachesDialog", "min.", None, QtGui.QApplication.UnicodeUTF8)) self.label_11.setText(QtGui.QApplication.translate("SearchGeocachesDialog", "max.", None, QtGui.QApplication.UnicodeUTF8)) self.label_8.setText(QtGui.QApplication.translate("SearchGeocachesDialog", "Terrain", None, QtGui.QApplication.UnicodeUTF8)) self.label_13.setText(QtGui.QApplication.translate("SearchGeocachesDialog", "min.", None, QtGui.QApplication.UnicodeUTF8)) self.label_14.setText(QtGui.QApplication.translate("SearchGeocachesDialog", "max.", None, QtGui.QApplication.UnicodeUTF8)) self.groupBox_2.setTitle(QtGui.QApplication.translate("SearchGeocachesDialog", "Location", None, QtGui.QApplication.UnicodeUTF8)) self.label_2.setText(QtGui.QApplication.translate("SearchGeocachesDialog", "Location", None, QtGui.QApplication.UnicodeUTF8)) self.comboBoxLocation.setItemText(0, QtGui.QApplication.translate("SearchGeocachesDialog", "anywhere", None, QtGui.QApplication.UnicodeUTF8)) self.comboBoxLocation.setItemText(1, QtGui.QApplication.translate("SearchGeocachesDialog", "around the current map center", None, QtGui.QApplication.UnicodeUTF8)) self.comboBoxLocation.setItemText(2, QtGui.QApplication.translate("SearchGeocachesDialog", "around my position", None, QtGui.QApplication.UnicodeUTF8)) self.label_3.setText(QtGui.QApplication.translate("SearchGeocachesDialog", "Radius", None, QtGui.QApplication.UnicodeUTF8)) self.spinBoxRadius.setSuffix(QtGui.QApplication.translate("SearchGeocachesDialog", " km", None, QtGui.QApplication.UnicodeUTF8)) self.groupBox_3.setTitle(QtGui.QApplication.translate("SearchGeocachesDialog", "Details (1)", None, QtGui.QApplication.UnicodeUTF8)) self.label_5.setText(QtGui.QApplication.translate("SearchGeocachesDialog", "Type(s)", None, QtGui.QApplication.UnicodeUTF8)) self.label_6.setText(QtGui.QApplication.translate("SearchGeocachesDialog", "Filter", None, QtGui.QApplication.UnicodeUTF8)) self.checkBoxHideFound.setText(QtGui.QApplication.translate("SearchGeocachesDialog", "hide found geocaches", None, QtGui.QApplication.UnicodeUTF8)) self.checkBoxShowOnlyMarked.setText(QtGui.QApplication.translate("SearchGeocachesDialog", "only marked geocaches", None, QtGui.QApplication.UnicodeUTF8)) agtl-0.8.0.3/files/advancedcaching/qt/ui_searchresultsdialog.py000066400000000000000000000105621151564747700245620ustar00rootroot00000000000000# -*- coding: utf-8 -*- # Form implementation generated from reading ui file 'files/qt/SearchResultsDialog.ui' # # Created: Wed Aug 4 14:54:50 2010 # by: PyQt4 UI code generator 4.7.2 # # WARNING! All changes made in this file will be lost! from PyQt4 import QtCore, QtGui class Ui_SearchResultsDialog(object): def setupUi(self, SearchResultsDialog): SearchResultsDialog.setObjectName("SearchResultsDialog") SearchResultsDialog.resize(613, 605) self.verticalLayout = QtGui.QVBoxLayout(SearchResultsDialog) self.verticalLayout.setObjectName("verticalLayout") self.tableWidgetResults = QtGui.QTableWidget(SearchResultsDialog) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.tableWidgetResults.sizePolicy().hasHeightForWidth()) self.tableWidgetResults.setSizePolicy(sizePolicy) self.tableWidgetResults.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers) self.tableWidgetResults.setAlternatingRowColors(True) self.tableWidgetResults.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection) self.tableWidgetResults.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows) self.tableWidgetResults.setShowGrid(False) self.tableWidgetResults.setWordWrap(True) self.tableWidgetResults.setObjectName("tableWidgetResults") self.tableWidgetResults.setColumnCount(5) self.tableWidgetResults.setRowCount(0) item = QtGui.QTableWidgetItem() self.tableWidgetResults.setHorizontalHeaderItem(0, item) item = QtGui.QTableWidgetItem() self.tableWidgetResults.setHorizontalHeaderItem(1, item) item = QtGui.QTableWidgetItem() self.tableWidgetResults.setHorizontalHeaderItem(2, item) item = QtGui.QTableWidgetItem() self.tableWidgetResults.setHorizontalHeaderItem(3, item) item = QtGui.QTableWidgetItem() self.tableWidgetResults.setHorizontalHeaderItem(4, item) self.tableWidgetResults.horizontalHeader().setVisible(True) self.tableWidgetResults.horizontalHeader().setCascadingSectionResizes(False) self.tableWidgetResults.horizontalHeader().setHighlightSections(True) self.tableWidgetResults.horizontalHeader().setMinimumSectionSize(40) self.tableWidgetResults.horizontalHeader().setSortIndicatorShown(True) self.tableWidgetResults.horizontalHeader().setStretchLastSection(False) self.tableWidgetResults.verticalHeader().setVisible(False) self.tableWidgetResults.verticalHeader().setSortIndicatorShown(False) self.tableWidgetResults.verticalHeader().setStretchLastSection(False) self.verticalLayout.addWidget(self.tableWidgetResults) self.pushButtonExportSelected = QtGui.QPushButton(SearchResultsDialog) self.pushButtonExportSelected.setObjectName("pushButtonExportSelected") self.verticalLayout.addWidget(self.pushButtonExportSelected) self.retranslateUi(SearchResultsDialog) QtCore.QMetaObject.connectSlotsByName(SearchResultsDialog) def retranslateUi(self, SearchResultsDialog): SearchResultsDialog.setWindowTitle(QtGui.QApplication.translate("SearchResultsDialog", "Search Results", None, QtGui.QApplication.UnicodeUTF8)) self.tableWidgetResults.setSortingEnabled(True) self.tableWidgetResults.horizontalHeaderItem(0).setText(QtGui.QApplication.translate("SearchResultsDialog", "Geocache", None, QtGui.QApplication.UnicodeUTF8)) self.tableWidgetResults.horizontalHeaderItem(1).setText(QtGui.QApplication.translate("SearchResultsDialog", "Size", None, QtGui.QApplication.UnicodeUTF8)) self.tableWidgetResults.horizontalHeaderItem(2).setText(QtGui.QApplication.translate("SearchResultsDialog", "T", None, QtGui.QApplication.UnicodeUTF8)) self.tableWidgetResults.horizontalHeaderItem(3).setText(QtGui.QApplication.translate("SearchResultsDialog", "D", None, QtGui.QApplication.UnicodeUTF8)) self.tableWidgetResults.horizontalHeaderItem(4).setText(QtGui.QApplication.translate("SearchResultsDialog", "Distance", None, QtGui.QApplication.UnicodeUTF8)) self.pushButtonExportSelected.setText(QtGui.QApplication.translate("SearchResultsDialog", "Export selected...", None, QtGui.QApplication.UnicodeUTF8)) agtl-0.8.0.3/files/advancedcaching/qt/ui_showimagedialog.py000066400000000000000000000054211151564747700236540ustar00rootroot00000000000000# -*- coding: utf-8 -*- # Form implementation generated from reading ui file 'files/qt/ShowImageDialog.ui' # # Created: Wed Aug 4 14:54:49 2010 # by: PyQt4 UI code generator 4.7.2 # # WARNING! All changes made in this file will be lost! from PyQt4 import QtCore, QtGui class Ui_ShowImageDialog(object): def setupUi(self, ShowImageDialog): ShowImageDialog.setObjectName("ShowImageDialog") ShowImageDialog.resize(384, 337) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Minimum) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(ShowImageDialog.sizePolicy().hasHeightForWidth()) ShowImageDialog.setSizePolicy(sizePolicy) self.horizontalLayout = QtGui.QHBoxLayout(ShowImageDialog) self.horizontalLayout.setObjectName("horizontalLayout") self.scrollArea = QtGui.QScrollArea(ShowImageDialog) self.scrollArea.setWidgetResizable(True) self.scrollArea.setObjectName("scrollArea") self.scrollAreaWidgetContents = QtGui.QWidget(self.scrollArea) self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 370, 323)) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.scrollAreaWidgetContents.sizePolicy().hasHeightForWidth()) self.scrollAreaWidgetContents.setSizePolicy(sizePolicy) self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents") self.horizontalLayout_2 = QtGui.QHBoxLayout(self.scrollAreaWidgetContents) self.horizontalLayout_2.setObjectName("horizontalLayout_2") self.labelImage = QtGui.QLabel(self.scrollAreaWidgetContents) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.labelImage.sizePolicy().hasHeightForWidth()) self.labelImage.setSizePolicy(sizePolicy) self.labelImage.setText("") self.labelImage.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop) self.labelImage.setObjectName("labelImage") self.horizontalLayout_2.addWidget(self.labelImage) self.scrollArea.setWidget(self.scrollAreaWidgetContents) self.horizontalLayout.addWidget(self.scrollArea) self.retranslateUi(ShowImageDialog) QtCore.QMetaObject.connectSlotsByName(ShowImageDialog) def retranslateUi(self, ShowImageDialog): ShowImageDialog.setWindowTitle(QtGui.QApplication.translate("ShowImageDialog", "Image", None, QtGui.QApplication.UnicodeUTF8)) agtl-0.8.0.3/files/advancedcaching/qtgui.py000066400000000000000000000201671151564747700205250ustar00rootroot00000000000000#!/usr/bin/python # -*- coding: utf-8 -*- # Copyright (C) 2010 Daniel Fett # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # Author: Daniel Fett agtl@danielfett.de # Jabber: fett.daniel@jaber.ccc.de # Bugtracker and GIT Repository: http://github.com/webhamster/advancedcaching # import logging logger = logging.getLogger('qtgui') from PyQt4.QtCore import * from PyQt4.QtGui import * from qt.ui_mainwindow import Ui_MainWindow from qt.geocachedetailswindow import QtGeocacheDetailsWindow from qt.searchgeocachesdialog import QtSearchGeocachesDialog from qt.searchdialog import QtSearchDialog from qt.optionsdialog import QtOptionsDialog import sys import geo from gui import Gui from qt.mapwidget import QtMap, QtOsdLayer, QtGeocacheLayer, QtMarksLayer d = lambda x: x.decode('utf-8') class QtGui(QMainWindow, Ui_MainWindow, Gui): USES = ['geonames'] def __init__(self, core, dataroot, parent=None): #print 'mwinit' self.app = QApplication(sys.argv) QMainWindow.__init__(self, parent) self.core = core self.setupUi(self) self.setup_ui_map(dataroot) self.setup_ui_custom() self.setup_ui_signals() def set_target(self, cache): self.core.set_target(cache) ############################################## # # GUI stuff # ############################################## def setup_ui_map(self, dataroot): QtGeocacheLayer.TOO_MANY_POINTS = 150 QtGeocacheLayer.MAX_NUM_RESULTS_SHOW = 1001 QtGeocacheLayer.CACHES_ZOOM_LOWER_BOUND = 5 noimage_cantload = "%s/noimage-cantload.png" % dataroot noimage_loading = "%s/noimage-loading.png" % dataroot QtMap.set_config(self.core.settings['map_providers'], self.core.settings['download_map_path'], noimage_cantload, noimage_loading) self.map = QtMap(self, geo.Coordinate(50, 7), 13) #self.mark_layer = QtSingleMarkLayer(geo.Coordinate(49, 6)) #self.map.add_layer(self.mark_layer) self.geocacheLayer = QtGeocacheLayer(self.__get_geocaches_callback, self.__show_cache) self.map.add_layer(self.geocacheLayer) self.marksLayer = QtMarksLayer() self.map.add_layer(self.marksLayer) self.osd_layer = QtOsdLayer() self.map.add_layer(self.osd_layer) self.setCentralWidget(self.map) def setup_ui_custom(self): self.qa = QActionGroup(None) self.qa.addAction(self.actionBlub_1) self.qa.addAction(self.actionBlub_2) self.progressBarLabel = QLabel() self.progressBar = QProgressBar() self.progressBar.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Preferred) self.progressBar.setMinimum(0) self.progressBar.setMaximum(100) self.statusBar.addPermanentWidget(self.progressBar) self.labelPosition = QLabel() self.statusBar.addPermanentWidget(self.progressBar) self.statusBar.addPermanentWidget(self.progressBarLabel) self.statusBar.addWidget(self.labelPosition) self.progressBar.hide() def setup_ui_signals(self): self.actionSearch_Place.triggered.connect(self.__show_search_place) self.actionUpdate_Geocache_Map.triggered.connect(self.__download_overview) self.actionDownload_Details_for_all_visible_Geocaches.triggered.connect(self.__download_details_map) self.actionSearch_Geocaches.triggered.connect(self.__show_search_geocaches) self.actionOptions.triggered.connect(self.__show_options) self.map.centerChanged.connect(self.__update_progress_bar) self.core.connect('target-changed', self.marksLayer.on_target_changed) self.core.connect('good-fix', self.marksLayer.on_good_fix) self.core.connect('no-fix', self.marksLayer.on_no_fix) self.core.connect('settings-changed', self.__on_settings_changed) self.core.connect('save-settings', self._on_save_settings) def show(self): QMainWindow.show(self) self.core.connect('map-marks-changed', lambda caller: self.geocacheLayer.refresh()) self.app.exec_() def __get_geocaches_callback(self, visible_area, maxresults): return self.core.pointprovider.get_points_filter(visible_area, None, maxresults) #return self.core.pointprovider.get_points_filter(visible_area, not self.settings['options_hide_found'], maxresults) ############################################## # # show subwindows # ############################################## def __show_search_geocaches(self): d = QtSearchGeocachesDialog(self.core, self.map.get_center(), self.core.current_position, self) d.show() def __show_cache(self, geocache): window = QtGeocacheDetailsWindow(self.core, self) window.show_geocache(geocache) window.show() def __show_search_place(self): #self.map.fit_to_bounds(47.301585, 55.021921, 8.407974, 10.244751) dialog = QtSearchDialog(self.core, self) dialog.show() dialog.locationSelected.connect(self.map.set_center) def __show_options(self): dialog = QtOptionsDialog(self.core, self.core.settings, self) dialog.setModal(True) dialog.saveSettings.connect(lambda: self.core.save_settings(dialog.get_settings(), self)) dialog.show() ############################################## # # called by Core and Signals # ############################################## def __update_progress_bar(self): text = self.map.get_center().get_latlon() self.labelPosition.setText(d(text)) def set_download_progress(self, fraction, text=''): self.progressBar.setValue(int(100 * fraction)) self.progressBarLabel.setText(text) self.progressBar.show() def hide_progress(self): self.progressBarLabel.setText('') self.progressBar.hide() def show_error(self, errormsg): QMessageBox.warning(None, "Error", "%s" % errormsg, "close") def show_success(self, message): hildon.hildon_banner_show_information(self.window, "", message) ############################################## # # Downloading Geocaches # ############################################## def __download_overview(self): self.core.on_download(self.map.get_visible_area()) def __download_details_map(self): self.core.on_download_descriptions(self.map.get_visible_area(), True) ############################################## # # Settings handling # ############################################## def _on_save_settings(self, caller): c = self.map.get_center() settings['map_position_lat'] = c.lat settings['map_position_lon'] = c.lon settings['map_zoom'] = self.map.get_zoom() settings['map_follow_position'] = self.marksLayer.get_follow_position() #if self.current_cache != None: # settings['last_selected_geocache'] = self.current_cache.name #for i in ['options_username', 'options_password', 'download_noimages', 'options_show_name', 'options_hide_found', 'options_show_html_description', 'options_map_double_size', 'options_rotate_screen', 'tts_interval']: # settings[i] = self.settings[i] caller.save_settings(settings, self) def __on_settings_changed(self, caller, settings, source): if 'last_target_lat' in settings: self.set_target(geo.Coordinate(settings['last_target_lat'], settings['last_target_lon'])) agtl-0.8.0.3/files/advancedcaching/run-core.py000066400000000000000000000022311151564747700211160ustar00rootroot00000000000000what = ''' import core core.start() ''' print 'profiling...' import cProfile p = cProfile.Profile() p.run(what) stats = p.getstats() print "BY CALLS:\n------------------------------------------------------------" def c(x, y): if x.callcount < y.callcount: return 1 elif x.callcount == y.callcount: return 0 else: return -1 stats.sort(cmp = c) for line in stats[:100]: print "%d %4f %s" % (line.callcount, line.totaltime, line.code) if line.calls == None: continue line.calls.sort(cmp = c) for line in line.calls[:10]: print "-- %d %4f %s" % (line.callcount, line.totaltime, line.code) print "BY TOTALTIME:\n------------------------------------------------------------" def c(x, y): if x.totaltime < y.totaltime: return 1 elif x.totaltime == y.totaltime: return 0 else: return -1 stats.sort(cmp = c) for line in stats[:30]: print "%d %4f %s" % (line.callcount, line.totaltime, line.code) if line.calls == None: continue line.calls.sort(cmp = c) for line in line.calls[:10]: print "-- %d %4f %s" % (line.callcount, line.totaltime, line.code)agtl-0.8.0.3/files/advancedcaching/simplegui.py000066400000000000000000001624711151564747700213770ustar00rootroot00000000000000#!/usr/bin/python # -*- coding: utf-8 -*- # Copyright (C) 2010 Daniel Fett # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # Author: Daniel Fett agtl@danielfett.de # Jabber: fett.daniel@jaber.ccc.de # Bugtracker and GIT Repository: http://github.com/webhamster/advancedcaching # # deps: python-html python-image python-netclient python-misc python-pygtk python-mime python-json # todo: # parse attributes? # add "next waypoint" button? # add description to displayed images? # add translation support? ### For the gui :-) import math from astral import Astral import geo import geocaching import gobject import gtk import logging logger = logging.getLogger('simplegui') try: import gtk.glade import extListview except (ImportError): logger.info( "Please install glade if you're NOT on the maemo platform.") import pango from os import path, extsep import re from cachedownloader import HTMLManipulations from gtkmap import * from gui import Gui class SimpleGui(Gui): USES = ['gpsprovider'] XMLFILE = "freerunner.glade" REDRAW_DISTANCE_TRACKING = 50 # distance from center of visible map in px REDRAW_DISTANCE_MINOR = 4 # distance from last displayed point in px DISTANCE_DISABLE_ARROW = 5 # meters MAX_NUM_RESULTS = 50 ARROW_OFFSET = 1.0 / 3.0 # Offset to center of arrow, calculated as 2-x = sqrt(1^2+(x+1)^2) ARROW_SHAPE = [(0, -2 + ARROW_OFFSET), (1, + 1 + ARROW_OFFSET), (0, 0 + ARROW_OFFSET), (-1, 1 + ARROW_OFFSET)] # arrow colors and sizes COLOR_ARROW_DEFAULT = gtk.gdk.color_parse("green") COLOR_ARROW_NEAR = gtk.gdk.color_parse("orange") COLOR_ARROW_ATTARGET = gtk.gdk.color_parse("red") COLOR_ARROW_DISABLED = gtk.gdk.color_parse("red") COLOR_ARROW_CIRCLE = gtk.gdk.color_parse("darkgray") COLOR_ARROW_OUTER_LINE = gtk.gdk.color_parse("black") COLOR_ARROW_CROSS = gtk.gdk.color_parse('darkslategray') COLOR_ARROW_ERROR = gtk.gdk.color_parse('gold') COLOR_NORTH_INDICATOR = gtk.gdk.color_parse("gold") COLOR_SUN_INDICATOR = gtk.gdk.color_parse("yellow") COLOR_SUN_INDICATOR_TEXT = gtk.gdk.color_parse("black") ARROW_LINE_WIDTH = 3 NORTH_INDICATOR_SIZE = 30 FONT_NORTH_INDICATOR = pango.FontDescription("Sans 9") FONT_SUN_INDICATOR = pango.FontDescription("Sans 8") # quality indicator COLOR_QUALITY_OUTER = gtk.gdk.color_parse("black") COLOR_QUALITY_INNER = gtk.gdk.color_parse("green") SETTINGS_CHECKBOXES = [ 'download_visible', 'download_notfound', 'download_new', 'download_nothing', 'download_resize', 'options_show_name', 'download_noimages', 'options_hide_found', 'options_map_double_size', ] SETTINGS_INPUTS = [ 'download_output_dir', 'download_resize_pixel', 'options_username', 'options_password', 'download_map_path' ] def __init__(self, core, dataroot): gtk.gdk.threads_init() self._prepare_images(dataroot) self.core = core self.core.connect('map-marks-changed', self._on_map_changed) self.core.connect('cache-changed', self._on_cache_changed) self.core.connect('target-changed', self._on_target_changed) self.core.connect('good-fix', self._on_good_fix) self.core.connect('no-fix', self._on_no_fix) self.core.connect('settings-changed', self._on_settings_changed) self.core.connect('save-settings', self._on_save_settings) self.core.connect('error', lambda caller, message: self.show_error(message)) self.core.connect('progress', lambda caller, fraction, text: self.set_progress(fraction, text)) self.core.connect('hide-progress', lambda caller: self.hide_progress()) self.core.connect('fieldnotes-changed', self._on_fieldnotes_changed) self.settings = {} Map.set_config(self.core.settings['map_providers'], self.core.settings['download_map_path'], self.noimage_cantload, self.noimage_loading) #OsdLayer.set_layout(pango.FontDescription("Nokia Sans Maps 13"), gtk.gdk.color_parse('black')) self.format = geo.Coordinate.FORMAT_DM # @type self.current_cache geocaching.GeocacheCoordinate self.current_cache = None self.gps_data = None self.gps_has_fix = False self.gps_last_good_fix = None self.gps_last_screen_position = (0, 0) self.block_changes = False self.image_zoomed = False self.image_no = 0 self.images = [] self.north_indicator_layout = None self.drawing_area_configured = self.drawing_area_arrow_configured = False self.notes_changed = False self.fieldnotes_changed = False self.astral = Astral() global xml xml = gtk.glade.XML(path.join(dataroot, self.XMLFILE)) self.load_ui() # self.build_tile_loaders() def _get_geocaches_callback(self, visible_area, maxresults): return self.core.pointprovider.get_points_filter(visible_area, False if self.settings['options_hide_found'] else None, maxresults) def _prepare_images(self, dataroot): p = "%s%s%%s" % (path.join(dataroot, '%s'), extsep) self.noimage_cantload = p % ('noimage-cantload', 'png') self.noimage_loading = p % ('noimage-loading', 'png') def _on_cache_changed(self, something, cache): if self.current_cache != None \ and cache.name == self.current_cache.name: self.show_cache(cache) return False def set_current_cache(self, cache): pass def _on_map_changed(self, something): self.map.redraw_layers() return False def load_ui(self): self.window = xml.get_widget("window1") xml.signal_autoconnect(self) # map drawing area self.drawing_area = xml.get_widget("drawingarea") self.drawing_area_arrow = xml.get_widget("drawingarea_arrow") self.filtermsg = xml.get_widget('label_filtermsg') self.scrolledwindow_image = xml.get_widget('scrolledwindow_image') self.image_cache = xml.get_widget('image_cache') self.image_cache_caption = xml.get_widget('label_cache_image_caption') self.notebook_cache = xml.get_widget('notebook_cache') self.notebook_all = xml.get_widget('notebook_all') self.notebook_search = xml.get_widget('notebook_search') self.progressbar = xml.get_widget('progress_download') self.button_download_details = xml.get_widget('button_download_details') self.button_track = xml.get_widget('togglebutton_track') self.check_result_marked = xml.get_widget('check_result_marked') self.label_fieldnotes = xml.get_widget('label_fieldnotes') self.button_upload_fieldnotes = xml.get_widget('button_upload_fieldnotes') self.button_check_updates = xml.get_widget('button_check_updates') self.label_bearing = xml.get_widget('label_bearing') self.label_dist = xml.get_widget('label_dist') self.label_altitude = xml.get_widget('label_altitude') self.label_latlon = xml.get_widget('label_latlon') self.label_target = xml.get_widget('label_target') self.label_quality = xml.get_widget('label_quality') self.input_export_path = xml.get_widget('input_export_path') # arrow drawing area self.drawing_area_arrow.connect("expose_event", self._expose_event_arrow) self.drawing_area_arrow.connect("configure_event", self._configure_event_arrow) self.drawing_area_arrow.set_events(gtk.gdk.EXPOSURE_MASK) # map self._configure_map() xml.get_widget('tableMap').attach(self.map, 0, 4, 1, 2) self.cache_elements = { 'name_downloaded': xml.get_widget('link_cache_name'), 'name_not_downloaded': xml.get_widget('button_cache_name'), 'title': xml.get_widget('label_cache_title'), 'type': xml.get_widget('label_cache_type'), 'size': xml.get_widget('label_cache_size'), 'terrain': xml.get_widget('label_cache_terrain'), 'difficulty': xml.get_widget('label_cache_difficulty'), 'desc': xml.get_widget('textview_cache_desc').get_buffer(), 'notes': xml.get_widget('textview_cache_notes').get_buffer(), 'fieldnotes': xml.get_widget('textview_cache_fieldnotes').get_buffer(), 'hints': xml.get_widget('label_cache_hints').get_buffer(), 'coords': xml.get_widget('label_cache_coords'), 'log_found': xml.get_widget('radiobutton_cache_log_found'), 'log_notfound': xml.get_widget('radiobutton_cache_log_notfound'), 'log_note': xml.get_widget('radiobutton_cache_log_note'), 'log_no': xml.get_widget('radiobutton_cache_log_no'), 'log_date': xml.get_widget('label_cache_log_date'), 'marked': xml.get_widget('check_cache_marked'), } self.search_elements = { 'type': { geocaching.GeocacheCoordinate.TYPE_REGULAR: xml.get_widget('check_search_type_traditional'), geocaching.GeocacheCoordinate.TYPE_MULTI: xml.get_widget('check_search_type_multi'), geocaching.GeocacheCoordinate.TYPE_MYSTERY: xml.get_widget('check_search_type_unknown'), geocaching.GeocacheCoordinate.TYPE_VIRTUAL: xml.get_widget('check_search_type_virtual'), 'all': xml.get_widget('check_search_type_other') }, 'name': xml.get_widget('entry_search_name'), 'status': xml.get_widget('combo_search_status'), 'size': { 1: xml.get_widget('check_search_size_1'), 2: xml.get_widget('check_search_size_2'), 3: xml.get_widget('check_search_size_3'), 4: xml.get_widget('check_search_size_4'), 5: xml.get_widget('check_search_size_5'), }, 'diff': { 'selector': xml.get_widget('combo_search_diff_sel'), 'value': xml.get_widget('combo_search_diff'), }, 'terr': { 'selector': xml.get_widget('combo_search_terr_sel'), 'value': xml.get_widget('combo_search_terr'), }, 'inview': xml.get_widget('check_search_inview'), } # # setting up TABLES # # Create the renderer used in the listview txtRdr = gtk.CellRendererText() ( ROW_TITLE, ROW_TYPE, ROW_SIZE, ROW_TERRAIN, ROW_DIFF, ROW_ID, ) = range(6) columns = ( ('name', [(txtRdr, gobject.TYPE_STRING)], (ROW_TITLE,), False, True), ('type', [(txtRdr, gobject.TYPE_STRING)], (ROW_TYPE,), False, True), ('size', [(txtRdr, gobject.TYPE_STRING)], (ROW_SIZE, ROW_ID), False, True), ('ter', [(txtRdr, gobject.TYPE_STRING)], (ROW_TERRAIN, ROW_ID), False, True), ('dif', [(txtRdr, gobject.TYPE_STRING)], (ROW_DIFF, ROW_ID), False, True), ('ID', [(txtRdr, gobject.TYPE_STRING)], (ROW_ID,), False, True), ) self.cachelist = listview = extListview.ExtListView(columns, sortable=True, useMarkup=True, canShowHideColumns=False) self.cachelist_contents = [] listview.connect('extlistview-button-pressed', self.on_search_cache_clicked) xml.get_widget('scrolledwindow_search').add(listview) ( COL_COORD_NAME, COL_COORD_LATLON, COL_COORD_ID, COL_COORD_COMMENT, ) = range(4) columns = ( ('name', [(txtRdr, gobject.TYPE_STRING)], (COL_COORD_NAME), False, True), ('pos', [(txtRdr, gobject.TYPE_STRING)], (COL_COORD_LATLON), False, True), ('id', [(txtRdr, gobject.TYPE_STRING)], (COL_COORD_ID), False, True), ('comment', [(txtRdr, gobject.TYPE_STRING)], (COL_COORD_COMMENT,), False, True), ) self.coordlist = extListview.ExtListView(columns, sortable=True, useMarkup=False, canShowHideColumns=False) self.coordlist.connect('extlistview-button-pressed', self.on_waypoint_clicked) xml.get_widget('scrolledwindow_coordlist').add(self.coordlist) self.notebook_all.set_current_page(1) gobject.timeout_add_seconds(10, self._check_notes_save) def _configure_map(self): try: coord = geo.Coordinate(self.settings['map_position_lat'], self.settings['map_position_lon']) zoom = self.settings['map_zoom'] except KeyError: coord = self._get_best_coordinate(geo.Coordinate(50, 10)) zoom = 6 self.map = Map(center=coord, zoom=zoom) self.geocache_layer = GeocacheLayer(self._get_geocaches_callback, self.show_cache) self.marks_layer = MarksLayer() self.map.add_layer(self.geocache_layer) self.map.add_layer(self.marks_layer) self.map.add_layer(OsdLayer()) self.core.connect('target-changed', self.marks_layer.on_target_changed) self.core.connect('good-fix', self.marks_layer.on_good_fix) self.core.connect('no-fix', self.marks_layer.on_no_fix) self.map.connect('tile-loader-changed', lambda widget, loader: self._update_zoom_buttons()) def on_marked_label_clicked(self, event=None, widget=None): w = xml.get_widget('check_cache_marked') w.set_active(not w.get_active()) def _check_notes_save(self): if self.current_cache != None and self.notes_changed: self.current_cache.notes = self.cache_elements['notes'].get_text(self.cache_elements['notes'].get_start_iter(), self.cache_elements['notes'].get_end_iter()) self.core.save_cache_attribute(self.current_cache, 'notes') self.notes_changed = False if self.current_cache != None and self.fieldnotes_changed: self.current_cache.fieldnotes = self.cache_elements['fieldnotes'].get_text(self.cache_elements['fieldnotes'].get_start_iter(), self.cache_elements['fieldnotes'].get_end_iter()) self.core.save_cache_attribute(self.current_cache, 'fieldnotes') self.fieldnotes_changed = False def _configure_event_arrow(self, widget, event): x, y, width, height = widget.get_allocation() self.pixmap_arrow = gtk.gdk.Pixmap(widget.window, width, height) self.xgc_arrow = widget.window.new_gc() if self.north_indicator_layout == None: # prepare font self.north_indicator_layout = widget.create_pango_layout("N") self.north_indicator_layout.set_alignment(pango.ALIGN_CENTER) self.north_indicator_layout.set_font_description(self.FONT_NORTH_INDICATOR) self.drawing_area_arrow_configured = True gobject.idle_add(self._draw_arrow) def on_window_destroy(self, target, more=None, data=None): self.core.on_destroy() gtk.main_quit() def _get_best_coordinate(self, start=None): if start != None: c = start elif self.gps_data != None and self.gps_data.position != None: c = self.gps_data.position elif self.core.current_target != None: c = self.core.current_target else: c = geo.Coordinate(0, 0) return c # called by core def display_results_advanced(self, caches): label = xml.get_widget('label_too_much_results') too_many = len(caches) > self.MAX_NUM_RESULTS if too_many: text = 'Too many results. Only showing first %d.' % self.MAX_NUM_RESULTS label.set_text(text) label.show() caches = caches[:self.MAX_NUM_RESULTS] else: label.hide() self.cachelist_contents = caches rows = [] for r in caches: if r.size == -1: s = "?" else: s = "%d" % r.size if r.difficulty == -1: d = "?" else: d = "%.1f" % (r.difficulty / 10) if r.terrain == -1: t = "?" else: t = "%.1f" % (r.terrain / 10) title = self._format_cache_title(r) rows.append((title, r.type, s, t, d, r.name, )) self.cachelist.replaceContent(rows) self.notebook_search.set_current_page(1) self.map.redraw_layers() @staticmethod def _format_cache_title(cache): m = cache.title.replace('&', '&').replace('<', '<').replace('>', '>') if cache.marked and cache.found: return '%s' % m elif cache.marked: return '%s' % m elif cache.found: return '%s' % m else: return m def _draw_arrow(self): outer_circle_size = 3 circle_border = 10 indicator_border = 40 distance_attarget = 50 distance_near = 150 disabled_border_size = 30 signal_width = 15 error_circle_size = 0.95 error_circle_width = 7 if not self.drawing_area_arrow_configured: return widget = self.drawing_area_arrow x, y, width, height = widget.get_allocation() disabled = not (self.gps_has_fix and self.gps_target_bearing != None and self.gps_target_distance != None) self.pixmap_arrow.draw_rectangle(widget.get_style().bg_gc[gtk.STATE_NORMAL], True, 0, 0, width, height) if disabled: self.xgc_arrow.set_rgb_fg_color(self.COLOR_ARROW_DISABLED) border = disabled_border_size self.pixmap_arrow.draw_line(self.xgc_arrow, border, border, width - border, height - border) self.pixmap_arrow.draw_line(self.xgc_arrow, border, height - border, width - border, border) self.drawing_area_arrow.queue_draw() return False # draw signal indicator if self.COLOR_QUALITY_OUTER != None: self.xgc_arrow.line_width = 1 self.xgc_arrow.set_rgb_fg_color(self.COLOR_QUALITY_OUTER) self.pixmap_arrow.draw_rectangle(self.xgc_arrow, True, width - signal_width - 2, 0, signal_width + 2, height) self.xgc_arrow.set_rgb_fg_color(self.COLOR_QUALITY_INNER) usable_height = height - 1 target_height = int(round(usable_height * self.gps_data.quality)) self.pixmap_arrow.draw_rectangle(self.xgc_arrow, True, width - signal_width - 1, usable_height - target_height, signal_width, target_height) display_bearing = self.gps_target_bearing - self.gps_data.bearing display_distance = self.gps_target_distance display_north = math.radians(self.gps_data.bearing) try: sun_angle = self.astral.get_sun_azimuth_from_fix(self.gps_data) except Exception, e: logger.exception("Couldn't get sun angle: %s" % e) sun_angle = None if sun_angle != None: display_sun = math.radians((- sun_angle + self.gps_data.bearing) % 360) # draw moving direction if self.COLOR_ARROW_CROSS != None: self.xgc_arrow.set_rgb_fg_color(self.COLOR_ARROW_CROSS) self.pixmap_arrow.draw_line(self.xgc_arrow, width / 2, height, width / 2, 0) self.pixmap_arrow.draw_line(self.xgc_arrow, 0, height / 2, width, height / 2) self.xgc_arrow.set_rgb_fg_color(self.COLOR_ARROW_CIRCLE) self.xgc_arrow.line_width = outer_circle_size circle_size = min(height, width) / 2 - circle_border indicator_dist = min(height, width) / 2 - indicator_border center_x, center_y = width / 2, height / 2 # outer circle self.pixmap_arrow.draw_arc(self.xgc_arrow, False, center_x - circle_size, center_y - circle_size, circle_size * 2, circle_size * 2, 0, 64 * 360) #position_x - indicator_radius / 2, position_y - indicator_radius / 2, if display_distance > self.DISTANCE_DISABLE_ARROW: self.xgc_arrow.line_width = error_circle_width self.xgc_arrow.set_rgb_fg_color(self.COLOR_ARROW_ERROR) self.xgc_arrow.set_dashes(1, (5, 5)) self.xgc_arrow.line_style = gtk.gdk.LINE_ON_OFF_DASH ecc = int(error_circle_size * circle_size) err = min(self.gps_data.error_bearing, 181) # don't draw multiple circles :-) err_start = int((90-(display_bearing + err)) * 64) err_delta = int(err * 2 * 64) self.pixmap_arrow.draw_arc(self.xgc_arrow, False, center_x - ecc, center_y - ecc, ecc * 2, ecc * 2, err_start, err_delta) self.xgc_arrow.line_style = gtk.gdk.LINE_SOLID if (display_distance < distance_attarget): color = self.COLOR_ARROW_ATTARGET elif (display_distance < distance_near): color = self.COLOR_ARROW_NEAR else: color = self.COLOR_ARROW_DEFAULT self.xgc_arrow.set_rgb_fg_color(color) if display_distance != None and display_distance > self.DISTANCE_DISABLE_ARROW: arrow_transformed = self._get_arrow_transformed(x, y, width, height, display_bearing) #self.xgc_arrow.line_style = gtk.gdk.LINE_SOLID self.pixmap_arrow.draw_polygon(self.xgc_arrow, True, arrow_transformed) self.xgc_arrow.set_rgb_fg_color(self.COLOR_ARROW_OUTER_LINE) self.xgc_arrow.line_width = self.ARROW_LINE_WIDTH self.pixmap_arrow.draw_polygon(self.xgc_arrow, False, arrow_transformed) # north indicator ni_w, ni_h = self.north_indicator_layout.get_size() position_x = int(width / 2 - math.sin(display_north) * indicator_dist - (ni_w / pango.SCALE) / 2) position_y = int(height / 2 - math.cos(display_north) * indicator_dist - (ni_h / pango.SCALE) / 2) self.xgc_arrow.set_function(gtk.gdk.COPY) self.xgc_arrow.set_rgb_fg_color(self.COLOR_NORTH_INDICATOR) self.pixmap_arrow.draw_layout(self.xgc_arrow, position_x, position_y, self.north_indicator_layout) # sun indicator if sun_angle != None: # center of sun indicator is this: sun_center_x = int(width / 2 - math.sin(display_sun) * indicator_dist) sun_center_y = int(height / 2 - math.cos(display_sun) * indicator_dist) # draw the text sun_indicator_layout = widget.create_pango_layout("sun") sun_indicator_layout.set_alignment(pango.ALIGN_CENTER) sun_indicator_layout.set_font_description(self.FONT_SUN_INDICATOR) # determine size of circle circle_size = int((sun_indicator_layout.get_size()[0] / pango.SCALE) / 2) # draw circle self.xgc_arrow.set_function(gtk.gdk.COPY) self.xgc_arrow.set_rgb_fg_color(self.COLOR_SUN_INDICATOR) self.pixmap_arrow.draw_arc(self.xgc_arrow, True, sun_center_x - circle_size, sun_center_y - circle_size, circle_size * 2, circle_size * 2, 0, 64 * 360) position_x = int(sun_center_x - (sun_indicator_layout.get_size()[0] / pango.SCALE) / 2) position_y = int(sun_center_y - (sun_indicator_layout.get_size()[1] / pango.SCALE) / 2) self.xgc_arrow.set_rgb_fg_color(self.COLOR_SUN_INDICATOR_TEXT) self.pixmap_arrow.draw_layout(self.xgc_arrow, position_x, position_y, sun_indicator_layout) else: # if we are closer than a few meters, the arrow will almost certainly # point in the wrong direction. therefore, we don't draw the arrow. circle_size = int(max(height / 2.5, width / 2.5)) self.pixmap_arrow.draw_arc(self.xgc_arrow, True, width / 2 - circle_size / 2, height / 2 - circle_size / 2, circle_size, circle_size, 0, 64 * 360) self.xgc_arrow.set_rgb_fg_color(self.COLOR_ARROW_OUTER_LINE) self.xgc_arrow.line_width = self.ARROW_LINE_WIDTH self.pixmap_arrow.draw_arc(self.xgc_arrow, False, width / 2 - circle_size / 2, height / 2 - circle_size / 2, circle_size, circle_size, 0, 64 * 360) self.drawing_area_arrow.queue_draw() return False @staticmethod def _get_arrow_transformed(root_x, root_y, width, height, angle): multiply = height / (2 * (2-SimpleGui.ARROW_OFFSET)) offset_x = width / 2 offset_y = height / 2 s = multiply * math.sin(math.radians(angle)) c = multiply * math.cos(math.radians(angle)) arrow_transformed = [(int(x * c + offset_x - y * s) + root_x, int(y * c + offset_y + x * s) + root_y) for x, y in SimpleGui.ARROW_SHAPE] return arrow_transformed def _expose_event_arrow(self, widget, event): x, y, width, height = event.area widget.window.draw_drawable(self.xgc_arrow, self.pixmap_arrow, x, y, x, y, width, height) return False def hide_progress(self): self.progressbar.hide() def _load_images(self): if self.current_cache == None: self._update_cache_image(reset=True) return if len(self.current_cache.get_images()) > 0: self.images = self.current_cache.get_images().items() else: self.images = {} self._update_cache_image(reset=True) def on_download_clicked(self, widget, data=None): self.core.on_download(self.map.get_visible_area()) def on_download_details_map_clicked(self, some, thing=None): logger.debug("Downloading geocaches on map.") self.core.on_download_descriptions(self.map.get_visible_area()) def on_download_details_sync_clicked(self, something): self.core.on_download_descriptions(self.map.get_visible_area()) def on_actions_clicked(self, widget, event): xml.get_widget('menu_actions').popup(None, None, None, event.button, event.get_time()) def on_cache_marked_toggled(self, widget): if self.current_cache == None: return self._update_mark(self.current_cache, widget.get_active()) def on_change_coord_clicked(self, something): self.set_target(self.show_coordinate_input(self.core.current_target)) def _get_search_selected_cache(self): index = self.cachelist.getFirstSelectedRowIndex() if index == None: return (None, None) cache = self.cachelist_contents[index] return (index, cache) def on_result_marked_toggled(self, widget): (index, cache) = self._get_search_selected_cache() if cache == None: return self._update_mark(cache, widget.get_active()) title = self._format_cache_title(cache) self.cachelist.setItem(index, 0, title) def _update_mark(self, cache, status): cache.marked = status self.core.save_cache_attribute(cache, 'marked') self.map.redraw_layers() def on_download_cache_clicked(self, something): self.core.on_download_cache(self.current_cache) def on_export_cache_clicked(self, something): if self.input_export_path.get_value().strip() == '': self.show_error("Please input path to export to.") return self.core.on_export_cache(self.current_cache, self.input_export_path.get_value()) def _on_good_fix(self, core, gps_data, distance, bearing): self.gps_data = gps_data self.gps_last_good_fix = gps_data self.gps_has_fix = True self.gps_target_distance = distance self.gps_target_bearing = bearing self._draw_arrow() self.update_gps_display() def on_image_next_clicked(self, something): if len(self.images) == 0: self._update_cache_image(reset=True) return self.image_no += 1 self.image_no %= len(self.images) self._update_cache_image() def on_image_zoom_clicked(self, something): self.image_zoomed = not self.image_zoomed self._update_cache_image() def _on_fieldnotes_changed(self, caller): widget = self.label_fieldnotes self._check_notes_save() l = self.core.get_new_fieldnotes_count() if l > 0: widget.set_text("you have created %d fieldnotes" % l) self.button_upload_fieldnotes.set_sensitive(True) else: widget.set_text("you have not created any new fieldnotes") self.button_upload_fieldnotes.set_sensitive(False) def on_list_marked_clicked(self, widget): self.core.on_start_search_advanced(marked=True) def _on_no_fix(self, caller, gps_data, status): self.gps_data = gps_data self.label_bearing.set_text("No Fix") self.label_latlon.set_text(status) self.gps_has_fix = False self.update_gps_display() self._draw_arrow() def on_notes_changed(self, something, somethingelse=None): self.notes_changed = True def on_fieldnotes_changed(self, something, somethingelse): self.fieldnotes_changed = True def on_fieldnotes_log_changed(self, something): from time import gmtime from time import strftime if self.current_cache == None: return if self.cache_elements['log_found'].get_active(): log = geocaching.GeocacheCoordinate.LOG_AS_FOUND elif self.cache_elements['log_notfound'].get_active(): log = geocaching.GeocacheCoordinate.LOG_AS_NOTFOUND elif self.cache_elements['log_note'].get_active(): log = geocaching.GeocacheCoordinate.LOG_AS_NOTE else: log = geocaching.GeocacheCoordinate.LOG_NO_LOG logdate = strftime('%Y-%m-%d', gmtime()) self.cache_elements['log_date'].set_text('fieldnote date: %s' % logdate) self.current_cache.logas = log self.current_cache.logdate = logdate self.core.save_fieldnote(self.current_cache) def on_save_config(self, something): if not self.block_changes: self._on_save_settings(self.core) def on_search_action_center_clicked(self, widget): (index, cache) = self._get_search_selected_cache() if cache == None: return self.set_center(cache) self.notebook_all.set_current_page(1) def on_search_action_set_target_clicked(self, widget): (index, cache) = self._get_search_selected_cache() if cache == None: return self.current_cache = cache self.set_target(cache) self.notebook_all.set_current_page(0) def on_search_action_view_details_clicked(self, widget): (index, cache) = self._get_search_selected_cache() if cache == None: return self.show_cache(cache) def on_search_advanced_clicked(self, something): def get_val_from_text(input, use_max): if not use_max: valmap = {'1..1.5': 1, '2..2.5': 2, '3..3.5': 3, '4..4.5': 4, '5': 5} default = 1 else: valmap = {'1..1.5': 1.5, '2..2.5': 2.5, '3..3.5': 3.5, '4..4.5': 4.5, '5': 5} default = 5 if input in valmap: return valmap[input] else: return default types = [a for a in [geocaching.GeocacheCoordinate.TYPE_REGULAR, geocaching.GeocacheCoordinate.TYPE_MULTI, geocaching.GeocacheCoordinate.TYPE_MYSTERY, geocaching.GeocacheCoordinate.TYPE_VIRTUAL] if self.search_elements['type'][a].get_active()] if self.search_elements['type']['all'].get_active() or len(types) == 0: types = None name_search = self.search_elements['name'].get_text() status = self.search_elements['status'].get_active_text() if status == 'not found': found = False marked = None elif status == 'found': found = True marked = None elif status == 'marked & new': found = False marked = True else: found = None marked = None sizes = [a for a in [1, 2, 3, 4, 5] if self.search_elements['size'][a].get_active()] if len(sizes) == 0: sizes = None search = {} for i in ['diff', 'terr']: sel = self.search_elements[i]['selector'].get_active_text() value = self.search_elements[i]['value'].get_active_text() if sel == 'min': search[i] = (get_val_from_text(value, False), 5) elif sel == 'max': search[i] = (0, get_val_from_text(value, True)) elif sel == '=': search[i] = (get_val_from_text(value, False), get_val_from_text(value, True)) else: search[i] = None if self.search_elements['inview'].get_active(): location = self.map.get_visible_area() else: location = None if found == None and name_search == None and sizes == None and \ search['terr'] == None and search['diff'] == None and types == None: self.filtermsg.hide() else: self.filtermsg.show() self.core.on_start_search_advanced(found=found, name_search=name_search, size=sizes, terrain=search['terr'], diff=search['diff'], ctype=types, location=location, marked=marked) def on_search_cache_clicked(self, listview, event, element): if element == None: return (index, cache) = self._get_search_selected_cache() if cache == None: return if event.type == gtk.gdk._2BUTTON_PRESS: self.show_cache(cache) self.set_center(cache) else: self.check_result_marked.set_active(cache.marked) def on_search_reset_clicked(self, something): self.filtermsg.hide() self.core.on_start_search_advanced() def on_set_target_clicked(self, something): if self.current_cache == None: return else: self.set_target(self.current_cache) self.notebook_all.set_current_page(0) def on_set_target_center(self, some, thing=None): self.set_target(self.map.get_center()) def on_show_target_clicked(self, some=None, data=None): if self.core.current_target == None: return else: self.set_center(self.core.current_target) def on_track_toggled(self, widget, data=None): logger.info("Track toggled, new state: " + repr(widget.get_active())) self.marks_layer.set_follow_position(widget.get_active()) def on_upload_fieldnotes(self, something): self.core.on_upload_fieldnotes() def on_waypoint_clicked(self, listview, event, element): if event.type != gtk.gdk._2BUTTON_PRESS or element == None: return if self.current_cache == None: return if element[0] == 0: self.set_target(self.current_cache) self.notebook_all.set_current_page(0) else: wpt = self.current_cache.get_waypoints()[element[0]-1] if wpt['lat'] == -1 or wpt['lon'] == -1: return self.set_target(geo.Coordinate(wpt['lat'], wpt['lon'], wpt['id'])) self.notebook_all.set_current_page(0) def on_zoomin_clicked(self, widget, data=None): self.map.relative_zoom(+ 1) def on_zoomout_clicked(self, widget, data=None): self.map.relative_zoom(-1) def _update_cache_image(self, reset=False): if reset: self.image_zoomed = False self.image_no = 0 if len(self.images) == 0: self.image_cache.set_from_stock(gtk.STOCK_CANCEL, -1) self.image_cache_caption.set_text("There's nothing to see here.") return try: if self.current_cache == None or len(self.images) <= self.image_no: self._update_cache_image(True) return filename = path.join(self.settings['download_output_dir'], self.images[self.image_no][0]) if not path.exists(filename): self.image_cache_caption.set_text("not found: %s" % filename) self.image_cache.set_from_stock(gtk.STOCK_GO_FORWARD, -1) return if not self.image_zoomed: mw, mh = self.scrolledwindow_image.get_allocation().width - 10, self.scrolledwindow_image.get_allocation().height - 10 pb = gtk.gdk.pixbuf_new_from_file_at_size(filename, mw, mh) else: pb = gtk.gdk.pixbuf_new_from_file(filename) self.image_cache.set_from_pixbuf(pb) caption = self.images[self.image_no][1] self.image_cache_caption.set_text("%d %s" % (self.image_no, caption)) self.image_cache_caption.set_use_markup(True) except Exception, e: logger.exception("Error loading image: %s" % e) @staticmethod def replace_image_tag(m): if m.group(1) != None and m.group(1).strip() != '': return ' [Image: %s] ' % m.group(1).strip() else: return ' [Image] ' def set_center(self, coord, noupdate=False, reset_track=True): logger.info("Set Center to %s with reset_track = %s" % (coord, reset_track)) self.map.set_center(coord, not noupdate) #if reset_track: # self.marks_layer.set_follow_position(False) def _set_track_mode(self, mode): self.marks_layer.set_follow_position(mode) try: self.button_track.set_active(mode) except: pass def _get_track_mode(self): return self.marks_layer.get_follow_position() def set_progress(self, fraction, text): self.progressbar.show() self.progressbar.set_text(text) self.progressbar.set_fraction(fraction) def set_target(self, cache): self.core.set_target(cache) def _on_target_changed(self, caller, cache, distance, bearing): self.gps_target_distance = distance self.gps_target_bearing = bearing self.label_target.set_text("%s\n%s" % (cache.get_lat(self.format), cache.get_lon(self.format))) self.label_target.set_use_markup(True) #self.set_center(cache) def show(self): self.window.show_all() gtk.main() # called by core def show_cache(self, cache): if cache == None: return self._check_notes_save() self.current_cache = cache # Title self.cache_elements['title'].set_text("%s %s" % (cache.name, cache.title)) self.cache_elements['title'].set_use_markup(True) # Type self.cache_elements['type'].set_text("%s" % cache.type) if cache.size == -1: self.cache_elements['size'].set_text("?") else: self.cache_elements['size'].set_text("%d/5" % cache.size) # Terrain if cache.terrain == -1: self.cache_elements['terrain'].set_text("?") else: self.cache_elements['terrain'].set_text("%s/5" % cache.get_terrain()) # Difficulty if cache.difficulty == -1: self.cache_elements['difficulty'].set_text("?") else: self.cache_elements['difficulty'].set_text("%s/5" % cache.get_difficulty()) # Description and short description text_shortdesc = self._strip_html(cache.shortdesc) if cache.status == geocaching.GeocacheCoordinate.STATUS_DISABLED: text_shortdesc = 'ATTENTION! This Cache is Disabled!\n--------------\n' + text_shortdesc text_longdesc = self._strip_html(re.sub(r'(?i)]+?>', ' [to get all images, re-download description] ', re.sub(r'\[\[img:([^\]]+)\]\]', lambda a: self._replace_image_callback(a, cache), cache.desc))) if text_longdesc == '': text_longdesc = '(no description available)' if not text_shortdesc == '': showdesc = text_shortdesc + "\n\n" + text_longdesc else: showdesc = text_longdesc self.cache_elements['desc'].set_text(showdesc) # is cache marked? self.cache_elements['marked'].set_active(True if cache.marked else False) # Set View self.notebook_cache.set_current_page(0) self.notebook_all.set_current_page(2) # logs logs = cache.get_logs() if len(logs) > 0: text_hints = 'LOGS:\n' for l in logs: if l['type'] == geocaching.GeocacheCoordinate.LOG_TYPE_FOUND: t = 'FOUND' elif l['type'] == geocaching.GeocacheCoordinate.LOG_TYPE_NOTFOUND: t = 'NOT FOUND' elif l['type'] == geocaching.GeocacheCoordinate.LOG_TYPE_NOTE: t = 'NOTE' elif l['type'] == geocaching.GeocacheCoordinate.LOG_TYPE_MAINTENANCE: t = 'MAINTENANCE' else: t = l['type'].upper() text_hints += '%s by %s at %4d/%d/%d: %s\n\n' % (t, l['finder'], int(l['year']), int(l['month']), int(l['day']), l['text']) text_hints += '\n----------------\n' else: text_hints = 'NO LOGS.\n\n' # hints hints = cache.hints.strip() if hints == '': hints = '(no hints available)' showdesc += "\n[no hints]" else: showdesc += "\n[hints available]" text_hints += 'HINTS:\n' + hints self.cache_elements['hints'].set_text(text_hints) # Waypoints format = lambda n: "%s %s" % (re.sub(r' ', '', n.get_lat(geo.Coordinate.FORMAT_DM)), re.sub(r' ', '', n.get_lon(geo.Coordinate.FORMAT_DM))) rows = [(cache.name, format(cache), '(cache coord)', '')] for w in cache.get_waypoints(): if not (w['lat'] == -1 and w['lon'] == -1): latlon = format(geo.Coordinate(w['lat'], w['lon'])) else: latlon = "???" rows.append((w['name'], latlon, w['id'], self._strip_html(w['comment']))) self.coordlist.replaceContent(rows) # Set button for downloading to correct state self.button_download_details.set_sensitive(True) # Load notes self.cache_elements['notes'].set_text(cache.notes if cache.notes != None else '') self.cache_elements['fieldnotes'].set_text(cache.fieldnotes if cache.fieldnotes != None else '') # Set field note (log) settings if cache.logas == geocaching.GeocacheCoordinate.LOG_AS_FOUND: self.cache_elements['log_found'].set_active(True) elif cache.logas == geocaching.GeocacheCoordinate.LOG_AS_NOTFOUND: self.cache_elements['log_notfound'].set_active(True) elif cache.logas == geocaching.GeocacheCoordinate.LOG_AS_NOTE: self.cache_elements['log_note'].set_active(True) else: self.cache_elements['log_no'].set_active(True) if cache.logdate != '': self.cache_elements['log_date'].set_text('fieldnote date: %s' % cache.logdate) else: self.cache_elements['log_date'].set_text('fieldnote date: not set') # Load images self._load_images() self.image_no = 0 if len(self.images) > 0: showdesc += "\n[%d image(s) available]" % len(self.images) else: showdesc += "\n[no images available]" # now, update the main text field a second time self.cache_elements['desc'].set_text(showdesc) #gobject.idle_add(self._draw_marks) #self.refresh() def _replace_image_callback(self, match, coordinate): if match.group(1) in coordinate.get_images(): desc = coordinate.get_images()[match.group(1)] if desc.strip() != '': return ' [image: %s] ' % desc else: return ' [image] ' else: return ' [image not found -- please re-download geocache description] ' def show_error(self, errormsg): gtk.gdk.threads_enter() error_dlg = gtk.MessageDialog(self.window, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE, "%s" % errormsg) error_dlg.connect('response', lambda w,d: error_dlg.destroy()) error_dlg.run() error_dlg.destroy() gtk.gdk.threads_leave() def show_success(self, message): gtk.gdk.threads_enter() suc_dlg = gtk.MessageDialog(type=gtk.MESSAGE_INFO \ , message_format=message \ , buttons=gtk.BUTTONS_OK) suc_dlg.run() suc_dlg.destroy() gtk.gdk.threads_leave() def show_coordinate_input(self, start): udr = UpdownRows(self.format, start, False) dialog = gtk.Dialog("Change Target", None, gtk.DIALOG_MODAL, (gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE)) frame = gtk.Frame("Latitude") frame.add(udr.table_lat) dialog.vbox.pack_start(frame) frame = gtk.Frame("Longitude") frame.add(udr.table_lon) dialog.vbox.pack_start(frame) dialog.show_all() dialog.run() dialog.destroy() c = udr.get_value() c.name = 'manual' return c @staticmethod def _strip_html(text): return HTMLManipulations.strip_html_visual(text, SimpleGui.replace_image_tag) def update_gps_display(self): if self.gps_data == None: return if self.gps_data.sats == 0: label = "No sats available" else: label = "%d/%d sats, error: ±%3.1fm" % (self.gps_data.sats, self.gps_data.sats_known, self.gps_data.error) if self.gps_data.dgps: label += " DGPS" self.label_quality.set_text(label) if self.gps_data.altitude == None or self.gps_data.bearing == None: return self.label_altitude.set_text("alt %3dm" % self.gps_data.altitude) self.label_bearing.set_text("%d°" % self.gps_data.bearing) self.label_latlon.set_text("%s\n%s" % (self.gps_data.position.get_lat(self.format), self.gps_data.position.get_lon(self.format))) self.label_latlon.set_use_markup(True) if self.core.current_target == None: return if self.gps_has_fix: target_distance = self.gps_target_distance label = geo.Coordinate.format_distance(target_distance) self.label_dist.set_text("%s" % label) self.label_dist.set_use_markup(True) #self.osd_string = "%d " else: self.label_dist.set_text("No Fix") self.label_dist.set_use_markup(True) #self.osd_string = "No Fix " def _on_settings_changed_gui(self, settings): for x in self.SETTINGS_CHECKBOXES: if x in self.settings: w = xml.get_widget('check_%s' % x) if w == None: continue w.set_active(self.settings[x]) for x in self.SETTINGS_INPUTS: if x in self.settings: w = xml.get_widget('input_%s' % x) if w == None: continue w.set_text(str(self.settings[x])) @staticmethod def shorten_name(s, chars): max_pos = chars min_pos = chars - 10 NOT_FOUND = -1 suffix = '…' # Case 1: Return string if it is shorter (or equal to) than the limit length = len(s) if length <= max_pos: return s else: # Case 2: Return it to nearest period if possible try: end = s.rindex('.', min_pos, max_pos) except ValueError: # Case 3: Return string to nearest space end = s.rfind(' ', min_pos, max_pos) if end == NOT_FOUND: end = max_pos return s[0:end] + suffix def _on_settings_changed(self, caller, settings, source): logger.debug("Got settings from %s, len() = %d, source = %s" % (caller, len(settings), source)) #if source == self: # return self.settings.update(settings) self.block_changes = True #if 'options_hide_found' in settings: # self.geocache_layer.set_show_found(not settings['options_hide_found']) if 'options_show_name' in settings: self.geocache_layer.set_show_name(settings['options_show_name']) if 'options_map_double_size' in settings: self.map.set_double_size(settings['options_map_double_size']) if 'map_zoom' in settings: if self.map.get_zoom() != settings['map_zoom']: self.map.set_zoom(settings['map_zoom']) if 'map_position_lat' in settings and 'map_position_lon' in settings: self.set_center(geo.Coordinate(settings['map_position_lat'], settings['map_position_lon']), reset_track = False) if 'map_follow_position' in settings: self._set_track_mode(settings['map_follow_position']) if 'last_target_lat' in settings: self.set_target(geo.Coordinate(settings['last_target_lat'], settings['last_target_lon'])) if 'last_selected_geocache' in settings and settings['last_selected_geocache'] not in (None, ''): cache = self.core.get_geocache_by_name(settings['last_selected_geocache']) if cache != None: self.set_current_cache(cache) self._on_settings_changed_gui(settings) self.block_changes = False def _on_save_settings(self, caller): c = self.map.get_center() settings = {} settings['map_position_lat'] = c.lat settings['map_position_lon'] = c.lon settings['map_zoom'] = self.map.get_zoom() settings['map_follow_position'] = self._get_track_mode() if self.current_cache != None: settings['last_selected_geocache'] = self.current_cache.name for x in self.SETTINGS_CHECKBOXES: w = xml.get_widget('check_%s' % x) if w != None: settings[x] = w.get_active() else: logger.info("Couldn't find widget: check_%s" % x) for x in self.SETTINGS_INPUTS: w = xml.get_widget('input_%s' % x) if w != None: settings[x] = w.get_text() else: logger.info("Couldn't find widget: input_%s" % x) caller.save_settings(settings, self) def on_button_check_updates_clicked(self, caller): updates = self.core.try_update() if updates != None: gobject.idle_add(self.show_success, "%d modules upgraded. There's no need to restart AGTL." % updates) return False class Updown(): def __init__(self, table, position, small): self.value = int(0) self.label = gtk.Label("0") self.label.set_use_markup(True) self.button_up = gtk.Button("+") self.button_down = gtk.Button("-") table.attach(self.button_up, position, position + 1, 0, 1) table.attach(self.label, position, position + 1, 1, 2, 0, 0) table.attach(self.button_down, position, position + 1, 2, 3) self.button_up.connect('clicked', self.value_up) self.button_down.connect('clicked', self.value_down) if small != None: if small: font = pango.FontDescription("sans 8") else: font = pango.FontDescription("sans 12") self.label.modify_font(font) self.button_up.child.modify_font(font) self.button_down.child.modify_font(font) def value_up(self, target): self.value = int((self.value + 1) % 10) self.update() def value_down(self, target): self.value = int((self.value - 1) % 10) self.update() def set_value(self, value): self.value = int(value) self.update() def update(self): self.label.set_markup("%d" % self.value) class PlusMinusUpdown(): def __init__(self, table, position, labels, small=None): self.is_neg = False self.labels = labels self.button = gtk.Button(labels[0]) table.attach(self.button, position, position + 1, 1, 2, gtk.FILL, gtk.FILL) self.button.connect('clicked', self.value_toggle) if small != None: self.button.child.modify_font(pango.FontDescription("sans 8")) def value_toggle(self, target): self.is_neg = not self.is_neg self.update() def set_value(self, value): self.is_neg = (value < 0) self.update() def get_value(self): if self.is_neg: return -1 else: return 1 def update(self): if self.is_neg: text = self.labels[0] else: text = self.labels[1] self.button.child.set_text(text) class UpdownRows(): def __init__(self, format, coord, large_dialog): self.format = format self.large_dialog = large_dialog if coord == None: coord = geo.Coordinate(50, 10, 'none') if format == geo.Coordinate.FORMAT_DM: [init_lat, init_lon] = coord.to_dm_array() elif format == geo.Coordinate.FORMAT_D: [init_lat, init_lon] = coord.to_d_array() [self.table_lat, self.chooser_lat] = self.generate_table(False, init_lat) [self.table_lon, self.chooser_lon] = self.generate_table(True, init_lon) self.switcher_lat.set_value(coord.lat) self.switcher_lon.set_value(coord.lon) def get_value(self): coord = geo.Coordinate(0, 0) lat_values = [ud.value for ud in self.chooser_lat] lon_values = [ud.value for ud in self.chooser_lon] if self.format == geo.Coordinate.FORMAT_DM: coord.from_dm_array(self.switcher_lat.get_value(), lat_values, self.switcher_lon.get_value(), lon_values) elif self.format == geo.Coordinate.FORMAT_D: coord.from_d_array(self.switcher_lat.get_value(), lat_values, self.switcher_lon.get_value(), lon_values) return coord def generate_table(self, is_long, initial_value): interrupt = {} if self.format == geo.Coordinate.FORMAT_DM and not is_long: small = 2 num = 7 interrupt[3] = "°" interrupt[6] = "." elif self.format == geo.Coordinate.FORMAT_DM and is_long: small = 3 num = 8 interrupt[4] = "°" interrupt[7] = "." elif self.format == geo.Coordinate.FORMAT_D and not is_long: small = 2 num = 7 interrupt[3] = "." elif self.format == geo.Coordinate.FORMAT_D and is_long: small = 3 num = 8 interrupt[4] = "." table = gtk.Table(3, num + len(interrupt) + 1, False) if is_long: self.switcher_lon = PlusMinusUpdown(table, 0, ['W', 'E'], None if self.large_dialog else False) else: self.switcher_lat = PlusMinusUpdown(table, 0, ['S', 'N'], None if self.large_dialog else False) chooser = [] cn = 0 for i in xrange(1, num + len(interrupt) + 1): if i in interrupt: table.attach(gtk.Label(interrupt[i]), i, i + 1, 1, 2, 0, 0) else: ud = Updown(table, i, (cn < small) if not self.large_dialog else None) if cn < len(initial_value): ud.set_value(initial_value[cn]) chooser.append(ud) cn = cn + 1 return [table, chooser] agtl-0.8.0.3/files/advancedcaching/threadpool.py000066400000000000000000000365351151564747700215430ustar00rootroot00000000000000# -*- coding: UTF-8 -*- """Easy to use object-oriented thread pool framework. A thread pool is an object that maintains a pool of worker threads to perform time consuming operations in parallel. It assigns jobs to the threads by putting them in a work request queue, where they are picked up by the next available thread. This then performs the requested operation in the background and puts the results in another queue. The thread pool object can then collect the results from all threads from this queue as soon as they become available or after all threads have finished their work. It's also possible, to define callbacks to handle each result as it comes in. The basic concept and some code was taken from the book "Python in a Nutshell, 2nd edition" by Alex Martelli, O'Reilly 2006, ISBN 0-596-10046-9, from section 14.5 "Threaded Program Architecture". I wrapped the main program logic in the ThreadPool class, added the WorkRequest class and the callback system and tweaked the code here and there. Kudos also to Florent Aide for the exception handling mechanism. Basic usage:: >>> pool = ThreadPool(poolsize) >>> requests = makeRequests(some_callable, list_of_args, callback) >>> [pool.putRequest(req) for req in requests] >>> pool.wait() See the end of the module code for a brief, annotated usage example. Website : http://chrisarndt.de/projects/threadpool/ """ __docformat__ = "restructuredtext en" __all__ = [ 'makeRequests', 'NoResultsPending', 'NoWorkersAvailable', 'ThreadPool', 'WorkRequest', 'WorkerThread' ] __author__ = "Christopher Arndt" __version__ = '1.2.7' __revision__ = "$Revision: 416 $" __date__ = "$Date: 2009-10-07 05:41:27 +0200 (Mi, 07. Okt 2009) $" __license__ = "MIT license" # standard library modules import sys import threading import Queue import traceback # exceptions class NoResultsPending(Exception): """All work requests have been processed.""" pass class NoWorkersAvailable(Exception): """No worker threads available to process remaining requests.""" pass # internal module helper functions def _handle_thread_exception(request, exc_info): """Default exception handler callback function. This just prints the exception info via ``traceback.print_exception``. """ traceback.print_exception(*exc_info) # utility functions def makeRequests(callable_, args_list, callback=None, exc_callback=_handle_thread_exception): """Create several work requests for same callable with different arguments. Convenience function for creating several work requests for the same callable where each invocation of the callable receives different values for its arguments. ``args_list`` contains the parameters for each invocation of callable. Each item in ``args_list`` should be either a 2-item tuple of the list of positional arguments and a dictionary of keyword arguments or a single, non-tuple argument. See docstring for ``WorkRequest`` for info on ``callback`` and ``exc_callback``. """ requests = [] for item in args_list: if isinstance(item, tuple): requests.append( WorkRequest(callable_, item[0], item[1], callback=callback, exc_callback=exc_callback) ) else: requests.append( WorkRequest(callable_, [item], None, callback=callback, exc_callback=exc_callback) ) return requests # classes class WorkerThread(threading.Thread): """Background thread connected to the requests/results queues. A worker thread sits in the background and picks up work requests from one queue and puts the results in another until it is dismissed. """ def __init__(self, requests_queue, results_queue, poll_timeout=5, **kwds): """Set up thread in daemonic mode and start it immediatedly. ``requests_queue`` and ``results_queue`` are instances of ``Queue.Queue`` passed by the ``ThreadPool`` class when it creates a new worker thread. """ threading.Thread.__init__(self, **kwds) self.setDaemon(1) self._requests_queue = requests_queue self._results_queue = results_queue self._poll_timeout = poll_timeout self._dismissed = threading.Event() self.start() def run(self): """Repeatedly process the job queue until told to exit.""" while True: if self._dismissed.isSet(): # we are dismissed, break out of loop break # get next work request. If we don't get a new request from the # queue after self._poll_timout seconds, we jump to the start of # the while loop again, to give the thread a chance to exit. try: request = self._requests_queue.get(True, self._poll_timeout) except Queue.Empty: continue else: if self._dismissed.isSet(): # we are dismissed, put back request in queue and exit loop self._requests_queue.put(request) break try: result = request.callable(*request.args, **request.kwds) self._results_queue.put((request, result)) except: request.exception = True self._results_queue.put((request, sys.exc_info())) def dismiss(self): """Sets a flag to tell the thread to exit when done with current job.""" self._dismissed.set() class WorkRequest: """A request to execute a callable for putting in the request queue later. See the module function ``makeRequests`` for the common case where you want to build several ``WorkRequest`` objects for the same callable but with different arguments for each call. """ def __init__(self, callable_, args=None, kwds=None, requestID=None, callback=None, exc_callback=_handle_thread_exception): """Create a work request for a callable and attach callbacks. A work request consists of the a callable to be executed by a worker thread, a list of positional arguments, a dictionary of keyword arguments. A ``callback`` function can be specified, that is called when the results of the request are picked up from the result queue. It must accept two anonymous arguments, the ``WorkRequest`` object and the results of the callable, in that order. If you want to pass additional information to the callback, just stick it on the request object. You can also give custom callback for when an exception occurs with the ``exc_callback`` keyword parameter. It should also accept two anonymous arguments, the ``WorkRequest`` and a tuple with the exception details as returned by ``sys.exc_info()``. The default implementation of this callback just prints the exception info via ``traceback.print_exception``. If you want no exception handler callback, just pass in ``None``. ``requestID``, if given, must be hashable since it is used by ``ThreadPool`` object to store the results of that work request in a dictionary. It defaults to the return value of ``id(self)``. """ if requestID is None: self.requestID = id(self) else: try: self.requestID = hash(requestID) except TypeError: raise TypeError("requestID must be hashable.") self.exception = False self.callback = callback self.exc_callback = exc_callback self.callable = callable_ self.args = args or [] self.kwds = kwds or {} def __str__(self): return "" % \ (self.requestID, self.args, self.kwds, self.exception) class ThreadPool: """A thread pool, distributing work requests and collecting results. See the module docstring for more information. """ def __init__(self, num_workers, q_size=0, resq_size=0, poll_timeout=5): """Set up the thread pool and start num_workers worker threads. ``num_workers`` is the number of worker threads to start initially. If ``q_size > 0`` the size of the work *request queue* is limited and the thread pool blocks when the queue is full and it tries to put more work requests in it (see ``putRequest`` method), unless you also use a positive ``timeout`` value for ``putRequest``. If ``resq_size > 0`` the size of the *results queue* is limited and the worker threads will block when the queue is full and they try to put new results in it. .. warning: If you set both ``q_size`` and ``resq_size`` to ``!= 0`` there is the possibilty of a deadlock, when the results queue is not pulled regularly and too many jobs are put in the work requests queue. To prevent this, always set ``timeout > 0`` when calling ``ThreadPool.putRequest()`` and catch ``Queue.Full`` exceptions. """ self._requests_queue = Queue.Queue(q_size) self._results_queue = Queue.Queue(resq_size) self.workers = [] self.dismissedWorkers = [] self.workRequests = {} self.createWorkers(num_workers, poll_timeout) def createWorkers(self, num_workers, poll_timeout=5): """Add num_workers worker threads to the pool. ``poll_timout`` sets the interval in seconds (int or float) for how ofte threads should check whether they are dismissed, while waiting for requests. """ for i in range(num_workers): self.workers.append(WorkerThread(self._requests_queue, self._results_queue, poll_timeout=poll_timeout)) def dismissWorkers(self, num_workers, do_join=False): """Tell num_workers worker threads to quit after their current task.""" dismiss_list = [] for i in range(min(num_workers, len(self.workers))): worker = self.workers.pop() worker.dismiss() dismiss_list.append(worker) if do_join: for worker in dismiss_list: worker.join() else: self.dismissedWorkers.extend(dismiss_list) def joinAllDismissedWorkers(self): """Perform Thread.join() on all worker threads that have been dismissed. """ for worker in self.dismissedWorkers: worker.join() self.dismissedWorkers = [] def putRequest(self, request, block=True, timeout=None): """Put work request into work queue and save its id for later.""" assert isinstance(request, WorkRequest) # don't reuse old work requests assert not getattr(request, 'exception', None) self._requests_queue.put(request, block, timeout) self.workRequests[request.requestID] = request def poll(self, block=False): """Process any new results in the queue.""" while True: # still results pending? if not self.workRequests: raise NoResultsPending # are there still workers to process remaining requests? elif block and not self.workers: raise NoWorkersAvailable try: # get back next results request, result = self._results_queue.get(block=block) # has an exception occured? if request.exception and request.exc_callback: request.exc_callback(request, result) # hand results to callback, if any if request.callback and not \ (request.exception and request.exc_callback): request.callback(request, result) del self.workRequests[request.requestID] except Queue.Empty: break def wait(self): """Wait for results, blocking until all have arrived.""" while 1: try: self.poll(True) except NoResultsPending: break ################ # USAGE EXAMPLE ################ if __name__ == '__main__': import random import time # the work the threads will have to do (rather trivial in our example) def do_something(data): time.sleep(random.randint(1,5)) result = round(random.random() * data, 5) # just to show off, we throw an exception once in a while if result > 5: raise RuntimeError("Something extraordinary happened!") return result # this will be called each time a result is available def print_result(request, result): print "**** Result from request #%s: %r" % (request.requestID, result) # this will be called when an exception occurs within a thread # this example exception handler does little more than the default handler def handle_exception(request, exc_info): if not isinstance(exc_info, tuple): # Something is seriously wrong... print request print exc_info raise SystemExit print "**** Exception occured in request #%s: %s" % \ (request.requestID, exc_info) # assemble the arguments for each job to a list... data = [random.randint(1,10) for i in range(20)] # ... and build a WorkRequest object for each item in data requests = makeRequests(do_something, data, print_result, handle_exception) # to use the default exception handler, uncomment next line and comment out # the preceding one. #requests = makeRequests(do_something, data, print_result) # or the other form of args_lists accepted by makeRequests: ((,), {}) data = [((random.randint(1,10),), {}) for i in range(20)] requests.extend( makeRequests(do_something, data, print_result, handle_exception) #makeRequests(do_something, data, print_result) # to use the default exception handler, uncomment next line and comment # out the preceding one. ) # we create a pool of 3 worker threads print "Creating thread pool with 3 worker threads." main = ThreadPool(3) # then we put the work requests in the queue... for req in requests: main.putRequest(req) print "Work request #%s added." % req.requestID # or shorter: # [main.putRequest(req) for req in requests] # ...and wait for the results to arrive in the result queue # by using ThreadPool.wait(). This would block until results for # all work requests have arrived: # main.wait() # instead we can poll for results while doing something else: i = 0 while True: try: time.sleep(0.5) main.poll() print "Main thread working...", print "(active worker threads: %i)" % (threading.activeCount()-1, ) if i == 10: print "**** Adding 3 more worker threads..." main.createWorkers(3) if i == 20: print "**** Dismissing 2 worker threads..." main.dismissWorkers(2) i += 1 except KeyboardInterrupt: print "**** Interrupted!" break except NoResultsPending: print "**** No pending results." break if main.dismissedWorkers: print "Joining all dismissed worker threads..." main.joinAllDismissedWorkers() agtl-0.8.0.3/files/agtl000077500000000000000000000001161151564747700146050ustar00rootroot00000000000000#!/usr/bin/python import advancedcaching.core advancedcaching.core.start () agtl-0.8.0.3/files/glade/000077500000000000000000000000001151564747700150065ustar00rootroot00000000000000agtl-0.8.0.3/files/glade/freerunner.glade000066400000000000000000002564311151564747700201720ustar00rootroot00000000000000 480 600 True 480 600 True True 6 True True queue True 5 3 True True 3 2 3 True alt 12 m 1 2 GTK_FILL True 1 <big><b>- m</b></big> True 1 2 1 2 GTK_FILL True 343° 2 3 1 2 GTK_FILL True True 0.40000000000000002 Satelliten: 12/12 3 GTK_FILL change 50 True True True 2 3 4 5 GTK_FILL True True True center 0 True No Target set True 1 3 3 4 True GPS False tab True 3 100 True 5 True + 50 True True True 0 - 50 True True True 1 track 50 True True True 2 actions 50 True True True 3 2 3 GTK_FILL True filter is active - reset at "cache search" GTK_FILL 1 True map view 1 False tab True True <b>Geocache</b> (none selected) True True end False 0 True False 1 True 2 4 True True <b>size</b> True 2 3 True ? 3 4 True <b>terrain</b> True 1 2 True ? 1 2 1 2 True <b>diff.</b> True 2 3 1 2 True ? 3 4 1 2 True <b>type</b> True True ? 1 2 False 2 True False 3 True True 8 True True True never automatic True queue True True 5 False word-char False Please select a geocache by clicking it on the map or double clicking it on the "cache search" results table. True desc. False tab True 0.10000000149011612 0.10000000149011612 15 True True word-char True 35 1 True hints 1 False tab True True double click a coordinate to set it as the target False False 0 True True automatic automatic 1 2 True coords 2 False tab True True True automatic automatic True queue True gtk-missing-image 0 True click "next" button to see image True False 1 50 True gtk-zoom-in True True True True 0 gtk-go-forward True True True True 1 False end 2 3 True images 3 False tab True True True automatic automatic True queue True True 0 True fieldnote date: not set False 1 True 2 2 don't post notes True True False True True post as 'found' True True False True True radiobutton_cache_log_no 1 2 post as 'not found' True True False True True radiobutton_cache_log_no 1 2 1 2 post note True True False True True radiobutton_cache_log_no 1 2 False 2 True to upload these notes, go to sync tab False 3 4 True field note 4 False tab True True True automatic automatic True queue True True 0 5 True notes 5 False tab 4 50 True True download details 50 True False True True 0 set as target 50 True False True True 1 False 5 2 30 True details 2 False tab True True 4 True 3 4 True True gtk-find suchen... True only new True True False True radio_search_found_whatever False False 0 found True True False True radio_search_found_whatever False False 1 found+new True True False True True False False 2 1 2 True trad True True False True True 0 mul True True False True True 1 ? True True False True True 2 vir True True False True 3 all True True False True 4 2 3 False 6 0 True True automatic automatic 1 True True gtk-revert-to-saved 50 True True True True 0 gtk-find 50 True True True True 1 False 2 3 True True search 3 False tab True True 0 none True 12 True True 0 you have not created any new field notes yet False 4 0 gtk-ok True True True True False 1 True False True False word False After uploading, fieldnotes can be accessed through your profile page. They are private and can be used as a template for a log entry. 2 True <b>upload field notes</b> True label_item 0 True 0 none True 12 True only visible caches True True False True False 0 only not-found caches True True False True True False 1 don't update existing descriptions True True False True False 2 no download, just create html files True True False True False 3 True 2 resize images (not working) False True False True True False True True 0 False True px True True False 6 1 1 2 False 4 gtk-ok True True True True False 5 True <b>download cache descriptions</b> True label_item 1 4 True sync 4 False tab True True automatic automatic True queue True 11 2 True 0.10000000149011612 user name 1 2 GTK_FILL True 0.10000000149011612 password 2 3 GTK_FILL GTK_FILL 150 True True 1 2 1 2 GTK_FILL 150 True True False 1 2 2 3 GTK_FILL True 0.10000000149011612 OSM map tile path (restart after changing) 4 5 GTK_FILL GTK_FILL 150 True True 1 2 4 5 GTK_FILL True 0.10000000149011612 output directory (don't change after first download) 6 7 GTK_FILL 150 True True 1 2 6 7 GTK_FILL don't download images True True False True 2 7 8 True 0.05000000074505806 <b>login data</b> True 2 GTK_FILL 10 True 0.05000000074505806 <b>map tiles</b> True 2 3 4 GTK_FILL 10 True 0.05000000074505806 <b>cache download</b> True 2 5 6 GTK_FILL 10 True 0.05000000074505806 <b>display</b> True 2 8 9 GTK_FILL 10 show cache id on map True True False True 2 9 10 GTK_FILL hide found caches on map True True False True 2 10 11 GTK_FILL 5 True opt. 5 False tab 0 True True False 1 True gtk-save True True show target True True True download geocaches True True True fetch details for visible True True True set center as target True agtl-0.8.0.3/files/glade/main.glade000066400000000000000000002740011151564747700167340ustar00rootroot00000000000000 600 500 True True vertical True True True 3 3 True both-horiz True Zoom In True zoom-in False True True Zoom Out True zoom-out False True True Geocaches Herunterladen document-save False True 2 GTK_FILL True True True vertical True True vertical button True True True none http://glade.gnome.org 0 button False True True True 1 0 True 0.10000000149011612 0.10000000149011612 Geocache mit einem doch schon ganz schön langen Namen True 25 1 False 0 True False 1 True 2 4 True True Größe 2 3 True klein 3 4 True Terrain 1 2 True 4/5 1 2 1 2 True Diff. 2 3 1 2 True 2.5/5 3 4 1 2 True Typ True multi 1 2 False 2 True False 3 True True automatic automatic True True True 0.10000000149011612 0.10000000149011612 15 Dies ist eine Beispiel Cachebeschreibung, die nur als Beispiel dient. Sie enthält auch Absätze, wie diesen. Beispiel Cachebeschreibung, die nur als Beispiel dient. Und noch Text. Hier gehts weiter. Sie enthält auch Absätze, wie diesen. Beispiel Cachebeschreibung, die nur als Beispiel dient. Und noch Text. True word-char True 30 True Kurz False True 0.10000000149011612 0.10000000149011612 15 Dies ist eine Beispiel Cachebeschreibung, die nur als Beispiel dient. Sie enthält auch Absätze, wie diesen. Beispiel Cachebeschreibung, die nur als Beispiel dient. Und noch Text. Hier gehts weiter. Sie enthält auch Absätze, wie diesen. Beispiel Cachebeschreibung, die nur als Beispiel dient. Und noch Text. True word-char True 30 1 True Lang 1 False True 0.10000000149011612 0.10000000149011612 15 Dies ist eine Beispiel Cachebeschreibung, die nur als Beispiel dient. Sie enthält auch Absätze, wie diesen. Beispiel Cachebeschreibung, die nur als Beispiel dient. Und noch Text. Hier gehts weiter. Sie enthält auch Absätze, wie diesen. Beispiel Cachebeschreibung, die nur als Beispiel dient. Und noch Text. True word-char True 30 2 True Hints 2 False True 0.10000000149011612 0.10000000149011612 15 Dies ist eine Beispiel Cachebeschreibung, die nur als Beispiel dient. Sie enthält auch Absätze, wie diesen. Beispiel Cachebeschreibung, die nur als Beispiel dient. Und noch Text. Hier gehts weiter. Sie enthält auch Absätze, wie diesen. Beispiel Cachebeschreibung, die nur als Beispiel dient. Und noch Text. True True word-char True 30 3 True Koordinaten 3 False 4 gefunden True True False True False 5 True geocaching.com-Seite True True True none http://glade.gnome.org False 0 loggen True True True none http://glade.gnome.org 1 False 6 False True True queue True True True 3 1 3 True 7 True Zoom-Stufe: 0 True True adjustment1 True 1 True Suchen: 2 True True 3 OK True True True 4 2 3 GTK_FILL True Kartenansicht False True vertical True 5 10 True True Name oder ID: 0 True True 1 5 True True Besitzer: 0 True True 1 5 10 True 0 none True 12 True vertical traditional True True False True True 0 multi True True False True True 1 unknown True True False True True 2 virtual True True False True 3 alle True True False True 4 True <b>Typ:</b> True 2 4 2 3 True 0 none True 12 True vertical micro True True False True True 0 small True True False True True 1 regular True True False True True 2 big True True False True True 3 andere True True False True True 4 True <b>Größe:</b> True 4 6 2 3 True 0 none True 12 True True True vertical adjustment2 False 0 0 bottom False 0 True True vertical adjustment3 on on 5 0 bottom False 1 True <b>Terrain:</b> True 6 8 2 3 True 0 none True 12 True True True vertical adjustment4 False 1 0 bottom False 0 True True vertical adjustment5 5 0 bottom False 1 True <b>Schwierigkeit:</b> True 8 10 2 3 True 10 1 2 True 10 3 4 True 14 True end gtk-revert-to-saved True True True True False False 0 gtk-find True True True True False False 1 10 4 5 True vertical True 0 none True vertical egal True True False True True False False 0 ja True True False True radio_search_found False False 1 nein True True False True radio_search_found False False 2 True <b>gefunden?</b> True 0 auch Caches ohne Details finden True True False True 1 2 2 3 False 6 0 True True automatic automatic 1 True 15 end Filter für Kartenansicht True True True False False 0 markierte Herunterladen True True True False False 1 False 2 1 True Filtern und Suchen 1 False True vertical Nur sichtbare Caches herunterladen True True False True 0 Nur nicht-gefundene Caches herunterladen True True False True True 1 Nur neue Cache-Beschreibungen herunterladen True True False True 2 Nichts herunterladen, nur Seiten aus Datenbank schreiben True True False True 3 HTML-Index-Seite anlegen True True False True 4 True 3 2 True Ausgabeverzeichnis True True 1 2 True True 1 2 1 2 danach ausführen: True True False True 1 2 Bilder verkleinern auf True True False True 2 3 True True True 0 True Pixel True False 6 1 1 2 2 3 5 Herunterladen starten! True True True image1 False False 6 True 0.10000000000000001 7 2 False True Cache-Download 2 False True vertical True True Position: 0 True True 1 True Kommentar: 2 True True 3 False 0 True 16 True end gtk-new True True True True False False 0 gtk-save True True True True bottom False False 1 False 1 True True automatic automatic 2 3 True Eigene Wegpunkte 3 False True vertical True 0 none True 12 True vertical Name neben Cache-Symbol anzeigen True True False True 0 True <b>Darstellung</b> True False 0 True 0 none True 12 True 2 2 True 0 Benutzername True 0 Passwort 1 2 GTK_FILL True True 1 2 True True False 1 2 1 2 True <b>Zugangsdaten</b> True False 1 4 False True Optionen 4 False 0 True 2 False 1 1 1 18 1 1 1 1 1 6 1 1 1 5 1 6 1 1 1 1 1 6 1 1 1 1 1 6 1 1 1 True gtk-save True gtk-apply agtl-0.8.0.3/files/glade/options.glade000066400000000000000000000240711151564747700175030ustar00rootroot00000000000000 5 popup True center-on-parent True applications-system dialog False True vertical 2 True 0 in True 3 4 True Verzeichnis für heruntergeladene Geoaches: 4 True True 4 1 2 heruntergeladene Bilder verkleinern auf True True False True 2 2 3 True True 4 2 3 2 3 GTK_FILL True Pixel 3 4 2 3 True <b>Optionen zum Herunterladen</b> True 1 True 0 in True 12 True 2 2 True Benutzername True Passwort 1 2 True True 1 2 True True True 1 2 1 2 True <b>Zugangsdaten</b> True 2 True end gtk-cancel True True True True False False 0 gtk-apply True True True True False False 1 False end 0 button2 button1 agtl-0.8.0.3/files/glade/simple.glade000066400000000000000000002740011151564747700173010ustar00rootroot00000000000000 600 500 True True vertical True True True 3 3 True both-horiz True Zoom In True zoom-in False True True Zoom Out True zoom-out False True True Geocaches Herunterladen document-save False True 2 GTK_FILL True True True vertical True True vertical button True True True none http://glade.gnome.org 0 button False True True True 1 0 True 0.10000000149011612 0.10000000149011612 Geocache mit einem doch schon ganz schön langen Namen True 25 1 False 0 True False 1 True 2 4 True True Größe 2 3 True klein 3 4 True Terrain 1 2 True 4/5 1 2 1 2 True Diff. 2 3 1 2 True 2.5/5 3 4 1 2 True Typ True multi 1 2 False 2 True False 3 True True automatic automatic True True True 0.10000000149011612 0.10000000149011612 15 Dies ist eine Beispiel Cachebeschreibung, die nur als Beispiel dient. Sie enthält auch Absätze, wie diesen. Beispiel Cachebeschreibung, die nur als Beispiel dient. Und noch Text. Hier gehts weiter. Sie enthält auch Absätze, wie diesen. Beispiel Cachebeschreibung, die nur als Beispiel dient. Und noch Text. True word-char True 30 True Kurz False True 0.10000000149011612 0.10000000149011612 15 Dies ist eine Beispiel Cachebeschreibung, die nur als Beispiel dient. Sie enthält auch Absätze, wie diesen. Beispiel Cachebeschreibung, die nur als Beispiel dient. Und noch Text. Hier gehts weiter. Sie enthält auch Absätze, wie diesen. Beispiel Cachebeschreibung, die nur als Beispiel dient. Und noch Text. True word-char True 30 1 True Lang 1 False True 0.10000000149011612 0.10000000149011612 15 Dies ist eine Beispiel Cachebeschreibung, die nur als Beispiel dient. Sie enthält auch Absätze, wie diesen. Beispiel Cachebeschreibung, die nur als Beispiel dient. Und noch Text. Hier gehts weiter. Sie enthält auch Absätze, wie diesen. Beispiel Cachebeschreibung, die nur als Beispiel dient. Und noch Text. True word-char True 30 2 True Hints 2 False True 0.10000000149011612 0.10000000149011612 15 Dies ist eine Beispiel Cachebeschreibung, die nur als Beispiel dient. Sie enthält auch Absätze, wie diesen. Beispiel Cachebeschreibung, die nur als Beispiel dient. Und noch Text. Hier gehts weiter. Sie enthält auch Absätze, wie diesen. Beispiel Cachebeschreibung, die nur als Beispiel dient. Und noch Text. True True word-char True 30 3 True Koordinaten 3 False 4 gefunden True True False True False 5 True geocaching.com-Seite True True True none http://glade.gnome.org False 0 loggen True True True none http://glade.gnome.org 1 False 6 False True True queue True True True 3 1 3 True 7 True Zoom-Stufe: 0 True True adjustment1 True 1 True Suchen: 2 True True 3 OK True True True 4 2 3 GTK_FILL True Kartenansicht False True vertical True 5 10 True True Name oder ID: 0 True True 1 5 True True Besitzer: 0 True True 1 5 10 True 0 none True 12 True vertical traditional True True False True True 0 multi True True False True True 1 unknown True True False True True 2 virtual True True False True 3 alle True True False True 4 True <b>Typ:</b> True 2 4 2 3 True 0 none True 12 True vertical micro True True False True True 0 small True True False True True 1 regular True True False True True 2 big True True False True True 3 andere True True False True True 4 True <b>Größe:</b> True 4 6 2 3 True 0 none True 12 True True True vertical adjustment2 False 0 0 bottom False 0 True True vertical adjustment3 on on 5 0 bottom False 1 True <b>Terrain:</b> True 6 8 2 3 True 0 none True 12 True True True vertical adjustment4 False 1 0 bottom False 0 True True vertical adjustment5 5 0 bottom False 1 True <b>Schwierigkeit:</b> True 8 10 2 3 True 10 1 2 True 10 3 4 True 14 True end gtk-revert-to-saved True True True True False False 0 gtk-find True True True True False False 1 10 4 5 True vertical True 0 none True vertical egal True True False True True False False 0 ja True True False True radio_search_found False False 1 nein True True False True radio_search_found False False 2 True <b>gefunden?</b> True 0 auch Caches ohne Details finden True True False True 1 2 2 3 False 6 0 True True automatic automatic 1 True 15 end Filter für Kartenansicht True True True False False 0 markierte Herunterladen True True True False False 1 False 2 1 True Filtern und Suchen 1 False True vertical Nur sichtbare Caches herunterladen True True False True 0 Nur nicht-gefundene Caches herunterladen True True False True True 1 Nur neue Cache-Beschreibungen herunterladen True True False True 2 Nichts herunterladen, nur Seiten aus Datenbank schreiben True True False True 3 HTML-Index-Seite anlegen True True False True 4 True 3 2 True Ausgabeverzeichnis True True 1 2 True True 1 2 1 2 danach ausführen: True True False True 1 2 Bilder verkleinern auf True True False True 2 3 True True True 0 True Pixel True False 6 1 1 2 2 3 5 Herunterladen starten! True True True image1 False False 6 True 0.10000000000000001 7 2 False True Cache-Download 2 False True vertical True True Position: 0 True True 1 True Kommentar: 2 True True 3 False 0 True 16 True end gtk-new True True True True False False 0 gtk-save True True True True bottom False False 1 False 1 True True automatic automatic 2 3 True Eigene Wegpunkte 3 False True vertical True 0 none True 12 True vertical Name neben Cache-Symbol anzeigen True True False True 0 True <b>Darstellung</b> True False 0 True 0 none True 12 True 2 2 True 0 Benutzername True 0 Passwort 1 2 GTK_FILL True True 1 2 True True False 1 2 1 2 True <b>Zugangsdaten</b> True False 1 4 False True Optionen 4 False 0 True 2 False 1 1 1 18 1 1 1 1 1 6 1 1 1 5 1 6 1 1 1 1 1 6 1 1 1 1 1 6 1 1 1 True gtk-save True gtk-apply agtl-0.8.0.3/files/qt/000077500000000000000000000000001151564747700143565ustar00rootroot00000000000000agtl-0.8.0.3/files/qt/GeocacheDetailsWindow.ui000066400000000000000000000333651151564747700211230ustar00rootroot00000000000000 GeocacheDetailsWindow 0 0 882 492 MainWindow 4 Information 75 true Full Name 75 true ID 75 true Type 75 true Size 75 true Terrain 75 true Difficulty 75 true Owner 75 true Status Description true 0 0 852 399 Description Qt::RichText Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop true Logs && Hints true 0 0 852 371 Logs Qt::RichText Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop true Show Hint Coordinate Calculation 0 0 Coordinates 3 5 Coordinates Source Name Description 1 2 Details 0 0 TextLabel Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop 0 1 Variables Wert A 12 Images QListView::IconMode Notes 0 0 882 21 Geocache Set as Target Write Fieldnote Download Details agtl-0.8.0.3/files/qt/MainWindow.ui000066400000000000000000000131031151564747700167670ustar00rootroot00000000000000 MainWindow 0 0 767 392 0 0 MainWindow false false 0 0 0 0 767 21 Map Search Help About &AGTL Tools toolBar BottomToolBarArea false Hallo Welt! Tschüss Welt! Select Map Style Search Geocaches Test Download Map Tiles true blub bla true Blub 1 true Blub 2 Search Place Ctrl+Shift+P :/icons/advancedcaching/data/emoticon_grin.png:/icons/advancedcaching/data/emoticon_grin.png Update Geocache Locations :/icons/advancedcaching/data/comment.png:/icons/advancedcaching/data/comment.png Download Details for visible Geocaches Options Export all visible Geocaches agtl-0.8.0.3/files/qt/OptionsDialog.ui000066400000000000000000000130201151564747700174640ustar00rootroot00000000000000 OptionsDialog 0 0 412 384 Options Login Data Username Password Qt::ImhNone QLineEdit::PasswordEchoOnEdit Please make sure that the website language ist set to english. Otherwise, downloading of geocaches won't work. false true Map Display Show Geocache Names on Map Show Map in double size (ugly!) Hide Found Geocaches Text-to-speech settings Enable Voice Guidance Off Automatic 10 Seconds 20 Seconds 30 Seconds 50 Seconds 100 Seconds 3 Minutes 5 Minutes 10 Minutes Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() OptionsDialog accept() 248 254 157 274 buttonBox rejected() OptionsDialog reject() 316 260 286 274 agtl-0.8.0.3/files/qt/SearchDialog.ui000066400000000000000000000016001151564747700172370ustar00rootroot00000000000000 SearchDialog 0 0 400 300 Search Place Search agtl-0.8.0.3/files/qt/SearchGeocachesDialog.ui000066400000000000000000000272661151564747700210610ustar00rootroot00000000000000 SearchGeocachesDialog 0 0 874 481 Search Geocaches Name QFrame::NoFrame Geocache Name Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok Details (2) QFormLayout::ExpandingFieldsGrow If you change something here, only geocaches for which details have been downloaded will be shown in the result. true Size micro Checked small Checked regular Checked huge Checked other Checked Difficulty min. QAbstractSpinBox::UpDownArrows QAbstractSpinBox::CorrectToNearestValue 1 1.000000000000000 5.000000000000000 0.500000000000000 max. 1 1.000000000000000 5.000000000000000 0.500000000000000 5.000000000000000 Terrain min. 1 1.000000000000000 5.000000000000000 0.500000000000000 max. 1 1.000000000000000 5.000000000000000 0.500000000000000 5.000000000000000 Location Location anywhere around the current map center around my position Radius false km 1 100 2 Details (1) QFormLayout::ExpandingFieldsGrow Type(s) Filter hide found geocaches true only marked geocaches dialogButtonBox accepted() SearchGeocachesDialog accept() 248 254 157 274 dialogButtonBox rejected() SearchGeocachesDialog reject() 316 260 286 274 agtl-0.8.0.3/files/qt/SearchResultsDialog.ui000066400000000000000000000075511151564747700206340ustar00rootroot00000000000000 SearchResultsDialog 0 0 613 605 Search Results 0 0 QAbstractItemView::NoEditTriggers true QAbstractItemView::ExtendedSelection QAbstractItemView::SelectRows false true true true false true 40 true false false false false false true false false true 40 false true false Geocache Size T D Distance Export selected... agtl-0.8.0.3/files/qt/ShowImageDialog.ui000066400000000000000000000035041151564747700177220ustar00rootroot00000000000000 ShowImageDialog 0 0 384 337 0 0 Image true 0 0 370 323 0 0 0 0 Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop agtl-0.8.0.3/files/qt/icons.qrc000066400000000000000000000011251151564747700161770ustar00rootroot00000000000000 ../advancedcaching/data/calendar_edit.png ../advancedcaching/data/comment.png ../advancedcaching/data/cross.png ../advancedcaching/data/cross.png ../advancedcaching/data/delete.png ../advancedcaching/data/emoticon_grin.png ../advancedcaching/data/error.png ../advancedcaching/data/group.png ../advancedcaching/data/asterisk_yellow.png ../advancedcaching/data/accept.png agtl-0.8.0.3/files/setup.py000066400000000000000000000057751151564747700154620ustar00rootroot00000000000000from distutils.core import setup from setuptools import setup files = ['data/*', 'actors/*.py', 'qt/*.py'] setup(name='agtl', version='0.8.0.3-freerunner0', description='Towards paperless geocaching', author='Daniel Fett', author_email='agtl@fragcom.de', url='http://wiki.openmoko.org/wiki/Advanced_Geocaching_Tool_for_Linux', classifiers=[ 'Development Status :: 4 - Beta', 'Environment :: X11 Applications', 'Intended Audience :: End Users/Desktop', 'License :: OSI Approved :: GNU General Public License (GPL)', 'Programming Language :: Python' ], #Name the folder where your packages live: #(If you have other packages (dirs) or modules (py files) then #put them into the package directory - they will be found #recursively.) packages = ['advancedcaching'], #'package' package must contain files (see list above) #I called the package 'package' thus cleverly confusing the whole issue... #This dict maps the package name =to=> directories #It says, package *needs* these files. package_data = {'advancedcaching' : files }, #'runner' is in the root. scripts = ["agtl"], long_description = """AGTL, the all-in-one solution for on- and offline geocaching, makes geocaching paperless! It downloads geocaches including their description, hints, difficulty levels and images. No premium account needed. Searching for caches in your local db is a matter of seconds. . - Map view - supporting Open Street Maps and Open Cycle Maps by default, configurable for other map types, including google maps. - GPS view - shows the distance and direction to the selected geocache. - Cache details - all necessary details are available even in offline mode. - Paperless geocaching features - take notes for a geocache on the go, see the hints and spoiler images, check the latest logs. - Multicache calculation help - Let your phone do the math for you. Working for the most multi-stage geocaches, AGTL finds the coordinate calculations and let you enter the missing variables. (N900 only) - Fieldnotes support - Ever came home after a long tour and asked yourself which geocaches you found? Never again: Log your find in the field and upload notes and log text when you're at home. Review them on the geocaching website and post the logs. - Text-to-Speech-Feature! - Select a target, activate TTS and put your headphones on to enjoy completely stealth geocaching. (N900 only) - Download map tiles for selected zoom levels - for offline use. - Advanced waypoint handling - AGTL finds waypoints in the geocache descriptions, in the list of waypoints and even in your notes. For your convenience, they're displayed on the map as well - see where you have to go next. - Search for places - in the geonames.org database to navigate quickly. (N900 only) - Sun compass - Compensates the lack of a magnetic compass. (N900 only) - Instant update feature - Follow web site updates as soon as possible. . AGTL is Open source and in active development.""" # #This next part it for the Cheese Shop, look a little down the page. #classifiers = [] ) agtl-0.8.0.3/ipkg-utils-1.7/000077500000000000000000000000001151564747700152235ustar00rootroot00000000000000agtl-0.8.0.3/ipkg-utils-1.7/.#ipkg-make-index.1.12000077500000000000000000000061321151564747700206260ustar00rootroot00000000000000#!/usr/bin/python # $id: $ import sys, os, posixpath from glob import glob import commands import ipkg import getopt verbose = 0 def usage(): sys.stderr.write("%s [-h] [-s] [-l Packages.filelist] [-p Packages] [-v] packagesdir\n" % (sys.argv[0],)) sys.exit(-1) def to_morgue(filename): if verbose: sys.stderr.write ("Moving " + filename + " to morgue") command("mv", pkg_dir + "/" + filename, pkg_dir + "/morgue") packages_filename = None filelist_filename = "Packages.filelist" opt_s = 0 opt_m = 0 (opts, remaining_args) = getopt.getopt(sys.argv[1:], "hl:p:vs") for (optkey, optval) in opts: if optkey == '-h': usage() if optkey == '-s': opt_s = 1 if optkey == '-p': packages_filename = optval if optkey == '-l': filelist_filename = optval if optkey == '-v': verbose = 1 if optkey == '-m': opt_m = 1 pkg_dir=remaining_args[0] if ( not pkg_dir ): sys.stderr.write("Usage: ipkg-make-index \n") sys.exit(1) packages = {} if (verbose): sys.stderr.write("Reading in all the package info from %s\n" % (pkg_dir, )) files=glob(pkg_dir + '/*.ipk') files.sort() for filename in files: if (verbose): sys.stderr.write("Reading info for package %s\n" % (filename,)) pkg = ipkg.Package(filename) name = pkg.package if (not packages.has_key(name)): packages[name] = pkg (s, outtext) = commands.getstatusoutput("ipkg-compare-versions %s '>' %s" % (pkg.version, packages[name].version)) if s == 0: if opt_m: to_morgue(packages[name]) packages[name] = pkg else: if opt_m: to_morgue(filename) if opt_s: print filename if opt_s: sys.exit(0) if verbose: sys.stderr.write("Generating Packages file\n") if packages_filename: old_stdout = sys.stdout tmp_packages_filename = ("%s.%d" % (packages_filename, os.getpid())) sys.stdout = open(tmp_packages_filename, "w") names = packages.keys() names.sort() for name in names: if (verbose): sys.stderr.write("Writing info for package %s\n" % (name,)) print packages[name] if packages_filename: sys.stdout.close() os.rename(tmp_packages_filename, packages_filename) sys.stdout = old_stdout if verbose: sys.stderr.write("Generate Packages.filelist file\n") files = {} names = packages.keys() names.sort() for name in names: for fn in packages[name].get_file_list(): (h,t) = os.path.split(fn) if not t: continue if not files.has_key(t): files[t] = name+':'+fn else: files[t] = files[t] + ',' + name+':'+fn if filelist_filename: tmp_filelist_filename = ("%s.%d" % (filelist_filename, os.getpid())) sys.stdout = open(tmp_filelist_filename, "w") names = files.keys() names.sort() for name in names: print name,files[name] sys.stdout.close() if posixpath.exists(filelist_filename): os.unlink(filelist_filename) os.rename(tmp_filelist_filename, filelist_filename) agtl-0.8.0.3/ipkg-utils-1.7/CONTROL/000077500000000000000000000000001151564747700163435ustar00rootroot00000000000000agtl-0.8.0.3/ipkg-utils-1.7/CONTROL/CVS/000077500000000000000000000000001151564747700167765ustar00rootroot00000000000000agtl-0.8.0.3/ipkg-utils-1.7/CONTROL/CVS/Entries000066400000000000000000000000521151564747700203270ustar00rootroot00000000000000/control/1.1/Sun Jul 29 18:23:10 2001// D agtl-0.8.0.3/ipkg-utils-1.7/CONTROL/CVS/Repository000066400000000000000000000000231151564747700210730ustar00rootroot00000000000000ipkg-utils/CONTROL agtl-0.8.0.3/ipkg-utils-1.7/CONTROL/CVS/Root000066400000000000000000000000301151564747700176350ustar00rootroot00000000000000:ext:handhelds.org:/cvs agtl-0.8.0.3/ipkg-utils-1.7/CONTROL/control000066400000000000000000000003441151564747700177470ustar00rootroot00000000000000Package: ipkg-build Section: base Priority: optional Version: 1.6 Architecture: all Maintainer: Carl Worth Depends: sed, grep, gzip, tar Description: Script for building .ipk packages for the ipkg system. agtl-0.8.0.3/ipkg-utils-1.7/Makefile000066400000000000000000000013651151564747700166700ustar00rootroot00000000000000UTILS = ipkg-build ipkg-deb-unbuild ipkg-unbuild ipkg-compare-versions ipkg-upload PREFIX=/usr/local all: build build: ipkg-compare-versions python setup.py build ipkg-compare-versions: ipkg-compare-versions.c $(CC) $(CFLAGS) -o ipkg-compare-versions ipkg-compare-versions.c install: ${UTILS} cp ${UTILS} $(PREFIX)/bin python setup.py install chmod agu+rx ipkg-make-index cp -f ipkg-make-index $(PREFIX)/bin binary: build mkdir -p ipkg-build-binary/usr/bin cp ipkg-build ipkg-build-binary/usr/bin mkdir -p ipkg-build-binary/CONTROL cp CONTROL/control ipkg-build-binary/CONTROL chown -R root:root ipkg-build-binary ipkg-build ipkg-build-binary . rm -rf ipkg-build-binary clean: rm -rf ipkg-build-binary ipkg-compare-versions *.pyc build agtl-0.8.0.3/ipkg-utils-1.7/Makefile.python000066400000000000000000000001771151564747700202100ustar00rootroot00000000000000 install: python setup.py build python setup.py install chmod agu+rx ipkg-make-index cp -f ipkg-make-index /usr/local/bin agtl-0.8.0.3/ipkg-utils-1.7/build/000077500000000000000000000000001151564747700163225ustar00rootroot00000000000000agtl-0.8.0.3/ipkg-utils-1.7/build/lib.linux-i686-2.6/000077500000000000000000000000001151564747700212235ustar00rootroot00000000000000agtl-0.8.0.3/ipkg-utils-1.7/build/lib.linux-i686-2.6/ipkg.py000066400000000000000000000271731151564747700225410ustar00rootroot00000000000000#!/usr/bin/env python # Copyright (C) 2001 Alexander S. Guy # Andern Research Labs # # 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, 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., 59 Temple Place - Suite 330, # Boston, MA 02111-1307, USA. */ # # Copyright 2001, Russell Nelson # Added reading in of packages. # Added missing package information fields. # Changed render_control() to __repr__(). # # Current Issues: # The API doesn't validate package information fields. It should be # throwing exceptions in the right places. # Executions of tar could silently fail. # Executions of tar *do* fail, and loudly, because you have to specify a full filename, # and tar complains if any files are missing, and the ipkg spec doesn't require # people to say "./control.tar.gz" or "./control" when they package files. # It would be much better to require ./control or disallow ./control (either) # rather than letting people pick. Some freedoms aren't worth their cost. import tempfile import os import sys import glob import md5 import re import string import commands from stat import ST_SIZE class Package: """A class for creating objects to manipulate (e.g. create) ipkg packages.""" def __init__(self, fn=None): self.package = None self.version = None self.architecture = None self.maintainer = None self.source = None self.description = None self.depends = None self.provides = None self.replaces = None self.conflicts = None self.recommends = None self.suggests = None self.section = None self.filename_header = None self.file_list = [] self.md5 = None self.size = None self.installed_size = None self.filename = None self.isdeb = 0 if fn: # see if it is deb format f = open(fn, "r") magic = f.read(4) f.close() if (magic == "! '2': # when using Python 2.0 or newer self.md5 = sum.hexdigest() else: self.md5 = string.join(map((lambda x:"%02x" % ord(x)),sum.digest()),'') stat = os.stat(fn) self.size = stat[ST_SIZE] self.filename = os.path.basename(fn) ## sys.stderr.write(" extracting control.tar.gz from %s\n"% (fn,)) if self.isdeb: control = os.popen("ar p "+fn+" control.tar.gz | tar xfzO - '*control'","r") else: control = os.popen("tar xfzO "+fn+" '*control.tar.gz' | tar xfzO - '*control'","r") line = control.readline() while 1: if not line: break line = string.rstrip(line) lineparts = re.match(r'([\w-]*?):\s*(.*)', line) if lineparts: name = string.lower(lineparts.group(1)) value = lineparts.group(2) while 1: line = control.readline() if not line: break if line[0] != ' ': break line = string.rstrip(line) value = value + '\n' + line # don't allow package to override its own filename if name == "filename": self.filename_header = value else: if self.__dict__.has_key(name): self.__dict__[name] = value else: line = control.readline() control.close() if self.isdeb: data = os.popen("ar p "+fn+" data.tar.gz | tar tfz -","r") else: data = os.popen("tar xfzO "+fn+" '*data.tar.gz' | tar tfz -","r") while 1: line = data.readline() if not line: break self.file_list.append(string.rstrip(line)) data.close() self.scratch_dir = None self.file_dir = None self.meta_dir = None def read_control(self, control): import os line = control.readline() while 1: if not line: break line = string.rstrip(line) lineparts = re.match(r'([\w-]*?):\s*(.*)', line) if lineparts: name = string.lower(lineparts.group(1)) value = lineparts.group(2) while 1: line = control.readline() if not line: break if line[0] != ' ': break value = value + '\n' + line if name == 'size': self.size = int(value) elif self.__dict__.has_key(name): self.__dict__[name] = value if line[0] == '\n': return # consumes one blank line at end of package descriptoin else: line = control.readline() pass return def _setup_scratch_area(self): self.scratch_dir = "%s/%sipkg" % (tempfile.gettempdir(), tempfile.gettempprefix()) self.file_dir = "%s/files" % (self.scratch_dir) self.meta_dir = "%s/meta" % (self.scratch_dir) os.mkdir(self.scratch_dir) os.mkdir(self.file_dir) os.mkdir(self.meta_dir) def set_package(self, package): self.package = package def get_package(self): return self.package def set_version(self, version): self.version = version def get_version(self): return self.version def set_architecture(self, architecture): self.architecture = architecture def get_architecture(self): return self.architecture def set_maintainer(self, maintainer): self.maintainer = maintainer def get_maintainer(self): return self.maintainer def set_source(self, source): self.source = source def get_source(self): return self.source def set_description(self, description): self.description = description def get_description(self): return self.description def set_depends(self, depends): self.depends = depends def get_depends(self, depends): return self.depends def set_provides(self, provides): self.provides = provides def get_provides(self, provides): return self.provides def set_replaces(self, replaces): self.replaces = replaces def get_replaces(self, replaces): return self.replaces def set_conflicts(self, conflicts): self.conflicts = conflicts def get_conflicts(self, conflicts): return self.conflicts def set_suggests(self, suggests): self.suggests = suggests def get_suggests(self, suggests): return self.suggests def set_section(self, section): self.section = section def get_section(self, section): return self.section def get_file_list(self): return self.file_list def write_package(self, dirname): buf = self.render_control() file = open("%s/control" % self.meta_dir, 'w') file.write(buf) self._setup_scratch_area() cmd = "cd %s ; tar cvfz %s/control.tar.gz control" % (self.meta_dir, self.scratch_dir) cmd_out, cmd_in, cmd_err = os.popen3(cmd) while cmd_err.readline() != "": pass cmd_out.close() cmd_in.close() cmd_err.close() bits = "control.tar.gz" if self.file_list: cmd = "cd %s ; tar cvfz %s/data.tar.gz" % (self.file_dir, self.scratch_dir) cmd_out, cmd_in, cmd_err = os.popen3(cmd) while cmd_err.readline() != "": pass cmd_out.close() cmd_in.close() cmd_err.close() bits = bits + " data.tar.gz" file = "%s_%s_%s.ipk" % (self.package, self.version, self.architecture) cmd = "cd %s ; tar cvfz %s/%s %s" % (self.scratch_dir, dirname, file, bits) cmd_out, cmd_in, cmd_err = os.popen3(cmd) while cmd_err.readline() != "": pass cmd_out.close() cmd_in.close() cmd_err.close() def __repr__(self): out = "" # XXX - Some checks need to be made, and some exceptions # need to be thrown. -- a7r if self.package: out = out + "Package: %s\n" % (self.package) if self.version: out = out + "Version: %s\n" % (self.version) if self.depends: out = out + "Depends: %s\n" % (self.depends) if self.provides: out = out + "Provides: %s\n" % (self.provides) if self.replaces: out = out + "Replaces: %s\n" % (self.replaces) if self.conflicts: out = out + "Conflicts: %s\n" % (self.conflicts) if self.suggests: out = out + "Suggests: %s\n" % (self.suggests) if self.recommends: out = out + "Recommends: %s\n" % (self.recommends) if self.section: out = out + "Section: %s\n" % (self.section) if self.architecture: out = out + "Architecture: %s\n" % (self.architecture) if self.maintainer: out = out + "Maintainer: %s\n" % (self.maintainer) if self.md5: out = out + "MD5Sum: %s\n" % (self.md5) if self.size: out = out + "Size: %d\n" % int(self.size) if self.installed_size: out = out + "InstalledSize: %d\n" % int(self.installed_size) if self.filename: out = out + "Filename: %s\n" % (self.filename) if self.source: out = out + "Source: %s\n" % (self.source) if self.description: out = out + "Description: %s\n" % (self.description) out = out + "\n" return out def __del__(self): # XXX - Why is the `os' module being yanked out before Package objects # are being destroyed? -- a7r pass class Packages: """A currently unimplemented wrapper around the ipkg utility.""" def __init__(self): self.packages = {} return def add_package(self, pkg): package = pkg.package arch = pkg.architecture name = ("%s:%s" % (package, arch)) if (not self.packages.has_key(name)): self.packages[name] = pkg (s, outtext) = commands.getstatusoutput("ipkg-compare-versions %s '>' %s" % (pkg.version, self.packages[name].version)) if (s == 0): self.packages[name] = pkg return 0 else: return 1 def read_packages_file(self, fn): f = open(fn, "r") while 1: pkg = Package() pkg.read_control(f) if pkg.get_package(): self.add_package(pkg) else: break f.close() return def write_packages_file(self, fn): f = open(fn, "w") names = self.packages.keys() names.sort() for name in names: f.write(self.packages[name].__repr__()) return def keys(self): return self.packages.keys() def __getitem__(self, key): return self.packages[key] if __name__ == "__main__": package = Package() package.set_package("FooBar") package.set_version("0.1-fam1") package.set_architecture("arm") package.set_maintainer("Testing ") package.set_depends("libc") package.set_description("A test of the APIs.") print "<" sys.stdout.write(package) print ">" package.write_package("/tmp") agtl-0.8.0.3/ipkg-utils-1.7/build/scripts-2.6/000077500000000000000000000000001151564747700203145ustar00rootroot00000000000000agtl-0.8.0.3/ipkg-utils-1.7/build/scripts-2.6/ipkg-compare-indexes000077500000000000000000000026331151564747700242610ustar00rootroot00000000000000#!/usr/bin/python import sys, os from glob import glob import commands import ipkg pkg_dir1 = sys.argv[1] pkg_dir2 = sys.argv[2] if ( not pkg_dir1 or not pkg_dir2 ): sys.stderr.write("Usage: ipkg-update-index \n") sys.exit(1) pkgs1 = ipkg.Packages() pkgs1.read_packages_file(pkg_dir1 + '/Packages') pkgs2 = ipkg.Packages() pkgs2.read_packages_file(pkg_dir2 + '/Packages') names1 = pkgs1.packages.keys() names2 = pkgs2.packages.keys() ## union of the two names lists pkgs = {} for name in names1: pkgs[name] = pkgs1.packages[name] for name in names2: pkgs[name] = pkgs2.packages[name] names = pkgs.keys() names.sort() for name in names: pkg1 = None pkg2 = None if pkgs1.packages.has_key(name): pkg1 = pkgs1.packages[name] if pkgs2.packages.has_key(name): pkg2 = pkgs2.packages[name] if pkg1 and pkg2 and pkg1.version != pkg2.version: print "CHANGED: %s from version %s to %s (%s)" % (pkg1.package, pkg1.version, pkg2.version, pkg2.maintainer) cmd = "ipkg-diff %s %s > %s " % ((pkg_dir1 + pkg1.filename), (pkg_dir2 + pkg2.filename), (pkg1.package + '.diff')) print cmd commands.getstatusoutput(cmd) if not pkg1: print "NEW: %s version %s (%s)"% (pkg2.package, pkg2.version, pkg2.maintainer) if not pkg2: print "DELETE: %s version %s (%s)"% (pkg1.package, pkg1.version, pkg1.maintainer) agtl-0.8.0.3/ipkg-utils-1.7/build/scripts-2.6/ipkg-make-index000077500000000000000000000113021151564747700232110ustar00rootroot00000000000000#!/usr/bin/python # $Id: ipkg-make-index,v 1.20 2003/10/30 02:32:09 jamey Exp $ import sys, os, posixpath from glob import glob import commands import ipkg import getopt import string verbose = 0 def usage(): sys.stderr.write("%s [-h] [-s] [-m] [-l Packages.filelist] [-p Packages] [-r Packages.old] [-v] packagesdir\n" % (sys.argv[0],)) sys.exit(-1) def to_morgue(filename): morgue_dir = pkg_dir + "/morgue" if verbose: sys.stderr.write ("Moving " + filename + " to morgue\n") if not os.path.exists(morgue_dir): os.mkdir(morgue_dir) os.rename(pkg_dir + "/" + filename, morgue_dir + "/" + filename) if os.path.exists(pkg_dir + "/" + filename + ".asc"): os.rename(pkg_dir + "/" + filename + ".asc", morgue_dir + "/" + filename + ".asc") old_filename = None packages_filename = None filelist_filename = "Packages.filelist" opt_s = 0 opt_m = 0 (opts, remaining_args) = getopt.getopt(sys.argv[1:], "hl:p:vsmr:") for (optkey, optval) in opts: if optkey == '-h': usage() if optkey == '-s': opt_s = 1 if optkey == '-p': packages_filename = optval if optkey == '-l': filelist_filename = optval if optkey == '-v': verbose = 1 if optkey == '-m': opt_m = 1 if optkey == '-r': old_filename = optval pkg_dir=remaining_args[0] if ( not pkg_dir ): usage() packages = ipkg.Packages() old_pkg_hash = {} if packages_filename and not old_filename and os.path.exists(packages_filename): old_filename = packages_filename if old_filename: if (verbose): sys.stderr.write("Reading package list from " + old_filename + "\n") old_packages = ipkg.Packages() old_packages.read_packages_file(old_filename) for k in old_packages.packages.keys(): p = old_packages.packages[k] old_pkg_hash[p.filename] = p if (verbose): sys.stderr.write("Reading in all the package info from %s\n" % (pkg_dir, )) files=glob(pkg_dir + '/*.ipk') + glob(pkg_dir + '/*.deb') files.sort() for filename in files: basename = os.path.basename(filename) if old_pkg_hash.has_key(basename): if (verbose): sys.stderr.write("Found %s in Packages\n" % (filename,)) pkg = old_pkg_hash[basename] else: if (verbose): sys.stderr.write("Reading info for package %s\n" % (filename,)) pkg = ipkg.Package(filename) pkg_key = ("%s:%s" % (pkg.package, pkg.architecture)) if (packages.packages.has_key(pkg_key)): old_filename = packages.packages[pkg_key].filename else: old_filename = "" s = packages.add_package(pkg) if s == 0: if old_filename: # old package was displaced by newer if opt_m: to_morgue(old_filename) if opt_s: print pkg_dir + "/" + old_filename else: if opt_m: to_morgue(basename) if opt_s: print filename if opt_s: sys.exit(0) if verbose: sys.stderr.write("Generating Packages file\n") if packages_filename: old_stdout = sys.stdout tmp_packages_filename = ("%s.%d" % (packages_filename, os.getpid())) sys.stdout = open(tmp_packages_filename, "w") names = packages.packages.keys() names.sort() for name in names: pkg = packages.packages[name] if (verbose): sys.stderr.write("Writing info for package %s\n" % (pkg.package,)) print pkg if packages_filename: sys.stdout.close() sys.stdout = old_stdout gzip_filename = ("%s.gz" % packages_filename) tmp_gzip_filename = ("%s.%d" % (gzip_filename, os.getpid())) gzip_cmd = "gzip -9c < %s > %s" % (tmp_packages_filename, tmp_gzip_filename) (rc, outtext) = commands.getstatusoutput(gzip_cmd) print outtext os.rename(tmp_packages_filename, packages_filename) os.rename(tmp_gzip_filename, gzip_filename) if verbose: sys.stderr.write("Generate Packages.filelist file\n") files = {} names = packages.packages.keys() names.sort() for name in names: for fn in packages[name].get_file_list(): (h,t) = os.path.split(fn) if not t: continue if not files.has_key(t): files[t] = name+':'+fn else: files[t] = files[t] + ',' + name+':'+fn if filelist_filename: tmp_filelist_filename = ("%s.%d" % (filelist_filename, os.getpid())) sys.stdout = open(tmp_filelist_filename, "w") names = files.keys() names.sort() for name in names: print name,files[name] sys.stdout.close() if posixpath.exists(filelist_filename): os.unlink(filelist_filename) os.rename(tmp_filelist_filename, filelist_filename) agtl-0.8.0.3/ipkg-utils-1.7/build/scripts-2.6/ipkg-update-index000077500000000000000000000010451151564747700235610ustar00rootroot00000000000000#!/usr/bin/python import sys, os from glob import glob import commands import ipkg pkg_dir=sys.argv[1] pkg_filename = sys.argv[2] if ( not pkg_dir or not pkg_filename ): sys.stderr.write("Usage: ipkg-update-index \n") sys.exit(1) packages = ipkg.Packages() packages.read_packages_file(pkg_dir + '/Packages') names = packages.packages.keys() packages.add_package(ipkg.Package(pkg_filename)) packages.write_packages_file(pkg_dir + '/Packages.new') os.rename(pkg_dir + '/Packages.new', pkg_dir + '/Packages') agtl-0.8.0.3/ipkg-utils-1.7/ipkg-accept-incoming000066400000000000000000000005361151564747700211420ustar00rootroot00000000000000#!/bin/sh set -e INCOMING="/home/familiar/incoming" OUTGOING="/home/familiar/website/data/familiar/releases/unstable/binary/armv4l/" pkgs=`find -maxdepth 1 $INCOMING -name '*.ipk'` if [ -n "$pkgs" ]; then for pkg in $pkgs; do (cd $OUTGOING/unpacked; ipkg-unbuild $pkg) mv $pkg $OUTGOING done cd $OUTGOING ipkg-make-index . > Packages fi agtl-0.8.0.3/ipkg-utils-1.7/ipkg-build000077500000000000000000000141101151564747700171750ustar00rootroot00000000000000#!/bin/sh # ipkg-build -- construct a .ipk from a directory # Carl Worth # based on a script by Steve Redler IV, steve@sr-tech.com 5-21-2001 # 2003-04-25 rea@sr.unh.edu # Updated to work on Familiar Pre0.7rc1, with busybox tar. # Note it Requires: binutils-ar (since the busybox ar can't create) # For UID debugging it needs a better "find". set -e version=1.0 ipkg_extract_value() { sed -e "s/^[^:]*:[[:space:]]*//" } required_field() { field=$1 value=`grep "^$field:" < $CONTROL/control | ipkg_extract_value` if [ -z "$value" ]; then echo "*** Error: $CONTROL/control is missing field $field" >&2 return 1 fi echo $value return 0 } disallowed_field() { field=$1 value=`grep "^$field:" < $CONTROL/control | ipkg_extract_value` if [ -n "$value" ]; then echo "*** Error: $CONTROL/control contains disallowed field $field" >&2 return 1 fi echo $value return 0 } pkg_appears_sane() { local pkg_dir=$1 local owd=$PWD cd $pkg_dir PKG_ERROR=0 tilde_files=`find . -name '*~'` if [ -n "$tilde_files" ]; then if [ "$noclean" = "1" ]; then echo "*** Warning: The following files have names ending in '~'. You probably want to remove them: " >&2 ls -ld $tilde_files echo >&2 else echo "*** Removing the following files: $tilde_files" rm -f "$tilde_files" fi fi large_uid_files=`find . -uid +99 || true` if [ "$ogargs" = "" ] && [ -n "$large_uid_files" ]; then echo "*** Warning: The following files have a UID greater than 99. You probably want to chown these to a system user: " >&2 ls -ld $large_uid_files echo >&2 fi if [ ! -f "$CONTROL/control" ]; then echo "*** Error: Control file $pkg_dir/$CONTROL/control not found." >&2 cd $owd return 1 fi pkg=`required_field Package` [ "$?" -ne 0 ] && PKG_ERROR=1 version=`required_field Version | sed 's/Version://; s/^.://g;'` [ "$?" -ne 0 ] && PKG_ERROR=1 arch=`required_field Architecture` [ "$?" -ne 0 ] && PKG_ERROR=1 required_field Maintainer >/dev/null [ "$?" -ne 0 ] && PKG_ERROR=1 required_field Description >/dev/null [ "$?" -ne 0 ] && PKG_ERROR=1 section=`required_field Section` [ "$?" -ne 0 ] && PKG_ERROR=1 if [ -z "$section" ]; then echo "The Section field should have one of the following values:" >&2 echo "admin, base, comm, editors, extras, games, graphics, kernel, libs, misc, net, text, web, x11" >&2 fi priority=`required_field Priority` [ "$?" -ne 0 ] && PKG_ERROR=1 if [ -z "$priority" ]; then echo "The Priority field should have one of the following values:" >&2 echo "required, important, standard, optional, extra." >&2 echo "If you don't know which priority value you should be using, then use \`optional'" >&2 fi source=`required_field Source` [ "$?" -ne 0 ] && PKG_ERROR=1 if [ -z "$source" ]; then echo "The Source field contain the URL's or filenames of the source code and any patches" echo "used to build this package. Either gnu-style tarballs or Debian source packages " echo "are acceptable. Relative filenames may be used if they are distributed in the same" echo "directory as the .ipk file." fi disallowed_filename=`disallowed_field Filename` [ "$?" -ne 0 ] && PKG_ERROR=1 if echo $pkg | grep '[^a-z0-9.+-]'; then echo "*** Error: Package name $name contains illegal characters, (other than [a-z0-9.+-])" >&2 PKG_ERROR=1; fi local bad_fields=`sed -ne 's/^\([^[:space:]][^:[:space:]]\+[[:space:]]\+\)[^:].*/\1/p' < $CONTROL/control | sed -e 's/\\n//'` if [ -n "$bad_fields" ]; then bad_fields=`echo $bad_fields` echo "*** Error: The following fields in $CONTROL/control are missing a ':'" >&2 echo " $bad_fields" >&2 echo "ipkg-build: This may be due to a missing initial space for a multi-line field value" >&2 PKG_ERROR=1 fi for script in $CONTROL/preinst $CONTROL/postinst $CONTROL/prerm $CONTROL/postrm; do if [ -f $script -a ! -x $script ]; then echo "*** Error: package script $script is not executable" >&2 PKG_ERROR=1 fi done if [ -f $CONTROL/conffiles ]; then for cf in `cat $CONTROL/conffiles`; do if [ ! -f ./$cf ]; then echo "*** Error: $CONTROL/conffiles mentions conffile $cf which does not exist" >&2 PKG_ERROR=1 fi done fi cd $owd return $PKG_ERROR } ### # ipkg-build "main" ### ogargs="" outer=ar noclean=0 usage="Usage: $0 [-c] [-C] [-o owner] [-g group] []" while getopts "cg:ho:v" opt; do case $opt in o ) owner=$OPTARG ogargs="--owner=$owner" ;; g ) group=$OPTARG ogargs="$ogargs --group=$group" ;; c ) outer=tar ;; C ) noclean=1 ;; v ) echo $version exit 0 ;; h ) echo $usage >&2 ;; \? ) echo $usage >&2 esac done shift $(($OPTIND - 1)) # continue on to process additional arguments case $# in 1) dest_dir=$PWD ;; 2) dest_dir=$2 if [ "$dest_dir" = "." -o "$dest_dir" = "./" ] ; then dest_dir=$PWD fi ;; *) echo $usage >&2 exit 1 ;; esac pkg_dir=$1 if [ ! -d $pkg_dir ]; then echo "*** Error: Directory $pkg_dir does not exist" >&2 exit 1 fi # CONTROL is second so that it takes precedence CONTROL= [ -d $pkg_dir/DEBIAN ] && CONTROL=DEBIAN [ -d $pkg_dir/CONTROL ] && CONTROL=CONTROL if [ -z "$CONTROL" ]; then echo "*** Error: Directory $pkg_dir has no CONTROL subdirectory." >&2 exit 1 fi if ! pkg_appears_sane $pkg_dir; then echo >&2 echo "ipkg-build: Please fix the above errors and try again." >&2 exit 1 fi tmp_dir=$dest_dir/IPKG_BUILD.$$ mkdir $tmp_dir echo $CONTROL > $tmp_dir/tarX ( cd $pkg_dir && tar $ogargs -czf $tmp_dir/data.tar.gz . -X $tmp_dir/tarX ) ( cd $pkg_dir/$CONTROL && tar $ogargs -czf $tmp_dir/control.tar.gz . ) rm $tmp_dir/tarX echo "2.0" > $tmp_dir/debian-binary pkg_file=$dest_dir/${pkg}_${version}_${arch}.ipk rm -f $pkg_file if [ "$outer" = "ar" ] ; then ( cd $tmp_dir && ar -crf $pkg_file ./debian-binary ./data.tar.gz ./control.tar.gz ) else ( cd $tmp_dir && tar -zcf $pkg_file ./debian-binary ./data.tar.gz ./control.tar.gz ) fi rm $tmp_dir/debian-binary $tmp_dir/data.tar.gz $tmp_dir/control.tar.gz rmdir $tmp_dir echo "Packaged contents of $pkg_dir into $pkg_file" agtl-0.8.0.3/ipkg-utils-1.7/ipkg-buildpackage000077500000000000000000000125641151564747700205240ustar00rootroot00000000000000#!/bin/sh # # $Id: ipkg-buildpackage,v 1.1 2001/07/26 15:36:36 oku Exp $ # # Author: Oliver Kurth # # Description: # builds an ipkg package from a source tarball using ipkg-build. Idea stolen # from Debian dpkg-buildpackage. # # - unpack the source tarball. # - cd to the source distribution, change whatever you need to make the # package compile and work on the ipaq. # - create a directory 'ipkg' # - put all files which go to the 'CONTROL' directory of the root of the # installed package into ipkg. # - the version (Version field in control) should match the version of the # source tarball, optionally with a digit (eg. -1) appended. # - additionally, put a file there called 'rules' which is a shell script # accepting arguments 'build', 'install' and 'clean'. It can be a # Makefile starting with the line '#!/usr/bin/make -f' # * the build target does things like ./configure and make. # * the install target installs to a temporary directory (make # DESTDIR=/tmp/package) and removes unnecessary items (eg. man pages) # * clean cleans ;-) # # You should run this with fakeroot (1) or as root. # If all went well, you will find a diff file and an *.ipk file in the # directory above. set -e #SCRIPTDIR=/usr/local/bin SCRIPTDIR=/other/kurth/ipaq-dev/familiar/dist/ipkg/util/ SCRIPTNAME=`basename $0` ipkg_extract_value() { sed -e "s/^[^:]*:[[:space:]]*//" } required_field() { field=$1 value=`grep "^$field:" < $CONTROL/control | ipkg_extract_value` if [ -z "$value" ]; then echo "ipkg-build: Error: $CONTROL/control is missing field $field" ; PKG_ERROR=1 fi echo $value } pkg_appears_sane_control() { local pkg_dir=$1 local owd=`pwd` cd $pkg_dir if [ ! -f "$CONTROL/control" ]; then echo "ipkg-build: Error: Control file $pkg_dir/$CONTROL/control not found." cd $owd return 1 fi pkg=`required_field Package` version=`required_field Version | sed 's/.*://;'` arch=`required_field Architecture` required_field Maintainer >/dev/null required_field Description >/dev/null if echo $pkg | grep '[^a-z0-9.+-]'; then echo "ipkg-build: Error: Package name $name contains illegal characters, (other than [a-z0-9.+-])" PKG_ERROR=1; fi local bad_fields=`sed -ne 's/^\([^[:space:]][^:[:space:]]\+[[:space:]]\+\)[^:].*/\1/p' < $CONTROL/control | sed -e 's/\\n//'` if [ -n "$bad_fields" ]; then bad_fields=`echo $bad_fields` echo "ipkg-build: Error: The following fields in $CONTROL/control are missing a ':'" echo " $bad_fields" echo "ipkg-build: This may be due to a missing initial space for a multi-line field value" PKG_ERROR=1 fi cd $owd return $PKG_ERROR } pkg_appears_sane_scripts() { local pkg_dir=$1 local owd=`pwd` cd $pkg_dir for script in $CONTROL/preinst $CONTROL/postinst $CONTROL/prerm $CONTROL/postrm; do if [ -f $script -a ! -x $script ]; then echo "ipkg-build: Error: package script $script is not executable" PKG_ERROR=1 fi done cd $owd return $PKG_ERROR } pkg_dir_abs=`pwd` pkg_dir_base=`basename ${pkg_dir_abs}` pkg_dir=. [ -d ./CONTROL ] && CONTROL=./CONTROL [ -d ./ipkg ] && CONTROL=./ipkg if [ -z "$CONTROL" ]; then echo "${SCRIPT_NAME}: Error: Directory $pkg_dir has no ipkg or CONTROL subdirectory." exit 1 fi if ! pkg_appears_sane_control $pkg_dir && pkg_appears_sane_control $pkg_dir; then echo "Please fix the above errors and try again." exit 1 fi echo "Package = $pkg" echo "Version = $version" version_up=`echo ${version} | sed "s/\-[0-9]\+//"` echo "Upstream Version = $version_up" pkg_upname=${pkg}-${version_up} echo "Package Name = $pkg_upname" pkg_tarball=${pkg_upname}.tar.gz echo "Tarball Name = $pkg_tarball" if [ ! -x ${CONTROL}/rules ]; then echo "${CONTROL}/rules not found or not executable." exit 1 fi # # unpack upstream tarball and make diff # if [ -e ../${pkg_tarball} ] ; then ( set +e cd .. rm -rf ${pkg_upname}.tmp rm -rf ${pkg_upname}.orig mkdir ${pkg_upname}.tmp cd ${pkg_upname}.tmp echo "unpacking source tarball ${pkg_tarball}" tar zxf ${pkg_dir_abs}/../${pkg_tarball} dir=`find . -maxdepth 1 -name "*" -type d` cnt=`echo $dir | wc -l` if [ ${cnt} != 1 ] ; then echo "Arrghh !!!!" echo "upstream package contains more than one top level directory" echo "giving up..." exit 1 fi mv ${dir} ${pkg_upname}.orig mv ${pkg_upname}.orig .. cd .. echo "creating diff" diff -uNr ${pkg_upname}.orig ${pkg_dir_base} > ${pkg}-${version}.diff rm -f ${pkg}-${version}.diff.gz echo "gzipping ${pkg}-${version}.diff" gzip ${pkg}-${version}.diff echo "Removing temporary source directorys" rm -rf ${pkg_upname}.tmp rm -rf ${pkg_upname}.orig ) fi # build package if ! ${CONTROL}/rules build; then echo "Build failed" exit 1 fi # install it to tmp directory if ! ${CONTROL}/rules install; then echo "Install failed" exit 1 fi # copy contents of control directory mkdir /tmp/${pkg}/CONTROL files_required="control" files_optional="preinst postinst prerm postrm" for i in ${files_required} ; do file=${CONTROL}/$i if [ -e ${file} ] ; then cp $file /tmp/${pkg}/CONTROL else echo "required file ${file} missing" fi done for i in ${files_optional} ; do file=${CONTROL}/$i if [ -e ${file} ] ; then cp $file /tmp/${pkg}/CONTROL fi done # build the ipk package owd=`pwd` cd .. ipkg-build /tmp/${pkg} || exit 1 rm -rf /tmp/${pkg} cd $owd if ! ${CONTROL}/rules clean; then echo "Clean failed" exit 1 fi # Yep. That's it! exit 0 agtl-0.8.0.3/ipkg-utils-1.7/ipkg-compare-indexes000077500000000000000000000026371151564747700211740ustar00rootroot00000000000000#!/usr/bin/env python import sys, os from glob import glob import commands import ipkg pkg_dir1 = sys.argv[1] pkg_dir2 = sys.argv[2] if ( not pkg_dir1 or not pkg_dir2 ): sys.stderr.write("Usage: ipkg-update-index \n") sys.exit(1) pkgs1 = ipkg.Packages() pkgs1.read_packages_file(pkg_dir1 + '/Packages') pkgs2 = ipkg.Packages() pkgs2.read_packages_file(pkg_dir2 + '/Packages') names1 = pkgs1.packages.keys() names2 = pkgs2.packages.keys() ## union of the two names lists pkgs = {} for name in names1: pkgs[name] = pkgs1.packages[name] for name in names2: pkgs[name] = pkgs2.packages[name] names = pkgs.keys() names.sort() for name in names: pkg1 = None pkg2 = None if pkgs1.packages.has_key(name): pkg1 = pkgs1.packages[name] if pkgs2.packages.has_key(name): pkg2 = pkgs2.packages[name] if pkg1 and pkg2 and pkg1.version != pkg2.version: print "CHANGED: %s from version %s to %s (%s)" % (pkg1.package, pkg1.version, pkg2.version, pkg2.maintainer) cmd = "ipkg-diff %s %s > %s " % ((pkg_dir1 + pkg1.filename), (pkg_dir2 + pkg2.filename), (pkg1.package + '.diff')) print cmd commands.getstatusoutput(cmd) if not pkg1: print "NEW: %s version %s (%s)"% (pkg2.package, pkg2.version, pkg2.maintainer) if not pkg2: print "DELETE: %s version %s (%s)"% (pkg1.package, pkg1.version, pkg1.maintainer) agtl-0.8.0.3/ipkg-utils-1.7/ipkg-compare-versions000077500000000000000000000167541151564747700214120ustar00rootroot00000000000000ELF`4L4 (444444    ,8   HHHDDQtdRtd   /lib/ld-linux.so.2GNUGNULSE%4تg0oB    " K9 g}5RC<)nKv0`8__gmon_start__libc.so.6_IO_stdin_usedstrcpyexitstrtolstrlenstrtoulmalloc__ctype_b_locstderrstrchrfprintfstrcmp__libc_start_mainGLIBC_2.3GLIBC_2.0ii ii 8   $ ( , US[Ôtu X[5%%h%h%h% h%h %h(%h0%h8p% h@`%$hHP%(hP@%,hX01^PTRh0h@QVh3US=<u?@9s@@9r<[]Ít&'Utt $ÐU8EuEE u E EEEEt$E%t΋E EEEt'FE%tE;EtUUEE ;EtU U E}uB}uEE 9s)E PE@T$$E}tEEU(E D$E$ED$E$tD$E$u}D$E$tD$E$u ED$E$_uEoD$E$@u }LD$E$u })8ML$T$$$U(E u D$:E $%E}tQD$ ED$E $BEE9Et4fEEuTQEE EU EE $$,‹EPE@U T$$US<})E |8L$T$$E D$D$$$D$,|$,t1E 8\$,\$ L$T$$r|E D$D$$D$,|$,t1E 8\$,\$ L$T$$(E D$D$D$D$$$<[]ÐU]Ít&'UWVSOé)t$1ED$E D$E$9rރ[^_]Ë$ÐUS t fЋu[]ÐUS[Y[.-<=<>=><<>>=unknown operator: %sversion string is emptyepoch in version is not numbernothing after colon in version numberusage: %s: version op refversion Invalid version `%s': %s  T ̌o܁ `ooo ʄڄ *:JGCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3.symtab.strtab.shstrtab.interp.note.ABI-tag.note.gnu.build-id.gnu.hash.dynsym.dynstr.gnu.version.gnu.version_r.rel.dyn.rel.plt.init.text.fini.rodata.eh_frame.ctors.dtors.jcr.dynamic.got.got.plt.data.bss.comment44#HH 1hh$HPDo܁$N V^oko0z  ` TT0``ľ       <0088 08#[. 4Hh܁   T  `̌  08 (5K<Z@ht    0 !01`8 G [bťz084"/#4FS@Zcv8DԈ[8RT crtstuff.c__CTOR_LIST____DTOR_LIST____JCR_LIST____do_global_dtors_auxcompleted.7021dtor_idx.7023frame_dummy__CTOR_END____FRAME_END____JCR_END____do_global_ctors_auxipkg-compare-versions.cverrevcmp_GLOBAL_OFFSET_TABLE___init_array_end__init_array_start_DYNAMICdata_start__libc_csu_fini_start__gmon_start___Jv_RegisterClasses_fp_hwstrchr@@GLIBC_2.0_fini__libc_start_main@@GLIBC_2.0_IO_stdin_usedstrtol@@GLIBC_2.0__data_start__ctype_b_loc@@GLIBC_2.3stderr@@GLIBC_2.0strtoul@@GLIBC_2.0strlen@@GLIBC_2.0__dso_handleversionsatisfied3strcpy@@GLIBC_2.0__DTOR_END____libc_csu_initfprintf@@GLIBC_2.0__bss_startmalloc@@GLIBC_2.0_endversioncompare_edatastrcmp@@GLIBC_2.0parseversionexit@@GLIBC_2.0__i686.get_pc_thunk.bxmain_initagtl-0.8.0.3/ipkg-utils-1.7/ipkg-compare-versions.c000066400000000000000000000104771151564747700216240ustar00rootroot00000000000000/* * libdpkg - Debian packaging suite library routines * vercmp.c - comparison of version numbers * * Copyright (C) 1995 Ian Jackson * * This 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, * or (at your option) any later version. * * This 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 dpkg; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include # define _(Text) Text struct versionrevision { unsigned long epoch; char *version; #if 0 const char *revision; const char *familiar_revision; #endif }; static int verrevcmp(const char *val, const char *ref) { int vc, rc; long vl, rl; const char *vp, *rp; const char *vsep, *rsep; if (!val) val= ""; if (!ref) ref= ""; for (;;) { vp= val; while (*vp && !isdigit(*vp)) vp++; rp= ref; while (*rp && !isdigit(*rp)) rp++; for (;;) { vc= val == vp ? 0 : *val++; rc= ref == rp ? 0 : *ref++; if (!rc && !vc) break; if (vc && !isalpha(vc)) vc += 256; /* assumes ASCII character set */ if (rc && !isalpha(rc)) rc += 256; if (vc != rc) return vc - rc; } val= vp; ref= rp; vl=0; if (isdigit(*vp)) vl= strtol(val,(char**)&val,10); rl=0; if (isdigit(*rp)) rl= strtol(ref,(char**)&ref,10); if (vl != rl) return vl - rl; vc = *val; rc = *ref; vsep = strchr(".-", vc); rsep = strchr(".-", rc); if (vsep && !rsep) return -1; if (!vsep && rsep) return +1; if (!*val && !*ref) return 0; if (!*val) return -1; if (!*ref) return +1; } } int versioncompare(const struct versionrevision *version, const struct versionrevision *refversion) { int r; if (version->epoch > refversion->epoch) return 1; if (version->epoch < refversion->epoch) return -1; r= verrevcmp(version->version,refversion->version); if (r) return r; return r; #if 0 r= verrevcmp(version->revision,refversion->revision); if (r) return r; return verrevcmp(version->familiar_revision,refversion->familiar_revision); #endif } int versionsatisfied3(const struct versionrevision *it, const struct versionrevision *ref, const char *op) { int r; r= versioncompare(it,ref); if (strcmp(op, "<=") == 0 || strcmp(op, "<") == 0) return r <= 0; if (strcmp(op, ">=") == 0 || strcmp(op, ">") == 0) return r >= 0; if (strcmp(op, "<<") == 0) return r < 0; if (strcmp(op, ">>") == 0) return r > 0; if (strcmp(op, "=") == 0) return r == 0; fprintf(stderr, "unknown operator: %s", op); exit(1); } const char *parseversion(struct versionrevision *rversion, const char *string) { char *hyphen, *colon, *eepochcolon; unsigned long epoch; if (!*string) return _("version string is empty"); colon= strchr(string,':'); if (colon) { epoch= strtoul(string,&eepochcolon,10); if (colon != eepochcolon) return _("epoch in version is not number"); if (!*++colon) return _("nothing after colon in version number"); string= colon; rversion->epoch= epoch; } else { rversion->epoch= 0; } #if 0 rversion->revision = ""; rversion->familiar_revision = ""; #endif rversion->version= malloc(strlen(string)+1); strcpy(rversion->version, string); #if 0 fprintf(stderr,"Parsed version: %lu, %s\n", rversion->epoch, rversion->version); #endif return 0; } int main(int argc, char *argv[]) { const char *err; struct versionrevision ver, ref; if (argc < 4) { fprintf(stderr, "usage: %s: version op refversion\n", argv[0]); return 2; } err = parseversion(&ver, argv[1]); if (err) { fprintf(stderr, "Invalid version `%s': %s\n", argv[1], err); return 2; } err = parseversion(&ref, argv[3]); if (err) { fprintf(stderr, "Invalid version `%s': %s\n", argv[3], err); return 2; } return ! versionsatisfied3(&ver, &ref, argv[2]); } agtl-0.8.0.3/ipkg-utils-1.7/ipkg-compare-versions.sh000077500000000000000000000030641151564747700220110ustar00rootroot00000000000000#!/bin/sh set -e # This is a little experiment to see how expensive it would be to # compare versions in a shell script. This script is not done yet # and the nasiest part is still left undone, (the fact that in Debian # versions all letters compare less than all non-letters). # # It looks to me like version comprehension might be the feature that pushes # ipkg from /bin/sh to compiled C code... if [ $# -lt 3 ]; then echo " usage: ipkg-compare-versions v1 op v2 where op in (<<, <=, =, >=, >>) Return value is 0 if v1 op v2 is satisfied, 1 otherwise" exit 2 fi v1=$1 op=$2 v2=$3 # Debian has a little historical problem with operators... may_be_equal=0 case $op in '>>') op="-gt" ;; '<<') op="-lt" ;; '>'|'>=') op="-gt" may_be_equal=1 ;; '<'|'<=') op="-lt" may_be_equal=1 ;; '=') may_be_equal=1 ;; *) echo "ipkg_compare_versions: Invalid operator \`$op' valid operators are (<<, <=, =, >=, >>)" exit 1 ;; esac if [ $may_be_equal == 1 -a $v1 == $v2 ]; then exit 0; elif [ $op == '=' ]; then exit 1; fi epoch1=`echo $v1 | sed -ne 's/:.*//p'` v1=`echo $v1 | sed -e 's/^[^:]*://'` epoch2=`echo $v2 | sed -ne 's/:.*//p'` v2=`echo $v2 | sed -e 's/^[^:]*://'` upstream1=`echo $v1 | sed -e '/-/s/\(.*\)-.*/\1/'` debian_rev1=`echo $v1 | sed -ne 's/.*-//p'` upstream2=`echo $v2 | sed -e '/-/s/\(.*\)-.*/\1/'` debian_rev2=`echo $v2 | sed -ne 's/.*-//p'` echo "$epoch1:$upstream1-$debian_rev1 $op $epoch2:$upstream2-$debian_rev2" exit 3 [ -z $epoch1 ] && epoch1="0" [ -z $epoch2 ] && epoch2="0" if [ $epoch1 != $epoch2 ]; then exit `[ $epoch1 $op $epoch2 ]` fi exit 3 agtl-0.8.0.3/ipkg-utils-1.7/ipkg-deb-build000077500000000000000000000104551151564747700177350ustar00rootroot00000000000000#!/bin/sh # ipkg-deb-build -- construct a debian file format .ipk from a directory # Jamey Hicks # Carl Worth # based on a script by Steve Redler IV, steve@sr-tech.com 5-21-2001 set -e ipkg_extract_value() { sed -e "s/^[^:]*:[[:space:]]*//" } required_field() { field=$1 value=`grep "^$field:" < $CONTROL/control | ipkg_extract_value` if [ -z "$value" ]; then echo "*** Error: $CONTROL/control is missing field $field" >&2 return 1 fi echo $value return 0 } pkg_appears_sane() { local pkg_dir=$1 local owd=`pwd` cd $pkg_dir PKG_ERROR=0 tilde_files=`find . -name '*~'` if [ -n "$tilde_files" ]; then echo "*** Warning: The following files have names ending in '~'. You probably want to remove them: " >&2 ls -ld $tilde_files echo >&2 fi large_uid_files=`find . -uid +99` if [ -n "$large_uid_files" ]; then echo "*** Warning: The following files have a UID greater than 99. You probably want to chown these to a system user: " >&2 ls -ld $large_uid_files echo >&2 fi if [ ! -f "$CONTROL/control" ]; then echo "*** Error: Control file $pkg_dir/$CONTROL/control not found." >&2 cd $owd return 1 fi pkg=`required_field Package` [ "$?" -ne 0 ] && PKG_ERROR=1 version=`required_field Version | sed 's/.*://;'` [ "$?" -ne 0 ] && PKG_ERROR=1 arch=`required_field Architecture` [ "$?" -ne 0 ] && PKG_ERROR=1 required_field Maintainer >/dev/null [ "$?" -ne 0 ] && PKG_ERROR=1 required_field Description >/dev/null [ "$?" -ne 0 ] && PKG_ERROR=1 section=`required_field Section` [ "$?" -ne 0 ] && PKG_ERROR=1 if [ -z "$section" ]; then echo "The Section field should have one of the following values:" >&2 echo "admin, base, comm, editors, extras, games, graphics, kernel, libs, misc, net, text, web, x11" >&2 fi priority=`required_field Priority` [ "$?" -ne 0 ] && PKG_ERROR=1 if [ -z "$priority" ]; then echo "The Priority field should have one of the following values:" >&2 echo "required, important, standard, optional, extra." >&2 echo "If you don't know which priority value you should be using, then use \`optional'" >&2 fi if echo $pkg | grep '[^a-z0-9.+-]'; then echo "*** Error: Package name $name contains illegal characters, (other than [a-z0-9.+-])" >&2 PKG_ERROR=1; fi local bad_fields=`sed -ne 's/^\([^[:space:]][^:[:space:]]\+[[:space:]]\+\)[^:].*/\1/p' < $CONTROL/control | sed -e 's/\\n//'` if [ -n "$bad_fields" ]; then bad_fields=`echo $bad_fields` echo "*** Error: The following fields in $CONTROL/control are missing a ':'" >&2 echo " $bad_fields" >&2 echo "ipkg-build: This may be due to a missing initial space for a multi-line field value" >&2 PKG_ERROR=1 fi for script in $CONTROL/preinst $CONTROL/postinst $CONTROL/prerm $CONTROL/postrm; do if [ -f $script -a ! -x $script ]; then echo "*** Error: package script $script is not executable" >&2 PKG_ERROR=1 fi done if [ -f $CONTROL/conffiles ]; then for cf in `cat $CONTROL/conffiles`; do if [ ! -f ./$cf ]; then echo "*** Error: $CONTROL/conffiles mentions conffile $cf which does not exist" >&2 PKG_ERROR=1 fi done fi cd $owd return $PKG_ERROR } ### # ipkg-build "main" ### case $# in 1) dest_dir=. ;; 2) dest_dir=$2 ;; *) echo "Usage: ipkg-build []" >&2 exit 1 ;; esac pkg_dir=$1 if [ ! -d $pkg_dir ]; then echo "*** Error: Directory $pkg_dir does not exist" >&2 exit 1 fi # CONTROL is second so that it takes precedence CONTROL= [ -d $pkg_dir/DEBIAN ] && CONTROL=DEBIAN [ -d $pkg_dir/CONTROL ] && CONTROL=CONTROL if [ -z "$CONTROL" ]; then echo "*** Error: Directory $pkg_dir has no CONTROL subdirectory." >&2 exit 1 fi if ! pkg_appears_sane $pkg_dir; then echo >&2 echo "ipkg-build: Please fix the above errors and try again." >&2 exit 1 fi tmp_dir=$dest_dir/IPKG_BUILD.$$ mkdir $tmp_dir tar -C $pkg_dir -czf $tmp_dir/data.tar.gz . --exclude=$CONTROL tar -C $pkg_dir/$CONTROL -czf $tmp_dir/control.tar.gz . echo "2.0" > $tmp_dir/debian-binary pkg_file=${pkg}_${version}_${arch}.ipk pkg_dir=$PWD cd $tmp_dir ar crf $pkg_dir/$pkg_file ./debian-binary ./data.tar.gz ./control.tar.gz cd $pkg_dir mv $pkg_file $dest_dir rm -f $tmp_dir/debian-binary $tmp_dir/data.tar.gz $tmp_dir/control.tar.gz rmdir $tmp_dir echo "Packaged contents of $pkg_dir into $pkg_file" agtl-0.8.0.3/ipkg-utils-1.7/ipkg-deb-unbuild000077500000000000000000000007251151564747700202770ustar00rootroot00000000000000#!/bin/bash set -e if [ $# -lt 1 ]; then echo "usage: $0 package_name" exit 1 fi ## This script is a nasty hack... especially the cp of $filename. Yuck! filename="$1" basefile=`echo $filename | sed 's/.*\///'` pkg=`echo $basefile | sed 's/\.deb$//' | sed 's/\.ipk$//'` mkdir $pkg cp $filename $pkg cd $pkg ar -x $basefile tar xzf data.tar.gz mkdir DEBIAN cd DEBIAN tar xzf ../control.tar.gz cd .. rm -f control.tar.gz data.tar.gz debian-binary $basefile cd ../.. agtl-0.8.0.3/ipkg-utils-1.7/ipkg-diff000077500000000000000000000003351151564747700170120ustar00rootroot00000000000000#!/bin/sh pkg1=$1 dir1=`echo $pkg1 | sed s/.ipk//` dir1=`basename $dir1` pkg2=$2 dir2=`echo $pkg2 | sed s/.ipk//` dir2=`basename $dir2` ipkg-unbuild $pkg2 ipkg-unbuild $pkg1 diff -urN $dir1 $dir2 rm -fr $dir1 $dir2 agtl-0.8.0.3/ipkg-utils-1.7/ipkg-extract-file000077500000000000000000000017531151564747700204760ustar00rootroot00000000000000#!/bin/sh set -e if [ $# -lt 1 ]; then echo "usage: $0: package.ipk filename ..." exit 1 fi if [ $# -eq 2 ]; then ipkgfilename=$1 filename=$2 else echo "usage: $0 ipkgfilename filename" exit -1 fi case $ipkgfilename in http:*) wget -N $ipkgfilename ipkgfilename=`basename $ipkgfilename` echo eez http url $ipkgfilename ;; ftp:*) wget -N $ipkgfilename ipkgfilename=`basename $ipkgfilename` echo eez ftp url $ipkgfilename ;; esac tmpdir=/tmp/ipkg-extract-$$ mkdir $tmpdir pkgdir=$tmpdir/`basename $ipkgfilename | sed 's/.*\///;s/.ipk$//;s/.deb$//'` mkdir -p $pkgdir/CONTROL cur_dir=$PWD cd $pkgdir; (ar x $cur_dir/$ipkgfilename || tar zxf $cur_dir/$ipkgfilename) >& /dev/null cd $cur_dir tar xzf $pkgdir/data.tar.gz -C $pkgdir tar xzf $pkgdir/control.tar.gz -C $pkgdir/CONTROL rm -f $pkgdir/control.tar.gz $pkgdir/data.tar.gz $pkgdir/debian-binary cp $pkgdir/$filename `basename $filename` ls -l `basename $filename` file `basename $filename` rm -fr $tmpdir agtl-0.8.0.3/ipkg-utils-1.7/ipkg-ipk000077500000000000000000000001711151564747700166630ustar00rootroot00000000000000#!/bin/sh echo "ipkg-ipk is now deprecated in favor of the new ipkg-build." echo "please use ipkg-build instead" exit 1 agtl-0.8.0.3/ipkg-utils-1.7/ipkg-link000077500000000000000000000045641151564747700170470ustar00rootroot00000000000000#!/bin/sh # # # Modified by Aman Gupta usage () { echo "Usage: " echo " $0 add packagename (links \"packagename\" to root filesystem)" echo " $0 remove packagename (unlinks \"packagename\" from root filesystem)" echo " $0 list mountpoint (lists packages on \"mountpoint\", e.g. '/mnt/card')" echo " $0 mount mountpoint (links all packages on \"mountpoint\", e.g. '/mnt/card')" echo " $0 umount mountpoint (unlinks all packages on \"mountpoint\", e.g. '/mnt/card')" exit } findpackage () { echo "*** Locating package" # Does the list file exist? if [ -e "/mnt/card/usr/lib/ipkg/info/$PACKAGE.list" ]; then PREFIX="/mnt/card" else if [ -e "/mnt/cf/usr/lib/ipkg/info/$PACKAGE.list" ]; then PREFIX="/mnt/cf" else if [ -e "/mnt/ram/usr/lib/ipkg/info/$PACKAGE.list" ]; then PREFIX="/mnt/ram" else echo "Package \"$PACKAGE\" not found." exit fi fi fi files=`cat "$PREFIX/usr/lib/ipkg/info/$PACKAGE.list"` echo "*** Found package on $PREFIX" } add () { echo "*** Adding $PACKAGE" echo "$files" | while read line; do if [ ! -e "$line" ]; then # Only if it doesn't already exist. if [ -d "$PREFIX$line" ]; then # It's a directory. `mkdir "$line"` else # It's a file. `ln -s "$PREFIX$line" "$line"` fi fi done } remove () { echo "*** Removing $PACKAGE" files=`cat "$PREFIX/usr/lib/ipkg/info/$PACKAGE.list" | sort -r` echo "$files" | while read line; do if [ -e "$line" ]; then # File/Directory exists. if [ -d "$line" ]; then # Directory. contents=$(ls -1 "$line") if [ ! "$contents" ]; then # Empty directory rmdir "$line" fi elif [ -L "$line" ]; then rm "$line" fi fi done } list () { filelist="" files=`ls -1 $LOCATION/usr/lib/ipkg/info/*.list` for filename in $files; do filename=${filename##*/} filename=${filename%%.list} filelist="$filelist $filename" done } COMMAND=$1 PACKAGE=$2 LOCATION=$2 if [ $# -ne 2 ] then usage fi echo "*** Command: $COMMAND" case "$COMMAND" in "add" ) findpackage add ;; "remove" ) findpackage remove ;; "list" ) list for file in $filelist; do echo $file done ;; "mount" ) list for file in $filelist; do $0 add $file done ;; "umount" ) list for file in $filelist; do $0 remove $file done esac echo "*** Done." echo "" exit agtl-0.8.0.3/ipkg-utils-1.7/ipkg-make-familiar000077500000000000000000000004501151564747700205770ustar00rootroot00000000000000#!/bin/sh set -e . /etc/ipkg.conf MKFSJFFS2_FLAGS="-p -e 0x40000" ipkg update if ! ipkg install $*; then echo "ipkg-make-familiar: *** Crash and Burn ***" exit 1 fi echo "Creating familiar.jffs2" chown -R root:root $IPKG_ROOT cd $IPKG_ROOT mkfs.jffs2 $MKFSJFFS2_FLAGS > ../familiar.jffs2 agtl-0.8.0.3/ipkg-utils-1.7/ipkg-make-index000077500000000000000000000113021151564747700201200ustar00rootroot00000000000000#!/usr/bin/python # $Id: ipkg-make-index,v 1.20 2003/10/30 02:32:09 jamey Exp $ import sys, os, posixpath from glob import glob import commands import ipkg import getopt import string verbose = 0 def usage(): sys.stderr.write("%s [-h] [-s] [-m] [-l Packages.filelist] [-p Packages] [-r Packages.old] [-v] packagesdir\n" % (sys.argv[0],)) sys.exit(-1) def to_morgue(filename): morgue_dir = pkg_dir + "/morgue" if verbose: sys.stderr.write ("Moving " + filename + " to morgue\n") if not os.path.exists(morgue_dir): os.mkdir(morgue_dir) os.rename(pkg_dir + "/" + filename, morgue_dir + "/" + filename) if os.path.exists(pkg_dir + "/" + filename + ".asc"): os.rename(pkg_dir + "/" + filename + ".asc", morgue_dir + "/" + filename + ".asc") old_filename = None packages_filename = None filelist_filename = "Packages.filelist" opt_s = 0 opt_m = 0 (opts, remaining_args) = getopt.getopt(sys.argv[1:], "hl:p:vsmr:") for (optkey, optval) in opts: if optkey == '-h': usage() if optkey == '-s': opt_s = 1 if optkey == '-p': packages_filename = optval if optkey == '-l': filelist_filename = optval if optkey == '-v': verbose = 1 if optkey == '-m': opt_m = 1 if optkey == '-r': old_filename = optval pkg_dir=remaining_args[0] if ( not pkg_dir ): usage() packages = ipkg.Packages() old_pkg_hash = {} if packages_filename and not old_filename and os.path.exists(packages_filename): old_filename = packages_filename if old_filename: if (verbose): sys.stderr.write("Reading package list from " + old_filename + "\n") old_packages = ipkg.Packages() old_packages.read_packages_file(old_filename) for k in old_packages.packages.keys(): p = old_packages.packages[k] old_pkg_hash[p.filename] = p if (verbose): sys.stderr.write("Reading in all the package info from %s\n" % (pkg_dir, )) files=glob(pkg_dir + '/*.ipk') + glob(pkg_dir + '/*.deb') files.sort() for filename in files: basename = os.path.basename(filename) if old_pkg_hash.has_key(basename): if (verbose): sys.stderr.write("Found %s in Packages\n" % (filename,)) pkg = old_pkg_hash[basename] else: if (verbose): sys.stderr.write("Reading info for package %s\n" % (filename,)) pkg = ipkg.Package(filename) pkg_key = ("%s:%s" % (pkg.package, pkg.architecture)) if (packages.packages.has_key(pkg_key)): old_filename = packages.packages[pkg_key].filename else: old_filename = "" s = packages.add_package(pkg) if s == 0: if old_filename: # old package was displaced by newer if opt_m: to_morgue(old_filename) if opt_s: print pkg_dir + "/" + old_filename else: if opt_m: to_morgue(basename) if opt_s: print filename if opt_s: sys.exit(0) if verbose: sys.stderr.write("Generating Packages file\n") if packages_filename: old_stdout = sys.stdout tmp_packages_filename = ("%s.%d" % (packages_filename, os.getpid())) sys.stdout = open(tmp_packages_filename, "w") names = packages.packages.keys() names.sort() for name in names: pkg = packages.packages[name] if (verbose): sys.stderr.write("Writing info for package %s\n" % (pkg.package,)) print pkg if packages_filename: sys.stdout.close() sys.stdout = old_stdout gzip_filename = ("%s.gz" % packages_filename) tmp_gzip_filename = ("%s.%d" % (gzip_filename, os.getpid())) gzip_cmd = "gzip -9c < %s > %s" % (tmp_packages_filename, tmp_gzip_filename) (rc, outtext) = commands.getstatusoutput(gzip_cmd) print outtext os.rename(tmp_packages_filename, packages_filename) os.rename(tmp_gzip_filename, gzip_filename) if verbose: sys.stderr.write("Generate Packages.filelist file\n") files = {} names = packages.packages.keys() names.sort() for name in names: for fn in packages[name].get_file_list(): (h,t) = os.path.split(fn) if not t: continue if not files.has_key(t): files[t] = name+':'+fn else: files[t] = files[t] + ',' + name+':'+fn if filelist_filename: tmp_filelist_filename = ("%s.%d" % (filelist_filename, os.getpid())) sys.stdout = open(tmp_filelist_filename, "w") names = files.keys() names.sort() for name in names: print name,files[name] sys.stdout.close() if posixpath.exists(filelist_filename): os.unlink(filelist_filename) os.rename(tmp_filelist_filename, filelist_filename) agtl-0.8.0.3/ipkg-utils-1.7/ipkg-show-deps000077500000000000000000000044421151564747700200160ustar00rootroot00000000000000#!/usr/bin/python # $Id: ipkg-show-deps,v 1.2 2004/03/06 13:08:33 pb Exp $ import sys, os, posixpath from glob import glob import commands import ipkg import getopt import string import re verbose = 0 def usage(): sys.stderr.write("%s [-p Packages] package ...\n" % (sys.argv[0],)) sys.exit(-1) packages_filename = "Packages" (opts, remaining_args) = getopt.getopt(sys.argv[1:], "hp:") for (optkey, optval) in opts: if optkey == '-h': usage() if optkey == '-p': packages_filename = optval if ( not remaining_args ): usage() packages = ipkg.Packages() packages.read_packages_file(packages_filename) required = {} provider_hash = {} def split_list(str): r = [] l = string.split(str, ",") for i in l: ll = string.split(i, "|") for ii in ll: ii = string.strip(ii) r.append(ii) return r for i in packages.packages.keys(): p = packages.packages[i] if not provider_hash.has_key(p.package): provider_hash[p.package] = [] provider_hash[p.package].append(p) if p.provides: provides = string.split(p.provides, ",") for prov in provides: prov = string.strip(prov) if not provider_hash.has_key(prov): provider_hash[prov] = [] provider_hash[prov].append(p) def find_package(name): if provider_hash.has_key(name): return provider_hash[name] return None def recurse(pkg): required[pkg.package] = 1 if pkg.depends: deps = split_list(pkg.depends) for dep in deps: dep = re.sub("\s*\(.*\)", "", dep) dep = re.sub("\*$", "", dep) newpkgs = find_package(dep) if newpkgs: for newpkg in newpkgs: if required.has_key(newpkg.package): return recurse(newpkgs[0]) else: sys.stderr.write("unsatisfied dependency of %s on '%s'\n" % (pkg.package, dep)) for root in remaining_args: pkgs = find_package(root) if not pkgs: sys.stderr.write("Can't find root package '%s'\n" % root) sys.exit(-1) for p in pkgs: recurse(p) for pkg in required.keys(): print pkg agtl-0.8.0.3/ipkg-utils-1.7/ipkg-unbuild000077500000000000000000000007551151564747700175520ustar00rootroot00000000000000#!/bin/sh set -e if [ $# -lt 1 ]; then echo "usage: $0: package.ipk" exit 1 fi while [ $# -gt 0 ]; do filename=$1 shift pkg=`echo $filename | sed 's/.*\///;s/.ipk$//;s/.deb$//'` mkdir -p $pkg mkdir -p $pkg/CONTROL pkg_dir=$PWD cd $pkg; (ar x ../$filename || tar zxf ../$filename) >& /dev/null cd $pkg_dir tar xzf $pkg/data.tar.gz -C $pkg tar xzf $pkg/control.tar.gz -C $pkg/CONTROL rm -f $pkg/control.tar.gz $pkg/data.tar.gz $pkg/debian-binary done agtl-0.8.0.3/ipkg-utils-1.7/ipkg-update-index000077500000000000000000000010541151564747700204700ustar00rootroot00000000000000#!/usr/bin/env python2.1 import sys, os from glob import glob import commands import ipkg pkg_dir=sys.argv[1] pkg_filename = sys.argv[2] if ( not pkg_dir or not pkg_filename ): sys.stderr.write("Usage: ipkg-update-index \n") sys.exit(1) packages = ipkg.Packages() packages.read_packages_file(pkg_dir + '/Packages') names = packages.packages.keys() packages.add_package(ipkg.Package(pkg_filename)) packages.write_packages_file(pkg_dir + '/Packages.new') os.rename(pkg_dir + '/Packages.new', pkg_dir + '/Packages') agtl-0.8.0.3/ipkg-utils-1.7/ipkg-upload000077500000000000000000000025561151564747700173750ustar00rootroot00000000000000#!/bin/sh set -e # User-configurable options url=http://www.handhelds.org/cgi-bin/upload-package.cgi feed=unstable # The actual script if [ $# -lt 1 ] ; then echo "Usage: $0 file.ipk [file2.ipk ...]" >&2 echo "Uploads packages to Familiar's $feed feed at handhelds.org" >&2 exit 1 fi if [ -z `which curl` ] ; then echo "Sorry: $0 requires curl which appears to not be available." >&2 exit 1 fi for pkg in $* ; do if [ ! -e "$pkg" ] ; then echo "$pkg: No such file or directory" exit 1; fi if [ ! -e "$pkg.asc" ] ; then gpg -sb --armor $pkg fi done for pkg in $* ; do echo "Uploading ${pkg} to $feed feed" case $pkg in *.batch) tarfile=/tmp/ipkg-upload.$$.tar rm -rf $tarfile tar cf $tarfile `awk '{ print $2 }' < ${pkg}` curl \ -F feedname=${feed} \ -F datafilename=@${tarfile} \ -F batchfilename=@${pkg} \ -F signaturefilename=@${pkg}.asc \ $url 2>&1 | tee ${pkg}.upload.html | egrep "(Error|Warning):" | sed 's:::g;s:::g' rm -f ${tarfile} grep -i signature ${pkg}.upload.html ;; *) curl \ -F feedname=${feed} \ -F filename=@${pkg} \ -F signaturefilename=@${pkg}.asc \ -F sourcefilename="" \ $url 2>&1 | tee ${pkg}.upload.html | egrep "(Error|Warning):" | sed 's:::g;s:::g' grep -i signature ${pkg}.upload.html ;; esac echo "Done. See complete results in ${pkg}.upload.html" done agtl-0.8.0.3/ipkg-utils-1.7/ipkg.py000066400000000000000000000271731151564747700165410ustar00rootroot00000000000000#!/usr/bin/env python # Copyright (C) 2001 Alexander S. Guy # Andern Research Labs # # 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, 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., 59 Temple Place - Suite 330, # Boston, MA 02111-1307, USA. */ # # Copyright 2001, Russell Nelson # Added reading in of packages. # Added missing package information fields. # Changed render_control() to __repr__(). # # Current Issues: # The API doesn't validate package information fields. It should be # throwing exceptions in the right places. # Executions of tar could silently fail. # Executions of tar *do* fail, and loudly, because you have to specify a full filename, # and tar complains if any files are missing, and the ipkg spec doesn't require # people to say "./control.tar.gz" or "./control" when they package files. # It would be much better to require ./control or disallow ./control (either) # rather than letting people pick. Some freedoms aren't worth their cost. import tempfile import os import sys import glob import md5 import re import string import commands from stat import ST_SIZE class Package: """A class for creating objects to manipulate (e.g. create) ipkg packages.""" def __init__(self, fn=None): self.package = None self.version = None self.architecture = None self.maintainer = None self.source = None self.description = None self.depends = None self.provides = None self.replaces = None self.conflicts = None self.recommends = None self.suggests = None self.section = None self.filename_header = None self.file_list = [] self.md5 = None self.size = None self.installed_size = None self.filename = None self.isdeb = 0 if fn: # see if it is deb format f = open(fn, "r") magic = f.read(4) f.close() if (magic == "! '2': # when using Python 2.0 or newer self.md5 = sum.hexdigest() else: self.md5 = string.join(map((lambda x:"%02x" % ord(x)),sum.digest()),'') stat = os.stat(fn) self.size = stat[ST_SIZE] self.filename = os.path.basename(fn) ## sys.stderr.write(" extracting control.tar.gz from %s\n"% (fn,)) if self.isdeb: control = os.popen("ar p "+fn+" control.tar.gz | tar xfzO - '*control'","r") else: control = os.popen("tar xfzO "+fn+" '*control.tar.gz' | tar xfzO - '*control'","r") line = control.readline() while 1: if not line: break line = string.rstrip(line) lineparts = re.match(r'([\w-]*?):\s*(.*)', line) if lineparts: name = string.lower(lineparts.group(1)) value = lineparts.group(2) while 1: line = control.readline() if not line: break if line[0] != ' ': break line = string.rstrip(line) value = value + '\n' + line # don't allow package to override its own filename if name == "filename": self.filename_header = value else: if self.__dict__.has_key(name): self.__dict__[name] = value else: line = control.readline() control.close() if self.isdeb: data = os.popen("ar p "+fn+" data.tar.gz | tar tfz -","r") else: data = os.popen("tar xfzO "+fn+" '*data.tar.gz' | tar tfz -","r") while 1: line = data.readline() if not line: break self.file_list.append(string.rstrip(line)) data.close() self.scratch_dir = None self.file_dir = None self.meta_dir = None def read_control(self, control): import os line = control.readline() while 1: if not line: break line = string.rstrip(line) lineparts = re.match(r'([\w-]*?):\s*(.*)', line) if lineparts: name = string.lower(lineparts.group(1)) value = lineparts.group(2) while 1: line = control.readline() if not line: break if line[0] != ' ': break value = value + '\n' + line if name == 'size': self.size = int(value) elif self.__dict__.has_key(name): self.__dict__[name] = value if line[0] == '\n': return # consumes one blank line at end of package descriptoin else: line = control.readline() pass return def _setup_scratch_area(self): self.scratch_dir = "%s/%sipkg" % (tempfile.gettempdir(), tempfile.gettempprefix()) self.file_dir = "%s/files" % (self.scratch_dir) self.meta_dir = "%s/meta" % (self.scratch_dir) os.mkdir(self.scratch_dir) os.mkdir(self.file_dir) os.mkdir(self.meta_dir) def set_package(self, package): self.package = package def get_package(self): return self.package def set_version(self, version): self.version = version def get_version(self): return self.version def set_architecture(self, architecture): self.architecture = architecture def get_architecture(self): return self.architecture def set_maintainer(self, maintainer): self.maintainer = maintainer def get_maintainer(self): return self.maintainer def set_source(self, source): self.source = source def get_source(self): return self.source def set_description(self, description): self.description = description def get_description(self): return self.description def set_depends(self, depends): self.depends = depends def get_depends(self, depends): return self.depends def set_provides(self, provides): self.provides = provides def get_provides(self, provides): return self.provides def set_replaces(self, replaces): self.replaces = replaces def get_replaces(self, replaces): return self.replaces def set_conflicts(self, conflicts): self.conflicts = conflicts def get_conflicts(self, conflicts): return self.conflicts def set_suggests(self, suggests): self.suggests = suggests def get_suggests(self, suggests): return self.suggests def set_section(self, section): self.section = section def get_section(self, section): return self.section def get_file_list(self): return self.file_list def write_package(self, dirname): buf = self.render_control() file = open("%s/control" % self.meta_dir, 'w') file.write(buf) self._setup_scratch_area() cmd = "cd %s ; tar cvfz %s/control.tar.gz control" % (self.meta_dir, self.scratch_dir) cmd_out, cmd_in, cmd_err = os.popen3(cmd) while cmd_err.readline() != "": pass cmd_out.close() cmd_in.close() cmd_err.close() bits = "control.tar.gz" if self.file_list: cmd = "cd %s ; tar cvfz %s/data.tar.gz" % (self.file_dir, self.scratch_dir) cmd_out, cmd_in, cmd_err = os.popen3(cmd) while cmd_err.readline() != "": pass cmd_out.close() cmd_in.close() cmd_err.close() bits = bits + " data.tar.gz" file = "%s_%s_%s.ipk" % (self.package, self.version, self.architecture) cmd = "cd %s ; tar cvfz %s/%s %s" % (self.scratch_dir, dirname, file, bits) cmd_out, cmd_in, cmd_err = os.popen3(cmd) while cmd_err.readline() != "": pass cmd_out.close() cmd_in.close() cmd_err.close() def __repr__(self): out = "" # XXX - Some checks need to be made, and some exceptions # need to be thrown. -- a7r if self.package: out = out + "Package: %s\n" % (self.package) if self.version: out = out + "Version: %s\n" % (self.version) if self.depends: out = out + "Depends: %s\n" % (self.depends) if self.provides: out = out + "Provides: %s\n" % (self.provides) if self.replaces: out = out + "Replaces: %s\n" % (self.replaces) if self.conflicts: out = out + "Conflicts: %s\n" % (self.conflicts) if self.suggests: out = out + "Suggests: %s\n" % (self.suggests) if self.recommends: out = out + "Recommends: %s\n" % (self.recommends) if self.section: out = out + "Section: %s\n" % (self.section) if self.architecture: out = out + "Architecture: %s\n" % (self.architecture) if self.maintainer: out = out + "Maintainer: %s\n" % (self.maintainer) if self.md5: out = out + "MD5Sum: %s\n" % (self.md5) if self.size: out = out + "Size: %d\n" % int(self.size) if self.installed_size: out = out + "InstalledSize: %d\n" % int(self.installed_size) if self.filename: out = out + "Filename: %s\n" % (self.filename) if self.source: out = out + "Source: %s\n" % (self.source) if self.description: out = out + "Description: %s\n" % (self.description) out = out + "\n" return out def __del__(self): # XXX - Why is the `os' module being yanked out before Package objects # are being destroyed? -- a7r pass class Packages: """A currently unimplemented wrapper around the ipkg utility.""" def __init__(self): self.packages = {} return def add_package(self, pkg): package = pkg.package arch = pkg.architecture name = ("%s:%s" % (package, arch)) if (not self.packages.has_key(name)): self.packages[name] = pkg (s, outtext) = commands.getstatusoutput("ipkg-compare-versions %s '>' %s" % (pkg.version, self.packages[name].version)) if (s == 0): self.packages[name] = pkg return 0 else: return 1 def read_packages_file(self, fn): f = open(fn, "r") while 1: pkg = Package() pkg.read_control(f) if pkg.get_package(): self.add_package(pkg) else: break f.close() return def write_packages_file(self, fn): f = open(fn, "w") names = self.packages.keys() names.sort() for name in names: f.write(self.packages[name].__repr__()) return def keys(self): return self.packages.keys() def __getitem__(self, key): return self.packages[key] if __name__ == "__main__": package = Package() package.set_package("FooBar") package.set_version("0.1-fam1") package.set_architecture("arm") package.set_maintainer("Testing ") package.set_depends("libc") package.set_description("A test of the APIs.") print "<" sys.stdout.write(package) print ">" package.write_package("/tmp") agtl-0.8.0.3/ipkg-utils-1.7/makePackage000077500000000000000000000005651151564747700173500ustar00rootroot00000000000000#!/usr/bin/python # The general algorithm this program follows goes like this: # Run tar to extract control from control.tar.gz from the package. # Insert the filename, size, and md5 lines before the description. # Call it like this: # find . -name \*.ipk | xargs -n 1 makePackage > Packages import sys import ipkg fn = sys.argv[1] pkg = ipkg.Package(fn) print pkg agtl-0.8.0.3/ipkg-utils-1.7/setup.py000066400000000000000000000015461151564747700167430ustar00rootroot00000000000000import distutils.core long_description = """ iPKG utilities implemented in Python ipkg.py implements Package class ipkg-make-index.py generates index for feed with only latest version of package listed """ distutils.core.setup( name = 'iPKG', version = '0.1', description = 'ipkg utilities implemented in python', long_description = long_description, author = 'Alexander Guy, Jamey Hicks', author_email = 'a7r@andern.org', licence = '??', platforms = 'POSIX', keywords = 'ipkg familiar', url = 'http://www.handhelds.org/sources.html/', py_modules = [ 'ipkg', 'ipkg-make-index' ], scripts = ['ipkg-compare-indexes', 'ipkg-make-index', 'ipkg-update-index'] ) agtl-0.8.0.3/ipkg-utils-1.7/upload-package.cgi000077500000000000000000000301131151564747700205650ustar00rootroot00000000000000#!/usr/local/bin/python import sys, os, cgi, commands, time import re import posixfile import ConfigParser import ipkg import smtplib import libxml2 config = '/etc/packman.conf' feeds_base = '/home/jamey/feeds' feeds_base = '/home/ftp/feeds' cgi.logfile = '/tmp/upload' tmp_dir = '/tmp' ipkgfind_url = 'http://ipkgfind.handhelds.org' update_package_list = 0 cp = ConfigParser.ConfigParser() cp.read(config) try: if cp.has_option('config', 'feeds_base'): feeds_base = cp.get('config', 'feeds_base') if cp.has_option('config', 'logfile'): cgi.logfile = cp.get('config', 'logfile') if cp.has_option('config', 'tmp_dir'): tmp_dir = cp.get('config', 'tmp_dir') if cp.has_option('config', 'update_index'): update_package_list = cp.get('config', 'update_index') except: pass cp = None logprefix = time.strftime('%c') + ' -- ' try: for k in ['REMOTE_ADDR']: if (os.environ.has_key(k)): logprefix = logprefix + ('%s' % (os.environ[k],)) except: pass def log(string): cgi.log(logprefix + ': ' + string) def filename_is_valid(filename): if re.match(r'^[+-._A-Za-z0-9]+$', filename): return 1 return 0 def copy(srcfilename, dstfilename): srcfile = open(srcfilename, 'r') dstfile = open(dstfilename, 'w') if not srcfile: return 1 if not dstfile: return 1 while (1): str = srcfile.read(512) if (str): nbytes = dstfile.write(str) else: break dstfile.close() srcfile.close() return 0 def make_index(feed_dir): os.chdir(feed_dir) f = posixfile.open(tmp_dir + "/Packages.lck", 'w') f.lock('w|') (rc, outtext) = commands.getstatusoutput('/usr/local/bin/ipkg-make-index . > /tmp/Packages') if (rc != 0): print ('Failed to create Packages file with error=%s and output=%s' % (rc,outtext)) else: commands.getstatusoutput('mv /tmp/Packages Packages') f.lock('u') f.close() print 'Updated Packages' return def update_index(feed_dir, pkgfilename): f = posixfile.open(tmp_dir + "/Packages.lck", 'w') f.lock('w|') (rc, outtext) = commands.getstatusoutput('ipkg-update-index %s %s' % (feed_dir, pkgfilename) ) if (rc != 0): print ('Failed to update Packages file with error=%s and output=%s' % (rc,outtext)) f.lock('u') f.close() print ('added %s to Packages' % (pkgfilename,)) return def announce_to_cia(uploader, feedname, filename, changenotice=None): msg = "From: commits@handhelds.org\r\nTo: commits@picogui.org\r\nContent-Type: text/plain;\r\nSubject: SendToChannels handhelds.org\r\n\r\n{light blue}" + filename +"{normal} uploaded to {light green}" + feedname + "{normal} by {orange}" + uploader + "{normal}\r\n" mailmsg = "From: " + uploader + "\r\nTo: familiar-updates@handhelds.org\r\nContent-Type: text/plain;\r\nSubject: " + filename + " uploaded to " + feedname + "\r\n\r\n\r\n" if changenotice: mailmsg = mailmsg + changenotice + "\r\n" server = smtplib.SMTP('localhost') server.sendmail('commits@handhelds.org', 'commits@picogui.org', msg) server.sendmail(uploader, 'familiar-updates@handhelds.org', mailmsg) server.quit() def fixup_filename(filename): if filename and filename[:8] == "C:\\Temp\\": filename = filename[8:] return filename def update_feed_rss(uploader, feedname, filename, upload_time): rss_filename = feeds_base + '/' + feedname + '/upload.rss' m = re.match(r'([^_]+)_([^_]+)_([^_]+).ipk', filename) if not m: return (pkg_name, pkg_version, pkg_arch) = m.groups() doc = libxml2.parseFile(rss_filename) items = [] c = doc.children.children while c: if c.name == 'item': items.append(c) c = c.next oldest = items[len(items)-1] items_parent = items[0].parent c = oldest.children while c: if c.name == 'title': c.children.setContent('%s (version %s arch %s by %s on %s)' % (pkg_name, pkg_version, pkg_arch, uploader, upload_time)) elif c.name == 'link': c.children.setContent(ipkgfind_url + '/details.phtml?package=' + pkg_name) else: pass c = c.next # now move c from end of list to head of list oldest.unlinkNode() items_parent.children.addPrevSibling(oldest) f = open(rss_filename, 'w') doc.dump(f) def handle_upload(feeditem, fileitem, sigfileitem, srcfileitem, changenotice=None): global logprefix global update_package_list feedname = feeditem.value filename = fileitem.filename sigfilename = sigfileitem.filename srcfilename = srcfileitem.filename filename = fixup_filename(filename) sigfilename = fixup_filename(sigfilename) srcfilename = fixup_filename(srcfilename) # print "filename=%s\n" % (filename,) # print "sigfilename=%s\n" % (sigfilename,) # print "srcfilename=%s\n" % (srcfilename,) if not filename_is_valid(feedname): print ('%s is an invalid filename\n' % (feedname,)) log('invalid feedname %s' % (feedname,)) return 0 if not filename_is_valid(filename): print ('%s is an invalid filename\n' % (filename,)) log('invalid filename %s' % (filename,)) return 0 if not filename_is_valid(sigfilename): print ('%s is an invalid filename\n' % (sigfilename,)) log('invalid sigfilename %s' % (sigfilename,)) return 0 if srcfilename and not filename_is_valid(srcfilename): print ('%s is an invalid filename\n' % (srcfilename,)) log('invalid srcfilename %s' % (srcfilename,)) return 0 if (filename == 'keyring.gpg' or sigfilename == 'keyring.gpg'): print 'not allowed to overwrite keyring' return 0 feed_dir = feeds_base + '/' + feedname + '/' tmp_dir = "/tmp/" file = open(tmp_dir + filename, 'w') file.write(fileitem.value) file.close() sigfile = open(tmp_dir + sigfilename, 'w') sigfile.write(sigfileitem.value) sigfile.close() ipk = ipkg.Package(tmp_dir + filename) if not ipk: print '

Unable to parse ' + filename + '

' return 0 if not srcfilename and not ipk.source and ipk.architecture != "all": print '

Error: ' + filename + ' lacks a Source field in its control file.

' return 0 if not ipk.isdeb: print '

Error: ' + filename + ' is an old-format archive. Please upgrade ipkg-build.

' return 0 if ipk.filename_header: print '

Error: ' + filename + ' has a Filename header in its control file.

' return 0 keyringfile = feed_dir + 'keyring.gpg' try: (exitstatus, outtext) = commands.getstatusoutput("/usr/local/bin/gpgv --keyring %s %s" % (keyringfile, tmp_dir + sigfilename)) except: cgi.log('caught an exception') return 0 print '
' + outtext + '
' if (exitstatus != 0): log('/usr/local/bin/gpgv %s %s exitstatus=%d' % (filename, sigfilename, exitstatus,)) log(' ' + outtext) rc = exitstatus if (rc != 0): return 0 match = re.search("Good signature from.*\<(.+)\>", outtext) if (match): uploader = match.group(1) announce_to_cia(uploader, feedname, filename) update_feed_rss(uploader, feedname, filename, time.strftime('%c')) rc = copy(tmp_dir + filename, feed_dir + filename) if (rc != 0): print ('

error %s copying %s to %s' % (rc, tmp_dir + filename, feed_dir + filename)) os.unlink(tmp_dir + filename) rc = copy(tmp_dir + sigfilename, feed_dir + sigfilename) if (rc != 0): print ('

error %s copying %s to %s' % (rc, tmp_dir + sigfilename, feed_dir + sigfilename)) os.unlink(tmp_dir + sigfilename) cgi.logfile = feed_dir + 'upload.log' log('uploaded %s the package %s %s' % (feedname, filename, sigfilename)) if changenotice: cgi.log('\t' + changenotice) if srcfilename: log('srcfile=' + srcfilename) overridefile = open(feed_dir + filename + ".override", 'w') overridefile.write('Source: ' + srcfilename + '\n') overridefile.close() srcfile= open(tmp_dir + srcfilename, 'w') srcfile.write(srcfileitem.value) srcfile.close() log('srcfile written to /tmp') rc = copy(tmp_dir + srcfilename, feed_dir + srcfilename) log('copied srcfile to feed, rc=%s' % (rc,)) if (rc != 0): print ('

error %s copying %s to %s' % (rc, tmp_dir + sigfilename, feed_dir + sigfilename)) os.unlink(tmp_dir + srcfilename) log('uploaded %s the source file %s' % (feedname, srcfilename)) print "

Packages file now updated every 10 minutes by cron job

\n" # remove the tmp files return 1 def main(): global update_package_list print "Content-type: text/html\n" form = cgi.FieldStorage() print '' print 'Handhelds.Org Package Manager -- upload' print '

Handhelds.Org Package Manager -- upload

' if form and form.has_key("filename") and form.has_key("signaturefilename") and form.has_key("sourcefilename"): print '
    ' print '
  • ' print form["feedname"].value print '
  • ' print form["filename"].filename print '
  • ' print form["signaturefilename"].filename print '
  • ' print form["sourcefilename"].filename print '
' if form.has_key('update_index'): update_package_list = form["update_index"] if form.has_key('changenotice'): changenotice= form['changenotice'] else: changenotice=None if handle_upload(form["feedname"], form["filename"], form["signaturefilename"],form["sourcefilename"], changenotice): print '

Upload completed successfully' print ('

Updated Packages File' % (form["feedname"].value, )) else: print '

Upload failed' elif form and form.has_key("feedname") and form.has_key("update_index"): # feed_dir = feeds_base + '/' + form["feedname"].value + '/' # make_index(feed_dir) print "Packages file now updated hourly by cron job at 11 after the hour\n" else: print """

Signing Packages

The following command will generate the file foo.ipk.asc, containing an ASCII signature of the package foo.ipk:
           gpg -sb --armor foo.ipk
       

See Jamey's announcement of this package upload utility for some more details.

Package to Upload

Feed Name
Filename
OpenPGP Signature Filename
Sources Filename - Optional
Change Notice - Optional

Please make sure that your package's control file has a Source field.

The Source field is intended to allow automated retrieval of the source package from which your package was built. If you are uploading a source package along with your binary package, the Source field should contain the name of the source package. Otherwise, it should contain one of the following:

  • a single URL pointing to a source tarball
  • multiple URLs pointing to the multiple files (.dsc, .orig.tar.gz, .diff.gz) comprising a Debian source package
""" print '' print '' main() agtl-0.8.0.3/maemo/000077500000000000000000000000001151564747700137265ustar00rootroot00000000000000agtl-0.8.0.3/maemo/advancedcaching/000077500000000000000000000000001151564747700170105ustar00rootroot00000000000000agtl-0.8.0.3/maemo/advancedcaching/build_myapp.py000077500000000000000000000117651151564747700217040ustar00rootroot00000000000000#!/usr/bin/python2.5 # -*- coding: utf-8 -*- ## 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; version 2 only. ## ## 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. ## import py2deb import os if __name__ == "__main__": try: os.chdir(os.path.dirname(sys.argv[0])) except: pass print p=py2deb.Py2deb("advancedcaching") #This is the package name and MUST be in lowercase! (using e.g. "advancedcaching" fails miserably...) p.description='''AGTL, the all-in-one solution for on- and offline geocaching, makes geocaching paperless! It downloads geocaches including their description, hints, difficulty levels and images. No premium account needed. Searching for caches in your local db is a matter of seconds. . - Map view - supporting Open Street Maps and Open Cycle Maps by default, configurable for other map types, including google maps. - GPS view - shows the distance and direction to the selected geocache. - Cache details - all necessary details are available even in offline mode. - Paperless geocaching features - take notes for a geocache on the go, see the hints and spoiler images, check the latest logs. - Multicache calculation help - Let your phone do the math for you. Working for the most multi-stage geocaches, AGTL finds the coordinate calculations and let you enter the missing variables. - Fieldnotes support - Ever came home after a long tour and asked yourself which geocaches you found? Never again: Log your find in the field and upload notes and log text when you're at home. Review them on the geocaching website and post the logs. - Text-to-Speech-Feature! - Select a target, activate TTS and put your headphones on to enjoy completely stealth geocaching. - Download map tiles for selected zoom levels - for offline use. - Advanced waypoint handling - AGTL finds waypoints in the geocache descriptions, in the list of waypoints and even in your notes. For your convenience, they're displayed on the map as well - see where you have to go next. - Search for places - in the geonames.org database to navigate quickly. - Sun compass - Compensates the lack of a magnetic compass. - Instant update feature - Follow web site updates as soon as possible. . AGTL is Open source and in active development.''' p.author="Daniel Fett" p.mail="advancedcaching@fragcom.de" p.depends = "python2.5, python-gtk2, python-simplejson, python-location, python-hildon (>= 0.9.0-1maemo17), python-gtkhtml2, python-dbus, python-osso" p.section="user/navigation" p.icon = "src/usr/share/icons/hicolor/48x48/hildon/advancedcaching.png" p.arch="all" #should be all for python, any for all arch p.urgency="low" #not used in maemo onl for deb os p.distribution="fremantle" p.repository="extras-devel" p.xsbc_bugtracker="http://github.com/webhamster/advancedcaching\nXB-Maemo-Display-Name: Advanced Geocaching Tool for Linux" # p.postinstall="""#!/bin/sh # chmod +x /usr/bin/advancedcaching.py""" #Set here your post install script # p.postremove="""#!/bin/sh # chmod +x /usr/bin/advancedcaching.py""" #Set here your post remove script # p.preinstall="""#!/bin/sh # chmod +x /usr/bin/advancedcaching.py""" #Set here your pre install script # p.preremove="""#!/bin/sh # chmod +x /usr/bin/advancedcaching.py""" #Set here your pre remove script version = "0.8.0.1" build = "0" # for the first build of this version of your software. Increment for later re-builds of the same version of your software. # Text with changelog information to be displayed in the package "Details" tab of the Maemo Application Manager changeloginformation = """ Bug fix release and new feature 'fieldnotes default text'. - Include settings dialog to set the default text for fieldnotes - Fix follow position setting (N900) - Fix configure username/password message on GTK interface - Fix size of options dialog - Set correct openstreetmap tile server - Fix disappearing icons in portrait mode - Set gpsd connection to nonblocking on GTK interface - Online updates for GTK interface """ dir_name = "src" #Name of the subfolder containing your package source files (e.g. usr\share\icons\hicolor\scalable\myappicon.svg, usr\lib\myapp\somelib.py). We suggest to leave it named src in all projects and will refer to that in the wiki article on maemo.org #Thanks to DareTheHair from talk.maemo.org for this snippet that recursively builds the file list for root, dirs, files in os.walk(dir_name): real_dir = root[len(dir_name):] fake_file = [] for f in files: fake_file.append(root + os.sep + f + "|" + f) if len(fake_file) > 0: p[real_dir] = fake_file print p r = p.generate(version,build,changelog=changeloginformation,tar=True,dsc=True,changes=True,build=False,src=True) agtl-0.8.0.3/make-ipk.sh000077500000000000000000000016261151564747700146720ustar00rootroot00000000000000#!/bin/sh IPKG='ipkg-utils-1.7/ipkg-build' PKGROOT='freerunner' VERSION="Version: "`grep -oP "(?<=version=').*(?=')" files/setup.py` # create dirs first, maybe they don't exist yet mkdir -p $PKGROOT/usr/bin/ mkdir -p $PKGROOT/usr/share/applications/ mkdir -p $PKGROOT/usr/share/pixmaps/ mkdir -p $PKGROOT/usr/lib/site-python/advancedcaching/ # copy files for package to their desired place cp --preserve=all files/agtl $PKGROOT/usr/bin/ cp files/advancedcaching.desktop $PKGROOT/usr/share/applications/ cp files/advancedcaching.png $PKGROOT/usr/share/pixmaps/ cp -R files/advancedcaching/* $PKGROOT/usr/lib/site-python/advancedcaching/ # remove overhead rm -rf $PKGROOT/usr/lib/site-python/advancedcaching/*.pyc rm -rf $PKGROOT/usr/lib/site-python/advancedcaching/*.py~ # remove alls gedit temporary files too sed -i -e "s/Version: .*/$VERSION/" $PKGROOT/CONTROL/control $IPKG -o root -g root freerunner agtl-0.8.0.3/make-maemo-deb.sh000077500000000000000000000022411151564747700157270ustar00rootroot00000000000000#!/bin/sh #IPKG='../ipkg-utils-1.7/ipkg-build' PKGROOT='maemo/advancedcaching/src/' VERSION=$1 BUILD=$2 if [ "$VERSION" == "" ] ; then echo "gimme version, plz" exit fi if [ "$BUILD" == "" ] ; then echo "gimme build, plz" exit fi mkdir -p $PKGROOT/usr/share/applications/hildon/ mkdir -p $PKGROOT/usr/share/icons/hicolor/48x48/hildon/ mkdir -p $PKGROOT/opt/advancedcaching/actors mkdir -p $PKGROOT/opt/advancedcaching/data cp files/advancedcaching-48.png $PKGROOT/usr/share/icons/hicolor/48x48/hildon/advancedcaching.png cp files/advancedcaching-maemo.desktop $PKGROOT/usr/share/applications/hildon/advancedcaching.desktop cp -R files/advancedcaching/* $PKGROOT/opt/advancedcaching/ rm $PKGROOT/opt/advancedcaching/*.pyc rm $PKGROOT/opt/advancedcaching/*.pyo rm $PKGROOT/opt/advancedcaching/*.class rm $PKGROOT/opt/advancedcaching/*~ rm -r $PKGROOT/opt/advancedcaching/advcaching.egg-info rm -r $PKGROOT/opt/advancedcaching/build rm -r $PKGROOT/opt/advancedcaching/dist sed -i -e "s/version = \".*\"/version = \"$VERSION\"/" $PKGROOT/../build_myapp.py sed -i -e "s/build = \".*\"/build = \"$BUILD\"/" $PKGROOT/../build_myapp.py cd $PKGROOT cd .. python2.5 build_myapp.py agtl-0.8.0.3/release-checklist.txt000066400000000000000000000005121151564747700167560ustar00rootroot00000000000000- check version number in core.py - check version number in cachedownloader.py - check version number in fieldnotesuploader.py - update changelog - commit new version - tag new version - push with --tags - build for python: cd files; python setup.py bdist_egg upload; make sure new directories are contained in the setup.py file agtl-0.8.0.3/stuff/000077500000000000000000000000001151564747700137575ustar00rootroot00000000000000agtl-0.8.0.3/stuff/gpx-example-formatted.xml000066400000000000000000000167161151564747700207260ustar00rootroot00000000000000 Cache Listing Generated from Geocaching.com This is an individual cache generated from Geocaching.com Account "DrAlzheimer" From Geocaching.com contact@geocaching.com http://www.geocaching.com Geocaching - High Tech Treasure Hunting cache, geocache GC20FX5 Geheimnisvolles Saarland: Wasserfall im Mookenloch by DrAlzheimer, Multi-cache (1.5/3) http://www.geocaching.com/seek/cache_details.aspx?guid=ad6f5c3d-c620-48f8-9921-782d325f9396 Geheimnisvolles Saarland: Wasserfall im Mookenloch Geocache Geocache|Multi-cache Geheimnisvolles Saarland: Wasserfall im Mookenloch DrAlzheimer DrAlzheimer Multi-cache Regular 1.5 3 Germany Saarland Receive more information such as cache descriptions and hints with a Premium Membership. Visit http://www.geocaching.com/subscribe for more details. Hints available to premium members only. LA20FX5 Sucht hier nach den Koordinaten für [b]STAGE 3[/b]. Ihr könnt euer Cachemobil hier parken, falls noch ein Plätzchen frei ist, ansonsten 500m weiter auf dem Parkplatz von Haus Sonnental. Full-Cycle-Cacher fahren natürlich weiter. Der Schilderwald http://www.geocaching.com/seek/wpt.aspx?WID=90cede5d-13e8-45a7-85c7-bbeace82bbae Der Schilderwald Stages of a Multicache Waypoint|Stages of a Multicache LB20FX5 Hier findet ihr nochmal die Koordinaten für [b]STAGE3[/b]. Das ist eine Backup-Station, falls ihr an [b]STAGE1[/b] nichts finden konntet. Haus Sonnental http://www.geocaching.com/seek/wpt.aspx?WID=d9cb1a3f-7023-43fe-91a7-b876bd3b9555 Haus Sonnental Stages of a Multicache Waypoint|Stages of a Multicache LC20FX5 Folgt dem Weg nach rechts und geht weiter zu [b]TRAIL2[/b]. Abzweigung http://www.geocaching.com/seek/wpt.aspx?WID=393199c5-f52f-4e1f-86ed-5ea4086ab966 Abzweigung Trailhead Waypoint|Trailhead LD20FX5 Folgt dem Weg weiter zu [b]TRAIL3[/b]. Weg http://www.geocaching.com/seek/wpt.aspx?WID=5e3228a3-21ed-40dc-bb18-6e74b2035305 Weg Trailhead Waypoint|Trailhead LE20FX5 Hier findet ihr den besten Einstieg zu [b]STAGE3[/b]. Folgt dann dem Weg weiter zu [b]TRAIL4[/b]. Weg http://www.geocaching.com/seek/wpt.aspx?WID=67ef6040-c731-4f73-a48f-f08fdec08963 Weg Trailhead Waypoint|Trailhead LG20FX5 Haltet euch hier rechts und geht weiter zu [b]TRAIL5[/b]. Abzweigung http://www.geocaching.com/seek/wpt.aspx?WID=656da828-09fe-43ad-bf37-37596d8afb52 Abzweigung Trailhead Waypoint|Trailhead LH20FX5 Biegt rechts ab und folgt dem ansteigenden Weg bis zum [b]FINAL[/b]. Abzweigung http://www.geocaching.com/seek/wpt.aspx?WID=59b6e90f-a263-4511-9975-daf3fe5ba381 Abzweigung Trailhead Waypoint|Trailhead LJ20FX5 Im Frühjahr und Sommer, wenn der Hang mit Farnen bedeckt ist, ist es hier besonders schön. Die Felswand http://www.geocaching.com/seek/wpt.aspx?WID=7f33e99c-da81-471b-ac1d-b41647a14a3f Die Felswand Reference Point Waypoint|Reference Point LK20FX5 Folgt dem Weg rechts zu [b]TRAIL7[/b]. Abzweigung http://www.geocaching.com/seek/wpt.aspx?WID=9e0cbbe9-5e33-4e0c-a9a4-fedd9988f2c6 Abzweigung Trailhead Waypoint|Trailhead LL20FX5 Ein schmaler Pfad biegt links ab. Folgt ihm den Hang hinunter und ihr gelangt direkt zum Parkplatz von Haus Sonnental. Wenn ihr jedoch den Weg weitergeht, könnt ihr noch den Cache "Alter Trinkwasserhochbehälter" (GC1WM95) machen. Abzweigung http://www.geocaching.com/seek/wpt.aspx?WID=14b55000-edb5-4b52-8c13-7c9baed44882 Abzweigung Trailhead Waypoint|Trailhead agtl-0.8.0.3/stuff/gpx-example.xml000066400000000000000000000145641151564747700167420ustar00rootroot00000000000000Cache Listing Generated from Geocaching.comThis is an individual cache generated from Geocaching.comAccount "DrAlzheimer" From Geocaching.comcontact@geocaching.comhttp://www.geocaching.comGeocaching - High Tech Treasure Huntingcache, geocacheGC20FX5Geheimnisvolles Saarland: Wasserfall im Mookenloch by DrAlzheimer, Multi-cache (1.5/3)http://www.geocaching.com/seek/cache_details.aspx?guid=ad6f5c3d-c620-48f8-9921-782d325f9396Geheimnisvolles Saarland: Wasserfall im MookenlochGeocacheGeocache|Multi-cacheGeheimnisvolles Saarland: Wasserfall im MookenlochDrAlzheimerDrAlzheimerMulti-cacheRegular1.53GermanySaarlandReceive more information such as cache descriptions and hints with a Premium Membership. Visit http://www.geocaching.com/subscribe for more details.Hints available to premium members only.LA20FX5Sucht hier nach den Koordinaten für [b]STAGE 3[/b]. Ihr könnt euer Cachemobil hier parken, falls noch ein Plätzchen frei ist, ansonsten 500m weiter auf dem Parkplatz von Haus Sonnental. Full-Cycle-Cacher fahren natürlich weiter.Der Schilderwaldhttp://www.geocaching.com/seek/wpt.aspx?WID=90cede5d-13e8-45a7-85c7-bbeace82bbaeDer SchilderwaldStages of a MulticacheWaypoint|Stages of a MulticacheLB20FX5Hier findet ihr nochmal die Koordinaten für [b]STAGE3[/b]. Das ist eine Backup-Station, falls ihr an [b]STAGE1[/b] nichts finden konntet.Haus Sonnentalhttp://www.geocaching.com/seek/wpt.aspx?WID=d9cb1a3f-7023-43fe-91a7-b876bd3b9555Haus SonnentalStages of a MulticacheWaypoint|Stages of a MulticacheLC20FX5Folgt dem Weg nach rechts und geht weiter zu [b]TRAIL2[/b].Abzweigunghttp://www.geocaching.com/seek/wpt.aspx?WID=393199c5-f52f-4e1f-86ed-5ea4086ab966AbzweigungTrailheadWaypoint|TrailheadLD20FX5Folgt dem Weg weiter zu [b]TRAIL3[/b].Weghttp://www.geocaching.com/seek/wpt.aspx?WID=5e3228a3-21ed-40dc-bb18-6e74b2035305WegTrailheadWaypoint|TrailheadLE20FX5Hier findet ihr den besten Einstieg zu [b]STAGE3[/b]. Folgt dann dem Weg weiter zu [b]TRAIL4[/b].Weghttp://www.geocaching.com/seek/wpt.aspx?WID=67ef6040-c731-4f73-a48f-f08fdec08963WegTrailheadWaypoint|TrailheadLG20FX5Haltet euch hier rechts und geht weiter zu [b]TRAIL5[/b].Abzweigunghttp://www.geocaching.com/seek/wpt.aspx?WID=656da828-09fe-43ad-bf37-37596d8afb52AbzweigungTrailheadWaypoint|TrailheadLH20FX5Biegt rechts ab und folgt dem ansteigenden Weg bis zum [b]FINAL[/b].Abzweigunghttp://www.geocaching.com/seek/wpt.aspx?WID=59b6e90f-a263-4511-9975-daf3fe5ba381AbzweigungTrailheadWaypoint|TrailheadLJ20FX5Im Frühjahr und Sommer, wenn der Hang mit Farnen bedeckt ist, ist es hier besonders schön.Die Felswandhttp://www.geocaching.com/seek/wpt.aspx?WID=7f33e99c-da81-471b-ac1d-b41647a14a3fDie FelswandReference PointWaypoint|Reference PointLK20FX5Folgt dem Weg rechts zu [b]TRAIL7[/b].Abzweigunghttp://www.geocaching.com/seek/wpt.aspx?WID=9e0cbbe9-5e33-4e0c-a9a4-fedd9988f2c6AbzweigungTrailheadWaypoint|TrailheadLL20FX5Ein schmaler Pfad biegt links ab. Folgt ihm den Hang hinunter und ihr gelangt direkt zum Parkplatz von Haus Sonnental. Wenn ihr jedoch den Weg weitergeht, könnt ihr noch den Cache "Alter Trinkwasserhochbehälter" (GC1WM95) machen.Abzweigunghttp://www.geocaching.com/seek/wpt.aspx?WID=14b55000-edb5-4b52-8c13-7c9baed44882AbzweigungTrailheadWaypoint|Trailhead agtl-0.8.0.3/stuff/setup.py000066400000000000000000000011261151564747700154710ustar00rootroot00000000000000__author__="daniel" __date__ ="$31.08.2009 13:48:46$" from setuptools import setup,find_packages setup ( name = 'advcaching', version = '0.1', packages = find_packages(), # Declare your packages' dependencies here, for eg: install_requires=['foo>=3'], # Fill in these to make your Egg ready for upload to # PyPI author = 'daniel', author_email = '', summary = 'Just another Python package for the cheese shop', url = '', license = '', long_description= 'Long description of the package', # could also include long_description, download_url, classifiers, etc. )