static GooCanvasItemModel * create_time_tick (GtkPolarView *pv, gdouble time, gfloat x, gfloat y) { GooCanvasItemModel *item; time_t t; gchar buff[7]; GtkAnchorType 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); /* convert julian date to struct tm */ t = (time - 2440587.5)*86400.; /* format either local time or UTC depending on check box */ if (sat_cfg_get_bool (SAT_CFG_BOOL_USE_LOCAL_TIME)) strftime (buff, 8, "%H:%M", localtime (&t)); else strftime (buff, 8, "%H:%M", gmtime (&t)); buff[6]='\0'; if (x > pv->cx) { anchor = GTK_ANCHOR_EAST; x -= 5; } else { anchor = GTK_ANCHOR_WEST; x += 5; } item = goo_canvas_text_model_new (root, buff, (gdouble) x, (gdouble) y, -1, anchor, "font", "Sans 7", "fill-color-rgba", col, NULL); goo_canvas_item_model_lower (item, NULL); return item; }
/** \brief Create and initialise widgets for the refresh rates tab. * * The widgets must be preloaded with values from config. If a config value * is NULL, sensible default values, eg. those from defaults.h should * be laoded. */ GtkWidget *sat_pref_refresh_create (GKeyFile *cfg) { GtkWidget *table; GtkWidget *vbox; GtkWidget *label; gint val; dirty = FALSE; reset = FALSE; /* create table */ table = gtk_table_new (6, 3, FALSE); gtk_table_set_row_spacings (GTK_TABLE (table), 10); gtk_table_set_col_spacings (GTK_TABLE (table), 5); /* data refresh */ label = gtk_label_new (_("Refresh data every")); gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1, GTK_FILL, GTK_SHRINK, 0, 0); dataspin = gtk_spin_button_new_with_range (100, 10000, 1); gtk_spin_button_set_increments (GTK_SPIN_BUTTON (dataspin), 1, 100); gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (dataspin), TRUE); gtk_spin_button_set_update_policy (GTK_SPIN_BUTTON (dataspin), GTK_UPDATE_IF_VALID); if (cfg != NULL) { val = mod_cfg_get_int (cfg, MOD_CFG_GLOBAL_SECTION, MOD_CFG_TIMEOUT_KEY, SAT_CFG_INT_MODULE_TIMEOUT); } else { val = sat_cfg_get_int (SAT_CFG_INT_MODULE_TIMEOUT); } gtk_spin_button_set_value (GTK_SPIN_BUTTON (dataspin), val); g_signal_connect (G_OBJECT (dataspin), "value-changed", G_CALLBACK (spin_changed_cb), NULL); gtk_table_attach (GTK_TABLE (table), dataspin, 1, 2, 0, 1, GTK_FILL, GTK_SHRINK, 0, 0); label = gtk_label_new (_("[msec]")); gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); gtk_table_attach (GTK_TABLE (table), label, 2, 3, 0, 1, GTK_FILL | GTK_EXPAND, GTK_SHRINK, 0, 0); /* separator */ gtk_table_attach (GTK_TABLE (table), gtk_hseparator_new (), 0, 3, 1, 2, GTK_FILL | GTK_EXPAND, GTK_SHRINK, 0, 0); /* List View */ label = gtk_label_new (_("Refresh list view every")); gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); gtk_table_attach (GTK_TABLE (table), label, 0, 1, 2, 3, GTK_FILL, GTK_SHRINK, 0, 0); listspin = gtk_spin_button_new_with_range (1, 50, 1); gtk_spin_button_set_increments (GTK_SPIN_BUTTON (listspin), 1, 5); gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (listspin), TRUE); gtk_spin_button_set_update_policy (GTK_SPIN_BUTTON (listspin), GTK_UPDATE_IF_VALID); if (cfg != NULL) { val = mod_cfg_get_int (cfg, MOD_CFG_LIST_SECTION, MOD_CFG_LIST_REFRESH, SAT_CFG_INT_LIST_REFRESH); } else { val = sat_cfg_get_int (SAT_CFG_INT_LIST_REFRESH); } gtk_spin_button_set_value (GTK_SPIN_BUTTON (listspin), val); g_signal_connect (G_OBJECT (listspin), "value-changed", G_CALLBACK (spin_changed_cb), NULL); gtk_table_attach (GTK_TABLE (table), listspin, 1, 2, 2, 3, GTK_FILL, GTK_SHRINK, 0, 0); label = gtk_label_new (_("[cycle]")); gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); gtk_table_attach (GTK_TABLE (table), label, 2, 3, 2, 3, GTK_FILL | GTK_EXPAND, GTK_SHRINK, 0, 0); /* Map View */ label = gtk_label_new (_("Refresh map view every")); gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); gtk_table_attach (GTK_TABLE (table), label, 0, 1, 3, 4, GTK_FILL, GTK_SHRINK, 0, 0); mapspin = gtk_spin_button_new_with_range (1, 50, 1); gtk_spin_button_set_increments (GTK_SPIN_BUTTON (mapspin), 1, 5); gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (mapspin), TRUE); gtk_spin_button_set_update_policy (GTK_SPIN_BUTTON (mapspin), GTK_UPDATE_IF_VALID); if (cfg != NULL) { val = mod_cfg_get_int (cfg, MOD_CFG_MAP_SECTION, MOD_CFG_MAP_REFRESH, SAT_CFG_INT_MAP_REFRESH); } else { val = sat_cfg_get_int (SAT_CFG_INT_MAP_REFRESH); } gtk_spin_button_set_value (GTK_SPIN_BUTTON (mapspin), val); g_signal_connect (G_OBJECT (mapspin), "value-changed", G_CALLBACK (spin_changed_cb), NULL); gtk_table_attach (GTK_TABLE (table), mapspin, 1, 2, 3, 4, GTK_FILL, GTK_SHRINK, 0, 0); label = gtk_label_new (_("[cycle]")); gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); gtk_table_attach (GTK_TABLE (table), label, 2, 3, 3, 4, GTK_FILL | GTK_EXPAND, GTK_SHRINK, 0, 0); /* Polar View */ label = gtk_label_new (_("Refresh polar view every")); gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); gtk_table_attach (GTK_TABLE (table), label, 0, 1, 4, 5, GTK_FILL, GTK_SHRINK, 0, 0); polarspin = gtk_spin_button_new_with_range (1, 50, 1); gtk_spin_button_set_increments (GTK_SPIN_BUTTON (polarspin), 1, 5); gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (polarspin), TRUE); gtk_spin_button_set_update_policy (GTK_SPIN_BUTTON (polarspin), GTK_UPDATE_IF_VALID); if (cfg != NULL) { val = mod_cfg_get_int (cfg, MOD_CFG_POLAR_SECTION, MOD_CFG_POLAR_REFRESH, SAT_CFG_INT_POLAR_REFRESH); } else { val = sat_cfg_get_int (SAT_CFG_INT_POLAR_REFRESH); } gtk_spin_button_set_value (GTK_SPIN_BUTTON (polarspin), val); g_signal_connect (G_OBJECT (polarspin), "value-changed", G_CALLBACK (spin_changed_cb), NULL); gtk_table_attach (GTK_TABLE (table), polarspin, 1, 2, 4, 5, GTK_FILL, GTK_SHRINK, 0, 0); label = gtk_label_new (_("[cycle]")); gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); gtk_table_attach (GTK_TABLE (table), label, 2, 3, 4, 5, GTK_FILL | GTK_EXPAND, GTK_SHRINK, 0, 0); /* Single-Sat View */ label = gtk_label_new (_("Refresh single-sat view every")); gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); gtk_table_attach (GTK_TABLE (table), label, 0, 1, 5, 6, GTK_FILL, GTK_SHRINK, 0, 0); singlespin = gtk_spin_button_new_with_range (1, 50, 1); gtk_spin_button_set_increments (GTK_SPIN_BUTTON (singlespin), 1, 5); gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (singlespin), TRUE); gtk_spin_button_set_update_policy (GTK_SPIN_BUTTON (singlespin), GTK_UPDATE_IF_VALID); if (cfg != NULL) { val = mod_cfg_get_int (cfg, MOD_CFG_SINGLE_SAT_SECTION, MOD_CFG_SINGLE_SAT_REFRESH, SAT_CFG_INT_SINGLE_SAT_REFRESH); } else { val = sat_cfg_get_int (SAT_CFG_INT_SINGLE_SAT_REFRESH); } gtk_spin_button_set_value (GTK_SPIN_BUTTON (singlespin), val); g_signal_connect (G_OBJECT (singlespin), "value-changed", G_CALLBACK (spin_changed_cb), NULL); gtk_table_attach (GTK_TABLE (table), singlespin, 1, 2, 5, 6, GTK_FILL, GTK_SHRINK, 0, 0); label = gtk_label_new (_("[cycle]")); gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); gtk_table_attach (GTK_TABLE (table), label, 2, 3, 5, 6, GTK_FILL | GTK_EXPAND, GTK_SHRINK, 0, 0); /* create vertical box */ vbox = gtk_vbox_new (FALSE, 0); gtk_container_set_border_width (GTK_CONTAINER (vbox), 20); gtk_box_pack_start (GTK_BOX (vbox), table, TRUE, TRUE, 0); /* create RESET button */ create_reset_button (cfg, GTK_BOX (vbox)); return vbox; }
/** \brief Create and initialise widgets for the single pass cfg tab. * * The widgets must be preloaded with values from config. If a config value * is NULL, sensible default values, eg. those from defaults.h should * be laoded. */ GtkWidget *sat_pref_single_sat_create (GKeyFile *cfg) { GtkWidget *table; GtkWidget *vbox; GtkWidget *label; guint i; /* create the table */ table = gtk_table_new ((SINGLE_SAT_FIELD_NUMBER+1)/COLUMNS + 1, COLUMNS, TRUE); //gtk_container_set_border_width (GTK_CONTAINER (table), 20); gtk_table_set_row_spacings (GTK_TABLE (table), 10); gtk_table_set_col_spacings (GTK_TABLE (table), 5); /* create header */ label = gtk_label_new (NULL); gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); gtk_label_set_markup (GTK_LABEL (label), _("<b>Visible Fields:</b>")); gtk_table_attach (GTK_TABLE (table), label, 0, 2, 0, 1, GTK_FILL, GTK_SHRINK, 0, 0); /* get visible column flags */ if (cfg != NULL) { flags = mod_cfg_get_int (cfg, MOD_CFG_SINGLE_SAT_SECTION, MOD_CFG_SINGLE_SAT_FIELDS, SAT_CFG_INT_SINGLE_SAT_FIELDS); } else { flags = sat_cfg_get_int (SAT_CFG_INT_SINGLE_SAT_FIELDS); } for (i = 0; i < SINGLE_SAT_FIELD_NUMBER; i++) { check[i] = gtk_check_button_new_with_label (SINGLE_SAT_FIELD_TITLE[i]); gtk_widget_set_tooltip_text (check[i], SINGLE_SAT_FIELD_HINT[i]); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check[i]), flags & (1 << i)); gtk_table_attach (GTK_TABLE (table), check[i], i % COLUMNS, (i % COLUMNS) + 1, Y0 + i / COLUMNS, Y0 + i / COLUMNS + 1, GTK_FILL, GTK_SHRINK, 0, 0); g_signal_connect (check[i], "toggled", G_CALLBACK (toggle_cb), GUINT_TO_POINTER (i)); } /* create vertical box */ vbox = gtk_vbox_new (FALSE, 5); gtk_container_set_border_width (GTK_CONTAINER (vbox), 20); gtk_box_pack_start (GTK_BOX (vbox), table, TRUE, TRUE, 0); /* create RESET button */ create_reset_button (cfg, GTK_BOX (vbox)); startflags = flags; dirty = FALSE; reset = FALSE; return vbox; }
/** \brief Create and show ground track for a satellite. * \param satmap The satellite map widget. * \param sat Pointer to the satellite object. * \param qth Pointer to the QTH data. * \param obj the satellite object. * * Gpredict allows the user to require the ground track for any number of orbits * ahead. Therfore, the resulting ground track may cross the map boundaries many * times, and using one single polyline for the whole ground track would look very * silly. To avoid this, the points will be split into several polylines. * */ void ground_track_create (GtkSatMap *satmap, sat_t *sat, qth_t *qth, sat_map_obj_t *obj) { long this_orbit; /* current orbit number */ long max_orbit; /* target orbit number, ie. this + num - 1 */ double t0; /* time when this_orbit starts */ double t; ssp_t *this_ssp; sat_log_log (SAT_LOG_LEVEL_DEBUG, _("%s: Creating ground track for %s"), __FUNCTION__, sat->nickname); /* just to be safe... if empty GSList is not NULL => segfault */ obj->track_data.latlon = NULL; /* get configuration parameters */ this_orbit = sat->orbit; max_orbit = sat->orbit -1 + mod_cfg_get_int (satmap->cfgdata, MOD_CFG_MAP_SECTION, MOD_CFG_MAP_TRACK_NUM, SAT_CFG_INT_MAP_TRACK_NUM); sat_log_log (SAT_LOG_LEVEL_DEBUG, _("%s: Start orbit: %d"), __FUNCTION__, this_orbit); sat_log_log (SAT_LOG_LEVEL_DEBUG, _("%s: End orbit %d"), __FUNCTION__, max_orbit); /* find the time when the current orbit started */ /* Iterate backwards in time until we reach sat->orbit < this_orbit. Use predict_calc from predict-tools.c as SGP/SDP driver. As a built-in safety, we stop iteration if the orbit crossing is more than 24 hours back in time. */ t0 = satmap->tstamp;//get_current_daynum (); /* use == instead of >= as it is more robust */ for (t = t0; (sat->orbit == this_orbit) && ((t + 1.0) > t0); t -= 0.0007) { predict_calc (sat, qth, t); } /* set it so that we are in the same orbit as this_orbit and not a different one */ t += 2*0.0007; t0 = t; predict_calc (sat, qth, t0); sat_log_log (SAT_LOG_LEVEL_DEBUG, _("%s: T0: %f (%d)"), __FUNCTION__, t0, sat->orbit); /* calculate (lat,lon) for the required orbits */ while ((sat->orbit <= max_orbit) && (sat->orbit >= this_orbit) && (!decayed(sat))) { /* We use 30 sec time steps. If resolution is too fine, the line drawing routine will filter out unnecessary points */ t += 0.00035; predict_calc (sat, qth, t); /* store this SSP */ /* Note: g_slist_append() has to traverse the entire list to find the end, which is inefficient when adding multiple elements. Therefore, we use g_slist_prepend() and reverse the entire list when we are done. */ this_ssp = g_try_new (ssp_t, 1); if (this_ssp == NULL) { sat_log_log (SAT_LOG_LEVEL_ERROR, _("%s: MAYDAY: Insufficient memory for ground track!"), __FUNCTION__); return; } this_ssp->lat = sat->ssplat; this_ssp->lon = sat->ssplon; obj->track_data.latlon = g_slist_prepend (obj->track_data.latlon, this_ssp); } /* log if there is a problem with the orbit calculation */ if (sat->orbit != (max_orbit+1)) { sat_log_log (SAT_LOG_LEVEL_ERROR, _("%s: Problem computing ground track for %s"), __FUNCTION__, sat->nickname); return; } /* Reset satellite structure to eliminate glitches in single sat view and other places when new ground track is layed out */ predict_calc(sat, qth, satmap->tstamp); /* reverse GSList */ obj->track_data.latlon = g_slist_reverse (obj->track_data.latlon); /* split points into polylines */ create_polylines (satmap, sat, qth, obj); /* misc book-keeping */ obj->track_orbit = this_orbit; }
/** \brief Create polylines. */ 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; (void) sat; /* prevent unused parameter compiler warning */ (void) qth; /* prevent unused parameter compiler warning */ /* initialise parameters */ lastx = -50.0; lasty = -50.0; 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++) { buff = (ssp_t *) g_slist_nth_data (obj->track_data.latlon, i); ssp = g_try_new (ssp_t, 1); gtk_sat_map_lonlat_to_xy (satmap, buff->lon, buff->lat, &ssp->lon, &ssp->lat); /* if this is the first point, just add it to the list */ if (i == start) { points = g_slist_prepend (points, ssp); lastx = ssp->lon; lasty = ssp->lat; } else { /* if SSP is on the other side of the map */ if (ssp_wrap_detected (satmap, lastx, ssp->lon)) { points = g_slist_reverse (points); num_points = g_slist_length (points); /* 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++) { buff = (ssp_t *) g_slist_nth_data (points, j); gpoints->coords[2*j] = buff->lon; gpoints->coords[2*j+1] = buff->lat; } /* 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 */ g_slist_foreach (points, free_ssp, NULL); g_slist_free (points); points = NULL; start = i; lastx = ssp->lon; lasty = ssp->lat; num_points = 0; /* Add current SSP to the new list */ points = g_slist_prepend (points, ssp); lastx = ssp->lon; lasty = ssp->lat; } /* else if this SSP is separable from the previous */ else if ((fabs (lastx - ssp->lon) > 1.0 ) || (fabs(lasty - ssp->lat)>1.0)){ /* add SSP to list */ points = g_slist_prepend (points, ssp); lastx = ssp->lon; lasty = ssp->lon; } /* else if do nothing */ } } /* create (last) line if we have at least two points */ points = g_slist_reverse (points); num_points = g_slist_length (points); if (num_points > 1) { /* convert SSPs to GooCanvasPoints */ gpoints = goo_canvas_points_new (num_points); for (j = 0; j < num_points; j++) { buff = (ssp_t *) g_slist_nth_data (points, j); gpoints->coords[2*j] = buff->lon; gpoints->coords[2*j+1] = buff->lat; } /* 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 */ g_slist_foreach (points, free_ssp, NULL); g_slist_free (points); } }
/** \brief Manage toggling of Ground Track. * \param item The menu item that was toggled. * \param data Pointer to the GtkPolarView structure. * */ static void track_toggled (GtkCheckMenuItem *item, gpointer data) { GtkPolarView *pv = GTK_POLAR_VIEW (data); sat_obj_t *obj = NULL; sat_t *sat; qth_t *qth; gint idx,i; GooCanvasItemModel *root; pass_detail_t *detail; guint num; GooCanvasPoints *points; gfloat x,y; guint32 col; guint tres,ttidx; /* 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_BUG, _("%s:%d: Failed to get satellite object."), __FILE__, __LINE__); return; } /* toggle flag */ obj->showtrack = !obj->showtrack; gtk_check_menu_item_set_active (item, obj->showtrack); root = goo_canvas_get_root_item_model (GOO_CANVAS (pv->canvas)); if (obj->showtrack) { /* add sky track */ /* create points */ num = g_slist_length (obj->pass->details); if (num == 0) { sat_log_log (SAT_LOG_LEVEL_BUG, _("%s:%d: Pass has no details."), __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); /* 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); ttidx = 1; for (i = 1; i < num-1; i++) { 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; if (!(i % tres)) { /* create a time tick */ if (ttidx<TRACK_TICK_NUM) obj->trtick[ttidx] = create_time_tick (pv, detail->time, x, y); 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; /* 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); /* put track on the bottom of the sack */ goo_canvas_item_model_lower (obj->track, NULL); } else { /* delete sky track */ 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); } } } }