/**************************************************************************************

   Fotocx - edit photos and manage collections

   Copyright 2007-2026 Michael Cornelison
   source code URL: https://kornelix.net
   contact: mkornelix@gmail.com

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version. See https://www.gnu.org/licenses

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
   See the GNU General Public License for more details.

***************************************************************************************

   Geotag mapping (internet world map)

   m_worldmap                 initialize internet world map
   paint_map_markers          paint map markers where images are located
   m_map_regions              add custom map regions, goto region
   m_map_location             input a (partial) location name, goto map location
   m_set_map_markers          show map markers for all images or current gallery
   m_map_zoomin               zoom map in on an image location
   map_zoomto                 callable with input geocoordinates and zoom level
   mapscale                   get map scale at zoom level
   map_mousefunc              respond to clicks on map
   find_map_images            find images at map marker

***************************************************************************************/

#define EX extern                                                                      //  enable extern declarations
#include <champlain-gtk/champlain-gtk.h>
#include "fotocx.h"                                                                    //  (variables in fotocx.h are refs)

using namespace zfuncs;


/**************************************************************************************/

//  internet map using libchamplain (M view)

namespace maps
{
   GtkWidget                   *mapwidget = 0;
   ChamplainView               *mapview = 0;
   ChamplainMapSourceFactory   *map_factory = 0;
   ChamplainMapSource          *map_source = 0;
   ChamplainMarkerLayer        *markerlayer = 0;
   ChamplainMarker             *marker[maximages];
   ClutterColor                *markercolor;
   ChamplainRenderer           *renderer;
   ChamplainMapSource          *error_source;
   ChamplainNetworkTileSource  *tile_source;
   ChamplainFileCache          *file_cache;
   ChamplainMemoryCache        *memory_cache;
   ChamplainMapSourceChain     *source_chain;
// ch                          *map_source_chain = "mff-relief";
   ch                          *map_source_chain = "osm-mapnik";
}


void map_mousefunc(GtkWidget *, GdkEventButton *, void *);                             //  mouse click function for map view
void find_map_images(float flati, float flongi);                                       //  find images at clicked position


/**************************************************************************************/

//  initialize for internet map

void m_worldmap(GtkWidget *, ch *menu)
{
   using namespace maps;

   if (menu) F1_help_topic = TX("WORLD MAP");

   printf("m_load_map \n");

   if (Xindexlev < 1) {
      index_rebuild(1,0);                                                              //  25.1
      if (Nxxrec == 0) {
         zmessageACK(Mwin,TX("image index required"));
         return;
      }
   }

   load_imagelocs();                                                                   //  load image geolocs[] data

   if (markerlayer) {                                                                  //  refresh map markers
      paint_map_markers();
      return;
   }

   mapwidget = gtk_champlain_embed_new();                                              //  libchamplain map drawing area
   if (! mapwidget) goto fail;
   gtk_container_add(GTK_CONTAINER(Mvbox),mapwidget);

// ------------------------------------------------------------------------------
   GdkWindow   *gdkwin;
   gdkwin = gtk_widget_get_window(mapwidget);                                          //  replace "hand" cursor with arrow
   gdk_window_set_cursor(gdkwin,0);                                                    //  these have no effect      FIXME
   gdk_window_set_cursor(gdkwin,arrowcursor);
   gdk_window_set_device_cursor(gdkwin,zfuncs::mouse,arrowcursor);
// ------------------------------------------------------------------------------

   mapview = gtk_champlain_embed_get_view(GTK_CHAMPLAIN_EMBED(mapwidget));
   if (! mapview) goto fail;

   champlain_view_set_min_zoom_level(mapview,3);
   map_factory = champlain_map_source_factory_dup_default();
   map_source = champlain_map_source_factory_create_cached_source(map_factory,map_source_chain);
   champlain_view_set_map_source(mapview,map_source);

   markerlayer = champlain_marker_layer_new_full(CHAMPLAIN_SELECTION_SINGLE);
   if (! markerlayer) goto fail;
   champlain_view_add_layer(mapview,CHAMPLAIN_LAYER(markerlayer));
   champlain_marker_layer_set_selection_mode(markerlayer,CHAMPLAIN_SELECTION_NONE);
   markercolor = clutter_color_new(255,0,0,255);

   gtk_widget_add_events(mapwidget,GDK_BUTTON_PRESS_MASK);                             //  connect mouse events to map
   G_SIGNAL(mapwidget,"button-press-event",map_mousefunc,0);
   G_SIGNAL(mapwidget,"button-release-event",map_mousefunc,0);
   G_SIGNAL(mapwidget,"motion-notify-event",map_mousefunc,0);

   paint_map_markers();                                                                //  paint map markers where images
   return;

fail:
   zmessageACK(Mwin,TX("libchamplain failure"));
   return;
}


