From d443a3c2509889533ca812c163056bace396b586 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Wed, 14 Jun 2023 20:35:58 +0200 Subject: New upstream version 0.32.1 --- src/MapWidget.vala | 788 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 788 insertions(+) create mode 100644 src/MapWidget.vala (limited to 'src/MapWidget.vala') diff --git a/src/MapWidget.vala b/src/MapWidget.vala new file mode 100644 index 0000000..ddfae38 --- /dev/null +++ b/src/MapWidget.vala @@ -0,0 +1,788 @@ +/* Copyright 2016 Software Freedom Conservancy Inc. + * + * This software is licensed under the GNU LGPL (version 2.1 or later). + * See the COPYING file in this distribution. + */ + +private class MarkerImageSet { + public float marker_image_width; + public float marker_image_height; + public Clutter.Image? marker_image; + public Clutter.Image? marker_selected_image; + public Clutter.Image? marker_highlighted_image; +} + +private enum SelectionAction { + SET, + ADD, + REMOVE +} + +private abstract class PositionMarker : Object { + protected bool _highlighted = false; + protected bool _selected = false; + protected MarkerImageSet image_set; + + protected PositionMarker(Champlain.Marker champlain_marker, MarkerImageSet image_set) { + this.champlain_marker = champlain_marker; + this.image_set = image_set; + champlain_marker.selectable = true; + champlain_marker.set_content(image_set.marker_image); + float w = image_set.marker_image_width; + float h = image_set.marker_image_height; + champlain_marker.set_size(w, h); + champlain_marker.set_translation(-w * MapWidget.MARKER_IMAGE_HORIZONTAL_PIN_RATIO, + -h * MapWidget.MARKER_IMAGE_VERTICAL_PIN_RATIO, 0); + } + + public Champlain.Marker champlain_marker { get; protected set; } + + public bool highlighted { + get { + return _highlighted; + } + set { + if (_highlighted == value) + return; + _highlighted = value; + var base_image = _selected ? image_set.marker_selected_image : image_set.marker_image; + champlain_marker.set_content(value ? image_set.marker_highlighted_image : base_image); + } + } + public bool selected { + get { + return _selected; + } + set { + if (_selected == value) + return; + _selected = value; + if (!_highlighted) { + var base_image = value ? image_set.marker_selected_image : image_set.marker_image; + champlain_marker.set_content(base_image); + } + champlain_marker.set_selected(value); + } + } +} + +private class DataViewPositionMarker : PositionMarker { + private Gee.LinkedList _data_view_position_markers = + new Gee.LinkedList(); + + public weak DataView view { get; protected set; } + + public DataViewPositionMarker(DataView view, Champlain.Marker champlain_marker, + MarkerImageSet image_set) { + base(champlain_marker, image_set); + this.view = view; + + this._data_view_position_markers.add(this); + } + + public void bind_mouse_events(MapWidget map_widget) { + champlain_marker.button_release_event.connect ((event) => { + if (event.button > 1) + return true; + bool mod = (bool)(event.modifier_state & + (Clutter.ModifierType.CONTROL_MASK | Clutter.ModifierType.SHIFT_MASK)); + SelectionAction action = SelectionAction.SET; + if (mod) + action = _selected ? SelectionAction.REMOVE : SelectionAction.ADD; + selected = (action != SelectionAction.REMOVE); + map_widget.select_data_views(_data_view_position_markers, action); + return true; + }); + champlain_marker.enter_event.connect ((event) => { + highlighted = true; + map_widget.highlight_data_views(_data_view_position_markers); + return true; + }); + champlain_marker.leave_event.connect ((event) => { + highlighted = false; + map_widget.unhighlight_data_views(_data_view_position_markers); + return true; + }); + } +} + +private class MarkerGroup : PositionMarker { + private Gee.Collection _data_view_position_markers = + new Gee.LinkedList(); + private Gee.Collection _position_markers = new Gee.LinkedList(); + private Champlain.BoundingBox bbox = new Champlain.BoundingBox(); + + public void bind_mouse_events(MapWidget map_widget) { + champlain_marker.button_release_event.connect ((event) => { + if (event.button > 1) + return true; + bool mod = (bool)(event.modifier_state & + (Clutter.ModifierType.CONTROL_MASK | Clutter.ModifierType.SHIFT_MASK)); + SelectionAction action = SelectionAction.SET; + if (mod) + action = _selected ? SelectionAction.REMOVE : SelectionAction.ADD; + selected = (action != SelectionAction.REMOVE); + foreach (var m in _data_view_position_markers) { + m.selected = _selected; + } + map_widget.select_data_views(_data_view_position_markers.read_only_view, action); + return true; + }); + champlain_marker.enter_event.connect ((event) => { + highlighted = true; + map_widget.highlight_data_views(_data_view_position_markers.read_only_view); + return true; + }); + champlain_marker.leave_event.connect ((event) => { + highlighted = false; + map_widget.unhighlight_data_views(_data_view_position_markers.read_only_view); + return true; + }); + } + + public Gee.Collection position_markers { + owned get { return _position_markers.read_only_view; } + } + + public MarkerGroup(Champlain.Marker champlain_marker, MarkerImageSet image_set) { + base(champlain_marker, image_set); + } + + public void add_position_marker(PositionMarker marker) { + var data_view_position_marker = marker as DataViewPositionMarker; + if (data_view_position_marker != null) + _data_view_position_markers.add(data_view_position_marker); + var new_champlain_marker = marker.champlain_marker; + bbox.extend(new_champlain_marker.latitude, new_champlain_marker.longitude); + double lat, lon; + bbox.get_center(out lat, out lon); + champlain_marker.set_location(lat, lon); + _position_markers.add(marker); + } +} + +private class MarkerGroupRaster : Object { + private const long MARKER_GROUP_RASTER_WIDTH_PX = 30l; + private const long MARKER_GROUP_RASTER_HEIGHT_PX = 30l; + + private weak MapWidget map_widget; + private weak Champlain.View map_view; + private weak Champlain.MarkerLayer marker_layer; + + public bool is_empty { + get { + return position_markers.is_empty; + } + } + + // position_markers_tree is a two-dimensional tree for grouping position + // markers indexed by x (outer tree) and y (inner tree) raster coordinates. + // It maps coordinates to the PositionMarker (DataViewMarker or MarkerGroup) + // corresponding to them. + // If either raster index keys are empty, there is no marker within the + // raster cell. If both exist there are two possibilities: + // (1) the value is a MarkerGroup which means that multiple markers are + // grouped together, or (2) the value is a PositionMarker (but not a + // MarkerGroup) which means that there is exactly one marker in the raster + // cell. The tree is recreated every time the zoom level changes. + private Gee.TreeMap?> position_markers_tree = + new Gee.TreeMap?>(); + // The marker group's collection keeps track of and owns all PositionMarkers including the marker groups + private Gee.Map data_view_map = new Gee.HashMap(); + private Gee.Set position_markers = new Gee.HashSet(); + + public MarkerGroupRaster(MapWidget map_widget, Champlain.View map_view, Champlain.MarkerLayer marker_layer) { + this.map_widget = map_widget; + this.map_view = map_view; + this.marker_layer = marker_layer; + map_widget.zoom_changed.connect(regroup); + } + + public void clear() { + lock (position_markers) { + data_view_map.clear(); + position_markers_tree.clear(); + position_markers.clear(); + } + } + + public void clear_selection() { + lock (position_markers) { + foreach (PositionMarker m in position_markers) { + m.selected = false; + } + } + } + + public unowned PositionMarker? find_position_marker(DataView data_view) { + if (!data_view_map.has_key(data_view)) + return null; + unowned PositionMarker? m; + lock (position_markers) { + m = data_view_map.get(data_view); + } + return m; + } + + public void rasterize_marker(PositionMarker position_marker, bool already_on_map=false) { + var data_view_position_marker = position_marker as DataViewPositionMarker; + var champlain_marker = position_marker.champlain_marker; + long x, y; + + lock (position_markers) { + rasterize_coords(champlain_marker.longitude, champlain_marker.latitude, out x, out y); + var yg = position_markers_tree.get(x); + if (yg == null) { + yg = new Gee.TreeMap(); + position_markers_tree.set(x, yg); + } + var cell = yg.get(y); + if (cell == null) { + // first marker in this raster cell + yg.set(y, position_marker); + position_markers.add(position_marker); + if (!already_on_map) + marker_layer.add_marker(position_marker.champlain_marker); + if (data_view_position_marker != null) + data_view_map.set(data_view_position_marker.view, position_marker); + + } else { + var marker_group = cell as MarkerGroup; + if (marker_group == null) { + // single marker already occupies raster cell: create new group + GpsCoords rasterized_gps_coords = GpsCoords() { + has_gps = 1, + longitude = map_view.x_to_longitude(x), + latitude = map_view.y_to_latitude(y) + }; + marker_group = map_widget.create_marker_group(rasterized_gps_coords); + marker_group.add_position_marker(cell); + if (cell.selected) // group becomes selected if any contained marker is + marker_group.selected = true; + if (cell is DataViewPositionMarker) + data_view_map.set(((DataViewPositionMarker) cell).view, marker_group); + yg.set(y, marker_group); + position_markers.add(marker_group); + position_markers.remove(cell); + marker_layer.add_marker(marker_group.champlain_marker); + marker_layer.remove_marker(cell.champlain_marker); + } + // group already exists, add new marker to it + marker_group.add_position_marker(position_marker); + if (already_on_map) + marker_layer.remove_marker(position_marker.champlain_marker); + if (data_view_position_marker != null) + data_view_map.set(data_view_position_marker.view, marker_group); + } + } + } + + private void rasterize_coords(double longitude, double latitude, out long x, out long y) { + x = (Math.lround(map_view.longitude_to_x(longitude) / MARKER_GROUP_RASTER_WIDTH_PX)) * + MARKER_GROUP_RASTER_WIDTH_PX + (MARKER_GROUP_RASTER_WIDTH_PX / 2); + y = (Math.lround(map_view.latitude_to_y(latitude) / MARKER_GROUP_RASTER_HEIGHT_PX)) * + MARKER_GROUP_RASTER_HEIGHT_PX + (MARKER_GROUP_RASTER_HEIGHT_PX / 2); + } + + internal void regroup() { + lock (position_markers) { + var position_markers_current = (owned) position_markers; + position_markers = new Gee.HashSet(); + position_markers_tree.clear(); + + foreach (var pm in position_markers_current) { + var marker_group = pm as MarkerGroup; + if (marker_group != null) { + marker_layer.remove_marker(marker_group.champlain_marker); + foreach (var position_marker in marker_group.position_markers) { + rasterize_marker(position_marker, false); + } + } else { + rasterize_marker(pm, true); + } + } + position_markers_current = null; + } + } +} + +private class MapWidget : Gtk.Bin { + private const string MAPBOX_API_TOKEN = "pk.eyJ1IjoiamVuc2dlb3JnIiwiYSI6ImNqZ3FtYmhrMTBkOW8yeHBlNG8xN3hlNTAifQ.ek7i8UHeNIlkKi10fhgFgg"; + private const uint DEFAULT_ZOOM_LEVEL = 8; + + private static MapWidget instance = null; + private bool hide_map = false; + + private GtkChamplain.Embed gtk_champlain_widget = new GtkChamplain.Embed(); + private Champlain.View map_view = null; + private Champlain.Scale map_scale = new Champlain.Scale(); + private Champlain.MarkerLayer marker_layer = new Champlain.MarkerLayer(); + public bool map_edit_lock { get; set; } + private MarkerGroupRaster marker_group_raster = null; + private Gee.Map data_view_marker_cache = + new Gee.HashMap(); + private weak Page? page = null; + private Clutter.Image? map_edit_locked_image; + private Clutter.Image? map_edit_unlocked_image; + private Clutter.Actor map_edit_lock_button = new Clutter.Actor(); + private uint position_markers_timeout = 0; + + public const float MARKER_IMAGE_HORIZONTAL_PIN_RATIO = 0.5f; + public const float MARKER_IMAGE_VERTICAL_PIN_RATIO = 0.825f; + public float map_edit_lock_image_width { get; private set; } + public float map_edit_lock_image_height { get; private set; } + public MarkerImageSet marker_image_set { get; private set; } + public MarkerImageSet marker_group_image_set { get; private set; } + public const Clutter.Color marker_point_color = { 10, 10, 255, 192 }; + + public signal void zoom_changed(); + + private MapWidget() { + setup_map(); + add(gtk_champlain_widget); + } + + public static MapWidget get_instance() { + if (instance == null) + instance = new MapWidget(); + return instance; + } + + public override bool drag_motion(Gdk.DragContext context, int x, int y, uint time) { + if (!map_edit_lock) + map_view.stop_go_to(); + else + Gdk.drag_status(context, 0, time); + return true; + } + + public override void drag_data_received(Gdk.DragContext context, int x, int y, + Gtk.SelectionData selection_data, uint info, uint time) { + bool success = false; + Gee.List? media = unserialize_media_sources(selection_data.get_data(), + selection_data.get_length()); + if (media != null && media.size > 0) { + double lat = map_view.y_to_latitude(y); + double lon = map_view.x_to_longitude(x); + success = internal_drop_received(media, lat, lon); + } + + Gtk.drag_finish(context, success, false, time); + } + + public new void set_visible(bool visible) { + /* hides Gtk.Widget.set_visible */ + hide_map = !visible; + base.set_visible(visible); + } + + public override void show_all() { + if (!hide_map) + base.show_all(); + } + + public void set_page(Page page) { + bool page_changed = false; + if (this.page != page) { + this.page = page; + page_changed = true; + clear(); + } + ViewCollection view_collection = page.get_view(); + if (view_collection == null) + return; + + if (page_changed) { + data_view_marker_cache.clear(); + foreach (DataObject view in view_collection.get_all()) { + if (view is DataView) + add_data_view((DataView) view); + } + show_position_markers(); + } + // In any case, the selection did change.. + var selected = view_collection.get_selected(); + if (selected != null) { + marker_group_raster.clear_selection(); + foreach (DataView v in view_collection.get_selected()) { + + var position_marker = marker_group_raster.find_position_marker(v); + if (position_marker != null) + position_marker.selected = true; + if (position_marker is MarkerGroup) { + DataViewPositionMarker? m = data_view_marker_cache.get(v); + if (m != null) + m.selected = true; + } + } + } + } + + public void clear() { + data_view_marker_cache.clear(); + marker_layer.remove_all(); + marker_group_raster.clear(); + } + + public void add_data_view(DataView view) { + DataSource view_source = view.get_source(); + if (!(view_source is Positionable)) + return; + Positionable p = (Positionable) view_source; + GpsCoords gps_coords = p.get_gps_coords(); + if (gps_coords.has_gps <= 0) + return; + PositionMarker position_marker = create_position_marker(view); + marker_group_raster.rasterize_marker(position_marker); + } + + public void show_position_markers() { + if (marker_group_raster.is_empty) + return; + + map_view.stop_go_to(); + double lat, lon; + var bbox = marker_layer.get_bounding_box(); + var zoom_level = map_view.get_zoom_level(); + var zoom_level_test = zoom_level < 2 ? 0 : zoom_level - 2; + bbox.get_center(out lat, out lon); + + if (map_view.get_bounding_box_for_zoom_level(zoom_level_test).covers(lat, lon)) { + // Don't zoom in/out if target is in proximity + map_view.ensure_visible(bbox, true); + } else if (zoom_level >= DEFAULT_ZOOM_LEVEL) { + // zoom out to DEFAULT_ZOOM_LEVEL first, then move + map_view.set_zoom_level(DEFAULT_ZOOM_LEVEL); + map_view.ensure_visible(bbox, true); + } else { + // move first, then zoom in to DEFAULT_ZOOM_LEVEL + map_view.go_to(lat, lon); + // There seems to be a runtime issue with the animation_completed signal + // sig = map_view.animation_completed["go-to"].connect((v) => { ... } + // so we're using a timeout-based approach instead. It should be kept in sync with + // the animation time (500ms by default.) + if (position_markers_timeout > 0) + Source.remove(position_markers_timeout); + position_markers_timeout = Timeout.add(500, () => { + map_view.center_on(lat, lon); // ensure the timeout wasn't too fast + if (map_view.get_zoom_level() < DEFAULT_ZOOM_LEVEL) + map_view.set_zoom_level(DEFAULT_ZOOM_LEVEL); + map_view.ensure_visible(bbox, true); + position_markers_timeout = 0; + return Source.REMOVE; + }); + } + } + + public void select_data_views(Gee.Collection ms, + SelectionAction action = SelectionAction.SET) { + if (page == null) + return; + + ViewCollection page_view = page.get_view(); + if (page_view != null) { + Marker marked = page_view.start_marking(); + foreach (var m in ms) { + if (m.view is CheckerboardItem) { + marked.mark(m.view); + } + } + if (action == SelectionAction.REMOVE) { + page_view.unselect_marked(marked); + } else { + if (action == SelectionAction.SET) + page_view.unselect_all(); + page_view.select_marked(marked); + } + } + } + + public void highlight_data_views(Gee.Collection ms) { + if (page == null) + return; + + bool did_adjust_view = false; + foreach (var m in ms) { + if (!(m.view is CheckerboardItem)) { + continue; + } + + CheckerboardItem item = m.view as CheckerboardItem; + + if (!did_adjust_view && page is CheckerboardPage) { + ((CheckerboardPage) page).scroll_to_item(item); + did_adjust_view = true; + } + item.brighten(); + } + } + + public void unhighlight_data_views(Gee.Collection ms) { + if (page == null) + return; + + foreach (var m in ms) { + if (m.view is CheckerboardItem) { + CheckerboardItem item = (CheckerboardItem) m.view; + item.unbrighten(); + } + } + } + + public void highlight_position_marker(DataView v) { + var position_marker = marker_group_raster.find_position_marker(v); + if (position_marker != null) { + position_marker.highlighted = true; + } + } + + public void unhighlight_position_marker(DataView v) { + var position_marker = marker_group_raster.find_position_marker(v); + if (position_marker != null) { + position_marker.highlighted = false; + } + } + + public void media_source_position_changed(Gee.List media, GpsCoords gps_coords) { + if (page == null) + return; + var view_collection = page.get_view(); + foreach (var source in media) { + var view = view_collection.get_view_for_source(source); + if (view == null) + continue; + var marker = data_view_marker_cache.get(view); + if (marker != null) { + if (gps_coords.has_gps > 0) { + // update individual marker cache + marker.champlain_marker.set_location(gps_coords.latitude, gps_coords.longitude); + } else { + // TODO: position removal not supported by GUI + // remove marker from cache, map_layer + // remove from marker_group_raster (needs a removal method which also removes the + // item from the group if (marker_group_raster.find_position_marker(view) is MarkerGroup) + } + } + } + marker_group_raster.regroup(); + } + + private Champlain.MapSource create_map_source() { + var map_source = new Champlain.MapSourceChain(); + var file_cache = new Champlain.FileCache.full(10 * 1024 * 1024, + AppDirs.get_cache_dir().get_child("tiles").get_child("mapbox-outdoors").get_path(), + new Champlain.ImageRenderer()); + var memory_cache = new Champlain.MemoryCache.full(10 * 1024 * 1024, new Champlain.ImageRenderer()); + var error_source = new Champlain.NullTileSource.full(new Champlain.ImageRenderer()); + + var tile_source = new Champlain.NetworkTileSource.full("mapbox-outdoors", + "Mapbox outdoors tiles", + "", + "", + 0, + 19, + 512, + Champlain.MapProjection.MERCATOR, + "https://api.mapbox.com/styles/v1/mapbox/outdoors-v11/tiles/#Z#/#X#/#Y#?access_token=" + + MAPBOX_API_TOKEN, + new Champlain.ImageRenderer()); + + var user_agent = "Shotwell/%s libchamplain/%s".printf(_VERSION, Champlain.VERSION_S); + tile_source.set_user_agent(user_agent); + tile_source.max_conns = 2; + + map_source.push(error_source); + map_source.push(tile_source); + map_source.push(file_cache); + map_source.push(memory_cache); + + return map_source; + } + + private Clutter.Actor create_attribution_actor() { + const string IMPROVE_TEXT = N_("Improve this map"); + var label = new Gtk.Label(null); + label.set_markup("© Mapbox © OpenStreetMap %s".printf(IMPROVE_TEXT)); + label.get_style_context().add_class("map-attribution"); + + return new GtkClutter.Actor.with_contents(label); + } + + private void setup_map() { + map_view = gtk_champlain_widget.get_view(); + map_view.add_layer(marker_layer); + map_view.set_map_source(create_map_source()); + + var map_attribution_text = create_attribution_actor(); + map_attribution_text.content_gravity = Clutter.ContentGravity.BOTTOM_RIGHT; + map_attribution_text.set_x_align(Clutter.ActorAlign.END); + map_attribution_text.set_x_expand(true); + map_attribution_text.set_y_align(Clutter.ActorAlign.END); + map_attribution_text.set_y_expand(true); + + // add lock/unlock button to top left corner of map + map_edit_lock_button.content_gravity = Clutter.ContentGravity.TOP_RIGHT; + map_edit_lock_button.reactive = true; + map_edit_lock_button.set_x_align(Clutter.ActorAlign.END); + map_edit_lock_button.set_x_expand(true); + map_edit_lock_button.set_y_align(Clutter.ActorAlign.START); + map_edit_lock_button.set_y_expand(true); + + map_edit_lock_button.button_release_event.connect((a, e) => { + if (e.button != 1 /* CLUTTER_BUTTON_PRIMARY */) + return false; + map_edit_lock = !map_edit_lock; + map_edit_lock_button.set_content(map_edit_lock ? + map_edit_locked_image : map_edit_unlocked_image); + return true; + }); + map_view.add_child(map_edit_lock_button); + map_view.add_child(map_attribution_text); + + gtk_champlain_widget.has_tooltip = true; + gtk_champlain_widget.query_tooltip.connect((x, y, keyboard_tooltip, tooltip) => { + Gdk.Rectangle lock_rect = { + (int) map_edit_lock_button.x, + (int) map_edit_lock_button.y, + (int) map_edit_lock_button.width, + (int) map_edit_lock_button.height, + }; + Gdk.Rectangle mouse_pos = { x, y, 1, 1 }; + if (!lock_rect.intersect(mouse_pos, null)) + return false; + tooltip.set_text(_("Lock or unlock map for geotagging by dragging pictures onto the map")); + return true; + }); + + // add scale to bottom left corner of the map + map_scale.content_gravity = Clutter.ContentGravity.BOTTOM_LEFT; + map_scale.connect_view(map_view); + map_scale.set_x_align(Clutter.ActorAlign.START); + map_scale.set_x_expand(true); + map_scale.set_y_align(Clutter.ActorAlign.END); + map_scale.set_y_expand(true); + map_view.add_child(map_scale); + + map_view.set_zoom_on_double_click(false); + map_view.notify.connect((o, p) => { + if (p.name == "zoom-level") + zoom_changed(); + }); + + Gtk.TargetEntry[] dnd_targets = { + LibraryWindow.DND_TARGET_ENTRIES[LibraryWindow.TargetType.URI_LIST], + LibraryWindow.DND_TARGET_ENTRIES[LibraryWindow.TargetType.MEDIA_LIST] + }; + Gtk.drag_dest_set(this, Gtk.DestDefaults.ALL, dnd_targets, + Gdk.DragAction.COPY | Gdk.DragAction.LINK | Gdk.DragAction.ASK); + button_press_event.connect(map_zoom_handler); + set_size_request(200, 200); + + marker_group_raster = new MarkerGroupRaster(this, map_view, marker_layer); + + // Load icons + float w, h; + marker_image_set = new MarkerImageSet(); + marker_group_image_set = new MarkerImageSet(); + marker_image_set.marker_image = Resources.get_icon_as_clutter_image( + Resources.ICON_GPS_MARKER, out w, out h); + marker_image_set.marker_image_width = w; + marker_image_set.marker_image_height = h; + marker_image_set.marker_selected_image = Resources.get_icon_as_clutter_image( + Resources.ICON_GPS_MARKER_SELECTED, out w, out h); + marker_image_set.marker_highlighted_image = Resources.get_icon_as_clutter_image( + Resources.ICON_GPS_MARKER_HIGHLIGHTED, out w, out h); + + marker_group_image_set.marker_image = Resources.get_icon_as_clutter_image( + Resources.ICON_GPS_GROUP_MARKER, out w, out h); + marker_group_image_set.marker_image_width = w; + marker_group_image_set.marker_image_height = h; + marker_group_image_set.marker_selected_image = Resources.get_icon_as_clutter_image( + Resources.ICON_GPS_GROUP_MARKER_SELECTED, out w, out h); + marker_group_image_set.marker_highlighted_image = Resources.get_icon_as_clutter_image( + Resources.ICON_GPS_GROUP_MARKER_HIGHLIGHTED, out w, out h); + + map_edit_locked_image = Resources.get_icon_as_clutter_image( + Resources.ICON_MAP_EDIT_LOCKED, out w, out h); + map_edit_unlocked_image = Resources.get_icon_as_clutter_image( + Resources.ICON_MAP_EDIT_UNLOCKED, out w, out h); + map_edit_lock_image_width = w; + map_edit_lock_image_height = h; + if (map_edit_locked_image == null) { + warning("Couldn't load map edit lock image"); + } else { + map_edit_lock_button.set_content(map_edit_locked_image); + map_edit_lock_button.set_size(map_edit_lock_image_width, map_edit_lock_image_height); + map_edit_lock = true; + } + } + + private Champlain.Marker create_champlain_marker(GpsCoords gps_coords) { + assert(gps_coords.has_gps > 0); + + Champlain.Marker champlain_marker; + champlain_marker = new Champlain.Marker(); + champlain_marker.set_pivot_point(0.5f, 0.5f); // set center of marker + champlain_marker.set_location(gps_coords.latitude, gps_coords.longitude); + return champlain_marker; + } + + private DataViewPositionMarker create_position_marker(DataView view) { + var position_marker = data_view_marker_cache.get(view); + if (position_marker != null) + return position_marker; + DataSource data_source = view.get_source(); + Positionable p = (Positionable) data_source; + GpsCoords gps_coords = p.get_gps_coords(); + Champlain.Marker champlain_marker = create_champlain_marker(gps_coords); + position_marker = new DataViewPositionMarker(view, champlain_marker, marker_image_set); + position_marker.bind_mouse_events(this); + data_view_marker_cache.set(view, position_marker); + return (owned) position_marker; + } + + internal MarkerGroup create_marker_group(GpsCoords gps_coords) { + Champlain.Marker champlain_marker = create_champlain_marker(gps_coords); + var g = new MarkerGroup(champlain_marker, marker_group_image_set); + g.bind_mouse_events(this); + return (owned) g; + } + + private bool map_zoom_handler(Gdk.EventButton event) { + if (event.type == Gdk.EventType.2BUTTON_PRESS) { + if (event.button == 1 || event.button == 3) { + double lat = map_view.y_to_latitude(event.y); + double lon = map_view.x_to_longitude(event.x); + if (event.button == 1) { + map_view.zoom_in(); + } else { + map_view.zoom_out(); + } + map_view.center_on(lat, lon); + return true; + } + } + return false; + } + + private bool internal_drop_received(Gee.List media, double lat, double lon) { + if (map_edit_lock) + return false; + + bool success = false; + GpsCoords gps_coords = GpsCoords() { + has_gps = 1, + latitude = lat, + longitude = lon + }; + foreach (var m in media) { + Positionable p = m as Positionable; + if (p != null) { + p.set_gps_coords(gps_coords); + success = true; + } + } + media_source_position_changed(media, gps_coords); + return success; + } +} -- cgit v1.2.3