~ubuntu-branches/debian/wheezy/agtl/wheezy

« back to all changes in this revision

Viewing changes to files/advancedcaching/simplegui.py

  • Committer: Bazaar Package Importer
  • Author(s): Heiko Stuebner
  • Date: 2011-01-22 13:55:12 UTC
  • mfrom: (1.1.5 upstream)
  • Revision ID: james.westby@ubuntu.com-20110122135512-1mik0vssgpnx2fgu
Tags: 0.8.0.3-1
New upstream release

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
#!/usr/bin/python
2
2
# -*- coding: utf-8 -*-
3
3
 
4
 
#        Copyright (C) 2009 Daniel Fett
5
 
#         This program is free software: you can redistribute it and/or modify
 
4
#   Copyright (C) 2010 Daniel Fett
 
5
#   This program is free software: you can redistribute it and/or modify
6
6
#   it under the terms of the GNU General Public License as published by
7
7
#   the Free Software Foundation, either version 3 of the License, or
8
8
#   (at your option) any later version.
15
15
#   You should have received a copy of the GNU General Public License
16
16
#   along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
17
#
18
 
#        Author: Daniel Fett advancedcaching@fragcom.de
 
18
#   Author: Daniel Fett agtl@danielfett.de
 
19
#   Jabber: fett.daniel@jaber.ccc.de
 
20
#   Bugtracker and GIT Repository: http://github.com/webhamster/advancedcaching
19
21
#
20
22
 
21
23
 
22
24
# deps: python-html python-image python-netclient python-misc python-pygtk python-mime python-json
23
25
 
24
26
# todo:
25
 
# download logs
26
 
# parse attributes
27
 
# add "next waypoint" button
28
 
# add description to displayed images
 
27
# parse attributes?
 
28
# add "next waypoint" button?
 
29
# add description to displayed images?
29
30
# add translation support?
30
 
# download in seperate thread?
31
 
 
32
 
 
33
31
 
34
32
### For the gui :-)
35
33
import math
36
34
 
37
 
import extListview
 
35
from astral import Astral
38
36
import geo
39
37
import geocaching
40
38
import gobject
41
39
import gtk
42
 
import gtk.glade
43
 
import openstreetmap
44
 
import os
 
40
 
 
41
import logging
 
42
logger = logging.getLogger('simplegui')
 
43
 
 
44
try:
 
45
    import gtk.glade
 
46
    import extListview
 
47
except (ImportError):
 
48
    logger.info( "Please install glade if you're NOT on the maemo platform.")
45
49
import pango
 
50
from os import path, extsep
46
51
import re
47
 
 
48
 
 
49
 
class SimpleGui(object):
 
52
from cachedownloader import HTMLManipulations
 
53
from gtkmap import *
 
54
from gui import Gui
 
55
 
 
56
 
 
57
class SimpleGui(Gui):
50
58
    USES = ['gpsprovider']
51
59
    XMLFILE = "freerunner.glade"
52
60
 
53
 
    MAP_FACTOR = 0
54
 
    CACHE_SIZE = 20
55
 
    CLICK_RADIUS = 20
56
 
    TOO_MUCH_POINTS = 30
57
 
    CACHES_ZOOM_LOWER_BOUND = 9
58
 
    CACHE_DRAW_SIZE = 10
59
 
    CACHE_DRAW_FONT = pango.FontDescription("Sans 4")
60
 
    MESSAGE_DRAW_FONT = pango.FontDescription("Sans 5")
61
 
    MESSAGE_DRAW_COLOR = gtk.gdk.color_parse('black')
62
 
 
63
 
 
64
61
    REDRAW_DISTANCE_TRACKING = 50 # distance from center of visible map in px
65
62
    REDRAW_DISTANCE_MINOR = 4 # distance from last displayed point in px
66
63
    DISTANCE_DISABLE_ARROW = 5 # meters
67
64
 
68
65
    MAX_NUM_RESULTS = 50
69
 
    MAX_NUM_RESULTS_SHOW = 100
70
66
 
71
67
 
72
68
    ARROW_OFFSET = 1.0 / 3.0 # Offset to center of arrow, calculated as 2-x = sqrt(1^2+(x+1)^2)
73
69
    ARROW_SHAPE = [(0, -2 + ARROW_OFFSET), (1, + 1 + ARROW_OFFSET), (0, 0 + ARROW_OFFSET), (-1, 1 + ARROW_OFFSET)]
74
70
 
75
 
    # map markers colors
76
 
    COLOR_MARKED = gtk.gdk.color_parse('yellow')
77
 
    COLOR_DEFAULT = gtk.gdk.color_parse('blue')
78
 
    COLOR_FOUND = gtk.gdk.color_parse('grey')
79
 
    COLOR_REGULAR = gtk.gdk.color_parse('green')
80
 
    COLOR_MULTI = gtk.gdk.color_parse('orange')
81
 
    COLOR_CACHE_CENTER = gtk.gdk.color_parse('black')
82
 
    COLOR_CURRENT_CACHE = gtk.gdk.color_parse('red')
83
 
    COLOR_WAYPOINTS = gtk.gdk.color_parse('deeppink')
84
 
    COLOR_CURRENT_POSITION = gtk.gdk.color_parse('red')
85
 
    COLOR_TARGET = gtk.gdk.color_parse('black')
86
 
    COLOR_CROSSHAIR = gtk.gdk.color_parse("black")
87
 
    COLOR_LINE_INVERT = gtk.gdk.color_parse("blue")
88
71
 
89
72
    # arrow colors and sizes
90
73
    COLOR_ARROW_DEFAULT = gtk.gdk.color_parse("green")
93
76
    COLOR_ARROW_DISABLED = gtk.gdk.color_parse("red")
94
77
    COLOR_ARROW_CIRCLE = gtk.gdk.color_parse("darkgray")
95
78
    COLOR_ARROW_OUTER_LINE = gtk.gdk.color_parse("black")
 
79
    COLOR_ARROW_CROSS = gtk.gdk.color_parse('darkslategray')
 
80
    COLOR_ARROW_ERROR = gtk.gdk.color_parse('gold')
 
81
    COLOR_NORTH_INDICATOR = gtk.gdk.color_parse("gold")
 
82
    COLOR_SUN_INDICATOR = gtk.gdk.color_parse("yellow")
 
83
    COLOR_SUN_INDICATOR_TEXT = gtk.gdk.color_parse("black")
 
84
    ARROW_LINE_WIDTH = 3
96
85
    NORTH_INDICATOR_SIZE = 30
 
86
    FONT_NORTH_INDICATOR = pango.FontDescription("Sans 9")
 
87
    FONT_SUN_INDICATOR = pango.FontDescription("Sans 8")
 
88
 
97
89
 
98
90
    # quality indicator
99
91
    COLOR_QUALITY_OUTER = gtk.gdk.color_parse("black")
108
100
        'options_show_name',
109
101
        'download_noimages',
110
102
        'options_hide_found',
 
103
        'options_map_double_size',
111
104
    ]
112
105
    SETTINGS_INPUTS = [
113
106
        'download_output_dir',
117
110
        'download_map_path'
118
111
    ]
119
112
                
120
 
    def __init__(self, core, pointprovider, userpointprovider, dataroot):
 
113
    def __init__(self, core, dataroot):
121
114
    
122
115
        gtk.gdk.threads_init()
123
 
        self.ts = openstreetmap.TileServer()
124
 
        openstreetmap.TileLoader.noimage = gtk.gdk.pixbuf_new_from_file(os.path.join(dataroot, 'noimage.png'))
125
 
                
 
116
        self._prepare_images(dataroot)
 
117
        
126
118
        self.core = core
127
 
        self.pointprovider = pointprovider
128
 
        self.userpointprovider = userpointprovider
 
119
        self.core.connect('map-marks-changed', self._on_map_changed)
 
120
        self.core.connect('cache-changed', self._on_cache_changed)
 
121
        self.core.connect('target-changed', self._on_target_changed)
 
122
        self.core.connect('good-fix', self._on_good_fix)
 
123
        self.core.connect('no-fix', self._on_no_fix)
 
124
        self.core.connect('settings-changed', self._on_settings_changed)
 
125
        self.core.connect('save-settings', self._on_save_settings)
 
126
        self.core.connect('error', lambda caller, message: self.show_error(message))
 
127
        self.core.connect('progress', lambda caller, fraction, text: self.set_progress(fraction, text))
 
128
        self.core.connect('hide-progress', lambda caller: self.hide_progress())
 
129
        self.core.connect('fieldnotes-changed', self._on_fieldnotes_changed)
 
130
 
 
131
        self.settings = {}
 
132
 
 
133
        Map.set_config(self.core.settings['map_providers'], self.core.settings['download_map_path'], self.noimage_cantload, self.noimage_loading)
 
134
        #OsdLayer.set_layout(pango.FontDescription("Nokia Sans Maps 13"), gtk.gdk.color_parse('black'))
 
135
 
129
136
                
130
137
        self.format = geo.Coordinate.FORMAT_DM
131
138
 
132
139
        # @type self.current_cache geocaching.GeocacheCoordinate
133
140
        self.current_cache = None
134
141
                
135
 
        self.current_target = None
136
142
        self.gps_data = None
137
143
        self.gps_has_fix = False
138
 
        self.gps_last_position = None
 
144
        self.gps_last_good_fix = None
 
145
        self.gps_last_screen_position = (0, 0)
139
146
                
140
 
        self.dragging = False
141
147
        self.block_changes = False
142
148
                
143
149
        self.image_zoomed = False
144
150
        self.image_no = 0
145
 
        self.images = []
146
 
 
147
 
        
148
 
        self.pixmap_north_indicator = None
 
151
        self.images = []        
 
152
        self.north_indicator_layout = None
149
153
        self.drawing_area_configured = self.drawing_area_arrow_configured = False
150
 
        self.drag_offset_x = 0
151
 
        self.drag_offset_y = 0
152
154
        self.notes_changed = False
153
155
        self.fieldnotes_changed = False
154
 
        self.map_center_x, self.map_center_y = 100, 100
155
 
        self.inhibit_zoom = False
156
 
        self.inhibit_expose = False
157
 
        
 
156
 
 
157
        self.astral = Astral()
158
158
        
159
159
        global xml
160
 
        xml = gtk.glade.XML(os.path.join(dataroot, self.XMLFILE))
 
160
        xml = gtk.glade.XML(path.join(dataroot, self.XMLFILE))
161
161
        self.load_ui()
 
162
        # self.build_tile_loaders()
 
163
 
 
164
 
 
165
    def _get_geocaches_callback(self, visible_area, maxresults):
 
166
        return self.core.pointprovider.get_points_filter(visible_area, False if self.settings['options_hide_found'] else None, maxresults)
 
167
 
 
168
 
 
169
    def _prepare_images(self, dataroot):
 
170
        p = "%s%s%%s" % (path.join(dataroot, '%s'), extsep)
 
171
        self.noimage_cantload = p % ('noimage-cantload', 'png')
 
172
        self.noimage_loading = p % ('noimage-loading', 'png')
 
173
 
 
174
 
 
175
    def _on_cache_changed(self, something, cache):
 
176
        
 
177
        if self.current_cache != None \
 
178
            and cache.name == self.current_cache.name:
 
179
            self.show_cache(cache)
 
180
        return False
 
181
 
 
182
    def set_current_cache(self, cache):
 
183
        pass
 
184
 
 
185
 
 
186
    def _on_map_changed(self, something):
 
187
        self.map.redraw_layers()
 
188
        return False
 
189
 
162
190
        
163
191
    def load_ui(self):
164
192
        self.window = xml.get_widget("window1")
178
206
        self.button_download_details = xml.get_widget('button_download_details')
179
207
        self.button_track = xml.get_widget('togglebutton_track')
180
208
        self.check_result_marked = xml.get_widget('check_result_marked')
 
209
        self.label_fieldnotes = xml.get_widget('label_fieldnotes')
 
210
        self.button_upload_fieldnotes = xml.get_widget('button_upload_fieldnotes')
 
211
        self.button_check_updates = xml.get_widget('button_check_updates')
181
212
                
182
213
        self.label_bearing = xml.get_widget('label_bearing')
183
214
        self.label_dist = xml.get_widget('label_dist')
188
219
        
189
220
        self.input_export_path = xml.get_widget('input_export_path')
190
221
                
191
 
        self.drawing_area.set_double_buffered(False)
192
 
        self.drawing_area.connect("expose_event", self.expose_event)
193
 
        self.drawing_area.connect("configure_event", self.__configure_event)
194
 
        self.drawing_area.connect("button_press_event", self.__drag_start)
195
 
        self.drawing_area.connect("scroll_event", self.scroll)
196
 
        self.drawing_area.connect("button_release_event", self.__drag_end)
197
 
        self.drawing_area.connect("motion_notify_event", self.__drag)
198
 
        self.drawing_area.set_events(gtk.gdk.EXPOSURE_MASK | gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.BUTTON_RELEASE_MASK | gtk.gdk.POINTER_MOTION_MASK | gtk.gdk.SCROLL)
199
 
                
200
222
        # arrow drawing area
201
 
        self.drawing_area_arrow.connect("expose_event", self.expose_event_arrow)
202
 
        self.drawing_area_arrow.connect("configure_event", self.__configure_event_arrow)
 
223
        self.drawing_area_arrow.connect("expose_event", self._expose_event_arrow)
 