//  paint markers corresponding to image locations on map

void paint_map_markers()
{
   using namespace maps;

   float    flati, flongi;
   float    plati = 999, plongi = 999;

   champlain_marker_layer_remove_all(markerlayer);

   if (gallerymap)                                                                     //  use gallerymap[] if present
   {                                                                                   //  mark gallery images on map
      for (int ii = 0; ii < Ngallerymap; ii++)
      {
         flati = gallerymap[ii].flati;                                                 //  image geocoordinates
         flongi = gallerymap[ii].flongi;
         if (flati == plati && flongi == plongi) continue;                             //  skip repetitions
         plati = flati;
         plongi = flongi;
         marker[ii] = (ChamplainMarker *) champlain_point_new_full(map_dotsize,markercolor);
         champlain_location_set_location(CHAMPLAIN_LOCATION(marker[ii]),flati,flongi);
         champlain_marker_layer_add_marker(markerlayer,marker[ii]);
      }
   }

   else
   {
      for (int ii = 0; ii < Nimagelocs; ii++)                                          //  mark all images on map
      {
         flati = imagelocs[ii]->flati;
         flongi = imagelocs[ii]->flongi;
         marker[ii] = (ChamplainMarker *) champlain_point_new_full(map_dotsize,markercolor);
         champlain_location_set_location(CHAMPLAIN_LOCATION(marker[ii]),flati,flongi);
         champlain_marker_layer_add_marker(markerlayer,marker[ii]);
      }
   }

   gtk_widget_show_all(mapwidget);
   return;
}


/**************************************************************************************/

//  Save current map region (center and scale) with a given name,
//    or retrieve a previously saved map region.

namespace map_regions_names
{
   zdialog     *zdmapreg = 0;
   ch          regname[80];
   double      reglati = 0, reglongi = 0;
   int         regzoom = 12;
   ch          buff[100];
}


//  menu function

void m_map_regions(GtkWidget *, ch *)
{
   using namespace map_regions_names;

   int   map_regions_dialog_event(zdialog *zd, ch *event);
   int   map_regions_clickfunc(GtkWidget *, int line, int pos, ch *input);

   zdialog     *zd;
   GtkWidget   *mtext;
   ch          *pp;
   FILE        *fid;

   F1_help_topic = TX("MAP REGIONS");

   printf("m_map_regions \n");

   if (Xindexlev < 1) {
      index_rebuild(1,0);                                                              //  25.1
      if (Nxxrec == 0) {
         zmessageACK(Mwin,TX("image index required"));
         return;
      }
   }

   viewmode('M');

/***
       ________________________________
      |           Map regions          |
      | ______________________________ |
      ||                              ||
      || map region name 1            ||
      || long map region name 2       ||     scrolling window
      || map region name 3            ||
      ||  ...                         ||
      ||______________________________||
      |                                |
      | map region: [________________] |     text entry for region name
      |                                |
      |             [Add] [Delete] [X] |
      |________________________________|


      [region]       empty until filled-in or a region from the list is clicked
      [add]          current region is added to list or replaced
      [delete]       current region is deleted from list

      region position and scale is from current map location and scale
      region list is kept in alphabetic order

***/

   if (zdmapreg) return;                                                               //  already active

   zd = zdialog_new(TX("Map regions"),Mwin,TX("Add"),TX("Delete"),"X",null);
   zdmapreg = zd;
   zdialog_add_widget(zd,"scrwin","scrregs","dialog",0,"expand");
   zdialog_add_widget(zd,"text","mtext","scrregs");
   zdialog_add_widget(zd,"hbox","hbvn","dialog",0,"space=3");
   zdialog_add_widget(zd,"label","labvn","hbvn",TX("map region:"),"space=3");
   zdialog_add_widget(zd,"zentry","regname","hbvn","","space=3");

   zdialog_resize(zd,200,300);
   zdialog_run(zd,map_regions_dialog_event,"mouse");

   mtext = zdialog_gtkwidget(zd,"mtext");                                              //  map region list in dialog
   txwidget_clear(mtext);

   fid = fopen(map_regions_file,"r");                                                  //  map region list file
   if (fid) {
      while (true) {
         pp = fgets_trim(buff,100,fid,1);                                              //  read region | lati | longi | zoom
         if (! pp) break;
         pp = substring(buff,'|',1);                                                   //  isolate region
         if (! pp) continue;
         if (strlen(pp) < 2) continue;
         txwidget_append(mtext,0,"%s \n",pp);                                          //  write into dialog list
      }
      fclose(fid);
   }

   txwidget_set_eventfunc(mtext,map_regions_clickfunc);                                //  set mouse/KB event function
   return;
}


