source: subversion/applications/rendering/gosmore/gosmore.cpp @ 24484

Last change on this file since 24484 was 24431, checked in by nic, 9 years ago

Make dragging more user friendly

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