224
        self.drawing_area_arrow.connect("configure_event", self._configure_event_arrow)
203
225
        self.drawing_area_arrow.set_events(gtk.gdk.EXPOSURE_MASK)
204
 
                
 
226
 
 
227
        # map
 
228
        self._configure_map()
 
229
        xml.get_widget('tableMap').attach(self.map, 0, 4, 1, 2)
 
230
 
205
231
                
206
232
        self.cache_elements = {
207
233
            'name_downloaded': xml.get_widget('link_cache_name'),
260
286
        # Create the renderer used in the listview
261
287
        txtRdr        = gtk.CellRendererText()
262
288
        (
263
 
            ROW_TITLE,
264
 
            ROW_TYPE,
265
 
            ROW_SIZE,
266
 
            ROW_TERRAIN,
267
 
            ROW_DIFF,
268
 
            ROW_ID,
269
 
        ) = range(6)
 
289
         ROW_TITLE,
 
290
         ROW_TYPE,
 
291
         ROW_SIZE,
 
292
         ROW_TERRAIN,
 
293
         ROW_DIFF,
 
294
         ROW_ID,
 
295
         ) = range(6)
270
296
        columns = (
271
 
            ('name', [(txtRdr, gobject.TYPE_STRING)], (ROW_TITLE,), False, True),
272
 
            ('type', [(txtRdr, gobject.TYPE_STRING)], (ROW_TYPE,), False, True),
273
 
            ('size', [(txtRdr, gobject.TYPE_STRING)], (ROW_SIZE, ROW_ID), False, True),
274
 
            ('ter', [(txtRdr, gobject.TYPE_STRING)], (ROW_TERRAIN, ROW_ID), False, True),
275
 
            ('dif', [(txtRdr, gobject.TYPE_STRING)], (ROW_DIFF, ROW_ID), False, True),
276
 
            ('ID', [(txtRdr, gobject.TYPE_STRING)], (ROW_ID,), False, True),
277
 
        )
 
297
                   ('name', [(txtRdr, gobject.TYPE_STRING)], (ROW_TITLE,), False, True),
 
298
                   ('type', [(txtRdr, gobject.TYPE_STRING)], (ROW_TYPE,), False, True),
 
299
                   ('size', [(txtRdr, gobject.TYPE_STRING)], (ROW_SIZE, ROW_ID), False, True),
 
300
                   ('ter', [(txtRdr, gobject.TYPE_STRING)], (ROW_TERRAIN, ROW_ID), False, True),
 
301
                   ('dif', [(txtRdr, gobject.TYPE_STRING)], (ROW_DIFF, ROW_ID), False, True),
 
302
                   ('ID', [(txtRdr, gobject.TYPE_STRING)], (ROW_ID,), False, True),
 
303
                   )
278
304
        self.cachelist = listview = extListview.ExtListView(columns, sortable=True, useMarkup=True, canShowHideColumns=False)
279
305
        self.cachelist_contents = []
280
306
        listview.connect('extlistview-button-pressed', self.on_search_cache_clicked)
281
307
        xml.get_widget('scrolledwindow_search').add(listview)
282
308
                
283
309
        (
284
 
            COL_COORD_NAME,
285
 
            COL_COORD_LATLON,
286
 
            COL_COORD_ID,
287
 
            COL_COORD_COMMENT,
288
 
        ) = range(4)
 
310
         COL_COORD_NAME,
 
311
         COL_COORD_LATLON,
 
312
         COL_COORD_ID,
 
313
         COL_COORD_COMMENT,
 
314
         ) = range(4)
289
315
        columns = (
290
 
            ('name', [(txtRdr, gobject.TYPE_STRING)], (COL_COORD_NAME), False, True),
291
 
            ('pos', [(txtRdr, gobject.TYPE_STRING)], (COL_COORD_LATLON), False, True),
292
 
            ('id', [(txtRdr, gobject.TYPE_STRING)], (COL_COORD_ID), False, True),
293
 
            ('comment', [(txtRdr, gobject.TYPE_STRING)], (COL_COORD_COMMENT,), False, True),
294
 
        )
 
316
                   ('name', [(txtRdr, gobject.TYPE_STRING)], (COL_COORD_NAME), False, True),
 
317
                   ('pos', [(txtRdr, gobject.TYPE_STRING)], (COL_COORD_LATLON), False, True),
 
318
                   ('id', [(txtRdr, gobject.TYPE_STRING)], (COL_COORD_ID), False, True),
 
319
                   ('comment', [(txtRdr, gobject.TYPE_STRING)], (COL_COORD_COMMENT,), False, True),
 
320
                   )
295
321
        self.coordlist = extListview.ExtListView(columns, sortable=True, useMarkup=False, canShowHideColumns=False)
296
322
        self.coordlist.connect('extlistview-button-pressed', self.on_waypoint_clicked)
297
323
        xml.get_widget('scrolledwindow_coordlist').add(self.coordlist)
298
324
 
299
325
        self.notebook_all.set_current_page(1)
300
 
        gobject.timeout_add_seconds(10, self.__check_notes_save)
301
 
 
302
 
 
 
326
        gobject.timeout_add_seconds(10, self._check_notes_save)
 
327
 
 
328
    def _configure_map(self):
 
329
        try:
 
330
            coord = geo.Coordinate(self.settings['map_position_lat'], self.settings['map_position_lon'])
 
331
            zoom = self.settings['map_zoom']
 
332
        except KeyError:
 
333
            coord = self._get_best_coordinate(geo.Coordinate(50, 10))
 
334
            zoom = 6
 
335
 
 
336
        self.map = Map(center=coord, zoom=zoom)
 
337
        self.geocache_layer = GeocacheLayer(self._get_geocaches_callback, self.show_cache)
 
338
        self.marks_layer = MarksLayer()
 
339
        self.map.add_layer(self.geocache_layer)
 
340
        self.map.add_layer(self.marks_layer)
 
341
        self.map.add_layer(OsdLayer())
 
342
 
 
343
        self.core.connect('target-changed', self.marks_layer.on_target_changed)
 
344
        self.core.connect('good-fix', self.marks_layer.on_good_fix)
 
345
        self.core.connect('no-fix', self.marks_layer.on_no_fix)
 
346
        self.map.connect('tile-loader-changed', lambda widget, loader: self._update_zoom_buttons())
 
347
        
303
348
    def on_marked_label_clicked(self, event=None, widget=None):
304
349
        w = xml.get_widget('check_cache_marked')
305
350
        w.set_active(not w.get_active())
306
 
        print w
307
 
 
308
 
    def dmap(self, widget):
309
 
        pass
310
 
 
311
 
    def dunmap(self, widget):
312
 
        pass
313
 
 
314
 
    def __check_notes_save(self):
 
351
 
 
352
    def _check_notes_save(self):
315
353
        if self.current_cache != None and self.notes_changed:
316
 
            self.core.on_notes_changed(self.current_cache, self.cache_elements['notes'].get_text(self.cache_elements['notes'].get_start_iter(), self.cache_elements['notes'].get_end_iter()))
 
354
            self.current_cache.notes = self.cache_elements['notes'].get_text(self.cache_elements['notes'].get_start_iter(), self.cache_elements['notes'].get_end_iter())
 
355
            self.core.save_cache_attribute(self.current_cache, 'notes')
317
356
            self.notes_changed = False
318
357
 
319
358
        if self.current_cache != None and self.fieldnotes_changed:
320
 
            self.core.on_fieldnotes_changed(self.current_cache, self.cache_elements['fieldnotes'].get_text(self.cache_elements['fieldnotes'].get_start_iter(), self.cache_elements['fieldnotes'].get_end_iter()))
 
359
            self.current_cache.fieldnotes = self.cache_elements['fieldnotes'].get_text(self.cache_elements['fieldnotes'].get_start_iter(), self.cache_elements['fieldnotes'].get_end_iter())
 
360
            self.core.save_cache_attribute(self.current_cache, 'fieldnotes')
321
361
            self.fieldnotes_changed = False
322
362
                
323
 
    def __configure_event(self, widget, event):
324
 
        x, y, width, height = widget.get_allocation()
325
 
        self.map_width = int(width  + 2 * width * self.MAP_FACTOR)
326
 
        self.map_height = int(height + 2 * height * self.MAP_FACTOR)
327
 
        self.pixmap = gtk.gdk.Pixmap(widget.window, self.map_width, self.map_height)
328
 
                        
329
 
        self.pixmap_marks = gtk.gdk.Pixmap(widget.window, self.map_width, self.map_height)
330
 
        self.xgc = widget.window.new_gc()
331
 
        self.drawing_area_configured = True
332
 
        self.draw_at_x = 0
333
 
        self.draw_at_y = 0
334
 
        self.draw_root_x = int(-width * self.MAP_FACTOR)
335
 
        self.draw_root_y = int(-height * self.MAP_FACTOR)
336
 
 
337
 
        gobject.idle_add(self.__draw_map)
338
 
                
339
 
                
340
 
    def __configure_event_arrow(self, widget, event):
 
363
                
 
364
                
 
365
    def _configure_event_arrow(self, widget, event):
341
366
        x, y, width, height = widget.get_allocation()
342
367
        self.pixmap_arrow = gtk.gdk.Pixmap(widget.window, width, height)
343
368
        self.xgc_arrow = widget.window.new_gc()
344
 
 
345
 
        if self.pixmap_north_indicator == None:
 
369
        if self.north_indicator_layout == None:
346
370
            # prepare font
347
 
            font = pango.FontDescription("Sans 9")
348
 
            north_indicator_layout = widget.create_pango_layout("N")
349
 
            north_indicator_layout.set_alignment(pango.ALIGN_CENTER)
350
 
            north_indicator_layout.set_font_description(font)
351
 
 
352
 
            self.pixmap_north_indicator = gtk.gdk.Pixmap(widget.window, self.NORTH_INDICATOR_SIZE, self.NORTH_INDICATOR_SIZE)
353
 
            self.xgc_arrow.set_rgb_fg_color(gtk.gdk.color_parse("white"))
354
 
            self.pixmap_north_indicator.draw_rectangle(self.xgc_arrow, True, 0, 0, self.NORTH_INDICATOR_SIZE, self.NORTH_INDICATOR_SIZE)
355
 
            #self.xgc_arrow.set_rgb_fg_color(gtk.gdk.color_parse("black"))
356
 
            #self.pixmap_north_indicator.draw_arc(self.xgc_arrow, True, 0, 0, self.NORTH_INDICATOR_SIZE, self.NORTH_INDICATOR_SIZE, 0, 64 * 360)
357
 
            x, y = north_indicator_layout.get_size()
358
 
            self.xgc_arrow.set_rgb_fg_color(gtk.gdk.color_parse("black"))
359
 
            self.pixmap_north_indicator.draw_layout(self.xgc_arrow, (self.NORTH_INDICATOR_SIZE - x / pango.SCALE) / 2, (self.NORTH_INDICATOR_SIZE - y / pango.SCALE) / 2, north_indicator_layout)
360
 
            # print "%d %d" %((self.NORTH_INDICATOR_SIZE - x / pango.SCALE) / 2, (self.NORTH_INDICATOR_SIZE - y / pango.SCALE) / 2)
361
 
 
 
371
            self.north_indicator_layout = widget.create_pango_layout("N")
 
372
            self.north_indicator_layout.set_alignment(pango.ALIGN_CENTER)
 
373
            self.north_indicator_layout.set_font_description(self.FONT_NORTH_INDICATOR)
362
374
 
363
375
        self.drawing_area_arrow_configured = True
364
 
        gobject.idle_add(self.__draw_arrow)
365
 
                
366
 
    def __coord2point(self, coord):
367
 
        point = self.ts.deg2num(coord)
368
 
        size = self.ts.tile_size()
369
 
                
370
 
        p_x = int(point[0] * size - self.map_center_x * size + self.map_width / 2)
371
 
        p_y = int(point[1] * size - self.map_center_y * size + self.map_height / 2)
372
 
        return [p_x, p_y]
373
 
                
374
 
        
375
 
                
376
 
    def __decode_htmlentities(self, string):
377
 
        def substitute_entity(match):
378
 
            from htmlentitydefs import name2codepoint as n2cp
379
 
            ent = match.group(3)
380
 
            if match.group(1) == "#":
381
 
                # decoding by number
382
 
                if match.group(2) == '':
383
 
                    # number is in decimal
384
 
                    return unichr(int(ent))
385
 
                elif match.group(2) == 'x':
386
 
                    # number is in hex
387
 
                    return unichr(int('0x' + ent, 16))
388
 
            else:
389
 
                # they were using a name
390
 
                cp = n2cp.get(ent)
391
 
                if cp:
392
 
                    return unichr(cp)
393
 
                else:
394
 
                    return match.group()
395
 
 
396
 
        entity_re = re.compile(r'&(#?)(x?)(\w+);')
397
 
        return entity_re.subn(substitute_entity, string)[0]
398
 
        
399
 
    def on_window_destroy(self, target):
 
376
        gobject.idle_add(self._draw_arrow)
 
377
                
 
378
        
 
379
    def on_window_destroy(self, target, more=None, data=None):
400
380
        self.core.on_destroy()
401
381
        gtk.main_quit()
402
382
 
403
 
    def do_events(self):
404
 
        while gtk.events_pending():
405
 
            gtk.main_iteration()