//  dialog event and completion callback function

int map_regions_dialog_event(zdialog *zd, ch *event)
{
   using namespace maps;
   using namespace map_regions_names;

   int         ii, cc;
   ch          *pp;
   GtkWidget   *mtext;
   FILE        *fidr;
   zlist_t     *ZLregs = 0;
   ch          regname2[100];

   if (! zd->zstat) return 1;                                                          //  wait for completion

   if (zd->zstat == 1)                                                                 //  [add] new map region record
   {
      zdialog_fetch(zd,"regname",regname,80);
      if (strTrim2(regname) < 2) {
         zmessageACK(Mwin,TX("supply a reasonable name"));
         return 1;
      }

      reglati = champlain_view_get_center_latitude(mapview);                           //  get current map region
      reglongi = champlain_view_get_center_longitude(mapview);
      regzoom = champlain_view_get_zoom_level(mapview);

      snprintf(buff,100,"%s|%.4f|%.4f|%d",regname,reglati,reglongi,regzoom);           //  prepare new region rec.

      ZLregs = zlist_from_file(map_regions_file);                                      //  get region list

      strcpy(regname2,regname);                                                        //  get regname|
      strcat(regname2,"|");
      cc = strlen(regname2);

      for (ii = 0; ii < zlist_count(ZLregs); ii++)                                     //  remove matching name from region list
         if (strmatchcaseN(regname2,zlist_get(ZLregs,ii),cc))
            zlist_remove(ZLregs,ii);

      for (ii = 0; ii < zlist_count(ZLregs); ii++) {
         if (strcasecmp(regname2,zlist_get(ZLregs,ii)) < 0) {                          //  insert new region in sort order
            zlist_insert(ZLregs,buff,ii);
            break;
         }
      }

      if (ii == zlist_count(ZLregs))                                                   //  new region is last
         zlist_append(ZLregs,buff,0);

      zlist_to_file(ZLregs,map_regions_file);                                          //  replace file
      goto update_dialog;
   }

   if (zd->zstat == 2)                                                                 //  [delete] selected map region record
   {
      zdialog_fetch(zd,"regname",regname,80);

      ZLregs = zlist_from_file(map_regions_file);                                      //  get region list

      strcpy(regname2,regname);                                                        //  get regname|
      strcat(regname2,"|");
      cc = strlen(regname2);

      for (ii = 0; ii < zlist_count(ZLregs); ii++)                                     //  remove matching name from region list
         if (strmatchcaseN(regname2,zlist_get(ZLregs,ii),cc))
            zlist_remove(ZLregs,ii);

      zlist_to_file(ZLregs,map_regions_file);                                          //  replace file
      goto update_dialog;
   }

   zdialog_free(zd);                                                                   //  cancel
   zdmapreg = 0;
   return 1;

update_dialog:

   zd->zstat = 0;                                                                      //  keep dialog active

   if (ZLregs) zlist_free(ZLregs);

   mtext = zdialog_gtkwidget(zd,"mtext");                                              //  map region name list in dialog
   txwidget_clear(mtext);                                                              //  clear list

   fidr = fopen(map_regions_file,"r");                                                 //  update dialog list from file
   if (! fidr) return 1;

   while (true) {
      pp = fgets_trim(buff,100,fidr,1);                                                //  read region | lati | longi | zoom
      if (! pp) break;
      pp = substring(buff,'|',1);                                                      //  isolate region
      if (! pp) continue;
      if (strlen(pp) < 2) continue;
      txwidget_append2(mtext,0,"%s \n",pp);                                            //  write into dialog list
   }
   fclose(fidr);

   return 1;
}


