[cairo-commit] roadster/src db.c, 1.20, 1.21 db.h, 1.9, 1.10 location.c, 1.3, 1.4 location.h, 1.1, 1.2 locationset.c, 1.8, 1.9 locationset.h, 1.4, 1.5 main.c, 1.17, 1.18 mainwindow.c, 1.33, 1.34 map.c, 1.35, 1.36 map.h, 1.14, 1.15 map_draw_gdk.c, 1.11, 1.12 search_road.c, 1.16, 1.17 tooltip.c, 1.2, 1.3 tooltip.h, 1.1, 1.2

Ian McIntosh commit at pdx.freedesktop.org
Tue Mar 29 01:16:22 PST 2005


Committed by: ian

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

Modified Files:
	db.c db.h location.c location.h locationset.c locationset.h 
	main.c mainwindow.c map.c map.h map_draw_gdk.c search_road.c 
	tooltip.c tooltip.h 
Log Message:
	* src/db.c: Add parsing for WKB points.  Change an index on the LocationAttributeValue table.
	* src/location.c: Add support for inserting POIs.
	* src/locationset.c: Add support for loading a list of POI sets.  Cleanup.
	* src/main.c: Re-ordered init to allow loading of POI set list on GUI init.
	* src/mainwindow.c: Added POI set list in sidebar.  Added support for POI tooltips.  Cleanup.
	* src/map.c: Added support for POI loading/freeing and hit-testing.
	* src/map_draw_gdk.c: Added support for POI rendering.
	* src/tooltip.c: Added support for changing tooltip background color.


Index: db.c
===================================================================
RCS file: /cvs/cairo/roadster/src/db.c,v
retrieving revision 1.20
retrieving revision 1.21
diff -u -d -r1.20 -r1.21
--- db.c	28 Mar 2005 18:49:50 -0000	1.20
+++ db.c	29 Mar 2005 09:16:20 -0000	1.21
@@ -487,6 +487,20 @@
 #define WKB_POINT                  1	// only two we care about
 #define WKB_LINESTRING             2
 
+void db_parse_wkb_point(const gint8* data, mappoint_t* pPoint)
+{
+	g_assert(sizeof(double) == 8);	// mysql gives us 8 bytes per point
+
+	gint nByteOrder = *data++;	// first byte tells us the byte order
+	g_assert(nByteOrder == 1);
+
+	gint nGeometryType = *((gint32*)data)++;
+	g_assert(nGeometryType == WKB_POINT);
+
+	pPoint->m_fLatitude = *((double*)data)++;
+	pPoint->m_fLongitude = *((double*)data)++;
+}
+
 void db_parse_wkb_linestring(const gint8* data, GPtrArray* pPointsArray, gboolean (*callback_alloc_point)(mappoint_t**))
 {
 	g_assert(sizeof(double) == 8);	// mysql gives us 8 bytes per point
@@ -604,7 +618,7 @@
 		// the actual value, a text blob
 		" Value TEXT NOT NULL,"
 		" PRIMARY KEY (ID),"			// for fast updates/deletes (needed only if POIs can have multiple values per name, otherwise LocationID_AttributeID is unique)
-		" INDEX (LocationID),"			// for searching values for a given POI
+		" INDEX (LocationID, AttributeNameID)," // for searching values for a given POI
 		" FULLTEXT(Value));", NULL);		// for sexy fulltext searching of values!
 
 	// Location Set

Index: db.h
===================================================================
RCS file: /cvs/cairo/roadster/src/db.h,v
retrieving revision 1.9
retrieving revision 1.10
diff -u -d -r1.9 -r1.10
--- db.h	28 Mar 2005 18:49:50 -0000	1.9
+++ db.h	29 Mar 2005 09:16:20 -0000	1.10
@@ -80,6 +80,7 @@
 void db_free_escaped_string(gchar* pszString);
 
 void db_parse_wkb_linestring(const gint8* data, GPtrArray* pPointsArray, gboolean (*callback_alloc_point)(mappoint_t**));
+void db_parse_wkb_point(const gint8* data, mappoint_t* pPoint);
 
 void db_enable_keys(void);
 void db_disable_keys(void);

Index: location.c
===================================================================
RCS file: /cvs/cairo/roadster/src/location.c,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -d -r1.3 -r1.4
--- location.c	28 Mar 2005 18:49:50 -0000	1.3
+++ location.c	29 Mar 2005 09:16:20 -0000	1.4
@@ -38,7 +38,7 @@
 }
 
 // get a new point struct from the allocator
-gboolean location_new(location_t** ppLocation)
+gboolean location_alloc(location_t** ppLocation)
 {
 	g_return_val_if_fail(ppLocation != NULL, FALSE);
 	g_return_val_if_fail(*ppLocation == NULL, FALSE);	// must be a pointer to a NULL pointer
@@ -58,6 +58,28 @@
 	g_return_if_fail(pLocation != NULL);
 	g_return_if_fail(g_Location.m_pLocationChunkAllocator != NULL);
 
+	g_free(pLocation->m_pszName);
+
 	// give back to allocator
 	g_mem_chunk_free(g_Location.m_pLocationChunkAllocator, pLocation);
 }
+
+gboolean location_insert(gint nLocationSetID, mappoint_t* pPoint, gint* pnReturnID)
+{
+	g_assert(pPoint != NULL);
+	g_assert(nLocationSetID > 0);
+
+	g_assert(pnReturnID != NULL);
+	g_assert(*pnReturnID == 0);	// must be pointer to an int==0
+
+	// create query SQL
+	gchar* pszSQL = g_strdup_printf(
+		"INSERT INTO Location SET ID=NULL, LocationSetID=%d, Coordinates=GeometryFromText('POINT(%f %f)');",
+		nLocationSetID, pPoint->m_fLatitude, pPoint->m_fLongitude);
+
+	db_query(pszSQL, NULL);
+	g_free(pszSQL);
+
+	*pnReturnID = db_get_last_insert_id();
+	return TRUE;
+}

Index: location.h
===================================================================
RCS file: /cvs/cairo/roadster/src/location.h,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -d -r1.1 -r1.2
--- location.h	8 Mar 2005 18:40:50 -0000	1.1
+++ location.h	29 Mar 2005 09:16:20 -0000	1.2
@@ -28,6 +28,10 @@
 
 G_BEGIN_DECLS
 
+#define LOCATION_ATTRIBUTE_ID_NAME	(1)		// "name" must be #1 in the DB
+#define LOCATION_ATTRIBUTE_ID_ADDRESS	(2)		// "address" must be #2 in the DB
+#define LOCATION_ATTRIBUTE_ID________	(3)		// "" must be #3 in the DB
+
 // a single location (eg. "Someday Cafe")
 typedef struct location {
 	gint m_nID;
@@ -35,6 +39,11 @@
 	mappoint_t m_Coordinates;
 } location_t;
 
+void location_init();
+gboolean location_alloc(location_t** ppLocation);
+void location_free(location_t* pLocation);
+gboolean location_insert(gint nLocationSetID, mappoint_t* pPoint, gint* pnReturnID);
+
 G_END_DECLS
 
 #endif /* _LOCATION_H */

Index: locationset.c
===================================================================
RCS file: /cvs/cairo/roadster/src/locationset.c,v
retrieving revision 1.8
retrieving revision 1.9
diff -u -d -r1.8 -r1.9
--- locationset.c	28 Mar 2005 18:49:50 -0000	1.8
+++ locationset.c	29 Mar 2005 09:16:20 -0000	1.9
@@ -21,6 +21,10 @@
  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  */
 
+// This module has two functions:
+// 1. to provide a list of locationset names, and
+// 2. to provide functions to alloc/free and manage locationsets.
+
 #include <stdlib.h>
 #include <gtk/gtk.h>
 #include "main.h"
@@ -29,9 +33,6 @@
 #include "db.h"
 #include "util.h"
 
-#define DB_LOCATIONS_TABLENAME		("Location")
-#define DB_LOCATIONSETS_TABLENAME	("LocationSet")
-
 struct {
 	GPtrArray* m_pLocationSetArray;	// an array of locationsets
 	GHashTable* m_pLocationSetHash;	// stores pointers to locationsets, indexed by ID
@@ -51,61 +52,47 @@
 }
 
 // get a new locationset struct from the allocator
-static locationset_t* locationset_new(void)
+static gboolean locationset_alloc(locationset_t** ppReturn)
 {
 	g_return_val_if_fail(g_LocationSet.m_pLocationSetChunkAllocator != NULL, NULL);
 
-	return g_mem_chunk_alloc0(g_LocationSet.m_pLocationSetChunkAllocator);
+	*ppReturn = g_mem_chunk_alloc0(g_LocationSet.m_pLocationSetChunkAllocator);
+	return TRUE;
 }
 
-static void locationset_clear(locationset_t* pLocationSet)
+gboolean locationset_insert(const gchar* pszName, gint* pnReturnID)
 {
-	g_return_if_fail(pLocationSet != NULL);
-	g_return_if_fail(g_LocationSet.m_pLocationSetChunkAllocator != NULL);
-
-	// free all location objects
-	gint i;
-	for(i=((pLocationSet->m_pLocationsArray->len)-1) ; i>=0 ; i--) {
-		location_t* pLocation = g_ptr_array_remove_index_fast(pLocationSet->m_pLocationsArray, i);
-		location_free(pLocation);
-	}
-}
+	g_assert(pszName != NULL);
+	g_assert(pnReturnID != NULL);
+	g_assert(*pnReturnID == 0);
 
-// return a locationset struct (and all locations) to the allocator
-static void locationset_free(locationset_t* pLocationSet)
-{
-	locationset_clear(pLocationSet);
+	gchar* pszSafeName = db_make_escaped_string(pszName);
+	gchar* pszSQL = g_strdup_printf("INSERT INTO LocationSet SET Name='%s'", pszSafeName);
+	db_free_escaped_string(pszSafeName);
 
-	// give back to allocator
-	g_mem_chunk_free(g_LocationSet.m_pLocationSetChunkAllocator, pLocationSet);
-}
+	// create query SQL
+	db_query(pszSQL, NULL);
+	g_free(pszSQL);
 
-static void locationset_clear_all_locations(void)
-{
-	// delete all sets but don't delete array of sets
-	gint i;
-	for(i=(g_LocationSet.m_pLocationSetArray->len)-1 ; i>=0 ; i--) {
-		locationset_t* pLocationSet = g_ptr_array_index(g_LocationSet.m_pLocationSetArray, i);
-		locationset_clear(pLocationSet);
-	}
-//	g_hash_table_foreach_steal(g_LocationSet.m_pLocationSets, callback_delete_pointset, NULL);
-//	g_assert(g_hash_table_size(g_LocationSet.m_pLocationSets) == 0);
+	*pnReturnID = db_get_last_insert_id();
+	return TRUE;
 }
 