406
 
                
 
383
    def _get_best_coordinate(self, start=None):
 
384
        if start != None:
 
385
            c = start
 
386
        elif self.gps_data != None and self.gps_data.position != None:
 
387
            c = self.gps_data.position
 
388
        elif self.core.current_target != None:
 
389
            c = self.core.current_target
 
390
        else:
 
391
            c = geo.Coordinate(0, 0)
 
392
        return c
407
393
                
408
394
    # called by core
409
395
    def display_results_advanced(self, caches):
410
396
        label = xml.get_widget('label_too_much_results')
411
 
        too_much = len(caches) > self.MAX_NUM_RESULTS
412
 
        if too_much:
413
 
            text = 'Too much results. Only showing first %d.' % self.MAX_NUM_RESULTS
 
397
        too_many = len(caches) > self.MAX_NUM_RESULTS
 
398
        if too_many:
 
399
            text = 'Too many results. Only showing first %d.' % self.MAX_NUM_RESULTS
414
400
            label.set_text(text)
415
401
            label.show()
416
402
            caches = caches[:self.MAX_NUM_RESULTS]
433
419
                t = "?"
434
420
            else:
435
421
                t = "%.1f" % (r.terrain / 10)
436
 
            title =  self.__format_cache_title(r)
 
422
            title = self._format_cache_title(r)
437
423
            rows.append((title, r.type, s, t, d, r.name, ))
438
424
        self.cachelist.replaceContent(rows)
439
425
        self.notebook_search.set_current_page(1)
440
 
        self.redraw_marks()
 
426
        self.map.redraw_layers()
441
427
 
442
428
 
443
429
    @staticmethod
444
 
    def __format_cache_title(cache):
 
430
    def _format_cache_title(cache):
445
431
        m = cache.title.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;')
446
432
        if cache.marked and cache.found:
447
433
            return '<span bgcolor="yellow" fgcolor="gray">%s</span>' % m
452
438
        else:
453
439
            return m
454
440
 
455
 
    def __draw_arrow(self):                
 
441
    def _draw_arrow(self):
 
442
 
 
443
        outer_circle_size = 3
 
444
        circle_border = 10
 
445
        indicator_border = 40
 
446
        distance_attarget = 50
 
447
        distance_near = 150
 
448
        disabled_border_size = 30
 
449
        signal_width = 15
 
450
        error_circle_size = 0.95
 
451
        error_circle_width = 7
 
452
        
456
453
        if not self.drawing_area_arrow_configured:
457
454
            return
458
455
        widget = self.drawing_area_arrow
459
456
        x, y, width, height = widget.get_allocation()
460
457
                        
461
 
        disabled = (not self.gps_has_fix or self.current_target == None or self.gps_data == None or self.gps_data.position == None)
 
458
        disabled = not (self.gps_has_fix and self.gps_target_bearing != None and self.gps_target_distance != None)
462
459
                        
463
460
        self.pixmap_arrow.draw_rectangle(widget.get_style().bg_gc[gtk.STATE_NORMAL],
464
461
                                         True, 0, 0, width, height)
465
462
                                         
466
463
        if disabled:
467
464
            self.xgc_arrow.set_rgb_fg_color(self.COLOR_ARROW_DISABLED)
468
 
 
 
465
            border = disabled_border_size
 
466
            self.pixmap_arrow.draw_line(self.xgc_arrow, border, border, width - border, height - border)
 
467
            self.pixmap_arrow.draw_line(self.xgc_arrow, border, height - border, width - border, border)
469
468
            self.drawing_area_arrow.queue_draw()
470
469
                
471
470
            return False
472
471
        
473
472
        # draw signal indicator
474
 
        self.xgc_arrow.line_width = 1
475
 
        signal_width = 15
476
 
        self.xgc_arrow.set_rgb_fg_color(self.COLOR_QUALITY_OUTER)
477
 
        self.pixmap_arrow.draw_rectangle(self.xgc_arrow, True, width - signal_width - 2, 0, signal_width + 2, height)
478
 
        self.xgc_arrow.set_rgb_fg_color(self.COLOR_QUALITY_INNER)
479
 
        usable_height = height - 1
480
 
        target_height = int(round(usable_height*self.gps_data.quality))
481
 
        self.pixmap_arrow.draw_rectangle(self.xgc_arrow, True, width - signal_width - 1, usable_height - target_height, signal_width, target_height)
 
473
        if self.COLOR_QUALITY_OUTER != None:
 
474
            self.xgc_arrow.line_width = 1
 
475
            self.xgc_arrow.set_rgb_fg_color(self.COLOR_QUALITY_OUTER)
 
476
            self.pixmap_arrow.draw_rectangle(self.xgc_arrow, True, width - signal_width - 2, 0, signal_width + 2, height)
 
477
            self.xgc_arrow.set_rgb_fg_color(self.COLOR_QUALITY_INNER)
 
478
            usable_height = height - 1
 
479
            target_height = int(round(usable_height * self.gps_data.quality))
 
480
            self.pixmap_arrow.draw_rectangle(self.xgc_arrow, True, width - signal_width - 1, usable_height - target_height, signal_width, target_height)
482
481
 
483
 
        display_bearing = self.gps_data.position.bearing_to(self.current_target) - self.gps_data.bearing
484
 
        display_distance = self.gps_data.position.distance_to(self.current_target)
 
482
        display_bearing = self.gps_target_bearing - self.gps_data.bearing
 
483
        display_distance = self.gps_target_distance
485
484
        display_north = math.radians(self.gps_data.bearing)
486
 
 
487
 
        # draw north indicator
 
485
        try:
 
486
            sun_angle = self.astral.get_sun_azimuth_from_fix(self.gps_data)
 
487
        except Exception, e:
 
488
            logger.exception("Couldn't get sun angle: %s" % e)
 
489
            sun_angle = None
 
490
            
 
491
        if sun_angle != None:
 
492
            display_sun = math.radians((- sun_angle + self.gps_data.bearing) % 360)
 
493
 
 
494
        # draw moving direction
 
495
        if self.COLOR_ARROW_CROSS != None:
 
496
            self.xgc_arrow.set_rgb_fg_color(self.COLOR_ARROW_CROSS)
 
497
            self.pixmap_arrow.draw_line(self.xgc_arrow, width / 2, height, width / 2, 0)
 
498
            self.pixmap_arrow.draw_line(self.xgc_arrow, 0, height / 2, width, height / 2)
 
499
 
 
500
 
488
501
        self.xgc_arrow.set_rgb_fg_color(self.COLOR_ARROW_CIRCLE)
489
 
        self.xgc_arrow.line_width = 3
490
 
        indicator_radius = 30
491
 
        indicator_dist = height / 2 - indicator_radius / 2
 
502
        self.xgc_arrow.line_width = outer_circle_size
 
503
        circle_size = min(height, width) / 2 - circle_border
 
504
        indicator_dist = min(height, width) / 2 - indicator_border
492
505
        center_x, center_y = width / 2, height / 2
493
506
 
494
 
        #  outer circle
495
 
                
496
 
 
497
 
        self.pixmap_arrow.draw_arc(self.xgc_arrow, False, center_x - indicator_dist, center_y - indicator_dist, indicator_dist * 2, indicator_dist * 2, 0, 64 * 360)
 
507
        # outer circle
 
508
        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)
498
509
        #position_x - indicator_radius / 2, position_y - indicator_radius / 2,
499
 
                
500
 
        position_x = width / 2 - math.sin(display_north) * indicator_dist
501
 
        position_y = height / 2 - math.cos(display_north) * indicator_dist
502
 
        self.xgc_arrow.set_function(gtk.gdk.AND)
503
 
        self.pixmap_arrow.draw_drawable(self.xgc_arrow, self.pixmap_north_indicator, 0, 0, position_x - self.NORTH_INDICATOR_SIZE / 2, position_y - self.NORTH_INDICATOR_SIZE / 2, -1, -1)
504
 
        self.xgc_arrow.set_function(gtk.gdk.COPY)
505
 
 
506
 
 
507
 
        if (display_distance < 50):
 
510
 
 
511
        if display_distance > self.DISTANCE_DISABLE_ARROW:
 
512
            self.xgc_arrow.line_width = error_circle_width
 
513
            self.xgc_arrow.set_rgb_fg_color(self.COLOR_ARROW_ERROR)
 
514
            self.xgc_arrow.set_dashes(1, (5, 5))
 
515
 
 
516
            self.xgc_arrow.line_style = gtk.gdk.LINE_ON_OFF_DASH 
 
517
            ecc = int(error_circle_size * circle_size)
 
518
            err = min(self.gps_data.error_bearing, 181) # don't draw multiple circles :-)
 
519
            err_start = int((90-(display_bearing + err)) * 64)
 
520
            err_delta = int(err * 2 * 64)
 
521
            self.pixmap_arrow.draw_arc(self.xgc_arrow, False, center_x - ecc, center_y - ecc, ecc * 2, ecc * 2, err_start, err_delta)
 
522
            self.xgc_arrow.line_style = gtk.gdk.LINE_SOLID
 
523
 
 
524
 
 
525
 
 
526
 
 
527
        if (display_distance < distance_attarget):
508
528
            color = self.COLOR_ARROW_ATTARGET
509
 
        elif (display_distance < 150):
 
529
        elif (display_distance < distance_near):
510
530
            color = self.COLOR_ARROW_NEAR
511
531
        else:
512
532
            color = self.COLOR_ARROW_DEFAULT
514
534
                
515
535
        self.xgc_arrow.set_rgb_fg_color(color)
516
536
 
517
 
        if display_distance > self.DISTANCE_DISABLE_ARROW:
518
 
            arrow_transformed = self.__get_arrow_transformed(x, y, width, height, display_bearing)
 
537
        if display_distance != None and display_distance > self.DISTANCE_DISABLE_ARROW:
 
538
            arrow_transformed = self._get_arrow_transformed(x, y, width, height, display_bearing)
519
539
            #self.xgc_arrow.line_style = gtk.gdk.LINE_SOLID
520
 
            self.xgc_arrow.line_width = 5
521
540
            self.pixmap_arrow.draw_polygon(self.xgc_arrow, True, arrow_transformed)
522
541
            self.xgc_arrow.set_rgb_fg_color(self.COLOR_ARROW_OUTER_LINE)
 
542
            self.xgc_arrow.line_width = self.ARROW_LINE_WIDTH
523
543
            self.pixmap_arrow.draw_polygon(self.xgc_arrow, False, arrow_transformed)
 
544
 
 
545
            # north indicator
 
546
            ni_w, ni_h = self.north_indicator_layout.get_size()
 
547
            position_x = int(width / 2 - math.sin(display_north) * indicator_dist - (ni_w / pango.SCALE) / 2)
 
548
            position_y = int(height / 2 - math.cos(display_north) * indicator_dist - (ni_h / pango.SCALE) / 2)
 
549
            self.xgc_arrow.set_function(gtk.gdk.COPY)
 
550
            self.xgc_arrow.set_rgb_fg_color(self.COLOR_NORTH_INDICATOR)
 
551
            self.pixmap_arrow.draw_layout(self.xgc_arrow, position_x, position_y, self.north_indicator_layout)
 
552
 
 
553
            # sun indicator
 
554
            if sun_angle != None:
 
555
                # center of sun indicator is this:
 
556
                sun_center_x = int(width / 2 - math.sin(display_sun) * indicator_dist)
 
557
                sun_center_y = int(height / 2 - math.cos(display_sun) * indicator_dist)
 
558
                # draw the text            
 
559
                sun_indicator_layout = widget.create_pango_layout("sun")
 
560
                sun_indicator_layout.set_alignment(pango.ALIGN_CENTER)
 
561
                sun_indicator_layout.set_font_description(self.FONT_SUN_INDICATOR)
 
562
                # determine size of circle
 
563
                circle_size = int((sun_indicator_layout.get_size()[0] / pango.SCALE) / 2)
 
564
                # draw circle
 
565
                self.xgc_arrow.set_function(gtk.gdk.COPY)
 
566
                self.xgc_arrow.set_rgb_fg_color(self.COLOR_SUN_INDICATOR)
 
567
                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)
 
568
                position_x = int(sun_center_x - (sun_indicator_layout.get_size()[0] / pango.SCALE) / 2)
 
569
                position_y = int(sun_center_y - (sun_indicator_layout.get_size()[1] / pango.SCALE) / 2)
 
570
 
 
571
                self.xgc_arrow.set_rgb_fg_color(self.COLOR_SUN_INDICATOR_TEXT)
 
572
                self.pixmap_arrow.draw_layout(self.xgc_arrow, position_x, position_y, sun_indicator_layout)
 
573
        
 
574
            
 
575
            
 
576
 
524
577
        else:
525
578
            # if we are closer than a few meters, the arrow will almost certainly
526
579
            # point in the wrong direction. therefore, we don't draw the arrow.
527
 
            circle_size = max(height / 2.5, width / 2.5)
 
580
            circle_size = int(max(height / 2.5, width / 2.5))
528
581
            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)
529
582
            self.xgc_arrow.set_rgb_fg_color(self.COLOR_ARROW_OUTER_LINE)
 
583
            self.xgc_arrow.line_width = self.ARROW_LINE_WIDTH
530
584
            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)
531
585
 
532
 
                
 
586
        
533
587
        self.drawing_area_arrow.queue_draw()
534
588
        return False