//  get clicked region name and set corresponding map region and zoom level

int map_regions_clickfunc(GtkWidget *widget, int line, int pos, ch *input)
{
   using namespace map_regions_names;

   ch       *pp1, *pp2;
   FILE     *fidr;
   zdialog  *zd = zdmapreg;

   if (! zd) return 1;

   if (*input == GDK_KEY_F1) {                                                         //  key F1 pressed, show help
      show_help(F1_help_topic);
      return 1;
   }

   pp1 = txwidget_line(widget,line,1);                                                 //  get clicked line, highlight
   if (! pp1 || ! *pp1) return 1;
   txwidget_highlight_line(widget,line);

   strTrim2(regname,pp1);
   zdialog_stuff(zd,"regname",regname);

   fidr = fopen(map_regions_file,"r");                                                 //  open/read map regs file
   if (! fidr) {
      zmessageACK(Mwin,strerror(errno));
      return 1;
   }

   while (true)                                                                        //  read next region record
   {
      pp2 = fgets_trim(buff,100,fidr);
      if (! pp2) break;
      pp2 = substring(buff,'|',1);
      if (! pp2) continue;
      if (strmatch(regname,pp2)) break;                                                //  found matching record
   }

   fclose(fidr);
   if (! pp2 || ! strmatch(regname,pp2)) goto notfound;

   reglati = reglongi = regzoom = 0;

   pp1 = substring(buff,'|',2);                                                        //  get map region data from record
   if (! pp1) goto baddata;
   reglati = atofz(pp1);
   if (reglati <= -90 || reglati >= +90) goto baddata;

   pp1 = substring(buff,'|',3);
   if (! pp1) goto baddata;
   reglongi = atofz(pp1);
   if (reglongi <= -180 || reglongi >= +180) goto baddata;

   pp1 = substring(buff,'|',4);
   if (! pp1) goto baddata;
   regzoom = atoi(pp1);
   if (regzoom < 1 || regzoom > 20) goto baddata;

   map_zoomto(reglati,reglongi,regzoom);                                               //  set this map region
   return 1;

notfound:
   printf("*** map region not found: %s \n",regname);
   return 0;

baddata:
   printf("*** map region invalid: %s %.4f %.4f %d \n",
                  regname,reglati,reglongi,regzoom);
   return 0;
}


/**************************************************************************************/

//  Input a (partial) location name, choose full name from picklist,
//  goto corresponding map location

void m_map_location(GtkWidget *, ch *)
{
   zdialog  *zd;
   int      zstat, err;
   ch       location[40], country[40];
   float    flati = 0, flongi = 0;

   F1_help_topic = TX("MAP LOCATION");

/***
          ________________________________
         |     Go to map location         |
         |                                |
         | enter (partial) location names |
         | location [___________________] |
         | country  [___________________] |
         |                                |
         |                       [OK] [X] |
         |________________________________|

***/

   zd = zdialog_new(TX("Go to map location"),Mwin,"OK","X",null);
   zdialog_add_widget(zd,"hbox","hbent","dialog",0,"space=5");
   zdialog_add_widget(zd,"label","labent","hbent","enter (partial) location names","space=3");           //  26.0
   zdialog_add_widget(zd,"hbox","hbloc","dialog",0,"space=3");
   zdialog_add_widget(zd,"label","labloc","hbloc","location","space=3");
   zdialog_add_widget(zd,"zentry","location","hbloc",0,"space=3|expand");
   zdialog_add_widget(zd,"hbox","hbcon","dialog",0,"space=3");
   zdialog_add_widget(zd,"label","labcon","hbcon","country","space=3");
   zdialog_add_widget(zd,"zentry","country","hbcon",0,"space=3|expand");

   zdialog_resize(zd,300,0);
   zdialog_load_inputs(zd);
   zdialog_run(zd,0,"mouse");

   zstat = zdialog_wait(zd);                                                           //  get partial location input
   if (zstat != 1) {
      zdialog_free(zd);
      return;
   }

   zdialog_fetch(zd,"location",location,40);
   zdialog_fetch(zd,"country",country,40);
   zdialog_free(zd);

   err = choose_location(location,country,flati,flongi);                               //  present picklist, choose location     26.0
   if (err) return;

   map_zoomto(flati,flongi,11);                                                        //  goto map location                     26.0
   printf("location: %s %s  %.4f %.4f \n",location,country,flati,flongi);

   return;
}


