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

Last change on this file since 22745 was 22410, checked in by nic, 9 years ago

New way of doing junction penalties. Still not perfect.
Rebuild: Add and index route refs
Change copyright to Simplified BSD
bboxSplit: Behave better when subprocess is gosmore
First check in of daily planet update script.

  • Property svn:executable set to *
File size: 152.0 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 and Dmitry.
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
1091int firstDrag[2] = { -1, -1 }, lastDrag[2], pressTime;
1092
1093#ifndef NOGTK
1094struct wayPointStruct {
1095  int lat, lon;
1096};
1097deque<wayPointStruct> wayPoint;
1098
1099void ExtractClipboard (GtkClipboard *, const gchar *t, void */*data*/)
1100{
1101  unsigned lonFirst = FALSE, hash = 0;
1102  int minLat = INT_MAX, minLon = INT_MAX, maxLat = INT_MIN, maxLon = INT_MIN;
1103  double deg[2];
1104  static unsigned oldh = 0; // Sometimes an extra queue_clear is needed
1105  wayPoint.clear ();
1106  if (!t) return;
1107  for (; *t != '\0'; t++) {
1108    if (strncasecmp (t, "lat", 3) == 0) lonFirst = FALSE;
1109    else if (strncasecmp (t, "lon", 3) == 0) lonFirst = TRUE;
1110
1111    for (int i = 0; i < 2 && (isdigit (*t) || *t == '-'); i++) {
1112      deg[i] = atof (t);
1113      while (isdigit (*t) || *t == '-') t++;
1114      if (*t != '.') {
1115        // 25S 28E or 25°58′25″S 28°7′42″E or 25 58.5 S 28 6.3 E
1116        while (*t != '\0' && !isdigit (*t) && !isalpha (*t)) t++;
1117        deg[i] += atof (t) / (deg[i] < 0 ? -60 : 60);
1118        while (isdigit (*t) || *t == '.' || *t == ',') t++;
1119        while (*t != '\0' && !isalnum (*t)) t++;
1120        deg[i] += atof (t) / (deg[i] < 0 ? -3600 : 3600);
1121        while (*t != '\0' && !isalpha (*t)) t++;
1122      }
1123      else { // -25.12 28.1
1124        while (*t != '\0' && (isalnum (*t) || *t == '-' || *t == '.')) t++;
1125        while (*t != '\0' && !isalnum (*t)) t++;
1126      }
1127     
1128      if (*t != '\0' && strchr ("westWEST", *t) && !isalpha (t[1])) {
1129        // If t[1] is a letter, then it could be something like
1130        // "-25.1 28.2 school".
1131        if (strchr ("swSW", *t)) deg[i] = -deg[i];
1132        lonFirst = i == (strchr ("snSN", *t) ? 1 : 0);
1133        for (t++; isspace (*t); t++) {}
1134      }
1135      if (deg[i] < -180 || deg[i] > 180) break;
1136      if (i == 0 && (strncasecmp (t, "lat", 3) == 0 ||
1137                     strncasecmp (t, "lon", 3) == 0)) { // lat=-25.7 lon=28.2
1138        for (t += 3; t != '\0' && !isalnum (*t); t++) {}
1139      }
1140      if (i == 1) { // Success !
1141        //printf ("%lf %lf %u\n", deg[lonFirst ? 1 : 0], deg[lonFirst ? 0 : 1],
1142        //  lonFirst); // Debugging
1143        wayPoint.push_back (wayPointStruct ());
1144        wayPoint.back ().lon = Longitude (deg[lonFirst ? 0 : 1]);
1145        wayPoint.back ().lat = Latitude (deg[lonFirst ? 1 : 0]);
1146        lonFirst = FALSE; // Not too sure if we should reset lonFirst here.
1147        hash += wayPoint.back ().lon + wayPoint.back ().lat;
1148        // Bad but adequate hash function.
1149        if (minLon > wayPoint.back ().lon) minLon = wayPoint.back ().lon;
1150        if (maxLon < wayPoint.back ().lon) maxLon = wayPoint.back ().lon;
1151        if (minLat > wayPoint.back ().lat) minLat = wayPoint.back ().lat;
1152        if (maxLat < wayPoint.back ().lat) maxLat = wayPoint.back ().lat;
1153      }
1154    }
1155  }
1156  if (oldh != hash && !wayPoint.empty ()) {
1157    clat = minLat / 2 + maxLat / 2;
1158    clon = minLon / 2 + maxLon / 2;
1159    zoom = maxLat - minLat + maxLon - minLon + (1 << 15);
1160    gtk_widget_queue_clear (draw);
1161  }
1162  oldh = hash;
1163}
1164
1165int UpdateWayPoints (GtkWidget *, GdkEvent *, gpointer *)
1166{
1167  GtkClipboard *c = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
1168  gtk_clipboard_request_text (c, ExtractClipboard, &wayPoint);
1169  return FALSE;
1170}
1171
1172gint Drag (GtkWidget * /*widget*/, GdkEventMotion *event, void * /*w_cur*/)
1173{
1174  if ((option == mapMode || option == optionMode) &&
1175          (event->state & GDK_BUTTON1_MASK)) {
1176    if (firstDrag[0] >= 0) gdk_draw_drawable (draw->window,
1177      draw->style[0].fg_gc[0], draw->window, 
1178      0, 0, lrint (event->x) - lastDrag[0], lrint (event->y) - lastDrag[1],
1179      draw->allocation.width, draw->allocation.height);
1180    lastDrag[0] = lrint (event->x);
1181    lastDrag[1] = lrint (event->y);
1182    if (firstDrag[0] < 0) {
1183      memcpy (firstDrag, lastDrag, sizeof (firstDrag));
1184      pressTime = event->time;
1185    }
1186  }
1187  return FALSE;
1188}
1189
1190GtkWidget *bar;
1191int UpdateProcessFunction(void */*userData*/, double t, double d,
1192                                          double /*ultotal*/, double /*ulnow*/)
1193{
1194  gdk_threads_enter ();
1195  gtk_progress_set_value (GTK_PROGRESS (bar), d * 100.0 / t);
1196  gdk_threads_leave ();
1197  return 0;
1198}
1199
1200void *UpdateMapThread (void *n)
1201{
1202  CURL *curl;
1203  CURLcode res;
1204  FILE *outfile;
1205 
1206  curl = curl_easy_init();
1207  if(curl) {
1208    outfile = fopen("tmp.zip", "w");
1209 
1210    // string zip ((string)(char*)n + ".zip", cmd ("unzip " + zip);
1211    string url ("http://dev.openstreetmap.de/gosmore/" + (string)(char*)n + ".zip");
1212    curl_easy_setopt(curl, CURLOPT_URL, url.c_str ());
1213    curl_easy_setopt(curl, CURLOPT_WRITEDATA, outfile);
1214    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite); //DefaultCurlWrite);
1215    curl_easy_setopt(curl, CURLOPT_READFUNCTION, fread); //my_read_func);
1216    curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
1217    curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, UpdateProcessFunction);
1218    curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, ""); // Bar);
1219 
1220    res = curl_easy_perform(curl);
1221 
1222    fclose(outfile);
1223    system ("unzip tmp.zip"); //cmd.c_str ());
1224    string dst ((string)(char*)n + ".pak");
1225    rename ("gosmore.pak", dst.c_str ());
1226    unlink ("tmp.zip");
1227    gdk_threads_enter ();
1228    gtk_progress_bar_set_text (GTK_PROGRESS_BAR (bar), "Done");
1229/*  ChangePak (NULL, clon ^ 0x80000000, clat);
1230    Expose () I don't think it will work in this thread. SEGV. */
1231   
1232    gdk_threads_leave ();
1233
1234    curl_easy_cleanup(curl);
1235  }
1236  free (n); // Malloced in one thread freed in another.
1237  return NULL; 
1238}
1239
1240#endif
1241#if defined (_WIN32) && !defined (_WIN32_WCE)
1242DWORD WINAPI UpdateMapThread (LPVOID n)
1243{
1244  WSADATA d;
1245  WSAStartup (MAKEWORD (1, 1), &d);
1246  struct hostent *he = gethostbyname ("dev.openstreetmap.de");
1247  int s = socket (AF_INET, SOCK_STREAM, 0);
1248  struct sockaddr_in name;
1249  if (he && s != INVALID_SOCKET) {
1250    memset (&name, 0, sizeof (name));
1251    name.sin_family = AF_INET;
1252    name.sin_port = htons (80);
1253    memcpy (&name.sin_addr, he->h_addr_list[0], 4);
1254    string header = string ("GET /gosmore/") + string ((char*) n, 16) + 
1255                 ".zip HTTP/1.0\r\n"
1256                 "Host: dev.openstreetmap.de\r\n"
1257                 "\r\n";
1258    if (connect (s, (sockaddr *) &name, sizeof (name)) == 0 &&
1259        send (s, header.c_str (), strlen (header.c_str ()), 0) > 0) {
1260      char reply[4096], *ptr = reply, *lnl = NULL;
1261      int code, len, cnt = recv (s, reply, sizeof (reply), 0);
1262      sscanf (reply, "%*s %d", &code);
1263      while (cnt > 0 && ptr[0] != '\n' || !lnl) {
1264        if (cnt > 16 && (ptr[0] == '\n' || ptr[0] == '\r') &&
1265            strnicmp (ptr + 1, "Content-Length:", 15) == 0) {
1266          len = atoi (ptr + 16);
1267        }
1268        lnl = *ptr == '\n' ? ptr : *ptr == '\r' ? lnl : NULL;
1269        cnt--;
1270        ptr++;
1271        if (cnt < 1) {
1272          memmove (reply, ptr, cnt);
1273          ptr = reply;
1274          cnt += recv (s, ptr, sizeof (reply) - cnt, 0);
1275        }
1276      }
1277      if (cnt-- > 0) { // Get rid of the '\n'
1278        ptr++; // Get rid of the '\n'
1279        FILE *z = fopen ("tmp.zip", "wb");
1280        code = 0;
1281        do {
1282          fwrite (ptr, cnt, 1, z);
1283          if ((code + cnt) / (len / 1000 + 1) > code / (len / 1000 + 1)) {
1284            PostMessage (mWnd, WM_USER + 2, 0, (code + cnt) / (len / 1000 + 1));
1285          }
1286          code += cnt;
1287          ptr = reply;
1288        } while ((cnt = recv (s, reply, sizeof (reply), 0)) > 0);
1289        fclose (z);
1290        STARTUPINFO si;
1291        PROCESS_INFORMATION pi;
1292        ZeroMemory (&si, sizeof (si));
1293        ZeroMemory (&pi, sizeof (pi));
1294        si.cb = sizeof (si);
1295        CreateProcess ("7z.exe", "7z x -y tmp.zip", NULL, NULL,
1296          FALSE, 0, NULL, NULL, &si, &pi);
1297        WaitForSingleObject (pi.hProcess, INFINITE);
1298        CloseHandle (pi.hProcess);
1299        string dst (string ((char*) n, 16) + ".pak");
1300        rename ("gosmore.pak", dst.c_str ());
1301        _unlink ("tmp.zip");
1302      }
1303    }
1304    else closesocket (s);
1305  }
1306  free (n);
1307  PostMessage (mWnd, WM_USER + 2, 0, 0);
1308  return 0;
1309}
1310#endif
1311
1312#define CompactOptions ((draw->allocation.width * draw->allocation.height < 400 * 400))
1313int ListXY (int cnt, int isY)
1314{ // Returns either the x or the y for a certain list item
1315  int max = mapMode; //option == optionMode ? mapNode :
1316  int w = CompactOptions ? 70 : 105, h = CompactOptions ? 45 : 80;
1317  while ((draw->allocation.width/w) * (draw->allocation.height/h - 1) > max) {
1318    w++;
1319    h++;
1320  }
1321  return isY ? cnt / (draw->allocation.width / w) * h + h / 2 - listYOffset :
1322    (cnt % (draw->allocation.width / w)) * w + w / 2;
1323}
1324
1325#ifndef NOGTK
1326typedef GdkGC *HDC;
1327
1328static GdkGC *maskGC = NULL, *fg_gc;
1329static GdkBitmap *mask = NULL;
1330// create bitmap for generation the mask image for icons
1331// all icons must be smaller than these dimensions
1332static GdkBitmap *maskicon = NULL;
1333static GdkPixmap *icons = NULL;
1334
1335#else
1336HDC icons, maskDc;
1337HFONT sysFont;
1338LOGFONT logFont;
1339
1340#define gtk_combo_box_get_active(x) 1
1341#define gdk_draw_drawable(win,dgc,sdc,x,y,dx,dy,w,h) \
1342  BitBlt (dgc, dx, dy, w, h, maskDc, x, y, SRCAND); \
1343  BitBlt (dgc, dx, dy, w, h, sdc, x, y, SRCPAINT)
1344#define gdk_draw_line(win,gc,sx,sy,dx,dy) \
1345  do { MoveToEx (gc, sx, sy, NULL); LineTo (gc, dx, dy); } while (0)
1346
1347#endif
1348
1349static HDC mygc = NULL, iconsgc = NULL;
1350
1351#ifdef PANGO_VERSION
1352PangoContext *pc;
1353PangoLayout  *pl;
1354#endif
1355
1356void DrawString (int x, int y, const char *optStr)
1357{
1358  #if PANGO_VERSION
1359  PangoMatrix mat = PANGO_MATRIX_INIT;
1360  pango_context_set_matrix (pc, &mat);
1361  pango_layout_set_text (pl, optStr, -1);
1362  gdk_draw_layout (GDK_DRAWABLE (draw->window),
1363                     fg_gc /*draw->style->fg_gc[0]*/, x, y, pl);
1364  #else
1365  SelectObject (mygc, sysFont);
1366  const unsigned char *sStart = (const unsigned char*) optStr;
1367  UTF16 wcTmp[70], *tStart = (UTF16 *) wcTmp;
1368  if (ConvertUTF8toUTF16 (&sStart,  sStart + strlen (optStr), &tStart,
1369           tStart + sizeof (wcTmp) / sizeof (wcTmp[0]), lenientConversion)
1370      == conversionOK) {
1371    ExtTextOutW (mygc, x, y, 0, NULL, (wchar_t*) wcTmp, tStart - wcTmp, NULL);
1372  }
1373  #endif
1374}
1375
1376void DrawPoI (int dstx, int dsty, int *icon)
1377{
1378  if (icon[2] == 0 || dstx < -icon[2] || dsty < -icon[3] ||
1379    dstx > draw->allocation.width + icon[2] ||
1380    // GDK need these tests for the Start&EndRoute markers
1381    dsty > draw->allocation.height + icon[3]) return;
1382  #ifndef NOGTK
1383  // for gdk we first need to extract the portion of the mask
1384  if (!maskicon) maskicon = gdk_pixmap_new(NULL, 100, 100, 1);
1385  gdk_draw_drawable (maskicon, maskGC, mask,
1386                     icon[0], icon[1], 0, 0,
1387                     icon[2], icon[3]);
1388  // and set the clip region using that portion
1389  gdk_gc_set_clip_origin(iconsgc, dstx - icon[2] / 2, dsty - icon[3] / 2);
1390  gdk_gc_set_clip_mask(iconsgc, maskicon);
1391  #endif
1392  gdk_draw_drawable (draw->window, iconsgc, icons,
1393    icon[0], icon[1], dstx - icon[2] / 2, dsty - icon[3] / 2,
1394    icon[2], icon[3]);
1395}
1396
1397void GeoSearch (const char *key)
1398{
1399  const char *comma = strchr (key, ',');
1400  if (!comma) comma = strstr (key, " near ");
1401  if (comma) {
1402    const char *cName = comma + (*comma == ',' ? 1 : 6);
1403    string citi = string ("city:") + (cName + strspn (cName, " "));
1404    const char *tag = gosmData + *GosmIdxSearch (citi.c_str (), 0);
1405    while (*--tag) {}
1406    ChangePak (NULL, ((wayType *)tag)[-1].clon, ((wayType *)tag)[-1].clat);
1407    string xkey = string (key, comma - key);
1408    //printf ("%s tag=%s\nxke %s\n", cName, tag + 1, xkey.c_str ());
1409    GosmSearch (((wayType *)tag)[-1].clon, ((wayType *)tag)[-1].clat, xkey.c_str ());
1410  }
1411  else GosmSearch (clon, clat, key);
1412}
1413
1414int HandleKeyboard (GdkEventButton *event)
1415{ // Some WinCE devices, like the Mio Moov 200 does not have an input method
1416  // and any call to activate it or set the text on an EDIT or STATIC (label)
1417  // control will crash the application. So under WinCE we default to our
1418  // own keyboard.
1419  //
1420  // Draw our own keyboard (Expose Event) or handle the key (Click)
1421  if (Keyboard) return FALSE; // Using the Windows keyboard
1422  // DrawString (30, 5, searchStr.c_str ()); // For testing under GTK
1423  #ifdef _WIN32_WCE
1424  if (!event) {
1425    RECT r;
1426    r.left = 0;
1427    r.top = draw->allocation.height - 32 * 3;
1428    r.right = draw->allocation.width;
1429    r.bottom = draw->allocation.height;
1430    FillRect (mygc, &r, (HBRUSH) GetStockObject (WHITE_BRUSH)); //brush[KeyboardNum]);
1431    SelectObject (mygc, GetStockObject (BLACK_PEN));
1432  }
1433
1434  const char *kbLayout[] = { "qwertyuiop", "asdfghjkl", " zxcvbnm,$" };
1435  for (int i = 0; i < 3; i++) {
1436    for (int j = 0; kbLayout[i][j] != '\0'; j++) {
1437      int hb = draw->allocation.width / strlen (kbLayout[0]) / 2, ys = 16;
1438      int x = (2 * j + (i & 1)) * hb, y = draw->allocation.height - (3 - i) * ys * 2;
1439      if (event && event->y >= y && event->y < y + ys + ys && event->x < x + hb + hb) {
1440        if (kbLayout[i][j] != '$') searchStr += kbLayout[i][j];
1441        else if (searchStr.length () > 0) searchStr.erase (searchStr.length () - 1, 1);
1442        logprintf ("'%s'\n", searchStr.c_str());
1443        GeoSearch (searchStr.c_str ());
1444        gtk_widget_queue_clear (draw);
1445        return TRUE;
1446      }
1447      if (!event) {
1448        if (j > 0) gdk_draw_line (draw->window, mygc, x, y, x, y + ys + ys);
1449        else gdk_draw_line (draw->window, mygc, 0, y, draw->allocation.width, y);
1450        string chr = string ("") + kbLayout[i][j];
1451        if (kbLayout[i][j] == ' ') DrawString (x + hb - 5, y + ys / 2, "[ ]");
1452        else if (kbLayout[i][j] != '$') DrawString (x + hb, y + ys / 2, chr.c_str ());
1453        else { // Now draw the backspace symbol :
1454          gdk_draw_line (draw->window, mygc, x + 3, y + ys, x + hb + hb - 3, y + ys);
1455          gdk_draw_line (draw->window, mygc, x + 3, y + ys, x + hb, y + 3);
1456          gdk_draw_line (draw->window, mygc, x + 3, y + ys, x + hb, y + ys + ys - 3);
1457        }
1458      }
1459    }
1460  }
1461  #endif
1462  return FALSE;
1463}
1464
1465int Click (GtkWidget * /*widget*/, GdkEventButton *event, void * /*para*/)
1466{
1467  static int lastRelease = 0;
1468  int w = draw->allocation.width, h = draw->allocation.height;
1469
1470  // Anything that covers more than 3 pixels in either direction is a drag.
1471  int isDrag = DebounceDrag
1472        ? firstDrag[0] >= 0 && (lastRelease + 100 > (int) event->time ||
1473                                  pressTime + 100 < (int) event->time)
1474        : firstDrag[0] >= 0 && (abs((int)(firstDrag[0] - event->x)) > 3 ||
1475                                abs((int)(firstDrag[1] - event->y)) > 3);
1476 
1477  // logprintf("Click (isDrag = %d): firstDrag = %d,%d; event = %d,%d\n",
1478  //        isDrag, firstDrag[0], firstDrag[1], event->x, event->y);
1479
1480  if (ButtonSize <= 0) ButtonSize = 4;
1481  int b = (draw->allocation.height - lrint (event->y)) / (ButtonSize * 20);
1482  if (objectAddRow >= 0) {
1483    int perRow = (w - ButtonSize * 20) / ADD_WIDTH;
1484    if (event->x < w - ButtonSize * 20) {
1485      #ifdef NOGTK
1486      newWays[newWayCnt].klas = objectAddRow + event->x / ADD_WIDTH +
1487                                event->y / ADD_HEIGHT * perRow;
1488      SipShowIM (SIPF_ON);
1489      if (DialogBox (hInst, MAKEINTRESOURCE (IDD_SETTAGS), NULL,
1490          (DLGPROC) DlgSetTagsProc)) {} //DialogBox (hInst,
1491          //MAKEINTRESOURCE (IDD_SETTAGS2), NULL, (DLGPROC) DlgSetTags2Proc);
1492      newWays[newWayCnt].cnt = 0;
1493      #endif
1494      objectAddRow = -1;
1495    }
1496    else objectAddRow = int (event->y) * (restriction_no_right_turn / perRow
1497                                  + 2) / draw->allocation.height * perRow;
1498  }
1499  else if (event->x > w - ButtonSize * 20 && b <
1500      (Layout >
1501       (MenuKey == 0 || option != mapMode ? 0 : 1) ? 3 : 0)) HitButton (b);
1502  else if (option == optionMode) {
1503    if (isDrag) {
1504      listYOffset = max (0, listYOffset + (int)lrint (firstDrag[1]-event->y));
1505    }
1506    else {
1507      for (int best = 9999, i = 0; i < mapMode; i++) {
1508        int d = lrint (fabs (ListXY (i, FALSE) - event->x) +
1509                       fabs (ListXY (i, TRUE) - event->y));
1510        if (d < best) {
1511          best = d;
1512          option = i;
1513        }
1514      }
1515      if (option <= OrientNorthwardsNum) HitButton (1);
1516      if (option >= ViewOSMNum && option <= ViewGMapsNum) {
1517        char lstr[200];
1518        int zl = 0;
1519        while (zl < 32 && (zoom >> zl)) zl++;
1520        sprintf (lstr,
1521         option == ViewOSMNum ? "%sopenstreetmap.org/?lat=%.5lf&lon=%.5lf&zoom=%d%s" :
1522         option == EditInPotlatchNum ? "%sopenstreetmap.org/edit?lat=%.5lf&lon=%.5lf&zoom=%d%s" :
1523         "%smaps.google.com/?ll=%.5lf,%.5lf&z=%d%s",
1524        #ifdef WIN32
1525          "http://", LatInverse (clat), LonInverse (clon), 33 - zl, "");
1526        #ifndef _WIN32_WCE
1527        ShellExecute (NULL, TEXT ("open"), lstr, NULL, NULL,
1528          SW_SHOWNORMAL);
1529        #else
1530        MessageBox (NULL, TEXT ("Not implemented"), TEXT ("Error"), MB_APPLMODAL|MB_OK);
1531        #endif
1532        #else
1533          "gnome-open 'http://", LatInverse (clat), LonInverse (clon), 33 - zl, "'");
1534        option = system (lstr); // Shut up GCC w.r.t. return value
1535        #endif
1536        option = mapMode;
1537      }
1538      #ifndef NOGTK
1539      else if (option == UpdateMapNum) {
1540        struct stat s;
1541        if (currentBbox[0] == '\0') {
1542          gtk_dialog_run (GTK_DIALOG (gtk_message_dialog_new (NULL,
1543            GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
1544            "Error:\n"
1545            "Gosmore is running with a custom map\n"
1546            "Download aborted.")));
1547        }
1548        else if (stat (currentBbox, &s) == 0 &&
1549           (s.st_mtime > time (NULL) - 3600*24*7 ||
1550            s.st_ctime > time (NULL) - 3600 * 24 * 7)) {
1551          gtk_dialog_run (GTK_DIALOG (gtk_message_dialog_new (NULL,
1552            GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
1553            "Error:\n"
1554            "%s has changed during the last 7 days,\n"
1555            "and is most likely up-to-date.\n"
1556            "Download aborted.", currentBbox)));
1557        }
1558        else {
1559          string msg (string ("Downloading ") + currentBbox);
1560          gtk_progress_bar_set_text (GTK_PROGRESS_BAR (bar), msg.c_str ());
1561          g_thread_create (&UpdateMapThread, strndup (currentBbox, 16), FALSE, NULL);
1562        }
1563        option = mapMode;
1564      }
1565      #else
1566      #ifndef _WIN32_WCE
1567      else if (option == UpdateMapNum) {
1568        struct stat s;
1569        if (currentBbox[0] == '\0') {
1570          MessageBox (NULL, "Error:\n"
1571            "Gosmore is running with a custom map\n"
1572            "Download aborted.", "Error", MB_APPLMODAL|MB_OK);
1573        }
1574        else if (stat (currentBbox, &s) == 0 &&
1575           (s.st_mtime > time (NULL) - 3600*24*7 ||
1576            s.st_ctime > time (NULL) - 3600 * 24 * 7)) {
1577          MessageBox (NULL, "Error:\n"
1578            "The .pak file has changed during the last 7 days,\n"
1579            "and is most likely up-to-date.\n"
1580            "Download aborted.", "Error", MB_APPLMODAL|MB_OK);
1581        }
1582        else {
1583          DWORD threadId;
1584          CreateThread (NULL, 0, UpdateMapThread, strdup (currentBbox), 0,
1585            &threadId);
1586        }
1587        option = mapMode;
1588      }
1589      #endif
1590      #endif
1591    }
1592  }
1593  else if (option == searchMode) {
1594    int row = event->y / SearchSpacing;
1595    if (!HandleKeyboard (event) && row < searchCnt && gosmSstr[row]) {
1596      SetLocation (gosmSway[row]->clon, gosmSway[row]->clat);
1597      zoom = gosmSway[row]->dlat + gosmSway[row]->dlon + (1 << 15);
1598      if (zoom <= (1 << 15)) zoom = Style (gosmSway[row])->scaleMax;
1599      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (followGPSr), FALSE);
1600      FollowGPSr = FALSE;
1601      option = mapMode;
1602      highlight = string (gosmSstr[row], strcspn (gosmSstr[row], "\n"));
1603      gtk_widget_queue_clear (draw);
1604    }
1605  }
1606  else {
1607    #ifdef ROUTE_TEST
1608    if (event->state & GDK_SHIFT_MASK) {
1609      return RouteTest (NULL /*widget*/, event, NULL /*para*/);
1610    }
1611    #endif
1612    int perpixel = zoom / w, dx = event->x - w / 2, dy = h / 2 - event->y;
1613    if (isDrag) {
1614      dx = firstDrag[0] - event->x;
1615      dy = event->y - firstDrag[1];
1616    }
1617    int lon = clon + lrint (perpixel *
1618      (cosAzimuth * (Display3D ? 0 : dx) - sinAzimuth * dy));
1619    int lat = clat + lrint (perpixel *
1620      (cosAzimuth * dy + sinAzimuth * (Display3D ? 0 : dx)));
1621    if (event->button == 1) {
1622      if (Display3D) {
1623        double newa = atan2 (sinAzimuth, cosAzimuth) - dx * M_PI / 580;
1624        cosAzimuth = cos (newa);
1625        sinAzimuth = sin (newa);
1626      }
1627      SetLocation (lon, lat);
1628
1629      #ifdef NOGTK
1630      if (AddWayOrNode && newWays[newWayCnt].cnt < NEWWAY_MAX_COORD) {
1631        newWays[newWayCnt].coord[newWays[newWayCnt].cnt][0] = clon;
1632        newWays[newWayCnt].coord[newWays[newWayCnt].cnt++][1] = clat;
1633      }
1634      #endif
1635      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (followGPSr), FALSE);
1636      FollowGPSr = 0;
1637    }
1638    else if (event->button == 2) {
1639      flon = lon;
1640      flat = lat;
1641      GosmFreeRoute ();
1642      shortest = NULL;
1643    }
1644    else {
1645      tlon = lon;
1646      tlat = lat;
1647      CallRoute (TRUE, 0, 0);
1648    }
1649  }
1650  firstDrag[0] = -1;
1651  lastRelease = event->time;
1652  gtk_widget_queue_clear (draw); 
1653  return FALSE;
1654}
1655
1656#if 0 //ifdef CHILDREN
1657struct childStruct {
1658  int minlon, minlat, maxlon, maxlat, z;
1659  int pipe[2];
1660} child[70];
1661#endif
1662#define STATEINFO OPTIONS o (clat, 0, 0) o (clon, 0, 0) \
1663 o (sinAzimuth, 0, 0) o (cosAzimuth, 0, 0) o (zoom, 0, 0) o (option, 0, 0) \
1664 o (draw->allocation.width, 0, 0) o (draw->allocation.height, 0, 0)
1665#define o(x,min,max) sizeof (x) +
1666static const size_t stateSize = STATEINFO 0;
1667#undef o
1668
1669#if 0
1670typedef struct {  /* For 3D, a list of segments is generated that is */
1671  ndType *nd;     /* z-sorted and rendered. */
1672  int f[2], t[2], tlen;
1673  char *text;
1674} renderNd;
1675#endif
1676
1677/*inline double YDivisor (double x) { return x; }
1678inline double Clamp (double x) { return x; }*/
1679/*
1680inline int YDivisor (int y)
1681{
1682  return y > 5256 || y < -5256 ? y : y < 0 ? -5256 : 5256;
1683}
1684*/
1685/*
1686inline int Clamp2 (int x)
1687{
1688  return x < -32760 ? -32760 : x > 32760 ? 32760 : x;
1689}*/
1690
1691void Draw3DLine (int sx, int sy, int dx, int dy)
1692{
1693  if (Display3D) {
1694    if (sy < 0) {
1695      if (dy < 0) return;
1696      sx = dx + (dx - sx) * (/*clip.height*/ 1024 - dy) / (dy - sy);
1697      sy = /*clip.height*/ 1024;
1698    }
1699    else if (dy < 0) {
1700      dx = sx + (sx - dx) * (/*clip.height*/ 1024 - sy) / (sy - dy);
1701      dy = /*clip.height*/ 1024;
1702    }
1703  }
1704  gdk_draw_line (draw->window, mygc, sx, sy, dx, dy);
1705}
1706
1707int TestOrSet (int *bits, int set, int x0, int y0, int ax, int ay,
1708       int bx, int by)
1709/* This funtion manipulates bits in a rectangular area in a bitfield. (x0, y0)
1710   is one of the corners. (ax,ay) and (bx,by) are to vectors that define
1711   two of the sides. ay > 0
1712   The precise operation is determined by the 'set' boolean
1713*/
1714{
1715  if (by < 0) { // Top not given, so we find it first.
1716    x0 += bx;
1717    y0 += by;
1718    int nx = ax, ny = ay;
1719    ax = -bx;
1720    ay = -by;
1721    bx = nx;
1722    by = ny;
1723  }
1724  if (y0 < 0 || y0 + ay + by > draw->allocation.height ||
1725      x0 + ax < 0 || x0 + bx > draw->allocation.width) return TRUE;
1726  // Do not place anything offscreen.
1727  const int shf = 9;
1728  x0 <<= shf;
1729  int x1 = x0, d0 = (ax << shf) / (ay + 1), d1 = (bx << shf) / (by + 1);
1730  int bpr = (draw->allocation.width + 31) / 32;
1731  bits += bpr * y0;
1732  for (int cnt = ay + by; cnt > 0; cnt--) {
1733    x0 += d0;
1734    x1 += d1;
1735    for (int i = x0 >> shf; i < (x1 >> shf); i++) {
1736      if (set) bits[i >> 5] |= 1 << (i & 31);
1737      else if (bits[i >> 5] & (1 << (i & 31))) return TRUE;
1738    } // This loop can be optimized
1739    //gdk_draw_line (draw->window, mygc, x0 >> shf, y0, x1 >> shf, y0);
1740    // Uncomment this line to see if we're testing the right spot
1741    // (and it looks kind of interesting )
1742    bits += bpr;
1743    if (cnt == by) d0 = (bx << shf) / by;
1744    if (cnt == ay) d1 = (ax << shf) / ay;
1745    y0++;
1746  }
1747  return FALSE;
1748}
1749
1750/* Choose the part of the way that is best to render the text on. Currently
1751   the straightest part. We look at for the two points where the direct
1752   distance is long enough and it is also the closest to the distance
1753   between the two points along the curve.
1754   TODO: Use the number of junctions between the two points (T / 4 way)
1755   TODO: Consider moments (standard deviation)
1756*/
1757struct linePtType {
1758  int x, y, cumulative;
1759  linePtType (int _x, int _y, int _c) : x (_x), y (_y), cumulative (_c) {}
1760};
1761
1762struct text2Brendered {
1763  const char *s; // Either \n or \0 terminated
1764  int x, y, x2, y2, dst;
1765  text2Brendered (void) {}
1766};
1767
1768void ConsiderText (queue<linePtType> *q, int finish, int len, int *best,
1769  text2Brendered *t)
1770{
1771  while (!q->empty ()) {
1772    int clip[2] = { 0, 0 }; // Used with q->front or q->back is off-screen
1773    int dx = q->back ().x - q->front ().x, dy = q->back ().y - q->front ().y;
1774    if (q->size () == 2) { // cumulative can't cope with clipping, so we
1775                           // only do it when we know detour will be 0
1776      for (int i = 0; i < 2; i++) {
1777        linePtType *f = !i ? &q->front () : &q->back ();
1778        if (f->x < 10 && dx != 0) clip[i] = max (clip[i], 256 * (10 - f->x) / (i ? -dx : dx));
1779        if (f->y < 10 && dy != 0) clip[i] = max (clip[i], 256 * (10 - f->y) / (i ? -dy : dy));
1780        int r2x = f->x - draw->allocation.width + 10;
1781        if (r2x > 0 && dx != 0) clip[i] = max (clip[i], 256 * r2x / (i ? dx : -dx));
1782        int r2y = f->y - draw->allocation.height + 10;
1783        if (r2y > 0 && dy != 0) clip[i] = max (clip[i], 256 * r2y / (i ? dy : -dy));
1784      }
1785    }
1786    int dst = isqrt (Sqr (dx) + Sqr (dy)) * (256 - clip[0] - clip[1]) / 256;
1787    int detour = q->size () == 2 ? 0 : q->back ().cumulative - q->front ().cumulative - dst;
1788    if (detour <= *best) {
1789      if (dst * DetailLevel > len * 14) {
1790        t->x = q->front ().x + dx * clip[0] / 256;
1791        t->y = q->front ().y + dy * clip[0] / 256;
1792        t->x2 = q->back ().x - dx * clip[1] / 256;
1793        t->y2 = q->back ().y - dy * clip[1] / 256;
1794        t->dst = dst;
1795        *best = detour;
1796      }
1797      if (!finish) break;
1798    }
1799    q->pop ();
1800  } // While shortening the queue
1801}
1802
1803int WaySizeCmp (ndType **a, ndType **b)
1804{
1805  return Way (*a)->dlat * (__int64) Way (*a)->dlon >
1806         Way (*b)->dlat * (__int64) Way (*b)->dlon ? 1 : -1;
1807}
1808
1809#ifdef NOGTK
1810int DrawExpose (HPEN *pen, HBRUSH *brush)
1811{
1812  struct {
1813    int width, height;
1814  } clip;
1815/*  clip.width = GetSystemMetrics(SM_CXSCREEN);
1816  clip.height = GetSystemMetrics(SM_CYSCREEN); */
1817  WCHAR wcTmp[70];
1818
1819  iconsgc = mygc;
1820
1821
1822  SetTextColor (mygc, Background ? 0 : 0xffffff);
1823  if (objectAddRow >= 0) {
1824    SelectObject (mygc, sysFont);
1825    //SetBkMode (mygc, TRANSPARENT);
1826    SelectObject (mygc, GetStockObject (BLACK_PEN));
1827    for (int y = 0, i = objectAddRow; y < draw->allocation.height;
1828              y += ADD_HEIGHT) {
1829      //gdk_draw_line (draw->window, mygc, 0, y, draw->allocation.width, y);
1830      gdk_draw_line (draw->window, mygc,
1831        draw->allocation.width - ButtonSize * 20,
1832        draw->allocation.height * i / restriction_no_right_turn,
1833        draw->allocation.width,
1834        draw->allocation.height * i / restriction_no_right_turn);
1835      RECT klip;
1836      klip.bottom = y + ADD_HEIGHT;
1837      klip.top = y;
1838      for (int x = 0; x < draw->allocation.width - ButtonSize * 20 -
1839          ADD_WIDTH && i < restriction_no_right_turn; x += ADD_WIDTH, i++) {
1840        int *icon = style[i].x + 4 * IconSet;
1841        gdk_draw_drawable (draw->window, mygc, icons, icon[0], icon[1],
1842          x - icon[2] / 2 + ADD_WIDTH / 2, y, icon[2], icon[3]);
1843        klip.left = x + 8;
1844        klip.right = x + ADD_WIDTH - 8;
1845        #ifndef _WIN32_WCE
1846        DrawString (x + 8, y + ADD_HEIGHT - 16, klasTable[i].desc);
1847        #else
1848        ExtTextOut (mygc, x + 8, y + ADD_HEIGHT - 16, ETO_CLIPPED,
1849          &klip, klasTable[i].desc, wcslen (klasTable[i].desc), NULL);
1850        #endif
1851      }
1852    }
1853    return FALSE;
1854  } // if displaying the klas / style / rule selection screen
1855#else
1856
1857void SetColour (GdkColor *c, int hexTrip)
1858{
1859  c->red =    (hexTrip >> 16)        * 0x101;
1860  c->green = ((hexTrip >> 8) & 0xff) * 0x101;
1861  c->blue =   (hexTrip       & 0xff) * 0x101;
1862  gdk_colormap_alloc_color (gdk_window_get_colormap (draw->window),
1863      c, FALSE, TRUE);
1864}
1865         
1866gint DrawExpose (void)
1867{
1868  static GdkColor styleColour[2 << STYLE_BITS][2];
1869  static GdkColor /*routeColour, validateColour,*/ resultArrowColour;
1870  if (!mygc || !iconsgc) {
1871    mygc = gdk_gc_new (draw->window);
1872    fg_gc = gdk_gc_new (draw->window);
1873    iconsgc = gdk_gc_new (draw->window);
1874    for (int i = 0; i < stylecount; i++) {
1875      for (int j = 0; j < 2; j++) {
1876        SetColour (&styleColour[i][j],
1877         !j ? style[i].areaColour 
1878          : style[i].lineColour != -1 ? style[i].lineColour
1879          : (style[i].areaColour >> 1) & 0xefefef); // Dark border for polys
1880      }
1881    }
1882    /*SetColour (&routeColour, 0x00ff00);
1883    SetColour (&validateColour, 0xff9999);*/
1884    SetColour (&resultArrowColour, 0);
1885    gdk_gc_set_fill (mygc, GDK_SOLID);
1886
1887    if (!icons) icons = gdk_pixmap_create_from_xpm (draw->window, &mask,
1888      NULL, FindResource ("icons.xpm"));
1889    maskGC = gdk_gc_new(mask);
1890  }
1891  static int oldBackground = -1;
1892  if (oldBackground != Background) {
1893    /*static const int bgVal[9] = { 0, 0xe0ffff, 0xd3d3d3, 0xe6e6fa,
1894      0xffffe0, 0xf5deb3, 0x7b68ee, 0x6b8e23, 0xffffff };
1895    GdkColor bg; */
1896    //SetColour (&bg, bgVal[
1897    gdk_window_set_background (draw->window, &styleColour[
1898              firstElemStyle + Background - (Background > 8 ? 8 : 0)][0]);
1899    oldBackground = Background;
1900  }
1901  #if 0 //ifdef CHILDREN
1902  if (1) {
1903    vector<char> msg;
1904    msg.resize (4 + 3 * sizeof (XID), 0); // Zero the header
1905    *(XID*)&msg[4] = GDK_WINDOW_XID (draw->window);
1906    *(XID*)&msg[4 + sizeof (XID)] = GDK_PIXMAP_XID (icons);
1907    *(XID*)&msg[4 + sizeof (XID) * 2] = GDK_PIXMAP_XID (mask);
1908    #define o(x,min,max) msg.resize (msg.size () + sizeof (x)); \
1909                    memcpy (&msg[msg.size () - sizeof (x)], &x, sizeof (x));
1910    STATEINFO
1911    #undef o
1912    write (child[0].pipe[1], &msg[0], msg.size ());
1913    // Avoid flicker here : gtk_widget_set_double_buffered
1914    //sleep (1);
1915    read (child[0].pipe[0], &msg[0], 4);
1916    /* Wait for finish to prevent queuing too many requests */
1917    return FALSE;
1918  }
1919  #endif
1920  GdkRectangle r =
1921    { 0, 0, draw->allocation.width, draw->allocation.height };
1922  gdk_window_begin_paint_rect (draw->window, &r);
1923
1924//  gdk_gc_set_clip_rectangle (mygc, &clip);
1925//  gdk_gc_set_foreground (mygc, &styleColour[0][0]);
1926//  gdk_gc_set_line_attributes (mygc,
1927//    1, GDK_LINE_SOLID, GDK_CAP_PROJECTING, GDK_JOIN_MITER);
1928   
1929//  clip.width = draw->allocation.width - ZOOM_PAD_SIZE;
1930//  gdk_gc_set_clip_rectangle (mygc, &clip);
1931 
1932  GdkRectangle clip;
1933  clip.x = 0;
1934  clip.y = 0;
1935
1936  PangoMatrix mat = PANGO_MATRIX_INIT;
1937  pc = gdk_pango_context_get_for_screen (gdk_screen_get_default ());
1938  pl = pango_layout_new (pc);
1939  pango_layout_set_width (pl, -1); // No wrapping 200 * PANGO_SCALE);
1940  if (Background == 0) {
1941    PangoAttribute *wit = pango_attr_foreground_new (0xffff, 0xffff, 0xffff);
1942    PangoAttrList *list = pango_attr_list_new ();//pango_layout_get_attributes (pl);
1943    pango_attr_list_insert (list, wit);
1944    pango_layout_set_attributes (pl, list);
1945    pango_attr_list_unref (list);
1946  }
1947/*    PangoAttribute *wit = pango_attr_background_new (0xffff, 0xffff, 0xffff);
1948    PangoAttrList *list = pango_attr_list_new ();//pango_layout_get_attributes (pl);
1949    pango_attr_list_insert (list, wit);
1950    pango_layout_set_attributes (pl, list);
1951    pango_attr_list_unref (list); */
1952#endif // GTK
1953  if (option == mapMode) ChangePak (NULL, clon, clat);
1954  // This call can be almost anywhere, e.g. SetLocation(). Calling it in
1955  // searchMode with GeoSearch may invalidate some of the results.
1956
1957  clip.height = draw->allocation.height;
1958  clip.width = draw->allocation.width;
1959 
1960  if (ButtonSize <= 0) ButtonSize = 4;
1961
1962  if (zoom < 0 || zoom > 1023456789) zoom = 1023456789;
1963  if (zoom / clip.width <= 1) zoom += 4000;
1964  int cosa = lrint (4294967296.0 * cosAzimuth * clip.width / zoom);
1965  int sina = lrint (4294967296.0 * sinAzimuth * clip.width / zoom);
1966  int xadj =
1967    clip.width / 2 - ((clon * (__int64) cosa + clat * (__int64) sina) >> 32);
1968  __int64 yadj =
1969    clip.height / 2 - ((clon * (__int64) sina - clat * (__int64) cosa) >> 32);
1970
1971  #define FAR3D  100000 // 3D view has a limit of roughly 5 km forwards
1972  #define WIDE3D 100000 // and roughly 5km between top left & top right corner
1973  #define CAMERA2C 20000 // How far the camera is behind the user (clat/lon)
1974  #define HEIGHT   12000 // Height of the camera
1975  #define PIX45     256 // Y value corresponding to 45 degrees down
1976  #define XFix PIX45
1977 
1978  #define MUL 64
1979  if (Display3D) {
1980    cosa = lrint (cosAzimuth * MUL);
1981    sina = lrint (sinAzimuth * MUL);
1982   
1983    #define myint int
1984    /* The 3D computations can all be done in signed 32 bits integers,
1985       provided overflow bits are simply discarded. The C specification says
1986       however that ints that overflow are undefined (as well as any
1987       expression that touches them). So if the 3D display looks garbled
1988       under a new compiler, try running with #define myint __int64
1989    */
1990   
1991    yadj = (clon + (int)(sinAzimuth * CAMERA2C)) * (myint) sina -
1992           (clat - (int)(cosAzimuth * CAMERA2C)) * (myint) cosa;
1993    xadj = -(clon + (int)(sinAzimuth * CAMERA2C)) * (myint) cosa -
1994            (clat - (int)(cosAzimuth * CAMERA2C)) * (myint) sina;
1995  }
1996  #define Depth(lon,lat) \
1997    (int)(yadj + (lat) * (myint) cosa - (lon) * (myint) sina)
1998  #define X1(lon,lat) \
1999    (int)(xadj + (lon) * (myint) cosa + (lat) * (myint) sina)
2000  #define AdjDepth(lon,lat) (Depth (lon, lat) < PIX45 * HEIGHT * MUL / 5000 \
2001    && Depth (lon, lat) > -PIX45 * HEIGHT * MUL / 5000 ? \
2002    PIX45 * HEIGHT * MUL / 5000 : Depth (lon, lat))
2003  #define Y(lon,lat) (Display3D ? PIX45 * HEIGHT * MUL / AdjDepth (lon, lat) \
2004  : yadj + (int)(((lon) * (__int64) sina - (lat) * (__int64) cosa) >> 32))
2005  #define X(lon,lat) (Display3D ? clip.width / 2 + \
2006   ((AdjDepth (lon, lat) > 0 ? 1 : -1) * \
2007      (X1 (lon, lat) / 32000 - AdjDepth (lon, lat) / XFix) > 0 ? 32000 : \
2008    (AdjDepth (lon, lat) > 0 ? 1 : -1) * \
2009      (X1 (lon, lat) / 32000 + AdjDepth (lon, lat) / XFix) < 0 ? -32000 : \
2010   X1(lon,lat) / (AdjDepth (lon, lat) / XFix)) \
2011  : xadj + (int)(((lon) * (__int64) cosa + (lat) * (__int64) sina) >> 32))
2012
2013  /* These macros calling macros may result in very long bloated code and
2014     inefficient machine code, depending on how well the compiler optimizes.
2015  */
2016
2017  if (option == mapMode) {
2018//    int perpixel = zoom / clip.width;
2019    int *block = (int*) calloc ((clip.width + 31) / 32 * 4, clip.height);
2020
2021    stack<text2Brendered> text2B;
2022    text2B.push (text2Brendered ()); // Always have a spare one open
2023    vector<ndType*> area;
2024    stack<ndType*> dlist[12];
2025    // 5 under + 1 gound level + 5 above + icons
2026   
2027    if (ShowCompass) {
2028      for (int i = 0; i < 2; i++) {
2029        for (int m = -20; m <= 20; m += 40) {
2030          text2B.top ().s = m < 0 ? (i ? "N" : "W") : i ? "S" : "E";
2031          text2B.top ().x = clip.width - 40 +
2032            lrint ((i ? -sinAzimuth : cosAzimuth) * m) - 50;
2033          text2B.top ().x2 = text2B.top ().x + 100;
2034          text2B.top ().dst = 100;
2035          text2B.top ().y2 = text2B.top ().y = clip.height - 40 +
2036            lrint ((i ? cosAzimuth : sinAzimuth) * m);
2037          text2B.push (text2Brendered ());
2038        }
2039      }
2040    }
2041   
2042    // render map
2043    /* We need the smallest bbox that covers the test area. For 2D, the
2044       test area is a rectangle that is not aligned with the axis, so the
2045       bbox is the maxs and mins of the latitudes and longitudes of the 4
2046       corners. For 3D, the test area is a triangle, with the camera
2047       coordinate included twice, hence 4 tests
2048    */
2049    int latRadius[2] = { 0, 0 }, lonRadius[2] = { 0, 0 };
2050    for (int wc = -1; wc <= 1; wc += 2) { // width and
2051      for (int hc = -1; hc <= 1; hc += 2) { // height coefficients
2052        int w = !Display3D ? zoom : hc > 0 ? WIDE3D : 0, h = !Display3D
2053          ? zoom / clip.width * clip.height : hc > 0 ? FAR3D : CAMERA2C;
2054        int lon = lrint (w * cosAzimuth * wc - h * sinAzimuth * hc);
2055        int lat = lrint (h * cosAzimuth * hc + w * sinAzimuth * wc);
2056        lonRadius[0] = min (lonRadius[0], lon);
2057        lonRadius[1] = max (lonRadius[1], lon);
2058        latRadius[0] = min (latRadius[0], lat);
2059        latRadius[1] = max (latRadius[1], lat);
2060      }
2061    }
2062    OsmItr itr (clon + lonRadius[0] - 1000, clat + latRadius[0] - 1000,
2063                clon + lonRadius[1] + 1000, clat + latRadius[1] + 1000);
2064    // Widen this a bit so that we render nodes that are just a bit offscreen ?
2065    while (Next (itr)) {
2066      ndType *nd = itr.nd[0];
2067      wayType *w = Way (nd);
2068
2069      if (Style (w)->scaleMax < zoom / clip.width * 350 / (DetailLevel + 6)
2070          && !Display3D && w->dlat < zoom / clip.width * 20 &&
2071                           w->dlon < zoom / clip.width * 20) continue;
2072      // With 3D, the icons are filtered only much later when we know z.
2073      if (nd->other[0] != 0) {
2074        nd = itr.nd[0] + itr.nd[0]->other[0];
2075        if (nd->lat == INT_MIN) nd = itr.nd[0]; // Node excluded from build
2076        else if (itr.left <= nd->lon && nd->lon < itr.right &&
2077            itr.top  <= nd->lat && nd->lat < itr.bottom) continue;
2078      } // Only process this way when the Itr gives us the first node, or
2079      // the first node that's inside the viewing area
2080      if (nd->other[0] == 0 && nd->other[1] == 0) dlist[11].push (nd);
2081      else if (Style (w)->areaColour != -1) area.push_back (nd);
2082      else dlist[Layer (w) + 5].push (nd);
2083    }
2084    qsort (&area[0], area.size (), sizeof (area[0]),
2085      (int (*)(const void *a, const void *b))WaySizeCmp);
2086    //for (; !dlist[0].empty (); dlist[0].pop ()) {
2087    //  ndType *nd = dlist[0].top ();
2088    for (; !area.empty(); area.pop_back ()) {
2089      ndType *nd = area.back ();
2090      wayType *w = Way (nd);
2091      while (nd->other[0] != 0) nd += nd->other[0];
2092      #if defined (_WIN32_CE) || defined (NOGTK)
2093      #define GdkPoint POINT
2094      #endif
2095      vector<GdkPoint> pt;
2096      int oldx = 0, oldy = 0, x = 0 /* Shut up gcc*/, y = 0 /*Shut up gcc*/;
2097      int firstx = INT_MIN, firsty = INT_MIN /* Shut up gcc */;
2098      for (; nd->other[1] != 0; nd += nd->other[1]) {
2099        if (nd->lat != INT_MIN) {
2100          pt.push_back (GdkPoint ());
2101          pt.back ().x = x = X (nd->lon, nd->lat);
2102          pt.back ().y = y = Y (nd->lon, nd->lat);
2103          if (Display3D) {
2104            if (firstx == INT_MIN) {
2105              firstx = x;
2106              firsty = y;
2107            }
2108            if (y > 0 && oldy < 0) {
2109              pt.back ().x = x + (x - oldx) * (1024 - y) / (y - oldy);
2110              pt.back ().y = 1024; // Insert modified instance of old point
2111              pt.push_back (GdkPoint ());
2112              pt.back ().x = x; // before current point.
2113              pt.back ().y = y;
2114            }
2115            else if (y < 0) {
2116              if (oldy < 0) pt.pop_back ();
2117              else {
2118                pt.back ().x = oldx + (oldx - x) * (1024 - oldy) / (oldy - y);
2119                pt.back ().y = 1024;
2120              }
2121            }
2122            oldx = x;
2123            oldy = y;
2124          }
2125          //pt[pts].x = X (nd->lon, nd->lat);
2126          //pt[pts++].y = Y (nd->lon, nd->lat);
2127        }
2128      }
2129     
2130      if (Display3D && y < 0 && firsty > 0) {
2131        pt.push_back (GdkPoint ());
2132        pt.back ().x = firstx + (firstx - x) * (1024 - firsty) / (firsty - y);
2133        pt.back ().y = 1024;
2134      }
2135      if (Display3D && firsty < 0 && y > 0) {
2136        pt.push_back (GdkPoint ());
2137        pt.back ().x = x + (x - firstx) * (1024 - y) / (y - firsty);
2138        pt.back ().y = 1024;
2139      }
2140      if (!pt.empty ()) {
2141        #ifdef NOGTK
2142        SelectObject (mygc, brush[StyleNr (w)]);
2143        SelectObject (mygc, pen[StyleNr (w)]);
2144        Polygon (mygc, &pt[0], pt.size ());
2145        #else
2146        gdk_gc_set_foreground (mygc, &styleColour[Style (w) - style][0]);
2147        gdk_draw_polygon (draw->window, mygc, TRUE, &pt[0], pt.size ());
2148        gdk_gc_set_foreground (mygc, &styleColour[Style (w) - style][1]);
2149        gdk_gc_set_line_attributes (mygc, Style (w)->lineWidth,
2150          Style (w)->dashed ? GDK_LINE_ON_OFF_DASH
2151          : GDK_LINE_SOLID, GDK_CAP_PROJECTING, GDK_JOIN_MITER);
2152        gdk_draw_polygon (draw->window, mygc, FALSE, &pt[0], pt.size ());
2153        #endif
2154        // Text placement: The basic idea is here : http://alienryderflex.com/polygon_fill/
2155        text2B.top ().dst = strcspn ((char*)(w + 1) + 1, "\n") * 9;
2156        text2B.top ().x = -1;
2157        for (unsigned i = 0; i < pt.size (); i++) {
2158          int iy = (pt[i].y + pt[i < pt.size () - 1 ? i + 1 : 0].y) / 2;
2159          // Look for a large horisontal space inside the poly at this y value
2160          vector<int> nx;
2161          for (unsigned j = 0, k = pt.size () - 1; j < pt.size (); j++) {
2162            if ((pt[j].y < iy && pt[k].y >= iy) || (pt[k].y < iy && pt[j].y >= iy)) {
2163              nx.push_back (pt[j].x + (pt[k].x - pt[j].x) * (iy - pt[j].y) /
2164                (pt[k].y - pt[j].y));
2165            }
2166            k = j;
2167          }
2168          sort (nx.begin (), nx.end ());
2169          for (unsigned int j = 0; j < nx.size (); j += 2) {
2170            if (nx[j + 1] - nx[j] > text2B.top ().dst) {
2171              text2B.top ().x = nx[j];
2172              text2B.top ().x2 = nx[j + 1];
2173              text2B.top ().y = iy - 5;
2174              text2B.top ().dst = nx[j + 1] - nx[j];
2175            }
2176          }
2177        }
2178        if (text2B.top ().x >= 0) {
2179          text2B.top ().y2 = text2B.top ().y;
2180          text2B.top ().s = (char*)(w + 1) + 1;
2181          text2B.push (text2Brendered ());
2182        }
2183      } // Polygon not empty
2184    } // For each area
2185
2186    queue<linePtType> q;
2187    for (int l = 0; l < 12; l++) {
2188      for (; !dlist[l].empty (); dlist[l].pop ()) {
2189        ndType *nd = dlist[l].top ();
2190        wayType *w = Way (nd);
2191
2192        int best = 30;
2193        int len = strcspn ((char *)(w + 1) + 1, "\n");
2194       
2195        // single-point node
2196        if (nd->other[0] == 0 && nd->other[1] == 0) {
2197          int x = X (nd->lon, nd->lat), y = Y (nd->lon, nd->lat);
2198          int *icon = Style (w)->x + 4 * IconSet, wd = icon[2], ht = icon[3];
2199          if ((!Display3D || y > Style (w)->scaleMax / 400) && !TestOrSet (
2200                      block, FALSE, x - wd / 2, y - ht / 2, 0, ht, wd, 0)) {
2201            TestOrSet (block, TRUE, x - wd / 2, y - ht / 2, 0, ht, wd, 0);
2202            DrawPoI (x, y, Style (w)->x + 4 * IconSet);
2203           
2204            #if 0 //def NOGTK
2205            SelectObject (mygc, sysFont);
2206            //SetBkMode (mygc, TRANSPARENT);
2207            const unsigned char *sStart = (const unsigned char *)(w + 1) + 1;
2208            UTF16 *tStart = (UTF16 *) wcTmp;
2209            if (ConvertUTF8toUTF16 (&sStart,  sStart + len, &tStart, tStart +
2210                  sizeof (wcTmp) / sizeof (wcTmp[0]), lenientConversion)
2211                == conversionOK) {
2212              ExtTextOutW (mygc, x - len * 3, y + icon[3] / 2, 0, NULL,
2213                  wcTmp, (wchar_t *) tStart - wcTmp, NULL);
2214            }
2215            #endif
2216            text2B.top ().x = x - 100;
2217            text2B.top ().x2 = x + 100;
2218            text2B.top ().dst = 200;
2219            text2B.top ().y2 = text2B.top ().y = y +
2220                               Style (w)->x[IconSet * 4 + 3] / 2;
2221            if (Sqr ((__int64) Style (w)->scaleMax / 2 /
2222                (zoom / clip.width)) * DetailLevel > len * len * 100 &&
2223                len > 0) best = 0;
2224          }
2225        }
2226        // ways (including areas on WinMob : FIXME)
2227        else if (nd->other[1] != 0) {
2228          // perform validation (on non-areas)
2229          bool valid;
2230          if (ValidateMode && Style(w)->areaColour == -1) {
2231            valid = len > 0 && StyleNr (w) != highway_road;
2232            // most ways should have labels and they should not be
2233            // highway=road
2234           
2235            // valid = valid && ... (add more validation here)
2236
2237            // // LOG
2238            // logprintf("valid = (len > 0) = %d > 0 = %d (%s)\n",
2239            //      len,valid,(char *)(w + 1) + 1);
2240
2241          } else {
2242            valid = true; 
2243          }
2244          if (highlight != "") {
2245            for (char *ptr = (char *)(w + 1) + 1; valid && *ptr != '\0'; ) {
2246              if (strncmp (highlight.c_str (), ptr, strcspn (ptr, "\n"))
2247                  == 0) valid = false;
2248              while (*ptr != '\0' && *ptr++ != '\n') {}
2249            } // Should highlighting get its own pen ?
2250          }
2251          // two stages -> validate (if needed) then normal rendering
2252          ndType *orig = nd;
2253          for (int stage = ( valid ? 1 : 0);stage<2;stage++) {
2254            nd = orig;
2255            if (stage==0) {
2256            #ifndef NOGTK
2257              gdk_gc_set_foreground (mygc,
2258                &styleColour[firstElemStyle + ValidateModeNum][1]); //&validateColour);
2259              gdk_gc_set_line_attributes (mygc, 10,
2260                       GDK_LINE_SOLID, GDK_CAP_PROJECTING, GDK_JOIN_MITER);
2261            #else
2262              SelectObject (mygc, pen[firstElemStyle + ValidateModeNum]);
2263                //pen[VALIDATE_PEN]);
2264            #endif
2265            }
2266            else if (stage == 1) {
2267              #ifndef NOGTK
2268              gdk_gc_set_foreground (mygc, &styleColour[Style (w) - style][1]);
2269              gdk_gc_set_line_attributes (mygc, Style (w)->lineWidth,
2270                    Style (w)->dashed ? GDK_LINE_ON_OFF_DASH
2271                    : GDK_LINE_SOLID, GDK_CAP_PROJECTING, GDK_JOIN_MITER);
2272              #else
2273              SelectObject (mygc, pen[StyleNr (w)]);
2274              #endif
2275            }
2276            int oldx = X (nd->lon, nd->lat), oldy = Y (nd->lon, nd->lat);
2277            int cumulative = 0;
2278            q.push (linePtType (oldx, oldy, cumulative));
2279            do {
2280              ndType *next = nd + nd->other[1];
2281              if (next->lat == INT_MIN) break; // Node excluded from build
2282              int x = X (next->lon, next->lat), x2;
2283              int y = Y (next->lon, next->lat), y2;
2284//            printf ("%6.0lf %6.0lf - %6.0lf %6.0lf - %lf\n", x, y, oldx, oldy,
2285//              AdjDepth (next->lon, next->lat));
2286              if ((x <= clip.width || oldx <= clip.width) &&
2287                  (x >= 0 || oldx >= 0) && (y >= 0 || oldy >= 0) &&
2288                  (y <= clip.height || oldy <= clip.height)) {
2289//                printf ("%4d %4d - %4d %4d\n", x,y,oldx,oldy);
2290                /* If we're doing 3D and oldy is negative, it means the point
2291                   was behind the camera. Then we must draw an infinitely long
2292                   line from (x,y) with the same gradient as (x,y)-(oldx,oldy),
2293                   but away from (oldx,oldy). Or at least up to some y value
2294                   below the bottom of the screen. So we adjust oldx and oldy.
2295                   
2296                   When y is negative, we do something very similar. */
2297                if (!Display3D || y > 0) {
2298                  x2 = x;
2299                  y2 = y;
2300                  if (Display3D && oldy <= 0) {
2301                 /*   if (nx < 32760 && nx > -32760 &&
2302                      oldx < 32760 && oldx > -32760 &&
2303                      oldy < 32760 && oldy > -32760) */
2304                    oldx = x + (x - oldx) * (clip.height + 10 - y) /
2305                      (y - oldy);
2306                    oldy = clip.height + 10;
2307                  }
2308                }
2309                else /*if (oldy > 0 which is true)*/ {
2310/*                  if (nx < 32760 && nx > -32760 &&
2311                    oldx < 32760 && oldx > -32760 &&
2312                    oldy < 32760 && oldy > -32760) */
2313                  x2 = oldx +
2314                    (oldx - x) * (clip.height + 10 - oldy) / (oldy - y);
2315                  y2 = clip.height + 10;
2316                }
2317                gdk_draw_line (draw->window, mygc, oldx, oldy, x2, y2);
2318                // Draw3DLine
2319                if (oldx < 0 || oldx >= clip.width ||
2320                    oldy < 0 || oldy >= clip.height) {
2321                  cumulative += 9999; // Insert a break in the queue
2322                  q.push (linePtType (oldx, oldy, cumulative));
2323                  // TODO: Interpolate the segment to get a point that is
2324                  // closer to the screen. The same applies to the other push
2325                }
2326                cumulative += isqrt (Sqr (oldx - x2) + Sqr (oldy - y2));
2327                q.push (linePtType (x2, y2, cumulative));
2328                ConsiderText (&q, FALSE, len, &best, &text2B.top ());
2329              }
2330              nd = next;
2331              oldx = x;
2332              oldy = y;
2333            } while (itr.left <= nd->lon && nd->lon < itr.right &&
2334                     itr.top  <= nd->lat && nd->lat < itr.bottom &&
2335                     nd->other[1] != 0);
2336            ConsiderText (&q, TRUE, len, &best, &text2B.top ());
2337          }
2338        } /* If it has one or more segments */
2339         
2340        if (best < 30) {
2341          text2B.top ().s = (char *)(w + 1) + 1;
2342          text2B.push (text2Brendered ());
2343        }
2344      } /* for each way / icon */
2345    } // For each layer
2346  //  gdk_gc_set_foreground (draw->style->fg_gc[0], &highwayColour[rail]);
2347  //  gdk_gc_set_line_attributes (draw->style->fg_gc[0],
2348  //    1, GDK_LINE_SOLID, GDK_CAP_PROJECTING, GDK_JOIN_MITER);
2349
2350    // render route
2351    routeNodeType *rt;
2352    if (shortest && (rt = shortest->shortest)) {
2353      double len;
2354      int nodeCnt = 1, x = X (rt->nd->lon, rt->nd->lat);
2355      int y = Y (rt->nd->lon, rt->nd->lat);
2356      __int64 sumLat = rt->nd->lat;
2357      #ifndef NOGTK
2358      gdk_gc_set_foreground (mygc,
2359        &styleColour[firstElemStyle + StartRouteNum][1]); //routeColour);
2360      gdk_gc_set_line_attributes (mygc, 6,
2361        GDK_LINE_SOLID, GDK_CAP_PROJECTING, GDK_JOIN_MITER);
2362      #define CHARWIDTH 12
2363      #else
2364      SelectObject (mygc, pen[firstElemStyle + StartRouteNum]);
2365      #define CHARWIDTH 6
2366      #endif
2367      if (routeSuccess) Draw3DLine (X (flon, flat), Y (flon, flat), x, y);
2368     
2369      len = sqrt (Sqr ((double) (rt->nd->lat - flat)) +
2370        Sqr ((double) (rt->nd->lon - flon)));
2371      for (; rt->shortest; rt = rt->shortest) {
2372        int nx = X (rt->shortest->nd->lon, rt->shortest->nd->lat);
2373        int ny = Y (rt->shortest->nd->lon, rt->shortest->nd->lat);
2374        if ((nx >= 0 || x >= 0) && (nx < clip.width || x < clip.width) &&
2375            (ny >= 0 || y >= 0) && (ny < clip.height || y < clip.height)) {
2376          // Gdk looks only at the lower 16 bits ?
2377          Draw3DLine (x, y, nx, ny);
2378        }
2379        len += sqrt (Sqr ((double) (rt->nd->lat - rt->shortest->nd->lat)) +
2380          Sqr ((double) (rt->nd->lon - rt->shortest->nd->lon)));
2381        sumLat += rt->nd->lat;
2382        nodeCnt++;
2383        x = nx;
2384        y = ny;
2385      }
2386      Draw3DLine (x, y, X (tlon, tlat), Y (tlon, tlat));
2387      len += sqrt (Sqr ((double) (rt->nd->lat - tlat)) +
2388        Sqr ((double) (rt->nd->lon - tlon)));
2389      char distStr[13];
2390      sprintf (distStr, "%.3lf km", len * (20000 / 2147483648.0) *
2391        cos (LatInverse (sumLat / nodeCnt) * (M_PI / 180)));
2392      DrawString (clip.width - CHARWIDTH * strlen (distStr), 10, distStr);
2393      #if 0 //ndef NOGTK
2394      gdk_draw_string (draw->window, f, fg_gc, //draw->style->fg_gc[0],
2395        clip.width - 7 * strlen (distStr), 10, distStr);
2396      #else
2397      #endif
2398    }
2399    DrawPoI (X (flon, flat), Y (flon, flat), IconSet * 4 +
2400      style[firstElemStyle + StartRouteNum].x);
2401    DrawPoI (X (tlon, tlat), Y (tlon, tlat), IconSet * 4 +
2402      style[firstElemStyle + EndRouteNum].x);
2403    #ifndef NOGTK
2404    for (deque<wayPointStruct>::iterator w = wayPoint.begin ();
2405         w != wayPoint.end (); w++) {
2406      DrawPoI (X (w->lon, w->lat), Y (w->lon, w->lat),
2407        style[firstElemStyle + wayPointIconNum].x);
2408    }
2409   
2410    for (int i = 1; shortest && ShowActiveRouteNodes && i < routeHeapSize; i++) {
2411      gdk_draw_line (draw->window, mygc,
2412        X (routeHeap[i].r->nd->lon, routeHeap[i].r->nd->lat) - 2,
2413        Y (routeHeap[i].r->nd->lon, routeHeap[i].r->nd->lat),
2414        X (routeHeap[i].r->nd->lon, routeHeap[i].r->nd->lat) + 2,
2415        Y (routeHeap[i].r->nd->lon, routeHeap[i].r->nd->lat));
2416    }
2417    #else
2418    for (int j = 0; j <= newWayCnt; j++) {
2419      int x = X (newWays[j].coord[0][0], newWays[j].coord[0][1]);
2420      int y = Y (newWays[j].coord[0][0], newWays[j].coord[0][1]);
2421      if (newWays[j].cnt == 1) {
2422        int *icon = style[j < newWayCnt ? newWays[j].klas : place_village].x
2423          + 4 * IconSet;
2424        gdk_draw_drawable (draw->window, mygc, icons, icon[0], icon[1],
2425          x - icon[2] / 2, y - icon[3] / 2, icon[2], icon[3]);
2426      }
2427      else {
2428        SelectObject (mygc, pen[j < newWayCnt ? newWays[j].klas: 0]);
2429        MoveToEx (mygc, x, y, NULL);
2430        for (int i = 1; i < newWays[j].cnt; i++) {
2431          LineTo (mygc, X (newWays[j].coord[i][0], newWays[j].coord[i][1]),
2432                        Y (newWays[j].coord[i][0], newWays[j].coord[i][1]));
2433        }
2434      }
2435    }
2436    if (ShowTrace) {
2437      for (gpsNewStruct *ptr = gpsTrack; ptr < gpsNew; ptr++) {
2438        SetPixel (mygc, X (ptr->lon, ptr->lat), Y (ptr->lon, ptr->lat), 0);
2439      }
2440    }
2441    #endif
2442    text2B.pop ();
2443    while (!text2B.empty ()) {
2444      text2Brendered *t = &text2B.top();
2445      #ifdef PANGO_VERSION
2446      PangoRectangle rect;
2447      #else
2448      struct { int width, height; } rect;
2449      struct { double xx, xy, yy, yx; } mat;
2450      #endif
2451      int x0 = (t->x + t->x2) / 2, y0 = (t->y + t->y2) / 2;
2452      mat.yy = mat.xx = fabs (t->x - t->x2) / (double) t->dst;
2453      mat.xy = (t->y - t->y2) / (double)(t->x > t->x2 ? -t->dst : t->dst);
2454      mat.yx = -mat.xy;
2455
2456      double move = 0.6;
2457      for (const char *txt = t->s; *txt != '\0';) {
2458        #if PANGO_VERSION
2459        pango_context_set_matrix (pc, &mat);
2460        pango_layout_set_text (pl,
2461          string (txt, strcspn (txt, "\n")).c_str (), -1);
2462        pango_layout_get_pixel_extents (pl, &rect, NULL);
2463        #else
2464        rect.width = strcspn (txt, "\n") * 9;
2465        rect.height = 11;
2466        #endif
2467        y0 += lrint (mat.xx * (rect.height + 3) * move);
2468        x0 += lrint (mat.xy * (rect.height + 3) * move);
2469        move = 1.2;
2470        if (TestOrSet (block, FALSE, 
2471          lrint (x0 - rect.width * mat.xx / 2 - mat.xy * rect.height / 3),
2472          lrint (y0 - rect.width * mat.yx / 2 - mat.yy * rect.height / 3),
2473          lrint (mat.xy * (rect.height)), lrint (mat.xx * (rect.height)),
2474          lrint (mat.xx * (rect.width + 10)),
2475          lrint (mat.yx * (rect.width + 10)))) break;
2476        TestOrSet (block, TRUE, 
2477          lrint (x0 - rect.width * mat.xx / 2 - mat.xy * rect.height / 3),
2478          lrint (y0 - rect.width * mat.yx / 2 - mat.yy * rect.height / 3),
2479          lrint (mat.xy * (rect.height)), lrint (mat.xx * (rect.height)),
2480          lrint (mat.xx * (rect.width + 10)),
2481          lrint (mat.yx * (rect.width + 10)));
2482        #ifndef NOGTK
2483        gdk_draw_layout (GDK_DRAWABLE (draw->window),
2484          fg_gc /*draw->style->fg_gc[0]*/,
2485          x0 - (rect.width * mat.xx + rect.height * fabs (mat.xy)) / 2,
2486          y0 - (rect.height * mat.yy + rect.width * fabs (mat.xy)) / 2, pl);
2487        #else
2488        double hoek = atan2 (t->y2 - t->y, t->x - t->x2);
2489        if (t->x2 < t->x) hoek += M_PI;
2490        logFont.lfEscapement = logFont.lfOrientation = 1800 + int ((1800 / M_PI) * hoek);
2491       
2492        HFONT customFont = CreateFontIndirect (&logFont);
2493        HGDIOBJ oldf = SelectObject (mygc, customFont);
2494        //SetBkMode (mygc, TRANSPARENT);
2495        const unsigned char *sStart = (const unsigned char *) txt;
2496        UTF16 *tStart = (UTF16 *) wcTmp;
2497        int len = strcspn (txt, "\n");
2498        if (ConvertUTF8toUTF16 (&sStart,  sStart + len, &tStart,
2499              tStart + sizeof (wcTmp) / sizeof (wcTmp[0]),  lenientConversion)
2500            == conversionOK) {
2501          ExtTextOutW (mygc,
2502                x0 - lrint (len * 4 * mat.xx + rect.height / 2 * mat.xy),
2503                y0 + lrint (len * 4 * mat.xy - rect.height / 2 * mat.xx), 
2504                0, NULL, wcTmp, (wchar_t *) tStart - wcTmp, NULL);
2505        }
2506        SelectObject (mygc, oldf);
2507        DeleteObject (customFont);
2508        #endif
2509        if (zoom / clip.width > 20) break;
2510        while (*txt != '\0' && *txt++ != '\n') {}
2511      }
2512      text2B.pop ();
2513    }
2514    for (deque<tsItem>::iterator i = tsList.begin (); i != tsList.end (); i++) {
2515      int x, y, j;
2516      const char *p = i->cmd.c_str ();
2517      for (j = 0; j < 2; j++) {
2518        double coef = atof (p);
2519        p += strcspn (p, "whWH;");
2520        (j ? y : x) = lrint (*p == ';' ? coef : atoi (p + 1) + 
2521          coef * (j ? draw->allocation.height : draw->allocation.width));
2522        while (*p != '\0' && *p++ != ';') {}
2523      //char *xs = i->cmd.c_str (), *ys = xs + strcspn (xs, ";");
2524      }
2525      #ifndef NOGTK
2526      gdk_draw_pixbuf (GDK_DRAWABLE (draw->window), fg_gc, i->pix, 0, 0, x, y,
2527        gdk_pixbuf_get_width (i->pix), gdk_pixbuf_get_height (i->pix), GDK_RGB_DITHER_NONE, 0, 0);
2528      #else
2529      #endif
2530    }
2531    free (block);
2532    if (FollowGPSr && command[0] && command[0] == command[1] && command[0] == command[2]) {
2533      DrawPoI (draw->allocation.width / 2, draw->allocation.height / 6,
2534        style[firstElemStyle + command[0]].x + 8); // Always square.big
2535    }
2536  } // Not in the menu
2537  else if (option == searchMode) {
2538    for (int i = 0, y = SearchSpacing / 2; i < searchCnt && gosmSstr[i];
2539             i++, y += SearchSpacing) {
2540      DrawPoI (SearchSpacing / 2, y, Style (gosmSway[i])->x + 4 * IconSet);
2541     
2542      double dist = sqrt (Sqr ((__int64) clon - gosmSway[i]->clon) +
2543          Sqr ((__int64) clat - gosmSway[i]->clat)) * (20000 / 2147483648.0) *
2544        cos (LatInverse (clat) * (M_PI / 180));
2545      char distance[10]; // Formula inaccurate over long distances hence "Far"
2546      sprintf (distance, dist > 998 ? "Far" : dist > 1 ? "%.0lf km" :
2547        "%.0lf m", dist > 1 ? dist : dist * 1000);
2548      DrawString (SearchSpacing + 33 - 11 * strcspn (distance, " "), y - 10,
2549        distance); // Right adjustment is inaccurate
2550     
2551      #ifndef NOGTK
2552      gdk_gc_set_foreground (mygc, &resultArrowColour);
2553      gdk_gc_set_line_attributes (mygc, 1,
2554        GDK_LINE_SOLID, GDK_CAP_PROJECTING, GDK_JOIN_MITER);
2555      #else
2556      SelectObject (mygc, GetStockObject (BLACK_PEN));
2557      #endif
2558      int x = SearchSpacing + 70;
2559      __int64 lx = X (gosmSway[i]->clon, gosmSway[i]->clat) - clip.width / 2;
2560      __int64 ly = Y (gosmSway[i]->clon, gosmSway[i]->clat) - clip.height / 2;
2561      double norm = lx || ly ? sqrt (lx * lx + ly * ly) / 64 : 1;
2562      int u = lrint (lx / norm), v = lrint (ly / norm);
2563      gdk_draw_line (draw->window, mygc, x + u / 8, y + v / 8,
2564        x - u / 8, y - v / 8);
2565      gdk_draw_line (draw->window, mygc, x + u / 8, y + v / 8, 
2566        x + u / 12 + v / 20, y - u / 20 + v / 12);
2567      gdk_draw_line (draw->window, mygc, x + u / 8, y + v / 8,
2568        x + u / 12 - v / 20, y + u / 20 + v / 12);
2569           
2570      string s (gosmSstr[i], strcspn (gosmSstr[i], "\n"));
2571      char *name = (char *)(gosmSway[i] + 1) + 1;
2572      if (name != gosmSstr[i]) s += " (" +
2573                     string (name, strcspn (name, "\n")) + ")";
2574      DrawString (SearchSpacing + x, y - 10, s.c_str ());
2575
2576      gdk_draw_line (draw->window, mygc, 0, y + SearchSpacing / 2,
2577        clip.width, y + SearchSpacing / 2);
2578    }
2579    HandleKeyboard (NULL);
2580  }
2581  else if (option == optionMode) {
2582    for (int i = 0; i < wayPointIconNum; i++) {
2583      if (CompactOptions) {
2584        char l1[20], *s = (char*) optionNameTable[English][i], *ptr = s + 1;
2585        while (!isspace (*ptr) && !isupper (*ptr) && *ptr) ptr++;
2586        sprintf (l1, "%.*s", ptr - s, s);
2587        DrawString (ListXY (i, FALSE) - strlen (l1) * 5, ListXY (i, TRUE) - 8, l1);
2588        DrawString (ListXY (i, FALSE) - strlen (ptr) * 5, ListXY (i, TRUE) + 8, ptr);
2589      }
2590      else {
2591        DrawPoI (ListXY (i, FALSE), ListXY (i, TRUE) - 5,
2592          style[firstElemStyle + i].x); // Always classic.big
2593        DrawString (ListXY (i, FALSE) - strlen (optionNameTable[English][i]) *
2594          5, ListXY (i, TRUE) + style[firstElemStyle + i].x[3] / 2 - 5,
2595          optionNameTable[English][i]);
2596      }
2597    }
2598  }
2599  else {
2600    char optStr[30];
2601    if (option == VehicleNum) {
2602      #define M(v) Vehicle == v ## R ? #v :
2603      sprintf (optStr, "%s : %s", optionNameTable[English][option],
2604        RESTRICTIONS NULL);
2605      #undef M
2606    }
2607    else sprintf (optStr, "%s : %d", optionNameTable[English][option],
2608    #define o(en,min,max) option == en ## Num ? en :
2609    OPTIONS
2610    #undef o
2611      0);
2612    DrawString (50, draw->allocation.height / 2, optStr);
2613  }
2614  #ifndef NOGTK
2615  /* Buttons now on the top row
2616  gdk_draw_rectangle (draw->window, draw->style->bg_gc[0], TRUE,
2617    clip.width - ButtonSize * 20, clip.height - ButtonSize * 60,
2618    clip.width, clip.height);
2619  for (int i = 0; i < 3; i++) {
2620    gdk_draw_string (draw->window, f, draw->style->fg_gc[0],
2621      clip.width - ButtonSize * 10 - 5, clip.height + (f->ascent - f->descent)
2622      / 2 - ButtonSize * (20 * i + 10), i == 0 ? "O" : i == 1 ? "-" : "+");
2623  }
2624  */
2625  gdk_window_end_paint (draw->window);
2626  gdk_flush ();
2627  #else
2628  #ifdef NOGTK
2629  int i = (Layout > (MenuKey == 0 || option != mapMode ? 0 : 1) ? 3 : 0);
2630  RECT r;
2631  r.left = clip.width - ButtonSize * 20;
2632  r.top = clip.height - ButtonSize * 20 * i;
2633  r.right = clip.width;
2634  r.bottom = clip.height;
2635  FillRect (mygc, &r, (HBRUSH) GetStockObject(LTGRAY_BRUSH));
2636  SelectObject (mygc, sysFont);
2637  //SetBkMode (mygc, TRANSPARENT);
2638  while (--i >= 0) {
2639    ExtTextOut (mygc, clip.width - ButtonSize * 10 - 5, clip.height - 5 -
2640        ButtonSize * (20 * i + 10), 0, NULL, i == 0 ? TEXT ("O") :
2641        i == 1 ? TEXT ("-") : TEXT ("+"), 1, NULL);
2642  }
2643  #endif
2644
2645  char coord[64];
2646  if (ShowCoordinates == 1) {
2647    if(GpsIdle==999)
2648      snprintf (coord, 63, "%9.5lf %10.5lf GPS OFF %s", LatInverse (clat), LonInverse (clon),routeSuccess?"Route":"No Route");
2649    else
2650      snprintf (coord, 63, "%9.5lf %10.5lf GPS idle %ds %s", LatInverse (clat), LonInverse (clon),GpsIdle,routeSuccess?"Route":"No Route");
2651    DrawString (0, 5, coord);
2652  }
2653  else if (ShowCoordinates == 2) {
2654    MEMORYSTATUS memStat;
2655    GlobalMemoryStatus (&memStat);
2656    sprintf (coord, "%9d", memStat.dwAvailPhys );
2657    DrawString (0, 5, coord);
2658    //ExtTextOut (mygc, 0, 10, 0, NULL, coord, 9, NULL);
2659  }
2660  #endif
2661  #ifdef CAIRO_VERSION
2662//  cairo_destroy (cai);
2663  #endif
2664/*
2665  clip.height = draw->allocation.height;
2666  gdk_gc_set_clip_rectangle (draw->style->fg_gc[0], &clip);
2667  gdk_draw_string (draw->window, f, draw->style->fg_gc[0],
2668    clip.width/2, clip.height - f->descent, "gosmore");
2669  */
2670  return FALSE;
2671}
2672
2673#ifndef NOGTK
2674GtkWidget *searchW;
2675
2676int ToggleSearchResults (void)
2677{
2678  option = option == searchMode ? mapMode : searchMode;
2679  highlight = string ();
2680  gtk_widget_queue_clear (draw);
2681  return FALSE;
2682}
2683
2684int IncrementalSearch (void)
2685{
2686  option = searchMode;
2687  GeoSearch ((char*) gtk_entry_get_text (GTK_ENTRY (searchW)));
2688  gtk_widget_queue_clear (draw);
2689  return FALSE;
2690}
2691
2692void HitGtkButton (GtkWidget * /*w*/, void *data)
2693{
2694  HitButton ((intptr_t)data);
2695  gtk_widget_queue_clear (draw);
2696}
2697
2698//------------------------------------------------------------------------
2699// Callbacks that are called with the user drops an icon and then binds it
2700static gboolean DropOnDraw (GtkWidget *w, GdkDragContext *c, gint /*x*/,
2701  gint /* y */, guint time, gpointer)
2702{
2703  if (c->targets) {
2704    gtk_drag_get_data (w, c,
2705      GDK_POINTER_TO_ATOM (g_list_nth_data (c->targets, 0)), time);
2706  }
2707  return c->targets ? TRUE : FALSE;
2708}
2709
2710static void ReadTsList (void)
2711{
2712  FILE *fp = fopen ("main.ts", "r");
2713  gsize siz;
2714  for (; !tsList.empty (); tsList.pop_back()) g_object_unref (tsList.back().pix);
2715  while (fp && fread (&siz, sizeof (siz), 1, fp) == 1) {
2716    printf ("Reading %d\n", siz);
2717    void *buf = malloc (siz);
2718    fread (buf, siz, 1, fp);
2719    tsList.push_back (tsItem ());
2720    #ifndef NOGTK
2721    GInputStream *gis = g_memory_input_stream_new_from_data (buf, siz, NULL);
2722    GError *err = NULL;
2723    tsList.back ().pix = gdk_pixbuf_new_from_stream (gis, NULL, &err);
2724    #else
2725    tsList.back ().pix = CreateIconFromResource (buf, siz, TRUE, 0x30000);
2726    #endif
2727    int c; 
2728    while ((c = fgetc (fp)) != '\0') tsList.back ().cmd += c;
2729  }
2730  if (fp) fclose (fp);
2731}
2732
2733static void DropReceived (GtkWidget */*draw*/, GdkDragContext *c, gint x,
2734  gint y, GtkSelectionData *sdata, guint /*ttype*/, guint time, gpointer)
2735{
2736  //FILE *fp;
2737  gchar **arr = sdata != NULL ? gtk_selection_data_get_uris (sdata) : NULL;
2738  gchar *f = arr ? g_filename_from_uri (arr[0], NULL, NULL) : NULL;
2739//  printf ("x%sx\n", f); //gtk_selection_data_get_text (sdata));
2740//  printf ("%p\n", gtk_selection_data_get_pixbuf (sdata));
2741//  if (sdata != NULL && sdata->length >= 7) printf ("%s\n----%s----\n", sdata->data,
2742//    string ((char*) sdata->data + 7, strcspn ((char*) sdata->data + 7, "\n\r")).c_str());
2743
2744  GError *err = NULL;
2745  tsList.push_back (tsItem ());
2746  if ((f && (tsList.back ().pix = gdk_pixbuf_new_from_file (f, &err)))
2747      || (tsList.back ().pix = gtk_selection_data_get_pixbuf (sdata))) {
2748    //if (ttype == 0) printf ("Data=%s\n", sdata->data);
2749    printf ("C %p\n", tsList.back().pix);
2750    char a[40];
2751    if (draw->allocation.width <= x * 2) x -= draw->allocation.width;
2752    if (draw->allocation.height <= y * 2) y -= draw->allocation.height;
2753    sprintf (a, "%s %+d; %s %+d; ;", x < 0 ? "1.0 w" : "0.0 w", x,
2754                                     y < 0 ? "1.0 h" : "0.0 h", y);
2755    tsList.back ().cmd = string (a);
2756   
2757    GtkWidget *dialog, *dedit;
2758    dialog = gtk_dialog_new_with_buttons ("Set icon data", NULL, GTK_DIALOG_MODAL,
2759      GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, GTK_STOCK_DELETE, GTK_RESPONSE_NO,
2760      GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
2761    dedit = gtk_entry_new ();
2762    gtk_entry_set_text (GTK_ENTRY (dedit), tsList.back ().cmd.c_str ());
2763    gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), dedit);
2764    gtk_widget_show_all (dialog);
2765    int result = gtk_dialog_run (GTK_DIALOG (dialog));
2766    if (result == GTK_RESPONSE_OK) {
2767      printf ("OK\n");
2768      tsList.back ().cmd = string (gtk_entry_get_text (GTK_ENTRY (dedit)));
2769    }
2770    else if (result == GTK_RESPONSE_NO) {
2771      printf ("Delete\n");
2772      tsList.pop_back ();
2773    }
2774    gtk_widget_destroy (dialog);
2775
2776    FILE *fp = fopen ("main.ts", "w");
2777    for (deque<tsItem>::iterator i = tsList.begin (); i != tsList.end (); i++) {
2778      gsize siz;
2779      gchar *buf;
2780      printf ("%p %p\n", err, i->pix);
2781      gdk_pixbuf_save_to_buffer (i->pix, &buf, &siz, "ico", &err, NULL);
2782      printf ("Saving %u\n", siz);
2783  //    gdk_pixbuf_new_from_buffer (
2784      fwrite (&siz, sizeof (siz), 1, fp);
2785      fwrite (buf, siz, 1, fp);
2786      fwrite (i->cmd.c_str (), i->cmd.length () + 1, 1, fp);
2787    }
2788    fclose (fp);
2789   
2790    #if 0
2791      //(fp = fopen (string ((char*) sdata->data + 7, strcspn ((char*) sdata->data + 7, "\n\r")).c_str(), "r"))) {
2792    png_structp pngp = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
2793    printf ("%p\n", pngp);
2794    if (pngp) {
2795      png_infop infop = png_create_info_struct (pngp);
2796      printf ("i %p\n", infop);
2797      if (infop && !setjmp (png_jmpbuf (pngp))) {
2798        png_init_io (pngp, fp);
2799        png_read_png (pngp, infop, PNG_TRANSFORM_IDENTITY |
2800          PNG_TRANSFORM_STRIP_16 | PNG_TRANSFORM_PACKING |
2801          PNG_TRANSFORM_PACKSWAP /*| PNG_TRANSFORM_GRAY_TO_RGB Missing ?*/, NULL);
2802        png_bytep *row = png_get_rows(pngp, infop);
2803        printf ("%d %d %d\n", png_get_image_width (pngp, infop), png_get_image_height (pngp, infop),
2804          png_get_channels (pngp, infop));
2805        for (int i = 0; i < png_get_image_height (pngp, infop); i++) {
2806          for (int j = 0; j < png_get_image_width (pngp, infop); j++) {
2807            putchar (row[i][j * 4] & 128 ? '*' : ' ');
2808          }
2809          putchar ('\n');
2810        }
2811      }
2812      png_destroy_read_struct (&pngp, &infop, (png_infopp)NULL);
2813    }
2814    fclose (fp);
2815    #endif
2816  }
2817  else tsList.pop_back ();
2818  g_strfreev (arr);
2819  free (f);
2820  gtk_drag_finish (c, TRUE, FALSE, time);
2821}
2822
2823void SeUpdate (GtkWidget *w, gpointer p)
2824{
2825  GdkColor c;
2826  if (((intptr_t) p & 0xff) == 2) gtk_color_button_get_color (GTK_COLOR_BUTTON (w), &c);
2827  if (((intptr_t) p & 0xff) == 3) printf ("%s ", gtk_font_button_get_font_name (GTK_FONT_BUTTON (w)));
2828  if (((intptr_t) p & 0xff) == 4) printf ("%d ", gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (w)));
2829  if (((intptr_t) p & 0xff) == 5) printf ("%d ", gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w)));
2830  printf ("%p | %4x %4x %4x\n", p, c.red, c.green, c.blue);
2831}
2832
2833#endif // HEADLESS
2834
2835#if 0
2836void SelectName (GtkWidget * /*w*/, int row, int /*column*/,
2837  GdkEventButton * /*ev*/, void * /*data*/)
2838{
2839}
2840#endif
2841
2842#endif // HEADLESS
2843
2844inline void SerializeOptions (FILE *optFile, int r, const TCHAR *pakfile)
2845{
2846  LOG IconSet = 1;
2847  DetailLevel = 3;
2848  ButtonSize = 4;
2849  Background = 1;
2850  if (optFile) {
2851    #define o(en,min,max) Exit = r ? !fread (&en, sizeof (en), 1, optFile) \
2852                                   : !fwrite (&en, sizeof (en), 1, optFile);
2853    OPTIONS
2854    o (clat, 0, 0)
2855    o (clon, 0, 0)
2856    o (zoom, 0, 0)
2857    o (tlat, 0, 0)
2858    o (tlon, 0, 0)
2859    o (flat, 0, 0)
2860    o (flon, 0, 0)
2861    #undef o
2862    option = mapMode;
2863  }
2864  LOG if (r) ChangePak (pakfile, clon, clat); // This will set up Exit
2865  LOG if (Exit && r) ChangePak (NULL, clon, clat);
2866/*  char *tag = gosmData +
2867    *(int *)(ndBase + hashTable[bucketsMin1 + (bucketsMin1 >> 7) + 2]);
2868  while (*--tag) {}
2869  SetLocation (((wayType*)tag)[-1].clon, ((wayType*)tag)[-1].clat);
2870  zoom = ((wayType*)tag)[-1].dlat + ((wayType*)tag)[-1].dlon + (1 << 15); */
2871}
2872
2873#ifndef NOGTK
2874
2875//----------------------------- Load Control ------------------------------
2876// The idea is keep everything (pak file + temp data) in RAM and never
2877// refuse a request. Only when calculations indicate that resources are
2878// low will the oldest process return early with an incomplete (jump)
2879// route. A number of constants are hard coded.
2880#define MAX_INST 170
2881struct ldCtrlType {
2882  int free;
2883  struct {
2884    struct timeval start;
2885    volatile int maks; // Reads from this value should be atomic
2886  } inst[MAX_INST]; // A circular buffer
2887} *ld;
2888
2889void UpdateLdCtrl (int i, int calls, int li, int aliveCnt, int cpus)
2890{
2891  do {
2892  // From the newest process to the oldest
2893    i -= i == 0 ? 1 - MAX_INST : 1;
2894    if (ld->inst[i].start.tv_sec) {
2895      // Now we wind the clock back to the point where instance i started.
2896      // We make the calculation as if aliveCnt+1 equal instances were eating
2897      // memory from that point on. So we adjust calls for the amount of
2898      // memory the that wasn't consumed due to the later starting times of
2899      // the newer processes. A 2.2 Ghz Xeon takes roughly 12ms to complete
2900      // a call, but we make it 19ms to be safe.
2901      calls += ((ld->inst[li].start.tv_sec - ld->inst[i].start.tv_sec) *
2902        1000000 + ld->inst[li].start.tv_usec - ld->inst[i].start.tv_usec)
2903        / 19000 * aliveCnt;
2904      li = i; // li is oldest process still alive that was started after i.
2905      ld->inst[i].maks = aliveCnt > cpus / 2 ? 0 : calls / ++aliveCnt;
2906      //fprintf (stderr, "Setting maks for %d to %d\n", i, calls / aliveCnt);
2907    }
2908  } while (i != ld->free);
2909}
2910
2911int UserInterface (int argc, char *argv[], 
2912                   const char* pakfile, const char* stylefile) {
2913
2914  option = mapMode;
2915  IconSet = 1;
2916  DetailLevel = 2;
2917  FastestRoute = 1;
2918  Background = 1;
2919  Vehicle = motorcarR;
2920  char *h = getenv ("HOME");
2921  string optFname = string (h ? h : ".") + "/.gosmore.opt";
2922  FILE *optFile = fopen (optFname.c_str(), "r+");
2923  if (strcmp (pakfile, "nopak") == 0 && h) {
2924    string ddir (string (h) + "/.gosmore");
2925    mkdir (ddir.c_str (), 0755); // Usually already exists
2926    chdir (ddir.c_str ());
2927    SerializeOptions (optFile, TRUE, NULL);
2928  }
2929  else SerializeOptions (optFile, TRUE, pakfile);
2930  Keyboard = 1;
2931  if (Exit) {
2932    fprintf (stderr, "Cannot read %s\n"
2933             "You can (re)build it from\n"
2934             "the planet file e.g. " 
2935             "bzip2 -d planet-...osm.bz2 | %s rebuild\n",
2936             pakfile, argv[0]);
2937    #ifndef HEADLESS
2938    gtk_init (&argc, &argv);
2939    gtk_dialog_run (GTK_DIALOG (gtk_message_dialog_new (NULL,
2940      GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
2941      "Cannot read %s\nYou can (re)build it from\n"
2942      "the planet file e.g. bzip2 -d planet-...osm.bz2 | %s rebuild\n",
2943      pakfile, argv[0])));
2944    #endif
2945    return 8;
2946  }
2947  #if 0
2948  /* This code give an idea for the order of magnitude of the theorical
2949     limit of exhaustive search routing */
2950  __int64 total = 0;
2951  printf ("%d\n", hashTable[bucketsMin1 + 1]);
2952  for (ndType *nd = ndBase; nd < ndBase + hashTable[bucketsMin1 + 1]; nd++) {   
2953    if (nd->other[1]) total += lrint (sqrt (   
2954      Sqr ((__int64)(nd->lat - (nd + nd->other[1])->lat)) +   
2955      Sqr ((__int64)(nd->lon - (nd + nd->other[1])->lon))) *
2956      Style (Way (nd))->invSpeed[motorcarR]);
2957  }
2958  printf ("%lf\n", (double) total);
2959  exit (0);
2960  #endif
2961
2962  if (stylefile) {
2963#ifndef _WIN32
2964    GosmLoadAltStyle(stylefile,FindResource("icons.csv"));
2965#else
2966    fprintf(stderr, "Overiding style information is not currently supported"
2967            " in Windows.");
2968#endif
2969  }
2970
2971
2972  if (getenv ("QUERY_STRING")) {
2973    double x0, y0, x1, y1;
2974    char vehicle[20];
2975    sscanf (getenv ("QUERY_STRING"),
2976      "flat=%lf&flon=%lf&tlat=%lf&tlon=%lf&fast=%d&v=%19[a-z]",
2977      &y0, &x0, &y1, &x1, &FastestRoute, vehicle);
2978    flat = Latitude (y0);
2979    flon = Longitude (x0);
2980    tlat = Latitude (y1);
2981    tlon = Longitude (x1);
2982    #define M(v) if (strcmp (vehicle, #v) == 0) Vehicle = v ## R;
2983    RESTRICTIONS
2984    #undef M
2985    Route (TRUE, 0, 0, Vehicle, FastestRoute);
2986    #ifdef LD_CTRL
2987    struct timezone tz;
2988    FILE *f = fopen ("ld_ctrl", "r+");
2989    if (!f) {
2990      if ((f = fopen ("ld_ctrl", "w+")) == NULL) return 2;
2991      for (size_t i = 0; i < sizeof (*ld); i++) fputc (0, f);
2992      fflush (f);
2993    }
2994    if (!f || (char*)-1L == (char*) (ld = 
2995             (ldCtrlType *) mmap (NULL, sizeof (*ld), PROT_READ | PROT_WRITE,
2996                                  MAP_SHARED, fileno (f), 0))
2997        || lockf (F_LOCK, fileno (f), sizeof (*ld)) != 0) {
2998      printf ("Content-Type: text/plain\n\r\n\rLd ctrl error%p %p %s\n\r",
2999        f, ld, strerror (errno));
3000      return 0;
3001    }
3002    int calls = (sysconf (_SC_PAGESIZE) / 4096 * sysconf (_SC_PHYS_PAGES)
3003      - 2200000) / 1500, myInst = ld->free;
3004    // Calculate how much memory the machine has left after assuming the pak
3005    // file(s) are in RAM. Then calculate how many times RouteLoop() can be
3006    // called across all instances under the assumption that each call will
3007    // consume roughly 6MB of RAM.
3008    //fprintf (stderr, "Starting instance %d\n", ld->free);
3009    gettimeofday (&ld->inst[myInst].start, &tz);
3010    if (++ld->free >= MAX_INST) ld->free = 0;
3011    UpdateLdCtrl (ld->free, calls, 0, 0, sysconf (_SC_NPROCESSORS_ONLN));
3012    lockf (F_ULOCK, fileno (f), sizeof (*ld));
3013    for (int i = 0; i < ld->inst[myInst].maks && RouteLoop (); i++) {}
3014    lockf (F_LOCK, fileno (f), sizeof (*ld));
3015    ld->inst[myInst].start.tv_sec = 0; // Mark that we're done.
3016    UpdateLdCtrl (ld->free, calls, 0, 0, sysconf (_SC_NPROCESSORS_ONLN));
3017    lockf (F_ULOCK, fileno (f), sizeof (*ld));
3018    #else
3019    while (RouteLoop ()) {}
3020/*  It is reasonable to assume that there exists a short route in the actual
3021    road network between any two points on the that are closer than 1km to
3022    each. Therefore, once we come close to 'to', we should find a route
3023    quickly thereafter. If we don't it means 'to' is in a small, unlinked part
3024    of the network and trying to complete the route is wasting time.
3025   
3026    Well, I guess that in developing countries there are many sets of roads
3027    that run on opposite sides of rivers with no bridge or tunnel to connect
3028    them. So I'm commenting it out, because it's things like these that
3029    have bit me in the past.
3030    for (int i = 4000; i > 0 && RouteLoop (); i--) {
3031      if (flat - 100000 < shortest->nd->lat && shortest->nd->lat < flat + 100000 &&
3032          flon - 100000 < shortest->nd->lon && shortest->nd->lon < flon + 100000 &&
3033          i > 50) i = 50;
3034    } */
3035    #endif
3036    printf ("Content-Type: text/plain\n\r\n\r");
3037    if (!shortest) printf ("No route found\n\r");
3038    else {
3039      if (!routeSuccess) printf ("Jump\n\r");
3040      styleStruct *firstS = Style (Way (shortest->nd));
3041      double ups = lrint (3.6 / firstS->invSpeed[Vehicle]
3042          / firstS->aveSpeed[Vehicle] / (20000 / 2147483648.0) /
3043          cos (LatInverse (flat / 2 + tlat / 2) * (M_PI / 180)));
3044      // ups (Units per second) also works as an unsigned int.
3045
3046      for (; shortest; shortest = shortest->shortest) {
3047        wayType *w = Way (shortest->nd);
3048        char *name = (char*)(w + 1) + 1;
3049        unsigned style= StyleNr(w);
3050        printf ("%lf,%lf,%c%s,%s,%.0lf,%.*s\n\r", LatInverse (shortest->nd->lat),
3051          LonInverse (shortest->nd->lon), JunctionType (shortest->nd),
3052          ((1 << roundaboutR) & (Way (shortest->nd))->bits) ? "r" : "",
3053          style < sizeof(klasTable)/sizeof(klasTable[0]) ? klasTable[style].desc :
3054          "(unknown-style)", ((shortest->heapIdx < 0
3055          ? -shortest->heapIdx : routeHeap[shortest->heapIdx].best) -
3056          shortest->remain) / ups,
3057          (int) strcspn (name, "\n"), name);
3058      }
3059    }
3060    return 0;
3061  }
3062
3063  printf ("%s is in the public domain and comes without warranty\n",argv[0]);
3064  #ifndef HEADLESS
3065
3066  curl_global_init (CURL_GLOBAL_ALL);
3067  g_thread_init (NULL);  // Something to do with curl progress bar
3068  gtk_init (&argc, &argv);
3069#ifdef USE_GNOMESOUND
3070  gnome_sound_init ("localhost");
3071#endif
3072  draw = gtk_drawing_area_new ();
3073  gtk_widget_set_double_buffered (draw, FALSE);
3074  #if 0 // ndef CHILDREN
3075    XID id[3];
3076    char sString[50];
3077    fread (sString, 4, 1, stdin);
3078    fread (id, sizeof (id), 1, stdin);
3079    draw->window = gdk_window_foreign_new (id[0]);
3080    icons = gdk_pixmap_foreign_new (id[1]);
3081    mask = gdk_pixmap_foreign_new (id[2]);
3082    for (;;) {
3083      #define o(x,min,max) if (fread (&x, sizeof (x), 1, stdin)) {};
3084      STATEINFO
3085      #undef o
3086      //fprintf (stderr, "%d %p | %p %p | %d %d -- %d %d\n", id[0], draw->window,
3087      //  icons, mask, id[1], id[2], clon, clat);
3088      DrawExpose ();
3089      if (write (STDOUT_FILENO, sString, 4) != 4) exit (0);
3090      if (fread (sString, 4, 1, stdin) != 1) exit (0);
3091      fread (id, sizeof (id), 1, stdin); // Discard
3092    }
3093  #endif
3094  gtk_signal_connect (GTK_OBJECT (draw), "expose_event",
3095    (GtkSignalFunc) DrawExpose, NULL);
3096  gtk_signal_connect (GTK_OBJECT (draw), "button-release-event",
3097    (GtkSignalFunc) Click, NULL);
3098  gtk_signal_connect (GTK_OBJECT (draw), "motion_notify_event",
3099    (GtkSignalFunc) Drag, NULL);
3100  gtk_widget_set_events (draw, GDK_EXPOSURE_MASK | GDK_BUTTON_RELEASE_MASK |
3101    GDK_BUTTON_PRESS_MASK |  GDK_POINTER_MOTION_MASK);
3102  gtk_signal_connect (GTK_OBJECT (draw), "scroll_event",
3103                       (GtkSignalFunc) Scroll, NULL);
3104
3105#if 0 // New UI editor code                       
3106  static GtkTargetEntry drawDndTargets[] = {
3107    { (gchar*) "text/uri-list", 0, 0 },
3108    { (gchar*) "image/png", 0, 1 /* Will autodetect type on drop */ },
3109    { (gchar*) "image/ico", 0, 1 },
3110  };
3111  gtk_drag_dest_set (draw, GTK_DEST_DEFAULT_ALL /* GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_HIGHLIGHT*/, drawDndTargets,
3112    G_N_ELEMENTS (drawDndTargets), GDK_ACTION_COPY);
3113                       
3114  g_signal_connect (draw, "drag-data-received", G_CALLBACK (DropReceived), NULL);
3115  g_signal_connect (draw, "drag-drop", G_CALLBACK (DropOnDraw), NULL);
3116  ReadTsList ();
3117#endif
3118 
3119  GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
3120  gtk_signal_connect (GTK_OBJECT (window), "focus-in-event",
3121                       (GtkSignalFunc) UpdateWayPoints, NULL);
3122  /* The new layout will work better on smaller screens esp. touch screens by
3123  moving less used options to the menu and only displaying search results
3124  when they are required. It will also be more familiar to casual users
3125  because it will resemble a webbrowser */
3126  GtkWidget *hbox = gtk_hbox_new (FALSE, 3), *vbox = gtk_vbox_new (FALSE, 0);
3127  gtk_container_add (GTK_CONTAINER (window), vbox);
3128  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
3129
3130  GtkWidget *btn[3];
3131  for (int i = 0; i < 3; i++) {
3132    btn[i] = gtk_button_new_with_label (i == 0 ? "O" : i == 1 ? "-" : "+");
3133    gtk_widget_set_size_request (btn[i], 27, 20);
3134    gtk_box_pack_start (GTK_BOX (hbox), btn[i], FALSE, FALSE, 5);
3135    //gtk_widget_show (btn[i]);
3136    gtk_signal_connect (GTK_OBJECT (btn[i]), "clicked",
3137      GTK_SIGNAL_FUNC (HitGtkButton), (char*)i);
3138  } 
3139
3140  searchW = gtk_entry_new ();
3141  gtk_box_pack_start (GTK_BOX (hbox), searchW, FALSE, FALSE, 5);
3142  gtk_entry_set_text (GTK_ENTRY (searchW), "Search");
3143  gtk_signal_connect (GTK_OBJECT (searchW), "changed",
3144    GTK_SIGNAL_FUNC (IncrementalSearch), NULL);
3145  gtk_signal_connect (GTK_OBJECT (searchW), "button-press-event",
3146    GTK_SIGNAL_FUNC (ToggleSearchResults), NULL);
3147
3148  gtk_box_pack_start (GTK_BOX (vbox), draw, TRUE, TRUE, 0);
3149 
3150  location = gtk_entry_new ();
3151  gtk_box_pack_start (GTK_BOX (vbox), location, FALSE, FALSE, 5);
3152  gtk_signal_connect (GTK_OBJECT (location), "changed",
3153    GTK_SIGNAL_FUNC (ChangeLocation), NULL);
3154 
3155  display3D = gtk_toggle_button_new_with_label ("3D");
3156  gtk_box_pack_start (GTK_BOX (hbox), display3D, FALSE, FALSE, 5);
3157  gtk_signal_connect (GTK_OBJECT (display3D), "clicked",
3158    GTK_SIGNAL_FUNC (ChangeOption), NULL);
3159  //gtk_widget_show (display3D);
3160
3161  followGPSr = gtk_toggle_button_new_with_label ("Lock");
3162 
3163  #ifdef USE_GEOCLUE // Not used and never worked
3164  g_type_init ();
3165  GeoclueMaster *master = geoclue_master_get_default ();
3166  Gerror *error = NULL;
3167  GeoclueMasterClient *client =
3168    geoclue_master_create_client (master, NULL, &error);
3169  g_object_unref (master);
3170  if (client) {
3171//    if (!geoclue_master_client_set_requirements (client,
3172//      GEOCLUE_ACCURACY_LEVEL_LOCALITY, 0, TRUE, GEOCLUE_RESOURSE
3173    GeocluePosition *pos = geoclue_master_client_create_position (client, NULL);
3174    g_signal_connect (G_OBJECT (pos), "position-changed",
3175      G_CALLBACK (GeoclueUpdate), NULL);
3176    gtk_box_pack_start (GTK_BOX (hbox), followGPSr, FALSE, FALSE, 5);
3177    gtk_signal_connect (GTK_OBJECT (followGPSr), "clicked",
3178      GTK_SIGNAL_FUNC (ChangeOption), NULL);
3179  }
3180  #endif
3181 
3182  //#if !defined (_WIN32) && !defined (ROUTE_TEST)
3183  #if 0
3184  struct sockaddr_in sa;
3185  int gpsSock = socket (PF_INET, SOCK_STREAM, 0);
3186  sa.sin_family = AF_INET;
3187  sa.sin_port = htons (2947);
3188  sa.sin_addr.s_addr = htonl (0x7f000001); // (204<<24)|(17<<16)|(205<<8)|18;
3189  if (gpsSock != -1 &&
3190      connect (gpsSock, (struct sockaddr *)&sa, sizeof (sa)) == 0) {
3191    send (gpsSock, "R\n", 2, 0);
3192    gpsSockTag = gdk_input_add (/*gpsData->gps_fd*/ gpsSock, GDK_INPUT_READ,
3193      (GdkInputFunction) ReceiveNmea /*gps_poll*/, NULL);
3194  #endif
3195  #ifdef USE_GPSD
3196  gps_data_t *gpsData = gps_open ("127.0.0.1", "2947");
3197  if (gpsData) {
3198    gps_set_raw_hook (gpsData, GpsMove);
3199    #if GPSD_API_MAJOR_VERSION <= 3
3200    gps_query (gpsData, "w+x\n");
3201    #else
3202    gps_stream (gpsData, WATCH_ENABLE, NULL);
3203    #endif
3204    gpsSockTag = gdk_input_add (gpsData->gps_fd, GDK_INPUT_READ,
3205      (GdkInputFunction) gps_poll, gpsData);
3206   
3207    gtk_box_pack_start (GTK_BOX (hbox), followGPSr, FALSE, FALSE, 5);
3208    gtk_signal_connect (GTK_OBJECT (followGPSr), "clicked",
3209      GTK_SIGNAL_FUNC (ChangeOption), NULL);
3210    //gtk_widget_show (followGPSr);
3211  }
3212  #endif
3213
3214  GtkAdjustment *adj = (GtkAdjustment*) gtk_adjustment_new (0, 0, 100, 0, 0, 0);
3215  bar = gtk_progress_bar_new_with_adjustment (adj);
3216  gtk_container_add (GTK_CONTAINER (hbox), bar);
3217
3218  gtk_signal_connect (GTK_OBJECT (window), "delete_event",
3219    GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
3220 
3221  gtk_window_set_default_size (GTK_WINDOW (window), 550, 550);
3222//  gtk_widget_show (searchW);
3223//  gtk_widget_show (location);
3224//  gtk_widget_show (draw);
3225/*  gtk_widget_show (getDirs); */
3226//  gtk_widget_show (hbox);
3227//  gtk_widget_show (vbox);
3228  gtk_widget_show_all (window);
3229
3230/*  GtkWidget *styleEditor = gtk_window_new (GTK_WINDOW_TOPLEVEL);
3231  GtkWidget *seScroll = gtk_scrolled_window_new (NULL, NULL);
3232  //gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (seScroll), );
3233  gtk_container_add (GTK_CONTAINER (styleEditor), seScroll);
3234  #define TCOLS 6
3235  GtkWidget *seTable = gtk_table_new (100, TCOLS, FALSE);
3236  gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (seScroll), seTable);
3237  for (int i = 0; i < 100; i++) {
3238    GtkWidget *v[TCOLS];
3239    v[0] = gtk_label_new ("abcdefghijl" + i % 5);
3240    v[1] = gtk_label_new ("01234567" + i % 5);
3241//    GdkColor c = { 9999, 8888, 7777, 6666 };
3242    v[2] = gtk_color_button_new ();
3243    v[3] = gtk_font_button_new ();
3244    v[4] = gtk_spin_button_new_with_range (0, 50, 1);
3245    v[5] = gtk_toggle_button_new_with_label ("- -");
3246    const char *sName[] = { NULL, NULL, "color-set", "font-set", "value-changed", "clicked" };
3247    for (int j = 0; j < TCOLS; j++) {
3248    //gtk_clist_append (GTK_CLIST (seList), (gchar**) v);
3249      gtk_table_attach_defaults (GTK_TABLE (seTable), v[j], j, j + 1, i, i + 1);
3250      if (sName[j]) gtk_signal_connect (GTK_OBJECT (v[j]), sName[j],
3251        GTK_SIGNAL_FUNC (SeUpdate), (gpointer) (intptr_t) (i * 0x100 + j));
3252    }
3253  }
3254  gtk_widget_show_all (styleEditor); */
3255
3256  ChangeOption ();
3257  IncrementalSearch ();
3258  gtk_widget_grab_focus (searchW);
3259  gdk_threads_enter (); // Something to do with curl progress bar
3260  gtk_main ();
3261  gdk_threads_leave (); // Something to do with curl progress bar
3262  FlushGpx ();
3263  if (optFile) rewind (optFile);
3264  else optFile = fopen (optFname.c_str (), "w");
3265  SerializeOptions (optFile, FALSE, NULL);
3266 
3267  #endif // HEADLESS
3268  return 0;
3269}
3270
3271int main (int argc, char *argv[])
3272{
3273  #if 0 // ifdef CHILDREN
3274  if (1) {
3275    int cmd[2], result[2];
3276    pipe (cmd);
3277    pipe (result);
3278    child[0].pipe[0] = result[0];
3279    child[0].pipe[1] = cmd[1];
3280    if (fork () == 0) {
3281      dup2 (cmd[0], STDIN_FILENO);
3282      dup2 (result[1], STDOUT_FILENO);
3283      execl ("./gosmore16", "./gosmore16", NULL);
3284      perror ("Starting slave process gosmore16");
3285      _exit (1);
3286    }
3287  }
3288  #endif
3289 
3290  int nextarg = 1;
3291  bool rebuild = false;
3292  const char* master = "";
3293  int bbox[4] = { INT_MIN, INT_MIN, 0x7fffffff, 0x7fffffff };
3294 
3295  setlocale (LC_ALL, ""); /* Ensure decimal sign is "." for NMEA parsing. */
3296 
3297  if (argc > 1 && stricmp (argv[1], "sortRelations") == 0) {
3298    return SortRelations ();
3299  }
3300  if (argc > 1 && stricmp(argv[1], "rebuild") == 0) {
3301    if (argc < 6 && argc > 4) {
3302      fprintf (stderr, 
3303               "Usage : %s [rebuild [bbox for 2 pass]] [pakfile [stylefile]]\n"
3304               "See http://wiki.openstreetmap.org/index.php/gosmore\n", 
3305               argv[0]);
3306      return 1;
3307    }
3308    rebuild=true;
3309    nextarg++;
3310    if (argc >= 6) {
3311      master = FindResource("master.pak");
3312      bbox[0] = Latitude (atof (argv[2]));
3313      bbox[1] = Longitude (atof (argv[3]));
3314      bbox[2] = Latitude (atof (argv[4]));
3315      bbox[3] = Longitude (atof (argv[5]));
3316      nextarg += 4;
3317    }
3318  }
3319 
3320  // check if a pakfile was specified on the command line
3321  const char* pakfile;
3322  const char* stylefile = NULL;
3323  if (argc > nextarg) {
3324    pakfile=argv[nextarg];
3325    nextarg++;
3326    if (argc > nextarg)  {
3327      stylefile=argv[nextarg];
3328    } else if (rebuild) { 
3329      stylefile=FindResource("elemstyles.xml");
3330    }
3331  } else {
3332    pakfile=FindResource("gosmore.pak");
3333    if (rebuild) {
3334      stylefile=FindResource("elemstyles.xml");
3335    }
3336  }
3337 
3338  if (rebuild) {
3339#ifndef _WIN32
3340    printf("Building %s using style %s...\n",pakfile,stylefile);
3341
3342    RebuildPak(pakfile, stylefile, FindResource("icons.csv"), master, bbox);
3343#else
3344    fprintf(stderr,"Pakfile rebuild is not currently supported in Windows.\n");
3345#endif
3346  }
3347
3348  return UserInterface (argc, argv, pakfile, stylefile);
3349
3350  // close the logfile if it has been opened. No. Rather let libc to it.
3351  //if (logFP(false)) fclose(logFP(false));
3352}
3353#else // NOGTK / WIN32 and WINCE Native;
3354//-------------------------- WIN32 and WINCE Native ------------------
3355HANDLE port = INVALID_HANDLE_VALUE;
3356
3357HBITMAP bmp = NULL, bufBmp = NULL;
3358HDC iconsDc, bufDc;
3359HPEN pen[2 << STYLE_BITS];
3360HBRUSH brush[2 << STYLE_BITS];
3361UTF16 appendTmp[50];
3362
3363LRESULT CALLBACK MainWndProc(HWND hWnd,UINT message,
3364                                  WPARAM wParam,LPARAM lParam)
3365{
3366  PAINTSTRUCT ps;
3367  RECT rect;
3368  //static wchar_t msg[200] = TEXT("No coms");
3369  int topBar = Layout != 1 ? 30 : 0;
3370  static int updatePercent = 0;
3371
3372  switch(message) {
3373    #if 0
3374    case WM_HOTKEY:
3375      if (VK_TBACK == HIWORD(lParam) && (0 != (MOD_KEYUP & LOWORD(lParam)))) {
3376        PostQuitMessage (0);
3377      }
3378      break;
3379
3380    case WM_ACTIVATE:
3381      // Ensure that unwanted wince elements are hidden
3382      if (SHFullScreenPtr) {
3383        if (FullScreen) {
3384          (*SHFullScreenPtr)(mWnd, SHFS_HIDETASKBAR |
3385                             SHFS_HIDESTARTICON | SHFS_HIDESIPBUTTON);
3386        } else {
3387          (*SHFullScreenPtr)(mWnd, SHFS_HIDESIPBUTTON);
3388        }
3389      }
3390      break;
3391    #endif
3392 
3393    case WM_CREATE:
3394      LOG for (int i = 0; i < 3; i++) {
3395        buttons[i] = CreateWindow(TEXT ("BUTTON"), i == 0 ? TEXT ("O") :
3396                      i == 1 ? TEXT ("-") : TEXT ("+"), BS_PUSHBUTTON |
3397                      WS_CHILD | WS_VISIBLE | WS_TABSTOP,
3398                      0, 0, 0, 0, hWnd, (HMENU) (IDC_EDIT1 + 1 + i),
3399                      (HINSTANCE) GetWindowLong(hWnd, GWL_HINSTANCE), 
3400                      NULL);       // pointer not needed
3401      }
3402      LOG button3D = CreateWindow(TEXT ("BUTTON"), TEXT ("3D"), BS_CHECKBOX |
3403                    WS_CHILD | WS_VISIBLE | WS_TABSTOP,
3404                    0, 0, 0, 0, hWnd, (HMENU) (IDC_EDIT1 + 1 + 3),
3405                    (HINSTANCE) GetWindowLong(hWnd, GWL_HINSTANCE), 
3406                    NULL);       // pointer not needed
3407      LOG hwndEdit = CreateWindow(TEXT ("EDIT"),
3408                    NULL, WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT,
3409                    0, 0, 0, 0,  // set size in WM_SIZE message
3410                    hWnd, (HMENU) IDC_EDIT1/*ID_EDITCHILD*/,
3411                    (HINSTANCE) GetWindowLong(hWnd, GWL_HINSTANCE), 
3412                    NULL);       // pointer not needed
3413      if (Keyboard) SendMessage (hwndEdit, WM_SETTEXT, 0, (LPARAM) TEXT ("Search")); 
3414      //else SetClassLongPtr (hwndEdit, GCLP_HBRBACKGROUND, (LONG) GetStockObject (WHITE_BRUSH));
3415//      SendMessage (hwndEdit, EM_SETEVENTMASK, 0, ENM_UPDATE | ENM_SETFOCUS);
3416      break;
3417    case WM_SETFOCUS: 
3418      if (Keyboard) SetFocus(hwndEdit); 
3419      break;
3420    case WM_SIZE: 
3421      LOG draw->allocation.width = LOWORD (lParam);
3422      LOG draw->allocation.height = HIWORD (lParam) - topBar;
3423      if (Keyboard) MoveWindow (hwndEdit, Layout > 1 ? 8 : 140, topBar - 25,
3424        draw->allocation.width - (Layout > 1 ? 66 : 200), 20, TRUE);
3425      LOG MoveWindow(button3D, draw->allocation.width - 55,
3426        Layout != 1 ? 5 : -25, 50, 20, TRUE);
3427      for (int i = 0; i < 3; i++) { // Same as LBUTTON_UP. Put in function !!
3428        LOG MoveWindow (buttons[i], (2 * i + 1) * 70 / 3 - 15,
3429          Layout ? -25 : 5, 30, 20, TRUE);
3430      }
3431      LOG if (bufBmp) {
3432        DeleteObject (bufBmp);
3433        bufBmp = NULL;
3434      }
3435      LOG InvalidateRect (hWnd, NULL, FALSE);
3436      break;
3437    case WM_DESTROY:
3438      LOG PostQuitMessage(0);
3439      break;
3440    /*case WM_CTLCOLORSTATIC: // Tried to make hwndEdit a STATIC when !Keyboard
3441      SetBkMode ((HDC)wParam, TRANSPARENT);
3442      return (LONG) GetStockObject (WHITE_BRUSH); */
3443    case WM_PAINT:
3444      do { // Keep compiler happy.
3445        BeginPaint (hWnd, &ps);
3446      //GetClientRect (hWnd, &r);
3447      //SetTextColor(ps.hdc,(i==state)?RGB(0,128,0):RGB(0,0,0));
3448      //r.left = 50;
3449      // r.top = 50;
3450        if (bmp == NULL) {
3451          LOG bmp = LoadBitmap (hInst, MAKEINTRESOURCE (IDB_BITMAP1));
3452          LOG iconsDc = CreateCompatibleDC (ps.hdc);
3453          LOG SelectObject(iconsDc, bmp);
3454
3455          // get mask for iconsDc
3456          LOG bmp = LoadBitmap (hInst, MAKEINTRESOURCE (IDB_BITMAP2));
3457          LOG maskDc = CreateCompatibleDC (ps.hdc);
3458          LOG SelectObject(maskDc, bmp);
3459
3460          LOG bufDc = CreateCompatibleDC (ps.hdc); //bufDc //GetDC (hWnd));
3461          /*pen[ROUTE_PEN] = CreatePen (PS_SOLID, 6, 0x00ff00);
3462          pen[VALIDATE_PEN] = CreatePen (PS_SOLID, 10, 0x9999ff); */
3463          map<int,HPEN> pcache;
3464          map<int,HBRUSH> bcache;
3465          LOG for (int i = 0; i < stylecount; i++) {
3466            // replace line colour with area colour
3467            // if no line colour specified
3468            int c = style[i].lineColour != -1 ? style[i].lineColour
3469              : (style[i].areaColour & 0xfefefe) >> 1; 
3470            if (c != -1) {
3471              // logprintf ("PEN[%d] %d %x %d\n",i,style[i].dashed, c, style[i].lineWidth);
3472              int idx = (style[i].dashed ? 1 : 0) +
3473                (style[i].lineWidth & 0x3f) * 2 + ((c & 0xffffff) << 7);
3474              map<int,HPEN>::iterator f = pcache.find (idx);
3475              pen[i] = f != pcache.end() ? f->second :
3476                CreatePen (style[i].dashed ? PS_DASH : PS_SOLID,
3477                         max (1, style[i].lineWidth), (c >> 16) |
3478                         (c & 0xff00) |
3479                         ((c & 0xff) << 16));
3480              pcache[idx] = pen[i];
3481            }
3482            if ((c = style[i].areaColour) != -1) {
3483              // logprintf ("BR[%d] %x\n", i, c);
3484              map<int,HBRUSH>::iterator f = bcache.find (c);
3485              brush[i] = f != bcache.end () ? f->second :
3486                CreateSolidBrush ((c>>16) | (c&0xff00) | ((c&0xff) << 16));
3487              bcache[c] = brush[i];
3488            }
3489          }
3490          LOG sysFont = (HFONT) GetStockObject (SYSTEM_FONT);
3491          LOG GetObject (sysFont, sizeof (logFont), &logFont);
3492          #ifndef _WIN32_WCE
3493          logFont.lfWeight = 400;
3494          strcpy (logFont.lfFaceName, TEXT ("Arial"));
3495          /*#else
3496          logFont.lfWeight = 400; // TODO ******** Testing WM6
3497          wcscpy (logFont.lfFaceName, TEXT ("Arial")); */
3498          #endif
3499          LOG SetBkMode (bufDc, TRANSPARENT); // Is this really necessary ?
3500        }
3501        rect.top = rect.left = 0;
3502        rect.right = draw->allocation.width;
3503        rect.bottom = draw->allocation.height;
3504        if (bufBmp == NULL) { // i.e. after WM_SIZE
3505          LOG bufBmp = CreateCompatibleBitmap (ps.hdc, draw->allocation.width,
3506            draw->allocation.height);
3507          LOG SelectObject (bufDc, bufBmp);
3508          LOG FillRect (bufDc, &rect, (HBRUSH) GetStockObject(WHITE_BRUSH));
3509        }
3510        mygc = bufDc;
3511        icons = iconsDc;
3512        if (option == BackgroundNum) {
3513         FillRect (bufDc, &rect,
3514           brush[firstElemStyle + Background - (Background > 8 ? 8 : 0)]);
3515        }
3516        DrawExpose (pen, brush);
3517       
3518        BitBlt (ps.hdc, 0, topBar, rect.right,  rect.bottom, bufDc, 0, 0, SRCCOPY);
3519        if (updatePercent) {
3520          MoveToEx (ps.hdc, 0, topBar, NULL);
3521          LineTo (ps.hdc, updatePercent * draw->allocation.width / 1000, topBar);
3522        }
3523      //SetBkColor(ps.hdc,RGB(63,63,63));
3524        FillRect (bufDc, &rect, brush[firstElemStyle + Background - (Background > 8 ? 8 : 0)]);
3525         //(HBRUSH) GetStockObject(WHITE_BRUSH));
3526        rect.bottom = topBar;
3527        FillRect (ps.hdc, &rect, (HBRUSH) GetStockObject(WHITE_BRUSH));
3528        if (!Keyboard) {
3529          UTF16 wcTmp[70], *tStart = (UTF16 *) wcTmp;
3530          const unsigned char *sStart = (const unsigned char*) searchStr.c_str ();
3531          if (ConvertUTF8toUTF16 (&sStart, sStart + searchStr.length (),
3532                &tStart, tStart + sizeof (wcTmp) / sizeof (wcTmp[0]), lenientConversion)
3533              == conversionOK) {
3534            //SendMessage (hwndEdit, WM_SETTEXT, 0, (LPARAM) (wchar_t*) wcTmp);
3535            ExtTextOutW (ps.hdc, Layout > 1 ? 8 : 140, topBar - 25, 0, NULL,
3536              (wchar_t*) wcTmp, tStart - wcTmp, NULL);
3537          }
3538        }
3539//      HPEN pen = CreatePen (a[c2].lineDashed ? PS_DASH : PS_SOLID,
3540        EndPaint (hWnd, &ps);
3541      } while (0);
3542      break;
3543    case WM_CHAR:
3544
3545      break;
3546    case WM_KEYDOWN:
3547      // The TGPS 375 can generate 12 keys :
3548      // VK_RETURN, VK_UP, VK_DOWN, VK_LEFT, VK_RIGHT,
3549      // 193=0xC1=Zoom in, 194=0xC2=Zoom out, 198=0xC6=menu 197=0xC5=settings
3550      // 195=0xC3=V+, 196=0xC4=V- which is VK_APP1 to VK_APP6
3551      // and WM_CHAR:VK_BACK
3552      break;
3553    case WM_USER + 1:
3554      /*
3555      wsprintf (msg, TEXT ("%c%c %c%c %9.5lf %10.5lf %lf %lf"),
3556        gpsNew.fix.date[0], gpsNew.fix.date[1],
3557        gpsNew.fix.tm[4], gpsNew.fix.tm[5],
3558        gpsNew.fix.latitude, gpsNew.fix.longitude, gpsNew.fix.ele,
3559        gpsNew.fix.hdop); */
3560      DoFollowThing ((gpsNewStruct*)lParam);
3561      if (FollowGPSr) InvalidateRect (hWnd, NULL, FALSE);
3562      break;
3563    case WM_USER + 2:
3564       do {
3565         HDC wdc = GetDC (hWnd);
3566         updatePercent = lParam;
3567         MoveToEx (wdc, 0, topBar, NULL);
3568         LineTo (wdc, updatePercent * draw->allocation.width / 1000, topBar);
3569         ReleaseDC (hWnd, wdc);
3570       } while (0);
3571       break;
3572    case WM_LBUTTONDOWN:
3573      pressTime = GetTickCount ();
3574      SetCapture (hWnd);
3575      break;
3576    case WM_LBUTTONUP:
3577      ReleaseCapture ();
3578      if (gDisplayOff) {
3579        CeEnableBacklight(TRUE);
3580        gDisplayOff = FALSE;
3581        break;
3582      }
3583      GdkEventButton ev;
3584      if ((ev.y = HIWORD (lParam) - topBar) > 0) {
3585        ev.x = LOWORD (lParam);
3586        ev.time = GetTickCount ();
3587        ev.button = 1;
3588        Click (NULL, &ev, NULL);
3589        if (option == LayoutNum) {
3590          if (Keyboard) MoveWindow(hwndEdit, Layout > 1 ? 8 : 140,
3591            Layout != 1 ? 5 : -25,
3592            draw->allocation.width - (Layout > 1 ? 66 : 200), 20, TRUE);
3593          MoveWindow(button3D, draw->allocation.width - 55,
3594            Layout != 1 ? 5 : -25, 50, 20, TRUE);
3595          for (int i = 0; i < 3; i++) { // Same as WM_SIZE. Put in function !!
3596            MoveWindow (buttons[i], (2 * i + 1) * 70 / 3 - 15,
3597              Layout ? -25 : 5, 30, 20, TRUE);
3598          }
3599        }
3600        if (Keyboard && option != searchMode) SipShowIM (SIPF_OFF);
3601      }
3602      else if (!Keyboard && LOWORD (lParam) > (Layout > 1 ? 8 : 140)) {
3603        option = option == searchMode ? mapMode : searchMode;
3604      }
3605      firstDrag[0] = -1;
3606      InvalidateRect (hWnd, NULL, FALSE);
3607      break;
3608    case WM_MOUSEMOVE:
3609      if (wParam & MK_LBUTTON) {
3610        if (firstDrag[0] >= 0) {
3611          HDC wdc = GetDC (hWnd);
3612          int wadj = lastDrag[0] - LOWORD (lParam);
3613          int hadj = lastDrag[1] - HIWORD (lParam) + topBar;
3614          BitBlt (wdc, wadj < 0 ? -wadj : 0, (hadj < 0 ? -hadj : 0) + topBar, 
3615            draw->allocation.width - (wadj < 0 ? -wadj : wadj),
3616            draw->allocation.height + topBar - (hadj < 0 ? -hadj : hadj),
3617            wdc, wadj > 0 ? wadj : 0, (hadj > 0 ? hadj : 0) + topBar, SRCCOPY);
3618          ReleaseDC (hWnd, wdc);
3619        }
3620        lastDrag[0] = LOWORD (lParam);
3621        lastDrag[1] = HIWORD (lParam) - topBar;
3622        if (firstDrag[0] < 0) memcpy (firstDrag, lastDrag, sizeof (firstDrag));
3623      }
3624      break;
3625    case WM_MOUSEWHEEL:
3626      do {
3627        GdkEventScroll ev;
3628        POINT p;
3629        p.x = GET_X_LPARAM (lParam);
3630        p.y = GET_Y_LPARAM (lParam);
3631        ScreenToClient (hWnd, &p);
3632        ev.x = p.x;
3633        ev.y = p.y - topBar;
3634       
3635        ev.direction = GET_WHEEL_DELTA_WPARAM (wParam) > 0
3636          ? GDK_SCROLL_UP : GDK_SCROLL_DOWN;
3637        Scroll (NULL, &ev, NULL);
3638        InvalidateRect (hWnd, NULL, FALSE);
3639      } while (0);
3640      break;
3641    case WM_COMMAND:
3642      if (HIWORD (wParam) == BN_CLICKED &&
3643          LOWORD (wParam) > IDC_EDIT1 && LOWORD (wParam) <= IDC_EDIT1 + 3) {
3644        HitButton (LOWORD (wParam) - IDC_EDIT1 - 1);
3645        if (Keyboard && optionMode != searchMode) SipShowIM (SIPF_OFF);
3646        InvalidateRect (hWnd, NULL, FALSE);
3647      }
3648      if (HIWORD (wParam) == BN_CLICKED && LOWORD (wParam) == IDC_EDIT1 + 4) {
3649        Display3D ^= 1;
3650        Button_SetCheck (button3D, Display3D ? BST_CHECKED : BST_UNCHECKED);
3651        InvalidateRect (hWnd, NULL, FALSE);
3652      }
3653      if (HIWORD (wParam) == EN_UPDATE && LOWORD (wParam) == IDC_EDIT1) {
3654        char editStr[50];
3655
3656        memset (appendTmp, 0, sizeof (appendTmp));
3657        #ifndef _WIN32_WCE
3658        Edit_GetLine (hwndEdit, 0, editStr, sizeof (editStr));
3659        if (1) {
3660        #else
3661        int wstrlen = Edit_GetLine (hwndEdit, 0, appendTmp, sizeof (appendTmp));
3662        unsigned char *tStart = (unsigned char*) editStr;
3663        const UTF16 *sStart = (const UTF16 *) appendTmp;
3664        if (ConvertUTF16toUTF8 (&sStart,  sStart + wstrlen,
3665              &tStart, tStart + sizeof (gosmSstr), lenientConversion)
3666            == conversionOK) {
3667          *tStart = '\0';
3668          /* SipShowIM (SIPF_ON); The only way we can get here without the
3669          IM showing is if the device has a hardware keyboard/keypak */
3670        #endif
3671          option = searchMode;
3672          GeoSearch (editStr);
3673          InvalidateRect (hWnd, NULL, FALSE);
3674        }
3675     }
3676         break;
3677    default:
3678      return DefWindowProc (hWnd, message, wParam, lParam);
3679  }
3680  if (Exit) PostMessage (hWnd, WM_CLOSE, 0, 0);
3681  return FALSE;
3682}
3683
3684BOOL InitApplication (void)
3685{
3686  WNDCLASS wc;
3687
3688  wc.style=0;
3689  wc.lpfnWndProc=(WNDPROC)MainWndProc;
3690  wc.cbClsExtra=0;
3691  wc.cbWndExtra=0;
3692  wc.hInstance= hInst;
3693  wc.hIcon=LoadIcon(hInst, MAKEINTRESOURCE(ID_MAINICON)); 
3694  wc.hCursor=LoadCursor(NULL,IDC_ARROW);
3695  wc.hbrBackground=(HBRUSH) GetStockObject(WHITE_BRUSH);
3696  wc.lpszMenuName = NULL;
3697  wc.lpszClassName = TEXT ("GosmoreWClass");
3698
3699  return(RegisterClass(&wc));
3700}
3701
3702HWND InitInstance(int nCmdShow)
3703{
3704  HWND prev;
3705  // check if gosmore is already running
3706  prev = FindWindow(TEXT ("GosmoreWClass"), NULL);
3707  if (prev != NULL) {
3708    ShowWindow(prev, SW_RESTORE);
3709    SetForegroundWindow(prev);
3710    return FALSE;
3711  } else {
3712   
3713    mWnd = CreateWindow (TEXT ("GosmoreWClass"), TEXT ("gosmore"), 
3714    #ifdef _WIN32_WCE
3715    WS_DLGFRAME,
3716    #else
3717    WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
3718    #endif
3719                         CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 
3720                         CW_USEDEFAULT,NULL,NULL, hInst,NULL);
3721   
3722    if(!mWnd) return(FALSE);
3723   
3724    ShowWindow (mWnd,nCmdShow);
3725    //UpdateWindow (mWnd);
3726   
3727   
3728    return mWnd;
3729  }
3730}
3731
3732volatile int guiDone = FALSE;
3733
3734DWORD WINAPI NmeaReader (LPVOID lParam)
3735{
3736  static int lastgps=0;
3737  // loop back here if existing connection fails
3738  while (!guiDone) {
3739  #ifndef _WIN32_WCE
3740    Sleep (1000);
3741  #else
3742    // $GPGLL,2546.6752,S,02817.5780,E,210130.812,V,S*5B
3743    DWORD nBytes, got = 0;
3744    COMMTIMEOUTS commTiming;
3745    char rx[300];
3746   
3747    wchar_t portname[6];
3748    wsprintf (portname, TEXT ("COM%d:"), CommPort);
3749
3750    logprintf ("Attempting first connect to CommPort.\n");
3751
3752    // Attempt to reconnect to NMEA device every 1 second until connected
3753    while (!guiDone &&
3754           (port=CreateFile (portname, GENERIC_READ | GENERIC_WRITE, 0,
3755                 NULL, OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE) {
3756      Sleep(1000);
3757      //logprintf("Retrying connect to CommPort\n");
3758    }
3759
3760    if (port != INVALID_HANDLE_VALUE) {
3761
3762      logprintf("Connected to CommPort\n");
3763         
3764#if 1
3765      GetCommTimeouts (port, &commTiming);
3766      commTiming.ReadIntervalTimeout = 20;
3767      commTiming.ReadTotalTimeoutMultiplier = 0;
3768      commTiming.ReadTotalTimeoutConstant = 200; /* Bailout when nothing on the port */
3769     
3770      commTiming.WriteTotalTimeoutMultiplier=5; /* No writing */
3771      commTiming.WriteTotalTimeoutConstant=5;
3772      SetCommTimeouts (port, &commTiming);
3773#endif
3774      if (BaudRate) {
3775        DCB portState;
3776        if(!GetCommState(port, &portState)) {
3777          MessageBox (NULL, TEXT ("GetCommState Error"), TEXT (""),
3778                      MB_APPLMODAL|MB_OK);
3779          return(1);
3780        }
3781        portState.BaudRate = BaudRate;
3782        //portState.Parity=0;
3783        //portState.StopBits=ONESTOPBIT;
3784        //portState.ByteSize=8;
3785        //portState.fBinary=1;
3786        //portState.fParity=0;
3787        //portState.fOutxCtsFlow=0;
3788        //portState.fOutxDsrFlow=0;
3789        //portState.fDtrControl=DTR_CONTROL_ENABLE;
3790        //portState.fDsrSensitivity=0;
3791        //portState.fTXContinueOnXoff=1;
3792        //portState.fOutX=0;
3793        //portState.fInX=0;
3794        //portState.fErrorChar=0;
3795        //portState.fNull=0;
3796        //portState.fRtsControl=RTS_CONTROL_ENABLE;
3797        //portState.fAbortOnError=1;
3798       
3799        if(!SetCommState(port, &portState)) {
3800          MessageBox (NULL, TEXT ("SetCommState Error"), TEXT (""),
3801                      MB_APPLMODAL|MB_OK);
3802          return(1);
3803        }
3804      }
3805     
3806      /* Idea for Windows Mobile 5
3807         #include <gpsapi.h>
3808         if (WM5) {
3809         GPS_POSITION pos;
3810         HANDLE hand = GPSOpenDevice (NULL, NULL, NULL, 0);
3811         while (!guiDone && hand != NULL) {
3812         if (GPSGetPosition (hand, &pos, 500, 0) == ERROR_SUCCESS &&
3813         (pos.dwValidFields & GPS_VALID_LATITUDE)) {
3814         Sleep (800);
3815         pos.dblLatitude, pos.dblLongitude;
3816         }
3817         else Sleep (100);
3818         }
3819         if (hand) GPSCloseDevice (hand);
3820         } */
3821     
3822#if 0
3823      PurgeComm (port, PURGE_RXCLEAR); /* Baud rate wouldn't change without this ! */
3824      DWORD nBytes2 = 0;
3825      COMSTAT cStat;
3826      ClearCommError (port, &nBytes, &cStat);
3827      rx2 = (char*) malloc (600);
3828      ReadFile(port, rx, sizeof(rx), &nBytes, NULL);
3829      if(!GetCommState(port, &portState)) {
3830        MessageBox (NULL, TEXT ("GetCommState Error"), TEXT (""),
3831                    MB_APPLMODAL|MB_OK);
3832        return(1);
3833      }
3834      ReadFile(port, rx2, 600, &nBytes2, NULL);
3835#endif
3836     
3837      //char logName[80];
3838      //sprintf (logName, "%slog.nmea", docPrefix);
3839      //FILE *log = fopen (logName, "wb");
3840
3841      // keep reading nmea until guiDone or serial port fails
3842      bool status;
3843      while (!guiDone &&
3844             (status = ReadFile(port, rx + got, 
3845                                     sizeof(rx) - got, &nBytes, NULL))) {
3846        //      logprintf ("status = %d, nBytes = %d\n", status, nBytes);
3847        if (nBytes > 0) {
3848          got += nBytes;
3849          //if (log) fwrite (rx, nBytes, 1, log);
3850         
3851          //wndStr[0]='\0';
3852          //FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
3853          //MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),wndStr,STRLEN,NULL);
3854         
3855          if (ProcessNmea (rx, (unsigned*)&got)) {
3856            int now;
3857            now=GetTickCount();
3858            if(!lastgps) lastgps=now;
3859            GpsIdle=(lastgps-now)/1000;
3860            lastgps=now;
3861            PostMessage (mWnd, WM_USER + 1, 0, (int) /* intptr_t */ gpsNew);
3862          }
3863        } // if nBytes > 0
3864      } // while ReadFile(...)
3865      if (!guiDone) {
3866        logprintf("Connection to CommPort failed.\n");
3867      }
3868    } // if port != INVALID_FILE_HANDLE
3869  #endif
3870  } // while !guiDone
3871  guiDone = FALSE;
3872  GpsIdle=999; /* Let user know GPS is off */
3873  //if (log) fclose (log);
3874  CloseHandle (port);
3875  return 0;
3876}
3877
3878
3879void XmlOut (FILE *newWayFile, const char *k, const char *v)
3880{
3881  if (*v != '\0') {
3882    fprintf (newWayFile, "  <tag k='%s' v='", k);
3883    for (; *v != '\0'; v++) {
3884      if (*v == '\'') fprintf (newWayFile, "&apos;");
3885      else if (*v == '&') fprintf (newWayFile, "&amp;");
3886      else fputc (*v, newWayFile);
3887    }
3888    fprintf (newWayFile, "' />\n");
3889  }
3890}
3891
3892extern "C" {
3893int WINAPI WinMain(
3894    HINSTANCE  hInstance,         // handle of current instance
3895    HINSTANCE  hPrevInstance,     // handle of previous instance
3896    #ifdef _WIN32_WCE
3897    LPWSTR  lpszCmdLine,                  // pointer to command line
3898    #else
3899    LPSTR lpszCmdLine,
3900    #endif
3901    int  nCmdShow)                // show state of window
3902{
3903  if(hPrevInstance) return(FALSE);
3904  hInst = hInstance;
3905  gDisplayOff = FALSE;
3906  wchar_t argv0[80];
3907  GetModuleFileNameW (NULL, argv0, sizeof (argv0) / sizeof (argv0[0]));
3908  UTF16 *sStart = (UTF16*) argv0, *rchr = (UTF16*) wcsrchr (argv0, L'\\');
3909  wcscpy (rchr ? (wchar_t *) rchr + 1 : argv0, L"");
3910  unsigned char *tStart = (unsigned char *) docPrefix;
3911  ConvertUTF16toUTF8 ((const UTF16 **) &sStart, sStart + wcslen (argv0),
3912    &tStart, tStart + sizeof (docPrefix), lenientConversion);
3913  *tStart = '\0';
3914  #if 0
3915  GetModuleFileName (NULL, docPrefix, sizeof (docPrefix));
3916  if (strrchr (docPrefix, '\\')) *strrchr (docPrefix, '\\') = '\0';
3917  #endif
3918
3919  char optFileName[sizeof(docPrefix) + 13];
3920  sprintf (optFileName, "%s\\gosmore.opt", docPrefix);
3921  FILE *optFile = fopen (optFileName, "r"); 
3922  if (!optFile) {
3923    strcpy (docPrefix, "\\My Documents\\");
3924    optFile = fopen ("\\My Documents\\gosmore.opt", "rb");
3925  }
3926
3927  //store log file name
3928  sprintf (logFileName, "%s\\gosmore.log.txt", docPrefix);
3929
3930  #ifdef _WIN32_WCE
3931  wcscat (argv0, L"gosmore.pak");
3932  SerializeOptions (optFile, TRUE, argv0);
3933  #else
3934  SerializeOptions (optFile, TRUE, "gosmore.pak");
3935  Keyboard = 1;
3936  #endif
3937  int newWayFileNr = 0;
3938  LOG if (optFile) fread (&newWayFileNr, sizeof (newWayFileNr), 1, optFile);
3939  if (Exit) {
3940    MessageBox (NULL, TEXT ("Pak file not found"), TEXT (""),
3941      MB_APPLMODAL|MB_OK);
3942    return 1;
3943  }
3944  GtkWidget dumdraw;
3945  draw = &dumdraw;
3946
3947  LOG if(!InitApplication ()) return(FALSE);
3948  LOG if (!InitInstance (nCmdShow)) return(FALSE);
3949
3950  newWays[0].cnt = 0;
3951
3952  #ifdef _WIN32_WCE
3953  LOG InitCeGlue();
3954  if (SHFullScreenPtr) {
3955    if (FullScreen) {
3956      (*SHFullScreenPtr)(mWnd, SHFS_HIDETASKBAR |
3957                         SHFS_HIDESTARTICON | SHFS_HIDESIPBUTTON);
3958      MoveWindow (mWnd, 0, 0, GetSystemMetrics(SM_CXSCREEN),
3959                  GetSystemMetrics(SM_CYSCREEN), FALSE);
3960    } else {
3961      (*SHFullScreenPtr)(mWnd, SHFS_HIDESIPBUTTON);
3962    } 
3963  }
3964  #endif
3965
3966  DWORD threadId;
3967  if (CommPort == 0) {}
3968  else /* if((port=CreateFile (portname, GENERIC_READ | GENERIC_WRITE, 0,
3969          NULL, OPEN_EXISTING, 0, 0)) != INVALID_HANDLE_VALUE) */ {
3970    LOG CreateThread (NULL, 0, NmeaReader, NULL, 0, &threadId);
3971    }
3972  /*   else MessageBox (NULL, TEXT ("No Port"), TEXT (""), MB_APPLMODAL|MB_OK); */
3973
3974  MSG    msg;
3975  LOG while (GetMessage (&msg, NULL, 0, 0)) {
3976    //logprintf ("%d %d %d %d\n", msg.hwnd == mWnd, msg.message, msg.lParam, msg.wParam);
3977    int oldCsum = clat + clon, found = msg.message == WM_KEYDOWN;
3978    if (Keyboard && msg.hwnd == hwndEdit && msg.message == WM_LBUTTONDOWN) {
3979      option = option == searchMode ? mapMode : searchMode;
3980      SipShowIM (option == searchMode ? SIPF_ON : SIPF_OFF);
3981      InvalidateRect (mWnd, NULL, FALSE);
3982    } // I couldn't find an EN_ event that traps a click on the searchbar.
3983    if (msg.message == WM_KEYDOWN) {
3984      if ((msg.wParam == '0' && option != searchMode) || msg.wParam == MenuKey) {
3985        HitButton (0);
3986        if (Keyboard && optionMode != searchMode) SipShowIM (SIPF_OFF);
3987      }
3988      else if (msg.wParam == '8' && option != searchMode) HitButton (1);
3989      else if (msg.wParam == '9' && option != searchMode) HitButton (2);
3990
3991      else if (option == ZoomInKeyNum) ZoomInKey = msg.wParam;
3992      else if (option == ZoomOutKeyNum) ZoomOutKey = msg.wParam;
3993      else if (option == MenuKeyNum) MenuKey = msg.wParam;
3994     
3995      else if (msg.wParam == (DWORD) ZoomInKey) zoom = zoom * 3 / 4;
3996      else if (msg.wParam == (DWORD) ZoomOutKey) zoom = zoom * 4 / 3;
3997
3998      else if (VK_DOWN == msg.wParam) clat -= zoom / 2;
3999      else if (VK_UP == msg.wParam) clat += zoom / 2;
4000      else if (VK_LEFT == msg.wParam) clon -= zoom / 2;
4001      else if (VK_RIGHT == msg.wParam) clon += zoom / 2;
4002      else found = FALSE;
4003     
4004      if (found) InvalidateRect (mWnd, NULL, FALSE);
4005      if (oldCsum != clat + clon) FollowGPSr = FALSE;
4006    }
4007    if (!found) {
4008      TranslateMessage (&msg);
4009      DispatchMessage (&msg);
4010    }
4011  }
4012  guiDone = TRUE;
4013
4014  LOG while (port != INVALID_HANDLE_VALUE && guiDone) Sleep (1000);
4015
4016  optFile = fopen (optFileName, "r+b");
4017  if (!optFile) optFile = fopen ("\\My Documents\\gosmore.opt", "wb");
4018  LOG SerializeOptions (optFile, FALSE, NULL);
4019  if (optFile) {
4020    fwrite (&newWayFileNr, sizeof (newWayFileNr), 1, optFile);
4021    fclose (optFile);
4022  }
4023  LOG gpsNewStruct *first = FlushGpx ();
4024  if (newWayCnt > 0) {
4025    char VehicleName[80];
4026    #define M(v) Vehicle == v ## R ? #v :
4027    sprintf(VehicleName, "%s", RESTRICTIONS NULL);
4028    #undef M
4029
4030    char bname[80], fname[80];
4031    LOG getBaseFilename(bname, first);
4032    sprintf (fname, "%s.osm", bname);
4033
4034    FILE *newWayFile = fopen (fname, "w");
4035    if (newWayFile) {
4036      LOG fprintf (newWayFile, "<?xml version='1.0' encoding='UTF-8'?>\n"
4037                           "<osm version='0.6' generator='gosmore'>\n");
4038      for (int j, id = -1, i = 0; i < newWayCnt; i++) {
4039        for (j = 0; j < newWays[i].cnt; j++) {
4040          fprintf (newWayFile, "<node id='%d' visible='true' lat='%.5lf' "
4041            "lon='%.5lf' %s>\n", id - j, LatInverse (newWays[i].coord[j][1]),
4042            LonInverse (newWays[i].coord[j][0]),
4043            newWays[i].cnt <= 1 ? "" : "/");
4044        }
4045        if (newWays[i].cnt > 1) {
4046          fprintf (newWayFile, "<way id='%d' action='modify' "
4047            "visible='true'>\n", id - newWays[i].cnt);
4048          for (j = 0; j < newWays[i].cnt; j++) {
4049            fprintf (newWayFile, "  <nd ref='%d'/>\n", id--);
4050          }
4051        }
4052        id--;
4053        XmlOut (newWayFile, "todo", "FIXME - Added by gosmore");
4054        if (newWays[i].oneway) XmlOut (newWayFile, "oneway", "yes");
4055        if (newWays[i].bridge) XmlOut (newWayFile, "bridge", "yes");
4056        if (newWays[i].klas >= 0) fprintf (newWayFile, "%s",
4057          klasTable[newWays[i].klas].tags);
4058        XmlOut (newWayFile, "name", newWays[i].name);
4059        XmlOut (newWayFile, "note", newWays[i].note);
4060        fprintf (newWayFile, "</%s>\n", newWays[i].cnt <= 1 ? "node" : "way");
4061      }
4062      fprintf (newWayFile, "</osm>\n");
4063      fclose (newWayFile);
4064    }
4065  }
4066
4067  LOG if (logFP(false)) fclose(logFP(false));
4068
4069  return 0;
4070}
4071} // extern "C"
4072#endif
Note: See TracBrowser for help on using the repository browser.