535
589
 
536
 
    def __get_arrow_transformed(self, x, y, width, height, angle):
537
 
        multiply = height / (2 * (2-self.ARROW_OFFSET))
 
590
    @staticmethod
 
591
    def _get_arrow_transformed(root_x, root_y, width, height, angle):
 
592
        multiply = height / (2 * (2-SimpleGui.ARROW_OFFSET))
538
593
        offset_x = width / 2
539
594
        offset_y = height / 2
540
 
        s = math.sin(math.radians(angle))
541
 
        c = math.cos(math.radians(angle))
542
 
        arrow_transformed = []
543
 
        for (x, y) in self.ARROW_SHAPE:
544
 
            arrow_transformed.append((int(x * multiply * c + offset_x - y * multiply * s),
545
 
                                     int(y * multiply * c + offset_y + x * multiply * s)))
 
595
        s = multiply * math.sin(math.radians(angle))
 
596
        c = multiply * math.cos(math.radians(angle))
 
597
        arrow_transformed = [(int(x * c + offset_x - y * s) + root_x,
 
598
                              int(y * c + offset_y + x * s) + root_y) for x, y in SimpleGui.ARROW_SHAPE]
546
599
        return arrow_transformed
547
600
                
548
601
                
549
 
    def __drag(self, widget, event):
550
 
        if not self.dragging:
551
 
            return
552
 
        self.drag_offset_x = self.drag_start_x - event.x
553
 
        self.drag_offset_y = self.drag_start_y - event.y
554
 
                
555
 
    def __drag_end(self, widget, event):
556
 
        if not self.dragging:
557
 
            return
558
 
        self.dragging = False
559
 
        offset_x = (self.drag_start_x - event.x)
560
 
        offset_y = (self.drag_start_y - event.y)
561
 
        self.map_center_x += (offset_x / self.ts.tile_size())
562
 
        self.map_center_y += (offset_y / self.ts.tile_size())
563
 
        if offset_x ** 2 + offset_y ** 2 < self.CLICK_RADIUS ** 2:
564
 
            self.draw_at_x -= offset_x
565
 
            self.draw_at_y -= offset_y
566
 
            x, y = event.x, event.y
567
 
            c = self.screenpoint2coord([x, y])
568
 
            c1 = self.screenpoint2coord([x-self.CLICK_RADIUS, y-self.CLICK_RADIUS])
569
 
            c2 = self.screenpoint2coord([x + self.CLICK_RADIUS, y + self.CLICK_RADIUS])
570
 
            if self.settings['options_hide_found']:
571
 
                found = False
572
 
            else:
573
 
                found = None
574
 
            cache = self.pointprovider.get_nearest_point_filter(c, c1, c2, found)
575
 
            self.core.on_cache_selected(cache)
576
 
        self.draw_at_x = self.draw_at_y = 0
577
 
        if offset_x != 0 or offset_y != 0:
578
 
            gobject.idle_add(self.__draw_map)
579
 
                
580
 
                        
581
 
    def __drag_draw(self):
582
 
        if not self.dragging:
583
 
            return False
584
 
 
585
 
        delta = math.sqrt((self.last_drag_offset_x - self.drag_offset_x) ** 2 + (self.last_drag_offset_y - self.drag_offset_y) ** 2)
586
 
        if delta < 5:
587
 
            return True
588
 
 
589
 
        self.last_drag_offset_x = self.drag_offset_x
590
 
        self.last_drag_offset_y = self.drag_offset_y
591
 
 
592
 
        x, y, width, height = self.drawing_area.get_allocation()
593
 
        #gc = widget.get_style().fg_gc[gtk.STATE_NORMAL]
594
 
        self.drawing_area.window.draw_drawable(self.xgc,
595
 
                                               self.pixmap, -self.draw_at_x + self.drag_offset_x - self.draw_root_x, -self.draw_at_y + self.drag_offset_y - self.draw_root_y, 0, 0, width, height)
596
 
        return True
597
 
        
598
 
                
599
 
    def __drag_start(self, widget, event):
600
 
        self.drag_start_x = event.x
601
 
        self.drag_start_y = event.y
602
 
        self.drag_offset_x = 0
603
 
        self.drag_offset_y = 0
604
 
        self.last_drag_offset_x = 0
605
 
        self.last_drag_offset_y = 0
606
 
        self.dragging = True
607
 
        gobject.timeout_add(50, self.__drag_draw)
608
 
                
609
 
                
610
 
    def __draw_map(self):
611
 
        if not self.drawing_area_configured:
612
 
            return False
613
 
                
614
 
        if self.map_width == 0 or self.map_height == 0:
615
 
            return
616
 
        #print 'begin draw marks'
617
 
        self.__draw_marks()
618
 
                
619
 
        #print 'end draw marks'
620
 
                
621
 
        #self.xgc.set_function(gtk.gdk.COPY)
622
 
        #self.xgc.set_rgb_fg_color(gtk.gdk.color_parse('white'))
623
 
        #self.pixmap.draw_rectangle(self.xgc, True, 0, 0, self.map_width, self.map_height)
624
 
                        
625
 
        size = self.ts.tile_size()
626
 
        xi = int(self.map_center_x)
627
 
        yi = int(self.map_center_y)
628
 
        span_x = int(math.ceil(float(self.map_width) / (size * 2.0)))
629
 
        span_y = int(math.ceil(float(self.map_height) / (size * 2.0)))
630
 
        tiles = []
631
 
        for i in xrange(0, span_x + 1, 1):
632
 
            for j in xrange(0, span_y + 1, 1):
633
 
                for dir in xrange(0, 4, 1):
634
 
                    dir_ns = dir_ew = 1
635
 
                    if dir % 2 == 1: # if dir == 1 or dir == 3
636
 
                        dir_ns = -1
637
 
                    if dir > 1:
638
 
                        dir_ew = -1
639
 
                                
640
 
                    tile = (xi + (i * dir_ew), yi + (j * dir_ns))
641
 
                    if not tile in tiles:
642
 
                        tiles.append(tile)
643
 
                        #print "Requesting ", tile, " zoom ", ts.zoom
644
 
                        d = openstreetmap.TileLoader(tile, self.ts.zoom, self, self.settings['download_map_path'], (i * dir_ew) * span_x + (j * dir_ns))
645
 
                        d.start()
646
 
        #print 'end draw map'
647
 
 
648
 
    def __draw_marks_caches(self, coords):
649
 
        xgc = self.xgc
650
 
        draw_short = (len(coords) > self.TOO_MUCH_POINTS)
651
 
 
652
 
        for c in coords: # for each geocache
653
 
            radius = self.CACHE_DRAW_SIZE
654
 
            if c.found:
655
 
                color = self.COLOR_FOUND
656
 
            elif c.type == geocaching.GeocacheCoordinate.TYPE_REGULAR:
657
 
                color = self.COLOR_REGULAR
658
 
            elif c.type == geocaching.GeocacheCoordinate.TYPE_MULTI:
659
 
                color = self.COLOR_MULTI
660
 
            else:
661
 
                color = self.COLOR_DEFAULT
662
 
 
663
 
            p = self.__coord2point(c)
664
 
            xgc.set_rgb_fg_color(color)
665
 
 
666
 
            if draw_short:
667
 
                radius = radius / 2.0
668
 
 
669
 
            if c.marked:
670
 
                xgc.set_rgb_fg_color(self.COLOR_MARKED)
671
 
                self.pixmap_marks.draw_rectangle(xgc, True, p[0] - radius, p[1] - radius, radius * 2, radius * 2)
672
 
 
673
 
 
674
 
            xgc.set_rgb_fg_color(color)
675
 
            xgc.line_width = 3
676
 
            self.pixmap_marks.draw_rectangle(xgc, False, p[0] - radius, p[1] - radius, radius * 2, radius * 2)
677
 
 
678
 
            if draw_short:
679
 
                continue
680
 
 
681
 
 
682
 
            # print the name?
683
 
            if self.settings['options_show_name']:
684
 
                layout = self.drawing_area.create_pango_layout(c.name)
685
 
                layout.set_font_description(self.CACHE_DRAW_FONT)
686
 
                self.pixmap_marks.draw_layout(xgc, p[0] + 3 + radius, p[1] - 3 - radius, layout)
687
 
 
688
 
            # if we have a description for this cache...
689
 
            if c.was_downloaded():
690
 
                # draw something like:
691
 
                # ----
692
 
                # ----
693
 
                # ----
694
 
                # besides the icon
695
 
                width = 6
696
 
                dist = 2
697
 
                pos_x = p[0] + radius + 3 + 1
698
 
                pos_y = p[1] + 2
699
 
                xgc.line_width = 1
700
 
                for i in xrange(0, 3):
701
 
                    self.pixmap_marks.draw_line(xgc, pos_x, pos_y + dist * i, pos_x + width, pos_y + dist * i)
702
 
 
703
 
            # if this cache is the active cache
704
 
            if self.current_cache != None and c.name == self.current_cache.name:
705
 
                xgc.line_width = 3
706
 
                xgc.set_rgb_fg_color(self.COLOR_CURRENT_CACHE)
707
 
                radius = 7
708
 
                self.pixmap_marks.draw_rectangle(xgc, False, p[0] - radius, p[1] - radius, radius * 2, radius * 2)
709
 
 
710
 
            # if this cache is disabled
711
 
            if c.status == geocaching.GeocacheCoordinate.STATUS_DISABLED:
712
 
                xgc.line_width = 3
713
 
                xgc.set_rgb_fg_color(self.COLOR_CURRENT_CACHE)
714
 
                radius = 7
715
 
                self.pixmap_marks.draw_line(xgc, p[0]-radius, p[1]-radius, p[0]+radius, p[1]+radius)
716
 
 
717
 
            xgc.set_rgb_fg_color(self.COLOR_CACHE_CENTER)
718
 
            xgc.line_width = 1
719
 
            self.pixmap_marks.draw_line(xgc, p[0], p[1] - 2, p[0], p[1] + 3) #  |
720
 
            self.pixmap_marks.draw_line(xgc, p[0] - 2, p[1], p[0] + 3, p[1]) # ---
721
 
 
722
 
        # draw additional waypoints
723
 
        # --> print description!
724
 
        if self.current_cache != None and self.current_cache.get_waypoints() != None:
725
 
            xgc.set_function(gtk.gdk.AND)
726
 
            xgc.set_rgb_fg_color(self.COLOR_WAYPOINTS)
727
 
            xgc.line_width = 1
728
 
            radius = 4
729
 
            num = 0
730
 
            for w in self.current_cache.get_waypoints():
731
 
                if w['lat'] != -1 and w['lon'] != -1:
732
 
                    num = num + 1
733
 
                    p = self.__coord2point(geo.Coordinate(w['lat'], w['lon']))
734
 
                    self.pixmap_marks.draw_line(xgc, p[0], p[1] - 3, p[0], p[1] + 4) #  |
735
 
                    self.pixmap_marks.draw_line(xgc, p[0] - 3, p[1], p[0] + 4, p[1]) # ---
736
 
                    self.pixmap_marks.draw_arc(xgc, False, p[0] - radius, p[1] - radius, radius * 2, radius * 2, 0, 360 * 64)
737
 
                    layout = self.drawing_area.create_pango_layout('')
738
 
                    layout.set_markup('<i>%s</i>' % (w['id']))
739
 
                    layout.set_font_description(self.CACHE_DRAW_FONT)
740
 
                    self.pixmap_marks.draw_layout(xgc, p[0] + 3 + radius, p[1] - 3 - radius, layout)
741
 
 
742
 
    def __draw_marks_message(self, message):
743
 
        xgc = self.xgc
744
 
        xgc.set_rgb_fg_color(self.MESSAGE_DRAW_COLOR)
745
 
        layout = self.drawing_area.create_pango_layout(message)
746
 
        layout.set_font_description(self.MESSAGE_DRAW_FONT)
747
 
        self.pixmap_marks.draw_layout(xgc, 20, 20, layout)
748
 
 
749
 
    def __draw_marks(self):
750
 
            
751
 
        xgc = self.xgc
752
 
        xgc.set_function(gtk.gdk.COPY)
753
 
        self.xgc.set_rgb_fg_color(gtk.gdk.color_parse('white'))
754
 
        self.pixmap_marks.draw_rectangle(self.xgc, True, 0, 0, self.map_width, self.map_height)
755
 
                
756
 
        #
757
 
        # draw geocaches
758
 
        #
759
 
 
760
 
        if self.ts.get_zoom() < self.CACHES_ZOOM_LOWER_BOUND:
761
 
            self.__draw_marks_message('Zoom in to see geocaches.')
762
 
        else:
763
 
 
764
 
            if self.settings['options_hide_found']:
765
 
                found = False
766
 
            else:
767
 
                found = None
768
 
            coords = self.pointprovider.get_points_filter(self.get_visible_area(), found, self.MAX_NUM_RESULTS_SHOW)
769
 
            if len(coords) >= self.MAX_NUM_RESULTS_SHOW:
770
 
                self.__draw_marks_message('Too many geocaches to display.')
771
 
            else:
772
 
                self.__draw_marks_caches(coords)
773
 
            
774
 
                        
775
 
        #
776
 
        # next, draw the user defined points
777
 
        #
