[cairo-commit] roadster/src Makefile.am, 1.17, 1.18 animator.c, 1.5, 1.6 db.c, 1.25, 1.26 db.h, 1.10, 1.11 gotowindow.c, 1.9, 1.10 gpsclient.c, 1.10, 1.11 gpsclient.h, 1.3, 1.4 gui.c, 1.10, 1.11 gui.h, 1.2, 1.3 import_tiger.c, 1.19, 1.20 importwindow.c, 1.8, 1.9 layers.c, 1.16, NONE layers.h, 1.8, NONE locationeditwindow.c, NONE, 1.1 locationeditwindow.h, NONE, 1.1 locationset.c, 1.11, 1.12 main.c, 1.22, 1.23 main.h, 1.2, 1.3 mainwindow.c, 1.39, 1.40 map.c, 1.43, 1.44 map.h, 1.20, 1.21 map_draw_cairo.c, 1.21, 1.22 map_draw_gdk.c, 1.17, 1.18 map_style.c, NONE, 1.1 map_style.h, NONE, 1.1 prefs.c, 1.3, NONE prefs.h, 1.1, NONE road.c, 1.5, 1.6 scenemanager.c, 1.13, 1.14 search.c, 1.5, 1.6 search_road.c, 1.21, 1.22 searchwindow.c, 1.19, 1.20 util.c, 1.9, 1.10 util.h, 1.9, 1.10

Ian McIntosh commit at pdx.freedesktop.org
Wed Sep 14 13:06:56 PDT 2005


Committed by: ian

Update of /cvs/cairo/roadster/src
In directory gabe:/tmp/cvs-serv9354/src

Modified Files:
	Makefile.am animator.c db.c db.h gotowindow.c gpsclient.c 
	gpsclient.h gui.c gui.h import_tiger.c importwindow.c 
	locationset.c main.c main.h mainwindow.c map.c map.h 
	map_draw_cairo.c map_draw_gdk.c road.c scenemanager.c search.c 
	search_road.c searchwindow.c util.c util.h 
Added Files:
	locationeditwindow.c locationeditwindow.h map_style.c 
	map_style.h 
Removed Files:
	layers.c layers.h prefs.c prefs.h 
Log Message:
	* src/*.c: Removed many unneeded #includes.  Removed NUM_ELEMS in favor of existing glib G_N_ELEMENTS.
	* src/*window.c: Use GLADE_LINK_WIDGET to connect widget to Glade XML element.
	* src/gpsclient.c: Work to make GPSD support optional at compile time.
	* src/mainwindow.c: Fixed bug: mouse cursor returning to default pointer while scrolling. 
	* src/searchwindow.c: Change map zoomlevel when going to search results.  Some code refactoring.
	* src/gotowindow.c: Removed dead code.
	* src/map_style.c: Added to replace layers.c.  Add support for arbitrary dash patterns.
	* src/map_draw_gdk.c: Support new arbitrary dash patterns.
	* src/ma_draw_cairo.c: More work to restore Cairo drawing.
	* src/prefs.c: 
	* src/prefs.h:
	* src/layers.c:
	* src/layers.h: Removed.


Index: Makefile.am
===================================================================
RCS file: /cvs/cairo/roadster/src/Makefile.am,v
retrieving revision 1.17
retrieving revision 1.18
diff -u -d -r1.17 -r1.18
--- Makefile.am	28 Aug 2005 22:23:14 -0000	1.17
+++ Makefile.am	14 Sep 2005 20:06:53 -0000	1.18
@@ -24,7 +24,9 @@
 	mainwindow.c\
 	gotowindow.c\
 	map.c\
-	layers.c\
+	map_style.c\
+	map_draw_cairo.c\
+	map_draw_gdk.c\
 	import.c\
 	import_tiger.c\
 	importwindow.c\
@@ -33,6 +35,7 @@
 	gpsclient.c\
 	location.c\
 	locationset.c\
+	locationeditwindow.c\
 	searchwindow.c\
 	search_road.c\
 	search_location.c\
@@ -42,10 +45,7 @@
 	pointstring.c\
 	track.c\
 	glyph.c\
-	map_draw_cairo.c\
-	map_draw_gdk.c\
 	road.c\
-	prefs.c\
 	animator.c\
 	gfreelist.c\
 	history.c\

Index: animator.c
===================================================================
RCS file: /cvs/cairo/roadster/src/animator.c,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -d -r1.5 -r1.6
--- animator.c	28 Aug 2005 22:23:14 -0000	1.5
+++ animator.c	14 Sep 2005 20:06:53 -0000	1.6
@@ -25,11 +25,9 @@
 #  include <config.h>
 #endif
 
-#include <gtk/gtk.h>
+#include <glib.h>
 
-#include "main.h"
 #include "animator.h"
-#include "util.h"
 
 void animator_init()
 {
@@ -115,7 +113,7 @@
 	}
 
 	// Make 100% sure it's capped to 0.0 -> 1.0
-	fReturn = min(fReturn, 1.0);
-	fReturn = max(fReturn, 0.0);
+	fReturn = MIN(fReturn, 1.0);
+	fReturn = MAX(fReturn, 0.0);
 	return fReturn;
 }

Index: db.c
===================================================================
RCS file: /cvs/cairo/roadster/src/db.c,v
retrieving revision 1.25
retrieving revision 1.26
diff -u -d -r1.25 -r1.26
--- db.c	6 Sep 2005 02:44:19 -0000	1.25
+++ db.c	14 Sep 2005 20:06:53 -0000	1.26
@@ -38,7 +38,6 @@
 #include "db.h"
 #include "mainwindow.h"
 #include "util.h"
-#include "layers.h"
 #include "location.h"
 #include "locationset.h"
 
@@ -104,7 +103,7 @@
 
 	// Initialize the embedded server
 	// NOTE: if not linked with libmysqld, this call will do nothing (but will succeed)
- 	if(mysql_server_init(NUM_ELEMS(apszServerOptions), apszServerOptions, NULL) != 0) {
+ 	if(mysql_server_init(G_N_ELEMENTS(apszServerOptions), apszServerOptions, NULL) != 0) {
 		return;
 	}
 	g_free(pszDataDir);
@@ -577,12 +576,7 @@
 
 	    // lots of indexes:
 		" PRIMARY KEY (ID),"	// XXX: we'll probably want to keep a unique ID, but we don't use this for anything yet.
-
 		" INDEX(RoadNameID),"	// to get roads when we've matched a RoadName
-//	" INDEX(AddressLeftStart, AddressLeftEnd)," 	// drop these?  they reduce a few seeks on address searches IF the
-//	" INDEX(AddressRightStart, AddressRightEnd),"	// user puts in a street #. they take up 8*roadsegments bytes of 
-							// disk and eat up precious index cache memory
-
 		" SPATIAL KEY (Coordinates));", NULL);
 
 	// RoadName
@@ -612,7 +606,7 @@
 		" Code CHAR(3) NOT NULL,"			// eg. "MA"
 		" CountryID INT2 NOT NULL,"			// NOTE: 2 bytes
 		" PRIMARY KEY (ID),"
-		" INDEX (Name(5)));"				// 4 is enough for decent uniqueness.
+		" INDEX (Name(5)));"				// 5 is enough for decent uniqueness.
 	    ,NULL);
 
 	// Location
@@ -659,120 +653,3 @@
 		" Name VARCHAR(60) NOT NULL,"
 		" PRIMARY KEY (ID));", NULL);
 }
-
-#ifdef ROADSTER_DEAD_CODE
-/*
-static void db_disconnect(void)
-{
-	g_return_if_fail(g_pDB != NULL);
-
-	// close database and free all strings
-	mysql_close(g_pDB->m_pMySQLConnection);
-	g_free(g_pDB->m_pzHost);
-	g_free(g_pDB->m_pzUserName);
-	g_free(g_pDB->m_pzPassword);
-	g_free(g_pDB->m_pzDatabase);
-
-	// free structure itself
-	g_free(g_pDB);
-	g_pDB = NULL;
-}
-
-//
-// WordHash functionality
-//
-#define DB_WORDHASH_TABLENAME 	"WordHash"
-
-gint32 db_wordhash_insert(db_connection_t* pConnection, const gchar* pszWord)
-{
-	gchar azQuery[MAX_SQLBUFFER_LEN];
-	int nResult;
-
-	g_snprintf(azQuery, MAX_SQLBUFFER_LEN,
-		"INSERT INTO %s SET ID=NULL, Value='%s';",
-		DB_WORDHASH_TABLENAME, pszWord);
-
-	if((nResult = mysql_query(pConnection->m_pMySQLConnection, azQuery)) != MYSQL_RESULT_SUCCESS) {
-		g_message("db_word_to_int failed to insert: %s\n", mysql_error(pConnection->m_pMySQLConnection));
-		return 0;
-	}
-	return mysql_insert_id(pConnection->m_pMySQLConnection);
-}
-
-gint32 db_wordhash_lookup(db_connection_t* pConnection, const gchar* pszWord)
-{
-	MYSQL_RES* pResultSet;
-	MYSQL_ROW aRow;
-	gint nWordNumber = 0;
-	int nResult;
-	gchar azQuery[MAX_SQLBUFFER_LEN];
-
-	if(!db_is_connected(pConnection)) return FALSE;
-
-	// generate SQL
-	g_snprintf(azQuery, MAX_SQLBUFFER_LEN, "SELECT ID FROM %s WHERE Value='%s';", DB_WORDHASH_TABLENAME, pszWord);
-
-	// get row
-	if((nResult = mysql_query(pConnection->m_pMySQLConnection, azQuery)) != MYSQL_RESULT_SUCCESS) {
-		g_message("db_word_to_int failed to select: %s\n", mysql_error(pConnection->m_pMySQLConnection));
-		return 0;
-	}
-	if((pResultSet = MYSQL_GET_RESULT(pConnection->m_pMySQLConnection)) != NULL) {
-		if((aRow = mysql_fetch_row(pResultSet)) != NULL) {
-			nWordNumber = atoi(aRow[0]);
-		}
-		mysql_free_result(pResultSet);
-	}
-
-	// Successful?  Return it.
-	if(nWordNumber != 0) return nWordNumber;
-	else return 0;
-}
-
-void db_parse_pointstring(const gchar* pszText, pointstring_t* pPointString, gboolean (*callback_get_point)(mappoint_t**))
-{
-	// parse string and add points to the string
-	const gchar* p = pszText;
-	if(p[0] == 'L') { //g_str_has_prefix(p, "LINESTRING")) {
-		// format is "LINESTRING(1.2345 5.4321, 10 10, 20 20)"
-		mappoint_t* pPoint;
-
-		p += (11); 	// move past "LINESTRING("
-
-		while(TRUE) {
-			// g_print("reading a point...\n");
-			pPoint = NULL;
-			if(!callback_get_point(&pPoint)) return;
-
-			// read in latitude
-			pPoint->m_fLatitude = g_ascii_strtod(p, (gchar**)&p);
-
-			// space between coordinates
-			g_return_if_fail(*p == ' ');
-			p++;
-
-			// read in longitude
-			pPoint->m_fLongitude = g_ascii_strtod(p, (gchar**)&p);
-			
-			// g_print("got (%f,%f)\n", pPoint->m_fLatitude, pPoint->m_fLongitude);
-			
-			// add point to the list
-			g_ptr_array_add(pPointString->m_pPointsArray, pPoint);
-
-			if(*p == ',') {
-				p++;
-
-				// after this, we're ready to read in next point, so loop...
-				// g_print("looping for another!...\n");
-			}
-			else {
-				break;
-			}
-		}
-	}
-	else {
-		g_assert_not_reached();
-	}
-}
-*/
-#endif

Index: db.h
===================================================================
RCS file: /cvs/cairo/roadster/src/db.h,v
retrieving revision 1.10
retrieving revision 1.11
diff -u -d -r1.10 -r1.11
--- db.h	29 Mar 2005 09:16:20 -0000	1.10
+++ db.h	14 Sep 2005 20:06:53 -0000	1.11
@@ -41,7 +41,7 @@
 #define DB_FEATURES_TABLENAME	("Feature")
 
 #include "map.h"
-#include "layers.h"
+//#include "layers.h"
 #include "pointstring.h"
 
 void db_create_tables(void);

Index: gotowindow.c
===================================================================
RCS file: /cvs/cairo/roadster/src/gotowindow.c,v
retrieving revision 1.9
retrieving revision 1.10
diff -u -d -r1.9 -r1.10
--- gotowindow.c	28 Mar 2005 18:49:50 -0000	1.9
+++ gotowindow.c	14 Sep 2005 20:06:53 -0000	1.10
@@ -26,6 +26,7 @@
 #include "main.h"
 #include "mainwindow.h"
 #include "gotowindow.h"
