source: subversion/applications/rendering/gosmore/jni/gosmore.cpp @ 34655

Last change on this file since 34655 was 29825, checked in by nic, 6 years ago

close #4930
close #4502
Silence compiler warnings about unused and uninitialized variables.
Remove some features that were never completed

  • Property svn:executable set to *
File size: 162.2 KB
Line 
1/* Copyright 2010 Nic Roets as detailed in the README file. */
2/* Written by Nic Roets with contribution(s) from Dave Hansen, Ted Mielczarek
3   David Dean, Pablo D'Angelo, Dmitry and Adrian Batzill.
4   Thanks to
5   * Sven Geggus, Frederick Ramm, Johnny Rose Carlsen and Lambertus for hosting,
6   * Stephan Rossig, Simon Wood, David Dean, Lambertus and many others for testing,
7   * OSMF for partial funding. */
8// rm -f obj/local/armeabi/objs/gosmore/gosmore.o.d; ../../../../android/android-ndk-r5/ndk-build
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12#include <ctype.h>
13#include <math.h>
14#include <time.h>
15#include <string>
16#include <stack>
17#include <vector>
18#include <algorithm>
19#include <queue>
20#include <map>
21
22#define LG  //__android_log_print (ANDROID_LOG_WARN, "Gosmore", "%d", __LINE__);
23#ifdef ANDROID_NDK
24#include <android/log.h>
25#include <jni.h>
26#include <GLES/gl.h>
27#include <GLES/glext.h>
28#define NOGTK
29#define LOG //__android_log_print (ANDROID_LOG_WARN, "Gosmore", "%d", __LINE__);
30#endif
31using namespace std;
32#ifndef _WIN32
33#include <sys/mman.h>
34#include <arpa/inet.h>
35#include <netinet/in.h>
36#include <sys/types.h>
37#include <sys/socket.h>
38#include <sys/file.h>
39#include <sys/time.h>
40#include <errno.h>
41#define TEXT(x) x
42#define TCHAR char
43#else
44#include <windows.h>
45#endif
46#ifdef _WIN32_WCE
47#define NOGTK
48#endif
49#if defined(NOGTK) && defined (_WIN32)
50#include <io.h>
51#include <sys/stat.h>
52#include <windowsx.h>
53#ifdef _WIN32_WCE
54#include <sipapi.h>
55#include <aygshell.h>
56#include "ceglue.h"
57#else
58#include <mmsystem.h> // For playing a sound under W32
59#define SipShowIM(x)
60#define CeEnableBacklight(x) FALSE
61#define CreateFileForMapping(a,b,c,d,e,f,g) CreateFile (a,b,c,d,e,f,g)
62#endif
63#include "libgosm.h"
64#include "ConvertUTF.h"
65#include "resource.h"
66
67#define OLDOLDOPTIONS \
68  o (ModelessDialog,  0, 2)
69
70HINSTANCE hInst;
71static HWND   mWnd, dlgWnd = NULL, hwndEdit, button3D, buttons[3];
72#else
73#include <unistd.h>
74#include <sys/stat.h>
75#include "libgosm.h"
76#define wchar_t char
77#define wsprintf sprintf
78#endif
79#if !defined (HEADLESS) && !defined (NOGTK)
80#ifdef USE_GNOMESOUND
81#include <libgnome/libgnome.h>
82#endif
83#include <gtk/gtk.h>
84#include <gdk/gdk.h>
85#include <gdk/gdkx.h>
86#include <gdk-pixbuf/gdk-pixbuf.h>
87#include <curl/curl.h>
88#include <curl/easy.h>
89#endif
90#include "openglespolygon.h"
91#ifdef USE_GEOCLUE // Not used and never worked
92#include <geoclue/geoclue-position.h>
93#endif
94#ifdef USE_GPSD
95#include <gps.h>
96#endif
97
98#define OPTIONS \
99  o (FollowGPSr,      0, 2) \
100  o (AddWayOrNode,    0, 2) \
101  o (StartRoute,      0, 1) \
102  o (EndRoute,        0, 1) \
103  o (OrientNorthwards,0, 2) \
104  o (LoadGPX,         0, 1) \
105  o (FastestRoute,    0, 2) \
106  o (Vehicle,         motorcarR, onewayR) \
107  o (English,         0, \
108                 sizeof (optionNameTable) / sizeof (optionNameTable[0])) \
109  o (ButtonSize,      1, 5) /* Currently only used for AddWay scrollbar */ \
110  o (IconSet,         0, 4) \
111  o (DetailLevel,     0, 5) \
112  o (ValidateMode,    0, 2) \
113  o (Exit,            0, 2) \
114  o (DisplayOff,      0, 1) \
115  o (FullScreen,      0, 2) \
116  o (ShowCompass,     0, 2) \
117  o (Background,      0, 16) \
118  o (ShowCoordinates, 0, 3) \
119  o (ShowTrace,       0, 2) \
120  o (ViewOSM,         0, 1) \
121  o (EditInPotlatch,  0, 1) \
122  o (ViewGMaps,       0, 1) \
123  o (UpdateMap,       0, 1) \
124  o (Layout,          0, 3) \
125  o (CommPort,        0, 13) \
126  o (BaudRate,        0, 6) \
127  o (ZoomInKey,       0, 3) \
128  o (ZoomOutKey,      0, 3) \
129  o (MenuKey,         0, 3) \
130  o (Keyboard,        0, 2) \
131  o (DebounceDrag,    0, 2) \
132  o (Future2,         0, 1) \
133  o (Future3,         0, 1) \
134  o (Future4,         0, 1) \
135  o (ShowActiveRouteNodes, 0, 2) \
136  o (SearchSpacing,   32, 1) \
137
138int Display3D = 0; // Not an option but a button for now.
139
140#define COMMANDS o (cmdturnleft /* Must be first */, 0, 0) \
141  o (cmdkeepleft, 0, 0) \
142  o (cmdturnright, 0, 0) o (cmdkeepright, 0, 0) o (cmdstop, 0, 0) \
143  o (cmduturn, 0, 0) o (cmdround1, 0, 0) o (cmdround2, 0, 0) \
144  o (cmdround3, 0, 0) o (cmdround4, 0, 0) o (cmdround5, 0, 0) \
145  o (cmdround6, 0, 0) o (cmdround7, 0, 0) o (cmdround8, 0, 0)
146char docPrefix[80] = "";
147int GpsIdle=999;
148
149
150// We emulate just enough of gtk to make it work
151#if defined(NOGTK)
152#define gtk_widget_queue_clear(x) // After Click() returns we Invalidate
153#define gtk_toggle_button_set_active(x,y) // followGPRr
154struct GtkWidget { 
155  struct {
156    int width, height;
157  } allocation;
158  int window;
159};
160typedef int GtkComboBox;
161struct GdkEventButton {
162  int x, y, button, time;
163};
164
165struct GdkEventScroll {
166  int x, y, direction;
167};
168
169enum { GDK_SCROLL_UP, GDK_SCROLL_DOWN };
170
171/*#define ROUTE_PEN 0
172#define VALIDATE_PEN 1
173#define RESERVED_PENS 2 */
174
175#ifndef ANDROID_NDK
176#define LOG logprintf ("%d\n", __LINE__);
177#endif
178#else
179#define LOG // Less debug info needed because we have gdb
180#ifndef RES_DIR
181#define RES_DIR "/usr/share/gosmore/" /* Needed for "make CFLAGS=-g" */
182#endif
183const char *FindResource (const char *fname)
184{ // Occasional minor memory leak : The caller never frees the memory.
185  string s;
186  struct stat dummy;
187  // first check in current working directory
188  if (stat (fname, &dummy) == 0) return strdup (fname);
189  // then check in $HOME
190  if (getenv ("HOME")) {
191    s = (string) getenv ("HOME") + "/.gosmore/" + fname;
192    if (stat (s.c_str (), &dummy) == 0) return strdup (s.c_str());
193  }
194  // then check in RES_DIR
195  s = (string) RES_DIR + fname;
196  if (stat (s.c_str (), &dummy) == 0) return strdup (s.c_str());
197  // and then fall back on current working directory if it cannot be
198  // found anywhere else (this is so new files are created in the
199  // current working directory)
200  return strdup (fname);
201}
202#endif
203#ifndef HEADLESS
204GtkWidget *draw, *location, *display3D, *followGPSr;
205#endif
206
207// used for showing logs to a file (with default)
208// changed to more suitable value for WinCE in WinMain
209char logFileName[80] = "gosmore.log.txt";
210
211FILE * logFP(bool create = true) {
212  static FILE * f = NULL;
213  if (!f && create) {
214    f = fopen(logFileName,"at");
215    fprintf(f,"----- %s %s\n", __DATE__, __TIME__);
216  }
217  return f;
218}
219
220void logprintf(const char * format, ...)
221{
222// print [hh:mm:ss] timestamp to log first
223#ifdef _WIN32_CE
224  // wince doesn't implement the time.h functions
225  SYSTEMTIME t;
226  GetLocalTime(&t);
227  fprintf(logFP(), "[%02d:%02d:%02d] ", t.wHour, t.wMinute, t.wSecond);
228#else
229  time_t seconds = time(NULL);
230  struct tm * t = localtime(&seconds);
231  fprintf(logFP(), "[%02d:%02d:%02d] ",t->tm_hour,t->tm_min,t->tm_sec);
232#endif
233
234  // then print original log string
235  va_list args;
236  va_start(args, format);
237  vfprintf(logFP(), format, args);
238  va_end (args);
239}
240
241struct klasTableStruct {
242  const TCHAR *desc;
243  const char *tags;
244} klasTable[] = {
245#define s(k,v,shortname,extraTags) \
246  { TEXT (shortname), "  <tag k='" #k "' v='" #v "' />\n" extraTags },
247STYLES
248#undef s
249};
250
251#define notImplemented \
252  o (ShowCompass,     ) \
253  o (ShowPrecision,   ) \
254  o (ShowSpeed,       ) \
255  o (ShowHeading,     ) \
256  o (ShowElevation,   ) \
257  o (ShowDate,        ) \
258  o (ShowTime,        ) \
259
260#define o(en,min,max) en ## Num,
261enum {
262  OPTIONS wayPointIconNum, COMMANDS
263  mapMode, optionMode, searchMode, chooseObjectToAdd
264};
265#undef o
266
267int listYOffset; // Number of pixel. Changed by dragging.
268
269//  TEXT (#en), TEXT (de), TEXT (es), TEXT (fr), TEXT (it), TEXT (nl) },
270const char *optionNameTable[][mapMode] = {
271#define o(en,min,max) #en,
272  { OPTIONS }, // English is same as variable names
273#undef o
274
275#include "translations.c"
276};
277
278#define o(en,min,max) int en = min;
279OPTIONS // Define a global variable for each option
280#undef o
281
282int clon, clat, zoom, option = EnglishNum, gpsSockTag, setLocBusy = FALSE, gDisplayOff;
283/* zoom is the amount that fits into the window (regardless of window size) */
284
285TCHAR currentBbox[80] = TEXT ("");
286
287int ChangePak (const TCHAR *pakfile, int mlon, int mlat)
288{ // Returns TRUE if pakfile or a bbox was loaded, false if default was loaded or failure
289  static int bboxList[][4] = { 
290#include "bboxes.c"
291  }, world[] = { -512, -512, 512, 512 }, *bbox = NULL;
292
293  if (bbox && bbox[0] <= (mlon >> 22) && bbox[1] <= ((-mlat) >> 22) &&
294              bbox[2] >  (mlon >> 22) && bbox[3] >  ((-mlat) >> 22)) return 0;
295  LG GosmFreeRoute ();
296  memset (gosmSstr, 0, sizeof (gosmSstr));
297  shortest = NULL;
298       
299  LG if (!pakfile) {
300    int best = 0;
301    for (size_t j = 0; j < sizeof (bboxList) / sizeof (bboxList[0]); j++) {
302      int worst = min (mlon / 8 - (bboxList[j][0] << 19),
303                  min (-mlat / 8 - (bboxList[j][1] << 19),
304                  min ((bboxList[j][2] << 19) - mlon / 8,
305                       (bboxList[j][3] << 19) + mlat / 8)));
306      // Find the worst border of bbox j. worst < 0 implies we are
307      // outside it.
308      if (worst > best) { 
309        best = worst;
310        pakfile = currentBbox;
311        bbox = bboxList[j];
312        #ifdef _WIN32_WCE
313        GetModuleFileName (NULL, currentBbox, sizeof (currentBbox) / sizeof (currentBbox[0]));
314        wsprintf (wcsrchr (currentBbox, L'\\'), TEXT ("\\%04d%04d%04d%04d.pak"),
315        #else
316        sprintf (currentBbox, "%04d%04d%04d%04d.pak",
317        #endif
318          bboxList[j][0] + 512, bboxList[j][1] + 512,
319          bboxList[j][2] + 512, bboxList[j][3] + 512);
320      }
321    }
322  }
323  else bbox = world;
324
325  #ifdef WIN32
326  static HANDLE gmap = INVALID_HANDLE_VALUE, fm = INVALID_HANDLE_VALUE;
327  static void *map = NULL;
328  LOG if (map) UnmapViewOfFile (map);
329  LOG if (fm != INVALID_HANDLE_VALUE) CloseHandle (fm);
330  LOG if (gmap != INVALID_HANDLE_VALUE) CloseHandle (gmap);
331 
332  LOG gmap = CreateFileForMapping (pakfile, GENERIC_READ, FILE_SHARE_READ,
333    NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL /*FILE_FLAG_NO_BUFFERING*/,
334    NULL);
335  LOG if (gmap == INVALID_HANDLE_VALUE && currentBbox == pakfile) {
336    #ifdef _WIN32_WCE
337    wsprintf (wcsrchr (currentBbox, L'\\'), TEXT ("\\default.pak"));
338    LOG gmap = CreateFileForMapping (pakfile, GENERIC_READ, FILE_SHARE_READ,
339      NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL /*FILE_FLAG_NO_BUFFERING*/,
340      NULL);   
341    #else
342    LOG gmap = CreateFileForMapping ("default.pak", GENERIC_READ,
343      FILE_SHARE_READ,
344      NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL /*FILE_FLAG_NO_BUFFERING*/,
345      NULL);   
346    #endif
347  }
348  LOG fm = gmap == INVALID_HANDLE_VALUE ? INVALID_HANDLE_VALUE :
349    CreateFileMapping(gmap, NULL, PAGE_READONLY, 0, 0, 0);
350  LOG map = fm == INVALID_HANDLE_VALUE ? NULL :
351    MapViewOfFile (fm, FILE_MAP_READ, 0, 0, 0);
352  int len = map ? GetFileSize (gmap, NULL) : 0;
353  LOG Exit = !map || !GosmInit (map, GetFileSize(gmap, NULL));
354  LOG if (Exit && gmap != INVALID_HANDLE_VALUE) {
355    MessageBox (NULL, TEXT ("mmap problem. Pak file too big ?"),
356      TEXT (""), MB_APPLMODAL|MB_OK);
357  }
358
359  #ifdef _WIN32
360  FILE *gmap = _wfopen (/*"./gosmore.pak"*/ , TEXT ("rb"));
361
362  if (!gmap) {
363    MessageBox (NULL, TEXT ("No pak file"), TEXT (""), MB_APPLMODAL|MB_OK);
364    return 1;
365  }
366  fseek (gmap, 0, SEEK_END);
367  pakSize = ftell (gmap);
368  fseek (gmap, 0, SEEK_SET);
369  data = (char *) malloc (pakSize);
370  if (!data) {
371    MessageBox (NULL, TEXT ("Out of memory"), TEXT (""), MB_APPLMODAL|MB_OK);
372    return 1; // This may mean memory is available, but fragmented.
373  } // Splitting the 5 parts may help.
374  fread (data, pakSize, 1, gmap);
375  #endif
376  #else // defined (__linux__)
377  static void *map = (void*) -1;
378  static size_t len = 0 /* Shut up gcc */;
379//  printf ("%s %d %d\n", pakfile, (mlon >> 22) + 512, 512 - (mlat >> 22));
380  if (map != (void*) -1) munmap (map, len);
381 
382  FILE *pakmap = fopen64 (pakfile, "r");
383  FILE *gmap = !pakmap && currentBbox == pakfile
384                  ? fopen64 ("default.pak", "r") : pakmap;
385  if (!gmap && currentBbox == pakfile) {
386    gmap = fopen64 (RES_DIR "default.pak", "r");
387  }
388  LG len = gmap && fseek (gmap, 0, SEEK_END) == 0 ? ftell (gmap) : 0;
389  map = !len ? (void*)-1
390     : mmap (NULL, ftell (gmap), PROT_READ, MAP_SHARED, fileno (gmap), 0);
391  LG Exit = map == (void *) -1 || !GosmInit (map, len);
392  LG if (gmap) fclose (gmap);
393  #endif
394  /* // Slightly more portable:
395  GMappedFile *gmap = g_mapped_file_new (pakfile, FALSE, NULL);
396  Exit = !gmap || !GosmInit (g_mapped_file_get_contents (gmap),
397      g_mapped_file_get_length (gmap));
398  */
399//  __android_log_print(ANDROID_LOG_WARN, "Gosmore", "re %p %d", bbox, Exit);
400  LOG if (Exit) bbox = NULL;
401  LG return pakmap != NULL;
402}
403#ifdef ANDROID_NDK
404extern "C" jint Java_org_osmu_gosmore_MapActivity_changePak (JNIEnv*  env,
405    jobject thiz, jstring js, jint mlon, jint mlat)
406{
407  LG IconSet = 1;
408  DetailLevel = 3;
409  ButtonSize = 4;
410  Background = 1;
411  option= mapMode;
412  ShowCompass = 0;
413  const char *sdcard = env->GetStringUTFChars(js, NULL);
414  chdir (sdcard);
415  LG env->ReleaseStringUTFChars(js, sdcard);
416  if (ChangePak ("gosmore.pak", 0, 0)) return 2;
417  if (Exit && ChangePak (NULL, mlon, mlat)) return 2;
418  if (Exit) strcpy (currentBbox, "default");
419//  __android_log_print(ANDROID_LOG_WARN, "Gosmore", "re %s", currentBbox);
420  return Exit ? 0 : 1;
421}
422
423extern "C" jstring Java_org_osmu_gosmore_Update_currentBbox
424                       (JNIEnv* env, jobject thiz)
425{
426//  __android_log_print(ANDROID_LOG_WARN, "Gosmore", "re %s", currentBbox);
427  currentBbox[16] = '\0'; // Strip the .pak off for Java
428  return env->NewStringUTF (currentBbox);
429}
430
431#endif
432
433#ifndef HEADLESS
434
435double cosAzimuth = 1.0, sinAzimuth = 0.0;
436string highlight, searchStr ("Search");
437
438inline void SetLocation (int nlon, int nlat)
439{
440  clon = nlon;
441  clat = nlat;
442  #ifndef NOGTK
443  char lstr[50];
444  int zl = 0;
445  while (zl < 32 && (zoom >> zl)) zl++;
446  setlocale (LC_NUMERIC, "C");
447  sprintf (lstr, "?lat=%.5lf&lon=%.5lf&zoom=%d", LatInverse (nlat),
448    LonInverse (nlon), 33 - zl);
449  setLocBusy = TRUE;
450  gtk_entry_set_text (GTK_ENTRY (location), lstr);
451  setLocBusy = FALSE;
452  #endif
453}
454
455#ifndef NOGTK
456int ChangeLocation (void)
457{
458  if (setLocBusy) return FALSE;
459  char *lstr = (char *) gtk_entry_get_text (GTK_ENTRY (location));
460  double lat, lon;
461  while (*lstr != '?' && *lstr != '\0') lstr++;
462  setlocale (LC_NUMERIC, "C");
463  if (sscanf (lstr, "?lat=%lf&lon=%lf&zoom=%d", &lat, &lon, &zoom) == 3) {
464    clat = Latitude (lat);
465    clon = Longitude (lon);
466    zoom = 0xffffffff >> (zoom - 1);
467    gtk_widget_queue_clear (draw);
468  }
469  return FALSE;
470}
471
472int ChangeOption (void)
473{
474  Display3D = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (display3D));
475  FollowGPSr = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (followGPSr));
476  gtk_widget_queue_clear (draw);
477  return FALSE;
478}
479#endif
480/*-------------------------------- NMEA processing ------------------------*/
481/* My TGPS 374 frequently produces corrupt NMEA output (probably when the
482   CPU goes into sleep mode) and this may also be true for GPS receivers on
483   long serial lines. To overcome this, we ignore badly formed sentences and
484   we interpolate the date where the jumps in time values seems plausible.
485   
486   For the GPX output there is an extra layer of filtering : The minimum
487   number of observations are dropped so that the remaining observations does
488   not imply any impossible manuavers like traveling back in time or
489   reaching supersonic speed. This effeciently implemented transforming the
490   problem into one of finding the shortest path through a graph :
491   The nodes {a_i} are the list of n observations. Add the starting and
492   ending nodes a_0 and a_n+1, which are both connected to all the other
493   nodes. 2 observation nodes are connected if it's possible that the one
494   will follow the other. If two nodes {a_i} and {a_j} are connected, the
495   cost (weight) of the connection is j - 1 - i, i.e. the number of nodes
496   that needs to be dropped. */
497struct gpsNewStruct {
498  struct {
499    double latitude, longitude, track, speed, hdop, ele;
500    char date[6], tm[6];
501  } fix;
502  int lat, lon; // Mercator
503  unsigned dropped;
504  struct gpsNewStruct *dptr;
505} gpsTrack[18000], *gpsNew = gpsTrack;
506
507// Return the basefilename for saving .osm and .gpx files
508void getBaseFilename(char* basename, gpsNewStruct* first) {
509  char VehicleName[80];
510  #define M(v) Vehicle == v ## R ? #v :
511  sprintf(VehicleName, "%s", RESTRICTIONS NULL);
512  #undef M
513
514  if (first) {
515    sprintf (basename, "%s%.2s%.2s%.2s-%.6s-%s", docPrefix,
516             first->fix.date + 4, first->fix.date + 2, first->fix.date,
517             first->fix.tm, VehicleName);
518  } else {
519    // get time from computer if no gps traces
520#ifdef _WIN32_WCE
521    SYSTEMTIME t;
522    GetSystemTime(&t);
523    sprintf (basename, "%s%02d%02d%02d-%02d%02d%02d-%s", docPrefix,
524             t.wYear % 100, t.wMonth, t.wDay,
525             t.wHour, t.wMinute, t.wSecond, VehicleName);
526#endif
527  }
528}
529
530gpsNewStruct *FlushGpx (void)
531{
532  struct gpsNewStruct *a, *best, *first = NULL;
533  for (best = gpsNew; gpsTrack <= best && best->dropped == 0; best--) {}
534  gpsNew = gpsTrack;
535  if (best <= gpsTrack) return NULL; // No observations
536  for (a = best - 1; gpsTrack <= a && best < a + best->dropped; a--) {
537    if (best->dropped > best - a + a->dropped) best = a;
538  }
539  // We want .., best->dptr->dptr->dptr, best->dptr->dptr, best->dptr, best
540  // Now we reverse the linked list :
541  while (best) {
542    a = best->dptr;
543    best->dptr = first;
544    first = best;
545    best = a;
546  }
547 
548  char bname[80], fname[80];
549  getBaseFilename(bname, first);
550  sprintf (fname, "%s.gpx", bname);
551  FILE *gpx = fopen (fname, "wb");
552  if (!gpx) return first;
553  fprintf (gpx, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\
554<gpx\n\
555 version=\"1.0\"\n\
556creator=\"gosmore\"\n\
557xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\
558xmlns=\"http://www.topografix.com/GPX/1/0\"\n\
559xsi:schemaLocation=\"http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd\n\">\n\
560<trk>\n\
561<trkseg>\n");
562  for (best = first; best; best = best->dptr) { // Iterate the linked list
563    fprintf (gpx, "<trkpt lat=\"%.9lf\" lon=\"%.9lf\">\n",
564      best->fix.latitude, best->fix.longitude);
565    if (best->fix.ele < 1e+8) {
566      fprintf (gpx, "<ele>%.3lf</ele>\n", best->fix.ele);
567    }
568    fprintf (gpx, "<time>20%.2s-%.2s-%.2sT%.2s:%.2s:%.2sZ</time>\n</trkpt>\n",
569      best->fix.date + 4, best->fix.date + 2, best->fix.date,
570      best->fix.tm, best->fix.tm + 2, best->fix.tm + 4);
571   
572//    if (best->next && ) fprintf (gpx, "</trkseg>\n</trk>\n<trk>\n<trkseg>\n");
573  }
574  fprintf (gpx, "</trkseg>\n\
575</trk>\n\
576</gpx>\n");
577  return first;
578}
579
580int ProcessNmea (char *rx, unsigned *got)
581{
582  unsigned dataReady = FALSE, i;
583  for (i = 0; i < *got && rx[i] != '$'; i++, (*got)--) {}
584  while (rx[i] == '$') {
585    //for (j = 0; j < got; i++, j++) rx[j] = rx[i];
586    int fLen[19], fStart[19], fNr;
587    memmove (rx, rx + i, *got);
588    for (i = 1, fNr = 0; i < *got && rx[i] != '$' && fNr < 19; fNr++) {
589      fStart[fNr] = i;
590      while (i < *got && (rx[i] == '.' || isdigit (rx[i]))) i++;
591      fLen[fNr] = i - fStart[fNr];
592      while (i < *got && rx[i] != '$' && rx[i++] != ',') {}
593    }
594    while (i < *got && rx[i] != '$') i++;
595    int col = memcmp (rx, "$GPGLL", 6) == 0 ? 1 :
596      memcmp (rx, "$GPGGA", 6) == 0 ? 2 :
597      memcmp (rx, "$GPRMC", 6) == 0 ? 3 : 0;
598    // latitude is at fStart[col], longitude is at fStart[col + 2].
599    if (fNr >= (col == 0 ? 2 : col == 1 ? 6 : col == 2 ? 13 : 10)) {
600      if (col > 0 && fLen[col] > 6 && memchr ("SsNn", rx[fStart[col + 1]], 4)
601        && fLen[col + 2] > 7 && memchr ("EeWw", rx[fStart[col + 3]], 4) &&
602        fLen[col == 1 ? 5 : 1] >= 6 &&
603          memcmp (gpsNew->fix.tm, rx + fStart[col == 1 ? 5 : 1], 6) != 0) {
604        double nLat = (rx[fStart[col]] - '0') * 10 + rx[fStart[col] + 1]
605          - '0' + atof (rx + fStart[col] + 2) / 60;
606        double nLon = ((rx[fStart[col + 2]] - '0') * 10 +
607          rx[fStart[col + 2] + 1] - '0') * 10 + rx[fStart[col + 2] + 2]
608          - '0' + atof (rx + fStart[col + 2] + 3) / 60;
609        if (nLat > 90) nLat = nLat - 180;
610        if (nLon > 180) nLon = nLon - 360; // Mungewell's Sirf Star 3
611        if (tolower (rx[fStart[col + 1]]) == 's') nLat = -nLat;
612        if (tolower (rx[fStart[col + 3]]) == 'w') nLon = -nLon;
613        if (fabs (nLat) < 90 && fabs (nLon) < 180 &&
614            (nLat != 0 || nLon != 0)) { // JNC when starting up
615          if (gpsTrack + sizeof (gpsTrack) / sizeof (gpsTrack[0]) <=
616            ++gpsNew) FlushGpx ();
617          memcpy (gpsNew->fix.tm, rx + fStart[col == 1 ? 5 : 1], 6);
618          gpsNew->dropped = 0;
619          dataReady = TRUE; // Notify only when parsing is complete
620          gpsNew->fix.latitude = nLat;
621          gpsNew->fix.longitude = nLon;
622          gpsNew->lat = Latitude (nLat);
623          gpsNew->lon = Longitude (nLon);
624          gpsNew->fix.ele = 1e+9;
625        }
626      } // If the timestamp wasn't seen before
627      if (col == 2) {
628        gpsNew->fix.hdop = atof (rx + fStart[8]);
629        gpsNew->fix.ele = atof (rx + fStart[9]); // Check height of geoid ??
630      }
631      if (col == 3 && fLen[7] > 0 && fLen[8] > 0 && fLen[9] >= 6) {
632        memcpy (gpsNew->fix.date, rx + fStart[9], 6);
633        gpsNew->fix.speed = atof (rx + fStart[7]);
634        gpsNew->fix.track = atof (rx + fStart[8]);
635       
636        //-------------------------------------------------------------
637        // Now fix the dates and do a little bit of shortest path work
638        int i, j, k, l; // TODO : Change indexes into pointers
639        for (i = 0; gpsTrack < gpsNew + i && !gpsNew[i - 1].dropped &&
640             (((((gpsNew[i].fix.tm[0] - gpsNew[i - 1].fix.tm[0]) * 10 +
641                  gpsNew[i].fix.tm[1] - gpsNew[i - 1].fix.tm[1]) * 6 +
642                  gpsNew[i].fix.tm[2] - gpsNew[i - 1].fix.tm[2]) * 10 +
643                  gpsNew[i].fix.tm[3] - gpsNew[i - 1].fix.tm[3]) * 6 +
644                  gpsNew[i].fix.tm[4] - gpsNew[i - 1].fix.tm[4]) * 10 +
645                  gpsNew[i].fix.tm[5] - gpsNew[i - 1].fix.tm[5] < 30; i--) {}
646        // Search backwards for a discontinuity in tm
647       
648        for (j = i; gpsTrack < gpsNew + j && !gpsNew[j - 1].dropped; j--) {}
649        // Search backwards for the first observation missing its date
650       
651        for (k = j; k <= 0; k++) {
652          memcpy (gpsNew[k].fix.date,
653            gpsNew[gpsTrack < gpsNew + j && k < i ? j - 1 : 0].fix.date, 6);
654          gpsNew[k].dptr = NULL; // Try gpsNew[k] as first observation
655          gpsNew[k].dropped = gpsNew + k - gpsTrack + 1;
656          for (l = k - 1; gpsTrack < gpsNew + l && k - l < 300 &&
657               gpsNew[k].dropped > unsigned (k - l - 1); l--) {
658            // At the point where we consider 300 bad observations, we are
659            // more likely to be wasting CPU cycles.
660            int tdiff =
661              ((((((((gpsNew[k].fix.date[4] - gpsNew[l].fix.date[4]) * 10 +
662              gpsNew[k].fix.date[5] - gpsNew[l].fix.date[5]) * 12 +
663              (gpsNew[k].fix.date[2] - gpsNew[l].fix.date[2]) * 10 +
664              gpsNew[k].fix.date[3] - gpsNew[l].fix.date[3]) * 31 +
665              (gpsNew[k].fix.date[0] - gpsNew[l].fix.date[0]) * 10 +
666              gpsNew[k].fix.date[1] - gpsNew[l].fix.date[1]) * 24 +
667              (gpsNew[k].fix.tm[0] - gpsNew[l].fix.tm[0]) * 10 +
668              gpsNew[k].fix.tm[1] - gpsNew[l].fix.tm[1]) * 6 +
669              gpsNew[k].fix.tm[2] - gpsNew[l].fix.tm[2]) * 10 +
670              gpsNew[k].fix.tm[3] - gpsNew[l].fix.tm[3]) * 6 +
671              gpsNew[k].fix.tm[4] - gpsNew[l].fix.tm[4]) * 10 +
672              gpsNew[k].fix.tm[5] - gpsNew[l].fix.tm[5];
673             
674            /* Calculate as if every month has 31 days. It causes us to
675               underestimate the speed travelled in very rare circumstances,
676               (e.g. midnight GMT on Feb 28) allowing a few bad observation
677               to sneek in. */
678            if (0 < tdiff && tdiff < 3600 * 24 * 62 /* Assume GPS used more */
679                /* frequently than once every 2 months */ &&
680                fabs (gpsNew[k].fix.latitude - gpsNew[l].fix.latitude) +
681                fabs (gpsNew[k].fix.longitude - gpsNew[l].fix.longitude) *
682                  cos (gpsNew[k].fix.latitude * (M_PI / 180.0)) <
683                    tdiff * (1600 / 3600.0 * 360 / 40000) && // max 1600 km/h
684                gpsNew[k].dropped > gpsNew[l].dropped + k - l - 1) {
685              gpsNew[k].dropped = gpsNew[l].dropped + k - l - 1;
686              gpsNew[k].dptr = gpsNew + l;
687            }
688          } // For each possible connection
689        } // For each new observation
690      } // If it's a properly formatted RMC
691    } // If the sentence had enough columns for our purposes.
692    else if (i == *got) break; // Retry when we receive more data
693    *got -= i;
694  } /* If we know the sentence type */
695  return dataReady;
696}
697
698int command[3] = { 0, 0, 0 }, oldCommand = 0;
699
700void CallRoute (int recalculate, int plon, int plat)
701{
702  Route (recalculate, plon, plat, Vehicle, FastestRoute);
703  #ifdef _WIN32_WCE
704  MSG msg;
705  while (!PeekMessage (&msg, NULL, WM_KEYFIRST, WM_KEYLAST, PM_NOREMOVE) &&
706         !PeekMessage (&msg, NULL, WM_MOUSEFIRST, WM_MOUSELAST, PM_NOREMOVE) &&
707         RouteLoop ()) {}
708  #else
709  while (RouteLoop ()) {}
710  #endif
711}
712
713
714#ifndef ANDROID_NDK
715void DoFollowThing (gpsNewStruct *gps)
716{
717  static int lastTime = -1;
718  char *d = gps->fix.date, *t = gps->fix.tm;
719  int now = (((((t[0] - '0') * 10 + t[1] - '0') * 6 + t[2] - '0') * 10 +
720                 t[3] - '0') * 6 + t[4] - '0') * 10 + t[5];
721  if (lastTime - now > 60) {
722    lastTime = now;
723   
724    int cdays[12] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
725    double M = (cdays[(d[2] - '0') * 10 + d[3] - '0' - 1] +
726                   (d[4] - '0') * 10 + d[5] - '0' - 1) * M_PI * 2 / 365.242;
727    int dayLen = lrint (acos (-
728      tan (-cos (M + 10 * M_PI * 2 / 365) * 23.44 / 180 * M_PI) *
729      tan (gps->fix.latitude / 180 * M_PI)) * 2 / M_PI * 12 * 60 * 60);
730    /* See wikipedia/Declination for the "core" of the formula */
731    // TODO: acos() may fail in arctic / antarctic circle.
732    int noon = 12 * 60 * 60 - lrint (gps->fix.longitude / 360 * 24 * 60 * 60
733                - (-7.655 * sin (M) + 9.873 * sin (2 * M + 3.588)) * 60);
734    /* See wikipedia/Equation_of_time */
735    int sSinceSunrise = (now - noon + dayLen / 2 + 24 * 3600) % (24 * 3600);
736    Background = (Background & 7) + (sSinceSunrise < dayLen ? 8 : 0);
737    #if 0
738    noon += 7200; // Central African Time = UTC + 2 hours
739    //printf ("%.5lf %.5lf %s %.3lf\n", lat, lon, date, M / M_PI / 2);
740    printf ("Declination %.5lf\n", -cos (M + 10 * M_PI * 2 / 365) * 23.44);
741    printf ("Noon %02d%02d%02d\n", noon / 60 / 60,
742      noon / 60 % 60, noon % 60);
743    printf ("Sunrise %02d%02d%02d\n", (noon - dayLen / 2) / 60 / 60,
744      (noon - dayLen / 2) / 60 % 60, (noon - dayLen / 2) % 60);
745    printf ("Sunset %02d%02d%02d\n", (noon + dayLen / 2) / 60 / 60,
746      (noon + dayLen / 2) / 60 % 60, (noon + dayLen / 2) % 60);
747    printf ("%6d / %6d at %.6s\n", sSinceSunrise, dayLen, gps->fix.tm);
748    #endif
749  }
750  if (!/*gps->fix.mode >= MODE_2D &&*/ FollowGPSr) return;
751  SetLocation (Longitude (gps->fix.longitude), Latitude (gps->fix.latitude));
752  __int64 dlon = clon - flon, dlat = clat - flat;
753  flon = clon;
754  flat = clat;
755#else
756
757extern "C" void Java_org_osmu_gosmore_MapRenderer_startRoute (
758  JNIEnv*  env, jobject thiz, jdouble lon, jdouble lat)
759{
760  flon = Longitude (lon);
761  flat = Latitude (lat);
762  GosmFreeRoute ();
763  shortest = NULL;
764}
765
766extern "C" void Java_org_osmu_gosmore_MapRenderer_endRoute (
767  JNIEnv*  env, jobject thiz, jdouble lon, jdouble lat, jboolean fastest,
768  jint vehicle)
769{
770  tlon = Longitude (lon);
771  tlat = Latitude (lat);
772  Vehicle = vehicle;
773  FastestRoute = fastest;
774  Route (TRUE, 0, 0, Vehicle, FastestRoute);
775}
776
777extern "C" jint Java_org_osmu_gosmore_MapRenderer_doRoute (
778  JNIEnv*  env, jobject thiz)
779{
780  if (!RouteLoop()) return routeSuccess ? 999 : -1;
781  // I suspect shortest is never NULL here, but I still guard against it.
782  return (jint)(!shortest ? 0 : ((Sqr (__int64 (shortest->nd->lon - tlon)) +
783    Sqr (__int64 (shortest->nd->lat - tlat))) * 100) /
784    (Sqr (__int64 (flon - tlon)) +
785     Sqr (__int64 (flat - tlat)) + (1<<15)));
786}
787
788extern "C" jstring Java_org_osmu_gosmore_MapRenderer_navigate (
789  JNIEnv*  env, jobject thiz, jdouble lon, jdouble lat, jfloat speed,
790  jfloat bearing)
791{
792  const char *tts = "";
793  flon = Longitude (lon);
794  flat = Latitude (lat);
795  __int64 dlon = Longitude (lon + speed * 3600.0 /
796      40000000.0 / cos (lat * (M_PI / 180.0)) *
797      sin (bearing * (M_PI / 180.0))) - flon;
798  __int64 dlat = Latitude (lat + speed * 3600.0 /
799      40000000.0 * cos (bearing * (M_PI / 180.0))) - flat;
800  // Slightly faster would be dlon=speed*(...); dlat=speed*(...);
801#endif
802    // Predict the vector that will be traveled in the next 10seconds
803//    printf ("%5.1lf m/s Heading %3.0lf\n", gps->fix.speed, gps->fix.track);
804//    printf ("%lf %lf\n", gps->fix.latitude, gps->fix.longitude);
805   
806  if (routeSuccess) CallRoute (FALSE, dlon, dlat);
807
808  static ndType *decide[3] = { NULL, NULL, NULL }, *oldDecide = NULL;
809  decide[0] = NULL;
810  command[0] = 0;
811  if (shortest) {
812    routeNodeType *x = shortest->shortest;
813    if (!x) command[0] = cmdstopNum;
814    if (x && Sqr (dlon) + Sqr (dlon) > 10000 /* faster than ~3 km/h */ &&
815        dlon * (x->nd->lon - clon) + dlat * (x->nd->lat - clat) < 0) {
816      command[0] = cmduturnNum;
817      decide[0] = 0;
818    }
819    else if (x) {
820      int icmd = -1, nextJunction = TRUE; // True means the user need to take action at
821      // the first junction he comes across. Otherwise we're looking ahead.
822      double dist = sqrt (Sqr ((double) (x->nd->lat - flat)) +
823                          Sqr ((double) (x->nd->lon - flon)));
824      for (x = shortest; icmd < 0 && x->shortest &&
825           dist < 40000 /* roughly 300m */; x = x->shortest) {
826        int roundExit = 0;
827        while (icmd < 0 && x->shortest && ((1 << roundaboutR) &
828                           (Way (x->shortest->nd))->bits)) {
829          if (isupper (JunctionType (x->shortest->nd))) roundExit++;
830          x = x->shortest;
831        }
832        if (!x->shortest || roundExit) {
833          decide[0] = x->nd;
834          icmd = cmdround1Num - 1 + roundExit;
835          break;
836        }
837       
838        ndType *n0 = x->nd, *n1 = x->shortest->nd, *nd = n1;
839        //ndType *n2 = x->shortest->shortest ? x->shortest->shortest->nd : n1;
840        int n2lat =
841          x->shortest->shortest ? x->shortest->shortest->nd->lat : tlat;
842        int n2lon =
843          x->shortest->shortest ? x->shortest->shortest->nd->lon : tlon;
844        while (nd[-1].lon == nd->lon && nd[-1].lat == nd->lat) nd--;
845        int segCnt = 0; // Count number of segments at x->shortest
846        int n2Left = /* Keep compiler quiet*/ 0, fLeft = INT_MIN;
847        do {
848          // TODO : Only count segment traversable by 'Vehicle'
849          // Except for the case where a cyclist crosses a motorway_link.
850         
851          for (int o = 0; o <= 1; o++) {
852            segCnt++;
853            if (!nd->other[o]) continue;
854            ndType *forkO = nd + nd->other[o];
855            __int64 straight =
856              (forkO->lat - n1->lat) * (__int64) (n1->lat - n0->lat) +
857              (forkO->lon - n1->lon) * (__int64) (n1->lon - n0->lon), left =
858              (forkO->lat - n1->lat) * (__int64) (n1->lon - n0->lon) -
859              (forkO->lon - n1->lon) * (__int64) (n1->lat - n0->lat);
860            int isNd2 = forkO->lat == n2lat && forkO->lon == n2lon;
861            if (straight > left && straight > -left &&
862                (!o || !Way (nd)->bits & onewayR)) {
863              // If we are approaching a split, we can ignore oncoming
864              // oneways (the user can avoid them on his own).
865              //printf ("%d %d %d %lf\n", isNd2, o, Way (nd)->bits & onewayR, dist);
866              (isNd2 ? n2Left : fLeft) = left * 16 / straight;
867            }
868            if (isNd2) icmd = straight < left
869              ? nextJunction ? cmdturnleftNum : cmdkeepleftNum :
870              straight > -left ? -1
871              : nextJunction ? cmdturnrightNum : cmdkeeprightNum;
872          }
873        } while (++nd < ndBase + hashTable[bucketsMin1 + 1] &&
874                 nd->lon == nd[-1].lon && nd->lat == nd[-1].lat);
875        if (segCnt > 2) {
876          decide[0] = n1;
877          nextJunction = FALSE;
878        }
879        else icmd = -1;
880        if (icmd < 0 && fLeft != INT_MIN) { // If it's a split
881          icmd = fLeft < n2Left ? cmdkeepleftNum : cmdkeeprightNum;
882          //printf ("%d\n", segCnt);
883        }
884       
885        dist += sqrt (Sqr ((double) (n2lat - n1->lat)) +
886                      Sqr ((double) (n2lon - n1->lon)));
887      } // While looking ahead to the next turn.
888      if (icmd >= 0) command[0] = icmd;
889      if (!x->shortest && dist < 6000) {
890        command[0] = cmdstopNum;
891        decide[0] = NULL;
892      }
893    } // If not on final segment
894  } // If the routing was successful
895
896  if (command[0] && (oldCommand != command[0] || oldDecide != decide[0]) &&
897      command[0] == command[1] && command[1] == command[2] &&
898      decide[0] == decide[1] && decide[1] == decide[2]) {
899    oldCommand = command[0];
900    oldDecide = decide[0];
901    #define o(cmd,dummy1,dummy2) TEXT (#cmd),
902#ifdef ANDROID_NDK
903    static const char *cmds[] = {
904    "Turn left.", "Keep left.", "Turn right", "Keep right.",
905    "You have reached your destination.", "Make a U turn if possible.",
906    "At the roundabout, take the first exit.",
907    "At the roundabout, take the second exit.",
908    "At the roundabout, take the third exit.",
909    "At the roundabout, take the fourth exit.",
910    "At the roundabout, take the fifth exit.",
911    "At the roundabout, take the sixth exit.",
912    "At the roundabout, take the seventh exit.",
913    "At the roundabout, take the eighth exit." };
914    tts = cmds[command[0]-cmdturnleftNum];
915#elif defined(_WIN32_WCE)
916    static const wchar_t *cmdStr[] = { COMMANDS };
917    wchar_t argv0[80];
918    GetModuleFileName (NULL, argv0, sizeof (argv0) / sizeof (argv0[0]));
919    wsprintf (wcsrchr (argv0, L'\\'), TEXT ("\\%s.wav"),
920      cmdStr[command[0] - cmdturnleftNum] + 3);
921    // waveOutSetVolume (/*pcm*/0, 0xFFFF); // Puts the sound at maximum volume
922    PlaySound (argv0, NULL, SND_FILENAME | SND_NODEFAULT | SND_ASYNC );
923#else
924    static const char *cmdStr[] = { COMMANDS };
925    string wav = string (RES_DIR) +  // +3 is to strip the leading "cmd"
926      (cmdStr[command[0] - cmdturnleftNum] + 3) + ".wav";
927#ifdef _WIN32
928    string wwav = string (cmdStr[command[0] - cmdturnleftNum] + 3) + ".wav";
929    PlaySound (wwav.c_str(), NULL, SND_FILENAME | SND_NODEFAULT | SND_ASYNC );
930#elif  defined (USE_GNOMESOUND)
931    gnome_sound_play (wav.c_str ());
932#else
933    if (fork () == 0) execlp ("aplay", "aplay", wav.c_str (), NULL);
934#endif
935//    printf ("%s\n", wav.c_str()); //cmdStr[command[0] - cmdturnleftNum]);
936#endif
937    #undef o
938  }
939  memmove (command + 1, command, sizeof (command) - sizeof (command[0]));
940  memmove (decide + 1, decide, sizeof (decide) - sizeof (decide[0]));
941
942  double dist = sqrt (Sqr ((double) dlon) + Sqr ((double) dlat));
943  if (!OrientNorthwards && dist > 100.0) {
944    cosAzimuth = dlat / dist;
945    sinAzimuth = -dlon / dist;
946  }                                           
947  gtk_widget_queue_clear (draw);
948  #ifdef ANDROID_NDK
949  return env->NewStringUTF( tts);
950  #endif
951} // If following the GPSr and it has a fix.
952
953// Regrettably here are a few static variables with very generic names. They
954// are however unique in this file.
955static int cosa, sina, xadj, width = 320, height = 240;
956__int64 yadj;
957#define FAR3D  100000 // 3D view has a limit of roughly 5 km forwards
958#define WIDE3D 100000 // and roughly 5km between top left & top right corner
959#define CAMERA2C 20000 // How far the camera is behind the user (clat/lon)
960#define HEIGHT   12000 // Height of the camera
961#define PIX45     256 // Y value corresponding to 45 degrees down
962#define XFix PIX45
963
964#define myint int
965/* The 3D computations can all be done in signed 32 bits integers,
966   provided overflow bits are simply discarded. The C specification says
967   however that ints that overflow are undefined (as well as any
968   expression that touches them). So if the 3D display looks garbled
969   under a new compiler, try running with #define myint __int64
970*/
971
972#define MUL 64
973#define Depth(lon,lat) \
974           (int)(yadj + (lat) * (myint) cosa - (lon) * (myint) sina)
975#define X1(lon,lat) \
976           (int)(xadj + (lon) * (myint) cosa + (lat) * (myint) sina)
977#define AdjDepth(lon,lat) (Depth (lon, lat) < PIX45 * HEIGHT * MUL / 5000 \
978                      && Depth (lon, lat) > -PIX45 * HEIGHT * MUL / 5000 ? \
979                         PIX45 * HEIGHT * MUL / 5000 : Depth (lon, lat))
980#define Y(lon,lat) (Display3D ? PIX45 * HEIGHT * MUL / AdjDepth (lon, lat) \
981     : yadj + (int)(((lon) * (__int64) sina - (lat) * (__int64) cosa) >> 32))
982#define Y2(lon,lat) (Display3D ? PIX45 * HEIGHT * MUL * (__int64)65536 / AdjDepth (lon, lat) \
983 : (yadj<<16) + (int)(((lon) * (__int64) sina - (lat) * (__int64) cosa) >> 16))
984#define X(lon,lat) (Display3D ? width / 2 + \
985((AdjDepth (lon, lat) > 0 ? 1 : -1) * \
986  (X1 (lon, lat) / 32000 - AdjDepth (lon, lat) / XFix) > 0 ? 32000 : \
987(AdjDepth (lon, lat) > 0 ? 1 : -1) * \
988  (X1 (lon, lat) / 32000 + AdjDepth (lon, lat) / XFix) < 0 ? -32000 : \
989X1(lon,lat) / (AdjDepth (lon, lat) / XFix)) \
990: xadj + (int)(((lon) * (__int64) cosa + (lat) * (__int64) sina) >> 32))
991#define X2(lon,lat) (Display3D ? X(lon,lat)*65536 : \
992   (xadj<<16) + (int)(((lon) * (__int64) cosa + (lat) * (__int64) sina) >> 16))
993//X2 and Y2 are X and Y but 65536 times larger
994
995#ifndef NOGTK
996#ifdef ROUTE_TEST
997gint RouteTest (GtkWidget * /*widget*/, GdkEventButton *event, void *)
998{
999  static int ptime = 0;
1000  ptime = time (NULL);
1001  int w = width;
1002  int perpixel = zoom / w;
1003  clon += lrint ((event->x - w / 2) * perpixel);
1004  clat -= lrint ((event->y - height / 2) * perpixel);
1005/*    int plon = clon + lrint ((event->x - w / 2) * perpixel);
1006    int plat = clat -
1007      lrint ((event->y - height / 2) * perpixel); */
1008  FollowGPSr = TRUE;
1009  gpsNewStruct gNew;
1010  gNew.fix.latitude = LatInverse (clat);
1011  gNew.fix.longitude = LonInverse (clon);
1012  DoFollowThing (&gNew);
1013}
1014#else
1015#ifdef USE_GEOCLUE // Not used and never worked
1016static void GeoclueUpdate (GeocluePosition *position,
1017  GeocluePositionFields fields, int timestamp,
1018  double latitude, double longitude, double /*altitude*/,
1019  GeoclueAccuracy *, gpointer /*userdata*/)
1020{
1021  if (fields & GEO_CLUE_POSITION_FIELDS_LATITUDE &&
1022      fields & GEO_CLUE_POSITION_FIELDS_LONGITUDE) {
1023    gpsNewStruct gNew;
1024    gNew.fix.latitude = latitude;
1025    gNew.fix.longitude = longitude;
1026    DoFollowThing (&gNew);
1027  }
1028}
1029#endif
1030
1031#ifdef USE_GPSD
1032void GpsMove (gps_data_t *gps, char */*buf*/, size_t /*len*/
1033#if GPSD_API_MAJOR_VERSION <= 3
1034  , int /*level*/
1035#endif
1036  )
1037{
1038  gpsNew->fix.latitude = gps->fix.latitude;
1039  gpsNew->fix.longitude = gps->fix.longitude;
1040  if (gps->fix.time > 1e+9) { // gpsfake produces some 0 values.
1041    gpsNew->fix.tm[0] = llrint (gps->fix.time) % (3600 * 24) / 36000 + '0';
1042    gpsNew->fix.tm[1] = llrint (gps->fix.time) / 3600 % 10 + '0';
1043    gpsNew->fix.tm[2] = llrint (gps->fix.time) % 3600 / 600 + '0';
1044    gpsNew->fix.tm[3] = llrint (gps->fix.time) / 60 % 10 + '0';
1045    gpsNew->fix.tm[4] = llrint (gps->fix.time) % 60 / 10 + '0';
1046    gpsNew->fix.tm[5] = llrint (gps->fix.time) % 10 + '0';
1047  }
1048  printf ("%.6s\n", gpsNew->fix.tm);
1049  DoFollowThing (gpsNew);
1050}
1051#endif
1052
1053//void ReceiveNmea (gpointer /*data*/, gint source, GdkInputCondition /*c*/)
1054/*{
1055  static char rx[1200];
1056  static unsigned got = 0;
1057  int cnt = read (source, rx + got, sizeof (rx) - got);
1058  if (cnt == 0) {
1059    gdk_input_remove (gpsSockTag);
1060    return;
1061  }
1062  got += cnt;
1063 
1064  if (ProcessNmea (rx, &got)) DoFollowThing (gpsNew);
1065}*/
1066#endif // !ROUTE_TEST
1067
1068#elif defined(_WIN32) // else NOGTK
1069#define NEWWAY_MAX_COORD 10
1070struct newWaysStruct {
1071  int coord[NEWWAY_MAX_COORD][2], klas, cnt, oneway, bridge;
1072  char name[40], note[40];
1073} newWays[500];
1074
1075
1076int newWayCnt = 0;
1077
1078BOOL CALLBACK DlgSetTagsProc (HWND hwnd, UINT Msg, WPARAM wParam,
1079  LPARAM lParam)
1080{
1081  if (Msg != WM_COMMAND) return FALSE;
1082  HWND edit = GetDlgItem (hwnd, IDC_NAME);
1083  if (wParam == IDC_RD1 || wParam == IDC_RD2) {
1084    Edit_ReplaceSel (edit, TEXT (" Road"));
1085  }
1086  if (wParam == IDC_ST1 || wParam == IDC_ST2) {
1087    Edit_ReplaceSel (edit, TEXT (" Street"));
1088  }
1089  if (wParam == IDC_AVE1 || wParam == IDC_AVE2) {
1090    Edit_ReplaceSel (edit, TEXT (" Avenue"));
1091  }
1092  if (wParam == IDC_DR1 || wParam == IDC_DR2) {
1093    Edit_ReplaceSel (edit, TEXT (" Drive"));
1094  }
1095  if (wParam == IDOK) {
1096    #ifndef _WIN32_WCE
1097    Edit_GetLine (edit, 0, newWays[newWayCnt].name,
1098      sizeof (newWays[newWayCnt].name));
1099    Edit_GetLine (GetDlgItem (hwnd, IDC_NOTE), 0, newWays[newWayCnt].note,
1100      sizeof (newWays[newWayCnt].note));
1101    #else
1102    UTF16 name[40], *sStart = name;
1103    int wstrlen = Edit_GetLine (edit, 0, name, sizeof (name));
1104    unsigned char *tStart = (unsigned char*) newWays[newWayCnt].name;
1105    ConvertUTF16toUTF8 ((const UTF16 **)&sStart,  sStart + wstrlen,
1106        &tStart, tStart + sizeof (newWays[0].name), lenientConversion);
1107
1108    wstrlen = Edit_GetLine (GetDlgItem (hwnd, IDC_NOTE), 0,
1109      name, sizeof (name));
1110    sStart = name;
1111    tStart = (unsigned char*) newWays[newWayCnt].note;
1112    ConvertUTF16toUTF8 ((const UTF16 **)&sStart,  sStart + wstrlen,
1113        &tStart, tStart + sizeof (newWays[0].note), lenientConversion);
1114    #endif
1115    newWays[newWayCnt].oneway = IsDlgButtonChecked (hwnd, IDC_ONEWAY2);
1116    newWays[newWayCnt++].bridge = IsDlgButtonChecked (hwnd, IDC_BRIDGE2);
1117  }
1118  if (wParam == IDCANCEL || wParam == IDOK) {
1119    SipShowIM (SIPF_OFF);
1120    EndDialog (hwnd, wParam == IDOK);
1121    return TRUE;
1122  }
1123  return FALSE;
1124}
1125/*
1126BOOL CALLBACK DlgSetTags2Proc (HWND hwnd, UINT Msg, WPARAM wParam,
1127  LPARAM lParam)
1128{
1129  if (Msg == WM_INITDIALOG) {
1130    HWND klist = GetDlgItem (hwnd, IDC_CLASS);
1131    for (int i = 0; i < sizeof (klasTable) / sizeof (klasTable[0]); i++) {
1132      SendMessage (klist, LB_ADDSTRING, 0, (LPARAM) klasTable[i].desc);
1133    }
1134  }
1135  if (Msg == WM_COMMAND && wParam == IDOK) {
1136    newWays[newWayCnt].cnt = newWayCoordCnt;
1137    newWays[newWayCnt].oneway = IsDlgButtonChecked (hwnd, IDC_ONEWAY);
1138    newWays[newWayCnt].bridge = IsDlgButtonChecked (hwnd, IDC_BRIDGE);
1139    newWays[newWayCnt++].klas = SendMessage (GetDlgItem (hwnd, IDC_CLASS),
1140                                  LB_GETCURSEL, 0, 0);
1141  }
1142 
1143  if (Msg == WM_COMMAND && (wParam == IDCANCEL || wParam == IDOK)) {
1144    EndDialog (hwnd, wParam == IDOK);
1145    return TRUE;
1146  }
1147  return FALSE;
1148}*/
1149
1150#endif // NOGTK
1151
1152#ifndef ANDROID_NDK
1153int Scroll (GtkWidget * /*widget*/, GdkEventScroll *event, void * /*w_cur*/)
1154{
1155  if (Display3D) {
1156    int k = event->direction == GDK_SCROLL_UP ? 2000 : -2000;
1157    SetLocation (clon - lrint (sinAzimuth * k),
1158                 clat + lrint (cosAzimuth * k));
1159  }
1160  else {
1161    int w = width, h = height;
1162    int perpixel = zoom / w;
1163    if (event->direction == GDK_SCROLL_UP) zoom = zoom / 4 * 3;
1164    if (event->direction == GDK_SCROLL_DOWN) zoom = zoom / 3 * 4;
1165    SetLocation (clon + lrint ((perpixel - zoom / w) *
1166      (cosAzimuth * (event->x - w / 2) - sinAzimuth * (h / 2 - event->y))),
1167      clat + lrint ((perpixel - zoom / w) *
1168      (cosAzimuth * (h / 2 - event->y) + sinAzimuth * (event->x - w / 2))));
1169  }
1170  gtk_widget_queue_clear (draw);
1171  return FALSE;
1172}
1173#endif
1174
1175int objectAddRow = -1;
1176#define ADD_HEIGHT 32
1177#define ADD_WIDTH 64
1178void HitButton (int b)
1179{
1180  int returnToMap = b > 0 && option <= FastestRouteNum;
1181 
1182  #ifdef _WIN32
1183  if (AddWayOrNode && b == 0) {
1184    AddWayOrNode = 0;
1185    option = mapMode;
1186    if (newWays[newWayCnt].cnt) objectAddRow = 0;
1187    return;
1188  }
1189  #endif
1190  if (b == 0) {
1191    listYOffset = 0;
1192    option = option < mapMode ? mapMode
1193       : option == optionMode ? mapMode : optionMode;
1194  }
1195  else if (option == StartRouteNum) {
1196    flon = clon;
1197    flat = clat;
1198    GosmFreeRoute ();
1199    shortest = NULL;
1200  }
1201  else if (option == EndRouteNum) {
1202    tlon = clon;
1203    tlat = clat;
1204    CallRoute (TRUE, 0, 0);
1205  }
1206  #ifdef _WIN32
1207  else if (option == DisplayOffNum) {
1208    if (CeEnableBacklight(FALSE)) {
1209      gDisplayOff = TRUE;
1210    }
1211  }
1212  else if (option == BaudRateNum) BaudRate += b * 4800 - 7200;
1213  #endif
1214  #define o(en,min,max) else if (option == en ## Num) \
1215    en = (en - (min) + (b == 2 ? 1 : (max) - (min) - 1)) % \
1216      ((max) - (min)) + (min);
1217  OPTIONS
1218  #undef o
1219  else {
1220    if (b == 2) zoom = zoom / 4 * 3;
1221    if (b == 1) zoom = zoom / 3 * 4;
1222    if (b > 0) SetLocation (clon, clat);
1223  }
1224  if (option == OrientNorthwardsNum && OrientNorthwards) {
1225    cosAzimuth = 1.0;
1226    sinAzimuth = 0.0;
1227  }
1228  if (returnToMap) option = mapMode;
1229}
1230
1231#ifndef NOGTK
1232struct wayPointStruct {
1233  int lat, lon;
1234};
1235deque<wayPointStruct> wayPoint;
1236
1237void ExtractClipboard (GtkClipboard *, const gchar *t, void */*data*/)
1238{
1239  unsigned lonFirst = FALSE, hash = 0;
1240  int minLat = INT_MAX, minLon = INT_MAX, maxLat = INT_MIN, maxLon = INT_MIN;
1241  double deg[2];
1242  static unsigned oldh = 0; // Sometimes an extra queue_clear is needed
1243  wayPoint.clear ();
1244  if (!t) return;
1245  for (; *t != '\0'; t++) {
1246    if (strncasecmp (t, "lat", 3) == 0) lonFirst = FALSE;
1247    else if (strncasecmp (t, "lon", 3) == 0) lonFirst = TRUE;
1248
1249    for (int i = 0; i < 2 && (isdigit (*t) || *t == '-'); i++) {
1250      deg[i] = atof (t);
1251      while (isdigit (*t) || *t == '-') t++;
1252      if (*t != '.') {
1253        // 25S 28E or 25°58′25″S 28°7′42″E or 25 58.5 S 28 6.3 E
1254        while (*t != '\0' && !isdigit (*t) && !isalpha (*t)) t++;
1255        deg[i] += atof (t) / (deg[i] < 0 ? -60 : 60);
1256        while (isdigit (*t) || *t == '.' || *t == ',') t++;
1257        while (*t != '\0' && !isalnum (*t)) t++;
1258        deg[i] += atof (t) / (deg[i] < 0 ? -3600 : 3600);
1259        while (*t != '\0' && !isalpha (*t)) t++;
1260      }
1261      else { // -25.12 28.1
1262        while (*t != '\0' && (isalnum (*t) || *t == '-' || *t == '.')) t++;
1263        while (*t != '\0' && !isalnum (*t)) t++;
1264      }
1265     
1266      if (*t != '\0' && strchr ("westWEST", *t) && !isalpha (t[1])) {
1267        // If t[1] is a letter, then it could be something like
1268        // "-25.1 28.2 school".
1269        if (strchr ("swSW", *t)) deg[i] = -deg[i];
1270        lonFirst = i == (strchr ("snSN", *t) ? 1 : 0);
1271        for (t++; isspace (*t); t++) {}
1272      }
1273      if (deg[i] < -180 || deg[i] > 180) break;
1274      if (i == 0 && (strncasecmp (t, "lat", 3) == 0 ||
1275                     strncasecmp (t, "lon", 3) == 0)) { // lat=-25.7 lon=28.2
1276        for (t += 3; t != '\0' && !isalnum (*t); t++) {}
1277      }
1278      if (i == 1) { // Success !
1279        //printf ("%lf %lf %u\n", deg[lonFirst ? 1 : 0], deg[lonFirst ? 0 : 1],
1280        //  lonFirst); // Debugging
1281        wayPoint.push_back (wayPointStruct ());
1282        wayPoint.back ().lon = Longitude (deg[lonFirst ? 0 : 1]);
1283        wayPoint.back ().lat = Latitude (deg[lonFirst ? 1 : 0]);
1284        lonFirst = FALSE; // Not too sure if we should reset lonFirst here.
1285        hash += wayPoint.back ().lon + wayPoint.back ().lat;
1286        // Bad but adequate hash function.
1287        if (minLon > wayPoint.back ().lon) minLon = wayPoint.back ().lon;
1288        if (maxLon < wayPoint.back ().lon) maxLon = wayPoint.back ().lon;
1289        if (minLat > wayPoint.back ().lat) minLat = wayPoint.back ().lat;
1290        if (maxLat < wayPoint.back ().lat) maxLat = wayPoint.back ().lat;
1291      }
1292    }
1293  }
1294  if (oldh != hash && !wayPoint.empty ()) {
1295    clat = minLat / 2 + maxLat / 2;
1296    clon = minLon / 2 + maxLon / 2;
1297    zoom = maxLat - minLat + maxLon - minLon + (1 << 15);
1298    gtk_widget_queue_clear (draw);
1299  }
1300  oldh = hash;
1301}
1302
1303int UpdateWayPoints (GtkWidget *, GdkEvent *, gpointer *)
1304{
1305  GtkClipboard *c = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
1306  gtk_clipboard_request_text (c, ExtractClipboard, &wayPoint);
1307  return FALSE;
1308}
1309
1310//gint Drag (GtkWidget * /*widget*/, GdkEventMotion *event, void * /*w_cur*/)
1311/*
1312{
1313  if ((option == mapMode || option == optionMode) &&
1314          (event->state & GDK_BUTTON1_MASK)) {
1315    if (firstDrag[0] >= 0) gdk_draw_drawable (draw->window,
1316      draw->style[0].fg_gc[0], draw->window,
1317      0, 0, lrint (event->x) - lastDrag[0], lrint (event->y) - lastDrag[1],
1318      width, height);
1319    lastDrag[0] = lrint (event->x);
1320    lastDrag[1] = lrint (event->y);
1321    if (firstDrag[0] < 0) {
1322      memcpy (firstDrag, lastDrag, sizeof (firstDrag));
1323      pressTime = event->time;
1324    }
1325  }
1326  return FALSE;
1327}*/
1328
1329GtkWidget *bar;
1330int UpdateProcessFunction(void */*userData*/, double t, double d,
1331                                          double /*ultotal*/, double /*ulnow*/)
1332{
1333  gdk_threads_enter ();
1334  gtk_progress_set_value (GTK_PROGRESS (bar), d * 100.0 / t);
1335  gdk_threads_leave ();
1336  return 0;
1337}
1338
1339void *UpdateMapThread (void *n)
1340{
1341  CURL *curl;
1342  FILE *outfile;
1343 
1344  curl = curl_easy_init();
1345  if(curl) {
1346    outfile = fopen("tmp.zip", "w");
1347 
1348    // string zip ((string)(char*)n + ".zip", cmd ("unzip " + zip);
1349    string url ("http://dev.openstreetmap.de/gosmore/" + (string)(char*)n + ".zip");
1350    curl_easy_setopt(curl, CURLOPT_URL, url.c_str ());
1351    curl_easy_setopt(curl, CURLOPT_WRITEDATA, outfile);
1352    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite); //DefaultCurlWrite);
1353    curl_easy_setopt(curl, CURLOPT_READFUNCTION, fread); //my_read_func);
1354    curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
1355    curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, UpdateProcessFunction);
1356    curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, ""); // Bar);
1357 
1358    curl_easy_perform(curl);
1359 
1360    fclose(outfile);
1361    if (system ("unzip tmp.zip") == -1) fprintf (stderr, "Error:system()\n");
1362    string dst ((string)(char*)n + ".pak");
1363    rename ("gosmore.pak", dst.c_str ());
1364    unlink ("tmp.zip");
1365    gdk_threads_enter ();
1366    gtk_progress_bar_set_text (GTK_PROGRESS_BAR (bar), "Done");
1367/*  ChangePak (NULL, clon ^ 0x80000000, clat);
1368    Expose () I don't think it will work in this thread. SEGV. */
1369   
1370    gdk_threads_leave ();
1371
1372    curl_easy_cleanup(curl);
1373  }
1374  free (n); // Malloced in one thread freed in another.
1375  return NULL; 
1376}
1377
1378#endif
1379#if defined (_WIN32) && !defined (_WIN32_WCE)
1380DWORD WINAPI UpdateMapThread (LPVOID n)
1381{
1382  WSADATA d;
1383  WSAStartup (MAKEWORD (1, 1), &d);
1384  struct hostent *he = gethostbyname ("dev.openstreetmap.de");
1385  int s = socket (AF_INET, SOCK_STREAM, 0);
1386  struct sockaddr_in name;
1387  if (he && s != INVALID_SOCKET) {
1388    memset (&name, 0, sizeof (name));
1389    name.sin_family = AF_INET;
1390    name.sin_port = htons (80);
1391    memcpy (&name.sin_addr, he->h_addr_list[0], 4);
1392    string header = string ("GET /gosmore/") + string ((char*) n, 16) + 
1393                 ".zip HTTP/1.0\r\n"
1394                 "Host: dev.openstreetmap.de\r\n"
1395                 "\r\n";
1396    if (connect (s, (sockaddr *) &name, sizeof (name)) == 0 &&
1397        send (s, header.c_str (), strlen (header.c_str ()), 0) > 0) {
1398      char reply[4096], *ptr = reply, *lnl = NULL;
1399      int code, len, cnt = recv (s, reply, sizeof (reply), 0);
1400      sscanf (reply, "%*s %d", &code);
1401      while (cnt > 0 && ptr[0] != '\n' || !lnl) {
1402        if (cnt > 16 && (ptr[0] == '\n' || ptr[0] == '\r') &&
1403            strnicmp (ptr + 1, "Content-Length:", 15) == 0) {
1404          len = atoi (ptr + 16);
1405        }
1406        lnl = *ptr == '\n' ? ptr : *ptr == '\r' ? lnl : NULL;
1407        cnt--;
1408        ptr++;
1409        if (cnt < 1) {
1410          memmove (reply, ptr, cnt);
1411          ptr = reply;
1412          cnt += recv (s, ptr, sizeof (reply) - cnt, 0);
1413        }
1414      }
1415      if (cnt-- > 0) { // Get rid of the '\n'
1416        ptr++; // Get rid of the '\n'
1417        FILE *z = fopen ("tmp.zip", "wb");
1418        code = 0;
1419        do {
1420          fwrite (ptr, cnt, 1, z);
1421          if ((code + cnt) / (len / 1000 + 1) > code / (len / 1000 + 1)) {
1422            PostMessage (mWnd, WM_USER + 2, 0, (code + cnt) / (len / 1000 + 1));
1423          }
1424          code += cnt;
1425          ptr = reply;
1426        } while ((cnt = recv (s, reply, sizeof (reply), 0)) > 0);
1427        fclose (z);
1428        STARTUPINFO si;
1429        PROCESS_INFORMATION pi;
1430        ZeroMemory (&si, sizeof (si));
1431        ZeroMemory (&pi, sizeof (pi));
1432        si.cb = sizeof (si);
1433        CreateProcess ("7z.exe", "7z x -y tmp.zip", NULL, NULL,
1434          FALSE, 0, NULL, NULL, &si, &pi);
1435        WaitForSingleObject (pi.hProcess, INFINITE);
1436        CloseHandle (pi.hProcess);
1437        string dst (string ((char*) n, 16) + ".pak");
1438        rename ("gosmore.pak", dst.c_str ());
1439        _unlink ("tmp.zip");
1440      }
1441    }
1442    else closesocket (s);
1443  }
1444  free (n);
1445  PostMessage (mWnd, WM_USER + 2, 0, 0);
1446  return 0;
1447}
1448#endif
1449
1450/* These macros calling macros may result in very long bloated code and
1451   inefficient machine code, depending on how well the compiler optimizes.
1452*/
1453
1454#define CompactOptions ((width * height < 400 * 400))
1455int ListXY (int cnt, int isY)
1456{ // Returns either the x or the y for a certain list item
1457  int max = mapMode; //option == optionMode ? mapNode :
1458  int w = CompactOptions ? 70 : 105, h = CompactOptions ? 45 : 80;
1459  while ((width/w) * (height/h - 1) > max) {
1460    w++;
1461    h++;
1462  }
1463  return isY ? cnt / (width / w) * h + h / 2 - listYOffset :
1464    (cnt % (width / w)) * w + w / 2;
1465}
1466
1467#ifdef ANDROID_NDK // OPENGL
1468typedef jint HDC;
1469#define gdk_draw_drawable(win,dgc,sdc,x,y,dx,dy,w,h) \
1470        do { int crop[] = { x, y + h, w, -h }; \
1471        glEnable(GL_BLEND); \
1472        glEnable(GL_TEXTURE_2D); \
1473        glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop); \
1474        glDrawTexiOES(dx, height - dy, 0, crop[2], h); \
1475        glDisable(GL_TEXTURE_2D); \
1476        glDisable(GL_BLEND);\
1477        } while (0) // TODO Try to get a glMatrix that match OES coordinates
1478#define gdk_draw_line(win,gc,sx,sy,dx,dy) \
1479  do { GdkPoint l[2] = { sx, sy, dx, dy }; \
1480    glVertexPointer (2, GL_SHORT, 0, l); \
1481    glDrawArrays (GL_LINES, 0, 2); } while (0)
1482
1483static jmethodID drawTeks = 0;
1484
1485#elif !defined(NOGTK)
1486typedef GdkGC *HDC;
1487
1488static GdkGC *maskGC = NULL, *fg_gc;
1489static GdkBitmap *mask = NULL;
1490// create bitmap for generation the mask image for icons
1491// all icons must be smaller than these dimensions
1492static GdkBitmap *maskicon = NULL;
1493static GdkPixmap *icons = NULL;
1494
1495#else
1496HDC icons, maskDc;
1497HFONT sysFont;
1498LOGFONT logFont;
1499
1500#define gtk_combo_box_get_active(x) 1
1501#define gdk_draw_drawable(win,dgc,sdc,x,y,dx,dy,w,h) \
1502  BitBlt (dgc, dx, dy, w, h, maskDc, x, y, SRCAND); \
1503  BitBlt (dgc, dx, dy, w, h, sdc, x, y, SRCPAINT)
1504#define gdk_draw_line(win,gc,sx,sy,dx,dy) \
1505  do { MoveToEx (gc, sx, sy, NULL); LineTo (gc, dx, dy); } while (0)
1506
1507#endif
1508
1509static HDC mygc = NULL, iconsgc = NULL;
1510
1511#ifdef PANGO_VERSION
1512PangoContext *pc;
1513PangoLayout  *pl;
1514#endif
1515
1516void DrawString (int x, int y, const char *optStr)
1517{
1518  #ifdef PANGO_VERSION
1519  PangoMatrix mat = PANGO_MATRIX_INIT;
1520  pango_context_set_matrix (pc, &mat);
1521  pango_layout_set_text (pl, optStr, -1);
1522  gdk_draw_layout (GDK_DRAWABLE (draw->window),
1523                     fg_gc /*draw->style->fg_gc[0]*/, x, y, pl);
1524  #elif defined (_WIN32)
1525  SelectObject (mygc, sysFont);
1526  const unsigned char *sStart = (const unsigned char*) optStr;
1527  UTF16 wcTmp[70], *tStart = (UTF16 *) wcTmp;
1528  if (ConvertUTF8toUTF16 (&sStart,  sStart + strlen (optStr), &tStart,
1529           tStart + sizeof (wcTmp) / sizeof (wcTmp[0]), lenientConversion)
1530      == conversionOK) {
1531    ExtTextOutW (mygc, x, y, 0, NULL, (wchar_t*) wcTmp, tStart - wcTmp, NULL);
1532  }
1533  #endif
1534}
1535
1536void DrawPoI (int dstx, int dsty, int *icon)
1537{
1538  if (icon[2] == 0 || dstx < -icon[2] || dsty < -icon[3] ||
1539    dstx > width + icon[2] ||
1540    // GDK need these tests for the Start&EndRoute markers
1541    dsty > height + icon[3]) return;
1542  #ifndef NOGTK
1543  // for gdk we first need to extract the portion of the mask
1544  if (!maskicon) maskicon = gdk_pixmap_new(NULL, 100, 100, 1);
1545  gdk_draw_drawable (maskicon, maskGC, mask,
1546                     icon[0], icon[1], 0, 0,
1547                     icon[2], icon[3]);
1548  // and set the clip region using that portion
1549  gdk_gc_set_clip_origin(iconsgc, dstx - icon[2] / 2, dsty - icon[3] / 2);
1550  gdk_gc_set_clip_mask(iconsgc, maskicon);
1551  #endif
1552  gdk_draw_drawable (draw->window, iconsgc, icons,
1553    icon[0], icon[1], dstx - icon[2] / 2, dsty - icon[3] / 2,
1554    icon[2], icon[3]);
1555}
1556
1557void GeoSearch (const char *key)
1558{
1559  const char *comma = strchr (key, ',');
1560  if (!comma) comma = strstr (key, " near ");
1561  if (comma) {
1562    const char *cName = comma + (*comma == ',' ? 1 : 6);
1563    string citi = string ("city:") + (cName + strspn (cName, " "));
1564    const char *tag = gosmData + *GosmIdxSearch (citi.c_str (), 0);
1565    while (*--tag) {}
1566    ChangePak (NULL, ((wayType *)tag)[-1].clon, ((wayType *)tag)[-1].clat);
1567    string xkey = string (key, comma - key);
1568    //printf ("%s tag=%s\nxke %s\n", cName, tag + 1, xkey.c_str ());
1569    GosmSearch (((wayType *)tag)[-1].clon, ((wayType *)tag)[-1].clat, xkey.c_str ());
1570  }
1571  else GosmSearch (clon, clat, key);
1572}
1573
1574
1575#if defined (ANDROID_NDK)
1576/*extern "C" void Java_org_osmu_gosmore_MapView_setLocation (
1577                JNIEnv*  env, jobject thiz, jdouble lon, jdouble lat)
1578{
1579  LG clon = Longitude (lon);
1580  clat = Latitude (lat);
1581}*/
1582
1583#define DisplaySearchResults(); // Never in searchMode, no-op
1584extern "C" void Java_org_osmu_gosmore_Search_search (
1585                JNIEnv*  env, jobject thiz, jstring jquery)
1586{
1587        static jmethodID meth = 0;
1588        static int started = 0;
1589        if (!started) {
1590          started = 1;
1591          jclass cls=env->GetObjectClass(thiz);
1592          //cls =(jclass)( env->NewGlobalRef(jcls) );
1593      //cls = env->FindClass(
1594        //                "org/osmu/gosmore/MyClb");
1595
1596          meth = env->GetMethodID(cls, "searchResult",
1597                          "(IIIIDIILjava/lang/String;DDI)V");
1598//        env->ExceptionClear();
1599          /*  if (method != 0)*/
1600//          env->ExceptionClear();
1601        }
1602        const char *query = env->GetStringUTFChars(jquery, NULL);
1603        GeoSearch (query);
1604        LG env->ReleaseStringUTFChars(jquery, query);
1605
1606#else
1607void DisplaySearchResults (void)
1608{
1609#endif
1610            for (int i = 0, y = SearchSpacing / 2; i < searchCnt && gosmSstr[i];
1611                     i++, y += SearchSpacing) {
1612              double dist = sqrt (double (Sqr ((__int64) clon - gosmSway[i]->clon) +
1613                  Sqr ((__int64) clat - gosmSway[i]->clat))) * (20000 / 2147483648.0) *
1614                cos (LatInverse (clat) * (M_PI / 180));
1615
1616              int x = SearchSpacing + 70;
1617              __int64 lx = X (gosmSway[i]->clon, gosmSway[i]->clat) - width / 2;
1618              __int64 ly = Y (gosmSway[i]->clon, gosmSway[i]->clat) - height / 2;
1619              double norm = lx || ly ? sqrt (double(lx * lx + ly * ly)) / 64 : 1;
1620              int u = lrint (lx / norm), v = lrint (ly / norm);
1621              string s (gosmSstr[i], strcspn (gosmSstr[i], "\n"));
1622              char *name = (char *)(gosmSway[i] + 1) + 1;
1623              if (name != gosmSstr[i]) s += " (" +
1624                             string (name, strcspn (name, "\n")) + ")";
1625              #ifdef ANDROID_NDK
1626              jstring js = env->NewStringUTF (s.c_str ());
1627              int *z = Style (gosmSway[i])->x + 4 * IconSet;
1628              env->CallVoidMethod (thiz, meth, z[0], z[1], z[2], z[3],
1629                          dist, u, v, js,
1630                          LonInverse (gosmSway[i]->clon),
1631                          LatInverse (gosmSway[i]->clat),
1632                          gosmSway[i]->dlat + gosmSway[i]->dlon + (1 << 15));
1633              #else
1634              DrawPoI (SearchSpacing / 2, y, Style (gosmSway[i])->x + 4 * IconSet);
1635
1636              char distance[10]; // Formula inaccurate over long distances hence "Far"
1637              sprintf (distance, dist > 998 ? "Far" : dist > 1 ? "%.0lf km" :
1638                "%.0lf m", dist > 1 ? dist : dist * 1000);
1639              DrawString (SearchSpacing + 33 - 11 * strcspn (distance, " "), y - 10,
1640                distance); // Right adjustment is inaccurate
1641              gdk_draw_line (draw->window, mygc, x + u / 8, y + v / 8,
1642                x - u / 8, y - v / 8);
1643              gdk_draw_line (draw->window, mygc, x + u / 8, y + v / 8,
1644                x + u / 12 + v / 20, y - u / 20 + v / 12);
1645              gdk_draw_line (draw->window, mygc, x + u / 8, y + v / 8,
1646                x + u / 12 - v / 20, y + u / 20 + v / 12);
1647
1648              DrawString (SearchSpacing + x, y - 10, s.c_str ());
1649
1650              gdk_draw_line (draw->window, mygc, 0, y + SearchSpacing / 2,
1651                width, y + SearchSpacing / 2);
1652              #endif
1653            }
1654}
1655
1656
1657#ifndef _WIN32_WCE
1658int HandleKeyboard (int, int, int)
1659{
1660#else
1661int HandleKeyboard (int event, int ex, int ey)
1662{ // Some WinCE devices, like the Mio Moov 200 does not have an input method
1663  // and any call to activate it or set the text on an EDIT or STATIC (label)
1664  // control will crash the application. So under WinCE we default to our
1665  // own keyboard.
1666  //
1667  // Draw our own keyboard (Expose Event) or handle the key (Click)
1668  if (Keyboard) return FALSE; // Using the Windows keyboard
1669  // DrawString (30, 5, searchStr.c_str ()); // For testing under GTK
1670  if (!event) {
1671    RECT r;
1672    r.left = 0;
1673    r.top = height - 32 * 3;
1674    r.right = width;
1675    r.bottom = height;
1676    FillRect (mygc, &r, (HBRUSH) GetStockObject (WHITE_BRUSH)); //brush[KeyboardNum]);
1677    SelectObject (mygc, GetStockObject (BLACK_PEN));
1678  }
1679
1680  const char *kbLayout[] = { "qwertyuiop", "asdfghjkl", " zxcvbnm,$" };
1681  for (int i = 0; i < 3; i++) {
1682    for (int j = 0; kbLayout[i][j] != '\0'; j++) {
1683      int hb = width / strlen (kbLayout[0]) / 2, ys = 16;
1684      int x = (2 * j + (i & 1)) * hb, y = height - (3 - i) * ys * 2;
1685      if (event && ey >= y && ey < y + ys + ys && ex < x + hb + hb) {
1686        if (kbLayout[i][j] != '$') searchStr += kbLayout[i][j];
1687        else if (searchStr.length () > 0) searchStr.erase (searchStr.length () - 1, 1);
1688        logprintf ("'%s'\n", searchStr.c_str());
1689        GeoSearch (searchStr.c_str ());
1690        gtk_widget_queue_clear (draw);
1691        return TRUE;
1692      }
1693      if (!event) {
1694        if (j > 0) gdk_draw_line (draw->window, mygc, x, y, x, y + ys + ys);
1695        else gdk_draw_line (draw->window, mygc, 0, y, width, y);
1696        string chr = string ("") + kbLayout[i][j];
1697        if (kbLayout[i][j] == ' ') DrawString (x + hb - 5, y + ys / 2, "[ ]");
1698        else if (kbLayout[i][j] != '$') DrawString (x + hb, y + ys / 2, chr.c_str ());
1699        else { // Now draw the backspace symbol :
1700          gdk_draw_line (draw->window, mygc, x + 3, y + ys, x + hb + hb - 3, y + ys);
1701          gdk_draw_line (draw->window, mygc, x + 3, y + ys, x + hb, y + 3);
1702          gdk_draw_line (draw->window, mygc, x + 3, y + ys, x + hb, y + ys + ys - 3);
1703        }
1704      }
1705    }
1706  }
1707  #endif
1708  return FALSE;
1709}
1710
1711static int lastRelease = 0, oldx = -1, oldy, pressTime = -1;
1712
1713#if !defined (ANDROID_NDK)
1714/*extern "C" int Java_org_osmu_gosmore_MapRenderer_mouseEv (
1715                JNIEnv*  env, jobject thiz, jint x, jint y, jint evTime,
1716                jint button, jboolean click)*/
1717int MouseEv (int x, int y, int evTime, int button, int click)
1718{
1719  // Anything that covers more than 3 pixels in either direction is a drag.
1720  LG gtk_widget_queue_clear (draw); 
1721  int w = width, h = height;
1722  int isDrag = //DebounceDrag ?
1723        pressTime >= 0 && (lastRelease + 200 > evTime ||
1724                                pressTime + 200 < evTime);
1725//        : pressTime >= 0 && (abs((int)(firstDrag[0] - event->x)) > 3 ||
1726//                                abs((int)(firstDrag[1] - event->y)) > 3;
1727  // logprintf("Click (isDrag = %d): firstDrag = %d,%d; event = %d,%d\n",
1728  //        isDrag, firstDrag[0], firstDrag[1], event->x, event->y);
1729  if (pressTime == -1) pressTime = evTime;
1730  if (click) pressTime = -1;
1731  if (click) lastRelease = evTime;
1732  if (isDrag) {
1733    if (option == optionMode) {
1734      listYOffset = max (0, listYOffset + (int)lrint (oldy - y));
1735    }
1736    if (option == mapMode) {
1737      int lon = clon + lrint (zoom / w *
1738        (cosAzimuth * (Display3D ? 0 : oldx - x) - sinAzimuth * (y - oldy)));
1739      int lat = clat + lrint (zoom / w *
1740        (cosAzimuth * (y - oldy + sinAzimuth * (Display3D ? 0 : oldx - x))));
1741      if (Display3D) {
1742        double newa = atan2 (sinAzimuth, cosAzimuth) - (oldx-x) * M_PI / 580;
1743        cosAzimuth = cos (newa);
1744        sinAzimuth = sin (newa);
1745      }
1746      SetLocation (lon, lat);
1747    }
1748  }
1749  oldx = x;
1750  oldy = y;
1751  if (!click || isDrag) return 0;
1752
1753  if (ButtonSize <= 0) ButtonSize = 4;
1754  int b = (height - lrint (y)) / (ButtonSize * 20);
1755  if (objectAddRow >= 0) {
1756    int perRow = (w - ButtonSize * 20) / ADD_WIDTH;
1757    if (x < w - ButtonSize * 20) {
1758      #ifdef _WIN32
1759      newWays[newWayCnt].klas = objectAddRow + x / ADD_WIDTH +
1760                                y / ADD_HEIGHT * perRow;
1761      SipShowIM (SIPF_ON);
1762      if (DialogBox (hInst, MAKEINTRESOURCE (IDD_SETTAGS), NULL,
1763          (DLGPROC) DlgSetTagsProc)) {} //DialogBox (hInst,
1764          //MAKEINTRESOURCE (IDD_SETTAGS2), NULL, (DLGPROC) DlgSetTags2Proc);
1765      newWays[newWayCnt].cnt = 0;
1766      #endif
1767      objectAddRow = -1;
1768    }
1769    else objectAddRow = y * (restriction_no_right_turn / perRow + 2) /
1770                              height * perRow;
1771  }
1772  else if (x > w - ButtonSize * 20 && b <
1773      (Layout >
1774       (MenuKey == 0 || option != mapMode ? 0 : 1) ? 3 : 0)) HitButton (b);
1775  else if (option == optionMode) {
1776    if (1) {
1777      for (int best = 9999, i = 0; i < mapMode; i++) {
1778        int d = lrint (fabs (double(ListXY (i, FALSE) - x)) +
1779                       fabs (double(ListXY (i, TRUE) - y)));
1780        if (d < best) {
1781          best = d;
1782          option = i;
1783        }
1784      }
1785      if (option <= OrientNorthwardsNum) HitButton (1);
1786      if (option >= ViewOSMNum && option <= ViewGMapsNum) {
1787        char lstr[200];
1788        int zl = 0;
1789        while (zl < 32 && (zoom >> zl)) zl++;
1790        setlocale (LC_NUMERIC, "C");
1791        sprintf (lstr,
1792         option == ViewOSMNum ? "%sopenstreetmap.org/?lat=%.5lf&lon=%.5lf&zoom=%d%s" :
1793         option == EditInPotlatchNum ? "%sopenstreetmap.org/edit?lat=%.5lf&lon=%.5lf&zoom=%d%s" :
1794         "%smaps.google.com/?ll=%.5lf,%.5lf&z=%d%s",
1795        #ifdef WIN32
1796          "http://", LatInverse (clat), LonInverse (clon), 33 - zl, "");
1797        #ifndef _WIN32_WCE
1798        ShellExecute (NULL, TEXT ("open"), lstr, NULL, NULL,
1799          SW_SHOWNORMAL);
1800        #else
1801        MessageBox (NULL, TEXT ("Not implemented"), TEXT ("Error"), MB_APPLMODAL|MB_OK);
1802        #endif
1803        #else
1804          "gnome-open 'http://", LatInverse (clat), LonInverse (clon), 33 - zl, "'");
1805        option = system (lstr); // Shut up GCC w.r.t. return value
1806        #endif
1807        option = mapMode;
1808      }
1809      #ifndef NOGTK
1810      else if (option == UpdateMapNum) {
1811        struct stat s;
1812        if (currentBbox[0] == '\0') {
1813          gtk_dialog_run (GTK_DIALOG (gtk_message_dialog_new (NULL,
1814            GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
1815            "Error:\n"
1816            "Gosmore is running with a custom map\n"
1817            "Download aborted.")));
1818        }
1819        else if (stat (currentBbox, &s) == 0 &&
1820           (s.st_mtime > time (NULL) - 3600*24*7 ||
1821            s.st_ctime > time (NULL) - 3600 * 24 * 7)) {
1822          gtk_dialog_run (GTK_DIALOG (gtk_message_dialog_new (NULL,
1823            GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
1824            "Error:\n"
1825            "%s has changed during the last 7 days,\n"
1826            "and is most likely up-to-date.\n"
1827            "Download aborted.", currentBbox)));
1828        }
1829        else {
1830          string msg (string ("Downloading ") + currentBbox);
1831          gtk_progress_bar_set_text (GTK_PROGRESS_BAR (bar), msg.c_str ());
1832          g_thread_create (&UpdateMapThread, strndup (currentBbox, 16), FALSE, NULL);
1833        }
1834        option = mapMode;
1835      }
1836      #else
1837      #if defined (_WIN32) && !defined (_WIN32_WCE)
1838      else if (option == UpdateMapNum) {
1839        struct stat s;
1840        if (currentBbox[0] == '\0') {
1841          MessageBox (NULL, "Error:\n"
1842            "Gosmore is running with a custom map\n"
1843            "Download aborted.", "Error", MB_APPLMODAL|MB_OK);
1844        }
1845        else if (stat (currentBbox, &s) == 0 &&
1846           (s.st_mtime > time (NULL) - 3600*24*7 ||
1847            s.st_ctime > time (NULL) - 3600 * 24 * 7)) {
1848          MessageBox (NULL, "Error:\n"
1849            "The .pak file has changed during the last 7 days,\n"
1850            "and is most likely up-to-date.\n"
1851            "Download aborted.", "Error", MB_APPLMODAL|MB_OK);
1852        }
1853        else {
1854          DWORD threadId;
1855          CreateThread (NULL, 0, UpdateMapThread, strdup (currentBbox), 0,
1856            &threadId);
1857        }
1858        option = mapMode;
1859      }
1860      #endif
1861      #endif
1862    }
1863  }
1864  else if (option == searchMode) {
1865    int row = y / SearchSpacing;
1866    if (!HandleKeyboard (TRUE, x, y) && row < searchCnt && gosmSstr[row]) {
1867      SetLocation (gosmSway[row]->clon, gosmSway[row]->clat);
1868      zoom = gosmSway[row]->dlat + gosmSway[row]->dlon + (1 << 15);
1869      if (zoom <= (1 << 15)) zoom = Style (gosmSway[row])->scaleMax;
1870      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (followGPSr), FALSE);
1871      FollowGPSr = FALSE;
1872      option = mapMode;
1873      highlight = string (gosmSstr[row], strcspn (gosmSstr[row], "\n"));
1874      gtk_widget_queue_clear (draw);
1875    }
1876  }
1877  else {
1878    #ifdef ROUTE_TEST
1879    if (event->state & GDK_SHIFT_MASK) {
1880      return RouteTest (NULL /*widget*/, event, NULL /*para*/);
1881    }
1882    #endif
1883    int perpixel = zoom / w, dx = x - w / 2, dy = h / 2 - y;
1884    int lon = clon + lrint (perpixel *
1885      (cosAzimuth * (Display3D ? 0 : dx) - sinAzimuth * dy));
1886    int lat = clat + lrint (perpixel *
1887      (cosAzimuth * dy + sinAzimuth * (Display3D ? 0 : dx)));
1888    if (button == 1) {
1889      if (Display3D) {
1890        double newa = atan2 (sinAzimuth, cosAzimuth) - dx * M_PI / 580;
1891        cosAzimuth = cos (newa);
1892        sinAzimuth = sin (newa);
1893      }
1894      SetLocation (lon, lat);
1895
1896      #ifdef _WIN32
1897      if (AddWayOrNode && newWays[newWayCnt].cnt < NEWWAY_MAX_COORD) {
1898        newWays[newWayCnt].coord[newWays[newWayCnt].cnt][0] = clon;
1899        newWays[newWayCnt].coord[newWays[newWayCnt].cnt++][1] = clat;
1900      }
1901      #endif
1902      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (followGPSr), FALSE);
1903      FollowGPSr = 0;
1904    }
1905    else if (button == 2) {
1906      flon = lon;
1907      flat = lat;
1908      GosmFreeRoute ();
1909      shortest = NULL;
1910    }
1911    else {
1912      tlon = lon;
1913      tlat = lat;
1914      CallRoute (TRUE, 0, 0);
1915    }
1916  }
1917  return FALSE;
1918}
1919#endif
1920
1921#define STATEINFO OPTIONS o (clat, 0, 0) o (clon, 0, 0) \
1922 o (sinAzimuth, 0, 0) o (cosAzimuth, 0, 0) o (zoom, 0, 0) o (option, 0, 0) \
1923 o (width, 0, 0) o (height, 0, 0)
1924#define o(x,min,max) sizeof (x) +
1925static const size_t stateSize = STATEINFO 0;
1926#undef o
1927
1928#if 0
1929typedef struct {  /* For 3D, a list of segments is generated that is */
1930  ndType *nd;     /* z-sorted and rendered. */
1931  int f[2], t[2], tlen;
1932  char *text;
1933} renderNd;
1934#endif
1935
1936/*inline double YDivisor (double x) { return x; }
1937inline double Clamp (double x) { return x; }*/
1938/*
1939inline int YDivisor (int y)
1940{
1941  return y > 5256 || y < -5256 ? y : y < 0 ? -5256 : 5256;
1942}
1943*/
1944/*
1945inline int Clamp2 (int x)
1946{
1947  return x < -32760 ? -32760 : x > 32760 ? 32760 : x;
1948}*/
1949
1950void Draw3DLine (int sx, int sy, int dx, int dy)
1951{
1952  if (Display3D) {
1953    if (sy < 0) {
1954      if (dy < 0) return;
1955      sx = dx + (dx - sx) * (/*height*/ 1024 - dy) / (dy - sy);
1956      sy = /*height*/ 1024;
1957    }
1958    else if (dy < 0) {
1959      dx = sx + (sx - dx) * (/*height*/ 1024 - sy) / (sy - dy);
1960      dy = /*height*/ 1024;
1961    }
1962  }
1963  gdk_draw_line (draw->window, mygc, sx, sy, dx, dy);
1964}
1965
1966int TestOrSet (int *bits, int set, int x0, int y0, int ax, int ay,
1967       int bx, int by)
1968/* This funtion manipulates bits in a rectangular area in a bitfield. (x0, y0)
1969   is one of the corners. (ax,ay) and (bx,by) are to vectors that define
1970   two of the sides. ay > 0
1971   The precise operation is determined by the 'set' boolean
1972*/
1973{
1974  if (by < 0) { // Top not given, so we find it first.
1975    x0 += bx;
1976    y0 += by;
1977    int nx = ax, ny = ay;
1978    ax = -bx;
1979    ay = -by;
1980    bx = nx;
1981    by = ny;
1982  }
1983  if (y0 < 0 || y0 + ay + by > height ||
1984      x0 + ax < 0 || x0 + bx > width) return TRUE;
1985  // Do not place anything offscreen.
1986  const int shf = 9;
1987  x0 <<= shf;
1988  int x1 = x0, d0 = (ax << shf) / (ay + 1), d1 = (bx << shf) / (by + 1);
1989  int bpr = (width + 31) / 32;
1990  bits += bpr * y0;
1991  for (int cnt = ay + by; cnt > 0; cnt--) {
1992    x0 += d0;
1993    x1 += d1;
1994    for (int i = x0 >> shf; i < (x1 >> shf); i++) {
1995      if (set) bits[i >> 5] |= 1 << (i & 31);
1996      else if (bits[i >> 5] & (1 << (i & 31))) return TRUE;
1997    } // This loop can be optimized
1998    //gdk_draw_line (draw->window, mygc, x0 >> shf, y0, x1 >> shf, y0);
1999    // Uncomment this line to see if we're testing the right spot
2000    // (and it looks kind of interesting )
2001    bits += bpr;
2002    if (cnt == by) d0 = (bx << shf) / by;
2003    if (cnt == ay) d1 = (ax << shf) / ay;
2004    y0++;
2005  }
2006  return FALSE;
2007}
2008
2009/* Choose the part of the way that is best to render the text on. Currently
2010   the straightest part. We look at for the two points where the direct
2011   distance is long enough and it is also the closest to the distance
2012   between the two points along the curve.
2013   TODO: Use the number of junctions between the two points (T / 4 way)
2014   TODO: Consider moments (standard deviation)
2015*/
2016struct linePtType {
2017  int x, y, cumulative;
2018  linePtType (int _x, int _y, int _c) : x (_x), y (_y), cumulative (_c) {}
2019};
2020
2021struct text2Brendered {
2022  const char *s; // Either \n or \0 terminated
2023  int x, y, x2, y2, dst;
2024  text2Brendered (void) {}
2025};
2026
2027void ConsiderText (queue<linePtType> *q, int finish, int len, int *best,
2028  text2Brendered *t)
2029{
2030  while (!q->empty ()) {
2031    int clip[2] = { 0, 0 }; // Used with q->front or q->back is off-screen
2032    int dx = q->back ().x - q->front ().x, dy = q->back ().y - q->front ().y;
2033    if (q->size () == 2) { // cumulative can't cope with clipping, so we
2034                           // only do it when we know detour will be 0
2035      LG for (int i = 0; i < 2; i++) {
2036        LG linePtType *f = !i ? &q->front () : &q->back ();
2037        LG if (f->x < 10 && dx != 0) clip[i] = max (clip[i], 256 * (10 - f->x) / (i ? -dx : dx));
2038        if (f->y < 10 && dy != 0) clip[i] = max (clip[i], 256 * (10 - f->y) / (i ? -dy : dy));
2039        int r2x = f->x - width + 10;
2040        LG if (r2x > 0 && dx != 0) clip[i] = max (clip[i], 256 * r2x / (i ? dx : -dx));
2041        int r2y = f->y - height + 10;
2042        if (r2y > 0 && dy != 0) clip[i] = max (clip[i], 256 * r2y / (i ? dy : -dy));
2043      }
2044    }
2045    int dst = isqrt (Sqr (dx) + Sqr (dy)) * (256 - clip[0] - clip[1]) / 256;
2046    int detour = q->size () == 2 ? 0 : q->back ().cumulative - q->front ().cumulative - dst;
2047    if (detour <= *best) {
2048      LG if (dst * DetailLevel > len * 14) {
2049        t->x = q->front ().x + dx * clip[0] / 256;
2050        t->y = q->front ().y + dy * clip[0] / 256;
2051        t->x2 = q->back ().x - dx * clip[1] / 256;
2052        t->y2 = q->back ().y - dy * clip[1] / 256;
2053        t->dst = dst;
2054        *best = detour;
2055      }
2056      if (!finish) break;
2057    }
2058    q->pop ();
2059  } // While shortening the queue
2060}
2061
2062int WaySizeCmp (ndType **a, ndType **b)
2063{
2064  return Way (*a)->dlat * (__int64) Way (*a)->dlon >
2065         Way (*b)->dlat * (__int64) Way (*b)->dlon ? 1 : -1;
2066}
2067
2068vector<short> casingCmds;
2069#define CASING_MAX 20
2070static short casing[10 + CASING_MAX * 2];
2071static int casingx, casingy, casingPts = 0;
2072void MoveTo (int x, int y)
2073{
2074#ifdef ANDROID_NDK // OPENGL
2075  if (casingPts) {
2076    casing[6] = casing[4];
2077    casing[7] = casing[5];
2078    int i, circ = min ((abs(casing[2] - casing[6]) + abs (casing[3] - casing[7])) / 3, CASING_MAX);
2079    for (i = 1; i < circ; i++) {
2080      casing[i + i + 6] = casing[6] +
2081        (casing[2] - casing[6]) * (1 - cos (i * M_PI / circ))/2 -
2082        (casing[3] - casing[7]) * sin (i * M_PI / circ)/2;
2083      casing[i + i + 7] = casing[7] +
2084        (casing[3] - casing[7]) * (1 - cos (i * M_PI / circ))/2 +
2085        (casing[2] - casing[6]) * sin (i * M_PI / circ)/2;
2086    }
2087    casing[i + i + 6] = casing[2];
2088    casing[i + i + 7] = casing[3];
2089    glVertexPointer (2, GL_SHORT, 0, casing + 6);
2090    casingCmds.push_back (i + 1);
2091    casingCmds.push_back (0); // pad
2092    casingCmds.insert (casingCmds.end(), casing + 6, casing + 8 + i + i);
2093    //if (vol) glDrawArrays (GL_TRIANGLE_FAN, 0, i + 1);
2094    glDrawArrays (GL_LINE_STRIP, 0, i + 1);
2095  }
2096#endif
2097  casingx = x;
2098  casingy = y;
2099  casingPts = 0;
2100}
2101
2102void LineTo (int x, int y, int width)
2103{
2104#ifdef ANDROID_NDK // OPENGL
2105  int d = sqrt (double((x - casingx) * (x - casingx) +
2106                       (y - casingy) * (y - casingy))), i;
2107  casing[0] = casingx + (Display3D ? (y - casingy) * casingy * width / PIX45
2108    : (y - casingy) * width) / (d + d);
2109  casing[1] = casingy + (Display3D ? (casingx - x) * casingy * width / PIX45
2110    : (casingx - x) * width) / (d + d);
2111  casing[6] = casing[0] - (Display3D ? (y - casingy) * casingy * width / PIX45
2112    : (y - casingy) * width) / d;
2113  casing[7] = casing[1] - (Display3D ? (casingx - x) * casingy * width / PIX45
2114    : (casingx - x) * width) / d;
2115  if (!casingPts++) {
2116    int circ = min ((abs(casing[0] - casing[6]) + abs (casing[1] - casing[7])) / 3, CASING_MAX);
2117    for (i = 1; i < circ; i++) {
2118      casing[i + i + 6] = casing[6] + 
2119        (casing[0] - casing[6]) * (1 - cos (i * M_PI / circ))/2 +
2120        (casing[1] - casing[7]) * sin (i * M_PI / circ)/2;
2121      casing[i + i + 7] = casing[7] +
2122        (casing[1] - casing[7]) * (1 - cos (i * M_PI / circ))/2 -
2123        (casing[0] - casing[6]) * sin (i * M_PI / circ)/2;
2124    }
2125  }
2126  else {
2127    memcpy (casing + 8, (casing[2] - casingx) * (casingx - x) >
2128      (casing[3] - casingy) * (y - casingy) ? casing + 2 : casing + 4, sizeof (casing[2]) * 2);
2129    i = 2;
2130  }
2131 
2132  casing[2] = x + (Display3D ? (y - casingy) * y * width / PIX45
2133    : (y - casingy) * width) / (d + d);
2134  casing[3] = y + (Display3D ? (casingx - x) * y * width / PIX45
2135    : (casingx - x) * width) / (d + d);
2136  casing[4] = casing[2] - (Display3D ? (y - casingy) * y * width / PIX45
2137    : (y - casingy) * width) / d;
2138  casing[5] = casing[3] - (Display3D ? (casingx - x) * y * width / PIX45
2139    : (casingx - x) * width) / d;
2140  glVertexPointer (2, GL_SHORT, 0, casing);
2141  casingCmds.push_back (i + 3);
2142  casingCmds.push_back (0); // pad
2143  casingCmds.insert (casingCmds.end(), casing, casing + i + i + 6);
2144  //if (vol) glDrawArrays (GL_TRIANGLE_FAN, 0, i + 3);
2145  glDrawArrays (GL_LINE_LOOP, 0, i + 3); 
2146#else
2147  if (width) {} // Suppress warning from compiler
2148  gdk_draw_line (GDK_DRAWABLE (draw->window), mygc, casingx, casingy, x, y);
2149#endif
2150  casingx = x;
2151  casingy = y;
2152}
2153
2154void FinishCasing ()
2155{
2156  #ifdef ANDROID_NDK // OPENGL
2157  MoveTo (0, 0); // Draw round ending for last line
2158  glVertexPointer (2, GL_SHORT, 0, &casingCmds[0] + 2);
2159  for (int i = 0; i < casingCmds.size ();
2160      i += casingCmds[i] < 0 ? 4 : casingCmds[i] + casingCmds[i] + 2) {
2161    #ifdef ANDROID_NDK // OPENGL ES
2162    if (casingCmds[i] < 0) glColor4x (casingCmds[i + 1] * 0x101,
2163           casingCmds[i + 2] * 0x101, casingCmds[i + 3] * 0x101, 0x10000);
2164    #else
2165    if (casingCmds[i] < 0) glColor3f (casingCmds[i + 1] / 255.0,
2166           casingCmds[i + 2] / 255.0, casingCmds[i + 3] / 255.0);
2167    #endif
2168    else {
2169      glVertexPointer (2, GL_SHORT, 0, &casingCmds[i] + 2);
2170      glDrawArrays (GL_TRIANGLE_FAN, 0, casingCmds[i]);
2171    }
2172  }
2173  casingCmds.clear();
2174  #endif
2175}
2176
2177inline void SetCasingColor (short r, short g, short b)
2178{
2179  MoveTo (0, 0); // Complete previous line in previous colour
2180  casingCmds.push_back (-1);
2181  casingCmds.push_back (r); casingCmds.push_back (g); casingCmds.push_back (b);
2182}
2183
2184#if defined (ANDROID_NDK)
2185extern "C" void Java_org_osmu_gosmore_MapRenderer_render (
2186                JNIEnv*  env, jobject thiz, jdouble lon, jdouble lat,
2187                jint dir, jint _zoom, jboolean threeD, jboolean follow,
2188                jint _width, jint _height)
2189{
2190  static int started = 0, icons /* Not used */;
2191  LG width = _width;
2192  height = _height;
2193  Display3D = threeD;
2194  clon = Longitude (lon);
2195  clat = Latitude (lat);
2196  FollowGPSr = follow;
2197  sinAzimuth = sin (dir * (M_PI / 180.0));
2198  cosAzimuth = cos (dir * (M_PI / 180.0));
2199  zoom = _zoom;
2200  if (!started) {
2201    started = 1;
2202    jclass cls=env->GetObjectClass(thiz);
2203    //cls =(jclass)( env->NewGlobalRef(jcls) );
2204    //cls = env->FindClass(
2205    //            "org/osmu/gosmore/MyClb");
2206
2207    LG drawTeks = env->GetMethodID(cls, "drawTeks", "(Ljava/lang/String;IIFF)V");
2208    // env->ExceptionClear();
2209    /* if (drawTeksod != 0)*/
2210    // env->ExceptionClear();
2211  }
2212  glDisable(GL_BLEND);
2213  if (currentBbox[0] != '\0' && hashTable[bucketsMin1] < 500000 &&
2214      zoom < 100000000) {
2215    int nn = clat > -(1<<24) && clat < (1<<24) &&
2216             clon > -(1<<24) && clon < (1<<24);
2217    jstring js = env->NewStringUTF (nn ? "No location." : "No map.");
2218    env->CallVoidMethod (thiz, drawTeks, js, width / 2, height/3*2,
2219      (float)0, (float)1);
2220    js = env->NewStringUTF (nn ? "Please search or enable the GPS" :
2221      "Please update the map.");
2222    env->CallVoidMethod (thiz, drawTeks, js, width / 2, height/3*2 + 30,
2223      (float)0, (float)1);
2224  }
2225#elif defined(NOGTK)
2226int DrawExpose (HPEN *pen, HBRUSH *brush)
2227{
2228  width = draw->allocation.width;
2229  height = draw->allocation.height;
2230  WCHAR wcTmp[70];
2231
2232  iconsgc = mygc;
2233
2234
2235  SetTextColor (mygc, Background ? 0 : 0xffffff);
2236  if (objectAddRow >= 0) {
2237    SelectObject (mygc, sysFont);
2238    //SetBkMode (mygc, TRANSPARENT);
2239    SelectObject (mygc, GetStockObject (BLACK_PEN));
2240    for (int y = 0, i = objectAddRow; y < height;
2241              y += ADD_HEIGHT) {
2242      //gdk_draw_line (draw->window, mygc, 0, y, width, y);
2243      gdk_draw_line (draw->window, mygc,
2244        width - ButtonSize * 20,
2245        height * i / restriction_no_right_turn,
2246        width,
2247        height * i / restriction_no_right_turn);
2248      RECT klip;
2249      klip.bottom = y + ADD_HEIGHT;
2250      klip.top = y;
2251      for (int x = 0; x < width - ButtonSize * 20 -
2252          ADD_WIDTH && i < restriction_no_right_turn; x += ADD_WIDTH, i++) {
2253        int *icon = style[i].x + 4 * IconSet;
2254        gdk_draw_drawable (draw->window, mygc, icons, icon[0], icon[1],
2255          x - icon[2] / 2 + ADD_WIDTH / 2, y, icon[2], icon[3]);
2256        klip.left = x + 8;
2257        klip.right = x + ADD_WIDTH - 8;
2258        #ifndef _WIN32_WCE
2259        DrawString (x + 8, y + ADD_HEIGHT - 16, klasTable[i].desc);
2260        #else
2261        ExtTextOut (mygc, x + 8, y + ADD_HEIGHT - 16, ETO_CLIPPED,
2262          &klip, klasTable[i].desc, wcslen (klasTable[i].desc), NULL);
2263        #endif
2264      }
2265    }
2266    return FALSE;
2267  } // if displaying the klas / style / rule selection screen
2268#else
2269
2270void SetColour (GdkColor *c, int hexTrip)
2271{
2272  c->red =    (hexTrip >> 16)        * 0x101;
2273  c->green = ((hexTrip >> 8) & 0xff) * 0x101;
2274  c->blue =   (hexTrip       & 0xff) * 0x101;
2275  gdk_colormap_alloc_color (gdk_window_get_colormap (draw->window),
2276      c, FALSE, TRUE);
2277}
2278         
2279gint DrawExpose (void)
2280{
2281  static GdkColor styleColour[2 << STYLE_BITS][2];
2282  static GdkColor /*routeColour, validateColour,*/ resultArrowColour;
2283  height = draw->allocation.height;
2284  width = draw->allocation.width;
2285  if (!mygc || !iconsgc) {
2286    mygc = gdk_gc_new (draw->window);
2287    fg_gc = gdk_gc_new (draw->window);
2288    iconsgc = gdk_gc_new (draw->window);
2289    for (int i = 0; i < stylecount; i++) {
2290      for (int j = 0; j < 2; j++) {
2291        SetColour (&styleColour[i][j],
2292         !j ? style[i].areaColour 
2293          : style[i].lineColour != -1 ? style[i].lineColour
2294          : (style[i].areaColour >> 1) & 0xefefef); // Dark border for polys
2295      }
2296    }
2297    /*SetColour (&routeColour, 0x00ff00);
2298    SetColour (&validateColour, 0xff9999);*/
2299    SetColour (&resultArrowColour, 0);
2300    gdk_gc_set_fill (mygc, GDK_SOLID);
2301
2302    if (!icons) icons = gdk_pixmap_create_from_xpm (draw->window, &mask,
2303      NULL, FindResource ("icons.xpm"));
2304    maskGC = gdk_gc_new(mask);
2305  }
2306  static int oldBackground = -1;
2307  if (oldBackground != Background) {
2308    /*static const int bgVal[9] = { 0, 0xe0ffff, 0xd3d3d3, 0xe6e6fa,
2309      0xffffe0, 0xf5deb3, 0x7b68ee, 0x6b8e23, 0xffffff };
2310    GdkColor bg; */
2311    //SetColour (&bg, bgVal[
2312    gdk_window_set_background (draw->window, &styleColour[
2313              firstElemStyle + Background - (Background > 8 ? 8 : 0)][0]);
2314    oldBackground = Background;
2315  }
2316  GdkRectangle r =
2317    { 0, 0, width, height };
2318  gdk_window_begin_paint_rect (draw->window, &r);
2319
2320//  gdk_gc_set_foreground (mygc, &styleColour[0][0]);
2321//  gdk_gc_set_line_attributes (mygc,
2322//    1, GDK_LINE_SOLID, GDK_CAP_PROJECTING, GDK_JOIN_MITER);
2323   
2324  PangoMatrix mat = PANGO_MATRIX_INIT;
2325  pc = gdk_pango_context_get_for_screen (gdk_screen_get_default ());
2326  pl = pango_layout_new (pc);
2327  pango_layout_set_width (pl, -1); // No wrapping 200 * PANGO_SCALE);
2328  if (Background == 0) {
2329    PangoAttribute *wit = pango_attr_foreground_new (0xffff, 0xffff, 0xffff);
2330    PangoAttrList *list = pango_attr_list_new ();//pango_layout_get_attributes (pl);
2331    pango_attr_list_insert (list, wit);
2332    pango_layout_set_attributes (pl, list);
2333    pango_attr_list_unref (list);
2334  }
2335/*    PangoAttribute *wit = pango_attr_background_new (0xffff, 0xffff, 0xffff);
2336    PangoAttrList *list = pango_attr_list_new ();//pango_layout_get_attributes (pl);
2337    pango_attr_list_insert (list, wit);
2338    pango_layout_set_attributes (pl, list);
2339    pango_attr_list_unref (list); */
2340#endif // GTK
2341  LG if (option == mapMode) ChangePak (NULL, clon, clat);
2342  // This call can be almost anywhere, e.g. SetLocation(). Calling it in
2343  // searchMode with GeoSearch may invalidate some of the results.
2344
2345  if (ButtonSize <= 0) ButtonSize = 4;
2346
2347  if (zoom < 0 || zoom > 1023456789) zoom = 1023456789;
2348  if (zoom / width <= 1) zoom += 4000;
2349
2350  LG cosa = lrint (4294967296.0 * cosAzimuth * width / zoom);
2351  sina = lrint (4294967296.0 * sinAzimuth * width / zoom);
2352  xadj =
2353    width / 2 - ((clon * (__int64) cosa + clat * (__int64) sina) >> 32);
2354  yadj =
2355    height / 2 - ((clon * (__int64) sina - clat * (__int64) cosa) >> 32);
2356
2357  LG if (Display3D) {
2358    cosa = lrint (cosAzimuth * MUL);
2359    sina = lrint (sinAzimuth * MUL);
2360
2361    yadj = (clon + (int)(sinAzimuth * CAMERA2C)) * (myint) sina -
2362           (clat - (int)(cosAzimuth * CAMERA2C)) * (myint) cosa;
2363    xadj = -(clon + (int)(sinAzimuth * CAMERA2C)) * (myint) cosa -
2364            (clat - (int)(cosAzimuth * CAMERA2C)) * (myint) sina;
2365  }
2366
2367  if (option == mapMode) {
2368//    int perpixel = zoom / width;
2369    int *block = (int*) calloc ((width + 31) / 32 * 4, height);
2370
2371    stack<text2Brendered> text2B;
2372    text2B.push (text2Brendered ()); // Always have a spare one open
2373    vector<ndType*> area;
2374    stack<ndType*> dlist[12];
2375    // 5 under + 1 gound level + 5 above + icons
2376   
2377    if (ShowCompass) {
2378      for (int i = 0; i < 2; i++) {
2379        for (int m = -20; m <= 20; m += 40) {
2380          text2B.top ().s = m < 0 ? (i ? "N" : "W") : i ? "S" : "E";
2381          text2B.top ().x = width - 40 +
2382            lrint ((i ? -sinAzimuth : cosAzimuth) * m) - 50;
2383          text2B.top ().x2 = text2B.top ().x + 100;
2384          text2B.top ().dst = 100;
2385          text2B.top ().y2 = text2B.top ().y = height - 40 +
2386            lrint ((i ? cosAzimuth : sinAzimuth) * m);
2387          text2B.push (text2Brendered ());
2388        }
2389      }
2390    }
2391   
2392    // render map
2393    /* We need the smallest bbox that covers the test area. For 2D, the
2394       test area is a rectangle that is not aligned with the axis, so the
2395       bbox is the maxs and mins of the latitudes and longitudes of the 4
2396       corners. For 3D, the test area is a triangle, with the camera
2397       coordinate included twice, hence 4 tests
2398    */
2399    int latRadius[2] = { 0, 0 }, lonRadius[2] = { 0, 0 };
2400    for (int wc = -1; wc <= 1; wc += 2) { // width and
2401      for (int hc = -1; hc <= 1; hc += 2) { // height coefficients
2402        int w = !Display3D ? zoom : hc > 0 ? WIDE3D : 0, h = !Display3D
2403          ? zoom / width * height : hc > 0 ? FAR3D : CAMERA2C;
2404        int lon = lrint (w * cosAzimuth * wc - h * sinAzimuth * hc);
2405        int lat = lrint (h * cosAzimuth * hc + w * sinAzimuth * wc);
2406        lonRadius[0] = min (lonRadius[0], lon);
2407        lonRadius[1] = max (lonRadius[1], lon);
2408        latRadius[0] = min (latRadius[0], lat);
2409        latRadius[1] = max (latRadius[1], lat);
2410      }
2411    }
2412    LG OsmItr itr (clon + lonRadius[0] - 1000, clat + latRadius[0] - 1000,
2413                clon + lonRadius[1] + 1000, clat + latRadius[1] + 1000);
2414    // Widen this a bit so that we render nodes that are just a bit offscreen ?
2415    while (Next (itr)) {
2416      ndType *nd = itr.nd[0];
2417      wayType *w = Way (nd);
2418
2419      if (Style (w)->scaleMax < zoom / width * 350 / (DetailLevel + 6)
2420          && !Display3D && w->dlat < zoom / width * 20 &&
2421                           w->dlon < zoom / width * 20) continue;
2422      // With 3D, the icons are filtered only much later when we know z.
2423      if (nd->other[0] != 0) {
2424        nd = itr.nd[0] + itr.nd[0]->other[0];
2425        if (nd->lat == INT_MIN) nd = itr.nd[0]; // Node excluded from build
2426        else if (itr.left <= nd->lon && nd->lon < itr.right &&
2427            itr.top  <= nd->lat && nd->lat < itr.bottom) continue;
2428      } // Only process this way when the Itr gives us the first node, or
2429      // the first node that's inside the viewing area
2430      if (nd->other[0] == 0 && nd->other[1] == 0) dlist[11].push (nd);
2431      else if (Style (w)->areaColour != -1 ||
2432        StyleNr(w) == natural_coastline) area.push_back (nd);
2433      // I guess polygons that enter the bbox multiple times are drawn
2434      // multiple times.
2435      else dlist[Layer (w) + 5].push (nd);
2436    }
2437    LG qsort (&area[0], area.size (), sizeof (area[0]),
2438      (int (*)(const void *a, const void *b))WaySizeCmp);
2439    //for (; !dlist[0].empty (); dlist[0].pop ()) {
2440    //  ndType *nd = dlist[0].top ();
2441    vector<PolygonEdge> coast;
2442    vector<FixedPoint*> cpiece;
2443    for (; !area.empty(); area.pop_back ()) {
2444      ndType *nd = area.back ();
2445      wayType *w = Way (nd);
2446      if (StyleNr(w) != natural_coastline) {
2447        while (nd->other[0] != 0) nd += nd->other[0];
2448      }
2449      #if defined (_WIN32_CE) || defined (_WIN32)
2450      #define GdkPoint POINT
2451      #endif
2452      vector<FixedPoint> pt;
2453      int oldx = 0, oldy = 0, x = 0 /* Shut up gcc*/, y = 0 /*Shut up gcc*/;
2454      int firstx = INT_MIN, firsty = INT_MIN /* Shut up gcc */;
2455      for (; ; nd += nd->other[1]) {
2456        if (nd->lat != INT_MIN) {
2457          pt.push_back (FixedPoint ());
2458          pt.back ().x = x = X2 (nd->lon, nd->lat);
2459          pt.back ().y = y = Y2 (nd->lon, nd->lat);
2460          if (Display3D) {
2461            if (firstx == INT_MIN) {
2462              firstx = x;
2463              firsty = y;
2464            }
2465            if (y > 0 && oldy < 0) {
2466              pt.back ().x = x + (x - oldx) * __int64((1<<26) - y) / (y - oldy);
2467              pt.back ().y = 1<<26; // Insert modified instance of old point
2468              pt.push_back (FixedPoint ());
2469              pt.back ().x = x; // before current point.
2470              pt.back ().y = y;
2471            }
2472            else if (y < 0) {
2473              if (oldy < 0) pt.pop_back ();
2474              else {
2475                pt.back ().x = oldx + (oldx - x) * __int64((1<<26) - oldy) / (oldy - y);
2476                pt.back ().y = 1<<26;
2477              }
2478            }
2479            oldx = x;
2480            oldy = y;
2481          }
2482          //pt[pts].x = X (nd->lon, nd->lat);
2483          //pt[pts++].y = Y (nd->lon, nd->lat);
2484        }
2485        if (!nd->other[1]) break;
2486      }
2487     
2488      if (Display3D && y < 0 && firsty > 0) {
2489        pt.push_back (FixedPoint ());
2490        pt.back ().x = firstx + (firstx - x) * __int64((1<<26) - firsty) / (firsty - y);
2491        pt.back ().y = 1<<26;
2492      }
2493      if (Display3D && firsty < 0 && y > 0) {
2494        pt.push_back (FixedPoint ());
2495        pt.back ().x = x + (x - firstx) * __int64((1<<26) - y) / (y - firsty);
2496        pt.back ().y = 1<<26;
2497      }
2498      if (!pt.empty ()) {
2499        #if defined(ANDROID_NDK) || !defined(NOGTK)
2500        if (StyleNr(w) == natural_coastline) {
2501          cpiece.push_back ((FixedPoint*) malloc (pt.size() * sizeof (FixedPoint)));
2502          memcpy (cpiece.back(), &pt[0], pt.size() * sizeof (FixedPoint));
2503          AddClockwise (coast, cpiece.back(), pt.size());
2504        }
2505        else {
2506          vector<PolygonEdge> pe;
2507          AddPolygon (pe, &pt[0], pt.size ());
2508          #ifdef ANDROID_NDK
2509          #define glSColor(x) glColor4x ((x) >> 8, (x) & 0xffff, \
2510                              ((x) << 8)&0xffff, 0x10000)
2511          glSColor (Style(w)->areaColour);
2512          Fill (pe,FALSE); 
2513          #else
2514          gdk_gc_set_foreground (mygc, &styleColour[Style (w) - style][0]);
2515          Fill (pe, FALSE, draw->window, mygc);
2516          #endif
2517          /*if (pt.size () < 10) {
2518            vector<PolygonEdge> pe2;
2519            AddPolygon (pe2, &pt[0], pt.size ());
2520            Fill (pe2,draw->window,mygc);
2521          }*/
2522             /*if (!Fill (pe)) {
2523            __android_log_print (ANDROID_LOG_WARN, "Gosmore", "s %d", pt.size());
2524            for (int m = 0; m < pt.size (); m++)
2525              __android_log_print (ANDROID_LOG_WARN, "Gosmore", "%d %d", pt[m].x, pt[m].y);
2526          }*/
2527          // TODO: border
2528          //glSColor ((Style(w)->areaColour >> 1) & 0xefefef);
2529          //glLineWidth(1);
2530        #elif defined(NOGTK)
2531        if (StyleNr(w) != natural_coastline) {
2532          SelectObject (mygc, brush[StyleNr (w)]);
2533          SelectObject (mygc, pen[StyleNr (w)]);
2534          Polygon (mygc, &pt[0], pt.size ());
2535        #else
2536        if (StyleNr(w) != natural_coastline) {
2537          gdk_gc_set_foreground (mygc, &styleColour[Style (w) - style][0]);
2538          gdk_draw_polygon (draw->window, mygc, TRUE, &pt[0], pt.size ());
2539          gdk_gc_set_foreground (mygc, &styleColour[Style (w) - style][1]);
2540          gdk_gc_set_line_attributes (mygc, Style (w)->lineWidth,
2541            Style (w)->dashed ? GDK_LINE_ON_OFF_DASH
2542            : GDK_LINE_SOLID, GDK_CAP_PROJECTING, GDK_JOIN_MITER);
2543          gdk_draw_polygon (draw->window, mygc, FALSE, &pt[0], pt.size ());
2544        #endif
2545          // Text placement: The basic idea is here : http://alienryderflex.com/polygon_fill/
2546          text2B.top ().dst = strcspn ((char*)(w + 1) + 1, "\n") * 9;
2547          text2B.top ().x = -1;
2548          for (unsigned i = 0; i < pt.size (); i++) {
2549            pt[i].x >>= 16;
2550            pt[i].y >>= 16;
2551          }
2552          for (unsigned i = 0; i < pt.size (); i++) {
2553            int iy = (pt[i].y + pt[i < pt.size () - 1 ? i + 1 : 0].y) / 2;
2554            // Look for a large horisontal space inside the poly at this y value
2555            vector<int> nx;
2556            for (unsigned j = 0, k = pt.size () - 1; j < pt.size (); j++) {
2557              if ((pt[j].y < iy && pt[k].y >= iy) || (pt[k].y < iy && pt[j].y >= iy)) {
2558                nx.push_back (pt[j].x + (pt[k].x - pt[j].x) * (iy - pt[j].y) /
2559                  (pt[k].y - pt[j].y));
2560              }
2561              k = j;
2562            }
2563            sort (nx.begin (), nx.end ());
2564            for (unsigned int j = 0; j < nx.size (); j += 2) {
2565              if (nx[j + 1] - nx[j] > text2B.top ().dst) {
2566                text2B.top ().x = nx[j];
2567                text2B.top ().x2 = nx[j + 1];
2568                text2B.top ().y = iy - 5;
2569                text2B.top ().dst = nx[j + 1] - nx[j];
2570              }
2571            }
2572          }
2573          if (text2B.top ().x >= 0) {
2574            text2B.top ().y2 = text2B.top ().y;
2575            text2B.top ().s = (char*)(w + 1) + 1;
2576            text2B.push (text2Brendered ());
2577          }
2578        } // If not a coastline
2579      } // Polygon not empty
2580    } // For each area
2581    #ifdef ANDROID_NDK
2582    glColor4x (0x8000, 0x8000, 0x10000, 0x10000);
2583    LG Fill (coast, TRUE);
2584    for (; cpiece.size () > 0; cpiece.pop_back()) free (cpiece.back());
2585    glLineWidth (2);
2586    #elif !defined (NOGTK)
2587    gdk_gc_set_foreground (mygc, &styleColour[natural_coastline][1]);
2588    Fill (coast, TRUE, draw->window,mygc);
2589    for (; cpiece.size () > 0; cpiece.pop_back()) free (cpiece.back());
2590    #endif
2591
2592    LG queue<linePtType> q;
2593    LG for (int l = 0; l < 12; l++) {
2594      #ifdef ANDROID_NDK
2595      glColor4x (0, 0, 0, 0x10000);
2596      #endif
2597      LG for (; !dlist[l].empty (); dlist[l].pop ()) {
2598        ndType *nd = dlist[l].top ();
2599        wayType *w = Way (nd);
2600
2601        int best = 30;
2602        int len = strcspn ((char *)(w + 1) + 1, "\n");
2603       
2604        // single-point node
2605        LG if (nd->other[0] == 0 && nd->other[1] == 0) {
2606          int x = X (nd->lon, nd->lat), y = Y (nd->lon, nd->lat);
2607          int *icon = Style (w)->x + 4 * IconSet, wd = icon[2], ht = icon[3];
2608          if ((!Display3D || y > Style (w)->scaleMax / 400) && !TestOrSet (
2609                      block, FALSE, x - wd / 2, y - ht / 2, 0, ht, wd, 0)) {
2610            TestOrSet (block, TRUE, x - wd / 2, y - ht / 2, 0, ht, wd, 0);
2611            LG DrawPoI (x, y, Style (w)->x + 4 * IconSet);
2612           
2613            #if 0 //def NOGTK
2614            SelectObject (mygc, sysFont);
2615            //SetBkMode (mygc, TRANSPARENT);
2616            const unsigned char *sStart = (const unsigned char *)(w + 1) + 1;
2617            UTF16 *tStart = (UTF16 *) wcTmp;
2618            if (ConvertUTF8toUTF16 (&sStart,  sStart + len, &tStart, tStart +
2619                  sizeof (wcTmp) / sizeof (wcTmp[0]), lenientConversion)
2620                == conversionOK) {
2621              ExtTextOutW (mygc, x - len * 3, y + icon[3] / 2, 0, NULL,
2622                  wcTmp, (wchar_t *) tStart - wcTmp, NULL);
2623            }
2624            #endif
2625            text2B.top ().x = x - 100;
2626            text2B.top ().x2 = x + 100;
2627            text2B.top ().dst = 200;
2628            text2B.top ().y2 = text2B.top ().y = y +
2629                               Style (w)->x[IconSet * 4 + 3] / 2;
2630            if (Sqr ((__int64) Style (w)->scaleMax / 2 /
2631                (zoom / width)) * DetailLevel > len * len * 100 &&
2632                len > 0) best = 0;
2633          }
2634        }
2635        // ways (including areas on WinMob : FIXME)
2636        else if (nd->other[1] != 0) {
2637          // perform validation (on non-areas)
2638          bool valid;
2639          LG if (ValidateMode && Style(w)->areaColour == -1) {
2640            valid = len > 0 && StyleNr (w) != highway_road;
2641            // most ways should have labels and they should not be
2642            // highway=road
2643           
2644            // valid = valid && ... (add more validation here)
2645
2646            // // LOG
2647            // logprintf("valid = (len > 0) = %d > 0 = %d (%s)\n",
2648            //      len,valid,(char *)(w + 1) + 1);
2649
2650          } else {
2651            valid = true; 
2652          }
2653          if (highlight != "") {
2654            for (char *ptr = (char *)(w + 1) + 1; valid && *ptr != '\0'; ) {
2655              if (strncmp (highlight.c_str (), ptr, strcspn (ptr, "\n"))
2656                  == 0) valid = false;
2657              while (*ptr != '\0' && *ptr++ != '\n') {}
2658            } // Should highlighting get its own pen ?
2659          }
2660          // two stages -> validate (if needed) then normal rendering
2661          ndType *orig = nd;
2662          LG for (int stage = ( valid ? 1 : 0);stage<2;stage++) {
2663            nd = orig;
2664            if (stage==0) {
2665            #ifdef ANDROID_NDK
2666                glColor4x (0x10000, 0x999A, 0x999A, 0x1000);
2667                glLineWidth(10);
2668            #elif !defined(NOGTK)
2669              gdk_gc_set_foreground (mygc,
2670                &styleColour[firstElemStyle + ValidateModeNum][1]); //&validateColour);
2671              gdk_gc_set_line_attributes (mygc, 10,
2672                       GDK_LINE_SOLID, GDK_CAP_PROJECTING, GDK_JOIN_MITER);
2673            #else
2674              SelectObject (mygc, pen[firstElemStyle + ValidateModeNum]);
2675                //pen[VALIDATE_PEN]);
2676            #endif
2677            }
2678            else if (stage == 1) {
2679              #ifdef ANDROID_NDK
2680              //glSColor (Style(w)->lineColour);
2681              //glLineWidth(Style(w)->lineWidth);
2682              SetCasingColor (Style(w)->lineColour >> 16,
2683                (Style(w)->lineColour>>8)&0xff, Style(w)->lineColour & 0xff);
2684              #elif !defined(NOGTK)
2685              gdk_gc_set_foreground (mygc, &styleColour[Style (w) - style][1]);
2686              gdk_gc_set_line_attributes (mygc, Style (w)->lineWidth,
2687                    Style (w)->dashed ? GDK_LINE_ON_OFF_DASH
2688                    : GDK_LINE_SOLID, GDK_CAP_PROJECTING, GDK_JOIN_MITER);
2689              #else
2690              SelectObject (mygc, pen[StyleNr (w)]);
2691              #endif
2692            }
2693            int oldx = X (nd->lon, nd->lat), oldy = Y (nd->lon, nd->lat);
2694            int cumulative = 0;
2695            LG q.push (linePtType (oldx, oldy, cumulative));
2696            MoveTo (oldx, oldy);
2697            do {
2698              ndType *next = nd + nd->other[1];
2699              LG if (next->lat == INT_MIN) break; // Node excluded from build
2700              LG int x = X (next->lon, next->lat), x2;
2701              int y = Y (next->lon, next->lat), y2;
2702//            printf ("%6.0lf %6.0lf - %6.0lf %6.0lf - %lf\n", x, y, oldx, oldy,
2703//              AdjDepth (next->lon, next->lat));
2704              if ((x <= width || oldx <= width) &&
2705                  (x >= 0 || oldx >= 0) && (y >= 0 || oldy >= 0) &&
2706                  (y <= height || oldy <= height)) {
2707//                printf ("%4d %4d - %4d %4d\n", x,y,oldx,oldy);
2708                /* If we're doing 3D and oldy is negative, it means the point
2709                   was behind the camera. Then we must draw an infinitely long
2710                   line from (x,y) with the same gradient as (x,y)-(oldx,oldy),
2711                   but away from (oldx,oldy). Or at least up to some y value
2712                   below the bottom of the screen. So we adjust oldx and oldy.
2713                   
2714                   When y is negative, we do something very similar. */
2715                if (!Display3D || y > 0) {
2716                  x2 = x;
2717                  y2 = y;
2718                  if (Display3D && oldy <= 0) {
2719                 /*   if (nx < 32760 && nx > -32760 &&
2720                      oldx < 32760 && oldx > -32760 &&
2721                      oldy < 32760 && oldy > -32760) */
2722                    oldx = x + (x - oldx) * (height + 10 - y) /
2723                      (y - oldy);
2724                    oldy = height + 10;
2725                    MoveTo (oldx, oldy);
2726                  }
2727                }
2728                else /*if (oldy > 0 which is true)*/ {
2729/*                  if (nx < 32760 && nx > -32760 &&
2730                    oldx < 32760 && oldx > -32760 &&
2731                    oldy < 32760 && oldy > -32760) */
2732                  x2 = oldx +
2733                    (oldx - x) * (height + 10 - oldy) / (oldy - y);
2734                  y2 = height + 10;
2735                }
2736                #ifdef ANDROID_NDK
2737                LineTo (x2, y2, Style(w)->lineWidth *
2738                  max (width * 1000 / zoom, 2));
2739                #else
2740                LG gdk_draw_line (draw->window, mygc, oldx, oldy, x2, y2);
2741                #endif
2742                // Draw3DLine
2743                LG if (oldx < 0 || oldx >= width ||
2744                    oldy < 0 || oldy >= height) {
2745                  cumulative += 9999; // Insert a break in the queue
2746                  LG q.push (linePtType (oldx, oldy, cumulative));
2747                  // TODO: Interpolate the segment to get a point that is
2748                  // closer to the screen. The same applies to the other push
2749                }
2750                LG cumulative += isqrt (Sqr (oldx - x2) + Sqr (oldy - y2));
2751                LG q.push (linePtType (x2, y2, cumulative));
2752                LG ConsiderText (&q, FALSE, len, &best, &text2B.top ());
2753              }
2754              else MoveTo (x, y);
2755              LG nd = next;
2756              oldx = x;
2757              oldy = y;
2758            } while (itr.left <= nd->lon && nd->lon < itr.right &&
2759                     itr.top  <= nd->lat && nd->lat < itr.bottom &&
2760                     nd->other[1] != 0);
2761            LG ConsiderText (&q, TRUE, len, &best, &text2B.top ());
2762          }
2763        } /* If it has one or more segments */
2764         
2765        LG if (best < 30) {
2766          text2B.top ().s = (char *)(w + 1) + 1;
2767          text2B.push (text2Brendered ());
2768        }
2769      } /* for each way / icon */
2770      FinishCasing();
2771    } // For each layer
2772  //  gdk_gc_set_foreground (draw->style->fg_gc[0], &highwayColour[rail]);
2773  //  gdk_gc_set_line_attributes (draw->style->fg_gc[0],
2774  //    1, GDK_LINE_SOLID, GDK_CAP_PROJECTING, GDK_JOIN_MITER);
2775
2776    // render route
2777    LG routeNodeType *rt;
2778    if (shortest && (rt = shortest->shortest)) {
2779      double len;
2780      int nodeCnt = 1, x = X (rt->nd->lon, rt->nd->lat);
2781      int y = Y (rt->nd->lon, rt->nd->lat);
2782      __int64 sumLat = rt->nd->lat;
2783      #ifdef ANDROID_NDK
2784      glSColor (style[firstElemStyle + StartRouteNum].lineColour);
2785      glLineWidth (6);
2786      #define CHARWIDTH 12
2787      #elif !defined(NOGTK)
2788      gdk_gc_set_foreground (mygc,
2789        &styleColour[firstElemStyle + StartRouteNum][1]); //routeColour);
2790      gdk_gc_set_line_attributes (mygc, 6,
2791        GDK_LINE_SOLID, GDK_CAP_PROJECTING, GDK_JOIN_MITER);
2792      #define CHARWIDTH 12
2793      #else
2794      SelectObject (mygc, pen[firstElemStyle + StartRouteNum]);
2795      #define CHARWIDTH 6
2796      #endif
2797      if (routeSuccess) Draw3DLine (X (flon, flat), Y (flon, flat), x, y);
2798     
2799      len = sqrt (Sqr ((double) (rt->nd->lat - flat)) +
2800        Sqr ((double) (rt->nd->lon - flon)));
2801      for (; rt->shortest; rt = rt->shortest) {
2802        int nx = X (rt->shortest->nd->lon, rt->shortest->nd->lat);
2803        int ny = Y (rt->shortest->nd->lon, rt->shortest->nd->lat);
2804        if ((nx >= 0 || x >= 0) && (nx < width || x < width) &&
2805            (ny >= 0 || y >= 0) && (ny < height || y < height)) {
2806          // Gdk looks only at the lower 16 bits ?
2807          Draw3DLine (x, y, nx, ny);
2808        }
2809        len += sqrt (Sqr ((double) (rt->nd->lat - rt->shortest->nd->lat)) +
2810          Sqr ((double) (rt->nd->lon - rt->shortest->nd->lon)));
2811        sumLat += rt->nd->lat;
2812        nodeCnt++;
2813        x = nx;
2814        y = ny;
2815      }
2816      Draw3DLine (x, y, X (tlon, tlat), Y (tlon, tlat));
2817      len += sqrt (Sqr ((double) (rt->nd->lat - tlat)) +
2818        Sqr ((double) (rt->nd->lon - tlon)));
2819      char distStr[13];
2820      sprintf (distStr, "%.3lf km", len * (20000 / 2147483648.0) *
2821        cos (LatInverse (sumLat / nodeCnt) * (M_PI / 180)));
2822      #ifdef ANDROID_NDK
2823      jstring js = env->NewStringUTF (distStr);
2824      env->CallVoidMethod (thiz, drawTeks, js, width -
2825        CHARWIDTH/2 * strlen (distStr), 40, (float)0, (float)1);
2826      #else
2827      DrawString (width - CHARWIDTH * strlen (distStr), 10, distStr);
2828      #endif
2829      #if 0 //ndef NOGTK
2830      gdk_draw_string (draw->window, f, fg_gc, //draw->style->fg_gc[0],
2831        width - 7 * strlen (distStr), 10, distStr);
2832      #else
2833      #endif
2834    }
2835    LG DrawPoI (X (flon, flat), Y (flon, flat), IconSet * 4 +
2836      style[firstElemStyle + StartRouteNum].x);
2837    LG DrawPoI (X (tlon, tlat), Y (tlon, tlat), IconSet * 4 +
2838      style[firstElemStyle + EndRouteNum].x);
2839    #ifndef NOGTK
2840    LG for (deque<wayPointStruct>::iterator w = wayPoint.begin ();
2841         w != wayPoint.end (); w++) {
2842      DrawPoI (X (w->lon, w->lat), Y (w->lon, w->lat),
2843        style[firstElemStyle + wayPointIconNum].x);
2844    }
2845   
2846    LG for (int i = 1; shortest && ShowActiveRouteNodes && i < routeHeapSize; i++) {
2847      gdk_draw_line (draw->window, mygc,
2848        X (routeHeap[i].r->nd->lon, routeHeap[i].r->nd->lat) - 2,
2849        Y (routeHeap[i].r->nd->lon, routeHeap[i].r->nd->lat),
2850        X (routeHeap[i].r->nd->lon, routeHeap[i].r->nd->lat) + 2,
2851        Y (routeHeap[i].r->nd->lon, routeHeap[i].r->nd->lat));
2852    }
2853    #elif defined (_WIN32)
2854    for (int j = 0; j <= newWayCnt; j++) {
2855      int x = X (newWays[j].coord[0][0], newWays[j].coord[0][1]);
2856      int y = Y (newWays[j].coord[0][0], newWays[j].coord[0][1]);
2857      if (newWays[j].cnt == 1) {
2858        int *icon = style[j < newWayCnt ? newWays[j].klas : place_village].x
2859          + 4 * IconSet;
2860        gdk_draw_drawable (draw->window, mygc, icons, icon[0], icon[1],
2861          x - icon[2] / 2, y - icon[3] / 2, icon[2], icon[3]);
2862      }
2863      else {
2864        SelectObject (mygc, pen[j < newWayCnt ? newWays[j].klas: 0]);
2865        MoveToEx (mygc, x, y, NULL);
2866        for (int i = 1; i < newWays[j].cnt; i++) {
2867          LineTo (mygc, X (newWays[j].coord[i][0], newWays[j].coord[i][1]),
2868                        Y (newWays[j].coord[i][0], newWays[j].coord[i][1]));
2869        }
2870      }
2871    }
2872    if (ShowTrace) {
2873      for (gpsNewStruct *ptr = gpsTrack; ptr < gpsNew; ptr++) {
2874        SetPixel (mygc, X (ptr->lon, ptr->lat), Y (ptr->lon, ptr->lat), 0);
2875      }
2876    }
2877    #endif
2878    LG for (text2B.pop (); !text2B.empty ();  text2B.pop ()) {
2879      #ifndef ANDROID_NDK
2880      if (pressTime != -1) continue; // Don't render text while dragging
2881      #endif
2882      text2Brendered *t = &text2B.top();
2883      #ifdef PANGO_VERSION
2884      PangoRectangle rect;
2885      #else
2886      struct { int width, height; } rect;
2887      struct { double xx, xy, yy, yx; } mat;
2888      #endif
2889      int x0 = (t->x + t->x2) / 2, y0 = (t->y + t->y2) / 2;
2890      mat.yy = mat.xx = fabs (double (t->x - t->x2)) / (double) t->dst;
2891      mat.xy = (t->y - t->y2) / (double)(t->x > t->x2 ? -t->dst : t->dst);
2892      mat.yx = -mat.xy;
2893
2894      double move = 0.6;
2895      for (const char *txt = t->s; *txt != '\0';) {
2896        #if PANGO_VERSION
2897        pango_context_set_matrix (pc, &mat);
2898        pango_layout_set_text (pl,
2899          string (txt, strcspn (txt, "\n")).c_str (), -1);
2900        pango_layout_get_pixel_extents (pl, &rect, NULL);
2901        #else
2902        rect.width = strcspn (txt, "\n") * 9;
2903        rect.height = 11;
2904        #endif
2905        y0 += lrint (mat.xx * (rect.height + 3) * move);
2906        x0 += lrint (mat.xy * (rect.height + 3) * move);
2907        move = 1.2;
2908        if (TestOrSet (block, FALSE, 
2909          lrint (x0 - rect.width * mat.xx / 2 - mat.xy * rect.height / 3),
2910          lrint (y0 - rect.width * mat.yx / 2 - mat.yy * rect.height / 3),
2911          lrint (mat.xy * (rect.height)), lrint (mat.xx * (rect.height)),
2912          lrint (mat.xx * (rect.width + 10)),
2913          lrint (mat.yx * (rect.width + 10)))) break;
2914        TestOrSet (block, TRUE, 
2915          lrint (x0 - rect.width * mat.xx / 2 - mat.xy * rect.height / 3),
2916          lrint (y0 - rect.width * mat.yx / 2 - mat.yy * rect.height / 3),
2917          lrint (mat.xy * (rect.height)), lrint (mat.xx * (rect.height)),
2918          lrint (mat.xx * (rect.width + 10)),
2919          lrint (mat.yx * (rect.width + 10)));
2920        #ifdef ANDROID_NDK
2921        jstring js = env->NewStringUTF (
2922                        string (txt, strcspn (txt, "\n")).c_str ());
2923        env->CallVoidMethod (thiz, drawTeks, js, x0, y0,
2924                        (float)mat.xy, (float)mat.xx);
2925
2926        #elif !defined(NOGTK)
2927        gdk_draw_layout (GDK_DRAWABLE (draw->window),
2928          fg_gc /*draw->style->fg_gc[0]*/,
2929          x0 - (rect.width * mat.xx + rect.height * fabs (mat.xy)) / 2,
2930          y0 - (rect.height * mat.yy + rect.width * fabs (mat.xy)) / 2, pl);
2931        #else
2932        double hoek = atan2 (t->y2 - t->y, t->x - t->x2);
2933        if (t->x2 < t->x) hoek += M_PI;
2934        logFont.lfEscapement = logFont.lfOrientation = 1800 + int ((1800 / M_PI) * hoek);
2935       
2936        HFONT customFont = CreateFontIndirect (&logFont);
2937        HGDIOBJ oldf = SelectObject (mygc, customFont);
2938        //SetBkMode (mygc, TRANSPARENT);
2939        const unsigned char *sStart = (const unsigned char *) txt;
2940        UTF16 *tStart = (UTF16 *) wcTmp;
2941        int len = strcspn (txt, "\n");
2942        if (ConvertUTF8toUTF16 (&sStart,  sStart + len, &tStart,
2943              tStart + sizeof (wcTmp) / sizeof (wcTmp[0]),  lenientConversion)
2944            == conversionOK) {
2945          ExtTextOutW (mygc,
2946                x0 - lrint (len * 4 * mat.xx + rect.height / 2 * mat.xy),
2947                y0 + lrint (len * 4 * mat.xy - rect.height / 2 * mat.xx), 
2948                0, NULL, wcTmp, (wchar_t *) tStart - wcTmp, NULL);
2949        }
2950        SelectObject (mygc, oldf);
2951        DeleteObject (customFont);
2952        #endif
2953        if (zoom / width > 20) break;
2954        while (*txt != '\0' && *txt++ != '\n') {}
2955      }
2956    }
2957    free (block);
2958    if (FollowGPSr && command[0] && command[0] == command[1] && command[0] == command[2]) {
2959      DrawPoI (width / 2, height / 6,
2960        style[firstElemStyle + command[0]].x + 8); // Always square.big
2961    }
2962  } // Not in the menu
2963  else if (option == searchMode) {
2964    #ifndef NOGTK
2965    gdk_gc_set_foreground (mygc, &resultArrowColour);
2966    gdk_gc_set_line_attributes (mygc, 1,
2967       GDK_LINE_SOLID, GDK_CAP_PROJECTING, GDK_JOIN_MITER);
2968    #elif defined (_WIN32)
2969    SelectObject (mygc, GetStockObject (BLACK_PEN));
2970    #endif
2971        DisplaySearchResults ();
2972    HandleKeyboard (FALSE, 0, 0);
2973  }
2974  else if (option == optionMode) {
2975    for (int i = 0; i < wayPointIconNum; i++) {
2976      if (CompactOptions) {
2977        char l1[20], *s = (char*) optionNameTable[English][i], *ptr = s + 1;
2978        while (!isspace (*ptr) && !isupper (*ptr) && *ptr) ptr++;
2979        sprintf (l1, "%.*s", ptr - s, s);
2980        DrawString (ListXY (i, FALSE) - strlen (l1) * 5, ListXY (i, TRUE) - 8, l1);
2981        DrawString (ListXY (i, FALSE) - strlen (ptr) * 5, ListXY (i, TRUE) + 8, ptr);
2982      }
2983      else {
2984        DrawPoI (ListXY (i, FALSE), ListXY (i, TRUE) - 5,
2985          style[firstElemStyle + i].x); // Always classic.big
2986        DrawString (ListXY (i, FALSE) - strlen (optionNameTable[English][i]) *
2987          5, ListXY (i, TRUE) + style[firstElemStyle + i].x[3] / 2 - 5,
2988          optionNameTable[English][i]);
2989      }
2990    }
2991  }
2992  else {
2993    char optStr[30];
2994    if (option == VehicleNum) {
2995      #define M(v) Vehicle == v ## R ? #v :
2996      sprintf (optStr, "%s : %s", optionNameTable[English][option],
2997        RESTRICTIONS NULL);
2998      #undef M
2999    }
3000    else sprintf (optStr, "%s : %d", optionNameTable[English][option],
3001    #define o(en,min,max) option == en ## Num ? en :
3002    OPTIONS
3003    #undef o
3004      0);
3005    DrawString (50, height / 2, optStr);
3006  }
3007  #ifndef NOGTK
3008  /* Buttons now on the top row
3009  gdk_draw_rectangle (draw->window, draw->style->bg_gc[0], TRUE,
3010    width - ButtonSize * 20, height - ButtonSize * 60,
3011    width, height);
3012  for (int i = 0; i < 3; i++) {
3013    gdk_draw_string (draw->window, f, draw->style->fg_gc[0],
3014      width - ButtonSize * 10 - 5, height + (f->ascent - f->descent)
3015      / 2 - ButtonSize * (20 * i + 10), i == 0 ? "O" : i == 1 ? "-" : "+");
3016  }
3017  */
3018  gdk_window_end_paint (draw->window);
3019  gdk_flush ();
3020  #else
3021  #ifdef WIN32
3022  int i = (Layout > (MenuKey == 0 || option != mapMode ? 0 : 1) ? 3 : 0);
3023  RECT r;
3024  r.left = width - ButtonSize * 20;
3025  r.top = height - ButtonSize * 20 * i;
3026  r.right = width;
3027  r.bottom = height;
3028  FillRect (mygc, &r, (HBRUSH) GetStockObject(LTGRAY_BRUSH));
3029  SelectObject (mygc, sysFont);
3030  //SetBkMode (mygc, TRANSPARENT);
3031  while (--i >= 0) {
3032    ExtTextOut (mygc, width - ButtonSize * 10 - 5, height - 5 -
3033        ButtonSize * (20 * i + 10), 0, NULL, i == 0 ? TEXT ("O") :
3034        i == 1 ? TEXT ("-") : TEXT ("+"), 1, NULL);
3035  }
3036  #endif
3037
3038  char coord[64];
3039  if (ShowCoordinates == 1) {
3040    if(GpsIdle==999)
3041      snprintf (coord, 63, "%9.5lf %10.5lf zoom=%d GPS OFF %s %sfollowing", LatInverse (clat), LonInverse (clon),zoom,routeSuccess?"Route":"No Route",FollowGPSr?"":"not ");
3042    else
3043      snprintf (coord, 63, "%9.5lf %10.5lf zoom=%d GPS idle %ds %s %sfollowing", LatInverse (clat), LonInverse (clon),zoom,GpsIdle,routeSuccess?"Route":"No Route",FollowGPSr?"":"not ");
3044    DrawString (0, 5, coord);
3045  }
3046  #ifdef _WIN32
3047  else if (ShowCoordinates == 2) {
3048    MEMORYSTATUS memStat;
3049    GlobalMemoryStatus (&memStat);
3050    sprintf (coord, "%9d", memStat.dwAvailPhys );
3051    DrawString (0, 5, coord);
3052    //ExtTextOut (mygc, 0, 10, 0, NULL, coord, 9, NULL);
3053  }
3054  #endif
3055  #endif
3056  #ifdef CAIRO_VERSION
3057//  cairo_destroy (cai);
3058  #endif
3059  #ifdef ANDROID_NDK
3060  return;
3061  #else
3062  return FALSE;
3063  #endif
3064}
3065
3066#ifndef NOGTK
3067gint Drag (GtkWidget * /*widget*/, GdkEventMotion *event, void * /*w_cur*/)
3068{
3069  if (event->state & GDK_BUTTON1_MASK) MouseEv
3070          (event->x, event->y, event->time, 1, FALSE);
3071  return TRUE;
3072}
3073
3074int Click (GtkWidget * /*widget*/, GdkEventButton *event, void * /*para*/)
3075{
3076  MouseEv (event->x, event->y, event->time, event->button, TRUE);
3077  return TRUE;
3078}
3079
3080GtkWidget *searchW;
3081
3082int ToggleSearchResults (void)
3083{
3084  option = option == searchMode ? mapMode : searchMode;
3085  highlight = string ();
3086  gtk_widget_queue_clear (draw);
3087  return FALSE;
3088}
3089
3090int IncrementalSearch (void)
3091{
3092  option = searchMode;
3093  GeoSearch ((char*) gtk_entry_get_text (GTK_ENTRY (searchW)));
3094  gtk_widget_queue_clear (draw);
3095  return FALSE;
3096}
3097
3098void HitGtkButton (GtkWidget * /*w*/, void *data)
3099{
3100  HitButton ((intptr_t)data);
3101  gtk_widget_queue_clear (draw);
3102}
3103
3104//------------------------------------------------------------------------
3105// Callbacks that are called with the user drops an icon and then binds it
3106#if 0
3107static gboolean DropOnDraw (GtkWidget *w, GdkDragContext *c, gint /*x*/,
3108  gint /* y */, guint time, gpointer)
3109{
3110  if (c->targets) {
3111    gtk_drag_get_data (w, c,
3112      GDK_POINTER_TO_ATOM (g_list_nth_data (c->targets, 0)), time);
3113  }
3114  return c->targets ? TRUE : FALSE;
3115}
3116#endif
3117
3118#endif // NOGTK
3119
3120#endif // HEADLESS
3121
3122inline void SerializeOptions (FILE *optFile, int r, const TCHAR *pakfile)
3123{
3124  LOG IconSet = 1;
3125  DetailLevel = 3;
3126  ButtonSize = 4;
3127  Background = 1;
3128  if (optFile) {
3129    #define o(en,min,max) Exit = r ? !fread (&en, sizeof (en), 1, optFile) \
3130                                   : !fwrite (&en, sizeof (en), 1, optFile);
3131    OPTIONS
3132    o (clat, 0, 0)
3133    o (clon, 0, 0)
3134    o (zoom, 0, 0)
3135    o (tlat, 0, 0)
3136    o (tlon, 0, 0)
3137    o (flat, 0, 0)
3138    o (flon, 0, 0)
3139    #undef o
3140    option = mapMode;
3141  }
3142  LOG if (r) ChangePak (pakfile, clon, clat); // This will set up Exit
3143  LOG if (Exit && r) ChangePak (NULL, clon, clat);
3144/*  char *tag = gosmData +
3145    *(int *)(ndBase + hashTable[bucketsMin1 + (bucketsMin1 >> 7) + 2]);
3146  while (*--tag) {}
3147  SetLocation (((wayType*)tag)[-1].clon, ((wayType*)tag)[-1].clat);
3148  zoom = ((wayType*)tag)[-1].dlat + ((wayType*)tag)[-1].dlon + (1 << 15); */
3149}
3150
3151#ifndef NOGTK
3152
3153//----------------------------- Load Control ------------------------------
3154// The idea is keep everything (pak file + temp data) in RAM and never
3155// refuse a request. Only when calculations indicate that resources are
3156// low will the oldest process return early with an incomplete (jump)
3157// route. A number of constants are hard coded.
3158#define MAX_INST 170
3159struct ldCtrlType {
3160  int free;
3161  struct {
3162    struct timeval start;
3163    volatile int maks; // Reads from this value should be atomic
3164  } inst[MAX_INST]; // A circular buffer
3165} *ld;
3166
3167void UpdateLdCtrl (int i, int calls, int li, int aliveCnt, int cpus)
3168{
3169  do {
3170  // From the newest process to the oldest
3171    i -= i == 0 ? 1 - MAX_INST : 1;
3172    if (ld->inst[i].start.tv_sec) {
3173      // Now we wind the clock back to the point where instance i started.
3174      // We make the calculation as if aliveCnt+1 equal instances were eating
3175      // memory from that point on. So we adjust calls for the amount of
3176      // memory the that wasn't consumed due to the later starting times of
3177      // the newer processes. A 2.2 Ghz Xeon takes roughly 12ms to complete
3178      // a call, but we make it 19ms to be safe.
3179      calls += ((ld->inst[li].start.tv_sec - ld->inst[i].start.tv_sec) *
3180        1000000 + ld->inst[li].start.tv_usec - ld->inst[i].start.tv_usec)
3181        / 19000 * aliveCnt;
3182      li = i; // li is oldest process still alive that was started after i.
3183      ld->inst[i].maks = aliveCnt > cpus / 2 ? 0 : calls / ++aliveCnt;
3184      //fprintf (stderr, "Setting maks for %d to %d\n", i, calls / aliveCnt);
3185    }
3186  } while (i != ld->free);
3187}
3188
3189int UserInterface (int argc, char *argv[], 
3190                   const char* pakfile, const char* stylefile) {
3191
3192  option = mapMode;
3193  IconSet = 1;
3194  DetailLevel = 2;
3195  FastestRoute = 1;
3196  Background = 1;
3197  Vehicle = motorcarR;
3198  char *h = getenv ("HOME");
3199  string optFname = string (h ? h : ".") + "/.gosmore.opt";
3200  FILE *optFile = fopen (optFname.c_str(), "r+");
3201  if (strcmp (pakfile, "nopak") == 0 && h) {
3202    string ddir (string (h) + "/.gosmore");
3203    mkdir (ddir.c_str (), 0755); // Usually already exists
3204    if (chdir (ddir.c_str ())) perror ("chdir(\".gosmore\")");
3205    SerializeOptions (optFile, TRUE, NULL);
3206  }
3207  else SerializeOptions (optFile, TRUE, pakfile);
3208  Keyboard = 1;
3209  if (Exit) {
3210    fprintf (stderr, "Cannot read %s\n"
3211             "You can (re)build it from\n"
3212             "the planet file e.g. " 
3213             "bzip2 -d planet-...osm.bz2 | %s rebuild\n",
3214             pakfile, argv[0]);
3215    #ifndef HEADLESS
3216    gtk_init (&argc, &argv);
3217    gtk_dialog_run (GTK_DIALOG (gtk_message_dialog_new (NULL,
3218      GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
3219      "Cannot read %s\nYou can (re)build it from\n"
3220      "the planet file e.g. bzip2 -d planet-...osm.bz2 | %s rebuild\n",
3221      pakfile, argv[0])));
3222    #endif
3223    return 8;
3224  }
3225  #if 0
3226  /* This code give an idea for the order of magnitude of the theorical
3227     limit of exhaustive search routing */
3228  __int64 total = 0;
3229  printf ("%d\n", hashTable[bucketsMin1 + 1]);
3230  for (ndType *nd = ndBase; nd < ndBase + hashTable[bucketsMin1 + 1]; nd++) {   
3231    if (nd->other[1]) total += lrint (sqrt (   
3232      Sqr ((__int64)(nd->lat - (nd + nd->other[1])->lat)) +   
3233      Sqr ((__int64)(nd->lon - (nd + nd->other[1])->lon))) *
3234      Style (Way (nd))->invSpeed[motorcarR]);
3235  }
3236  printf ("%lf\n", (double) total);
3237  exit (0);
3238  #endif
3239
3240  if (stylefile) {
3241#ifndef _WIN32
3242    GosmLoadAltStyle(stylefile,FindResource("icons.csv"));
3243#else
3244    fprintf(stderr, "Overiding style information is not currently supported"
3245            " in Windows.");
3246#endif
3247  }
3248
3249
3250  if (getenv ("QUERY_STRING")) {
3251    double x0, y0, x1, y1;
3252    char vehicle[20];
3253    sscanf (getenv ("QUERY_STRING"),
3254      "flat=%lf&flon=%lf&tlat=%lf&tlon=%lf&fast=%d&v=%19[a-z]",
3255      &y0, &x0, &y1, &x1, &FastestRoute, vehicle);
3256    flat = Latitude (y0);
3257    flon = Longitude (x0);
3258    tlat = Latitude (y1);
3259    tlon = Longitude (x1);
3260    #define M(v) if (strcmp (vehicle, #v) == 0) Vehicle = v ## R;
3261    RESTRICTIONS
3262    #undef M
3263    Route (TRUE, 0, 0, Vehicle, FastestRoute);
3264    #ifdef LD_CTRL
3265    struct timezone tz;
3266    FILE *f = fopen ("ld_ctrl", "r+");
3267    if (!f) {
3268      if ((f = fopen ("ld_ctrl", "w+")) == NULL) return 2;
3269      for (size_t i = 0; i < sizeof (*ld); i++) fputc (0, f);
3270      fflush (f);
3271    }
3272    if (!f || (char*)-1L == (char*) (ld = 
3273             (ldCtrlType *) mmap (NULL, sizeof (*ld), PROT_READ | PROT_WRITE,
3274                                  MAP_SHARED, fileno (f), 0))
3275        //|| lockf (F_LOCK, fileno (f), sizeof (*ld)) != 0) {
3276                || lockf (fileno (f), F_LOCK, sizeof (*ld)) != 0) {
3277      printf ("Content-Type: text/plain\n\r\n\rLd ctrl error%p %p %s\n\r",
3278        f, ld, strerror (errno));
3279      return 0;
3280    }
3281    int calls = (sysconf (_SC_PAGESIZE) / 4096 * sysconf (_SC_PHYS_PAGES)
3282      - 2200000) / 1500, myInst = ld->free;
3283    // Calculate how much memory the machine has left after assuming the pak
3284    // file(s) are in RAM. Then calculate how many times RouteLoop() can be
3285    // called across all instances under the assumption that each call will
3286    // consume roughly 6MB of RAM.
3287    //fprintf (stderr, "Starting instance %d\n", ld->free);
3288    gettimeofday (&ld->inst[myInst].start, &tz);
3289    if (++ld->free >= MAX_INST) ld->free = 0;
3290    UpdateLdCtrl (ld->free, calls, 0, 0, sysconf (_SC_NPROCESSORS_ONLN));
3291    //lockf (F_ULOCK, fileno (f), sizeof (*ld));
3292        lockf (fileno (f), F_ULOCK, sizeof (*ld));
3293    for (int i = 0; i < ld->inst[myInst].maks && RouteLoop (); i++) {}
3294    //lockf (F_LOCK, fileno (f), sizeof (*ld));
3295        lockf (fileno (f), F_LOCK, sizeof (*ld));
3296    ld->inst[myInst].start.tv_sec = 0; // Mark that we're done.
3297    UpdateLdCtrl (ld->free, calls, 0, 0, sysconf (_SC_NPROCESSORS_ONLN));
3298    //lockf (F_ULOCK, fileno (f), sizeof (*ld));
3299        lockf (fileno (f), F_ULOCK, sizeof (*ld));
3300    #else
3301    while (RouteLoop ()) {}
3302/*  It is reasonable to assume that there exists a short route in the actual
3303    road network between any two points on the that are closer than 1km to
3304    each. Therefore, once we come close to 'to', we should find a route
3305    quickly thereafter. If we don't it means 'to' is in a small, unlinked part
3306    of the network and trying to complete the route is wasting time.
3307   
3308    Well, I guess that in developing countries there are many sets of roads
3309    that run on opposite sides of rivers with no bridge or tunnel to connect
3310    them. So I'm commenting it out, because it's things like these that
3311    have bit me in the past.
3312    for (int i = 4000; i > 0 && RouteLoop (); i--) {
3313      if (flat - 100000 < shortest->nd->lat && shortest->nd->lat < flat + 100000 &&
3314          flon - 100000 < shortest->nd->lon && shortest->nd->lon < flon + 100000 &&
3315          i > 50) i = 50;
3316    } */
3317    #endif
3318    printf ("Content-Type: text/plain\n\r\n\r");
3319    if (!shortest) printf ("No route found\n\r");
3320    else {
3321      if (!routeSuccess) printf ("Jump\n\r");
3322      styleStruct *firstS = Style (Way (shortest->nd));
3323      double ups = firstS->invSpeed[Vehicle] / 3.6 
3324          * firstS->aveSpeed[Vehicle] / 20000000 * 2147483648.0 / 
3325          cos (LatInverse (flat / 2 + tlat / 2) * (M_PI / 180));
3326      // ups (Units per second) also works as an unsigned int.
3327      double fSegLat = shortest->shortest ?
3328        shortest->shortest->nd->lat - shortest->nd->lat : 1;
3329      double fSegLon = shortest->shortest ?
3330        shortest->shortest->nd->lon - shortest->nd->lon : 1;
3331      double fpr = (fSegLat * (flat - shortest->nd->lat) +
3332                    fSegLon * (flon - shortest->nd->lon)) /
3333                   (Sqr (fSegLat) + Sqr (fSegLon));
3334      fpr = fpr > 1 ? 1 : fpr < 0 ? 0 : fpr; // Clamp to [0,1]
3335      for (; shortest; shortest = shortest->shortest) {
3336        wayType *w = Way (shortest->nd);
3337        char *name = (char*)(w + 1) + 1;
3338        unsigned style= StyleNr(w);
3339        printf ("%lf,%lf,%c%s,%s,%.0lf,%.*s\n\r",
3340          LatInverse (shortest->nd->lat + fSegLat * fpr),
3341          LonInverse (shortest->nd->lon + fSegLon * fpr), JunctionType (shortest->nd),
3342          ((1 << roundaboutR) & (Way (shortest->nd))->bits) ? "r" : "",
3343          style < sizeof(klasTable)/sizeof(klasTable[0]) ? klasTable[style].desc :
3344          "(unknown-style)", ((shortest->heapIdx < 0
3345          ? -shortest->heapIdx : routeHeap[shortest->heapIdx].best) -
3346          shortest->remain) / ups,
3347          (int) strcspn (name, "\n"), name);
3348        fpr = 0;
3349        if (!shortest->shortest) {
3350          // I don't know why, but sometimes shortest->dir is wrong. But
3351          // AFAIK it only happens at the first or last segment of the way,
3352          // so inverting 'dir' solves the problem...
3353          ndType *final = shortest->nd + shortest->nd->other[
3354            (shortest->nd->other[shortest->dir] ? 0 : 1) ^ shortest->dir];
3355          double pr = ((final->lat - shortest->nd->lat) * (double)
3356            (tlat - shortest->nd->lat) + (final->lon - shortest->nd->lon) *
3357            (double)(tlon - shortest->nd->lon)) /
3358            (Sqr ((double)(final->lat - shortest->nd->lat)) +
3359             Sqr ((double)(final->lon - shortest->nd->lon)) + 1);
3360          pr = pr > 1 ? 1 : pr < 0 ? 0 : pr; // Clamp to [0,1]
3361          printf("%lf,%lf,j,(unknown-style),0,fini\n\r",
3362      LatInverse (shortest->nd->lat + pr * (final->lat - shortest->nd->lat)),
3363      LonInverse (shortest->nd->lon + pr * (final->lon - shortest->nd->lon)));
3364//      shortest->dir, shortest->nd->other[shortest->dir]);
3365//      shortest->nd->other[1-shortest->dir]);
3366      //final->lat - shortest->nd->lat, final->lon - shortest->nd->lon);
3367        }
3368      }
3369    }
3370    return 0;
3371  }
3372
3373  printf ("%s is provided as is and any warranties are disclaimed. \n",argv[0]);
3374  #ifndef HEADLESS
3375
3376  curl_global_init (CURL_GLOBAL_ALL);
3377  g_thread_init (NULL);  // Something to do with curl progress bar
3378  gtk_init (&argc, &argv);
3379#ifdef USE_GNOMESOUND
3380  gnome_sound_init ("localhost");
3381#endif
3382  draw = gtk_drawing_area_new ();
3383  gtk_widget_set_double_buffered (draw, FALSE);
3384  gtk_signal_connect (GTK_OBJECT (draw), "expose_event",
3385    (GtkSignalFunc) DrawExpose, NULL);
3386  gtk_signal_connect (GTK_OBJECT (draw), "button-release-event",
3387    (GtkSignalFunc) Click, NULL);
3388  gtk_signal_connect (GTK_OBJECT (draw), "motion_notify_event",
3389    (GtkSignalFunc) Drag, NULL);
3390  gtk_widget_set_events (draw, GDK_EXPOSURE_MASK | GDK_BUTTON_RELEASE_MASK |
3391    GDK_BUTTON_PRESS_MASK |  GDK_POINTER_MOTION_MASK);
3392  gtk_signal_connect (GTK_OBJECT (draw), "scroll_event",
3393                       (GtkSignalFunc) Scroll, NULL);
3394
3395  static GtkTargetEntry drawDndTargets[] = {
3396    { (gchar*) "text/uri-list", 0, 0 },
3397    { (gchar*) "image/png", 0, 1 /* Will autodetect type on drop */ },
3398    { (gchar*) "image/ico", 0, 1 },
3399  };
3400  gtk_drag_dest_set (draw, GTK_DEST_DEFAULT_ALL /* GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_HIGHLIGHT*/, drawDndTargets,
3401    G_N_ELEMENTS (drawDndTargets), GDK_ACTION_COPY);
3402                       
3403//  g_signal_connect (draw, "drag-data-received", G_CALLBACK (DropReceived), NULL);
3404//  g_signal_connect (draw, "drag-drop", G_CALLBACK (DropOnDraw), NULL);
3405//  ReadTsList ();
3406 
3407  GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
3408  gtk_signal_connect (GTK_OBJECT (window), "focus-in-event",
3409                       (GtkSignalFunc) UpdateWayPoints, NULL);
3410  /* The new layout will work better on smaller screens esp. touch screens by
3411  moving less used options to the menu and only displaying search results
3412  when they are required. It will also be more familiar to casual users
3413  because it will resemble a webbrowser */
3414  GtkWidget *hbox = gtk_hbox_new (FALSE, 3), *vbox = gtk_vbox_new (FALSE, 0);
3415  gtk_container_add (GTK_CONTAINER (window), vbox);
3416  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
3417
3418  GtkWidget *btn[3];
3419  for (int i = 0; i < 3; i++) {
3420    btn[i] = gtk_button_new_with_label (i == 0 ? "O" : i == 1 ? "-" : "+");
3421    gtk_widget_set_size_request (btn[i], 27, 20);
3422    gtk_box_pack_start (GTK_BOX (hbox), btn[i], FALSE, FALSE, 5);
3423    //gtk_widget_show (btn[i]);
3424    gtk_signal_connect (GTK_OBJECT (btn[i]), "clicked",
3425      GTK_SIGNAL_FUNC (HitGtkButton), (char*)i);
3426  } 
3427
3428  searchW = gtk_entry_new ();
3429  gtk_box_pack_start (GTK_BOX (hbox), searchW, FALSE, FALSE, 5);
3430  gtk_entry_set_text (GTK_ENTRY (searchW), "Search");
3431  gtk_signal_connect (GTK_OBJECT (searchW), "changed",
3432    GTK_SIGNAL_FUNC (IncrementalSearch), NULL);
3433  gtk_signal_connect (GTK_OBJECT (searchW), "button-press-event",
3434    GTK_SIGNAL_FUNC (ToggleSearchResults), NULL);
3435
3436  gtk_box_pack_start (GTK_BOX (vbox), draw, TRUE, TRUE, 0);
3437 
3438  location = gtk_entry_new ();
3439  gtk_box_pack_start (GTK_BOX (vbox), location, FALSE, FALSE, 5);
3440  gtk_signal_connect (GTK_OBJECT (location), "changed",
3441    GTK_SIGNAL_FUNC (ChangeLocation), NULL);
3442 
3443  display3D = gtk_toggle_button_new_with_label ("3D");
3444  gtk_box_pack_start (GTK_BOX (hbox), display3D, FALSE, FALSE, 5);
3445  gtk_signal_connect (GTK_OBJECT (display3D), "clicked",
3446    GTK_SIGNAL_FUNC (ChangeOption), NULL);
3447  //gtk_widget_show (display3D);
3448
3449  followGPSr = gtk_toggle_button_new_with_label ("Lock");
3450 
3451  #ifdef USE_GEOCLUE // Not used and never worked
3452  g_type_init ();
3453  GeoclueMaster *master = geoclue_master_get_default ();
3454  Gerror *error = NULL;
3455  GeoclueMasterClient *client =
3456    geoclue_master_create_client (master, NULL, &error);
3457  g_object_unref (master);
3458  if (client) {
3459//    if (!geoclue_master_client_set_requirements (client,
3460//      GEOCLUE_ACCURACY_LEVEL_LOCALITY, 0, TRUE, GEOCLUE_RESOURSE
3461    GeocluePosition *pos = geoclue_master_client_create_position (client, NULL);
3462    g_signal_connect (G_OBJECT (pos), "position-changed",
3463      G_CALLBACK (GeoclueUpdate), NULL);
3464    gtk_box_pack_start (GTK_BOX (hbox), followGPSr, FALSE, FALSE, 5);
3465    gtk_signal_connect (GTK_OBJECT (followGPSr), "clicked",
3466      GTK_SIGNAL_FUNC (ChangeOption), NULL);
3467  }
3468  #endif
3469 
3470  //#if !defined (_WIN32) && !defined (ROUTE_TEST)
3471  #if 0
3472  struct sockaddr_in sa;
3473  int gpsSock = socket (PF_INET, SOCK_STREAM, 0);
3474  sa.sin_family = AF_INET;
3475  sa.sin_port = htons (2947);
3476  sa.sin_addr.s_addr = htonl (0x7f000001); // (204<<24)|(17<<16)|(205<<8)|18;
3477  if (gpsSock != -1 &&
3478      connect (gpsSock, (struct sockaddr *)&sa, sizeof (sa)) == 0) {
3479    send (gpsSock, "R\n", 2, 0);
3480    gpsSockTag = gdk_input_add (/*gpsData->gps_fd*/ gpsSock, GDK_INPUT_READ,
3481      (GdkInputFunction) ReceiveNmea /*gps_poll*/, NULL);
3482  #endif
3483  #ifdef USE_GPSD
3484  gps_data_t *gpsData = gps_open ("127.0.0.1", "2947");
3485  if (gpsData) {
3486    gps_set_raw_hook (gpsData, GpsMove);
3487    #if GPSD_API_MAJOR_VERSION <= 3
3488    gps_query (gpsData, "w+x\n");
3489    #else
3490    gps_stream (gpsData, WATCH_ENABLE, NULL);
3491    #endif
3492    gpsSockTag = gdk_input_add (gpsData->gps_fd, GDK_INPUT_READ,
3493      (GdkInputFunction) gps_poll, gpsData);
3494   
3495    gtk_box_pack_start (GTK_BOX (hbox), followGPSr, FALSE, FALSE, 5);
3496    gtk_signal_connect (GTK_OBJECT (followGPSr), "clicked",
3497      GTK_SIGNAL_FUNC (ChangeOption), NULL);
3498    //gtk_widget_show (followGPSr);
3499  }
3500  #endif
3501
3502  GtkAdjustment *adj = (GtkAdjustment*) gtk_adjustment_new (0, 0, 100, 0, 0, 0);
3503  bar = gtk_progress_bar_new_with_adjustment (adj);
3504  gtk_container_add (GTK_CONTAINER (hbox), bar);
3505
3506  gtk_signal_connect (GTK_OBJECT (window), "delete_event",
3507    GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
3508 
3509  gtk_window_set_default_size (GTK_WINDOW (window), 550, 550);
3510//  gtk_widget_show (searchW);
3511//  gtk_widget_show (location);
3512//  gtk_widget_show (draw);
3513/*  gtk_widget_show (getDirs); */
3514//  gtk_widget_show (hbox);
3515//  gtk_widget_show (vbox);
3516  gtk_widget_show_all (window);
3517
3518/*  GtkWidget *styleEditor = gtk_window_new (GTK_WINDOW_TOPLEVEL);
3519  GtkWidget *seScroll = gtk_scrolled_window_new (NULL, NULL);
3520  //gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (seScroll), );
3521  gtk_container_add (GTK_CONTAINER (styleEditor), seScroll);
3522  #define TCOLS 6
3523  GtkWidget *seTable = gtk_table_new (100, TCOLS, FALSE);
3524  gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (seScroll), seTable);
3525  for (int i = 0; i < 100; i++) {
3526    GtkWidget *v[TCOLS];
3527    v[0] = gtk_label_new ("abcdefghijl" + i % 5);
3528    v[1] = gtk_label_new ("01234567" + i % 5);
3529//    GdkColor c = { 9999, 8888, 7777, 6666 };
3530    v[2] = gtk_color_button_new ();
3531    v[3] = gtk_font_button_new ();
3532    v[4] = gtk_spin_button_new_with_range (0, 50, 1);
3533    v[5] = gtk_toggle_button_new_with_label ("- -");
3534    const char *sName[] = { NULL, NULL, "color-set", "font-set", "value-changed", "clicked" };
3535    for (int j = 0; j < TCOLS; j++) {
3536    //gtk_clist_append (GTK_CLIST (seList), (gchar**) v);
3537      gtk_table_attach_defaults (GTK_TABLE (seTable), v[j], j, j + 1, i, i + 1);
3538      if (sName[j]) gtk_signal_connect (GTK_OBJECT (v[j]), sName[j],
3539        GTK_SIGNAL_FUNC (SeUpdate), (gpointer) (intptr_t) (i * 0x100 + j));
3540    }
3541  }
3542  gtk_widget_show_all (styleEditor); */
3543
3544  ChangeOption ();
3545  IncrementalSearch ();
3546  gtk_widget_grab_focus (searchW);
3547  gdk_threads_enter (); // Something to do with curl progress bar
3548  gtk_main ();
3549  gdk_threads_leave (); // Something to do with curl progress bar
3550  FlushGpx ();
3551  if (optFile) rewind (optFile);
3552  else optFile = fopen (optFname.c_str (), "w");
3553  SerializeOptions (optFile, FALSE, NULL);
3554 
3555  #endif // HEADLESS
3556  return 0;
3557}
3558
3559int main (int argc, char *argv[])
3560{ 
3561  int nextarg = 1;
3562  bool rebuild = false;
3563  const char* master = "";
3564  int bbox[4] = { INT_MIN, INT_MIN, 0x7fffffff, 0x7fffffff };
3565 
3566  setlocale (LC_NUMERIC, "C"); /* Ensure decimal sign is "." for NMEA parsing. */
3567 
3568  if (argc > 1 && stricmp (argv[1], "sortRelations") == 0) {
3569    return SortRelations ();
3570  }
3571  if (argc > 1 && stricmp(argv[1], "rebuild") == 0) {
3572    if (argc < 6 && argc > 4) {
3573      fprintf (stderr, 
3574               "Usage : %s [rebuild [bbox for 2 pass]] [pakfile [stylefile]]\n"
3575               "See http://wiki.openstreetmap.org/index.php/gosmore\n", 
3576               argv[0]);
3577      return 1;
3578    }
3579    rebuild=true;
3580    nextarg++;
3581    if (argc >= 6) {
3582      master = FindResource("master.pak");
3583      bbox[0] = Latitude (atof (argv[2]));
3584      bbox[1] = Longitude (atof (argv[3]));
3585      bbox[2] = Latitude (atof (argv[4]));
3586      bbox[3] = Longitude (atof (argv[5]));
3587      nextarg += 4;
3588    }
3589  }
3590 
3591  // check if a pakfile was specified on the command line
3592  const char* pakfile;
3593  const char* stylefile = NULL;
3594  if (argc > nextarg) {
3595    pakfile=argv[nextarg];
3596    nextarg++;
3597    if (argc > nextarg)  {
3598      stylefile=argv[nextarg];
3599    } else if (rebuild) { 
3600      stylefile=FindResource("elemstyles.xml");
3601    }
3602  } else {
3603    pakfile=FindResource("gosmore.pak");
3604    if (rebuild) {
3605      stylefile=FindResource("elemstyles.xml");
3606    }
3607  }
3608 
3609  if (rebuild) {
3610#ifndef _WIN32
3611    printf("Building %s using style %s...\n",pakfile,stylefile);
3612
3613    RebuildPak(pakfile, stylefile, FindResource("icons.csv"), master, bbox);
3614#else
3615    fprintf(stderr,"Pakfile rebuild is not currently supported in Windows.\n");
3616#endif
3617  }
3618
3619  return UserInterface (argc, argv, pakfile, stylefile);
3620
3621  // close the logfile if it has been opened. No. Rather let libc to it.
3622  //if (logFP(false)) fclose(logFP(false));
3623}
3624#elif _WIN32 // NOGTK / WIN32 and WINCE Native;
3625//-------------------------- WIN32 and WINCE Native ------------------
3626HANDLE port = INVALID_HANDLE_VALUE;
3627
3628HBITMAP bmp = NULL, bufBmp = NULL;
3629HDC iconsDc, bufDc;
3630HPEN pen[2 << STYLE_BITS];
3631HBRUSH brush[2 << STYLE_BITS];
3632UTF16 appendTmp[50];
3633
3634LRESULT CALLBACK MainWndProc(HWND hWnd,UINT message,
3635                                  WPARAM wParam,LPARAM lParam)
3636{
3637  PAINTSTRUCT ps;
3638  RECT rect;
3639  //static wchar_t msg[200] = TEXT("No coms");
3640  int topBar = Layout != 1 ? 30 : 0;
3641  static int updatePercent = 0;
3642
3643  switch(message) {
3644    #if 0
3645    case WM_HOTKEY:
3646      if (VK_TBACK == HIWORD(lParam) && (0 != (MOD_KEYUP & LOWORD(lParam)))) {
3647        PostQuitMessage (0);
3648      }
3649      break;
3650
3651    case WM_ACTIVATE:
3652      // Ensure that unwanted wince elements are hidden
3653      if (SHFullScreenPtr) {
3654        if (FullScreen) {
3655          (*SHFullScreenPtr)(mWnd, SHFS_HIDETASKBAR |
3656                             SHFS_HIDESTARTICON | SHFS_HIDESIPBUTTON);
3657        } else {
3658          (*SHFullScreenPtr)(mWnd, SHFS_HIDESIPBUTTON);
3659        }
3660      }
3661      break;
3662    #endif
3663 
3664    case WM_CREATE:
3665      LOG for (int i = 0; i < 3; i++) {
3666        buttons[i] = CreateWindow(TEXT ("BUTTON"), i == 0 ? TEXT ("O") :
3667                      i == 1 ? TEXT ("-") : TEXT ("+"), BS_PUSHBUTTON |
3668                      WS_CHILD | WS_VISIBLE | WS_TABSTOP,
3669                      0, 0, 0, 0, hWnd, (HMENU) (IDC_EDIT1 + 1 + i),
3670                      (HINSTANCE) GetWindowLong(hWnd, GWL_HINSTANCE), 
3671                      NULL);       // pointer not needed
3672      }
3673      LOG button3D = CreateWindow(TEXT ("BUTTON"), TEXT ("3D"), BS_CHECKBOX |
3674                    WS_CHILD | WS_VISIBLE | WS_TABSTOP,
3675                    0, 0, 0, 0, hWnd, (HMENU) (IDC_EDIT1 + 1 + 3),
3676                    (HINSTANCE) GetWindowLong(hWnd, GWL_HINSTANCE), 
3677                    NULL);       // pointer not needed
3678      LOG hwndEdit = CreateWindow(TEXT ("EDIT"),
3679                    NULL, WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT,
3680                    0, 0, 0, 0,  // set size in WM_SIZE message
3681                    hWnd, (HMENU) IDC_EDIT1/*ID_EDITCHILD*/,
3682                    (HINSTANCE) GetWindowLong(hWnd, GWL_HINSTANCE), 
3683                    NULL);       // pointer not needed
3684      if (Keyboard) SendMessage (hwndEdit, WM_SETTEXT, 0, (LPARAM) TEXT ("Search")); 
3685      //else SetClassLongPtr (hwndEdit, GCLP_HBRBACKGROUND, (LONG) GetStockObject (WHITE_BRUSH));
3686//      SendMessage (hwndEdit, EM_SETEVENTMASK, 0, ENM_UPDATE | ENM_SETFOCUS);
3687      break;
3688    case WM_SETFOCUS: 
3689      if (Keyboard) SetFocus(hwndEdit); 
3690      break;
3691    case WM_SIZE: 
3692      LOG draw->allocation.width = LOWORD (lParam);
3693      LOG draw->allocation.height = HIWORD (lParam) - topBar;
3694      if (Keyboard) MoveWindow (hwndEdit, Layout > 1 ? 8 : 140, topBar - 25,
3695        draw->allocation.width - (Layout > 1 ? 66 : 200), 20, TRUE);
3696      LOG MoveWindow(button3D, draw->allocation.width - 55,
3697        Layout != 1 ? 5 : -25, 50, 20, TRUE);
3698      for (int i = 0; i < 3; i++) { // Same as LBUTTON_UP. Put in function !!
3699        LOG MoveWindow (buttons[i], (2 * i + 1) * 70 / 3 - 15,
3700          Layout ? -25 : 5, 30, 20, TRUE);
3701      }
3702      LOG if (bufBmp) {
3703        DeleteObject (bufBmp);
3704        bufBmp = NULL;
3705      }
3706      LOG InvalidateRect (hWnd, NULL, FALSE);
3707      break;
3708    case WM_DESTROY:
3709      LOG PostQuitMessage(0);
3710      break;
3711    /*case WM_CTLCOLORSTATIC: // Tried to make hwndEdit a STATIC when !Keyboard
3712      SetBkMode ((HDC)wParam, TRANSPARENT);
3713      return (LONG) GetStockObject (WHITE_BRUSH); */
3714    case WM_PAINT:
3715      do { // Keep compiler happy.
3716        BeginPaint (hWnd, &ps);
3717      //GetClientRect (hWnd, &r);
3718      //SetTextColor(ps.hdc,(i==state)?RGB(0,128,0):RGB(0,0,0));
3719      //r.left = 50;
3720      // r.top = 50;
3721        if (bmp == NULL) {
3722          LOG bmp = LoadBitmap (hInst, MAKEINTRESOURCE (IDB_BITMAP1));
3723          LOG iconsDc = CreateCompatibleDC (ps.hdc);
3724          LOG SelectObject(iconsDc, bmp);
3725
3726          // get mask for iconsDc
3727          LOG bmp = LoadBitmap (hInst, MAKEINTRESOURCE (IDB_BITMAP2));
3728          LOG maskDc = CreateCompatibleDC (ps.hdc);
3729          LOG SelectObject(maskDc, bmp);
3730
3731          LOG bufDc = CreateCompatibleDC (ps.hdc); //bufDc //GetDC (hWnd));
3732          /*pen[ROUTE_PEN] = CreatePen (PS_SOLID, 6, 0x00ff00);
3733          pen[VALIDATE_PEN] = CreatePen (PS_SOLID, 10, 0x9999ff); */
3734          map<int,HPEN> pcache;
3735          map<int,HBRUSH> bcache;
3736          LOG for (int i = 0; i < stylecount; i++) {
3737            // replace line colour with area colour
3738            // if no line colour specified
3739            int c = style[i].lineColour != -1 ? style[i].lineColour
3740              : (style[i].areaColour & 0xfefefe) >> 1; 
3741            if (c != -1) {
3742              // logprintf ("PEN[%d] %d %x %d\n",i,style[i].dashed, c, style[i].lineWidth);
3743              int idx = (style[i].dashed ? 1 : 0) +
3744                (style[i].lineWidth & 0x3f) * 2 + ((c & 0xffffff) << 7);
3745              map<int,HPEN>::iterator f = pcache.find (idx);
3746              pen[i] = f != pcache.end() ? f->second :
3747                CreatePen (style[i].dashed ? PS_DASH : PS_SOLID,
3748                         max (1, style[i].lineWidth), (c >> 16) |
3749                         (c & 0xff00) |
3750                         ((c & 0xff) << 16));
3751              pcache[idx] = pen[i];
3752            }
3753            if ((c = style[i].areaColour) != -1) {
3754              // logprintf ("BR[%d] %x\n", i, c);
3755              map<int,HBRUSH>::iterator f = bcache.find (c);
3756              brush[i] = f != bcache.end () ? f->second :
3757                CreateSolidBrush ((c>>16) | (c&0xff00) | ((c&0xff) << 16));
3758              bcache[c] = brush[i];
3759            }
3760          }
3761          LOG sysFont = (HFONT) GetStockObject (SYSTEM_FONT);
3762          LOG GetObject (sysFont, sizeof (logFont), &logFont);
3763          #ifndef _WIN32_WCE
3764          logFont.lfWeight = 400;
3765          strcpy (logFont.lfFaceName, TEXT ("Arial"));
3766          /*#else
3767          logFont.lfWeight = 400; // TODO ******** Testing WM6
3768          wcscpy (logFont.lfFaceName, TEXT ("Arial")); */
3769          #endif
3770          LOG SetBkMode (bufDc, TRANSPARENT); // Is this really necessary ?
3771        }
3772        rect.top = rect.left = 0;
3773        rect.right = draw->allocation.width;
3774        rect.bottom = draw->allocation.height;
3775        if (bufBmp == NULL) { // i.e. after WM_SIZE
3776          LOG bufBmp = CreateCompatibleBitmap (ps.hdc, draw->allocation.width,
3777            draw->allocation.height);
3778          LOG SelectObject (bufDc, bufBmp);
3779          LOG FillRect (bufDc, &rect, (HBRUSH) GetStockObject(WHITE_BRUSH));
3780        }
3781        mygc = bufDc;
3782        icons = iconsDc;
3783        if (option == BackgroundNum) {
3784         FillRect (bufDc, &rect,
3785           brush[firstElemStyle + Background - (Background > 8 ? 8 : 0)]);
3786        }
3787        DrawExpose (pen, brush);
3788       
3789        BitBlt (ps.hdc, 0, topBar, rect.right,  rect.bottom, bufDc, 0, 0, SRCCOPY);
3790        if (updatePercent) {
3791          MoveToEx (ps.hdc, 0, topBar, NULL);
3792          LineTo (ps.hdc, updatePercent * draw->allocation.width / 1000, topBar);
3793        }
3794      //SetBkColor(ps.hdc,RGB(63,63,63));
3795        FillRect (bufDc, &rect, brush[firstElemStyle + Background - (Background > 8 ? 8 : 0)]);
3796         //(HBRUSH) GetStockObject(WHITE_BRUSH));
3797        rect.bottom = topBar;
3798        FillRect (ps.hdc, &rect, (HBRUSH) GetStockObject(WHITE_BRUSH));
3799        if (!Keyboard) {
3800          UTF16 wcTmp[70], *tStart = (UTF16 *) wcTmp;
3801          const unsigned char *sStart = (const unsigned char*) searchStr.c_str ();
3802          if (ConvertUTF8toUTF16 (&sStart, sStart + searchStr.length (),
3803                &tStart, tStart + sizeof (wcTmp) / sizeof (wcTmp[0]), lenientConversion)
3804              == conversionOK) {
3805            //SendMessage (hwndEdit, WM_SETTEXT, 0, (LPARAM) (wchar_t*) wcTmp);
3806            ExtTextOutW (ps.hdc, Layout > 1 ? 8 : 140, topBar - 25, 0, NULL,
3807              (wchar_t*) wcTmp, tStart - wcTmp, NULL);
3808          }
3809        }
3810//      HPEN pen = CreatePen (a[c2].lineDashed ? PS_DASH : PS_SOLID,
3811        EndPaint (hWnd, &ps);
3812      } while (0);
3813      break;
3814    case WM_CHAR:
3815
3816      break;
3817    case WM_KEYDOWN:
3818      // The TGPS 375 can generate 12 keys :
3819      // VK_RETURN, VK_UP, VK_DOWN, VK_LEFT, VK_RIGHT,
3820      // 193=0xC1=Zoom in, 194=0xC2=Zoom out, 198=0xC6=menu 197=0xC5=settings
3821      // 195=0xC3=V+, 196=0xC4=V- which is VK_APP1 to VK_APP6
3822      // and WM_CHAR:VK_BACK
3823      break;
3824    case WM_USER + 1:
3825      /*
3826      wsprintf (msg, TEXT ("%c%c %c%c %9.5lf %10.5lf %lf %lf"),
3827        gpsNew.fix.date[0], gpsNew.fix.date[1],
3828        gpsNew.fix.tm[4], gpsNew.fix.tm[5],
3829        gpsNew.fix.latitude, gpsNew.fix.longitude, gpsNew.fix.ele,
3830        gpsNew.fix.hdop); */
3831      DoFollowThing ((gpsNewStruct*)lParam);
3832      if (FollowGPSr) InvalidateRect (hWnd, NULL, FALSE);
3833      break;
3834    case WM_USER + 2:
3835       do {
3836         HDC wdc = GetDC (hWnd);
3837         updatePercent = lParam;
3838         MoveToEx (wdc, 0, topBar, NULL);
3839         LineTo (wdc, updatePercent * draw->allocation.width / 1000, topBar);
3840         ReleaseDC (hWnd, wdc);
3841       } while (0);
3842       break;
3843    case WM_LBUTTONDOWN:
3844      //pressTime = GetTickCount ();
3845      SetCapture (hWnd);
3846      break;
3847    case WM_LBUTTONUP:
3848      ReleaseCapture ();
3849      if (gDisplayOff) {
3850        CeEnableBacklight(TRUE);
3851        gDisplayOff = FALSE;
3852        break;
3853      }
3854      if (1) {
3855        MouseEv ((short) LOWORD (lParam), (short) HIWORD (lParam) - topBar,
3856          GetTickCount (), 1, TRUE);
3857        if (option == LayoutNum) {
3858          if (Keyboard) MoveWindow(hwndEdit, Layout > 1 ? 8 : 140,
3859            Layout != 1 ? 5 : -25,
3860            draw->allocation.width - (Layout > 1 ? 66 : 200), 20, TRUE);
3861          MoveWindow(button3D, draw->allocation.width - 55,
3862            Layout != 1 ? 5 : -25, 50, 20, TRUE);
3863          for (int i = 0; i < 3; i++) { // Same as WM_SIZE. Put in function !!
3864            MoveWindow (buttons[i], (2 * i + 1) * 70 / 3 - 15,
3865              Layout ? -25 : 5, 30, 20, TRUE);
3866          }
3867        }
3868        if (Keyboard && option != searchMode) SipShowIM (SIPF_OFF);
3869      }
3870      else if (!Keyboard && LOWORD (lParam) > (Layout > 1 ? 8 : 140)) {
3871        option = option == searchMode ? mapMode : searchMode;
3872      }
3873      InvalidateRect (hWnd, NULL, FALSE);
3874      break;
3875    case WM_MOUSEMOVE:
3876      if (wParam & MK_LBUTTON) {
3877        MouseEv ((short) LOWORD (lParam), (short) HIWORD (lParam) - topBar,
3878                 GetTickCount (), 1, FALSE);
3879        InvalidateRect (hWnd, NULL, FALSE);
3880      }
3881/*      if (wParam & MK_LBUTTON) {
3882        if (firstDrag[0] >= 0) {
3883          HDC wdc = GetDC (hWnd);
3884          int wadj = lastDrag[0] - LOWORD (lParam);
3885          int hadj = lastDrag[1] - HIWORD (lParam) + topBar;
3886          BitBlt (wdc, wadj < 0 ? -wadj : 0, (hadj < 0 ? -hadj : 0) + topBar,
3887            draw->allocation.width - (wadj < 0 ? -wadj : wadj),
3888            draw->allocation.height + topBar - (hadj < 0 ? -hadj : hadj),
3889            wdc, wadj > 0 ? wadj : 0, (hadj > 0 ? hadj : 0) + topBar, SRCCOPY);
3890          ReleaseDC (hWnd, wdc);
3891        }
3892        lastDrag[0] = LOWORD (lParam);
3893        lastDrag[1] = HIWORD (lParam) - topBar;
3894        if (firstDrag[0] < 0) memcpy (firstDrag, lastDrag, sizeof (firstDrag));
3895      }*/
3896      break;
3897    case WM_MOUSEWHEEL:
3898      do {
3899        GdkEventScroll ev;
3900        POINT p;
3901        p.x = GET_X_LPARAM (lParam);
3902        p.y = GET_Y_LPARAM (lParam);
3903        ScreenToClient (hWnd, &p);
3904        ev.x = p.x;
3905        ev.y = p.y - topBar;
3906       
3907        ev.direction = GET_WHEEL_DELTA_WPARAM (wParam) > 0
3908          ? GDK_SCROLL_UP : GDK_SCROLL_DOWN;
3909        Scroll (NULL, &ev, NULL);
3910        InvalidateRect (hWnd, NULL, FALSE);
3911      } while (0);
3912      break;
3913    case WM_COMMAND:
3914      if (HIWORD (wParam) == BN_CLICKED &&
3915          LOWORD (wParam) > IDC_EDIT1 && LOWORD (wParam) <= IDC_EDIT1 + 3) {
3916        HitButton (LOWORD (wParam) - IDC_EDIT1 - 1);
3917        if (Keyboard && optionMode != searchMode) SipShowIM (SIPF_OFF);
3918        InvalidateRect (hWnd, NULL, FALSE);
3919      }
3920      if (HIWORD (wParam) == BN_CLICKED && LOWORD (wParam) == IDC_EDIT1 + 4) {
3921        Display3D ^= 1;
3922        Button_SetCheck (button3D, Display3D ? BST_CHECKED : BST_UNCHECKED);
3923        InvalidateRect (hWnd, NULL, FALSE);
3924      }
3925      if (HIWORD (wParam) == EN_UPDATE && LOWORD (wParam) == IDC_EDIT1) {
3926        char editStr[50];
3927
3928        memset (appendTmp, 0, sizeof (appendTmp));
3929        #ifndef _WIN32_WCE
3930        Edit_GetLine (hwndEdit, 0, editStr, sizeof (editStr));
3931        if (1) {
3932        #else
3933        int wstrlen = Edit_GetLine (hwndEdit, 0, appendTmp, sizeof (appendTmp));
3934        unsigned char *tStart = (unsigned char*) editStr;
3935        const UTF16 *sStart = (const UTF16 *) appendTmp;
3936        if (ConvertUTF16toUTF8 (&sStart,  sStart + wstrlen,
3937              &tStart, tStart + sizeof (gosmSstr), lenientConversion)
3938            == conversionOK) {
3939          *tStart = '\0';
3940          /* SipShowIM (SIPF_ON); The only way we can get here without the
3941          IM showing is if the device has a hardware keyboard/keypak */
3942        #endif
3943          option = searchMode;
3944          GeoSearch (editStr);
3945          InvalidateRect (hWnd, NULL, FALSE);
3946        }
3947     }
3948         break;
3949    default:
3950      return DefWindowProc (hWnd, message, wParam, lParam);
3951  }
3952  if (Exit) PostMessage (hWnd, WM_CLOSE, 0, 0);
3953  return FALSE;
3954}
3955
3956BOOL InitApplication (void)
3957{
3958  WNDCLASS wc;
3959
3960  wc.style=0;
3961  wc.lpfnWndProc=(WNDPROC)MainWndProc;
3962  wc.cbClsExtra=0;
3963  wc.cbWndExtra=0;
3964  wc.hInstance= hInst;
3965  wc.hIcon=LoadIcon(hInst, MAKEINTRESOURCE(ID_MAINICON)); 
3966  wc.hCursor=LoadCursor(NULL,IDC_ARROW);
3967  wc.hbrBackground=(HBRUSH) GetStockObject(WHITE_BRUSH);
3968  wc.lpszMenuName = NULL;
3969  wc.lpszClassName = TEXT ("GosmoreWClass");
3970
3971  return(RegisterClass(&wc));
3972}
3973
3974HWND InitInstance(int nCmdShow)
3975{
3976  HWND prev;
3977  // check if gosmore is already running
3978  prev = FindWindow(TEXT ("GosmoreWClass"), NULL);
3979  if (prev != NULL) {
3980    ShowWindow(prev, SW_RESTORE);
3981    SetForegroundWindow(prev);
3982    return FALSE;
3983  } else {
3984   
3985    mWnd = CreateWindow (TEXT ("GosmoreWClass"), TEXT ("gosmore"), 
3986    #ifdef _WIN32_WCE
3987    WS_DLGFRAME,
3988    #else
3989    WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
3990    #endif
3991                         CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 
3992                         CW_USEDEFAULT,NULL,NULL, hInst,NULL);
3993   
3994    if(!mWnd) return(FALSE);
3995   
3996    ShowWindow (mWnd,nCmdShow);
3997    //UpdateWindow (mWnd);
3998   
3999   
4000    return mWnd;
4001  }
4002}
4003
4004volatile int guiDone = FALSE;
4005
4006DWORD WINAPI NmeaReader (LPVOID lParam)
4007{
4008  static int lastgps=0;
4009  // loop back here if existing connection fails
4010  while (!guiDone) {
4011  #ifndef _WIN32_WCE
4012    Sleep (1000);
4013  #else
4014    // $GPGLL,2546.6752,S,02817.5780,E,210130.812,V,S*5B
4015    DWORD nBytes, got = 0;
4016    COMMTIMEOUTS commTiming;
4017    char rx[300];
4018   
4019    wchar_t portname[6];
4020    wsprintf (portname, TEXT ("COM%d:"), CommPort);
4021
4022    logprintf ("Attempting first connect to CommPort.\n");
4023
4024    // Attempt to reconnect to NMEA device every 1 second until connected
4025    while (!guiDone &&
4026           (port=CreateFile (portname, GENERIC_READ | GENERIC_WRITE, 0,
4027                 NULL, OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE) {
4028      Sleep(1000);
4029      //logprintf("Retrying connect to CommPort\n");
4030    }
4031
4032    if (port != INVALID_HANDLE_VALUE) {
4033
4034      logprintf("Connected to CommPort\n");
4035         
4036#if 1
4037      GetCommTimeouts (port, &commTiming);
4038      commTiming.ReadIntervalTimeout = 20;
4039      commTiming.ReadTotalTimeoutMultiplier = 0;
4040      commTiming.ReadTotalTimeoutConstant = 200; /* Bailout when nothing on the port */
4041     
4042      commTiming.WriteTotalTimeoutMultiplier=5; /* No writing */
4043      commTiming.WriteTotalTimeoutConstant=5;
4044      SetCommTimeouts (port, &commTiming);
4045#endif
4046      if (BaudRate) {
4047        DCB portState;
4048        if(!GetCommState(port, &portState)) {
4049          MessageBox (NULL, TEXT ("GetCommState Error"), TEXT (""),
4050                      MB_APPLMODAL|MB_OK);
4051          return(1);
4052        }
4053        portState.BaudRate = BaudRate;
4054        //portState.Parity=0;
4055        //portState.StopBits=ONESTOPBIT;
4056        //portState.ByteSize=8;
4057        //portState.fBinary=1;
4058        //portState.fParity=0;
4059        //portState.fOutxCtsFlow=0;
4060        //portState.fOutxDsrFlow=0;
4061        //portState.fDtrControl=DTR_CONTROL_ENABLE;
4062        //portState.fDsrSensitivity=0;
4063        //portState.fTXContinueOnXoff=1;
4064        //portState.fOutX=0;
4065        //portState.fInX=0;
4066        //portState.fErrorChar=0;
4067        //portState.fNull=0;
4068        //portState.fRtsControl=RTS_CONTROL_ENABLE;
4069        //portState.fAbortOnError=1;
4070       
4071        if(!SetCommState(port, &portState)) {
4072          MessageBox (NULL, TEXT ("SetCommState Error"), TEXT (""),
4073                      MB_APPLMODAL|MB_OK);
4074          return(1);
4075        }
4076      }
4077     
4078      /* Idea for Windows Mobile 5
4079         #include <gpsapi.h>
4080         if (WM5) {
4081         GPS_POSITION pos;
4082         HANDLE hand = GPSOpenDevice (NULL, NULL, NULL, 0);
4083         while (!guiDone && hand != NULL) {
4084         if (GPSGetPosition (hand, &pos, 500, 0) == ERROR_SUCCESS &&
4085         (pos.dwValidFields & GPS_VALID_LATITUDE)) {
4086         Sleep (800);
4087         pos.dblLatitude, pos.dblLongitude;
4088         }
4089         else Sleep (100);
4090         }
4091         if (hand) GPSCloseDevice (hand);
4092         } */
4093     
4094#if 0
4095      PurgeComm (port, PURGE_RXCLEAR); /* Baud rate wouldn't change without this ! */
4096      DWORD nBytes2 = 0;
4097      COMSTAT cStat;
4098      ClearCommError (port, &nBytes, &cStat);
4099      rx2 = (char*) malloc (600);
4100      ReadFile(port, rx, sizeof(rx), &nBytes, NULL);
4101      if(!GetCommState(port, &portState)) {
4102        MessageBox (NULL, TEXT ("GetCommState Error"), TEXT (""),
4103                    MB_APPLMODAL|MB_OK);
4104        return(1);
4105      }
4106      ReadFile(port, rx2, 600, &nBytes2, NULL);
4107#endif
4108     
4109      //char logName[80];
4110      //sprintf (logName, "%slog.nmea", docPrefix);
4111      //FILE *log = fopen (logName, "wb");
4112
4113      // keep reading nmea until guiDone or serial port fails
4114      bool status;
4115      while (!guiDone &&
4116             (status = ReadFile(port, rx + got, 
4117                                     sizeof(rx) - got, &nBytes, NULL))) {
4118        //      logprintf ("status = %d, nBytes = %d\n", status, nBytes);
4119        if (nBytes > 0) {
4120          got += nBytes;
4121          //if (log) fwrite (rx, nBytes, 1, log);
4122         
4123          //wndStr[0]='\0';
4124          //FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
4125          //MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),wndStr,STRLEN,NULL);
4126         
4127          if (ProcessNmea (rx, (unsigned*)&got)) {
4128            int now;
4129            now=GetTickCount();
4130            if(!lastgps) lastgps=now;
4131            GpsIdle=(now-lastgps)/1000;
4132            lastgps=now;
4133            PostMessage (mWnd, WM_USER + 1, 0, (int) /* intptr_t */ gpsNew);
4134          }
4135        } // if nBytes > 0
4136      } // while ReadFile(...)
4137      if (!guiDone) {
4138        logprintf("Connection to CommPort failed.\n");
4139      }
4140    } // if port != INVALID_FILE_HANDLE
4141  #endif
4142  } // while !guiDone
4143  guiDone = FALSE;
4144  GpsIdle=999; /* Let user know GPS is off */
4145  //if (log) fclose (log);
4146  CloseHandle (port);
4147  return 0;
4148}
4149
4150
4151void XmlOut (FILE *newWayFile, const char *k, const char *v)
4152{
4153  if (*v != '\0') {
4154    fprintf (newWayFile, "  <tag k='%s' v='", k);
4155    for (; *v != '\0'; v++) {
4156      if (*v == '\'') fprintf (newWayFile, "&apos;");
4157      else if (*v == '&') fprintf (newWayFile, "&amp;");
4158      else fputc (*v, newWayFile);
4159    }
4160    fprintf (newWayFile, "' />\n");
4161  }
4162}
4163
4164
4165extern "C" {
4166int WINAPI WinMain(
4167    HINSTANCE  hInstance,         // handle of current instance
4168    HINSTANCE  hPrevInstance,     // handle of previous instance
4169    #ifdef _WIN32_WCE
4170    LPWSTR  lpszCmdLine,                  // pointer to command line
4171    #else
4172    LPSTR lpszCmdLine,
4173    #endif
4174    int  nCmdShow)                // show state of window
4175{
4176  if(hPrevInstance) return(FALSE);
4177  hInst = hInstance;
4178  gDisplayOff = FALSE;
4179  wchar_t argv0[80];
4180  GetModuleFileNameW (NULL, argv0, sizeof (argv0) / sizeof (argv0[0]));
4181  UTF16 *sStart = (UTF16*) argv0, *rchr = (UTF16*) wcsrchr (argv0, L'\\');
4182  wcscpy (rchr ? (wchar_t *) rchr + 1 : argv0, L"");
4183  unsigned char *tStart = (unsigned char *) docPrefix;
4184  ConvertUTF16toUTF8 ((const UTF16 **) &sStart, sStart + wcslen (argv0),
4185    &tStart, tStart + sizeof (docPrefix), lenientConversion);
4186  *tStart = '\0';
4187  #ifndef _WIN32_WCE
4188  if (strncmp (lpszCmdLine, "rebuild", 7) == 0) {
4189    int bbox[4] = { INT_MIN, INT_MIN, 0x7fffffff, 0x7fffffff };
4190    _setmaxstdio (1024); // Try to prevent 'Too many open files'
4191    RebuildPak("gosmore.pak", "elemstyles.xml", "icons.csv", "", bbox);
4192  }
4193  else if (lpszCmdLine[0] != '\0') {
4194    chdir (docPrefix);
4195    char cmdl[200];
4196    sprintf (cmdl, "start cmd /C \"7z -so e %s | gosmore rebuild\"", lpszCmdLine);
4197    system (cmdl);
4198    return 0;
4199  }
4200  #endif
4201  #if 0
4202  GetModuleFileName (NULL, docPrefix, sizeof (docPrefix));
4203  if (strrchr (docPrefix, '\\')) *strrchr (docPrefix, '\\') = '\0';
4204  #endif
4205
4206  char optFileName[sizeof(docPrefix) + 13];
4207  sprintf (optFileName, "%s\\gosmore.opt", docPrefix);
4208  FILE *optFile = fopen (optFileName, "r"); 
4209  if (!optFile) {
4210    strcpy (docPrefix, "\\My Documents\\");
4211    optFile = fopen ("\\My Documents\\gosmore.opt", "rb");
4212  }
4213
4214  //store log file name
4215  sprintf (logFileName, "%s\\gosmore.log.txt", docPrefix);
4216
4217  #ifdef _WIN32_WCE
4218  wcscat (argv0, L"gosmore.pak");
4219  SerializeOptions (optFile, TRUE, argv0);
4220  #else
4221  SerializeOptions (optFile, TRUE, "gosmore.pak");
4222  Keyboard = 1;
4223  #endif
4224  int newWayFileNr = 0;
4225  LOG if (optFile) fread (&newWayFileNr, sizeof (newWayFileNr), 1, optFile);
4226  if (Exit) {
4227    MessageBox (NULL, TEXT ("Pak file not found"), TEXT (""),
4228      MB_APPLMODAL|MB_OK);
4229    return 1;
4230  }
4231  GtkWidget dumdraw;
4232  draw = &dumdraw;
4233
4234  LOG if(!InitApplication ()) return(FALSE);
4235  LOG if (!InitInstance (nCmdShow)) return(FALSE);
4236
4237  newWays[0].cnt = 0;
4238
4239  #ifdef _WIN32_WCE
4240  LOG InitCeGlue();
4241  if (SHFullScreenPtr) {
4242    if (FullScreen) {
4243      (*SHFullScreenPtr)(mWnd, SHFS_HIDETASKBAR |
4244                         SHFS_HIDESTARTICON | SHFS_HIDESIPBUTTON);
4245      MoveWindow (mWnd, 0, 0, GetSystemMetrics(SM_CXSCREEN),
4246                  GetSystemMetrics(SM_CYSCREEN), FALSE);
4247    } else {
4248      (*SHFullScreenPtr)(mWnd, SHFS_HIDESIPBUTTON);
4249    } 
4250  }
4251  #endif
4252
4253  DWORD threadId;
4254  if (CommPort == 0) {}
4255  else /* if((port=CreateFile (portname, GENERIC_READ | GENERIC_WRITE, 0,
4256          NULL, OPEN_EXISTING, 0, 0)) != INVALID_HANDLE_VALUE) */ {
4257    LOG CreateThread (NULL, 0, NmeaReader, NULL, 0, &threadId);
4258    }
4259  /*   else MessageBox (NULL, TEXT ("No Port"), TEXT (""), MB_APPLMODAL|MB_OK); */
4260
4261  MSG    msg;
4262  LOG while (GetMessage (&msg, NULL, 0, 0)) {
4263    //logprintf ("%d %d %d %d\n", msg.hwnd == mWnd, msg.message, msg.lParam, msg.wParam);
4264    int oldCsum = clat + clon, found = msg.message == WM_KEYDOWN;
4265    if (Keyboard && msg.hwnd == hwndEdit && msg.message == WM_LBUTTONDOWN) {
4266      option = option == searchMode ? mapMode : searchMode;
4267      SipShowIM (option == searchMode ? SIPF_ON : SIPF_OFF);
4268      InvalidateRect (mWnd, NULL, FALSE);
4269    } // I couldn't find an EN_ event that traps a click on the searchbar.
4270    if (msg.message == WM_KEYDOWN) {
4271      if ((msg.wParam == '0' && option != searchMode) || msg.wParam == MenuKey) {
4272        HitButton (0);
4273        if (Keyboard && optionMode != searchMode) SipShowIM (SIPF_OFF);
4274      }
4275      else if (msg.wParam == '8' && option != searchMode) HitButton (1);
4276      else if (msg.wParam == '9' && option != searchMode) HitButton (2);
4277
4278      else if (option == ZoomInKeyNum) ZoomInKey = msg.wParam;
4279      else if (option == ZoomOutKeyNum) ZoomOutKey = msg.wParam;
4280      else if (option == MenuKeyNum) MenuKey = msg.wParam;
4281     
4282      else if (msg.wParam == (DWORD) ZoomInKey) zoom = zoom * 3 / 4;
4283      else if (msg.wParam == (DWORD) ZoomOutKey) zoom = zoom * 4 / 3;
4284
4285      else if (VK_DOWN == msg.wParam) clat -= zoom / 2;
4286      else if (VK_UP == msg.wParam) clat += zoom / 2;
4287      else if (VK_LEFT == msg.wParam) clon -= zoom / 2;
4288      else if (VK_RIGHT == msg.wParam) clon += zoom / 2;
4289      else found = FALSE;
4290     
4291      if (found) InvalidateRect (mWnd, NULL, FALSE);
4292      if (oldCsum != clat + clon) FollowGPSr = FALSE;
4293    }
4294    if (!found) {
4295      TranslateMessage (&msg);
4296      DispatchMessage (&msg);
4297    }
4298  }
4299  guiDone = TRUE;
4300
4301  LOG while (port != INVALID_HANDLE_VALUE && guiDone) Sleep (1000);
4302
4303  optFile = fopen (optFileName, "r+b");
4304  if (!optFile) optFile = fopen ("\\My Documents\\gosmore.opt", "wb");
4305  LOG SerializeOptions (optFile, FALSE, NULL);
4306  if (optFile) {
4307    fwrite (&newWayFileNr, sizeof (newWayFileNr), 1, optFile);
4308    fclose (optFile);
4309  }
4310  LOG gpsNewStruct *first = FlushGpx ();
4311  if (newWayCnt > 0) {
4312    char VehicleName[80];
4313    #define M(v) Vehicle == v ## R ? #v :
4314    sprintf(VehicleName, "%s", RESTRICTIONS NULL);
4315    #undef M
4316
4317    char bname[80], fname[80];
4318    LOG getBaseFilename(bname, first);
4319    sprintf (fname, "%s.osm", bname);
4320
4321    FILE *newWayFile = fopen (fname, "w");
4322    if (newWayFile) {
4323      LOG fprintf (newWayFile, "<?xml version='1.0' encoding='UTF-8'?>\n"
4324                           "<osm version='0.6' generator='gosmore'>\n");
4325      for (int j, id = -1, i = 0; i < newWayCnt; i++) {
4326        for (j = 0; j < newWays[i].cnt; j++) {
4327          fprintf (newWayFile, "<node id='%d' visible='true' lat='%.5lf' "
4328            "lon='%.5lf' %s>\n", id - j, LatInverse (newWays[i].coord[j][1]),
4329            LonInverse (newWays[i].coord[j][0]),
4330            newWays[i].cnt <= 1 ? "" : "/");
4331        }
4332        if (newWays[i].cnt > 1) {
4333          fprintf (newWayFile, "<way id='%d' action='modify' "
4334            "visible='true'>\n", id - newWays[i].cnt);
4335          for (j = 0; j < newWays[i].cnt; j++) {
4336            fprintf (newWayFile, "  <nd ref='%d'/>\n", id--);
4337          }
4338        }
4339        id--;
4340        XmlOut (newWayFile, "todo", "FIXME - Added by gosmore");
4341        if (newWays[i].oneway) XmlOut (newWayFile, "oneway", "yes");
4342        if (newWays[i].bridge) XmlOut (newWayFile, "bridge", "yes");
4343        if (newWays[i].klas >= 0) fprintf (newWayFile, "%s",
4344          klasTable[newWays[i].klas].tags);
4345        XmlOut (newWayFile, "name", newWays[i].name);
4346        XmlOut (newWayFile, "note", newWays[i].note);
4347        fprintf (newWayFile, "</%s>\n", newWays[i].cnt <= 1 ? "node" : "way");
4348      }
4349      fprintf (newWayFile, "</osm>\n");
4350      fclose (newWayFile);
4351    }
4352  }
4353
4354  LOG if (logFP(false)) fclose(logFP(false));
4355
4356  return 0;
4357}
4358} // extern "C"
4359#endif
Note: See TracBrowser for help on using the repository browser.