-void locationset_load_locationsets()
+// Load all locationsets into memory
+void locationset_load_locationsets(void)
 {
-	gchar* pszSQL = g_strdup_printf("SELECT ID, Name FROM %s;", DB_LOCATIONSETS_TABLENAME);
+	gchar* pszSQL = g_strdup_printf("SELECT ID, Name FROM LocationSet;");
 
 	db_resultset_t* pResultSet = NULL;
 	if(db_query(pszSQL, &pResultSet)) {
 		db_row_t aRow;
 
 		while((aRow = db_fetch_row(pResultSet))) {
-			locationset_t* pNewLocationSet = locationset_new();
+			locationset_t* pNewLocationSet = NULL;
+			locationset_alloc(&pNewLocationSet);
 
 			pNewLocationSet->m_nID = atoi(aRow[0]);
 			pNewLocationSet->m_pszName = g_strdup(aRow[1]);
-			//pNewLocationSet->m_pLocationsArray = g_ptr_array_new();
 
 			// Add the new set to both data structures
 			g_ptr_array_add(g_LocationSet.m_pLocationSetArray, pNewLocationSet);
@@ -117,33 +104,12 @@
 	return;
 }
 
-const GPtrArray* locationset_get_set_array()
+const GPtrArray* locationset_get_array(void)
 {
 	return g_LocationSet.m_pLocationSetArray;
 }
 
-gboolean locationset_add_location(gint nLocationSetID, mappoint_t* pPoint, gint* pReturnID)
-{
-	g_assert(pPoint != NULL);
-	g_assert(pReturnID != NULL);
-	g_return_val_if_fail(nLocationSetID != 0, FALSE);
-
-	// create query SQL
-	gchar* pszSQL = g_strdup_printf(
-		"INSERT INTO %s SET ID=NULL, LocationSetID=%d, Coordinates=GeometryFromText('POINT(%f %f)');",
-		DB_LOCATIONS_TABLENAME,
-		nLocationSetID,
-		pPoint->m_fLatitude, pPoint->m_fLongitude);
-
-	db_query(pszSQL, NULL);
-	g_free(pszSQL);
-
-	// return the new ID
-	*pReturnID = db_get_last_insert_id();
-	return TRUE;
-}
-
-static gboolean locationset_find_by_id(gint nLocationSetID, locationset_t** ppLocationSet)
+gboolean locationset_find_by_id(gint nLocationSetID, locationset_t** ppLocationSet)
 {
 	locationset_t* pLocationSet = g_hash_table_lookup(g_LocationSet.m_pLocationSetHash, &nLocationSetID);
 	if(pLocationSet) {
@@ -153,161 +119,27 @@
 	return FALSE;	
 }
 
-// reads points in given rect into memory
-gboolean locationset_load_locations(maprect_t* pRect)
-{
-	g_assert_not_reached();	// not used/tested
-
-//	TIMER_BEGIN(mytimer, "BEGIN POINT LOAD");
-
-//~ //	g_return_val_if_fail(pGeometrySet != NULL, FALSE);
-//~ //	gint nZoomLevel = map_get_zoomlevel();
-
-//	if(!db_is_connected(g_pDB)) return FALSE;
-
-	//~ // HACKY: make a list of layer IDs "2,3,5,6"
-	//~ gchar azPointSetIDList[5000] = {0};
-	//~ gint nActivePointSetCount = 0;
-	//~ gint i;
-	//~ for(i=0 ; i <= pPointSetArray->len ;i++) {
-		//~ pointset_t* pPointSet = g_ptr_array_index(pPointSetArray, i);
-
-		//~ gchar azPointSetID[10];
-		//~ if(nActivePointSetCount > 0) g_snprintf(azPointSetID, 10, ",%d", pPointSet->m_nID);
-		//~ else g_snprintf(azPointSetID, 10, "%d", pPointSet->m_nID);
-		//~ g_strlcat(azPointSetIDList, azPointSetID, 5000);
-		//~ nActivePointSetCount++;
-	//~ }
-	//~ if(nActivePointSetCount == 0) {
-		//~ g_print("no active pointsets!\n");
-//~ //		layers_clear();
-		//~ return TRUE;
-	//~ }
-#define LC_EXTRALOAD	(0.05)
-
-	// generate SQL
-	gchar azQuery[2000];
-	g_snprintf(azQuery, 2000,
-		"SELECT ID, LocationSetID, AsText(Coordinates) FROM %s WHERE"
-//		" PointSetID IN (%s) AND" //
-		" MBRIntersects(GeomFromText('Polygon((%f %f,%f %f,%f %f,%f %f,%f %f))'), Coordinates)",
-		DB_LOCATIONS_TABLENAME,
-//		azPointSetIDList,
-		pRect->m_A.m_fLatitude + LC_EXTRALOAD, pRect->m_A.m_fLongitude - LC_EXTRALOAD, 	// upper left
-		pRect->m_A.m_fLatitude + LC_EXTRALOAD, pRect->m_B.m_fLongitude + LC_EXTRALOAD, 	// upper right
-		pRect->m_B.m_fLatitude - LC_EXTRALOAD, pRect->m_B.m_fLongitude + LC_EXTRALOAD, 	// bottom right
-		pRect->m_B.m_fLatitude - LC_EXTRALOAD, pRect->m_A.m_fLongitude - LC_EXTRALOAD, 	// bottom left
-		pRect->m_A.m_fLatitude + LC_EXTRALOAD, pRect->m_A.m_fLongitude - LC_EXTRALOAD	// upper left again
-		);
-//	TIMER_SHOW(mytimer, "after SQL generation");
-//~ g_print("sql: %s\n", azQuery);
-	db_resultset_t* pResultSet = NULL;
-	
-	if(db_query(azQuery, &pResultSet)) {
-//		TIMER_SHOW(mytimer, "after query");
-		guint32 uRowCount = 0;
-
-		locationset_clear_all_locations();
-
-		db_row_t aRow;
-		while((aRow = mysql_fetch_row(pResultSet))) {
-			uRowCount++;
-
-			// aRow[0] is ID
-			// aRow[1] is LocationSetID
-			// aRow[2] is Coordinates in mysql's text format
-//			g_print("data: %s, %s, %s\n", aRow[0], aRow[1], aRow[2]);
-
-			gint nLocationID = atoi(aRow[0]);
-			gint nLocationSetID = atoi(aRow[1]);
-
-			// find the locationset to add this to
-			locationset_t* pLocationSet = NULL;
-			if(locationset_find_by_id(nLocationSetID, &pLocationSet)) {
-				// if found (and it should be, at least once we filter by SetID in the SQL above)
-				// allocate a new location_t and add it to the set
-				location_t* pNewLocation = NULL;
-				if(location_new(&pNewLocation)) {
-					pNewLocation->m_nID = nLocationID;
-					
-					g_assert_not_reached();
-					// XXX: need to write this using MySQL WKB format
-					// db_parse_point(aRow[2], &pNewLocation->m_Coordinates);
-					g_ptr_array_add(pLocationSet->m_pLocationsArray, pNewLocation);
-				}
-			}
-			else {
-				g_warning("locationset_load_locations: loaded a point but setID (%d) not found to put it in\n", nLocationSetID);
-			}
-		} // end while loop on rows
-//		g_print(" -- got %d location(s)\n", uRowCount);
-//		TIMER_SHOW(mytimer, "after rows retrieved");
-
-		db_free_result(pResultSet);
-//		TIMER_SHOW(mytimer, "after free results");
-//		TIMER_END(mytimer, "END DB LOAD");
-		return TRUE;
-	}
-	return FALSE;
-}
-
-/**************************************************************
-** PointSets
-***************************************************************/
-
 #ifdef ROADSTER_DEAD_CODE
 /*
-gboolean db_pointset_insert(const gchar* pszName, gint* pReturnID)
-{
-	if(!db_is_connected()) return FALSE;
-	g_assert(pszName != NULL);
-	g_assert(pReturnID != NULL);
-
-	// create query SQL
-	gchar azQuery[MAX_SQLBUFFER_LEN];
-	gchar* pszEscapedName = db_make_escaped_string(pszName);
-	g_snprintf(azQuery, MAX_SQLBUFFER_LEN, "INSERT INTO %s SET ID=NULL, Name='%s';", DB_LOCATIONSETS_TABLENAME, pszEscapedName);
-	db_free_escaped_string(pszEscapedName);
-
-	// run query
-	if(!db_query(azQuery, NULL)) {
-		return FALSE;
-	}
-	// return the new ID
-	*pReturnID = db_insert_id();
-	return TRUE;	
-}
-gboolean db_pointset_delete(gint nPointSetID)
+// return a locationset struct (and all locations) to the allocator
+static void locationset_free(locationset_t* pLocationSet)
 {
-	if(!db_is_connected(g_pDB)) return FALSE;
-
-	gchar azQuery[MAX_SQLBUFFER_LEN];
-	g_snprintf(azQuery, MAX_SQLBUFFER_LEN, "DELETE FROM %s WHERE ID=%d;", DB_LOCATIONSETS_TABLENAME, nPointSetID);
-	if(MYSQL_RESULT_SUCCESS != mysql_query(g_pDB->m_pMySQLConnection, azQuery)) {
-		g_warning("db_pointset_delete: deleting pointset failed: %s (SQL: %s)\n", mysql_error(g_pDB->m_pMySQLConnection), azQuery);
-		return FALSE;
-	}
+	locationset_clear(pLocationSet);
 
-	g_snprintf(azQuery, MAX_SQLBUFFER_LEN, "DELETE FROM %s WHERE LocationSetID=%d;", DB_LOCATIONS_TABLENAME, nPointSetID);
-	if(MYSQL_RESULT_SUCCESS != mysql_query(g_pDB->m_pMySQLConnection, azQuery)) {
-		g_warning("db_pointset_delete: deleting points failed: %s (SQL: %s)\n", mysql_error(g_pDB->m_pMySQLConnection), azQuery);
-		return FALSE;
-	}
-	return TRUE;
+	// give back to allocator
+	g_mem_chunk_free(g_LocationSet.m_pLocationSetChunkAllocator, pLocationSet);
 }
 
-gboolean db_point_delete(gint nPointID)
+static void locationset_clear_all_locations(void)
 {
-	if(!db_is_connected()) return FALSE;
-
-	gchar azQuery[MAX_SQLBUFFER_LEN];
-	g_snprintf(azQuery, MAX_SQLBUFFER_LEN, "DELETE FROM %s WHERE ID=%d;", DB_LOCATIONS_TABLENAME, nPointID);
-		
-	if(MYSQL_RESULT_SUCCESS != mysql_query(g_pDB->m_pMySQLConnection, azQuery)) {
-		g_warning("db_point_delete: deleting point failed: %s (SQL: %s)\n", mysql_error(g_pDB->m_pMySQLConnection), azQuery);
-		return FALSE;
+	// delete all sets but don't delete array of sets
+	gint i;
+	for(i=(g_LocationSet.m_pLocationSetArray->len)-1 ; i>=0 ; i--) {
+		locationset_t* pLocationSet = g_ptr_array_index(g_LocationSet.m_pLocationSetArray, i);
+		locationset_clear(pLocationSet);
 	}
-	return TRUE;
+//	g_hash_table_foreach_steal(g_LocationSet.m_pLocationSets, callback_delete_pointset, NULL);
+//	g_assert(g_hash_table_size(g_LocationSet.m_pLocationSets) == 0);
 }
 */
 #endif

Index: locationset.h
===================================================================
RCS file: /cvs/cairo/roadster/src/locationset.h,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -d -r1.4 -r1.5
--- locationset.h	8 Mar 2005 18:40:50 -0000	1.4
+++ locationset.h	29 Mar 2005 09:16:20 -0000	1.5
@@ -27,27 +27,27 @@
 G_BEGIN_DECLS
 
 #include "map.h"
-	
+
 typedef struct locationsetstyle {
 	// icon?
-	int a;
+	// color?
+	int __unused;
 } locationsetstyle_t;
 
 // a set of locations (eg. "Coffee Shops")
 typedef struct locationset {
 	gint m_nID;
 	gchar* m_pszName;
-
 	locationsetstyle_t m_Style;
-
-	GPtrArray* m_pLocationsArray;
 } locationset_t;
 
 void locationset_init(void);
+
 void locationset_load_locationsets(void);
-gboolean locationset_load_locations(maprect_t* pRect);
-gboolean locationset_add_location(gint nLocationSetID, mappoint_t* pPoint, gint* pReturnID);
-const GPtrArray* locationset_get_set_array(void);
+const GPtrArray* locationset_get_array(void);
+gboolean locationset_find_by_id(gint nLocationSetID, locationset_t** ppLocationSet);
+
+gboolean locationset_insert(const gchar* pszName, gint* pnReturnID);
 
 G_END_DECLS
 

Index: main.c
===================================================================
RCS file: /cvs/cairo/roadster/src/main.c,v
retrieving revision 1.17
retrieving revision 1.18
diff -u -d -r1.17 -r1.18
--- main.c	28 Mar 2005 18:49:50 -0000	1.17
+++ main.c	29 Mar 2005 09:16:20 -0000	1.18
@@ -35,6 +35,8 @@
 #include "prefs.h"
 #include "animator.h"
 #include "road.h"
+#include "locationset.h"
+#include "location.h"
 
 static gboolean main_init(void);
 static void main_deinit(void);
@@ -53,9 +55,22 @@
 	if(!main_init())
 		return 1;
 
+/*         gint nNewLocationSetID = 0;                                                                                                                             */
+/*         locationset_insert("Coffee Shops", &nNewLocationSetID);                                                                                                 */
+/*         mappoint_t pt;                                                                                                                                          */
+/*         pt.m_fLatitude = 41.54944;                                                                                                                              */
+/*         pt.m_fLongitude = -70.61409;                                                                                                                            */
+/*                                                                                                                                                                 */
+/*         gint nNewLocationID = 0;                                                                                                                                */
+/*         location_insert(nNewLocationSetID, &pt, &nNewLocationID);                                                                                               */
+/*                                                                                                                                                                 */
+/*         gchar* pszSQL = g_strdup_printf("INSERT INTO LocationAttributeValue SET LocationID=%d, AttributeNameID=1, Value='1369 Coffee House';", nNewLocationID); */
+/*         db_query(pszSQL, NULL);                                                                                                                                 */
+/*         g_free(pszSQL);                                                                                                                                         */
+
 	prefs_read();
 
-	g_print("Running in %s mode\n", g_thread_supported() ? "multi-threaded" : "single-threaded");
+	g_print("Running %s\n", g_thread_supported() ? "multi-threaded" : "single-threaded");
 	gui_run();
 	main_deinit();
 
@@ -97,27 +112,33 @@
 	g_print("initializing scenemanager\n");
 	scenemanager_init();
 
-	g_print("initializing locationsets\n");
-	locationset_init();
-
 	g_print("initializing gpsclient\n");
 	gpsclient_init();
 
-	g_print("initializing gui\n");
-	gui_init();
+	g_print("initializing db\n");
+	db_init();
+
+	g_print("initializing locationsets\n");
+	locationset_init();
+
+	g_print("initializing locations\n");
+	location_init();
 
 	g_print("initializing animator\n");
 	animator_init();
 	
-	g_print("initializing db\n");
-	db_init();
-
 	g_print("connecting to db\n");
 	gboolean bConnected = db_connect(NULL, NULL, NULL, "");
 
 	g_print("creating database tables\n");
 	db_create_tables();
 
+	// Load sets from DB
+	locationset_load_locationsets();
+
+	g_print("initializing gui\n");
+	gui_init();
+
 	g_print("initialization complete\n");
 
 	return TRUE;

Index: mainwindow.c
===================================================================
RCS file: /cvs/cairo/roadster/src/mainwindow.c,v
retrieving revision 1.33
retrieving revision 1.34
diff -u -d -r1.33 -r1.34
--- mainwindow.c	28 Mar 2005 18:49:50 -0000	1.33
+++ mainwindow.c	29 Mar 2005 09:16:20 -0000	1.34
@@ -78,12 +78,19 @@
 #define LAYERLIST_COLUMN_ENABLED	(0)
 #define LAYERLIST_COLUMN_NAME		(1)
 
+// Locationset columns
+#define	LOCATIONSETLIST_COLUMN_ENABLED  (0)
+#define LOCATIONSETLIST_COLUMN_NAME	(1)
+
 // Limits
 #define MAX_SEARCH_TEXT_LENGTH		(100)
 #define SPEED_LABEL_FORMAT		("<span font_desc='32'>%.0f</span>")
 
 #define TOOLTIP_FORMAT			("%s")
 
+#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
 
@@ -276,43 +283,46 @@
 
 void mainwindow_init(GladeXML* pGladeXML)
 {
+	GtkCellRenderer* pCellRenderer;
+  	GtkTreeViewColumn* pColumn;
+
 	// 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_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);
-	g_MainWindow.m_pZoomOutButton		= GTK_BUTTON(glade_xml_get_widget(pGladeXML, "zoomoutbutton")); 	g_return_if_fail(g_MainWindow.m_pZoomOutButton != NULL);
-	g_MainWindow.m_pZoomOutMenuItem		= GTK_MENU_ITEM(glade_xml_get_widget(pGladeXML, "zoomoutmenuitem")); 	g_return_if_fail(g_MainWindow.m_pZoomOutMenuItem != 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);
+	g_MainWindow.m_pZoomOutButton		= GTK_BUTTON(glade_xml_get_widget(pGladeXML, "zoomoutbutton")); 		g_return_if_fail(g_MainWindow.m_pZoomOutButton != NULL);
+	g_MainWindow.m_pZoomOutMenuItem		= GTK_MENU_ITEM(glade_xml_get_widget(pGladeXML, "zoomoutmenuitem")); 		g_return_if_fail(g_MainWindow.m_pZoomOutMenuItem != NULL);
 
-	g_MainWindow.m_pContentBox 			= GTK_HBOX(glade_xml_get_widget(pGladeXML, "mainwindowcontentsbox"));	g_return_if_fail(g_MainWindow.m_pContentBox != NULL);
-	g_MainWindow.m_pLocationSetsTreeView= GTK_TREE_VIEW(glade_xml_get_widget(pGladeXML, "locationsetstreeview"));	g_return_if_fail(g_MainWindow.m_pLocationSetsTreeView != NULL);
+	g_MainWindow.m_pContentBox 		= GTK_HBOX(glade_xml_get_widget(pGladeXML, "mainwindowcontentsbox"));		g_return_if_fail(g_MainWindow.m_pContentBox != NULL);
+	g_MainWindow.m_pLocationSetsTreeView	= GTK_TREE_VIEW(glade_xml_get_widget(pGladeXML, "locationsetstreeview"));	g_return_if_fail(g_MainWindow.m_pLocationSetsTreeView != NULL);
 	g_MainWindow.m_pLayersListTreeView	= GTK_TREE_VIEW(glade_xml_get_widget(pGladeXML, "layerstreeview"));		g_return_if_fail(g_MainWindow.m_pLayersListTreeView != NULL);
-	g_MainWindow.m_pZoomScale			= GTK_HSCALE(glade_xml_get_widget(pGladeXML, "zoomscale"));				g_return_if_fail(g_MainWindow.m_pZoomScale != NULL);
+	g_MainWindow.m_pZoomScale		= GTK_HSCALE(glade_xml_get_widget(pGladeXML, "zoomscale"));			g_return_if_fail(g_MainWindow.m_pZoomScale != NULL);
 	g_MainWindow.m_pPositionLabel 		= GTK_LABEL(glade_xml_get_widget(pGladeXML, "positionlabel"));			g_return_if_fail(g_MainWindow.m_pPositionLabel != NULL);
 	g_MainWindow.m_pStatusbarGPSIcon 	= GTK_IMAGE(glade_xml_get_widget(pGladeXML, "statusbargpsicon"));		g_return_if_fail(g_MainWindow.m_pStatusbarGPSIcon != NULL);
 	g_MainWindow.m_pZoomScaleLabel 		= GTK_LABEL(glade_xml_get_widget(pGladeXML, "zoomscalelabel"));			g_return_if_fail(g_MainWindow.m_pZoomScaleLabel != NULL);
-	g_MainWindow.m_pToolbar				= GTK_TOOLBAR(glade_xml_get_widget(pGladeXML, "maintoolbar"));			g_return_if_fail(g_MainWindow.m_pToolbar != NULL);
-	g_MainWindow.m_pStatusbar			= GTK_VBOX(glade_xml_get_widget(pGladeXML, "statusbar"));				g_return_if_fail(g_MainWindow.m_pStatusbar != NULL);
-	g_MainWindow.m_pSidebox				= GTK_WIDGET(glade_xml_get_widget(pGladeXML, "mainwindowsidebox"));		g_return_if_fail(g_MainWindow.m_pSidebox != NULL);
-	g_MainWindow.m_pSidebarNotebook			= GTK_NOTEBOOK(glade_xml_get_widget(pGladeXML, "sidebarnotebook"));		g_return_if_fail(g_MainWindow.m_pSidebarNotebook != NULL);
-	g_MainWindow.m_pTooltips			= gtk_tooltips_new();
-	g_MainWindow.m_pSpeedLabel			= GTK_LABEL(glade_xml_get_widget(pGladeXML, "speedlabel"));		g_return_if_fail(g_MainWindow.m_pSpeedLabel != NULL);
+	g_MainWindow.m_pToolbar			= GTK_TOOLBAR(glade_xml_get_widget(pGladeXML, "maintoolbar"));			g_return_if_fail(g_MainWindow.m_pToolbar != NULL);
+	g_MainWindow.m_pStatusbar		= GTK_VBOX(glade_xml_get_widget(pGladeXML, "statusbar"));			g_return_if_fail(g_MainWindow.m_pStatusbar != NULL);
+	g_MainWindow.m_pSidebox			= GTK_WIDGET(glade_xml_get_widget(pGladeXML, "mainwindowsidebox"));		g_return_if_fail(g_MainWindow.m_pSidebox != NULL);
+	g_MainWindow.m_pSidebarNotebook		= GTK_NOTEBOOK(glade_xml_get_widget(pGladeXML, "sidebarnotebook"));		g_return_if_fail(g_MainWindow.m_pSidebarNotebook != NULL);
+	g_MainWindow.m_pTooltips		= gtk_tooltips_new();
+	g_MainWindow.m_pSpeedLabel		= GTK_LABEL(glade_xml_get_widget(pGladeXML, "speedlabel"));			g_return_if_fail(g_MainWindow.m_pSpeedLabel != NULL);
 
 	// GPS Widgets
-	g_MainWindow.m_GPS.m_pShowPositionCheckButton	= GTK_CHECK_BUTTON(glade_xml_get_widget(pGladeXML, "gpsshowpositioncheckbutton"));		g_return_if_fail(g_MainWindow.m_GPS.m_pShowPositionCheckButton != NULL);
-	g_MainWindow.m_GPS.m_pKeepPositionCenteredCheckButton= GTK_CHECK_BUTTON(glade_xml_get_widget(pGladeXML, "gpskeeppositioncenteredcheckbutton"));		g_return_if_fail(g_MainWindow.m_GPS.m_pKeepPositionCenteredCheckButton != NULL);
-	g_MainWindow.m_GPS.m_pShowTrailCheckButton = GTK_CHECK_BUTTON(glade_xml_get_widget(pGladeXML, "gpsshowtrailcheckbutton"));		g_return_if_fail(g_MainWindow.m_GPS.m_pKeepPositionCenteredCheckButton != NULL);
-	g_MainWindow.m_GPS.m_pStickToRoadsCheckButton = GTK_CHECK_BUTTON(glade_xml_get_widget(pGladeXML, "gpssticktoroadscheckbutton"));		g_return_if_fail(g_MainWindow.m_GPS.m_pStickToRoadsCheckButton != NULL);
-	g_MainWindow.m_pGPSSignalStrengthProgressBar = GTK_PROGRESS_BAR(glade_xml_get_widget(pGladeXML, "gpssignalprogressbar"));		g_return_if_fail(g_MainWindow.m_pGPSSignalStrengthProgressBar != NULL);
+	g_MainWindow.m_GPS.m_pShowPositionCheckButton		= GTK_CHECK_BUTTON(glade_xml_get_widget(pGladeXML, "gpsshowpositioncheckbutton"));		g_return_if_fail(g_MainWindow.m_GPS.m_pShowPositionCheckButton != NULL);
+	g_MainWindow.m_GPS.m_pKeepPositionCenteredCheckButton	= GTK_CHECK_BUTTON(glade_xml_get_widget(pGladeXML, "gpskeeppositioncenteredcheckbutton"));	g_return_if_fail(g_MainWindow.m_GPS.m_pKeepPositionCenteredCheckButton != NULL);
+	g_MainWindow.m_GPS.m_pShowTrailCheckButton 		= GTK_CHECK_BUTTON(glade_xml_get_widget(pGladeXML, "gpsshowtrailcheckbutton"));			g_return_if_fail(g_MainWindow.m_GPS.m_pKeepPositionCenteredCheckButton != NULL);
+	g_MainWindow.m_GPS.m_pStickToRoadsCheckButton 		= GTK_CHECK_BUTTON(glade_xml_get_widget(pGladeXML, "gpssticktoroadscheckbutton"));		g_return_if_fail(g_MainWindow.m_GPS.m_pStickToRoadsCheckButton != NULL);
+	g_MainWindow.m_pGPSSignalStrengthProgressBar 		= GTK_PROGRESS_BAR(glade_xml_get_widget(pGladeXML, "gpssignalprogressbar"));			g_return_if_fail(g_MainWindow.m_pGPSSignalStrengthProgressBar != NULL);
 
 	// History Widgets
-	g_MainWindow.m_pForwardButton = GTK_BUTTON(glade_xml_get_widget(pGladeXML, "forwardbutton"));		g_return_if_fail(g_MainWindow.m_pForwardButton != NULL);
-	g_MainWindow.m_pBackButton = GTK_BUTTON(glade_xml_get_widget(pGladeXML, "backbutton"));		g_return_if_fail(g_MainWindow.m_pBackButton != NULL);
-	g_MainWindow.m_pForwardMenuItem = GTK_MENU_ITEM(glade_xml_get_widget(pGladeXML, "forwardmenuitem"));		g_return_if_fail(g_MainWindow.m_pForwardMenuItem != NULL);
-	g_MainWindow.m_pBackMenuItem = GTK_MENU_ITEM(glade_xml_get_widget(pGladeXML, "backmenuitem"));		g_return_if_fail(g_MainWindow.m_pBackMenuItem != NULL);
+	g_MainWindow.m_pForwardButton 		= GTK_BUTTON(glade_xml_get_widget(pGladeXML, "forwardbutton"));			g_return_if_fail(g_MainWindow.m_pForwardButton != NULL);
+	g_MainWindow.m_pBackButton 		= GTK_BUTTON(glade_xml_get_widget(pGladeXML, "backbutton"));			g_return_if_fail(g_MainWindow.m_pBackButton != NULL);
+	g_MainWindow.m_pForwardMenuItem 	= GTK_MENU_ITEM(glade_xml_get_widget(pGladeXML, "forwardmenuitem"));		g_return_if_fail(g_MainWindow.m_pForwardMenuItem != NULL);
+	g_MainWindow.m_pBackMenuItem 		= GTK_MENU_ITEM(glade_xml_get_widget(pGladeXML, "backmenuitem"));		g_return_if_fail(g_MainWindow.m_pBackMenuItem != NULL);
 
 	g_MainWindow.m_pHistory = history_new();
 	g_assert(g_MainWindow.m_pHistory);
@@ -348,53 +358,57 @@
 
         g_MainWindow.m_nGPSLocationGlyph = glyph_load(PACKAGE_DATA_DIR"/car.svg");
 
-	/*
-	**
-	*/
-	// create layers tree view
+	// create location sets tree view
 	GtkListStore* pLocationSetsListStore = gtk_list_store_new(2, G_TYPE_BOOLEAN, G_TYPE_STRING);
 	gtk_tree_view_set_model(g_MainWindow.m_pLocationSetsTreeView, GTK_TREE_MODEL(pLocationSetsListStore));
 
-	GtkCellRenderer* pCellRenderer;
-  	GtkTreeViewColumn* pColumn;
-
-	// add a checkbox column for layer enabled/disabled
+	// NEW COLUMN: Visible checkbox
 	pCellRenderer = gtk_cell_renderer_toggle_new();
   	g_object_set_data(G_OBJECT(pCellRenderer), "column", (gint *)LAYERLIST_COLUMN_ENABLED);
 //	g_signal_connect(pCellRenderer, "toggled", G_CALLBACK(on_layervisible_checkbox_clicked), pLocationSetsListStore);
-	pColumn = gtk_tree_view_column_new_with_attributes("Visible", pCellRenderer, "active", LAYERLIST_COLUMN_ENABLED, NULL);	
+	pColumn = gtk_tree_view_column_new_with_attributes("Visible", pCellRenderer, "active", LOCATIONSETLIST_COLUMN_ENABLED, NULL);
 	gtk_tree_view_append_column(g_MainWindow.m_pLocationSetsTreeView, pColumn);
 
-	// add Name column (with a text renderer)
+	// NEW COLUMN: Name column
 	pCellRenderer = gtk_cell_renderer_text_new();
-	pColumn = gtk_tree_view_column_new_with_attributes("Layers", pCellRenderer, "text", LAYERLIST_COLUMN_NAME, NULL);	
+	pColumn = gtk_tree_view_column_new_with_attributes("Layers", pCellRenderer, "text", LOCATIONSETLIST_COLUMN_NAME, NULL);
 	gtk_tree_view_append_column(g_MainWindow.m_pLocationSetsTreeView, pColumn);
 
-	/*
-	**
-	*/
+	// Fill locationset list iwth live data
+	const GPtrArray* pLocationSetArray = locationset_get_array();
+	gint i;
+	GtkTreeIter iter;
+
+	for(i=0 ; i<pLocationSetArray->len ; i++) {
+		locationset_t* pLocationSet = g_ptr_array_index(pLocationSetArray, i);
+
+		gtk_list_store_append(pLocationSetsListStore, &iter);
+		gtk_list_store_set(pLocationSetsListStore, &iter, 
+				   LOCATIONSETLIST_COLUMN_NAME, pLocationSet->m_pszName, 
+				   LOCATIONSETLIST_COLUMN_ENABLED, TRUE,
+				   -1);
+	}
+
+#ifdef ROADSTER_DEAD_CODE
+/*
 	// create layers tree view
 	GtkListStore* pLayersListStore = gtk_list_store_new(2, G_TYPE_BOOLEAN, G_TYPE_STRING);
 	gtk_tree_view_set_model(g_MainWindow.m_pLayersListTreeView, GTK_TREE_MODEL(pLayersListStore));
 
-//	GtkCellRenderer* pCellRenderer;
-//  	GtkTreeViewColumn* pColumn;
-
 	// add a checkbox column for layer enabled/disabled
 	pCellRenderer = gtk_cell_renderer_toggle_new();
   	g_object_set_data (G_OBJECT (pCellRenderer ), "column", (gint *)LAYERLIST_COLUMN_ENABLED);
 //	g_signal_connect (pCellRenderer, "toggled", G_CALLBACK (on_layervisible_checkbox_clicked), pLayersListStore);
-	pColumn = gtk_tree_view_column_new_with_attributes("Visible", pCellRenderer, "active", LAYERLIST_COLUMN_ENABLED, NULL);	
+	pColumn = gtk_tree_view_column_new_with_attributes("Visible", pCellRenderer, "active", LAYERLIST_COLUMN_ENABLED, NULL);
 	gtk_tree_view_append_column(g_MainWindow.m_pLayersListTreeView, pColumn);
 
 	// add Layer Name column (with a text renderer)
 	pCellRenderer = gtk_cell_renderer_text_new();
-	pColumn = gtk_tree_view_column_new_with_attributes("Layers", pCellRenderer, "text", LAYERLIST_COLUMN_NAME, NULL);	
+	pColumn = gtk_tree_view_column_new_with_attributes("Layers", pCellRenderer, "text", LAYERLIST_COLUMN_NAME, NULL);
 	gtk_tree_view_append_column(g_MainWindow.m_pLayersListTreeView, pColumn);
+*/
+#endif
 
-	/*
-	**
-	*/
 	mainwindow_statusbar_update_zoomscale();
 	mainwindow_statusbar_update_position();	
 
@@ -802,145 +816,145 @@
 
 static gboolean mainwindow_on_mouse_button_click(GtkWidget* w, GdkEventButton *event)
 {
-	gint nX;
-	gint nY;
-
+	gint nX, nY;
 	gdk_window_get_pointer(w->window, &nX, &nY, NULL);
 
 	gint nWidth = GTK_WIDGET(g_MainWindow.m_pDrawingArea)->allocation.width;
 	gint nHeight = GTK_WIDGET(g_MainWindow.m_pDrawingArea)->allocation.height;
 	EDirection eScrollDirection = DIRECTION_NONE;
 
-	if(event->button == 1) {
-		// Left mouse button down?
-		if(event->type == GDK_BUTTON_PRESS) {
-			tooltip_hide(g_MainWindow.m_pTooltip);
-
-			// Is it at a border?
-			eScrollDirection = match_border(nX, nY, nWidth, nHeight, BORDER_SCROLL_CLICK_TARGET_SIZE);
-			if(eScrollDirection != DIRECTION_NONE) {
-				// begin a scroll
-				
-				//GdkCursor* pCursor = gdk_cursor_new(g_aDirectionCursors[eScrollDirection].m_nCursor);
-				//if(GDK_GRAB_SUCCESS == 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)) {
-				GdkCursor* pCursor = gdk_cursor_new(g_aDirectionCursors[eScrollDirection].m_nCursor);
-				gdk_window_set_cursor(GTK_WIDGET(g_MainWindow.m_pDrawingArea)->window, pCursor);
-				gdk_cursor_unref(pCursor);
-
-				g_MainWindow.m_bScrolling = TRUE;
-				g_MainWindow.m_eScrollDirection = eScrollDirection;
-				g_MainWindow.m_bScrollMovement = FALSE;	// no movement yet
-
-				// XXX: s.garrity asked for a single click to scroll once, BUT when we added double-click to slide
-				// it resulted in weird behavior
-				//mainwindow_scroll_direction(g_MainWindow.m_eScrollDirection, SCROLL_DISTANCE_IN_PIXELS);
-				
-				mainwindow_set_scroll_timeout();
+	// get mouse position on screen
+	screenpoint_t screenpoint = {nX,nY};
+	mappoint_t mappoint;
+	map_windowpoint_to_mappoint(g_MainWindow.m_pMap, &screenpoint, &mappoint);
+	
+	maphit_t* pHitStruct = NULL;
+	if(map_hit_test(g_MainWindow.m_pMap, &mappoint, &pHitStruct) && pHitStruct->m_eHitType == MAP_HITTYPE_LOCATION) {
+		if(event->button == 1) {
+			if(event->type == GDK_BUTTON_PRESS) {
+				g_print("click on location ID #%d\n", pHitStruct->m_LocationHit.m_nLocationID);
 			}
-			else {
-				// else begin a drag
-//                                 GdkCursor* pCursor = gdk_cursor_new(GDK_HAND2);
-//                                 if(GDK_GRAB_SUCCESS == 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)) {
-//                                 GdkCursor* pCursor = gdk_cursor_new(GDK_FLEUR);
-//                                 gdk_window_set_cursor(GTK_WIDGET(g_MainWindow.m_pDrawingArea)->window, pCursor);
-//                                 gdk_cursor_unref(pCursor);
-
-				g_MainWindow.m_bMouseDragging = TRUE;
-				g_MainWindow.m_bMouseDragMovement = FALSE;
-				g_MainWindow.m_ptClickLocation.m_nX = nX;
-				g_MainWindow.m_ptClickLocation.m_nY = nY;
-//                                 }
-//                                 gdk_cursor_unref(pCursor);
+			else if(event->type == GDK_2BUTTON_PRESS) {
+				g_print("double-click on location ID #%d\n", pHitStruct->m_LocationHit.m_nLocationID);
 			}
 		}
-		// Left mouse button up?
-		else if(event->type == GDK_BUTTON_RELEASE) {
-			// end mouse dragging, if active
-			if(g_MainWindow.m_bMouseDragging == TRUE) {
-				// restore cursor
-				GdkCursor* pCursor = gdk_cursor_new(GDK_LEFT_PTR);
-				gdk_window_set_cursor(GTK_WIDGET(g_MainWindow.m_pDrawingArea)->window, pCursor);
-				gdk_cursor_unref(pCursor);
+	}
+	else {
+		if(event->button == 1) {
+			// Left mouse button down?
+			if(event->type == GDK_BUTTON_PRESS) {
+				tooltip_hide(g_MainWindow.m_pTooltip);
 
-				g_MainWindow.m_bMouseDragging = FALSE;
-				if(g_MainWindow.m_bMouseDragMovement) {
-					mainwindow_cancel_draw_pretty_timeout();
-					mainwindow_draw_map(DRAWFLAG_ALL);
+				// Is it at a border?
+				eScrollDirection = match_border(nX, nY, nWidth, nHeight, BORDER_SCROLL_CLICK_TARGET_SIZE);
+				if(eScrollDirection != DIRECTION_NONE) {
+					// begin a scroll
+					GdkCursor* pCursor = gdk_cursor_new(g_aDirectionCursors[eScrollDirection].m_nCursor);
+					gdk_window_set_cursor(GTK_WIDGET(g_MainWindow.m_pDrawingArea)->window, pCursor);
+					gdk_cursor_unref(pCursor);
 
-					mainwindow_add_history();
+					g_MainWindow.m_bScrolling = TRUE;
+					g_MainWindow.m_eScrollDirection = eScrollDirection;
+					g_MainWindow.m_bScrollMovement = FALSE;	// no movement yet
+
+					mainwindow_set_scroll_timeout();
+				}
+				else {
+					g_MainWindow.m_bMouseDragging = TRUE;
+					g_MainWindow.m_bMouseDragMovement = FALSE;
+					g_MainWindow.m_ptClickLocation.m_nX = nX;
+					g_MainWindow.m_ptClickLocation.m_nY = nY;
 				}
 			}
+			// Left mouse button up?
+			else if(event->type == GDK_BUTTON_RELEASE) {
+				// end mouse dragging, if active
+				if(g_MainWindow.m_bMouseDragging == TRUE) {
+					// restore cursor
+					GdkCursor* pCursor = gdk_cursor_new(GDK_LEFT_PTR);
+					gdk_window_set_cursor(GTK_WIDGET(g_MainWindow.m_pDrawingArea)->window, pCursor);
+					gdk_cursor_unref(pCursor);
 
-			// end scrolling, if active
-			if(g_MainWindow.m_bScrolling == TRUE) {
-				// NOTE: don't restore cursor (mouse could *still* be over screen edge)
-
-				g_MainWindow.m_bScrolling = FALSE;
-				mainwindow_cancel_draw_pretty_timeout();
+					g_MainWindow.m_bMouseDragging = FALSE;
+					if(g_MainWindow.m_bMouseDragMovement) {
+						mainwindow_cancel_draw_pretty_timeout();
+						mainwindow_draw_map(DRAWFLAG_ALL);
 
-				// has there been any movement?
-				if(g_MainWindow.m_bScrollMovement) {
-					g_MainWindow.m_bScrollMovement = FALSE;
-					mainwindow_draw_map(DRAWFLAG_ALL);
+						mainwindow_add_history();
+					}
 				}
-				else {
-					// user clicked the edge of the screen, but so far we haven't moved at all (they released the button too fast)
-					// so consider this a 'click' and just jump a bit in that direction
-					gint nHeight = GTK_WIDGET(g_MainWindow.m_pDrawingArea)->allocation.height;
-					gint nWidth = GTK_WIDGET(g_MainWindow.m_pDrawingArea)->allocation.width;
 
-					gint nDistanceInPixels;
-					if(g_MainWindow.m_eScrollDirection == DIRECTION_N || g_MainWindow.m_eScrollDirection == DIRECTION_S) {
-						// scroll half the height of the screen
-						nDistanceInPixels = nHeight/2;
-					}
-					else if(g_MainWindow.m_eScrollDirection == DIRECTION_E || g_MainWindow.m_eScrollDirection == DIRECTION_W) {
-						nDistanceInPixels = nWidth/2;
+				// end scrolling, if active
+				if(g_MainWindow.m_bScrolling == TRUE) {
+					// NOTE: don't restore cursor (mouse could *still* be over screen edge)
+
+					g_MainWindow.m_bScrolling = FALSE;
+					mainwindow_cancel_draw_pretty_timeout();
+
+					// has there been any movement?
+					if(g_MainWindow.m_bScrollMovement) {
+						g_MainWindow.m_bScrollMovement = FALSE;
+						mainwindow_draw_map(DRAWFLAG_ALL);
 					}
 					else {
-						// half the distance from corner to opposite corner
-						nDistanceInPixels = sqrt(nHeight*nHeight + nWidth*nWidth)/2;
-					}
+						// user clicked the edge of the screen, but so far we haven't moved at all (they released the button too fast)
+						// so consider this a 'click' and just jump a bit in that direction
+						gint nHeight = GTK_WIDGET(g_MainWindow.m_pDrawingArea)->allocation.height;
+						gint nWidth = GTK_WIDGET(g_MainWindow.m_pDrawingArea)->allocation.width;
 
-					mainwindow_scroll_direction(g_MainWindow.m_eScrollDirection, nDistanceInPixels);
+						gint nDistanceInPixels;
+						if(g_MainWindow.m_eScrollDirection == DIRECTION_N || g_MainWindow.m_eScrollDirection == DIRECTION_S) {
+							// scroll half the height of the screen
+							nDistanceInPixels = nHeight/2;
+						}
+						else if(g_MainWindow.m_eScrollDirection == DIRECTION_E || g_MainWindow.m_eScrollDirection == DIRECTION_W) {
+							nDistanceInPixels = nWidth/2;
+						}
+						else {
+							// half the distance from corner to opposite corner
+							nDistanceInPixels = sqrt(nHeight*nHeight + nWidth*nWidth)/2;
+						}
+
+						mainwindow_scroll_direction(g_MainWindow.m_eScrollDirection, nDistanceInPixels);
+					}
+					g_MainWindow.m_eScrollDirection = DIRECTION_NONE;
+					mainwindow_add_history();
 				}
-				g_MainWindow.m_eScrollDirection = DIRECTION_NONE;
-				mainwindow_add_history();
 			}
-		}
-		else if(event->type == GDK_2BUTTON_PRESS) {
-			// can only double click in the middle (not on a scroll border)
-			eScrollDirection = match_border(nX, nY, nWidth, nHeight, BORDER_SCROLL_CLICK_TARGET_SIZE);
-			if(eScrollDirection == DIRECTION_NONE) {
-				animator_destroy(g_MainWindow.m_pAnimator);
-	
-				g_MainWindow.m_bSliding = TRUE;
-				g_MainWindow.m_pAnimator = animator_new(ANIMATIONTYPE_FAST_THEN_SLIDE, SLIDE_TIME_IN_SECONDS);
-	
-				// set startpoint
-				map_get_centerpoint(g_MainWindow.m_pMap, &g_MainWindow.m_ptSlideStartLocation);
-	
-				// set endpoint
-				screenpoint_t ptScreenPoint = {nX, nY};
-				map_windowpoint_to_mappoint(g_MainWindow.m_pMap, &ptScreenPoint, &(g_MainWindow.m_ptSlideEndLocation));
+			else if(event->type == GDK_2BUTTON_PRESS) {
+				// can only double click in the middle (not on a scroll border)
+				eScrollDirection = match_border(nX, nY, nWidth, nHeight, BORDER_SCROLL_CLICK_TARGET_SIZE);
+				if(eScrollDirection == DIRECTION_NONE) {
+					animator_destroy(g_MainWindow.m_pAnimator);
+
+					g_MainWindow.m_bSliding = TRUE;
+					g_MainWindow.m_pAnimator = animator_new(ANIMATIONTYPE_FAST_THEN_SLIDE, SLIDE_TIME_IN_SECONDS);
+
+					// set startpoint
+					map_get_centerpoint(g_MainWindow.m_pMap, &g_MainWindow.m_ptSlideStartLocation);
+
+					// set endpoint
+					screenpoint_t ptScreenPoint = {nX, nY};
+					map_windowpoint_to_mappoint(g_MainWindow.m_pMap, &ptScreenPoint, &(g_MainWindow.m_ptSlideEndLocation));
+				}
 			}
-//			map_center_on_windowpoint(g_MainWindow.m_pMap, nX, nY);
-//			mainwindow_draw_map(DRAWFLAG_ALL);
 		}
+		/*
+		// Right-click?
+	//         else if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
+	//         {
+	//                 // Save click location for use by callback
+	//                 g_MainWindow.m_ptClickLocation.m_nX = nX;
+	//                 g_MainWindow.m_ptClickLocation.m_nY = nY;
+	//
+	//                 // Show popup!
+	//                 gtk_menu_popup(g_MainWindow.m_pMapPopupMenu, NULL, NULL, NULL, NULL, event->button, event->time);
+	//                 return TRUE;
+	//         }
+		//	map_redraw_if_needed();
+		*/
 	}
-	// Right-click?
-//         else if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
-//         {
-//                 // Save click location for use by callback
-//                 g_MainWindow.m_ptClickLocation.m_nX = nX;
-//                 g_MainWindow.m_ptClickLocation.m_nY = nY;
-//
-//                 // Show popup!
-//                 gtk_menu_popup(g_MainWindow.m_pMapPopupMenu, NULL, NULL, NULL, NULL, event->button, event->time);
-//                 return TRUE;
-//         }
-	//	map_redraw_if_needed();
-
+	map_free_hitstruct(g_MainWindow.m_pMap, pHitStruct);
 	return TRUE;
 }
 
@@ -961,13 +975,13 @@
 	gint nWidth = GTK_WIDGET(g_MainWindow.m_pDrawingArea)->allocation.width;
 	gint nHeight = GTK_WIDGET(g_MainWindow.m_pDrawingArea)->allocation.height;
 
+	gint nCursor = GDK_LEFT_PTR;
+
 	if(g_MainWindow.m_bMouseDragging) {
 		g_MainWindow.m_bMouseDragMovement = TRUE;
 
 		// Set it here and no when first clicking because now we know it's a drag (on click it could be a double-click)
-		GdkCursor* pCursor = gdk_cursor_new(GDK_FLEUR);
-		gdk_window_set_cursor(GTK_WIDGET(g_MainWindow.m_pDrawingArea)->window, pCursor);
-		gdk_cursor_unref(pCursor);
+		nCursor = GDK_FLEUR;
 
 		gint nDeltaX = g_MainWindow.m_ptClickLocation.m_nX - nX;
                 gint nDeltaY = g_MainWindow.m_ptClickLocation.m_nY - nY;
@@ -989,9 +1003,7 @@
 
 		if(g_MainWindow.m_eScrollDirection != eScrollDirection) {
 			// update cursor
-			GdkCursor* pCursor = gdk_cursor_new(g_aDirectionCursors[eScrollDirection].m_nCursor);
-			gdk_window_set_cursor(GTK_WIDGET(g_MainWindow.m_pDrawingArea)->window, pCursor);
-			gdk_cursor_unref(pCursor);
+			nCursor = g_aDirectionCursors[eScrollDirection].m_nCursor;
 
 			// update direction if actively scrolling
 			g_MainWindow.m_eScrollDirection = eScrollDirection;
@@ -1005,11 +1017,7 @@
 	else {
 
 		EDirection eScrollDirection = match_border(nX, nY, nWidth, nHeight, BORDER_SCROLL_CLICK_TARGET_SIZE);
-		// just set cursor based on what we're hovering over
-		GdkCursor* pCursor = gdk_cursor_new(g_aDirectionCursors[eScrollDirection].m_nCursor);
-		gdk_window_set_cursor(GTK_WIDGET(g_MainWindow.m_pDrawingArea)->window, pCursor);
-		gdk_cursor_unref(pCursor);
-
+  
 		if(eScrollDirection == DIRECTION_NONE) {
 			// get mouse position on screen
 			screenpoint_t screenpoint;
@@ -1019,30 +1027,49 @@
 			map_windowpoint_to_mappoint(g_MainWindow.m_pMap, &screenpoint, &mappoint);
 	
 			// try to "hit" something on the map. a road, a location, whatever!
-			gchar* pszReturnString = NULL;
-			if(map_hit_test(g_MainWindow.m_pMap, &mappoint, &pszReturnString)) {
+			maphit_t* pHitStruct = NULL;
+			if(map_hit_test(g_MainWindow.m_pMap, &mappoint, &pHitStruct)) {
 				// A hit!  Move the tooltip here, format the text, and show it.
 				tooltip_set_upper_left_corner(g_MainWindow.m_pTooltip, (gint)(event->x_root) + TOOLTIP_OFFSET_X, (gint)(event->y_root) + TOOLTIP_OFFSET_Y);
 
-				gchar* pszMarkup = g_strdup_printf(TOOLTIP_FORMAT, pszReturnString);
+				gchar* pszMarkup = g_strdup_printf(TOOLTIP_FORMAT, pHitStruct->m_pszText);
 				tooltip_set_markup(g_MainWindow.m_pTooltip, pszMarkup);
 				g_free(pszMarkup);
 
+				// choose color based on type of hit
+				if(pHitStruct->m_eHitType == MAP_HITTYPE_LOCATION) {
+					GdkColor clr = {0, LOCATION_TOOLTIP_BG_COLOR};
+					tooltip_set_bg_color(g_MainWindow.m_pTooltip, &clr);
+
+					// also set mouse cursor to hand
+					nCursor = GDK_HAND2;
+				}
+				else {
+					GdkColor clr = {0, ROAD_TOOLTIP_BG_COLOR};
+					tooltip_set_bg_color(g_MainWindow.m_pTooltip, &clr);
+				}
+
 				tooltip_show(g_MainWindow.m_pTooltip);	// ensure it's visible
-				g_free(pszReturnString); pszReturnString = NULL;
+				
+				map_free_hitstruct(g_MainWindow.m_pMap, pHitStruct);
 			}
 			else {
 				// no hit. hide the tooltip
 				tooltip_hide(g_MainWindow.m_pTooltip);
 			}
-			g_assert(pszReturnString == NULL);
-
 		}
 		else {
+			nCursor = g_aDirectionCursors[eScrollDirection].m_nCursor;
+
 			// using a funky cursor. hide the tooltip
 			tooltip_hide(g_MainWindow.m_pTooltip);
 		}
 	}
+	// just set cursor based on what we're hovering over
+	GdkCursor* pCursor = gdk_cursor_new(nCursor);
+	gdk_window_set_cursor(GTK_WIDGET(g_MainWindow.m_pDrawingArea)->window, pCursor);
+	gdk_cursor_unref(pCursor);
+
 	return FALSE;
 }
 
@@ -1385,6 +1412,7 @@
 
 void mainwindow_on_addpointmenuitem_activate(GtkWidget *_unused, gpointer* __unused)
 {
+/*
 	mappoint_t point;
 	map_windowpoint_to_mappoint(g_MainWindow.m_pMap, &g_MainWindow.m_ptClickLocation, &point);
 
@@ -1398,6 +1426,7 @@
 	else {
 		g_print("insert failed\n");
 	}
+*/
 }
 
 void mainwindow_sidebar_set_tab(gint nTab)

Index: map.c
===================================================================
RCS file: /cvs/cairo/roadster/src/map.c,v
retrieving revision 1.35
retrieving revision 1.36
diff -u -d -r1.35 -r1.36
--- map.c	28 Mar 2005 18:49:50 -0000	1.35
+++ map.c	29 Mar 2005 09:16:20 -0000	1.36
@@ -36,6 +36,7 @@
 #include "point.h"
 #include "layers.h"
 #include "locationset.h"
+#include "location.h"
 #include "scenemanager.h"
 
 #ifdef THREADED_RENDERING
@@ -64,15 +65,18 @@
 /* Prototypes */
 
 // data loading
-static gboolean map_data_load_tiles(map_t* pMap, maprect_t* pRect);	// ensure tiles
-static gboolean map_data_load(map_t* pMap, maprect_t* pRect);
+static gboolean map_data_load_tiles(map_t* pMap, maprect_t* pRect);
+static gboolean map_data_load_geometry(map_t* pMap, maprect_t* pRect);
+static gboolean map_data_load_locations(map_t* pMap, maprect_t* pRect);
 
 // hit testing
-static gboolean map_hit_test_layer_roads(GPtrArray* pPointStringsArray, gdouble fMaxDistance, mappoint_t* pHitPoint, gchar** ppReturnString);
+static gboolean map_hit_test_layer_roads(GPtrArray* pPointStringsArray, gdouble fMaxDistance, mappoint_t* pHitPoint, maphit_t** ppReturnStruct);
 static gboolean map_hit_test_line(mappoint_t* pPoint1, mappoint_t* pPoint2, mappoint_t* pHitPoint, gdouble fDistance, mappoint_t* pReturnClosestPoint);
-
 static ESide map_side_test_line(mappoint_t* pPoint1, mappoint_t* pPoint2, mappoint_t* pClosestPointOnLine, mappoint_t* pHitPoint);
 
+static gboolean map_hit_test_locationsets(map_t* pMap, rendermetrics_t* pRenderMetrics, mappoint_t* pHitPoint, maphit_t** ppReturnStruct);
+static gboolean map_hit_test_locations(map_t* pMap, rendermetrics_t* pRenderMetrics, GPtrArray* pLocationsArray, mappoint_t* pHitPoint, maphit_t** ppReturnStruct);
+
 static void map_data_clear(map_t* pMap);
 void map_get_render_metrics(map_t* pMap, rendermetrics_t* pMetrics);
 
@@ -141,6 +145,27 @@
 {
 }
 
+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);
@@ -154,6 +179,8 @@
 					  TRUE,  /* clear to 0 (?) */
 					  sizeof(gint));
 
+	map_init_location_hash(pMap);
+
 	pMap->m_pTargetWidget = pTargetWidget;
 
 	scenemanager_new(&(pMap->m_pSceneManager));
@@ -174,6 +201,27 @@
 	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
+	}
+	else {
+		// 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);
+}
+
 void map_draw(map_t* pMap, gint nDrawFlags)
 {
 	g_assert(pMap != NULL);
@@ -193,7 +241,6 @@
 	TIMER_BEGIN(loadtimer, "--- BEGIN ALL DB LOAD");
 	map_data_clear(pMap);
 	map_data_load_tiles(pMap, &(pRenderMetrics->m_rWorldBoundingBox));
-//	locationset_load_locations(&(pRenderMetrics->m_rWorldBoundingBox));
 	TIMER_END(loadtimer, "--- END ALL DB LOAD");
 
 	gint nRenderMode = RENDERMODE_FAST;
@@ -472,12 +519,13 @@
 			rect.m_B.m_fLatitude = fLatStart + ((gdouble)(nLat+1) * MAP_TILE_WIDTH);
 			rect.m_B.m_fLongitude = fLonStart + ((gdouble)(nLon+1) * MAP_TILE_WIDTH);
 
-			map_data_load(pMap, &rect);
+			map_data_load_geometry(pMap, &rect);
+			map_data_load_locations(pMap, &rect);
 		}
 	}
 }
 
-static gboolean map_data_load(map_t* pMap, maprect_t* pRect)
+static gboolean map_data_load_geometry(map_t* pMap, maprect_t* pRect)
 {
 	g_return_val_if_fail(pMap != NULL, FALSE);
 	g_return_val_if_fail(pRect != NULL, FALSE);
@@ -582,8 +630,90 @@
 	}	
 }
 
+#define MIN_ZOOMLEVEL_FOR_LOCATIONS	(6)
+
+static gboolean map_data_load_locations(map_t* pMap, maprect_t* pRect)
+{
+	g_return_val_if_fail(pMap != NULL, FALSE);
+	g_return_val_if_fail(pRect != NULL, FALSE);
+
+	db_resultset_t* pResultSet = NULL;
+	db_row_t aRow;
+
+	gint nZoomLevel = map_get_zoomlevel(pMap);
+
+	if(nZoomLevel <= MIN_ZOOMLEVEL_FOR_LOCATIONS) {
+		return TRUE;
+	}
+
+	TIMER_BEGIN(mytimer, "BEGIN Locations LOAD");
+
+	// generate SQL
+	gchar* pszSQL;
+	pszSQL = g_strdup_printf(
+		"SELECT Location.ID, Location.LocationSetID, AsBinary(Location.Coordinates), LocationAttributeValue_Name.Value"	// LocationAttributeValue_Name.Value is the "Name" field of this Location
+		" FROM Location"
+		" LEFT JOIN LocationAttributeValue AS LocationAttributeValue_Name ON (LocationAttributeValue_Name.LocationID=Location.ID AND LocationAttributeValue_Name.AttributeNameID=%d)"
+		" WHERE"
+		" MBRIntersects(GeomFromText('Polygon((%f %f,%f %f,%f %f,%f %f,%f %f))'), Coordinates)",
+		LOCATION_ATTRIBUTE_ID_NAME,	// attribute ID for 'name'
+		pRect->m_A.m_fLatitude, pRect->m_A.m_fLongitude, 	// upper left
+		pRect->m_A.m_fLatitude, pRect->m_B.m_fLongitude, 	// upper right
+		pRect->m_B.m_fLatitude, pRect->m_B.m_fLongitude, 	// bottom right
+		pRect->m_B.m_fLatitude, pRect->m_A.m_fLongitude, 	// bottom left
+		pRect->m_A.m_fLatitude, pRect->m_A.m_fLongitude		// upper left again
+		);
+	//g_print("sql: %s\n", pszSQL);
+
+	db_query(pszSQL, &pResultSet);
+	g_free(pszSQL);
+
+	TIMER_SHOW(mytimer, "after query");
+
+	guint32 uRowCount = 0;
+	if(pResultSet) {
+		while((aRow = db_fetch_row(pResultSet))) {
+			uRowCount++;
+
+			// aRow[0] is ID
+			// aRow[1] is LocationSetID
+			// aRow[2] is Coordinates in mysql's binary format
+			// aRow[3] is Name
+
+			// Get layer type that this belongs on
+			gint nLocationSetID = atoi(aRow[1]);
+
+			// Extract
+			location_t* pNewLocation = NULL;
+			location_alloc(&pNewLocation);
+			
+			pNewLocation->m_nID = atoi(aRow[0]);
+
+			// Parse coordinates
+			db_parse_wkb_point(aRow[2], &(pNewLocation->m_Coordinates));
+
+			// make a copy of the name field, or "" (never leave it == NULL)
+			pNewLocation->m_pszName = g_strdup(aRow[3] != NULL ? aRow[3] : "");
+			map_store_location(pMap, pNewLocation, nLocationSetID);
+		} // end while loop on rows
+		//g_print("[%d rows]\n", uRowCount);
+		TIMER_SHOW(mytimer, "after rows retrieved");
+
+		db_free_result(pResultSet);
+		TIMER_SHOW(mytimer, "after free results");
+		TIMER_END(mytimer, "END Locations LOAD");
+
+		return TRUE;
+	}
+	else {
+//		g_print(" no rows\n");
+		return FALSE;
+	}	
+}
+
 static void map_data_clear(map_t* pMap)
 {
+	// Clear layers
 	gint i,j;
 	for(i=0 ; i<NUM_ELEMS(pMap->m_apLayerData) ; i++) {
 		maplayer_data_t* pLayerData = pMap->m_apLayerData[i];
@@ -595,6 +725,9 @@
 		}
 		g_assert(pLayerData->m_pRoadsArray->len == 0);
 	}
+
+	// Clear locations
+	map_init_location_hash(pMap);
 }
 
 double map_get_distance_in_meters(mappoint_t* pA, mappoint_t* pB)
@@ -627,9 +760,9 @@
 	map_get_render_metrics(pMap, &metrics);
 
 	gdouble fX1 = SCALE_X(&metrics, p1->m_fLongitude);
-	gdouble fY1 = SCALE_Y(&metrics, p1->m_fLongitude);
+	gdouble fY1 = SCALE_Y(&metrics, p1->m_fLatitude);
 	gdouble fX2 = SCALE_X(&metrics, p2->m_fLongitude);
-	gdouble fY2 = SCALE_Y(&metrics, p2->m_fLongitude);
+	gdouble fY2 = SCALE_Y(&metrics, p2->m_fLatitude);
 
 	gdouble fDeltaX = fX2 - fX1;
 	gdouble fDeltaY = fY2 - fY1;
@@ -653,9 +786,24 @@
 //  Hit Testing
 // ========================================================
 
+void map_free_hitstruct(map_t* pMap, maphit_t* pHitStruct)
+{
+	if(pHitStruct == NULL) return;
+
+	g_free(pHitStruct->m_pszText);
+	g_free(pHitStruct);
+}
+
 // XXX: perhaps make map_hit_test return a more complex structure indicating what type of hit it is?
-gboolean map_hit_test(map_t* pMap, mappoint_t* pMapPoint, gchar** ppReturnString)
+gboolean map_hit_test(map_t* pMap, mappoint_t* pMapPoint, maphit_t** ppReturnStruct)
 {
+	rendermetrics_t rendermetrics;
+	map_get_render_metrics(pMap, &rendermetrics);
+
+	if(map_hit_test_locationsets(pMap, &rendermetrics, pMapPoint, ppReturnStruct)) {
+		return TRUE;
+	}
+
 	// 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--) {
@@ -665,25 +813,25 @@
 		gdouble fLineWidth = max(g_aLayers[nLayer]->m_Style.m_aSubLayers[0].m_afLineWidths[pMap->m_uZoomLevel-1],
 					 g_aLayers[nLayer]->m_Style.m_aSubLayers[1].m_afLineWidths[pMap->m_uZoomLevel-1]);
 
-#define EXTRA_CLICKABLE_ROAD_IN_PIXELS	(8)
+#define EXTRA_CLICKABLE_ROAD_IN_PIXELS	(3)
 
 		// make thin roads a little easier to hit
-		fLineWidth = max(fLineWidth, MIN_ROAD_HIT_TARGET_WIDTH);
+	       // fLineWidth = max(fLineWidth, MIN_ROAD_HIT_TARGET_WIDTH);
 
 		// XXX: hack, map_pixels should really take a floating point instead.
 		gdouble fMaxDistance = map_pixels_to_degrees(pMap, 1, pMap->m_uZoomLevel) * ((fLineWidth/2) + EXTRA_CLICKABLE_ROAD_IN_PIXELS);  // half width on each side
 
-		if(map_hit_test_layer_roads(pMap->m_apLayerData[nLayer]->m_pRoadsArray, fMaxDistance, pMapPoint, ppReturnString)) {
+		if(map_hit_test_layer_roads(pMap->m_apLayerData[nLayer]->m_pRoadsArray, fMaxDistance, pMapPoint, ppReturnStruct)) {
 			return TRUE;
 		}
 	}
 	return FALSE;
 }
 
-static gboolean map_hit_test_layer_roads(GPtrArray* pRoadsArray, gdouble fMaxDistance, mappoint_t* pHitPoint, gchar** ppReturnString)
+static gboolean map_hit_test_layer_roads(GPtrArray* pRoadsArray, gdouble fMaxDistance, mappoint_t* pHitPoint, maphit_t** ppReturnStruct)
 {
-	g_assert(ppReturnString != NULL);
-	g_assert(*ppReturnString == NULL);	// pointer to null pointer
+	g_assert(ppReturnStruct != NULL);
+	g_assert(*ppReturnStruct == NULL);	// pointer to null pointer
 
 	/* this is helpful for testing with the g_print()s in map_hit_test_line() */
 /*         mappoint_t p1 = {2,2};                */
@@ -708,9 +856,12 @@
 
 			// hit test this line
 			if(map_hit_test_line(pPoint1, pPoint2, pHitPoint, fMaxDistance, &pointClosest)) {
-				// got a hit
+				// fill out a new maphit_t struct with details
+				maphit_t* pHitStruct = g_new0(maphit_t, 1);
+				pHitStruct->m_eHitType = MAP_HITTYPE_ROAD;
+
 				if(pRoad->m_pszName[0] == '\0') {
-					*ppReturnString = g_strdup("<i>unnamed road</i>");
+					pHitStruct->m_pszText = g_strdup("<i>unnamed road</i>");
 				}
 				else {
 					ESide eSide = map_side_test_line(pPoint1, pPoint2, &pointClosest, pHitPoint);
@@ -727,15 +878,16 @@
 					}
 
 					if(nAddressStart == 0 || nAddressEnd == 0) {
-						*ppReturnString = g_strdup_printf("%s", pRoad->m_pszName);
+						pHitStruct->m_pszText = g_strdup_printf("%s", pRoad->m_pszName);
 					}
 					else {
 						gint nMinAddres = min(nAddressStart, nAddressEnd);
 						gint nMaxAddres = max(nAddressStart, nAddressEnd);
 
-						*ppReturnString = g_strdup_printf("%s <b>#%d-%d</b>", pRoad->m_pszName, nMinAddres, nMaxAddres);
+						pHitStruct->m_pszText = g_strdup_printf("%s <b>#%d-%d</b>", pRoad->m_pszName, nMinAddres, nMaxAddres);
 					}
 				}
+				*ppReturnStruct = pHitStruct;
 				return TRUE;
 			}
 		}
@@ -858,6 +1010,82 @@
 	}
 }
 
+// hit test all locations
+static gboolean map_hit_test_locationsets(map_t* pMap, rendermetrics_t* pRenderMetrics, mappoint_t* pHitPoint, maphit_t** ppReturnStruct)
+{
+	gdouble fMaxDistance = map_pixels_to_degrees(pMap, 1, pMap->m_uZoomLevel) * 3;	// XXX: don't hardcode distance :)
+
+	const GPtrArray* pLocationSetsArray = locationset_get_array();
+	gint i;
+	for(i=0 ; i<pLocationSetsArray->len ; i++) {
+		locationset_t* pLocationSet = g_ptr_array_index(pLocationSetsArray, i);
+
+		// XXX: check that it's visible
+
+		// 2. Get array of Locations from the hash table using LocationSetID
+		GPtrArray* pLocationsArray;
+		pLocationsArray = g_hash_table_lookup(pMap->m_pLocationArrayHashTable, &(pLocationSet->m_nID));
+		if(pLocationsArray != NULL) {
+			if(map_hit_test_locations(pMap, pRenderMetrics, pLocationsArray, pHitPoint, ppReturnStruct)) {
+				return TRUE;
+			}
+		}
+		else {
+			// none loaded
+		}
+	}
+	return FALSE;
+}
+
+// hit-test an array of locations
+static gboolean map_hit_test_locations(map_t* pMap, rendermetrics_t* pRenderMetrics, GPtrArray* pLocationsArray, mappoint_t* pHitPoint, maphit_t** ppReturnStruct)
+{
+	gint i;
+	for(i=0 ; i<pLocationsArray->len ; i++) {
+		location_t* pLocation = g_ptr_array_index(pLocationsArray, i);
+
+		// bounding box test
+		if(pLocation->m_Coordinates.m_fLatitude < pRenderMetrics->m_rWorldBoundingBox.m_A.m_fLatitude
+		   || pLocation->m_Coordinates.m_fLongitude < pRenderMetrics->m_rWorldBoundingBox.m_A.m_fLongitude
+		   || pLocation->m_Coordinates.m_fLatitude > pRenderMetrics->m_rWorldBoundingBox.m_B.m_fLatitude
+		   || pLocation->m_Coordinates.m_fLongitude > pRenderMetrics->m_rWorldBoundingBox.m_B.m_fLongitude)
+		{
+		    continue;   // not visible
+		}
+
+		gdouble fX1 = SCALE_X(pRenderMetrics, pLocation->m_Coordinates.m_fLongitude);
+		gdouble fY1 = SCALE_Y(pRenderMetrics, pLocation->m_Coordinates.m_fLatitude);
+		gdouble fX2 = SCALE_X(pRenderMetrics, pHitPoint->m_fLongitude);
+		gdouble fY2 = SCALE_Y(pRenderMetrics, pHitPoint->m_fLatitude);
+
+		gdouble fDeltaX = fX2 - fX1;
+		gdouble fDeltaY = fY2 - fY1;
+
+		fDeltaX = abs(fDeltaX);
+		fDeltaY = abs(fDeltaY);
+
+		if(fDeltaX <= 3 && fDeltaY <= 3) {
+			// fill out a new maphit_t struct with details
+			maphit_t* pHitStruct = g_new0(maphit_t, 1);
+			pHitStruct->m_eHitType = MAP_HITTYPE_LOCATION;
+			pHitStruct->m_LocationHit.m_nLocationID = pLocation->m_nID;
+
+			pHitStruct->m_LocationHit.m_Coordinates.m_fLatitude = pLocation->m_Coordinates.m_fLatitude;
+			pHitStruct->m_LocationHit.m_Coordinates.m_fLongitude = pLocation->m_Coordinates.m_fLongitude;
+
+			if(pLocation->m_pszName[0] == '\0') {
+				pHitStruct->m_pszText = g_strdup_printf("<i>unnamed POI %d</i>", pLocation->m_nID);
+			}
+			else {
+				pHitStruct->m_pszText = g_strdup(pLocation->m_pszName);
+			}
+			*ppReturnStruct = pHitStruct;
+			return TRUE;
+		}
+	}
+	return FALSE;
+}
+
 gboolean map_can_zoom_in(map_t* pMap)
 {
 	// can we increase zoom level?

Index: map.h
===================================================================
RCS file: /cvs/cairo/roadster/src/map.h,v
retrieving revision 1.14
retrieving revision 1.15
diff -u -d -r1.14 -r1.15
--- map.h	28 Mar 2005 18:49:50 -0000	1.14
+++ map.h	29 Mar 2005 09:16:20 -0000	1.15
@@ -146,23 +146,51 @@
 } maplayer_data_t;
 
 typedef struct {
-	// Mutex and the data it controls (always lock before reading/writing)
-	//GMutex* m_pDataMutex;
-	 mappoint_t 			m_MapCenter;
-	 dimensions_t 			m_MapDimensions;
-	 guint16 			m_uZoomLevel;
-	 maplayer_data_t* m_apLayerData[ NUM_LAYERS + 1 ];
-	 GtkWidget*			m_pTargetWidget;
-	 scenemanager_t*		m_pSceneManager;
+	GPtrArray* m_pLocationsArray;
+} maplayer_locations_t;
 
-	 GArray*			m_pTracksArray;
+typedef struct
+{
+	mappoint_t 		m_MapCenter;
+	dimensions_t 		m_MapDimensions;
+	guint16 		m_uZoomLevel;
+	GtkWidget		*m_pTargetWidget;
+	scenemanager_t		*m_pSceneManager;
+
+	// data
+	GArray			*m_pTracksArray;
+	maplayer_data_t		*m_apLayerData[ NUM_LAYERS + 1 ];
+
+	// Locationsets
+	GHashTable		*m_pLocationArrayHashTable;
 
 	// Mutex and the data it controls (always lock before reading/writing)
 	//GMutex* m_pPixmapMutex;
-	 GdkPixmap* m_pPixmap;
+	GdkPixmap* m_pPixmap;
 } map_t;
 
 typedef enum {
+	MAP_HITTYPE_LOCATION=1,
+	MAP_HITTYPE_ROAD=2,
+} EMapHitType;
+
+typedef struct {
+	EMapHitType m_eHitType;
+	gchar* m_pszText;
+	union {
+		struct {
+			gint m_nLocationID;
+			mappoint_t m_Coordinates;
+		} m_LocationHit;
+
+		struct {
+			gint m_nRoadID;
+			mappoint_t m_ClosestPoint;
+		} m_RoadHit;
+	};
+} maphit_t;
+
+typedef enum {
 	SUBLAYER_RENDERTYPE_LINES,
 	SUBLAYER_RENDERTYPE_POLYGONS,
 	SUBLAYER_RENDERTYPE_LINE_LABELS,
@@ -228,7 +256,8 @@
 
 void map_add_track(map_t* pMap, gint hTrack);
 
-gboolean map_hit_test(map_t* pMap, mappoint_t* pMapPoint, gchar** ppReturnString);
+gboolean map_hit_test(map_t* pMap, mappoint_t* pMapPoint, maphit_t** ppReturnStruct);
+void map_free_hitstruct(map_t* pMap, maphit_t* pHitStruct);
 
 gboolean map_can_zoom_in(map_t* pMap);
 gboolean map_can_zoom_out(map_t* pMap);

Index: map_draw_gdk.c
===================================================================
RCS file: /cvs/cairo/roadster/src/map_draw_gdk.c,v
retrieving revision 1.11
retrieving revision 1.12
diff -u -d -r1.11 -r1.12
--- map_draw_gdk.c	28 Mar 2005 18:49:50 -0000	1.11
+++ map_draw_gdk.c	29 Mar 2005 09:16:20 -0000	1.12
@@ -39,12 +39,15 @@
 #include "layers.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, sublayerstyle_t* pSubLayerStyle, textlabelstyle_t* pLabelStyle);
 static void map_draw_gdk_layer_roads(map_t* pMap, GdkPixmap* pPixmap, rendermetrics_t* pRenderMetrics, GPtrArray* pRoadsArray, sublayerstyle_t* pSubLayerStyle, textlabelstyle_t* pLabelStyle);
 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);
 
 void map_draw_gdk(map_t* pMap, rendermetrics_t* pRenderMetrics, GdkPixmap* pPixmap, gint nDrawFlags)
 {
@@ -85,6 +88,7 @@
 		}
 
 		map_draw_gdk_tracks(pMap, pPixmap, pRenderMetrics);
+		map_draw_gdk_locations(pMap, pPixmap, pRenderMetrics);
 	}
 
 	// 3. Labels
@@ -316,3 +320,71 @@
    		}
 	}
 }
+
+static void map_draw_gdk_locations(map_t* pMap, GdkPixmap* pPixmap, rendermetrics_t* pRenderMetrics)
+{
+	const GPtrArray* pLocationSetsArray = locationset_get_array();
+	gint i;
+	for(i=0 ; i<pLocationSetsArray->len ; i++) {
+		locationset_t* pLocationSet = g_ptr_array_index(pLocationSetsArray, i);
+
+
+		// 2. Get array of Locations from the hash table using LocationSetID
+		GPtrArray* pLocationsArray;
+		pLocationsArray = g_hash_table_lookup(pMap->m_pLocationArrayHashTable, &(pLocationSet->m_nID));
+		if(pLocationsArray != NULL) {
+			// found existing array
+			map_draw_gdk_locationset(pMap, pPixmap, pRenderMetrics, pLocationSet, pLocationsArray);
+		}
+		else {
+			// none to draw
+		}
+	}
+}
+
+static void map_draw_gdk_locationset(map_t* pMap, GdkPixmap* pPixmap, rendermetrics_t* pRenderMetrics, locationset_t* pLocationSet, GPtrArray* pLocationsArray)
+{
+	gint i;
+	for(i=0 ; i<pLocationsArray->len ; i++) {
+		location_t* pLocation = g_ptr_array_index(pLocationsArray, i);
+
+		// bounding box test
+		if(pLocation->m_Coordinates.m_fLatitude < pRenderMetrics->m_rWorldBoundingBox.m_A.m_fLatitude
+		   || pLocation->m_Coordinates.m_fLongitude < pRenderMetrics->m_rWorldBoundingBox.m_A.m_fLongitude
+		   || pLocation->m_Coordinates.m_fLatitude > pRenderMetrics->m_rWorldBoundingBox.m_B.m_fLatitude
+		   || pLocation->m_Coordinates.m_fLongitude > pRenderMetrics->m_rWorldBoundingBox.m_B.m_fLongitude)
+		{
+		    continue;   // not visible
+		}
+
+		gint nX = (gint)SCALE_X(pRenderMetrics, pLocation->m_Coordinates.m_fLongitude);
+		gint nY = (gint)SCALE_Y(pRenderMetrics, pLocation->m_Coordinates.m_fLatitude);
+
+//		g_print("drawing at %d,%d\n", nX,nY);
+		
+		GdkGC* pGC = pMap->m_pTargetWidget->style->fg_gc[GTK_WIDGET_STATE(pMap->m_pTargetWidget)];
+
+		GdkColor clr1;
+		clr1.red = 20/255.0 * 65535;
+		clr1.green = 135/255.0 * 65535;
+		clr1.blue = 20/255.0 * 65535;
+		GdkColor clr2;
+		clr2.red = 255/255.0 * 65535;
+		clr2.green = 255/255.0 * 65535;
+		clr2.blue = 255/255.0 * 65535;
+
+		gdk_gc_set_rgb_fg_color(pGC, &clr1);
+		gdk_draw_rectangle(pPixmap, pGC, TRUE, 
+					nX-3,nY-3,
+					7, 7);
+		gdk_gc_set_rgb_fg_color(pGC, &clr2);
+		gdk_draw_rectangle(pPixmap, pGC, TRUE, 
+					nX-2,nY-2,
+					5, 5);
+		gdk_gc_set_rgb_fg_color(pGC, &clr1);
+		gdk_draw_rectangle(pPixmap, pGC, TRUE, 
+					nX-1,nY-1,
+					3, 3);
+	}
+}
+

Index: search_road.c
===================================================================
RCS file: /cvs/cairo/roadster/src/search_road.c,v
retrieving revision 1.16
retrieving revision 1.17
diff -u -d -r1.16 -r1.17
--- search_road.c	28 Mar 2005 18:49:50 -0000	1.16
+++ search_road.c	29 Mar 2005 09:16:20 -0000	1.17
@@ -363,7 +363,7 @@
 	g_free(pszStateClause);
 
 //	g_strlcpy(azQuery, , MAX_QUERY);
-	g_print("SQL: %s\n", azQuery);
+	//g_print("SQL: %s\n", azQuery);
 
 	db_resultset_t* pResultSet;
 	if(db_query(azQuery, &pResultSet)) {

Index: tooltip.c
===================================================================
RCS file: /cvs/cairo/roadster/src/tooltip.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -d -r1.2 -r1.3
--- tooltip.c	28 Mar 2005 18:49:50 -0000	1.2
+++ tooltip.c	29 Mar 2005 09:16:20 -0000	1.3
@@ -39,7 +39,7 @@
 	
 	// create tooltip window
 	pNew->m_pWindow = GTK_WINDOW(gtk_window_new(GTK_WINDOW_POPUP));
-	gtk_widget_set_name(GTK_WIDGET(pNew->m_pWindow), "gtk-tooltips");
+	//gtk_widget_set_name(GTK_WIDGET(pNew->m_pWindow), "gtk-tooltips");
 	gtk_widget_add_events(GTK_WIDGET(pNew->m_pWindow), GDK_POINTER_MOTION_MASK | GDK_EXPOSURE_MASK);
 	g_signal_connect(G_OBJECT(pNew->m_pWindow), "motion_notify_event", G_CALLBACK(tooltip_on_mouse_motion), NULL);
 	gtk_window_set_resizable(pNew->m_pWindow, FALSE);	// FALSE means window will resize to hug the label.
@@ -49,6 +49,7 @@
 	gtk_frame_set_shadow_type(pFrame, GTK_SHADOW_IN);
 	gtk_container_add(GTK_CONTAINER(pNew->m_pWindow), GTK_WIDGET(pFrame));
 
+
 	// create label and add to frame
 	pNew->m_pLabel = GTK_LABEL(gtk_label_new("testing"));
 	gtk_container_add(GTK_CONTAINER(pFrame), GTK_WIDGET(pNew->m_pLabel));
@@ -63,6 +64,11 @@
 	gtk_label_set_markup(pTooltip->m_pLabel, pszMarkup);
 }
 
+void tooltip_set_bg_color(tooltip_t* pTooltip, GdkColor* pColor)
+{
+	gtk_widget_modify_bg(GTK_WIDGET(pTooltip->m_pWindow), GTK_STATE_NORMAL, pColor);
+}
+
 void tooltip_set_upper_left_corner(tooltip_t* pTooltip, gint nX, gint nY)
 {
 	gtk_window_move(pTooltip->m_pWindow, nX, nY);

Index: tooltip.h
===================================================================
RCS file: /cvs/cairo/roadster/src/tooltip.h,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -d -r1.1 -r1.2
--- tooltip.h	23 Mar 2005 09:21:49 -0000	1.1
+++ tooltip.h	29 Mar 2005 09:16:20 -0000	1.2
@@ -37,5 +37,6 @@
 void tooltip_set_upper_left_corner(tooltip_t* pTooltip, gint nX, gint nY);
 void tooltip_show(tooltip_t* pTooltip);
 void tooltip_hide(tooltip_t* pTooltip);
+void tooltip_set_bg_color(tooltip_t* pTooltip, GdkColor* pColor);
 
 #endif




More information about the cairo-commit mailing list