/**************************************************************************************/

//  choose to mark map locations for all images or current gallery only

void m_set_map_markers(GtkWidget *, ch *)
{
   zdialog        *zd;
   int            zstat, showall = 0;

   F1_help_topic = TX("MAP MARKERS");

   printf("m_map_markers \n");

   if (Xindexlev < 1) {
      index_rebuild(1,0);                                                              //  25.1
      if (Nxxrec == 0) {
         zmessageACK(Mwin,TX("image index required"));
         return;
      }
   }

   viewmode('M');                                                                      //  set view mode M

/***
          _____________________________
         |      Set Map Markers        |
         |                             |
         | (o) mark all image files    |
         | (o) mark current gallery    |
         |                             |
         |                 [Apply] [X] |
         |_____________________________|

***/

   zd = zdialog_new("Set Map Markers",Mwin,TX("Apply"),"X",null);
   zdialog_add_widget(zd,"radio","all","dialog","mark all image files");
   zdialog_add_widget(zd,"radio","gallery","dialog","mark current gallery");
   zdialog_stuff(zd,"all",1);
   zdialog_stuff(zd,"gallery",0);

   zdialog_load_inputs(zd);
   zdialog_resize(zd,200,0);
   zdialog_set_modal(zd);
   zdialog_run(zd,0,"mouse");

   zstat = zdialog_wait(zd);
   if (zstat != 1) {
      zdialog_free(zd);
      return;
   }

   zdialog_fetch(zd,"all",showall);                                                    //  show all images
   zdialog_free(zd);

   if (showall) {
      if (gallerymap) {                                                                //  free gallerymap
         for (int ii = 0; ii < Ngallerymap; ii++)
            zfree(gallerymap[ii].file);
         zfree(gallerymap);
         gallerymap = 0;
      }
   }

   else get_gallerymap();                                                              //  show gallery images only

   if (FGM == 'M') paint_map_markers();                                                //  map view

   return;
}


/**************************************************************************************/

//  map zoom-in on location of a selected image file

void m_map_zoomin(GtkWidget *, ch *menu)
{
   using namespace maps;

   static ch      *file = 0;
   float          flati, flongi;
   xxrec_t        *xxrec;

   printf("m_map_zoomin \n");

   F1_help_topic = TX("SHOW ON MAP");

   if (file) zfree(file);
   file = 0;

   if (clicked_file) {                                                                 //  use clicked file if present
      file = clicked_file;
      clicked_file = 0;
   }
   else if (curr_file)                                                                 //  else current file
      file = zstrdup(curr_file,"map-zoomin");
   else return;

   xxrec = get_xxrec(file);
   if (! xxrec) return;                                                                //  deleted, not an image file

   get_gps_data(xxrec->gps_data,flati,flongi);
   if (flati == 0 && flongi == 0) return;

   viewmode('M');
   map_zoomto(flati,flongi,16);                                                        //  26.5
   return;
}


//  map zoom-in on specified location with specified zoom level

void map_zoomto(float flati, float flongi, int zoomlev)
{
   using namespace maps;

   m_worldmap(0,0);
   champlain_view_set_zoom_level(mapview,zoomlev);                                     //  swap posn of two calls                26.5
   champlain_view_center_on(mapview,flati,flongi);
   return;
}


//  get current map scale (meters/pixel) at given zoom level and geocoordinates