+#include "gui.h"
 
 struct {
 	GtkWindow* m_pWindow;
@@ -35,9 +36,9 @@
 
 void gotowindow_init(GladeXML* pGladeXML)
 {
-	g_GotoWindow.m_pWindow					= GTK_WINDOW(glade_xml_get_widget(pGladeXML, "gotowindow")); g_return_if_fail(g_GotoWindow.m_pWindow != NULL);
-	g_GotoWindow.m_pLongitudeEntry				= GTK_ENTRY(glade_xml_get_widget(pGladeXML, "longitudeentry")); g_return_if_fail(g_GotoWindow.m_pLongitudeEntry != NULL);
-	g_GotoWindow.m_pLatitudeEntry				= GTK_ENTRY(glade_xml_get_widget(pGladeXML, "latitudeentry")); g_return_if_fail(g_GotoWindow.m_pLatitudeEntry != NULL);
+	GLADE_LINK_WIDGET(pGladeXML, g_GotoWindow.m_pWindow, GTK_WINDOW, "gotowindow");
+	GLADE_LINK_WIDGET(pGladeXML, g_GotoWindow.m_pLongitudeEntry, GTK_ENTRY, "longitudeentry");
+	GLADE_LINK_WIDGET(pGladeXML, g_GotoWindow.m_pLatitudeEntry, GTK_ENTRY, "latitudeentry");
 
 	// don't delete window on X, just hide it
 	g_signal_connect(G_OBJECT(g_GotoWindow.m_pWindow), "delete_event", G_CALLBACK(gtk_widget_hide), NULL);
@@ -93,13 +94,6 @@
 //
 // Callbacks
 //
-void gotowindow_on_knownlocationtreeview_row_activated(GtkTreeView *treeview, GtkTreePath *arg1, GtkTreeViewColumn *arg2, gpointer user_data)
-{
-	if(gotowindow_go()) {
-		gotowindow_hide();
-	}
-}
-
 void gotowindow_on_gobutton_clicked(GtkButton *button, gpointer user_data)
 {
 	// hide window when "Go" button clicked (successfully)

Index: gpsclient.c
===================================================================
RCS file: /cvs/cairo/roadster/src/gpsclient.c,v
retrieving revision 1.10
retrieving revision 1.11
diff -u -d -r1.10 -r1.11
--- gpsclient.c	28 Aug 2005 22:23:14 -0000	1.10
+++ gpsclient.c	14 Sep 2005 20:06:53 -0000	1.11
@@ -21,14 +21,18 @@
  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  */
 
+#define HAVE_GPSD		// XXX: this should come from configure.ac
+
 #include <gtk/gtk.h>
 #include <gps.h>
 #include "main.h"
 #include "gpsclient.h"
 
 struct {
-	struct gps_data_t * m_pGPSConnection;
-	gpsdata_t* m_pPublicGPSData;
+#ifdef HAVE_GPSD 
+	struct gps_data_t * m_pGPSConnection;	// gps_data_t is from gpsd.h
+#endif
+	gpsdata_t* m_pPublicGPSData;			// our public struct (annoyingly similar name...)
 } g_GPSClient = {0};
 
 gboolean gpsclient_callback_data_waiting(GIOChannel *source, GIOCondition condition, gpointer data);
@@ -38,11 +42,16 @@
 {
 	g_GPSClient.m_pPublicGPSData = g_new0(gpsdata_t, 1);
 
+#ifndef HAVE_GPSD
+	// No libgpsd at compile time
+	g_GPSClient.m_pPublicGPSData->m_eStatus = GPS_STATUS_NO_GPS_COMPILED_IN;
+#endif
 	gpsclient_connect();
 }
 
 static void gpsclient_connect(void)
 {
+#ifdef HAVE_GPSD 
 	// don't do anything if already connected
 	if(g_GPSClient.m_pPublicGPSData->m_eStatus != GPS_STATUS_NO_GPSD) return;	// already connected
 
@@ -69,25 +78,28 @@
 	else {
 		g_GPSClient.m_pPublicGPSData->m_eStatus = GPS_STATUS_NO_GPSD;
 	}
+#endif
 }
 
-
 const gpsdata_t* gpsclient_getdata()
 {
 	gpsclient_connect();	// connect if necessary
-	
+
 	return g_GPSClient.m_pPublicGPSData;
 }
 
 // callback for g_io_add_watch on the GPSD file descriptor
-gboolean gpsclient_callback_data_waiting(GIOChannel *source, GIOCondition condition, gpointer data)
+gboolean gpsclient_callback_data_waiting(GIOChannel *_source_unused, GIOCondition eCondition, gpointer _data_unused)
 {
+#ifdef HAVE_GPSD
 //	g_print("Data from GPSD...\n");
+	g_assert(g_GPSClient.m_pGPSConnection != NULL);
+	g_assert(g_GPSClient.m_pPublicGPSData != NULL);
 
 	gpsdata_t* l = g_GPSClient.m_pPublicGPSData;	// our public data struct, for easy access
 
 	// is there data waiting on the socket?
-	if(condition == G_IO_IN) {
+	if(eCondition == G_IO_IN) {
 		// read new data
 		if(gps_poll(g_GPSClient.m_pGPSConnection) == -1) {
 			l->m_eStatus = GPS_STATUS_NO_GPSD;
@@ -145,9 +157,10 @@
 		}
 	}
 	else {
-		//g_print("condition: %d\n", condition);
+		//g_print("eCondition: %d\n", eCondition);
 	}
 	return TRUE; // TRUE = keep socket notification coming
+#endif
 }
 
 #ifdef ROADSTER_DEAD_CODE

Index: gpsclient.h
===================================================================
RCS file: /cvs/cairo/roadster/src/gpsclient.h,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -d -r1.3 -r1.4
--- gpsclient.h	3 Mar 2005 07:32:46 -0000	1.3
+++ gpsclient.h	14 Sep 2005 20:06:53 -0000	1.4
@@ -29,6 +29,8 @@
 #include "map.h"
 
 typedef enum {
+	GPS_STATUS_NO_GPS_COMPILED_IN = -1,		// no gpsd present at compile time
+
 	GPS_STATUS_NO_GPSD = 0,		// daemon is absent :(		(0 should be default state)
 	GPS_STATUS_NO_DEVICE = 1,	// no physical GPS device :(
 	GPS_STATUS_NO_SIGNAL = 2,	// can't get a signal :(

Index: gui.c
===================================================================
RCS file: /cvs/cairo/roadster/src/gui.c,v
retrieving revision 1.10
retrieving revision 1.11
diff -u -d -r1.10 -r1.11
--- gui.c	28 Aug 2005 22:23:14 -0000	1.10
+++ gui.c	14 Sep 2005 20:06:53 -0000	1.11
@@ -36,6 +36,7 @@
 #include "importwindow.h"
 #include "welcomewindow.h"
 #include "searchwindow.h"
+#include "locationeditwindow.h"
 
 void gui_init()
 {
@@ -49,6 +50,7 @@
 	importwindow_init(pGladeXML);
 	//datasetwindow_init(pGladeXML);
 	welcomewindow_init(pGladeXML);
+	locationeditwindow_init(pGladeXML);
 }
 
 GladeXML* gui_load_xml(gchar* pszFileName, gchar* pszXMLTreeRoot)

Index: gui.h
===================================================================
RCS file: /cvs/cairo/roadster/src/gui.h,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -d -r1.2 -r1.3
--- gui.h	23 Apr 2005 18:13:39 -0000	1.2
+++ gui.h	14 Sep 2005 20:06:54 -0000	1.3
@@ -27,17 +27,9 @@
 #include <gtk/gtk.h>
 #include <glade/glade.h>
 
-#define GLADE_FILE_NAME	("roadster.glade")
-
-extern GtkWidget *g_pApplicationWidget;
-extern GtkWidget *g_pDisplaySettingsWidget;
-
-struct gui_state_t {
-	float m_fCenterX;
-	float m_fCenterY;
+#define GLADE_LINK_WIDGET(glade, ptr, type, name)	ptr = type(glade_xml_get_widget(glade, name)); g_return_if_fail(ptr != NULL)
 
-	guint8 m_nZoomLevel;
-};
+#define GLADE_FILE_NAME	("roadster.glade")
 
 //
 // prototypes

Index: import_tiger.c
===================================================================
RCS file: /cvs/cairo/roadster/src/import_tiger.c,v
retrieving revision 1.19
retrieving revision 1.20
diff -u -d -r1.19 -r1.20
--- import_tiger.c	6 Sep 2005 02:44:19 -0000	1.19
+++ import_tiger.c	14 Sep 2005 20:06:54 -0000	1.20
@@ -1123,13 +1123,13 @@
 	gchar* pszFilePath;
 
 	gchar* apszExtensions[7] = {"MET", "RT1", "RT2", "RT7", "RT8", "RTC", "RTI"};	
-	gint8* apBuffers[NUM_ELEMS(apszExtensions)] = {0};
-	gint nSizes[NUM_ELEMS(apszExtensions)] = {0};
+	gint8* apBuffers[G_N_ELEMENTS(apszExtensions)] = {0};
+	gint nSizes[G_N_ELEMENTS(apszExtensions)] = {0};
 
 	// open, read, and delete (unlink) each file
 	gint i;
 	gboolean bSuccess = TRUE;
-	for(i=0 ; i<NUM_ELEMS(apszExtensions) ; i++) {
+	for(i=0 ; i<G_N_ELEMENTS(apszExtensions) ; i++) {
 		pszFilePath = g_strdup_printf("file://%s/TGR%05d.%s", pszDirectoryPath, nTigerSetNumber, apszExtensions[i]);
 		if(GNOME_VFS_OK != gnome_vfs_read_entire_file(pszFilePath, &nSizes[i], (char**)&apBuffers[i])) {
 			bSuccess = FALSE;
@@ -1141,15 +1141,15 @@
 	// did we read all files?
 	if(bSuccess) {
 		gint nStateID = (nTigerSetNumber / 1000);	// int division (eg. turn 25017 into 25)
-		if(nStateID < NUM_ELEMS(g_aStates)) {
+		if(nStateID < G_N_ELEMENTS(g_aStates)) {
 			gint nCountryID = 1;	// USA is #1 *gag*
 			db_insert_state(g_aStates[nStateID].m_pszName, g_aStates[nStateID].m_pszCode, nCountryID, &g_nStateID);
 		}
 
-		g_assert(NUM_ELEMS(apszExtensions) == 7);
+		g_assert(G_N_ELEMENTS(apszExtensions) == 7);
 		bSuccess = import_tiger_from_buffers(apBuffers[0], nSizes[0], apBuffers[1], nSizes[1], apBuffers[2], nSizes[2], apBuffers[3], nSizes[3], apBuffers[4], nSizes[4], apBuffers[5], nSizes[5], apBuffers[6], nSizes[6]);
 	}
-	for(i=0 ; i<NUM_ELEMS(apszExtensions) ; i++) {
+	for(i=0 ; i<G_N_ELEMENTS(apszExtensions) ; i++) {
 		g_free(apBuffers[i]); // can be null
 	}
 	return bSuccess;

Index: importwindow.c
===================================================================
RCS file: /cvs/cairo/roadster/src/importwindow.c,v
retrieving revision 1.8
retrieving revision 1.9
diff -u -d -r1.8 -r1.9
--- importwindow.c	6 Sep 2005 02:44:19 -0000	1.8
+++ importwindow.c	14 Sep 2005 20:06:54 -0000	1.9
@@ -29,6 +29,7 @@
 #include "mainwindow.h"
 #include "importwindow.h"
 #include "util.h"
+#include "gui.h"
 
 struct {
 	GtkWindow* m_pWindow;
@@ -43,14 +44,12 @@
 
 void importwindow_init(GladeXML* pGladeXML)
 {
-	g_ImportWindow.m_pWindow						= GTK_WINDOW(glade_xml_get_widget(pGladeXML, "importwindow")); g_return_if_fail(g_ImportWindow.m_pWindow != NULL);
-	g_ImportWindow.m_pProgressBar					= GTK_PROGRESS_BAR(glade_xml_get_widget(pGladeXML, "importprogressbar")); g_return_if_fail(g_ImportWindow.m_pProgressBar != NULL);
-	g_ImportWindow.m_pOKButton						= GTK_BUTTON(glade_xml_get_widget(pGladeXML, "importokbutton")); g_return_if_fail(g_ImportWindow.m_pOKButton != NULL);
-//	g_ImportWindow.m_pCancelButton					= GTK_BUTTON(glade_xml_get_widget(pGladeXML, "importcancelbutton")); g_return_if_fail(g_ImportWindow.m_pCancelButton != NULL);
-	g_ImportWindow.m_pLogTextView					= GTK_TEXT_VIEW(glade_xml_get_widget(pGladeXML, "importlogtextview")); g_return_if_fail(g_ImportWindow.m_pLogTextView != NULL);
+	GLADE_LINK_WIDGET(pGladeXML, g_ImportWindow.m_pWindow, GTK_WINDOW, "importwindow");
+	GLADE_LINK_WIDGET(pGladeXML, g_ImportWindow.m_pProgressBar, GTK_PROGRESS_BAR, "importprogressbar");
+	GLADE_LINK_WIDGET(pGladeXML, g_ImportWindow.m_pOKButton, GTK_BUTTON, "importokbutton");
+	GLADE_LINK_WIDGET(pGladeXML, g_ImportWindow.m_pLogTextView, GTK_TEXT_VIEW, "importlogtextview");
 }
 
-
 void importwindow_show(void)
 {
 	gtk_widget_show(GTK_WIDGET(g_ImportWindow.m_pWindow));

--- layers.c DELETED ---

--- layers.h DELETED ---

--- NEW FILE: locationeditwindow.c ---
/***************************************************************************
 *            locationeditwindow.c
 *
 *  Copyright  2005  Ian McIntosh
 *  ian_mcintosh at linuxadvocate.org
 ****************************************************************************/

/*
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Library General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <gtk/gtk.h>
#include <glib-object.h>

#include "locationeditwindow.h"
#include "main.h"
#include "gui.h"
#include "util.h"

struct {
	GtkWindow* m_pWindow;

	GtkTreeView* m_pAttributeTreeView;
	GtkListStore* m_pAttributeListStore;

	GtkLabel* m_pCustomAttributesLabel;
	GtkEntry* m_pLocationNameEntry;
	GtkComboBox* m_pLocationSetComboBox;
	GtkTextView* m_pLocationAddressTextView;

	GtkExpander* m_pAttributeExpander;
	GtkButton* m_pAttributeAddButton;
	GtkButton* m_pAttributeRemoveButton;

	GtkListStore* m_pAttributeNameListStore;

	//
	gint m_nEditingLocationID;
	gboolean m_bModified;

} g_LocationEditWindow = {0};

#define ATTRIBUTELIST_COLUMN_NAME 	(0)
#define ATTRIBUTELIST_COLUMN_VALUE 	(1)
#define ATTRIBUTELIST_COLUMN_NAMELISTMODEL_COLUMN (2)

#define ATTRIBUTENAMELIST_COLUMN_NAME 	(0)

static void locationeditwindow_set_title();
//static void locationeditwindow_nameentry_insert_text(GtkEditable *pEditable, gchar *pszNewText, gint nNewTextLen, gint *pPosition, gpointer pUserData);
static void locationeditwindow_configure_attribute_list();
static void locationeditwindow_something_changed_callback(GtkEditable *_unused, gpointer __unused);

void locationeditwindow_show_for_new(gint nDefaultLocationSetID);
void locationeditwindow_show_for_edit(gint nLocationID);


// When an 'entry' is created to do in-place editing in a tree, we want to attach this "insert-text" hander to it.
static void callback_install_insert_text_callback_on_entry(GtkCellRenderer *pCellRenderer, GtkCellEditable *pEditable, const gchar *pszTreePath, gpointer pUserData)
{
	if(GTK_IS_ENTRY(pEditable)) {
		g_signal_connect(G_OBJECT(GTK_ENTRY(pEditable)), "insert-text", G_CALLBACK(pUserData), NULL);
	}
	else if(GTK_IS_COMBO_BOX_ENTRY(pEditable)) {
		g_signal_connect(G_OBJECT(GTK_COMBO_BOX_ENTRY(pEditable)), "insert-text", G_CALLBACK(pUserData), NULL);
	}
}

// Save the results of in-place cell editing
static void callback_store_attribute_editing(GtkCellRendererText *pCell, const gchar *pszTreePath, const gchar *pszNewValue, gpointer *pUserData)
{
	gint nColumn = (gint)(pUserData);

	GtkTreeIter iter;
	GtkTreePath* pPath = gtk_tree_path_new_from_string(pszTreePath);
	if(gtk_tree_model_get_iter(GTK_TREE_MODEL(g_LocationEditWindow.m_pAttributeListStore), &iter, pPath)) {
		gtk_list_store_set(g_LocationEditWindow.m_pAttributeListStore, &iter, nColumn, pszNewValue, -1);
	}
	gtk_tree_path_free(pPath);
}

void locationeditwindow_init(GladeXML* pGladeXML)
{
	// Widgets
	GLADE_LINK_WIDGET(pGladeXML, g_LocationEditWindow.m_pWindow, GTK_WINDOW, "locationeditwindow");
	GLADE_LINK_WIDGET(pGladeXML, g_LocationEditWindow.m_pAttributeTreeView, GTK_TREE_VIEW, "locationattributestreeview");
	GLADE_LINK_WIDGET(pGladeXML, g_LocationEditWindow.m_pCustomAttributesLabel, GTK_LABEL, "customattributeslabel");
	GLADE_LINK_WIDGET(pGladeXML, g_LocationEditWindow.m_pLocationNameEntry, GTK_ENTRY, "locationnameentry");
	GLADE_LINK_WIDGET(pGladeXML, g_LocationEditWindow.m_pLocationSetComboBox, GTK_COMBO_BOX, "locationsetcombobox");
	GLADE_LINK_WIDGET(pGladeXML, g_LocationEditWindow.m_pLocationAddressTextView, GTK_TEXT_VIEW, "locationaddresstextview");

	GLADE_LINK_WIDGET(pGladeXML, g_LocationEditWindow.m_pAttributeExpander, GTK_EXPANDER, "attributeexpander");
	GLADE_LINK_WIDGET(pGladeXML, g_LocationEditWindow.m_pAttributeAddButton, GTK_BUTTON, "attributeaddbutton");
	GLADE_LINK_WIDGET(pGladeXML, g_LocationEditWindow.m_pAttributeRemoveButton, GTK_BUTTON, "attributeremovebutton");

	locationeditwindow_configure_attribute_list();

	// Update window title with changed to 'name'
	g_signal_connect(G_OBJECT(g_LocationEditWindow.m_pLocationNameEntry), "changed", G_CALLBACK(locationeditwindow_something_changed_callback), NULL);
	g_signal_connect(G_OBJECT(g_LocationEditWindow.m_pLocationSetComboBox), "changed", G_CALLBACK(locationeditwindow_something_changed_callback), NULL);

//	g_signal_connect(G_OBJECT(g_LocationEditWindow.m_pLocationAddressTextView), "changed", G_CALLBACK(locationeditwindow_something_changed_callback), NULL);

	locationeditwindow_show_for_new(1);		// XXX: debug
}

static void locationeditwindow_configure_attribute_list()
{
	GtkCellRenderer* pCellRenderer;
  	GtkTreeViewColumn* pColumn;
	GtkTreeIter iter;

	// set up the location attributes list
	g_LocationEditWindow.m_pAttributeListStore = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT);
	gtk_tree_view_set_model(g_LocationEditWindow.m_pAttributeTreeView, GTK_TREE_MODEL(g_LocationEditWindow.m_pAttributeListStore));

	g_LocationEditWindow.m_pAttributeNameListStore = gtk_list_store_new(1, G_TYPE_STRING);
	gtk_list_store_append (g_LocationEditWindow.m_pAttributeNameListStore, &iter);
		gtk_list_store_set (g_LocationEditWindow.m_pAttributeNameListStore, &iter, 0, "url", -1);
	gtk_list_store_append (g_LocationEditWindow.m_pAttributeNameListStore, &iter);
		gtk_list_store_set (g_LocationEditWindow.m_pAttributeNameListStore, &iter, 0, "phone", -1);

	// NEW COLUMN: Editable "name" column (combobox-entry control)
	pCellRenderer = gtk_cell_renderer_combo_new();
    g_object_set(G_OBJECT(pCellRenderer), 
				 "model", g_LocationEditWindow.m_pAttributeNameListStore,
				 "editable", TRUE,
				 NULL);
    g_object_set(G_OBJECT(pCellRenderer), 
				 "width-chars", 20,
				 NULL);

	// add signals
//     g_signal_connect(G_OBJECT(pCellRenderer),
//                      "editing-started", G_CALLBACK(callback_install_insert_text_callback_on_entry),
//                      locationeditwindow_nameentry_insert_text);
	g_signal_connect(G_OBJECT(pCellRenderer),
					 "edited", G_CALLBACK(callback_store_attribute_editing),
					 ATTRIBUTELIST_COLUMN_NAME);

    gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(g_LocationEditWindow.m_pAttributeTreeView), -1, "Field", pCellRenderer,
												"text", ATTRIBUTELIST_COLUMN_NAME,
												"text-column", ATTRIBUTELIST_COLUMN_NAMELISTMODEL_COLUMN,
												NULL);

	// NEW COLUMN: Editable "value" column (text input)
	pCellRenderer = gtk_cell_renderer_text_new();
    g_object_set(G_OBJECT(pCellRenderer), 
				 "editable", TRUE,
				 NULL);

	// add signals
//     g_signal_connect(G_OBJECT(pCellRenderer),
//                      "editing-started", G_CALLBACK(callback_install_insert_text_callback_on_entry),
//                      locationeditwindow_nameentry_insert_text);
	g_signal_connect(G_OBJECT(pCellRenderer),
					 "edited", G_CALLBACK(callback_store_attribute_editing),
					 (gpointer)ATTRIBUTELIST_COLUMN_VALUE);
	
  	pColumn = gtk_tree_view_column_new_with_attributes("Value", pCellRenderer,
													   "text", ATTRIBUTELIST_COLUMN_VALUE,
													   NULL);
	gtk_tree_view_append_column(g_LocationEditWindow.m_pAttributeTreeView, pColumn);

	// Add test data
	gtk_list_store_append(g_LocationEditWindow.m_pAttributeListStore, &iter);
	gtk_list_store_set(g_LocationEditWindow.m_pAttributeListStore, &iter, 
			   ATTRIBUTELIST_COLUMN_NAME, "phone", ATTRIBUTELIST_COLUMN_VALUE, "(617) 555-3314",
			   ATTRIBUTELIST_COLUMN_NAMELISTMODEL_COLUMN, ATTRIBUTENAMELIST_COLUMN_NAME,
			   -1);
	gtk_list_store_append(g_LocationEditWindow.m_pAttributeListStore, &iter);
	gtk_list_store_set(g_LocationEditWindow.m_pAttributeListStore, &iter,
			   ATTRIBUTELIST_COLUMN_NAME, "url", ATTRIBUTELIST_COLUMN_VALUE, "http://www.fusionrestaurant.com",
			   ATTRIBUTELIST_COLUMN_NAMELISTMODEL_COLUMN, ATTRIBUTENAMELIST_COLUMN_NAME,
			   -1);
}

void locationeditwindow_show_for_new(gint nDefaultLocationSetID)
{
	// Set controls to default values
	gtk_entry_set_text(g_LocationEditWindow.m_pLocationNameEntry, "");
	gtk_text_buffer_set_text(gtk_text_view_get_buffer(g_LocationEditWindow.m_pLocationAddressTextView), "", -1);
	gtk_combo_box_set_active(g_LocationEditWindow.m_pLocationSetComboBox, nDefaultLocationSetID);
	gtk_expander_set_expanded(g_LocationEditWindow.m_pAttributeExpander, FALSE);

	g_LocationEditWindow.m_bModified = FALSE;
	locationeditwindow_set_title();

	gtk_widget_grab_focus(GTK_WIDGET(g_LocationEditWindow.m_pLocationNameEntry));
	gtk_widget_show(GTK_WIDGET(g_LocationEditWindow.m_pWindow));
}

void locationeditwindow_show_for_edit(gint nLocationID)
{
	// void location_load_attributes(gint nLocationID, GPtrArray* pAttributeArray);
	g_LocationEditWindow.m_bModified = FALSE;
	locationeditwindow_set_title();
}

static void locationeditwindow_set_title()
{
	const gchar* pszNameValue = gtk_entry_get_text(g_LocationEditWindow.m_pLocationNameEntry);	// note: pointer to internal memory

	gchar* pszNewName;
	if(pszNameValue[0] == '\0') {
		pszNewName = g_strdup_printf("%sunnamed POI", (g_LocationEditWindow.m_bModified) ? "*" : "");
	}
	else {
		pszNewName = g_strdup_printf("%s%s", (g_LocationEditWindow.m_bModified) ? "*" : "", pszNameValue);
	}

	gtk_window_set_title(g_LocationEditWindow.m_pWindow, pszNewName);
	g_free(pszNewName);
}


// Widget Callbacks
static void locationeditwindow_something_changed_callback(GtkEditable *_unused, gpointer __unused)
{
	g_LocationEditWindow.m_bModified = TRUE;
	locationeditwindow_set_title();
}

// static void locationeditwindow_nameentry_insert_text(GtkEditable *pEditable, gchar *pszNewText, gint nNewTextLen, gint *pPosition, gpointer pUserData)
// {
//     g_print("nNewTextLen=%d\n", nNewTextLen);
//
//     int i;
//     gchar *pszReplacement = g_utf8_strdown(pszNewText, nNewTextLen);
//
//     g_signal_handlers_block_by_func(GTK_OBJECT(pEditable),
//                                     GTK_SIGNAL_FUNC(locationeditwindow_nameentry_insert_text),
//                                     pUserData);
//     gtk_editable_insert_text(pEditable, pszReplacement, nNewTextLen, pPosition);
//     g_signal_handlers_unblock_by_func(GTK_OBJECT(pEditable),
//                                       GTK_SIGNAL_FUNC(locationeditwindow_nameentry_insert_text),
//                                       pUserData);
//
//     gtk_signal_emit_stop_by_name(GTK_OBJECT(pEditable), "insert_text");
//
//     g_free(pszReplacement);
// }

--- NEW FILE: locationeditwindow.h ---
/***************************************************************************
 *            locationeditwindow.h
 *
 *  Copyright  2005  Ian McIntosh
 *  ian_mcintosh at linuxadvocate.org
 ****************************************************************************/

/*
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Library General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#ifndef _LOCATIONEDITWINDOW_H
#define _LOCATIONEDITWINDOW_H

#include <glade/glade.h>
#include "map.h"

G_BEGIN_DECLS

void locationeditwindow_init(GladeXML* pGladeXML);

G_END_DECLS

#endif

Index: locationset.c
===================================================================
RCS file: /cvs/cairo/roadster/src/locationset.c,v
retrieving revision 1.11
retrieving revision 1.12
diff -u -d -r1.11 -r1.12
--- locationset.c	23 Apr 2005 18:13:39 -0000	1.11
+++ locationset.c	14 Sep 2005 20:06:54 -0000	1.12
@@ -54,7 +54,7 @@
 // get a new locationset struct from the allocator
 static gboolean locationset_alloc(locationset_t** ppReturn)
 {
-	g_return_val_if_fail(g_LocationSet.m_pLocationSetChunkAllocator != NULL, NULL);
+	g_return_val_if_fail(g_LocationSet.m_pLocationSetChunkAllocator != NULL, FALSE);
 
 	locationset_t* pNew = g_mem_chunk_alloc0(g_LocationSet.m_pLocationSetChunkAllocator);
 

Index: main.c
===================================================================
RCS file: /cvs/cairo/roadster/src/main.c,v
retrieving revision 1.22
retrieving revision 1.23
diff -u -d -r1.22 -r1.23
--- main.c	28 Aug 2005 22:23:14 -0000	1.22
+++ main.c	14 Sep 2005 20:06:54 -0000	1.23
@@ -32,7 +32,6 @@
 #include "map.h"
 #include "gpsclient.h"
 #include "scenemanager.h"
-#include "prefs.h"
 #include "animator.h"
 #include "road.h"
 #include "locationset.h"
@@ -76,8 +75,6 @@
 	location_insert_attribute(nNewLocationID, LOCATION_ATTRIBUTE_ID_NAME, "1369 Coffee House", NULL);
 	location_insert_attribute(nNewLocationID, LOCATION_ATTRIBUTE_ID_ADDRESS, "757 Massachusetts Avenue\nCambridge, MA 02139", NULL);
 */
-	prefs_read();
-
 	g_print("Running %s\n", g_thread_supported() ? "multi-threaded" : "single-threaded");
 
 	gui_run();
@@ -98,9 +95,6 @@
 	gnome_vfs_make_directory(pszApplicationDir, 0700);
 	g_free(pszApplicationDir);
 #endif
-	g_print("initializing prefs\n");
-	prefs_init();	// note: doesn't READ prefs yet
-
 	g_print("initializing points\n");
 	point_init();
 	g_print("initializing pointstrings\n");
@@ -111,8 +105,8 @@
 
 	g_print("initializing tracks\n");
 	track_init();
-	g_print("initializing layers\n");
-	layers_init();
+	g_print("initializing map styles\n");
+	map_style_init();
 	g_print("initializing glyphs\n");
 	glyph_init();
 	g_print("initializing map\n");

Index: main.h
===================================================================
RCS file: /cvs/cairo/roadster/src/main.h,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -d -r1.2 -r1.3
--- main.h	31 Mar 2005 08:29:53 -0000	1.2
+++ main.h	14 Sep 2005 20:06:54 -0000	1.3
@@ -21,7 +21,22 @@
  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  */
 
+#ifndef _MAIN_H_
+#define _MAIN_H_
+
+#include <gtk/gtk.h>
+
 #define USE_GNOME_VFS		// comment this out to get a faster single-threaded compile (can't import, though)
-//#define ENABLE_TIMING
 #define USE_GFREELIST
 
+//#define ENABLE_TIMING
+
+typedef struct color {
+	gfloat m_fRed;
+	gfloat m_fGreen;
+	gfloat m_fBlue;
+	gfloat m_fAlpha;
+} color_t;
+
+#endif
+

Index: mainwindow.c
===================================================================
RCS file: /cvs/cairo/roadster/src/mainwindow.c,v
retrieving revision 1.39
retrieving revision 1.40
diff -u -d -r1.39 -r1.40
--- mainwindow.c	6 Sep 2005 02:44:19 -0000	1.39
+++ mainwindow.c	14 Sep 2005 20:06:54 -0000	1.40
@@ -39,7 +39,7 @@
 #include "gotowindow.h"
 #include "db.h"
 #include "map.h"
-#include "layers.h"
+#include "map_style.h"
 #include "importwindow.h"
 #include "welcomewindow.h"
 #include "locationset.h"
@@ -55,17 +55,19 @@
 #define PROGRAM_DESCRIPTION		"Mapping for everyone!"
 #define WEBSITE_URL				"http://linuxadvocate.org/projects/roadster"
 
+#define MAP_STYLE_FILENAME 		("layers.xml")
+
 // how long after stopping various movements should we redraw in high-quality mode
 #define DRAW_PRETTY_SCROLL_TIMEOUT_MS	(110)	// NOTE: should be longer than the SCROLL_TIMEOUT_MS below!!
 #define DRAW_PRETTY_ZOOM_TIMEOUT_MS	(180)
 #define DRAW_PRETTY_DRAG_TIMEOUT_MS	(250)
 #define DRAW_PRETTY_RESIZE_TIMEOUT_MS	(180)
 
-#define SCROLL_TIMEOUT_MS		(80)		// how often (in MS) to move
-#define SCROLL_DISTANCE_IN_PIXELS	(100)		// how far to move every (above) MS
+#define SCROLL_TIMEOUT_MS				(70)		// how often (in MS) to move
+#define SCROLL_DISTANCE_IN_PIXELS		(60)		// how far to move every (above) MS
 #define BORDER_SCROLL_CLICK_TARGET_SIZE	(16)		// the size of the click target (distance from edge of map view) to begin scrolling
 
-#define SLIDE_TIMEOUT_MS		(50)	// time between frames (in MS) for smooth-sliding (on double click?)
+#define SLIDE_TIMEOUT_MS			(50)	// time between frames (in MS) for smooth-sliding on double click
 #define	SLIDE_TIME_IN_SECONDS		(0.4)	// how long the whole slide should take, in seconds
 #define	SLIDE_TIME_IN_SECONDS_AUTO	(0.8)	// time for sliding to search results, etc.
 
@@ -75,21 +77,21 @@
 
 // Locationset columns
 #define	LOCATIONSETLIST_COLUMN_ENABLED  (0)
-#define LOCATIONSETLIST_COLUMN_NAME	(1)
-#define LOCATIONSETLIST_COLUMN_ID	(2)
+#define LOCATIONSETLIST_COLUMN_NAME		(1)
+#define LOCATIONSETLIST_COLUMN_ID		(2)
 
-//
-#define MOUSE_BUTTON_LEFT		(1)
-#define MOUSE_BUTTON_RIGHT		(3)
+// 
+#define MOUSE_BUTTON_LEFT				(1)
+#define MOUSE_BUTTON_RIGHT				(3)
 
 // Limits
-#define MAX_SEARCH_TEXT_LENGTH		(100)
-#define SPEED_LABEL_FORMAT		("<span font_desc='32'>%.0f</span>")
+#define MAX_SEARCH_TEXT_LENGTH			(100)
+#define SPEED_LABEL_FORMAT				("<span font_desc='32'>%.0f</span>")
 
-#define TOOLTIP_FORMAT			("%s")
+#define TOOLTIP_FORMAT					("%s")
 
-#define	ROAD_TOOLTIP_BG_COLOR		65535, 65535, 39000
-#define	LOCATION_TOOLTIP_BG_COLOR	52800, 60395, 65535
+#define	ROAD_TOOLTIP_BG_COLOR			65535, 65535, 39000
+#define	LOCATION_TOOLTIP_BG_COLOR		52800, 60395, 65535
 
 // Settings
 #define TIMER_GPS_REDRAW_INTERVAL_MS	(2500)		// lower this (to 1000?) when it's faster to redraw track
@@ -151,8 +153,8 @@
 
 	// Toolbar
 	GtkToolbar* m_pToolbar;
-	GtkToolButton* m_pPointerToolButton;
-	GtkToolButton* m_pZoomToolButton;
+//	GtkToolButton* m_pPointerToolButton;
+//	GtkToolButton* m_pZoomToolButton;
 	GtkHScale* m_pZoomScale;
 	GtkEntry* m_pSearchBox;
 	GtkImage* m_pStatusbarGPSIcon;
@@ -201,9 +203,10 @@
 	gboolean m_bScrolling;
 	EDirection m_eScrollDirection;
 	gboolean m_bScrollMovement;
-	
+
 	gboolean m_bMouseDragging;
 	gboolean m_bMouseDragMovement;
+
 	screenpoint_t m_ptClickLocation;	
 
 	gint m_nCurrentGPSPath;
@@ -234,7 +237,7 @@
 void cursor_init()
 {
 	int i;
-	for(i=0 ; i<NUM_ELEMS(g_Tools) ; i++) {
+	for(i=0 ; i<G_N_ELEMENTS(g_Tools) ; i++) {
 		g_Tools[i].m_Cursor.m_pGdkCursor = gdk_cursor_new(g_Tools[i].m_Cursor.m_CursorType);
 	}
 }
@@ -291,8 +294,8 @@
 	// Widgets
 	g_MainWindow.m_pWindow			= GTK_WINDOW(glade_xml_get_widget(pGladeXML, "mainwindow"));			g_return_if_fail(g_MainWindow.m_pWindow != NULL);
 	g_MainWindow.m_pMapPopupMenu		= GTK_MENU(glade_xml_get_widget(pGladeXML, "mappopupmenu"));			g_return_if_fail(g_MainWindow.m_pMapPopupMenu != NULL);
-	g_MainWindow.m_pPointerToolButton 	= GTK_TOOL_BUTTON(glade_xml_get_widget(pGladeXML, "pointertoolbutton"));	g_return_if_fail(g_MainWindow.m_pPointerToolButton != NULL);
-	g_MainWindow.m_pZoomToolButton 		= GTK_TOOL_BUTTON(glade_xml_get_widget(pGladeXML, "zoomtoolbutton")); 		g_return_if_fail(g_MainWindow.m_pZoomToolButton != NULL);
+//	g_MainWindow.m_pPointerToolButton 	= GTK_TOOL_BUTTON(glade_xml_get_widget(pGladeXML, "pointertoolbutton"));	g_return_if_fail(g_MainWindow.m_pPointerToolButton != NULL);
+//	g_MainWindow.m_pZoomToolButton 		= GTK_TOOL_BUTTON(glade_xml_get_widget(pGladeXML, "zoomtoolbutton")); 		g_return_if_fail(g_MainWindow.m_pZoomToolButton != NULL);
 
 	g_MainWindow.m_pZoomInButton		= GTK_BUTTON(glade_xml_get_widget(pGladeXML, "zoominbutton")); 			g_return_if_fail(g_MainWindow.m_pZoomInButton != NULL);
 	g_MainWindow.m_pZoomInMenuItem		= GTK_MENU_ITEM(glade_xml_get_widget(pGladeXML, "zoominmenuitem")); 		g_return_if_fail(g_MainWindow.m_pZoomInMenuItem != NULL);
@@ -335,8 +338,9 @@
 	g_MainWindow.m_pDrawingArea = GTK_DRAWING_AREA(gtk_drawing_area_new());
 	g_MainWindow.m_pTooltip = tooltip_new();
 
-	// create map
+	// create map and load style
 	map_new(&g_MainWindow.m_pMap, GTK_WIDGET(g_MainWindow.m_pDrawingArea));
+	map_style_load(g_MainWindow.m_pMap, MAP_STYLE_FILENAME);
 
 	// add signal handlers to drawing area
 	gtk_widget_add_events(GTK_WIDGET(g_MainWindow.m_pDrawingArea), GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
@@ -468,8 +472,13 @@
 //
 gboolean mainwindow_on_draw_pretty_timeout(gpointer _unused)
 {
-	g_MainWindow.m_nDrawPrettyTimeoutID = 0;
 	mainwindow_draw_map(DRAWFLAG_ALL);
+
+	// We've just drawn a complete frame, so we can consider this a new drag with no movement yet
+	g_MainWindow.m_bMouseDragMovement = FALSE;	// NOTE: may have already been FALSE, for example if we're not dragging... :)
+
+	// Returning FALSE from the timeout callback kills the timer.
+	g_MainWindow.m_nDrawPrettyTimeoutID = 0;
 	return FALSE;
 }
 
@@ -793,7 +802,7 @@
 
 void mainwindow_on_reloadstylesmenuitem_activate(GtkMenuItem *menuitem, gpointer user_data)
 {
-	layers_reload();
+	map_style_load(g_MainWindow.m_pMap, MAP_STYLE_FILENAME);
 	mainwindow_draw_map(DRAWFLAG_ALL);
 }
 
@@ -1013,16 +1022,15 @@
 
 static gboolean mainwindow_on_mouse_motion(GtkWidget* w, GdkEventMotion *event)
 {
-        gint nX,nY;
+    gint nX,nY;
 
+	// XXX: why do we do this?
 	if (event->is_hint) {
 		gdk_window_get_pointer(w->window, &nX, &nY, NULL);
 	}
-	else
-	{
+	else {
 		nX = event->x;
 		nY = event->y;
-		//nState = event->state;
 	}
 
 	gint nWidth = GTK_WIDGET(g_MainWindow.m_pDrawingArea)->allocation.width;
@@ -1054,18 +1062,18 @@
 
 		EDirection eScrollDirection = match_border(nX, nY, nWidth, nHeight, BORDER_SCROLL_CLICK_TARGET_SIZE);
 
-		if(g_MainWindow.m_eScrollDirection != eScrollDirection) {
-			// update cursor
-			nCursor = g_aDirectionCursors[eScrollDirection].m_nCursor;
+		// update cursor
+		nCursor = g_aDirectionCursors[eScrollDirection].m_nCursor;
 
-			// update direction if actively scrolling
-			g_MainWindow.m_eScrollDirection = eScrollDirection;
+		// update direction if actively scrolling
+		g_MainWindow.m_eScrollDirection = eScrollDirection;
 
+//		if(g_MainWindow.m_eScrollDirection != eScrollDirection) {
 			//GdkCursor* pCursor = gdk_cursor_new(g_aDirectionCursors[eScrollDirection].m_nCursor);
 			//gdk_pointer_ungrab(GDK_CURRENT_TIME);
 			//gdk_pointer_grab(GTK_WIDGET(g_MainWindow.m_pDrawingArea)->window, FALSE, GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_RELEASE_MASK, NULL, pCursor, GDK_CURRENT_TIME);
 			//gdk_cursor_unref(pCursor);
-		}
+//		}
 	}
 	else {
 		// If not dragging or scrolling, user is just moving mouse around.
@@ -1214,12 +1222,12 @@
 
 static void mainwindow_setup_selected_tool(void)
 {
-	if(gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(g_MainWindow.m_pPointerToolButton))) {
-		gui_set_tool(kToolPointer);
-	}
-	else if(gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(g_MainWindow.m_pZoomToolButton))) {
-		gui_set_tool(kToolZoom);
-	}
+//     if(gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(g_MainWindow.m_pPointerToolButton))) {
+//         gui_set_tool(kToolPointer);
+//     }
+//     else if(gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(g_MainWindow.m_pZoomToolButton))) {
+//         gui_set_tool(kToolZoom);
+//     }
 }
 
 // One handler for all 'tools' buttons
@@ -1231,7 +1239,7 @@
 
 void mainwindow_draw_map(gint nDrawFlags)
 {
-	map_draw(g_MainWindow.m_pMap, nDrawFlags);
+	map_draw(g_MainWindow.m_pMap, g_MainWindow.m_pMap->m_pPixmap, nDrawFlags);
 
 	// push it to screen
 	GdkPixmap* pMapPixmap = map_get_pixmap(g_MainWindow.m_pMap);
@@ -1275,8 +1283,7 @@
 
 /*
 ** GPS Functions
-*/ 
-
+*/
 gboolean mainwindow_on_gps_show_position_toggled(GtkWidget* _unused, gpointer* __unused)
 {
 
@@ -1341,9 +1348,14 @@
 		// unless we're "LIVE", set signal strength to 0
 		gtk_progress_bar_set_fraction(g_MainWindow.m_pGPSSignalStrengthProgressBar, 0.0);
 
-		if(pData->m_eStatus == GPS_STATUS_NO_SIGNAL) {
+		if(pData->m_eStatus == GPS_STATUS_NO_GPS_COMPILED_IN) {
+			util_set_image_to_stock(g_MainWindow.m_pStatusbarGPSIcon, GTK_STOCK_CANCEL, GTK_ICON_SIZE_MENU);
+			
+			gtk_tooltips_set_tip(GTK_TOOLTIPS(g_MainWindow.m_pTooltips), pWidget,
+					 "GPS support was not enabled at compile time", "");
+		}
+		else if(pData->m_eStatus == GPS_STATUS_NO_SIGNAL) {
 			// do NOT set speed to 0 if we drop the signal temporarily...
-
 			util_set_image_to_stock(g_MainWindow.m_pStatusbarGPSIcon, GTK_STOCK_CANCEL, GTK_ICON_SIZE_MENU);
 
 			gtk_tooltips_set_tip(GTK_TOOLTIPS(g_MainWindow.m_pTooltips), pWidget,
@@ -1404,7 +1416,7 @@
 		mainwindow_map_center_on_mappoint(&ptNew);
 		if(bDone) {
 			// when done, draw a full frame
-			mainwindow_draw_map(DRAWFLAG_GEOMETRY);
+			//mainwindow_draw_map(DRAWFLAG_GEOMETRY);
 			mainwindow_draw_map(DRAWFLAG_ALL);
 		}
 		else {

Index: map.c
===================================================================
RCS file: /cvs/cairo/roadster/src/map.c,v
retrieving revision 1.43
retrieving revision 1.44
diff -u -d -r1.43 -r1.44
--- map.c	6 Sep 2005 02:44:19 -0000	1.43
+++ map.c	14 Sep 2005 20:06:54 -0000	1.44
@@ -27,6 +27,7 @@
 #include <math.h>
 
 #include "main.h"
+#include "map_style.h"
 #include "gui.h"
 #include "map.h"
 #include "mainwindow.h"
@@ -34,14 +35,13 @@
 #include "db.h"
 #include "road.h"
 #include "point.h"
-#include "layers.h"
 #include "locationset.h"
 #include "location.h"
 #include "scenemanager.h"
 
 #define ENABLE_RIVER_TO_LAKE_LOADTIME_HACK	// change circular rivers to lakes when loading from disk
 //#define ENABLE_SCENEMANAGER_DEBUG_TEST
-#define ENABLE_LABELS_WHILE_DRAGGING
+//#define ENABLE_LABELS_WHILE_DRAGGING
 
 #ifdef THREADED_RENDERING
 #define RENDERING_THREAD_YIELD          g_thread_yield()
@@ -96,14 +96,14 @@
 //     {  200000, ""},
 //     {  100000, ""},
 
-	{  100000, UNIT_MILES, 2, 	UNIT_KILOMETERS, 2, "", },     	// 1
-	{   48000, UNIT_MILES, 1, 	UNIT_KILOMETERS, 1, "", },     	// 2
+	{   80000, UNIT_MILES, 2, 	UNIT_KILOMETERS, 2, "", },     	// 1
+	{   40000, UNIT_MILES, 1, 	UNIT_KILOMETERS, 1, "", },     	// 2
 	{   20000, UNIT_FEET, 2000, UNIT_METERS, 400, "", }, 		// 3
 	{   10000, UNIT_FEET, 1000, UNIT_METERS, 200, "", },		// 4
 	{    5000, UNIT_FEET, 500, 	UNIT_METERS, 100, "", },		// 5
 };
 
-gchar* g_apszMapObjectTypeNames[] = {
+gchar* g_apszMapObjectTypeNames[] = {	// XXX: would be nice to remove this. although we *do* need to maintain a link between imported data and styles
 	"",
 	"minor-roads",
 	"major-roads",
@@ -115,7 +115,8 @@
 	"parks",
 	"rivers",
 	"lakes",
-	"misc-areas"
+	"misc-areas",
+	"urban-areas",
 };
 
 // ========================================================
@@ -127,27 +128,6 @@
 {
 }
 
-void map_callback_free_locations_array(gpointer* p)
-{
-	GPtrArray* pLocationsArray = (GPtrArray*)p;
-	gint i;
-	for(i=(pLocationsArray->len-1) ; i>=0 ;i--) {
-		location_t* pLocation = g_ptr_array_remove_index_fast(pLocationsArray, i);
-		location_free(pLocation);
-	}
-}
-
-static void map_init_location_hash(map_t* pMap)
-{
-	// destroy it if it exists... it will call map_callback_free_locations_array() on all children and g_free on all keys (see below)
-	if(pMap->m_pLocationArrayHashTable != NULL) {
-		g_hash_table_destroy(pMap->m_pLocationArrayHashTable);
-	}
-	pMap->m_pLocationArrayHashTable = g_hash_table_new_full(g_int_hash, g_int_equal, 
-								g_free, 				/* key destroy function */
-								map_callback_free_locations_array);	/* value destroy function */
-}
-
 gboolean map_new(map_t** ppMap, GtkWidget* pTargetWidget)
 {
 	g_assert(ppMap != NULL);
@@ -172,7 +152,7 @@
 
 	// init containers for geometry data
 	gint i;
-	for(i=0 ; i<NUM_ELEMS(pMap->m_apLayerData) ; i++) {
+	for(i=0 ; i<G_N_ELEMENTS(pMap->m_apLayerData) ; i++) {
 		maplayer_data_t* pLayer = g_new0(maplayer_data_t, 1);
 		pLayer->m_pRoadsArray = g_ptr_array_new();
 		pMap->m_apLayerData[i] = pLayer;
@@ -187,32 +167,7 @@
 	return TRUE;
 }
 
-static void map_store_location(map_t* pMap, location_t* pLocation, gint nLocationSetID)
-{
-	GPtrArray* pLocationsArray;
-	pLocationsArray = g_hash_table_lookup(pMap->m_pLocationArrayHashTable, &nLocationSetID);
-	if(pLocationsArray != NULL) {
-		// found existing array
-		//g_print("existing for set %d\n", nLocationSetID);
-	}
-	else {
-		//g_print("new for set %d\n", nLocationSetID);
-
-		// need a new array
-		pLocationsArray = g_ptr_array_new();
-		g_assert(pLocationsArray != NULL);
-
-		gint* pKey = g_malloc(sizeof(gint));
-		*pKey = nLocationSetID;
-		g_hash_table_insert(pMap->m_pLocationArrayHashTable, pKey, pLocationsArray);
-	}
-
-	// add location to the array of locations!
-	g_ptr_array_add(pLocationsArray, pLocation);
-	//g_print("pLocationsArray->len = %d\n", pLocationsArray->len);
-}
-
-void map_draw(map_t* pMap, gint nDrawFlags)
+void map_draw(map_t* pMap, GdkPixmap* pTargetPixmap, gint nDrawFlags)
 {
 	g_assert(pMap != NULL);
 
@@ -230,7 +185,7 @@
 	scenemanager_clear(pMap->m_pSceneManager);
 	scenemanager_set_screen_dimensions(pMap->m_pSceneManager, pRenderMetrics->m_nWindowWidth, pRenderMetrics->m_nWindowHeight);
 
-	gint nRenderMode = RENDERMODE_FAST; // RENDERMODE_PRETTY
+	gint nRenderMode = RENDERMODE_FAST; //RENDERMODE_FAST; // RENDERMODE_PRETTY
 
 #ifdef ENABLE_LABELS_WHILE_DRAGGING
 	nDrawFlags |= DRAWFLAG_LABELS;	// always turn on labels
@@ -244,20 +199,19 @@
 	if(nRenderMode == RENDERMODE_FAST) {
 		// 
 		if(nDrawFlags & DRAWFLAG_GEOMETRY) {
-			map_draw_gdk(pMap, pRenderMetrics, pMap->m_pPixmap, DRAWFLAG_GEOMETRY);
-		}
-		
-		// Always draw labels with Cairo
-		if(nDrawFlags & DRAWFLAG_LABELS) {
-			map_draw_cairo(pMap, pRenderMetrics, pMap->m_pPixmap, DRAWFLAG_LABELS);
+			map_draw_gdk(pMap, pRenderMetrics, pTargetPixmap, DRAWFLAG_GEOMETRY);
+			nDrawFlags &= ~DRAWFLAG_GEOMETRY;
 		}
+
+		// Call cairo for finishing the scene
+		map_draw_cairo(pMap, pRenderMetrics, pTargetPixmap, nDrawFlags);
 	}
 	else {	// nRenderMode == RENDERMODE_PRETTY
-		map_draw_cairo(pMap, pRenderMetrics, pMap->m_pPixmap, nDrawFlags);
+		map_draw_cairo(pMap, pRenderMetrics, pTargetPixmap, nDrawFlags);
 	}
 
 #ifdef ENABLE_SCENEMANAGER_DEBUG_TEST
-	gdk_draw_polygon(pMap->m_pPixmap, pMap->m_pTargetWidget->style->fg_gc[GTK_WIDGET_STATE(pMap->m_pTargetWidget)],
+	gdk_draw_polygon(pTargetPixmap, pMap->m_pTargetWidget->style->fg_gc[GTK_WIDGET_STATE(pMap->m_pTargetWidget)],
 					 FALSE, aPoints, 4);
 #endif
 
@@ -278,7 +232,6 @@
 	// nothing since we're not using mutexes
 }
 
-
 // ========================================================
 //  Get/Set Functions
 // ========================================================
@@ -294,16 +247,26 @@
 	}
 }
 
-guint16 map_get_zoomlevel(map_t* pMap)
+guint16 map_get_zoomlevel(const map_t* pMap)
 {
 	return pMap->m_uZoomLevel;	// between MIN_ZOOM_LEVEL and MAX_ZOOM_LEVEL
 }
 
-guint32 map_get_zoomlevel_scale(map_t* pMap)
+guint32 map_get_zoomlevel_scale(const map_t* pMap)
 {
 	return g_sZoomLevels[pMap->m_uZoomLevel].m_uScale;	// returns "5000" for 1:5000 scale
 }
 
+gboolean map_can_zoom_in(const map_t* pMap)
+{
+	// can we increase zoom level?
+	return (pMap->m_uZoomLevel < MAX_ZOOM_LEVEL);
+}
+gboolean map_can_zoom_out(const map_t* pMap)
+{
+	return (pMap->m_uZoomLevel > MIN_ZOOM_LEVEL);
+}
+
 // ========================================================
 //  Coordinate Conversion Functions
 // ========================================================
@@ -335,7 +298,7 @@
 // convert pixels to a span of degrees
 double map_pixels_to_degrees(map_t* pMap, gint16 nPixels, guint16 uZoomLevel)
 {
-	double fMonitorPixelsPerInch = 85 + 1/3;
+	double fMonitorPixelsPerInch = 85.333;	// XXX: don't hardcode this
 	double fPixelsPerMeter = fMonitorPixelsPerInch * INCHES_PER_METER;
 	double fMetersOfPixels = ((float)nPixels) / fPixelsPerMeter;
 
@@ -348,7 +311,7 @@
 
 double map_degrees_to_pixels(map_t* pMap, gdouble fDegrees, guint16 uZoomLevel)
 {
-	double fMonitorPixelsPerInch = 85 + 1/3;
+	double fMonitorPixelsPerInch = 85.333;	// XXX: don't hardcode this
 
 	double fResultInMeters = WORLD_DEGREES_TO_METERS(fDegrees);
 	double fResultInPixels = (INCHES_PER_METER * fResultInMeters) * fMonitorPixelsPerInch;
@@ -398,7 +361,7 @@
 //	map_set_redraw_needed(TRUE);
 }
 
-void map_get_centerpoint(map_t* pMap, mappoint_t* pReturnPoint)
+void map_get_centerpoint(const map_t* pMap, mappoint_t* pReturnPoint)
 {
 	g_assert(pReturnPoint != NULL);
 
@@ -414,6 +377,9 @@
 	pMap->m_MapDimensions.m_uWidth = pDimensions->m_uWidth;
 	pMap->m_MapDimensions.m_uHeight = pDimensions->m_uHeight;
 
+	// XXX: free old pixmap?
+	//g_assert(pMap->m_pPixmap == NULL);
+
 	pMap->m_pPixmap = gdk_pixmap_new(
 			pMap->m_pTargetWidget->window,
 			pMap->m_MapDimensions.m_uWidth, pMap->m_MapDimensions.m_uHeight,
@@ -585,10 +551,11 @@
 			// Get layer type that this belongs on
 			gint nTypeID = atoi(aRow[1]);
 			if(nTypeID < MAP_OBJECT_TYPE_FIRST || nTypeID > MAP_OBJECT_TYPE_LAST) {
-				//g_warning("geometry record '%s' has bad type '%s'\n", aRow[0], aRow[1]);
+				g_warning("geometry record '%s' has bad type '%s'\n", aRow[0], aRow[1]);
 				continue;
 			}
 
+			if(nTypeID == 12) g_warning("(got a 12)");
 			// Extract points
 			road_t* pNewRoad = NULL;
 			road_alloc(&pNewRoad);
@@ -724,7 +691,7 @@
 {
 	// Clear layers
 	gint i,j;
-	for(i=0 ; i<NUM_ELEMS(pMap->m_apLayerData) ; i++) {
+	for(i=0 ; i<G_N_ELEMENTS(pMap->m_apLayerData) ; i++) {
 		maplayer_data_t* pLayerData = pMap->m_apLayerData[i];
 
 		// Free each
@@ -827,7 +794,7 @@
 
 	// Test things in the REVERSE order they are drawn (otherwise we'll match things that have been painted-over)
 	gint i;
-	for(i=NUM_ELEMS(layerdraworder)-1 ; i>=0 ; i--) {
+	for(i=G_N_ELEMENTS(layerdraworder)-1 ; i>=0 ; i--) {
 		if(layerdraworder[i].eSubLayerRenderType != SUBLAYER_RENDERTYPE_LINES) continue;
 
 		gint nLayer = layerdraworder[i].nLayer;
@@ -1181,20 +1148,96 @@
 	return FALSE;
 }
 
-gboolean map_can_zoom_in(map_t* pMap)
+
+// Get an attribute from a selected location
+const gchar* map_location_selection_get_attribute(const map_t* pMap, const locationselection_t* pLocationSelection, const gchar* pszAttributeName)
 {
-	// can we increase zoom level?
-	return (pMap->m_uZoomLevel < MAX_ZOOM_LEVEL);
+	// NOTE: always gets the 'first' one.  this is only used for the few hardcoded attributes (name, address, ...)
+	gint i;
+	for(i=0 ; i<pLocationSelection->m_pAttributesArray->len ; i++) {
+		locationattribute_t* pAttribute = g_ptr_array_index(pLocationSelection->m_pAttributesArray, i);
+		if(strcmp(pAttribute->m_pszName, pszAttributeName) == 0) {
+			return pAttribute->m_pszValue;
+		}
+	}
+	return NULL;
 }
-gboolean map_can_zoom_out(map_t* pMap)
+
+gboolean map_object_type_atoi(const gchar* pszName, gint* pnReturnObjectTypeID)
 {
-	return (pMap->m_uZoomLevel > MIN_ZOOM_LEVEL);
+	gint i;
+	for(i=0 ; i<MAP_NUM_OBJECT_TYPES ; i++) {
+		if(strcmp(pszName, g_apszMapObjectTypeNames[i]) == 0) {
+			*pnReturnObjectTypeID = i;
+			return TRUE;
+		}
+	}
+	return FALSE;
 }
 
+gboolean map_layer_render_type_atoi(const gchar* pszName, gint* pnReturnRenderTypeID)
+{
+	gchar* g_apszMapRenderTypeNames[] = {"lines", "polygons", "line-labels", "polygon-labels", "fill"};
 
-//
-// Map selected POI
-//
+	gint i;
+	for(i=0 ; i<G_N_ELEMENTS(g_apszMapRenderTypeNames) ; i++) {
+		if(strcmp(pszName, g_apszMapRenderTypeNames[i]) == 0) {
+			*pnReturnRenderTypeID = i;
+			return TRUE;
+		}
+	}
+	return FALSE;
+}
+
+void map_callback_free_locations_array(gpointer* p)
+{
+	GPtrArray* pLocationsArray = (GPtrArray*)p;
+	gint i;
+	for(i=(pLocationsArray->len-1) ; i>=0 ;i--) {
+		location_t* pLocation = g_ptr_array_remove_index_fast(pLocationsArray, i);
+		location_free(pLocation);
+	}
+}
+
+static void map_init_location_hash(map_t* pMap)
+{
+	// destroy it if it exists... it will call map_callback_free_locations_array() on all children and g_free on all keys (see below)
+	if(pMap->m_pLocationArrayHashTable != NULL) {
+		g_hash_table_destroy(pMap->m_pLocationArrayHashTable);
+	}
+	pMap->m_pLocationArrayHashTable = g_hash_table_new_full(g_int_hash, g_int_equal, 
+								g_free, 				/* key destroy function */
+								map_callback_free_locations_array);	/* value destroy function */
+}
+
+static void map_store_location(map_t* pMap, location_t* pLocation, gint nLocationSetID)
+{
+	GPtrArray* pLocationsArray;
+	pLocationsArray = g_hash_table_lookup(pMap->m_pLocationArrayHashTable, &nLocationSetID);
+	if(pLocationsArray != NULL) {
+		// found existing array
+		//g_print("existing for set %d\n", nLocationSetID);
+	}
+	else {
+		//g_print("new for set %d\n", nLocationSetID);
+
+		// need a new array
+		pLocationsArray = g_ptr_array_new();
+		g_assert(pLocationsArray != NULL);
+
+		gint* pKey = g_malloc(sizeof(gint));
+		*pKey = nLocationSetID;
+		g_hash_table_insert(pMap->m_pLocationArrayHashTable, pKey, pLocationsArray);
+	}
+
+	// add location to the array of locations!
+	g_ptr_array_add(pLocationsArray, pLocation);
+	//g_print("pLocationsArray->len = %d\n", pLocationsArray->len);
+}
+
+// ========================================================
+//  Functions to deal with "selected" locations (POI), which are always kept in RAM
+// ========================================================
 
 // lookup a selected location by ID
 locationselection_t* map_location_selection_get(map_t* pMap, gint nLocationID)
@@ -1208,7 +1251,7 @@
 	return NULL;
 }
 
-gint map_location_selection_sort_callback(gconstpointer ppA, gconstpointer ppB)
+gint map_location_selection_latitude_sort_callback(gconstpointer ppA, gconstpointer ppB)
 {
 	locationselection_t* pA = *((locationselection_t**)ppA);
 	locationselection_t* pB = *((locationselection_t**)ppB);
@@ -1237,12 +1280,14 @@
 
 	g_ptr_array_add(pMap->m_pLocationSelectionArray, pNew);
 
-	g_ptr_array_sort(pMap->m_pLocationSelectionArray, map_location_selection_sort_callback);
+	// sort in order of latitude
+	// POI that seem "closer" to the user (lower on the screen) will be drawn last (on top)
+	g_ptr_array_sort(pMap->m_pLocationSelectionArray, map_location_selection_latitude_sort_callback);
 
 	return TRUE;	// added
 }
 
-// add a Location to the selected list
+// remove a Location to the selected list
 gboolean map_location_selection_remove(map_t* pMap, gint nLocationID)
 {
 	gint i;
@@ -1255,42 +1300,3 @@
 	}
 	return FALSE;	// removed
 }
-
-// get an attribute from a selected location
-const gchar* map_location_selection_get_attribute(const map_t* pMap, const locationselection_t* pLocationSelection, const gchar* pszAttributeName)
-{
-	gint i;
-	for(i=0 ; i<pLocationSelection->m_pAttributesArray->len ; i++) {
-		locationattribute_t* pAttribute = g_ptr_array_index(pLocationSelection->m_pAttributesArray, i);
-		if(strcmp(pAttribute->m_pszName, pszAttributeName) == 0) {
-			return pAttribute->m_pszValue;
-		}
-	}
-	return NULL;
-}
-
-gboolean map_object_type_atoi(const gchar* pszName, gint* pnReturnObjectTypeID)
-{
-	gint i;
-	for(i=0 ; i<MAP_NUM_OBJECT_TYPES ; i++) {
-		if(strcmp(pszName, g_apszMapObjectTypeNames[i]) == 0) {
-			*pnReturnObjectTypeID = i;
-			return TRUE;
-		}
-	}
-	return FALSE;
-}
-
-gboolean map_layer_render_type_atoi(const gchar* pszName, gint* pnReturnRenderTypeID)
-{
-	gchar* g_apszMapRenderTypeNames[] = {"lines", "polygons", "line-labels", "polygon-labels"};
-
-	gint i;
-	for(i=0 ; i<G_N_ELEMENTS(g_apszMapRenderTypeNames) ; i++) {
-		if(strcmp(pszName, g_apszMapRenderTypeNames[i]) == 0) {
-			*pnReturnRenderTypeID = i;
-			return TRUE;
-		}
-	}
-	return FALSE;
-}

Index: map.h
===================================================================
RCS file: /cvs/cairo/roadster/src/map.h,v
retrieving revision 1.20
retrieving revision 1.21
diff -u -d -r1.20 -r1.21
--- map.h	6 Sep 2005 02:44:19 -0000	1.20
+++ map.h	14 Sep 2005 20:06:54 -0000	1.21
@@ -41,11 +41,12 @@
 #define MAP_OBJECT_TYPE_RIVER					(9)
 #define MAP_OBJECT_TYPE_LAKE					(10)
 #define MAP_OBJECT_TYPE_MISC_AREA				(11)
+#define MAP_OBJECT_TYPE_URBAN_AREA				(12)
 
-#define MAP_NUM_OBJECT_TYPES 					(12)
+#define MAP_NUM_OBJECT_TYPES 					(13)
 
 #define MAP_OBJECT_TYPE_FIRST					(1)
-#define MAP_OBJECT_TYPE_LAST					(11)
+#define MAP_OBJECT_TYPE_LAST					(12)
 
 //
 // Line CAP styles
@@ -103,7 +104,6 @@
 #define NUM_ZOOM_LEVELS					(5)
 #define MIN_ZOOM_LEVEL_FOR_LOCATIONS	(6)		// don't show POI above this level
 
-#include "layers.h"
 #include "scenemanager.h"
 
 // World space
@@ -202,9 +202,9 @@
 	GPtrArray		*m_pLocationSelectionArray;
 	GFreeList		*m_pLocationSelectionAllocator;
 
-	// Mutex and the data it controls (always lock before reading/writing)
-	//GMutex* m_pPixmapMutex;
 	GdkPixmap* m_pPixmap;
+
+	GPtrArray* m_pLayersArray;
 } map_t;
 
 typedef enum {
@@ -247,15 +247,14 @@
 	MAP_LAYER_RENDERTYPE_LINES,
 	MAP_LAYER_RENDERTYPE_POLYGONS,
 	MAP_LAYER_RENDERTYPE_LINE_LABELS,
-	MAP_LAYER_RENDERTYPE_POLYGON_LABELS
+	MAP_LAYER_RENDERTYPE_POLYGON_LABELS,
+	MAP_LAYER_RENDERTYPE_FILL,
 } EMapLayerRenderType;
 
 typedef struct {
 	gint nLayer;
 	gint nSubLayer;
 	EMapLayerRenderType eSubLayerRenderType;
-
-//	void (*pFunc)(map_t*, cairo_t*, rendermetrics_t*, GPtrArray*, sublayerstyle_t*, textlabelstyle_t*);
 } draworder_t;
 
 #define MAX_LOCATIONSELECTION_URLS	(5)
@@ -292,17 +291,21 @@
 gboolean map_new(map_t** ppMap, GtkWidget* pTargetWidget);
 
 // Gets and Sets
-guint16 map_get_zoomlevel(map_t* pMap);
-guint32 map_get_zoomlevel_scale(map_t* pMap);
+guint16 map_get_zoomlevel(const map_t* pMap);
+guint32 map_get_zoomlevel_scale(const map_t* pMap);
+
+gboolean map_can_zoom_in(const map_t* pMap);
+gboolean map_can_zoom_out(const map_t* pMap);
+
 void map_set_zoomlevel(map_t* pMap, guint16 uZoomLevel);
 //void map_get_render_metrics(rendermetrics_t* pMetrics);
 
 void map_set_redraw_needed(map_t* pMap, gboolean bNeeded);
-gboolean map_get_redraw_needed(map_t* pMap);
-guint32 map_get_scale(map_t* pMap);
+gboolean map_get_redraw_needed(const map_t* pMap);
+guint32 map_get_scale(const map_t* pMap);
 
 void map_set_centerpoint(map_t* pMap, const mappoint_t* pPoint);
-void map_get_centerpoint(map_t* pMap, mappoint_t* pReturnPoint);
+void map_get_centerpoint(const map_t* pMap, mappoint_t* pReturnPoint);
 void map_set_dimensions(map_t* pMap, const dimensions_t* pDimensions);
 
 // Conversions
@@ -320,15 +323,12 @@
 GdkPixmap* map_get_pixmap(map_t* pMap);
 void map_release_pixmap(map_t* pMap);
 
-void map_draw(map_t* pMap, gint nDrawFlags);
+void map_draw(map_t* pMap, GdkPixmap* pTargetPixmap, gint nDrawFlags);
 void map_add_track(map_t* pMap, gint hTrack);
 
 gboolean map_hit_test(map_t* pMap, mappoint_t* pMapPoint, maphit_t** ppReturnStruct);
 void map_hitstruct_free(map_t* pMap, maphit_t* pHitStruct);
 
-gboolean map_can_zoom_in(map_t* pMap);
-gboolean map_can_zoom_out(map_t* pMap);
-
 gboolean map_location_selection_add(map_t* pMap, gint nLocationID);
 gboolean map_location_selection_remove(map_t* pMap, gint nLocationID);
 

Index: map_draw_cairo.c
===================================================================
RCS file: /cvs/cairo/roadster/src/map_draw_cairo.c,v
retrieving revision 1.21
retrieving revision 1.22
diff -u -d -r1.21 -r1.22
--- map_draw_cairo.c	6 Sep 2005 02:44:19 -0000	1.21
+++ map_draw_cairo.c	14 Sep 2005 20:06:54 -0000	1.22
@@ -29,40 +29,39 @@
 #define ENABLE_LABEL_LIMIT_TO_ROAD				// take road line length into account when drawing labels!
 #define	ACCEPTABLE_LINE_LABEL_OVERDRAW_IN_PIXELS (15)	// XXX: make this a run-time variable
 #define ENABLE_DRAW_MAP_SCALE
+//#define ENABLE_MAP_DROP_SHADOW
 
-#define ENABLE_HACK_AROUND_CAIRO_LINE_CAP_BUG	// enable to ensure roads have rounded caps if the style dictates
+//#define ENABLE_HACK_AROUND_CAIRO_LINE_CAP_BUG	// enable to ensure roads have rounded caps if the style dictates
 												// supposedly fixed as of 1.0 but I haven't tested yet
 
 #define ROAD_FONT	"Free Sans" //Bitstream Vera Sans"
 #define AREA_FONT	"Free Sans" // "Bitstream Vera Sans"
[...1333 lines suppressed...]
-			//cairo_fill(pCairo);
-			cairo_restore(pCairo);
-
-			// claim the space this took up
-			scenemanager_claim_polygon(pMap->m_pSceneManager, aBoundingPolygon, 4);
-		}
-
-		cairo_restore(pCairo);
-
-		// claim the label (so it won't be drawn twice)
-		scenemanager_claim_label(pMap->m_pSceneManager, pszLabel);
-
-//         // success
-//         break;
-//     }
-//
-//     g_ptr_array_free(pPositionsPtrArray, FALSE);
-}
 */
 #endif

Index: map_draw_gdk.c
===================================================================
RCS file: /cvs/cairo/roadster/src/map_draw_gdk.c,v
retrieving revision 1.17
retrieving revision 1.18
diff -u -d -r1.17 -r1.18
--- map_draw_gdk.c	6 Sep 2005 02:44:19 -0000	1.17
+++ map_draw_gdk.c	14 Sep 2005 20:06:54 -0000	1.18
@@ -23,6 +23,8 @@
 
 #define MAX_GDK_LINE_SEGMENTS (2000)
 
+//#define ENABLE_MAP_GRAYSCALE_HACK
+
 #include <gdk/gdk.h>
 #include <gdk/gdkx.h>
 #include <gtk/gtk.h>
@@ -36,25 +38,35 @@
 #include "db.h"
 #include "road.h"
 #include "point.h"
-#include "layers.h"
+#include "map_style.h"
 #include "track.h"
 #include "locationset.h"
 #include "location.h"
 #include "scenemanager.h"
 
-static void map_draw_gdk_background(map_t* pMap, GdkPixmap* pPixmap);
-static void map_draw_gdk_layer_polygons(map_t* pMap, GdkPixmap* pPixmap, rendermetrics_t* pRenderMetrics, GPtrArray* pRoadsArray, layerstyle_t* pLayerStyle);
-static void map_draw_gdk_layer_lines(map_t* pMap, GdkPixmap* pPixmap, rendermetrics_t* pRenderMetrics, GPtrArray* pRoadsArray, layerstyle_t* pLayerStyle);
+//static void map_draw_gdk_background(map_t* pMap, GdkPixmap* pPixmap);
+static void map_draw_gdk_layer_polygons(map_t* pMap, GdkPixmap* pPixmap, rendermetrics_t* pRenderMetrics, GPtrArray* pRoadsArray, maplayerstyle_t* pLayerStyle);
+static void map_draw_gdk_layer_lines(map_t* pMap, GdkPixmap* pPixmap, rendermetrics_t* pRenderMetrics, GPtrArray* pRoadsArray, maplayerstyle_t* pLayerStyle);
+
+static void map_draw_gdk_layer_fill(map_t* pMap, GdkPixmap* pPixmap, rendermetrics_t* pRenderMetrics, maplayerstyle_t* pLayerStyle);
+
 static void map_draw_gdk_tracks(map_t* pMap, GdkPixmap* pPixmap, rendermetrics_t* pRenderMetrics);
 static void map_draw_gdk_locations(map_t* pMap, GdkPixmap* pPixmap, rendermetrics_t* pRenderMetrics);
 static void map_draw_gdk_locationset(map_t* pMap, GdkPixmap* pPixmap, rendermetrics_t* pRenderMetrics, locationset_t* pLocationSet, GPtrArray* pLocationsArray);
 
+//#define ENABLE_MAP_GRAYSCALE_HACK 	// just a little test.  black and white might be good for something
+
 void map_draw_gdk_set_color(GdkGC* pGC, color_t* pColor)
 {
 	GdkColor clr;
+
+#ifdef ENABLE_MAP_GRAYSCALE_HACK
+	clr.red = clr.green = clr.blue = ((pColor->m_fRed + pColor->m_fGreen + pColor->m_fBlue) / 3.0) * 65535;
+#else
 	clr.red = pColor->m_fRed * 65535;
 	clr.green = pColor->m_fGreen * 65535;
 	clr.blue = pColor->m_fBlue * 65535;
+#endif
 	gdk_gc_set_rgb_fg_color(pGC, &clr);
 }
 
@@ -62,26 +74,28 @@
 {
 	TIMER_BEGIN(maptimer, "BEGIN RENDER MAP (gdk)");
 
+	GdkGC* pGC = pMap->m_pTargetWidget->style->fg_gc[GTK_WIDGET_STATE(pMap->m_pTargetWidget)];
+
 	// 1. Save values (so we can restore them)
 	GdkGCValues gcValues;
-	gdk_gc_get_values(pMap->m_pTargetWidget->style->fg_gc[GTK_WIDGET_STATE(pMap->m_pTargetWidget)], &gcValues);
+	gdk_gc_get_values(pGC, &gcValues);
 
 	// 2. Drawing
-
-	// 2.1. Draw Background
-	if(nDrawFlags & DRAWFLAG_GEOMETRY) {
-		map_draw_gdk_background(pMap, pPixmap);
-	}
-
-	// 2.2. Render Layers
 	if(nDrawFlags & DRAWFLAG_GEOMETRY) {
 		gint i;
 
-		// draw list in reverse order (painter's algorithm: http://en.wikipedia.org/wiki/Painter's_algorithm )
-		for(i=g_pLayersArray->len-1 ; i>=0 ; i--) {
-			layer_t* pLayer = g_ptr_array_index(g_pLayersArray, i);
+		// 2.1. Draw Background
+//		map_draw_gdk_background(pMap, pPixmap);
 
-			if(pLayer->m_nDrawType == MAP_LAYER_RENDERTYPE_LINES) {
+		// 2.2. Draw layer list in reverse order (painter's algorithm: http://en.wikipedia.org/wiki/Painter's_algorithm )
+		for(i=pMap->m_pLayersArray->len-1 ; i>=0 ; i--) {
+			maplayer_t* pLayer = g_ptr_array_index(pMap->m_pLayersArray, i);
+
+			if(pLayer->m_nDrawType == MAP_LAYER_RENDERTYPE_FILL) {
+				map_draw_gdk_layer_fill(pMap, pPixmap,	pRenderMetrics,
+										 pLayer->m_paStylesAtZoomLevels[pRenderMetrics->m_nZoomLevel-1]);		// style
+			}
+			else if(pLayer->m_nDrawType == MAP_LAYER_RENDERTYPE_LINES) {
 				map_draw_gdk_layer_lines(pMap, pPixmap,	pRenderMetrics,
 										 pMap->m_apLayerData[pLayer->m_nDataSource]->m_pRoadsArray,				// data
 										 pLayer->m_paStylesAtZoomLevels[pRenderMetrics->m_nZoomLevel-1]);		// style
@@ -105,19 +119,19 @@
 	TIMER_END(maptimer, "END RENDER MAP (gdk)");
 }
 
-static void map_draw_gdk_background(map_t* pMap, GdkPixmap* pPixmap)
-{
-	GdkColor clr;
-	clr.red = 236/255.0 * 65535;
-	clr.green = 230/255.0 * 65535;
-	clr.blue = 230/255.0 * 65535;
-	gdk_gc_set_rgb_fg_color(pMap->m_pTargetWidget->style->fg_gc[GTK_WIDGET_STATE(pMap->m_pTargetWidget)], &clr);
-
-	gdk_draw_rectangle(pPixmap, pMap->m_pTargetWidget->style->fg_gc[GTK_WIDGET_STATE(pMap->m_pTargetWidget)],
-			TRUE, 0,0, pMap->m_MapDimensions.m_uWidth, pMap->m_MapDimensions.m_uHeight);
-}
+// static void map_draw_gdk_background(map_t* pMap, GdkPixmap* pPixmap)
+// {
+//     GdkColor clr;
+//     clr.red = 236/255.0 * 65535;
+//     clr.green = 230/255.0 * 65535;
+//     clr.blue = 230/255.0 * 65535;
+//     gdk_gc_set_rgb_fg_color(pMap->m_pTargetWidget->style->fg_gc[GTK_WIDGET_STATE(pMap->m_pTargetWidget)], &clr);
+//
+//     gdk_draw_rectangle(pPixmap, pMap->m_pTargetWidget->style->fg_gc[GTK_WIDGET_STATE(pMap->m_pTargetWidget)],
+//             TRUE, 0,0, pMap->m_MapDimensions.m_uWidth, pMap->m_MapDimensions.m_uHeight);
+// }
 
-static void map_draw_gdk_layer_polygons(map_t* pMap, GdkPixmap* pPixmap, rendermetrics_t* pRenderMetrics, GPtrArray* pRoadsArray, layerstyle_t* pLayerStyle)
+static void map_draw_gdk_layer_polygons(map_t* pMap, GdkPixmap* pPixmap, rendermetrics_t* pRenderMetrics, GPtrArray* pRoadsArray, maplayerstyle_t* pLayerStyle)
 {
 	mappoint_t* pPoint;
 	road_t* pRoad;
@@ -153,8 +167,8 @@
 				fMaxLon = max(pPoint->m_fLongitude,fMaxLon);
 				fMinLon = min(pPoint->m_fLongitude,fMinLon);
 
-				aPoints[iPoint].x = (gint)SCALE_X(pRenderMetrics, pPoint->m_fLongitude);
-				aPoints[iPoint].y = (gint)SCALE_Y(pRenderMetrics, pPoint->m_fLatitude);
+				aPoints[iPoint].x = pLayerStyle->m_nPixelOffsetX + (gint)SCALE_X(pRenderMetrics, pPoint->m_fLongitude);
+				aPoints[iPoint].y = pLayerStyle->m_nPixelOffsetY + (gint)SCALE_Y(pRenderMetrics, pPoint->m_fLatitude);
 			}
 
 			// rectangle overlap test
@@ -171,7 +185,15 @@
 	}
 }
 
-static void map_draw_gdk_layer_lines(map_t* pMap, GdkPixmap* pPixmap, rendermetrics_t* pRenderMetrics, GPtrArray* pRoadsArray, layerstyle_t* pLayerStyle)
+// useful for filling the screen with a color.  not much else.
+static void map_draw_gdk_layer_fill(map_t* pMap, GdkPixmap* pPixmap, rendermetrics_t* pRenderMetrics, maplayerstyle_t* pLayerStyle)
+{
+	map_draw_gdk_set_color(pMap->m_pTargetWidget->style->fg_gc[GTK_WIDGET_STATE(pMap->m_pTargetWidget)], &(pLayerStyle->m_clrPrimary));
+	gdk_draw_rectangle(pPixmap, pMap->m_pTargetWidget->style->fg_gc[GTK_WIDGET_STATE(pMap->m_pTargetWidget)],
+			TRUE, 0,0, pMap->m_MapDimensions.m_uWidth, pMap->m_MapDimensions.m_uHeight);
+}
+
+static void map_draw_gdk_layer_lines(map_t* pMap, GdkPixmap* pPixmap, rendermetrics_t* pRenderMetrics, GPtrArray* pRoadsArray, maplayerstyle_t* pLayerStyle)
 {
 	road_t* pRoad;
 	mappoint_t* pPoint;
@@ -181,17 +203,6 @@
 	if(pLayerStyle->m_fLineWidth <= 0.0) return;			// Don't draw invisible lines
 	if(pLayerStyle->m_clrPrimary.m_fAlpha == 0.0) return;	// invisible?  (not that we respect it in gdk drawing anyway)
 
-	// Use GDK dash style if ANY dash pattern is set
-	gint nDashStyle = GDK_LINE_SOLID;
-	if(g_aDashStyles[pLayerStyle->m_nDashStyle].m_nDashCount > 1) {
-		nDashStyle = GDK_LINE_ON_OFF_DASH;
-
-		gdk_gc_set_dashes(pMap->m_pTargetWidget->style->fg_gc[GTK_WIDGET_STATE(pMap->m_pTargetWidget)],
-				0, /* offset to start at */
-				g_aDashStyles[pLayerStyle->m_nDashStyle].m_panDashList,
-				g_aDashStyles[pLayerStyle->m_nDashStyle].m_nDashCount);
-	}
-
 	// Translate generic cap style into GDK constant
 	gint nCapStyle;
 	if(pLayerStyle->m_nCapStyle == MAP_CAP_STYLE_ROUND) {
@@ -204,6 +215,18 @@
 	// Convert to integer width.  Ouch!
 	gint nLineWidth = (gint)(pLayerStyle->m_fLineWidth);
 
+	// Use GDK dash style if ANY dash pattern is set
+	gint nDashStyle = GDK_LINE_SOLID;
+	if(pLayerStyle->m_pDashStyle != NULL) {
+		gdk_gc_set_dashes(pMap->m_pTargetWidget->style->fg_gc[GTK_WIDGET_STATE(pMap->m_pTargetWidget)],
+				0, /* offset to start at */
+				pLayerStyle->m_pDashStyle->m_panDashList,
+				pLayerStyle->m_pDashStyle->m_nDashCount);
+		
+		nDashStyle = GDK_LINE_ON_OFF_DASH;
+		// further set line attributes below...
+	}
+
 	// Set line style
 	gdk_gc_set_line_attributes(pMap->m_pTargetWidget->style->fg_gc[GTK_WIDGET_STATE(pMap->m_pTargetWidget)],
 			   nLineWidth, nDashStyle, nCapStyle, GDK_JOIN_ROUND);
@@ -236,8 +259,8 @@
 				fMaxLon = max(pPoint->m_fLongitude,fMaxLon);
 				fMinLon = min(pPoint->m_fLongitude,fMinLon);
 
-				aPoints[iPoint].x = (gint)SCALE_X(pRenderMetrics, pPoint->m_fLongitude);
-				aPoints[iPoint].y = (gint)SCALE_Y(pRenderMetrics, pPoint->m_fLatitude);
+				aPoints[iPoint].x = pLayerStyle->m_nPixelOffsetX + (gint)SCALE_X(pRenderMetrics, pPoint->m_fLongitude);
+				aPoints[iPoint].y = pLayerStyle->m_nPixelOffsetY + (gint)SCALE_Y(pRenderMetrics, pPoint->m_fLatitude);
 			}
 
 			// basic rectangle overlap test

--- NEW FILE: map_style.c ---
/***************************************************************************
 *  		map_style.c
 *
 *  Copyright 2005
 *  Ian McIntosh <ian_mcintosh at linuxadvocate.org>
 *  Nathan Fredrickson <nathan at silverorange.com>
 ****************************************************************************/

/*
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Library General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include <config.h>
#include <cairo.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include "main.h"
#include "map_style.h"

// utility functions for iterating through lists of XML things
#define EACH_ATTRIBUTE_OF_NODE(a,n)		(a) = (n)->properties ; (a) != NULL ; (a) = (a)->next
#define EACH_CHILD_OF_NODE(c,n)			(c) = (n)->children ; (c) != NULL ; (c) = (c)->next

GHashTable* g_pConstantsHash = NULL;	// XXX: globals suck. :(

static maplayer_t* map_style_new_layer();

static void map_style_load_from_file(map_t* pMap, const gchar* pszFileName);
static void map_style_parse_file(map_t* pMap, xmlDocPtr pDoc, xmlNodePtr pNode);
static void map_style_parse_layers(map_t* pMap, xmlDocPtr pDoc, xmlNodePtr pParentNode);
static void map_style_parse_layer(map_t* pMap, xmlDocPtr pDoc, xmlNodePtr pNode);
static void map_style_parse_layer_property(map_t* pMap, xmlDocPtr pDoc, maplayer_t *pLayer, xmlNodePtr pNode);
static void map_style_parse_constants(map_t* pMap, xmlDocPtr pDoc, xmlNodePtr pParentNode);
static void map_style_parse_constant(map_t* pMap, xmlDocPtr pDoc, xmlNodePtr pNode);

// Debugging
static void map_style_print_layer(maplayer_t *layer);
static void map_style_print_color(color_t *color);


// Callbacks
// void map_style_callback_generic_error(void * ctx, const char * msg, ...)
// {
//     g_print("map_style_callback_generic_error: %s\n", msg);
// }
//
// void map_style_callback_structured_error(void * userData, xmlErrorPtr error)
// {
//     g_print("map_style_callback_structured_error\n");
// }

//
// Functions
//
void map_style_init(void)
{
	/* init libxml */
	LIBXML_TEST_VERSION ;
}

void map_style_deinit(void)
{
	xmlCleanupParser();
}

void map_style_load(map_t* pMap, const gchar* pszFileName)
{
	g_assert(pMap != NULL);
	g_assert(pszFileName != NULL);
	
	if(pMap->m_pLayersArray != NULL) {
		g_warning("reloading styles currently leaks memory so... don't do it very often :)\n");
		pMap->m_pLayersArray = NULL;
	}

	pMap->m_pLayersArray = g_ptr_array_new();

	if(g_pConstantsHash != NULL) {
		g_hash_table_destroy(g_pConstantsHash);		// NOTE: This frees all keys and values for us.
	}
	g_pConstantsHash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);	// same as above
	map_style_load_from_file(pMap, pszFileName);
}

//
// Constants
//
gboolean map_style_constant_get(const gchar* pszName, gchar** ppszReturnValue)
{
	g_assert(pszName);
	g_assert(ppszReturnValue);
	g_assert(*ppszReturnValue == NULL);	// require pointer to NULL pointer

	gchar* pszValue = g_hash_table_lookup(g_pConstantsHash, pszName);
	if(pszValue != NULL) {
		*ppszReturnValue = pszValue;
//		g_print("map_style_constant_get(%s) == TRUE\n", pszName);
		return TRUE;
	}
//	g_print("map_style_constant_get(%s) == FALSE\n", pszName);
	return FALSE;
}

void map_style_constant_set(const gchar* pszName, const gchar* pszValue)
{
	g_assert(pszName != NULL);
	g_assert(pszValue != NULL);

//	g_print("map_style_constant_set(%s, %s)\n", pszName, pszValue);
	// NOTE: if there was an existing key with this name, the old key and old value will get auto-freed by glib
	g_hash_table_replace(g_pConstantsHash, (gchar*)pszName, (gchar*)pszValue);
}

static maplayer_t* map_style_new_layer()
{
	maplayer_t *pLayer = g_new0(maplayer_t, 1);

	gint i;
	for(i=0 ; i<NUM_ZOOM_LEVELS ; i++) {
		 maplayerstyle_t* pLayerStyle = g_new0(maplayerstyle_t, 1);

		 pLayerStyle->m_nCapStyle = MAP_CAP_STYLE_DEFAULT;

		 // XXX: need to init the maplayerstyle_t's?
		 pLayer->m_paStylesAtZoomLevels[i] = pLayerStyle;
	}

	return pLayer;
}

gboolean map_style_parse_zoomlevel(const gchar* pszZoomLevel, gint* pnReturnMinZoomLevel, gint* pnReturnMaxZoomLevel)
{
	g_assert(pszZoomLevel != NULL);
	g_assert(pnReturnMinZoomLevel != NULL);
	g_assert(pnReturnMaxZoomLevel != NULL);

	gchar* pszStr = g_strdup(pszZoomLevel);
	gboolean bReturn = TRUE;

	// Support the following formats:
	// "4-5", "4"

	gint nMin, nMax;
	gchar* pszSeparator = g_strrstr(pszStr, "-");
	if(pszSeparator != NULL) {
		// Parse a "5-8"
		*pszSeparator = '\0';
		nMin = atoi(pszStr);
		nMax = atoi(pszSeparator+1);
	}
	else {
		// For the single value format, we return the same value for both min and max.
		nMin = nMax = atoi(pszStr);
	}

	if(nMin < MIN_ZOOM_LEVEL || nMin > MAX_ZOOM_LEVEL) {
		g_warning("zoom-level '%s' out of valid range (%d to %d)\n", pszZoomLevel, MIN_ZOOM_LEVEL, MAX_ZOOM_LEVEL);
		bReturn = FALSE;
	}
	else if(nMax < MIN_ZOOM_LEVEL || nMax > MAX_ZOOM_LEVEL) {
		g_warning("zoom-level '%s' out of valid range (%d to %d)\n", pszZoomLevel, MIN_ZOOM_LEVEL, MAX_ZOOM_LEVEL);
		bReturn = FALSE;
	}
	else {
		*pnReturnMinZoomLevel = nMin;
		*pnReturnMaxZoomLevel = nMax;
	}
	g_free(pszStr);
	return bReturn;
}

#define MAX_VALUES_IN_DASH_STYLE	(20)
#define MAX_DASH_LENGTH		(50)
#define MIN_DASH_LENGTH		(1)

dashstyle_t* dashstyle_new(gdouble* pafValues, gint nValueCount)
{
	g_assert(pafValues != NULL);

	g_assert(nValueCount >= 2);

	dashstyle_t* pNewDashStyle = g_new0(dashstyle_t, 1);

	// The list of gdouble's can just be copied in
	pNewDashStyle->m_pafDashList = g_malloc(sizeof(gdouble) * nValueCount);
	memcpy(pNewDashStyle->m_pafDashList, pafValues, sizeof(gdouble) * nValueCount);

	// The list of int8's has to be converted list
	pNewDashStyle->m_panDashList = g_malloc(sizeof(gint8) * nValueCount);
	gint i;
	for(i=0 ; i<nValueCount ; i++) {
		gint8 nVal = (gint)pafValues[i];
		
		// 
		nVal = MIN(nVal, MAX_DASH_LENGTH);
		nVal = MAX(nVal, MIN_DASH_LENGTH);

		pNewDashStyle->m_panDashList[i] = nVal;
	}

	pNewDashStyle->m_nDashCount = nValueCount;

	return pNewDashStyle;
}

gboolean map_style_parse_dashstyle(const gchar* pszDashStyle, dashstyle_t** ppReturnDashStyle)
{
	g_assert(pszDashStyle);
	g_assert(ppReturnDashStyle);
	g_assert(*ppReturnDashStyle == NULL);		// require pointer to NULL pointer

	// Parse a string that looks like this: "3.5 5" etc.

	gdouble afValues[MAX_VALUES_IN_DASH_STYLE];
	gint nValueCount = 0;

	const gchar* pszWorker = pszDashStyle;
	while(*pszWorker != '\0' && nValueCount < MAX_VALUES_IN_DASH_STYLE) {
		// Read one value
		gdouble fNew = atof(pszWorker);		// NOTE: atof() stops after it hits a bad character (eg. a space)
		if(fNew != 0.0) {
			// Add to end of list
			afValues[nValueCount] = fNew;
			nValueCount++;

			// Skip to next whitespace
			while(*pszWorker != ' ' && *pszWorker != '\0') pszWorker++;		// XXX: use utf8 functions?
			// Now skip the whitespace!
			while(*pszWorker == ' ') pszWorker++;
		}
		else {
			// Done.
			break;
		}
	}

	if(nValueCount >= 2) {	// a dash of 1 doesn't make sense
		// Success.
		*ppReturnDashStyle = dashstyle_new(afValues, nValueCount);
		return TRUE;
	}
	return FALSE;
}

gchar* get_attribute_value(const xmlDocPtr pDoc, const xmlAttrPtr pAttribute)
{
	g_assert(pDoc != NULL);
	g_assert(pAttribute != NULL);

	// allocate a new glib string for this value.  free xmllib string.
	gchar* pszTmp = xmlNodeListGetString(pDoc, pAttribute->xmlChildrenNode, 1);
	gchar* pszValue = g_strdup((pszTmp) ? pszTmp : "");
	xmlFree(pszTmp);
	return pszValue;
}

static void map_style_load_from_file(map_t* pMap, const gchar* pszFileName)
{
	g_assert(pMap != NULL);
	g_assert(pszFileName != NULL);

	xmlDocPtr pDoc = NULL;
	xmlNodePtr pRootElement = NULL;

	// Load style definition file
	// try source directory first (good for development)
	gchar* pszPath = g_strdup_printf(PACKAGE_SOURCE_DIR"/data/%s", pszFileName);
	pDoc = xmlReadFile(pszPath, NULL, 0);
	g_free(pszPath);

	// try alternate path
	if(pDoc == NULL) {
		pszPath = g_strdup_printf(PACKAGE_DATA_DIR"/data/%s", pszFileName);
		pDoc = xmlReadFile(pszPath, NULL, 0);
		g_free(pszPath);

	}

	// still NULL?
	if(pDoc == NULL) {
		g_message("cannot load map style file %s\n", pszFileName);
//			gtk_exit(0);
	}
	else {
		pRootElement = xmlDocGetRootElement(pDoc);

		map_style_parse_file(pMap, pDoc, pRootElement);

		xmlFreeDoc(pDoc);
	}
}


/*****************************************************************
 * map_style_parse_* functions for parsing the xml
 *****************************************************************/
static void map_style_parse_file(map_t* pMap, xmlDocPtr pDoc, xmlNodePtr pParentNode)
{
	g_assert(pMap != NULL);
	g_assert(pDoc != NULL);
	g_assert(pParentNode != NULL);

	// Top-level node.
	if(pParentNode->type == XML_ELEMENT_NODE) {
		xmlNodePtr pChildNode = NULL;
		for(EACH_CHILD_OF_NODE(pChildNode, pParentNode)) {
			if(strcmp(pChildNode->name, "layers") == 0) {
				map_style_parse_layers(pMap, pDoc, pChildNode);
			}
			else if(strcmp(pChildNode->name, "constants") == 0) {
				map_style_parse_constants(pMap, pDoc, pChildNode);
			}
		}
	}
}

static void map_style_parse_layers(map_t* pMap, xmlDocPtr pDoc, xmlNodePtr pParentNode)
{
	g_assert(pMap != NULL);
	g_assert(pDoc != NULL);
	g_assert(pParentNode != NULL);

//	g_print("map_style_parse_layers()\n");
	xmlNodePtr pChildNode = NULL;

	// iterate over "layer" objects
	for(EACH_CHILD_OF_NODE(pChildNode, pParentNode)) {
	//for(pChildNode = pParentNode->children; pChildNode != NULL; pChildNode = pChildNode->next) {
		if(pChildNode->type == XML_ELEMENT_NODE && strcmp(pChildNode->name, "layer") == 0) {
			map_style_parse_layer(pMap, pDoc, pChildNode);
		}
	}
}

static void map_style_parse_constants(map_t* pMap, xmlDocPtr pDoc, xmlNodePtr pParentNode)
{
	g_assert(pMap != NULL);
	g_assert(pDoc != NULL);
	g_assert(pParentNode != NULL);

//	g_print("map_style_parse_constants()\n");
	xmlNodePtr pChildNode = NULL;

	for(EACH_CHILD_OF_NODE(pChildNode, pParentNode)) {
		if(pChildNode->type == XML_ELEMENT_NODE && strcmp(pChildNode->name, "constant") == 0) {
			map_style_parse_constant(pMap, pDoc, pChildNode);
		}
	}
}

static void map_style_parse_constant(map_t* pMap, xmlDocPtr pDoc, xmlNodePtr pNode)
{
	g_assert(pMap != NULL);
	g_assert(pDoc != NULL);
	g_assert(pNode != NULL);

//	g_print("map_style_parse_constant()\n");
	xmlAttrPtr pAttribute = NULL;

	gchar* pszName = NULL;
	gchar* pszValue = NULL;

	for(EACH_ATTRIBUTE_OF_NODE(pAttribute, pNode)) {
		if(strcmp(pAttribute->name, "name") == 0) {
			g_free(pszName);
			pszName = get_attribute_value(pDoc, pAttribute);
		}
		else if(strcmp(pAttribute->name, "value") == 0) {
			g_free(pszValue);
			pszValue = get_attribute_value(pDoc, pAttribute);
		}
	}

	if(pszName != NULL && pszValue != NULL) {
		// duplicate strings to keep long-term in the hash table
		map_style_constant_set(g_strdup(pszName), g_strdup(pszValue));		
	}
	g_free(pszName);
	g_free(pszValue);
//	g_free(pszName);
//	g_free(pszValue);
}

static void map_style_parse_layer(map_t* pMap, xmlDocPtr pDoc, xmlNodePtr pNode)
{
	g_assert(pMap != NULL);
	g_assert(pDoc != NULL);
	g_assert(pNode != NULL);

	xmlAttrPtr pAttribute = NULL;
	gint i;

	// create new layer
	maplayer_t *pLayer = map_style_new_layer();

	// read attributes of the 'layer' node
	for(EACH_ATTRIBUTE_OF_NODE(pAttribute, pNode)) {
		if(strcmp(pAttribute->name, "data-source") == 0) {
			gchar* pszDataSource = xmlNodeListGetString(pDoc, pAttribute->xmlChildrenNode, 1);

			if(!map_object_type_atoi(pszDataSource, &(pLayer->m_nDataSource))) {
				g_error("bad data source name %s\n", pszDataSource);
			}
		}
		else if(strcmp(pAttribute->name, "draw-type") == 0) {
			gchar* pszDrawType = xmlNodeListGetString(pDoc, pAttribute->xmlChildrenNode, 1);

			if(!map_layer_render_type_atoi(pszDrawType, &(pLayer->m_nDrawType))) {
				g_error("bad layer draw type name %s\n", pszDrawType);
			}
		}
	}

	// read children of the 'layer' node
	xmlNodePtr pChild = NULL;
	for(EACH_CHILD_OF_NODE(pChild, pNode)) {
		if(strcmp(pChild->name, "property") == 0) {
			map_style_parse_layer_property(pMap, pDoc, pLayer, pChild);
		}
	}

	// add it to list
	g_ptr_array_add(pMap->m_pLayersArray, pLayer);

//	map_style_print_layer(pLayer);
}

static void
map_style_parse_layer_property(map_t* pMap, xmlDocPtr pDoc, maplayer_t *pLayer, xmlNodePtr pNode)
{
	g_assert(pMap != NULL);
	g_assert(pLayer != NULL);
	g_assert(pNode != NULL);

	gchar* pszName = NULL;
	gchar* pszValue = NULL;
	gchar* pszZoomLevel = NULL;

	// Read 'name', 'value', and 'level' attributes of this property
	xmlAttrPtr pAttribute = NULL;
	for(EACH_ATTRIBUTE_OF_NODE(pAttribute, pNode)) {
		if(strcmp(pAttribute->name, "name") == 0) {
			g_free(pszName);
			pszName = get_attribute_value(pDoc, pAttribute);
		}
		else if(strcmp(pAttribute->name, "value") == 0) {
			g_free(pszValue);
			pszValue = get_attribute_value(pDoc, pAttribute);
		}
		else if((strcmp(pAttribute->name, "zoom-level") == 0) || (strcmp(pAttribute->name, "zoom-levels") == 0)) {
			g_free(pszZoomLevel);
			pszZoomLevel = get_attribute_value(pDoc, pAttribute);
		}
	}

	gint nMinZoomLevel = MIN_ZOOM_LEVEL;
	gint nMaxZoomLevel = MAX_ZOOM_LEVEL;
	if(pszZoomLevel != NULL) {
		map_style_parse_zoomlevel(pszZoomLevel, &nMinZoomLevel, &nMaxZoomLevel);
	}

	// If the 'value' is the name of a constant, replace it with the value of the constant
	gchar* pszConstantValue = NULL;
	if(map_style_constant_get(pszValue, &pszConstantValue)) {
		g_free(pszValue);
		pszValue = g_strdup(pszConstantValue);
	}

	if(pszName != NULL && pszValue != NULL) {
		gint i;
		if(strcmp(pszName, "line-width") == 0) {
			for(i = nMinZoomLevel - 1; i < nMaxZoomLevel ; i++) {
				pLayer->m_paStylesAtZoomLevels[i]->m_fLineWidth = (gdouble)atof(pszValue);
			}
		}
		else if(strcmp(pszName, "line-width") == 0) {
			for(i = nMinZoomLevel - 1; i < nMaxZoomLevel ; i++) {
				pLayer->m_paStylesAtZoomLevels[i]->m_fLineWidth = (gdouble)atof(pszValue);
			}
		}
		else if(strcmp(pszName, "pixel-offset-x") == 0) {
			for(i = nMinZoomLevel - 1; i < nMaxZoomLevel ; i++) {
				pLayer->m_paStylesAtZoomLevels[i]->m_nPixelOffsetX = (gint)atoi(pszValue);
			}
		}
		else if(strcmp(pszName, "pixel-offset-y") == 0) {
			for(i = nMinZoomLevel - 1; i < nMaxZoomLevel ; i++) {
				pLayer->m_paStylesAtZoomLevels[i]->m_nPixelOffsetY = (gint)atoi(pszValue);
			}
		}
		else if(strcmp(pszName, "color") == 0) {
			for(i = nMinZoomLevel - 1; i < nMaxZoomLevel ; i++) {
				util_parse_hex_color(pszValue, &(pLayer->m_paStylesAtZoomLevels[i]->m_clrPrimary));
			}
		}
		else if(strcmp(pszName, "halo-size") == 0) {
			for(i = nMinZoomLevel - 1; i < nMaxZoomLevel ; i++) {
				pLayer->m_paStylesAtZoomLevels[i]->m_fHaloSize = (gdouble)atof(pszValue);
			}
		}
		else if(strcmp(pszName, "dash-pattern") == 0) {
			for(i = nMinZoomLevel - 1; i < nMaxZoomLevel ; i++) {
				// NOTE: unlike all the atoi() calls in the loops, we actually DO need to parse this multiple times because each
				// layer should have its own copy
				dashstyle_t* pNewDashStyle = NULL;
				if(map_style_parse_dashstyle(pszValue, &pNewDashStyle)) {
					pLayer->m_paStylesAtZoomLevels[i]->m_pDashStyle = pNewDashStyle;
				}
				else {
					g_warning("bad dash style '%s' (should look like \"5.0 3.5\"\n", pszValue);
					break;
				}
			}
		}
		else if(strcmp(pszName, "bold") == 0) {
			gboolean bBold = (strcmp(pszValue, "yes") == 0);
			for(i = nMinZoomLevel - 1; i < nMaxZoomLevel ; i++) {
				pLayer->m_paStylesAtZoomLevels[i]->m_bFontBold = bBold;
			}
		}
		else if(strcmp(pszName, "halo-color") == 0) {
			for(i = nMinZoomLevel - 1; i < nMaxZoomLevel ; i++) {
				util_parse_hex_color(pszValue, &(pLayer->m_paStylesAtZoomLevels[i]->m_clrHalo));
			}
		}
		else if(strcmp(pszName, "font-size") == 0) {
			for(i = nMinZoomLevel - 1; i < nMaxZoomLevel ; i++) {
				pLayer->m_paStylesAtZoomLevels[i]->m_fFontSize = (gdouble)atof(pszValue);
			}
		}
//         else if(strcmp(pszName, "join-style") == 0) {
//             for(i = nMinZoomLevel - 1; i < nMaxZoomLevel ; i++) {
//                 if(strcmp(pszValue, "mitre") == 0) {
//                     pLayer->m_paStylesAtZoomLevels[i]->m_nJoinStyle = CAIRO_LINE_JOIN_MITER;
//                 }
//                 else if(strcmp(pszValue, "round") == 0) {
//                     pLayer->m_paStylesAtZoomLevels[i]->m_nJoinStyle = CAIRO_LINE_JOIN_ROUND;
//                 }
//             }
//         }
		else if(strcmp(pszName, "line-cap") == 0) {
			if(strcmp(pszValue, "square") == 0) {
				for(i = nMinZoomLevel - 1; i < nMaxZoomLevel ; i++) {
					pLayer->m_paStylesAtZoomLevels[i]->m_nCapStyle = MAP_CAP_STYLE_SQUARE;
				}
			}
			else {
				if(strcmp(pszValue, "round") != 0) { g_warning("bad value for line-cap found: '%s' (valid options are 'square' and 'round')\n", pszValue); }

				for(i = nMinZoomLevel - 1; i < nMaxZoomLevel ; i++) {
					pLayer->m_paStylesAtZoomLevels[i]->m_nCapStyle = MAP_CAP_STYLE_ROUND;
				}
			}
		}
	}
	g_free(pszName);
	g_free(pszValue);
	g_free(pszZoomLevel);
}

/******************************************************************
 * map_style_print_* functions for debugging
 *****************************************************************/
static void
map_style_print_color(color_t* pColor)
{
	g_assert(pColor != NULL);
	g_print("color: %3.2f, %3.2f, %3.2f, %3.2f\n", pColor->m_fRed, pColor->m_fGreen, pColor->m_fBlue, pColor->m_fAlpha);
}

/*
  	color_t m_clrPrimary;	// Color used for polygon fill or line stroke
	gdouble m_fLineWidth;

	gint m_nJoinStyle;
	gint m_nCapStyle;

	gint m_nDashStyle;

	// XXX: switch to this:
	//dashstyle_t m_pDashStyle;	// can be NULL

	// Used just for text
	gdouble m_fFontSize;
	gboolean m_bBold;
	gdouble m_fHaloSize;	// actually a stroke width
	color_t m_clrHalo;
*/

static void
map_style_print_layer(maplayer_t *pLayer)
{
	g_assert(pLayer != NULL);

	int i;
	for(i = 0 ; i < NUM_ZOOM_LEVELS ; i++) {
		g_print("\nzoom level %d\n", i+1);
		maplayerstyle_t* pStyle = pLayer->m_paStylesAtZoomLevels[i];

		g_print("  line width: %f\n", pStyle->m_fLineWidth);
		g_print("  primary "); map_style_print_color(&(pStyle->m_clrPrimary));
		g_print("  join style: %d\n", pStyle->m_nJoinStyle);
		g_print("  cap style: %d\n", pStyle->m_nCapStyle);
		//g_print("  dash style: %d\n", pStyle->m_nDashStyle);
	}
}

--- NEW FILE: map_style.h ---
/***************************************************************************
 *            map_style.h
 *
 *  Copyright 2005
 *  Ian McIntosh <ian_mcintosh at linuxadvocate.org>
 *  Nathan Fredrickson <nathan at silverorange.com>
 ****************************************************************************/

/*
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Library General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#ifndef _MAP_STYLE_H_
#define _MAP_STYLE_H_

#include <gtk/gtk.h>

G_BEGIN_DECLS

#include "main.h"
#include "map.h"

typedef struct dashstyle {
	gdouble* m_pafDashList;	// the dashes, as an array of gdouble's (for Cairo)
	gint8* m_panDashList;	// the dashes, as an array of gint8's (for GDK)
	gint m_nDashCount;		// how many
} dashstyle_t;

// defines the look of a layer
typedef struct layerstyle {
	color_t m_clrPrimary;	// Color used for polygon fill or line stroke
	gdouble m_fLineWidth;

	gint m_nJoinStyle;
	gint m_nCapStyle;
	
	dashstyle_t* m_pDashStyle;

	// XXX: switch to this:
	//dashstyle_t m_pDashStyle;	// can be NULL

	// Used just for text
	gdouble m_fFontSize;
	gboolean m_bFontBold;
	gdouble m_fHaloSize;	// actually a stroke width
	color_t m_clrHalo;
	gint m_nPixelOffsetX;
	gint m_nPixelOffsetY;
} maplayerstyle_t;

typedef struct layer {
	gint m_nDataSource;		// which data to use (lakes, roads...)
	gint m_nDrawType;		// as lines, polygons, etc.

	// A layer has a style for each zoomlevel
	maplayerstyle_t* m_paStylesAtZoomLevels[ NUM_ZOOM_LEVELS ];
} maplayer_t;

//extern layer_t * g_aLayers[NUM_LAYERS+1];
//extern GPtrArray* g_pLayersArray;

void map_style_init(void);
void map_style_deinit(void);

void map_style_load(map_t* pMap, const gchar* pszFileName);
void map_style_reload(map_t* pMap, const gchar* pszFileName);

G_END_DECLS

#endif /* _MAP_STYLE_H_ */

--- prefs.c DELETED ---

--- prefs.h DELETED ---

Index: road.c
===================================================================
RCS file: /cvs/cairo/roadster/src/road.c,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -d -r1.5 -r1.6
--- road.c	6 Sep 2005 02:44:19 -0000	1.5
+++ road.c	14 Sep 2005 20:06:54 -0000	1.6
@@ -251,7 +251,7 @@
 gboolean road_suffix_atoi(const gchar* pszSuffix, gint* pReturnSuffixID)
 {
 	gint i;
-	for(i=0 ; i<NUM_ELEMS(g_RoadNameSuffixLookup) ; i++) {
+	for(i=0 ; i<G_N_ELEMENTS(g_RoadNameSuffixLookup) ; i++) {
 		if(g_ascii_strcasecmp(pszSuffix, g_RoadNameSuffixLookup[i].m_pszName) == 0) {
 			*pReturnSuffixID = g_RoadNameSuffixLookup[i].m_nID;
 			return TRUE;

Index: scenemanager.c
===================================================================
RCS file: /cvs/cairo/roadster/src/scenemanager.c,v
retrieving revision 1.13
retrieving revision 1.14
diff -u -d -r1.13 -r1.14
--- scenemanager.c	6 Sep 2005 02:44:19 -0000	1.13
+++ scenemanager.c	14 Sep 2005 20:06:54 -0000	1.14
@@ -88,7 +88,7 @@
 		// else go on to test below
 	}
 	else if(nFlags & SCENEMANAGER_FLAG_PARTLY_ON_SCREEN) {
-		// one point must be withing screen box
+		// one point must be withing screen box   XXX: handle polygon bigger than window case?
 		gint i;
 		gboolean bFound = FALSE;
 		for(i=0 ; i<nNumPoints ; i++) {

Index: search.c
===================================================================
RCS file: /cvs/cairo/roadster/src/search.c,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -d -r1.5 -r1.6
--- search.c	28 Mar 2005 18:49:50 -0000	1.5
+++ search.c	14 Sep 2005 20:06:54 -0000	1.6
@@ -44,7 +44,7 @@
 	while(*pReader != '\0') {
 		if(g_ascii_isspace(*pReader)) {
 			if(g_ascii_isspace(*(pReader+1)) || *(pReader+1) == '\0') {
-				// don't copy this character (space) if the next one is a space also
+				// don't copy this character (space) if the next one is a space
 				// or if it's the last character
 			}
 			else {
@@ -72,9 +72,7 @@
 
 	gint nNumber = 0;
 
-	// remove double spaces
 	while(*pReader != '\0') {
-		
 		if(g_ascii_isdigit(*pReader)) {
 			nNumber *= 10;
 			nNumber += g_ascii_digit_value(*pReader);

Index: search_road.c
===================================================================
RCS file: /cvs/cairo/roadster/src/search_road.c,v
retrieving revision 1.21
retrieving revision 1.22
diff -u -d -r1.21 -r1.22
--- search_road.c	6 Sep 2005 02:44:19 -0000	1.21
+++ search_road.c	14 Sep 2005 20:06:54 -0000	1.22
@@ -34,7 +34,7 @@
 #include "search_road.h"
 #include "road.h"
 
-#define ROAD_RESULT_SUGGESTED_ZOOMLEVEL		(8)
+#define ROAD_RESULT_SUGGESTED_ZOOMLEVEL		(4)
 
 typedef struct {
 	gint m_nNumber;			// house number	eg. 51

Index: searchwindow.c
===================================================================
RCS file: /cvs/cairo/roadster/src/searchwindow.c,v
retrieving revision 1.19
retrieving revision 1.20
diff -u -d -r1.19 -r1.20
--- searchwindow.c	28 Aug 2005 22:23:14 -0000	1.19
+++ searchwindow.c	14 Sep 2005 20:06:54 -0000	1.20
@@ -22,6 +22,7 @@
  */
 
 #include <gtk/gtk.h>
+#include <glib-object.h>
 #include <glade/glade.h>
 
 #ifdef HAVE_CONFIG_H
@@ -33,18 +34,21 @@
 #include "search_location.h"
 #include "mainwindow.h"
 #include "searchwindow.h"
-
-#define GTK_PROCESS_MAINLOOP  while (gtk_events_pending ()) { gtk_main_iteration (); }
+#include "util.h"
+#include "gui.h"
 
 #define RESULTLIST_COLUMN_NAME 	0	// visible data
-#define RESULTLIST_LATITUDE	1
+#define RESULTLIST_LATITUDE		1
 #define RESULTLIST_LONGITUDE	2
-#define RESULTLIST_DISTANCE	3
+#define RESULTLIST_DISTANCE		3
 #define RESULTLIST_ZOOMLEVEL	4
 #define RESULTLIST_CLICKABLE	5
 
 #define MAGIC_GTK_NO_SORT_COLUMN (-2)	// why -2?  dunno.  is there a real define for this?  dunno.
 
+#define SEARCHWINDOW_RESULT_FORMAT	("<span size='small'>%s</span>")
+#define SEARCHWINDOW_INFO_FORMAT	("<span size='small'><i>%s</i></span>")
+
 struct {
 	GtkEntry* m_pSearchEntry;		// search text box (on the toolbar)
 	GtkButton* m_pSearchButton;		// search button (on the toolbar)
@@ -60,9 +64,9 @@
 
 void searchwindow_init(GladeXML* pGladeXML)
 {
-	g_SearchWindow.m_pSearchEntry 		= GTK_ENTRY(glade_xml_get_widget(pGladeXML, "searchentry"));			g_return_if_fail(g_SearchWindow.m_pSearchEntry != NULL);	
-	g_SearchWindow.m_pSearchButton		= GTK_BUTTON(glade_xml_get_widget(pGladeXML, "searchbutton"));			g_return_if_fail(g_SearchWindow.m_pSearchButton != NULL);	
-	g_SearchWindow.m_pResultsTreeView	= GTK_TREE_VIEW(glade_xml_get_widget(pGladeXML, "searchresultstreeview"));	g_return_if_fail(g_SearchWindow.m_pResultsTreeView != NULL);	
+	GLADE_LINK_WIDGET(pGladeXML, g_SearchWindow.m_pSearchEntry, GTK_ENTRY, "searchentry");
+	GLADE_LINK_WIDGET(pGladeXML, g_SearchWindow.m_pSearchButton, GTK_BUTTON, "searchbutton");
+	GLADE_LINK_WIDGET(pGladeXML, g_SearchWindow.m_pResultsTreeView, GTK_TREE_VIEW, "searchresultstreeview");
 
 	// create results tree view
 	g_SearchWindow.m_pResultsListStore = gtk_list_store_new(6, G_TYPE_STRING, G_TYPE_DOUBLE, G_TYPE_DOUBLE, G_TYPE_DOUBLE, G_TYPE_INT, G_TYPE_BOOLEAN);
@@ -76,7 +80,7 @@
 	pColumn = gtk_tree_view_column_new_with_attributes("Road", pCellRenderer, "markup", RESULTLIST_COLUMN_NAME, NULL);
 	gtk_tree_view_append_column(g_SearchWindow.m_pResultsTreeView, pColumn);	
 
-	/* attach handler for selection-changed signal */
+	// attach handler for selection-changed signal
 	GtkTreeSelection *pTreeSelection = gtk_tree_view_get_selection(g_SearchWindow.m_pResultsTreeView);
 	g_signal_connect(G_OBJECT(pTreeSelection), "changed", (GtkSignalFunc)searchwindow_on_resultslist_selection_changed, NULL);
 }
@@ -92,22 +96,28 @@
 
 void searchwindow_add_message(gchar* pszMessage)
 {
+	// Add a basic text message to the list, instead of a search result (eg. "no results")
 	GtkTreeIter iter;
 	gtk_list_store_append(g_SearchWindow.m_pResultsListStore, &iter);
-	gtk_list_store_set(g_SearchWindow.m_pResultsListStore, &iter, RESULTLIST_COLUMN_NAME, pszMessage, RESULTLIST_CLICKABLE, FALSE, -1);
+	gtk_list_store_set(g_SearchWindow.m_pResultsListStore, &iter, 
+					   RESULTLIST_COLUMN_NAME, pszMessage, 
+					   RESULTLIST_CLICKABLE, FALSE,
+					   -1);
 }
 
-// begin a search
 void searchwindow_on_findbutton_clicked(GtkWidget *pWidget, gpointer* p)
 {
-	// make list unsorted (sorting once at the end is much faster than for each insert)
-//	gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(g_SearchWindow.m_pResultsListStore), MAGIC_GTK_NO_SORT_COLUMN, GTK_SORT_ASCENDING);
+	// Begin a search
+
+	// XXX: make list unsorted (sorting once at the end is much faster than for each insert)
+	//gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(g_SearchWindow.m_pResultsListStore), MAGIC_GTK_NO_SORT_COLUMN, GTK_SORT_ASCENDING);
+
 	searchwindow_clear_results();
 
 	const gchar* pszSearch = gtk_entry_get_text(g_SearchWindow.m_pSearchEntry);
 
 	if(pszSearch[0] == '\0') {
-		gchar* pszBuffer = g_strdup_printf("<span size='small'><i>Type search words above.</i></span>");
+		gchar* pszBuffer = g_strdup_printf(SEARCHWINDOW_INFO_FORMAT, "Type search words above.");
 		searchwindow_add_message(pszBuffer);
 		g_free(pszBuffer);
 	}
@@ -119,11 +129,11 @@
 
 		if(g_SearchWindow.m_nNumResults == 0) {
 			// insert a "no results" message
-			gchar* pszBuffer = g_strdup_printf("<span size='small'><i>No results.</i></span>", pszSearch);
+			gchar* pszBuffer = g_strdup_printf(SEARCHWINDOW_INFO_FORMAT, "No results.");
 			searchwindow_add_message(pszBuffer);
 			g_free(pszBuffer);
 		}
-		// Sort the list by distance from viewer!
+		// Sort the list by distance from viewer
 		gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(g_SearchWindow.m_pResultsListStore), RESULTLIST_DISTANCE, GTK_SORT_ASCENDING);
 	}
 	// ensure the search results are visible
@@ -141,8 +151,17 @@
 
 	gdouble fDistance = map_get_distance_in_meters(&ptCenter, pPoint);
 
-	gchar* pszBuffer = g_markup_printf_escaped("<span size='small'>%s</span>", pszText);
-	//g_print("Adding: (%f,%f) (%f) %s\n", pPoint->m_fLatitude, pPoint->m_fLongitude, fDistance, pszBuffer);
+	gchar* pszBuffer = NULL;
+	if(g_utf8_validate(pszText, -1, NULL)) {
+		pszBuffer = g_markup_printf_escaped(SEARCHWINDOW_RESULT_FORMAT, pszText);
+	}
+	else {
+		g_warning("Search result not UTF-8: '%s'\n", pszText);
+		pszBuffer = g_strdup_printf(SEARCHWINDOW_RESULT_FORMAT, "<i>Invalid Name</i>");
+	}
+
+//	g_print("Adding: (%f,%f) (%f) %s\n", pPoint->m_fLatitude, pPoint->m_fLongitude, fDistance, pszBuffer);
+
 	gtk_list_store_append(g_SearchWindow.m_pResultsListStore, &iter);
 	gtk_list_store_set(g_SearchWindow.m_pResultsListStore, &iter,
 		RESULTLIST_COLUMN_NAME, pszBuffer,
@@ -177,8 +196,10 @@
 
 		if(!bClickable) return;	// XXX: is this the right way to make a treeview item not clickable?
 
-		mainwindow_map_slide_to_mappoint(&pt);
-		//mainwindow_set_zoomlevel(nZoomLevel);
+		// XXX: Slide or jump?  Should this be a setting?
+//		mainwindow_map_slide_to_mappoint(&pt);
+		mainwindow_set_zoomlevel(nZoomLevel);
+		mainwindow_map_center_on_mappoint(&pt);
 		mainwindow_draw_map(DRAWFLAG_ALL);
 	}
 }

Index: util.c
===================================================================
RCS file: /cvs/cairo/roadster/src/util.c,v
retrieving revision 1.9
retrieving revision 1.10
diff -u -d -r1.9 -r1.10
--- util.c	6 Sep 2005 02:44:19 -0000	1.9
+++ util.c	14 Sep 2005 20:06:54 -0000	1.10
@@ -181,8 +181,10 @@
 	return aLines;
 }
 
-gboolean util_parse_hex_color(const gchar* pszString, color_t* pReturnColor)
+gboolean util_parse_hex_color(const gchar* pszString, void* pvReturnColor)
 {
+	color_t* pReturnColor = (color_t*)pvReturnColor;
+
 	gchar *p = pszString;
 	if (*p == '#') p++;
 
@@ -219,12 +221,12 @@
 }
 #endif
 
-#if ROADSTER_DEAD_CODE
-void util_random_color(color_t* pColor)
+void util_random_color(void* p)
 {
+	color_t* pColor = (color_t*)p;
+
 	pColor->m_fRed = (random()%1000)/1000.0;
 	pColor->m_fGreen = (random()%1000)/1000.0;
 	pColor->m_fBlue = (random()%1000)/1000.0;
 	pColor->m_fAlpha = 1.0;
 }
-#endif /* ROADSTER_DEAD_CODE */

Index: util.h
===================================================================
RCS file: /cvs/cairo/roadster/src/util.h,v
retrieving revision 1.9
retrieving revision 1.10
diff -u -d -r1.9 -r1.10
--- util.h	6 Sep 2005 02:44:19 -0000	1.9
+++ util.h	14 Sep 2005 20:06:54 -0000	1.10
@@ -25,16 +25,13 @@
 #define _UTIL_H_
 
 #include <gtk/gtk.h>
-#include "layers.h"
 
 #define GTK_PROCESS_MAINLOOP  while (gtk_events_pending ()) { gtk_main_iteration (); }
 
-#define NUM_ELEMS(a) (sizeof(a) / sizeof(a[0]))
-
 #define SWAP(x, y)                   { (x) ^= (y) ^= (x) ^= (y); }
 
 void util_random_color(void* pColor);
-gboolean util_parse_hex_color(const gchar* pszString, color_t* pReturnColor);
+gboolean util_parse_hex_color(const gchar* pszString, void* pReturnColor);
 
 #ifdef ENABLE_TIMING
 #define TIMER_BEGIN(name, str)	GTimer* name = g_timer_new(); g_print("\n%s (%f)\n", str, g_timer_elapsed(name, NULL))
@@ -52,11 +49,6 @@
 #define is_even(x)		(((x) & 1) == 0)
 #define is_odd(x)		(((x) & 1) == 1)
 
-/* Funky, auto-lookup glade signal handlers.
-
-   XXX: Better would be to hook these up manually, remove these
-   declarations, and make the functions static.
-*/
 void util_close_parent_window(GtkWidget* pWidget, gpointer data);
 void util_open_uri(const char* pszURI);
 



More information about the cairo-commit mailing list