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

Last change on this file since 24203 was 24203, checked in by nic, 5 years ago

CGI output: Fix?? bug with 'to' coordinates

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