[cairo-commit] roadster/src Makefile.am, 1.15, 1.16 db.c, 1.17, 1.18 import_tiger.c, 1.14, 1.15 mainwindow.c, 1.28, 1.29 map.c, 1.30, 1.31 map.h, 1.9, 1.10 tooltip.c, NONE, 1.1 tooltip.h, NONE, 1.1

Ian McIntosh commit at pdx.freedesktop.org
Wed Mar 23 01:21:51 PST 2005


Committed by: ian

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

Modified Files:
	Makefile.am db.c import_tiger.c mainwindow.c map.c map.h 
Added Files:
	tooltip.c tooltip.h 
Log Message:
	* src/tooltip.h:
	* src/tooltip.c: Added.
	* src/mainwindow.c: Added tooltip (currently only showing name of road under mouse).  Change border-scroll corner hit targets to be L shaped and take 1/3 of each border.
	* src/map.c: Added hit testing: the ability to test a point for proximity to lines (roads).
	* src/*: Cleanup and code commenting.


Index: Makefile.am
===================================================================
RCS file: /cvs/cairo/roadster/src/Makefile.am,v
retrieving revision 1.15
retrieving revision 1.16
diff -u -d -r1.15 -r1.16
--- Makefile.am	20 Mar 2005 10:57:05 -0000	1.15
+++ Makefile.am	23 Mar 2005 09:21:49 -0000	1.16
@@ -50,7 +50,8 @@
 	prefs.c\
 	animator.c\
 	gfreelist.c\
-	history.c
+	history.c\
+	tooltip.c
 
 roadster_LDADD = \
 	$(GNOME_LIBS) \

Index: db.c
===================================================================
RCS file: /cvs/cairo/roadster/src/db.c,v
retrieving revision 1.17
retrieving revision 1.18
diff -u -d -r1.17 -r1.18
--- db.c	20 Mar 2005 03:37:09 -0000	1.17
+++ db.c	23 Mar 2005 09:21:49 -0000	1.18
@@ -165,7 +165,6 @@
 		g_warning("mysql_real_connect failed: %s\n", mysql_error(pMySQLConnection));
 		return FALSE;
 	}
-//	db_enable_keys(); // just in case
 
 	// on success, alloc our connection struct and fill it
 	db_connection_t* pNewConnection = g_new0(db_connection_t, 1);
@@ -523,74 +522,68 @@
 
 	// Road
 	db_query("CREATE TABLE IF NOT EXISTS Road("
-		" ID INT4 UNSIGNED NOT NULL AUTO_INCREMENT,"
+		" ID INT4 UNSIGNED NOT NULL AUTO_INCREMENT,"	// XXX: can we get away with INT3 ?
 		" TypeID INT1 UNSIGNED NOT NULL,"
 
-		" RoadNameID INT4 UNSIGNED NOT NULL,"
+		" RoadNameID INT3 UNSIGNED NOT NULL,"		// NOTE: 3 bytes
 
 		" AddressLeftStart INT2 UNSIGNED NOT NULL,"
 		" AddressLeftEnd INT2 UNSIGNED NOT NULL,"
 		" AddressRightStart INT2 UNSIGNED NOT NULL,"
 		" AddressRightEnd INT2 UNSIGNED NOT NULL,"
-		
-		" CityLeftID INT4 UNSIGNED NOT NULL,"
-		" CityRightID INT4 UNSIGNED NOT NULL,"
-		
+
+		" CityLeftID INT3 UNSIGNED NOT NULL,"		// NOTE: 3 bytes
+		" CityRightID INT3 UNSIGNED NOT NULL,"		// NOTE: 3 bytes
+
 		" ZIPCodeLeft CHAR(6) NOT NULL,"
 		" ZIPCodeRight CHAR(6) NOT NULL,"
 
 		" Coordinates point NOT NULL,"
 
 	    // lots of indexes:
-		" PRIMARY KEY (ID),"
-		" INDEX(TypeID),"
+		" 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),"
-		" INDEX(AddressRightStart, AddressRightEnd),"
+//	" 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
 	db_query("CREATE TABLE IF NOT EXISTS RoadName("
-		" ID INT4 UNSIGNED NOT NULL auto_increment,"
+		" ID INT3 UNSIGNED NOT NULL auto_increment,"	// NOTE: 3 bytes
 		" Name VARCHAR(30) NOT NULL,"
 		" SuffixID INT1 UNSIGNED NOT NULL,"
-		" PRIMARY KEY (ID),"
-		" UNIQUE KEY (Name(15), SuffixID));", NULL);
-
-	// Road_RoadName
-//         db_query("CREATE TABLE IF NOT EXISTS Road_RoadName("
-//                 " RoadID INT4 UNSIGNED NOT NULL,"
-//                 " RoadNameID INT4 UNSIGNED NOT NULL,"
-//
-//                 " PRIMARY KEY (RoadID, RoadNameID),"    // allows search on (RoadID,RoadName) and just (RoadID)
-//                 " INDEX(RoadNameID));", NULL);          // allows search the other way, going from a Name to a RoadID
+		" PRIMARY KEY (ID),"			// for joining RoadName to Road 
+		" UNIQUE KEY (Name(7)));", NULL);	// for searching by RoadName. 7 is enough for decent uniqueness(?)
 
 	// City
 	db_query("CREATE TABLE IF NOT EXISTS City("
 		// a unique ID for the value
-		" ID INT4 UNSIGNED NOT NULL AUTO_INCREMENT,"
-		" StateID INT4 UNSIGNED NOT NULL,"
-		" Name CHAR(60) NOT NULL,"
+		" ID INT3 UNSIGNED NOT NULL AUTO_INCREMENT,"	// NOTE: 3 bytes
+		" StateID INT2 UNSIGNED NOT NULL,"		// NOTE: 2 bytes
+		" Name CHAR(60) NOT NULL,"			// are city names ever 60 chars anyway??  TIGER think so
 		" PRIMARY KEY (ID),"
-		" INDEX (StateID),"	// for finding all cities by state (needed?)
-		" INDEX (Name(15)));"	// only index the first X chars of name (who types more than that?) (are city names ever 60 chars anyway??  TIGER think so)
+		" INDEX (StateID),"				// for finding all cities by state (needed?)
+		" INDEX (Name(6)));"				// 6 is enough for decent uniqueness.
 	    ,NULL);
 
 	// State
 	db_query("CREATE TABLE IF NOT EXISTS State("
 		// a unique ID for the value
-		" ID INT4 UNSIGNED NOT NULL AUTO_INCREMENT,"
+		" ID INT2 UNSIGNED NOT NULL AUTO_INCREMENT,"	// NOTE: 2 bytes (enough to go global..?)
 		" Name CHAR(40) NOT NULL,"
-		" Code CHAR(3) NOT NULL,"
-		" CountryID INT4 NOT NULL,"
+		" Code CHAR(3) NOT NULL,"			// eg. "MA"
+		" CountryID INT2 NOT NULL,"			// NOTE: 2 bytes
 		" PRIMARY KEY (ID),"
-		" INDEX (Name(15)));"	// only index the first X chars of name (who types more than that?)
+		" INDEX (Name(5)));"				// 4 is enough for decent uniqueness.
 	    ,NULL);
 
 	// Location
 	db_query("CREATE TABLE IF NOT EXISTS Location("
 		" ID INT4 UNSIGNED NOT NULL AUTO_INCREMENT,"
-		" LocationSetID INT4 NOT NULL,"
+		" LocationSetID INT3 NOT NULL,"				// NOTE: 3 bytes
 		" Coordinates point NOT NULL,"
 		" PRIMARY KEY (ID),"
 		" INDEX(LocationSetID),"
@@ -598,7 +591,7 @@
 
 	// Location Attribute Name
 	db_query("CREATE TABLE IF NOT EXISTS LocationAttributeName("
-		" ID INT4 UNSIGNED NOT NULL AUTO_INCREMENT,"
+		" ID INT3 UNSIGNED NOT NULL AUTO_INCREMENT,"		// NOTE: 3 bytes. (16 million possibilities)
 		" Name VARCHAR(30) NOT NULL,"
 		" PRIMARY KEY (ID),"
 		" UNIQUE INDEX (Name));", NULL);
@@ -610,36 +603,20 @@
 		// which location this value applies to
 		" LocationID INT4 UNSIGNED NOT NULL,"
 		// type 'name' of this name=value pair
-		" AttributeNameID INT4 UNSIGNED NOT NULL,"
+		" AttributeNameID INT3 UNSIGNED NOT NULL,"		// NOTE: 3 bytes.
 		// 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)
-		" INDEX (LocationID),"	// for searching values for a given POI
-		" FULLTEXT(Value));", 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
+		" FULLTEXT(Value));", NULL);		// for sexy fulltext searching of values!
 
 	// Location Set
 	db_query("CREATE TABLE IF NOT EXISTS LocationSet("
-		" ID INT4 UNSIGNED NOT NULL AUTO_INCREMENT,"
+		" ID INT3 UNSIGNED NOT NULL AUTO_INCREMENT,"		// NOTE: 3 bytes.	(would 2 be enough?)
 		" Name VARCHAR(60) NOT NULL,"
 		" PRIMARY KEY (ID));", NULL);
 }
 
-void db_enable_keys(void)
-{
-//	g_print("Enabling keys\n");
-//	db_query("ALTER TABLE Road ENABLE KEYS", NULL);
-//	db_query("ALTER TABLE RoadName ENABLE KEYS", NULL);
-//	db_query("ALTER TABLE Road_RoadName ENABLE KEYS", NULL);
-}
-
-void db_disable_keys(void)
-{
-//	g_print("Disabling keys\n");
-//	db_query("ALTER TABLE Road DISABLE KEYS", NULL);
-//	db_query("ALTER TABLE RoadName DISABLE KEYS", NULL);
-//	db_query("ALTER TABLE Road_RoadName DISABLE KEYS", NULL);
-}
-
 #ifdef ROADSTER_DEAD_CODE
 /*
 static void db_disconnect(void)

Index: import_tiger.c
===================================================================
RCS file: /cvs/cairo/roadster/src/import_tiger.c,v
retrieving revision 1.14
retrieving revision 1.15
diff -u -d -r1.14 -r1.15
--- import_tiger.c	20 Mar 2005 03:37:09 -0000	1.14
+++ import_tiger.c	23 Mar 2005 09:21:49 -0000	1.15
@@ -153,7 +153,7 @@
 	// store a list of city names
 	gint m_nFIPS55;	// index
 	char m_achName[TIGER_CITY_NAME_LEN + 1];	// note the +1!!
-	gint m_nCityID;								// a database ID, stored here after it is inserted
+	gint m_nCityID;					// a database ID, stored here after it is inserted
 } tiger_record_rtc_t;
 
 

Index: mainwindow.c
===================================================================
RCS file: /cvs/cairo/roadster/src/mainwindow.c,v
retrieving revision 1.28
retrieving revision 1.29
diff -u -d -r1.28 -r1.29
--- mainwindow.c	20 Mar 2005 10:57:05 -0000	1.28
+++ mainwindow.c	23 Mar 2005 09:21:49 -0000	1.29
@@ -45,6 +45,7 @@
 #include "glyph.h"
 #include "animator.h"
 #include "history.h"
+#include "tooltip.h"
 
 #include <gdk/gdk.h>
 #include <gdk/gdkx.h>
@@ -70,8 +71,7 @@
 
 #define SLIDE_TIMEOUT_MS		(50)	// time between frames (in MS) for smooth-sliding (on double click?)
 #define	SLIDE_TIME_IN_SECONDS		(0.7)	// how long the whole slide should take, in seconds
-
-#define	SLIDE_TIME_IN_SECONDS_AUTO	(1.3)
+#define	SLIDE_TIME_IN_SECONDS_AUTO	(0.5)	// time for sliding to search results, etc.
 
 // Layerlist columns
 #define LAYERLIST_COLUMN_ENABLED	(0)
@@ -82,7 +82,10 @@
 #define SPEED_LABEL_FORMAT		("<span font_desc='32'>%.0f</span>")
 
 // Settings
-#define TIMER_GPS_REDRAW_INTERVAL_MS	(2500)		// lower this (to 1?) when it's faster to redraw track
+#define TIMER_GPS_REDRAW_INTERVAL_MS	(2500)		// lower this (to 1000?) when it's faster to redraw track
+
+#define	TOOLTIP_OFFSET_X (20)
+#define	TOOLTIP_OFFSET_Y (0)
 
 #define MAX_DISTANCE_FOR_AUTO_SLIDE_IN_PIXELS	(3500.0)
 
@@ -112,6 +115,9 @@
 static gboolean mainwindow_callback_on_slide_timeout(gpointer pData);
 static void mainwindow_setup_selected_tool(void);
 
+static gboolean mainwindow_on_enter_notify(GtkWidget* w, GdkEventCrossing *event);
+static gboolean mainwindow_on_leave_notify(GtkWidget* w, GdkEventCrossing *event);
+
 void mainwindow_add_history();
 
 void mainwindow_map_center_on_mappoint(mappoint_t* pPoint);
@@ -169,7 +175,7 @@
 
 	// Drawing area
 	GtkDrawingArea* m_pDrawingArea;
-
+	tooltip_t* m_pTooltip;
 	map_t* m_pMap;
 
 	EToolType m_eSelectedTool;
@@ -293,17 +299,21 @@
 
 	// create drawing area
 	g_MainWindow.m_pDrawingArea = GTK_DRAWING_AREA(gtk_drawing_area_new());
+	g_MainWindow.m_pTooltip = tooltip_new();
+
 	// create map
 	map_new(&g_MainWindow.m_pMap, GTK_WIDGET(g_MainWindow.m_pDrawingArea));
 
 	// 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);
+	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);
 	g_signal_connect(G_OBJECT(g_MainWindow.m_pDrawingArea), "expose_event", G_CALLBACK(mainwindow_on_expose_event), NULL);
 	g_signal_connect(G_OBJECT(g_MainWindow.m_pDrawingArea), "configure_event", G_CALLBACK(mainwindow_on_configure_event), NULL);
 	g_signal_connect(G_OBJECT(g_MainWindow.m_pDrawingArea), "button_press_event", G_CALLBACK(mainwindow_on_mouse_button_click), NULL);
 	g_signal_connect(G_OBJECT(g_MainWindow.m_pDrawingArea), "button_release_event", G_CALLBACK(mainwindow_on_mouse_button_click), NULL);
 	g_signal_connect(G_OBJECT(g_MainWindow.m_pDrawingArea), "motion_notify_event", G_CALLBACK(mainwindow_on_mouse_motion), NULL);
 	g_signal_connect(G_OBJECT(g_MainWindow.m_pDrawingArea), "scroll_event", G_CALLBACK(mainwindow_on_mouse_scroll), NULL);
+	g_signal_connect(G_OBJECT(g_MainWindow.m_pDrawingArea), "enter_notify_event", G_CALLBACK(mainwindow_on_enter_notify), NULL);
+	g_signal_connect(G_OBJECT(g_MainWindow.m_pDrawingArea), "leave_notify_event", G_CALLBACK(mainwindow_on_leave_notify), NULL);
 
 	// Pack canvas into application window
 	gtk_box_pack_end(GTK_BOX(g_MainWindow.m_pContentBox), GTK_WIDGET(g_MainWindow.m_pDrawingArea),
@@ -714,12 +724,16 @@
 {
 	EDirection eDirection;
 
+	// Corner hit targets are L shaped and 1/3 of the two borders it touches
+	gint nXCorner = nWidth/3;
+	gint nYCorner = nHeight/3;
+
 	// LEFT EDGE?
 	if(nX <= nBorderSize) {
-		if(nY <= nBorderSize) {
+		if(nY <= nYCorner) {
 			eDirection = DIRECTION_NW;
 		}
-		else if((nY+nBorderSize) >= nHeight) {
+		else if((nY+nYCorner) >= nHeight) {
 			eDirection = DIRECTION_SW;
 		}
 		else {
@@ -728,10 +742,10 @@
 	}
 	// RIGHT EDGE?
 	else if((nX+nBorderSize) >= nWidth) {
-		if(nY <= BORDER_SCROLL_CLICK_TARGET_SIZE) {
+		if(nY <= nYCorner) {
 			eDirection = DIRECTION_NE;
 		}
-		else if((nY+nBorderSize) >= nHeight) {
+		else if((nY+nYCorner) >= nHeight) {
 			eDirection = DIRECTION_SE;
 		}
 		else {
@@ -740,11 +754,27 @@
 	}
 	// TOP?
 	else if(nY <= nBorderSize) {
-		eDirection = DIRECTION_N;
+		if(nX <= nXCorner) {
+			eDirection = DIRECTION_NW;
+		}
+		else if((nX+nXCorner) >= nWidth) {
+			eDirection = DIRECTION_NE;
+		}
+		else {
+			eDirection = DIRECTION_N;
+		}
 	}
 	// BOTTOM?
 	else if((nY+nBorderSize) >= nHeight) {
-		eDirection = DIRECTION_S;
+		if(nX <= nXCorner) {
+			eDirection = DIRECTION_SW;
+		}
+		else if((nX+nXCorner) >= nWidth) {
+			eDirection = DIRECTION_SE;
+		}
+		else {
+			eDirection = DIRECTION_S;
+		}
 	}
 	// center.
 	else {
@@ -767,6 +797,7 @@
 	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);
@@ -806,6 +837,9 @@
 		}
 		// Left mouse button up?
 		else if(event->type == GDK_BUTTON_RELEASE) {
+			//tooltip_set_upper_left_corner(g_MainWindow.m_pTooltip, (gint)(event->x_root) + TOOLTIP_OFFSET_X, (gint)(event->y_root) + TOOLTIP_OFFSET_Y);
+			//tooltip_show(g_MainWindow.m_pTooltip);
+
 			// restore cursor
 			GdkCursor* pCursor = gdk_cursor_new(GDK_LEFT_PTR);
 			gdk_window_set_cursor(GTK_WIDGET(g_MainWindow.m_pDrawingArea)->window, pCursor);
@@ -927,16 +961,60 @@
 		}
 	}
 	else {
-		EDirection eScrollDirection = match_border(nX, nY, nWidth, nHeight, BORDER_SCROLL_CLICK_TARGET_SIZE);
 
+		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;
+			screenpoint.m_nX = nX;
+			screenpoint.m_nY = nY;
+			mappoint_t mappoint;
+			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)) {
+				// 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(" %s ", pszReturnString);
+				tooltip_set_markup(g_MainWindow.m_pTooltip, pszMarkup);
+				g_free(pszMarkup);
+
+				tooltip_show(g_MainWindow.m_pTooltip);	// ensure it's visible
+				g_free(pszReturnString); pszReturnString = NULL;
+			}
+			else {
+				// no hit. hide the tooltip
+				tooltip_hide(g_MainWindow.m_pTooltip);
+			}
+			g_assert(pszReturnString == NULL);
+
+		}
+		else {
+			// using a funky cursor. hide the tooltip
+			tooltip_hide(g_MainWindow.m_pTooltip);
+		}
 	}
 	return FALSE;
 }
 
+static gboolean mainwindow_on_enter_notify(GtkWidget* w, GdkEventCrossing *event)
+{
+	tooltip_show(g_MainWindow.m_pTooltip);
+}
+
+static gboolean mainwindow_on_leave_notify(GtkWidget* w, GdkEventCrossing *event)
+{
+	tooltip_hide(g_MainWindow.m_pTooltip);
+}
+
+
 static gboolean mainwindow_on_mouse_scroll(GtkWidget* w, GdkEventScroll *event)
 {
 	// respond to scroll wheel events by zooming in and out
@@ -1302,6 +1380,20 @@
 //	mainwindow_draw_map(DRAWFLAG_GEOMETRY);
 }
 
+void mainwindow_on_backmenuitem_activate(GtkWidget* _unused, gpointer* __unused)
+{
+	history_go_back(g_MainWindow.m_pHistory);
+	mainwindow_go_to_current_history_item();
+	mainwindow_update_forward_back_buttons();
+}
+
+void mainwindow_on_forwardmenuitem_activate(GtkWidget* _unused, gpointer* __unused)
+{
+	history_go_forward(g_MainWindow.m_pHistory);
+	mainwindow_go_to_current_history_item();
+	mainwindow_update_forward_back_buttons();
+}
+
 void mainwindow_on_backbutton_clicked(GtkWidget* _unused, gpointer* __unused)
 {
 	history_go_back(g_MainWindow.m_pHistory);

Index: map.c
===================================================================
RCS file: /cvs/cairo/roadster/src/map.c,v
retrieving revision 1.30
retrieving revision 1.31
diff -u -d -r1.30 -r1.31
--- map.c	20 Mar 2005 10:57:05 -0000	1.30
+++ map.c	23 Mar 2005 09:21:49 -0000	1.31
@@ -27,12 +27,13 @@
 #include <math.h>
 
 //#define THREADED_RENDERING
+//#define SCENEMANAGER_DEBUG_TEST
 
-// #ifdef THREADED_RENDERING
-// #define RENDERING_THREAD_YIELD          g_thread_yield()
-// #else
+#ifdef THREADED_RENDERING
+#define RENDERING_THREAD_YIELD          g_thread_yield()
+#else
 #define RENDERING_THREAD_YIELD
-// #endif
+#endif
 
 #include "gui.h"
 #include "map.h"
@@ -45,6 +46,9 @@
 #include "locationset.h"
 #include "scenemanager.h"
 
+#define	RENDERMODE_FAST 	1	// Use 'fast' until Cairo catches up. :)
+#define	RENDERMODE_PRETTY 	2
+
 // NOTE on choosing tile size.
 // A) It is arbitrary and could be changed (even at runtime, although this would render useless everything in the cache)
 // B) Too big, and you'll see noticable pauses while scrolling.
@@ -56,17 +60,19 @@
 #define TILE_MODULUS			(23)		// how many of the above units each tile is on a side
 #define MAP_TILE_WIDTH			(TILE_MODULUS / TILE_SHIFT)	// width and height of a tile, in degrees
 
-//#define ROUND_FLOAT_TO_DECIMAL_PLACE(f,d)	(floor((f)*(d))/(d))	// d should be like 10 or 100.  10 will drop all but the first decimal.
+#define MIN_ROAD_HIT_TARGET_WIDTH	(4)	// make super thin roads a bit easier to hover over/click, in pixels
 
-// ADD:
-// 'Mal' - ?
-// 'Trce - Trace
 
 /* 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);
 
+// hit testing
+static gboolean map_hit_test_layer_lines(GPtrArray* pPointStringsArray, gdouble fMaxDistance, mappoint_t* pHitPoint, gchar** ppReturnString);
+static gboolean map_hit_test_line(mappoint_t* pPoint1, mappoint_t* pPoint2, mappoint_t* pHitPoint, gdouble fDistance);
+
 static void map_data_clear(map_t* pMap);
 void map_get_render_metrics(map_t* pMap, rendermetrics_t* pMetrics);
 
@@ -168,9 +174,6 @@
 	return TRUE;
 }
 
-#define	RENDERMODE_FAST 	1
-#define	RENDERMODE_PRETTY 	2
-
 void map_draw(map_t* pMap, gint nDrawFlags)
 {
 	g_assert(pMap != NULL);
@@ -182,7 +185,7 @@
 	map_get_render_metrics(pMap, &renderMetrics);
 	rendermetrics_t* pRenderMetrics = &renderMetrics;
 
-//g_print("drawing at %f,%f\n", pMap->m_MapCenter.m_fLatitude, pMap->m_MapCenter.m_fLongitude);
+	//g_print("drawing at %f,%f\n", pMap->m_MapCenter.m_fLatitude, pMap->m_MapCenter.m_fLongitude);
 
 	//
 	// Load geometry
@@ -195,9 +198,10 @@
 
 	gint nRenderMode = RENDERMODE_FAST;
 
-	// XXX test
-//         GdkRectangle rect = {200,200,100,100};
-//         scenemanager_claim_rectangle(pMap->m_pSceneManager, &rect);
+#ifdef SCENEMANAGER_DEBUG_TEST
+        GdkRectangle rect = {200,200,100,100};
+        scenemanager_claim_rectangle(pMap->m_pSceneManager, &rect);
+#endif
 
 	if(nRenderMode == RENDERMODE_FAST) {
 		// 
@@ -212,10 +216,11 @@
 	else {	// nRenderMode == RENDERMODE_PRETTY
 		map_draw_cairo(pMap, pRenderMetrics, pMap->m_pPixmap, nDrawFlags);
 	}
-	
-	// XXX test
-//         gdk_draw_rectangle(pMap->m_pPixmap, pMap->m_pTargetWidget->style->fg_gc[GTK_WIDGET_STATE(pMap->m_pTargetWidget)],
-//                         FALSE, 200,200, 100, 100);
+
+#ifdef SCENEMANAGER_DEBUG_TEST
+        gdk_draw_rectangle(pMap->m_pPixmap, pMap->m_pTargetWidget->style->fg_gc[GTK_WIDGET_STATE(pMap->m_pTargetWidget)],
+                           FALSE, 200,200, 100, 100);
+#endif
 
 	gtk_widget_queue_draw(pMap->m_pTargetWidget);
 }
@@ -226,13 +231,12 @@
 
 GdkPixmap* map_get_pixmap(map_t* pMap)
 {
-//	g_mutex_lock(pMap->m_pPixmapMutex);
 	return pMap->m_pPixmap;
 }
 
 void map_release_pixmap(map_t* pMap)
 {
-//	g_mutex_unlock(pMap->m_pPixmapMutex);
+	// nothing since we're not using mutexes
 }
 
 
@@ -242,8 +246,6 @@
 
 void map_set_zoomlevel(map_t* pMap, guint16 uZoomLevel)
 {
-//         g_mutex_lock(pMap->m_pDataMutex);
-
 	if(uZoomLevel > MAX_ZOOMLEVEL) uZoomLevel = MAX_ZOOMLEVEL;
 	else if(uZoomLevel < MIN_ZOOMLEVEL) uZoomLevel = MIN_ZOOMLEVEL;
 
@@ -251,7 +253,6 @@
 		pMap->m_uZoomLevel = uZoomLevel;
 //		map_set_redraw_needed(TRUE);
 	}
-//	g_mutex_unlock(pMap->m_pDataMutex);
 }
 
 guint16 map_get_zoomlevel(map_t* pMap)
@@ -371,9 +372,6 @@
 {
 	g_assert(pDimensions != NULL);
 
-//         g_mutex_lock(pMap->m_pDataMutex);
-//         g_mutex_lock(pMap->m_pPixmapMutex);
-
 	pMap->m_MapDimensions.m_uWidth = pDimensions->m_uWidth;
 	pMap->m_MapDimensions.m_uHeight = pDimensions->m_uHeight;
 
@@ -381,9 +379,6 @@
 			pMap->m_pTargetWidget->window,
 			pMap->m_MapDimensions.m_uWidth, pMap->m_MapDimensions.m_uHeight,
 			-1);
-
-//         g_mutex_unlock(pMap->m_pPixmapMutex);
-//         g_mutex_unlock(pMap->m_pDataMutex);
 }
 
 // ========================================================
@@ -412,50 +407,6 @@
 	pMetrics->m_rWorldBoundingBox.m_B.m_fLatitude = pMap->m_MapCenter.m_fLatitude + pMetrics->m_fScreenLatitude/2;	
 }
 
-/*
-void map_draw(map_t* pMap, cairo_t *pCairo)
-{
-	// Get render metrics
-	rendermetrics_t renderMetrics = {0};
-	map_get_render_metrics(pMap, &renderMetrics);
-	rendermetrics_t* pRenderMetrics = &renderMetrics;
-
-	scenemanager_clear(pMap->m_pSceneManager);
-
-	//
-	// Load geometry
-	//
-	TIMER_BEGIN(loadtimer, "--- BEGIN ALL DB LOAD");
-	map_data_load(pMap, &(pRenderMetrics->m_rWorldBoundingBox));
-	locationset_load_locations(&(pRenderMetrics->m_rWorldBoundingBox));
-	TIMER_END(loadtimer, "--- END ALL DB LOAD");
-
-//	const GPtrArray* pLocationSets = locationset_get_set_array();
-
-	//
-	// Draw map
-	//
-
-//         TIMER_BEGIN(loctimer, "\nBEGIN RENDER LOCATIONS");
-//                 // Render Locations
-//                 gint iLocationSet;
-//                 for(iLocationSet=0 ; iLocationSet<pLocationSets->len ; iLocationSet++) {
-//                         RENDERING_THREAD_YIELD;
-//
-//                         locationset_t* pLocationSet = g_ptr_array_index(pLocationSets, iLocationSet);
-//                         map_draw_layer_points(pMap, pCairo, pRenderMetrics, pLocationSet->m_pLocationsArray);
-//                 }
-//         TIMER_END(loctimer, "END RENDER LOCATIONS");
-
-//         map_draw_crosshair(pMap, pCairo, pRenderMetrics);
-
-	cairo_restore(pCairo);
-
-	// We don't need another redraw until something changes
-//	map_set_redraw_needed(FALSE);
-}
-*/
-
 static gboolean map_data_load_tiles(map_t* pMap, maprect_t* pRect)
 {
 //         g_print("*****\n"
@@ -538,44 +489,8 @@
 
 	TIMER_BEGIN(mytimer, "BEGIN Geometry LOAD");
 
-	// HACKY: make a list of layer IDs "2,3,5,6"
-//         gchar azLayerNumberList[200] = {0};
-//         gint nActiveLayerCount = 0;
-//         gint i;
-//         for(i=LAYER_FIRST ; i <= LAYER_LAST ;i++) {
-//                 if(g_aLayers[i]->m_Style.m_aSubLayers[0].m_afLineWidths[nZoomLevel-1] != 0.0 ||
-//                    g_aLayers[i]->m_Style.m_aSubLayers[1].m_afLineWidths[nZoomLevel-1] != 0.0)
-//                 {
-//                         gchar azLayerNumber[10];
-//
-//                         if(nActiveLayerCount > 0) g_snprintf(azLayerNumber, 10, ",%d", i);
-//                         else g_snprintf(azLayerNumber, 10, "%d", i);
-//
-//                         g_strlcat(azLayerNumberList, azLayerNumber, 200);
-//                         nActiveLayerCount++;
-//                 }
-//         }
-//         if(nActiveLayerCount == 0) {
-//                 g_print("no visible layers!\n");
-//                 return TRUE;
-//         }
-
-	// MySQL doesn't optimize away GeomFromText(...) and instead executes this ONCE PER ROW.
-	// That's a whole lot of parsing and was causing my_strtod to eat up 9% of Roadster's CPU time.
-	// Assinging it to a temp variable alleviates that problem.
-
-	gchar* pszSQL;
-//         pszSQL = g_strdup_printf("SET @wkb=GeomFromText('Polygon((%f %f,%f %f,%f %f,%f %f,%f %f))')",
-//                 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
-//                 );
-//         db_query(pszSQL, NULL);
-//         g_free(pszSQL);
-
 	// generate SQL
+	gchar* pszSQL;
 	pszSQL = g_strdup_printf(
 		"SELECT Road.ID, Road.TypeID, AsBinary(Road.Coordinates), RoadName.Name, RoadName.SuffixID"
 		" FROM Road "
@@ -584,7 +499,6 @@
 		//" TypeID IN (%s) AND"
                 //" MBRIntersects(@wkb, Coordinates)"
 		" MBRIntersects(GeomFromText('Polygon((%f %f,%f %f,%f %f,%f %f,%f %f))'), Coordinates)"
-//		azLayerNumberList,
 		,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
@@ -678,8 +592,6 @@
 
 double map_get_distance_in_meters(mappoint_t* pA, mappoint_t* pB)
 {
-//	g_assert_not_reached();	// unused/tested
-
 	// This functions calculates the length of the arc of the "greatcircle" that goes through
 	// the two points A and B and whos center is the center of the sphere, O.
 
@@ -730,152 +642,146 @@
 	g_array_append_val(pMap->m_pTracksArray, hTrack);
 }
 
-#if ROADSTER_DEAD_CODE
-/*
-
 // ========================================================
-//  Redraw
+//  Hit Testing
 // ========================================================
 
-void map_set_redraw_needed(gboolean bNeeded)
+// 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)
 {
-	pMap->m_bRedrawNeeded = bNeeded;
-}
+	// 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--) {
+		gint nLayer = layerdraworder[i].nLayer;
 
-gboolean map_get_redraw_needed()
-{
-	return pMap->m_bRedrawNeeded;
-}
+		// use width from whichever layer it's wider in
+		gdouble fLineWidth = max(g_aLayers[nLayer]->m_Style.m_aSubLayers[0].m_afLineWidths[pMap->m_uZoomLevel],
+					 g_aLayers[nLayer]->m_Style.m_aSubLayers[1].m_afLineWidths[pMap->m_uZoomLevel]);
 
-gpointer map_draw_thread(gpointer);
+		// make thin roads a little easier to hit
+		fLineWidth = max(fLineWidth, MIN_ROAD_HIT_TARGET_WIDTH);
 
-void map_draw_thread_begin(map_t* pMap, GtkWidget* pTargetWidget)
-{
-#ifdef THREADED_RENDERING
-	g_thread_create(map_draw_thread, pMap, FALSE, NULL);
-#else
-	map_draw_thread(pMap);
-#endif
-}
+		// XXX: hack, map_pixels should really take a floating point instead.
+		gdouble fMaxDistance = map_pixels_to_degrees(pMap, 1, pMap->m_uZoomLevel) * (fLineWidth/2);	// half width on each side
 
-#include <gdk/gdk.h>
+		if(map_hit_test_layer_lines(pMap->m_apLayerData[nLayer]->m_pPointStringsArray, fMaxDistance, pMapPoint, ppReturnString)) {
+			return TRUE;
+		}
+	}
+	return FALSE;
+}
 
-gpointer map_draw_thread(gpointer pData)
+static gboolean map_hit_test_layer_lines(GPtrArray* pPointStringsArray, gdouble fMaxDistance, mappoint_t* pHitPoint, gchar** ppReturnString)
 {
-g_print("THREAD: begin\n");
-	map_t* pMap = (map_t*)pData;
-	g_assert(pMap != NULL);
-
-//	db_lock();
-
-#ifdef THREADED_RENDERING
-	db_begin_thread();	// database needs to know we're a new thread
-#endif
-
-	g_mutex_lock(pMap->m_pDataMutex);
+	g_assert(ppReturnString != NULL);
+	g_assert(*ppReturnString == NULL);	// pointer to null pointer
 
-#ifdef THREADED_RENDERING
-        gdk_threads_enter();
-#endif
-		// create pixel buffer of appropriate size
-		GdkPixmap* pPixmapTemp = gdk_pixmap_new(pMap->m_pTargetWidget->window, pMap->m_MapDimensions.m_uWidth, pMap->m_MapDimensions.m_uHeight, -1);
-		g_assert(pPixmapTemp);
-	
-		Display* dpy;
-		Drawable drawable;
-		dpy = gdk_x11_drawable_get_xdisplay(pPixmapTemp);
-		drawable = gdk_x11_drawable_get_xid(pPixmapTemp);
+	/* this is helpful for testing with the g_print()s in map_hit_test_line() */
+/*         mappoint_t p1 = {2,2};                */
+/*         mappoint_t p2 = {-10,10};             */
+/*         mappoint_t p3 = {0,10};               */
+/*         map_hit_test_line(&p1, &p2, &p3, 20); */
+/*         return FALSE;                         */
 
-g_print("THREAD: creating cairo...\n");
-	cairo_t* pCairo = cairo_create ();
+	// Loop through line strings, order doesn't matter here since they're all on the same level.
+	gint iString;
+	for(iString=0 ; iString<pPointStringsArray->len ; iString++) {
+		pointstring_t* pPointString = g_ptr_array_index(pPointStringsArray, iString);
+		if(pPointString->m_pPointsArray->len < 2) continue;
 
-g_print("THREAD: calling cairo_set_target_drawable...\n");
-	// draw on the off-screen buffer
-	cairo_set_target_drawable(pCairo, dpy, drawable);
-#ifdef THREADED_RENDERING
-        gdk_threads_leave();
-#endif
+		// start on 1 so we can do -1 trick below
+		gint iPoint;
+		for(iPoint=1 ; iPoint<pPointString->m_pPointsArray->len ; iPoint++) {
+			mappoint_t* pPoint1 = g_ptr_array_index(pPointString->m_pPointsArray, iPoint-1);
+			mappoint_t* pPoint2 = g_ptr_array_index(pPointString->m_pPointsArray, iPoint);
 
-g_print("THREAD: drawing...\n");
-	map_draw(pMap, pCairo);
+			// hit test this line
+			if(map_hit_test_line(pPoint1, pPoint2, pHitPoint, fMaxDistance)) {
+				// got a hit
+				if(pPointString->m_pszName[0] == '\0') {
+					*ppReturnString = g_strdup("<i>unnamed road</i>");
+				}
+				else {
+					*ppReturnString = g_strdup(pPointString->m_pszName);
+				}
+				return TRUE;
+			}
+		}
+	}
+	return FALSE;
+}
 
-g_print("THREAD: destroying cairo...\n");
-#ifdef THREADED_RENDERING
-        gdk_threads_enter();
-#endif
-	cairo_destroy(pCairo);
-#ifdef THREADED_RENDERING
-        gdk_threads_leave();
-#endif
+// Does the given point come close enough to the line segment to be considered a hit?
+static gboolean map_hit_test_line(mappoint_t* pPoint1, mappoint_t* pPoint2, mappoint_t* pHitPoint, gdouble fMaxDistance)
+{
+	// Some bad ASCII art demonstrating the situation:
+	//
+	//             / (u)
+	//          /  |
+	//       /     |
+	// (0,0) =====(a)========== (v)
 
-	// Copy final image to (pMap->m_pPixmap)
-g_print("THREAD: copying pixmap\n");
-	g_mutex_lock(pMap->m_pPixmapMutex);
+	// v is the translated-to-origin vector of line (road)
+	// u is the translated-to-origin vector of the hitpoint
+	// a is the closest point on v to the end of u (the hit point)
 
-#ifdef THREADED_RENDERING
-        gdk_threads_enter();
-#endif
-		gdk_draw_pixmap(pMap->m_pPixmap,
-		  pMap->m_pTargetWidget->style->fg_gc[GTK_WIDGET_STATE(pMap->m_pTargetWidget)],
-		  pPixmapTemp,
-		  0, 0,
-		  0, 0,
-		  pMap->m_MapDimensions.m_uWidth, pMap->m_MapDimensions.m_uHeight);
-		gdk_pixmap_unref(pPixmapTemp);
-#ifdef THREADED_RENDERING
-        gdk_threads_leave();
-#endif
+	//
+	// 1. Convert p1->p2 vector into a vector (v) that is assumed to come out of the origin (0,0)
+	//
+	mappoint_t v;
+	v.m_fLatitude = pPoint2->m_fLatitude - pPoint1->m_fLatitude;	// 10->90 becomes 0->80 (just store 80)
+	v.m_fLongitude = pPoint2->m_fLongitude - pPoint1->m_fLongitude;
 
-	g_mutex_unlock(pMap->m_pDataMutex);
-	g_mutex_unlock(pMap->m_pPixmapMutex);
-//	db_unlock();
+	gdouble fLengthV = sqrt((v.m_fLatitude*v.m_fLatitude) + (v.m_fLongitude*v.m_fLongitude)); 
+	if(fLengthV == 0.0) return FALSE;	// bad data: a line segment with no length?
 
-g_print("THREAD: done drawing\n");
-#ifdef THREADED_RENDERING
-        gdk_threads_enter();
-#endif
-	gtk_widget_queue_draw(pMap->m_pTargetWidget);
-#ifdef THREADED_RENDERING
-        gdk_threads_leave();
-	db_end_thread();
-#endif
-}
+	//
+	// 2. Make a unit vector out of v (meaning same direction but length=1) by dividing v by v's length
+	//
+	mappoint_t unitv;
+	unitv.m_fLatitude = v.m_fLatitude / fLengthV;
+	unitv.m_fLongitude = v.m_fLongitude / fLengthV;	// unitv is now a unit (=1.0) length v
 
-	TIMER_BEGIN(gdktimer, "starting gdk");
-	gint i;
+	//
+	// 3. Translate the hitpoint in the same way we translated v
+	//
+	mappoint_t u;
+	u.m_fLatitude = pHitPoint->m_fLatitude - pPoint1->m_fLatitude;
+	u.m_fLongitude = pHitPoint->m_fLongitude - pPoint1->m_fLongitude;
 
-	for(i=0 ; i<500 ; i++) {
-	GdkPoint points[5];
-	points[0].x = random() % 10000;
-	points[0].y = random() % 10000;
-	points[1].x = random() % 10000;
-	points[1].y = random() % 10000;
-	points[2].x = random() % 10000;
-	points[2].y = random() % 10000;
-	points[3].x = random() % 10000;
-	points[3].y = random() % 10000;
-	points[4].x = random() % 10000;
-	points[4].y = random() % 10000;
+	//
+	// 4. Use the dot product of (unitv) and (u) to find (a), the point along (v) that is closest to (u). see diagram above.
+	//
+	gdouble fLengthAlongV = (unitv.m_fLatitude * u.m_fLatitude) + (unitv.m_fLongitude * u.m_fLongitude);
 
-		GdkColor clr;
-		clr.red = clr.green = clr.blue = 45535;
-		gdk_gc_set_rgb_fg_color(pMap->m_pTargetWidget->style->fg_gc[GTK_WIDGET_STATE(pMap->m_pTargetWidget)], &clr);
-		
-		gdk_gc_set_line_attributes(pMap->m_pTargetWidget->style->fg_gc[GTK_WIDGET_STATE(pMap->m_pTargetWidget)],
-                                           12,GDK_LINE_SOLID,GDK_CAP_ROUND,GDK_JOIN_MITER);
-		gdk_draw_lines(pPixmapTemp, pMap->m_pTargetWidget->style->fg_gc[GTK_WIDGET_STATE(pMap->m_pTargetWidget)],
-			//TRUE,
-			points, 5);
+	// Does it fall along the length of the line *segment* v?  (we know it falls along the infinite line v, but that does us no good.)
+	// (This produces false negatives on round/butt end caps, but that's better that a false positive when another line is actually there!)
+	if(fLengthAlongV > 0 && fLengthAlongV < fLengthV) {
+		mappoint_t a;
+		a.m_fLatitude = v.m_fLatitude * (fLengthAlongV / fLengthV);	// multiply each component by the percentage
+		a.m_fLongitude = v.m_fLongitude * (fLengthAlongV / fLengthV);
+		// NOTE: (a) is *not* where it actually hit on the *map*.  don't draw this point!  we'd have to translate it back away from the origin.
 
-		gdk_gc_set_line_attributes(pMap->m_pTargetWidget->style->fg_gc[GTK_WIDGET_STATE(pMap->m_pTargetWidget)],
-					   9,GDK_LINE_SOLID,GDK_CAP_ROUND,GDK_JOIN_MITER);
+		//
+		// 5. Calculate the distance from the end of (u) to (a).  If it's less than the fMaxDistance, it's a hit.
+		//
+		gdouble fRise = u.m_fLatitude - a.m_fLatitude;
+		gdouble fRun = u.m_fLongitude - a.m_fLongitude;
+		gdouble fDistanceSquared = fRise*fRise + fRun*fRun;	// compare squared distances. same results but without the sqrt.
 
-		clr.red = clr.green = clr.blue = 65535;
-		gdk_gc_set_rgb_fg_color(pMap->m_pTargetWidget->style->fg_gc[GTK_WIDGET_STATE(pMap->m_pTargetWidget)], &clr);
-		gdk_draw_lines(pPixmapTemp, pMap->m_pTargetWidget->style->fg_gc[GTK_WIDGET_STATE(pMap->m_pTargetWidget)],
-			//TRUE,
-			points, 5);
+		if(fDistanceSquared <= (fMaxDistance*fMaxDistance)) {
+			/* debug aids */
+			/* g_print("pPoint1 (%f,%f)\n", pPoint1->m_fLatitude, pPoint1->m_fLongitude);       */
+			/* g_print("pPoint2 (%f,%f)\n", pPoint2->m_fLatitude, pPoint2->m_fLongitude);       */
+			/* g_print("pHitPoint (%f,%f)\n", pHitPoint->m_fLatitude, pHitPoint->m_fLongitude); */
+			/* g_print("v (%f,%f)\n", v.m_fLatitude, v.m_fLongitude);                           */
+			/* g_print("u (%f,%f)\n", u.m_fLatitude, u.m_fLongitude);                           */
+			/* g_print("unitv (%f,%f)\n", unitv.m_fLatitude, unitv.m_fLongitude);               */
+			/* g_print("fDotProduct = %f\n", fDotProduct);                                      */
+			/* g_print("a (%f,%f)\n", a.m_fLatitude, a.m_fLongitude);                           */
+			/* g_print("fDistance = %f\n", sqrt(fDistanceSquared));                             */
+			return TRUE;
+		}
 	}
-	TIMER_END(gdktimer, "ending gdk");
-*/
-#endif /* ROADSTER_DEAD_CODE */
+	return FALSE;
+}

Index: map.h
===================================================================
RCS file: /cvs/cairo/roadster/src/map.h,v
retrieving revision 1.9
retrieving revision 1.10
diff -u -d -r1.9 -r1.10
--- map.h	20 Mar 2005 10:57:05 -0000	1.9
+++ map.h	23 Mar 2005 09:21:49 -0000	1.10
@@ -218,4 +218,6 @@
 
 void map_add_track(map_t* pMap, gint hTrack);
 
+gboolean map_hit_test(map_t* pMap, mappoint_t* pMapPoint, gchar** ppReturnString);
+
 #endif

--- NEW FILE: tooltip.c ---
/***************************************************************************
 *            tooltip.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.
 */

#include <gtk/gtk.h>
#include "tooltip.h"

void tooltip_init()
{

}

static gboolean tooltip_on_mouse_motion(GtkWidget* w, GdkEventMotion *event);

tooltip_t* tooltip_new()
{
	tooltip_t* pNew = g_new0(tooltip_t, 1);
	
	// 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_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.

	// create frame (draws the nice outline for us) and add to window (we don't need to keep a pointer to this)
	GtkFrame* pFrame = GTK_FRAME(gtk_frame_new(NULL));
	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));

	pNew->m_bEnabled = TRUE;	// XXX: note: currently no API to disable it

	return pNew;
}