778
 
        """
779
 
                coords = self.userpointprovider.get_points_filter((self.pixmap_markspoint2coord([0,0]), self.pixmap_markspoint2coord([self.map_width, self.map_height])))
780
 
 
781
 
                xgc.set_function(gtk.gdk.AND)
782
 
                radius = 7
783
 
                color = gtk.gdk.color_parse('darkorchid')
784
 
                for c in coords: # for each geocache
785
 
                        p = self.coord2point(c)
786
 
                        xgc.line_width = 3                
787
 
                        xgc.set_rgb_fg_color(color)
788
 
                        radius = 8
789
 
                        self.pixmap_marks.draw_line(xgc, p[0] - radius, p[1], p[0], p[1] + radius)
790
 
                        self.pixmap_marks.draw_line(xgc, p[0], p[1] + radius, p[0] + radius, p[1])
791
 
                        self.pixmap_marks.draw_line(xgc, p[0] + radius, p[1], p[0], p[1] - radius)
792
 
                        self.pixmap_marks.draw_line(xgc, p[0], p[1] - radius, p[0] - radius, p[1])
793
 
                        xgc.line_width = 1
794
 
                        self.pixmap_marks.draw_line(xgc, p[0], p[1] - 2, p[0], p[1] + 3) #  |
795
 
                        self.pixmap_marks.draw_line(xgc, p[0] - 2, p[1], p[0] + 3, p[1]) # ---
796
 
                        layout = self.drawing_area.create_pango_layout(c.name)
797
 
                        layout.set_font_description(font)
798
 
                        self.pixmap_marks.draw_layout(xgc, p[0] + 3 + radius, p[1] - 3 - radius, layout)
799
 
                
800
 
                """
801
 
        #
802
 
        # and now for our current data!
803
 
        #
804
 
                
805
 
                
806
 
                
807
 
        # if we have a target, draw it
808
 
        if self.current_target != None:
809
 
            t = self.__coord2point(self.current_target)
810
 
            if t != False and self.point_in_screen(t):
811
 
                        
812
 
        
813
 
                xgc.line_width = 2
814
 
                radius_o = 10
815
 
                radius_i = 3
816
 
                xgc.set_function(gtk.gdk.INVERT)
817
 
                xgc.set_rgb_fg_color(self.COLOR_TARGET)
818
 
                self.pixmap_marks.draw_line(xgc, t[0] - radius_o, t[1], t[0] - radius_i, t[1])
819
 
                self.pixmap_marks.draw_line(xgc, t[0] + radius_o, t[1], t[0] + radius_i, t[1])
820
 
                self.pixmap_marks.draw_line(xgc, t[0], t[1] + radius_o, t[0], t[1] + radius_i)
821
 
                self.pixmap_marks.draw_line(xgc, t[0], t[1] - radius_o, t[0], t[1] - radius_i)
822
 
        else:
823
 
            t = False
824
 
                
825
 
                
826
 
                
827
 
        if self.gps_data != None and self.gps_data.position != None:
828
 
            # if we have a position, draw a black cross
829
 
            p = self.__coord2point(self.gps_data.position)
830
 
            if p != False:
831
 
                self.gps_last_position = p
832
 
                if self.point_in_screen(p):
833
 
                
834
 
                    xgc.line_width = 2
835
 
                    radius_o = 20
836
 
                    radius_i = 7
837
 
                    xgc.set_function(gtk.gdk.COPY)
838
 
                    xgc.set_rgb_fg_color(self.COLOR_CURRENT_POSITION)
839
 
                                
840
 
                    # \  /
841
 
                    #
842
 
                    # /  \
843
 
                    self.pixmap_marks.draw_line(xgc, p[0] - radius_o, p[1] - radius_o, p[0] - radius_i, p[1] - radius_i)
844
 
                    self.pixmap_marks.draw_line(xgc, p[0] + radius_o, p[1] + radius_o, p[0] + radius_i, p[1] + radius_i)
845
 
                    self.pixmap_marks.draw_line(xgc, p[0] + radius_o, p[1] - radius_o, p[0] + radius_i, p[1] - radius_i)
846
 
                    self.pixmap_marks.draw_line(xgc, p[0] - radius_o, p[1] + radius_o, p[0] - radius_i, p[1] + radius_i)
847
 
                    self.pixmap_marks.draw_point(xgc, p[0], p[1])
848
 
                
849
 
                '''
850
 
                                # if we have a bearing, draw it.
851
 
                                if self.gps_data.bearing. != None:
852
 
                                        bearing = self.gps_data.bearing
853
 
                        
854
 
                                        xgc.line_width = 1
855
 
                                        length = 10
856
 
                                        xgc.set_function(gtk.gdk.COPY)
857
 
                                        xgc.set_rgb_fg_color(gtk.gdk.color_parse("blue"))
858
 
                                        self.pixmap_marks.draw_line(xgc, p[0], p[1], int(p[0] + math.cos(bearing) * length), int(p[1] + math.sin(bearing) * length))
859
 
                                '''
860
 
                                
861
 
                                
862
 
                # and a line between target and position if we have both
863
 
                if t != False:
864
 
                    xgc.line_width = 5
865
 
                    xgc.set_function(gtk.gdk.AND_INVERT)
866
 
                    xgc.set_rgb_fg_color(self.COLOR_LINE_INVERT)
867
 
                    if self.point_in_screen(t) and self.point_in_screen(p):
868
 
                        self.pixmap_marks.draw_line(xgc, p[0], p[1], t[0], t[1])
869
 
                    elif self.point_in_screen(p):
870
 
                        direction = math.radians(self.current_target.bearing_to(self.gps_data.position))
871
 
                        # correct max length: sqrt(width**2 + height**2)
872
 
                        length = self.map_width
873
 
                        self.pixmap_marks.draw_line(xgc, p[0], p[1], int(p[0] - math.sin(direction) * length), int(p[1] + math.cos(direction) * length))
874
 
                                        
875
 
                    elif self.point_in_screen(t):
876
 
                        direction = math.radians(self.gps_data.position.bearing_to(self.current_target))
877
 
                        length = self.map_width + self.map_height
878
 
                        self.pixmap_marks.draw_line(xgc, t[0], t[1], int(t[0] - math.sin(direction) * length), int(t[1] + math.cos(direction) * length))
879
 
                                        
880
 
                                
881
 
 
882
 
        # draw cross across the screen
883
 
        xgc.line_width = 1
884
 
        xgc.set_function(gtk.gdk.INVERT)
885
 
        xgc.set_rgb_fg_color(self.COLOR_CROSSHAIR)
886
 
 
887
 
        radius_inner = 30
888
 
        self.pixmap_marks.draw_line(xgc, self.map_width / 2, 0, self.map_width / 2, self.map_height / 2 - radius_inner)
889
 
        self.pixmap_marks.draw_line(xgc, self.map_width / 2, self.map_height / 2 + radius_inner, self.map_width / 2, self.map_height)
890
 
        self.pixmap_marks.draw_line(xgc, 0, self.map_height / 2, self.map_width / 2 - radius_inner, self.map_height / 2)
891
 
        self.pixmap_marks.draw_line(xgc, self.map_width / 2 + radius_inner, self.map_height / 2, self.map_width, self.map_height / 2)
892
 
                
893
 
        xgc.set_function(gtk.gdk.COPY)
894
 
        return False
895
 
        
896
 
    def expose_event(self, widget, event):
897
 
        if self.inhibit_expose or self.dragging:
898
 
            return
899
 
        x, y, width, height = event.area
900
 
 
901
 
        widget.window.draw_drawable(self.xgc, self.pixmap, x, y, self.draw_root_x + self.draw_at_x  + x, self.draw_root_y + self.draw_at_y + y, width, height)
902
 
        self.xgc.set_function(gtk.gdk.AND)
903
 
        widget.window.draw_drawable(self.xgc, self.pixmap_marks, x, y, self.draw_root_x + self.draw_at_x  + x, self.draw_root_y + self.draw_at_y + y, width, height)
904
 
        self.xgc.set_function(gtk.gdk.COPY)
905
 
                
906
 
        return False
907
 
                
908
 
        
909
 
    def expose_event_arrow(self, widget, event):
 
602
        
 
603
    def _expose_event_arrow(self, widget, event):
910
604
        x, y, width, height = event.area
911
605
        widget.window.draw_drawable(self.xgc_arrow, self.pixmap_arrow, x, y, x, y, width, height)
912
606
        return False
913
607
 
914
 
 
915
 
    def get_visible_area(self):
916
 
        return (self.pixmappoint2coord([0, 0]), self.pixmappoint2coord([self.map_width, self.map_height]))
917
 
 
918
608
    def hide_progress(self):
919
609
        self.progressbar.hide()
920
610
                
921
 
                        
922
 
                
923
 
    def __load_images(self):
 
611
    def _load_images(self):
924
612
        if self.current_cache == None:
925
 
            self.__update_cache_image(reset = True)
 
613
            self._update_cache_image(reset=True)
926
614
            return
927
615
        if len(self.current_cache.get_images()) > 0:
928
616
            self.images = self.current_cache.get_images().items()
929
617
        else:
930
618
            self.images = {}
931
 
        self.__update_cache_image(reset = True)
932
 
 
933
 
    def on_download_clicked(self, widget):
934
 
        self.do_events()
935
 
        self.core.on_download(self.get_visible_area())
936
 
        
937
 
        self.__draw_map()
938
 
 
939
 
 
940
 
    def on_download_details_map_clicked(self, some):
941
 
        self.core.on_download_descriptions(self.get_visible_area(), True)
942
 
        self.__draw_map()
 
619
        self._update_cache_image(reset=True)
 
620
 
 
621
    def on_download_clicked(self, widget, data=None):
 
622
        self.core.on_download(self.map.get_visible_area())
 
623
 
 
624
    def on_download_details_map_clicked(self, some, thing=None):
 
625
        logger.debug("Downloading geocaches on map.")
 
626
        self.core.on_download_descriptions(self.map.get_visible_area())
943
627
 
944
628
    def on_download_details_sync_clicked(self, something):
945
 
        self.core.on_download_descriptions(self.get_visible_area())
946
 
        self.__draw_map()
 
629
        self.core.on_download_descriptions(self.map.get_visible_area())
947
630
                
948
631
    def on_actions_clicked(self, widget, event):
949
632
        xml.get_widget('menu_actions').popup(None, None, None, event.button, event.get_time())
951
634
    def on_cache_marked_toggled(self, widget):
952
635
        if self.current_cache == None:
953
636
            return
954
 
        self.__update_mark(self.current_cache, widget.get_active())
 
637
        self._update_mark(self.current_cache, widget.get_active())
955
638
 
956
639
    def on_change_coord_clicked(self, something):
957
 
        self.set_target(self.show_coordinate_input(self.current_target))
 
640
        self.set_target(self.show_coordinate_input(self.core.current_target))
958
641
 
959
 
    def __get_search_selected_cache(self):
 
642
    def _get_search_selected_cache(self):
960
643
        index = self.cachelist.getFirstSelectedRowIndex()
961
644
        if index == None:
962
645
            return (None, None)
964
647
        return (index, cache)
965
648
        
966
649
    def on_result_marked_toggled(self, widget):
967
 
        (index, cache) = self.__get_search_selected_cache()
 
650
        (index, cache) = self._get_search_selected_cache()
968
651
        if cache == None:
969
652
            return
970
 
        self.__update_mark(cache, widget.get_active())
971
 
        title = self.__format_cache_title(cache)
 
653
        self._update_mark(cache, widget.get_active())
 
654
        title = self._format_cache_title(cache)
972
655
        self.cachelist.setItem(index, 0, title)
973
656
 
974
 
    def __update_mark(self, cache, status):
 
657
    def _update_mark(self, cache, status):
975
658
        cache.marked = status
976
 
        if status:
977
 
            s = 1
978
 
        else:
979
 
            s = 0
980
 
        self.pointprovider.update_field(cache, 'marked', s)
981
 
        self.redraw_marks()
982
 
 
 
659
        self.core.save_cache_attribute(cache, 'marked')
 
660
        self.map.redraw_layers()
983
661
                
984
662
    def on_download_cache_clicked(self, something):
985
663
        self.core.on_download_cache(self.current_cache)
986
 
        self.show_cache(self.current_cache)
987
664
        
988
665
    def on_export_cache_clicked(self, something):
989
666
        if self.input_export_path.get_value().strip() == '':
991
668
            return
992
669
        self.core.on_export_cache(self.current_cache, self.input_export_path.get_value())
993
670
        
994
 
    def on_good_fix(self, gps_data):
 
671
    def _on_good_fix(self, core, gps_data, distance, bearing):
995
672
        self.gps_data = gps_data
 
673
        self.gps_last_good_fix = gps_data
996
674
        self.gps_has_fix = True
997
 
        self.__draw_arrow()
998
 
        #self.do_events()
 
675
        self.gps_target_distance = distance
 
676
        self.gps_target_bearing = bearing
 
677
        self._draw_arrow()
999
678
        self.update_gps_display()
1000
 
                
1001
 
        if self.dragging:
1002
 
            return
1003
 
                
1004
 
        # redraw marks if we need to
1005
 
        if not self.drawing_area_configured:
1006
 
            return False
1007
 
                
1008
 
        x, y = self.__coord2point(self.gps_data.position)
1009
 
        if self.gps_last_position != None:
1010
 
                                    
1011
 
            l, m = self.gps_last_position
1012
 
            dist_from_last = (x - l) ** 2 + (y - m) ** 2
