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

Last change on this file since 15979 was 15977, checked in by daviddean, 10 years ago

Added support for adding openstreetbugs to gosmore through the appendOSBtoOSM.sh script. Currently hard-coded to OSB in Brisbane, Australia. Should probably make more flexible.

  • Property svn:executable set to *
File size: 88.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#ifndef _WIN32
16#include <sys/mman.h>
17#include <arpa/inet.h>
18#include <netinet/in.h>
19#include <sys/types.h>
20#include <sys/socket.h>
21#define TEXT(x) x
22#else
23#include <windows.h>
24#endif
25#ifdef _WIN32_WCE
26#include <windowsx.h>
27//#include <winuserm.h> // For playing a sound ??
28#include <sipapi.h>
29#include <aygshell.h>
30#include "libgosm.h"
31#include "ceglue.h"
32#include "ConvertUTF.h"
33#include "resource.h"
34
35// Unfortunately eMbedded Visual C++ TEXT() function does not use UTF8
36// So we have to repeat the OPTIONS table
37#define OPTIONS \
38  o (FollowGPSr,      0, 2) \
39  o (AddWayOrNode,    0, 2) \
40  o (Search,          0, 1) \
41  o (StartRoute,      0, 1) \
42  o (EndRoute,        0, 1) \
43  o (OrientNorthwards,0, 2) \
44  o (FastestRoute,    0, 2) \
45  o (Vehicle,         motorcarR, onewayR) \
46  o (English,         0, \
47            sizeof (optionNameTable) / sizeof (optionNameTable[0])) \
48  o (ButtonSize,      1, 5) \
49  o (IconSet,         0, 4) \
50  o (DetailLevel,     0, 5) \
51  o (CommPort,        0, 13) \
52  o (BaudRate,        0, 6) \
53  o (QuickOptions,    0, 2) \
54  o (Exit,            0, 2) \
55  o (ZoomInKey,       0, 3) \
56  o (ZoomOutKey,      0, 3) \
57  o (MenuKey,         0, 3) \
58  o (HideZoomButtons, 0, 2) \
59  o (ShowCoordinates, 0, 3) \
60  o (ShowTrace,       0, 2) \
61  o (ModelessDialog,  0, 2) \
62  o (FullScreen,      0, 2) \
63  o (ValidateMode,    0, 2) \
64  o (DisplayOff,      0, 1)
65#else
66#include <unistd.h>
67#include <sys/stat.h>
68#include <string>
69#include "libgosm.h"
70using namespace std;
71#define wchar_t char
72#define wsprintf sprintf
73#define OPTIONS \
74  o (FollowGPSr,      0, 2) \
75  o (Search,          0, 1) \
76  o (StartRoute,      0, 1) \
77  o (EndRoute,        0, 1) \
78  o (OrientNorthwards,0, 2) \
79  o (FastestRoute,    0, 2) \
80  o (Vehicle,         motorcarR, onewayR) \
81  o (English,         0, \
82                 sizeof (optionNameTable) / sizeof (optionNameTable[0])) \
83  o (ButtonSize,      1, 5) \
84  o (IconSet,         0, 4) \
85  o (DetailLevel,     0, 5) \
86  o (ShowActiveRouteNodes, 0, 2) \
87  o (ValidateMode,    0, 2)
88
89#define HideZoomButtons 0
90#define MenuKey 0
91#endif
92char docPrefix[80] = "";
93
94#if !defined (HEADLESS) && !defined (_WIN32_WCE)
95#include <gtk/gtk.h>
96#endif
97
98#if __FreeBSD__ || __APPLE__ // Thanks to Ted Mielczarek & Dmitry
99#define fopen64(x,y) fopen(x,y)
100#endif
101
102// We emulate just enough of gtk to make it work
103#ifdef _WIN32_WCE
104#define gtk_widget_queue_clear(x) // After Click() returns we Invalidate
105HWND hwndList;
106#define gtk_toggle_button_set_active(x,y) // followGPRr
107struct GtkWidget { 
108  struct {
109    int width, height;
110  } allocation;
111  int window;
112};
113typedef int GtkComboBox;
114struct GdkEventButton {
115  int x, y, button;
116};
117
118#define ROUTE_PEN 0
119#define VALIDATE_PEN 1
120#define RESERVED_PENS 2
121
122HINSTANCE hInst;
123HWND   mWnd, dlgWnd = NULL;
124
125BOOL CALLBACK DlgSearchProc (
126        HWND hwnd, 
127        UINT Msg, 
128        WPARAM wParam, 
129        LPARAM lParam);
130#else
131const char *FindResource (const char *fname)
132{
133  static string s;
134  struct stat dummy;
135  // first check in current working directory
136  if (stat (fname, &dummy) == 0) return fname;
137  // then check in $HOME
138  s = (string) getenv ("HOME") + "/.gosmore/" + fname;
139  if (stat (s.c_str (), &dummy) == 0) return s.c_str();
140  // then check in RES_DIR
141  s = (string) RES_DIR + fname;
142  if (stat (s.c_str (), &dummy) == 0) return s.c_str();
143  // and then fall back on current working directory if it cannot be
144  // found anywhere else (this is so new files are created in the
145  // current working directory)
146  return fname;
147}
148#endif
149
150// used for showing logs to a file (with default)
151// changed to more suitable value for WinCE in WinMain
152char logFileName[80] = "gosmore.log.txt";
153
154FILE * logFP(bool create = true) {
155  static FILE * f;
156  if (!f && create) {
157    f = fopen(logFileName,"at");
158    fprintf(f,"-----\n");
159  }
160  return f;
161}
162
163void logprintf(char * format, ...)
164{
165// print [hh:mm:ss] timestamp to log first
166#ifdef _WIN32_CE
167  // wince doesn't implement the time.h functions
168  SYSTEMTIME t;
169  GetLocalTime(&t);
170  fprintf(logFP(), "[%02d:%02d:%02d] ", t.wHour, t.wMinute, t.wSecond);
171#else
172  time_t seconds = time(NULL);
173  struct tm * t = localtime(&seconds);
174  fprintf(logFP(), "[%02d:%02d:%02d] ",t->tm_hour,t->tm_min,t->tm_sec);
175#endif
176
177  // then print original log string
178  va_list args;
179  va_start(args, format);
180  vfprintf(logFP(), format, args);
181  va_end (args);
182}
183
184
185struct klasTableStruct {
186  const wchar_t *desc;
187  const char *tags;
188} klasTable[] = {
189#define s(k,v,shortname,extraTags) \
190  { TEXT (shortname), "  <tag k='" #k "' v='" #v "' />\n" extraTags },
191STYLES
192#undef s
193};
194
195#define notImplemented \
196  o (ShowCompass,     ) \
197  o (ShowPrecision,   ) \
198  o (ShowSpeed,       ) \
199  o (ShowHeading,     ) \
200  o (ShowElevation,   ) \
201  o (ShowDate,        ) \
202  o (ShowTime,        ) \
203
204#define o(en,min,max) en ## Num,
205enum { OPTIONS numberOfOptions, chooseObjectToAdd };
206#undef o
207
208//  TEXT (#en), TEXT (de), TEXT (es), TEXT (fr), TEXT (it), TEXT (nl) },
209const char *optionNameTable[][numberOfOptions] = {
210#define o(en,min,max) #en,
211  { OPTIONS }, // English is same as variable names
212#undef o
213
214#ifdef _WIN32_WCE
215#include "translations.c"
216#endif
217};
218
219#define o(en,min,max) int en = min;
220OPTIONS
221#undef o
222
223#ifndef HEADLESS
224#define STATUS_BAR    0
225
226GtkWidget *draw, *location, *followGPSr, *orientNorthwards, *validateMode;
227GtkComboBox *iconSet, *carBtn, *fastestBtn, *detailBtn;
228int clon, clat, zoom, option = EnglishNum, gpsSockTag, setLocBusy = FALSE, gDisplayOff;
229/* zoom is the amount that fits into the window (regardless of window size) */
230double cosAzimuth = 1.0, sinAzimuth = 0.0;
231
232inline void SetLocation (int nlon, int nlat)
233{
234  clon = nlon;
235  clat = nlat;
236  #ifndef _WIN32_WCE
237  char lstr[50];
238  int zl = 0;
239  while (zl < 32 && (zoom >> zl)) zl++;
240  sprintf (lstr, "?lat=%.5lf&lon=%.5lf&zoom=%d", LatInverse (nlat),
241    LonInverse (nlon), 33 - zl);
242  setLocBusy = TRUE;
243  gtk_entry_set_text (GTK_ENTRY (location), lstr);
244  setLocBusy = FALSE;
245  #endif
246}
247
248#ifndef _WIN32_WCE
249int ChangeLocation (void)
250{
251  if (setLocBusy) return FALSE;
252  char *lstr = (char *) gtk_entry_get_text (GTK_ENTRY (location));
253  double lat, lon;
254  while (*lstr != '?' && *lstr != '\0') lstr++;
255  if (sscanf (lstr, "?lat=%lf&lon=%lf&zoom=%d", &lat, &lon, &zoom) == 3) {
256    clat = Latitude (lat);
257    clon = Longitude (lon);
258    zoom = 0xffffffff >> (zoom - 1);
259    gtk_widget_queue_clear (draw);
260  }
261  return FALSE;
262}
263
264int ChangeOption (void)
265{
266  IconSet = gtk_combo_box_get_active (iconSet);
267  Vehicle = gtk_combo_box_get_active (carBtn) + motorcarR;
268  DetailLevel = 4 - gtk_combo_box_get_active (detailBtn);
269  FollowGPSr = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (followGPSr));
270  OrientNorthwards =
271    gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (orientNorthwards));
272  ValidateMode = 
273    gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (validateMode));
274  if (OrientNorthwards) {
275    cosAzimuth = 1.0;
276    sinAzimuth = 0.0;
277  }
278  FastestRoute = !gtk_combo_box_get_active (fastestBtn);
279  gtk_widget_queue_clear (draw);
280  return FALSE;
281}
282#endif
283/*-------------------------------- NMEA processing ------------------------*/
284/* My TGPS 374 frequently produces corrupt NMEA output (probably when the
285   CPU goes into sleep mode) and this may also be true for GPS receivers on
286   long serial lines. To overcome this, we ignore badly formed sentences and
287   we interpolate the date where the jumps in time values seems plausible.
288   
289   For the GPX output there is an extra layer of filtering : The minimum
290   number of observations are dropped so that the remaining observations does
291   not imply any impossible manuavers like traveling back in time or
292   reaching supersonic speed. This effeciently implemented transforming the
293   problem into one of finding the shortest path through a graph :
294   The nodes {a_i} are the list of n observations. Add the starting and
295   ending nodes a_0 and a_n+1, which are both connected to all the other
296   nodes. 2 observation nodes are connected if it's possible that the one
297   will follow the other. If two nodes {a_i} and {a_j} are connected, the
298   cost (weight) of the connection is j - 1 - i, i.e. the number of nodes
299   that needs to be dropped. */
300struct gpsNewStruct {
301  struct {
302    double latitude, longitude, track, speed, hdop, ele;
303    char date[6], tm[6];
304  } fix;
305  int lat, lon; // Mercator
306  unsigned dropped;
307  struct gpsNewStruct *dptr;
308} gpsTrack[18000], *gpsNew = gpsTrack;
309
310// Return the basefilename for saving .osm and .gpx files
311void getBaseFilename(char* basename, gpsNewStruct* first) {
312  char VehicleName[80];
313  #define M(v) Vehicle == v ## R ? #v :
314  sprintf(VehicleName, "%s", RESTRICTIONS NULL);
315  #undef M
316
317  if (first) {
318    sprintf (basename, "%s%.2s%.2s%.2s-%.6s-%s", docPrefix,
319             first->fix.date + 4, first->fix.date + 2, first->fix.date,
320             first->fix.tm, VehicleName);
321  } else {
322    // get time from computer if no gps traces
323#ifdef _WIN32_WCE
324    SYSTEMTIME t;
325    GetSystemTime(&t);
326    sprintf (basename, "%s%02d%02d%02d-%02d%02d%02d-%s", docPrefix,
327             t.wYear % 100, t.wMonth, t.wDay,
328             t.wHour, t.wMinute, t.wSecond, VehicleName);
329#endif
330  }
331}
332
333gpsNewStruct *FlushGpx (void)
334{
335  struct gpsNewStruct *a, *best, *first = NULL;
336  for (best = gpsNew; gpsTrack <= best && best->dropped == 0; best--) {}
337  gpsNew = gpsTrack;
338  if (best <= gpsTrack) return NULL; // No observations
339  for (a = best - 1; gpsTrack <= a && best < a + best->dropped; a--) {
340    if (best->dropped > best - a + a->dropped) best = a;
341  }
342  // We want .., best->dptr->dptr->dptr, best->dptr->dptr, best->dptr, best
343  // Now we reverse the linked list :
344  while (best) {
345    a = best->dptr;
346    best->dptr = first;
347    first = best;
348    best = a;
349  }
350 
351  char bname[80], fname[80];
352  getBaseFilename(bname, first);
353  sprintf (fname, "%s.gpx", bname);
354  FILE *gpx = fopen (fname, "wb");
355  if (!gpx) return first;
356  fprintf (gpx, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\
357<gpx\n\
358 version=\"1.0\"\n\
359creator=\"gosmore\"\n\
360xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\
361xmlns=\"http://www.topografix.com/GPX/1/0\"\n\
362xsi:schemaLocation=\"http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd\n\">\n\
363<trk>\n\
364<trkseg>\n");
365  for (best = first; best; best = best->dptr) { // Iterate the linked list
366    fprintf (gpx, "<trkpt lat=\"%12.9lf\" lon=\"%12.9lf\">\n",
367      best->fix.latitude, best->fix.longitude);
368    if (best->fix.ele < 1e+8) {
369      fprintf (gpx, "<ele>%.3lf</ele>\n", best->fix.ele);
370    }
371    fprintf (gpx, "<time>20%.2s-%.2s-%.2sT%.2s:%.2s:%.2sZ</time>\n</trkpt>\n",
372      best->fix.date + 4, best->fix.date + 2, best->fix.date,
373      best->fix.tm, best->fix.tm + 2, best->fix.tm + 4);
374   
375//    if (best->next && ) fprintf (gpx, "</trkseg>\n</trk>\n<trk>\n<trkseg>\n");
376  }
377  fprintf (gpx, "</trkseg>\n\
378</trk>\n\
379</gpx>\n");
380  return first;
381}
382
383int ProcessNmea (char *rx, unsigned *got)
384{
385  unsigned dataReady = FALSE, i;
386  for (i = 0; i < *got && rx[i] != '$'; i++, (*got)--) {}
387  while (rx[i] == '$') {
388    //for (j = 0; j < got; i++, j++) rx[j] = rx[i];
389    int fLen[19], fStart[19], fNr;
390    memmove (rx, rx + i, *got);
391    for (i = 1, fNr = 0; i < *got && rx[i] != '$' && fNr < 19; fNr++) {
392      fStart[fNr] = i;
393      while (i < *got && (rx[i] == '.' || isdigit (rx[i]))) i++;
394      fLen[fNr] = i - fStart[fNr];
395      while (i < *got && rx[i] != '$' && rx[i++] != ',') {}
396    }
397    while (i < *got && rx[i] != '$') i++;
398    int col = memcmp (rx, "$GPGLL", 6) == 0 ? 1 :
399      memcmp (rx, "$GPGGA", 6) == 0 ? 2 :
400      memcmp (rx, "$GPRMC", 6) == 0 ? 3 : 0;
401    // latitude is at fStart[col], longitude is at fStart[col + 2].
402    if (fNr >= (col == 0 ? 2 : col == 1 ? 6 : col == 2 ? 13 : 10)) {
403      if (col > 0 && fLen[col] > 6 && memchr ("SsNn", rx[fStart[col + 1]], 4)
404        && fLen[col + 2] > 7 && memchr ("EeWw", rx[fStart[col + 3]], 4) &&
405        fLen[col == 1 ? 5 : 1] >= 6 &&
406          memcmp (gpsNew->fix.tm, rx + fStart[col == 1 ? 5 : 1], 6) != 0) {
407        double nLat = (rx[fStart[col]] - '0') * 10 + rx[fStart[col] + 1]
408          - '0' + atof (rx + fStart[col] + 2) / 60;
409        double nLon = ((rx[fStart[col + 2]] - '0') * 10 +
410          rx[fStart[col + 2] + 1] - '0') * 10 + rx[fStart[col + 2] + 2]
411          - '0' + atof (rx + fStart[col + 2] + 3) / 60;
412        if (nLat > 90) nLat = nLat - 180;
413        if (nLon > 180) nLon = nLon - 360; // Mungewell's Sirf Star 3
414        if (tolower (rx[fStart[col + 1]]) == 's') nLat = -nLat;
415        if (tolower (rx[fStart[col + 3]]) == 'w') nLon = -nLon;
416        if (fabs (nLat) < 90 && fabs (nLon) < 180 &&
417            (nLat != 0 || nLon != 0)) { // JNC when starting up
418          if (gpsTrack + sizeof (gpsTrack) / sizeof (gpsTrack[0]) <=
419            ++gpsNew) FlushGpx ();
420          memcpy (gpsNew->fix.tm, rx + fStart[col == 1 ? 5 : 1], 6);
421          gpsNew->dropped = 0;
422          dataReady = TRUE; // Notify only when parsing is complete
423          gpsNew->fix.latitude = nLat;
424          gpsNew->fix.longitude = nLon;
425          gpsNew->lat = Latitude (nLat);
426          gpsNew->lon = Longitude (nLon);
427          gpsNew->fix.ele = 1e+9;
428        }
429      } // If the timestamp wasn't seen before
430      if (col == 2) {
431        gpsNew->fix.hdop = atof (rx + fStart[8]);
432        gpsNew->fix.ele = atof (rx + fStart[9]); // Check height of geoid ??
433      }
434      if (col == 3 && fLen[7] > 0 && fLen[8] > 0 && fLen[9] >= 6) {
435        memcpy (gpsNew->fix.date, rx + fStart[9], 6);
436        gpsNew->fix.speed = atof (rx + fStart[7]);
437        gpsNew->fix.track = atof (rx + fStart[8]);
438       
439        //-------------------------------------------------------------
440        // Now fix the dates and do a little bit of shortest path work
441        int i, j, k, l; // TODO : Change indexes into pointers
442        for (i = 0; gpsTrack < gpsNew + i && !gpsNew[i - 1].dropped &&
443             (((((gpsNew[i].fix.tm[0] - gpsNew[i - 1].fix.tm[0]) * 10 +
444                  gpsNew[i].fix.tm[1] - gpsNew[i - 1].fix.tm[1]) * 6 +
445                  gpsNew[i].fix.tm[2] - gpsNew[i - 1].fix.tm[2]) * 10 +
446                  gpsNew[i].fix.tm[3] - gpsNew[i - 1].fix.tm[3]) * 6 +
447                  gpsNew[i].fix.tm[4] - gpsNew[i - 1].fix.tm[4]) * 10 +
448                  gpsNew[i].fix.tm[5] - gpsNew[i - 1].fix.tm[5] < 30; i--) {}
449        // Search backwards for a discontinuity in tm
450       
451        for (j = i; gpsTrack < gpsNew + j && !gpsNew[j - 1].dropped; j--) {}
452        // Search backwards for the first observation missing its date
453       
454        for (k = j; k <= 0; k++) {
455          memcpy (gpsNew[k].fix.date,
456            gpsNew[gpsTrack < gpsNew + j && k < i ? j - 1 : 0].fix.date, 6);
457          gpsNew[k].dptr = NULL; // Try gpsNew[k] as first observation
458          gpsNew[k].dropped = gpsNew + k - gpsTrack + 1;
459          for (l = k - 1; gpsTrack < gpsNew + l && k - l < 300 &&
460               gpsNew[k].dropped > unsigned (k - l - 1); l--) {
461            // At the point where we consider 300 bad observations, we are
462            // more likely to be wasting CPU cycles.
463            int tdiff =
464              ((((((((gpsNew[k].fix.date[4] - gpsNew[l].fix.date[4]) * 10 +
465              gpsNew[k].fix.date[5] - gpsNew[l].fix.date[5]) * 12 +
466              (gpsNew[k].fix.date[2] - gpsNew[l].fix.date[2]) * 10 +
467              gpsNew[k].fix.date[3] - gpsNew[l].fix.date[3]) * 31 +
468              (gpsNew[k].fix.date[0] - gpsNew[l].fix.date[0]) * 10 +
469              gpsNew[k].fix.date[1] - gpsNew[l].fix.date[1]) * 24 +
470              (gpsNew[k].fix.tm[0] - gpsNew[l].fix.tm[0]) * 10 +
471              gpsNew[k].fix.tm[1] - gpsNew[l].fix.tm[1]) * 6 +
472              gpsNew[k].fix.tm[2] - gpsNew[l].fix.tm[2]) * 10 +
473              gpsNew[k].fix.tm[3] - gpsNew[l].fix.tm[3]) * 6 +
474              gpsNew[k].fix.tm[4] - gpsNew[l].fix.tm[4]) * 10 +
475              gpsNew[k].fix.tm[5] - gpsNew[l].fix.tm[5];
476             
477            /* Calculate as if every month has 31 days. It causes us to
478               underestimate the speed travelled in very rare circumstances,
479               (e.g. midnight GMT on Feb 28) allowing a few bad observation
480               to sneek in. */
481            if (0 < tdiff && tdiff < 3600 * 24 * 62 /* Assume GPS used more */
482                /* frequently than once every 2 months */ &&
483                fabs (gpsNew[k].fix.latitude - gpsNew[l].fix.latitude) +
484                fabs (gpsNew[k].fix.longitude - gpsNew[l].fix.longitude) *
485                  cos (gpsNew[k].fix.latitude * (M_PI / 180.0)) <
486                    tdiff * (1600 / 3600.0 * 360 / 40000) && // max 1600 km/h
487                gpsNew[k].dropped > gpsNew[l].dropped + k - l - 1) {
488              gpsNew[k].dropped = gpsNew[l].dropped + k - l - 1;
489              gpsNew[k].dptr = gpsNew + l;
490            }
491          } // For each possible connection
492        } // For each new observation
493      } // If it's a properly formatted RMC
494    } // If the sentence had enough columns for our purposes.
495    else if (i == *got) break; // Retry when we receive more data
496    *got -= i;
497  } /* If we know the sentence type */
498  return dataReady;
499}
500
501void DoFollowThing (gpsNewStruct *gps)
502{
503  if (!/*gps->fix.mode >= MODE_2D &&*/ FollowGPSr) return;
504  SetLocation (Longitude (gps->fix.longitude), Latitude (gps->fix.latitude));
505/*    int plon = Longitude (gps->fix.longitude + gps->fix.speed * 3600.0 /
506      40000000.0 / cos (gps->fix.latitude * (M_PI / 180.0)) *
507      sin (gps->fix.track * (M_PI / 180.0)));
508    int plat = Latitude (gps->fix.latitude + gps->fix.speed * 3600.0 /
509      40000000.0 * cos (gps->fix.track * (M_PI / 180.0))); */
510    // Predict the vector that will be traveled in the next 10seconds
511//    printf ("%5.1lf m/s Heading %3.0lf\n", gps->fix.speed, gps->fix.track);
512//    printf ("%lf %lf\n", gps->fix.latitude, gps->fix.longitude);
513   
514  __int64 dlon = clon - flon, dlat = clat - flat;
515  flon = clon;
516  flat = clat;
517  if (route) Route (FALSE, dlon, dlat, Vehicle, FastestRoute);
518
519  static ndType *decide[3] = { NULL, NULL, NULL }, *oldDecide = NULL;
520  static const wchar_t *command[3] = { NULL, NULL, NULL }, *oldCommand = NULL;
521  decide[0] = NULL;
522  command[0] = NULL;
523  if (shortest) {
524    routeNodeType *x = shortest->shortest;
525    if (!x) command[0] = TEXT ("stop");
526    if (x && Sqr (dlon) + Sqr (dlon) > 10000 /* faster than ~3 km/h */ &&
527        dlon * (x->nd->lon - clon) + dlat * (x->nd->lat - clat) < 0) {
528      command[0] = TEXT ("uturn");
529      decide[0] = NULL;
530    }
531    else if (x) {
532      int nextJunction = TRUE;
533      double dist = sqrt (Sqr ((double) (x->nd->lat - flat)) +
534                          Sqr ((double) (x->nd->lon - flon)));
535      for (x = shortest; x->shortest &&
536           dist < 40000 /* roughly 300m */; x = x->shortest) {
537        int roundExit = 0;
538        while (x->shortest && ((1 << roundaboutR) &
539                       (Way (x->shortest->nd))->bits)) {
540          if (isupper (JunctionType (x->shortest->nd))) roundExit++;
541          x = x->shortest;
542        }
543        if (!x->shortest || roundExit) {
544          decide[0] = x->nd;
545          static const wchar_t *rtxt[] = { NULL, TEXT ("round1"),
546            TEXT ("round2"),
547            TEXT ("round3"), TEXT ("round4"), TEXT ("round5"),
548            TEXT ("round6"), TEXT ("round7"), TEXT ("round8") };
549          command[0] = rtxt[roundExit];
550          break;
551        }
552       
553        ndType *n0 = x->nd, *n1 = x->shortest->nd, *nd = n1;
554        int n2lat =
555          x->shortest->shortest ? x->shortest->shortest->nd->lat : tlat;
556        int n2lon =
557          x->shortest->shortest ? x->shortest->shortest->nd->lon : tlon;
558        while (nd > ndBase && nd[-1].lon == nd->lon &&
559          nd[-1].lat == nd->lat) nd--;
560        int segCnt = 0; // Count number of segments at x->shortest
561        do {
562          // TODO : Only count segment traversable by 'Vehicle'
563          // Except for the case where a cyclist crosses a motorway.
564          if (nd->other[0] >= 0) segCnt++;
565          if (nd->other[1] >= 0) segCnt++;
566        } while (++nd < ndBase + hashTable[bucketsMin1 + 1] &&
567                 nd->lon == nd[-1].lon && nd->lat == nd[-1].lat);
568        if (segCnt > 2) {
569          __int64 straight =
570            (n2lat - n1->lat) * (__int64) (n1->lat - n0->lat) +
571            (n2lon - n1->lon) * (__int64) (n1->lon - n0->lon), left =
572            (n2lat - n1->lat) * (__int64) (n1->lon - n0->lon) -
573            (n2lon - n1->lon) * (__int64) (n1->lat - n0->lat);
574          decide[0] = n1;
575          if (straight < left) {
576            command[0] = nextJunction ? TEXT ("turnleft") : TEXT ("keepleft");
577            break;
578          }
579          if (straight < -left) {
580            command[0] = nextJunction
581                             ? TEXT ("turnright"): TEXT ("keepright");
582            break;
583          }
584          nextJunction = FALSE;
585        }
586        dist += sqrt (Sqr ((double) (n2lat - n1->lat)) +
587                      Sqr ((double) (n2lon - n1->lon)));
588      } // While looking ahead to the next turn.
589      if (!x->shortest && dist < 6000) {
590        command[0] = TEXT ("stop");
591        decide[0] = NULL;
592      }
593    } // If not on final segment
594  } // If the routing was successful
595
596  if (command[0] && (oldCommand != command[0] || oldDecide != decide[0]) &&
597      command[0] == command[1] && command[1] == command[2] &&
598      decide[0] == decide[1] && decide[1] == decide[2]) {
599    oldCommand = command[0];
600    oldDecide = decide[0];
601#ifdef _WIN32_WCE
602    wchar_t argv0[80];
603    GetModuleFileName (NULL, argv0, sizeof (argv0) / sizeof (argv0[0]));
604    wcscpy (argv0 + wcslen (argv0) - 12, command[0]); // gosm_arm.exe to a.wav
605    wcscpy (argv0 + wcslen (argv0), TEXT (".wav"));
606    PlaySound (argv0, NULL, SND_FILENAME | SND_NODEFAULT | SND_ASYNC );
607#else
608    printf ("%s\n", command[0]);
609#endif
610  }
611  memmove (command + 1, command, sizeof (command) - sizeof (command[0]));
612  memmove (decide + 1, decide, sizeof (decide) - sizeof (decide[0]));
613
614  double dist = sqrt (Sqr ((double) dlon) + Sqr ((double) dlat));
615  if (!OrientNorthwards && dist > 100.0) {
616    cosAzimuth = dlat / dist;
617    sinAzimuth = -dlon / dist;
618  }                                           
619  gtk_widget_queue_clear (draw);
620} // If following the GPSr and it has a fix.
621
622#ifndef _WIN32_WCE
623#ifdef ROUTE_TEST
624gint RouteTest (GtkWidget * /*widget*/, GdkEventButton *event, void *)
625{
626  static int ptime = 0;
627  ptime = time (NULL);
628  int w = draw->allocation.width;
629  int perpixel = zoom / w;
630  clon += lrint ((event->x - w / 2) * perpixel);
631  clat -= lrint ((event->y - draw->allocation.height / 2) * perpixel);
632/*    int plon = clon + lrint ((event->x - w / 2) * perpixel);
633    int plat = clat -
634      lrint ((event->y - draw->allocation.height / 2) * perpixel); */
635  FollowGPSr = TRUE;
636  gpsNewStruct gNew;
637  gNew.fix.latitude = LatInverse (clat);
638  gNew.fix.longitude = LonInverse (clon);
639  DoFollowThing (&gNew);
640}
641#else
642// void GpsMove (gps_data_t *gps, char */*buf*/, size_t /*len*/, int /*level*/)
643void ReceiveNmea (gpointer /*data*/, gint source, GdkInputCondition /*c*/)
644{
645  static char rx[1200];
646  static unsigned got = 0;
647  int cnt = read (source, rx + got, sizeof (rx) - got);
648  if (cnt == 0) {
649    gdk_input_remove (gpsSockTag);
650    return;
651  }
652  got += cnt;
653 
654  if (ProcessNmea (rx, &got)) DoFollowThing (gpsNew);
655}
656#endif // !ROUTE_TEST
657
658gint Scroll (GtkWidget * /*widget*/, GdkEventScroll *event, void * /*w_cur*/)
659{
660  if (event->direction == GDK_SCROLL_UP) zoom = zoom / 4 * 3;
661  if (event->direction == GDK_SCROLL_DOWN) zoom = zoom / 3 * 4;
662  SetLocation (clon, clat);
663  gtk_widget_queue_clear (draw);
664  return FALSE;
665}
666
667#else // _WIN32_WCE
668#define NEWWAY_MAX_COORD 10
669struct newWaysStruct {
670  int coord[NEWWAY_MAX_COORD][2], klas, cnt, oneway, bridge;
671  char name[40], note[40];
672} newWays[500];
673
674
675int newWayCnt = 0;
676
677BOOL CALLBACK DlgSetTagsProc (HWND hwnd, UINT Msg, WPARAM wParam,
678  LPARAM lParam)
679{
680  if (Msg != WM_COMMAND) return FALSE;
681  HWND edit = GetDlgItem (hwnd, IDC_NAME);
682  if (wParam == IDC_RD1 || wParam == IDC_RD2) {
683    Edit_ReplaceSel (edit, TEXT (" Road"));
684  }
685  if (wParam == IDC_ST1 || wParam == IDC_ST2) {
686    Edit_ReplaceSel (edit, TEXT (" Street"));
687  }
688  if (wParam == IDC_AVE1 || wParam == IDC_AVE2) {
689    Edit_ReplaceSel (edit, TEXT (" Avenue"));
690  }
691  if (wParam == IDC_DR1 || wParam == IDC_DR2) {
692    Edit_ReplaceSel (edit, TEXT (" Drive"));
693  }
694  if (wParam == IDOK) {
695    UTF16 name[40], *sStart = name;
696    int wstrlen = Edit_GetLine (edit, 0, name, sizeof (name));
697    unsigned char *tStart = (unsigned char*) newWays[newWayCnt].name;
698    ConvertUTF16toUTF8 ((const UTF16 **)&sStart,  sStart + wstrlen,
699        &tStart, tStart + sizeof (newWays[0].name), lenientConversion);
700
701    wstrlen = Edit_GetLine (GetDlgItem (hwnd, IDC_NOTE), 0,
702      name, sizeof (name));
703    sStart = name;
704    tStart = (unsigned char*) newWays[newWayCnt].note;
705    ConvertUTF16toUTF8 ((const UTF16 **)&sStart,  sStart + wstrlen,
706        &tStart, tStart + sizeof (newWays[0].note), lenientConversion);
707
708    newWays[newWayCnt].oneway = IsDlgButtonChecked (hwnd, IDC_ONEWAY2);
709    newWays[newWayCnt++].bridge = IsDlgButtonChecked (hwnd, IDC_BRIDGE2);
710  }
711  if (wParam == IDCANCEL || wParam == IDOK) {
712    SipShowIM (SIPF_OFF);
713    EndDialog (hwnd, wParam == IDOK);
714    return TRUE;
715  }
716  return FALSE;
717}
718/*
719BOOL CALLBACK DlgSetTags2Proc (HWND hwnd, UINT Msg, WPARAM wParam,
720  LPARAM lParam)
721{
722  if (Msg == WM_INITDIALOG) {
723    HWND klist = GetDlgItem (hwnd, IDC_CLASS);
724    for (int i = 0; i < sizeof (klasTable) / sizeof (klasTable[0]); i++) {
725      SendMessage (klist, LB_ADDSTRING, 0, (LPARAM) klasTable[i].desc);
726    }
727  }
728  if (Msg == WM_COMMAND && wParam == IDOK) {
729    newWays[newWayCnt].cnt = newWayCoordCnt;
730    newWays[newWayCnt].oneway = IsDlgButtonChecked (hwnd, IDC_ONEWAY);
731    newWays[newWayCnt].bridge = IsDlgButtonChecked (hwnd, IDC_BRIDGE);
732    newWays[newWayCnt++].klas = SendMessage (GetDlgItem (hwnd, IDC_CLASS),
733                                  LB_GETCURSEL, 0, 0);
734  }
735 
736  if (Msg == WM_COMMAND && (wParam == IDCANCEL || wParam == IDOK)) {
737    EndDialog (hwnd, wParam == IDOK);
738    return TRUE;
739  }
740  return FALSE;
741}*/
742
743BOOL CALLBACK DlgChooseOProc (HWND hwnd, UINT Msg, WPARAM wParam,
744  LPARAM lParam)
745{
746  if (Msg == WM_INITDIALOG) {
747    HWND klist = GetDlgItem (hwnd, IDC_LISTO);
748    for (int i = 0; i < numberOfOptions; i++) {
749      const unsigned char *sStart = (const unsigned char*)
750        optionNameTable[English][i];
751      UTF16 wcTmp[30], *tStart = wcTmp;
752      if (ConvertUTF8toUTF16 (&sStart,  sStart + strlen ((char*) sStart) + 1,
753            &tStart, wcTmp + sizeof (wcTmp) / sizeof (wcTmp[0]),
754            lenientConversion) == conversionOK) {
755        SendMessage (klist, LB_ADDSTRING, 0, (LPARAM) wcTmp);
756      }
757    }
758  }
759 
760  if (Msg == WM_COMMAND && (wParam == IDCANCEL || wParam == IDOK)) {
761    EndDialog (hwnd, wParam == IDOK ? SendMessage (
762      GetDlgItem (hwnd, IDC_LISTO), LB_GETCURSEL, 0, 0) : -1);
763    return TRUE;
764  }
765  return FALSE;
766}
767#endif // _WIN32_WCE
768
769int objectAddRow = -1;
770#define ADD_HEIGHT 32
771#define ADD_WIDTH 64
772void HitButton (int b)
773{
774  int returnToMap = b > 0 && option <= FastestRouteNum;
775  #ifdef _WIN32_WCE
776  if (AddWayOrNode && b == 0) {
777    AddWayOrNode = 0;
778    option = numberOfOptions;
779    if (newWays[newWayCnt].cnt) objectAddRow = 0;
780    return;
781  }
782  if (QuickOptions && b == 0) {
783    option = DialogBox (hInst, MAKEINTRESOURCE (IDD_CHOOSEO), NULL,
784      (DLGPROC) DlgChooseOProc);
785    if (option == -1) option = numberOfOptions;
786   
787    #define o(en,min,max) \
788      if (option == en ## Num && min == 0 && max <= 2) b = 1;
789    OPTIONS
790    #undef o
791    if (b == 0) return;
792    returnToMap = TRUE;
793    // If it's a binary option, fall through to toggle it
794  }
795  #endif
796    if (b == 0) option = (option + 1) % (numberOfOptions + 1);
797    else if (option == StartRouteNum) {
798      flon = clon;
799      flat = clat;
800      free (route);
801      route = NULL;
802      shortest = NULL;
803    }
804    else if (option == EndRouteNum) {
805      tlon = clon;
806      tlat = clat;
807      Route (TRUE, 0, 0, Vehicle, FastestRoute);
808    }
809    #ifdef _WIN32_WCE
810    else if (option == SearchNum) {
811      SipShowIM (SIPF_ON);
812      if (ModelessDialog) ShowWindow (dlgWnd, SW_SHOW);
813      else DialogBox (hInst, MAKEINTRESOURCE(IDD_DLGSEARCH),
814               NULL, (DLGPROC)DlgSearchProc);
815    }
816    else if (option == DisplayOffNum) {
817      if (CeEnableBacklight(FALSE)) {
818        gDisplayOff = TRUE;
819      }
820    }
821    else if (option == BaudRateNum) BaudRate += b * 4800 - 7200;
822    #endif
823    #define o(en,min,max) else if (option == en ## Num) \
824      en = (en - (min) + (b == 2 ? 1 : (max) - (min) - 1)) % \
825        ((max) - (min)) + (min);
826    OPTIONS
827    #undef o
828    else {
829      if (b == 2) zoom = zoom / 4 * 3;
830      if (b == 1) zoom = zoom / 3 * 4;
831      if (b > 0) SetLocation (clon, clat);
832    }
833    if (option == OrientNorthwardsNum && OrientNorthwards) {
834      cosAzimuth = 1.0;
835      sinAzimuth = 0.0;
836    }
837    if (returnToMap) option = numberOfOptions;
838}
839
840int Click (GtkWidget * /*widget*/, GdkEventButton *event, void * /*para*/)
841{
842  int w = draw->allocation.width, h = draw->allocation.height;
843  #ifdef ROUTE_TEST
844  if (event->state) {
845    return RouteTest (NULL /*widget*/, event, NULL /*para*/);
846  }
847  #endif
848  if (ButtonSize <= 0) ButtonSize = 4;
849  int b = (draw->allocation.height - lrint (event->y)) / (ButtonSize * 20);
850  if (objectAddRow >= 0) {
851    int perRow = (w - ButtonSize * 20) / ADD_WIDTH;
852    if (event->x < w - ButtonSize * 20) {
853      #ifdef _WIN32_WCE
854      newWays[newWayCnt].klas = objectAddRow + event->x / ADD_WIDTH +
855                                event->y / ADD_HEIGHT * perRow;
856      SipShowIM (SIPF_ON);
857      if (DialogBox (hInst, MAKEINTRESOURCE (IDD_SETTAGS), NULL,
858          (DLGPROC) DlgSetTagsProc)) {} //DialogBox (hInst,
859          //MAKEINTRESOURCE (IDD_SETTAGS2), NULL, (DLGPROC) DlgSetTags2Proc);
860      newWays[newWayCnt].cnt = 0;
861      #endif
862      objectAddRow = -1;
863    }
864    else objectAddRow = int (event->y) * (restriction_no_right_turn / perRow
865                                  + 2) / draw->allocation.height * perRow;
866  }
867  else if (event->x > w - ButtonSize * 20 && b <
868      (!HideZoomButtons || option != numberOfOptions ? 3 : 
869      MenuKey != 0 ? 0 : 1)) HitButton (b);
870  else {
871    int perpixel = zoom / w;
872    int lon = clon + lrint (cosAzimuth * perpixel * (event->x - w / 2) -
873                            sinAzimuth * perpixel * (h / 2 - event->y));
874    int lat = clat + lrint (cosAzimuth * perpixel * (h / 2 - event->y) +
875                            sinAzimuth * perpixel * (event->x - w / 2));
876    if (event->button == 1) {
877      SetLocation (lon, lat);
878
879      #ifdef _WIN32_WCE
880      if (AddWayOrNode && newWays[newWayCnt].cnt < NEWWAY_MAX_COORD) {
881        newWays[newWayCnt].coord[newWays[newWayCnt].cnt][0] = clon;
882        newWays[newWayCnt].coord[newWays[newWayCnt].cnt++][1] = clat;
883      }
884      #endif
885      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (followGPSr), FALSE);
886      FollowGPSr = 0;
887    }
888    else if (event->button == 2) {
889      flon = lon;
890      flat = lat;
891      free (route);
892      route = NULL;
893      shortest = NULL;
894    }
895    else {
896      tlon = lon;
897      tlat = lat;
898      Route (TRUE, 0, 0, Vehicle, FastestRoute);
899    }
900  }
901  gtk_widget_queue_clear (draw);
902  return FALSE;
903}
904
905#if 0
906void GetDirections (GtkWidget *, gpointer)
907{
908  char *msg;
909  if (!shortest) msg = strdup (
910    "Mark the starting point with the middle button and the\n"
911    "end point with the right button. Then click Get Directions again\n");
912  else {
913    for (int i = 0; i < 2; i++) {
914      int len = 0;
915      char *last = "";
916      __int64 dlon = 0, dlat = 1, bSqr = 1; /* Point North */
917      for (routeNodeType *x = shortest; x; x = x->shortest) {
918        halfSegType *other = (halfSegType *)(data + x->hs->other);
919        int forward = x->hs->wayPtr != TO_HALFSEG;
920        wayType *w = Way (forward ? x->hs : other);
921       
922        // I think the formula below can be substantially simplified using
923        // the method used in GpsMove
924        __int64 nlon = other->lon - x->hs->lon, nlat = other->lat-x->hs->lat;
925        __int64 cSqr = Sqr (nlon) + Sqr (nlat);
926        __int64 lhs = bSqr + cSqr - Sqr (nlon - dlon) - Sqr (nlat - dlat);
927        /* Use cosine rule to determine if the angle is obtuse or greater than
928           45 degrees */
929        if (lhs < 0 || Sqr (lhs) < 2 * bSqr * cSqr) {
930          /* (-nlat,nlon) is perpendicular to (nlon,nlat). Then we use
931             Pythagoras test for obtuse angle for left and right */
932          if (!i) len += 11;
933          else len += sprintf (msg + len, "%s turn\n",
934            nlon * dlat < nlat * dlon ? "Left" : "Right");
935        }
936        dlon = nlon;
937        dlat = nlat;
938        bSqr = cSqr;
939       
940        if (strcmp (w->name + data, last)) {
941          last = w->name + data;
942          if (!i) len += strlen (last) + 1;
943          else len += sprintf (msg + len, "%s\n", last);
944        }
945      }
946      if (!i) msg = (char*) malloc (len + 1);
947    } // First calculate len, then create message.
948  }
949  GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
950  GtkWidget *view = gtk_text_view_new ();
951  GtkWidget *scrol = gtk_scrolled_window_new (NULL, NULL);
952//  gtk_scrolled_winGTK_POLICY_AUTOMATIC,
953//    GTK_POLICY_ALWAYS);
954  GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
955  gtk_text_view_set_editable (GTK_TEXT_VIEW (view), FALSE);
956  gtk_text_buffer_set_text (buffer, msg, -1);
957  free (msg);
958  gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrol), view);
959  gtk_container_add (GTK_CONTAINER (window), scrol);
960  gtk_widget_set_size_request (window, 300, 300);
961  gtk_widget_show (view);
962  gtk_widget_show (scrol);
963  gtk_widget_show (window);
964}
965
966#endif
967
968struct name2renderType { // Build a list of names, sort by name,
969  wayType *w;            // make unique by name, sort by y, then render
970  int x, y, width;       // only if their y's does not overlap
971};
972
973#ifdef _WIN32_WCE
974int Expose (HDC mygc, HDC icons, HDC mask, HPEN *pen)
975{
976  struct {
977    int width, height;
978  } clip;
979/*  clip.width = GetSystemMetrics(SM_CXSCREEN);
980  clip.height = GetSystemMetrics(SM_CYSCREEN); */
981  HFONT sysFont = (HFONT) GetStockObject (SYSTEM_FONT);
982  LOGFONT logFont;
983  GetObject (sysFont, sizeof (logFont), &logFont);
984  WCHAR wcTmp[70];
985
986  HDC iconsgc = mygc;
987
988#define gtk_combo_box_get_active(x) 1
989#define gdk_draw_drawable(win,dgc,sdc,x,y,dx,dy,w,h) \
990  BitBlt (dgc, dx, dy, w, h, mask, x, y, SRCAND); \
991  BitBlt (dgc, dx, dy, w, h, sdc, x, y, SRCPAINT)
992#define gdk_draw_line(win,gc,sx,sy,dx,dy) \
993  MoveToEx (gc, sx, sy, NULL); LineTo (gc, dx, dy)
994
995  if (objectAddRow >= 0) {
996    SelectObject (mygc, sysFont);
997    SetBkMode (mygc, TRANSPARENT);
998    SelectObject (mygc, GetStockObject (BLACK_PEN));
999    for (int y = 0, i = objectAddRow; y < draw->allocation.height;
1000              y += ADD_HEIGHT) {
1001      //gdk_draw_line (draw->window, mygc, 0, y, draw->allocation.width, y);
1002      gdk_draw_line (draw->window, mygc,
1003        draw->allocation.width - ButtonSize * 20,
1004        draw->allocation.height * i / restriction_no_right_turn,
1005        draw->allocation.width,
1006        draw->allocation.height * i / restriction_no_right_turn);
1007      RECT klip;
1008      klip.bottom = y + ADD_HEIGHT;
1009      klip.top = y;
1010      for (int x = 0; x < draw->allocation.width - ButtonSize * 20 -
1011          ADD_WIDTH && i < restriction_no_right_turn; x += ADD_WIDTH, i++) {
1012        int *icon = style[i].x + 4 * IconSet;
1013        gdk_draw_drawable (draw->window, mygc, icons, icon[0], icon[1],
1014          x - icon[2] / 2 + ADD_WIDTH / 2, y, icon[2], icon[3]);
1015        klip.left = x + 8;
1016        klip.right = x + ADD_WIDTH - 8;
1017        ExtTextOut (mygc, x + 8, y + ADD_HEIGHT - 16, ETO_CLIPPED,
1018          &klip, klasTable[i].desc, wcslen (klasTable[i].desc), NULL);
1019      }
1020    }
1021    return FALSE;
1022  } // if displaying the klas / style / rule selection screen
1023#else
1024gint Expose (void)
1025{
1026  static GdkColor styleColour[2 << STYLE_BITS][2], routeColour, validateColour;
1027  static GdkPixmap *icons = NULL;
1028  static GdkBitmap *mask = NULL;
1029  static GdkGC *mygc = NULL, *iconsgc = NULL;;
1030  static GdkGC *maskGC = NULL;
1031  // create bitmap for generation the mask image for icons
1032  // all icons must be smaller than these dimensions
1033  static GdkBitmap *maskicon = gdk_pixmap_new(NULL, 100, 100, 1);
1034  if (!mygc || !iconsgc) {
1035    mygc = gdk_gc_new (draw->window);
1036    iconsgc = gdk_gc_new (draw->window);
1037    for (int i = 0; i < 1 || style[i - 1].scaleMax; i++) {
1038      for (int j = 0; j < 2; j++) {
1039        int c = !j ? style[i].areaColour 
1040          : style[i].lineColour != -1 ? style[i].lineColour
1041          : (style[i].areaColour >> 1) & 0xefefef; // Dark border
1042        styleColour[i][j].red =    (c >> 16)        * 0x101;
1043        styleColour[i][j].green = ((c >> 8) & 0xff) * 0x101;
1044        styleColour[i][j].blue =   (c       & 0xff) * 0x101;
1045        gdk_colormap_alloc_color (gdk_window_get_colormap (draw->window),
1046          &styleColour[i][j], FALSE, TRUE);
1047      }
1048    }
1049    routeColour.green = 0xffff;
1050    routeColour.red = routeColour.blue = 0;
1051    gdk_colormap_alloc_color (gdk_window_get_colormap (draw->window),
1052      &routeColour, FALSE, TRUE);
1053    validateColour.red = 0xffff;
1054    validateColour.green = validateColour.blue = 0x9999;
1055    gdk_colormap_alloc_color (gdk_window_get_colormap (draw->window),
1056      &validateColour, FALSE, TRUE);
1057    gdk_gc_set_fill (mygc, GDK_SOLID);
1058
1059    icons = gdk_pixmap_create_from_xpm (draw->window, &mask, NULL,
1060      FindResource ("icons.xpm"));
1061    maskGC = gdk_gc_new(mask);
1062  } 
1063
1064//  gdk_gc_set_clip_rectangle (mygc, &clip);
1065//  gdk_gc_set_foreground (mygc, &styleColour[0][0]);
1066//  gdk_gc_set_line_attributes (mygc,
1067//    1, GDK_LINE_SOLID, GDK_CAP_PROJECTING, GDK_JOIN_MITER);
1068   
1069//  clip.width = draw->allocation.width - ZOOM_PAD_SIZE;
1070//  gdk_gc_set_clip_rectangle (mygc, &clip);
1071 
1072  GdkFont *f = gtk_style_get_font (draw->style);
1073  GdkRectangle clip;
1074  clip.x = 0;
1075  clip.y = 0;
1076
1077  PangoMatrix mat;
1078  PangoContext *pc = gdk_pango_context_get_for_screen (
1079    gdk_screen_get_default ());
1080  PangoLayout *pl = pango_layout_new (pc);
1081  pango_layout_set_width (pl, -1); // No wrapping 200 * PANGO_SCALE);
1082#endif // !_WIN32_WCE
1083
1084  clip.height = draw->allocation.height - STATUS_BAR;
1085  clip.width = draw->allocation.width;
1086  if (ButtonSize <= 0) ButtonSize = 4;
1087/*  #ifdef CAIRO_VERSION
1088  cairo_t *cai = gdk_cairo_create (draw->window);
1089  if (DetailLevel < 2) {
1090    cairo_font_options_t *caiFontOptions = cairo_font_options_create ();
1091    cairo_get_font_options (cai, caiFontOptions);
1092    cairo_font_options_set_antialias (caiFontOptions, CAIRO_ANTIALIAS_NONE);
1093    cairo_set_font_options (cai, caiFontOptions);
1094  }
1095  cairo_matrix_t mat;
1096  cairo_matrix_init_identity (&mat);
1097  #endif */
1098  if (option == numberOfOptions) {
1099    if (zoom < 0) zoom = 2012345678;
1100    if (zoom / clip.width <= 1) zoom += 4000;
1101    int cosa = lrint (4294967296.0 * cosAzimuth * clip.width / zoom);
1102    int sina = lrint (4294967296.0 * sinAzimuth * clip.width / zoom);
1103    int xadj = clip.width / 2 -
1104                 ((clon * (__int64) cosa + clat * (__int64) sina) >> 32);
1105    int yadj = clip.height / 2 -
1106                 ((clon * (__int64) sina - clat * (__int64) cosa) >> 32);
1107    #define X(lon,lat) (xadj + \
1108                 (((lon) * (__int64) cosa + (lat) * (__int64) sina) >> 32))
1109    #define Y(lon,lat) (yadj + \
1110                 (((lon) * (__int64) sina - (lat) * (__int64) cosa) >> 32))
1111
1112    int lonRadius = lrint (fabs (cosAzimuth) * zoom +
1113          fabs (sinAzimuth) * zoom / clip.width * clip.height) / 2 + 1000;
1114    int latRadius = lrint (fabs (cosAzimuth) * zoom / clip.width *
1115          clip.height + fabs (sinAzimuth) * zoom) / 2 + 10000;
1116//    int perpixel = zoom / clip.width;
1117    int doAreas = TRUE, blockIcon[2 * 128];
1118    memset (blockIcon, 0, sizeof (blockIcon)); // One bit per 16 x 16 area
1119  //    zoom / sqrt (draw->allocation.width * draw->allocation.height);
1120
1121    // render map
1122    for (int thisLayer = -5, nextLayer; thisLayer < 6;
1123         thisLayer = nextLayer, doAreas = !doAreas) {
1124      OsmItr itr (clon - lonRadius, clat - latRadius,
1125                  clon + lonRadius, clat + latRadius);
1126      // Widen this a bit so that we render nodes that are just a bit offscreen ?
1127      nextLayer = 6;
1128     
1129      while (Next (itr)) {
1130        ndType *nd = itr.nd[0];
1131        wayType *w = Way (nd);
1132        if (Style (w)->scaleMax <
1133                  zoom / clip.width * 175 / (DetailLevel + 6)) continue;
1134       
1135        int wLayer = nd->other[0] < 0 && nd->other[1] < 0 ? 5 : Layer (w);
1136        if (DetailLevel < 2 && Style (w)->areaColour != -1) {
1137          if (thisLayer > -5) continue;  // Draw all areas with layer -5
1138        }
1139        else if (zoom < 100000*100) {
1140        // Under low-zoom we draw everything on layer -5 (faster)
1141          if (thisLayer < wLayer && wLayer < nextLayer) nextLayer = wLayer;
1142          if (DetailLevel > 1) {
1143            if (doAreas) nextLayer = thisLayer;
1144            if (Style (w)->areaColour != -1 ? !doAreas : doAreas) continue;
1145          }
1146          if (wLayer != thisLayer) continue;
1147        }
1148        if (nd->other[0] >= 0) {
1149          nd = ndBase + itr.nd[0]->other[0];
1150          if (nd->lat == INT_MIN) nd = itr.nd[0]; // Node excluded from build
1151          else if (itr.left <= nd->lon && nd->lon < itr.right &&
1152              itr.top  <= nd->lat && nd->lat < itr.bottom) continue;
1153        } // Only process this way when the Itr gives us the first node, or
1154        // the first node that's inside the viewing area
1155
1156        #ifndef _WIN32_WCE
1157        __int64 maxLenSqr = 0;
1158        double x0 = 0.0, y0 = 0.0; /* shut up gcc */
1159        #else
1160        int best = 0, bestW, bestH, x0, y0;
1161        #endif
1162        int len = strcspn ((char *)(w + 1) + 1, "\n");
1163       
1164        // single-point node
1165        if (nd->other[0] < 0 && nd->other[1] < 0) {
1166          int x = X (nd->lon, nd->lat), y = Y (nd->lon, nd->lat);
1167          int *b = blockIcon + (x / (48 * 32) + y / 22 * 1) %
1168                      (sizeof (blockIcon) / sizeof (blockIcon[0]));
1169          if (!(*b & (1 << (x / 48 % 32)))) {
1170            *b |= 1 << (x / 48 % 32);
1171            int *icon = Style (w)->x + 4 * IconSet;
1172            if (icons && icon[2] != 0) {
1173              int dstx = x - icon[2] / 2;
1174              int dsty = y - icon[3] / 2;
1175              #ifndef _WIN32_WCE
1176              // for gdk we first need to extract the portion of the mask
1177              gdk_draw_drawable (maskicon, maskGC, mask,
1178                                 icon[0], icon[1], 0, 0,
1179                                 icon[2], icon[3]);
1180              // and set the clip region using that portion
1181              gdk_gc_set_clip_origin(iconsgc, dstx, dsty);
1182              gdk_gc_set_clip_mask(iconsgc, maskicon);
1183              #endif
1184              gdk_draw_drawable (draw->window, iconsgc, icons,
1185                icon[0], icon[1], dstx, dsty,
1186                icon[2], icon[3]);
1187            }
1188           
1189            #ifdef _WIN32_WCE
1190            SelectObject (mygc, sysFont);
1191            SetBkMode (mygc, TRANSPARENT);
1192            const unsigned char *sStart = (const unsigned char *)(w + 1) + 1;
1193            UTF16 *tStart = (UTF16 *) wcTmp;
1194            if (ConvertUTF8toUTF16 (&sStart,  sStart + len, &tStart, tStart +
1195                  sizeof (wcTmp) / sizeof (wcTmp[0]), lenientConversion)
1196                == conversionOK) {
1197              ExtTextOut (mygc, x - len * 3, y + icon[3] / 2, 0, NULL,
1198                  wcTmp, (wchar_t *) tStart - wcTmp, NULL);
1199            }
1200            #endif
1201            #ifdef PANGO_VERSION
1202            //if (Style (w)->scaleMax > zoom / 2 || zoom < 2000) {
1203              mat.xx = mat.yy = 1.0;
1204              mat.xy = mat.yx = 0;
1205              x0 = x /*- mat.xx / 3 * len*/; /* Render the name of the node */
1206              y0 = y /* + mat.xx * f->ascent */ + icon[3] / 2;
1207              maxLenSqr = Sqr ((__int64) Style (w)->scaleMax / 2);
1208              //4000000000000LL; // Without scaleMax, use 400000000
1209            //}
1210            #endif
1211          }
1212        }
1213        #ifndef _WIN32_WCE
1214        // filled areas
1215        else if (Style (w)->areaColour != -1) {
1216          while (nd->other[0] >= 0) nd = ndBase + nd->other[0];
1217          static GdkPoint pt[1000];
1218          unsigned pts;
1219          for (pts = 0; pts < sizeof (pt) / sizeof (pt[0]) && nd->other[1] >= 0;
1220               nd = ndBase + nd->other[1]) {
1221            if (nd->lat != INT_MIN) {
1222              pt[pts].x = X (nd->lon, nd->lat);
1223              pt[pts++].y = Y (nd->lon, nd->lat);
1224            }
1225          }
1226          gdk_gc_set_foreground (mygc, &styleColour[Style (w) - style][0]);
1227          gdk_draw_polygon (draw->window, mygc, TRUE, pt, pts);
1228          gdk_gc_set_foreground (mygc, &styleColour[Style (w) - style][1]);
1229          gdk_gc_set_line_attributes (mygc, Style (w)->lineWidth,
1230            Style (w)->dashed ? GDK_LINE_ON_OFF_DASH
1231            : GDK_LINE_SOLID, GDK_CAP_PROJECTING, GDK_JOIN_MITER);
1232          gdk_draw_polygon (draw->window, mygc, FALSE, pt, pts);
1233        }
1234
1235        #endif
1236        // ways (including areas on WinMob)
1237        else if (nd->other[1] >= 0 || Style(w)->areaColour != -1) {
1238          // perform validation (on non-areas)
1239          bool valid;
1240          if (ValidateMode && Style(w)->areaColour == -1) {
1241            valid = (len > 0); // most ways should have labels
1242            // valid = valid && ... (add more validation here)
1243
1244            // // LOG
1245            // logprintf("valid = (len > 0) = %d > 0 = %d (%s)\n",
1246            //      len,valid,(char *)(w + 1) + 1);
1247
1248          } else {
1249            valid = true; 
1250          }
1251          // two stages -> validate (if needed) then normal rendering
1252          ndType *orig = nd;
1253          for (int stage = ( valid ? 1 : 0);stage<2;stage++) {
1254            nd = orig;
1255            if (stage==0) {
1256            #ifndef _WIN32_WCE
1257              gdk_gc_set_foreground (mygc, &validateColour);
1258              gdk_gc_set_line_attributes (mygc, 10,
1259                       GDK_LINE_SOLID, GDK_CAP_PROJECTING, GDK_JOIN_MITER);
1260            #else
1261              SelectObject (mygc, pen[VALIDATE_PEN]);
1262            #endif
1263            }
1264            else if (stage == 1) {
1265              #ifndef _WIN32_WCE
1266              gdk_gc_set_foreground (mygc, &styleColour[Style (w) - style][1]);
1267              gdk_gc_set_line_attributes (mygc, Style (w)->lineWidth,
1268                    Style (w)->dashed ? GDK_LINE_ON_OFF_DASH
1269                    : GDK_LINE_SOLID, GDK_CAP_PROJECTING, GDK_JOIN_MITER);
1270              #else
1271              SelectObject (mygc, pen[StyleNr (w) + RESERVED_PENS]);
1272              #endif
1273            }
1274            int oldx = X (nd->lon, nd->lat);
1275            int oldy = Y (nd->lon, nd->lat);
1276            do {
1277              ndType *next = ndBase + nd->other[1];
1278              if (next->lat == INT_MIN) break; // Node excluded from build
1279              int x = X (next->lon, next->lat);
1280              int y = Y (next->lon, next->lat);
1281              if ((x <= clip.width || oldx <= clip.width) &&
1282                  (x >= 0 || oldx >= 0) && (y >= 0 || oldy >= 0) &&
1283                  (y <= clip.height || oldy <= clip.height)) {
1284                gdk_draw_line (draw->window, mygc, oldx, oldy, x, y);
1285                #ifdef _WIN32_WCE
1286                int newb = oldx > x ? oldx - x : x - oldx;
1287                if (newb < oldy - y) newb = oldy - y;
1288                if (newb < y - oldy) newb = y - oldy;
1289                if (best < newb) {
1290                  best = newb;
1291                  bestW = (x > oldx ? -1 : 1) * (x - oldx);
1292                  bestH = (x > oldx ? -1 : 1) * (oldy - y);
1293                  x0 = next->lon / 2 + nd->lon / 2;
1294                  y0 = next->lat / 2 + nd->lat / 2;
1295                }
1296                #endif
1297                #ifdef PANGO_VERSION
1298                __int64 lenSqr = (nd->lon - next->lon) * (__int64)(nd->lon - next->lon) +
1299                    (nd->lat - next->lat) * (__int64)(nd->lat - next->lat);
1300                if (lenSqr > maxLenSqr) {
1301                  maxLenSqr = lenSqr;
1302                  double lonDiff = (nd->lon - next->lon) * cosAzimuth +
1303                                   (nd->lat - next->lat) * sinAzimuth;
1304                  mat.yy = mat.xx = 1.0 * fabs (lonDiff) / sqrt (lenSqr);
1305                  mat.xy = (lonDiff > 0 ? 1.0 : -1.0) *
1306                           ((nd->lat - next->lat) * cosAzimuth -
1307                            (nd->lon - next->lon) * sinAzimuth) / sqrt (lenSqr);
1308                  mat.yx = -mat.xy;
1309                  x0 = X (nd->lon / 2 + next->lon / 2,
1310                          nd->lat / 2 + next->lat / 2);// +
1311  //                  mat.yx * f->descent / 1.0 - mat.xx / 1.0 * 3 * len;
1312                  y0 = Y (nd->lon / 2 + next->lon / 2,
1313                          nd->lat / 2 + next->lat / 2);// +
1314  //                  mat.xx * f->descent / 1.0 - mat.yx / 1.0 * 3 * len;
1315                 }
1316                 #endif
1317              }
1318              nd = next;
1319              oldx = x;
1320              oldy = y;
1321            } while (itr.left <= nd->lon && nd->lon < itr.right &&
1322                     itr.top  <= nd->lat && nd->lat < itr.bottom &&
1323                     nd->other[1] >= 0);
1324          }
1325        } /* If it has one or more segments */
1326         
1327        #ifdef _WIN32_WCE
1328        if (best > len * 4) {
1329          double hoek = atan2 (bestH, bestW);
1330          logFont.lfEscapement = logFont.lfOrientation =
1331            1800 + int ((1800 / M_PI) * hoek);
1332         
1333          HFONT customFont = CreateFontIndirect (&logFont);
1334          HGDIOBJ oldf = SelectObject (mygc, customFont);
1335          SetBkMode (mygc, TRANSPARENT);
1336          const unsigned char *sStart = (const unsigned char *)(w + 1) + 1;
1337          UTF16 *tStart = (UTF16 *) wcTmp;
1338          if (ConvertUTF8toUTF16 (&sStart,  sStart + len, &tStart, tStart +
1339                sizeof (wcTmp) / sizeof (wcTmp[0]), lenientConversion)
1340              == conversionOK) {
1341            ExtTextOut (mygc, X (x0, y0) + int (len * 3 * cos (hoek)),
1342                  Y (x0, y0) - int (len * 3 * sin (hoek)), 0, NULL,
1343                  wcTmp, (wchar_t *) tStart - wcTmp, NULL);
1344          }
1345          SelectObject (mygc, oldf);
1346          DeleteObject (customFont);
1347        }
1348        #endif
1349        #ifdef PANGO_VERSION
1350        if (maxLenSqr * DetailLevel > (zoom / clip.width) *
1351              (__int64) (zoom / clip.width) * len * len * 100 && len > 0) {
1352          double move = 0.6;
1353          for (char *txt = (char *)(w + 1) + 1; *txt != '\0';) {
1354            //cairo_set_font_matrix (cai, &mat);
1355            char *line = (char *) malloc (strcspn (txt, "\n") + 1);
1356            memcpy (line, txt, strcspn (txt, "\n"));
1357            line[strcspn (txt, "\n")] = '\0';
1358            //cairo_move_to (cai, x0, y0);
1359            //cairo_show_text (cai, line);
1360            pango_context_set_matrix (pc, &mat);
1361            pango_layout_set_text (pl, line, -1);
1362            PangoRectangle rect;
1363            pango_layout_get_pixel_extents (pl, &rect, NULL);
1364            y0 += mat.xx * (f->ascent + f->descent) * move;
1365            x0 += mat.xy * (f->ascent + f->descent) * move;
1366            move = 1.2;
1367            gdk_draw_layout (GDK_DRAWABLE (draw->window),
1368              draw->style->fg_gc[0],
1369              x0 - (rect.width * mat.xx + rect.height * fabs (mat.xy)) / 2,
1370              y0 - (rect.height * mat.yy + rect.width * fabs (mat.xy)) / 2, pl);
1371            free (line);
1372            if (zoom / clip.width > 20) break;
1373            while (*txt != '\0' && *txt++ != '\n') {}
1374          }
1375        }
1376        #endif
1377      } /* for each OsmItr */
1378    } // For each layer
1379  //  gdk_gc_set_foreground (draw->style->fg_gc[0], &highwayColour[rail]);
1380  //  gdk_gc_set_line_attributes (draw->style->fg_gc[0],
1381  //    1, GDK_LINE_SOLID, GDK_CAP_PROJECTING, GDK_JOIN_MITER);
1382
1383    // render route
1384    routeNodeType *x;
1385    if (shortest && (x = shortest->shortest)) {
1386      double len;
1387      int nodeCnt = 1;
1388      __int64 sumLat = x->nd->lat;
1389      #ifndef _WIN32_WCE
1390      gdk_gc_set_foreground (mygc, &routeColour);
1391      gdk_gc_set_line_attributes (mygc, 6,
1392        GDK_LINE_SOLID, GDK_CAP_PROJECTING, GDK_JOIN_MITER);
1393      #else
1394      SelectObject (mygc, pen[ROUTE_PEN]);
1395      #endif
1396      if (routeHeapSize > 1) {
1397        gdk_draw_line (draw->window, mygc, X (flon, flat), Y (flon, flat),
1398          X (x->nd->lon, x->nd->lat), Y (x->nd->lon, x->nd->lat));
1399      }
1400      len = sqrt (Sqr ((double) (x->nd->lat - flat)) +
1401        Sqr ((double) (x->nd->lon - flon)));
1402      for (; x->shortest; x = x->shortest) {
1403        gdk_draw_line (draw->window, mygc, X (x->nd->lon, x->nd->lat),
1404          Y (x->nd->lon, x->nd->lat),
1405          X (x->shortest->nd->lon, x->shortest->nd->lat),
1406          Y (x->shortest->nd->lon, x->shortest->nd->lat));
1407        len += sqrt (Sqr ((double) (x->nd->lat - x->shortest->nd->lat)) +
1408          Sqr ((double) (x->nd->lon - x->shortest->nd->lon)));
1409        sumLat += x->nd->lat;
1410        nodeCnt++;
1411      }
1412      gdk_draw_line (draw->window, mygc, X (x->nd->lon, x->nd->lat),
1413        Y (x->nd->lon, x->nd->lat), X (tlon, tlat), Y (tlon, tlat));
1414      len += sqrt (Sqr ((double) (x->nd->lat - tlat)) +
1415        Sqr ((double) (x->nd->lon - tlon)));
1416      wchar_t distStr[13];
1417      wsprintf (distStr, TEXT ("%.3lf km"), len * (20000 / 2147483648.0) *
1418        cos (LatInverse (sumLat / nodeCnt) * (M_PI / 180)));
1419      #ifndef _WIN32_WCE
1420      gdk_draw_string (draw->window, f, draw->style->fg_gc[0],
1421        clip.width - 7 * strlen (distStr), 10, distStr);
1422      #else
1423      SelectObject (mygc, sysFont);
1424      SetBkMode (mygc, TRANSPARENT);
1425      ExtTextOut (mygc, clip.width - 7 * wcslen (distStr), 0, 0, NULL,
1426        distStr, wcslen (distStr), NULL);
1427      #endif
1428    }
1429    #ifndef _WIN32_WCE
1430    for (int i = 1; ShowActiveRouteNodes && i < routeHeapSize; i++) {
1431      gdk_draw_line (draw->window, mygc,
1432        X (routeHeap[i]->nd->lon, routeHeap[i]->nd->lat) - 2,
1433        Y (routeHeap[i]->nd->lon, routeHeap[i]->nd->lat),
1434        X (routeHeap[i]->nd->lon, routeHeap[i]->nd->lat) + 2,
1435        Y (routeHeap[i]->nd->lon, routeHeap[i]->nd->lat));
1436    }
1437    #else
1438    for (int j = 0; j <= newWayCnt; j++) {
1439      int x = X (newWays[j].coord[0][0], newWays[j].coord[0][1]);
1440      int y = Y (newWays[j].coord[0][0], newWays[j].coord[0][1]);
1441      if (newWays[j].cnt == 1) {
1442        int *icon = style[j < newWayCnt ? newWays[j].klas : place_village].x
1443          + 4 * IconSet;
1444        gdk_draw_drawable (draw->window, mygc, icons, icon[0], icon[1],
1445          x - icon[2] / 2, y - icon[3] / 2, icon[2], icon[3]);
1446      }
1447      else {
1448        SelectObject (mygc, pen[j < newWayCnt ? newWays[j].klas + RESERVED_PENS: 0]);
1449        MoveToEx (mygc, x, y, NULL);
1450        for (int i = 1; i < newWays[j].cnt; i++) {
1451          LineTo (mygc, X (newWays[j].coord[i][0], newWays[j].coord[i][1]),
1452                        Y (newWays[j].coord[i][0], newWays[j].coord[i][1]));
1453        }
1454      }
1455    }
1456    if (ShowTrace) {
1457      for (gpsNewStruct *ptr = gpsTrack; ptr < gpsNew; ptr++) {
1458        SetPixel (mygc, X (ptr->lon, ptr->lat), Y (ptr->lon, ptr->lat), 0);
1459      }
1460    }
1461    #endif
1462  } // Not in the menu
1463  else {
1464    char optStr[30];
1465    if (option == VehicleNum) {
1466      #define M(v) Vehicle == v ## R ? #v :
1467      sprintf (optStr, "%s : %s", optionNameTable[English][option],
1468        RESTRICTIONS NULL);
1469      #undef M
1470    }
1471    else sprintf (optStr, "%s : %d", optionNameTable[English][option],
1472    #define o(en,min,max) option == en ## Num ? en :
1473    OPTIONS
1474    #undef o
1475      0);
1476    #ifndef _WIN32_WCE
1477    mat.xx = mat.yy = 1.0;
1478    mat.xy = mat.yx = 0.0;
1479    pango_context_set_matrix (pc, &mat);
1480    pango_layout_set_text (pl, optStr, -1);
1481    gdk_draw_layout (GDK_DRAWABLE (draw->window),
1482              draw->style->fg_gc[0], 50, draw->allocation.height / 2, pl);
1483    #else
1484    SelectObject (mygc, sysFont);
1485    SetBkMode (mygc, TRANSPARENT);
1486    const unsigned char *sStart = (const unsigned char*) optStr;
1487    UTF16 *tStart = (UTF16 *) wcTmp;
1488    if (ConvertUTF8toUTF16 (&sStart,  sStart + strlen (optStr), &tStart,
1489             tStart + sizeof (wcTmp) / sizeof (wcTmp[0]), lenientConversion)
1490        == conversionOK) {
1491      ExtTextOut (mygc, 50, draw->allocation.height / 2, 0, NULL,
1492         wcTmp, (wchar_t*) tStart - wcTmp, NULL);
1493    }
1494    #endif
1495  }
1496  #ifndef _WIN32_WCE
1497  gdk_draw_rectangle (draw->window, draw->style->bg_gc[0], TRUE,
1498    clip.width - ButtonSize * 20, clip.height - ButtonSize * 60,
1499    clip.width, clip.height);
1500  for (int i = 0; i < 3; i++) {
1501    gdk_draw_string (draw->window, f, draw->style->fg_gc[0],
1502      clip.width - ButtonSize * 10 - 5, clip.height + (f->ascent - f->descent)
1503      / 2 - ButtonSize * (20 * i + 10), i == 0 ? "O" : i == 1 ? "-" : "+");
1504  }
1505  #else
1506  int i = !HideZoomButtons || option != numberOfOptions ? 3 :
1507                                                MenuKey != 0 ? 0 : 1;
1508  RECT r;
1509  r.left = clip.width - ButtonSize * 20;
1510  r.top = clip.height - ButtonSize * 20 * i;
1511  r.right = clip.width;
1512  r.bottom = clip.height;
1513  FillRect (mygc, &r, (HBRUSH) GetStockObject(LTGRAY_BRUSH));
1514  SelectObject (mygc, sysFont);
1515  SetBkMode (mygc, TRANSPARENT);
1516  while (--i >= 0) {
1517    ExtTextOut (mygc, clip.width - ButtonSize * 10 - 5, clip.height - 5 -
1518        ButtonSize * (20 * i + 10), 0, NULL, i == 0 ? TEXT ("O") :
1519        i == 1 ? TEXT ("-") : TEXT ("+"), 1, NULL);
1520  }
1521
1522  wchar_t coord[21];
1523  if (ShowCoordinates == 1) {
1524    wsprintf (coord, TEXT ("%9.5lf %10.5lf"), LatInverse (clat),
1525      LonInverse (clon));
1526    ExtTextOut (mygc, 0, 0, 0, NULL, coord, 20, NULL);
1527  }
1528  else if (ShowCoordinates == 2) {
1529    MEMORYSTATUS memStat;
1530    GlobalMemoryStatus (&memStat);
1531    wsprintf (coord, TEXT ("%9d"), memStat.dwAvailPhys );
1532    ExtTextOut (mygc, 0, 0, 0, NULL, coord, 9, NULL);
1533  }
1534  #endif
1535  #ifdef CAIRO_VERSION
1536//  cairo_destroy (cai);
1537  #endif
1538/*
1539  clip.height = draw->allocation.height;
1540  gdk_gc_set_clip_rectangle (draw->style->fg_gc[0], &clip);
1541  gdk_draw_string (draw->window, f, draw->style->fg_gc[0],
1542    clip.width/2, clip.height - f->descent, "gosmore");
1543  */
1544  return FALSE;
1545}
1546
1547#ifndef _WIN32_WCE
1548GtkWidget *searchW;
1549GtkWidget *list;
1550
1551int IncrementalSearch (void)
1552{
1553  GosmSearch (clon, clat, (char *) gtk_entry_get_text (GTK_ENTRY (searchW)));
1554  gtk_clist_freeze (GTK_CLIST (list));
1555  gtk_clist_clear (GTK_CLIST (list));
1556  for (int i = 0; i < searchCnt; i++) {
1557    if (gosmSstr[i]) gtk_clist_append (GTK_CLIST (list), &gosmSstr[i]);
1558  }
1559  gtk_clist_thaw (GTK_CLIST (list));
1560  return FALSE;
1561}
1562#endif
1563
1564void SelectName (GtkWidget * /*w*/, int row, int /*column*/,
1565  GdkEventButton * /*ev*/, void * /*data*/)
1566{
1567  SetLocation (gosmSway[row]->clon, gosmSway[row]->clat);
1568  zoom = gosmSway[row]->dlat + gosmSway[row]->dlon + (1 << 15);
1569  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (followGPSr), FALSE);
1570  FollowGPSr = FALSE;
1571  gtk_widget_queue_clear (draw);
1572}
1573
1574void InitializeOptions (void)
1575{
1576  char *tag = gosmData +
1577    *(int *)(ndBase + hashTable[bucketsMin1 + (bucketsMin1 >> 7) + 2]);
1578  while (*--tag) {}
1579  SetLocation (((wayType*)tag)[-1].clon, ((wayType*)tag)[-1].clat);
1580  zoom = ((wayType*)tag)[-1].dlat + ((wayType*)tag)[-1].dlon + (1 << 15);
1581}
1582
1583#endif // HEADLESS
1584
1585#ifndef _WIN32_WCE
1586int UserInterface (int argc, char *argv[], 
1587                   const char* pakfile, const char* stylefile) {
1588
1589  #if defined (__linux__)
1590  FILE *gmap = fopen64 (pakfile, "r");
1591  if (!gmap || fseek (gmap, 0, SEEK_END) != 0 ||
1592      !GosmInit (mmap (NULL, ftell (gmap), PROT_READ, MAP_SHARED,
1593                fileno (gmap), 0), ftell (gmap))) {
1594  #else
1595  GMappedFile *gmap = g_mapped_file_new (pakfile, FALSE, NULL);
1596  if (!gmap || !GosmInit (g_mapped_file_get_contents (gmap),
1597      g_mapped_file_get_length (gmap))) {
1598  #endif
1599    fprintf (stderr, "Cannot read %s\n"
1600             "You can (re)build it from\n"
1601             "the planet file e.g. " 
1602             "bzip2 -d planet-...osm.bz2 | %s rebuild\n",
1603             pakfile, argv[0]);
1604    #ifndef HEADLESS
1605    gtk_init (&argc, &argv);
1606    gtk_dialog_run (GTK_DIALOG (gtk_message_dialog_new (NULL,
1607      GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
1608      "Cannot read %s\nYou can (re)build it from\n"
1609      "the planet file e.g. bzip2 -d planet-...osm.bz2 | %s rebuild\n",
1610      pakfile, argv[0])));
1611    #endif
1612    return 8;
1613  }
1614
1615  if (stylefile) {
1616#ifndef _WIN32
1617    GosmLoadAltStyle(stylefile,FindResource("icons.csv"));
1618#else
1619    fprintf(stderr, "Overiding style information is not currently supported"
1620            " in Windows.");
1621#endif
1622  }
1623
1624
1625  if (getenv ("QUERY_STRING")) {
1626    double x0, y0, x1, y1;
1627    char vehicle[20];
1628    sscanf (getenv ("QUERY_STRING"),
1629      "flat=%lf&flon=%lf&tlat=%lf&tlon=%lf&fast=%d&v=%19[a-z]",
1630      &y0, &x0, &y1, &x1, &FastestRoute, vehicle);
1631    flat = Latitude (y0);
1632    flon = Longitude (x0);
1633    tlat = Latitude (y1);
1634    tlon = Longitude (x1);
1635    #define M(v) if (strcmp (vehicle, #v) == 0) Vehicle = v ## R;
1636    RESTRICTIONS
1637    #undef M
1638    Route (TRUE, 0, 0, Vehicle, FastestRoute);
1639    printf ("Content-Type: text/plain\n\r\n\r");
1640    if (!shortest) printf ("No route found\n\r");
1641    else if (routeHeapSize <= 1) printf ("Jump\n\r");
1642    for (; shortest; shortest = shortest->shortest) {
1643      wayType *w = Way (shortest->nd);
1644      char *name = (char*)(w + 1) + 1;
1645      printf ("%lf,%lf,%c,%s,%.*s\n\r", LatInverse (shortest->nd->lat),
1646        LonInverse (shortest->nd->lon), JunctionType (shortest->nd),
1647        klasTable[StyleNr (w)].desc, (int) strcspn (name, "\n"), name);
1648    }
1649    return 0;
1650  }
1651
1652  printf ("%s is in the public domain and comes without warranty\n",argv[0]);
1653  #ifndef HEADLESS
1654 
1655  gtk_init (&argc, &argv);
1656  draw = gtk_drawing_area_new ();
1657  gtk_signal_connect (GTK_OBJECT (draw), "expose_event",
1658    (GtkSignalFunc) Expose, NULL);
1659  gtk_signal_connect (GTK_OBJECT (draw), "button_press_event",
1660    (GtkSignalFunc) Click, NULL);
1661  gtk_widget_set_events (draw, GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK |
1662    GDK_POINTER_MOTION_MASK);
1663  gtk_signal_connect (GTK_OBJECT (draw), "scroll_event",
1664                       (GtkSignalFunc) Scroll, NULL);
1665 
1666  GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1667  GtkWidget *hbox = gtk_hbox_new (FALSE, 5), *vbox = gtk_vbox_new (FALSE, 0);
1668  gtk_container_add (GTK_CONTAINER (window), hbox);
1669  gtk_box_pack_start (GTK_BOX (hbox), draw, TRUE, TRUE, 0);
1670  gtk_box_pack_end (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
1671
1672  searchW = gtk_entry_new ();
1673  gtk_box_pack_start (GTK_BOX (vbox), searchW, FALSE, FALSE, 5);
1674  gtk_entry_set_text (GTK_ENTRY (searchW), "Search");
1675  gtk_signal_connect (GTK_OBJECT (searchW), "changed",
1676    GTK_SIGNAL_FUNC (IncrementalSearch), NULL);
1677 
1678  list = gtk_clist_new (1);
1679  gtk_clist_set_selection_mode (GTK_CLIST (list), GTK_SELECTION_SINGLE);
1680  gtk_box_pack_start (GTK_BOX (vbox), list, TRUE, TRUE, 5);
1681  gtk_signal_connect (GTK_OBJECT (list), "select_row",
1682    GTK_SIGNAL_FUNC (SelectName), NULL);
1683   
1684  carBtn = GTK_COMBO_BOX (gtk_combo_box_new_text ());
1685  #define M(x) if (motorcarR <= x ## R && x ## R < onewayR) \
1686                             gtk_combo_box_append_text (carBtn, #x);
1687  RESTRICTIONS
1688  #undef M
1689  gtk_combo_box_set_active (carBtn, 0);
1690  gtk_box_pack_start (GTK_BOX (vbox), GTK_WIDGET (carBtn), FALSE, FALSE, 5);
1691  gtk_signal_connect (GTK_OBJECT (carBtn), "changed",
1692    GTK_SIGNAL_FUNC (ChangeOption), NULL);
1693
1694  fastestBtn = GTK_COMBO_BOX (gtk_combo_box_new_text ());
1695  gtk_combo_box_append_text (fastestBtn, "fastest");
1696  gtk_combo_box_append_text (fastestBtn, "shortest");
1697  gtk_combo_box_set_active (fastestBtn, 0);
1698  gtk_box_pack_start (GTK_BOX (vbox),
1699    GTK_WIDGET (fastestBtn), FALSE, FALSE, 5);
1700  gtk_signal_connect (GTK_OBJECT (fastestBtn), "changed",
1701    GTK_SIGNAL_FUNC (ChangeOption), NULL);
1702
1703  detailBtn = GTK_COMBO_BOX (gtk_combo_box_new_text ());
1704  gtk_combo_box_append_text (detailBtn, "Highest");
1705  gtk_combo_box_append_text (detailBtn, "High");
1706  gtk_combo_box_append_text (detailBtn, "Normal");
1707  gtk_combo_box_append_text (detailBtn, "Low");
1708  gtk_combo_box_append_text (detailBtn, "Lowest");
1709  gtk_combo_box_set_active (detailBtn, 2);
1710  gtk_box_pack_start (GTK_BOX (vbox), GTK_WIDGET (detailBtn), FALSE, FALSE,5);
1711  gtk_signal_connect (GTK_OBJECT (detailBtn), "changed",
1712    GTK_SIGNAL_FUNC (ChangeOption), NULL);
1713
1714  iconSet = GTK_COMBO_BOX (gtk_combo_box_new_text ());
1715  gtk_combo_box_append_text (iconSet, "Classic.Big");
1716  gtk_combo_box_append_text (iconSet, "Classic.Small                       ");
1717  gtk_combo_box_append_text (iconSet, "Square.Big");
1718  gtk_combo_box_append_text (iconSet, "Square.Small");
1719  gtk_combo_box_set_active (iconSet, 1);
1720  gtk_box_pack_start (GTK_BOX (vbox), GTK_WIDGET (iconSet), FALSE, FALSE, 5);
1721  gtk_signal_connect (GTK_OBJECT (iconSet), "changed",
1722    GTK_SIGNAL_FUNC (ChangeOption), NULL);
1723
1724//  GtkWidget *getDirs = gtk_button_new_with_label ("Get Directions");
1725/*  gtk_box_pack_start (GTK_BOX (vbox), getDirs, FALSE, FALSE, 5);
1726  gtk_signal_connect (GTK_OBJECT (getDirs), "clicked",
1727    GTK_SIGNAL_FUNC (GetDirections), NULL);
1728*/
1729  location = gtk_entry_new ();
1730  gtk_box_pack_start (GTK_BOX (vbox), location, FALSE, FALSE, 5);
1731  gtk_signal_connect (GTK_OBJECT (location), "changed",
1732    GTK_SIGNAL_FUNC (ChangeLocation), NULL);
1733 
1734  orientNorthwards = gtk_check_button_new_with_label ("OrientNorthwards");
1735  gtk_box_pack_start (GTK_BOX (vbox), orientNorthwards, FALSE, FALSE, 5);
1736  gtk_signal_connect (GTK_OBJECT (orientNorthwards), "clicked",
1737    GTK_SIGNAL_FUNC (ChangeOption), NULL);
1738  gtk_widget_show (orientNorthwards);
1739
1740  validateMode = gtk_check_button_new_with_label ("Validation Mode");
1741  gtk_box_pack_start (GTK_BOX (vbox), validateMode, FALSE, FALSE, 5);
1742  gtk_signal_connect (GTK_OBJECT (validateMode), "clicked",
1743    GTK_SIGNAL_FUNC (ChangeOption), NULL);
1744  gtk_widget_show (validateMode);
1745
1746  followGPSr = gtk_check_button_new_with_label ("Follow GPSr");
1747 
1748  #if !defined (_WIN32) && !defined (ROUTE_TEST)
1749  struct sockaddr_in sa;
1750  int gpsSock = socket (PF_INET, SOCK_STREAM, 0);
1751  sa.sin_family = AF_INET;
1752  sa.sin_port = htons (2947);
1753  sa.sin_addr.s_addr = htonl (0x7f000001); // (204<<24)|(17<<16)|(205<<8)|18);
1754  if (gpsSock != -1 &&
1755      connect (gpsSock, (struct sockaddr *)&sa, sizeof (sa)) == 0) {
1756    send (gpsSock, "R\n", 2, 0);
1757    gpsSockTag = gdk_input_add (/*gpsData->gps_fd*/ gpsSock, GDK_INPUT_READ,
1758      (GdkInputFunction) ReceiveNmea /*gps_poll*/, NULL);
1759
1760    gtk_box_pack_start (GTK_BOX (vbox), followGPSr, FALSE, FALSE, 5);
1761    gtk_signal_connect (GTK_OBJECT (followGPSr), "clicked",
1762      GTK_SIGNAL_FUNC (ChangeOption), NULL);
1763    gtk_widget_show (followGPSr);
1764  }
1765  #endif
1766
1767  gtk_signal_connect (GTK_OBJECT (window), "delete_event",
1768    GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
1769 
1770  gtk_widget_set_usize (window, 750, 550);
1771  gtk_widget_show (searchW);
1772  gtk_widget_show (list);
1773  gtk_widget_show (location);
1774  gtk_widget_show (draw);
1775  gtk_widget_show (GTK_WIDGET (carBtn));
1776  gtk_widget_show (GTK_WIDGET (fastestBtn));
1777  gtk_widget_show (GTK_WIDGET (detailBtn));
1778  gtk_widget_show (GTK_WIDGET (iconSet));
1779/*  gtk_widget_show (getDirs); */
1780  gtk_widget_show (hbox);
1781  gtk_widget_show (vbox);
1782  gtk_widget_show (window);
1783  option = numberOfOptions;
1784  ChangeOption ();
1785  IncrementalSearch ();
1786  InitializeOptions ();
1787  gtk_main ();
1788  FlushGpx ();
1789 
1790  #endif // HEADLESS
1791  return 0;
1792}
1793
1794int main (int argc, char *argv[])
1795{
1796  int nextarg = 1;
1797  bool rebuild = false;
1798  const char* master = "";
1799  int bbox[4] = { INT_MIN, INT_MIN, 0x7fffffff, 0x7fffffff };
1800  if (argc > 1 && stricmp(argv[1], "rebuild") == 0) {
1801    if (argc < 6 && argc > 4) {
1802      fprintf (stderr, 
1803               "Usage : %s [rebuild [bbox for 2 pass]] [pakfile [stylefile]]\n"
1804               "See http://wiki.openstreetmap.org/index.php/gosmore\n", 
1805               argv[0]);
1806      return 1;
1807    }
1808    rebuild=true;
1809    nextarg++;
1810    if (argc >= 6) {
1811      master = FindResource("master.pak");
1812      bbox[0] = Latitude (atof (argv[2]));
1813      bbox[1] = Longitude (atof (argv[3]));
1814      bbox[2] = Latitude (atof (argv[4]));
1815      bbox[3] = Longitude (atof (argv[5]));
1816      nextarg += 4;
1817    }
1818  }
1819 
1820  // check if a pakfile was specified on the command line
1821  const char* pakfile;
1822  const char* stylefile = NULL;
1823  if (argc > nextarg) {
1824    pakfile=argv[nextarg];
1825    nextarg++;
1826    if (argc > nextarg)  {
1827      stylefile=argv[nextarg];
1828    } else if (rebuild) { 
1829      stylefile=FindResource("elemstyles.xml");
1830    }
1831  } else {
1832    pakfile=FindResource("gosmore.pak");
1833    if (rebuild) {
1834      stylefile=FindResource("elemstyles.xml");
1835    }
1836  }
1837 
1838  if (rebuild) {
1839#ifndef _WIN32
1840    printf("Building %s using style %s...\n",pakfile,stylefile);
1841
1842    RebuildPak(pakfile, stylefile, FindResource("icons.csv"), master, bbox);
1843#else
1844    fprintf(stderr,"Pakfile rebuild is not currently supported in Windows.\n");
1845#endif
1846  }
1847
1848  return UserInterface (argc, argv, pakfile, stylefile);
1849
1850  // close the logfile if it has been opened
1851  if (logFP(false)) fclose(logFP(false));
1852}
1853#else // _WIN32_WCE
1854//----------------------------- _WIN32_WCE ------------------
1855HANDLE port = INVALID_HANDLE_VALUE;
1856
1857HBITMAP bmp;
1858HDC iconsDc, maskDc, bufDc;
1859HPEN pen[2 << STYLE_BITS];
1860int pakSize;
1861UTF16 appendTmp[50];
1862
1863BOOL CALLBACK DlgSearchProc (
1864        HWND hwnd, 
1865        UINT Msg, 
1866        WPARAM wParam, 
1867        LPARAM lParam)
1868{
1869  SHINITDLGINFO di;
1870
1871    switch (Msg) {
1872    case WM_INITDIALOG:
1873      if (SHInitDialogPtr) {
1874        di.dwMask = SHIDIM_FLAGS;
1875        di.dwFlags = SHIDIF_SIZEDLG;
1876        di.hDlg = hwnd;
1877        (*SHInitDialogPtr)(&di);
1878      }
1879      return TRUE;
1880    case WM_SIZE: 
1881      {
1882        int width = LOWORD(lParam) - GetSystemMetrics(SM_CXVSCROLL);
1883        int height = LOWORD(lParam);
1884        HWND hEdit = GetDlgItem(hwnd, IDC_EDIT1);
1885        HWND hList = GetDlgItem(hwnd, IDC_LIST1);
1886        RECT rWnd, rEdit, rList;
1887       
1888        // Find the locations of the edit and list, and map to client
1889        // coordinates
1890        GetWindowRect(hEdit, &rEdit); 
1891        MapWindowPoints(HWND_DESKTOP, hwnd, (LPPOINT) &rEdit, 2);
1892        GetWindowRect(hList, &rList);
1893        MapWindowPoints(HWND_DESKTOP, hwnd, (LPPOINT) &rList, 2);
1894
1895        // Change the width of the edit and list to match the client area
1896        MoveWindow(hEdit, rEdit.left, rEdit.top, width-2*rEdit.left, 
1897                   rEdit.bottom-rEdit.top, TRUE);
1898        MoveWindow(hList, rList.left, rList.top, width-2*rList.left, 
1899                   rList.bottom-rList.top, TRUE);
1900      }
1901      return TRUE;
1902    case WM_COMMAND:
1903      if (LOWORD (wParam) == IDC_EDIT1) {
1904        HWND edit = GetDlgItem (hwnd, IDC_EDIT1);
1905        char editStr[50];
1906
1907        memset (appendTmp, 0, sizeof (appendTmp));
1908        int wstrlen = Edit_GetLine (edit, 0, appendTmp, sizeof (appendTmp));
1909        unsigned char *tStart = (unsigned char*) editStr;
1910        const UTF16 *sStart = (const UTF16 *) appendTmp;
1911        if (ConvertUTF16toUTF8 (&sStart,  sStart + wstrlen,
1912              &tStart, tStart + sizeof (gosmSstr), lenientConversion)
1913            == conversionOK) {
1914          *tStart = '\0';
1915          hwndList = GetDlgItem (hwnd, IDC_LIST1);
1916          SendMessage (hwndList, LB_RESETCONTENT, 0, 0);
1917          GosmSearch (clon, clat, editStr);
1918          for (int i = 0; i < searchCnt && gosmSstr[i]; i++) {
1919            const unsigned char *sStart = (const unsigned char*) gosmSstr[i];
1920            UTF16 *tStart = appendTmp;
1921            if (ConvertUTF8toUTF16 (&sStart,  sStart + strlen (gosmSstr[i]) + 1,
1922              &tStart, appendTmp + sizeof (appendTmp) / sizeof (appendTmp[0]),
1923              lenientConversion) == conversionOK) {
1924              SendMessage (hwndList, LB_ADDSTRING, 0, (LPARAM) appendTmp);
1925            }
1926          }
1927        }
1928        return TRUE;
1929      }
1930      else if (wParam == IDC_SEARCHGO
1931         || LOWORD (wParam) == IDC_LIST1 && HIWORD (wParam) == LBN_DBLCLK) {
1932        HWND hwndList = GetDlgItem (hwnd, IDC_LIST1);
1933        int idx = SendMessage (hwndList, LB_GETCURSEL, 0, 0);
1934        SipShowIM (SIPF_OFF);
1935        if (ModelessDialog) ShowWindow (hwnd, SW_HIDE);
1936        else EndDialog (hwnd, 0);
1937        if (idx != LB_ERR) SelectName (NULL, idx, 0, NULL, NULL);
1938        InvalidateRect (mWnd, NULL, FALSE);
1939        return TRUE;
1940      }
1941      else if (wParam == IDC_BUTTON1) {
1942        SipShowIM (SIPF_OFF);
1943        if (ModelessDialog) ShowWindow (hwnd, SW_HIDE);
1944        else EndDialog (hwnd, 0);
1945      }
1946    }
1947    return FALSE;
1948}
1949
1950LRESULT CALLBACK MainWndProc(HWND hWnd,UINT message,
1951                                  WPARAM wParam,LPARAM lParam)
1952{
1953  PAINTSTRUCT ps;
1954  RECT rect;
1955  static wchar_t msg[200] = TEXT("No coms");
1956  static int done = FALSE;
1957
1958  switch(message) {
1959    #if 0
1960    case WM_HOTKEY:
1961      if (VK_TBACK == HIWORD(lParam) && (0 != (MOD_KEYUP & LOWORD(lParam)))) {
1962        PostQuitMessage (0);
1963      }
1964      break;
1965    #endif
1966
1967    case WM_ACTIVATE:
1968      // Ensure that unwanted wince elements are hidden
1969      if (SHFullScreenPtr) {
1970        if (FullScreen) {
1971          (*SHFullScreenPtr)(mWnd, SHFS_HIDETASKBAR |
1972                             SHFS_HIDESTARTICON | SHFS_HIDESIPBUTTON);
1973        } else {
1974          (*SHFullScreenPtr)(mWnd, SHFS_HIDESIPBUTTON);
1975        }
1976      }
1977      break;
1978    case WM_DESTROY:
1979      PostQuitMessage(0);
1980      break;
1981    case WM_PAINT:
1982      do { // Keep compiler happy.
1983        BeginPaint (hWnd, &ps);
1984      //GetClientRect (hWnd, &r);
1985      //SetBkColor(ps.hdc,RGB(63,63,63));
1986      //SetTextColor(ps.hdc,(i==state)?RGB(0,128,0):RGB(0,0,0));
1987      //r.left = 50;
1988      // r.top = 50;
1989        if (!done) {
1990          bmp = LoadBitmap (hInst, MAKEINTRESOURCE (IDB_BITMAP1));
1991          iconsDc = CreateCompatibleDC (ps.hdc);
1992          SelectObject(iconsDc, bmp);
1993
1994          // get mask for iconsDc
1995          bmp = LoadBitmap (hInst, MAKEINTRESOURCE (IDB_BITMAP2));
1996          maskDc = CreateCompatibleDC (ps.hdc);
1997          SelectObject(maskDc, bmp);
1998
1999          bufDc = CreateCompatibleDC (ps.hdc); //bufDc //GetDC (hWnd));
2000          bmp = CreateCompatibleBitmap (ps.hdc, GetSystemMetrics(SM_CXSCREEN),
2001            GetSystemMetrics(SM_CYSCREEN));
2002          SelectObject (bufDc, bmp);
2003          pen[ROUTE_PEN] = CreatePen (PS_SOLID, 6, 0x00ff00);
2004          pen[VALIDATE_PEN] = CreatePen (PS_SOLID, 10, 0x9999ff);
2005          for (int i = 0; i < 1 || style[i - 1].scaleMax; i++) {
2006            // replace line colour with area colour
2007            // if no line colour specified
2008            int c = style[i].lineColour != -1 ? style[i].lineColour
2009              : style[i].areaColour; 
2010            pen[i + RESERVED_PENS] = 
2011              CreatePen (style[i].dashed ? PS_DASH : PS_SOLID,
2012                         style[i].lineWidth, (c >> 16) |
2013                         (c & 0xff00) |
2014                         ((c & 0xff) << 16));
2015          }
2016          done = TRUE;
2017        }
2018        rect.top = rect.left = 0;
2019        rect.right = GetSystemMetrics(SM_CXSCREEN);
2020        rect.bottom = GetSystemMetrics(SM_CYSCREEN);
2021        Expose (bufDc, iconsDc, maskDc, pen);
2022        BitBlt (ps.hdc, 0, 0, rect.right,  rect.bottom, bufDc, 0, 0, SRCCOPY);
2023        FillRect (bufDc, &rect, (HBRUSH) GetStockObject(WHITE_BRUSH));
2024//      HPEN pen = CreatePen (a[c2].lineDashed ? PS_DASH : PS_SOLID,
2025        EndPaint (hWnd, &ps);
2026      } while (0);
2027      break;
2028    case WM_CHAR:
2029
2030      break;
2031    case WM_KEYDOWN:
2032      // The TGPS 375 can generate 12 keys :
2033      // VK_RETURN, VK_UP, VK_DOWN, VK_LEFT, VK_RIGHT,
2034      // 193=0xC1=Zoom in, 194=0xC2=Zoom out, 198=0xC6=menu 197=0xC5=settings
2035      // 195=0xC3=V+, 196=0xC4=V- which is VK_APP1 to VK_APP6
2036      // and WM_CHAR:VK_BACK
2037      InvalidateRect (hWnd, NULL, FALSE);
2038      if (wParam == '0' || wParam == MenuKey) HitButton (0);
2039      if (wParam == '8') HitButton (1);
2040      if (wParam == '9') HitButton (2);
2041      if (Exit) PostMessage (hWnd, WM_CLOSE, 0, 0);
2042      if (ZoomInKeyNum <= option && option < HideZoomButtonsNum) {
2043        #define o(en,min,max) if (option == en ## Num) en = wParam;
2044        OPTIONS
2045        #undef o
2046        break;
2047      }
2048      if (wParam == (DWORD) ZoomInKey) zoom = zoom * 3 / 4;
2049      if (wParam == (DWORD) ZoomOutKey) zoom = zoom * 4 / 3;
2050
2051      do { // Keep compiler happy
2052        int oldCsum = clat + clon;
2053        if (VK_DOWN == wParam) clat -= zoom / 2;
2054        else if (VK_UP == wParam) clat += zoom / 2;
2055        else if (VK_LEFT == wParam) clon -= zoom / 2;
2056        else if (VK_RIGHT == wParam) clon += zoom / 2;
2057        if (oldCsum != clat + clon) FollowGPSr = FALSE;
2058      } while (0);
2059      break;
2060    case WM_USER + 1:
2061      /*
2062      wsprintf (msg, TEXT ("%c%c %c%c %9.5lf %10.5lf %lf %lf"),
2063        gpsNew.fix.date[0], gpsNew.fix.date[1],
2064        gpsNew.fix.tm[4], gpsNew.fix.tm[5],
2065        gpsNew.fix.latitude, gpsNew.fix.longitude, gpsNew.fix.ele,
2066        gpsNew.fix.hdop); */
2067      DoFollowThing ((gpsNewStruct*)lParam);
2068      if (FollowGPSr) InvalidateRect (hWnd, NULL, FALSE);
2069      break;
2070    case WM_LBUTTONDOWN:
2071      //MoveTo (LOWORD(lParam), HIWORD(lParam));
2072      //PostQuitMessage (0);
2073      //if (HIWORD(lParam) < 30) {
2074        // state=LOWORD(lParam)/STATEWID;
2075      //}
2076      if (gDisplayOff) {
2077        CeEnableBacklight(TRUE);
2078        gDisplayOff = FALSE;
2079        break;
2080      }
2081      GdkEventButton ev;
2082      ev.x = LOWORD (lParam);
2083      ev.y = HIWORD (lParam);
2084      ev.button = 1;
2085      Click (NULL, &ev, NULL);
2086      if (Exit) PostMessage (hWnd, WM_CLOSE, 0, 0);
2087      InvalidateRect (hWnd, NULL, FALSE);
2088      break;
2089    case WM_LBUTTONUP:
2090      break;
2091    case WM_MOUSEMOVE:
2092      //LineTo (LOWORD(lParam), HIWORD(lParam));
2093      break;
2094    /*case WM_COMMAND:
2095     //switch(wParam) {
2096     //}
2097     break; */
2098    default:
2099      return(DefWindowProc(hWnd,message,wParam,lParam));
2100  }
2101  return FALSE;
2102}
2103
2104BOOL InitApplication (void)
2105{
2106  WNDCLASS wc;
2107
2108  wc.style=0;
2109  wc.lpfnWndProc=(WNDPROC)MainWndProc;
2110  wc.cbClsExtra=0;
2111  wc.cbWndExtra=0;
2112  wc.hInstance= hInst;
2113  wc.hIcon=LoadIcon(hInst, MAKEINTRESOURCE(ID_MAINICON)); 
2114  wc.hCursor=LoadCursor(NULL,IDC_ARROW);
2115  wc.hbrBackground=(HBRUSH) GetStockObject(WHITE_BRUSH);
2116  wc.lpszMenuName = NULL;
2117  wc.lpszClassName = TEXT ("GosmoreWClass");
2118
2119  return(RegisterClass(&wc));
2120}
2121
2122HWND InitInstance(int nCmdShow)
2123{
2124  HWND prev;
2125  // check if gosmore is already running
2126  prev = FindWindow(TEXT ("GosmoreWClass"), NULL);
2127  if (prev != NULL) {
2128    ShowWindow(prev, SW_RESTORE);
2129    SetForegroundWindow(prev);
2130    return FALSE;
2131  } else {
2132   
2133    mWnd = CreateWindow (TEXT ("GosmoreWClass"), TEXT ("gosmore"), WS_DLGFRAME,
2134                         CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 
2135                         CW_USEDEFAULT,NULL,NULL, hInst,NULL);
2136   
2137    if(!mWnd) return(FALSE);
2138   
2139    ShowWindow (mWnd,nCmdShow);
2140    //UpdateWindow (mWnd);
2141   
2142   
2143    return mWnd;
2144  }
2145}
2146
2147volatile int guiDone = FALSE;
2148
2149DWORD WINAPI NmeaReader (LPVOID lParam)
2150{
2151  // loop back here if existing connection fails
2152  while (!guiDone) {
2153    // $GPGLL,2546.6752,S,02817.5780,E,210130.812,V,S*5B
2154    DWORD nBytes, got = 0;
2155    COMMTIMEOUTS commTiming;
2156    char rx[300];
2157   
2158    wchar_t portname[6];
2159    wsprintf (portname, TEXT ("COM%d:"), CommPort);
2160
2161    logprintf ("Attempting first connect to CommPort.\n");
2162
2163    // Attempt to reconnect to NMEA device every 1 second until connected
2164    while (!guiDone &&
2165           (port=CreateFile (portname, GENERIC_READ | GENERIC_WRITE, 0,
2166                 NULL, OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE) {
2167      Sleep(1000);
2168      //logprintf("Retrying connect to CommPort\n");
2169    }
2170
2171    if (port != INVALID_HANDLE_VALUE) {
2172
2173      logprintf("Connected to CommPort\n");
2174         
2175#if 1
2176      GetCommTimeouts (port, &commTiming);
2177      commTiming.ReadIntervalTimeout = 20;
2178      commTiming.ReadTotalTimeoutMultiplier = 0;
2179      commTiming.ReadTotalTimeoutConstant = 200; /* Bailout when nothing on the port */
2180     
2181      commTiming.WriteTotalTimeoutMultiplier=5; /* No writing */
2182      commTiming.WriteTotalTimeoutConstant=5;
2183      SetCommTimeouts (port, &commTiming);
2184#endif
2185      if (BaudRate) {
2186        DCB portState;
2187        if(!GetCommState(port, &portState)) {
2188          MessageBox (NULL, TEXT ("GetCommState Error"), TEXT (""),
2189                      MB_APPLMODAL|MB_OK);
2190          return(1);
2191        }
2192        portState.BaudRate = BaudRate;
2193        //portState.Parity=0;
2194        //portState.StopBits=ONESTOPBIT;
2195        //portState.ByteSize=8;
2196        //portState.fBinary=1;
2197        //portState.fParity=0;
2198        //portState.fOutxCtsFlow=0;
2199        //portState.fOutxDsrFlow=0;
2200        //portState.fDtrControl=DTR_CONTROL_ENABLE;
2201        //portState.fDsrSensitivity=0;
2202        //portState.fTXContinueOnXoff=1;
2203        //portState.fOutX=0;
2204        //portState.fInX=0;
2205        //portState.fErrorChar=0;
2206        //portState.fNull=0;
2207        //portState.fRtsControl=RTS_CONTROL_ENABLE;
2208        //portState.fAbortOnError=1;
2209       
2210        if(!SetCommState(port, &portState)) {
2211          MessageBox (NULL, TEXT ("SetCommState Error"), TEXT (""),
2212                      MB_APPLMODAL|MB_OK);
2213          return(1);
2214        }
2215      }
2216     
2217      /* Idea for Windows Mobile 5
2218         #include <gpsapi.h>
2219         if (WM5) {
2220         GPS_POSITION pos;
2221         HANDLE hand = GPSOpenDevice (NULL, NULL, NULL, 0);
2222         while (!guiDone && hand != NULL) {
2223         if (GPSGetPosition (hand, &pos, 500, 0) == ERROR_SUCCESS &&
2224         (pos.dwValidFields & GPS_VALID_LATITUDE)) {
2225         Sleep (800);
2226         pos.dblLatitude, pos.dblLongitude;
2227         }
2228         else Sleep (100);
2229         }
2230         if (hand) GPSCloseDevice (hand);
2231         } */
2232     
2233#if 0
2234      PurgeComm (port, PURGE_RXCLEAR); /* Baud rate wouldn't change without this ! */
2235      DWORD nBytes2 = 0;
2236      COMSTAT cStat;
2237      ClearCommError (port, &nBytes, &cStat);
2238      rx2 = (char*) malloc (600);
2239      ReadFile(port, rx, sizeof(rx), &nBytes, NULL);
2240      if(!GetCommState(port, &portState)) {
2241        MessageBox (NULL, TEXT ("GetCommState Error"), TEXT (""),
2242                    MB_APPLMODAL|MB_OK);
2243        return(1);
2244      }
2245      ReadFile(port, rx2, 600, &nBytes2, NULL);
2246#endif
2247     
2248      //char logName[80];
2249      //sprintf (logName, "%slog.nmea", docPrefix);
2250      //FILE *log = fopen (logName, "wb");
2251
2252      // keep reading nmea until guiDone or serial port fails
2253      bool status;
2254      while (!guiDone &&
2255             (status = ReadFile(port, rx + got, 
2256                                     sizeof(rx) - got, &nBytes, NULL))) {
2257        //      logprintf ("status = %d, nBytes = %d\n", status, nBytes);
2258        if (nBytes > 0) {
2259          got += nBytes;
2260          //if (log) fwrite (rx, nBytes, 1, log);
2261         
2262          //wndStr[0]='\0';
2263          //FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
2264          //MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),wndStr,STRLEN,NULL);
2265         
2266          if (ProcessNmea (rx, (unsigned*)&got)) {
2267            PostMessage (mWnd, WM_USER + 1, 0, (int) /* intptr_t */ gpsNew);
2268          }
2269        } // if nBytes > 0
2270      } // while ReadFile(...)
2271      if (!guiDone) {
2272        logprintf("Connection to CommPort failed.\n");
2273      }
2274    } // if port != INVALID_FILE_HANDLE
2275  } // while !guiDone
2276  guiDone = FALSE;
2277  //if (log) fclose (log);
2278  CloseHandle (port);
2279  return 0;
2280}
2281
2282
2283void XmlOut (FILE *newWayFile, char *k, char *v)
2284{
2285  if (*v != '\0') {
2286    fprintf (newWayFile, "  <tag k='%s' v='", k);
2287    for (; *v != '\0'; v++) {
2288      if (*v == '\'') fprintf (newWayFile, "&apos;");
2289      else if (*v == '&') fprintf (newWayFile, "&amp;");
2290      else fputc (*v, newWayFile);
2291    }
2292    fprintf (newWayFile, "' />\n");
2293  }
2294}
2295
2296int WINAPI WinMain(
2297    HINSTANCE  hInstance,         // handle of current instance
2298    HINSTANCE  hPrevInstance,     // handle of previous instance
2299    LPWSTR  lpszCmdLine,                  // pointer to command line
2300    int  nCmdShow)                // show state of window
2301{
2302  if(hPrevInstance) return(FALSE);
2303  hInst = hInstance;
2304  gDisplayOff = FALSE;
2305  wchar_t argv0[80];
2306  GetModuleFileName (NULL, argv0, sizeof (argv0) / sizeof (argv0[0]));
2307  UTF16 *sStart = (UTF16*) argv0, *rchr = (UTF16*) wcsrchr (argv0, '\\');
2308  wcscpy (rchr ? (wchar_t *) rchr + 1 : argv0, TEXT (""));
2309  unsigned char *tStart = (unsigned char *) docPrefix;
2310  ConvertUTF16toUTF8 ((const UTF16 **) &sStart, sStart + wcslen (argv0),
2311    &tStart, tStart + sizeof (docPrefix), lenientConversion);
2312  *tStart = '\0';
2313
2314  char optFileName[sizeof(docPrefix) + 13];
2315  sprintf (optFileName, "%s\\gosmore.opt", docPrefix);
2316  FILE *optFile = fopen (optFileName, "r"); 
2317  if (!optFile) {
2318    strcpy (docPrefix, "\\My Documents\\");
2319    optFile = fopen ("\\My Documents\\gosmore.opt", "rb");
2320  }
2321
2322  //store log file name
2323  sprintf (logFileName, "%s\\gosmore.log.txt", docPrefix);
2324
2325  wcscat (argv0, TEXT ("gosmore.pak")); // _arm.exe to ore.pak
2326  HANDLE gmap = CreateFileForMapping (argv0, GENERIC_READ, FILE_SHARE_READ,
2327    NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
2328  if (gmap == INVALID_HANDLE_VALUE) {
2329    MessageBox (NULL, TEXT ("No pak file"), TEXT (""), MB_APPLMODAL|MB_OK);
2330    return 1;
2331  }
2332  pakSize = GetFileSize(gmap, NULL);
2333  gmap = CreateFileMapping(gmap, NULL, PAGE_READONLY, 0, 0, 0);
2334  if (!GosmInit (MapViewOfFile (gmap, FILE_MAP_READ, 0, 0, 0), pakSize)) {
2335    MessageBox (NULL, TEXT ("mmap problem. Pak file too big ?"),
2336      TEXT (""), MB_APPLMODAL|MB_OK);
2337    return 1;
2338  }
2339
2340  #if 0
2341  FILE *gmap = _wfopen (/*"./gosmore.pak"*/ argv0, TEXT ("rb"));
2342
2343  if (!gmap) {
2344    MessageBox (NULL, TEXT ("No pak file"), TEXT (""), MB_APPLMODAL|MB_OK);
2345    return 1;
2346  }
2347  fseek (gmap, 0, SEEK_END);
2348  pakSize = ftell (gmap);
2349  fseek (gmap, 0, SEEK_SET);
2350  data = (char *) malloc (pakSize);
2351  if (!data) {
2352    MessageBox (NULL, TEXT ("Out of memory"), TEXT (""), MB_APPLMODAL|MB_OK);
2353    return 1; // This may mean memory is available, but fragmented.
2354  } // Splitting the 5 parts may help.
2355  fread (data, pakSize, 1, gmap);
2356  #endif
2357/*  style = (struct styleStruct *)(data + 4);
2358  hashTable = (int *) (data + pakSize);
2359  ndBase = (ndType *)(data + hashTable[-1]);
2360  bucketsMin1 = hashTable[-2];
2361  hashTable -= bucketsMin1 + (bucketsMin1 >> 7) + 5;
2362*/
2363  if(!InitApplication ()) return(FALSE);
2364  if (!InitInstance (nCmdShow)) return(FALSE);
2365
2366  newWays[0].cnt = 0;
2367  IconSet = 1;
2368  DetailLevel = 3;
2369  ButtonSize = 4;
2370  int newWayFileNr = 0;
2371  if (optFile) {
2372    #define o(en,min,max) fread (&en, sizeof (en), 1, optFile);
2373    OPTIONS
2374    #undef o
2375    fread (&newWayFileNr, sizeof (newWayFileNr), 1, optFile);
2376    option = numberOfOptions;
2377  }
2378  Exit = 0;
2379  InitializeOptions ();
2380
2381  InitCeGlue();
2382  if (SHFullScreenPtr) {
2383    if (FullScreen) {
2384      (*SHFullScreenPtr)(mWnd, SHFS_HIDETASKBAR |
2385                         SHFS_HIDESTARTICON | SHFS_HIDESIPBUTTON);
2386      MoveWindow (mWnd, 0, 0, GetSystemMetrics(SM_CXSCREEN),
2387                  GetSystemMetrics(SM_CYSCREEN), FALSE);
2388    } else {
2389      (*SHFullScreenPtr)(mWnd, SHFS_HIDESIPBUTTON);
2390    } 
2391  }
2392
2393  GtkWidget dumdraw;
2394  RECT r;
2395  GetClientRect(mWnd,&r);
2396  dumdraw.allocation.width = r.right;
2397  dumdraw.allocation.height = r.bottom;
2398  draw = &dumdraw;
2399
2400  dlgWnd = CreateDialog (hInst, MAKEINTRESOURCE(IDD_DLGSEARCH),
2401    NULL, (DLGPROC)DlgSearchProc); // Just in case user goes modeless
2402
2403  DWORD threadId;
2404  if (CommPort == 0) {}
2405  else /* if((port=CreateFile (portname, GENERIC_READ | GENERIC_WRITE, 0,
2406          NULL, OPEN_EXISTING, 0, 0)) != INVALID_HANDLE_VALUE) */ {
2407    CreateThread (NULL, 0, NmeaReader, NULL, 0, &threadId);
2408    }
2409  /*   else MessageBox (NULL, TEXT ("No Port"), TEXT (""), MB_APPLMODAL|MB_OK); */
2410
2411  MSG    msg;
2412  while (GetMessage (&msg, NULL, 0, 0)) {
2413    TranslateMessage (&msg);
2414    DispatchMessage (&msg);
2415  }
2416  guiDone = TRUE;
2417
2418  while (port != INVALID_HANDLE_VALUE && guiDone) Sleep (1000);
2419
2420  optFile = fopen (optFileName, "r+b");
2421  if (!optFile) optFile = fopen ("\\My Documents\\gosmore.opt", "wb");
2422  if (optFile) {
2423    #define o(en,min,max) fwrite (&en, sizeof (en),1, optFile);
2424    OPTIONS
2425    #undef o
2426    fwrite (&newWayFileNr, sizeof (newWayFileNr), 1, optFile);
2427    fclose (optFile);
2428  }
2429  gpsNewStruct *first = FlushGpx ();
2430  if (newWayCnt > 0) {
2431    char VehicleName[80];
2432    #define M(v) Vehicle == v ## R ? #v :
2433    sprintf(VehicleName, "%s", RESTRICTIONS NULL);
2434    #undef M
2435
2436    char bname[80], fname[80];
2437    getBaseFilename(bname, first);
2438    sprintf (fname, "%s.osm", bname);
2439
2440    FILE *newWayFile = fopen (fname, "w");
2441    if (newWayFile) {
2442      fprintf (newWayFile, "<?xml version='1.0' encoding='UTF-8'?>\n"
2443                           "<osm version='0.6' generator='gosmore'>\n");
2444      for (int j, id = -1, i = 0; i < newWayCnt; i++) {
2445        for (j = 0; j < newWays[i].cnt; j++) {
2446          fprintf (newWayFile, "<node id='%d' visible='true' lat='%.5lf' "
2447            "lon='%.5lf' %s>\n", id - j, LatInverse (newWays[i].coord[j][1]),
2448            LonInverse (newWays[i].coord[j][0]),
2449            newWays[i].cnt <= 1 ? "" : "/");
2450        }
2451        if (newWays[i].cnt > 1) {
2452          fprintf (newWayFile, "<way id='%d' action='modify' "
2453            "visible='true'>\n", id - newWays[i].cnt);
2454          for (j = 0; j < newWays[i].cnt; j++) {
2455            fprintf (newWayFile, "  <nd ref='%d'/>\n", id--);
2456          }
2457        }
2458        id--;
2459        XmlOut (newWayFile, "todo", "FIXME - Added by gosmore");
2460        if (newWays[i].oneway) XmlOut (newWayFile, "oneway", "yes");
2461        if (newWays[i].bridge) XmlOut (newWayFile, "bridge", "yes");
2462        if (newWays[i].klas >= 0) fprintf (newWayFile, "%s",
2463          klasTable[newWays[i].klas].tags);
2464        XmlOut (newWayFile, "name", newWays[i].name);
2465        XmlOut (newWayFile, "note", newWays[i].note);
2466        fprintf (newWayFile, "</%s>\n", newWays[i].cnt <= 1 ? "node" : "way");
2467      }
2468      fprintf (newWayFile, "</osm>\n");
2469      fclose (newWayFile);
2470    }
2471  }
2472
2473  if (logFP(false)) fclose(logFP(false));
2474
2475  return 0;
2476}
2477#endif
Note: See TracBrowser for help on using the repository browser.