float mapscale(int zoomlev, float flat, float flong)
{
   using namespace maps;
   float fmpp = champlain_map_source_get_meters_per_pixel(map_source,zoomlev,flat,flong);
   return fmpp;
}


/**************************************************************************************/

//  Respond to mouse clicks on map image.

void map_mousefunc(GtkWidget *widget, GdkEventButton *event, void *)
{
   using namespace maps;

   int         mx, my, px, py;
   int         iim, iic, Fuseloc;
   int         KBshift, KBalt, button;
   ch          *location = 0, *country = 0;
   float       flati, flongi, glati, glongi, km;
   int         dist, capturedist = map_dotsize + 2;                                    //  mouse - marker capture distance
   static int  downtime;
   ch          text[100], gps_data[24];
   int         mapww, maphh;
   zdialog     *zd = zd_mapgeotags;

   static ChamplainMarker *marker = 0;
   ClutterColor *newcolor = clutter_color_new(0,0,255,255);

   if (! mapview) return;                                                              //  map not available

   mx = event->x;                                                                      //  mouse position in map widget
   my = event->y;

   KBshift = event->state & GDK_SHIFT_MASK;                                            //  state of shift key
   KBalt = event->state & GDK_MOD1_MASK;

   button = event->button;
   if (button == 1 && KBalt) button = 3;                                               //  left butt + ALT key >> right butt

   flati = champlain_view_y_to_latitude(mapview,my);                                   //  corresp. map geocoordinates
   flongi = champlain_view_x_to_longitude(mapview,mx);

   glati = glongi = 0;

   km = nearest_loc(flati,flongi,iim,iic);                                             //  find nearest known location

   if (iim > -1) {
      glati = imagelocs[iim]->flati;                                                   //  image location
      glongi = imagelocs[iim]->flongi;
      location = imagelocs[iim]->location;
      country = imagelocs[iim]->country;
   }

   if (iic > -1) {                                                                     //  world cities location
      glati = worldlocs[iic]->flati;
      glongi = worldlocs[iic]->flongi;
      location = worldlocs[iic]->location;
      country = worldlocs[iic]->country;
   }
   
   if (km > 20) location = 0;                                                          //  nothing within 20 km                  26.0
   
   Fuseloc = 0;

   if (location) {
      px = champlain_view_longitude_to_x(mapview,glongi);                              //  map pixel location
      py = champlain_view_latitude_to_y(mapview,glati);
      dist = sqrtf((px-mx) * (px-mx) + (py-my) * (py-my));                             //  distance in pixels
      if (dist <= capturedist) Fuseloc = 1;                                            //  mouse is within capture distance
   }

   if (event->type == GDK_BUTTON_PRESS) {
      downtime = event->time;
      return;
   }

   if (event->type == GDK_BUTTON_RELEASE)                                              //  detect button click
   {                                                                                   //  to ignore drags
      if (event->time - downtime > 600) return;

      if (zd)                                                                          //  stuff calling dialog
      {
         if (Fuseloc) {
            zdialog_stuff(zd,"location",location);                                     //  use nearest location data
            zdialog_stuff(zd,"country",country);
            snprintf(gps_data,24,"%.4f %.4f",glati,glongi);                            //  25.1
            zdialog_stuff(zd,"gps_data",gps_data);
         }

         else {
            snprintf(gps_data,24,"%.4f %.4f",flati,flongi);                            //  new location clicked                  25.1
            zdialog_stuff(zd,"gps_data",gps_data);

            if (markerlayer) {                                                         //  26.5
               if (marker) champlain_marker_layer_remove_marker(markerlayer,marker);   //  add map marker                        26.3
               marker = (ChamplainMarker *) champlain_point_new_full(map_dotsize,newcolor);
               champlain_location_set_location(CHAMPLAIN_LOCATION(marker),flati,flongi);
               champlain_marker_layer_add_marker(markerlayer,marker);
            }

            if (location) {
               zdialog_stuff(zd,"location",location);                                  //  location if found
               zdialog_stuff(zd,"country",country);
            }
            else {
               zdialog_stuff(zd,"location","");
               zdialog_stuff(zd,"country","");
            }

            zdialog_send_event(zd,"geomap");                                           //  activate calling dialog
         }
      }

      else if (button == 1)                                                            //  left click
      {
         if (KBshift) {
            snprintf(text,20,"%.4f %.4f",flati,flongi);                                //  shift key - show coordinates
            poptext_mouse(text,20,-20,0.1,3);
         }

         else if (flati || flongi)                                                     //  on marker - show corresp. images      26.1
            find_map_images(flati,flongi);

         else {
            champlain_view_center_on(mapview,flati,flongi);                            //  zoom-in at clicked location
            champlain_view_zoom_in(mapview);
            mapww = gtk_widget_get_allocated_width(mapwidget);                         //  move mouse to center
            maphh = gtk_widget_get_allocated_height(mapwidget);
            move_pointer(mapwidget,mapww/2,maphh/2);
         }
      }

      else if (button == 3)                                                            //  right click
         champlain_view_zoom_out(mapview);                                             //  zoom out

      return;
   }

   downtime = 0;                                                                       //  mouse motion

   if (location) {
      snprintf(text,100,"%s \n %.4f %.4f",location,flati,flongi);                      //  show location, coordinates
      poptext_mouse(text,20,-20,0.1,3);
   }

   else {
      snprintf(text,20,"%.4f %.4f",flati,flongi);                                      //  show coordinates
      poptext_mouse(text,20,-20,0.1,3);
   }

   return;
}


