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

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

Searching: Add a few French prefixes and fix the "the" prefix.

Breaks backward compatibility.

Rebuild: Ignore more irrelevent tags, incl postal_code.
Elemstyles: Less crowded and more meaningful rendering in European cities.
Rendering: Fix text bug. (Thanks Willem-Jan). Fix area bug.

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