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

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

Fixed buffer overflow problem causing crash on WinMob? startup

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