//  find images within the marker size, show gallery of images.
//  privat function for map_mousefunc(), called when a location is clicked

void find_map_images(float flati, float flongi)
{
   using namespace maps;

   int         ii, nn = 0;
   int         x1, y1, x2, y2;
   int         capturedist = map_dotsize + 2;                                          //  mouse - marker capture distance
   float       glati, glongi, grange;
   FILE        *fid;
   xxrec_t     *xxrec;

   if (Xindexlev < 1) {
      index_rebuild(1,0);                                                              //  25.1
      if (Nxxrec == 0) {
         zmessageACK(Mwin,TX("image index required"));
         return;
      }
   }

   x1 = champlain_view_longitude_to_x(mapview,flongi);                                 //  target map pixel location
   y1 = champlain_view_latitude_to_y(mapview,flati);

   fid = fopen(searchresults_file,"w");                                                //  open output file
   if (! fid) {
      zmessageACK(Mwin,TX("output file error: %s"),strerror(errno));
      return;
   }

   if (gallerymap)                                                                     //  show gallery images at location
   {
      for (ii = 0; ii < Ngallerymap; ii++)                                             //  loop all gallery files
      {
         zmainloop();                                                                  //  keep GTK alive

         glati = gallerymap[ii].flati;                                                 //  image geocoordinates
         glongi = gallerymap[ii].flongi;

         x2 = champlain_view_longitude_to_x(mapview,glongi);                           //  image map pixel location
         y2 = champlain_view_latitude_to_y(mapview,glati);

         grange = sqrtf((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));                            //  mouse - image pixel distance
         if (grange < 1.5 * capturedist) {                                             //  within distance limit, select
            fprintf(fid,"%s\n",gallerymap[ii].file);                                   //  output matching file
            nn++;
         }
      }
   }

   else                                                                                //  show all images at location
   {
      for (ii = 0; ii < Nxxrec; ii++)
      {
         zmainloop();                                                                  //  keep GTK alive

         xxrec = xxrec_tab[ii];

         if (! *xxrec->gps_data) continue;

         get_gps_data(xxrec->gps_data,glati,glongi);

         x2 = champlain_view_longitude_to_x(mapview,glongi);                           //  image map pixel location
         y2 = champlain_view_latitude_to_y(mapview,glati);

         grange = sqrtf((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));                            //  mouse - image pixel distance
         if (grange < 1.5 * capturedist) {                                             //  within distance limit, select
            fprintf(fid,"%s\n",xxrec->file);                                           //  output matching file
            nn++;
         }
      }
   }

   fclose(fid);

   if (! nn) {
      poptext_mouse(TX("No matching images found"),10,0,0,3);
      return;
   }

   navi::gallerytype = SEARCH;                                                         //  search results
   gallery(searchresults_file,"initF",0);                                              //  generate gallery of matching files
   gallery(0,"paint",0);
   viewmode('G');

   return;
}



