/*
* Copyright (C) 2006, 2007 John Costigan.
*
* POI and GPS-Info code originally written by Cezary Jackiewicz.
*
* Default map data provided by http://www.openstreetmap.org/
*
* This file is part of Maemo Mapper.
*
* Maemo Mapper 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 3 of the License, or
* (at your option) any later version.
*
* Maemo Mapper 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Maemo Mapper. If not, see .
*/
#define _GNU_SOURCE
#include
#include
#include
#include
#include
#include
#include
#include
#include "types.h"
#include "data.h"
#include "defines.h"
#include "display.h"
#include "gdk-pixbuf-rotate.h"
#include "gps.h"
#include "maps.h"
#include "path.h"
#include "poi.h"
#include "settings.h"
#include "util.h"
#define VELVEC_SIZE_FACTOR (4)
static GtkWidget *_sat_details_panel = NULL;
static GtkWidget *_sdi_lat = NULL;
static GtkWidget *_sdi_lon = NULL;
static GtkWidget *_sdi_spd = NULL;
static GtkWidget *_sdi_alt = NULL;
static GtkWidget *_sdi_hea = NULL;
static GtkWidget *_sdi_tim = NULL;
static GtkWidget *_sdi_vie = NULL;
static GtkWidget *_sdi_use = NULL;
static GtkWidget *_sdi_fix = NULL;
static GtkWidget *_sdi_fqu = NULL;
static GtkWidget *_sdi_msp = NULL;
static gboolean _redraw_count = 0;
static gint _mark_bufx1 = -1;
static gint _mark_bufx2 = -1;
static gint _mark_bufy1 = -1;
static gint _mark_bufy2 = -1;
static gint _mark_minx = -1;
static gint _mark_miny = -1;
static gint _mark_width = -1;
static gint _mark_height = -1;
static GdkRectangle _scale_rect = { 0, 0, 0, 0};
static GdkRectangle _zoom_rect = { 0, 0, 0, 0};
static gint _dl_errors = 0;
/** Pango stuff. */
GdkRectangle _comprose_rect = { 0, 0, 0, 0};
PangoLayout *_scale_layout = NULL;
PangoLayout *_zoom_layout = NULL;
PangoLayout *_comprose_layout = NULL;
GdkGC *_speed_limit_gc1 = NULL;
GdkGC *_speed_limit_gc2 = NULL;
PangoLayout *_speed_limit_layout = NULL;
PangoLayout *_sat_panel_layout = NULL;
PangoLayout *_heading_panel_layout = NULL;
PangoFontDescription *_heading_panel_fontdesc = NULL;
GdkGC *_sat_info_gc1 = NULL;
GdkGC *_sat_info_gc2 = NULL;
PangoLayout *_sat_info_layout = NULL;
PangoLayout *_sat_details_layout = NULL;
PangoLayout *_sat_details_expose_layout = NULL;
#define SCALE_WIDTH (100)
static gboolean
speed_excess(void)
{
printf("%s()\n", __PRETTY_FUNCTION__);
if(!_speed_excess)
return FALSE;
hildon_play_system_sound(
"/usr/share/sounds/ui-information_note.wav");
vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
return TRUE;
}
static void
speed_limit(void)
{
GdkGC *gc;
gfloat cur_speed;
gchar *buffer;
static gint x = 0, y = 0, width = 0, height = 0;
printf("%s()\n", __PRETTY_FUNCTION__);
cur_speed = _gps.speed * UNITS_CONVERT[_units];
if(cur_speed > _speed_limit)
{
gc = _speed_limit_gc1;
if(!_speed_excess)
{
_speed_excess = TRUE;
g_timeout_add(5000, (GSourceFunc)speed_excess, NULL);
}
}
else
{
gc = _speed_limit_gc2;
_speed_excess = FALSE;
}
/* remove previous number */
if (width != 0 && height != 0) {
gtk_widget_queue_draw_area (_map_widget,
x - 5,
y - 5,
width + 10,
height + 10);
gdk_window_process_all_updates();
}
buffer = g_strdup_printf("%0.0f", cur_speed);
pango_layout_set_text(_speed_limit_layout, buffer, -1);
pango_layout_get_pixel_size(_speed_limit_layout, &width, &height);
switch (_speed_location)
{
case SPEED_LOCATION_TOP_RIGHT:
x = _map_widget->allocation.width - 10 - width;
y = 5;
break;
case SPEED_LOCATION_BOTTOM_RIGHT:
x = _map_widget->allocation.width - 10 - width;
y = _map_widget->allocation.height - 10 - height;
break;
case SPEED_LOCATION_BOTTOM_LEFT:
x = 10;
y = _map_widget->allocation.height - 10 - height;
break;
default:
x = 10;
y = 10;
break;
}
gdk_draw_layout(_map_widget->window,
gc,
x, y,
_speed_limit_layout);
g_free(buffer);
vprintf("%s(): return\n", __PRETTY_FUNCTION__);
}
gboolean
gps_display_details(void)
{
gchar *buffer, litbuf[16];
printf("%s()\n", __PRETTY_FUNCTION__);
if(_gps.fix < 2)
{
/* no fix no fun */
gtk_label_set_label(GTK_LABEL(_sdi_lat), " --- ");
gtk_label_set_label(GTK_LABEL(_sdi_lon), " --- ");
gtk_label_set_label(GTK_LABEL(_sdi_spd), " --- ");
gtk_label_set_label(GTK_LABEL(_sdi_alt), " --- ");
gtk_label_set_label(GTK_LABEL(_sdi_hea), " --- ");
gtk_label_set_label(GTK_LABEL(_sdi_tim), " --:--:-- ");
}
else
{
gfloat speed = _gps.speed * UNITS_CONVERT[_units];
/* latitude */
lat_format(_gps.lat, litbuf);
gtk_label_set_label(GTK_LABEL(_sdi_lat), litbuf);
/* longitude */
lon_format(_gps.lon, litbuf);
gtk_label_set_label(GTK_LABEL(_sdi_lon), litbuf);
/* speed */
switch(_units)
{
case UNITS_MI:
buffer = g_strdup_printf("%.1f mph", speed);
break;
case UNITS_NM:
buffer = g_strdup_printf("%.1f kn", speed);
break;
default:
buffer = g_strdup_printf("%.1f km/h", speed);
break;
}
gtk_label_set_label(GTK_LABEL(_sdi_spd), buffer);
g_free(buffer);
/* altitude */
switch(_units)
{
case UNITS_MI:
case UNITS_NM:
buffer = g_strdup_printf("%d ft",
(gint)(_pos.altitude * 3.2808399f));
break;
default:
buffer = g_strdup_printf("%d m", _pos.altitude);
break;
}
gtk_label_set_label(GTK_LABEL(_sdi_alt), buffer);
g_free(buffer);
/* heading */
buffer = g_strdup_printf("%0.0f°", _gps.heading);
gtk_label_set_label(GTK_LABEL(_sdi_hea), buffer);
g_free(buffer);
/* local time */
strftime(litbuf, 15, "%X", localtime(&_pos.time));
gtk_label_set_label(GTK_LABEL(_sdi_tim), litbuf);
}
/* Sat in view */
buffer = g_strdup_printf("%d", _gps.satinview);
gtk_label_set_label(GTK_LABEL(_sdi_vie), buffer);
g_free(buffer);
/* Sat in use */
buffer = g_strdup_printf("%d", _gps.satinuse);
gtk_label_set_label(GTK_LABEL(_sdi_use), buffer);
g_free(buffer);
/* fix */
switch(_gps.fix)
{
case 2:
case 3: buffer = g_strdup_printf("%dD fix", _gps.fix); break;
default: buffer = g_strdup_printf("nofix"); break;
}
gtk_label_set_label(GTK_LABEL(_sdi_fix), buffer);
g_free(buffer);
if(_gps.fix == 1)
buffer = g_strdup("none");
else
{
switch (_gps.fixquality)
{
case 1 : buffer = g_strdup_printf(_("SPS")); break;
case 2 : buffer = g_strdup_printf(_("DGPS")); break;
case 3 : buffer = g_strdup_printf(_("PPS")); break;
case 4 : buffer = g_strdup_printf(_("Real Time Kinematic")); break;
case 5 : buffer = g_strdup_printf(_("Float RTK")); break;
case 6 : buffer = g_strdup_printf(_("Estimated")); break;
case 7 : buffer = g_strdup_printf(_("Manual")); break;
case 8 : buffer = g_strdup_printf(_("Simulation")); break;
default : buffer = g_strdup_printf(_("none")); break;
}
}
gtk_label_set_label(GTK_LABEL(_sdi_fqu), buffer);
g_free(buffer);
/* max speed */
{
gfloat maxspeed = _gps.maxspeed * UNITS_CONVERT[_units];
/* speed */
switch(_units)
{
case UNITS_MI:
buffer = g_strdup_printf("%.1f mph", maxspeed);
break;
case UNITS_NM:
buffer = g_strdup_printf("%.1f kn", maxspeed);
break;
default:
buffer = g_strdup_printf("%.1f km/h", maxspeed);
break;
}
gtk_label_set_label(GTK_LABEL(_sdi_msp), buffer);
g_free(buffer);
}
/* refresh sat panel */
gtk_widget_queue_draw_area(GTK_WIDGET(_sat_details_panel),
0, 0,
_sat_details_panel->allocation.width,
_sat_details_panel->allocation.height);
vprintf("%s(): return\n", __PRETTY_FUNCTION__);
return TRUE;
}
void
gps_display_data(void)
{
gchar *buffer, litbuf[LL_FMT_LEN];
printf("%s()\n", __PRETTY_FUNCTION__);
if(_gps.fix < 2)
{
/* no fix no fun */
gtk_label_set_label(GTK_LABEL(_text_lat), " --- ");
gtk_label_set_label(GTK_LABEL(_text_lon), " --- ");
gtk_label_set_label(GTK_LABEL(_text_speed), " --- ");
gtk_label_set_label(GTK_LABEL(_text_alt), " --- ");
gtk_label_set_label(GTK_LABEL(_text_time), " --:--:-- ");
}
else
{
gfloat speed = _gps.speed * UNITS_CONVERT[_units];
/* latitude */
lat_format(_gps.lat, litbuf);
gtk_label_set_label(GTK_LABEL(_text_lat), litbuf);
/* longitude */
lon_format(_gps.lon, litbuf);
gtk_label_set_label(GTK_LABEL(_text_lon), litbuf);
/* speed */
switch(_units)
{
case UNITS_MI:
buffer = g_strdup_printf("Spd: %.1f mph", speed);
break;
case UNITS_NM:
buffer = g_strdup_printf("Spd: %.1f kn", speed);
break;
default:
buffer = g_strdup_printf("Spd: %.1f km/h", speed);
break;
}
gtk_label_set_label(GTK_LABEL(_text_speed), buffer);
g_free(buffer);
/* altitude */
switch(_units)
{
case UNITS_MI:
case UNITS_NM:
buffer = g_strdup_printf("Alt: %d ft",
(gint)(_pos.altitude * 3.2808399f));
break;
default:
buffer = g_strdup_printf("Alt: %d m", _pos.altitude);
}
gtk_label_set_label(GTK_LABEL(_text_alt), buffer);
g_free(buffer);
/* local time */
strftime(litbuf, 15, "%X", localtime(&_pos.time));
gtk_label_set_label(GTK_LABEL(_text_time), litbuf);
}
/* refresh sat panel */
gtk_widget_queue_draw_area(GTK_WIDGET(_sat_panel),
0, 0,
_sat_panel->allocation.width,
_sat_panel->allocation.height);
/* refresh heading panel*/
gtk_widget_queue_draw_area(GTK_WIDGET(_heading_panel),
0, 0,
_heading_panel->allocation.width,
_heading_panel->allocation.height);
vprintf("%s(): return\n", __PRETTY_FUNCTION__);
return;
}
void
gps_hide_text(void)
{
printf("%s()\n", __PRETTY_FUNCTION__);
/* Clear gps data */
_gps.fix = 1;
_gps.satinuse = 0;
_gps.satinview = 0;
if(_gps_info)
gps_display_data();
vprintf("%s(): return\n", __PRETTY_FUNCTION__);
}
void
gps_show_info(void)
{
printf("%s()\n", __PRETTY_FUNCTION__);
if(_gps_info && _enable_gps)
gtk_widget_show_all(GTK_WIDGET(_gps_widget));
else
{
gps_hide_text();
gtk_widget_hide(GTK_WIDGET(_gps_widget));
}
vprintf("%s(): return\n", __PRETTY_FUNCTION__);
}
static void
draw_sat_info(GtkWidget *widget, gint x0, gint y0,
gint width, gint height, gboolean showsnr)
{
GdkGC *gc;
gint step, i, j, snr_height, bymargin, xoffset, yoffset;
gint x, y, x1, y1;
gchar *tmp = NULL;
printf("%s()\n", __PRETTY_FUNCTION__);
xoffset = x0;
yoffset = y0;
/* Bootom margin - 12% */
bymargin = height * 0.88f;
/* Bottom line */
gdk_draw_line(widget->window,
widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
xoffset + 5, yoffset + bymargin,
xoffset + width - 10 - 2, yoffset + bymargin);
gdk_draw_line(widget->window,
widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
xoffset + 5, yoffset + bymargin - 1,
xoffset + width - 10 - 2, yoffset + bymargin - 1);
if(_gps.satinview > 0 )
{
/* Left margin - 5pix, Right margin - 5pix */
step = (width - 10) / _gps.satinview;
for(i = 0; i < _gps.satinview; i++)
{
/* Sat used or not */
gc = _sat_info_gc1;
for(j = 0; j < _gps.satinuse ; j++)
{
if(_gps.satforfix[j] == _gps_sat[i].prn)
{
gc = _sat_info_gc2;
break;
}
}
x = 5 + i * step;
snr_height = _gps_sat[i].snr * height * 0.78f / 100;
y = height * 0.1f + (height * 0.78f - snr_height);
/* draw sat rectangle... */
gdk_draw_rectangle(widget->window,
gc,
TRUE,
xoffset + x,
yoffset + y,
step - 2,
snr_height);
if(showsnr && _gps_sat[i].snr > 0)
{
/* ...snr.. */
tmp = g_strdup_printf("%02d", _gps_sat[i].snr);
pango_layout_set_text(_sat_info_layout, tmp, 2);
pango_layout_get_pixel_size(_sat_info_layout, &x1, &y1);
gdk_draw_layout(widget->window,
widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
xoffset + x + ((step - 2) - x1)/2,
yoffset + y - 15,
_sat_info_layout);
g_free(tmp);
}
/* ...and sat number */
tmp = g_strdup_printf("%02d", _gps_sat[i].prn);
pango_layout_set_text(_sat_info_layout, tmp, 2);
pango_layout_get_pixel_size(_sat_info_layout, &x1, &y1);
gdk_draw_layout(widget->window,
widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
xoffset + x + ((step - 2) - x1)/2 ,
yoffset + bymargin + 1,
_sat_info_layout);
g_free(tmp);
}
}
vprintf("%s(): return\n", __PRETTY_FUNCTION__);
return;
}
static void
draw_sat_details(GtkWidget *widget, gint x0, gint y0,
gint width, gint height)
{
gint i, j, x, y, size, halfsize, xoffset, yoffset;
gint x1, y1;
gfloat tmp;
GdkColor color;
GdkGC *gc1, *gc2, *gc3, *gc;
gchar *buffer = NULL;
printf("%s()\n", __PRETTY_FUNCTION__);
size = MIN(width, height);
halfsize = size/2;
if(width > height)
{
xoffset = x0 + (width - height - 10) / 2;
yoffset = y0 + 5;
}
else
{
xoffset = x0 + 5;
yoffset = y0 + (height - width - 10) / 2;
}
/* 90 */
gdk_draw_arc(widget->window,
widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
FALSE,
xoffset + 2, yoffset + 2, size - 4, size - 4,
0, 64 * 360);
/* 60 */
gdk_draw_arc(widget->window,
widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
FALSE,
xoffset + size/6, yoffset + size/6,
size/6*4, size/6*4,
0, 64 * 360);
/* 30 */
gdk_draw_arc(widget->window,
widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
FALSE,
xoffset + size/6*2, yoffset + size/6*2,
size/6*2, size/6*2,
0, 64 * 360);
gint line[12] = {0,30,60,90,120,150,180,210,240,270,300,330};
for(i = 0; i < 6; i++)
{
/* line */
tmp = deg2rad(line[i]);
gdk_draw_line(widget->window,
widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
xoffset + halfsize + (halfsize -2) * sinf(tmp),
yoffset + halfsize - (halfsize -2) * cosf(tmp),
xoffset + halfsize - (halfsize -2) * sinf(tmp),
yoffset + halfsize + (halfsize -2) * cosf(tmp));
}
for(i = 0; i < 12; i++)
{
tmp = deg2rad(line[i]);
/* azimuth */
if(line[i] == 0)
buffer = g_strdup_printf("N");
else
buffer = g_strdup_printf("%d°", line[i]);
pango_layout_set_text(_sat_details_layout, buffer, -1);
pango_layout_get_pixel_size(_sat_details_layout, &x, &y);
gdk_draw_layout(widget->window,
widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
(xoffset + halfsize + (halfsize - size/12) * sinf(tmp)) - x/2,
(yoffset + halfsize - (halfsize - size/12) * cosf(tmp)) - y/2,
_sat_details_layout);
g_free(buffer);
}
/* elevation 30 */
tmp = deg2rad(30);
buffer = g_strdup_printf("30°");
pango_layout_set_text(_sat_details_layout, buffer, -1);
pango_layout_get_pixel_size(_sat_details_layout, &x, &y);
gdk_draw_layout(widget->window,
widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
(xoffset + halfsize + size/6*2 * sinf(tmp)) - x/2,
(yoffset + halfsize - size/6*2 * cosf(tmp)) - y/2,
_sat_details_layout);
g_free(buffer);
/* elevation 60 */
tmp = deg2rad(30);
buffer = g_strdup_printf("60°");
pango_layout_set_text(_sat_details_layout, buffer, -1);
pango_layout_get_pixel_size(_sat_details_layout, &x, &y);
gdk_draw_layout(widget->window,
widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
(xoffset + halfsize + size/6 * sinf(tmp)) - x/2,
(yoffset + halfsize - size/6 * cosf(tmp)) - y/2,
_sat_details_layout);
g_free(buffer);
color.red = 0;
color.green = 0;
color.blue = 0;
gc1 = gdk_gc_new (widget->window);
gdk_gc_set_rgb_fg_color (gc1, &color);
color.red = 0;
color.green = 0;
color.blue = 0xffff;
gc2 = gdk_gc_new (widget->window);
gdk_gc_set_rgb_fg_color (gc2, &color);
color.red = 0xffff;
color.green = 0xffff;
color.blue = 0xffff;
gc3 = gdk_gc_new (widget->window);
gdk_gc_set_rgb_fg_color (gc3, &color);
for(i = 0; i < _gps.satinview; i++)
{
/* Sat used or not */
gc = gc1;
for(j = 0; j < _gps.satinuse ; j++)
{
if(_gps.satforfix[j] == _gps_sat[i].prn)
{
gc = gc2;
break;
}
}
tmp = deg2rad(_gps_sat[i].azimuth);
x = xoffset + halfsize
+ (90 - _gps_sat[i].elevation)*halfsize/90 * sinf(tmp);
y = yoffset + halfsize
- (90 - _gps_sat[i].elevation)*halfsize/90 * cosf(tmp);
gdk_draw_arc (widget->window,
gc, TRUE,
x - 10, y - 10, 20, 20,
0, 64 * 360);
buffer = g_strdup_printf("%02d", _gps_sat[i].prn);
pango_layout_set_text(_sat_details_layout, buffer, -1);
pango_layout_get_pixel_size(_sat_details_layout, &x1, &y1);
gdk_draw_layout(widget->window,
gc3,
x - x1/2,
y - y1/2,
_sat_details_layout);
g_free(buffer);
}
g_object_unref (gc1);
g_object_unref (gc2);
g_object_unref (gc3);
vprintf("%s(): return\n", __PRETTY_FUNCTION__);
return;
}
static gboolean
sat_details_panel_expose(GtkWidget *widget, GdkEventExpose *event)
{
gint width, height, x, y;
gchar *buffer = NULL;
printf("%s()\n", __PRETTY_FUNCTION__);
width = widget->allocation.width;
height = widget->allocation.height * 0.9;
draw_sat_info(widget, 0, 0, width/2, height, TRUE);
draw_sat_details(widget, width/2, 0, width/2, height);
buffer = g_strdup_printf(
"%s: %d; %s: %d",
_("Satellites in view"), _gps.satinview,
_("in use"), _gps.satinuse);
pango_layout_set_text(_sat_details_expose_layout, buffer, -1);
pango_layout_get_pixel_size(_sat_details_expose_layout, &x, &y);
gdk_draw_layout(widget->window,
widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
10,
height*0.9 + 10,
_sat_details_expose_layout);
g_free(buffer);
buffer = g_strdup_printf("HDOP: %.01f", _gps.hdop);
pango_layout_set_text(_sat_details_expose_layout, buffer, -1);
pango_layout_get_pixel_size(_sat_details_expose_layout, &x, &y);
gdk_draw_layout(widget->window,
widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
(width/8) - x/2,
(height/6) - y/2,
_sat_details_expose_layout);
g_free(buffer);
buffer = g_strdup_printf("PDOP: %.01f", _gps.pdop);
pango_layout_set_text(_sat_details_expose_layout, buffer, -1);
pango_layout_get_pixel_size(_sat_details_expose_layout, &x, &y);
gdk_draw_layout(widget->window,
widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
(width/8) - x/2,
(height/6) - y/2 + 20,
_sat_details_expose_layout);
g_free(buffer);
buffer = g_strdup_printf("VDOP: %.01f", _gps.vdop);
pango_layout_set_text(_sat_details_expose_layout, buffer, -1);
pango_layout_get_pixel_size(_sat_details_expose_layout, &x, &y);
gdk_draw_layout(widget->window,
widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
(width/8) - x/2,
(height/6) - y/2 + 40,
_sat_details_expose_layout);
g_free(buffer);
vprintf("%s(): return\n", __PRETTY_FUNCTION__);
return TRUE;
}
void
gps_details(void)
{
static GtkWidget *dialog = NULL;
static GtkWidget *table = NULL;
static GtkWidget *label = NULL;
static GtkWidget *notebook = NULL;
printf("%s()\n", __PRETTY_FUNCTION__);
if(dialog == NULL)
{
dialog = gtk_dialog_new_with_buttons(_("GPS Details"),
GTK_WINDOW(_window), GTK_DIALOG_MODAL,
GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
NULL);
gtk_window_set_default_size(GTK_WINDOW(dialog), 600, 300);
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
notebook = gtk_notebook_new(), TRUE, TRUE, 0);
/* textual info */
gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
table = gtk_table_new(4, 6, FALSE),
label = gtk_label_new(_("GPS Information")));
_sat_details_panel = gtk_drawing_area_new ();
gtk_widget_set_size_request (_sat_details_panel, 300, 300);
/* sat details info */
gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
_sat_details_panel,
label = gtk_label_new(_("Satellites details")));
g_signal_connect (G_OBJECT (_sat_details_panel), "expose_event",
G_CALLBACK (sat_details_panel_expose), NULL);
gtk_table_attach(GTK_TABLE(table),
label = gtk_label_new(_("Latitude")),
0, 1, 0, 1, GTK_EXPAND | GTK_FILL, 0, 20, 4);
gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
gtk_table_attach(GTK_TABLE(table),
_sdi_lat = gtk_label_new(" --- "),
1, 2, 0, 1, GTK_EXPAND | GTK_FILL, 0, 2, 4);
gtk_misc_set_alignment(GTK_MISC(_sdi_lat), 0.f, 0.5f);
gtk_table_attach(GTK_TABLE(table),
label = gtk_label_new(_("Longitude")),
0, 1, 1, 2, GTK_EXPAND | GTK_FILL, 0, 20, 4);
gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
gtk_table_attach(GTK_TABLE(table),
_sdi_lon = gtk_label_new(" --- "),
1, 2, 1, 2, GTK_EXPAND | GTK_FILL, 0, 2, 4);
gtk_misc_set_alignment(GTK_MISC(_sdi_lon), 0.f, 0.5f);
gtk_table_attach(GTK_TABLE(table),
label = gtk_label_new(_("Speed")),
0, 1, 2, 3, GTK_EXPAND | GTK_FILL, 0, 20, 4);
gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
gtk_table_attach(GTK_TABLE(table),
_sdi_spd = gtk_label_new(" --- "),
1, 2, 2, 3, GTK_EXPAND | GTK_FILL, 0, 2, 4);
gtk_misc_set_alignment(GTK_MISC(_sdi_spd), 0.f, 0.5f);
gtk_table_attach(GTK_TABLE(table),
label = gtk_label_new(_("Altitude")),
0, 1, 3, 4, GTK_EXPAND | GTK_FILL, 0, 20, 4);
gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
gtk_table_attach(GTK_TABLE(table),
_sdi_alt = gtk_label_new(" --- "),
1, 2, 3, 4, GTK_EXPAND | GTK_FILL, 0, 2, 4);
gtk_misc_set_alignment(GTK_MISC(_sdi_alt), 0.f, 0.5f);
gtk_table_attach(GTK_TABLE(table),
label = gtk_label_new(_("Heading")),
0, 1, 4, 5, GTK_EXPAND | GTK_FILL, 0, 20, 4);
gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
gtk_table_attach(GTK_TABLE(table),
_sdi_hea = gtk_label_new(" --- "),
1, 2, 4, 5, GTK_EXPAND | GTK_FILL, 0, 2, 4);
gtk_misc_set_alignment(GTK_MISC(_sdi_hea), 0.f, 0.5f);
gtk_table_attach(GTK_TABLE(table),
label = gtk_label_new(_("Local time")),
0, 1, 5, 6, GTK_EXPAND | GTK_FILL, 0, 20, 4);
gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
gtk_table_attach(GTK_TABLE(table),
_sdi_tim = gtk_label_new(" --:--:-- "),
1, 2, 5, 6, GTK_EXPAND | GTK_FILL, 0, 2, 4);
gtk_misc_set_alignment(GTK_MISC(_sdi_tim), 0.f, 0.5f);
gtk_table_attach(GTK_TABLE(table),
label = gtk_label_new(_("Sat in view")),
2, 3, 0, 1, GTK_EXPAND | GTK_FILL, 0, 20, 4);
gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
gtk_table_attach(GTK_TABLE(table),
_sdi_vie = gtk_label_new("0"),
3, 4, 0, 1, GTK_EXPAND | GTK_FILL, 0, 2, 4);
gtk_misc_set_alignment(GTK_MISC(_sdi_vie), 0.f, 0.5f);
gtk_table_attach(GTK_TABLE(table),
label = gtk_label_new(_("Sat in use")),
2, 3, 1, 2, GTK_EXPAND | GTK_FILL, 0, 20, 4);
gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
gtk_table_attach(GTK_TABLE(table),
_sdi_use = gtk_label_new("0"),
3, 4, 1, 2, GTK_EXPAND | GTK_FILL, 0, 2, 4);
gtk_misc_set_alignment(GTK_MISC(_sdi_use), 0.f, 0.5f);
gtk_table_attach(GTK_TABLE(table),
label = gtk_label_new(_("Fix")),
2, 3, 2, 3, GTK_EXPAND | GTK_FILL, 0, 20, 4);
gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
gtk_table_attach(GTK_TABLE(table),
_sdi_fix = gtk_label_new(_("nofix")),
3, 4, 2, 3, GTK_EXPAND | GTK_FILL, 0, 2, 4);
gtk_misc_set_alignment(GTK_MISC(_sdi_fix), 0.f, 0.5f);
gtk_table_attach(GTK_TABLE(table),
label = gtk_label_new(_("Fix Quality")),
2, 3, 3, 4, GTK_EXPAND | GTK_FILL, 0, 20, 4);
gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
gtk_table_attach(GTK_TABLE(table),
_sdi_fqu = gtk_label_new(_("none")),
3, 4, 3, 4, GTK_EXPAND | GTK_FILL, 0, 2, 4);
gtk_misc_set_alignment(GTK_MISC(_sdi_fqu), 0.f, 0.5f);
gtk_table_attach(GTK_TABLE(table),
label = gtk_label_new(_("Max speed")),
2, 3, 5, 6, GTK_EXPAND | GTK_FILL, 0, 20, 4);
gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
gtk_table_attach(GTK_TABLE(table),
_sdi_msp = gtk_label_new(" --- "),
3, 4, 5, 6, GTK_EXPAND | GTK_FILL, 0, 2, 4);
gtk_misc_set_alignment(GTK_MISC(_sdi_msp), 0.f, 0.5f);
}
gtk_widget_show_all(dialog);
_satdetails_on = TRUE;
gps_display_details();
while(GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(dialog)))
{
_satdetails_on = FALSE;
break;
}
gtk_widget_hide(dialog);
vprintf("%s(): return\n", __PRETTY_FUNCTION__);
}
/**
* Render a single track line to _map_pixmap. If either point on the line
* is a break (defined as unity == 0), a circle is drawn at the other point.
* IT IS AN ERROR FOR BOTH POINTS TO INDICATE A BREAK.
*/
void
map_render_segment(GdkGC *gc_norm, GdkGC *gc_alt,
gint unitx1, gint unity1, gint unitx2, gint unity2)
{
/* vprintf("%s(%d, %d, %d, %d)\n", __PRETTY_FUNCTION__,
unitx1, unity1, unitx2, unity2); */
if(!unity1)
{
gint x2, y2;
unit2buf(unitx2, unity2, x2, y2);
gdk_draw_arc(_map_pixmap, gc_alt,
FALSE, /* FALSE: not filled. */
x2 - _draw_width,
y2 - _draw_width,
2 * _draw_width,
2 * _draw_width,
0, /* start at 0 degrees. */
360 * 64);
}
else if(!unity2)
{
gint x1, y1;
unit2buf(unitx1, unity1, x1, y1);
gdk_draw_arc(_map_pixmap, gc_alt,
FALSE, /* FALSE: not filled. */
x1 - _draw_width,
y1 - _draw_width,
2 * _draw_width,
2 * _draw_width,
0, /* start at 0 degrees. */
360 * 64);
}
else
{
gint x1, y1, x2, y2;
unit2buf(unitx1, unity1, x1, y1);
unit2buf(unitx2, unity2, x2, y2);
gdk_draw_line(_map_pixmap, gc_norm, x1, y1, x2, y2);
}
/* vprintf("%s(): return\n", __PRETTY_FUNCTION__); */
}
/**
* Render all track data onto the _map_pixmap. Note that this does not
* clear the pixmap of previous track data (use map_force_redraw() for
* that), and also note that this method does not queue any redraws, so it
* is up to the caller to decide which part of the track really needs to be
* redrawn.
*/
static void
map_render_path(Path *path, GdkGC **gc)
{
Point *curr;
WayPoint *wcurr;
printf("%s()\n", __PRETTY_FUNCTION__);
/* gc is a pointer to the first GC to use (for plain points). (gc + 1)
* is a pointer to the GC to use for waypoints, and (gc + 2) is a pointer
* to the GC to use for breaks. */
/* else there is a route to draw. */
for(curr = path->head, wcurr = path->whead; curr++ != path->tail; )
{
/* Draw the line from (curr - 1) to (curr). */
map_render_segment(gc[0], gc[2],
curr[-1].unitx, curr[-1].unity, curr->unitx, curr->unity);
/* Now, check if curr is a waypoint. */
if(wcurr <= path->wtail && wcurr->point == curr)
{
gint x1, y1;
unit2buf(wcurr->point->unitx, wcurr->point->unity, x1, y1);
gdk_draw_arc(_map_pixmap,
gc[1],
FALSE, /* FALSE: not filled. */
x1 - _draw_width,
y1 - _draw_width,
2 * _draw_width,
2 * _draw_width,
0, /* start at 0 degrees. */
360 * 64);
wcurr++;
}
}
vprintf("%s(): return\n", __PRETTY_FUNCTION__);
}
void
map_render_paths()
{
printf("%s()\n", __PRETTY_FUNCTION__);
if((_show_paths & ROUTES_MASK) && _route.head != _route.tail)
{
WayPoint *next_way;
map_render_path(&_route, _gc + COLORABLE_ROUTE);
next_way = path_get_next_way();
/* Now, draw the next waypoint on top of all other waypoints. */
if(next_way)
{
gint x1, y1;
unit2buf(next_way->point->unitx, next_way->point->unity, x1, y1);
/* Draw the next waypoint as a break. */
gdk_draw_arc(_map_pixmap,
_gc[COLORABLE_ROUTE_BREAK],
FALSE, /* FALSE: not filled. */
x1 - _draw_width,
y1 - _draw_width,
2 * _draw_width,
2 * _draw_width,
0, /* start at 0 degrees. */
360 * 64);
}
}
if(_show_paths & TRACKS_MASK)
map_render_path(&_track, _gc + COLORABLE_TRACK);
vprintf("%s(): return\n", __PRETTY_FUNCTION__);
}
/**
* Update all GdkGC objects to reflect the current _draw_width.
*/
#define UPDATE_GC(gc) \
gdk_gc_set_line_attributes(gc, \
_draw_width, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND);
void
update_gcs()
{
gint i;
printf("%s()\n", __PRETTY_FUNCTION__);
for(i = 0; i < COLORABLE_ENUM_COUNT; i++)
{
gdk_color_alloc(gtk_widget_get_colormap(_map_widget), &_color[i]);
if(_gc[i])
g_object_unref(_gc[i]);
_gc[i] = gdk_gc_new(_map_pixmap);
gdk_gc_set_foreground(_gc[i], &_color[i]);
gdk_gc_set_line_attributes(_gc[i],
_draw_width, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND);
}
/* Update the _map_widget's gc's. */
gdk_gc_set_line_attributes(
_map_widget->style->fg_gc[GTK_STATE_ACTIVE],
2, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
gdk_gc_set_line_attributes(
_map_widget->style->bg_gc[GTK_STATE_ACTIVE],
2, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
vprintf("%s(): return\n", __PRETTY_FUNCTION__);
}
/**
* Call gtk_window_present() on Maemo Mapper. This also checks the
* configuration and brings up the Settings dialog if the GPS Receiver is
* not set up, the first time it is called.
*/
gboolean
window_present()
{
static gint been_here = 0;
static gint done_here = 0;
printf("%s()\n", __PRETTY_FUNCTION__);
if(!been_here++)
{
/* Set connection state first, to avoid going into this if twice. */
if(_gri.type == GPS_RCVR_NONE && _enable_gps)
{
GtkWidget *confirm;
gtk_window_present(GTK_WINDOW(_window));
confirm = hildon_note_new_confirmation(GTK_WINDOW(_window),
_("It looks like this is your first time running"
" Maemo Mapper. Press OK to view the the help pages."
" Otherwise, press Cancel to continue."));
if(GTK_RESPONSE_OK == gtk_dialog_run(GTK_DIALOG(confirm)))
ossohelp_show(_osso, HELP_ID_INTRO, 0);
gtk_widget_destroy(confirm);
if(settings_dialog())
{
popup_error(_window,
_("OpenStreetMap.org provides public, free-to-use maps. "
"You can also download a sample set of repositories from "
" the internet by using the \"Download...\" button."));
repoman_dialog();
if(_curr_repo->type != REPOTYPE_NONE)
{
confirm = hildon_note_new_confirmation(GTK_WINDOW(_window),
_("You will now see a blank screen. You can download"
" maps using the \"Manage Maps\" menu item in the"
" \"Maps\" menu. Or, press OK to enable"
" Auto-Download."));
if(GTK_RESPONSE_OK == gtk_dialog_run(GTK_DIALOG(confirm)))
{
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(
_menu_maps_auto_download_item), TRUE);
}
gtk_widget_destroy(confirm);
}
}
else
gtk_main_quit();
}
/* Connect to receiver. */
if(_enable_gps)
rcvr_connect();
++done_here; /* Don't ask... */
}
if(done_here)
{
gtk_window_present(GTK_WINDOW(_window));
g_timeout_add(250, (GSourceFunc)banner_reset, NULL);
}
vprintf("%s(): return\n", __PRETTY_FUNCTION__);
return FALSE;
}
/**
* "Set" the mark, which translates the current GPS position into on-screen
* units in preparation for drawing the mark with map_draw_mark().
*/
static void
map_set_mark()
{
gfloat sqrt_speed, tmp, vel_offset_devx, vel_offset_devy;
printf("%s()\n", __PRETTY_FUNCTION__);
tmp = deg2rad(_gps.heading);
sqrt_speed = VELVEC_SIZE_FACTOR * sqrtf(10 + _gps.speed);
gdk_pixbuf_rotate_vector(&vel_offset_devx, &vel_offset_devy,
_map_rotate_matrix,
sqrt_speed * sinf(tmp), -sqrt_speed * cosf(tmp));
unit2buf(_pos.unitx, _pos.unity, _mark_bufx1, _mark_bufy1);
_mark_bufx2 = _mark_bufx1 + (_show_velvec ? (vel_offset_devx + 0.5f) : 0);
_mark_bufy2 = _mark_bufy1 + (_show_velvec ? (vel_offset_devy + 0.5f) : 0);
_mark_minx = MIN(_mark_bufx1, _mark_bufx2) - (2 * _draw_width);
_mark_miny = MIN(_mark_bufy1, _mark_bufy2) - (2 * _draw_width);
_mark_width = abs(_mark_bufx1 - _mark_bufx2) + (4 * _draw_width);
_mark_height = abs(_mark_bufy1 - _mark_bufy2) + (4 * _draw_width);
vprintf("%s(): return\n", __PRETTY_FUNCTION__);
}
/**
* Force a redraw of the entire _map_pixmap, including fetching the
* background maps from disk and redrawing the tracks on top of them.
*/
void
map_force_redraw()
{
printf("%s()\n", __PRETTY_FUNCTION__);
gdk_draw_pixbuf(
_map_pixmap,
_map_widget->style->fg_gc[GTK_STATE_ACTIVE],
_map_pixbuf,
0, 0,
0, 0,
_screen_width_pixels, _screen_height_pixels,
GDK_RGB_DITHER_NONE, 0, 0);
MACRO_MAP_RENDER_DATA();
MACRO_QUEUE_DRAW_AREA();
vprintf("%s(): return\n", __PRETTY_FUNCTION__);
}
static Point
map_calc_new_center(gint zoom)
{
Point new_center;
printf("%s()\n", __PRETTY_FUNCTION__);
switch(_center_mode)
{
case CENTER_LEAD:
{
gfloat tmp = deg2rad(_gps.heading);
gfloat screen_pixels = _screen_width_pixels
+ (((gint)_screen_height_pixels
- (gint)_screen_width_pixels)
* fabs(cosf(deg2rad(
ROTATE_DIR_ENUM_DEGREES[_rotate_dir] -
(_center_rotate ? 0
: (_next_map_rotate_angle
- (gint)(_gps.heading)))))));
gfloat lead_pixels = 0.0025f
* pixel2zunit((gint)screen_pixels, zoom)
* _lead_ratio
* VELVEC_SIZE_FACTOR
* (_lead_is_fixed ? 7 : sqrtf(_gps.speed));
new_center.unitx = _pos.unitx + (gint)(lead_pixels * sinf(tmp));
new_center.unity = _pos.unity - (gint)(lead_pixels * cosf(tmp));
break;
}
case CENTER_LATLON:
new_center.unitx = _pos.unitx;
new_center.unity = _pos.unity;
break;
default:
new_center.unitx = _next_center.unitx;
new_center.unity = _next_center.unity;
}
vprintf("%s(): return (%d, %d)\n", __PRETTY_FUNCTION__,
new_center.unitx, new_center.unity);
return new_center;
}
/**
* Center the view on the given unitx/unity.
*/
void
map_center_unit_full(Point new_center,
gint zoom, gint rotate_angle)
{
MapRenderTask *mrt;
printf("%s(%d, %d)\n", __PRETTY_FUNCTION__,
new_center.unitx, new_center.unity);
if(!_mouse_is_down)
{
/* Assure that _center.unitx/y are bounded. */
BOUND(new_center.unitx, 0, WORLD_SIZE_UNITS);
BOUND(new_center.unity, 0, WORLD_SIZE_UNITS);
mrt = g_slice_new(MapRenderTask);
++_redraw_count;
mrt->repo = _curr_repo;
mrt->old_offsetx = _map_offset_devx;
mrt->old_offsety = _map_offset_devy;
mrt->new_center = _next_center = new_center;
mrt->screen_width_pixels = _screen_width_pixels;
mrt->screen_height_pixels = _screen_height_pixels;
mrt->zoom = _next_zoom = zoom;
mrt->rotate_angle = _next_map_rotate_angle = rotate_angle;
gtk_widget_queue_draw_area(
_map_widget,
_redraw_wait_bounds.x,
_redraw_wait_bounds.y,
_redraw_wait_bounds.width + (_redraw_count - 1)
* HOURGLASS_SEPARATION,
_redraw_wait_bounds.height);
g_thread_pool_push(_mrt_thread_pool, mrt, NULL);
}
vprintf("%s(): return\n", __PRETTY_FUNCTION__);
}
void
map_center_unit(Point new_center)
{
map_center_unit_full(new_center, _next_zoom,
_center_mode > 0 && _center_rotate
? _gps.heading : _next_map_rotate_angle);
}
void
map_rotate(gint rotate_angle)
{
if(_center_mode > 0 && gtk_check_menu_item_get_active(
GTK_CHECK_MENU_ITEM(_menu_view_rotate_auto_item)))
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(
_menu_view_rotate_auto_item), FALSE);
map_center_unit_full(map_calc_new_center(_next_zoom), _next_zoom,
(_next_map_rotate_angle + rotate_angle) % 360);
}
void
map_center_zoom(gint zoom)
{
map_center_unit_full(map_calc_new_center(zoom), zoom,
_center_mode > 0 && _center_rotate
? _gps.heading : _next_map_rotate_angle);
}
/**
* Pan the view by the given number of units in the X and Y directions.
*/
void
map_pan(gint delta_unitx, gint delta_unity)
{
Point new_center;
printf("%s(%d, %d)\n", __PRETTY_FUNCTION__, delta_unitx, delta_unity);
if(_center_mode > 0)
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(
_menu_view_ac_none_item), TRUE);
new_center.unitx = _center.unitx + delta_unitx;
new_center.unity = _center.unity + delta_unity;
map_center_unit_full(new_center, _next_zoom,
_center_mode > 0 && _center_rotate
? _gps.heading : _next_map_rotate_angle);
vprintf("%s(): return\n", __PRETTY_FUNCTION__);
}
/**
* Initiate a move of the mark from the old location to the current
* location. This function queues the draw area of the old mark (to force
* drawing of the background map), then updates the mark, then queus the
* draw area of the new mark.
*/
void
map_move_mark()
{
printf("%s()\n", __PRETTY_FUNCTION__);
/* Just queue the old and new draw areas. */
gtk_widget_queue_draw_area(_map_widget,
_mark_minx + _map_offset_devx,
_mark_miny + _map_offset_devy,
_mark_width,
_mark_height);
map_set_mark();
gtk_widget_queue_draw_area(_map_widget,
_mark_minx + _map_offset_devx,
_mark_miny + _map_offset_devy,
_mark_width,
_mark_height);
vprintf("%s(): return\n", __PRETTY_FUNCTION__);
}
/**
* Make sure the mark is up-to-date. This function triggers a panning of
* the view if the mark is appropriately close to the edge of the view.
*/
void
map_refresh_mark(gboolean force_redraw)
{
printf("%s()\n", __PRETTY_FUNCTION__);
gint new_center_devx, new_center_devy;
Point new_center = map_calc_new_center(_next_zoom);
unit2buf(new_center.unitx, new_center.unity,
new_center_devx, new_center_devy);
if(force_redraw || (_center_mode > 0 && _gps.speed > 0 &&
(((unsigned)(new_center_devx - (_screen_width_pixels * _center_ratio / 20))
> ((10 - _center_ratio) * _screen_width_pixels / 10))
|| ((unsigned)(new_center_devy - (_screen_height_pixels * _center_ratio / 20))
> ((10 - _center_ratio) * _screen_height_pixels / 10))
|| (_center_rotate &&
abs(_next_map_rotate_angle - _gps.heading)
> (4*(10-_rotate_sens))))))
{
map_move_mark();
map_center_unit(new_center);
}
else
{
/* We're not changing the view - just move the mark. */
map_move_mark();
}
/* Draw speed info */
if(_speed_limit_on)
speed_limit();
vprintf("%s(): return\n", __PRETTY_FUNCTION__);
}
gboolean
map_download_refresh_idle(MapUpdateTask *mut)
{
vprintf("%s(%p, %d, %d, %d)\n", __PRETTY_FUNCTION__, mut,
mut->zoom, mut->tilex, mut->tiley);
/* Test if download succeeded (only if retries != 0). */
if(mut->pixbuf && mut->repo == _curr_repo)
{
gint zoff = mut->zoom - _zoom;
/* Update the UI to reflect the updated map database. */
/* Only refresh at same or "lower" (more detailed) zoom level. */
if(mut->update_type == MAP_UPDATE_AUTO && (unsigned)zoff <= 4)
{
gfloat destx, desty;
gint boundx, boundy, width, height;
pixel2buf(
tile2pixel(mut->tilex << zoff)
+ ((TILE_SIZE_PIXELS << zoff) >> 1),
tile2pixel(mut->tiley << zoff)
+ ((TILE_SIZE_PIXELS << zoff) >> 1),
destx, desty);
/* Multiply the matrix to cause blitting. */
if(zoff)
gdk_pixbuf_rotate_matrix_mult_number(
_map_rotate_matrix, 1 << zoff);
gdk_pixbuf_rotate(_map_pixbuf,
destx, desty,
_map_rotate_matrix,
mut->pixbuf,
TILE_SIZE_PIXELS / 2,
TILE_SIZE_PIXELS / 2,
TILE_SIZE_PIXELS,
TILE_SIZE_PIXELS,
&boundx, &boundy, &width, &height);
/* Un-multiply the matrix that we used for blitting. */
if(zoff)
gdk_pixbuf_rotate_matrix_mult_number(
_map_rotate_matrix, 1.f / (1 << zoff));
if(width * height)
{
gdk_draw_pixbuf(
_map_pixmap,
_map_widget->style->fg_gc[GTK_STATE_ACTIVE],
_map_pixbuf,
boundx, boundy,
boundx, boundy,
width, height,
GDK_RGB_DITHER_NONE, 0, 0);
MACRO_MAP_RENDER_DATA();
gtk_widget_queue_draw_area(
_map_widget, boundx, boundy, width, height);
}
}
g_object_unref(mut->pixbuf);
}
else if(mut->vfs_result != GNOME_VFS_OK)
{
_dl_errors++;
}
if(++_curr_download == _num_downloads)
{
if(_download_banner)
{
gtk_widget_destroy(_download_banner);
_download_banner = NULL;
}
_num_downloads = _curr_download = 0;
g_thread_pool_stop_unused_threads();
#ifndef MAPDB_SQLITE
if(_curr_repo->db)
gdbm_sync(_curr_repo->db);
#endif
if(_dl_errors)
{
gchar buffer[BUFFER_SIZE];
snprintf(buffer, sizeof(buffer), "%d %s", _dl_errors,
_("maps failed to download."));
MACRO_BANNER_SHOW_INFO(_window, buffer);
_dl_errors = 0;
}
else if(mut->update_type != MAP_UPDATE_AUTO)
{
/* Update the map. */
map_refresh_mark(TRUE);
}
}
else if(_download_banner)
{
hildon_banner_set_fraction(HILDON_BANNER(_download_banner),
_curr_download / (double)_num_downloads);
}
g_mutex_lock(_mut_priority_mutex);
g_hash_table_remove(_mut_exists_table, mut);
g_mutex_unlock(_mut_priority_mutex);
g_slice_free(MapUpdateTask, mut);
vprintf("%s(): return\n", __PRETTY_FUNCTION__);
return FALSE;
}
/**
* Set the current zoom level. If the given zoom level is the same as the
* current zoom level, or if the new zoom is invalid
* (not MIN_ZOOM <= new_zoom < MAX_ZOOM), then this method does nothing.
*/
void
map_set_zoom(gint new_zoom)
{
printf("%s(%d)\n", __PRETTY_FUNCTION__, _zoom);
/* This if condition also checks for new_zoom >= 0. */
if((unsigned)new_zoom > MAX_ZOOM)
return;
map_center_zoom(new_zoom / _curr_repo->view_zoom_steps
* _curr_repo->view_zoom_steps);
vprintf("%s(): return\n", __PRETTY_FUNCTION__);
}
static gboolean
map_replace_pixbuf_idle(MapRenderTask *mrt)
{
printf("%s()\n", __PRETTY_FUNCTION__);
if(!_mouse_is_down
&& mrt->screen_width_pixels == _screen_width_pixels
&& mrt->screen_height_pixels == _screen_height_pixels)
{
g_object_unref(_map_pixbuf);
_map_pixbuf = mrt->pixbuf;
_center = mrt->new_center;
_zoom = mrt->zoom;
_map_rotate_angle = mrt->rotate_angle;
gdk_pixbuf_rotate_matrix_fill_for_rotation(
_map_rotate_matrix,
deg2rad(ROTATE_DIR_ENUM_DEGREES[_rotate_dir]
- _map_rotate_angle));
gdk_pixbuf_rotate_matrix_fill_for_rotation(
_map_reverse_matrix,
deg2rad(_map_rotate_angle
- ROTATE_DIR_ENUM_DEGREES[_rotate_dir]));
g_slice_free(MapRenderTask, mrt);
--_redraw_count;
_map_offset_devx = 0;
_map_offset_devy = 0;
map_set_mark();
map_force_redraw();
}
else
{
/* Ignore this new pixbuf. We have newer ones coming. */
g_object_unref(mrt->pixbuf);
g_slice_free(MapRenderTask, mrt);
gtk_widget_queue_draw_area(
_map_widget,
_redraw_wait_bounds.x,
_redraw_wait_bounds.y,
_redraw_wait_bounds.width + (_redraw_count - 1)
* HOURGLASS_SEPARATION,
_redraw_wait_bounds.height * _redraw_count);
--_redraw_count;
}
vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
return FALSE;
}
gboolean
thread_render_map(MapRenderTask *mrt)
{
gfloat matrix[4];
gint start_tilex, start_tiley, stop_tilex, stop_tiley;
gint x = 0, y, num_tilex, num_tiley;
gint diag_halflength_units;
gfloat angle_rad;
gint tile_rothalf_pixels;
gint curr_tile_to_draw, num_tiles_to_draw;
gfloat *tile_dev;
ThreadLatch *refresh_latch = NULL;
gint cache_amount;
static gint auto_download_batch_id = 0;
printf("%s(%d, %d, %d, %d, %d, %d)\n", __PRETTY_FUNCTION__,
mrt->screen_width_pixels, mrt->screen_height_pixels,
mrt->new_center.unitx, mrt->new_center.unity, mrt->zoom,
mrt->rotate_angle);
/* If there are more render tasks in the queue, skip this one. */
if(g_thread_pool_unprocessed(_mrt_thread_pool))
{
g_slice_free(MapRenderTask, mrt);
gtk_widget_queue_draw_area(
_map_widget,
_redraw_wait_bounds.x,
_redraw_wait_bounds.y,
_redraw_wait_bounds.width + (_redraw_count - 1)
* HOURGLASS_SEPARATION,
_redraw_wait_bounds.height * _redraw_count);
--_redraw_count;
vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
return FALSE;
}
angle_rad = deg2rad(ROTATE_DIR_ENUM_DEGREES[_rotate_dir]
- mrt->rotate_angle);
gdk_pixbuf_rotate_matrix_fill_for_rotation(matrix, angle_rad);
/* Determine (roughly) the tiles we might have to process.
* Basically, we take the center unit and subtract the maximum dimension
* of the screen plus the maximum additional pixels of a rotated tile.
*/
tile_rothalf_pixels = MAX(
fabs(TILE_HALFDIAG_PIXELS * sinf((PI / 4) - angle_rad)),
fabs(TILE_HALFDIAG_PIXELS * cosf((PI / 4) - angle_rad)));
mrt->zoom = _next_zoom;
if(mrt->repo->type != REPOTYPE_NONE && mrt->repo->db)
cache_amount = _auto_download_precache;
else
cache_amount = 1; /* No cache. */
diag_halflength_units = pixel2zunit(TILE_HALFDIAG_PIXELS
+ MAX(mrt->screen_width_pixels, mrt->screen_height_pixels) / 2,
mrt->zoom);
start_tilex = unit2ztile(
mrt->new_center.unitx - diag_halflength_units, mrt->zoom);
start_tilex = MAX(start_tilex - (cache_amount - 1), 0);
start_tiley = unit2ztile(
mrt->new_center.unity - diag_halflength_units, mrt->zoom);
start_tiley = MAX(start_tiley - (cache_amount - 1), 0);
stop_tilex = unit2ztile(mrt->new_center.unitx + diag_halflength_units,
mrt->zoom);
stop_tilex = MIN(stop_tilex + (cache_amount - 1),
unit2ztile(WORLD_SIZE_UNITS, mrt->zoom));
stop_tiley = unit2ztile(mrt->new_center.unity + diag_halflength_units,
mrt->zoom);
stop_tiley = MIN(stop_tiley + (cache_amount - 1),
unit2ztile(WORLD_SIZE_UNITS, mrt->zoom));
num_tilex = (stop_tilex - start_tilex + 1);
num_tiley = (stop_tiley - start_tiley + 1);
tile_dev = g_new0(gfloat, num_tilex * num_tiley * 2);
++auto_download_batch_id;
/* Iterate through the tiles and mark which ones need retrieval. */
num_tiles_to_draw = 0;
for(y = 0; y < num_tiley; ++y)
{
for(x = 0; x < num_tilex; ++x)
{
gfloat devx, devy;
/* Find the device location of this tile's center. */
pixel2buf_full(
tile2pixel(x + start_tilex) + (TILE_SIZE_PIXELS >> 1),
tile2pixel(y + start_tiley) + (TILE_SIZE_PIXELS >> 1),
devx, devy,
mrt->new_center, mrt->zoom, matrix);
/* Skip this tile under the following conditions:
* devx < -tile_rothalf_pixels
* devx > _screen_width_pixels + tile_rothalf_pixels
* devy < -tile_rothalf_pixels
* devy > _screen_height_pixels + tile_rothalf_pixels
*/
if(((unsigned)(devx + tile_rothalf_pixels))
< (_screen_width_pixels + (2 * tile_rothalf_pixels))
&& ((unsigned)(devy + tile_rothalf_pixels))
< (_screen_height_pixels + (2 * tile_rothalf_pixels)))
{
tile_dev[2 * (y * num_tilex + x)] = devx;
tile_dev[2 * (y * num_tilex + x) + 1] = devy;
++num_tiles_to_draw;
}
else
{
tile_dev[2 * (y * num_tilex + x)] = FLT_MAX;
}
}
}
mrt->pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8,
mrt->screen_width_pixels, mrt->screen_height_pixels);
/* Iterate through the tiles, get them (or queue a download if they're
* not in the cache), and rotate them into the pixbuf. */
for(y = curr_tile_to_draw = 0; y < num_tiley; ++y)
{
gint tiley = y + start_tiley;
for(x = 0; x < num_tilex; ++x)
{
GdkPixbuf *tile_pixbuf = NULL;
gboolean started_download = FALSE;
gint zoff;
gint tilex;
tilex = x + start_tilex;
zoff = mrt->repo->double_size ? 1 : 0;
/* Iteratively try to retrieve a map to draw the tile. */
while((mrt->zoom + zoff) <= MAX_ZOOM && zoff < TILE_SIZE_P2)
{
/* Check if we're actually going to draw this map. */
if(tile_dev[2 * (y*num_tilex + x)] != FLT_MAX)
{
if(NULL != (tile_pixbuf = mapdb_get(
mrt->repo, mrt->zoom + zoff,
tilex >> zoff,
tiley >> zoff)))
{
/* Found a map. */
break;
}
}
/* Else we're not going to be drawing this map, so just check
* if it's in the database. */
else if(mapdb_exists(
mrt->repo, mrt->zoom + zoff,
tilex >> zoff,
tiley >> zoff, FALSE))
{
break;
}
/* No map; download, if we should. */
if(!started_download && _auto_download
&& mrt->repo->type != REPOTYPE_NONE
/* Make sure this map matches the dl_zoom_steps,
* or that there currently is no cache. */
&& (!mrt->repo->db || !((mrt->zoom + zoff
- (mrt->repo->double_size ? 1 : 0))
% mrt->repo->dl_zoom_steps))
/* Make sure this tile is even possible. */
&& ((unsigned)tilex < unit2ztile(WORLD_SIZE_UNITS,
mrt->zoom + zoff)
&& (unsigned)tiley < unit2ztile(WORLD_SIZE_UNITS,
mrt->zoom + zoff)))
{
started_download = TRUE;
if(!refresh_latch)
{
refresh_latch = g_slice_new(ThreadLatch);
refresh_latch->is_open = FALSE;
refresh_latch->is_done_adding_tasks = FALSE;
refresh_latch->num_tasks = 1;
refresh_latch->num_done = 0;
refresh_latch->mutex = g_mutex_new();
refresh_latch->cond = g_cond_new();
}
else
++refresh_latch->num_tasks;
mapdb_initiate_update(
mrt->repo,
mrt->zoom + zoff,
tilex >> zoff,
tiley >> zoff,
MAP_UPDATE_AUTO,
auto_download_batch_id,
(abs((tilex >> zoff) - unit2ztile(
mrt->new_center.unitx, mrt->zoom + zoff))
+ abs((tiley >> zoff) - unit2ztile(
mrt->new_center.unity, mrt->zoom + zoff))),
refresh_latch);
}
/* Try again at a coarser resolution. */
++zoff;
}
if(tile_pixbuf)
{
gint boundx, boundy, width, height;
if(zoff)
gdk_pixbuf_rotate_matrix_mult_number(matrix, 1 << zoff);
gdk_pixbuf_rotate(mrt->pixbuf,
tile_dev[2 * (y * num_tilex + x)],
tile_dev[2 * (y * num_tilex + x) + 1],
matrix,
tile_pixbuf,
((tilex - ((tilex >> zoff) << zoff))
<< (TILE_SIZE_P2 - zoff))
+ (TILE_SIZE_PIXELS >> (1 + zoff)),
((tiley - ((tiley>>zoff) << zoff))
<< (TILE_SIZE_P2 - zoff))
+ (TILE_SIZE_PIXELS >> (1 + zoff)),
TILE_SIZE_PIXELS >> zoff,
TILE_SIZE_PIXELS >> zoff,
&boundx, &boundy, &width, &height);
g_object_unref(tile_pixbuf);
/* Un-multiply the matrix that we used for blitting. */
if(zoff)
gdk_pixbuf_rotate_matrix_mult_number(
matrix, 1.f / (1 << zoff));
}
/* usleep(10000); DEBUG */
}
}
/* Don't replace the pixbuf unless/until the mouse is released. */
g_mutex_lock(_mouse_mutex);
g_idle_add_full(G_PRIORITY_HIGH_IDLE,
(GSourceFunc)map_replace_pixbuf_idle, mrt, NULL);
g_mutex_unlock(_mouse_mutex);
/* Release the view-change lock. */
if(refresh_latch)
{
g_mutex_lock(refresh_latch->mutex);
if(refresh_latch->num_tasks == refresh_latch->num_done)
{
/* Fast little workers, aren't they? */
g_mutex_unlock(refresh_latch->mutex);
g_cond_free(refresh_latch->cond);
g_mutex_free(refresh_latch->mutex);
g_slice_free(ThreadLatch, refresh_latch);
}
else
{
refresh_latch->is_done_adding_tasks = TRUE;
refresh_latch->is_open = TRUE;
g_cond_signal(refresh_latch->cond);
g_mutex_unlock(refresh_latch->mutex);
}
}
g_free(tile_dev);
vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
return FALSE;
}
gboolean
map_cb_configure(GtkWidget *widget, GdkEventConfigure *event)
{
gint old_screen_width_pixels, old_screen_height_pixels;
GdkPixbuf *old_map_pixbuf;
printf("%s(%d, %d)\n", __PRETTY_FUNCTION__,
_map_widget->allocation.width, _map_widget->allocation.height);
if(_map_widget->allocation.width == 1
&& _map_widget->allocation.height == 1)
/* Special case - first allocation - not persistent. */
return TRUE;
old_screen_width_pixels = _screen_width_pixels;
old_screen_height_pixels = _screen_height_pixels;
_screen_width_pixels = _map_widget->allocation.width;
_screen_height_pixels = _map_widget->allocation.height;
_screen_halfwidth_pixels = _screen_width_pixels / 2;
_screen_halfheight_pixels = _screen_height_pixels / 2;
g_object_unref(_map_pixmap);
_map_pixmap = gdk_pixmap_new(
_map_widget->window,
_screen_width_pixels, _screen_height_pixels,
-1); /* -1: use bit depth of widget->window. */
old_map_pixbuf = _map_pixbuf;
_map_pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8,
_screen_width_pixels, _screen_height_pixels);
{
gint oldnew_diffx = (gint)(_screen_width_pixels
- old_screen_width_pixels) / 2;
gint oldnew_diffy = (gint)(_screen_height_pixels
- old_screen_height_pixels) / 2;
gdk_pixbuf_copy_area(old_map_pixbuf,
MAX(0, -oldnew_diffx), MAX(0, -oldnew_diffy),
MIN(_screen_width_pixels, old_screen_width_pixels),
MIN(_screen_height_pixels, old_screen_height_pixels),
_map_pixbuf,
MAX(0, oldnew_diffx), MAX(0, oldnew_diffy));
}
g_object_unref(old_map_pixbuf);
/* Set _scale_rect. */
_scale_rect.x = (_screen_width_pixels - SCALE_WIDTH) / 2;
_scale_rect.width = SCALE_WIDTH;
pango_layout_set_text(_scale_layout, "0", -1);
pango_layout_get_pixel_size(_scale_layout, NULL, &_scale_rect.height);
_scale_rect.y = _screen_height_pixels - _scale_rect.height - 1;
/* Set _zoom rect. */
pango_layout_set_text(_zoom_layout, "00", -1);
pango_layout_get_pixel_size(_zoom_layout, &_zoom_rect.width,
&_zoom_rect.height);
_zoom_rect.width *= 1.25;
pango_layout_set_width(_zoom_layout, _zoom_rect.width);
pango_layout_context_changed(_zoom_layout);
_zoom_rect.x = _scale_rect.x - _zoom_rect.width;
_zoom_rect.y = _screen_height_pixels - _zoom_rect.height - 1;
/* Set _comprose_rect. */
_comprose_rect.x = _screen_width_pixels - 25 - _comprose_rect.width;
_comprose_rect.y = _screen_height_pixels - 25 - _comprose_rect.height;
map_set_mark();
map_force_redraw();
/* If Auto-Center is set to Lead, then recalc center. */
if(_center_mode == CENTER_LEAD)
map_center_unit(map_calc_new_center(_next_zoom));
else
map_center_unit(_next_center);
vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
return TRUE;
}
gboolean
sat_panel_expose(GtkWidget *widget, GdkEventExpose *event)
{
gchar *tmp = NULL;
gint x, y;
printf("%s()\n", __PRETTY_FUNCTION__);
draw_sat_info(widget,
0, 0,
widget->allocation.width,
widget->allocation.height,
FALSE);
/* Sat View/In Use */
tmp = g_strdup_printf("%d/%d", _gps.satinuse, _gps.satinview);
pango_layout_set_text(_sat_panel_layout, tmp, -1);
pango_layout_set_alignment(_sat_panel_layout, PANGO_ALIGN_LEFT);
gdk_draw_layout(widget->window,
widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
20, 2,
_sat_panel_layout);
g_free(tmp);
switch(_gps.fix)
{
case 2:
case 3: tmp = g_strdup_printf("%dD fix", _gps.fix); break;
default: tmp = g_strdup_printf("nofix"); break;
}
pango_layout_set_text(_sat_panel_layout, tmp, -1);
pango_layout_set_alignment(_sat_panel_layout, PANGO_ALIGN_RIGHT);
pango_layout_get_pixel_size(_sat_panel_layout, &x, &y);
gdk_draw_layout(widget->window,
widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
widget->allocation.width - 20 - x, 2,
_sat_panel_layout);
g_free(tmp);
vprintf("%s(): return\n", __PRETTY_FUNCTION__);
return TRUE;
}
gboolean
heading_panel_expose(GtkWidget *widget, GdkEventExpose *event)
{
gint size, xoffset, yoffset, i, x, y;
gint dir;
gfloat tmp;
gchar *text;
printf("%s()\n", __PRETTY_FUNCTION__);
size = MIN(widget->allocation.width, widget->allocation.height);
if(widget->allocation.width > widget->allocation.height)
{
xoffset = (widget->allocation.width - widget->allocation.height) / 2;
yoffset = 0;
}
else
{
xoffset = 0;
yoffset = (widget->allocation.height - widget->allocation.width) / 2;
}
pango_font_description_set_size(_heading_panel_fontdesc,12*PANGO_SCALE);
pango_layout_set_font_description(_heading_panel_layout,
_heading_panel_fontdesc);
pango_layout_set_alignment(_heading_panel_layout, PANGO_ALIGN_CENTER);
text = g_strdup_printf("%3.0f°", _gps.heading);
pango_layout_set_text(_heading_panel_layout, text, -1);
pango_layout_get_pixel_size(_heading_panel_layout, &x, &y);
gdk_draw_layout(widget->window,
widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
xoffset + size/2 - x/2,
yoffset + size - y - 2, _heading_panel_layout);
g_free(text);
gdk_draw_arc (widget->window,
widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
FALSE,
xoffset, yoffset + size/2,
size, size,
0, 64 * 180);
/* Simple arrow for heading*/
gdk_draw_line(widget->window,
widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
xoffset + size/2 + 3,
yoffset + size - y - 5,
xoffset + size/2,
yoffset + size/2 + 5);
gdk_draw_line(widget->window,
widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
xoffset + size/2 - 3,
yoffset + size - y - 5,
xoffset + size/2,
yoffset + size/2 + 5);
gdk_draw_line(widget->window,
widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
xoffset + size/2 - 3,
yoffset + size - y - 5,
xoffset + size/2,
yoffset + size - y - 8);
gdk_draw_line(widget->window,
widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
xoffset + size/2 + 3,
yoffset + size - y - 5,
xoffset + size/2,
yoffset + size - y - 8);
gint angle[5] = {-90,-45,0,45,90};
gint fsize[5] = {0,4,10,4,0};
for(i = 0; i < 5; i++)
{
dir = (gint)(_gps.heading/45)*45 + angle[i];
switch(dir)
{
case 0:
case 360: text = g_strdup("N"); break;
case 45:
case 405:
text = g_strdup("NE"); break;
case 90:
text = g_strdup("E"); break;
case 135:
text = g_strdup("SE"); break;
case 180:
text = g_strdup("S"); break;
case 225:
text = g_strdup("SW"); break;
case 270:
case -90:
text = g_strdup("W"); break;
case 315:
case -45:
text = g_strdup("NW"); break;
default :
text = g_strdup("??");
break;
}
tmp = deg2rad(dir - _gps.heading);
gdk_draw_line(widget->window,
widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
xoffset + size/2 + ((size/2 - 5) * sinf(tmp)),
yoffset + size - ((size/2 - 5) * cosf(tmp)),
xoffset + size/2 + ((size/2 + 5) * sinf(tmp)),
yoffset + size - ((size/2 + 5) * cosf(tmp)));
x = fsize[i];
if(abs((gint)(_gps.heading/45)*45 - _gps.heading)
> abs((gint)(_gps.heading/45)*45 + 45 - _gps.heading)
&& (i > 0))
x = fsize[i - 1];
pango_font_description_set_size(_heading_panel_fontdesc,
(10 + x)*PANGO_SCALE);
pango_layout_set_font_description(_heading_panel_layout,
_heading_panel_fontdesc);
pango_layout_set_text(_heading_panel_layout, text, -1);
pango_layout_get_pixel_size(_heading_panel_layout, &x, &y);
x = xoffset + size/2 + ((size/2 + 15) * sinf(tmp)) - x/2,
y = yoffset + size - ((size/2 + 15) * cosf(tmp)) - y/2,
gdk_draw_layout(widget->window,
widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
x, y, _heading_panel_layout);
g_free(text);
}
vprintf("%s(): return\n", __PRETTY_FUNCTION__);
return TRUE;
}
gboolean
map_cb_expose(GtkWidget *widget, GdkEventExpose *event)
{
gint i;
printf("%s(%d, %d, %d, %d)\n", __PRETTY_FUNCTION__,
event->area.x, event->area.y,
event->area.width, event->area.height);
/* Perform rotation. */
gdk_draw_drawable(
_map_widget->window,
_gc[COLORABLE_MARK],
_map_pixmap,
event->area.x - _map_offset_devx, event->area.y - _map_offset_devy,
event->area.x, event->area.y,
event->area.width, event->area.height);
/* Draw the mark. */
gdk_draw_arc(
_map_widget->window,
_gps_state == RCVR_FIXED
? _gc[COLORABLE_MARK] : _gc[COLORABLE_MARK_OLD],
FALSE, /* not filled. */
_mark_bufx1 - _draw_width + _map_offset_devx,
_mark_bufy1 - _draw_width + _map_offset_devy,
2 * _draw_width, 2 * _draw_width,
0, 360 * 64);
gdk_draw_line(
_map_widget->window,
_gps_state == RCVR_FIXED
? (_show_velvec
? _gc[COLORABLE_MARK_VELOCITY] : _gc[COLORABLE_MARK])
: _gc[COLORABLE_MARK_OLD],
_mark_bufx1 + _map_offset_devx,
_mark_bufy1 + _map_offset_devy,
_mark_bufx2 + _map_offset_devx,
_mark_bufy2 + _map_offset_devy);
/* draw zoom box if so wanted */
if(_show_zoomlevel) {
gchar *buffer = g_strdup_printf("%d", _zoom);
gdk_draw_rectangle(_map_widget->window,
_map_widget->style->bg_gc[GTK_STATE_ACTIVE],
TRUE,
_zoom_rect.x, _zoom_rect.y,
_zoom_rect.width, _zoom_rect.height);
gdk_draw_rectangle(_map_widget->window,
_map_widget->style->fg_gc[GTK_STATE_ACTIVE],
FALSE,
_zoom_rect.x, _zoom_rect.y,
_zoom_rect.width, _zoom_rect.height);
pango_layout_set_text(_zoom_layout, buffer, -1);
gdk_draw_layout(_map_widget->window,
_map_widget->style->fg_gc[GTK_STATE_ACTIVE],
_zoom_rect.x + _zoom_rect.width / 2,
_zoom_rect.y, _zoom_layout);
}
/* Draw scale, if necessary. */
if(_show_scale)
{
gdk_rectangle_intersect(&event->area, &_scale_rect, &event->area);
if(event->area.width && event->area.height)
{
gdk_draw_rectangle(_map_widget->window,
_map_widget->style->bg_gc[GTK_STATE_ACTIVE],
TRUE,
_scale_rect.x, _scale_rect.y,
_scale_rect.width, _scale_rect.height);
gdk_draw_rectangle(_map_widget->window,
_map_widget->style->fg_gc[GTK_STATE_ACTIVE],
FALSE,
_scale_rect.x, _scale_rect.y,
_scale_rect.width, _scale_rect.height);
/* Now calculate and draw the distance. */
{
gchar buffer[16];
gfloat distance;
gfloat lat1, lon1, lat2, lon2;
gint width;
unit2latlon(_center.unitx - pixel2unit(SCALE_WIDTH / 2 - 4),
_center.unity, lat1, lon1);
unit2latlon(_center.unitx + pixel2unit(SCALE_WIDTH / 2 - 4),
_center.unity, lat2, lon2);
distance = calculate_distance(lat1, lon1, lat2, lon2)
* UNITS_CONVERT[_units];
if(distance < 1.f)
snprintf(buffer, sizeof(buffer), "%0.2f %s", distance,
UNITS_ENUM_TEXT[_units]);
else if(distance < 10.f)
snprintf(buffer, sizeof(buffer), "%0.1f %s", distance,
UNITS_ENUM_TEXT[_units]);
else
snprintf(buffer, sizeof(buffer), "%0.f %s", distance,
UNITS_ENUM_TEXT[_units]);
pango_layout_set_text(_scale_layout, buffer, -1);
pango_layout_get_pixel_size(_scale_layout, &width, NULL);
/* Draw the layout itself. */
gdk_draw_layout(_map_widget->window,
_map_widget->style->fg_gc[GTK_STATE_ACTIVE],
_scale_rect.x + (_scale_rect.width - width) / 2,
_scale_rect.y, _scale_layout);
/* Draw little hashes on the ends. */
gdk_draw_line(_map_widget->window,
_map_widget->style->fg_gc[GTK_STATE_ACTIVE],
_scale_rect.x + 4,
_scale_rect.y + _scale_rect.height / 2 - 4,
_scale_rect.x + 4,
_scale_rect.y + _scale_rect.height / 2 + 4);
gdk_draw_line(_map_widget->window,
_map_widget->style->fg_gc[GTK_STATE_ACTIVE],
_scale_rect.x + 4,
_scale_rect.y + _scale_rect.height / 2,
_scale_rect.x + (_scale_rect.width - width) / 2 - 4,
_scale_rect.y + _scale_rect.height / 2);
gdk_draw_line(_map_widget->window,
_map_widget->style->fg_gc[GTK_STATE_ACTIVE],
_scale_rect.x + _scale_rect.width - 4,
_scale_rect.y + _scale_rect.height / 2 - 4,
_scale_rect.x + _scale_rect.width - 4,
_scale_rect.y + _scale_rect.height / 2 + 4);
gdk_draw_line(_map_widget->window,
_map_widget->style->fg_gc[GTK_STATE_ACTIVE],
_scale_rect.x + _scale_rect.width - 4,
_scale_rect.y + _scale_rect.height / 2,
_scale_rect.x + (_scale_rect.width + width) / 2 + 4,
_scale_rect.y + _scale_rect.height / 2);
}
}
}
/* Draw the compass rose, if necessary. */
if(_show_comprose)
{
GdkPoint points[3];
gint offsetx, offsety;
gfloat x, y;
offsetx = _comprose_rect.x;
offsety = _comprose_rect.y;
gdk_pixbuf_rotate_vector(&x, &y, _map_rotate_matrix, 0, 12);
points[0].x = offsetx + x + 0.5f; points[0].y = offsety + y + 0.5f;
gdk_pixbuf_rotate_vector(&x, &y, _map_rotate_matrix, 20, 30);
points[1].x = offsetx + x + 0.5f; points[1].y = offsety + y + 0.5f;
gdk_pixbuf_rotate_vector(&x, &y, _map_rotate_matrix, 0, -45);
points[2].x = offsetx + x + 0.5f; points[2].y = offsety + y + 0.5f;
gdk_pixbuf_rotate_vector(&x, &y, _map_rotate_matrix, -20, 30);
points[3].x = offsetx + x + 0.5f; points[3].y = offsety + y + 0.5f;
gdk_draw_polygon(_map_widget->window,
_map_widget->style->bg_gc[GTK_STATE_ACTIVE],
TRUE, /* FILLED */
points, 4);
gdk_draw_polygon(_map_widget->window,
_map_widget->style->fg_gc[GTK_STATE_ACTIVE],
FALSE, /* NOT FILLED */
points, 4);
gdk_draw_layout(_map_widget->window,
_map_widget->style->fg_gc[GTK_STATE_ACTIVE],
_comprose_rect.x - _comprose_rect.width / 2 + 1,
_comprose_rect.y - _comprose_rect.height / 2 - 4,
_comprose_layout);
}
/* Draw a stopwatch if we're redrawing the map. */
for(i = _redraw_count - 1; i >= 0; i--)
{
gdk_draw_pixbuf(
_map_widget->window,
_map_widget->style->fg_gc[GTK_STATE_ACTIVE],
_redraw_wait_icon,
0, 0,
_redraw_wait_bounds.x + i
* HOURGLASS_SEPARATION,
_redraw_wait_bounds.y,
_redraw_wait_bounds.width,
_redraw_wait_bounds.height,
GDK_RGB_DITHER_NONE, 0, 0);
}
vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
return TRUE;
}
static void
latlon_cb_copy_clicked(GtkWidget *widget, LatlonDialog *lld) {
gchar buffer[42];
snprintf(buffer, sizeof(buffer),
"%s %s",
gtk_label_get_text(GTK_LABEL(lld->lat)),
gtk_label_get_text(GTK_LABEL(lld->lon)));
gtk_clipboard_set_text(
gtk_clipboard_get(GDK_SELECTION_CLIPBOARD), buffer, -1);
}
static void
latlon_cb_fmt_changed(GtkWidget *widget, LatlonDialog *lld) {
DegFormat fmt;
fmt = gtk_combo_box_get_active(GTK_COMBO_BOX(lld->fmt_combo));
{
gint old = _degformat; /* augh... */
gchar buffer[LL_FMT_LEN];
_degformat = fmt;
lat_format(lld->glat, buffer);
gtk_label_set_label(GTK_LABEL(lld->lat), buffer);
lon_format(lld->glon, buffer);
gtk_label_set_label(GTK_LABEL(lld->lon), buffer);
_degformat = old;
}
}
gboolean
latlon_dialog(gdouble lat, gdouble lon)
{
LatlonDialog lld;
GtkWidget *dialog;
GtkWidget *table;
GtkWidget *label;
GtkWidget *txt_lat;
GtkWidget *txt_lon;
GtkWidget *cmb_format;
GtkWidget *btn_copy = NULL;
printf("%s()\n", __PRETTY_FUNCTION__);
dialog = gtk_dialog_new_with_buttons(_("Show Position"),
GTK_WINDOW(_window), GTK_DIALOG_MODAL,
GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
NULL);
/* Set the lat/lon strings. */
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
table = gtk_table_new(5, 2, FALSE), TRUE, TRUE, 0);
gtk_table_attach(GTK_TABLE(table),
label = gtk_label_new(_("Lat")),
0, 1, 0, 1, GTK_FILL, 0, 2, 4);
gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
gtk_table_attach(GTK_TABLE(table),
txt_lat = gtk_label_new(""),
1, 2, 0, 1, GTK_FILL, 0, 2, 4);
gtk_misc_set_alignment(GTK_MISC(txt_lat), 1.f, 0.5f);
gtk_table_attach(GTK_TABLE(table),
label = gtk_label_new(_("Lon")),
0, 1, 1, 2, GTK_FILL, 0, 2, 4);
gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
gtk_table_attach(GTK_TABLE(table),
txt_lon = gtk_label_new(""),
1, 2, 1, 2, GTK_FILL, 0, 2, 4);
gtk_misc_set_alignment(GTK_MISC(txt_lon), 1.f, 0.5f);
gtk_table_attach(GTK_TABLE(table),
label = gtk_label_new(_("Format")),
0, 1, 2, 3, GTK_FILL, 0, 2, 4);
gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
gtk_table_attach(GTK_TABLE(table),
label = gtk_alignment_new(0.f, 0.5f, 0.f, 0.f),
1, 2, 2, 3, GTK_FILL, 0, 2, 4);
gtk_container_add(GTK_CONTAINER(label),
cmb_format = gtk_combo_box_new_text());
gtk_table_attach(GTK_TABLE(table),
btn_copy = gtk_button_new_with_label(_("Copy")),
0, 2, 3, 4, GTK_FILL, 0, 2, 4);
/* Lat/Lon */
{
gchar buffer[LL_FMT_LEN];
lat_format(lat, buffer);
gtk_label_set_label(GTK_LABEL(txt_lat), buffer);
lat_format(lon, buffer);
gtk_label_set_label(GTK_LABEL(txt_lon), buffer);
}
/* Fill in formats */
{
int i;
for(i = 0; i < DEG_FORMAT_ENUM_COUNT; i++) {
gtk_combo_box_append_text(GTK_COMBO_BOX(cmb_format),
DEG_FORMAT_ENUM_TEXT[i]);
}
gtk_combo_box_set_active(GTK_COMBO_BOX(cmb_format), _degformat);
}
/* setup cb context */
lld.fmt_combo = cmb_format;
lld.glat = lat;
lld.glon = lon;
lld.lat = txt_lat;
lld.lon = txt_lon;
/* Connect Signals */
g_signal_connect(G_OBJECT(cmb_format), "changed",
G_CALLBACK(latlon_cb_fmt_changed), &lld);
g_signal_connect(G_OBJECT(btn_copy), "clicked",
G_CALLBACK(latlon_cb_copy_clicked), &lld);
gtk_widget_show_all(dialog);
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_hide(dialog);
vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
return TRUE;
}
/**
* This is a multi-purpose function for allowing the user to select a file
* for either reading or writing. If chooser_action is
* GTK_FILE_CHOOSER_ACTION_OPEN, then bytes_out and size_out must be
* non-NULL. If chooser_action is GTK_FILE_CHOOSER_ACTION_SAVE, then
* handle_out must be non-NULL. Either dir or file (or both) can be NULL.
* This function returns TRUE if a file was successfully opened.
*/
gboolean
display_open_file(GtkWidget *parent, gchar **bytes_out,
GnomeVFSHandle **handle_out, gint *size_out,
gchar **dir, gchar **file, GtkFileChooserAction chooser_action)
{
GtkWidget *dialog;
gboolean success = FALSE;
printf("%s()\n", __PRETTY_FUNCTION__);
dialog= hildon_file_chooser_dialog_new(GTK_WINDOW(_window),chooser_action);
if(dir && *dir)
gtk_file_chooser_set_current_folder_uri(
GTK_FILE_CHOOSER(dialog), *dir);
if(file && *file)
{
GValue val;
gtk_file_chooser_set_uri(
GTK_FILE_CHOOSER(dialog), *file);
if(chooser_action == GTK_FILE_CHOOSER_ACTION_SAVE)
{
/* Work around a bug in HildonFileChooserDialog. */
memset(&val, 0, sizeof(val));
g_value_init(&val, G_TYPE_BOOLEAN);
g_value_set_boolean(&val, FALSE);
g_object_set_property(G_OBJECT(dialog), "autonaming", &val);
gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog),
strrchr(*file, '/') + 1);
}
}
gtk_widget_show_all(dialog);
while(!success && gtk_dialog_run(GTK_DIALOG(dialog))==GTK_RESPONSE_OK)
{
gchar *file_uri_str;
GnomeVFSResult vfs_result;
/* Get the selected filename. */
file_uri_str = gtk_file_chooser_get_uri(GTK_FILE_CHOOSER(dialog));
if((chooser_action == GTK_FILE_CHOOSER_ACTION_OPEN
&& (GNOME_VFS_OK != (vfs_result = gnome_vfs_read_entire_file(
file_uri_str, size_out, bytes_out))))
|| (chooser_action == GTK_FILE_CHOOSER_ACTION_SAVE
&& GNOME_VFS_OK != (vfs_result = gnome_vfs_create(
handle_out, file_uri_str,
GNOME_VFS_OPEN_WRITE, FALSE, 0664))))
{
gchar buffer[BUFFER_SIZE];
snprintf(buffer, sizeof(buffer),
"%s:\n%s", chooser_action == GTK_FILE_CHOOSER_ACTION_OPEN
? _("Failed to open file for reading")
: _("Failed to open file for writing"),
gnome_vfs_result_to_string(vfs_result));
popup_error(dialog, buffer);
}
else
success = TRUE;
g_free(file_uri_str);
}
if(success)
{
/* Success!. */
if(dir)
{
g_free(*dir);
*dir = gtk_file_chooser_get_current_folder_uri(
GTK_FILE_CHOOSER(dialog));
}
/* If desired, save the file for later. */
if(file)
{
g_free(*file);
*file = gtk_file_chooser_get_uri(GTK_FILE_CHOOSER(dialog));
}
}
gtk_widget_destroy(dialog);
vprintf("%s(): return %d\n", __PRETTY_FUNCTION__, success);
return success;
}
void
display_init()
{
PangoContext *pango_context;
PangoFontDescription *pango_font;
GdkColor color;
printf("%s()\n", __PRETTY_FUNCTION__);
/* Cache some pango and GCs for drawing. */
pango_context = gtk_widget_get_pango_context(_map_widget);
_scale_layout = pango_layout_new(pango_context);
pango_font = pango_font_description_new();
pango_font_description_set_size(pango_font, 12 * PANGO_SCALE);
pango_layout_set_font_description(_scale_layout, pango_font);
/* zoom box */
pango_context = gtk_widget_get_pango_context(_map_widget);
_zoom_layout = pango_layout_new(pango_context);
pango_font = pango_font_description_new();
pango_font_description_set_size(pango_font, 12 * PANGO_SCALE);
pango_layout_set_font_description(_zoom_layout, pango_font);
pango_layout_set_alignment(_zoom_layout, PANGO_ALIGN_CENTER);
/* compose rose */
pango_context = gtk_widget_get_pango_context(_map_widget);
_comprose_layout = pango_layout_new(pango_context);
pango_font = pango_font_description_new();
pango_font_description_set_size(pango_font, 16 * PANGO_SCALE);
pango_font_description_set_weight(pango_font, PANGO_WEIGHT_BOLD);
pango_layout_set_font_description(_comprose_layout, pango_font);
pango_layout_set_alignment(_comprose_layout, PANGO_ALIGN_CENTER);
pango_layout_set_text(_comprose_layout, "N", -1);
{
PangoRectangle rect;
pango_layout_get_pixel_extents(_comprose_layout, &rect, NULL);
_comprose_rect.width = rect.width + 3;
_comprose_rect.height = rect.height + 3;
}
/* speed limit */
_speed_limit_gc1 = gdk_gc_new (_map_widget->window);
color.red = 0xffff;
color.green = 0;
color.blue = 0;
gdk_gc_set_rgb_fg_color(_speed_limit_gc1, &color);
color.red = 0;
color.green = 0;
color.blue = 0;
_speed_limit_gc2 = gdk_gc_new(_map_widget->window);
gdk_gc_set_rgb_fg_color(_speed_limit_gc2, &color);
pango_context = gtk_widget_get_pango_context(_map_widget);
_speed_limit_layout = pango_layout_new(pango_context);
pango_font = pango_font_description_new();
pango_font_description_set_size(pango_font, 64 * PANGO_SCALE);
pango_layout_set_font_description(_speed_limit_layout,
pango_font);
pango_layout_set_alignment(_speed_limit_layout, PANGO_ALIGN_CENTER);
/* draw_sat_info() */
_sat_info_gc1 = gdk_gc_new(_map_widget->window);
color.red = 0;
color.green = 0;
color.blue = 0;
gdk_gc_set_rgb_fg_color(_sat_info_gc1, &color);
color.red = 0;
color.green = 0;
color.blue = 0xffff;
_sat_info_gc2 = gdk_gc_new(_map_widget->window);
gdk_gc_set_rgb_fg_color(_sat_info_gc2, &color);
pango_context = gtk_widget_get_pango_context(_map_widget);
_sat_info_layout = pango_layout_new(pango_context);
pango_font = pango_font_description_new();
pango_font_description_set_family(pango_font,"Sans Serif");
pango_font_description_set_size(pango_font, 8*PANGO_SCALE);
pango_layout_set_font_description(_sat_info_layout, pango_font);
pango_layout_set_alignment(_sat_info_layout, PANGO_ALIGN_CENTER);
/* sat_panel_expose() */
pango_context = gtk_widget_get_pango_context(_map_widget);
_sat_panel_layout = pango_layout_new(pango_context);
pango_font = pango_font_description_new();
pango_font_description_set_family(pango_font,"Sans Serif");
pango_font_description_set_size(pango_font, 14*PANGO_SCALE);
pango_layout_set_font_description (_sat_panel_layout, pango_font);
/* heading_panel_expose() */
pango_context = gtk_widget_get_pango_context(_map_widget);
_heading_panel_layout = pango_layout_new(pango_context);
pango_font = pango_font_description_new();
pango_font_description_set_family(pango_font,"Sans Serif");
/* draw_sat_details() */
pango_context = gtk_widget_get_pango_context(_map_widget);
_sat_details_layout = pango_layout_new(pango_context);
pango_font = pango_font_description_new();
pango_font_description_set_family(pango_font,"Sans Serif");
pango_font_description_set_size(pango_font, 10*PANGO_SCALE);
pango_layout_set_font_description(_sat_details_layout,
pango_font);
pango_layout_set_alignment(_sat_details_layout, PANGO_ALIGN_CENTER);
/* sat_details_panel_expose() */
pango_context = gtk_widget_get_pango_context(_map_widget);
_sat_details_expose_layout = pango_layout_new(pango_context);
pango_font = pango_font_description_new();
pango_font_description_set_family(
pango_font,"Sans Serif");
pango_layout_set_alignment(_sat_details_expose_layout,
PANGO_ALIGN_CENTER);
pango_font_description_set_size(pango_font,
14*PANGO_SCALE);
pango_layout_set_font_description(_sat_details_expose_layout,
pango_font);
/* Load the _redraw_wait_icon. */
{
GError *error = NULL;
gchar *icon_path = "/usr/share/icons/hicolor/scalable/hildon"
"/qgn_list_gene_image_file_wait.png";
_redraw_wait_bounds.x = 0;
_redraw_wait_bounds.y = 0;
_redraw_wait_icon = gdk_pixbuf_new_from_file(icon_path, &error);
if(!_redraw_wait_icon || error)
{
printf("Error parsing pixbuf: %s\n",
error ? error->message : icon_path);
_redraw_wait_bounds.width = 0;
_redraw_wait_bounds.height = 0;
_redraw_wait_icon = NULL;
}
else
{
_redraw_wait_bounds.width
= gdk_pixbuf_get_width(_redraw_wait_icon);
_redraw_wait_bounds.height
= gdk_pixbuf_get_height(_redraw_wait_icon);
}
}
g_signal_connect(G_OBJECT(_map_widget), "configure_event",
G_CALLBACK(map_cb_configure), NULL);
g_signal_connect(G_OBJECT(_map_widget), "expose_event",
G_CALLBACK(map_cb_expose), NULL);
g_signal_connect(G_OBJECT(_sat_panel), "expose_event",
G_CALLBACK(sat_panel_expose), NULL);
g_signal_connect(G_OBJECT(_heading_panel), "expose_event",
G_CALLBACK(heading_panel_expose), NULL);
vprintf("%s(): return\n", __PRETTY_FUNCTION__);
}