1013
 
 
1014
 
            # if we are tracking the user, redraw if far enough from center:
1015
 
            if self.button_track.get_active():
1016
 
                n, o = self.map_width / 2, self.map_height / 2
1017
 
                dist_from_center = (x - n) ** 2 + (y - o) ** 2
1018
 
                if dist_from_center > self.REDRAW_DISTANCE_TRACKING ** 2:
1019
 
                    self.set_center(self.gps_data.position)
1020
 
                    # update last position, as it is now drawed
1021
 
                    self.gps_last_position = (x, y)
1022
 
                    return
1023
 
 
1024
 
            # if we are tracking and we have not moved out of the center
1025
 
            # or if we are not tracking the user
1026
 
            # in either case, if we have moved far enough since last draw, redraw just the marks
1027
 
            if dist_from_last > self.REDRAW_DISTANCE_MINOR ** 2:
1028
 
                a, b = self.get_visible_area()
1029
 
                if self.gps_data.position.lat > min(a.lat, b.lat) \
1030
 
                    and self.gps_data.position.lat < max(a.lat, b.lat) \
1031
 
                    and self.gps_data.position.lon > min(a.lon, b.lon) \
1032
 
                    and self.gps_data.position.lon < max(a.lon, b.lon):
1033
 
                        self.redraw_marks()
1034
 
                # update last position, as it is now drawed
1035
 
                self.gps_last_position = (x, y)
1036
 
            return
1037
 
        else:
1038
 
            self.redraw_marks()
1039
 
            # also update when it was None
1040
 
            self.gps_last_position = (x, y)
1041
 
                
1042
 
                
1043
 
    def redraw_marks(self):
1044
 
 
1045
 
        self.__draw_marks()
1046
 
        self.refresh()
1047
679
        
1048
680
    def on_image_next_clicked(self, something):
1049
681
        if len(self.images) == 0:
1050
 
            self.__update_cache_image(reset = True)
 
682
            self._update_cache_image(reset=True)
1051
683
            return
1052
684
        self.image_no += 1
1053
685
        self.image_no %= len(self.images)
1054
 
        self.__update_cache_image()
 
686
        self._update_cache_image()
1055
687
                
1056
688
        
1057
689
    def on_image_zoom_clicked(self, something):
1058
690
        self.image_zoomed = not self.image_zoomed
1059
 
        self.__update_cache_image()
 
691
        self._update_cache_image()
1060
692
 
1061
 
    def on_label_fieldnotes_mapped(self, widget):
1062
 
        self.__check_notes_save()
1063
 
        l = self.pointprovider.get_new_fieldnotes_count()
 
693
    def _on_fieldnotes_changed(self, caller):
 
694
        widget = self.label_fieldnotes
 
695
        self._check_notes_save()
 
696
        l = self.core.get_new_fieldnotes_count()
1064
697
        if l > 0:
1065
698
            widget.set_text("you have created %d fieldnotes" % l)
 
699
            self.button_upload_fieldnotes.set_sensitive(True)
1066
700
        else:
1067
701
            widget.set_text("you have not created any new fieldnotes")
 
702
            self.button_upload_fieldnotes.set_sensitive(False)
 
703
 
1068
704
                
1069
705
    def on_list_marked_clicked(self, widget):
1070
 
        self.core.on_start_search_advanced(marked = True)
1071
 
 
1072
 
 
1073
 
    def on_no_fix(self, gps_data, status):
 
706
        self.core.on_start_search_advanced(marked=True)
 
707
 
 
708
 
 
709
    def _on_no_fix(self, caller, gps_data, status):
1074
710
        self.gps_data = gps_data
1075
711
        self.label_bearing.set_text("No Fix")
1076
712
        self.label_latlon.set_text(status)
1077
713
        self.gps_has_fix = False
1078
714
        self.update_gps_display()
1079
 
        self.__draw_arrow()
 
715
        self._draw_arrow()
1080
716
 
1081
 
    def on_notes_changed(self, something, somethingelse):
 
717
    def on_notes_changed(self, something, somethingelse=None):
1082
718
        self.notes_changed = True
1083
719
        
1084
720
    def on_fieldnotes_changed(self, something, somethingelse):
1089
725
        from time import strftime
1090
726
        if self.current_cache == None:
1091
727
            return
1092
 
        log = geocaching.GeocacheCoordinate.LOG_NO_LOG
1093
728
        if self.cache_elements['log_found'].get_active():
1094
729
            log = geocaching.GeocacheCoordinate.LOG_AS_FOUND
1095
 
            self.pointprovider.update_field(self.current_cache, 'found', '1')
1096
730
        elif self.cache_elements['log_notfound'].get_active():
1097
731
            log = geocaching.GeocacheCoordinate.LOG_AS_NOTFOUND
1098
 
            self.pointprovider.update_field(self.current_cache, 'found', '0')
1099
732
        elif self.cache_elements['log_note'].get_active():
1100
733
            log = geocaching.GeocacheCoordinate.LOG_AS_NOTE
 
734
        else:
 
735
            log = geocaching.GeocacheCoordinate.LOG_NO_LOG
 
736
 
1101
737
        logdate = strftime('%Y-%m-%d', gmtime())
1102
738
        self.cache_elements['log_date'].set_text('fieldnote date: %s' % logdate)
1103
 
        self.pointprovider.update_field(self.current_cache, 'logas', log)
1104
 
        self.pointprovider.update_field(self.current_cache, 'logdate', logdate)
1105
 
 
 
739
        self.current_cache.logas = log
 
740
        self.current_cache.logdate = logdate
 
741
        self.core.save_fieldnote(self.current_cache)
1106
742
 
1107
743
    def on_save_config(self, something):
1108
744
        if not self.block_changes:
1109
 
            self.core.on_config_changed(self.read_settings())
 
745
            self._on_save_settings(self.core)
1110
746
 
1111
747
    def on_search_action_center_clicked(self, widget):
1112
 
        (index, cache) = self.__get_search_selected_cache()
 
748
        (index, cache) = self._get_search_selected_cache()
1113
749
        if cache == None:
1114
750
            return
1115
751
        self.set_center(cache)
1116
752
        self.notebook_all.set_current_page(1)
1117
753
 
1118
754
    def on_search_action_set_target_clicked(self, widget):
1119
 
        (index, cache) = self.__get_search_selected_cache()
 
755
        (index, cache) = self._get_search_selected_cache()
1120
756
        if cache == None:
1121
757
            return
1122
758
        self.current_cache = cache
1124
760
        self.notebook_all.set_current_page(0)
1125
761
 
1126
762
    def on_search_action_view_details_clicked(self, widget):
1127
 
        (index, cache) = self.__get_search_selected_cache()
 
763
        (index, cache) = self._get_search_selected_cache()
1128
764
        if cache == None:
1129
765
            return
1130
766
        self.show_cache(cache)
1138
774
            else:
1139
775
                valmap = {'1..1.5': 1.5, '2..2.5': 2.5, '3..3.5': 3.5, '4..4.5': 4.5, '5': 5}
1140
776
                default = 5
1141
 
            if input in valmap.keys():
 
777
            if input in valmap:
1142
778
                return valmap[input]
1143
779
            else:
1144
780
                return default
1187
823
 
1188
824
 
1189
825
        if self.search_elements['inview'].get_active():
1190
 
            location = self.get_visible_area()
 
826
            location = self.map.get_visible_area()
1191
827
        else:
1192
828
            location = None
1193
829
        if found == None and name_search == None and sizes == None and \
1195
831
            self.filtermsg.hide()
1196
832
        else:
1197
833
            self.filtermsg.show()
1198
 
        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)
 
834
        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)
1199
835
 
1200
836
    def on_search_cache_clicked(self, listview, event, element):
1201
837
        if element == None:
1202
838
            return
1203
 
        (index, cache) = self.__get_search_selected_cache()
 
839
        (index, cache) = self._get_search_selected_cache()
1204
840
        if cache == None:
1205
841
            return
1206
842
 
1207
843
        if event.type == gtk.gdk._2BUTTON_PRESS:
1208
 
            self.core.on_cache_selected(cache)
 
844
            self.show_cache(cache)
1209
845
            self.set_center(cache)
1210
846
        else:
1211
847
            self.check_result_marked.set_active(cache.marked)
1223
859
            self.set_target(self.current_cache)
1224
860
            self.notebook_all.set_current_page(0)
1225
861
 
1226
 
    def on_set_target_center(self, something):
1227
 
        self.set_target(self.ts.num2deg(self.map_center_x, self.map_center_y))
 
862
    def on_set_target_center(self, some, thing=None):
 
863
        self.set_target(self.map.get_center())
1228
864
 
1229
 
    def on_show_target_clicked(self, some):
1230
 
        if self.current_target == None:
 
865
    def on_show_target_clicked(self, some=None, data=None):
 
866
        if self.core.current_target == None:
1231
867
            return
1232
868
        else:
1233
 
            self.set_center(self.current_target)
 
869
            self.set_center(self.core.current_target)
1234
870
                
1235
 
    def on_track_toggled(self, something):
1236
 
        if self.button_track.get_active() and self.gps_data != None and self.gps_data.position != None:
1237
 
            self.set_center(self.gps_data.position)
 
871
    def on_track_toggled(self, widget, data=None):
 
872
        logger.info("Track toggled, new state: " + repr(widget.get_active()))
 
873
        self.marks_layer.set_follow_position(widget.get_active())
1238
874
 
1239
875
    def on_upload_fieldnotes(self, something):
1240
876
        self.core.on_upload_fieldnotes()
1241
 
        self.on_label_fieldnotes_mapped(None)
1242
877
        
1243
878
    def on_waypoint_clicked(self, listview, event, element):
1244
879
        if event.type != gtk.gdk._2BUTTON_PRESS or element == None:
1245
880
            return
1246
 
        print element[0]
 
881
 
1247
882
        if self.current_cache == None:
1248
883
            return
1249
884
        if element[0] == 0:
1255
890
                return
1256
891
            self.set_target(geo.Coordinate(wpt['lat'], wpt['lon'], wpt['id']))
1257
892
            self.notebook_all.set_current_page(0)
1258
 
                
1259
 
    def on_zoom_changed(self, blub):
1260
 
        if not self.inhibit_zoom:
1261
 
            self.zoom()
1262
 
                
1263
 
    def on_zoomin_clicked(self, widget):
1264
 
        self.zoom(+ 1)
1265
 
                
1266
 
    def on_zoomout_clicked(self, widget):
1267
 
        self.zoom(-1)
1268
 
                
1269
 
    def __update_cache_image(self, reset = False):
 
893
                                
 
894
    def on_zoomin_clicked(self, widget, data=None):
 
895
        self.map.relative_zoom(+ 1)
 
896
                
 
897
    def on_zoomout_clicked(self, widget, data=None):
 
898
        self.map.relative_zoom(-1)
 
899
                
 
900
    def _update_cache_image(self, reset=False):
1270
901
        if reset:
1271
902
            self.image_zoomed = False
1272
903
            self.image_no = 0
1273
904
            if len(self.images) == 0:
1274
 
                self.image_cache.set_from_stock(gtk.STOCK_CANCEL   , -1)
 
905
                self.image_cache.set_from_stock(gtk.STOCK_CANCEL, -1)
1275
906
                self.image_cache_caption.set_text("There's nothing to see here.")
1276
907
                return
1277
908
        try:
1278
909
            if self.current_cache == None or len(self.images) <= self.image_no:
1279
 
                self.__update_cache_image(True)
 
910
                self._update_cache_image(True)
1280
911
                return
1281
 
            filename = os.path.join(self.settings['download_output_dir'], self.images[self.image_no][0])
1282
 
            if not os.path.exists(filename):
 
912
            filename = path.join(self.settings['download_output_dir'], self.images[self.image_no][0])
 
913
            if not path.exists(filename):
1283
914
                self.image_cache_caption.set_text("not found: %s" % filename)
1284
915
                self.image_cache.set_from_stock(gtk.STOCK_GO_FORWARD, -1)
1285
916
                return
1295
926
 
1296
927
            self.image_cache_caption.set_text("<b>%d</b> %s" % (self.image_no, caption))
1297
928
            self.image_cache_caption.set_use_markup(True)
1298
 
        except Exception as e:
1299
 
            print "Error loading image: %s" % e
1300
 
                        
1301
 
                
1302
 
    def pixmappoint2coord(self, point):
1303
 
        size = self.ts.tile_size()
1304
 
        coord = self.ts.num2deg(\
1305
 
            (point[0] + self.map_center_x * size - self.map_width / 2) / size, \
1306
 
            (point[1] + self.map_center_y * size - self.map_height / 2) / size \
1307
 
            )
1308
 
        return coord
1309
 
                
1310
 
    def point_in_screen(self, point):
1311
 
        return (point[0] >= 0 and point[1] >= 0 and point[0] < self.map_width and point[1] < self.map_height)
1312
 
 
1313
 
    def read_settings(self):
1314
 
        c = self.ts.num2deg(self.map_center_x, self.map_center_y)
1315
 
        settings = { \
1316
 
            'map_position_lat': c.lat,\
1317
 
            'map_position_lon': c.lon,\
1318
 
            'map_zoom': self.ts.get_zoom() \
1319
 
        }
1320
 
        if self.current_target != None:
1321
 
            settings['last_target_lat'] = self.current_target.lat
