diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7568181d7..029a68898 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,7 @@ jobs: run: | sudo apt-get update sudo apt-get install -y build-essential libtool intltool autoconf automake libcurl4-openssl-dev \ - pkg-config libglib2.0-dev libgtk-3-dev libgoocanvas-2.0-dev + pkg-config libglib2.0-dev libgtk-3-dev - name: Run autoconf run: ./autogen.sh diff --git a/.travis.yml b/.travis.yml index a0f38bc88..c24707dae 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,7 @@ compiler: before_install: - sudo apt-get update -qq - sudo apt-get install -y libtool intltool autoconf automake libcurl4-openssl-dev - - sudo apt-get install -y pkg-config libglib2.0-dev libgtk-3-dev libgoocanvas-2.0-dev + - sudo apt-get install -y pkg-config libglib2.0-dev libgtk-3-dev script: - ./autogen.sh diff --git a/README b/README index b464c9675..561b80bae 100644 --- a/README +++ b/README @@ -31,7 +31,6 @@ for successful compilation of Gpredict: - Gtk+ 3 or later - GLib 2.32 or later -- GooCanvas 2 or 3 - Libcurl 7.16 or later - Hamlib (runtime only, not required for build) @@ -40,7 +39,7 @@ often with -dev or -devel in the package name, e.g. libgtk-3-dev. On Debian and Ubuntu systems you can install the build dependencies using: sudo apt install libtool intltool autoconf automake libcurl4-openssl-dev - sudo apt install pkg-config libglib2.0-dev libgtk-3-dev libgoocanvas-2.0-dev + sudo apt install pkg-config libglib2.0-dev libgtk-3-dev To build and install gpredict from source, first unpack the source package: diff --git a/configure.ac b/configure.ac index 6dcba4365..228902dd7 100644 --- a/configure.ac +++ b/configure.ac @@ -47,19 +47,12 @@ else AC_MSG_ERROR(Gpredict requires libglib-dev 2.40 or later) fi -# check for goocanvas 2 or 3 (depends on gtk and glib) -if $PKG_CONFIG --atleast-version=2.0 goocanvas-2.0; then - CFLAGS="$CFLAGS `$PKG_CONFIG --cflags goocanvas-2.0`" - LIBS="$LIBS `$PKG_CONFIG --libs goocanvas-2.0`" - havegoocanvas2=true +# check for gtk+ 3.0 or later +if $PKG_CONFIG --atleast-version=3.0 gtk+-3.0; then + CFLAGS="$CFLAGS `$PKG_CONFIG --cflags gtk+-3.0`" + LIBS="$LIBS `$PKG_CONFIG --libs gtk+-3.0`" else - if $PKG_CONFIG --atleast-version=3.0 goocanvas-3.0; then - CFLAGS="$CFLAGS `$PKG_CONFIG --cflags goocanvas-3.0`" - LIBS="$LIBS `$PKG_CONFIG --libs goocanvas-3.0`" - havegoocanvas3=true - else - AC_MSG_ERROR(Gpredict requires libgoocanvas-2.0-dev) - fi + AC_MSG_ERROR(Gpredict requires libgtk-3-dev) fi # check for libgps (optional) @@ -103,12 +96,6 @@ GTHR_V=`$PKG_CONFIG --modversion gthread-2.0` GDK_V=`$PKG_CONFIG --modversion gdk-3.0` GTK_V=`$PKG_CONFIG --modversion gtk+-3.0` CURL_V=`$PKG_CONFIG --modversion libcurl` -if test "$havegoocanvas2" = true ; then - GOOC_V=`$PKG_CONFIG --modversion goocanvas-2.0` -fi -if test "$havegoocanvas3" = true ; then - GOOC_V=`$PKG_CONFIG --modversion goocanvas-3.0` -fi if test "$havelibgps" = true ; then GPS_V=`$PKG_CONFIG --modversion libgps` fi @@ -148,7 +135,6 @@ echo Gio version........ : $GIO_V echo Gthread version.... : $GTHR_V echo Gdk version........ : $GDK_V echo Gtk+ version....... : $GTK_V -echo GooCanvas version.. : $GOOC_V echo Libcurl version.... : $CURL_V if test "$havelibgps" = true ; then echo Libgps version..... : $GPS_V diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index fdf614d1c..ea492b6a7 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -22,7 +22,6 @@ parts: - libgtk-3-dev - libcurl4-openssl-dev - libglib2.0-dev - - libgoocanvas-2.0-dev stage-packages: - libcanberra-gtk3-module diff --git a/src/gtk-azel-plot.c b/src/gtk-azel-plot.c index 8387da3be..324a099dd 100644 --- a/src/gtk-azel-plot.c +++ b/src/gtk-azel-plot.c @@ -29,8 +29,8 @@ #include #endif #include -#include #include +#include #include "config-keys.h" #include "gpredict-utils.h" @@ -55,6 +55,31 @@ static GtkBoxClass *parent_class = NULL; static void gtk_azel_plot_destroy(GtkWidget * widget) { + GtkAzelPlot *azel = GTK_AZEL_PLOT(widget); + guint i; + + g_free(azel->curs_text); + azel->curs_text = NULL; + + g_free(azel->az_points); + azel->az_points = NULL; + + g_free(azel->el_points); + azel->el_points = NULL; + + for (i = 0; i < AZEL_PLOT_NUM_TICKS; i++) + { + g_free(azel->xlabels[i]); + g_free(azel->azlabels[i]); + g_free(azel->ellabels[i]); + azel->xlabels[i] = NULL; + azel->azlabels[i] = NULL; + azel->ellabels[i] = NULL; + } + + g_free(azel->font); + azel->font = NULL; + (*GTK_WIDGET_CLASS(parent_class)->destroy) (widget); } @@ -76,6 +101,7 @@ static void gtk_azel_plot_init(GtkAzelPlot * azel, (void)g_class; azel->qth = NULL; + azel->pass = NULL; azel->width = 0; azel->height = 0; azel->x0 = 0; @@ -86,6 +112,11 @@ static void gtk_azel_plot_init(GtkAzelPlot * azel, azel->qthinfo = FALSE; azel->cursinfo = FALSE; azel->extratick = FALSE; + azel->curs_text = NULL; + azel->az_points = NULL; + azel->el_points = NULL; + azel->num_points = 0; + azel->font = NULL; } GType gtk_azel_plot_get_type() @@ -140,295 +171,301 @@ static void el_to_xy(GtkAzelPlot * p, gdouble t, gdouble el, gdouble * x, tpp = (p->pass->los - p->pass->aos) / (p->xmax - p->x0); *x = p->x0 + (t - p->pass->aos) / tpp; - /* Az */ + /* El */ dpp = (gdouble) (90.0 / (p->y0 - p->ymax)); *y = (gdouble) (p->y0 - el / dpp); } -/** - * Manage new size allocation. - * - * This function is called when the canvas receives a new size allocation, - * e.g. when the container is re-sized. The function re-calculates the graph - * dimensions based on the new canvas size. - */ -static void size_allocate_cb(GtkWidget * widget, GtkAllocation * allocation, - gpointer data) +/** Convert canvas based coordinates to Az/El. */ +static void xy_to_graph(GtkAzelPlot * p, gfloat x, gfloat y, gdouble * t, + gdouble * az, gdouble * el) { - GtkAzelPlot *azel; - GooCanvasPoints *pts; - gdouble dx, dy; - gdouble xstep, ystep; - guint i, n; - pass_detail_t *detail; + gdouble tpp; /* time per pixel */ + gdouble dpp; /* degrees per pixel */ + + /* time */ + tpp = (p->pass->los - p->pass->aos) / (p->xmax - p->x0); + *t = p->pass->aos + tpp * (x - p->x0); + + /* az */ + dpp = p->maxaz / (p->y0 - p->ymax); + *az = dpp * (p->y0 - y); + + /* el */ + dpp = 90.0 / (p->y0 - p->ymax); + *el = dpp * (p->y0 - y); +} + +static void calculate_graph_points(GtkAzelPlot *azel) +{ + guint i, n; + pass_detail_t *detail; + gdouble dx, dy; + + n = g_slist_length(azel->pass->details); + azel->num_points = n; + + g_free(azel->az_points); + g_free(azel->el_points); + + azel->az_points = g_new(gdouble, n * 2); + azel->el_points = g_new(gdouble, n * 2); + + for (i = 0; i < n; i++) + { + detail = PASS_DETAIL(g_slist_nth_data(azel->pass->details, i)); + az_to_xy(azel, detail->time, detail->az, &dx, &dy); + azel->az_points[2 * i] = dx; + azel->az_points[2 * i + 1] = dy; + + el_to_xy(azel, detail->time, detail->el, &dx, &dy); + azel->el_points[2 * i] = dx; + azel->el_points[2 * i + 1] = dy; + } + + /* Adjust endpoints */ + if (n > 0) + { + azel->az_points[0] = azel->x0; + azel->az_points[2 * n - 2] = azel->xmax; + azel->el_points[1] = azel->y0; + azel->el_points[2 * n - 1] = azel->y0; + } +} - if (gtk_widget_get_realized(widget)) +static void calculate_tick_labels(GtkAzelPlot *azel) +{ + guint i; + gdouble xstep; + gdouble t, az, el; + gchar buff[7]; + + xstep = (azel->xmax - azel->x0) / (AZEL_PLOT_NUM_TICKS + 1); + + for (i = 0; i < AZEL_PLOT_NUM_TICKS; i++) { - /* get graph dimensions */ - azel = GTK_AZEL_PLOT(data); + /* x tick labels - time */ + xy_to_graph(azel, azel->x0 + (i + 1) * xstep, 0.0, &t, &az, &el); + daynum_to_str(buff, 7, "%H:%M", t); + + g_free(azel->xlabels[i]); + azel->xlabels[i] = g_strdup(buff); + + /* Az tick labels */ + g_free(azel->azlabels[i]); + azel->azlabels[i] = g_strdup_printf("%.0f\302\260", + azel->maxaz / (AZEL_PLOT_NUM_TICKS + 1) * (i + 1)); + + /* El tick labels */ + g_free(azel->ellabels[i]); + azel->ellabels[i] = g_strdup_printf("%.0f\302\260", + 90.0 / (AZEL_PLOT_NUM_TICKS + 1) * (i + 1)); + } +} - azel->width = allocation->width; - azel->height = allocation->height; +static gboolean on_draw(GtkWidget *widget, cairo_t *cr, gpointer data) +{ + GtkAzelPlot *azel = GTK_AZEL_PLOT(data); + gdouble xstep, ystep; + guint i; + PangoLayout *layout; + PangoFontDescription *font_desc; - goo_canvas_set_bounds(GOO_CANVAS(GTK_AZEL_PLOT(azel)->canvas), 0, 0, - azel->width, azel->height); + (void)widget; - /* update coordinate system */ - azel->x0 = AZEL_X_MARGIN; - azel->xmax = azel->width - AZEL_X_MARGIN; - azel->y0 = azel->height - AZEL_Y_MARGIN; - azel->ymax = AZEL_Y_MARGIN; + /* Background */ + cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); + cairo_paint(cr); - /* background item */ - g_object_set(azel->bgd, "width", (gdouble) azel->width, - "height", (gdouble) azel->height, NULL); + /* Frame */ + cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); + cairo_set_line_width(cr, 1.0); + cairo_rectangle(cr, azel->x0, azel->ymax, azel->xmax - azel->x0, azel->y0 - azel->ymax); + cairo_stroke(cr); - /* frame */ - g_object_set(azel->frame, - "x", (gdouble) azel->x0, - "y", (gdouble) azel->ymax, - "width", (gdouble) (azel->xmax - azel->x0), - "height", (gdouble) (azel->y0 - AZEL_Y_MARGIN), NULL); + xstep = (azel->xmax - azel->x0) / (AZEL_PLOT_NUM_TICKS + 1); + ystep = (azel->y0 - azel->ymax) / (AZEL_PLOT_NUM_TICKS + 1); - xstep = (azel->xmax - azel->x0) / (AZEL_PLOT_NUM_TICKS + 1); - ystep = (azel->y0 - azel->ymax) / (AZEL_PLOT_NUM_TICKS + 1); + /* Tick marks */ + for (i = 0; i < AZEL_PLOT_NUM_TICKS; i++) + { + /* bottom x tick marks */ + cairo_move_to(cr, azel->x0 + (i + 1) * xstep, azel->y0); + cairo_line_to(cr, azel->x0 + (i + 1) * xstep, azel->y0 - MARKER_SIZE); - /* tick marks */ - for (i = 0; i < AZEL_PLOT_NUM_TICKS; i++) - { - pts = goo_canvas_points_new(2); - - /* bottom x tick marks */ - pts->coords[0] = (gdouble) (azel->x0 + (i + 1) * xstep); - pts->coords[1] = (gdouble) azel->y0; - pts->coords[2] = (gdouble) (azel->x0 + (i + 1) * xstep); - pts->coords[3] = (gdouble) (azel->y0 - MARKER_SIZE); - g_object_set(azel->xticksb[i], "points", pts, NULL); - - /* top x tick marks */ - pts->coords[0] = (gdouble) (azel->x0 + (i + 1) * xstep); - pts->coords[1] = (gdouble) azel->ymax; - pts->coords[2] = (gdouble) (azel->x0 + (i + 1) * xstep); - pts->coords[3] = (gdouble) (azel->ymax + MARKER_SIZE); - g_object_set(azel->xtickst[i], "points", pts, NULL); - - /* x tick labels */ - g_object_set(azel->xlab[i], - "x", (gfloat) (azel->x0 + (i + 1) * xstep), - "y", (gfloat) (azel->y0 + 5.0), NULL); - - /* left y tick marks */ - pts->coords[0] = (gdouble) (gdouble) azel->x0; - pts->coords[1] = (gdouble) (gdouble) (azel->y0 - (i + 1) * ystep); - pts->coords[2] = (gdouble) (gdouble) (azel->x0 + MARKER_SIZE); - pts->coords[3] = (gdouble) (gdouble) (azel->y0 - (i + 1) * ystep); - g_object_set(azel->yticksl[i], "points", pts, NULL); - - /* right y tick marks */ - pts->coords[0] = (gdouble) azel->xmax; - pts->coords[1] = (gdouble) (azel->y0 - (i + 1) * ystep); - pts->coords[2] = (gdouble) (azel->xmax - MARKER_SIZE); - pts->coords[3] = (gdouble) (azel->y0 - (i + 1) * ystep); - g_object_set(azel->yticksr[i], "points", pts, NULL); - - goo_canvas_points_unref(pts); - - /* Az tick labels */ - g_object_set(azel->azlab[i], - "x", (gfloat) (azel->x0 - 5), - "y", (gfloat) (azel->y0 - (i + 1) * ystep), NULL); - - /* El tick labels */ - g_object_set(azel->ellab[i], - "x", (gfloat) (azel->xmax + 5), - "y", (gfloat) (azel->y0 - (i + 1) * ystep), NULL); - } + /* top x tick marks */ + cairo_move_to(cr, azel->x0 + (i + 1) * xstep, azel->ymax); + cairo_line_to(cr, azel->x0 + (i + 1) * xstep, azel->ymax + MARKER_SIZE); - /* Az legend */ - g_object_set(azel->azleg, - "x", (gfloat) (azel->x0 - 7), - "y", (gfloat) (azel->ymax), NULL); + /* left y tick marks */ + cairo_move_to(cr, azel->x0, azel->y0 - (i + 1) * ystep); + cairo_line_to(cr, azel->x0 + MARKER_SIZE, azel->y0 - (i + 1) * ystep); - /* El legend */ - g_object_set(azel->elleg, - "x", (gfloat) (azel->xmax + 7), - "y", (gfloat) (azel->ymax), NULL); + /* right y tick marks */ + cairo_move_to(cr, azel->xmax, azel->y0 - (i + 1) * ystep); + cairo_line_to(cr, azel->xmax - MARKER_SIZE, azel->y0 - (i + 1) * ystep); + } + cairo_stroke(cr); - /* x legend */ - g_object_set(azel->xleg, - "x", (gfloat) (azel->x0 + (azel->xmax - azel->x0) / 2), - "y", (gfloat) (azel->height - 5), NULL); + /* Set up font */ + layout = pango_cairo_create_layout(cr); + font_desc = pango_font_description_from_string(azel->font ? azel->font : "Sans 9"); + pango_layout_set_font_description(layout, font_desc); - /* Az graph */ - n = g_slist_length(azel->pass->details); - pts = goo_canvas_points_new(n); + /* Draw tick labels */ + for (i = 0; i < AZEL_PLOT_NUM_TICKS; i++) + { + int tw, th; - for (i = 0; i < n; i++) + /* x tick labels */ + if (azel->xlabels[i]) { - detail = PASS_DETAIL(g_slist_nth_data(azel->pass->details, i)); - az_to_xy(azel, detail->time, detail->az, &dx, &dy); - pts->coords[2 * i] = dx; - pts->coords[2 * i + 1] = dy; + cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); + pango_layout_set_text(layout, azel->xlabels[i], -1); + pango_layout_get_pixel_size(layout, &tw, &th); + cairo_move_to(cr, azel->x0 + (i + 1) * xstep - tw / 2, azel->y0 + 5); + pango_cairo_show_layout(cr, layout); } - pts->coords[0] = azel->x0; - pts->coords[2 * n - 2] = azel->xmax; - g_object_set(azel->azg, "points", pts, NULL); - goo_canvas_points_unref(pts); - /* El graph */ - n = g_slist_length(azel->pass->details); - pts = goo_canvas_points_new(n); + /* Az tick labels (left, blue) */ + if (azel->azlabels[i]) + { + cairo_set_source_rgba(cr, 0.0, 0.0, 0.75, 1.0); + pango_layout_set_text(layout, azel->azlabels[i], -1); + pango_layout_get_pixel_size(layout, &tw, &th); + cairo_move_to(cr, azel->x0 - 5 - tw, azel->y0 - (i + 1) * ystep - th / 2); + pango_cairo_show_layout(cr, layout); + } - for (i = 0; i < n; i++) + /* El tick labels (right, red) */ + if (azel->ellabels[i]) { - detail = PASS_DETAIL(g_slist_nth_data(azel->pass->details, i)); - el_to_xy(azel, detail->time, detail->el, &dx, &dy); - pts->coords[2 * i] = dx; - pts->coords[2 * i + 1] = dy; + cairo_set_source_rgba(cr, 0.75, 0.0, 0.0, 1.0); + pango_layout_set_text(layout, azel->ellabels[i], -1); + pango_layout_get_pixel_size(layout, &tw, &th); + cairo_move_to(cr, azel->xmax + 5, azel->y0 - (i + 1) * ystep - th / 2); + pango_cairo_show_layout(cr, layout); } - pts->coords[1] = azel->y0; - pts->coords[2 * n - 1] = azel->y0; - g_object_set(azel->elg, "points", pts, NULL); - goo_canvas_points_unref(pts); - - /* cursor track */ - g_object_set(azel->curs, - "x", (gfloat) (azel->x0 + (azel->xmax - azel->x0) / 2), - "y", (gfloat) 5.0, NULL); } -} + /* Az legend */ + { + int tw, th; + cairo_set_source_rgba(cr, 0.0, 0.0, 0.75, 1.0); + pango_layout_set_text(layout, _("Az"), -1); + pango_layout_get_pixel_size(layout, &tw, &th); + cairo_move_to(cr, azel->x0 - 7 - tw, azel->ymax); + pango_cairo_show_layout(cr, layout); + } -/** Convert canvas based coordinates to Az/El. */ -static void xy_to_graph(GtkAzelPlot * p, gfloat x, gfloat y, gdouble * t, - gdouble * az, gdouble * el) -{ - gdouble tpp; /* time per pixel */ - gdouble dpp; /* degrees per pixel */ + /* El legend */ + { + int tw, th; + cairo_set_source_rgba(cr, 0.75, 0.0, 0.0, 1.0); + pango_layout_set_text(layout, _("El"), -1); + pango_layout_get_pixel_size(layout, &tw, &th); + cairo_move_to(cr, azel->xmax + 7, azel->ymax); + pango_cairo_show_layout(cr, layout); + } - /* time */ - tpp = (p->pass->los - p->pass->aos) / (p->xmax - p->x0); - *t = p->pass->aos + tpp * (x - p->x0); + /* x legend */ + { + int tw, th; + const gchar *xleg = sat_cfg_get_bool(SAT_CFG_BOOL_USE_LOCAL_TIME) ? _("Local Time") : _("UTC"); + cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); + pango_layout_set_text(layout, xleg, -1); + pango_layout_get_pixel_size(layout, &tw, &th); + cairo_move_to(cr, azel->x0 + (azel->xmax - azel->x0) / 2 - tw / 2, azel->height - 5 - th); + pango_cairo_show_layout(cr, layout); + } - /* az */ - dpp = p->maxaz / (p->y0 - p->ymax); - *az = dpp * (p->y0 - y); + /* Cursor text */ + if (azel->curs_text && azel->cursinfo) + { + int tw, th; + cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); + pango_layout_set_text(layout, azel->curs_text, -1); + pango_layout_get_pixel_size(layout, &tw, &th); + cairo_move_to(cr, azel->x0 + (azel->xmax - azel->x0) / 2 - tw / 2, 5); + pango_cairo_show_layout(cr, layout); + } - /* el */ - dpp = 90.0 / (p->y0 - p->ymax); - *el = dpp * (p->y0 - y); + pango_font_description_free(font_desc); + g_object_unref(layout); + + /* Draw Az graph (blue) */ + if (azel->az_points && azel->num_points > 1) + { + cairo_set_source_rgba(cr, 0.0, 0.0, 0.75, 1.0); + cairo_set_line_width(cr, 1.0); + cairo_move_to(cr, azel->az_points[0], azel->az_points[1]); + for (i = 1; i < (guint)azel->num_points; i++) + { + cairo_line_to(cr, azel->az_points[2 * i], azel->az_points[2 * i + 1]); + } + cairo_stroke(cr); + } + + /* Draw El graph (red) */ + if (azel->el_points && azel->num_points > 1) + { + cairo_set_source_rgba(cr, 0.75, 0.0, 0.0, 1.0); + cairo_set_line_width(cr, 1.0); + cairo_move_to(cr, azel->el_points[0], azel->el_points[1]); + for (i = 1; i < (guint)azel->num_points; i++) + { + cairo_line_to(cr, azel->el_points[2 * i], azel->el_points[2 * i + 1]); + } + cairo_stroke(cr); + } + return FALSE; } -/** Manage mouse motion events. */ -static gboolean on_motion_notify(GooCanvasItem * item, - GooCanvasItem * target, - GdkEventMotion * event, gpointer data) +static gboolean on_motion_notify(GtkWidget *widget, GdkEventMotion *event, gpointer data) { GtkAzelPlot *azel = GTK_AZEL_PLOT(data); gdouble t, az, el; gfloat x, y; - gchar *text; gchar buff[10]; - (void)item; /* avoid unused warning compiler warning. */ - (void)target; /* avoid unused warning compiler warning. */ + (void)widget; if (azel->cursinfo) { - /* get (x,y) */ x = event->x; y = event->y; - /* get (t,az,el) */ - - /* show vertical line at that time */ - - /* print (t,az,el) */ if ((x > azel->x0) && (x < azel->xmax) && (y > azel->ymax) && (y < azel->y0)) { - xy_to_graph(azel, x, y, &t, &az, &el); - daynum_to_str(buff, 10, "%H:%M:%S", t); - /* cursor track */ - text = - g_strdup_printf("T: %s, AZ: %.0f\302\260, EL: %.0f\302\260", + g_free(azel->curs_text); + azel->curs_text = g_strdup_printf("T: %s, AZ: %.0f\302\260, EL: %.0f\302\260", buff, az, el); - g_object_set(azel->curs, "text", text, NULL); - g_free(text); } else { - g_object_set(azel->curs, "text", "", NULL); + g_free(azel->curs_text); + azel->curs_text = NULL; } + gtk_widget_queue_draw(azel->canvas); } return TRUE; } -/** - * Finish canvas item setup. - * @param canvas - * @param item - * @param model - * @param data Pointer to the GtkAzelPlot object. - * - * This function is called when a canvas item is created. Its purpose is to connect - * the corresponding signals to the created items. - */ -static void on_item_created(GooCanvas * canvas, - GooCanvasItem * item, - GooCanvasItemModel * model, gpointer data) -{ - (void)canvas; - - if (!goo_canvas_item_model_get_parent(model)) - { - /* root item / canvas */ - g_signal_connect(item, "motion_notify_event", - G_CALLBACK(on_motion_notify), data); - } -} - - -/** - * Manage canvas realise signals. - * - * This function is used to re-initialise the graph dimensions when - * the graph is realized, i.e. displayed for the first time. This is - * necessary in order to compensate for missing "re-allocate" signals for - * graphs that have not yet been realised, e.g. when opening several module - */ -static void on_canvas_realized(GtkWidget * canvas, gpointer data) -{ - GtkAllocation aloc; - - gtk_widget_get_allocation(canvas, &aloc); - size_allocate_cb(canvas, &aloc, data); - -} - -static GooCanvasItemModel *create_canvas_model(GtkAzelPlot * azel) +static void size_allocate_cb(GtkWidget * widget, GtkAllocation * allocation, + gpointer data) { - GooCanvasItemModel *root; - guint32 col; - guint i; - gdouble xstep, ystep; - gdouble t, az, el; - gchar buff[7]; - gchar *txt; - - root = goo_canvas_group_model_new(NULL, NULL); + GtkAzelPlot *azel = GTK_AZEL_PLOT(data); - /* default font */ - g_object_get_property(G_OBJECT(gtk_settings_get_default()), "gtk-font-name", &azel->font); + (void)widget; - /* graph dimensions */ - azel->width = AZEL_DEFAULT_SIZE; - azel->height = AZEL_DEFAULT_SIZE; + azel->width = allocation->width; + azel->height = allocation->height; /* update coordinate system */ azel->x0 = AZEL_X_MARGIN; @@ -436,199 +473,25 @@ static GooCanvasItemModel *create_canvas_model(GtkAzelPlot * azel) azel->y0 = azel->height - AZEL_Y_MARGIN; azel->ymax = AZEL_Y_MARGIN; - /* background item */ - azel->bgd = goo_canvas_rect_model_new(root, 0.0, 0.0, - azel->width, azel->height, - "fill-color-rgba", 0xFFFFFFFF, - "stroke-color-rgba", 0xFFFFFFFF, - NULL); - - col = sat_cfg_get_int(SAT_CFG_INT_POLAR_AXIS_COL); - - /* frame */ - azel->frame = goo_canvas_rect_model_new(root, - (gdouble) azel->x0, - (gdouble) azel->ymax, - (gdouble) (azel->xmax - azel->x0), - (gdouble) (azel->y0 - - AZEL_Y_MARGIN), - "stroke-color-rgba", 0x000000FF, - "line-cap", CAIRO_LINE_CAP_SQUARE, - "line-join", CAIRO_LINE_JOIN_MITER, - "line-width", 1.0, NULL); - - xstep = (azel->xmax - azel->x0) / (AZEL_PLOT_NUM_TICKS + 1); - ystep = (azel->y0 - azel->ymax) / (AZEL_PLOT_NUM_TICKS + 1); - -// We use these defines to shorten the function names (indent) -#define MKLINE goo_canvas_polyline_model_new_line -#define MKTEXT goo_canvas_text_model_new - - /* tick marks */ - for (i = 0; i < AZEL_PLOT_NUM_TICKS; i++) + /* Recalculate graph points and tick labels */ + if (azel->pass) { - /* bottom x tick marks */ - azel->xticksb[i] = MKLINE(root, - (gdouble) (azel->x0 + (i + 1) * xstep), - (gdouble) azel->y0, - (gdouble) (azel->x0 + (i + 1) * xstep), - (gdouble) (azel->y0 - MARKER_SIZE), - "fill-color-rgba", col, - "line-cap", CAIRO_LINE_CAP_SQUARE, - "line-join", CAIRO_LINE_JOIN_MITER, - "line-width", 1.0, NULL); - - /* top x tick marks */ - azel->xtickst[i] = MKLINE(root, - (gdouble) (azel->x0 + (i + 1) * xstep), - (gdouble) azel->ymax, - (gdouble) (azel->x0 + (i + 1) * xstep), - (gdouble) (azel->ymax + MARKER_SIZE), - "fill-color-rgba", col, - "line-cap", CAIRO_LINE_CAP_SQUARE, - "line-join", CAIRO_LINE_JOIN_MITER, - "line-width", 1.0, NULL); - - /* x tick labels */ - /* get time */ - xy_to_graph(azel, azel->x0 + (i + 1) * xstep, 0.0, &t, &az, &el); - - daynum_to_str(buff, 7, "%H:%M", t); - - azel->xlab[i] = MKTEXT(root, buff, - (gfloat) (azel->x0 + (i + 1) * xstep), - (gfloat) (azel->y0 + 5), - -1, - GOO_CANVAS_ANCHOR_N, - "font", g_value_get_string(&azel->font), - "fill-color-rgba", 0x000000FF, NULL); - - /* left y tick marks */ - azel->yticksl[i] = MKLINE(root, - (gdouble) azel->x0, - (gdouble) (azel->y0 - (i + 1) * ystep), - (gdouble) (azel->x0 + MARKER_SIZE), - (gdouble) (azel->y0 - (i + 1) * ystep), - "fill-color-rgba", col, - "line-cap", CAIRO_LINE_CAP_SQUARE, - "line-join", CAIRO_LINE_JOIN_MITER, - "line-width", 1.0, NULL); - - /* right y tick marks */ - azel->yticksr[i] = MKLINE(root, - (gdouble) azel->xmax, - (gdouble) (azel->y0 - (i + 1) * ystep), - (gdouble) (azel->xmax - MARKER_SIZE), - (gdouble) (azel->y0 - (i + 1) * ystep), - "fill-color-rgba", col, - "line-cap", CAIRO_LINE_CAP_SQUARE, - "line-join", CAIRO_LINE_JOIN_MITER, - "line-width", 1.0, NULL); - - /* Az tick labels */ - txt = g_strdup_printf("%.0f\302\260", - azel->maxaz / (AZEL_PLOT_NUM_TICKS + 1) * (i + - 1)); - azel->azlab[i] = - MKTEXT(root, txt, (gfloat) (azel->x0 - 5), - (gfloat) (azel->y0 - (i + 1) * ystep), -1, - GOO_CANVAS_ANCHOR_E, - "font", g_value_get_string(&azel->font), - "fill-color-rgba", 0x0000BFFF, NULL); - g_free(txt); - - /* El tick labels */ - txt = - g_strdup_printf("%.0f\302\260", - 90.0 / (AZEL_PLOT_NUM_TICKS + 1) * (i + 1)); - azel->ellab[i] = - MKTEXT(root, txt, (gfloat) (azel->xmax + 5), - (gfloat) (azel->y0 - (i + 1) * ystep), -1, - GOO_CANVAS_ANCHOR_W, - "font", g_value_get_string(&azel->font), - "fill-color-rgba", 0xBF0000FF, NULL); - g_free(txt); + calculate_graph_points(azel); + calculate_tick_labels(azel); } - - /* x legend */ - if (sat_cfg_get_bool(SAT_CFG_BOOL_USE_LOCAL_TIME)) - { - azel->xleg = MKTEXT(root, _("Local Time"), - (gfloat) (azel->x0 + (azel->xmax - azel->x0) / 2), - (gfloat) (azel->height - 5), - -1, - GOO_CANVAS_ANCHOR_S, - "font", g_value_get_string(&azel->font), - "fill-color-rgba", 0x000000FF, NULL); - } - else - { - azel->xleg = MKTEXT(root, _("UTC"), - (gfloat) (azel->x0 + (azel->xmax - azel->x0) / 2), - (gfloat) (azel->height - 5), - -1, - GOO_CANVAS_ANCHOR_S, - "font", g_value_get_string(&azel->font), - "fill-color-rgba", 0x000000FF, NULL); - } - - /* cursor text in upper right corner */ - azel->curs = MKTEXT(root, "", - (gfloat) (azel->x0 + (azel->xmax - azel->x0) / 2), - 5.0, - -1, - GOO_CANVAS_ANCHOR_N, - "font", g_value_get_string(&azel->font), - "fill-color-rgba", 0x000000FF, NULL); - - /* Az legend */ - azel->azleg = MKTEXT(root, _("Az"), - (gfloat) (azel->x0 - 7), - (gfloat) azel->ymax, - -1, - GOO_CANVAS_ANCHOR_NE, - "font", g_value_get_string(&azel->font), - "fill-color-rgba", 0x0000BFFF, NULL); - - /* El legend */ - azel->elleg = MKTEXT(root, _("El"), - (gfloat) (azel->xmax + 7), - (gfloat) azel->ymax, - -1, - GOO_CANVAS_ANCHOR_NW, - "font", g_value_get_string(&azel->font), - "fill-color-rgba", 0xBF0000FF, NULL); - - /* Az graph */ - azel->azg = MKLINE(root, 0, 0, 10, 10, - "stroke-color-rgba", 0x0000BFFF, - "line-cap", CAIRO_LINE_CAP_SQUARE, - "line-join", CAIRO_LINE_JOIN_MITER, - "line-width", 1.0, NULL); - - /* El graph */ - azel->elg = MKLINE(root, 30, 30, 40, 40, - "stroke-color-rgba", 0xBF0000FF, - "fill-color-rgba", 0xBF00001A, - "line-cap", CAIRO_LINE_CAP_SQUARE, - "line-join", CAIRO_LINE_JOIN_MITER, - "line-width", 1.0, NULL); - - return root; } /** * Create a new GtkAzelPlot widget. - * @param cfgdata The configuration data of the parent module. - * @param sats Pointer to the hash table containing the associated satellites. * @param qth Pointer to the ground station data. + * @param pass Pointer to the satellite pass to display. */ -GtkWidget *gtk_azel_plot_new(qth_t * qth, pass_t * pass) +GtkWidget *gtk_azel_plot_new(qth_t * qth, pass_t * pass) { GtkAzelPlot *azel; - GooCanvasItemModel *root; guint i, n; pass_detail_t *detail; + GValue font_value = G_VALUE_INIT; azel = GTK_AZEL_PLOT(g_object_new(GTK_TYPE_AZEL_PLOT, NULL)); azel->qth = qth; @@ -639,6 +502,17 @@ GtkWidget *gtk_azel_plot_new(qth_t * qth, pass_t * pass) azel->extratick = sat_cfg_get_bool(SAT_CFG_BOOL_POL_SHOW_EXTRA_AZ_TICKS); azel->cursinfo = TRUE; + /* get colors */ + azel->col_axis = sat_cfg_get_int(SAT_CFG_INT_POLAR_AXIS_COL); + azel->col_az = 0x0000BFFF; + azel->col_el = 0xBF0000FF; + + /* get default font */ + g_value_init(&font_value, G_TYPE_STRING); + g_object_get_property(G_OBJECT(gtk_settings_get_default()), "gtk-font-name", &font_value); + azel->font = g_value_dup_string(&font_value); + g_value_unset(&font_value); + /* check maximum Az */ n = g_slist_length(pass->details); for (i = 0; i < n; i++) @@ -656,27 +530,28 @@ GtkWidget *gtk_azel_plot_new(qth_t * qth, pass_t * pass) else azel->maxaz = 180.0; - /* create the canvas */ - azel->canvas = goo_canvas_new(); - gtk_widget_set_size_request(azel->canvas, AZEL_DEFAULT_SIZE, - AZEL_DEFAULT_SIZE); - goo_canvas_set_bounds(GOO_CANVAS(azel->canvas), 0, 0, - AZEL_DEFAULT_SIZE, AZEL_DEFAULT_SIZE); - - /* connect size-request signal */ - g_signal_connect(azel->canvas, "size-allocate", - G_CALLBACK(size_allocate_cb), azel); - g_signal_connect(azel->canvas, "item_created", - G_CALLBACK(on_item_created), azel); - g_signal_connect_after(azel->canvas, "realize", - G_CALLBACK(on_canvas_realized), azel); + /* initial dimensions */ + azel->width = AZEL_DEFAULT_SIZE; + azel->height = AZEL_DEFAULT_SIZE; + azel->x0 = AZEL_X_MARGIN; + azel->xmax = azel->width - AZEL_X_MARGIN; + azel->y0 = azel->height - AZEL_Y_MARGIN; + azel->ymax = AZEL_Y_MARGIN; - gtk_widget_show(azel->canvas); + /* calculate initial graph points */ + calculate_graph_points(azel); + calculate_tick_labels(azel); + + /* create the drawing area */ + azel->canvas = gtk_drawing_area_new(); + gtk_widget_set_size_request(azel->canvas, AZEL_DEFAULT_SIZE, AZEL_DEFAULT_SIZE); + gtk_widget_add_events(azel->canvas, GDK_POINTER_MOTION_MASK); - /* Create the canvas model */ - root = create_canvas_model(azel); - goo_canvas_set_root_item_model(GOO_CANVAS(azel->canvas), root); - g_object_unref(root); + g_signal_connect(azel->canvas, "draw", G_CALLBACK(on_draw), azel); + g_signal_connect(azel->canvas, "motion-notify-event", G_CALLBACK(on_motion_notify), azel); + g_signal_connect(azel->canvas, "size-allocate", G_CALLBACK(size_allocate_cb), azel); + + gtk_widget_show(azel->canvas); gtk_box_pack_start(GTK_BOX(azel), azel->canvas, TRUE, TRUE, 0); return GTK_WIDGET(azel); diff --git a/src/gtk-azel-plot.h b/src/gtk-azel-plot.h index ceb526cde..138c2696d 100644 --- a/src/gtk-azel-plot.h +++ b/src/gtk-azel-plot.h @@ -29,7 +29,6 @@ #include #include -#include #include #include "gtk-sat-data.h" @@ -56,25 +55,25 @@ typedef struct _GtkAzelPlotClass GtkAzelPlotClass; struct _GtkAzelPlot { GtkBox box; - GtkWidget *canvas; /*!< The canvas widget */ - - GooCanvasItemModel *bgd; /*!< Rectangle used to paint background */ - GooCanvasItemModel *curs; /*!< cusor info */ - GooCanvasItemModel *frame; /*!< frame */ - GooCanvasItemModel *azg; /*!< Az graph */ - GooCanvasItemModel *elg; /*!< El graph */ - GooCanvasItemModel *xticksb[AZEL_PLOT_NUM_TICKS]; /*!< x tick marks bottom */ - GooCanvasItemModel *xtickst[AZEL_PLOT_NUM_TICKS]; /*!< x tick marks top */ - GooCanvasItemModel *xlabels[AZEL_PLOT_NUM_TICKS]; /*!< x tick labels */ - GooCanvasItemModel *yticksl[AZEL_PLOT_NUM_TICKS]; /*!< x tick marks left */ - GooCanvasItemModel *yticksr[AZEL_PLOT_NUM_TICKS]; /*!< x tick marks right */ - GooCanvasItemModel *ylabelsl[AZEL_PLOT_NUM_TICKS]; /*!< left y tick labels */ - GooCanvasItemModel *ylabelsr[AZEL_PLOT_NUM_TICKS]; /*!< right y tick labels */ - - GooCanvasItemModel *xlab[AZEL_PLOT_NUM_TICKS]; /*!< x tick labels */ - GooCanvasItemModel *azlab[AZEL_PLOT_NUM_TICKS]; /*!< Az tick labels */ - GooCanvasItemModel *ellab[AZEL_PLOT_NUM_TICKS]; /*!< El tick labels */ - GooCanvasItemModel *azleg, *elleg, *xleg; /*!< Az and El legend */ + GtkWidget *canvas; /*!< The drawing area widget */ + + /* Colors */ + guint32 col_axis; /*!< Axis/frame color */ + guint32 col_az; /*!< Azimuth graph color */ + guint32 col_el; /*!< Elevation graph color */ + + /* Text elements */ + gchar *curs_text; /*!< Cursor info text */ + + /* Graph data points */ + gdouble *az_points; /*!< Array of azimuth graph points */ + gdouble *el_points; /*!< Array of elevation graph points */ + gint num_points; /*!< Number of data points */ + + /* Tick labels */ + gchar *xlabels[AZEL_PLOT_NUM_TICKS]; /*!< x tick label texts */ + gchar *azlabels[AZEL_PLOT_NUM_TICKS]; /*!< Az tick label texts */ + gchar *ellabels[AZEL_PLOT_NUM_TICKS]; /*!< El tick label texts */ qth_t *qth; /*!< Pointer to current location. */ pass_t *pass; @@ -90,7 +89,7 @@ struct _GtkAzelPlot { gboolean cursinfo; /*!< Track the mouse cursor. */ gboolean extratick; /*!< Show extra ticks */ - GValue font; /*!< Default font */ + gchar *font; /*!< Default font name */ }; struct _GtkAzelPlotClass { diff --git a/src/gtk-polar-plot.c b/src/gtk-polar-plot.c index 1bbca5bf5..d0d019654 100644 --- a/src/gtk-polar-plot.c +++ b/src/gtk-polar-plot.c @@ -40,8 +40,8 @@ #include #endif #include -#include #include +#include #include "config-keys.h" #include "gpredict-utils.h" @@ -61,7 +61,16 @@ #define MARKER_SIZE_HALF 2 -static GtkVBoxClass *parent_class = NULL; +static GtkBoxClass *parent_class = NULL; + +/** Convert rgba color to cairo-friendly format */ +static void rgba_to_cairo(guint32 rgba, gdouble *r, gdouble *g, gdouble *b, gdouble *a) +{ + *r = ((rgba >> 24) & 0xFF) / 255.0; + *g = ((rgba >> 16) & 0xFF) / 255.0; + *b = ((rgba >> 8) & 0xFF) / 255.0; + *a = (rgba & 0xFF) / 255.0; +} static void gtk_polar_plot_init(GtkPolarPlot * polview, gpointer g_class) @@ -78,17 +87,37 @@ static void gtk_polar_plot_init(GtkPolarPlot * polview, polview->qthinfo = FALSE; polview->cursinfo = FALSE; polview->extratick = FALSE; - polview->target = NULL; + polview->track_points = NULL; + polview->track_count = 0; + polview->target_az = -1.0; + polview->target_el = -1.0; + polview->ctrl_az = -1.0; + polview->ctrl_el = -1.0; + polview->rotor_az = -1.0; + polview->rotor_el = -1.0; + polview->curs_text = NULL; + polview->font = NULL; } static void gtk_polar_plot_destroy(GtkWidget * widget) { - if (GTK_POLAR_PLOT(widget)->pass != NULL) + GtkPolarPlot *polv = GTK_POLAR_PLOT(widget); + + if (polv->pass != NULL) { - free_pass(GTK_POLAR_PLOT(widget)->pass); - GTK_POLAR_PLOT(widget)->pass = NULL; + free_pass(polv->pass); + polv->pass = NULL; } + g_free(polv->track_points); + polv->track_points = NULL; + + g_free(polv->curs_text); + polv->curs_text = NULL; + + g_free(polv->font); + polv->font = NULL; + (*GTK_WIDGET_CLASS(parent_class)->destroy) (widget); } @@ -130,41 +159,6 @@ GType gtk_polar_plot_get_type() return gtk_polar_plot_type; } -static GooCanvasItemModel *create_time_tick(GtkPolarPlot * pv, gdouble time, - gfloat x, gfloat y) -{ - GooCanvasItemModel *item; - GooCanvasAnchorType anchor; - GooCanvasItemModel *root; - guint32 col; - gchar buff[6]; - - root = goo_canvas_get_root_item_model(GOO_CANVAS(pv->canvas)); - - col = sat_cfg_get_int(SAT_CFG_INT_POLAR_TRACK_COL); - - daynum_to_str(buff, 6, "%H:%M", time); - - if (x > pv->cx) - { - anchor = GOO_CANVAS_ANCHOR_EAST; - x -= 5; - } - else - { - anchor = GOO_CANVAS_ANCHOR_WEST; - x += 5; - } - - item = goo_canvas_text_model_new(root, buff, - (gdouble) x, (gdouble) y, - -1, anchor, - "font", g_value_get_string(&pv->font), - "fill-color-rgba", col, NULL); - - return item; -} - /** Convert Az/El to canvas based XY coordinates. */ static void azel_to_xy(GtkPolarPlot * p, gdouble az, gdouble el, gfloat * x, gfloat * y) @@ -261,31 +255,30 @@ static void xy_to_azel(GtkPolarPlot * p, gfloat x, gfloat y, static void create_track(GtkPolarPlot * pv) { guint i; - GooCanvasItemModel *root; pass_detail_t *detail; guint num; - GooCanvasPoints *points; gfloat x, y; - guint32 col; guint tres, ttidx; - root = goo_canvas_get_root_item_model(GOO_CANVAS(pv->canvas)); - /* create points */ num = g_slist_length(pv->pass->details); - /* time resolution for time ticks; we need - 3 additional points to AOS and LOS ticks. - */ - tres = (num - 2) / (TRACK_TICK_NUM - 1); + g_free(pv->track_points); + pv->track_points = g_new(gdouble, num * 2); + pv->track_count = num; - points = goo_canvas_points_new(num); + /* time resolution for time ticks */ + tres = (num - 2) / (TRACK_TICK_NUM - 1); /* first point should be (aos_az,0.0) */ azel_to_xy(pv, pv->pass->aos_az, 0.0, &x, &y); - points->coords[0] = (double)x; - points->coords[1] = (double)y; - pv->trtick[0] = create_time_tick(pv, pv->pass->aos, x, y); + pv->track_points[0] = (gdouble)x; + pv->track_points[1] = (gdouble)y; + + /* first time tick */ + pv->trtick[0].x = x; + pv->trtick[0].y = y; + daynum_to_str(pv->trtick[0].text, 6, "%H:%M", pv->pass->aos); ttidx = 1; @@ -294,48 +287,40 @@ static void create_track(GtkPolarPlot * pv) detail = PASS_DETAIL(g_slist_nth_data(pv->pass->details, i)); if (detail->el >= 0.0) azel_to_xy(pv, detail->az, detail->el, &x, &y); - points->coords[2 * i] = (double)x; - points->coords[2 * i + 1] = (double)y; + pv->track_points[2 * i] = (gdouble)x; + pv->track_points[2 * i + 1] = (gdouble)y; if (!(i % tres)) { if (ttidx < TRACK_TICK_NUM) + { /* create a time tick */ - pv->trtick[ttidx] = create_time_tick(pv, detail->time, x, y); + pv->trtick[ttidx].x = x; + pv->trtick[ttidx].y = y; + daynum_to_str(pv->trtick[ttidx].text, 6, "%H:%M", detail->time); + } ttidx++; } } /* last point should be (los_az, 0.0) */ azel_to_xy(pv, pv->pass->los_az, 0.0, &x, &y); - points->coords[2 * (num - 1)] = (double)x; - points->coords[2 * (num - 1) + 1] = (double)y; - - /* create poly-line */ - col = sat_cfg_get_int(SAT_CFG_INT_POLAR_TRACK_COL); - - pv->track = goo_canvas_polyline_model_new(root, FALSE, 0, - "points", points, - "line-width", 1.0, - "stroke-color-rgba", col, - "line-cap", - CAIRO_LINE_CAP_SQUARE, - "line-join", - CAIRO_LINE_JOIN_MITER, NULL); - goo_canvas_points_unref(points); + pv->track_points[2 * (num - 1)] = (gdouble)x; + pv->track_points[2 * (num - 1) + 1] = (gdouble)y; } /** * Transform pole coordinates. * - * This function transforms the pols coordinates (x,y) taking into account + * This function transforms the pole coordinates (x,y) taking into account * the orientation of the polar plot. */ static void -correct_pole_coor(GtkPolarPlot * polv, - polar_plot_pole_t pole, - gfloat * x, gfloat * y, GooCanvasAnchorType * anch) +correct_pole_coor(GtkPolarPlot * polv, polar_plot_pole_t pole, + gfloat * x, gfloat * y, gboolean *anchor_west) { + *anchor_west = TRUE; + switch (pole) { case POLAR_PLOT_POLE_N: @@ -343,12 +328,10 @@ correct_pole_coor(GtkPolarPlot * polv, { /* North and South are swapped */ *y = *y + POLV_LINE_EXTRA; - *anch = GOO_CANVAS_ANCHOR_NORTH; } else { *y = *y - POLV_LINE_EXTRA; - *anch = GOO_CANVAS_ANCHOR_SOUTH; } break; @@ -357,12 +340,11 @@ correct_pole_coor(GtkPolarPlot * polv, { /* East and West are swapped */ *x = *x - POLV_LINE_EXTRA; - *anch = GOO_CANVAS_ANCHOR_EAST; + *anchor_west = FALSE; } else { *x = *x + POLV_LINE_EXTRA; - *anch = GOO_CANVAS_ANCHOR_WEST; } break; @@ -371,12 +353,10 @@ correct_pole_coor(GtkPolarPlot * polv, { /* North and South are swapped */ *y = *y - POLV_LINE_EXTRA; - *anch = GOO_CANVAS_ANCHOR_SOUTH; } else { *y = *y + POLV_LINE_EXTRA; - *anch = GOO_CANVAS_ANCHOR_NORTH; } break; @@ -385,12 +365,11 @@ correct_pole_coor(GtkPolarPlot * polv, { /* East and West are swapped */ *x = *x + POLV_LINE_EXTRA; - *anch = GOO_CANVAS_ANCHOR_WEST; } else { *x = *x - POLV_LINE_EXTRA; - *anch = GOO_CANVAS_ANCHOR_EAST; + *anchor_west = FALSE; } break; @@ -402,159 +381,34 @@ correct_pole_coor(GtkPolarPlot * polv, } } -static GooCanvasItemModel *create_canvas_model(GtkPolarPlot * polv) -{ - GooCanvasItemModel *root; - gfloat x, y; - guint32 col; - GooCanvasAnchorType anch = GOO_CANVAS_ANCHOR_CENTER; - - root = goo_canvas_group_model_new(NULL, NULL); - - polv->bgd = goo_canvas_rect_model_new(root, 0.0, 0.0, - POLV_DEFAULT_SIZE, POLV_DEFAULT_SIZE, - "fill-color-rgba", 0xFFFFFFFF, - "stroke-color-rgba", 0xFFFFFFFF, - NULL); - - /* graph dimensions */ - polv->size = POLV_DEFAULT_SIZE; - polv->r = (polv->size / 2) - POLV_DEFAULT_MARGIN; - polv->cx = POLV_DEFAULT_SIZE / 2; - polv->cy = POLV_DEFAULT_SIZE / 2; - - col = sat_cfg_get_int(SAT_CFG_INT_POLAR_AXIS_COL); - - /* default font */ - g_object_get_property(G_OBJECT(gtk_settings_get_default()), "gtk-font-name", &polv->font); - - /* Add elevation circles at 0, 30 and 60 deg */ - polv->C00 = goo_canvas_ellipse_model_new(root, - polv->cx, polv->cy, - polv->r, polv->r, - "line-width", 1.0, - "stroke-color-rgba", col, NULL); - - polv->C30 = goo_canvas_ellipse_model_new(root, - polv->cx, polv->cy, - 0.6667 * polv->r, - 0.6667 * polv->r, "line-width", - 1.0, "stroke-color-rgba", col, - NULL); - - polv->C60 = goo_canvas_ellipse_model_new(root, - polv->cx, polv->cy, - 0.333 * polv->r, 0.3333 * polv->r, - "line-width", 1.0, - "stroke-color-rgba", col, NULL); - - /* add horixontal and vertical guidance lines */ - polv->hl = goo_canvas_polyline_model_new_line(root, - polv->cx - polv->r - - POLV_LINE_EXTRA, polv->cy, - polv->cx + polv->r + - POLV_LINE_EXTRA, polv->cy, - "stroke-color-rgba", col, - "line-width", 1.0, NULL); - polv->vl = - goo_canvas_polyline_model_new_line(root, polv->cx, - polv->cy - polv->r - - POLV_LINE_EXTRA, polv->cx, - polv->cy + polv->r + - POLV_LINE_EXTRA, - "stroke-color-rgba", col, - "line-width", 1.0, NULL); - - /* N, S, E and W labels. */ - col = sat_cfg_get_int(SAT_CFG_INT_POLAR_TICK_COL); - azel_to_xy(polv, 0.0, 0.0, &x, &y); - correct_pole_coor(polv, POLAR_PLOT_POLE_N, &x, &y, &anch); - polv->N = goo_canvas_text_model_new(root, _("N"), - x, - y, - -1, - anch, - "font", g_value_get_string(&polv->font), - "fill-color-rgba", col, NULL); - - azel_to_xy(polv, 180.0, 0.0, &x, &y); - correct_pole_coor(polv, POLAR_PLOT_POLE_S, &x, &y, &anch); - polv->S = goo_canvas_text_model_new(root, _("S"), - x, - y, - -1, - anch, - "font", g_value_get_string(&polv->font), - "fill-color-rgba", col, NULL); - - azel_to_xy(polv, 90.0, 0.0, &x, &y); - correct_pole_coor(polv, POLAR_PLOT_POLE_E, &x, &y, &anch); - polv->E = goo_canvas_text_model_new(root, _("E"), - x, - y, - -1, - anch, - "font", g_value_get_string(&polv->font), - "fill-color-rgba", col, NULL); - - azel_to_xy(polv, 270.0, 0.0, &x, &y); - correct_pole_coor(polv, POLAR_PLOT_POLE_W, &x, &y, &anch); - polv->W = goo_canvas_text_model_new(root, _("W"), - x, - y, - -1, - anch, - "font", g_value_get_string(&polv->font), - "fill-color-rgba", col, NULL); - - /* cursor text */ - col = sat_cfg_get_int(SAT_CFG_INT_POLAR_INFO_COL); - polv->curs = goo_canvas_text_model_new(root, "", - polv->cx - polv->r - - 2 * POLV_LINE_EXTRA, - polv->cy + polv->r + - POLV_LINE_EXTRA, -1, GOO_CANVAS_ANCHOR_W, - "font", g_value_get_string(&polv->font), - "fill-color-rgba", col, NULL); - - /* location info */ - polv->locnam = goo_canvas_text_model_new(root, polv->qth->name, - polv->cx - polv->r - - 2 * POLV_LINE_EXTRA, - polv->cy - polv->r - - POLV_LINE_EXTRA, -1, - GOO_CANVAS_ANCHOR_SW, - "font", g_value_get_string(&polv->font), - "fill-color-rgba", col, NULL); - - return root; -} - /** Update sky track drawing after size allocate. */ static void update_track(GtkPolarPlot * pv) { guint num, i; - GooCanvasPoints *points; gfloat x, y; pass_detail_t *detail; guint tres, ttidx; + if (pv->pass == NULL) + return; + /* create points */ num = g_slist_length(pv->pass->details); - points = goo_canvas_points_new(num); + g_free(pv->track_points); + pv->track_points = g_new(gdouble, num * 2); + pv->track_count = num; /* first point should be (aos_az,0.0) */ azel_to_xy(pv, pv->pass->aos_az, 0.0, &x, &y); - points->coords[0] = (double)x; - points->coords[1] = (double)y; + pv->track_points[0] = (gdouble)x; + pv->track_points[1] = (gdouble)y; /* time tick 0 */ - g_object_set(pv->trtick[0], "x", (gdouble) x, "y", (gdouble) y, NULL); + pv->trtick[0].x = x; + pv->trtick[0].y = y; - /* time resolution for time ticks; we need - 3 additional points to AOS and LOS ticks. - */ + /* time resolution for time ticks */ tres = (num - 2) / (TRACK_TICK_NUM - 1); ttidx = 1; @@ -563,8 +417,8 @@ static void update_track(GtkPolarPlot * pv) detail = PASS_DETAIL(g_slist_nth_data(pv->pass->details, i)); if (detail->el >= 0.0) azel_to_xy(pv, detail->az, detail->el, &x, &y); - points->coords[2 * i] = (double)x; - points->coords[2 * i + 1] = (double)y; + pv->track_points[2 * i] = (gdouble)x; + pv->track_points[2 * i + 1] = (gdouble)y; if (!(i % tres)) { @@ -577,8 +431,8 @@ static void update_track(GtkPolarPlot * pv) /* update time tick */ if (ttidx < TRACK_TICK_NUM) { - g_object_set(pv->trtick[ttidx], - "x", (gdouble) x, "y", (gdouble) y, NULL); + pv->trtick[ttidx].x = x; + pv->trtick[ttidx].y = y; } ttidx++; } @@ -586,142 +440,212 @@ static void update_track(GtkPolarPlot * pv) /* last point should be (los_az, 0.0) */ azel_to_xy(pv, pv->pass->los_az, 0.0, &x, &y); - points->coords[2 * (num - 1)] = (double)x; - points->coords[2 * (num - 1) + 1] = (double)y; - - g_object_set(pv->track, "points", points, NULL); - - goo_canvas_points_unref(points); + pv->track_points[2 * (num - 1)] = (gdouble)x; + pv->track_points[2 * (num - 1) + 1] = (gdouble)y; } -/** - * Manage new size allocation. - * - * This function is called when the canvas receives a new size allocation, - * e.g. when the container is re-sized. The function re-calculates the graph - * dimensions based on the new canvas size. - */ -static void size_allocate_cb(GtkWidget * widget, GtkAllocation * allocation, - gpointer data) +static gboolean on_draw(GtkWidget *widget, cairo_t *cr, gpointer data) { - GtkPolarPlot *polv; - GooCanvasPoints *prec; + GtkPolarPlot *polv = GTK_POLAR_PLOT(data); + gdouble r, g, b, a; gfloat x, y; - GooCanvasAnchorType anch = GOO_CANVAS_ANCHOR_CENTER; + gboolean anchor_west; + PangoLayout *layout; + PangoFontDescription *font_desc; + gint tw, th; + guint i; - if (gtk_widget_get_realized(widget)) + (void)widget; + + /* White background */ + cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); + cairo_paint(cr); + + /* Set up font */ + layout = pango_cairo_create_layout(cr); + font_desc = pango_font_description_from_string(polv->font ? polv->font : "Sans 9"); + pango_layout_set_font_description(layout, font_desc); + + /* Axis color for circles and lines */ + rgba_to_cairo(polv->col_axis, &r, &g, &b, &a); + cairo_set_source_rgba(cr, r, g, b, a); + cairo_set_line_width(cr, 1.0); + + /* 0 degree circle */ + cairo_arc(cr, polv->cx, polv->cy, polv->r, 0, 2 * M_PI); + cairo_stroke(cr); + + /* 30 degree circle */ + cairo_arc(cr, polv->cx, polv->cy, 0.6667 * polv->r, 0, 2 * M_PI); + cairo_stroke(cr); + + /* 60 degree circle */ + cairo_arc(cr, polv->cx, polv->cy, 0.333 * polv->r, 0, 2 * M_PI); + cairo_stroke(cr); + + /* Horizontal line */ + cairo_move_to(cr, polv->cx - polv->r - POLV_LINE_EXTRA, polv->cy); + cairo_line_to(cr, polv->cx + polv->r + POLV_LINE_EXTRA, polv->cy); + cairo_stroke(cr); + + /* Vertical line */ + cairo_move_to(cr, polv->cx, polv->cy - polv->r - POLV_LINE_EXTRA); + cairo_line_to(cr, polv->cx, polv->cy + polv->r + POLV_LINE_EXTRA); + cairo_stroke(cr); + + /* N/S/E/W labels */ + rgba_to_cairo(polv->col_tick, &r, &g, &b, &a); + cairo_set_source_rgba(cr, r, g, b, a); + + /* N label */ + azel_to_xy(polv, 0.0, 0.0, &x, &y); + correct_pole_coor(polv, POLAR_PLOT_POLE_N, &x, &y, &anchor_west); + pango_layout_set_text(layout, _("N"), -1); + pango_layout_get_pixel_size(layout, &tw, &th); + cairo_move_to(cr, x - tw / 2, y - th); + pango_cairo_show_layout(cr, layout); + + /* E label */ + azel_to_xy(polv, 90.0, 0.0, &x, &y); + correct_pole_coor(polv, POLAR_PLOT_POLE_E, &x, &y, &anchor_west); + pango_layout_set_text(layout, _("E"), -1); + pango_layout_get_pixel_size(layout, &tw, &th); + if (anchor_west) + cairo_move_to(cr, x, y - th / 2); + else + cairo_move_to(cr, x - tw, y - th / 2); + pango_cairo_show_layout(cr, layout); + + /* S label */ + azel_to_xy(polv, 180.0, 0.0, &x, &y); + correct_pole_coor(polv, POLAR_PLOT_POLE_S, &x, &y, &anchor_west); + pango_layout_set_text(layout, _("S"), -1); + pango_layout_get_pixel_size(layout, &tw, &th); + cairo_move_to(cr, x - tw / 2, y); + pango_cairo_show_layout(cr, layout); + + /* W label */ + azel_to_xy(polv, 270.0, 0.0, &x, &y); + correct_pole_coor(polv, POLAR_PLOT_POLE_W, &x, &y, &anchor_west); + pango_layout_set_text(layout, _("W"), -1); + pango_layout_get_pixel_size(layout, &tw, &th); + if (anchor_west) + cairo_move_to(cr, x, y - th / 2); + else + cairo_move_to(cr, x - tw, y - th / 2); + pango_cairo_show_layout(cr, layout); + + /* Location name (if enabled) */ + if (polv->qthinfo && polv->qth) { - /* get graph dimensions */ - polv = GTK_POLAR_PLOT(data); + rgba_to_cairo(polv->col_info, &r, &g, &b, &a); + cairo_set_source_rgba(cr, r, g, b, a); + pango_layout_set_text(layout, polv->qth->name, -1); + pango_layout_get_pixel_size(layout, &tw, &th); + cairo_move_to(cr, polv->cx - polv->r - 2 * POLV_LINE_EXTRA, + polv->cy - polv->r - POLV_LINE_EXTRA - th); + pango_cairo_show_layout(cr, layout); + } - polv->size = MIN(allocation->width, allocation->height); - polv->r = (polv->size / 2) - POLV_DEFAULT_MARGIN; - polv->cx = allocation->width / 2; - polv->cy = allocation->height / 2; + /* Cursor tracking text */ + if (polv->cursinfo && polv->curs_text) + { + rgba_to_cairo(polv->col_info, &r, &g, &b, &a); + cairo_set_source_rgba(cr, r, g, b, a); + pango_layout_set_text(layout, polv->curs_text, -1); + cairo_move_to(cr, polv->cx - polv->r - 2 * POLV_LINE_EXTRA, + polv->cy + polv->r + POLV_LINE_EXTRA); + pango_cairo_show_layout(cr, layout); + } - goo_canvas_set_bounds(GOO_CANVAS(GTK_POLAR_PLOT(polv)->canvas), 0, 0, - allocation->width, allocation->height); - - /* background item */ - g_object_set(polv->bgd, "width", (gdouble) allocation->width, - "height", (gdouble) allocation->height, NULL); - - /* update coordinate system */ - g_object_set(polv->C00, - "center-x", (gdouble) polv->cx, - "center-y", (gdouble) polv->cy, - "radius-x", (gdouble) polv->r, - "radius-y", (gdouble) polv->r, NULL); - g_object_set(polv->C30, - "center-x", (gdouble) polv->cx, - "center-y", (gdouble) polv->cy, - "radius-x", (gdouble) 0.6667 * polv->r, - "radius-y", (gdouble) 0.6667 * polv->r, NULL); - g_object_set(polv->C60, - "center-x", (gdouble) polv->cx, - "center-y", (gdouble) polv->cy, - "radius-x", (gdouble) 0.333 * polv->r, - "radius-y", (gdouble) 0.333 * polv->r, NULL); - - /* horizontal line */ - prec = goo_canvas_points_new(2); - prec->coords[0] = polv->cx - polv->r - POLV_LINE_EXTRA; - prec->coords[1] = polv->cy; - prec->coords[2] = polv->cx + polv->r + POLV_LINE_EXTRA; - prec->coords[3] = polv->cy; - g_object_set(polv->hl, "points", prec, NULL); - - /* vertical line */ - prec->coords[0] = polv->cx; - prec->coords[1] = polv->cy - polv->r - POLV_LINE_EXTRA; - prec->coords[2] = polv->cx; - prec->coords[3] = polv->cy + polv->r + POLV_LINE_EXTRA; - g_object_set(polv->vl, "points", prec, NULL); - - /* free memory */ - goo_canvas_points_unref(prec); - - /* N/E/S/W */ - azel_to_xy(polv, 0.0, 0.0, &x, &y); - correct_pole_coor(polv, POLAR_PLOT_POLE_N, &x, &y, &anch); - g_object_set(polv->N, "x", x, "y", y, NULL); - - azel_to_xy(polv, 90.0, 0.0, &x, &y); - correct_pole_coor(polv, POLAR_PLOT_POLE_E, &x, &y, &anch); - g_object_set(polv->E, "x", x, "y", y, NULL); - - azel_to_xy(polv, 180.0, 0.0, &x, &y); - correct_pole_coor(polv, POLAR_PLOT_POLE_S, &x, &y, &anch); - g_object_set(polv->S, "x", x, "y", y, NULL); - - azel_to_xy(polv, 270.0, 0.0, &x, &y); - correct_pole_coor(polv, POLAR_PLOT_POLE_W, &x, &y, &anch); - g_object_set(polv->W, "x", x, "y", y, NULL); - - /* cursor track */ - g_object_set(polv->curs, - "x", (gfloat) (polv->cx - polv->r - 2 * POLV_LINE_EXTRA), - "y", (gfloat) (polv->cy + polv->r + POLV_LINE_EXTRA), - NULL); - - /* location name */ - g_object_set(polv->locnam, - "x", (gfloat) (polv->cx - polv->r - 2 * POLV_LINE_EXTRA), - "y", (gfloat) (polv->cy - polv->r - POLV_LINE_EXTRA), - NULL); + /* Draw satellite track */ + if (polv->track_points && polv->track_count > 1) + { + rgba_to_cairo(polv->col_track, &r, &g, &b, &a); + cairo_set_source_rgba(cr, r, g, b, a); + cairo_set_line_width(cr, 1.0); + cairo_move_to(cr, polv->track_points[0], polv->track_points[1]); + for (i = 1; i < (guint)polv->track_count; i++) + { + cairo_line_to(cr, polv->track_points[2 * i], polv->track_points[2 * i + 1]); + } + cairo_stroke(cr); - /* sky track */ - if (polv->pass != NULL) - update_track(polv); + /* Draw time ticks along track */ + for (i = 0; i < TRACK_TICK_NUM; i++) + { + x = polv->trtick[i].x; + y = polv->trtick[i].y; + + pango_layout_set_text(layout, polv->trtick[i].text, -1); + pango_layout_get_pixel_size(layout, &tw, &th); + + if (x > polv->cx) + cairo_move_to(cr, x - tw - 5, y - th / 2); + else + cairo_move_to(cr, x + 5, y - th / 2); + + pango_cairo_show_layout(cr, layout); + } } -} -/** - * Manage canvas realise signals. - * - * This function is used to re-initialise the graph dimensions when - * the graph is realized, i.e. displayed for the first time. This is - * necessary in order to compensate for missing "re-allocate" signals for - * graphs that have not yet been realised, e.g. when opening several module - */ -static void on_canvas_realized(GtkWidget * canvas, gpointer data) -{ - GtkAllocation aloc; + /* Draw target (small filled square) */ + if (polv->target_az >= 0.0 && polv->target_el >= 0.0) + { + azel_to_xy(polv, polv->target_az, polv->target_el, &x, &y); + rgba_to_cairo(polv->col_sat, &r, &g, &b, &a); + cairo_set_source_rgba(cr, r, g, b, a); + cairo_rectangle(cr, x - MARKER_SIZE_HALF, y - MARKER_SIZE_HALF, + 2 * MARKER_SIZE_HALF, 2 * MARKER_SIZE_HALF); + cairo_fill(cr); + } - gtk_widget_get_allocation(canvas, &aloc); - size_allocate_cb(canvas, &aloc, data); + /* Draw controller (small circle) */ + if (polv->ctrl_az >= 0.0 && polv->ctrl_el >= 0.0) + { + azel_to_xy(polv, polv->ctrl_az, polv->ctrl_el, &x, &y); + rgba_to_cairo(polv->col_sat, &r, &g, &b, &a); + cairo_set_source_rgba(cr, r, g, b, a); + cairo_arc(cr, x, y, 7, 0, 2 * M_PI); + cairo_stroke(cr); + } + + /* Draw rotor position (cross hair) */ + if (polv->rotor_az >= 0.0 && polv->rotor_el >= 0.0) + { + azel_to_xy(polv, polv->rotor_az, polv->rotor_el, &x, &y); + rgba_to_cairo(polv->col_sat, &r, &g, &b, &a); + cairo_set_source_rgba(cr, r, g, b, a); + cairo_set_line_width(cr, 1.0); + + /* Up */ + cairo_move_to(cr, x, y - 4); + cairo_line_to(cr, x, y - 14); + /* Right */ + cairo_move_to(cr, x + 4, y); + cairo_line_to(cr, x + 14, y); + /* Down */ + cairo_move_to(cr, x, y + 4); + cairo_line_to(cr, x, y + 14); + /* Left */ + cairo_move_to(cr, x - 4, y); + cairo_line_to(cr, x - 14, y); + + cairo_stroke(cr); + } + + pango_font_description_free(font_desc); + g_object_unref(layout); + + return FALSE; } -/** Manage mouse motion events. */ -static gboolean on_motion_notify(GooCanvasItem * item, GooCanvasItem * target, - GdkEventMotion * event, gpointer data) +static gboolean on_motion_notify(GtkWidget *widget, GdkEventMotion *event, gpointer data) { GtkPolarPlot *polv = GTK_POLAR_PLOT(data); gfloat az, el; - gchar *text; - (void)item; - (void)target; + (void)widget; if (polv->cursinfo) { @@ -729,38 +653,57 @@ static gboolean on_motion_notify(GooCanvasItem * item, GooCanvasItem * target, if (el > 0.0) { - /* cursor track */ - text = g_strdup_printf("AZ %.0f\302\260\nEL %.0f\302\260", az, el); - g_object_set(polv->curs, "text", text, NULL); - g_free(text); + g_free(polv->curs_text); + polv->curs_text = g_strdup_printf("AZ %.0f\302\260\nEL %.0f\302\260", az, el); } else { - g_object_set(polv->curs, "text", "", NULL); + g_free(polv->curs_text); + polv->curs_text = NULL; } + + gtk_widget_queue_draw(polv->canvas); } return TRUE; } /** - * Finish canvas item setup. - * - * This function is called when a canvas item is created. Its purpose is to connect - * the corresponding signals to the created items. + * Manage new size allocation. */ -static void on_item_created(GooCanvas * canvas, GooCanvasItem * item, - GooCanvasItemModel * model, gpointer data) +static void size_allocate_cb(GtkWidget * widget, GtkAllocation * allocation, + gpointer data) { - (void)canvas; - if (!goo_canvas_item_model_get_parent(model)) + GtkPolarPlot *polv; + + if (gtk_widget_get_realized(widget)) { - /* root item / canvas */ - g_signal_connect(item, "motion_notify_event", - (GCallback) on_motion_notify, data); + polv = GTK_POLAR_PLOT(data); + + polv->size = MIN(allocation->width, allocation->height); + polv->r = (polv->size / 2) - POLV_DEFAULT_MARGIN; + polv->cx = allocation->width / 2; + polv->cy = allocation->height / 2; + + /* sky track */ + if (polv->pass != NULL) + update_track(polv); + + gtk_widget_queue_draw(widget); } } +/** + * Manage canvas realise signals. + */ +static void on_canvas_realized(GtkWidget * canvas, gpointer data) +{ + GtkAllocation aloc; + + gtk_widget_get_allocation(canvas, &aloc); + size_allocate_cb(canvas, &aloc, data); +} + /** * Create a new GtkPolarPlot widget. * @@ -772,7 +715,7 @@ static void on_item_created(GooCanvas * canvas, GooCanvasItem * item, GtkWidget *gtk_polar_plot_new(qth_t * qth, pass_t * pass) { GtkPolarPlot *polv; - GooCanvasItemModel *root; + GValue font_value = G_VALUE_INIT; polv = GTK_POLAR_PLOT(g_object_new(GTK_TYPE_POLAR_PLOT, NULL)); @@ -787,27 +730,38 @@ GtkWidget *gtk_polar_plot_new(qth_t * qth, pass_t * pass) polv->extratick = sat_cfg_get_bool(SAT_CFG_BOOL_POL_SHOW_EXTRA_AZ_TICKS); polv->cursinfo = TRUE; - /* create the canvas */ - polv->canvas = goo_canvas_new(); - gtk_widget_set_size_request(polv->canvas, - POLV_DEFAULT_SIZE, POLV_DEFAULT_SIZE); - goo_canvas_set_bounds(GOO_CANVAS(polv->canvas), 0, 0, - POLV_DEFAULT_SIZE, POLV_DEFAULT_SIZE); - - /* connect size-request signal */ - g_signal_connect(polv->canvas, "size-allocate", - G_CALLBACK(size_allocate_cb), polv); - g_signal_connect(polv->canvas, "item_created", - (GCallback) on_item_created, polv); - g_signal_connect_after(polv->canvas, "realize", - (GCallback) on_canvas_realized, polv); + /* get colors */ + polv->col_bgd = 0xFFFFFFFF; + polv->col_axis = sat_cfg_get_int(SAT_CFG_INT_POLAR_AXIS_COL); + polv->col_tick = sat_cfg_get_int(SAT_CFG_INT_POLAR_TICK_COL); + polv->col_info = sat_cfg_get_int(SAT_CFG_INT_POLAR_INFO_COL); + polv->col_sat = sat_cfg_get_int(SAT_CFG_INT_POLAR_SAT_COL); + polv->col_track = sat_cfg_get_int(SAT_CFG_INT_POLAR_TRACK_COL); - gtk_widget_show(polv->canvas); + /* get default font */ + g_value_init(&font_value, G_TYPE_STRING); + g_object_get_property(G_OBJECT(gtk_settings_get_default()), "gtk-font-name", &font_value); + polv->font = g_value_dup_string(&font_value); + g_value_unset(&font_value); + + /* graph dimensions */ + polv->size = POLV_DEFAULT_SIZE; + polv->r = (polv->size / 2) - POLV_DEFAULT_MARGIN; + polv->cx = POLV_DEFAULT_SIZE / 2; + polv->cy = POLV_DEFAULT_SIZE / 2; + + /* create the canvas (drawing area) */ + polv->canvas = gtk_drawing_area_new(); + gtk_widget_set_size_request(polv->canvas, POLV_DEFAULT_SIZE, POLV_DEFAULT_SIZE); + gtk_widget_add_events(polv->canvas, GDK_POINTER_MOTION_MASK); - /* Create the canvas model */ - root = create_canvas_model(polv); - goo_canvas_set_root_item_model(GOO_CANVAS(polv->canvas), root); - g_object_unref(root); + /* connect signals */ + g_signal_connect(polv->canvas, "draw", G_CALLBACK(on_draw), polv); + g_signal_connect(polv->canvas, "motion-notify-event", G_CALLBACK(on_motion_notify), polv); + g_signal_connect(polv->canvas, "size-allocate", G_CALLBACK(size_allocate_cb), polv); + g_signal_connect_after(polv->canvas, "realize", G_CALLBACK(on_canvas_realized), polv); + + gtk_widget_show(polv->canvas); if (polv->pass != NULL) create_track(polv); @@ -826,35 +780,24 @@ GtkWidget *gtk_polar_plot_new(qth_t * qth, pass_t * pass) */ void gtk_polar_plot_set_pass(GtkPolarPlot * plot, pass_t * pass) { - GooCanvasItemModel *root; - gint idx, i; - - /* remove sky track, time ticks and the pass itself */ + /* remove sky track and the pass itself */ if (plot->pass != NULL) { - /* remove sat from canvas */ - root = goo_canvas_get_root_item_model(GOO_CANVAS(plot->canvas)); - idx = goo_canvas_item_model_find_child(root, plot->track); - - if (idx != -1) - goo_canvas_item_model_remove_child(root, idx); - - for (i = 0; i < TRACK_TICK_NUM; i++) - { - idx = goo_canvas_item_model_find_child(root, plot->trtick[i]); - if (idx != -1) - goo_canvas_item_model_remove_child(root, idx); - } - free_pass(plot->pass); plot->pass = NULL; } + g_free(plot->track_points); + plot->track_points = NULL; + plot->track_count = 0; + if (pass != NULL) { plot->pass = copy_pass(pass); create_track(plot); } + + gtk_widget_queue_draw(plot->canvas); } /** @@ -868,55 +811,13 @@ void gtk_polar_plot_set_pass(GtkPolarPlot * plot, pass_t * pass) */ void gtk_polar_plot_set_target_pos(GtkPolarPlot * plot, gdouble az, gdouble el) { - GooCanvasItemModel *root; - gint idx; - gfloat x, y; - guint32 col; - if (plot == NULL) return; - root = goo_canvas_get_root_item_model(GOO_CANVAS(plot->canvas)); + plot->target_az = az; + plot->target_el = el; - if ((az < 0.0) || (el < 0.0)) - { - if (plot->target != NULL) - { - /* the target object is visible; delete it */ - idx = goo_canvas_item_model_find_child(root, plot->target); - if (idx != -1) - goo_canvas_item_model_remove_child(root, idx); - - plot->target = NULL; - } - /* else the target object is not visible; nothing to do */ - } - else - { - /* we need to either update or create the object */ - azel_to_xy(plot, az, el, &x, &y); - - if (plot->target != NULL) - { - /* the target object already exists; move it */ - g_object_set(plot->target, - "x", x - MARKER_SIZE_HALF, - "y", y - MARKER_SIZE_HALF, NULL); - } - else - { - /* the target object does not exist; create it */ - col = sat_cfg_get_int(SAT_CFG_INT_POLAR_SAT_COL); - plot->target = goo_canvas_rect_model_new(root, - x - MARKER_SIZE_HALF, - y - MARKER_SIZE_HALF, - 2 * MARKER_SIZE_HALF, - 2 * MARKER_SIZE_HALF, - "fill-color-rgba", col, - "stroke-color-rgba", col, - NULL); - } - } + gtk_widget_queue_draw(plot->canvas); } /** @@ -930,51 +831,13 @@ void gtk_polar_plot_set_target_pos(GtkPolarPlot * plot, gdouble az, gdouble el) */ void gtk_polar_plot_set_ctrl_pos(GtkPolarPlot * plot, gdouble az, gdouble el) { - GooCanvasItemModel *root; - gint idx; - gfloat x, y; - guint32 col; - if (plot == NULL) return; - root = goo_canvas_get_root_item_model(GOO_CANVAS(plot->canvas)); - - if ((az < 0.0) || (el < 0.0)) - { - if (plot->ctrl != NULL) - { - /* the target object is visible; delete it */ - idx = goo_canvas_item_model_find_child(root, plot->ctrl); - if (idx != -1) - goo_canvas_item_model_remove_child(root, idx); - - plot->ctrl = NULL; - } - /* else the target object is not visible; nothing to do */ - } - else - { - /* we need to either update or create the object */ - azel_to_xy(plot, az, el, &x, &y); + plot->ctrl_az = az; + plot->ctrl_el = el; - if (plot->ctrl != NULL) - { - /* the target object already exists; move it */ - g_object_set(plot->ctrl, "center_x", x, "center_y", y, NULL); - } - else - { - /* the target object does not exist; create it */ - col = sat_cfg_get_int(SAT_CFG_INT_POLAR_SAT_COL); - plot->ctrl = goo_canvas_ellipse_model_new(root, - x, y, 7, 7, - "fill-color-rgba", - 0xFF00000F, - "stroke-color-rgba", col, - "line-width", 0.8, NULL); - } - } + gtk_widget_queue_draw(plot->canvas); } /** @@ -988,155 +851,13 @@ void gtk_polar_plot_set_ctrl_pos(GtkPolarPlot * plot, gdouble az, gdouble el) */ void gtk_polar_plot_set_rotor_pos(GtkPolarPlot * plot, gdouble az, gdouble el) { - GooCanvasItemModel *root; - GooCanvasPoints *prec; - gint idx; - gfloat x, y; - guint32 col; - if (plot == NULL) return; - root = goo_canvas_get_root_item_model(GOO_CANVAS(plot->canvas)); + plot->rotor_az = az; + plot->rotor_el = el; - if ((az < 0.0) || (el < 0.0)) - { - if (plot->rot1 != NULL) - { - /* the target object is visible; delete it */ - idx = goo_canvas_item_model_find_child(root, plot->rot1); - if (idx != -1) - goo_canvas_item_model_remove_child(root, idx); - - plot->rot1 = NULL; - } - if (plot->rot2 != NULL) - { - /* the target object is visible; delete it */ - idx = goo_canvas_item_model_find_child(root, plot->rot2); - if (idx != -1) - goo_canvas_item_model_remove_child(root, idx); - - plot->rot2 = NULL; - } - if (plot->rot3 != NULL) - { - /* the target object is visible; delete it */ - idx = goo_canvas_item_model_find_child(root, plot->rot3); - if (idx != -1) - goo_canvas_item_model_remove_child(root, idx); - - plot->rot3 = NULL; - } - if (plot->rot4 != NULL) - { - /* the target object is visible; delete it */ - idx = goo_canvas_item_model_find_child(root, plot->rot4); - if (idx != -1) - goo_canvas_item_model_remove_child(root, idx); - - plot->rot4 = NULL; - } - } - else - { - /* we need to either update or create the object */ - azel_to_xy(plot, az, el, &x, &y); - col = sat_cfg_get_int(SAT_CFG_INT_POLAR_SAT_COL); - - if (plot->rot1 != NULL) - { - /* the target object already exists; move it */ - prec = goo_canvas_points_new(2); - prec->coords[0] = x; - prec->coords[1] = y - 4; - prec->coords[2] = x; - prec->coords[3] = y - 14; - g_object_set(plot->rot1, "points", prec, NULL); - goo_canvas_points_unref(prec); - } - else - { - /* the target object does not exist; create it */ - plot->rot1 = goo_canvas_polyline_model_new_line(root, - x, y - 4, x, - y - 14, - "fill-color-rgba", - col, - "stroke-color-rgba", - col, "line-width", - 1.0, NULL); - } - if (plot->rot2 != NULL) - { - /* the target object already exists; move it */ - prec = goo_canvas_points_new(2); - prec->coords[0] = x + 4; - prec->coords[1] = y; - prec->coords[2] = x + 14; - prec->coords[3] = y; - g_object_set(plot->rot2, "points", prec, NULL); - goo_canvas_points_unref(prec); - } - else - { - /* the target object does not exist; create it */ - plot->rot2 = goo_canvas_polyline_model_new_line(root, - x + 4, y, x + 14, - y, - "fill-color-rgba", - col, - "stroke-color-rgba", - col, "line-width", - 1.0, NULL); - } - if (plot->rot3 != NULL) - { - /* the target object already exists; move it */ - prec = goo_canvas_points_new(2); - prec->coords[0] = x; - prec->coords[1] = y + 4; - prec->coords[2] = x; - prec->coords[3] = y + 14; - g_object_set(plot->rot3, "points", prec, NULL); - goo_canvas_points_unref(prec); - } - else - { - /* the target object does not exist; create it */ - plot->rot3 = goo_canvas_polyline_model_new_line(root, - x, y + 4, x, - y + 14, - "fill-color-rgba", - col, - "stroke-color-rgba", - col, "line-width", - 1.0, NULL); - } - if (plot->rot4 != NULL) - { - /* the target object already exists; move it */ - prec = goo_canvas_points_new(2); - prec->coords[0] = x - 4; - prec->coords[1] = y; - prec->coords[2] = x - 14; - prec->coords[3] = y; - g_object_set(plot->rot4, "points", prec, NULL); - goo_canvas_points_unref(prec); - } - else - { - /* the target object does not exist; create it */ - plot->rot4 = goo_canvas_polyline_model_new_line(root, - x - 4, y, x - 14, - y, - "fill-color-rgba", - col, - "stroke-color-rgba", - col, "line-width", - 1.0, NULL); - } - } + gtk_widget_queue_draw(plot->canvas); } /** diff --git a/src/gtk-polar-plot.h b/src/gtk-polar-plot.h index bea6f93dc..585d05375 100644 --- a/src/gtk-polar-plot.h +++ b/src/gtk-polar-plot.h @@ -28,7 +28,6 @@ #include #include #include -#include #include #include "gtk-sat-data.h" @@ -69,24 +68,43 @@ typedef enum { POLAR_PLOT_POLE_W = 3 } polar_plot_pole_t; +/** Time tick data for sky track */ +typedef struct { + gfloat x; /*!< X coordinate */ + gfloat y; /*!< Y coordinate */ + gchar text[6]; /*!< Time string */ +} polar_plot_tick_t; + struct _GtkPolarPlot { GtkBox box; - GtkWidget *canvas; /*!< The canvas widget */ + GtkWidget *canvas; /*!< The drawing area widget */ + + /* Colors */ + guint32 col_bgd; /*!< Background color */ + guint32 col_axis; /*!< Axis color */ + guint32 col_tick; /*!< Tick label color */ + guint32 col_info; /*!< Info text color */ + guint32 col_sat; /*!< Satellite color */ + guint32 col_track; /*!< Track color */ - GooCanvasItemModel *C00, *C30, *C60; /*!< 0, 30 and 60 deg elevation circles */ - GooCanvasItemModel *hl, *vl; /*!< horizontal and vertical lines */ - GooCanvasItemModel *N, *S, *E, *W; /*!< North, South, East and West labels */ - GooCanvasItemModel *locnam; /*!< Location name */ - GooCanvasItemModel *curs; /*!< cursor tracking text */ + /* Text elements */ + gchar *curs_text; /*!< Cursor tracking text */ pass_t *pass; - GooCanvasItemModel *bgd; /*!< Background */ - GooCanvasItemModel *track; /*!< Sky track. */ - GooCanvasItemModel *target; /*!< Target object marker */ - GooCanvasItemModel *ctrl; /*!< Position marker for the controller */ - GooCanvasItemModel *rot1, *rot2, *rot3, *rot4; /*!< Position marker for the rotor */ - GooCanvasItemModel *trtick[TRACK_TICK_NUM]; /*!< Time ticks along the sky track */ + + /* Track points */ + gdouble *track_points; /*!< Array of track coordinates */ + gint track_count; /*!< Number of track points */ + polar_plot_tick_t trtick[TRACK_TICK_NUM]; /*!< Time ticks along the sky track */ + + /* Target/controller/rotor positions */ + gdouble target_az; /*!< Target azimuth (-1 if hidden) */ + gdouble target_el; /*!< Target elevation (-1 if hidden) */ + gdouble ctrl_az; /*!< Controller azimuth (-1 if hidden) */ + gdouble ctrl_el; /*!< Controller elevation (-1 if hidden) */ + gdouble rotor_az; /*!< Rotor azimuth (-1 if hidden) */ + gdouble rotor_el; /*!< Rotor elevation (-1 if hidden) */ qth_t *qth; /*!< Pointer to current location. */ @@ -102,7 +120,7 @@ struct _GtkPolarPlot { gboolean cursinfo; /*!< Track the mouse cursor. */ gboolean extratick; /*!< Show extra ticks */ - GValue font; /*!< Default font */ + gchar *font; /*!< Default font name */ }; struct _GtkPolarPlotClass { diff --git a/src/gtk-polar-view-popup.c b/src/gtk-polar-view-popup.c index 444e448df..1910b66f2 100644 --- a/src/gtk-polar-view-popup.c +++ b/src/gtk-polar-view-popup.c @@ -27,7 +27,6 @@ #endif #include -#include #include #include "config-keys.h" diff --git a/src/gtk-polar-view.c b/src/gtk-polar-view.c index bbea9a2dc..62e55a0a9 100644 --- a/src/gtk-polar-view.c +++ b/src/gtk-polar-view.c @@ -19,7 +19,7 @@ */ #include #include -#include +#include #ifdef HAVE_CONFIG_H #include @@ -46,9 +46,17 @@ #define POLV_LINE_EXTRA 5 static void update_sat(gpointer key, gpointer value, gpointer data); -static void update_track(gpointer key, gpointer value, gpointer data); -static GtkVBoxClass *parent_class = NULL; +static GtkBoxClass *parent_class = NULL; + +/** Convert rgba color to cairo-friendly format */ +static void rgba_to_cairo(guint32 rgba, gdouble *r, gdouble *g, gdouble *b, gdouble *a) +{ + *r = ((rgba >> 24) & 0xFF) / 255.0; + *g = ((rgba >> 16) & 0xFF) / 255.0; + *b = ((rgba >> 8) & 0xFF) / 255.0; + *a = (rgba & 0xFF) / 255.0; +} static void gtk_polar_view_store_showtracks(GtkPolarView * pv) { @@ -62,7 +70,6 @@ static void gtk_polar_view_store_showtracks(GtkPolarView * pv) MOD_CFG_POLAR_HIDETRACKS); } -/** Load the satellites that we should not highlight coverage */ static void gtk_polar_view_load_showtracks(GtkPolarView * pv) { mod_cfg_get_integer_list_boolean(pv->cfgdata, @@ -76,9 +83,55 @@ static void gtk_polar_view_load_showtracks(GtkPolarView * pv) pv->showtracks_on); } +static void free_sat_obj(gpointer data) +{ + sat_obj_t *obj = SAT_OBJ(data); + if (obj) + { + g_free(obj->nickname); + g_free(obj->tooltip); + g_slist_free_full(obj->track_points, g_free); + if (obj->pass) + free_pass(obj->pass); + g_free(obj); + } +} + static void gtk_polar_view_destroy(GtkWidget * widget) { - gtk_polar_view_store_showtracks(GTK_POLAR_VIEW(widget)); + GtkPolarView *polv = GTK_POLAR_VIEW(widget); + + gtk_polar_view_store_showtracks(polv); + + g_free(polv->curs_text); + polv->curs_text = NULL; + + g_free(polv->next_text); + polv->next_text = NULL; + + g_free(polv->sel_text); + polv->sel_text = NULL; + + g_free(polv->font); + polv->font = NULL; + + if (polv->obj) + { + g_hash_table_destroy(polv->obj); + polv->obj = NULL; + } + + if (polv->showtracks_on) + { + g_hash_table_destroy(polv->showtracks_on); + polv->showtracks_on = NULL; + } + + if (polv->showtracks_off) + { + g_hash_table_destroy(polv->showtracks_off); + polv->showtracks_off = NULL; + } (*GTK_WIDGET_CLASS(parent_class)->destroy) (widget); } @@ -119,6 +172,10 @@ static void gtk_polar_view_init(GtkPolarView * polview, polview->cursinfo = FALSE; polview->extratick = FALSE; polview->resize = FALSE; + polview->curs_text = NULL; + polview->next_text = NULL; + polview->sel_text = NULL; + polview->font = NULL; } GType gtk_polar_view_get_type() @@ -148,216 +205,6 @@ GType gtk_polar_view_get_type() return gtk_polar_view_type; } -/** - * Manage new size allocation. - * - * This function is called when the canvas receives a new size allocation, - * e.g. when the container is re-sized. The function re-calculates the graph - * dimensions based on the new canvas size. - */ -static void size_allocate_cb(GtkWidget * widget, GtkAllocation * allocation, - gpointer data) -{ - (void)widget; - (void)allocation; - (void)data; - GTK_POLAR_VIEW(data)->resize = TRUE; -} - -/** - * Manage canvas realise signals. - * - * This function is used to re-initialise the graph dimensions when - * the graph is realized, i.e. displayed for the first time. This is - * necessary in order to compensate for missing "re-allocate" signals for - * graphs that have not yet been realised, e.g. when opening several module - */ -static void on_canvas_realized(GtkWidget * canvas, gpointer data) -{ - GtkAllocation aloc; - - gtk_widget_get_allocation(canvas, &aloc); - size_allocate_cb(canvas, &aloc, data); -} - -/** - * Manage button press events - * - * This function is called when a mouse button is pressed on a satellite object. - * If the pressed button is #3 (right button) the satellite popup menu will be - * created and executed. - */ -static gboolean on_button_press(GooCanvasItem * item, - GooCanvasItem * target, - GdkEventButton * event, gpointer data) -{ - GooCanvasItemModel *model = goo_canvas_item_get_model(item); - GtkPolarView *polv = GTK_POLAR_VIEW(data); - gint catnum = - GPOINTER_TO_INT(g_object_get_data(G_OBJECT(model), "catnum")); - gint *catpoint = NULL; - sat_t *sat = NULL; - - (void)target; - - switch (event->button) - { - /* double-left-click */ - case 1: - if (event->type == GDK_2BUTTON_PRESS) - { - catpoint = g_try_new0(gint, 1); - *catpoint = catnum; - - sat = SAT(g_hash_table_lookup(polv->sats, catpoint)); - if (sat != NULL) - { - show_sat_info(sat, gtk_widget_get_toplevel(GTK_WIDGET(data))); - } - else - { - /* double-clicked on map */ - } - } - - g_free(catpoint); - break; - /* pop-up menu */ - case 3: - catpoint = g_try_new0(gint, 1); - *catpoint = catnum; - - sat = SAT(g_hash_table_lookup(polv->sats, catpoint)); - - if (sat != NULL) - { - gtk_polar_view_popup_exec(sat, polv->qth, - polv, event, - gtk_widget_get_toplevel(GTK_WIDGET - (polv))); - } - else - { - sat_log_log(SAT_LOG_LEVEL_ERROR, - _ - ("%s:%d: Could not find satellite (%d) in hash table"), - __FILE__, __LINE__, catnum); - } - g_free(catpoint); - break; - default: - break; - } - - return TRUE; -} - -/** - * Clear selection. - * - * This function is used to clear the old selection when a new satellite - * is selected. - */ -static void clear_selection(gpointer key, gpointer val, gpointer data) -{ - gint *old = key; - gint *new = data; - sat_obj_t *obj = SAT_OBJ(val); - guint32 col; - - if ((*old != *new) && (obj->selected)) - { - obj->selected = FALSE; - - col = sat_cfg_get_int(SAT_CFG_INT_POLAR_SAT_COL); - - g_object_set(obj->marker, - "fill-color-rgba", col, "stroke-color-rgba", col, NULL); - g_object_set(obj->label, - "fill-color-rgba", col, "stroke-color-rgba", col, NULL); - } -} - -/** - * Manage button release events. - * - * This function is called when the mouse button is released above - * a satellite object. It will act as a button click and if the released - * button is the left one, the click will correspond to selecting or - * deselecting a satellite - */ -static gboolean on_button_release(GooCanvasItem * item, - GooCanvasItem * target, - GdkEventButton * event, gpointer data) -{ - GooCanvasItemModel *model = goo_canvas_item_get_model(item); - GtkPolarView *polv = GTK_POLAR_VIEW(data); - gint catnum = - GPOINTER_TO_INT(g_object_get_data(G_OBJECT(model), "catnum")); - gint *catpoint = NULL; - sat_obj_t *obj = NULL; - guint32 color; - - (void)target; - - catpoint = g_try_new0(gint, 1); - *catpoint = catnum; - - switch (event->button) - { - /* Select / de-select satellite */ - case 1: - obj = SAT_OBJ(g_hash_table_lookup(polv->obj, catpoint)); - if (obj == NULL) - { - sat_log_log(SAT_LOG_LEVEL_ERROR, - _ - ("%s:%d: Can not find clicked object (%d) in hash table"), - __FILE__, __LINE__, catnum); - } - else - { - obj->selected = !obj->selected; - - if (obj->selected) - { - color = mod_cfg_get_int(polv->cfgdata, - MOD_CFG_POLAR_SECTION, - MOD_CFG_POLAR_SAT_SEL_COL, - SAT_CFG_INT_POLAR_SAT_SEL_COL); - } - else - { - color = mod_cfg_get_int(polv->cfgdata, - MOD_CFG_POLAR_SECTION, - MOD_CFG_POLAR_SAT_COL, - SAT_CFG_INT_POLAR_SAT_COL); - *catpoint = 0; - - g_object_set(polv->sel, "text", "", NULL); - } - - g_object_set(obj->marker, - "fill-color-rgba", color, - "stroke-color-rgba", color, NULL); - g_object_set(obj->label, - "fill-color-rgba", color, - "stroke-color-rgba", color, NULL); - - /* clear other selections */ - g_hash_table_foreach(polv->obj, clear_selection, catpoint); - } - break; - - default: - break; - } - - g_free(catpoint); - - return TRUE; -} - /** Convert Az/El to canvas based XY coordinates. */ static void azel_to_xy(GtkPolarView * p, gdouble az, gdouble el, gfloat * x, gfloat * y) @@ -454,326 +301,473 @@ static void xy_to_azel(GtkPolarView * p, gfloat x, gfloat y, gfloat * az, } } -/** Manage mouse motion events. */ -static gboolean on_motion_notify(GooCanvasItem * item, - GooCanvasItem * target, - GdkEventMotion * event, gpointer data) +/** + * Transform pole coordinates. + */ +static void +correct_pole_coor(GtkPolarView * polv, polar_view_pole_t pole, + gfloat * x, gfloat * y, gboolean *anchor_south, gboolean *anchor_east) { - GtkPolarView *polv = GTK_POLAR_VIEW(data); - gfloat az, el; - gchar *text; + *anchor_south = FALSE; + *anchor_east = FALSE; - (void)item; - (void)target; - - if (polv->cursinfo) + switch (pole) { + case POLAR_VIEW_POLE_N: + if ((polv->swap == POLAR_VIEW_SENW) || (polv->swap == POLAR_VIEW_SWNE)) + { + *y = *y + POLV_LINE_EXTRA; + } + else + { + *y = *y - POLV_LINE_EXTRA; + *anchor_south = TRUE; + } + break; - xy_to_azel(polv, event->x, event->y, &az, &el); + case POLAR_VIEW_POLE_E: + if ((polv->swap == POLAR_VIEW_NWSE) || (polv->swap == POLAR_VIEW_SWNE)) + { + *x = *x - POLV_LINE_EXTRA; + *anchor_east = TRUE; + } + else + { + *x = *x + POLV_LINE_EXTRA; + } + break; - if (el > 0.0) + case POLAR_VIEW_POLE_S: + if ((polv->swap == POLAR_VIEW_SENW) || (polv->swap == POLAR_VIEW_SWNE)) { - /* cursor track */ - text = g_strdup_printf("AZ %.0f\302\260\nEL %.0f\302\260", az, el); - g_object_set(polv->curs, "text", text, NULL); - g_free(text); + *y = *y - POLV_LINE_EXTRA; + *anchor_south = TRUE; } else { - g_object_set(polv->curs, "text", "", NULL); + *y = *y + POLV_LINE_EXTRA; } - } + break; - return TRUE; + case POLAR_VIEW_POLE_W: + if ((polv->swap == POLAR_VIEW_NWSE) || (polv->swap == POLAR_VIEW_SWNE)) + { + *x = *x + POLV_LINE_EXTRA; + } + else + { + *x = *x - POLV_LINE_EXTRA; + *anchor_east = TRUE; + } + break; + + default: + break; + } } -/** - * Finish canvas item setup. - * - * @param canvas - * @param item - * @param model - * @param data Pointer to the GtkPolarView object. - * - * This function is called when a canvas item is created. Its purpose is to connect - * the corresponding signals to the created items. - */ -static void on_item_created(GooCanvas * canvas, - GooCanvasItem * item, - GooCanvasItemModel * model, gpointer data) +static gboolean on_draw(GtkWidget *widget, cairo_t *cr, gpointer data) { - (void)canvas; + GtkPolarView *polv = GTK_POLAR_VIEW(data); + gdouble r, g, b, a; + gfloat x, y; + gboolean anchor_south, anchor_east; + PangoLayout *layout; + PangoFontDescription *font_desc; + gint tw, th; + GHashTableIter iter; + gpointer key, value; + sat_obj_t *obj; + GSList *node; + gdouble *point; + guint i; + + (void)widget; + + /* Background */ + rgba_to_cairo(polv->col_bgd, &r, &g, &b, &a); + cairo_set_source_rgba(cr, r, g, b, a); + cairo_paint(cr); + + /* Set up font */ + layout = pango_cairo_create_layout(cr); + font_desc = pango_font_description_from_string(polv->font ? polv->font : "Sans 9"); + pango_layout_set_font_description(layout, font_desc); + + /* Axis color for circles and lines */ + rgba_to_cairo(polv->col_axis, &r, &g, &b, &a); + cairo_set_source_rgba(cr, r, g, b, a); + cairo_set_line_width(cr, 1.0); + + /* 0 degree circle */ + cairo_arc(cr, polv->cx, polv->cy, polv->r, 0, 2 * M_PI); + cairo_stroke(cr); + + /* 30 degree circle */ + cairo_arc(cr, polv->cx, polv->cy, 0.6667 * polv->r, 0, 2 * M_PI); + cairo_stroke(cr); + + /* 60 degree circle */ + cairo_arc(cr, polv->cx, polv->cy, 0.333 * polv->r, 0, 2 * M_PI); + cairo_stroke(cr); + + /* Horizontal line */ + cairo_move_to(cr, polv->cx - polv->r - POLV_LINE_EXTRA, polv->cy); + cairo_line_to(cr, polv->cx + polv->r + POLV_LINE_EXTRA, polv->cy); + cairo_stroke(cr); + + /* Vertical line */ + cairo_move_to(cr, polv->cx, polv->cy - polv->r - POLV_LINE_EXTRA); + cairo_line_to(cr, polv->cx, polv->cy + polv->r + POLV_LINE_EXTRA); + cairo_stroke(cr); + + /* N/S/E/W labels */ + rgba_to_cairo(polv->col_tick, &r, &g, &b, &a); + cairo_set_source_rgba(cr, r, g, b, a); + + /* N label */ + azel_to_xy(polv, 0.0, 0.0, &x, &y); + correct_pole_coor(polv, POLAR_VIEW_POLE_N, &x, &y, &anchor_south, &anchor_east); + pango_layout_set_text(layout, _("N"), -1); + pango_layout_get_pixel_size(layout, &tw, &th); + cairo_move_to(cr, x - tw / 2, anchor_south ? y : y - th); + pango_cairo_show_layout(cr, layout); + + /* E label */ + azel_to_xy(polv, 90.0, 0.0, &x, &y); + correct_pole_coor(polv, POLAR_VIEW_POLE_E, &x, &y, &anchor_south, &anchor_east); + pango_layout_set_text(layout, _("E"), -1); + pango_layout_get_pixel_size(layout, &tw, &th); + cairo_move_to(cr, anchor_east ? x - tw : x, y - th / 2); + pango_cairo_show_layout(cr, layout); + + /* S label */ + azel_to_xy(polv, 180.0, 0.0, &x, &y); + correct_pole_coor(polv, POLAR_VIEW_POLE_S, &x, &y, &anchor_south, &anchor_east); + pango_layout_set_text(layout, _("S"), -1); + pango_layout_get_pixel_size(layout, &tw, &th); + cairo_move_to(cr, x - tw / 2, anchor_south ? y : y - th); + pango_cairo_show_layout(cr, layout); - if (!goo_canvas_item_model_get_parent(model)) + /* W label */ + azel_to_xy(polv, 270.0, 0.0, &x, &y); + correct_pole_coor(polv, POLAR_VIEW_POLE_W, &x, &y, &anchor_south, &anchor_east); + pango_layout_set_text(layout, _("W"), -1); + pango_layout_get_pixel_size(layout, &tw, &th); + cairo_move_to(cr, anchor_east ? x - tw : x, y - th / 2); + pango_cairo_show_layout(cr, layout); + + /* Location name (if enabled) */ + if (polv->qthinfo && polv->qth) { - /* root item / canvas */ - g_signal_connect(item, "motion_notify_event", - (GCallback) on_motion_notify, data); + rgba_to_cairo(polv->col_info, &r, &g, &b, &a); + cairo_set_source_rgba(cr, r, g, b, a); + pango_layout_set_text(layout, polv->qth->name, -1); + pango_layout_get_pixel_size(layout, &tw, &th); + cairo_move_to(cr, polv->cx - polv->r - 2 * POLV_LINE_EXTRA, + polv->cy - polv->r - POLV_LINE_EXTRA - th); + pango_cairo_show_layout(cr, layout); } - else if (!g_object_get_data(G_OBJECT(item), "skip-signal-connection")) + /* Cursor tracking text */ + if (polv->cursinfo && polv->curs_text) { - g_signal_connect(item, "button_press_event", - (GCallback) on_button_press, data); - g_signal_connect(item, "button_release_event", - (GCallback) on_button_release, data); + rgba_to_cairo(polv->col_info, &r, &g, &b, &a); + cairo_set_source_rgba(cr, r, g, b, a); + pango_layout_set_text(layout, polv->curs_text, -1); + cairo_move_to(cr, polv->cx - polv->r - 2 * POLV_LINE_EXTRA, + polv->cy + polv->r + POLV_LINE_EXTRA); + pango_cairo_show_layout(cr, layout); } -} -/** - * Transform pole coordinates. - * - * This function transforms the pols coordinates (x,y) taking into account - * the orientation of the polar plot. - */ -static void -correct_pole_coor(GtkPolarView * polv, - polar_view_pole_t pole, - gfloat * x, gfloat * y, GooCanvasAnchorType * anch) -{ + /* Next event text */ + if (polv->eventinfo && polv->next_text) + { + rgba_to_cairo(polv->col_info, &r, &g, &b, &a); + cairo_set_source_rgba(cr, r, g, b, a); + pango_layout_set_alignment(layout, PANGO_ALIGN_RIGHT); + pango_layout_set_text(layout, polv->next_text, -1); + pango_layout_get_pixel_size(layout, &tw, &th); + cairo_move_to(cr, polv->cx + polv->r + 2 * POLV_LINE_EXTRA - tw, + polv->cy - polv->r - POLV_LINE_EXTRA - th); + pango_cairo_show_layout(cr, layout); + pango_layout_set_alignment(layout, PANGO_ALIGN_LEFT); + } - switch (pole) + /* Selected satellite text */ + if (polv->sel_text) { - case POLAR_VIEW_POLE_N: - if ((polv->swap == POLAR_VIEW_SENW) || (polv->swap == POLAR_VIEW_SWNE)) - { - /* North and South are swapped */ - *y = *y + POLV_LINE_EXTRA; - *anch = GOO_CANVAS_ANCHOR_NORTH; - } - else + rgba_to_cairo(polv->col_info, &r, &g, &b, &a); + cairo_set_source_rgba(cr, r, g, b, a); + pango_layout_set_alignment(layout, PANGO_ALIGN_RIGHT); + pango_layout_set_text(layout, polv->sel_text, -1); + pango_layout_get_pixel_size(layout, &tw, &th); + cairo_move_to(cr, polv->cx + polv->r + 2 * POLV_LINE_EXTRA - tw, + polv->cy + polv->r + POLV_LINE_EXTRA); + pango_cairo_show_layout(cr, layout); + pango_layout_set_alignment(layout, PANGO_ALIGN_LEFT); + } + + /* Draw satellite objects */ + if (polv->obj) + { + g_hash_table_iter_init(&iter, polv->obj); + while (g_hash_table_iter_next(&iter, &key, &value)) { - *y = *y - POLV_LINE_EXTRA; - *anch = GOO_CANVAS_ANCHOR_SOUTH; + obj = SAT_OBJ(value); + + /* Draw track if enabled */ + if (obj->showtrack && obj->track_points) + { + rgba_to_cairo(polv->col_track, &r, &g, &b, &a); + cairo_set_source_rgba(cr, r, g, b, a); + cairo_set_line_width(cr, 1.0); + + node = obj->track_points; + if (node) + { + point = (gdouble *)node->data; + cairo_move_to(cr, point[0], point[1]); + node = node->next; + + while (node) + { + point = (gdouble *)node->data; + cairo_line_to(cr, point[0], point[1]); + node = node->next; + } + cairo_stroke(cr); + } + + /* Draw time ticks */ + for (i = 0; i < TRACK_TICK_NUM; i++) + { + if (obj->trtick[i].text[0] != '\0') + { + x = obj->trtick[i].x; + y = obj->trtick[i].y; + pango_layout_set_text(layout, obj->trtick[i].text, -1); + pango_layout_get_pixel_size(layout, &tw, &th); + + if (x > polv->cx) + cairo_move_to(cr, x - tw - 5, y - th / 2); + else + cairo_move_to(cr, x + 5, y - th / 2); + + pango_cairo_show_layout(cr, layout); + } + } + } + + /* Draw satellite marker */ + if (polv->satmarker) + { + if (obj->selected) + rgba_to_cairo(polv->col_sat_sel, &r, &g, &b, &a); + else + rgba_to_cairo(polv->col_sat, &r, &g, &b, &a); + + cairo_set_source_rgba(cr, r, g, b, a); + cairo_rectangle(cr, obj->x - MARKER_SIZE_HALF, obj->y - MARKER_SIZE_HALF, + 2 * MARKER_SIZE_HALF, 2 * MARKER_SIZE_HALF); + cairo_fill(cr); + } + + /* Draw satellite name */ + if (polv->satname && obj->nickname) + { + if (obj->selected) + rgba_to_cairo(polv->col_sat_sel, &r, &g, &b, &a); + else + rgba_to_cairo(polv->col_sat, &r, &g, &b, &a); + + cairo_set_source_rgba(cr, r, g, b, a); + pango_layout_set_text(layout, obj->nickname, -1); + pango_layout_get_pixel_size(layout, &tw, &th); + cairo_move_to(cr, obj->x - tw / 2, obj->y + 2); + pango_cairo_show_layout(cr, layout); + } } + } - break; + pango_font_description_free(font_desc); + g_object_unref(layout); - case POLAR_VIEW_POLE_E: - if ((polv->swap == POLAR_VIEW_NWSE) || (polv->swap == POLAR_VIEW_SWNE)) + return FALSE; +} + +static sat_obj_t *find_sat_at_pos(GtkPolarView *polv, gfloat mx, gfloat my) +{ + GHashTableIter iter; + gpointer key, value; + sat_obj_t *obj; + gfloat dx, dy; + const gfloat hit_radius = 10.0; + + if (polv->obj == NULL) + return NULL; + + g_hash_table_iter_init(&iter, polv->obj); + while (g_hash_table_iter_next(&iter, &key, &value)) + { + obj = SAT_OBJ(value); + dx = mx - obj->x; + dy = my - obj->y; + if (dx * dx + dy * dy < hit_radius * hit_radius) + return obj; + } + return NULL; +} + +static gboolean on_button_press(GtkWidget *widget, GdkEventButton *event, gpointer data) +{ + GtkPolarView *polv = GTK_POLAR_VIEW(data); + sat_obj_t *obj; + sat_t *sat = NULL; + gint *catpoint = NULL; + + (void)widget; + + obj = find_sat_at_pos(polv, event->x, event->y); + + if (obj == NULL) + return FALSE; + + switch (event->button) + { + case 1: + if (event->type == GDK_2BUTTON_PRESS) { - /* East and West are swapped */ - *x = *x - POLV_LINE_EXTRA; - *anch = GOO_CANVAS_ANCHOR_EAST; + /* Double-click: show satellite info */ + catpoint = g_try_new0(gint, 1); + *catpoint = obj->catnum; + sat = SAT(g_hash_table_lookup(polv->sats, catpoint)); + if (sat != NULL) + { + show_sat_info(sat, gtk_widget_get_toplevel(GTK_WIDGET(polv))); + } + g_free(catpoint); } - else + break; + + case 3: + /* Right-click: popup menu */ + catpoint = g_try_new0(gint, 1); + *catpoint = obj->catnum; + sat = SAT(g_hash_table_lookup(polv->sats, catpoint)); + if (sat != NULL) { - *x = *x + POLV_LINE_EXTRA; - *anch = GOO_CANVAS_ANCHOR_WEST; + gtk_polar_view_popup_exec(sat, polv->qth, polv, event, + gtk_widget_get_toplevel(GTK_WIDGET(polv))); } + g_free(catpoint); break; - case POLAR_VIEW_POLE_S: - if ((polv->swap == POLAR_VIEW_SENW) || (polv->swap == POLAR_VIEW_SWNE)) - { - /* North and South are swapped */ - *y = *y - POLV_LINE_EXTRA; - *anch = GOO_CANVAS_ANCHOR_SOUTH; - } - else - { - *y = *y + POLV_LINE_EXTRA; - *anch = GOO_CANVAS_ANCHOR_NORTH; - } - break; + default: + break; + } + + return TRUE; +} + +static void clear_selection(gpointer key, gpointer val, gpointer data) +{ + gint *old = key; + gint *new = data; + sat_obj_t *obj = SAT_OBJ(val); + + if ((*old != *new) && (obj->selected)) + { + obj->selected = FALSE; + } +} + +static gboolean on_button_release(GtkWidget *widget, GdkEventButton *event, gpointer data) +{ + GtkPolarView *polv = GTK_POLAR_VIEW(data); + sat_obj_t *obj; + gint *catpoint; + + (void)widget; + + if (event->button != 1) + return FALSE; + + obj = find_sat_at_pos(polv, event->x, event->y); + + if (obj == NULL) + return FALSE; + + obj->selected = !obj->selected; + + catpoint = g_try_new0(gint, 1); + *catpoint = obj->catnum; + + if (!obj->selected) + { + g_free(polv->sel_text); + polv->sel_text = NULL; + *catpoint = 0; + } + + /* clear other selections */ + g_hash_table_foreach(polv->obj, clear_selection, catpoint); + + g_free(catpoint); + + gtk_widget_queue_draw(polv->canvas); + + return TRUE; +} + +static gboolean on_motion_notify(GtkWidget *widget, GdkEventMotion *event, gpointer data) +{ + GtkPolarView *polv = GTK_POLAR_VIEW(data); + gfloat az, el; + + (void)widget; - case POLAR_VIEW_POLE_W: - if ((polv->swap == POLAR_VIEW_NWSE) || (polv->swap == POLAR_VIEW_SWNE)) + if (polv->cursinfo) + { + xy_to_azel(polv, event->x, event->y, &az, &el); + + if (el > 0.0) { - /* East and West are swapped */ - *x = *x + POLV_LINE_EXTRA; - *anch = GOO_CANVAS_ANCHOR_WEST; + g_free(polv->curs_text); + polv->curs_text = g_strdup_printf("AZ %.0f\302\260\nEL %.0f\302\260", az, el); } else { - *x = *x - POLV_LINE_EXTRA; - *anch = GOO_CANVAS_ANCHOR_EAST; + g_free(polv->curs_text); + polv->curs_text = NULL; } - break; - default: - /* FIXME: bug */ - break; + gtk_widget_queue_draw(polv->canvas); } + + return TRUE; } -static GooCanvasItemModel *create_canvas_model(GtkPolarView * polv) +static void size_allocate_cb(GtkWidget * widget, GtkAllocation * allocation, + gpointer data) { - GooCanvasItemModel *root; - gfloat x, y; - guint32 col; - GooCanvasAnchorType anch = GOO_CANVAS_ANCHOR_CENTER; - - root = goo_canvas_group_model_new(NULL, NULL); - - /* graph dimensions */ - polv->size = POLV_DEFAULT_SIZE; - polv->r = (polv->size / 2) - POLV_DEFAULT_MARGIN; - polv->cx = POLV_DEFAULT_SIZE / 2; - polv->cy = POLV_DEFAULT_SIZE / 2; - - /* default font */ - g_object_get_property(G_OBJECT(gtk_settings_get_default()), "gtk-font-name", &polv->font); - - col = mod_cfg_get_int(polv->cfgdata, - MOD_CFG_POLAR_SECTION, - MOD_CFG_POLAR_BGD_COL, SAT_CFG_INT_POLAR_BGD_COL); - - polv->bgd = goo_canvas_rect_model_new(root, 0.0, 0.0, - POLV_DEFAULT_SIZE, POLV_DEFAULT_SIZE, - "fill-color-rgba", col, - "stroke-color-rgba", 0xFFFFFFFF, - NULL); - - col = mod_cfg_get_int(polv->cfgdata, - MOD_CFG_POLAR_SECTION, - MOD_CFG_POLAR_AXIS_COL, SAT_CFG_INT_POLAR_AXIS_COL); - - /* Add elevation circles at 0, 30 and 60 deg */ - polv->C00 = goo_canvas_ellipse_model_new(root, - polv->cx, polv->cy, - polv->r, polv->r, - "line-width", 1.0, - "stroke-color-rgba", col, NULL); - - polv->C30 = goo_canvas_ellipse_model_new(root, - polv->cx, polv->cy, - 0.6667 * polv->r, - 0.6667 * polv->r, "line-width", - 1.0, "stroke-color-rgba", col, - NULL); - - polv->C60 = goo_canvas_ellipse_model_new(root, - polv->cx, polv->cy, - 0.333 * polv->r, 0.3333 * polv->r, - "line-width", 1.0, - "stroke-color-rgba", col, NULL); - - /* add horixontal and vertical guidance lines */ - polv->hl = goo_canvas_polyline_model_new_line(root, - polv->cx - polv->r - - POLV_LINE_EXTRA, polv->cy, - polv->cx + polv->r + - POLV_LINE_EXTRA, polv->cy, - "stroke-color-rgba", col, - "line-width", 1.0, NULL); - - polv->vl = goo_canvas_polyline_model_new_line(root, - polv->cx, - polv->cy - polv->r - - POLV_LINE_EXTRA, polv->cx, - polv->cy + polv->r + - POLV_LINE_EXTRA, - "stroke-color-rgba", col, - "line-width", 1.0, NULL); - - /* N, S, E and W labels. */ - col = mod_cfg_get_int(polv->cfgdata, - MOD_CFG_POLAR_SECTION, - MOD_CFG_POLAR_TICK_COL, SAT_CFG_INT_POLAR_TICK_COL); - azel_to_xy(polv, 0.0, 0.0, &x, &y); - correct_pole_coor(polv, POLAR_VIEW_POLE_N, &x, &y, &anch); - polv->N = goo_canvas_text_model_new(root, _("N"), - x, - y, - -1, - anch, - "font", g_value_get_string(&polv->font), - "fill-color-rgba", col, NULL); - - azel_to_xy(polv, 180.0, 0.0, &x, &y); - correct_pole_coor(polv, POLAR_VIEW_POLE_S, &x, &y, &anch); - polv->S = goo_canvas_text_model_new(root, _("S"), - x, - y, - -1, - anch, - "font", g_value_get_string(&polv->font), - "fill-color-rgba", col, NULL); + (void)widget; + (void)allocation; + GTK_POLAR_VIEW(data)->resize = TRUE; +} - azel_to_xy(polv, 90.0, 0.0, &x, &y); - correct_pole_coor(polv, POLAR_VIEW_POLE_E, &x, &y, &anch); - polv->E = goo_canvas_text_model_new(root, _("E"), - x, - y, - -1, - anch, - "font", g_value_get_string(&polv->font), - "fill-color-rgba", col, NULL); +static void on_canvas_realized(GtkWidget * canvas, gpointer data) +{ + GtkAllocation aloc; - azel_to_xy(polv, 270.0, 0.0, &x, &y); - correct_pole_coor(polv, POLAR_VIEW_POLE_W, &x, &y, &anch); - polv->W = goo_canvas_text_model_new(root, _("W"), - x, - y, - -1, - anch, - "font", g_value_get_string(&polv->font), - "fill-color-rgba", col, NULL); - - /* cursor text */ - col = mod_cfg_get_int(polv->cfgdata, - MOD_CFG_POLAR_SECTION, - MOD_CFG_POLAR_INFO_COL, SAT_CFG_INT_POLAR_INFO_COL); - polv->curs = goo_canvas_text_model_new(root, "", - polv->cx - polv->r - - 2 * POLV_LINE_EXTRA, - polv->cy + polv->r + - POLV_LINE_EXTRA, -1, - GOO_CANVAS_ANCHOR_W, - "font", g_value_get_string(&polv->font), - "fill-color-rgba", col, - NULL); - - /* location info */ - polv->locnam = goo_canvas_text_model_new(root, polv->qth->name, - polv->cx - polv->r - - 2 * POLV_LINE_EXTRA, - polv->cy - polv->r - - POLV_LINE_EXTRA, -1, - GOO_CANVAS_ANCHOR_SW, - "font", g_value_get_string(&polv->font), - "fill-color-rgba", col, - NULL); - - /* next event */ - polv->next = goo_canvas_text_model_new(root, "", - polv->cx + polv->r + - 2 * POLV_LINE_EXTRA, - polv->cy - polv->r - - POLV_LINE_EXTRA, -1, - GOO_CANVAS_ANCHOR_E, - "font", g_value_get_string(&polv->font), - "fill-color-rgba", col, - "alignment", PANGO_ALIGN_RIGHT, - NULL); - - /* selected satellite text */ - polv->sel = goo_canvas_text_model_new(root, "", - polv->cx + polv->r + - 2 * POLV_LINE_EXTRA, - polv->cy + polv->r + POLV_LINE_EXTRA, - -1, GOO_CANVAS_ANCHOR_E, - "font", g_value_get_string(&polv->font), - "fill-color-rgba", col, - "alignment", PANGO_ALIGN_RIGHT, - NULL); - - return root; + gtk_widget_get_allocation(canvas, &aloc); + size_allocate_cb(canvas, &aloc, data); } -/** - * Create a new GtkPolarView widget. - * - * @param cfgdata The configuration data of the parent module. - * @param sats Pointer to the hash table containing the associated satellites. - * @param qth Pointer to the ground station data. - */ -GtkWidget *gtk_polar_view_new(GKeyFile * cfgdata, GHashTable * sats, - qth_t * qth) +GtkWidget *gtk_polar_view_new(GKeyFile * cfgdata, GHashTable * sats, qth_t * qth) { - GtkPolarView *polv; - GooCanvasItemModel *root; + GtkPolarView *polv; + GValue font_value = G_VALUE_INIT; polv = GTK_POLAR_VIEW(g_object_new(GTK_TYPE_POLAR_VIEW, NULL)); @@ -781,11 +775,9 @@ GtkWidget *gtk_polar_view_new(GKeyFile * cfgdata, GHashTable * sats, polv->sats = sats; polv->qth = qth; - polv->obj = g_hash_table_new_full(g_int_hash, g_int_equal, g_free, NULL); - polv->showtracks_on = g_hash_table_new_full(g_int_hash, g_int_equal, - g_free, NULL); - polv->showtracks_off = g_hash_table_new_full(g_int_hash, g_int_equal, - g_free, NULL); + polv->obj = g_hash_table_new_full(g_int_hash, g_int_equal, g_free, free_sat_obj); + polv->showtracks_on = g_hash_table_new_full(g_int_hash, g_int_equal, g_free, NULL); + polv->showtracks_off = g_hash_table_new_full(g_int_hash, g_int_equal, g_free, NULL); /* get settings */ polv->refresh = mod_cfg_get_int(cfgdata, MOD_CFG_POLAR_SECTION, @@ -824,31 +816,53 @@ GtkWidget *gtk_polar_view_new(GKeyFile * cfgdata, GHashTable * sats, polv->extratick = mod_cfg_get_bool(cfgdata, MOD_CFG_POLAR_SECTION, MOD_CFG_POLAR_SHOW_EXTRA_AZ_TICKS, SAT_CFG_BOOL_POL_SHOW_EXTRA_AZ_TICKS); + gtk_polar_view_load_showtracks(polv); - /* create the canvas */ - polv->canvas = goo_canvas_new(); - g_object_set(G_OBJECT(polv->canvas), "has-tooltip", TRUE, NULL); - gtk_widget_set_size_request(polv->canvas, POLV_DEFAULT_SIZE, POLV_DEFAULT_SIZE); - goo_canvas_set_bounds(GOO_CANVAS(polv->canvas), 0, 0, - POLV_DEFAULT_SIZE, POLV_DEFAULT_SIZE); - - /* connect size-request signal */ - g_signal_connect(polv->canvas, "size-allocate", - G_CALLBACK(size_allocate_cb), polv); - g_signal_connect(polv->canvas, "item_created", - (GCallback) on_item_created, polv); - g_signal_connect_after(polv->canvas, "realize", - (GCallback) on_canvas_realized, polv); - gtk_widget_show(polv->canvas); + /* get colors */ + polv->col_bgd = mod_cfg_get_int(polv->cfgdata, MOD_CFG_POLAR_SECTION, + MOD_CFG_POLAR_BGD_COL, SAT_CFG_INT_POLAR_BGD_COL); + polv->col_axis = mod_cfg_get_int(polv->cfgdata, MOD_CFG_POLAR_SECTION, + MOD_CFG_POLAR_AXIS_COL, SAT_CFG_INT_POLAR_AXIS_COL); + polv->col_tick = mod_cfg_get_int(polv->cfgdata, MOD_CFG_POLAR_SECTION, + MOD_CFG_POLAR_TICK_COL, SAT_CFG_INT_POLAR_TICK_COL); + polv->col_info = mod_cfg_get_int(polv->cfgdata, MOD_CFG_POLAR_SECTION, + MOD_CFG_POLAR_INFO_COL, SAT_CFG_INT_POLAR_INFO_COL); + polv->col_sat = mod_cfg_get_int(polv->cfgdata, MOD_CFG_POLAR_SECTION, + MOD_CFG_POLAR_SAT_COL, SAT_CFG_INT_POLAR_SAT_COL); + polv->col_sat_sel = mod_cfg_get_int(polv->cfgdata, MOD_CFG_POLAR_SECTION, + MOD_CFG_POLAR_SAT_SEL_COL, SAT_CFG_INT_POLAR_SAT_SEL_COL); + polv->col_track = mod_cfg_get_int(polv->cfgdata, MOD_CFG_POLAR_SECTION, + MOD_CFG_POLAR_TRACK_COL, SAT_CFG_INT_POLAR_TRACK_COL); + + /* get default font */ + g_value_init(&font_value, G_TYPE_STRING); + g_object_get_property(G_OBJECT(gtk_settings_get_default()), "gtk-font-name", &font_value); + polv->font = g_value_dup_string(&font_value); + g_value_unset(&font_value); + + /* graph dimensions */ + polv->size = POLV_DEFAULT_SIZE; + polv->r = (polv->size / 2) - POLV_DEFAULT_MARGIN; + polv->cx = POLV_DEFAULT_SIZE / 2; + polv->cy = POLV_DEFAULT_SIZE / 2; - /* Create the canvas model */ - root = create_canvas_model(polv); - goo_canvas_set_root_item_model(GOO_CANVAS(polv->canvas), root); + /* create the canvas (drawing area) */ + polv->canvas = gtk_drawing_area_new(); + gtk_widget_set_has_tooltip(polv->canvas, TRUE); + gtk_widget_set_size_request(polv->canvas, POLV_DEFAULT_SIZE, POLV_DEFAULT_SIZE); + gtk_widget_add_events(polv->canvas, GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | + GDK_BUTTON_RELEASE_MASK); - g_object_unref(root); + /* connect signals */ + g_signal_connect(polv->canvas, "draw", G_CALLBACK(on_draw), polv); + g_signal_connect(polv->canvas, "motion-notify-event", G_CALLBACK(on_motion_notify), polv); + g_signal_connect(polv->canvas, "button-press-event", G_CALLBACK(on_button_press), polv); + g_signal_connect(polv->canvas, "button-release-event", G_CALLBACK(on_button_release), polv); + g_signal_connect(polv->canvas, "size-allocate", G_CALLBACK(size_allocate_cb), polv); + g_signal_connect_after(polv->canvas, "realize", G_CALLBACK(on_canvas_realized), polv); - //gtk_box_pack_start (GTK_BOX (polv), GTK_POLAR_VIEW (polv)->swin, TRUE, TRUE, 0); + gtk_widget_show(polv->canvas); gtk_box_pack_start(GTK_BOX(polv), polv->canvas, TRUE, TRUE, 0); return GTK_WIDGET(polv); @@ -857,14 +871,9 @@ GtkWidget *gtk_polar_view_new(GKeyFile * cfgdata, GHashTable * sats, static void update_polv_size(GtkPolarView * polv) { GtkAllocation allocation; - GooCanvasPoints *prec; - gfloat x, y; - GooCanvasAnchorType anch = GOO_CANVAS_ANCHOR_CENTER; - if (gtk_widget_get_realized(GTK_WIDGET(polv))) { - /* get graph dimensions */ gtk_widget_get_allocation(GTK_WIDGET(polv), &allocation); polv->size = MIN(allocation.width, allocation.height); @@ -872,96 +881,40 @@ static void update_polv_size(GtkPolarView * polv) polv->cx = allocation.width / 2; polv->cy = allocation.height / 2; - /* update canvas bounds to match new size */ - goo_canvas_set_bounds(GOO_CANVAS(GTK_POLAR_VIEW(polv)->canvas), 0, 0, - allocation.width, allocation.height); - - /* background item */ - g_object_set(polv->bgd, "width", (gdouble) allocation.width, - "height", (gdouble) allocation.height, NULL); - - /* update coordinate system */ - g_object_set(polv->C00, - "center-x", (gdouble) polv->cx, - "center-y", (gdouble) polv->cy, - "radius-x", (gdouble) polv->r, - "radius-y", (gdouble) polv->r, NULL); - g_object_set(polv->C30, - "center-x", (gdouble) polv->cx, - "center-y", (gdouble) polv->cy, - "radius-x", (gdouble) 0.6667 * polv->r, - "radius-y", (gdouble) 0.6667 * polv->r, NULL); - g_object_set(polv->C60, - "center-x", (gdouble) polv->cx, - "center-y", (gdouble) polv->cy, - "radius-x", (gdouble) 0.333 * polv->r, - "radius-y", (gdouble) 0.333 * polv->r, NULL); - - /* horizontal line */ - prec = goo_canvas_points_new(2); - prec->coords[0] = polv->cx - polv->r - POLV_LINE_EXTRA; - prec->coords[1] = polv->cy; - prec->coords[2] = polv->cx + polv->r + POLV_LINE_EXTRA; - prec->coords[3] = polv->cy; - g_object_set(polv->hl, "points", prec, NULL); - - /* vertical line */ - prec->coords[0] = polv->cx; - prec->coords[1] = polv->cy - polv->r - POLV_LINE_EXTRA; - prec->coords[2] = polv->cx; - prec->coords[3] = polv->cy + polv->r + POLV_LINE_EXTRA; - g_object_set(polv->vl, "points", prec, NULL); - - /* free memory */ - goo_canvas_points_unref(prec); - - /* N/E/S/W */ - azel_to_xy(polv, 0.0, 0.0, &x, &y); - correct_pole_coor(polv, POLAR_VIEW_POLE_N, &x, &y, &anch); - g_object_set(polv->N, "x", x, "y", y, NULL); - - azel_to_xy(polv, 90.0, 0.0, &x, &y); - correct_pole_coor(polv, POLAR_VIEW_POLE_E, &x, &y, &anch); - g_object_set(polv->E, "x", x, "y", y, NULL); - - azel_to_xy(polv, 180.0, 0.0, &x, &y); - correct_pole_coor(polv, POLAR_VIEW_POLE_S, &x, &y, &anch); - g_object_set(polv->S, "x", x, "y", y, NULL); - - azel_to_xy(polv, 270.0, 0.0, &x, &y); - correct_pole_coor(polv, POLAR_VIEW_POLE_W, &x, &y, &anch); - g_object_set(polv->W, "x", x, "y", y, NULL); - - /* cursor track */ - g_object_set(polv->curs, - "x", (gfloat) (polv->cx - polv->r - 2 * POLV_LINE_EXTRA), - "y", (gfloat) (polv->cy + polv->r + POLV_LINE_EXTRA), - NULL); - - /* location name */ - g_object_set(polv->locnam, - "x", (gfloat) (polv->cx - polv->r - 2 * POLV_LINE_EXTRA), - "y", (gfloat) (polv->cy - polv->r - POLV_LINE_EXTRA), - NULL); - - /* next event */ - g_object_set(polv->next, - "x", (gfloat) (polv->cx + polv->r + 2 * POLV_LINE_EXTRA), - "y", (gfloat) (polv->cy - polv->r - POLV_LINE_EXTRA), - NULL); - - /* selection info */ - g_object_set(polv->sel, - "x", (gfloat) polv->cx + polv->r + 2 * POLV_LINE_EXTRA, - "y", (gfloat) polv->cy + polv->r + POLV_LINE_EXTRA, NULL); - + /* Update satellite positions */ g_hash_table_foreach(polv->sats, update_sat, polv); - - /* sky tracks */ - g_hash_table_foreach(polv->obj, update_track, polv); } } +/** Convert LOS timestamp to human readable countdown string */ +static gchar *los_time_to_str(GtkPolarView * polv, sat_t * sat) +{ + guint h, m, s; + gdouble number, now; + gchar *text = NULL; + + now = polv->tstamp; + number = sat->los - now; + + /* convert julian date to seconds */ + s = (guint) (number * 86400); + + /* extract hours */ + h = (guint) floor(s / 3600); + s -= 3600 * h; + + /* extract minutes */ + m = (guint) floor(s / 60); + s -= 60 * m; + + if (h > 0) + text = g_strdup_printf(_("LOS in %02d:%02d:%02d"), h, m, s); + else + text = g_strdup_printf(_("LOS in %02d:%02d"), m, s); + + return text; +} + void gtk_polar_view_update(GtkWidget * widget) { GtkPolarView *polv = GTK_POLAR_VIEW(widget); @@ -996,7 +949,6 @@ void gtk_polar_view_update(GtkWidget * widget) /* update countdown to NEXT AOS label */ if (polv->eventinfo) { - if (polv->ncat > 0) { catnr = g_try_new0(gint, 1); @@ -1004,88 +956,48 @@ void gtk_polar_view_update(GtkWidget * widget) sat = SAT(g_hash_table_lookup(polv->sats, catnr)); g_free(catnr); - /* last desperate sanity check */ if (sat != NULL) { - - now = polv->tstamp; //get_current_daynum (); + now = polv->tstamp; number = polv->naos - now; - /* convert julian date to seconds */ s = (guint) (number * 86400); - - /* extract hours */ h = (guint) floor(s / 3600); s -= 3600 * h; - - /* extract minutes */ m = (guint) floor(s / 60); s -= 60 * m; if (h > 0) - buff = - g_strdup_printf(_("Next: %s\nin %02d:%02d:%02d"), - sat->nickname, h, m, s); + buff = g_strdup_printf(_("Next: %s\nin %02d:%02d:%02d"), + sat->nickname, h, m, s); else buff = g_strdup_printf(_("Next: %s\nin %02d:%02d"), sat->nickname, m, s); - - g_object_set(polv->next, "text", buff, NULL); - - g_free(buff); + g_free(polv->next_text); + polv->next_text = buff; } else { - sat_log_log(SAT_LOG_LEVEL_ERROR, - _("%s: Can not find NEXT satellite."), - __func__); - g_object_set(polv->next, "text", _("Next: ERR"), NULL); + sat_log_log(SAT_LOG_LEVEL_ERROR, _("%s: Can not find NEXT satellite."), __func__); + g_free(polv->next_text); + polv->next_text = g_strdup(_("Next: ERR")); } } else { - g_object_set(polv->next, "text", _("Next: N/A"), NULL); + g_free(polv->next_text); + polv->next_text = g_strdup(_("Next: N/A")); } } else { - g_object_set(polv->next, "text", "", NULL); + g_free(polv->next_text); + polv->next_text = NULL; } - } -} - -/** Convert LOS timestamp to human readable countdown string */ -static gchar *los_time_to_str(GtkPolarView * polv, sat_t * sat) -{ - guint h, m, s; - gdouble number, now; - gchar *text = NULL; - now = polv->tstamp; //get_current_daynum (); - number = sat->los - now; - - /* convert julian date to seconds */ - s = (guint) (number * 86400); - - /* extract hours */ - h = (guint) floor(s / 3600); - s -= 3600 * h; - - /* extract minutes */ - m = (guint) floor(s / 60); - s -= 60 * m; - - if (h > 0) - { - text = g_strdup_printf(_("LOS in %02d:%02d:%02d"), h, m, s); + gtk_widget_queue_draw(polv->canvas); } - else - { - text = g_strdup_printf(_("LOS in %02d:%02d"), m, s); - } - - return text; } static void update_sat(gpointer key, gpointer value, gpointer data) @@ -1095,15 +1007,15 @@ static void update_sat(gpointer key, gpointer value, gpointer data) GtkPolarView *polv = GTK_POLAR_VIEW(data); sat_obj_t *obj = NULL; gfloat x, y; - GooCanvasItemModel *root; - gint idx, i; - gdouble now; // = get_current_daynum (); - gchar *text; + gdouble now; gchar *losstr; - gchar *tooltip; - guint32 colour; + gchar *text; + guint num, i; + pass_detail_t *detail; + gdouble *point; + guint tres, ttidx; - (void)key; /* avoid unused parameter compiler warning */ + (void)key; catnum = g_new0(gint, 1); *catnum = sat->tle.catnr; @@ -1123,241 +1035,116 @@ static void update_sat(gpointer key, gpointer value, gpointer data) /* if sat is out of range */ if ((sat->el < 0.00) || decayed(sat)) { - obj = SAT_OBJ(g_hash_table_lookup(polv->obj, catnum)); - /* if sat is on canvas */ if (obj != NULL) { - /* remove sat from canvas */ - root = goo_canvas_get_root_item_model(GOO_CANVAS(polv->canvas)); - - idx = goo_canvas_item_model_find_child(root, obj->marker); - if (idx != -1) - { - goo_canvas_item_model_remove_child(root, idx); - } - - idx = goo_canvas_item_model_find_child(root, obj->label); - if (idx != -1) - { - goo_canvas_item_model_remove_child(root, idx); - } - - /* remove sky track */ - if (obj->showtrack) - { - gtk_polar_view_delete_track(polv, obj, sat); - } - - /* free pass info */ - free_pass(obj->pass); - obj->pass = NULL; - /* if this was the selected satellite we need to clear the info text */ if (obj->selected) { - g_object_set(polv->sel, "text", "", NULL); + g_free(polv->sel_text); + polv->sel_text = NULL; } - g_free(obj); - - /* remove sat object from hash table */ + /* remove sat object from hash table (this will free it) */ g_hash_table_remove(polv->obj, catnum); - - /* FIXME: remove track from chart */ } g_free(catnum); } - - /* sat is within range */ else { + /* sat is within range */ obj = SAT_OBJ(g_hash_table_lookup(polv->obj, catnum)); azel_to_xy(polv, sat->az, sat->el, &x, &y); - /* if sat is already on canvas */ if (obj != NULL) { + /* update existing satellite */ + obj->x = x; + obj->y = y; + + /* update nickname */ + g_free(obj->nickname); + obj->nickname = g_strdup(sat->nickname); + /* update LOS count down */ if (sat->los > 0.0) - { losstr = los_time_to_str(polv, sat); - } else - { - losstr = - g_strdup_printf(_("%s\nAlways in range"), sat->nickname); - } - - /* update label */ - g_object_set(obj->label, "text", sat->nickname, NULL); + losstr = g_strdup_printf(_("%s\nAlways in range"), sat->nickname); /* update tooltip */ - tooltip = g_markup_printf_escaped("%s\n" - "Az: %5.1f\302\260\n" - "El: %5.1f\302\260\n" - "%s", - sat->nickname, - sat->az, sat->el, losstr); - - g_object_set(obj->marker, - "x", x - MARKER_SIZE_HALF, - "y", y - MARKER_SIZE_HALF, "tooltip", tooltip, NULL); - g_object_set(obj->label, - "x", x, "y", y + 2, "tooltip", tooltip, NULL); - - g_free(tooltip); - - /* update selection info if satellite is - selected - */ + g_free(obj->tooltip); + obj->tooltip = g_markup_printf_escaped("%s\nAz: %5.1f\302\260\nEl: %5.1f\302\260\n%s", + sat->nickname, sat->az, sat->el, losstr); + + /* update selection info */ if (obj->selected) { - text = g_strdup_printf("%s\n%s", sat->nickname, losstr); - g_object_set(polv->sel, "text", text, NULL); - g_free(text); + g_free(polv->sel_text); + polv->sel_text = g_strdup_printf("%s\n%s", sat->nickname, losstr); } - /* Current pass and sky track needs update if they were calculated at - * a different location or time (time controller) - */ + /* Check if pass needs update */ if (obj->pass) { /** FIXME: threshold */ - gboolean qth_upd = - qth_small_dist(polv->qth, (obj->pass->qth_comp)) > 1.0; - gboolean time_upd = !((obj->pass->aos <= now) && - (obj->pass->los >= now)); + gboolean qth_upd = qth_small_dist(polv->qth, (obj->pass->qth_comp)) > 1.0; + gboolean time_upd = !((obj->pass->aos <= now) && (obj->pass->los >= now)); if (qth_upd || time_upd) { sat_log_log(SAT_LOG_LEVEL_DEBUG, - _ - ("%s:%s: Updating satellite pass SAT:%d Q:%d T:%d\n"), - __FILE__, __func__, *catnum, qth_upd, - time_upd); - - root = - goo_canvas_get_root_item_model(GOO_CANVAS - (polv->canvas)); - - /* remove sky track */ - if (obj->showtrack) - { - idx = - goo_canvas_item_model_find_child(root, obj->track); - if (idx != -1) - goo_canvas_item_model_remove_child(root, idx); - - for (i = 0; i < TRACK_TICK_NUM; i++) - { - idx = - goo_canvas_item_model_find_child(root, - obj->trtick - [i]); - if (idx != -1) - goo_canvas_item_model_remove_child(root, idx); - } - } + _("%s:%s: Updating satellite pass SAT:%d Q:%d T:%d\n"), + __FILE__, __func__, *catnum, qth_upd, time_upd); - /* free pass info */ + /* Free old track and pass */ + g_slist_free_full(obj->track_points, g_free); + obj->track_points = NULL; free_pass(obj->pass); obj->pass = NULL; - /*compute new pass */ + /* Compute new pass */ obj->pass = get_current_pass(sat, polv->qth, now); - /* Finally, create the sky track if necessary */ - if (obj->showtrack) + /* Recreate track if needed */ + if (obj->showtrack && obj->pass) gtk_polar_view_create_track(polv, obj, sat); } } + g_free(losstr); g_free(catnum); // FIXME: why free here, what about else? } else { - /* add sat to canvas */ - obj = g_try_new(sat_obj_t, 1); + /* add new satellite */ + obj = g_try_new0(sat_obj_t, 1); if (obj != NULL) { - /* space was allocated now use it */ obj->selected = FALSE; + obj->x = x; + obj->y = y; + obj->catnum = sat->tle.catnr; + obj->nickname = g_strdup(sat->nickname); + obj->track_points = NULL; - if (g_hash_table_lookup_extended - (polv->showtracks_on, catnum, NULL, NULL)) - { + if (g_hash_table_lookup_extended(polv->showtracks_on, catnum, NULL, NULL)) obj->showtrack = TRUE; - } - else if (g_hash_table_lookup_extended - (polv->showtracks_off, catnum, NULL, NULL)) - { + else if (g_hash_table_lookup_extended(polv->showtracks_off, catnum, NULL, NULL)) obj->showtrack = FALSE; - } else - { obj->showtrack = polv->showtrack; - } - obj->istarget = FALSE; - root = - goo_canvas_get_root_item_model(GOO_CANVAS(polv->canvas)); - - colour = mod_cfg_get_int(polv->cfgdata, - MOD_CFG_POLAR_SECTION, - MOD_CFG_POLAR_SAT_COL, - SAT_CFG_INT_POLAR_SAT_COL); + obj->istarget = FALSE; /* create tooltip */ - tooltip = g_markup_printf_escaped("%s\n" - "Az: %5.1f\302\260\n" - "El: %5.1f\302\260\n", - sat->nickname, - sat->az, sat->el); - - obj->marker = goo_canvas_rect_model_new(root, - x - MARKER_SIZE_HALF, - y - MARKER_SIZE_HALF, - 2 * MARKER_SIZE_HALF, - 2 * MARKER_SIZE_HALF, - "fill-color-rgba", - colour, - "stroke-color-rgba", - colour, "tooltip", - tooltip, NULL); - obj->label = - goo_canvas_text_model_new(root, sat->nickname, x, y + 2, - -1, GOO_CANVAS_ANCHOR_NORTH, - "font", g_value_get_string(&polv->font), - "fill-color-rgba", colour, - "tooltip", tooltip, NULL); - - g_free(tooltip); - if (goo_canvas_item_model_find_child(root, obj->marker) != -1) - goo_canvas_item_model_raise(obj->marker, NULL); - else - sat_log_log(SAT_LOG_LEVEL_ERROR, - _ - ("%s: marker added to polarview not showing %d."), - __func__, *catnum); - - if (goo_canvas_item_model_find_child(root, obj->label) != -1) - goo_canvas_item_model_raise(obj->label, NULL); - else - sat_log_log(SAT_LOG_LEVEL_ERROR, - _ - ("%s: label added to polarview not showing %d."), - __func__, *catnum); - - g_object_set_data(G_OBJECT(obj->marker), "catnum", - GINT_TO_POINTER(*catnum)); - g_object_set_data(G_OBJECT(obj->label), "catnum", - GINT_TO_POINTER(*catnum)); + obj->tooltip = g_markup_printf_escaped("%s\nAz: %5.1f\302\260\nEl: %5.1f\302\260\n", + sat->nickname, sat->az, sat->el); /* get info about the current pass */ obj->pass = get_current_pass(sat, polv->qth, now); @@ -1365,218 +1152,70 @@ static void update_sat(gpointer key, gpointer value, gpointer data) /* add sat to hash table */ g_hash_table_insert(polv->obj, catnum, obj); - /* Finally, create the sky track if necessary */ + /* create the sky track if necessary */ if (obj->showtrack) gtk_polar_view_create_track(polv, obj, sat); - - /* show or hide satellite name and marker */ - if (!polv->satname) - g_object_set(obj->label, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL); - if (!polv->satmarker) - g_object_set(obj->marker, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL); - - } else { - /* obj == NULL */ sat_log_log(SAT_LOG_LEVEL_ERROR, _("%s: Cannot allocate memory for satellite %d."), __func__, sat->tle.catnr); + g_free(catnum); return; } } } } -/** Update sky track drawing after size allocate. */ -static void update_track(gpointer key, gpointer value, gpointer data) +void gtk_polar_view_create_track(GtkPolarView * pv, sat_obj_t * obj, sat_t * sat) { - sat_obj_t *obj = SAT_OBJ(value);; - GtkPolarView *pv = GTK_POLAR_VIEW(data); guint num, i; - GooCanvasPoints *points; - gfloat x, y; - pass_detail_t *detail; - guint tres, ttidx; - - (void)key; - - if (obj->showtrack) - { - if (obj->pass == NULL) - { - sat_log_log(SAT_LOG_LEVEL_ERROR, - _("%s:%d: Failed to get satellite pass."), - __FILE__, __LINE__); - return; - } - - /* create points */ - num = g_slist_length(obj->pass->details); - if (num == 0) - { - sat_log_log(SAT_LOG_LEVEL_ERROR, - _("%s:%d: Pass had no points in it."), - __FILE__, __LINE__); - return; - } - - points = goo_canvas_points_new(num); - - /* first point should be (aos_az,0.0) */ - azel_to_xy(pv, obj->pass->aos_az, 0.0, &x, &y); - points->coords[0] = (double)x; - points->coords[1] = (double)y; - - /* time tick 0 */ - g_object_set(obj->trtick[0], "x", (gdouble) x, "y", (gdouble) y, NULL); - - /* time resolution for time ticks; we need - 3 additional points to AOS and LOS ticks. - */ - tres = (num - 2) / (TRACK_TICK_NUM - 1); - ttidx = 1; - - for (i = 1; i < num - 1; i++) - { - detail = PASS_DETAIL(g_slist_nth_data(obj->pass->details, i)); - if (detail->el >= 0) - azel_to_xy(pv, detail->az, detail->el, &x, &y); - points->coords[2 * i] = (double)x; - points->coords[2 * i + 1] = (double)y; - - if (!(i % tres)) - { - /* update time tick */ - if (ttidx < TRACK_TICK_NUM) - g_object_set(obj->trtick[ttidx], - "x", (gdouble) x, "y", (gdouble) y, NULL); - ttidx++; - } - } - - /* last point should be (los_az, 0.0) */ - azel_to_xy(pv, obj->pass->los_az, 0.0, &x, &y); - points->coords[2 * (num - 1)] = (double)x; - points->coords[2 * (num - 1) + 1] = (double)y; - - g_object_set(obj->track, "points", points, NULL); - - goo_canvas_points_unref(points); - } -} - -static GooCanvasItemModel *create_time_tick(GtkPolarView * pv, gdouble time, - gfloat x, gfloat y) -{ - GooCanvasItemModel *item; - gchar buff[6]; - GooCanvasAnchorType anchor; - GooCanvasItemModel *root; - guint32 col; - - root = goo_canvas_get_root_item_model(GOO_CANVAS(pv->canvas)); - - col = mod_cfg_get_int(pv->cfgdata, - MOD_CFG_POLAR_SECTION, - MOD_CFG_POLAR_TRACK_COL, - SAT_CFG_INT_POLAR_TRACK_COL); - - daynum_to_str(buff, 6, "%H:%M", time); - - if (x > pv->cx) - { - anchor = GOO_CANVAS_ANCHOR_EAST; - x -= 5; - } - else - { - anchor = GOO_CANVAS_ANCHOR_WEST; - x += 5; - } - - item = goo_canvas_text_model_new(root, buff, - (gdouble) x, (gdouble) y, - -1, anchor, - "font", g_value_get_string(&pv->font), - "fill-color-rgba", col, NULL); - - return item; -} - -/** - * Create a sky track for a satellite. - * - * @param pv Pointer to the GtkPolarView object. - * @param obj Pointer to the sat_obj_t object. - * @param sat Pointer to the sat_t object. - * - * Note: This function is only used when the the satellite comes within range - * and the ALWAYS_SHOW_SKY_TRACK option is TRUE. - */ -void gtk_polar_view_create_track(GtkPolarView * pv, sat_obj_t * obj, - sat_t * sat) -{ - guint i; - GooCanvasItemModel *root; pass_detail_t *detail; - guint num; - GooCanvasPoints *points; gfloat x, y; - guint32 col; + gdouble *point; guint tres, ttidx; (void)sat; - /* get satellite object */ - /*obj = SAT_OBJ(g_object_get_data (G_OBJECT (item), "obj")); - sat = SAT(g_object_get_data (G_OBJECT (item), "sat")); - qth = (qth_t *)(g_object_get_data (G_OBJECT (item), "qth")); */ - if (obj == NULL) { - sat_log_log(SAT_LOG_LEVEL_ERROR, - _("%s:%d: Failed to get satellite object."), - __FILE__, __LINE__); + sat_log_log(SAT_LOG_LEVEL_ERROR, _("%s:%d: Failed to get satellite object."), __FILE__, __LINE__); return; } if (obj->pass == NULL) { - sat_log_log(SAT_LOG_LEVEL_ERROR, - _("%s:%d: Failed to get satellite pass."), - __FILE__, __LINE__); + sat_log_log(SAT_LOG_LEVEL_ERROR, _("%s:%d: Failed to get satellite pass."), __FILE__, __LINE__); return; } - root = goo_canvas_get_root_item_model(GOO_CANVAS(pv->canvas)); + /* Clear existing track points */ + g_slist_free_full(obj->track_points, g_free); + obj->track_points = NULL; - /* add sky track */ - - /* create points */ + /* Create points */ num = g_slist_length(obj->pass->details); if (num == 0) { - sat_log_log(SAT_LOG_LEVEL_ERROR, - _("%s:%d: Pass had no points in it."), __FILE__, __LINE__); + sat_log_log(SAT_LOG_LEVEL_ERROR, _("%s:%d: Pass had no points in it."), __FILE__, __LINE__); return; } - /* time resolution for time ticks; we need - 3 additional points to AOS and LOS ticks. - */ - tres = (num - 2) / (TRACK_TICK_NUM - 1); - - points = goo_canvas_points_new(num); - - + /* time resolution for time ticks */ + tres = (num > 2) ? (num - 2) / (TRACK_TICK_NUM - 1) : 1; /* first point should be (aos_az,0.0) */ azel_to_xy(pv, obj->pass->aos_az, 0.0, &x, &y); - points->coords[0] = (double)x; - points->coords[1] = (double)y; - obj->trtick[0] = create_time_tick(pv, obj->pass->aos, x, y); + point = g_new(gdouble, 2); + point[0] = x; + point[1] = y; + obj->track_points = g_slist_append(obj->track_points, point); + + /* first time tick */ + obj->trtick[0].x = x; + obj->trtick[0].y = y; + daynum_to_str(obj->trtick[0].text, 6, "%H:%M", obj->pass->aos); ttidx = 1; @@ -1585,83 +1224,65 @@ void gtk_polar_view_create_track(GtkPolarView * pv, sat_obj_t * obj, detail = PASS_DETAIL(g_slist_nth_data(obj->pass->details, i)); if (detail->el >= 0.0) azel_to_xy(pv, detail->az, detail->el, &x, &y); - points->coords[2 * i] = (double)x; - points->coords[2 * i + 1] = (double)y; + + point = g_new(gdouble, 2); + point[0] = x; + point[1] = y; + obj->track_points = g_slist_append(obj->track_points, point); if (tres != 0 && !(i % tres)) { - /* create a time tick */ if (ttidx < TRACK_TICK_NUM) - obj->trtick[ttidx] = create_time_tick(pv, detail->time, x, y); + { + gfloat tx = x; + if (tx > pv->cx) + tx -= 5; + else + tx += 5; + + obj->trtick[ttidx].x = tx; + obj->trtick[ttidx].y = y; + daynum_to_str(obj->trtick[ttidx].text, 6, "%H:%M", detail->time); + } ttidx++; } } - /* last point should be (los_az, 0.0) */ + /* last point should be (los_az, 0.0) */ azel_to_xy(pv, obj->pass->los_az, 0.0, &x, &y); - points->coords[2 * (num - 1)] = (double)x; - points->coords[2 * (num - 1) + 1] = (double)y; - - /* create poly-line */ - col = mod_cfg_get_int(pv->cfgdata, - MOD_CFG_POLAR_SECTION, - MOD_CFG_POLAR_TRACK_COL, - SAT_CFG_INT_POLAR_TRACK_COL); - - obj->track = goo_canvas_polyline_model_new(root, FALSE, 0, - "points", points, - "line-width", 1.0, - "stroke-color-rgba", col, - "line-cap", - CAIRO_LINE_CAP_SQUARE, - "line-join", - CAIRO_LINE_JOIN_MITER, NULL); - goo_canvas_points_unref(points); + point = g_new(gdouble, 2); + point[0] = x; + point[1] = y; + obj->track_points = g_slist_append(obj->track_points, point); } -void gtk_polar_view_delete_track(GtkPolarView * pv, sat_obj_t * obj, - sat_t * sat) +void gtk_polar_view_delete_track(GtkPolarView * pv, sat_obj_t * obj, sat_t * sat) { - gint idx, i; - GooCanvasItemModel *root; - + (void)pv; (void)sat; - root = goo_canvas_get_root_item_model(GOO_CANVAS(pv->canvas)); - idx = goo_canvas_item_model_find_child(root, obj->track); - - if (idx != -1) - { - goo_canvas_item_model_remove_child(root, idx); - } - - for (i = 0; i < TRACK_TICK_NUM; i++) + if (obj) { - idx = goo_canvas_item_model_find_child(root, obj->trtick[i]); + g_slist_free_full(obj->track_points, g_free); + obj->track_points = NULL; - if (idx != -1) - { - goo_canvas_item_model_remove_child(root, idx); - } + /* Clear time ticks */ + memset(obj->trtick, 0, sizeof(obj->trtick)); } } -/** Reload reference to satellites (e.g. after TLE update). */ void gtk_polar_view_reload_sats(GtkWidget * polv, GHashTable * sats) { GTK_POLAR_VIEW(polv)->sats = sats; - GTK_POLAR_VIEW(polv)->naos = 0.0; GTK_POLAR_VIEW(polv)->ncat = 0; } -/** Select a satellite */ void gtk_polar_view_select_sat(GtkWidget * widget, gint catnum) { GtkPolarView *polv = GTK_POLAR_VIEW(widget); gint *catpoint = NULL; sat_obj_t *obj = NULL; - guint32 color; catpoint = g_try_new0(gint, 1); *catpoint = catnum; @@ -1676,23 +1297,12 @@ void gtk_polar_view_select_sat(GtkWidget * widget, gint catnum) else { obj->selected = TRUE; - - color = mod_cfg_get_int(polv->cfgdata, - MOD_CFG_POLAR_SECTION, - MOD_CFG_POLAR_SAT_SEL_COL, - SAT_CFG_INT_POLAR_SAT_SEL_COL); - - g_object_set(obj->marker, - "fill-color-rgba", color, - "stroke-color-rgba", color, NULL); - g_object_set(obj->label, - "fill-color-rgba", color, - "stroke-color-rgba", color, NULL); - } /* clear previous selection, if any */ g_hash_table_foreach(polv->obj, clear_selection, catpoint); g_free(catpoint); + + gtk_widget_queue_draw(polv->canvas); } diff --git a/src/gtk-polar-view.h b/src/gtk-polar-view.h index 18d181af7..2d091cde7 100644 --- a/src/gtk-polar-view.h +++ b/src/gtk-polar-view.h @@ -4,7 +4,6 @@ #include #include #include -#include #include #include "gtk-sat-data.h" @@ -29,6 +28,12 @@ extern "C" { typedef struct _GtkPolarView GtkPolarView; typedef struct _GtkPolarViewClass GtkPolarViewClass; +/** Time tick data for sky track */ +typedef struct { + gfloat x; /*!< X coordinate */ + gfloat y; /*!< Y coordinate */ + gchar text[6]; /*!< Time string */ +} track_tick_t; /** Satellite object on graph. */ typedef struct { @@ -36,10 +41,13 @@ typedef struct { gboolean showtrack; /*!< Show ground track. */ gboolean istarget; /*!< Is this object the target. */ pass_t *pass; /*!< Details of the current pass. */ - GooCanvasItemModel *marker; /*!< Item showing position of satellite. */ - GooCanvasItemModel *label; /*!< Item showing the satellite name. */ - GooCanvasItemModel *track; /*!< Sky track. */ - GooCanvasItemModel *trtick[TRACK_TICK_NUM]; /*!< Time ticks along the sky track */ + gfloat x; /*!< X position of marker */ + gfloat y; /*!< Y position of marker */ + gchar *nickname; /*!< Satellite nickname for label */ + gchar *tooltip; /*!< Tooltip text */ + GSList *track_points; /*!< List of track points (each point is two gdoubles: x,y) */ + track_tick_t trtick[TRACK_TICK_NUM]; /*!< Time ticks along the sky track */ + gint catnum; /*!< Catalogue number */ } sat_obj_t; #define SAT_OBJ(obj) ((sat_obj_t *)obj) @@ -67,16 +75,21 @@ typedef enum { struct _GtkPolarView { GtkBox vbox; - GtkWidget *canvas; /*!< The canvas widget */ + GtkWidget *canvas; /*!< The drawing area widget */ + + /* Colors stored for drawing */ + guint32 col_bgd; /*!< Background color */ + guint32 col_axis; /*!< Axis color */ + guint32 col_tick; /*!< Tick label color */ + guint32 col_info; /*!< Info text color */ + guint32 col_sat; /*!< Satellite color */ + guint32 col_sat_sel; /*!< Selected satellite color */ + guint32 col_track; /*!< Track color */ - GooCanvasItemModel *bgd; - GooCanvasItemModel *C00, *C30, *C60; /*!< 0, 30 and 60 deg elevation circles */ - GooCanvasItemModel *hl, *vl; /*!< horizontal and vertical lines */ - GooCanvasItemModel *N, *S, *E, *W; /*!< North, South, East and West labels */ - GooCanvasItemModel *locnam; /*!< Location name */ - GooCanvasItemModel *curs; /*!< cursor tracking text */ - GooCanvasItemModel *next; /*!< next event text */ - GooCanvasItemModel *sel; /*!< Text showing info about selected satellite. */ + /* Text elements */ + gchar *curs_text; /*!< Cursor tracking text */ + gchar *next_text; /*!< Next event text */ + gchar *sel_text; /*!< Selected satellite info text */ GHashTable *showtracks_on; GHashTable *showtracks_off; @@ -90,7 +103,7 @@ struct _GtkPolarView { GHashTable *sats; /*!< Satellites. */ qth_t *qth; /*!< Pointer to current location. */ - GHashTable *obj; /*!< Canvas items representing each visible satellite */ + GHashTable *obj; /*!< Satellite objects (sat_obj_t) for each visible satellite */ guint cx; /*!< center X */ guint cy; /*!< center Y */ @@ -111,7 +124,7 @@ struct _GtkPolarView { gboolean showtrack; /*!< Automatically show sky tracks. */ gboolean resize; /*!< Flag indicating that the view has been resized. */ - GValue font; /*!< Default font */ + gchar *font; /*!< Default font name */ }; struct _GtkPolarViewClass { diff --git a/src/gtk-sat-map-ground-track.c b/src/gtk-sat-map-ground-track.c index f3996e212..8ad708a3e 100644 --- a/src/gtk-sat-map-ground-track.c +++ b/src/gtk-sat-map-ground-track.c @@ -34,6 +34,7 @@ #endif #include #include +#include #include "config-keys.h" #include "gtk-sat-map.h" @@ -46,10 +47,17 @@ #include "sgpsdp/sgp4sdp4.h" +/** Structure to hold line segment data */ +typedef struct { + gdouble *points; /*!< Array of x,y coordinate pairs */ + gint count; /*!< Number of points in this segment */ +} line_segment_t; + static void create_polylines(GtkSatMap * satmap, sat_t * sat, qth_t * qth, sat_map_obj_t * obj); static gboolean ssp_wrap_detected(GtkSatMap * satmap, gdouble x1, gdouble x2); static void free_ssp(gpointer ssp, gpointer data); +static void free_line_segment(gpointer data); /** @@ -214,6 +222,17 @@ void ground_track_update(GtkSatMap * satmap, sat_t * sat, qth_t * qth, } } +/** Free a line segment structure */ +static void free_line_segment(gpointer data) +{ + line_segment_t *seg = (line_segment_t *)data; + if (seg) + { + g_free(seg->points); + g_free(seg); + } +} + /** * Delete the ground track for a satellite. * @@ -226,46 +245,18 @@ void ground_track_update(GtkSatMap * satmap, sat_t * sat, qth_t * qth, void ground_track_delete(GtkSatMap * satmap, sat_t * sat, qth_t * qth, sat_map_obj_t * obj, gboolean clear_ssp) { - guint i, n; - gint j; - GooCanvasItemModel *line; - GooCanvasItemModel *root; - + (void)satmap; + (void)sat; (void)qth; sat_log_log(SAT_LOG_LEVEL_DEBUG, _("%s: Deleting ground track for %s"), __func__, sat->nickname); - root = goo_canvas_get_root_item_model(GOO_CANVAS(satmap->canvas)); - - /* remove plylines */ + /* Free line segments */ if (obj->track_data.lines != NULL) { - n = g_slist_length(obj->track_data.lines); - - for (i = 0; i < n; i++) - { - /* get line */ - line = - GOO_CANVAS_ITEM_MODEL(g_slist_nth_data - (obj->track_data.lines, i)); - - /* find its ID and remove it */ - j = goo_canvas_item_model_find_child(root, line); - if (j == -1) - { - sat_log_log(SAT_LOG_LEVEL_ERROR, - _("%s: Could not find part %d of ground track"), - __func__, j); - } - else - { - goo_canvas_item_model_remove_child(root, j); - } - } - - g_slist_free(obj->track_data.lines); + g_slist_free_full(obj->track_data.lines, free_line_segment); obj->track_data.lines = NULL; } @@ -285,6 +276,12 @@ void ground_track_delete(GtkSatMap * satmap, sat_t * sat, qth_t * qth, obj->track_orbit = 0; } + + /* Request redraw */ + if (satmap && satmap->canvas) + { + gtk_widget_queue_draw(satmap->canvas); + } } /** @@ -300,19 +297,16 @@ static void free_ssp(gpointer ssp, gpointer data) g_free(ssp); } -/** Create polylines. */ +/** Create polylines (line segments) for Cairo drawing. */ static void create_polylines(GtkSatMap * satmap, sat_t * sat, qth_t * qth, sat_map_obj_t * obj) { ssp_t *ssp, *buff; /* map coordinates */ double lastx, lasty; GSList *points = NULL; - GooCanvasItemModel *root; - GooCanvasItemModel *line; - GooCanvasPoints *gpoints; guint start; guint i, j, n, num_points; - guint32 col; + line_segment_t *segment; (void)sat; (void)qth; @@ -323,9 +317,6 @@ static void create_polylines(GtkSatMap * satmap, sat_t * sat, qth_t * qth, start = 0; num_points = 0; n = g_slist_length(obj->track_data.latlon); - col = mod_cfg_get_int(satmap->cfgdata, - MOD_CFG_MAP_SECTION, - MOD_CFG_MAP_TRACK_COL, SAT_CFG_INT_MAP_TRACK_COL); /* loop over each SSP */ for (i = 0; i < n; i++) @@ -353,36 +344,24 @@ static void create_polylines(GtkSatMap * satmap, sat_t * sat, qth_t * qth, /* we need at least 2 points to draw a line */ if (num_points > 1) { - /* convert SSPs to GooCanvasPoints */ - gpoints = goo_canvas_points_new(num_points); - for (j = 0; j < num_points; j++) + /* Create a line segment with points array */ + segment = g_try_new(line_segment_t, 1); + if (segment) { - buff = (ssp_t *) g_slist_nth_data(points, j); - gpoints->coords[2 * j] = buff->lon; - gpoints->coords[2 * j + 1] = buff->lat; + segment->count = num_points; + segment->points = g_new(gdouble, num_points * 2); + + for (j = 0; j < num_points; j++) + { + buff = (ssp_t *)g_slist_nth_data(points, j); + segment->points[2 * j] = buff->lon; + segment->points[2 * j + 1] = buff->lat; + } + + /* Store segment in sat object */ + obj->track_data.lines = + g_slist_append(obj->track_data.lines, segment); } - - /* create a new polyline using the current set of points */ - root = - goo_canvas_get_root_item_model(GOO_CANVAS - (satmap->canvas)); - - line = goo_canvas_polyline_model_new(root, FALSE, 0, - "points", gpoints, - "line-width", 1.0, - "stroke-color-rgba", - col, "line-cap", - CAIRO_LINE_CAP_SQUARE, - "line-join", - CAIRO_LINE_JOIN_MITER, - NULL); - goo_canvas_points_unref(gpoints); - goo_canvas_item_model_lower(line, obj->marker); - - /* store line in sat object */ - obj->track_data.lines = - g_slist_append(obj->track_data.lines, line); - } /* reset parameters and continue with a new set */ @@ -421,35 +400,35 @@ static void create_polylines(GtkSatMap * satmap, sat_t * sat, qth_t * qth, if (num_points > 1) { - /* convert SSPs to GooCanvasPoints */ - gpoints = goo_canvas_points_new(num_points); - for (j = 0; j < num_points; j++) + /* Create a line segment with points array */ + segment = g_try_new(line_segment_t, 1); + if (segment) { - buff = (ssp_t *) g_slist_nth_data(points, j); - gpoints->coords[2 * j] = buff->lon; - gpoints->coords[2 * j + 1] = buff->lat; - } + segment->count = num_points; + segment->points = g_new(gdouble, num_points * 2); - /* create a new polyline using the current set of points */ - root = goo_canvas_get_root_item_model(GOO_CANVAS(satmap->canvas)); - - line = goo_canvas_polyline_model_new(root, FALSE, 0, - "points", gpoints, - "line-width", 1.0, - "stroke-color-rgba", col, - "line-cap", CAIRO_LINE_CAP_SQUARE, - "line-join", - CAIRO_LINE_JOIN_MITER, NULL); - goo_canvas_points_unref(gpoints); - goo_canvas_item_model_lower(line, obj->marker); + for (j = 0; j < num_points; j++) + { + buff = (ssp_t *)g_slist_nth_data(points, j); + segment->points[2 * j] = buff->lon; + segment->points[2 * j + 1] = buff->lat; + } - /* store line in sat object */ - obj->track_data.lines = g_slist_append(obj->track_data.lines, line); + /* Store segment in sat object */ + obj->track_data.lines = + g_slist_append(obj->track_data.lines, segment); + } /* reset parameters and continue with a new set */ g_slist_foreach(points, free_ssp, NULL); g_slist_free(points); } + + /* Request redraw */ + if (satmap && satmap->canvas) + { + gtk_widget_queue_draw(satmap->canvas); + } } /** Check whether ground track wraps around map borders */ diff --git a/src/gtk-sat-map-ground-track.h b/src/gtk-sat-map-ground-track.h index a054e009a..bf839ec59 100644 --- a/src/gtk-sat-map-ground-track.h +++ b/src/gtk-sat-map-ground-track.h @@ -27,7 +27,6 @@ #include #include -#include #include #include "gtk-sat-map.h" diff --git a/src/gtk-sat-map-popup.c b/src/gtk-sat-map-popup.c index c57e072b1..c38b8e80f 100644 --- a/src/gtk-sat-map-popup.c +++ b/src/gtk-sat-map-popup.c @@ -205,12 +205,10 @@ static void coverage_toggled(GtkCheckMenuItem * item, gpointer data) covcol = 0x00000000; } - g_object_set(obj->range1, "fill-color-rgba", covcol, NULL); - - if (obj->newrcnum == 2) - { - g_object_set(obj->range2, "fill-color-rgba", covcol, NULL); - } + (void)covcol; /* coverage color is applied during on_draw */ + + /* request redraw to update coverage display */ + gtk_widget_queue_draw(satmap->canvas); } /** diff --git a/src/gtk-sat-map.c b/src/gtk-sat-map.c index b2d5a8c9e..071a98237 100644 --- a/src/gtk-sat-map.c +++ b/src/gtk-sat-map.c @@ -22,7 +22,6 @@ #include #endif -#include #include #include #include @@ -52,9 +51,9 @@ #define TERMINATOR_UPDATE_INTERVAL (15.0/86400.0) static void gtk_sat_map_class_init(GtkSatMapClass * class, - gpointer class_data); + gpointer class_data); static void gtk_sat_map_init(GtkSatMap * polview, - gpointer g_class); + gpointer g_class); static void gtk_sat_map_destroy(GtkWidget * widget); static void size_allocate_cb(GtkWidget * widget, GtkAllocation * allocation, gpointer data); @@ -66,40 +65,35 @@ static void lonlat_to_xy(GtkSatMap * m, gdouble lon, gdouble lat, gfloat * x, gfloat * y); static void xy_to_lonlat(GtkSatMap * m, gfloat x, gfloat y, gfloat * lon, gfloat * lat); -static gboolean on_motion_notify(GooCanvasItem * item, GooCanvasItem * target, - GdkEventMotion * event, gpointer data); -static void on_item_created(GooCanvas * canvas, GooCanvasItem * item, - GooCanvasItemModel * model, gpointer data); -static void on_canvas_realized(GtkWidget * canvas, gpointer data); -static gboolean on_button_press(GooCanvasItem * item, - GooCanvasItem * target, - GdkEventButton * event, gpointer data); -static gboolean on_button_release(GooCanvasItem * item, - GooCanvasItem * target, - GdkEventButton * event, gpointer data); +static gboolean on_motion_notify(GtkWidget * widget, GdkEventMotion * event, + gpointer data); +static gboolean on_button_press(GtkWidget * widget, GdkEventButton * event, + gpointer data); +static gboolean on_button_release(GtkWidget * widget, GdkEventButton * event, + gpointer data); +static gboolean on_draw(GtkWidget * widget, cairo_t * cr, gpointer data); static void clear_selection(gpointer key, gpointer val, gpointer data); static void load_map_file(GtkSatMap * satmap, float clon); -static GooCanvasItemModel *create_canvas_model(GtkSatMap * satmap); static gdouble arccos(gdouble, gdouble); static gboolean pole_is_covered(sat_t * sat); static gboolean north_pole_is_covered(sat_t * sat); static gboolean south_pole_is_covered(sat_t * sat); static gboolean mirror_lon(sat_t * sat, gdouble rangelon, gdouble * mlon, gdouble mapbreak); -static guint calculate_footprint(GtkSatMap * satmap, sat_t * sat); -static void split_points(GtkSatMap * satmap, sat_t * sat, gdouble sspx); +static guint calculate_footprint(GtkSatMap * satmap, sat_t * sat, + sat_map_obj_t * obj); +static void split_points(GtkSatMap * satmap, sat_t * sat, gdouble sspx, + gdouble * points1, gint * n1, + gdouble * points2, gint * n2); static void sort_points_x(GtkSatMap * satmap, sat_t * sat, - GooCanvasPoints * points, gint num); + gdouble * points, gint num); static void sort_points_y(GtkSatMap * satmap, sat_t * sat, - GooCanvasPoints * points, gint num); + gdouble * points, gint num); static gint compare_coordinates_x(gconstpointer a, gconstpointer b, gpointer data); static gint compare_coordinates_y(gconstpointer a, gconstpointer b, gpointer data); static void update_selected(GtkSatMap * satmap, sat_t * sat); -static void draw_grid_lines(GtkSatMap * satmap, GooCanvasItemModel * root); -static void redraw_grid_lines(GtkSatMap * satmap); -static void draw_terminator(GtkSatMap * satmap, GooCanvasItemModel * root); static void redraw_terminator(GtkSatMap * satmap); static gchar *aoslos_time_to_str(GtkSatMap * satmap, sat_t * sat); static void gtk_sat_map_load_showtracks(GtkSatMap * map); @@ -108,11 +102,23 @@ static void gtk_sat_map_load_hide_coverages(GtkSatMap * map); static void gtk_sat_map_store_hidecovs(GtkSatMap * satmap); static void reset_ground_track(gpointer key, gpointer value, gpointer user_data); +static sat_map_obj_t *find_sat_at_pos(GtkSatMap * satmap, gfloat mx, gfloat my); -static GtkVBoxClass *parent_class = NULL; -static GooCanvasPoints *points1; -static GooCanvasPoints *points2; +static GtkBoxClass *parent_class = NULL; +/* Temporary arrays for footprint calculation */ +static gdouble *temp_points1 = NULL; +static gdouble *temp_points2 = NULL; + +/** Convert rgba color to cairo-friendly format */ +static void rgba_to_cairo(guint32 rgba, gdouble * r, gdouble * g, + gdouble * b, gdouble * a) +{ + *r = ((rgba >> 24) & 0xFF) / 255.0; + *g = ((rgba >> 16) & 0xFF) / 255.0; + *b = ((rgba >> 8) & 0xFF) / 255.0; + *a = (rgba & 0xFF) / 255.0; +} GType gtk_sat_map_get_type() { @@ -142,7 +148,7 @@ GType gtk_sat_map_get_type() } static void gtk_sat_map_class_init(GtkSatMapClass * class, - gpointer class_data) + gpointer class_data) { GtkWidgetClass *widget_class; @@ -154,7 +160,7 @@ static void gtk_sat_map_class_init(GtkSatMapClass * class, } static void gtk_sat_map_init(GtkSatMap * satmap, - gpointer g_class) + gpointer g_class) { (void)g_class; @@ -184,101 +190,76 @@ static void gtk_sat_map_init(GtkSatMap * satmap, satmap->showgrid = FALSE; satmap->keepratio = FALSE; satmap->resize = FALSE; + satmap->locnam_text = NULL; + satmap->curs_text = NULL; + satmap->next_text = NULL; + satmap->sel_text = NULL; + satmap->terminator_points = NULL; + satmap->terminator_count = 0; + satmap->font = NULL; + satmap->map = NULL; + satmap->grid_lines_valid = FALSE; } static void gtk_sat_map_destroy(GtkWidget * widget) { - GtkSatMap *satmap = GTK_SAT_MAP(widget); - GooCanvasItemModel *root; - gint idx; - guint i; + GtkSatMap *satmap = GTK_SAT_MAP(widget); /* check widget isn't already destroyed */ - if (satmap->obj) { + if (satmap->obj) + { /* save config */ gtk_sat_map_store_showtracks(GTK_SAT_MAP(widget)); gtk_sat_map_store_hidecovs(GTK_SAT_MAP(widget)); - /* sat objects need a reference to the widget to free all alocations */ + /* sat objects need a reference to the widget to free all allocations */ g_hash_table_foreach(satmap->obj, free_sat_obj, satmap); g_hash_table_destroy(satmap->obj); satmap->obj = NULL; - /* these objects destruct themselves cleanly */ - g_object_unref(satmap->origmap); - satmap->origmap = NULL; - g_hash_table_destroy(satmap->showtracks); - satmap->showtracks = NULL; - g_hash_table_destroy(satmap->hidecovs); - satmap->hidecovs = NULL; - - root = goo_canvas_get_root_item_model(GOO_CANVAS(satmap->canvas)); - - idx = goo_canvas_item_model_find_child(root, satmap->qthmark); - if (idx != -1) - goo_canvas_item_model_remove_child(root, idx); - satmap->qthmark = NULL; - - idx = goo_canvas_item_model_find_child(root, satmap->qthlabel); - if (idx != -1) - goo_canvas_item_model_remove_child(root, idx); - satmap->qthlabel = NULL; - - idx = goo_canvas_item_model_find_child(root, satmap->locnam); - if (idx != -1) - goo_canvas_item_model_remove_child(root, idx); - satmap->locnam = NULL; - - idx = goo_canvas_item_model_find_child(root, satmap->curs); - if (idx != -1) - goo_canvas_item_model_remove_child(root, idx); - satmap->curs = NULL; - - idx = goo_canvas_item_model_find_child(root, satmap->next); - if (idx != -1) - goo_canvas_item_model_remove_child(root, idx); - satmap->next = NULL; - - idx = goo_canvas_item_model_find_child(root, satmap->sel); - if (idx != -1) - goo_canvas_item_model_remove_child(root, idx); - satmap->sel = NULL; - - idx = goo_canvas_item_model_find_child(root, satmap->terminator); - if (idx != -1) - goo_canvas_item_model_remove_child(root, idx); - satmap->terminator = NULL; - - for (i = 0; i < 5; i++) + /* free the original map pixbuf */ + if (satmap->origmap) { - idx = goo_canvas_item_model_find_child(root, satmap->gridh[i]); - if (idx != -1) - goo_canvas_item_model_remove_child(root, idx); - satmap->gridh[i] = NULL; - - idx = goo_canvas_item_model_find_child(root, satmap->gridhlab[i]); - if (idx != -1) - goo_canvas_item_model_remove_child(root, idx); - satmap->gridhlab[i] = NULL; + g_object_unref(satmap->origmap); + satmap->origmap = NULL; } - for (i = 0; i < 11; i++) + /* free the scaled map pixbuf */ + if (satmap->map) { - idx = goo_canvas_item_model_find_child(root, satmap->gridv[i]); - if (idx != -1) - goo_canvas_item_model_remove_child(root, idx); - satmap->gridv[i] = NULL; - - idx = goo_canvas_item_model_find_child(root, satmap->gridvlab[i]); - if (idx != -1) - goo_canvas_item_model_remove_child(root, idx); - satmap->gridvlab[i] = NULL; + g_object_unref(satmap->map); + satmap->map = NULL; } - idx = goo_canvas_item_model_find_child(root, satmap->map); - if (idx != -1) - goo_canvas_item_model_remove_child(root, idx); - satmap->map = NULL; + g_hash_table_destroy(satmap->showtracks); + satmap->showtracks = NULL; + g_hash_table_destroy(satmap->hidecovs); + satmap->hidecovs = NULL; + + /* free text strings */ + g_free(satmap->locnam_text); + satmap->locnam_text = NULL; + g_free(satmap->curs_text); + satmap->curs_text = NULL; + g_free(satmap->next_text); + satmap->next_text = NULL; + g_free(satmap->sel_text); + satmap->sel_text = NULL; + g_free(satmap->font); + satmap->font = NULL; + g_free(satmap->infobgd); + satmap->infobgd = NULL; + + /* free terminator points */ + g_free(satmap->terminator_points); + satmap->terminator_points = NULL; + satmap->terminator_count = 0; + + /* free temporary point arrays */ + g_free(temp_points1); + temp_points1 = NULL; + g_free(temp_points2); + temp_points2 = NULL; } (*GTK_WIDGET_CLASS(parent_class)->destroy) (widget); } @@ -287,8 +268,8 @@ GtkWidget *gtk_sat_map_new(GKeyFile * cfgdata, GHashTable * sats, qth_t * qth) { GtkSatMap *satmap; - GooCanvasItemModel *root; guint32 col; + GValue font_value = G_VALUE_INIT; satmap = g_object_new(GTK_TYPE_SAT_MAP, NULL); @@ -338,23 +319,66 @@ GtkWidget *gtk_sat_map_new(GKeyFile * cfgdata, GHashTable * sats, SAT_CFG_BOOL_MAP_SHOW_GRID); satmap->show_terminator = mod_cfg_get_bool(cfgdata, - MOD_CFG_MAP_SECTION, - MOD_CFG_MAP_SHOW_TERMINATOR, - SAT_CFG_BOOL_MAP_SHOW_TERMINATOR); + MOD_CFG_MAP_SECTION, + MOD_CFG_MAP_SHOW_TERMINATOR, + SAT_CFG_BOOL_MAP_SHOW_TERMINATOR); satmap->keepratio = mod_cfg_get_bool(cfgdata, MOD_CFG_MAP_SECTION, MOD_CFG_MAP_KEEP_RATIO, SAT_CFG_BOOL_MAP_KEEP_RATIO); + col = mod_cfg_get_int(cfgdata, MOD_CFG_MAP_SECTION, MOD_CFG_MAP_INFO_BGD_COL, SAT_CFG_INT_MAP_INFO_BGD_COL); - satmap->infobgd = rgba2html(col); - satmap->canvas = goo_canvas_new(); - g_object_set(G_OBJECT(satmap->canvas), "has-tooltip", TRUE, NULL); + /* Load colors */ + satmap->col_qth = mod_cfg_get_int(cfgdata, + MOD_CFG_MAP_SECTION, + MOD_CFG_MAP_QTH_COL, + SAT_CFG_INT_MAP_QTH_COL); + satmap->col_info = mod_cfg_get_int(cfgdata, + MOD_CFG_MAP_SECTION, + MOD_CFG_MAP_INFO_COL, + SAT_CFG_INT_MAP_INFO_COL); + satmap->col_grid = mod_cfg_get_int(cfgdata, + MOD_CFG_MAP_SECTION, + MOD_CFG_MAP_GRID_COL, + SAT_CFG_INT_MAP_GRID_COL); + satmap->col_tick = satmap->col_grid; + satmap->col_sat = mod_cfg_get_int(cfgdata, + MOD_CFG_MAP_SECTION, + MOD_CFG_MAP_SAT_COL, + SAT_CFG_INT_MAP_SAT_COL); + satmap->col_sat_sel = mod_cfg_get_int(cfgdata, + MOD_CFG_MAP_SECTION, + MOD_CFG_MAP_SAT_SEL_COL, + SAT_CFG_INT_MAP_SAT_SEL_COL); + satmap->col_shadow = mod_cfg_get_int(cfgdata, + MOD_CFG_MAP_SECTION, + MOD_CFG_MAP_SHADOW_ALPHA, + SAT_CFG_INT_MAP_SHADOW_ALPHA); + satmap->col_track = mod_cfg_get_int(cfgdata, + MOD_CFG_MAP_SECTION, + MOD_CFG_MAP_TRACK_COL, + SAT_CFG_INT_MAP_TRACK_COL); + satmap->col_terminator = mod_cfg_get_int(cfgdata, + MOD_CFG_MAP_SECTION, + MOD_CFG_MAP_TERMINATOR_COL, + SAT_CFG_INT_MAP_TERMINATOR_COL); + + /* Get default font */ + g_value_init(&font_value, G_TYPE_STRING); + g_object_get_property(G_OBJECT(gtk_settings_get_default()), "gtk-font-name", + &font_value); + satmap->font = g_value_dup_string(&font_value); + g_value_unset(&font_value); + + /* Create drawing area canvas */ + satmap->canvas = gtk_drawing_area_new(); + gtk_widget_set_has_tooltip(satmap->canvas, TRUE); float clon = (float)mod_cfg_get_int(cfgdata, MOD_CFG_MAP_SECTION, @@ -363,31 +387,47 @@ GtkWidget *gtk_sat_map_new(GKeyFile * cfgdata, GHashTable * sats, load_map_file(satmap, clon); - /* Initial size request should be based on map size - but if we do this we can not shrink the canvas below this size - even though the map shrinks => better to set default size of - main container window. - */ - /* gtk_widget_set_size_request (satmap->canvas, */ - /* gdk_pixbuf_get_width (satmap->origmap), */ - /* gdk_pixbuf_get_height (satmap->origmap)); */ - - goo_canvas_set_bounds(GOO_CANVAS(satmap->canvas), 0, 0, - gdk_pixbuf_get_width(satmap->origmap), - gdk_pixbuf_get_height(satmap->origmap)); + /* Initial size */ + satmap->width = 200; + satmap->height = 100; + satmap->x0 = 0; + satmap->y0 = 0; + /* Allocate temporary point arrays for footprint calculation */ + temp_points1 = g_new0(gdouble, 720); + temp_points2 = g_new0(gdouble, 720); + + /* Connect signals */ + gtk_widget_add_events(satmap->canvas, GDK_POINTER_MOTION_MASK | + GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK); + + g_signal_connect(satmap->canvas, "draw", G_CALLBACK(on_draw), satmap); + g_signal_connect(satmap->canvas, "motion-notify-event", + G_CALLBACK(on_motion_notify), satmap); + g_signal_connect(satmap->canvas, "button-press-event", + G_CALLBACK(on_button_press), satmap); + g_signal_connect(satmap->canvas, "button-release-event", + G_CALLBACK(on_button_release), satmap); g_signal_connect(satmap->canvas, "size-allocate", G_CALLBACK(size_allocate_cb), satmap); - g_signal_connect(satmap->canvas, "item_created", - (GCallback) on_item_created, satmap); - g_signal_connect_after(satmap->canvas, "realize", - (GCallback) on_canvas_realized, satmap); gtk_widget_show(satmap->canvas); - root = create_canvas_model(satmap); - goo_canvas_set_root_item_model(GOO_CANVAS(satmap->canvas), root); - g_object_unref(root); + /* Initialize QTH info text */ + if (satmap->qthinfo) + { + satmap->locnam_text = g_strdup_printf(" %s \302\267 %s ", + satmap->infobgd, + satmap->qth->name, + satmap->qth->loc); + } + + /* Initialize next event text */ + if (satmap->eventinfo) + { + satmap->next_text = g_strdup_printf(" ... ", + satmap->infobgd); + } gtk_sat_map_load_showtracks(satmap); gtk_sat_map_load_hide_coverages(satmap); @@ -398,125 +438,402 @@ GtkWidget *gtk_sat_map_new(GKeyFile * cfgdata, GHashTable * sats, return GTK_WIDGET(satmap); } -static GooCanvasItemModel *create_canvas_model(GtkSatMap * satmap) +/** Draw callback for the canvas */ +static gboolean on_draw(GtkWidget * widget, cairo_t * cr, gpointer data) { - GooCanvasItemModel *root; - gchar *buff; + GtkSatMap *satmap = GTK_SAT_MAP(data); + gdouble r, g, b, a; + PangoLayout *layout; + PangoFontDescription *font_desc; + gint tw, th; + GHashTableIter iter; + gpointer key, value; + sat_map_obj_t *obj; gfloat x, y; - guint32 col; + gdouble xstep, ystep; + guint i; + gfloat lon, lat; + gchar *buf; + gchar hmf = ' '; + guint32 globe_shadow_col; + GSList *line_node; + gdouble *line_points; + guint num_points; - satmap->terminator = NULL; - root = goo_canvas_group_model_new(NULL, NULL); + (void)widget; - /* map dimensions */ - satmap->width = 200; // was: gdk_pixbuf_get_width (satmap->origmap); - satmap->height = 100; // was: gdk_pixbuf_get_height (satmap->origmap); - satmap->x0 = 0; - satmap->y0 = 0; + /* Draw background map */ + if (satmap->map) + { + gdk_cairo_set_source_pixbuf(cr, satmap->map, satmap->x0, satmap->y0); + cairo_paint(cr); + } - /* default font */ - g_object_get_property(G_OBJECT(gtk_settings_get_default()), "gtk-font-name", &satmap->font); + /* Set up font */ + layout = pango_cairo_create_layout(cr); + font_desc = pango_font_description_from_string(satmap->font ? satmap->font : "Sans 9"); + pango_layout_set_font_description(layout, font_desc); - /* background map */ - satmap->map = goo_canvas_image_model_new(root, - satmap->origmap, - satmap->x0, satmap->y0, NULL); + /* Draw grid lines if enabled */ + if (satmap->showgrid && satmap->width > 0 && satmap->height > 0) + { + rgba_to_cairo(satmap->col_grid, &r, &g, &b, &a); + cairo_set_source_rgba(cr, r, g, b, a); + cairo_set_line_width(cr, 0.5); + + xstep = (gdouble)(30.0 * satmap->width / 360.0); + ystep = (gdouble)(30.0 * satmap->height / 180.0); - goo_canvas_item_model_lower(satmap->map, NULL); - draw_grid_lines(satmap, root); + /* Horizontal grid lines */ + for (i = 0; i < 5; i++) + { + cairo_move_to(cr, (gdouble)satmap->x0, + (gdouble)(satmap->y0 + (i + 1) * ystep)); + cairo_line_to(cr, (gdouble)(satmap->x0 + satmap->width), + (gdouble)(satmap->y0 + (i + 1) * ystep)); + cairo_stroke(cr); + + /* Label */ + xy_to_lonlat(satmap, satmap->x0, satmap->y0 + (i + 1) * ystep, + &lon, &lat); + hmf = ' '; + if (sat_cfg_get_bool(SAT_CFG_BOOL_USE_NSEW)) + { + if (lat < 0.00) + { + lat = -lat; + hmf = 'S'; + } + else + { + hmf = 'N'; + } + } + buf = g_strdup_printf("%.0f\302\260%c", lat, hmf); + pango_layout_set_text(layout, buf, -1); + pango_layout_get_pixel_size(layout, &tw, &th); + cairo_move_to(cr, (gdouble)(satmap->x0 + 15), + (gdouble)(satmap->y0 + (i + 1) * ystep)); + pango_cairo_show_layout(cr, layout); + g_free(buf); + } - if (satmap->show_terminator) - draw_terminator(satmap, root); + /* Vertical grid lines */ + for (i = 0; i < 11; i++) + { + cairo_move_to(cr, (gdouble)(satmap->x0 + (i + 1) * xstep), + (gdouble)satmap->y0); + cairo_line_to(cr, (gdouble)(satmap->x0 + (i + 1) * xstep), + (gdouble)(satmap->y0 + satmap->height)); + cairo_stroke(cr); + + /* Label */ + xy_to_lonlat(satmap, satmap->x0 + (i + 1) * xstep, satmap->y0, + &lon, &lat); + hmf = ' '; + if (sat_cfg_get_bool(SAT_CFG_BOOL_USE_NSEW)) + { + if (lon < 0.00) + { + lon = -lon; + hmf = 'W'; + } + else + { + hmf = 'E'; + } + } + buf = g_strdup_printf("%.0f\302\260%c", lon, hmf); + pango_layout_set_text(layout, buf, -1); + pango_layout_get_pixel_size(layout, &tw, &th); + cairo_move_to(cr, (gdouble)(satmap->x0 + (i + 1) * xstep), + (gdouble)(satmap->y0 + satmap->height - 5 - th)); + pango_cairo_show_layout(cr, layout); + g_free(buf); + } + } - /* QTH mark */ - col = mod_cfg_get_int(satmap->cfgdata, - MOD_CFG_MAP_SECTION, - MOD_CFG_MAP_QTH_COL, SAT_CFG_INT_MAP_QTH_COL); + /* Draw terminator if enabled */ + if (satmap->show_terminator && satmap->terminator_points && + satmap->terminator_count > 2) + { + globe_shadow_col = mod_cfg_get_int(satmap->cfgdata, + MOD_CFG_MAP_SECTION, + MOD_CFG_MAP_GLOBAL_SHADOW_COL, + SAT_CFG_INT_MAP_GLOBAL_SHADOW_COL); + + rgba_to_cairo(globe_shadow_col, &r, &g, &b, &a); + cairo_set_source_rgba(cr, r, g, b, a); + + cairo_move_to(cr, satmap->terminator_points[0], + satmap->terminator_points[1]); + for (i = 1; i < (guint)satmap->terminator_count; i++) + { + cairo_line_to(cr, satmap->terminator_points[2 * i], + satmap->terminator_points[2 * i + 1]); + } + cairo_close_path(cr); + cairo_fill_preserve(cr); + + rgba_to_cairo(satmap->col_terminator, &r, &g, &b, &a); + cairo_set_source_rgba(cr, r, g, b, a); + cairo_set_line_width(cr, 1.0); + cairo_stroke(cr); + } + + /* Draw QTH marker */ lonlat_to_xy(satmap, satmap->qth->lon, satmap->qth->lat, &x, &y); - satmap->qthmark = goo_canvas_rect_model_new(root, - x - MARKER_SIZE_HALF, - y - MARKER_SIZE_HALF, - 2 * MARKER_SIZE_HALF, - 2 * MARKER_SIZE_HALF, - "fill-color-rgba", col, - "stroke-color-rgba", col, - NULL); - satmap->qthlabel = goo_canvas_text_model_new(root, satmap->qth->name, - x, y + 2, -1, - GOO_CANVAS_ANCHOR_NORTH, - "font", g_value_get_string(&satmap->font), - "fill-color-rgba", col, NULL); - - /* QTH info */ - col = mod_cfg_get_int(satmap->cfgdata, - MOD_CFG_MAP_SECTION, - MOD_CFG_MAP_INFO_COL, SAT_CFG_INT_MAP_INFO_COL); + rgba_to_cairo(satmap->col_qth, &r, &g, &b, &a); + cairo_set_source_rgba(cr, r, g, b, a); + cairo_rectangle(cr, x - MARKER_SIZE_HALF, y - MARKER_SIZE_HALF, + 2 * MARKER_SIZE_HALF, 2 * MARKER_SIZE_HALF); + cairo_fill(cr); + + /* Draw QTH label */ + pango_layout_set_text(layout, satmap->qth->name, -1); + pango_layout_get_pixel_size(layout, &tw, &th); + cairo_move_to(cr, x - tw / 2, y + 2); + pango_cairo_show_layout(cr, layout); + + /* Draw satellite objects */ + if (satmap->obj) + { + g_hash_table_iter_init(&iter, satmap->obj); + while (g_hash_table_iter_next(&iter, &key, &value)) + { + obj = SAT_MAP_OBJ(value); - satmap->locnam = goo_canvas_text_model_new(root, "", - satmap->x0 + 2, satmap->y0 + 1, - -1, - GOO_CANVAS_ANCHOR_NORTH_WEST, - "font", g_value_get_string(&satmap->font), - "fill-color-rgba", col, - "use-markup", TRUE, NULL); + /* Draw ground track if enabled */ + if (obj->showtrack && obj->track_data.lines) + { + rgba_to_cairo(satmap->col_track, &r, &g, &b, &a); + cairo_set_source_rgba(cr, r, g, b, a); + cairo_set_line_width(cr, 1.0); - /* set text only if QTH info is enabled */ - if (satmap->qthinfo) + line_node = obj->track_data.lines; + while (line_node) + { + line_points = (gdouble *)line_node->data; + if (line_points) + { + num_points = (guint)line_points[0]; + if (num_points > 1) + { + cairo_move_to(cr, line_points[1], line_points[2]); + for (i = 1; i < num_points; i++) + { + cairo_line_to(cr, line_points[2 * i + 1], + line_points[2 * i + 2]); + } + cairo_stroke(cr); + } + } + line_node = line_node->next; + } + } + + /* Check visibility conditions */ + gboolean show_fp = satmap->satfp || obj->selected; + gboolean show_marker = satmap->satmarker || obj->selected; + gboolean show_label = satmap->satname || obj->selected; + + /* Draw range circle(s) / footprint */ + if (show_fp && obj->showcov) + { + guint32 covcol = mod_cfg_get_int(satmap->cfgdata, + MOD_CFG_MAP_SECTION, + MOD_CFG_MAP_SAT_COV_COL, + SAT_CFG_INT_MAP_SAT_COV_COL); + + /* Draw first range circle */ + if (obj->range1_points && obj->range1_count > 2) + { + rgba_to_cairo(covcol, &r, &g, &b, &a); + cairo_set_source_rgba(cr, r, g, b, a); + + cairo_move_to(cr, obj->range1_points[0], + obj->range1_points[1]); + for (i = 1; i < (guint)obj->range1_count; i++) + { + cairo_line_to(cr, obj->range1_points[2 * i], + obj->range1_points[2 * i + 1]); + } + cairo_close_path(cr); + cairo_fill_preserve(cr); + + if (obj->selected) + rgba_to_cairo(satmap->col_sat_sel, &r, &g, &b, &a); + else + rgba_to_cairo(satmap->col_sat, &r, &g, &b, &a); + cairo_set_source_rgba(cr, r, g, b, a); + cairo_set_line_width(cr, 1.0); + cairo_stroke(cr); + } + + /* Draw second range circle if present */ + if (obj->range2_points && obj->range2_count > 2) + { + rgba_to_cairo(covcol, &r, &g, &b, &a); + cairo_set_source_rgba(cr, r, g, b, a); + + cairo_move_to(cr, obj->range2_points[0], + obj->range2_points[1]); + for (i = 1; i < (guint)obj->range2_count; i++) + { + cairo_line_to(cr, obj->range2_points[2 * i], + obj->range2_points[2 * i + 1]); + } + cairo_close_path(cr); + cairo_fill_preserve(cr); + + if (obj->selected) + rgba_to_cairo(satmap->col_sat_sel, &r, &g, &b, &a); + else + rgba_to_cairo(satmap->col_sat, &r, &g, &b, &a); + cairo_set_source_rgba(cr, r, g, b, a); + cairo_set_line_width(cr, 1.0); + cairo_stroke(cr); + } + } + + /* Draw satellite marker shadow */ + if (show_marker) + { + rgba_to_cairo(satmap->col_shadow, &r, &g, &b, &a); + cairo_set_source_rgba(cr, 0, 0, 0, a); + cairo_rectangle(cr, obj->x - MARKER_SIZE_HALF + 1, + obj->y - MARKER_SIZE_HALF + 1, + 2 * MARKER_SIZE_HALF, 2 * MARKER_SIZE_HALF); + cairo_fill(cr); + } + + /* Draw satellite marker */ + if (show_marker) + { + if (obj->selected) + rgba_to_cairo(satmap->col_sat_sel, &r, &g, &b, &a); + else + rgba_to_cairo(satmap->col_sat, &r, &g, &b, &a); + cairo_set_source_rgba(cr, r, g, b, a); + cairo_rectangle(cr, obj->x - MARKER_SIZE_HALF, + obj->y - MARKER_SIZE_HALF, + 2 * MARKER_SIZE_HALF, 2 * MARKER_SIZE_HALF); + cairo_fill(cr); + } + + /* Draw satellite label shadow */ + if (show_label && obj->nickname) + { + rgba_to_cairo(satmap->col_shadow, &r, &g, &b, &a); + cairo_set_source_rgba(cr, 0, 0, 0, a); + pango_layout_set_text(layout, obj->nickname, -1); + pango_layout_get_pixel_size(layout, &tw, &th); + + if (obj->x < 50) + cairo_move_to(cr, obj->x + 3 + 1, obj->y + 1); + else if ((satmap->width - obj->x) < 50) + cairo_move_to(cr, obj->x - 3 - tw + 1, obj->y + 1); + else if ((satmap->height - obj->y) < 25) + cairo_move_to(cr, obj->x - tw / 2 + 1, obj->y - 2 - th + 1); + else + cairo_move_to(cr, obj->x - tw / 2 + 1, obj->y + 2 + 1); + pango_cairo_show_layout(cr, layout); + } + + /* Draw satellite label */ + if (show_label && obj->nickname) + { + if (obj->selected) + rgba_to_cairo(satmap->col_sat_sel, &r, &g, &b, &a); + else + rgba_to_cairo(satmap->col_sat, &r, &g, &b, &a); + cairo_set_source_rgba(cr, r, g, b, a); + pango_layout_set_text(layout, obj->nickname, -1); + pango_layout_get_pixel_size(layout, &tw, &th); + + if (obj->x < 50) + cairo_move_to(cr, obj->x + 3, obj->y); + else if ((satmap->width - obj->x) < 50) + cairo_move_to(cr, obj->x - 3 - tw, obj->y); + else if ((satmap->height - obj->y) < 25) + cairo_move_to(cr, obj->x - tw / 2, obj->y - 2 - th); + else + cairo_move_to(cr, obj->x - tw / 2, obj->y + 2); + pango_cairo_show_layout(cr, layout); + } + } + } + + /* Draw info text elements */ + rgba_to_cairo(satmap->col_info, &r, &g, &b, &a); + cairo_set_source_rgba(cr, r, g, b, a); + + /* QTH info (top-left) */ + if (satmap->qthinfo && satmap->locnam_text) { - /* For now only QTH name and location. - It would be nice with coordinates (remember NWSE setting) - and Maidenhead locator, when using hamlib. - - Note: I used pango markup to set the background color, I didn't find any - other obvious ways to get the text height in pixels to draw rectangle. - */ - buff = - g_strdup_printf(" %s \302\267 %s ", - satmap->infobgd, satmap->qth->name, - satmap->qth->loc); - g_object_set(satmap->locnam, "text", buff, NULL); - g_free(buff); - } - - /* next event */ - satmap->next = goo_canvas_text_model_new(root, "", - satmap->x0 + satmap->width - 2, - satmap->y0 + 1, -1, - GOO_CANVAS_ANCHOR_NORTH_EAST, - "font", g_value_get_string(&satmap->font), - "fill-color-rgba", col, - "use-markup", TRUE, NULL); - - /* set text only if QTH info is enabled */ - if (satmap->eventinfo) + pango_layout_set_markup(layout, satmap->locnam_text, -1); + cairo_move_to(cr, (gdouble)satmap->x0 + 2, (gdouble)satmap->y0 + 1); + pango_cairo_show_layout(cr, layout); + } + + /* Next event (top-right) */ + if (satmap->eventinfo && satmap->next_text) + { + pango_layout_set_markup(layout, satmap->next_text, -1); + pango_layout_get_pixel_size(layout, &tw, &th); + cairo_move_to(cr, (gdouble)(satmap->x0 + satmap->width - 2 - tw), + (gdouble)satmap->y0 + 1); + pango_cairo_show_layout(cr, layout); + } + + /* Cursor tracking (bottom-left) */ + if (satmap->cursinfo && satmap->curs_text) + { + pango_layout_set_markup(layout, satmap->curs_text, -1); + pango_layout_get_pixel_size(layout, &tw, &th); + cairo_move_to(cr, (gdouble)satmap->x0 + 2, + (gdouble)(satmap->y0 + satmap->height - 1 - th)); + pango_cairo_show_layout(cr, layout); + } + + /* Selected satellite info (bottom-right) */ + if (satmap->sel_text) + { + pango_layout_set_markup(layout, satmap->sel_text, -1); + pango_layout_get_pixel_size(layout, &tw, &th); + cairo_move_to(cr, (gdouble)(satmap->x0 + satmap->width - 2 - tw), + (gdouble)(satmap->y0 + satmap->height - 1 - th)); + pango_cairo_show_layout(cr, layout); + } + + pango_font_description_free(font_desc); + g_object_unref(layout); + + return FALSE; +} + +/** Find satellite object at given position */ +static sat_map_obj_t *find_sat_at_pos(GtkSatMap * satmap, gfloat mx, gfloat my) +{ + GHashTableIter iter; + gpointer key, value; + sat_map_obj_t *obj; + gfloat dx, dy; + const gfloat hit_radius = 10.0; + + if (satmap->obj == NULL) + return NULL; + + g_hash_table_iter_init(&iter, satmap->obj); + while (g_hash_table_iter_next(&iter, &key, &value)) { - buff = g_strdup_printf(" ... ", - satmap->infobgd); - g_object_set(satmap->next, "text", buff, NULL); - g_free(buff); - } - - /* cursor track */ - satmap->curs = goo_canvas_text_model_new(root, "", - satmap->x0 + 2, - satmap->y0 + satmap->height - 1, - -1, - GOO_CANVAS_ANCHOR_SOUTH_WEST, - "font", g_value_get_string(&satmap->font), - "fill-color-rgba", col, - "use-markup", TRUE, NULL); - - /* info about a selected satellite */ - satmap->sel = goo_canvas_text_model_new(root, "", - satmap->x0 + satmap->width - 2, - satmap->y0 + satmap->height - 1, - -1, - GOO_CANVAS_ANCHOR_SOUTH_EAST, - "font", g_value_get_string(&satmap->font), - "fill-color-rgba", col, - "use-markup", TRUE, NULL); - - return root; + obj = SAT_MAP_OBJ(value); + dx = mx - obj->x; + dy = my - obj->y; + if (dx * dx + dy * dy < hit_radius * hit_radius) + return obj; + } + return NULL; } static void size_allocate_cb(GtkWidget * widget, GtkAllocation * allocation, @@ -531,32 +848,25 @@ static void update_map_size(GtkSatMap * satmap) { GtkAllocation allocation; GdkPixbuf *pbuf; - gfloat x, y; - gfloat ratio; /* ratio between map width and height */ - gfloat size; /* size = min (alloc.w, ratio*alloc.h) */ + gfloat ratio; if (gtk_widget_get_realized(GTK_WIDGET(satmap))) { - /* get graph dimensions */ gtk_widget_get_allocation(GTK_WIDGET(satmap), &allocation); if (satmap->keepratio) { - /* Use allocation->width and allocation->height to calculate - * new X0 Y0 width and height. Map proportions must be kept. - */ - ratio = gdk_pixbuf_get_width(satmap->origmap) / - gdk_pixbuf_get_height(satmap->origmap); + ratio = (gfloat)gdk_pixbuf_get_width(satmap->origmap) / + (gfloat)gdk_pixbuf_get_height(satmap->origmap); - size = MIN(allocation.width, ratio * allocation.height); + gfloat size = MIN(allocation.width, ratio * allocation.height); - satmap->width = (guint) size; - satmap->height = (guint) (size / ratio); + satmap->width = (guint)size; + satmap->height = (guint)(size / ratio); satmap->x0 = (allocation.width - satmap->width) / 2; satmap->y0 = (allocation.height - satmap->height) / 2; - /* rescale pixbuf */ pbuf = gdk_pixbuf_scale_simple(satmap->origmap, satmap->width, satmap->height, @@ -569,70 +879,26 @@ static void update_map_size(GtkSatMap * satmap) satmap->width = allocation.width; satmap->height = allocation.height; - /* rescale pixbuf */ pbuf = gdk_pixbuf_scale_simple(satmap->origmap, satmap->width, satmap->height, GDK_INTERP_BILINEAR); } - /* set canvas bounds to match new size */ - goo_canvas_set_bounds(GOO_CANVAS(GTK_SAT_MAP(satmap)->canvas), 0, 0, - satmap->width, satmap->height); - - - /* redraw static elements */ - g_object_set(satmap->map, - "pixbuf", pbuf, - "x", (gdouble) satmap->x0, - "y", (gdouble) satmap->y0, NULL); - g_object_unref(pbuf); - - redraw_grid_lines(satmap); + if (satmap->map) + g_object_unref(satmap->map); + satmap->map = pbuf; if (satmap->show_terminator) redraw_terminator(satmap); - lonlat_to_xy(satmap, satmap->qth->lon, satmap->qth->lat, &x, &y); - g_object_set(satmap->qthmark, - "x", x - MARKER_SIZE_HALF, - "y", y - MARKER_SIZE_HALF, NULL); - g_object_set(satmap->qthlabel, "x", x, "y", y + 2, NULL); - - g_object_set(satmap->locnam, - "x", (gdouble) satmap->x0 + 2, - "y", (gdouble) satmap->y0 + 1, NULL); - - g_object_set(satmap->next, - "x", (gdouble) satmap->x0 + satmap->width - 2, - "y", (gdouble) satmap->y0 + 1, NULL); - - g_object_set(satmap->curs, - "x", (gdouble) satmap->x0 + 2, - "y", (gdouble) satmap->y0 + satmap->height - 1, NULL); - - g_object_set(satmap->sel, - "x", (gdouble) satmap->x0 + satmap->width - 2, - "y", (gdouble) satmap->y0 + satmap->height - 1, NULL); - g_hash_table_foreach(satmap->sats, update_sat, satmap); satmap->resize = FALSE; - } -} - -static void on_canvas_realized(GtkWidget * canvas, gpointer data) -{ - GtkSatMap *satmap = GTK_SAT_MAP(data); - - (void)canvas; - goo_canvas_item_model_raise(satmap->sel, NULL); - goo_canvas_item_model_raise(satmap->locnam, NULL); - goo_canvas_item_model_raise(satmap->next, NULL); - goo_canvas_item_model_raise(satmap->curs, NULL); + gtk_widget_queue_draw(satmap->canvas); + } } -/* Update the GtkSatMap widget. Called periodically from GtkSatModule. */ void gtk_sat_map_update(GtkWidget * widget) { GtkSatMap *satmap = GTK_SAT_MAP(widget); @@ -642,30 +908,11 @@ void gtk_sat_map_update(GtkWidget * widget) gint *catnr; guint h, m, s; gchar *ch, *cm, *cs; - gfloat x, y; - gdouble oldx, oldy; /* check whether there are any pending resize requests */ if (satmap->resize) update_map_size(satmap); - /* check if qth has moved significantly, if so move it */ - lonlat_to_xy(satmap, satmap->qth->lon, satmap->qth->lat, &x, &y); - g_object_get(satmap->qthmark, "x", &oldx, "y", &oldy, NULL); - - if ((fabs(oldx - x) >= 2 * MARKER_SIZE_HALF) || - (fabs(oldy - y) >= 2 * MARKER_SIZE_HALF)) - { - g_object_set(satmap->qthmark, - "x", x - MARKER_SIZE_HALF, - "y", y - MARKER_SIZE_HALF, NULL); - g_object_set(satmap->qthlabel, "x", x, "y", y + 2, NULL); - g_object_set(satmap->locnam, - "x", (gdouble) satmap->x0 + 2, - "y", (gdouble) satmap->y0 + 1, NULL); - satmap->counter = satmap->refresh; - } - /* check refresh rate and refresh sats/qth if time */ /* FIXME add location check */ if (satmap->counter < satmap->refresh) @@ -679,15 +926,6 @@ void gtk_sat_map_update(GtkWidget * widget) satmap->naos = 0.0; satmap->ncat = 0; - lonlat_to_xy(satmap, satmap->qth->lon, satmap->qth->lat, &x, &y); - g_object_set(satmap->qthmark, - "x", x - MARKER_SIZE_HALF, - "y", y - MARKER_SIZE_HALF, NULL); - g_object_set(satmap->qthlabel, "x", x, "y", y + 2, NULL); - g_object_set(satmap->locnam, - "x", (gdouble) satmap->x0 + 2, - "y", (gdouble) satmap->y0 + 1, NULL); - g_hash_table_foreach(satmap->sats, update_sat, satmap); /* Update the Solar Terminator if necessary */ @@ -715,10 +953,10 @@ void gtk_sat_map_update(GtkWidget * widget) number = satmap->naos - now; /* convert julian date to seconds */ - s = (guint) (number * 86400); + s = (guint)(number * 86400); /* extract hours */ - h = (guint) floor(s / 3600); + h = (guint)floor(s / 3600); s -= 3600 * h; /* leading zero */ @@ -728,7 +966,7 @@ void gtk_sat_map_update(GtkWidget * widget) ch = g_strdup(""); /* extract minutes */ - m = (guint) floor(s / 60); + m = (guint)floor(s / 60); s -= 60 * m; /* leading zero */ @@ -743,26 +981,22 @@ void gtk_sat_map_update(GtkWidget * widget) else cs = g_strdup(":"); + g_free(satmap->next_text); if (h > 0) - buff = - g_markup_printf_escaped(_ - (" " - "Next: %s in %s%d:%s%d%s%d "), - satmap->infobgd, - sat->nickname, ch, h, cm, - m, cs, s); + buff = g_markup_printf_escaped( + _(" " + "Next: %s in %s%d:%s%d%s%d "), + satmap->infobgd, + sat->nickname, ch, h, cm, m, cs, s); else - buff = - g_markup_printf_escaped(_ - (" " - "Next: %s in %s%d%s%d "), - satmap->infobgd, - sat->nickname, cm, m, cs, - s); + buff = g_markup_printf_escaped( + _(" " + "Next: %s in %s%d%s%d "), + satmap->infobgd, + sat->nickname, cm, m, cs, s); - g_object_set(satmap->next, "text", buff, NULL); + satmap->next_text = buff; - g_free(buff); g_free(ch); g_free(cm); g_free(cs); @@ -772,35 +1006,35 @@ void gtk_sat_map_update(GtkWidget * widget) sat_log_log(SAT_LOG_LEVEL_ERROR, _("%s: Can not find NEXT satellite."), __func__); - g_object_set(satmap->next, "text", _("Next: ERR"), NULL); + g_free(satmap->next_text); + satmap->next_text = g_strdup(_("Next: ERR")); } } else { - g_object_set(satmap->next, "text", _("Next: N/A"), NULL); + g_free(satmap->next_text); + satmap->next_text = g_strdup(_("Next: N/A")); } } else { - g_object_set(satmap->next, "text", "", NULL); + g_free(satmap->next_text); + satmap->next_text = NULL; } + + gtk_widget_queue_draw(satmap->canvas); } } -/* Assumes that -180 <= lon <= 180 and -90 <= lat <= 90 */ static void lonlat_to_xy(GtkSatMap * p, gdouble lon, gdouble lat, gfloat * x, gfloat * y) { *x = p->x0 + (lon - p->left_side_lon) * p->width / 360.0; *y = p->y0 + (90.0 - lat) * p->height / 180.0; while (*x < 0) - { *x += p->width; - } while (*x > p->width) - { *x -= p->width; - } } static void xy_to_lonlat(GtkSatMap * p, gfloat x, gfloat y, gfloat * lon, @@ -809,123 +1043,75 @@ static void xy_to_lonlat(GtkSatMap * p, gfloat x, gfloat y, gfloat * lon, *lat = 90.0 - (180.0 / p->height) * (y - p->y0); *lon = (360.0 / p->width) * (x - p->x0) + p->left_side_lon; while (*lon > 180) - { *lon -= 360; - } while (*lon < -180) - { *lon += 360; - } } -static gboolean on_motion_notify(GooCanvasItem * item, - GooCanvasItem * target, - GdkEventMotion * event, gpointer data) +static gboolean on_motion_notify(GtkWidget * widget, GdkEventMotion * event, + gpointer data) { GtkSatMap *satmap = GTK_SAT_MAP(data); gfloat lat, lon; - gchar *text; - (void)target; - (void)item; + (void)widget; - /* set text only if QTH info is enabled */ if (satmap->cursinfo) { xy_to_lonlat(satmap, event->x, event->y, &lon, &lat); - /* cursor track */ - text = g_strdup_printf(" " - "LON:%.0f\302\260 LAT:%.0f\302\260 ", - satmap->infobgd, lon, lat); + g_free(satmap->curs_text); + satmap->curs_text = g_strdup_printf( + " " + "LON:%.0f\302\260 LAT:%.0f\302\260 ", + satmap->infobgd, lon, lat); - g_object_set(satmap->curs, "text", text, NULL); - g_free(text); + gtk_widget_queue_draw(satmap->canvas); } return TRUE; } -/* - * Finish canvas item setup. - * - * This function is called when a canvas item is created. Its purpose is to connect - * the corresponding signals to the created items. - * - * The root item, ie the background is connected to motion notify event, while other - * items (sats) are connected to mouse click events. - * - * @bug Should filter out QTH or do we want to click on the QTH? - */ -static void on_item_created(GooCanvas * canvas, - GooCanvasItem * item, - GooCanvasItemModel * model, gpointer data) -{ - (void)canvas; - if (!goo_canvas_item_model_get_parent(model)) - { - /* root item / canvas */ - g_signal_connect(item, "motion_notify_event", - (GCallback) on_motion_notify, data); - } - else if (!g_object_get_data(G_OBJECT(item), "skip-signal-connection")) - { - g_signal_connect(item, "button_press_event", - (GCallback) on_button_press, data); - g_signal_connect(item, "button_release_event", - (GCallback) on_button_release, data); - } -} - -static gboolean on_button_press(GooCanvasItem * item, - GooCanvasItem * target, GdkEventButton * event, +static gboolean on_button_press(GtkWidget * widget, GdkEventButton * event, gpointer data) { - GooCanvasItemModel *model = goo_canvas_item_get_model(item); GtkSatMap *satmap = GTK_SAT_MAP(data); - gint catnum = - GPOINTER_TO_INT(g_object_get_data(G_OBJECT(model), "catnum")); + sat_map_obj_t *obj; gint *catpoint = NULL; sat_t *sat = NULL; - (void)target; + (void)widget; + + obj = find_sat_at_pos(satmap, event->x, event->y); + + if (obj == NULL) + return FALSE; switch (event->button) { - /* double-left-click */ case 1: if (event->type == GDK_2BUTTON_PRESS) { catpoint = g_try_new0(gint, 1); - *catpoint = catnum; + *catpoint = obj->catnum; sat = SAT(g_hash_table_lookup(satmap->sats, catpoint)); if (sat != NULL) { show_sat_info(sat, gtk_widget_get_toplevel(GTK_WIDGET(data))); } - else - { - /* double-clicked on map */ - } + g_free(catpoint); } - g_free(catpoint); break; - /* pop-up menu */ case 3: catpoint = g_try_new0(gint, 1); - *catpoint = catnum; + *catpoint = obj->catnum; sat = SAT(g_hash_table_lookup(satmap->sats, catpoint)); if (sat != NULL) { gtk_sat_map_popup_exec(sat, satmap->qth, satmap, event, - gtk_widget_get_toplevel(GTK_WIDGET - (satmap))); - } - else - { - /* clicked on map -> map pop-up in the future */ + gtk_widget_get_toplevel(GTK_WIDGET(satmap))); } g_free(catpoint); break; @@ -936,81 +1122,42 @@ static gboolean on_button_press(GooCanvasItem * item, return TRUE; } -static gboolean on_button_release(GooCanvasItem * item, - GooCanvasItem * target, - GdkEventButton * event, gpointer data) +static gboolean on_button_release(GtkWidget * widget, GdkEventButton * event, + gpointer data) { - GooCanvasItemModel *model = goo_canvas_item_get_model(item); GtkSatMap *satmap = GTK_SAT_MAP(data); - gint catnum = - GPOINTER_TO_INT(g_object_get_data(G_OBJECT(model), "catnum")); - gint *catpoint = NULL; sat_map_obj_t *obj = NULL; - guint32 col; + gint *catpoint; - (void)target; + (void)widget; - catpoint = g_try_new0(gint, 1); - *catpoint = catnum; + if (event->button != 1) + return FALSE; - switch (event->button) - { - /* Select / de-select satellite */ - case 1: - obj = SAT_MAP_OBJ(g_hash_table_lookup(satmap->obj, catpoint)); - if (obj == NULL) - { - sat_log_log(SAT_LOG_LEVEL_ERROR, - _ - ("%s:%d: Can not find clicked object (%d) in hash table"), - __FILE__, __LINE__, catnum); - } - else - { - obj->selected = !obj->selected; + obj = find_sat_at_pos(satmap, event->x, event->y); - if (obj->selected) - { - col = mod_cfg_get_int(satmap->cfgdata, - MOD_CFG_MAP_SECTION, - MOD_CFG_MAP_SAT_SEL_COL, - SAT_CFG_INT_MAP_SAT_SEL_COL); - } - else - { - col = mod_cfg_get_int(satmap->cfgdata, - MOD_CFG_MAP_SECTION, - MOD_CFG_MAP_SAT_COL, - SAT_CFG_INT_MAP_SAT_COL); - *catpoint = 0; + if (obj == NULL) + return FALSE; - g_object_set(satmap->sel, "text", "", NULL); - } + obj->selected = !obj->selected; - g_object_set(obj->marker, - "fill-color-rgba", col, - "stroke-color-rgba", col, NULL); - g_object_set(obj->label, - "fill-color-rgba", col, - "stroke-color-rgba", col, NULL); - g_object_set(obj->range1, "stroke-color-rgba", col, NULL); - - if (obj->oldrcnum == 2) - g_object_set(obj->range2, "stroke-color-rgba", col, NULL); - - /* clear other selections */ - g_hash_table_foreach(satmap->obj, clear_selection, catpoint); - - /* update all satellites to update marker visibilities */ - g_hash_table_foreach(satmap->sats, update_sat, satmap); - } - break; - default: - break; + catpoint = g_try_new0(gint, 1); + *catpoint = obj->catnum; + + if (!obj->selected) + { + g_free(satmap->sel_text); + satmap->sel_text = NULL; + *catpoint = 0; } + g_hash_table_foreach(satmap->obj, clear_selection, catpoint); + g_hash_table_foreach(satmap->sats, update_sat, satmap); + g_free(catpoint); + gtk_widget_queue_draw(satmap->canvas); + return TRUE; } @@ -1019,20 +1166,10 @@ static void clear_selection(gpointer key, gpointer val, gpointer data) gint *old = key; gint *new = data; sat_map_obj_t *obj = SAT_MAP_OBJ(val); - guint32 col; if ((*old != *new) && (obj->selected)) { obj->selected = FALSE; - - /** FIXME: this is only global default; need the satmap here! */ - col = sat_cfg_get_int(SAT_CFG_INT_MAP_SAT_COL); - - g_object_set(obj->marker, "fill-color-rgba", col, "stroke-color-rgba", col, NULL); - g_object_set(obj->label, "fill-color-rgba", col, "stroke-color-rgba", col, NULL); - g_object_set(obj->range1, "stroke-color-rgba", col, NULL); - if (obj->oldrcnum == 2) - g_object_set(obj->range2, "stroke-color-rgba", col, NULL); } } @@ -1041,7 +1178,6 @@ void gtk_sat_map_select_sat(GtkWidget * satmap, gint catnum) GtkSatMap *smap = GTK_SAT_MAP(satmap); gint *catpoint = NULL; sat_map_obj_t *obj = NULL; - guint32 col; catpoint = g_try_new0(gint, 1); *catpoint = catnum; @@ -1056,64 +1192,20 @@ void gtk_sat_map_select_sat(GtkWidget * satmap, gint catnum) else { obj->selected = TRUE; - - col = mod_cfg_get_int(smap->cfgdata, - MOD_CFG_MAP_SECTION, - MOD_CFG_MAP_SAT_SEL_COL, - SAT_CFG_INT_MAP_SAT_SEL_COL); - - g_object_set(obj->marker, - "fill-color-rgba", col, "stroke-color-rgba", col, NULL); - g_object_set(obj->label, - "fill-color-rgba", col, "stroke-color-rgba", col, NULL); - g_object_set(obj->range1, "stroke-color-rgba", col, NULL); - - if (obj->oldrcnum == 2) - g_object_set(obj->range2, "stroke-color-rgba", col, NULL); - - /* clear other selections */ g_hash_table_foreach(smap->obj, clear_selection, catpoint); - - /* update all satellites to update marker visibilities */ g_hash_table_foreach(smap->sats, update_sat, smap); + gtk_widget_queue_draw(smap->canvas); } g_free(catpoint); } -/* - * Reconfigure map. - * - * This function should eventually reload all configuration for the GtkSatMap. - * Currently this function is not implemented for any of the views. Reconfiguration - * is done by recreating the whole module. - */ void gtk_sat_map_reconf(GtkWidget * widget, GKeyFile * cfgdat) { (void)widget; (void)cfgdat; } -/* - * Safely load a map file. - * - * @param satmap The GtkSatMap widget - * @param clon The longitude that should be the center of the map - * - * This function is called shortly after the canvas has been created. Its purpose - * is to load a mapfile into satmap->origmap. - * - * The function ensures that satmap->origmap will contain a valid GdkPixpuf, by - * using the following logic: - * - * - Get either module specific or global map file using mod_cfg_get_str - * - If the returned file does not exist try sat_cfg_get_str_def - * - If loading of default map does not succeed, create a dummy GdkPixbuf - * (and raise all possible alarms) - * - * @note satmap->cfgdata should contain a valid GKeyFile. - * - */ static void load_map_file(GtkSatMap * satmap, float clon) { gchar *buff; @@ -1121,19 +1213,16 @@ static void load_map_file(GtkSatMap * satmap, float clon) GError *error = NULL; GdkPixbuf *tmpbuf; - /* get local, global or default map file */ buff = mod_cfg_get_str(satmap->cfgdata, MOD_CFG_MAP_SECTION, MOD_CFG_MAP_FILE, SAT_CFG_STR_MAP_FILE); if (g_path_is_absolute(buff)) { - /* map is user specific, ie. in $HOME/.gpredict2/maps/ */ mapfile = g_strdup(buff); } else { - /* build complete path */ mapfile = map_file_name(buff); } g_free(buff); @@ -1141,7 +1230,6 @@ static void load_map_file(GtkSatMap * satmap, float clon) sat_log_log(SAT_LOG_LEVEL_DEBUG, _("%s:%d: Loading map file %s"), __FILE__, __LINE__, mapfile); - /* check that file exists, if not get the default */ if (g_file_test(mapfile, G_FILE_TEST_EXISTS)) { sat_log_log(SAT_LOG_LEVEL_DEBUG, @@ -1153,7 +1241,6 @@ static void load_map_file(GtkSatMap * satmap, float clon) _("%s:%d: Could not find map file %s"), __FILE__, __LINE__, mapfile); - /* get default map file */ g_free(mapfile); mapfile = sat_cfg_get_str_def(SAT_CFG_STR_MAP_FILE); @@ -1162,7 +1249,6 @@ static void load_map_file(GtkSatMap * satmap, float clon) __FILE__, __LINE__, mapfile); } - /* try to load the map file */ tmpbuf = gdk_pixbuf_new_from_file(mapfile, &error); if (error != NULL) @@ -1172,12 +1258,10 @@ static void load_map_file(GtkSatMap * satmap, float clon) __FILE__, __LINE__, error->message); g_clear_error(&error); - /* create a dummy GdkPixbuf to avoid crash */ tmpbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, 400, 200); gdk_pixbuf_fill(tmpbuf, 0x0F0F0F0F); } - /* create a blank map with same parameters as tmpbuf */ satmap->origmap = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, gdk_pixbuf_get_bits_per_sample(tmpbuf), @@ -1188,7 +1272,6 @@ static void load_map_file(GtkSatMap * satmap, float clon) g_object_unref(tmpbuf); g_free(mapfile); - /* Calculate longitude at the left side (-180 deg if center is at 0 deg longitude) */ satmap->left_side_lon = -180.0; if (clon > 0.0) satmap->left_side_lon += clon; @@ -1209,7 +1292,6 @@ static gdouble arccos(gdouble x, gdouble y) return 0.0; } -/* Check whether the footprint covers the North or South pole. */ static gboolean pole_is_covered(sat_t * sat) { if (north_pole_is_covered(sat) || south_pole_is_covered(sat)) @@ -1218,7 +1300,6 @@ static gboolean pole_is_covered(sat_t * sat) return FALSE; } -/* Check whether the footprint covers the North pole. */ static gboolean north_pole_is_covered(sat_t * sat) { int ret1; @@ -1238,7 +1319,6 @@ static gboolean north_pole_is_covered(sat_t * sat) return FALSE; } -/* Check whether the footprint covers the South pole. */ static gboolean south_pole_is_covered(sat_t * sat) { int ret1; @@ -1258,14 +1338,12 @@ static gboolean south_pole_is_covered(sat_t * sat) return FALSE; } -/* Mirror the footprint longitude. */ static gboolean mirror_lon(sat_t * sat, gdouble rangelon, gdouble * mlon, gdouble mapbreak) { gdouble diff; gboolean warped = FALSE; - /* make it so rangelon is on left of ssplon */ diff = (sat->ssplon - rangelon); while (diff < 0) diff += 360; @@ -1277,7 +1355,7 @@ static gboolean mirror_lon(sat_t * sat, gdouble rangelon, gdouble * mlon, *mlon -= 360; while (*mlon < -180) *mlon += 360; - //printf("Something %s %f %f %f\n",sat->nickname, sat->ssplon, rangelon,mapbreak); + if (((sat->ssplon >= mapbreak) && (sat->ssplon < mapbreak + 180)) || ((sat->ssplon < mapbreak - 180) && (sat->ssplon >= mapbreak - 360))) { @@ -1288,7 +1366,6 @@ static gboolean mirror_lon(sat_t * sat, gdouble rangelon, gdouble * mlon, else { warped = TRUE; - //printf ("sat %s warped for first \n",sat->nickname); } } else @@ -1297,48 +1374,14 @@ static gboolean mirror_lon(sat_t * sat, gdouble rangelon, gdouble * mlon, ((*mlon < mapbreak - 180) && (*mlon >= mapbreak - 360))) { warped = TRUE; - //printf ("sat %s warped for second \n",sat->nickname); } } return warped; } -/** - * Calculate satellite footprint and coverage area. - * - * @param satmap TheGtkSatMap widget. - * @param sat The satellite. - * @param points1 Initialised GooCanvasPoints structure with 360 points. - * @param points2 Initialised GooCanvasPoints structure with 360 points. - * @return The number of range circle parts. - * - * This function calculates the "left" side of the range circle and mirrors - * the points in longitude to create the "right side of the range circle, too. - * In order to be able to use the footprint points to create a set of subsequent - * lines connected to each other (poly-lines) the function may have to perform - * one of the following three actions: - * - * 1. If the footprint covers the North or South pole, we need to sort the points - * and add two extra points: One to begin the range circle (e.g. -180,90) and - * one to end the range circle (e.g. 180,90). This is necessary to create a - * complete and consistent set of points suitable for a polyline. The addition - * of the extra points is done by the sort_points function. - * - * 2. Else if parts of the range circle is on one side of the map, while parts of - * it is on the right side of the map, i.e. the range circle runs off the border - * of the map, it calls the split_points function to split the points into two - * complete and consistent sets of points that are suitable to create two - * poly-lines. - * - * 3. Else nothing needs to be done since the points are already suitable for - * a polyline. - * - * The function will re-initialise points1 and points2 according to its needs. The - * total number of points will always be 360, even with the addition of the two - * extra points. - */ -static guint calculate_footprint(GtkSatMap * satmap, sat_t * sat) +static guint calculate_footprint(GtkSatMap * satmap, sat_t * sat, + sat_map_obj_t * obj) { guint azi; gfloat sx, sy, msx, msy, ssx, ssy; @@ -1346,12 +1389,8 @@ static guint calculate_footprint(GtkSatMap * satmap, sat_t * sat) gdouble rangelon, rangelat, mlon; gboolean warped = FALSE; guint numrc = 1; + gint n1, n2; - /* Range circle calculations. - * Borrowed from gsat 0.9.0 by Xavier Crehueras, EB3CZS - * who borrowed from John Magliacane, KD2BD. - * Optimized by Alexandru Csete and William J Beksi. - */ ssplat = sat->ssplat * de2ra; ssplon = sat->ssplon * de2ra; beta = (0.5 * sat->footprint) / xkmper; @@ -1385,319 +1424,244 @@ static guint calculate_footprint(GtkSatMap * satmap, sat_t * sat) rangelat = rangelat / de2ra; rangelon = rangelon / de2ra; - /* mirror longitude */ if (mirror_lon(sat, rangelon, &mlon, satmap->left_side_lon)) warped = TRUE; lonlat_to_xy(satmap, rangelon, rangelat, &sx, &sy); lonlat_to_xy(satmap, mlon, rangelat, &msx, &msy); - points1->coords[2 * azi] = sx; - points1->coords[2 * azi + 1] = sy; + temp_points1[2 * azi] = sx; + temp_points1[2 * azi + 1] = sy; - /* Add mirrored point */ - points1->coords[718 - 2 * azi] = msx; - points1->coords[719 - 2 * azi] = msy; + temp_points1[718 - 2 * azi] = msx; + temp_points1[719 - 2 * azi] = msy; } - /* points1 now contains 360 pairs of map-based XY coordinates. - Check whether actions 1, 2 or 3 have to be performed. - */ - - /* pole is covered => sort points1 and add additional points */ if (pole_is_covered(sat)) { - - sort_points_x(satmap, sat, points1, 360); + sort_points_x(satmap, sat, temp_points1, 360); numrc = 1; + n1 = 360; + n2 = 0; } - - /* pole not covered but range circle has been warped - => split points */ else if (warped == TRUE) { - lonlat_to_xy(satmap, sat->ssplon, sat->ssplat, &ssx, &ssy); - split_points(satmap, sat, ssx); + n1 = 360; + n2 = 360; + split_points(satmap, sat, ssx, temp_points1, &n1, temp_points2, &n2); numrc = 2; - } else { - /* the nominal condition => points1 is adequate */ numrc = 1; + n1 = 360; + n2 = 0; + } + + g_free(obj->range1_points); + obj->range1_points = g_new(gdouble, n1 * 2); + memcpy(obj->range1_points, temp_points1, n1 * 2 * sizeof(gdouble)); + obj->range1_count = n1; + + if (numrc == 2 && n2 > 0) + { + g_free(obj->range2_points); + obj->range2_points = g_new(gdouble, n2 * 2); + memcpy(obj->range2_points, temp_points2, n2 * 2 * sizeof(gdouble)); + obj->range2_count = n2; + } + else + { + g_free(obj->range2_points); + obj->range2_points = NULL; + obj->range2_count = 0; } return numrc; } -/** - * Split and sort polyline points. - * - * @param satmap The GtkSatMap structure. - * @param points1 GooCanvasPoints containing the footprint points. - * @param points2 A GooCanvasPoints structure containing the second set of points. - * @param sspx Canvas based x-coordinate of SSP. - * @bug We should ensure that the endpoints in points1 have x=x0, while in - * the endpoints in points2 should have x=x0+width (TBC). - * - * @note This function works on canvas-based coordinates rather than lat/lon - * @note DO NOT USE this function when the footprint covers one of the poles - * (the end result may freeze the X-server requiring a hard-reset!) - */ -static void split_points(GtkSatMap * satmap, sat_t * sat, gdouble sspx) +static void split_points(GtkSatMap * satmap, sat_t * sat, gdouble sspx, + gdouble * points1, gint * n1, + gdouble * points2, gint * n2) { - GooCanvasPoints *tps1, *tps2; - gint n, n1, n2, ns, i, j, k; + gdouble *tps1, *tps2; + gint n, np1, np2, ns, i, j, k; - /* initialize parameters */ - n = points1->num_points; - n1 = 0; - n2 = 0; + n = 360; + np1 = 0; + np2 = 0; i = 0; j = 0; k = 0; ns = 0; - tps1 = goo_canvas_points_new(n); - tps2 = goo_canvas_points_new(n); + tps1 = g_new0(gdouble, n * 2); + tps2 = g_new0(gdouble, n * 2); - //if ((sspx >= (satmap->x0 + satmap->width - 0.6)) || - // (sspx >= (satmap->x0 - 0.6))) { - //if ((sspx == (satmap->x0 + satmap->width)) || - // (sspx == (satmap->x0))) { if ((sat->ssplon >= 179.4) || (sat->ssplon <= -179.4)) { - /* sslon = +/-180 deg. - - copy points with (x > satmap->x0+satmap->width/2) to tps1 - - copy points with (x < satmap->x0+satmap->width/2) to tps2 - - sort tps1 and tps2 - */ for (i = 0; i < n; i++) { - if (points1->coords[2 * i] > (satmap->x0 + satmap->width / 2)) + if (points1[2 * i] > (satmap->x0 + satmap->width / 2)) { - tps1->coords[2 * n1] = points1->coords[2 * i]; - tps1->coords[2 * n1 + 1] = points1->coords[2 * i + 1]; - n1++; + tps1[2 * np1] = points1[2 * i]; + tps1[2 * np1 + 1] = points1[2 * i + 1]; + np1++; } else { - tps2->coords[2 * n2] = points1->coords[2 * i]; - tps2->coords[2 * n2 + 1] = points1->coords[2 * i + 1]; - n2++; + tps2[2 * np2] = points1[2 * i]; + tps2[2 * np2 + 1] = points1[2 * i + 1]; + np2++; } } - sort_points_y(satmap, sat, tps1, n1); - sort_points_y(satmap, sat, tps2, n2); + sort_points_y(satmap, sat, tps1, np1); + sort_points_y(satmap, sat, tps2, np2); } else if (sspx < (satmap->x0 + satmap->width / 2)) { - /* We are on the left side of the map. - Scan through points1 until we get to x > sspx (i=ns): - - - copy the points forwards until x < (x0+w/2) => tps2 - - continue to copy until the end => tps1 - - copy the points from i=0 to i=ns => tps1. - - Copy tps1 => points1 and tps2 => points2 - */ - while (points1->coords[2 * i] <= sspx) + while (points1[2 * i] <= sspx) { i++; } ns = i - 1; - while (points1->coords[2 * i] > (satmap->x0 + satmap->width / 2)) + while (points1[2 * i] > (satmap->x0 + satmap->width / 2)) { - tps2->coords[2 * j] = points1->coords[2 * i]; - tps2->coords[2 * j + 1] = points1->coords[2 * i + 1]; + tps2[2 * j] = points1[2 * i]; + tps2[2 * j + 1] = points1[2 * i + 1]; i++; j++; - n2++; + np2++; } while (i < n) { - tps1->coords[2 * k] = points1->coords[2 * i]; - tps1->coords[2 * k + 1] = points1->coords[2 * i + 1]; + tps1[2 * k] = points1[2 * i]; + tps1[2 * k + 1] = points1[2 * i + 1]; i++; k++; - n1++; + np1++; } for (i = 0; i <= ns; i++) { - tps1->coords[2 * k] = points1->coords[2 * i]; - tps1->coords[2 * k + 1] = points1->coords[2 * i + 1]; + tps1[2 * k] = points1[2 * i]; + tps1[2 * k + 1] = points1[2 * i + 1]; k++; - n1++; + np1++; } } else { - /* We are on the right side of the map. - Scan backwards through points1 until x < sspx (i=ns): - - - copy the points i=ns,i-- until x >= x0+w/2 => tps2 - - copy the points until we reach i=0 => tps1 - - copy the points from i=n to i=ns => tps1 - - */ i = n - 1; - while (points1->coords[2 * i] >= sspx) + while (points1[2 * i] >= sspx) { i--; } ns = i + 1; - while (points1->coords[2 * i] < (satmap->x0 + satmap->width / 2)) + while (points1[2 * i] < (satmap->x0 + satmap->width / 2)) { - tps2->coords[2 * j] = points1->coords[2 * i]; - tps2->coords[2 * j + 1] = points1->coords[2 * i + 1]; + tps2[2 * j] = points1[2 * i]; + tps2[2 * j + 1] = points1[2 * i + 1]; i--; j++; - n2++; + np2++; } while (i >= 0) { - tps1->coords[2 * k] = points1->coords[2 * i]; - tps1->coords[2 * k + 1] = points1->coords[2 * i + 1]; + tps1[2 * k] = points1[2 * i]; + tps1[2 * k + 1] = points1[2 * i + 1]; i--; k++; - n1++; + np1++; } for (i = n - 1; i >= ns; i--) { - tps1->coords[2 * k] = points1->coords[2 * i]; - tps1->coords[2 * k + 1] = points1->coords[2 * i + 1]; + tps1[2 * k] = points1[2 * i]; + tps1[2 * k + 1] = points1[2 * i + 1]; k++; - n1++; + np1++; } } - //g_print ("NS:%d N1:%d N2:%d\n", ns, n1, n2); - - /* free points and copy new contents */ - goo_canvas_points_unref(points1); - goo_canvas_points_unref(points2); - - points1 = goo_canvas_points_new(n1); - for (i = 0; i < n1; i++) + for (i = 0; i < np1; i++) { - points1->coords[2 * i] = tps1->coords[2 * i]; - points1->coords[2 * i + 1] = tps1->coords[2 * i + 1]; + points1[2 * i] = tps1[2 * i]; + points1[2 * i + 1] = tps1[2 * i + 1]; } - goo_canvas_points_unref(tps1); + *n1 = np1; - points2 = goo_canvas_points_new(n2); - for (i = 0; i < n2; i++) + for (i = 0; i < np2; i++) { - points2->coords[2 * i] = tps2->coords[2 * i]; - points2->coords[2 * i + 1] = tps2->coords[2 * i + 1]; + points2[2 * i] = tps2[2 * i]; + points2[2 * i + 1] = tps2[2 * i + 1]; } - goo_canvas_points_unref(tps2); + *n2 = np2; - /* stretch end points to map borders */ - if (points1->coords[0] > (satmap->x0 + satmap->width / 2)) - { - points1->coords[0] = satmap->x0 + satmap->width; - points1->coords[2 * (n1 - 1)] = satmap->x0 + satmap->width; - points2->coords[0] = satmap->x0; - points2->coords[2 * (n2 - 1)] = satmap->x0; - } - else + g_free(tps1); + g_free(tps2); + + if (np1 > 0 && np2 > 0) { - points2->coords[0] = satmap->x0 + satmap->width; - points2->coords[2 * (n2 - 1)] = satmap->x0 + satmap->width; - points1->coords[0] = satmap->x0; - points1->coords[2 * (n1 - 1)] = satmap->x0; + if (points1[0] > (satmap->x0 + satmap->width / 2)) + { + points1[0] = satmap->x0 + satmap->width; + points1[2 * (np1 - 1)] = satmap->x0 + satmap->width; + points2[0] = satmap->x0; + points2[2 * (np2 - 1)] = satmap->x0; + } + else + { + points2[0] = satmap->x0 + satmap->width; + points2[2 * (np2 - 1)] = satmap->x0 + satmap->width; + points1[0] = satmap->x0; + points1[2 * (np1 - 1)] = satmap->x0; + } } } -/** - * Sort points according to X coordinates. - * - * @param satmap The GtkSatMap structure. - * @param sat The satellite data structure. - * @param points The points to sort. - * @param num The number of points. By specifying it as parameter we can - * sort incomplete arrays. - * - * This function sorts the points in ascending order with respect - * to their x value. After sorting the function adds two extra points - * to the array using the following algorithms: - * - * move point at position 0 to position 1 - * move point at position N to position N-1 - * if (ssplat > 0) - * insert (x0,y0) into position 0 - * insert (x0+width,y0) into position N - * else - * insert (x0,y0+height) into position 0 - * insert (x0+width,y0+height) into position N - * - * This way we loose the points at position 1 and N-1, but that does not - * make any big difference anyway, since we have 360 points in total. - * - */ static void sort_points_x(GtkSatMap * satmap, sat_t * sat, - GooCanvasPoints * points, gint num) + gdouble * points, gint num) { gsize size = 2 * sizeof(double); #if GLIB_CHECK_VERSION(2, 82, 0) - g_sort_array(points->coords, num, size, compare_coordinates_x, NULL); + g_sort_array(points, num, size, compare_coordinates_x, NULL); #else - g_qsort_with_data(points->coords, num, size, compare_coordinates_x, NULL); + g_qsort_with_data(points, num, size, compare_coordinates_x, NULL); #endif - /* move point at position 0 to position 1 */ - points->coords[2] = satmap->x0; - points->coords[3] = points->coords[1]; + points[2] = satmap->x0; + points[3] = points[1]; - /* move point at position N to position N-1 */ - points->coords[716] = satmap->x0 + satmap->width; //points->coords[718]; - points->coords[717] = points->coords[719]; + points[716] = satmap->x0 + satmap->width; + points[717] = points[719]; if (sat->ssplat > 0.0) { - /* insert (x0-1,y0) into position 0 */ - points->coords[0] = satmap->x0; - points->coords[1] = satmap->y0; + points[0] = satmap->x0; + points[1] = satmap->y0; - /* insert (x0+width,y0) into position N */ - points->coords[718] = satmap->x0 + satmap->width; - points->coords[719] = satmap->y0; + points[718] = satmap->x0 + satmap->width; + points[719] = satmap->y0; } else { - /* insert (x0,y0+height) into position 0 */ - points->coords[0] = satmap->x0; - points->coords[1] = satmap->y0 + satmap->height; + points[0] = satmap->x0; + points[1] = satmap->y0 + satmap->height; - /* insert (x0+width,y0+height) into position N */ - points->coords[718] = satmap->x0 + satmap->width; - points->coords[719] = satmap->y0 + satmap->height; + points[718] = satmap->x0 + satmap->width; + points[719] = satmap->y0 + satmap->height; } } -/** - * Sort points according to Y coordinates. - * - * @param satmap The GtkSatMap structure. - * @param sat The satellite data structure. - * @param points The points to sort. - * @param num The number of points. By specifying it as parameter we can - * sort incomplete arrays. - * - * This function sorts the points in ascending order with respect - * to their y value. - */ static void sort_points_y(GtkSatMap * satmap, sat_t * sat, - GooCanvasPoints * points, gint num) + gdouble * points, gint num) { gsize size; @@ -1706,30 +1670,12 @@ static void sort_points_y(GtkSatMap * satmap, sat_t * sat, size = 2 * sizeof(double); #if GLIB_CHECK_VERSION(2, 82, 0) - g_sort_array(points->coords, num, size, compare_coordinates_y, NULL); + g_sort_array(points, num, size, compare_coordinates_y, NULL); #else - g_qsort_with_data(points->coords, num, size, compare_coordinates_y, NULL); + g_qsort_with_data(points, num, size, compare_coordinates_y, NULL); #endif } -/** - * Compare two X coordinates. - * - * @param a Pointer to one coordinate (x,y) both double. - * @param b Pointer to the second coordinate (x,y) both double. - * @param data User data; always NULL. - * @return Negative value if a < b; zero if a = b; positive value if a > b. - * - * This function is used by the g_qsort_with_data function to compare two - * elements in the coordinate array of the GooCanvasPoints structure. We have - * to remember that GooCanvasPoints->coords is an array of XY values, i.e. - * [X0,Y0,X1,Y1,...,Xn,Yn], but we only want to sort according to the X - * coordinate. Therefore, we let the g_qsort_with_data believe that the whole - * pair is one element (which is OK even according to the API docs) but we - * cast the pointers to an array with two elements and compare only the first - * one (x). - * - */ static gint compare_coordinates_x(gconstpointer a, gconstpointer b, gpointer data) { @@ -1739,35 +1685,13 @@ static gint compare_coordinates_x(gconstpointer a, gconstpointer b, (void)data; if (ea[0] < eb[0]) - { return -1; - } else if (ea[0] > eb[0]) - { return 1; - } return 0; } -/** - * Compare two Y coordinates. - * - * @param a Pointer to one coordinate (x,y) both double. - * @param b Pointer to the second coordinate (x,y) both double. - * @param data User data; always NULL. - * @return Negative value if a < b; zero if a = b; positive value if a > b. - * - * This function is used by the g_qsort_with_data function to compare two - * elements in the coordinate array of the GooCanvasPoints structure. We have - * to remember that GooCanvasPoints->coords is an array of XY values, i.e. - * [X0,Y0,X1,Y1,...,Xn,Yn], but we only want to sort according to the Y - * coordinate. Therefore, we let the g_qsort_with_data believe that the whole - * pair is one element (which is OK even according to the API docs) but we - * cast the pointers to an array with two elements and compare only the second - * one (y). - * - */ static gint compare_coordinates_y(gconstpointer a, gconstpointer b, gpointer data) { @@ -1777,37 +1701,19 @@ static gint compare_coordinates_y(gconstpointer a, gconstpointer b, (void)data; if (ea[1] < eb[1]) - { return -1; - } - else if (ea[1] > eb[1]) - { return 1; - } return 0; } -/** - * Plot a satellite. - * - * @param key The hash table key. - * @param value Pointer to the satellite. - * @param data Pointer to the GtkSatMap widget. - * - * This function creates and initializes the canvas objects (rectangle, label, - * footprint) for a satellite. The function is called as a g_hash_table_foreach - * callback. - */ static void plot_sat(gpointer key, gpointer value, gpointer data) { GtkSatMap *satmap = GTK_SAT_MAP(data); sat_map_obj_t *obj = NULL; sat_t *sat = SAT(value); - GooCanvasItemModel *root; gint *catnum; - guint32 col, covcol, shadowcol; gfloat x, y; gchar *tooltip; @@ -1817,14 +1723,13 @@ static void plot_sat(gpointer key, gpointer value, gpointer data) { return; } - /* get satellite and SSP */ + catnum = g_new0(gint, 1); *catnum = sat->tle.catnr; lonlat_to_xy(satmap, sat->ssplon, sat->ssplat, &x, &y); - /* create and initialize a sat object */ - obj = g_try_new(sat_map_obj_t, 1); + obj = g_try_new0(sat_map_obj_t, 1); if (obj == NULL) { @@ -1853,48 +1758,20 @@ static void plot_sat(gpointer key, gpointer value, gpointer data) { obj->showcov = FALSE; } + obj->istarget = FALSE; obj->oldrcnum = 0; obj->newrcnum = 0; - obj->range2 = NULL; obj->catnum = sat->tle.catnr; obj->track_data.latlon = NULL; obj->track_data.lines = NULL; obj->track_orbit = 0; - root = goo_canvas_get_root_item_model(GOO_CANVAS(satmap->canvas)); - - /* satellite color */ - col = mod_cfg_get_int(satmap->cfgdata, - MOD_CFG_MAP_SECTION, - MOD_CFG_MAP_SAT_COL, SAT_CFG_INT_MAP_SAT_COL); - - /* area coverage colour */ - covcol = mod_cfg_get_int(satmap->cfgdata, - MOD_CFG_MAP_SECTION, - MOD_CFG_MAP_SAT_COV_COL, - SAT_CFG_INT_MAP_SAT_COV_COL); - /* coverage color */ - if (obj->showcov) - { - covcol = mod_cfg_get_int(satmap->cfgdata, - MOD_CFG_MAP_SECTION, - MOD_CFG_MAP_SAT_COV_COL, - SAT_CFG_INT_MAP_SAT_COV_COL); - } - else - { - covcol = 0x00000000; - } - + obj->x = x; + obj->y = y; - /* shadow colour (only alpha channel) */ - shadowcol = mod_cfg_get_int(satmap->cfgdata, - MOD_CFG_MAP_SECTION, - MOD_CFG_MAP_SHADOW_ALPHA, - SAT_CFG_INT_MAP_SHADOW_ALPHA); + obj->nickname = g_strdup(sat->nickname); - /* create tooltip */ tooltip = g_markup_printf_escaped("%s\n" "Lon: %5.1f\302\260\n" "Lat: %5.1f\302\260\n" @@ -1903,182 +1780,43 @@ static void plot_sat(gpointer key, gpointer value, gpointer data) sat->nickname, sat->ssplon, sat->ssplat, sat->az, sat->el); + obj->tooltip = tooltip; + + obj->range1_points = NULL; + obj->range1_count = 0; + obj->range2_points = NULL; + obj->range2_count = 0; - /* create satellite marker and label + shadows. We create shadows first */ - obj->shadowm = goo_canvas_rect_model_new(root, - x - MARKER_SIZE_HALF + 1, - y - MARKER_SIZE_HALF + 1, - 2 * MARKER_SIZE_HALF, - 2 * MARKER_SIZE_HALF, - "fill-color-rgba", 0x00, - "stroke-color-rgba", shadowcol, - NULL); - obj->marker = goo_canvas_rect_model_new(root, - x - MARKER_SIZE_HALF, - y - MARKER_SIZE_HALF, - 2 * MARKER_SIZE_HALF, - 2 * MARKER_SIZE_HALF, - "fill-color-rgba", col, - "stroke-color-rgba", col, - "tooltip", tooltip, NULL); - - obj->shadowl = goo_canvas_text_model_new(root, sat->nickname, - x + 1, - y + 3, - -1, - GOO_CANVAS_ANCHOR_NORTH, - "font", g_value_get_string(&satmap->font), - "fill-color-rgba", shadowcol, - NULL); - obj->label = goo_canvas_text_model_new(root, sat->nickname, - x, - y + 2, - -1, - GOO_CANVAS_ANCHOR_NORTH, - "font", g_value_get_string(&satmap->font), - "fill-color-rgba", col, - "tooltip", tooltip, NULL); - - g_free(tooltip); - - g_object_set_data(G_OBJECT(obj->marker), "catnum", - GINT_TO_POINTER(*catnum)); - g_object_set_data(G_OBJECT(obj->label), "catnum", - GINT_TO_POINTER(*catnum)); - - /* initialize points for footprint */ - points1 = goo_canvas_points_new(360); - points2 = goo_canvas_points_new(360); - - /* calculate footprint */ - obj->newrcnum = calculate_footprint(satmap, sat); + obj->newrcnum = calculate_footprint(satmap, sat, obj); obj->oldrcnum = obj->newrcnum; - /* invisible footprint for decayed sats (STS fix) */ - /* if (sat->otype == ORBIT_TYPE_DECAYED) { */ - /* col = 0x00000000; */ - /* } */ - - /* always create first part of range circle */ - obj->range1 = goo_canvas_polyline_model_new(root, FALSE, 0, - "points", points1, - "line-width", 1.0, - "fill-color-rgba", covcol, - "stroke-color-rgba", col, - "line-cap", - CAIRO_LINE_CAP_SQUARE, - "line-join", - CAIRO_LINE_JOIN_MITER, NULL); - g_object_set_data(G_OBJECT(obj->range1), "catnum", - GINT_TO_POINTER(*catnum)); - - /* create second part if available */ - if (obj->newrcnum == 2) - { - //g_print ("N1:%d N2:%d\n", points1->num_points, points2->num_points); - - obj->range2 = goo_canvas_polyline_model_new(root, FALSE, 0, - "points", points2, - "line-width", 1.0, - "fill-color-rgba", covcol, - "stroke-color-rgba", col, - "line-cap", - CAIRO_LINE_CAP_SQUARE, - "line-join", - CAIRO_LINE_JOIN_MITER, - NULL); - g_object_set_data(G_OBJECT(obj->range2), "catnum", - GINT_TO_POINTER(*catnum)); - - } - - goo_canvas_points_unref(points1); - goo_canvas_points_unref(points2); - - /* add sat to hash table */ g_hash_table_insert(satmap->obj, catnum, obj); - - /* hide objects if so configured */ - if (!satmap->satname) - { - g_object_set(obj->label, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL); - g_object_set(obj->shadowl, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL); - } - if (!satmap->satfp) - { - g_object_set(obj->range1, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL); - if (obj->newrcnum == 2) - g_object_set(obj->range2, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL); - } - if (!satmap->satmarker) - { - g_object_set(obj->marker, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL); - g_object_set(obj->shadowm, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL); - } } -/** - * Free a satellite object. - * - * @param key The hash table key. - * @param value Pointer to the satellite. - * @param data Pointer to the GtkSatMap widget. - * - * This function removes the canvas objects allocated by `plot_sat`. It needs - * access the the GtkSatMap widget to remove the elements from the canvas and - * to pass on to `ground_track_delete`. The function must be called as a - * g_hash_table_foreach callback in order to pass in the GtkSatMap. - */ static void free_sat_obj(gpointer key, gpointer value, gpointer data) { - sat_map_obj_t *obj = SAT_MAP_OBJ(value); - sat_t *sat = NULL; - GtkSatMap *satmap = GTK_SAT_MAP(data); - GooCanvasItemModel *root; - gint idx; + sat_map_obj_t *obj = SAT_MAP_OBJ(value); + sat_t *sat = NULL; + GtkSatMap *satmap = GTK_SAT_MAP(data); (void)key; - root = goo_canvas_get_root_item_model(GOO_CANVAS(satmap->canvas)); - - idx = goo_canvas_item_model_find_child(root, obj->marker); - if (idx != -1) - goo_canvas_item_model_remove_child(root, idx);; - obj->marker = NULL; - - idx = goo_canvas_item_model_find_child(root, obj->shadowm); - if (idx != -1) - goo_canvas_item_model_remove_child(root, idx);; - obj->shadowm = NULL; - - idx = goo_canvas_item_model_find_child(root, obj->label); - if (idx != -1) - goo_canvas_item_model_remove_child(root, idx); - obj->label = NULL; - - idx = goo_canvas_item_model_find_child(root, obj->shadowl); - if (idx != -1) - goo_canvas_item_model_remove_child(root, idx); - obj->shadowl = NULL; - - idx = goo_canvas_item_model_find_child(root, obj->range1); - if (idx != -1) - goo_canvas_item_model_remove_child(root, idx); - obj->range1 = NULL; - - idx = goo_canvas_item_model_find_child(root, obj->range2); - if (idx != -1) - goo_canvas_item_model_remove_child(root, idx); - obj->range2 = NULL; - if (obj->showtrack) { sat = SAT(g_hash_table_lookup(satmap->sats, &obj->catnum)); ground_track_delete(satmap, sat, satmap->qth, obj, TRUE); } + + g_free(obj->nickname); + obj->nickname = NULL; + g_free(obj->tooltip); + obj->tooltip = NULL; + g_free(obj->range1_points); + obj->range1_points = NULL; + g_free(obj->range2_points); + obj->range2_points = NULL; } -/** Update a given satellite. */ static void update_sat(gpointer key, gpointer value, gpointer data) { gint *catnum; @@ -2086,24 +1824,16 @@ static void update_sat(gpointer key, gpointer value, gpointer data) sat_map_obj_t *obj = NULL; sat_t *sat = SAT(value); gfloat x, y; - gdouble oldx, oldy; - gdouble now; // = get_current_daynum (); - GooCanvasItemModel *root; - gint idx; - guint32 col, covcol; + gfloat oldx, oldy; + gdouble now; gchar *tooltip; gchar *aosstr; - //gdouble sspla,ssplo; - - root = goo_canvas_get_root_item_model(GOO_CANVAS(satmap->canvas)); - catnum = g_new0(gint, 1); *catnum = sat->tle.catnr; now = satmap->tstamp; - /* update next AOS */ if (sat->aos > now) { if ((sat->aos < satmap->naos) || (satmap->naos == 0.0)) @@ -2115,11 +1845,11 @@ static void update_sat(gpointer key, gpointer value, gpointer data) obj = SAT_MAP_OBJ(g_hash_table_lookup(satmap->obj, catnum)); - /* get rid of a decayed satellite */ if (decayed(sat) && obj != NULL) { free_sat_obj(NULL, obj, satmap); g_hash_table_remove(satmap->obj, catnum); + g_free(catnum); return; } @@ -2127,27 +1857,27 @@ static void update_sat(gpointer key, gpointer value, gpointer data) { if (decayed(sat)) { + g_free(catnum); return; } else { - /* satellite was decayed now is visible - time controller backed up time */ plot_sat(key, value, data); + g_free(catnum); return; } } if (obj->selected) { - /* update satmap->sel */ update_selected(satmap, sat); } - g_object_set(obj->label, "text", sat->nickname, NULL); - g_object_set(obj->shadowl, "text", sat->nickname, NULL); + g_free(obj->nickname); + obj->nickname = g_strdup(sat->nickname); aosstr = aoslos_time_to_str(satmap, sat); + g_free(obj->tooltip); tooltip = g_markup_printf_escaped("%s\n" "Lon: %5.1f\302\260\n" "Lat: %5.1f\302\260\n" @@ -2157,197 +1887,48 @@ static void update_sat(gpointer key, gpointer value, gpointer data) sat->nickname, sat->ssplon, sat->ssplat, sat->az, sat->el, aosstr); - g_object_set(obj->marker, "tooltip", tooltip, NULL); - g_object_set(obj->label, "tooltip", tooltip, NULL); - g_free(tooltip); + obj->tooltip = tooltip; g_free(aosstr); lonlat_to_xy(satmap, sat->ssplon, sat->ssplat, &x, &y); - /* update only if satellite has moved at least - 2 * MARKER_SIZE_HALF (no need to drain CPU all the time) - */ - g_object_get(obj->marker, "x", &oldx, "y", &oldy, NULL); + oldx = obj->x; + oldy = obj->y; if ((fabs(oldx - x) >= 2 * MARKER_SIZE_HALF) || (fabs(oldy - y) >= 2 * MARKER_SIZE_HALF)) { - g_object_set(obj->marker, - "x", (gdouble) (x - MARKER_SIZE_HALF), - "y", (gdouble) (y - MARKER_SIZE_HALF), NULL); - g_object_set(obj->shadowm, - "x", (gdouble) (x - MARKER_SIZE_HALF + 1), - "y", (gdouble) (y - MARKER_SIZE_HALF + 1), NULL); - - if (x < 50) - { - g_object_set(obj->label, - "x", (gdouble) (x + 3), - "y", (gdouble) (y), "anchor", GOO_CANVAS_ANCHOR_WEST, - NULL); - g_object_set(obj->shadowl, "x", (gdouble) (x + 3 + 1), "y", - (gdouble) (y + 1), "anchor", GOO_CANVAS_ANCHOR_WEST, - NULL); - } - else if ((satmap->width - x) < 50) - { - g_object_set(obj->label, - "x", (gdouble) (x - 3), - "y", (gdouble) (y), "anchor", GOO_CANVAS_ANCHOR_EAST, - NULL); - g_object_set(obj->shadowl, "x", (gdouble) (x - 3 + 1), "y", - (gdouble) (y + 1), "anchor", GOO_CANVAS_ANCHOR_EAST, - NULL); - } - else if ((satmap->height - y) < 25) - { - g_object_set(obj->label, - "x", (gdouble) (x), - "y", (gdouble) (y - 2), - "anchor", GOO_CANVAS_ANCHOR_SOUTH, NULL); - g_object_set(obj->shadowl, - "x", (gdouble) (x + 1), - "y", (gdouble) (y - 2 + 1), - "anchor", GOO_CANVAS_ANCHOR_SOUTH, NULL); - } - else - { - g_object_set(obj->label, - "x", (gdouble) (x), - "y", (gdouble) (y + 2), - "anchor", GOO_CANVAS_ANCHOR_NORTH, NULL); - g_object_set(obj->shadowl, - "x", (gdouble) (x + 1), - "y", (gdouble) (y + 2 + 1), - "anchor", GOO_CANVAS_ANCHOR_NORTH, NULL); - } + obj->x = x; + obj->y = y; - /* initialize points for footprint */ - points1 = goo_canvas_points_new(360); - points2 = goo_canvas_points_new(360); - - /* calculate footprint */ - obj->newrcnum = calculate_footprint(satmap, sat); - - /* always update first part */ - g_object_set(obj->range1, "points", points1, NULL); - - if (obj->newrcnum == 2) - { - if (obj->oldrcnum == 1) - { - /* we need to create the second part */ - if (obj->selected) - { - col = mod_cfg_get_int(satmap->cfgdata, - MOD_CFG_MAP_SECTION, - MOD_CFG_MAP_SAT_SEL_COL, - SAT_CFG_INT_MAP_SAT_SEL_COL); - } - else - { - col = mod_cfg_get_int(satmap->cfgdata, - MOD_CFG_MAP_SECTION, - MOD_CFG_MAP_SAT_COL, - SAT_CFG_INT_MAP_SAT_COL); - } - /* coverage color */ - covcol = mod_cfg_get_int(satmap->cfgdata, - MOD_CFG_MAP_SECTION, - MOD_CFG_MAP_SAT_COV_COL, - SAT_CFG_INT_MAP_SAT_COV_COL); - obj->range2 = goo_canvas_polyline_model_new(root, FALSE, 0, - "points", points2, - "line-width", 1.0, - "fill-color-rgba", - covcol, - "stroke-color-rgba", - col, "line-cap", - CAIRO_LINE_CAP_SQUARE, - "line-join", - CAIRO_LINE_JOIN_MITER, - NULL); - g_object_set_data(G_OBJECT(obj->range2), "catnum", - GINT_TO_POINTER(*catnum)); - - if (!satmap->satfp && !obj->selected) - g_object_set(obj->range2, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL); - - } - else - { - /* just update the second part */ - g_object_set(obj->range2, "points", points2, NULL); - } - } - else - { - if (obj->oldrcnum == 2) - { - /* remove second part */ - idx = goo_canvas_item_model_find_child(root, obj->range2); - if (idx != -1) - { - goo_canvas_item_model_remove_child(root, idx); - } - } - } - - /* update rc-number */ + obj->newrcnum = calculate_footprint(satmap, sat, obj); obj->oldrcnum = obj->newrcnum; - - goo_canvas_points_unref(points1); - goo_canvas_points_unref(points2); } - /* if ground track is visible check whether we have passed into a - new orbit, in which case we need to recalculate the ground track - */ if (obj->showtrack) { if (obj->track_orbit != sat->orbit) { ground_track_update(satmap, sat, satmap->qth, obj, TRUE); } - /* otherwise we may be in a map rescale process */ else if (satmap->resize) { ground_track_update(satmap, sat, satmap->qth, obj, FALSE); } } - /* update marker visibilities */ - gboolean vis = satmap->satname || obj->selected; - g_object_set(obj->label, "visibility", vis ? GOO_CANVAS_ITEM_VISIBLE : GOO_CANVAS_ITEM_INVISIBLE, NULL); - g_object_set(obj->shadowl, "visibility", vis ? GOO_CANVAS_ITEM_VISIBLE : GOO_CANVAS_ITEM_INVISIBLE, NULL); - - vis = satmap->satfp || obj->selected; - g_object_set(obj->range1, "visibility", vis ? GOO_CANVAS_ITEM_VISIBLE : GOO_CANVAS_ITEM_INVISIBLE, NULL); - if (obj->newrcnum == 2) - g_object_set(obj->range2, "visibility", vis ? GOO_CANVAS_ITEM_VISIBLE : GOO_CANVAS_ITEM_INVISIBLE, NULL); - - vis = satmap->satmarker || obj->selected; - g_object_set(obj->marker, "visibility", vis ? GOO_CANVAS_ITEM_VISIBLE : GOO_CANVAS_ITEM_INVISIBLE, NULL); - g_object_set(obj->shadowm, "visibility", vis ? GOO_CANVAS_ITEM_VISIBLE : GOO_CANVAS_ITEM_INVISIBLE, NULL); - g_free(catnum); } -/** - * Update information about the selected satellite. - * - * @param satmap Pointer to the GtkSatMap widget. - * @param sat Pointer to the selected satellite - */ static void update_selected(GtkSatMap * satmap, sat_t * sat) { guint h, m, s; gchar *ch, *cm, *cs; gchar *alsstr, *text; gdouble number, now; - gboolean isgeo = FALSE; /* set to TRUE if satellite appears to be GEO */ + gboolean isgeo = FALSE; - now = satmap->tstamp; //get_current_daynum (); + now = satmap->tstamp; if (sat->el > 0.0) { @@ -2374,50 +1955,41 @@ static void update_selected(GtkSatMap * satmap, sat_t * sat) } } - /* if satellite appears to be GEO don't attempt to show AOS/LOS */ if (isgeo) { if (sat->el > 0.0) { - text = - g_markup_printf_escaped - (" %s: Always in range ", - satmap->infobgd, sat->nickname); + text = g_markup_printf_escaped( + " %s: Always in range ", + satmap->infobgd, sat->nickname); } else { - text = - g_markup_printf_escaped - (" %s: Always out of range ", - satmap->infobgd, sat->nickname); + text = g_markup_printf_escaped( + " %s: Always out of range ", + satmap->infobgd, sat->nickname); } } else { - /* convert julian date to seconds */ - s = (guint) (number * 86400); + s = (guint)(number * 86400); - /* extract hours */ - h = (guint) floor(s / 3600); + h = (guint)floor(s / 3600); s -= 3600 * h; - /* leading zero */ if ((h > 0) && (h < 10)) ch = g_strdup("0"); else ch = g_strdup(""); - /* extract minutes */ - m = (guint) floor(s / 60); + m = (guint)floor(s / 60); s -= 60 * m; - /* leading zero */ if (m < 10) cm = g_strdup("0"); else cm = g_strdup(""); - /* leading zero */ if (s < 10) cs = g_strdup(":0"); else @@ -2444,257 +2016,8 @@ static void update_selected(GtkSatMap * satmap, sat_t * sat) g_free(alsstr); } - /* update info text */ - g_object_set(satmap->sel, "text", text, NULL); - g_free(text); -} - -static void draw_grid_lines(GtkSatMap * satmap, GooCanvasItemModel * root) -{ - gdouble xstep, ystep; - gfloat lon, lat; - guint32 col; - guint i; - gchar *buf, hmf = ' '; - - /* initialize algo parameters */ - col = mod_cfg_get_int(satmap->cfgdata, - MOD_CFG_MAP_SECTION, - MOD_CFG_MAP_GRID_COL, SAT_CFG_INT_MAP_GRID_COL); - - xstep = (gdouble) (30.0 * satmap->width / 360.0); - ystep = (gdouble) (30.0 * satmap->height / 180.0); - -#define MKLINE goo_canvas_polyline_model_new_line - - /* horizontal grid */ - for (i = 0; i < 5; i++) - { - /* line */ - satmap->gridh[i] = MKLINE(root, - (gdouble) satmap->x0, - (gdouble) (satmap->y0 + (i + 1) * ystep), - (gdouble) (satmap->x0 + satmap->width), - (gdouble) (satmap->y0 + (i + 1) * ystep), - "stroke-color-rgba", col, - "line-cap", CAIRO_LINE_CAP_SQUARE, - "line-join", CAIRO_LINE_JOIN_MITER, - "line-width", 0.5, NULL); - - /* FIXME: Use dotted line pattern? */ - - /* label */ - xy_to_lonlat(satmap, satmap->x0, satmap->y0 + (i + 1) * ystep, &lon, - &lat); - if (sat_cfg_get_bool(SAT_CFG_BOOL_USE_NSEW)) - { - if (lat < 0.00) - { - lat = -lat; - hmf = 'S'; - } - else - { - hmf = 'N'; - } - } - buf = g_strdup_printf("%.0f\302\260%c", lat, hmf); - satmap->gridhlab[i] = goo_canvas_text_model_new(root, - buf, - (gdouble) (satmap->x0 + - 15), - (gdouble) (satmap->y0 + - (i + - 1) * - ystep), -1, - GOO_CANVAS_ANCHOR_NORTH, - "font", g_value_get_string(&satmap->font), - "fill-color-rgba", col, - NULL); - g_free(buf); - - /* lower items to be just above the background map - or below it if lines are invisible - */ - if (satmap->showgrid) - { - goo_canvas_item_model_raise(satmap->gridh[i], satmap->map); - goo_canvas_item_model_raise(satmap->gridhlab[i], satmap->map); - } - else - { - goo_canvas_item_model_lower(satmap->gridh[i], satmap->map); - goo_canvas_item_model_lower(satmap->gridhlab[i], satmap->map); - } - } - - /* vertical grid */ - for (i = 0; i < 11; i++) - { - /* line */ - satmap->gridv[i] = MKLINE(root, - (gdouble) (satmap->x0 + (i + 1) * xstep), - (gdouble) satmap->y0, - (gdouble) (satmap->x0 + (i + 1) * xstep), - (gdouble) (satmap->y0 + satmap->height), - "stroke-color-rgba", col, - "line-cap", CAIRO_LINE_CAP_SQUARE, - "line-join", CAIRO_LINE_JOIN_MITER, - "line-width", 0.5, NULL); - - /* label */ - xy_to_lonlat(satmap, satmap->x0 + (i + 1) * xstep, satmap->y0, &lon, - &lat); - if (sat_cfg_get_bool(SAT_CFG_BOOL_USE_NSEW)) - { - if (lon < 0.00) - { - lon = -lon; - hmf = 'W'; - } - else - { - hmf = 'E'; - } - } - buf = g_strdup_printf("%.0f\302\260%c", lon, hmf); - satmap->gridvlab[i] = goo_canvas_text_model_new(root, - buf, - (gdouble) (satmap->x0 + - (i + - 1) * - xstep), - (gdouble) (satmap->y0 + - satmap->height - - 5), -1, - GOO_CANVAS_ANCHOR_EAST, - "font", g_value_get_string(&satmap->font), - "fill-color-rgba", col, - NULL); - g_free(buf); - - /* lower items to be just above the background map - or below it if lines are invisible - */ - if (satmap->showgrid) - { - goo_canvas_item_model_raise(satmap->gridv[i], satmap->map); - goo_canvas_item_model_raise(satmap->gridvlab[i], satmap->map); - } - else - { - goo_canvas_item_model_lower(satmap->gridv[i], satmap->map); - goo_canvas_item_model_lower(satmap->gridvlab[i], satmap->map); - } - } -} - -static void draw_terminator(GtkSatMap * satmap, GooCanvasItemModel * root) -{ - guint32 terminator_col; - guint32 globe_shadow_col; - - /* initialize algo parameters */ - terminator_col = mod_cfg_get_int(satmap->cfgdata, - MOD_CFG_MAP_SECTION, - MOD_CFG_MAP_TERMINATOR_COL, - SAT_CFG_INT_MAP_TERMINATOR_COL); - - globe_shadow_col = mod_cfg_get_int(satmap->cfgdata, - MOD_CFG_MAP_SECTION, - MOD_CFG_MAP_GLOBAL_SHADOW_COL, - SAT_CFG_INT_MAP_GLOBAL_SHADOW_COL); - - /* We do not set any polygon vertices here, but trust that the redraw_terminator - will be called in due course to do the job. */ - - satmap->terminator = goo_canvas_polyline_model_new(root, FALSE, 0, - "line-width", 1.0, - "fill-color-rgba", - globe_shadow_col, - "stroke-color-rgba", - terminator_col, - "line-cap", - CAIRO_LINE_CAP_SQUARE, - "line-join", - CAIRO_LINE_JOIN_MITER, - NULL); - - goo_canvas_item_model_raise(satmap->terminator, satmap->map); - - satmap->terminator_last_tstamp = satmap->tstamp; -} - -static void redraw_grid_lines(GtkSatMap * satmap) -{ - GooCanvasPoints *line; - gdouble xstep, ystep; - guint i; - - xstep = (gdouble) 30.0 *((gdouble) satmap->width) / 360.0; - ystep = (gdouble) 30.0 *((gdouble) satmap->height) / 180.0; - - /* horizontal grid */ - for (i = 0; i < 5; i++) - { - - /* update line */ - line = goo_canvas_points_new(2); - line->coords[0] = (gdouble) satmap->x0; - line->coords[1] = (gdouble) (satmap->y0 + (i + 1) * ystep); - line->coords[2] = (gdouble) (satmap->x0 + satmap->width); - line->coords[3] = (gdouble) (satmap->y0 + (i + 1) * ystep); - g_object_set(satmap->gridh[i], "points", line, NULL); - goo_canvas_points_unref(line); - - /* update label */ - g_object_set(satmap->gridhlab[i], - "x", (gdouble) (satmap->x0 + 15), - "y", (gdouble) (satmap->y0 + (i + 1) * ystep), NULL); - - /* lower items to be just above the background map - or below it if lines are invisible - */ - if (satmap->showgrid) - { - goo_canvas_item_model_raise(satmap->gridh[i], satmap->map); - goo_canvas_item_model_raise(satmap->gridhlab[i], satmap->map); - } - else - goo_canvas_item_model_lower(satmap->gridh[i], satmap->map); - } - - /* vertical grid */ - for (i = 0; i < 11; i++) - { - /* update line */ - line = goo_canvas_points_new(2); - line->coords[0] = (gdouble) (satmap->x0 + (i + 1) * xstep); - line->coords[1] = (gdouble) satmap->y0; - line->coords[2] = (gdouble) (satmap->x0 + (i + 1) * xstep); - line->coords[3] = (gdouble) (satmap->y0 + satmap->height); - g_object_set(satmap->gridv[i], "points", line, NULL); - goo_canvas_points_unref(line); - - /* update label */ - g_object_set(satmap->gridvlab[i], - "x", (gdouble) (satmap->x0 + (i + 1) * xstep), - "y", (gdouble) (satmap->y0 + satmap->height - 5), NULL); - - /* lower items to be just above the background map - or below it if lines are invisible - */ - if (satmap->showgrid) - { - goo_canvas_item_model_raise(satmap->gridv[i], satmap->map); - goo_canvas_item_model_raise(satmap->gridvlab[i], satmap->map); - } - else - { - goo_canvas_item_model_lower(satmap->gridv[i], satmap->map); - goo_canvas_item_model_lower(satmap->gridvlab[i], satmap->map); - } - } + g_free(satmap->sel_text); + satmap->sel_text = text; } static inline gdouble sgn(gdouble const t) @@ -2704,37 +2027,18 @@ static inline gdouble sgn(gdouble const t) static void redraw_terminator(GtkSatMap * satmap) { - /* Set of (x, y) points along the terminator, one on each line of longitude in - increments of longitudinal degrees. */ - GooCanvasPoints *line; - - /* A variable which iterates over the longitudes. */ int longitude; - - /* Working variables in the computation of the terminator coordinates. */ gfloat x, y; - - /* Vector normal to plane containing a line of longitude (z component is - always zero). */ - /* Note: our coordinates have z along the Earth's axis, x pointing through the - intersection of the Greenwich Meridian and the Equator, and y right-handedly - perpendicular to both. */ gdouble lx, ly; - - /* The position of the sun as latitude, longitude. */ geodetic_t geodetic; - - /* Vector which points from the centre of the Earth to the Sun in inertial - coordinates. */ vector_t sun_; - - /* The same vector in geodesic coordinates. */ gdouble sx, sy, sz; - - /* Vector cross-product of (lx,ly,lz) and sun vector. */ gdouble rx, ry, rz; - line = goo_canvas_points_new(363); + if (satmap->terminator_points == NULL) + { + satmap->terminator_points = g_new(gdouble, 363 * 2); + } Calculate_Solar_Position(satmap->tstamp, &sun_); Calculate_LatLonAlt(satmap->tstamp, &sun_, &geodetic); @@ -2743,41 +2047,40 @@ static void redraw_terminator(GtkSatMap * satmap) sy = cos(geodetic.lat) * sin(-geodetic.lon); sz = sin(geodetic.lat); - for (longitude = -180; longitude <= 180; ++longitude) { int centered_longitude = longitude + (satmap->left_side_lon - 180.0); lx = cos(de2ra * (centered_longitude + sgn(sz) * 90)); ly = sin(de2ra * (centered_longitude + sgn(sz) * 90)); - /* lz = 0.0; */ - rx = ly * sz /* -lz*sy */ ; - ry = /* lz*sx */ -lx * sz; + rx = ly * sz; + ry = -lx * sz; rz = -lx * sy - ly * sx; - gdouble length = sqrt(rx * rx + ry * ry + rz * rz); + gdouble length = sqrt(rx * rx + ry * ry + rz * rz); lonlat_to_xy(satmap, - centered_longitude, asin(rz / length) * (1.0 / de2ra), &x, &y); + centered_longitude, asin(rz / length) * (1.0 / de2ra), + &x, &y); - if( 180 == longitude ) + if (180 == longitude) { - // make sure the last point is on the right side - x = satmap->x0 + satmap->width; + x = satmap->x0 + satmap->width; } - line->coords[2 * (longitude + 181)] = x; - line->coords[2 * (longitude + 181) + 1] = y; + satmap->terminator_points[2 * (longitude + 181)] = x; + satmap->terminator_points[2 * (longitude + 181) + 1] = y; } - line->coords[0] = satmap->x0; - line->coords[1] = sz < 0.0 ? satmap->y0 : ( satmap->y0 + satmap->height ); + satmap->terminator_points[0] = satmap->x0; + satmap->terminator_points[1] = sz < 0.0 ? satmap->y0 : + (satmap->y0 + satmap->height); - line->coords[724] = satmap->x0 + satmap->width; - line->coords[725] = sz < 0.0 ? satmap->y0 : ( satmap->y0 + satmap->height ); + satmap->terminator_points[724] = satmap->x0 + satmap->width; + satmap->terminator_points[725] = sz < 0.0 ? satmap->y0 : + (satmap->y0 + satmap->height); - g_object_set(satmap->terminator, "points", line, NULL); - goo_canvas_points_unref(line); + satmap->terminator_count = 363; } void gtk_sat_map_lonlat_to_xy(GtkSatMap * m, @@ -2786,13 +2089,13 @@ void gtk_sat_map_lonlat_to_xy(GtkSatMap * m, { gfloat fx, fy; - fx = (gfloat) * x; - fy = (gfloat) * y; + fx = (gfloat)*x; + fy = (gfloat)*y; lonlat_to_xy(m, lon, lat, &fx, &fy); - *x = (gdouble) fx; - *y = (gdouble) fy; + *x = (gdouble)fx; + *y = (gdouble)fy; } void gtk_sat_map_reload_sats(GtkWidget * satmap, GHashTable * sats) @@ -2801,16 +2104,15 @@ void gtk_sat_map_reload_sats(GtkWidget * satmap, GHashTable * sats) GTK_SAT_MAP(satmap)->naos = 0.0; GTK_SAT_MAP(satmap)->ncat = 0; - /* reset ground track orbit to force repaint */ g_hash_table_foreach(GTK_SAT_MAP(satmap)->obj, reset_ground_track, NULL); } static void reset_ground_track(gpointer key, gpointer value, gpointer user_data) { - sat_map_obj_t *obj = (sat_map_obj_t *) value; - (void) key; - (void) user_data; + sat_map_obj_t *obj = (sat_map_obj_t *)value; + (void)key; + (void)user_data; obj->track_orbit = 0; } @@ -2821,8 +2123,7 @@ static gchar *aoslos_time_to_str(GtkSatMap * satmap, sat_t * sat) gdouble number, now; gchar *text = NULL; - - now = satmap->tstamp; //get_current_daynum (); + now = satmap->tstamp; if (sat->el > 0.0) { number = sat->los - now; @@ -2832,15 +2133,12 @@ static gchar *aoslos_time_to_str(GtkSatMap * satmap, sat_t * sat) number = sat->aos - now; } - /* convert julian date to seconds */ - s = (guint) (number * 86400); + s = (guint)(number * 86400); - /* extract hours */ - h = (guint) floor(s / 3600); + h = (guint)floor(s / 3600); s -= 3600 * h; - /* extract minutes */ - m = (guint) floor(s / 60); + m = (guint)floor(s / 60); s -= 60 * m; if (sat->el > 0.0) @@ -2851,17 +2149,14 @@ static gchar *aoslos_time_to_str(GtkSatMap * satmap, sat_t * sat) return text; } -/** Load the satellites that we should show tracks for */ static void gtk_sat_map_load_showtracks(GtkSatMap * satmap) { mod_cfg_get_integer_list_boolean(satmap->cfgdata, MOD_CFG_MAP_SECTION, MOD_CFG_MAP_SHOWTRACKS, satmap->showtracks); - } -/** Save the satellites that we should not show ground tracks */ static void gtk_sat_map_store_showtracks(GtkSatMap * satmap) { mod_cfg_set_integer_list_boolean(satmap->cfgdata, @@ -2870,7 +2165,6 @@ static void gtk_sat_map_store_showtracks(GtkSatMap * satmap) MOD_CFG_MAP_SHOWTRACKS); } -/** Save the satellites that we should not highlight coverage */ static void gtk_sat_map_store_hidecovs(GtkSatMap * satmap) { mod_cfg_set_integer_list_boolean(satmap->cfgdata, @@ -2879,7 +2173,6 @@ static void gtk_sat_map_store_hidecovs(GtkSatMap * satmap) MOD_CFG_MAP_HIDECOVS); } -/** Load the satellites that we should not highlight coverage */ static void gtk_sat_map_load_hide_coverages(GtkSatMap * satmap) { mod_cfg_get_integer_list_boolean(satmap->cfgdata, diff --git a/src/gtk-sat-map.h b/src/gtk-sat-map.h index 8b3c2a821..c19ec557a 100644 --- a/src/gtk-sat-map.h +++ b/src/gtk-sat-map.h @@ -4,7 +4,6 @@ #include #include #include -#include #include #include "gtk-sat-data.h" @@ -34,14 +33,14 @@ typedef struct { /** Data storage for ground tracks */ typedef struct { GSList *latlon; /*!< List of ssp_t */ - GSList *lines; /*!< List of GooCanvasPolyLine */ + GSList *lines; /*!< List of line segments (stored as point arrays) */ } ground_track_t; /** * Satellite object. * * This data structure represents a satellite object on the map. It consists of a - * small square representing the position, a label showinf the satellite name, and + * small square representing the position, a label showing the satellite name, and * the range circle. The range circle can have one or two parts, depending on * whether it is split or not. The oldrcnum and newrcnum fields are used for * keeping track of whether the range circle has one or two parts. @@ -54,13 +53,17 @@ typedef struct { gboolean showcov; /*!< Show coverage area. FIXME: redundant*/ gboolean istarget; /*!< is this object the target */ - /* graphical elements */ - GooCanvasItemModel *marker; /*!< A small rectangle showing sat pos. */ - GooCanvasItemModel *shadowm; /*!< Shadow under satellite marker. */ - GooCanvasItemModel *label; /*!< Satellite name. */ - GooCanvasItemModel *shadowl; /*!< Shadow under satellite name */ - GooCanvasItemModel *range1; /*!< First part of the range circle. */ - GooCanvasItemModel *range2; /*!< Second part of the range circle. */ + /* graphical elements - stored as coordinates for Cairo drawing */ + gfloat x; /*!< X position of marker */ + gfloat y; /*!< Y position of marker */ + gchar *nickname; /*!< Satellite nickname for label */ + gchar *tooltip; /*!< Tooltip text */ + + /* Range circle points */ + gdouble *range1_points; /*!< First part of the range circle points. */ + gint range1_count; /*!< Number of points in range1. */ + gdouble *range2_points; /*!< Second part of the range circle points. */ + gint range2_count; /*!< Number of points in range2. */ /* book keeping */ guint oldrcnum; /*!< Number of RC parts in prev. cycle. */ @@ -78,28 +81,23 @@ typedef struct { typedef struct { GtkBox vbox; - GtkWidget *canvas; /*!< The canvas widget. */ - - GooCanvasItemModel *map; /*!< The canvas map item. */ + GtkWidget *canvas; /*!< The drawing area widget. */ gdouble left_side_lon; /*!< Left-most longitude (used when center is not 0 lon). */ - GooCanvasItemModel *qthmark; /*!< QTH marker, e.g. small rectangle. */ - GooCanvasItemModel *qthlabel; /*!< Label showing the QTH name. */ - - GooCanvasItemModel *locnam; /*!< Location name. */ - GooCanvasItemModel *curs; /*!< Cursor tracking text. */ - GooCanvasItemModel *next; /*!< Next event text. */ - GooCanvasItemModel *sel; /*!< Text showing info about the selected satellite. */ - - GooCanvasItemModel *gridv[11]; /*!< Vertical grid lines, 30 deg resolution. */ - GooCanvasItemModel *gridvlab[11]; /*!< Vertical grid labels. */ - GooCanvasItemModel *gridh[5]; /*!< Horizontal grid lines, 30 deg resolution. */ - GooCanvasItemModel *gridhlab[5]; /*!< Horizontal grid labels. */ + /* Text elements */ + gchar *locnam_text; /*!< Location name text. */ + gchar *curs_text; /*!< Cursor tracking text. */ + gchar *next_text; /*!< Next event text. */ + gchar *sel_text; /*!< Text showing info about the selected satellite. */ - GooCanvasItemModel *terminator; /*!< Outline of sun shadow on Earth. */ + /* Grid line positions (calculated during draw) */ + gboolean grid_lines_valid; /*!< Whether grid lines need recalculation */ - gdouble terminator_last_tstamp; /*!< Timestamp of the last terminator drawn. Used to prevent redrawing the terminator too often. */ + /* Terminator points */ + gdouble *terminator_points; /*!< Terminator polyline points. */ + gint terminator_count; /*!< Number of terminator points. */ + gdouble terminator_last_tstamp; /*!< Timestamp of the last terminator drawn. */ gdouble naos; /*!< Next event time. */ gint ncat; /*!< Next event catnum. */ @@ -110,7 +108,7 @@ typedef struct { GHashTable *sats; /*!< Pointer to satellites (owned by parent GtkSatModule). */ qth_t *qth; /*!< Pointer to current location. */ - GHashTable *obj; /*!< Canvas items representing each satellite. */ + GHashTable *obj; /*!< Satellite objects (sat_map_obj_t) for each satellite. */ GHashTable *showtracks; /*!< A hash of satellites to show tracks for. */ GHashTable *hidecovs; /*!< A hash of satellites to hide coverage for. */ @@ -134,10 +132,20 @@ typedef struct { gboolean resize; /*!< Flag indicating that the map has been resized. */ gchar *infobgd; /*!< Background color of info text. */ + guint32 col_qth; /*!< QTH marker color. */ + guint32 col_info; /*!< Info text color. */ + guint32 col_grid; /*!< Grid color. */ + guint32 col_tick; /*!< Tick color. */ + guint32 col_sat; /*!< Satellite color. */ + guint32 col_sat_sel; /*!< Selected satellite color. */ + guint32 col_shadow; /*!< Shadow color. */ + guint32 col_track; /*!< Track color. */ + guint32 col_terminator; /*!< Terminator color. */ GdkPixbuf *origmap; /*!< Original map kept here for high quality scaling. */ + GdkPixbuf *map; /*!< Scaled map for current size. */ - GValue font; /*!< Default font */ + gchar *font; /*!< Default font name */ } GtkSatMap; diff --git a/src/gtk-sky-glance.c b/src/gtk-sky-glance.c index 1d54d57e8..2e431ebaa 100644 --- a/src/gtk-sky-glance.c +++ b/src/gtk-sky-glance.c @@ -31,12 +31,6 @@ * The sky at a glance widget provides a convenient overview of the upcoming * satellite passes in a timeline format. The widget is tied to a specific * module and uses the QTH and satellite data from the module. - * - * Note about the sizing policy: - * Initially we require 10 pixels per sat + 5 pix margin between the sats. - * - * When we get additional space due to resizing, the space will be allocated - * to make the rectangles taller. */ #ifdef HAVE_CONFIG_H @@ -44,8 +38,8 @@ #endif #include -#include #include +#include #include "config-keys.h" #include "gpredict-utils.h" @@ -67,7 +61,16 @@ #define SKG_FOOTER 50 #define SKG_CURSOR_WIDTH 0.5 -static GtkVBoxClass *parent_class = NULL; +static GtkBoxClass *parent_class = NULL; + +/** Convert rgba color to cairo-friendly format */ +static void rgba_to_cairo(guint32 rgba, gdouble *r, gdouble *g, gdouble *b, gdouble *a) +{ + *r = ((rgba >> 24) & 0xFF) / 255.0; + *g = ((rgba >> 16) & 0xFF) / 255.0; + *b = ((rgba >> 8) & 0xFF) / 255.0; + *a = (rgba & 0xFF) / 255.0; +} static void gtk_sky_glance_init(GtkSkyGlance * skg, gpointer g_class) @@ -87,65 +90,76 @@ static void gtk_sky_glance_init(GtkSkyGlance * skg, skg->satcnt = 0; skg->ts = 0.0; skg->te = 0.0; + skg->num_ticks = 0; + skg->major_x = NULL; + skg->minor_x = NULL; + skg->tick_labels = NULL; + skg->cursor_x = 0.0; + skg->time_label = NULL; + skg->font = NULL; +} + +static void free_sat_label(gpointer data) +{ + sat_label_t *label = (sat_label_t *)data; + if (label) + { + g_free(label->name); + g_free(label); + } } -/** - * Destroy the GtkSkyGlance widget - * - * @param object Pointer to the GtkSkyGlance widget - * - * This function is called when the GtkSkyGlance widget is destroyed. It frees - * the memory that has been allocated when the widget was created. - * - * @bug For some reason, this function is called twice when parent is destroyed. - */ static void gtk_sky_glance_destroy(GtkWidget * widget) { - sky_pass_t *skypass; - guint i, n; + GtkSkyGlance *skg = GTK_SKY_GLANCE(widget); + sky_pass_t *skypass; + guint i, n; /* free passes */ /* FIXME: TBC whether this is enough */ - if (GTK_SKY_GLANCE(widget)->passes != NULL) + if (skg->passes != NULL) { - n = g_slist_length(GTK_SKY_GLANCE(widget)->passes); + n = g_slist_length(skg->passes); for (i = 0; i < n; i++) { - skypass = - (sky_pass_t *) g_slist_nth_data(GTK_SKY_GLANCE(widget)->passes, - i); + skypass = (sky_pass_t *)g_slist_nth_data(skg->passes, i); free_pass(skypass->pass); g_free(skypass); } - - g_slist_free(GTK_SKY_GLANCE(widget)->passes); - GTK_SKY_GLANCE(widget)->passes = NULL; + g_slist_free(skg->passes); + skg->passes = NULL; } - /* for the rest we only need to free the GSList because the - canvas items will be freed when removed from canvas. - */ - if (GTK_SKY_GLANCE(widget)->satlab != NULL) - { - g_slist_free(GTK_SKY_GLANCE(widget)->satlab); - GTK_SKY_GLANCE(widget)->satlab = NULL; - } - if (GTK_SKY_GLANCE(widget)->majors != NULL) - { - g_slist_free(GTK_SKY_GLANCE(widget)->majors); - GTK_SKY_GLANCE(widget)->majors = NULL; - } - if (GTK_SKY_GLANCE(widget)->minors != NULL) + /* free satellite labels */ + if (skg->satlab != NULL) { - g_slist_free(GTK_SKY_GLANCE(widget)->minors); - GTK_SKY_GLANCE(widget)->minors = NULL; + g_slist_free_full(skg->satlab, free_sat_label); + skg->satlab = NULL; } - if (GTK_SKY_GLANCE(widget)->labels != NULL) + + /* free tick data */ + g_free(skg->major_x); + skg->major_x = NULL; + + g_free(skg->minor_x); + skg->minor_x = NULL; + + if (skg->tick_labels) { - g_slist_free(GTK_SKY_GLANCE(widget)->labels); - GTK_SKY_GLANCE(widget)->labels = NULL; + for (i = 0; i < (guint)skg->num_ticks; i++) + { + g_free(skg->tick_labels[i]); + } + g_free(skg->tick_labels); + skg->tick_labels = NULL; } + g_free(skg->time_label); + skg->time_label = NULL; + + g_free(skg->font); + skg->font = NULL; + (*GTK_WIDGET_CLASS(parent_class)->destroy) (widget); } @@ -191,13 +205,6 @@ GType gtk_sky_glance_get_type() /** * Convert time value to x position. - * - * @param skg The GtkSkyGlance widget. - * @param t Julian date user is presented with brief info about the - * satellite pass and a suggestion to click on the box for more info. - * @return X coordinate. - * - * No error checking is made to ensure that we are within visible range. */ static gdouble t2x(GtkSkyGlance * skg, gdouble t) { @@ -210,12 +217,6 @@ static gdouble t2x(GtkSkyGlance * skg, gdouble t) /** * Convert x coordinate to Julian date. - * - * @param skg The GtkSkyGlance widget. - * @param x The X coordinate. - * @return The Julian date corresponding to X. - * - * No error checking is made to ensure that we are within visible range. */ static gdouble x2t(GtkSkyGlance * skg, gdouble x) { @@ -226,217 +227,201 @@ static gdouble x2t(GtkSkyGlance * skg, gdouble x) return (skg->ts + frac * (skg->te - skg->ts)); } -/** - * Manage new size allocation. - * - * This function is called when the canvas receives a new size allocation, - * e.g. when the container is re-sized. The function re-calculates the graph - * dimensions based on the new canvas size. - */ -static void size_allocate_cb(GtkWidget * widget, GtkAllocation * allocation, - gpointer data) +static sky_pass_t *find_pass_at_pos(GtkSkyGlance *skg, gdouble mx, gdouble my) { - GtkSkyGlance *skg; - GooCanvasPoints *pts; - GooCanvasItem *obj; - gint i, j, n; - guint curcat; - gdouble th, tm; - gdouble xh, xm; - sky_pass_t *skp; - gdouble x, y, w, h; + GSList *node; + sky_pass_t *skypass; - if (gtk_widget_get_realized(widget)) + node = skg->passes; + while (node) { - /* get graph dimensions */ - skg = GTK_SKY_GLANCE(data); - skg->w = allocation->width; - skg->h = allocation->height - SKG_FOOTER; - skg->x0 = 0; - skg->y0 = 0; - skg->pps = (skg->h - SKG_MARGIN) / skg->numsat - SKG_MARGIN; - goo_canvas_set_bounds(GOO_CANVAS(GTK_SKY_GLANCE(skg)->canvas), 0, 0, - allocation->width, allocation->height); + skypass = (sky_pass_t *)node->data; + if (mx >= skypass->x && mx <= skypass->x + skypass->w && + my >= skypass->y && my <= skypass->y + skypass->h) + { + return skypass; + } + node = node->next; + } + return NULL; +} + +static gboolean on_draw(GtkWidget *widget, cairo_t *cr, gpointer data) +{ + GtkSkyGlance *skg = GTK_SKY_GLANCE(data); + gdouble r, g, b, a; + PangoLayout *layout; + PangoFontDescription *font_desc; + gint tw, th; + guint i; + sky_pass_t *skypass; + GSList *node; + sat_label_t *label; - /* background */ - g_object_set(skg->bgd, "x", (gdouble) skg->x0, "y", (gdouble) skg->y0, - "width", (gdouble) skg->w, "height", (gdouble) skg->h, - NULL); + (void)widget; - /* update cursor tracking line */ - g_object_set(skg->cursor, "x", (gdouble) skg->x0, "y", (gdouble) skg->y0, - "width", SKG_CURSOR_WIDTH, "height", (gdouble) skg->h, NULL); + /* Set up font */ + layout = pango_cairo_create_layout(cr); + font_desc = pango_font_description_from_string(skg->font ? skg->font : "Sans 9"); + pango_layout_set_font_description(layout, font_desc); - /* time label */ - g_object_set(skg->timel, "x", (gdouble) skg->x0 + 5, NULL); + /* Background */ + cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); + cairo_rectangle(cr, skg->x0, skg->y0, skg->w, skg->h); + cairo_fill(cr); - /* update footer */ - g_object_set(skg->footer, - "x", (gdouble) skg->x0, - "y", (gdouble) skg->h, - "width", (gdouble) skg->w, - "height", (gdouble) SKG_FOOTER, NULL); + /* Draw satellite pass boxes */ + node = skg->passes; + while (node) + { + skypass = (sky_pass_t *)node->data; + + /* Fill */ + rgba_to_cairo(skypass->fcol, &r, &g, &b, &a); + cairo_set_source_rgba(cr, r, g, b, a); + cairo_rectangle(cr, skypass->x, skypass->y, skypass->w, skypass->h); + cairo_fill(cr); + + /* Border */ + rgba_to_cairo(skypass->bcol, &r, &g, &b, &a); + cairo_set_source_rgba(cr, r, g, b, a); + cairo_set_line_width(cr, 1.0); + cairo_rectangle(cr, skypass->x, skypass->y, skypass->w, skypass->h); + cairo_stroke(cr); + + node = node->next; + } - g_object_set(skg->axisl, - "x", (gdouble) (skg->w / 2), - "y", (gdouble) (skg->h + SKG_FOOTER - 5), NULL); + /* Draw satellite labels */ + node = skg->satlab; + while (node) + { + label = (sat_label_t *)node->data; - /* get the first hour and first 30 min slot */ - th = ceil(skg->ts * 24.0) / 24.0; + rgba_to_cairo(label->color, &r, &g, &b, &a); + cairo_set_source_rgba(cr, r, g, b, a); + pango_layout_set_text(layout, label->name, -1); + pango_layout_get_pixel_size(layout, &tw, &th); - /* workaround for bug 1839140 (first hour incorrexct) */ - th += 0.00069; + if (label->anchor == 1) /* East anchor */ + cairo_move_to(cr, label->x - tw, label->y - th / 2); + else /* West anchor */ + cairo_move_to(cr, label->x, label->y - th / 2); - if ((th - skg->ts) > 0.0208333) - { - tm = th - 0.0208333; - } - else + pango_cairo_show_layout(cr, layout); + + node = node->next; + } + + /* Cursor tracking line */ + if (skg->cursor_x > skg->x0 && skg->cursor_x < skg->x0 + skg->w) + { + cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.7); + cairo_set_line_width(cr, SKG_CURSOR_WIDTH); + cairo_move_to(cr, skg->cursor_x, skg->y0); + cairo_line_to(cr, skg->cursor_x, skg->y0 + skg->h); + cairo_stroke(cr); + + /* Time label */ + if (skg->time_label) { - tm = th + 0.0208333; + pango_layout_set_text(layout, skg->time_label, -1); + pango_layout_get_pixel_size(layout, &tw, &th); + cairo_move_to(cr, skg->x0 + 5, skg->y0); + pango_cairo_show_layout(cr, layout); } + } - /* the number of steps equals the number of hours */ - n = sat_cfg_get_int(SAT_CFG_INT_SKYATGL_TIME); - for (i = 0; i < n; i++) - { - xh = t2x(skg, th); - - pts = goo_canvas_points_new(2); - pts->coords[0] = xh; - pts->coords[1] = skg->h; - pts->coords[2] = xh; - pts->coords[3] = skg->h + 10; - obj = g_slist_nth_data(skg->majors, i); - g_object_set(obj, "points", pts, NULL); - goo_canvas_points_unref(pts); - - obj = g_slist_nth_data(skg->labels, i); - g_object_set(obj, - "x", (gdouble) xh, - "y", (gdouble) (skg->h + 12), NULL); - - /* 30 min tick */ - xm = t2x(skg, tm); - - pts = goo_canvas_points_new(2); - pts->coords[0] = xm; - pts->coords[1] = skg->h; - pts->coords[2] = xm; - pts->coords[3] = skg->h + 5; - obj = g_slist_nth_data(skg->minors, i); - g_object_set(obj, "points", pts, NULL); - goo_canvas_points_unref(pts); + /* Footer background */ + cairo_set_source_rgba(cr, 0.0, 0.0, 0.25, 1.0); + cairo_rectangle(cr, skg->x0, skg->h, skg->w, SKG_FOOTER); + cairo_fill(cr); - th += 0.04167; - tm += 0.04167; - } + /* Time axis ticks and labels */ + cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); + cairo_set_line_width(cr, 1.0); - /* update pass items */ - n = g_slist_length(skg->passes); - j = -1; - curcat = 0; - y = 10.0; - h = 10.0; - for (i = 0; i < n; i++) + for (i = 0; i < (guint)skg->num_ticks; i++) + { + /* Major tick */ + if (skg->major_x) { - /* get pass */ - skp = (sky_pass_t *) g_slist_nth_data(skg->passes, i); + cairo_move_to(cr, skg->major_x[i], skg->h); + cairo_line_to(cr, skg->major_x[i], skg->h + 10); + cairo_stroke(cr); - x = t2x(skg, skp->pass->aos); - w = t2x(skg, skp->pass->los) - x; - - /* new satellite? */ - if (skp->catnum != curcat) + /* Tick label */ + if (skg->tick_labels && skg->tick_labels[i]) { - j++; - curcat = skp->catnum; - y = j * (skg->pps + SKG_MARGIN) + SKG_MARGIN; - h = skg->pps; - - /* update label */ - obj = g_slist_nth_data(skg->satlab, j); - if (x > (skg->x0 + 100)) - g_object_set(obj, "x", x - 5, "y", y + h / 2.0, - "anchor", GOO_CANVAS_ANCHOR_E, NULL); - else - g_object_set(obj, "x", x + w + 5, "y", y + h / 2.0, - "anchor", GOO_CANVAS_ANCHOR_W, NULL); + pango_layout_set_text(layout, skg->tick_labels[i], -1); + pango_layout_get_pixel_size(layout, &tw, &th); + cairo_move_to(cr, skg->major_x[i] - tw / 2, skg->h + 12); + pango_cairo_show_layout(cr, layout); } + } - g_object_set(skp->box, - "x", x, "y", y, "width", w, "height", h, NULL); - /* need to raise item, otherwise it will not receive new events */ - goo_canvas_item_raise(skp->box, NULL); + /* Minor tick */ + if (skg->minor_x) + { + cairo_move_to(cr, skg->minor_x[i], skg->h); + cairo_line_to(cr, skg->minor_x[i], skg->h + 5); + cairo_stroke(cr); } } -} -/** - * Manage canvas realise signals. - * - * This function is used to re-initialise the graph dimensions when - * the graph is realized, i.e. displayed for the first time. This is - * necessary in order to compensate for missing "re-allocate" signals for - * graphs that have not yet been realised, e.g. when opening several module - */ -static void on_canvas_realized(GtkWidget * canvas, gpointer data) -{ - GtkAllocation aloc; + /* Axis label */ + { + const gchar *axis_text = sat_cfg_get_bool(SAT_CFG_BOOL_USE_LOCAL_TIME) ? _("TIME") : _("UTC"); + pango_layout_set_text(layout, axis_text, -1); + pango_layout_get_pixel_size(layout, &tw, &th); + cairo_move_to(cr, skg->w / 2 - tw / 2, skg->h + SKG_FOOTER - 5 - th); + pango_cairo_show_layout(cr, layout); + } - gtk_widget_get_allocation(canvas, &aloc); - size_allocate_cb(canvas, &aloc, data); + pango_font_description_free(font_desc); + g_object_unref(layout); + + return FALSE; } -/** Manage mouse motion events. */ -static gboolean on_motion_notify(GooCanvasItem * item, GooCanvasItem * target, - GdkEventMotion * event, gpointer data) +static gboolean on_motion_notify(GtkWidget *widget, GdkEventMotion *event, gpointer data) { GtkSkyGlance *skg = GTK_SKY_GLANCE(data); gdouble t; gchar buff[6]; - (void)item; - (void)target; + (void)widget; - /* update cursor tracking line and time label */ - g_object_set(skg->cursor, "x", (gdouble) event->x, "y", (gdouble) skg->y0, - "width", SKG_CURSOR_WIDTH, "height", (gdouble) skg->h, NULL); + skg->cursor_x = event->x; /* get time corresponding to x */ t = x2t(skg, event->x); - daynum_to_str(buff, 6, "%H:%M", t); - g_object_set(skg->timel, "text", buff, NULL); + + g_free(skg->time_label); + skg->time_label = g_strdup(buff); + + gtk_widget_queue_draw(skg->canvas); return TRUE; } -/** - * Manage button release events. - * - * @param item The GooCanvasItem object that received the button press event. - * @param target The target of the event (what?). - * @param event Event data, such as X and Y coordinates. - * @param data User data; points to the GtkSkyAtGlance object. - * @return Always TRUE to prevent further propagation of the event. - * - * This function is called when the mouse button is released above - * a satellite pass object. - */ -static gboolean on_button_release(GooCanvasItem * item, GooCanvasItem * target, - GdkEventButton * event, gpointer data) +static gboolean on_button_release(GtkWidget *widget, GdkEventButton *event, gpointer data) { GtkSkyGlance *skg = GTK_SKY_GLANCE(data); - pass_t *pass; + sky_pass_t *skypass; pass_t *new_pass; - (void)target; + (void)widget; + + if (event->button != 1) + return FALSE; - /* get pointer to pass_t structure */ - pass = (pass_t *) g_object_get_data(G_OBJECT(item), "pass"); + skypass = find_pass_at_pos(skg, event->x, event->y); + if (skypass == NULL) + return FALSE; - if (G_UNLIKELY(pass == NULL)) + if (skypass->pass == NULL) { sat_log_log(SAT_LOG_LEVEL_ERROR, _("%s::%s: Could not retrieve pass_t object"), @@ -444,147 +429,117 @@ static gboolean on_button_release(GooCanvasItem * item, GooCanvasItem * target, return TRUE; } - switch (event->button) - { - /* LEFT button released */ - case 1: - new_pass = copy_pass(pass); - sat_log_log(SAT_LOG_LEVEL_ERROR, - _ - ("%s::%s: Showing pass details for %s - we may have a memory leak here"), - __FILE__, __func__, pass->satname); - - /* show the pass details */ - show_pass(pass->satname, skg->qth, new_pass, NULL); - - break; + new_pass = copy_pass(skypass->pass); + sat_log_log(SAT_LOG_LEVEL_DEBUG, + _("%s::%s: Showing pass details for %s"), + __FILE__, __func__, skypass->pass->satname); - default: - sat_log_log(SAT_LOG_LEVEL_DEBUG, - _("%s::%s: Button %d has no function..."), - __FILE__, __func__, event->button); - break; - } + /* show the pass details */ + show_pass(skypass->pass->satname, skg->qth, new_pass, NULL); return TRUE; } - -/** - * Create the model for the GtkSkyGlance canvas - * - * @param skg Pointer to the GtkSkyGlance widget - */ -static void create_canvas_items(GtkSkyGlance * skg) +static void size_allocate_cb(GtkWidget * widget, GtkAllocation * allocation, + gpointer data) { - GooCanvasItem *root; - GooCanvasItem *hrt, *hrl, *hrm; - guint i, n; + GtkSkyGlance *skg; + gint i, j, n; + guint curcat; gdouble th, tm; - gdouble xh, xm; - gchar buff[3]; + sky_pass_t *skp; + gdouble x, y, w, h; + sat_label_t *label; + GSList *node; - root = goo_canvas_get_root_item(GOO_CANVAS(skg->canvas)); - g_signal_connect(root, "motion_notify_event", - (GCallback) on_motion_notify, skg); - - /* background */ - skg->bgd = goo_canvas_rect_new(root, skg->x0, skg->y0, skg->w, skg->h, - "fill-color-rgba", 0xFFFFFFFF, - "stroke-color-rgba", 0xFFFFFFFF, NULL); - - /* cursor tracking line */ - skg->cursor = goo_canvas_polyline_new_line(root, - skg->x0, skg->y0, - skg->x0, skg->h, - "stroke-color-rgba", 0x000000AF, - "line-width", SKG_CURSOR_WIDTH, - NULL); - - /* time label */ - skg->timel = goo_canvas_text_new(root, "--:--", - skg->x0 + 5, skg->y0, - -1, GOO_CANVAS_ANCHOR_NW, - "font", g_value_get_string(&skg->font), - "fill-color-rgba", 0x000000AF, NULL); - - /* footer */ - skg->footer = goo_canvas_rect_new(root, - skg->x0, skg->h, - skg->w, SKG_FOOTER, - "fill-color-rgba", 0x00003FFF, - "stroke-color-rgba", 0xFFFFFFFF, NULL); - - /* time ticks and labels */ - if (sat_cfg_get_bool(SAT_CFG_BOOL_USE_LOCAL_TIME)) - skg->axisl = goo_canvas_text_new(root, _("TIME"), - skg->w / 2, - skg->h + SKG_FOOTER - 5, - -1, GOO_CANVAS_ANCHOR_S, - "font", g_value_get_string(&skg->font), - "fill-color-rgba", 0xFFFFFFFF, NULL); - else - skg->axisl = goo_canvas_text_new(root, _("UTC"), - skg->w / 2, - skg->h + SKG_FOOTER - 5, - -1, GOO_CANVAS_ANCHOR_S, - "font", g_value_get_string(&skg->font), - "fill-color-rgba", 0xFFFFFFFF, NULL); + if (gtk_widget_get_realized(widget)) + { + skg = GTK_SKY_GLANCE(data); + skg->w = allocation->width; + skg->h = allocation->height - SKG_FOOTER; + skg->x0 = 0; + skg->y0 = 0; + skg->pps = (skg->h - SKG_MARGIN) / skg->numsat - SKG_MARGIN; + /* Update tick positions */ + th = ceil(skg->ts * 24.0) / 24.0; + th += 0.00069; /* workaround for bug 1839140 */ - /* get the first hour and first 30 min slot */ - th = ceil(skg->ts * 24.0) / 24.0; + if ((th - skg->ts) > 0.0208333) + tm = th - 0.0208333; + else + tm = th + 0.0208333; - /* workaround for bug 1839140 (first hour incorrexct) */ - th += 0.00069; + for (i = 0; i < skg->num_ticks; i++) + { + if (skg->major_x) + skg->major_x[i] = t2x(skg, th); + if (skg->minor_x) + skg->minor_x[i] = t2x(skg, tm); - /* the first 30 min tick can be either before - or after the first hour tick - */ - if ((th - skg->ts) > 0.0208333) - { - tm = th - 0.0208333; - } - else - { - tm = th + 0.0208333; - } + th += 0.04167; + tm += 0.04167; + } - /* the number of steps equals the number of hours */ - n = sat_cfg_get_int(SAT_CFG_INT_SKYATGL_TIME); - for (i = 0; i < n; i++) - { - /* hour tick */ - xh = t2x(skg, th); - hrt = - goo_canvas_polyline_new_line(root, xh, skg->h, xh, skg->h + 10, - "stroke-color-rgba", 0xFFFFFFFF, - NULL); - - /* hour tick label */ - daynum_to_str(buff, 3, "%H", th); + /* Update pass box positions */ + n = g_slist_length(skg->passes); + j = -1; + curcat = 0; + y = 10.0; + h = 10.0; + node = skg->satlab; + for (i = 0; i < n; i++) + { + skp = (sky_pass_t *)g_slist_nth_data(skg->passes, i); - hrl = goo_canvas_text_new(root, buff, xh, skg->h + 12, - -1, GOO_CANVAS_ANCHOR_N, - "font", g_value_get_string(&skg->font), - "fill-color-rgba", 0xFFFFFFFF, NULL); + x = t2x(skg, skp->pass->aos); + w = t2x(skg, skp->pass->los) - x; - /* 30 min tick */ - xm = t2x(skg, tm); - hrm = goo_canvas_polyline_new_line(root, xm, skg->h, xm, skg->h + 5, - "stroke-color-rgba", 0xFFFFFFFF, - NULL); + /* new satellite? */ + if (skp->catnum != curcat) + { + j++; + curcat = skp->catnum; + y = j * (skg->pps + SKG_MARGIN) + SKG_MARGIN; + h = skg->pps; - /* store canvas items */ - skg->majors = g_slist_append(skg->majors, hrt); - skg->labels = g_slist_append(skg->labels, hrl); - skg->minors = g_slist_append(skg->minors, hrm); + /* update label position */ + if (node) + { + label = (sat_label_t *)node->data; + label->y = y + h / 2.0; + if (x > (skg->x0 + 100)) + { + label->x = x - 5; + label->anchor = 1; /* East */ + } + else + { + label->x = x + w + 5; + label->anchor = 0; /* West */ + } + node = node->next; + } + } - th += 0.0416667; - tm += 0.0416667; + skp->x = x; + skp->y = y; + skp->w = w; + skp->h = h; + } + + gtk_widget_queue_draw(skg->canvas); } } +static void on_canvas_realized(GtkWidget * canvas, gpointer data) +{ + GtkAllocation aloc; + + gtk_widget_get_allocation(canvas, &aloc); + size_allocate_cb(canvas, &aloc, data); +} + /** Fetch the basic colour and add alpha channel */ static void get_colors(guint i, guint * bcol, guint * fcol) { @@ -652,14 +607,6 @@ static void get_colors(guint i, guint * bcol, guint * fcol) /** * Create canvas items for a satellite - * - * @param key Pointer to the hash key (catnum of sat) - * @param value Pointer to the current satellite. - * @param data Pointer to the GtkSkyGlance object. - * - * This function is called by g_hash_table_foreach with each satellite in - * the satellite hash table. It gets the passes for the current satellite - * and creates the corresponding canvas items. */ static void create_sat(gpointer key, gpointer value, gpointer data) { @@ -670,20 +617,11 @@ static void create_sat(gpointer key, gpointer value, gpointer data) guint i, n; pass_t *tmppass = NULL; sky_pass_t *skypass; - guint bcol, fcol; /* colors */ - GooCanvasItem *root; - GooCanvasItem *label; + guint bcol, fcol; + sat_label_t *label; (void)key; - /* tooltips vars */ - gchar *tooltip; /* the complete tooltips string */ - gchar aosstr[100]; /* AOS time string */ - gchar losstr[100]; /* LOS time string */ - gchar tcastr[100]; /* TCA time string */ - - /* get canvas root */ - root = goo_canvas_get_root_item(GOO_CANVAS(skg->canvas)); get_colors(skg->satcnt++, &bcol, &fcol); maxdt = skg->te - skg->ts; @@ -694,13 +632,12 @@ static void create_sat(gpointer key, gpointer value, gpointer data) _("%s:%d: %s has %d passes within %.4f days\n"), __FILE__, __LINE__, sat->nickname, n, maxdt); - /* add sky_pass_t items to skg->passes */ if (passes != NULL) { /* add pass items */ for (i = 0; i < n; i++) { - skypass = g_try_new(sky_pass_t, 1); + skypass = g_try_new0(sky_pass_t, 1); if (skypass == NULL) { sat_log_log(SAT_LOG_LEVEL_ERROR, @@ -709,62 +646,71 @@ static void create_sat(gpointer key, gpointer value, gpointer data) continue; } - /* create pass structure items */ skypass->catnum = sat->tle.catnr; - tmppass = (pass_t *) g_slist_nth_data(passes, i); + tmppass = (pass_t *)g_slist_nth_data(passes, i); skypass->pass = copy_pass(tmppass); + skypass->bcol = bcol; + skypass->fcol = fcol; - daynum_to_str(aosstr, TIME_FORMAT_MAX_LENGTH, - sat_cfg_get_str(SAT_CFG_STR_TIME_FORMAT), - skypass->pass->aos); - daynum_to_str(losstr, TIME_FORMAT_MAX_LENGTH, - sat_cfg_get_str(SAT_CFG_STR_TIME_FORMAT), - skypass->pass->los); - daynum_to_str(tcastr, TIME_FORMAT_MAX_LENGTH, - sat_cfg_get_str(SAT_CFG_STR_TIME_FORMAT), - skypass->pass->tca); - - /* box tooltip will contain pass summary */ - tooltip = g_strdup_printf(_("%s\n" - "AOS: %s Az:%.0f\302\260\n" - "TCA: %s Az:%.0f\302\260 El:%.1f\302\260\n" - "LOS: %s Az:%.0f\302\260\n" - "Click for details"), - skypass->pass->satname, - aosstr, skypass->pass->aos_az, - tcastr, skypass->pass->maxel_az, - skypass->pass->max_el, losstr, - skypass->pass->los_az); - - skypass->box = goo_canvas_rect_new(root, 10, 10, 20, 20, - "stroke-color-rgba", bcol, - "fill-color-rgba", fcol, - "line-width", 1.0, - "antialias", - CAIRO_ANTIALIAS_NONE, "tooltip", - tooltip, "can-focus", TRUE, - NULL); - g_free(tooltip); - - /* store this pass in list */ - skg->passes = g_slist_append(skg->passes, skypass); - - /* store a pointer to the pass data in the GooCanvasItem so that we - can access it later during various events, e.g mouse click */ - g_object_set_data(G_OBJECT(skypass->box), "pass", skypass->pass); + /* Initial position will be set in size_allocate_cb */ + skypass->x = 0; + skypass->y = 0; + skypass->w = 10; + skypass->h = 10; - g_signal_connect(skypass->box, "button_release_event", - (GCallback) on_button_release, skg); + skg->passes = g_slist_append(skg->passes, skypass); } free_passes(passes); /* add satellite label */ - label = goo_canvas_text_new(root, sat->nickname, - 5, 0, -1, GOO_CANVAS_ANCHOR_W, - "font", g_value_get_string(&skg->font), - "fill-color-rgba", bcol, NULL); - skg->satlab = g_slist_append(skg->satlab, label); + label = g_try_new0(sat_label_t, 1); + if (label) + { + label->name = g_strdup(sat->nickname); + label->color = bcol; + label->x = 5; + label->y = 0; + label->anchor = 0; + skg->satlab = g_slist_append(skg->satlab, label); + } + } +} + +/** + * Create the time tick data + */ +static void create_time_ticks(GtkSkyGlance * skg) +{ + guint i; + gdouble th, tm; + gchar buff[3]; + + skg->num_ticks = sat_cfg_get_int(SAT_CFG_INT_SKYATGL_TIME); + + skg->major_x = g_new0(gdouble, skg->num_ticks); + skg->minor_x = g_new0(gdouble, skg->num_ticks); + skg->tick_labels = g_new0(gchar *, skg->num_ticks); + + /* get the first hour and first 30 min slot */ + th = ceil(skg->ts * 24.0) / 24.0; + th += 0.00069; /* workaround for bug 1839140 */ + + if ((th - skg->ts) > 0.0208333) + tm = th - 0.0208333; + else + tm = th + 0.0208333; + + for (i = 0; i < (guint)skg->num_ticks; i++) + { + skg->major_x[i] = t2x(skg, th); + skg->minor_x[i] = t2x(skg, tm); + + daynum_to_str(buff, 3, "%H", th); + skg->tick_labels[i] = g_strdup(buff); + + th += 0.0416667; + tm += 0.0416667; } } @@ -779,6 +725,7 @@ GtkWidget *gtk_sky_glance_new(GHashTable * sats, qth_t * qth, gdouble ts) { GtkSkyGlance *skg; guint number; + GValue font_value = G_VALUE_INIT; /* check that we have at least one satellite */ number = g_hash_table_size(sats); @@ -789,7 +736,10 @@ GtkWidget *gtk_sky_glance_new(GHashTable * sats, qth_t * qth, gdouble ts) skg = GTK_SKY_GLANCE(g_object_new(GTK_TYPE_SKY_GLANCE, NULL)); /* default font */ - g_object_get_property(G_OBJECT(gtk_settings_get_default()), "gtk-font-name", &skg->font); + g_value_init(&font_value, G_TYPE_STRING); + g_object_get_property(G_OBJECT(gtk_settings_get_default()), "gtk-font-name", &font_value); + skg->font = g_value_dup_string(&font_value); + g_value_unset(&font_value); /* FIXME? */ skg->sats = sats; @@ -800,31 +750,33 @@ GtkWidget *gtk_sky_glance_new(GHashTable * sats, qth_t * qth, gdouble ts) /* if ts = 0 use current time */ skg->ts = ts > 0.0 ? ts : get_current_daynum(); - skg->te = skg->ts + - sat_cfg_get_int(SAT_CFG_INT_SKYATGL_TIME) * (1.0 / 24.0); + skg->te = skg->ts + sat_cfg_get_int(SAT_CFG_INT_SKYATGL_TIME) * (1.0 / 24.0); /* calculate preferred sizes */ skg->w = SKG_DEFAULT_WIDTH; skg->h = skg->numsat * SKG_PIX_PER_SAT + (skg->numsat + 1) * SKG_MARGIN; skg->pps = SKG_PIX_PER_SAT; - /* create the canvas */ - skg->canvas = goo_canvas_new(); - g_object_set(G_OBJECT(skg->canvas), "has-tooltip", TRUE, NULL); + /* create the canvas (drawing area) */ + skg->canvas = gtk_drawing_area_new(); + gtk_widget_set_has_tooltip(skg->canvas, TRUE); gtk_widget_set_size_request(skg->canvas, skg->w, skg->h + SKG_FOOTER); - goo_canvas_set_bounds(GOO_CANVAS(skg->canvas), 0, 0, - skg->w, skg->h + SKG_FOOTER); + gtk_widget_add_events(skg->canvas, GDK_POINTER_MOTION_MASK | + GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK); - /* connect size-request signal */ - g_signal_connect(skg->canvas, "size-allocate", - (GCallback) size_allocate_cb, skg); - g_signal_connect_after(skg->canvas, "realize", - (GCallback) on_canvas_realized, skg); + /* connect signals */ + g_signal_connect(skg->canvas, "draw", G_CALLBACK(on_draw), skg); + g_signal_connect(skg->canvas, "motion-notify-event", G_CALLBACK(on_motion_notify), skg); + g_signal_connect(skg->canvas, "button-release-event", G_CALLBACK(on_button_release), skg); + g_signal_connect(skg->canvas, "size-allocate", G_CALLBACK(size_allocate_cb), skg); + g_signal_connect_after(skg->canvas, "realize", G_CALLBACK(on_canvas_realized), skg); gtk_widget_show(skg->canvas); - /* Create the canvas items */ - create_canvas_items(skg); + /* Create the time tick data */ + create_time_ticks(skg); + + /* Create satellite pass data */ g_hash_table_foreach(skg->sats, create_sat, skg); gtk_box_pack_start(GTK_BOX(skg), skg->canvas, TRUE, TRUE, 0); diff --git a/src/gtk-sky-glance.h b/src/gtk-sky-glance.h index 27b925ac0..12e389f3d 100644 --- a/src/gtk-sky-glance.h +++ b/src/gtk-sky-glance.h @@ -30,7 +30,6 @@ #include #include #include -#include #include #include "gtk-sat-data.h" @@ -57,7 +56,12 @@ typedef struct _GtkSkyGlanceClass GtkSkyGlanceClass; typedef struct { guint catnum; /* Catalog number of satellite */ pass_t *pass; /* Details of the corresponding pass. */ - GooCanvasItem *box; /* Canvas item showing the pass */ + gdouble x; /* X position of box */ + gdouble y; /* Y position of box */ + gdouble w; /* Width of box */ + gdouble h; /* Height of box */ + guint32 bcol; /* Border color */ + guint32 fcol; /* Fill color */ } sky_pass_t; @@ -68,16 +72,16 @@ typedef struct { struct _GtkSkyGlance { GtkBox vbox; - GtkWidget *canvas; + GtkWidget *canvas; /*!< The drawing area widget */ GHashTable *sats; /* Local copy of satellites. */ qth_t *qth; /* Pointer to current location. */ - GSList *passes; /* Canvas items representing each pass. - * Each element in the list is of type sky_pass_t. - */ - GSList *satlab; /* Canvas items showing satellite names. */ + GSList *passes; /* List of sky_pass_t representing each pass. */ + GSList *satlab; /* List of satellite label data (name, color, position). */ + /* Satellite label data structure */ + /* Each element is a struct { gchar *name; guint32 color; gdouble x, y; } */ guint x0; guint y0; @@ -94,20 +98,28 @@ struct _GtkSkyGlance { */ gdouble ts, te; /* Start and end times (Julian date) */ - GSList *majors; /* Major ticks for every hour */ - GSList *minors; /* Minor ticks for every 30 min */ - GSList *labels; /* Tick labels for every hour */ + /* Time tick data */ + gint num_ticks; /* Number of time ticks */ + gdouble *major_x; /* X positions of major ticks */ + gdouble *minor_x; /* X positions of minor ticks */ + gchar **tick_labels; /* Tick label strings */ - GooCanvasItem *bgd; /* Canvas background */ - GooCanvasItem *footer; /* Footer area with time ticks and labels */ - GooCanvasItem *axisl; /* Axis label */ - GooCanvasItem *cursor; /* Vertical line tracking the cursor */ - GooCanvasItem *timel; /* Label showing time under cursor */ + /* Cursor tracking */ + gdouble cursor_x; /* Current cursor X position */ + gchar *time_label; /* Time label text at cursor */ - GValue font; /*!< Default font */ + gchar *font; /*!< Default font name */ }; +/** Satellite label structure for drawing */ +typedef struct { + gchar *name; /* Satellite name */ + guint32 color; /* Label color */ + gdouble x; /* X position */ + gdouble y; /* Y position */ + gint anchor; /* Anchor type: 0=west, 1=east */ +} sat_label_t; struct _GtkSkyGlanceClass { GtkBoxClass parent_class; }; diff --git a/win32/README b/win32/README index 05a30c57a..c35caaa2f 100644 --- a/win32/README +++ b/win32/README @@ -5,7 +5,7 @@ At present, MSYS2 on a Windows system is required to gather dependencies before building a Win32 version of gpredict using this build system. -Install MSYS2, open a shell and install the Gtk3 and Goocanvas2 packages, +Install MSYS2, open a shell and install the Gtk3 packages, following the instructions from GTK team: https://www.gtk.org/download/windows.php diff --git a/win32/config.mk b/win32/config.mk index 9aa95dddc..30aa1ca66 100644 --- a/win32/config.mk +++ b/win32/config.mk @@ -1,12 +1,9 @@ # Configuration variables governing the build of gpredict for win32 -# pkgconfig path, assumes goocanvas-3.0.0 and gtk+-3.10.4 win32 packages +# pkgconfig path, assumes gtk+-3.10.4 win32 packages # unpacked and paths adjusted (pkg-config files) at the same folder level # as gpredict. Downloads used: # -# http://ftp.gnome.org/pub/GNOME/binaries/win32/goocanvas/3.0/ -# download both: goocanvas-3.0.0-win32.zip, goocanvas-dev-3.0.0-win32.zip -# # http://win32builder.gnome.org/gtk+-bundle_3.10.4-20131202_win32.zip # NB: I had to create new .pc files for both gtk+-3.0.pc and gdk.pc # (thanks GNOME for missing crucial bits out of builds). My versions