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

Last change on this file since 19605 was 19605, checked in by nic, 10 years ago

Add multipolygon and border support
Fix a few warnings and bugs

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