void tooltip_set_markup(tooltip_t* pTooltip, const gchar* pszMarkup)
{
	gtk_label_set_markup(pTooltip->m_pLabel, pszMarkup);
}

void tooltip_set_upper_left_corner(tooltip_t* pTooltip, gint nX, gint nY)
{
	gtk_window_move(pTooltip->m_pWindow, nX, nY);
}

void tooltip_show(tooltip_t* pTooltip)
{
	if(pTooltip->m_bEnabled) {
		gtk_widget_show_all(GTK_WIDGET(pTooltip->m_pWindow));
	}
}

void tooltip_hide(tooltip_t* pTooltip)
{
	gtk_widget_hide(GTK_WIDGET(pTooltip->m_pWindow));
}

static gboolean tooltip_on_mouse_motion(GtkWidget* pWidget, GdkEventMotion *__unused)
{
	// in case the mouse makes its way onto the tooltip, hide it.
	gtk_widget_hide(pWidget);
}



--- NEW FILE: tooltip.h ---
/***************************************************************************
 *            tooltip.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 _TOOLTIP_H
#define _TOOLTIP_H

typedef struct {
	GtkWindow* m_pWindow;
	GtkLabel* m_pLabel;
	
	gboolean m_bEnabled;
} tooltip_t;

void tooltip_init();
tooltip_t* tooltip_new();
void tooltip_set_markup(tooltip_t* pTooltip, const gchar* pszMarkup);
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);

#endif




More information about the cairo-commit mailing list