260
286
# Create the renderer used in the listview
261
287
txtRdr = gtk.CellRendererText()
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),
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),
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)
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),
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),
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)
299
325
self.notebook_all.set_current_page(1)
300
gobject.timeout_add_seconds(10, self.__check_notes_save)
326
gobject.timeout_add_seconds(10, self._check_notes_save)
328
def _configure_map(self):
330
coord = geo.Coordinate(self.settings['map_position_lat'], self.settings['map_position_lon'])
331
zoom = self.settings['map_zoom']
333
coord = self._get_best_coordinate(geo.Coordinate(50, 10))
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())
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())
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())
308
def dmap(self, widget):
311
def dunmap(self, widget):
314
def __check_notes_save(self):
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
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
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)
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
334
self.draw_root_x = int(-width * self.MAP_FACTOR)
335
self.draw_root_y = int(-height * self.MAP_FACTOR)
337
gobject.idle_add(self.__draw_map)
340
def __configure_event_arrow(self, widget, event):
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()
345
if self.pixmap_north_indicator == None:
369
if self.north_indicator_layout == None:
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)
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)
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)
363
375
self.drawing_area_arrow_configured = True
364
gobject.idle_add(self.__draw_arrow)
366
def __coord2point(self, coord):
367
point = self.ts.deg2num(coord)
368
size = self.ts.tile_size()
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)
376
def __decode_htmlentities(self, string):
377
def substitute_entity(match):
378
from htmlentitydefs import name2codepoint as n2cp
380
if match.group(1) == "#":
382
if match.group(2) == '':
383
# number is in decimal
384
return unichr(int(ent))
385
elif match.group(2) == 'x':
387
return unichr(int('0x' + ent, 16))
389
# they were using a name
396
entity_re = re.compile(r'&(#?)(x?)(\w+);')
397
return entity_re.subn(substitute_entity, string)[0]
399
def on_window_destroy(self, target):
376
gobject.idle_add(self._draw_arrow)
379
def on_window_destroy(self, target, more=None, data=None):
400
380
self.core.on_destroy()
404
while gtk.events_pending():
383
def _get_best_coordinate(self, start=None):
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
391
c = geo.Coordinate(0, 0)
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
413
text = 'Too much results. Only showing first %d.' % self.MAX_NUM_RESULTS
397
too_many = len(caches) > self.MAX_NUM_RESULTS
399
text = 'Too many results. Only showing first %d.' % self.MAX_NUM_RESULTS
414
400
label.set_text(text)
416
402
caches = caches[:self.MAX_NUM_RESULTS]
455
def __draw_arrow(self):
441
def _draw_arrow(self):
443
outer_circle_size = 3
445
indicator_border = 40
446
distance_attarget = 50
448
disabled_border_size = 30
450
error_circle_size = 0.95
451
error_circle_width = 7
456
453
if not self.drawing_area_arrow_configured:
458
455
widget = self.drawing_area_arrow
459
456
x, y, width, height = widget.get_allocation()
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)
463
460
self.pixmap_arrow.draw_rectangle(widget.get_style().bg_gc[gtk.STATE_NORMAL],
464
461
True, 0, 0, width, height)
467
464
self.xgc_arrow.set_rgb_fg_color(self.COLOR_ARROW_DISABLED)
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()
473
472
# draw signal indicator
474
self.xgc_arrow.line_width = 1
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)
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)
487
# draw north indicator
486
sun_angle = self.astral.get_sun_azimuth_from_fix(self.gps_data)
488
logger.exception("Couldn't get sun angle: %s" % e)
491
if sun_angle != None:
492
display_sun = math.radians((- sun_angle + self.gps_data.bearing) % 360)
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)
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
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)
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,
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)
507
if (display_distance < 50):
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))
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
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
512
532
color = self.COLOR_ARROW_DEFAULT
515
535
self.xgc_arrow.set_rgb_fg_color(color)
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)
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)
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)
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)
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)
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)
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)
533
587
self.drawing_area_arrow.queue_draw()
536
def __get_arrow_transformed(self, x, y, width, height, angle):
537
multiply = height / (2 * (2-self.ARROW_OFFSET))
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
549
def __drag(self, widget, event):
550
if not self.dragging:
552
self.drag_offset_x = self.drag_start_x - event.x
553
self.drag_offset_y = self.drag_start_y - event.y
555
def __drag_end(self, widget, event):
556
if not self.dragging:
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']:
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)
581
def __drag_draw(self):
582
if not self.dragging:
585
delta = math.sqrt((self.last_drag_offset_x - self.drag_offset_x) ** 2 + (self.last_drag_offset_y - self.drag_offset_y) ** 2)
589
self.last_drag_offset_x = self.drag_offset_x
590
self.last_drag_offset_y = self.drag_offset_y
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)
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
607
gobject.timeout_add(50, self.__drag_draw)
610
def __draw_map(self):
611
if not self.drawing_area_configured:
614
if self.map_width == 0 or self.map_height == 0:
616
#print 'begin draw marks'
619
#print 'end draw marks'
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)
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)))
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):
635
if dir % 2 == 1: # if dir == 1 or dir == 3
640
tile = (xi + (i * dir_ew), yi + (j * dir_ns))
641
if not tile in tiles:
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))
646
#print 'end draw map'
648
def __draw_marks_caches(self, coords):
650
draw_short = (len(coords) > self.TOO_MUCH_POINTS)
652
for c in coords: # for each geocache
653
radius = self.CACHE_DRAW_SIZE
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
661
color = self.COLOR_DEFAULT
663
p = self.__coord2point(c)
664
xgc.set_rgb_fg_color(color)
667
radius = radius / 2.0
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)
674
xgc.set_rgb_fg_color(color)
676
self.pixmap_marks.draw_rectangle(xgc, False, p[0] - radius, p[1] - radius, radius * 2, radius * 2)
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)
688
# if we have a description for this cache...
689
if c.was_downloaded():
690
# draw something like:
697
pos_x = p[0] + radius + 3 + 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)
703
# if this cache is the active cache
704
if self.current_cache != None and c.name == self.current_cache.name:
706
xgc.set_rgb_fg_color(self.COLOR_CURRENT_CACHE)
708
self.pixmap_marks.draw_rectangle(xgc, False, p[0] - radius, p[1] - radius, radius * 2, radius * 2)
710
# if this cache is disabled
711
if c.status == geocaching.GeocacheCoordinate.STATUS_DISABLED:
713
xgc.set_rgb_fg_color(self.COLOR_CURRENT_CACHE)
715
self.pixmap_marks.draw_line(xgc, p[0]-radius, p[1]-radius, p[0]+radius, p[1]+radius)
717
xgc.set_rgb_fg_color(self.COLOR_CACHE_CENTER)
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]) # ---
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)
730
for w in self.current_cache.get_waypoints():
731
if w['lat'] != -1 and w['lon'] != -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)
742
def __draw_marks_message(self, message):
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)
749
def __draw_marks(self):
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)
760
if self.ts.get_zoom() < self.CACHES_ZOOM_LOWER_BOUND:
761
self.__draw_marks_message('Zoom in to see geocaches.')
764
if self.settings['options_hide_found']:
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.')
772
self.__draw_marks_caches(coords)
776
# next, draw the user defined points
779
coords = self.userpointprovider.get_points_filter((self.pixmap_markspoint2coord([0,0]), self.pixmap_markspoint2coord([self.map_width, self.map_height])))
781
xgc.set_function(gtk.gdk.AND)
783
color = gtk.gdk.color_parse('darkorchid')
784
for c in coords: # for each geocache
785
p = self.coord2point(c)
787
xgc.set_rgb_fg_color(color)
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])
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)
802
# and now for our current data!
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):
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)
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)
831
self.gps_last_position = p
832
if self.point_in_screen(p):
837
xgc.set_function(gtk.gdk.COPY)
838
xgc.set_rgb_fg_color(self.COLOR_CURRENT_POSITION)
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])
850
# if we have a bearing, draw it.
851
if self.gps_data.bearing. != None:
852
bearing = self.gps_data.bearing
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))
862
# and a line between target and position if we have both
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))
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))
882
# draw cross across the screen
884
xgc.set_function(gtk.gdk.INVERT)
885
xgc.set_rgb_fg_color(self.COLOR_CROSSHAIR)
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)
893
xgc.set_function(gtk.gdk.COPY)
896
def expose_event(self, widget, event):
897
if self.inhibit_expose or self.dragging:
899
x, y, width, height = event.area
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)
909
def expose_event_arrow(self, widget, event):
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)
915
def get_visible_area(self):
916
return (self.pixmappoint2coord([0, 0]), self.pixmappoint2coord([self.map_width, self.map_height]))
918
608
def hide_progress(self):
919
609
self.progressbar.hide()
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)
927
615
if len(self.current_cache.get_images()) > 0:
928
616
self.images = self.current_cache.get_images().items()
931
self.__update_cache_image(reset = True)
933
def on_download_clicked(self, widget):
935
self.core.on_download(self.get_visible_area())
940
def on_download_details_map_clicked(self, some):
941
self.core.on_download_descriptions(self.get_visible_area(), True)
619
self._update_cache_image(reset=True)
621
def on_download_clicked(self, widget, data=None):
622
self.core.on_download(self.map.get_visible_area())
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())
944
628
def on_download_details_sync_clicked(self, something):
945
self.core.on_download_descriptions(self.get_visible_area())
629
self.core.on_download_descriptions(self.map.get_visible_area())
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())
992
669
self.core.on_export_cache(self.current_cache, self.input_export_path.get_value())
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
675
self.gps_target_distance = distance
676
self.gps_target_bearing = bearing
999
678
self.update_gps_display()
1004
# redraw marks if we need to
1005
if not self.drawing_area_configured:
1008
x, y = self.__coord2point(self.gps_data.position)
1009
if self.gps_last_position != None:
1011
l, m = self.gps_last_position
1012
dist_from_last = (x - l) ** 2 + (y - m) ** 2
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)
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):
1034
# update last position, as it is now drawed
1035
self.gps_last_position = (x, y)
1039
# also update when it was None
1040
self.gps_last_position = (x, y)
1043
def redraw_marks(self):
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)
1052
684
self.image_no += 1
1053
685
self.image_no %= len(self.images)
1054
self.__update_cache_image()
686
self._update_cache_image()
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()
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()
1065
698
widget.set_text("you have created %d fieldnotes" % l)
699
self.button_upload_fieldnotes.set_sensitive(True)
1067
701
widget.set_text("you have not created any new fieldnotes")
702
self.button_upload_fieldnotes.set_sensitive(False)
1069
705
def on_list_marked_clicked(self, widget):
1070
self.core.on_start_search_advanced(marked = True)
1073
def on_no_fix(self, gps_data, status):
706
self.core.on_start_search_advanced(marked=True)
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()
1081
def on_notes_changed(self, something, somethingelse):
717
def on_notes_changed(self, something, somethingelse=None):
1082
718
self.notes_changed = True
1084
720
def on_fieldnotes_changed(self, something, somethingelse):
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)
1599
if self.current_target == None:
1184
if self.core.current_target == None:
1602
display_dist = self.gps_data.position.distance_to(self.current_target)
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)
1193
#self.osd_string = "<span gravity='west' size='xx-large'>%d </span>"
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)
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']))
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']))
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>"
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)
1631
1206
w.set_active(self.settings[x])
1632
elif x in self.DEFAULT_SETTINGS.keys():
1633
w = xml.get_widget('check_%s' % x)
1636
w.set_active(self.DEFAULT_SETTINGS[x])
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)
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)
1648
w.set_text(self.DEFAULT_SETTINGS[x])
1216
def shorten_name(s, chars):
1218
min_pos = chars - 10
1224
# Case 1: Return string if it is shorter (or equal to) than the limit
1226
if length <= max_pos:
1229
# Case 2: Return it to nearest period if possible
1231
end = s.rindex('.', min_pos, max_pos)
1233
# Case 3: Return string to nearest space
1234
end = s.rfind(' ', min_pos, max_pos)
1235
if end == NOT_FOUND:
1237
return s[0:end] + suffix
1240
def _on_settings_changed(self, caller, settings, source):
1241
logger.debug("Got settings from %s, len() = %d, source = %s" % (caller, len(settings), source))
1244
self.settings.update(settings)
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'])
1265
self.set_current_cache(cache)
1266
self._on_settings_changed_gui(settings)
1650
1267
self.block_changes = False
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()
1659
newzoom = self.ts.get_zoom() + direction
1660
self.ts.set_zoom(newzoom)
1661
self.set_center(center)
1271
def _on_save_settings(self, caller):
1272
c = self.map.get_center()
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()
1279
if self.current_cache != None:
1280
settings['last_selected_geocache'] = self.current_cache.name
1282
for x in self.SETTINGS_CHECKBOXES:
1283
w = xml.get_widget('check_%s' % x)
1285
settings[x] = w.get_active()
1287
logger.info("Couldn't find widget: check_%s" % x)
1289
for x in self.SETTINGS_INPUTS:
1290
w = xml.get_widget('input_%s' % x)
1292
settings[x] = w.get_text()
1294
logger.info("Couldn't find widget: input_%s" % x)
1295
caller.save_settings(settings, self)
1297
def on_button_check_updates_clicked(self, caller):
1298
updates = self.core.try_update()
1300
gobject.idle_add(self.show_success, "%d modules upgraded. There's no need to restart AGTL." % updates)
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)
1677
font = pango.FontDescription("sans 8")
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)
1317
font = pango.FontDescription("sans 8")
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)
1684
1324
def value_up(self, target):
1685
1325
self.value = int((self.value + 1) % 10)