1322
 
            settings['last_target_lon'] = self.current_target.lon
1323
 
            settings['last_target_name'] = self.current_target.name
1324
 
                        
1325
 
        for x in self.SETTINGS_CHECKBOXES:
1326
 
            w = xml.get_widget('check_%s' % x)
1327
 
            if w != None:
1328
 
                settings[x] = w.get_active()
1329
 
                
1330
 
        for x in self.SETTINGS_INPUTS:
1331
 
            w = xml.get_widget('input_%s' % x)
1332
 
            if w != None:
1333
 
                settings[x] = w.get_text()
1334
 
                
1335
 
        self.settings = settings
1336
 
                
1337
 
        return settings
1338
 
                        
1339
 
                
1340
 
    def refresh(self):
1341
 
        self.drawing_area.queue_draw()
1342
 
                        
1343
 
    def replace_image_tag(self, m):
 
929
        except Exception, e:
 
930
            logger.exception("Error loading image: %s" % e)
 
931
                        
 
932
                       
 
933
    @staticmethod 
 
934
    def replace_image_tag(m):
1344
935
        if m.group(1) != None and m.group(1).strip() != '':
1345
 
            return ' [Bild: %s] ' % m.group(1).strip()
1346
 
        else:
1347
 
            return ' [Bild] '
1348
 
                        
1349
 
    def screenpoint2coord(self, point):
1350
 
        size = self.ts.tile_size()
1351
 
        coord = self.ts.num2deg( \
1352
 
            ((point[0] - self.draw_root_x - self.draw_at_x) + self.map_center_x * size - self.map_width / 2) / size,  \
1353
 
            ((point[1] - self.draw_root_y - self.draw_at_y) + self.map_center_y * size - self.map_height / 2) / size \
1354
 
            )
1355
 
        return coord
1356
 
        
1357
 
    def scroll(self, widget, event):
1358
 
        if event.direction == gtk.gdk.SCROLL_DOWN:
1359
 
            self.zoom(-1)
1360
 
        else:
1361
 
            self.zoom(+ 1)
1362
 
        
1363
 
                
1364
 
    def set_center(self, coord, noupdate = False):
1365
 
        #xml.get_widget("notebook_all").set_current_page(0)
1366
 
        self.map_center_x, self.map_center_y = self.ts.deg2num(coord)
1367
 
        self.draw_at_x = 0
1368
 
        self.draw_at_y = 0
1369
 
        if not noupdate:
1370
 
            self.__draw_map()
1371
 
                
1372
 
    #called by core
1373
 
    def set_download_progress(self, fraction, text):
 
936
            return ' [Image: %s] ' % m.group(1).strip()
 
937
        else:
 
938
            return ' [Image] '
 
939
        
 
940
        
 
941
                
 
942
    def set_center(self, coord, noupdate=False, reset_track=True):
 
943
        logger.info("Set Center to %s with reset_track = %s" % (coord, reset_track))
 
944
        self.map.set_center(coord, not noupdate)
 
945
        #if reset_track:
 
946
        #    self.marks_layer.set_follow_position(False)
 
947
 
 
948
    def _set_track_mode(self, mode):
 
949
        self.marks_layer.set_follow_position(mode)
 
950
        try:
 
951
            self.button_track.set_active(mode)
 
952
        except:
 
953
            pass
 
954
 
 
955
    def _get_track_mode(self):
 
956
        return self.marks_layer.get_follow_position()
 
957
                
 
958
    def set_progress(self, fraction, text):
1374
959
        self.progressbar.show()
1375
960
        self.progressbar.set_text(text)
1376
961
        self.progressbar.set_fraction(fraction)
1377
 
        self.do_events()
1378
 
                
 
962
 
 
963
 
1379
964
    def set_target(self, cache):
1380
 
        self.current_target = cache
 
965
        self.core.set_target(cache)
 
966
 
 
967
    def _on_target_changed(self, caller, cache, distance, bearing):
 
968
        self.gps_target_distance = distance
 
969
        self.gps_target_bearing = bearing
1381
970
        self.label_target.set_text("<span size='large'>%s\n%s</span>" % (cache.get_lat(self.format), cache.get_lon(self.format)))
1382
971
        self.label_target.set_use_markup(True)
 
972
        
1383
973
        #self.set_center(cache)
1384
974
                
1385
975
    def show(self):
1391
981
    def show_cache(self, cache):
1392
982
        if cache == None:
1393
983
            return
1394
 
        self.__check_notes_save()
 
984
        self._check_notes_save()
1395
985
        self.current_cache = cache
1396
986
 
1397
987
        # Title
1418
1008
            self.cache_elements['difficulty'].set_text("%s/5" % cache.get_difficulty())
1419
1009
                                                
1420
1010
        # Description and short description
1421
 
        text_shortdesc = self.__strip_html(cache.shortdesc)
 
1011
        text_shortdesc = self._strip_html(cache.shortdesc)
1422
1012
        if cache.status == geocaching.GeocacheCoordinate.STATUS_DISABLED:
1423
1013
            text_shortdesc = 'ATTENTION! This Cache is Disabled!\n--------------\n' + text_shortdesc
1424
 
        text_longdesc = self.__strip_html(re.sub(r'(?i)<img[^>]+?>', ' [to get all images, re-download description] ', re.sub(r'\[\[img:([^\]]+)\]\]', lambda a: self.__replace_image_callback(a, cache), cache.desc)))
 
1014
        text_longdesc = self._strip_html(re.sub(r'(?i)<img[^>]+?>', ' [to get all images, re-download description] ', re.sub(r'\[\[img:([^\]]+)\]\]', lambda a: self._replace_image_callback(a, cache), cache.desc)))
1425
1015
 
1426
1016
        if text_longdesc == '':
1427
1017
            text_longdesc = '(no description available)'
1432
1022
        self.cache_elements['desc'].set_text(showdesc)
1433
1023
 
1434
1024
        # is cache marked?
1435
 
        self.cache_elements['marked'].set_active(cache.marked)
 
1025
        self.cache_elements['marked'].set_active(True if cache.marked else False)
1436
1026
 
1437
1027
        # Set View
1438
1028
        self.notebook_cache.set_current_page(0)
1439
1029
        self.notebook_all.set_current_page(2)
1440
1030
 
1441
 
        # Update view here for fast user feedback
1442
 
        self.do_events()
1443
 
 
1444
1031
        # logs
1445
1032
        logs = cache.get_logs()
1446
1033
        if len(logs) > 0:
1460
1047
            text_hints += '\n----------------\n'
1461
1048
        else:
1462
1049
            text_hints = 'NO LOGS.\n\n'
1463
 
        
 
1050
 
1464
1051
 
1465
1052
        # hints
1466
1053
        hints = cache.hints.strip()
1469
1056
            showdesc += "\n[no hints]"
1470
1057
        else:
1471
1058
            showdesc += "\n[hints available]"
1472
 
        text_hints += 'HINTS:\n'+hints
 
1059
        text_hints += 'HINTS:\n' + hints
1473
1060
 
1474
1061
        self.cache_elements['hints'].set_text(text_hints)
1475
1062
 
1481
1068
                latlon = format(geo.Coordinate(w['lat'], w['lon']))
1482
1069
            else:
1483
1070
                latlon = "???"
1484
 
            rows.append((w['name'], latlon, w['id'], self.__strip_html(w['comment'])))
 
1071
            rows.append((w['name'], latlon, w['id'], self._strip_html(w['comment'])))
1485
1072
        self.coordlist.replaceContent(rows)
1486
1073
                        
1487
1074
        # Set button for downloading to correct state
1488
1075
        self.button_download_details.set_sensitive(True)
1489
 
                
 
1076
 
1490
1077
        # Load notes
1491
 
        self.cache_elements['notes'].set_text(cache.notes)
1492
 
        self.cache_elements['fieldnotes'].set_text(cache.fieldnotes)
 
1078
        self.cache_elements['notes'].set_text(cache.notes if cache.notes != None else '')
 
1079
        self.cache_elements['fieldnotes'].set_text(cache.fieldnotes if cache.fieldnotes != None else '')
1493
1080
 
1494
1081
        # Set field note (log) settings
1495
 
        if cache.log_as == geocaching.GeocacheCoordinate.LOG_AS_FOUND:
 
1082
        if cache.logas == geocaching.GeocacheCoordinate.LOG_AS_FOUND:
1496
1083
            self.cache_elements['log_found'].set_active(True)
1497
 
        elif cache.log_as == geocaching.GeocacheCoordinate.LOG_AS_NOTFOUND:
 
1084
        elif cache.logas == geocaching.GeocacheCoordinate.LOG_AS_NOTFOUND:
1498
1085
            self.cache_elements['log_notfound'].set_active(True)
1499
 
        elif cache.log_as == geocaching.GeocacheCoordinate.LOG_AS_NOTE:
 
1086
        elif cache.logas == geocaching.GeocacheCoordinate.LOG_AS_NOTE:
1500
1087
            self.cache_elements['log_note'].set_active(True)
1501
1088
        else:
1502
1089
            self.cache_elements['log_no'].set_active(True)
1503
1090
 
1504
 
        if cache.log_date != '':
1505
 
            self.cache_elements['log_date'].set_text('fieldnote date: %s' % cache.log_date)
 
1091
        if cache.logdate != '':
 
1092
            self.cache_elements['log_date'].set_text('fieldnote date: %s' % cache.logdate)
1506
1093
        else:
1507
1094
            self.cache_elements['log_date'].set_text('fieldnote date: not set')
1508
1095
 
1509
1096
        # Load images
1510
 
        self.__load_images()
 
1097
        self._load_images()
1511
1098
        self.image_no = 0
1512
1099
        if len(self.images) > 0:
1513
1100
            showdesc += "\n[%d image(s) available]" % len(self.images)
1516
1103
        # now, update the main text field a second time
1517
1104
        self.cache_elements['desc'].set_text(showdesc)
1518
1105
 
1519
 
        gobject.idle_add(self.__draw_marks)
 
1106
        #gobject.idle_add(self._draw_marks)
1520
1107
        #self.refresh()
1521
1108
 
1522
1109
 
1523
 
    def __replace_image_callback(self, match, coordinate):
1524
 
        if match.group(1) in coordinate.get_images().keys():
 
1110
    def _replace_image_callback(self, match, coordinate):
 
1111
        if match.group(1) in coordinate.get_images():
1525
1112
            desc = coordinate.get_images()[match.group(1)]
1526
1113
            if desc.strip() != '':
1527
1114
                return ' [image: %s] ' % desc
1532
1119
 
1533
1120
                
1534
1121
    def show_error(self, errormsg):
1535
 
        if isinstance(errormsg, Exception):
1536
 
            raise errormsg
1537
 
        error_dlg = gtk.MessageDialog(type = gtk.MESSAGE_ERROR, \
1538
 
            message_format = "%s" % errormsg, \
1539
 
            buttons = gtk.BUTTONS_OK)
 
1122
        gtk.gdk.threads_enter()
 
1123
        error_dlg = gtk.MessageDialog(self.window,
 
1124
            gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR,
 
1125
            gtk.BUTTONS_CLOSE,  "%s" % errormsg)
 
1126
 
 
1127
        error_dlg.connect('response', lambda w,d: error_dlg.destroy())
1540
1128
        error_dlg.run()
1541
1129
        error_dlg.destroy()
 
1130
        gtk.gdk.threads_leave()
 
1131
        
1542
1132
 
1543
1133
    def show_success(self, message):
1544
 
        suc_dlg = gtk.MessageDialog(type = gtk.MESSAGE_INFO \
1545
 
            , message_format = message \
1546
 
            , buttons = gtk.BUTTONS_OK)
 
1134
        gtk.gdk.threads_enter()
 
1135
        suc_dlg = gtk.MessageDialog(type=gtk.MESSAGE_INFO \
 
1136
                                    , message_format=message \
 
1137
                                    , buttons=gtk.BUTTONS_OK)
1547
1138
        suc_dlg.run()
1548
1139
        suc_dlg.destroy()
 
1140
        gtk.gdk.threads_leave()
1549
1141
 
1550
1142
    def show_coordinate_input(self, start):
1551
 
        udr = UpdownRows(self.format, start)
 
1143
        udr = UpdownRows(self.format, start, False)
1552
1144
        dialog = gtk.Dialog("Change Target", None, gtk.DIALOG_MODAL, (gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE))
1553
 
                
1554
1145
        frame = gtk.Frame("Latitude")
1555
1146
        frame.add(udr.table_lat)
1556
1147
        dialog.vbox.pack_start(frame)
1566
1157
        c.name = 'manual'
1567
1158
        return c
1568
1159
 
1569
 
        
1570
 
    def __strip_html(self, text):
1571
 
        text = text.replace("\n", " ")
1572
 
        text = re.sub(r"""(?i)<img[^>]+alt=["']?([^'"> ]+)[^>]+>""", self.replace_image_tag, text)
1573
 
        text = re.sub(r'(?i)<(br|p)[^>]*?>', "\n", text)
1574
 
        text = re.sub(r'<[^>]*?>', '', text)
1575
 
        text = self.__decode_htmlentities(text)
1576
 
        text = re.sub(r'[\n\r]+\s*[\n\r]+', '\n', text)
1577
 
        return text.strip()
 
1160
    @staticmethod 
 
1161
    def _strip_html(text):
 
1162
        return HTMLManipulations.strip_html_visual(text, SimpleGui.replace_image_tag)
1578
1163
                
1579
1164
                        
1580
1165
    def update_gps_display(self):
1596
1181
        self.label_latlon.set_text("<span size='large'>%s\n%s</span>" % (self.gps_data.position.get_lat(self.format), self.gps_data.position.get_lon(self.format)))
1597
1182
        self.label_latlon.set_use_markup(True)
1598
1183
                
1599
 
        if self.current_target == None:
 
1184
        if self.core.current_target == None:
1600
1185
            return
1601
1186
                        
1602
 
        display_dist = self.gps_data.position.distance_to(self.current_target)
1603
 
                
1604
 
        target_distance = self.gps_data.position.distance_to(self.current_target)
1605
 
        if target_distance >= 1000:
1606
 
            label = "%3dkm" % round(target_distance / 1000)
1607
 
        elif display_dist >= 100:
1608
 
            label = "%3dm" % round(target_distance)
 
1187
        if self.gps_has_fix:
 
1188
            target_distance = self.gps_target_distance
 
1189
            label = geo.Coordinate.format_distance(target_distance)
 
1190
            self.label_dist.set_text("<span size='large'>%s</span>" % label)
 
1191
            self.label_dist.set_use_markup(True)
 
1192
            
 
1193
            #self.osd_string = "<span gravity='west' size='xx-large'>%d </span>"
1609
1194
        else:
1610
 
            label = "%2.1fm" % round(target_distance, 1)
1611
 
        self.label_dist.set_text("<span size='large'>%s</span>" % label)
1612
 
        self.label_dist.set_use_markup(True)
1613
 
        
1614
 
        
1615
 
                
1616
 
                
1617
 
    def write_settings(self, settings):
1618
 
        self.settings = settings
1619
 
        self.block_changes = True
1620
 
        self.ts.set_zoom(self.settings['map_zoom'])
1621
 
        self.set_center(geo.Coordinate(self.settings['map_position_lat'], self.settings['map_position_lon']))
1622
 
                
1623
 
        if 'last_target_lat' in self.settings.keys():
1624
 
            self.set_target(geo.Coordinate(self.settings['last_target_lat'], self.settings['last_target_lon'], self.settings['last_target_name']))
1625
 
 
 
1195
            self.label_dist.set_text("<span size='large'>No Fix</span>")
 
1196
            self.label_dist.set_use_markup(True)
 
1197
            #self.osd_string = "<span gravity='west' size='xx-large'>No Fix </span>"
 
1198
        
 
1199
                
 
1200
    def _on_settings_changed_gui(self, settings):
1626
1201
        for x in self.SETTINGS_CHECKBOXES:
1627
 
            if x in self.settings.keys():
 
1202
            if x in self.settings:
1628
1203
                w = xml.get_widget('check_%s' % x)
1629
1204
                if w == None:
1630
1205
                    continue
1631
1206
                w.set_active(self.settings[x])
1632
 
            elif x in self.DEFAULT_SETTINGS.keys():
1633
 
                w = xml.get_widget('check_%s' % x)
1634
 
                if w == None:
1635
 
                    continue
1636
 
                w.set_active(self.DEFAULT_SETTINGS[x])
1637
1207
        
1638
1208
        for x in self.SETTINGS_INPUTS:
1639
 
            if x in self.settings.keys():
 
1209
            if x in self.settings:
1640
1210
                w = xml.get_widget('input_%s' % x)
1641
1211
                if w == None:
1642
1212
                    continue
1643
1213
                w.set_text(str(self.settings[x]))
1644
 
            elif x in self.DEFAULT_SETTINGS.keys():
1645
 
                w = xml.get_widget('input_%s' % x)
1646
 
                if w == None:
1647
 
                    continue
1648
 
                w.set_text(self.DEFAULT_SETTINGS[x])
1649
 
                                        
 
1214
 
 
1215
    @staticmethod
 
1216
    def shorten_name(s, chars):
 
1217
        max_pos = chars
 
1218
        min_pos = chars - 10
 
1219
 
 
1220
        NOT_FOUND = -1
 
1221
 
 
1222
        suffix = '…'
 
1223
 
 
1224
        # Case 1: Return string if it is shorter (or equal to) than the limit
 
1225
        length = len(s)
 
1226
        if length <= max_pos:
 
1227
            return s
 
1228
        else:
 
1229
            # Case 2: Return it to nearest period if possible
 
1230
            try:
 
1231
                end = s.rindex('.', min_pos, max_pos)
 
1232
            except ValueError:
 
1233
                # Case 3: Return string to nearest space
 
1234
                end = s.rfind(' ', min_pos, max_pos)
 
1235
                if end == NOT_FOUND:
 
1236
                    end = max_pos
 
1237
            return s[0:end] + suffix
 
1238
 
 
1239
 
 
1240
    def _on_settings_changed(self, caller, settings, source):
 
1241
        logger.debug("Got settings from %s, len() = %d, source = %s" % (caller, len(settings), source))
 
1242
        #if source == self:
 
1243
        #    return
 
1244
        self.settings.update(settings)
 
1245
 
 
1246
        self.block_changes = True
 
1247
        #if 'options_hide_found' in settings:
 
1248
        #    self.geocache_layer.set_show_found(not settings['options_hide_found'])
 
1249
        if 'options_show_name' in settings:
 
1250
            self.geocache_layer.set_show_name(settings['options_show_name'])
 
1251
        if 'options_map_double_size' in settings:
 
1252
            self.map.set_double_size(settings['options_map_double_size'])
 
1253
        if 'map_zoom' in settings:
 
1254
            if self.map.get_zoom() != settings['map_zoom']:
 
1255
                self.map.set_zoom(settings['map_zoom'])
 
1256
        if 'map_position_lat' in settings and 'map_position_lon' in settings:
 
1257
            self.set_center(geo.Coordinate(settings['map_position_lat'], settings['map_position_lon']), reset_track = False)
 
1258
        if 'map_follow_position' in settings:
 
1259
            self._set_track_mode(settings['map_follow_position'])
 
1260
        if 'last_target_lat' in settings:
 
1261
            self.set_target(geo.Coordinate(settings['last_target_lat'], settings['last_target_lon']))
 
1262
        if 'last_selected_geocache' in settings and settings['last_selected_geocache'] not in (None, ''):
 
1263
            cache = self.core.get_geocache_by_name(settings['last_selected_geocache'])
 
1264
            if cache != None:
 
1265
                self.set_current_cache(cache)
 
1266
        self._on_settings_changed_gui(settings)
1650
1267
        self.block_changes = False
1651
 
                
1652
 
    def zoom(self, direction = None):
1653
 
        size = self.ts.tile_size()
1654
 
        center = self.ts.num2deg(self.map_center_x - float(self.draw_at_x) / size, self.map_center_y - float(self.draw_at_y) / size)
1655
 
        if direction == None:
1656
 
            #newzoom = self.zoom_adjustment.get_value()
1657
 
            print "not"
1658
 
        else:
1659
 
            newzoom = self.ts.get_zoom() + direction
1660
 
        self.ts.set_zoom(newzoom)
1661
 
        self.set_center(center)
1662
 
                
1663
 
                
 
1268
 
 
1269
 
 
1270
 
 
1271
    def _on_save_settings(self, caller):
 
1272
        c = self.map.get_center()
 
1273
        settings = {}
 
1274
        settings['map_position_lat'] = c.lat
 
1275
        settings['map_position_lon'] = c.lon
 
1276
        settings['map_zoom'] = self.map.get_zoom()
 
1277
        settings['map_follow_position'] = self._get_track_mode()
 
1278
 
 
1279
        if self.current_cache != None:
 
1280
            settings['last_selected_geocache'] = self.current_cache.name
 
1281
 
 
1282
        for x in self.SETTINGS_CHECKBOXES:
 
1283
            w = xml.get_widget('check_%s' % x)
 
1284
            if w != None:
 
1285
                settings[x] = w.get_active()
 
1286
            else:
 
1287
                logger.info("Couldn't find widget: check_%s" % x)
 
1288
 
 
1289
        for x in self.SETTINGS_INPUTS:
 
1290
            w = xml.get_widget('input_%s' % x)
 
1291
            if w != None:
 
1292
                settings[x] = w.get_text()
 
1293
            else:
 
1294
                logger.info("Couldn't find widget: input_%s" % x)
 
1295
        caller.save_settings(settings, self)
 
1296
        
 
1297
    def on_button_check_updates_clicked(self, caller):
 
1298
        updates = self.core.try_update()
 
1299
        if updates != None:
 
1300
            gobject.idle_add(self.show_success, "%d modules upgraded. There's no need to restart AGTL." % updates)
 
1301
        return False
1664
1302
 
1665
1303
class Updown():
1666
1304
    def __init__(self, table, position, small):
1667
1305
        self.value = int(0)
1668
 
        self.label = gtk.Label("0")
 
1306
        self.label = gtk.Label("<b>0</b>")
 
1307
        self.label.set_use_markup(True)
1669
1308
        self.button_up = gtk.Button("+")
1670
1309
        self.button_down = gtk.Button("-")
1671
1310
        table.attach(self.button_up, position, position + 1, 0, 1)
1672
 
        table.attach(self.label, position, position + 1, 1, 2)
 
1311
        table.attach(self.label, position, position + 1, 1, 2, 0, 0)
1673
1312
        table.attach(self.button_down, position, position + 1, 2, 3)
1674
1313
        self.button_up.connect('clicked', self.value_up)
1675
1314
        self.button_down.connect('clicked', self.value_down)
1676
 
        if small:
1677
 
            font = pango.FontDescription("sans 8")
1678
 
        else:
1679
 
            font = pango.FontDescription("sans 12")
1680
 
        self.label.modify_font(font)
1681
 
        self.button_up.child.modify_font(font)
1682
 
        self.button_down.child.modify_font(font)
 
1315
        if small != None:
 
1316
            if small:
 
1317
                font = pango.FontDescription("sans 8")
 
1318
            else:
 
1319
                font = pango.FontDescription("sans 12")
 
1320
            self.label.modify_font(font)
 
1321
            self.button_up.child.modify_font(font)
 
1322
            self.button_down.child.modify_font(font)
1683
1323
        
1684
1324
    def value_up(self, target):
1685
1325
        self.value = int((self.value + 1) % 10)
1694
1334
        self.update()
1695
1335
                
1696
1336
    def update(self):
1697
 
        self.label.set_text(str(self.value))
1698
 
                
 
1337
        self.label.set_markup("<b>%d</b>" % self.value)
1699
1338
 
1700
1339
                
1701
1340
class PlusMinusUpdown():
1702
 
    def __init__(self, table, position, labels):
 
1341
    def __init__(self, table, position, labels, small=None):
1703
1342
        self.is_neg = False
1704
1343
        self.labels = labels
1705
1344
        self.button = gtk.Button(labels[0])
1706
 
        table.attach(self.button, position, position + 1, 1, 2)
 
1345
        table.attach(self.button, position, position + 1, 1, 2, gtk.FILL, gtk.FILL)
1707
1346
        self.button.connect('clicked', self.value_toggle)
1708
 
        self.button.child.modify_font(pango.FontDescription("sans 8"))
 
1347
        if small != None:
 
1348
            self.button.child.modify_font(pango.FontDescription("sans 8"))
1709
1349
        
1710
1350
    def value_toggle(self, target):
1711
1351
        self.is_neg = not self.is_neg
1729
1369
        self.button.child.set_text(text)
1730
1370
 
1731
1371
class UpdownRows():
1732
 
    def __init__(self, format, coord):
 
1372
    def __init__(self, format, coord, large_dialog):
1733
1373
        self.format = format
 
1374
        self.large_dialog = large_dialog
1734
1375
        if coord == None:
1735
1376
            coord = geo.Coordinate(50, 10, 'none')
1736
1377
        if format == geo.Coordinate.FORMAT_DM:
1776
1417
        table = gtk.Table(3, num + len(interrupt) + 1, False)
1777
1418
                
1778
1419
        if is_long:
1779
 
            self.switcher_lon = PlusMinusUpdown(table, 0, ['W', 'E'])
 
1420
            self.switcher_lon = PlusMinusUpdown(table, 0, ['W', 'E'], None if self.large_dialog else False)
1780
1421
        else:
1781
 
            self.switcher_lat = PlusMinusUpdown(table, 0, ['S', 'N'])
 
1422
            self.switcher_lat = PlusMinusUpdown(table, 0, ['S', 'N'], None if self.large_dialog else False)
1782
1423
                
1783
1424
        chooser = []
1784
1425
        cn = 0
1785
1426
        for i in xrange(1, num + len(interrupt) + 1):
1786
1427
            if i in interrupt:
1787
 
                table.attach(gtk.Label(interrupt[i]), i, i + 1, 1, 2)
 
1428
                table.attach(gtk.Label(interrupt[i]), i, i + 1, 1, 2, 0, 0)
1788
1429
            else:
1789
 
                ud = Updown(table, i, cn < small)
 
1430
                ud = Updown(table, i, (cn < small) if not self.large_dialog else None)
1790
1431
                if cn < len(initial_value):
1791
1432
                    ud.set_value(initial_value[cn])
1792
1433
                    chooser.append(ud)
1793
1434
                    cn = cn + 1
1794
1435
 
1795
1436
        return [table, chooser]
 
1437