Пример #1
0
// The following function assumes that the set of selectable objects
// all rectangles.
void ConnectivityHistCanvas::UpdateSelection(bool shiftdown, bool pointsel)
{
    bool rect_sel = (!pointsel && (brushtype == rectangle));

    std::vector<bool>& hs = highlight_state->GetHighlight();
    std::vector<int>& nh = highlight_state->GetNewlyHighlighted();
    std::vector<int>& nuh = highlight_state->GetNewlyUnhighlighted();
    int total_newly_selected = 0;
    int total_newly_unselected = 0;

    int total_sel_shps = selectable_shps.size();

    wxPoint lower_left;
    wxPoint upper_right;
    if (rect_sel) {
        GenUtils::StandardizeRect(sel1, sel2, lower_left, upper_right);
    }
    if (!shiftdown) {
        bool any_selected = false;
        for (int i=0; i<total_sel_shps; i++) {
            GdaRectangle* rec = (GdaRectangle*) selectable_shps[i];
            if ((pointsel && rec->pointWithin(sel1)) ||
                    (rect_sel &&
                     GenUtils::RectsIntersect(rec->lower_left, rec->upper_right,
                                              lower_left, upper_right))) {
                //LOG_MSG(wxString::Format("ival %d selected", i));
                any_selected = true;
                break;
            } else {
                //LOG_MSG(wxString::Format("ival %d not selected", i));
                //LOG_MSG(wxString::Format(""));
            }
        }
        if (!any_selected) {
            highlight_state->SetEventType(HighlightState::unhighlight_all);
            highlight_state->notifyObservers();
            return;
        }
    }

    for (int i=0; i<total_sel_shps; i++) {
        GdaRectangle* rec = (GdaRectangle*) selectable_shps[i];
        bool selected = ((pointsel && rec->pointWithin(sel1)) ||
                         (rect_sel &&
                          GenUtils::RectsIntersect(rec->lower_left,
                                  rec->upper_right,
                                  lower_left, upper_right)));
        bool all_sel = (ival_obs_cnt[i] == ival_obs_sel_cnt[i]);
        if (pointsel && all_sel && selected) {
            // unselect all in ival
            for (std::list<int>::iterator it=ival_to_obs_ids[i].begin();
                    it != ival_to_obs_ids[i].end(); it++) {
                nuh[total_newly_unselected++] = (*it);
            }
        } else if (!all_sel && selected) {
            // select currently unselected in ival
            for (std::list<int>::iterator it=ival_to_obs_ids[i].begin();
                    it != ival_to_obs_ids[i].end(); it++) {
                if (hs[*it]) continue;
                nh[total_newly_selected++] = (*it);
            }
        } else if (!selected && !shiftdown) {
            // unselect all selected in ival
            for (std::list<int>::iterator it=ival_to_obs_ids[i].begin();
                    it != ival_to_obs_ids[i].end(); it++) {
                if (!hs[*it]) continue;
                nuh[total_newly_unselected++] = (*it);
            }
        }
    }

    if (total_newly_selected > 0 || total_newly_unselected > 0) {
        highlight_state->SetTotalNewlyHighlighted(total_newly_selected);
        highlight_state->SetTotalNewlyUnhighlighted(total_newly_unselected);
        NotifyObservables();
    }
    UpdateStatusBar();
}
Пример #2
0
void LineChartCanvas::PopulateCanvas()
{
	LOG_MSG("Entering LineChartCanvas::PopulateCanvas");
	BOOST_FOREACH( GdaShape* shp, background_shps ) { delete shp; }
	background_shps.clear();
	BOOST_FOREACH( GdaShape* shp, selectable_shps ) { delete shp; }
	selectable_shps.clear();
	BOOST_FOREACH( GdaShape* shp, foreground_shps ) { delete shp; }
	foreground_shps.clear();
	tm_rects.clear(); // NOTE: tm_rects are owned by background_shps
	summ_avg_circs[0] = 0; // NOTE: summ_avg_circs contents are owned
	summ_avg_circs[1] = 0; //       by background_shps.
	summ_avg_circs[2] = 0;
	summ_avg_circs[3] = 0;
	comb_circs.clear();
	sel_circs.clear();
	excl_circs.clear();
	
	wxSize size(GetVirtualSize());
	int win_width = size.GetWidth();
	int win_height = size.GetHeight();
	double scale_x, scale_y, trans_x, trans_y;
	GdaScaleTrans::calcAffineParams(shps_orig_xmin, shps_orig_ymin,
                                    shps_orig_xmax, shps_orig_ymax,
                                    virtual_screen_marg_top,
                                    virtual_screen_marg_bottom,
                                    virtual_screen_marg_left,
                                    virtual_screen_marg_right,
                                    win_width, win_height,
                                    fixed_aspect_ratio_mode,
                                    fit_to_window_mode,
                                    &scale_x, &scale_y, &trans_x, &trans_y,
                                    0, 0,
                                    &current_shps_width, &current_shps_height);
																	
	fixed_aspect_ratio_val = current_shps_width / current_shps_height;
	
	
	double y_min = 0;
	double y_max = 100;
	if (lcs.Y_avg_valid) {
		y_min = lcs.Y_avg_min;
		y_max = lcs.Y_avg_max;
	}
	if ((lcs.compare_regimes || lcs.compare_r_and_t) && lcs.Y_sel_avg_valid) {
		if (lcs.Y_sel_avg_min < y_min) y_min = lcs.Y_sel_avg_min;
		if (lcs.Y_sel_avg_max > y_max) y_max = lcs.Y_sel_avg_max;
	}
	if ((lcs.compare_regimes || lcs.compare_r_and_t) && lcs.Y_excl_avg_valid) {
		if (lcs.Y_excl_avg_min < y_min) y_min = lcs.Y_excl_avg_min;
		if (lcs.Y_excl_avg_max > y_max) y_max = lcs.Y_excl_avg_max;
	}
	double y_pad = 0.1 * (y_max - y_min);
	double axis_min = y_min - y_pad;
	double axis_max = y_max + y_pad;
    
	if (y_min >= 0 && axis_min < 0)
        axis_min = 0;
    
    if (!def_y_min.IsEmpty())
          def_y_min.ToDouble(&axis_min);
    
    if (!def_y_max.IsEmpty())
          def_y_max.ToDouble(&axis_max);

	axis_scale_y = AxisScale(axis_min, axis_max, 4, y_axis_precision);
	
	//LOG_MSG(wxString(axis_scale_y.ToString().c_str(), wxConvUTF8));
	scaleY = 100.0 / (axis_scale_y.scale_range);
	
	TableInterface* table_int = project->GetTableInt();
	// create axes
	std::vector<wxString> tm_strs;
	table_int->GetTimeStrings(tm_strs);
	bool time_variant = lcs.Y.size() > 1;
	
	wxRealPoint* y_pts = 0;
	size_t num_points = lcs.Y_avg.size();
	
	if (time_variant && num_points > 0) {
		// draw summary average circs
		// Will define avg point 1 and avg point 2 depending on
		// state of lcs.  Will ultimately need these points to
		// be detectable objects so that we can report summaries.
		// NULL indicates avg is not currently defined.
		
		
		if (lcs.compare_regimes) {
			if (lcs.Y_sel_tm0_avg_valid) {
				summ_avg_circs[0] = 
					MakeSummAvgHelper(lcs.Y_sel_tm0_avg,GdaConst::ln_cht_clr_sel_dark);
			}
			if (lcs.Y_excl_tm0_avg_valid) {
				summ_avg_circs[1] =
					MakeSummAvgHelper(lcs.Y_excl_tm0_avg,GdaConst::ln_cht_clr_exl_dark);
			}
		} else if (lcs.compare_time_periods) {
			if (lcs.Y_avg_tm0_valid) {
				summ_avg_circs[0] =
					MakeSummAvgHelper(lcs.Y_avg_tm0,GdaConst::ln_cht_clr_tm1_dark);
			}
			if (lcs.Y_avg_tm1_valid) {
				summ_avg_circs[1] =
					MakeSummAvgHelper(lcs.Y_avg_tm1,GdaConst::ln_cht_clr_tm2_dark);
			}
		} else if (lcs.compare_r_and_t) {
			if (lcs.Y_sel_tm0_avg_valid) {
				summ_avg_circs[0] = 
				MakeSummAvgHelper(lcs.Y_sel_tm0_avg,GdaConst::ln_cht_clr_sel_dark,GdaConst::ln_cht_clr_tm1_light);
			}
			if (lcs.Y_excl_tm0_avg_valid) {
				summ_avg_circs[1] =
				MakeSummAvgHelper(lcs.Y_excl_tm0_avg,GdaConst::ln_cht_clr_exl_dark,GdaConst::ln_cht_clr_tm1_light);
			}
			if (lcs.Y_sel_tm1_avg_valid) {
				summ_avg_circs[2] = 
				MakeSummAvgHelper(lcs.Y_sel_tm1_avg,GdaConst::ln_cht_clr_sel_dark,GdaConst::ln_cht_clr_tm2_light);
			}
			if (lcs.Y_excl_tm1_avg_valid) {
				summ_avg_circs[3] =
				MakeSummAvgHelper(lcs.Y_excl_tm1_avg,GdaConst::ln_cht_clr_exl_dark,GdaConst::ln_cht_clr_tm2_light);
			}
		}
	}
	
	if (time_variant && num_points > 0) {
		size_t tms = lcs.Y_avg.size();
		y_pts = new wxRealPoint[num_points];
		
		
		// Draw subset highlight line and invisible selection
		// rectangles first
		if (lcs.Y_avg_valid) {
			for (size_t t=0; t<tms; ++t) {
				double fracX = ((double) t)/((double) (tms-1));
				double x = fracX * 100.0;
				double y = (lcs.Y_avg[t] - axis_scale_y.scale_min) * scaleY;
				
				// draw a rectangle behind x-axis line to indicate
				// time period 0 or 1.
				double x0 = 0.0;
				double x1 = 100.0;
				double x_p1 = ((double) (t+1))/((double) (tms-1)) * 100.0;
				double x_m1 = ((double) (t-1))/((double) (tms-1)) * 100.0;
				if (t == 0) {
					x1 = (x+x_p1)/2.0;
				} else if (t+1 == tms) {
					x0 = (x_m1+x)/2.0;
				} else { // not an end-point
					x0 = (x_m1+x)/2.0;
					x1 = (x+x_p1)/2.0;
				}
				if (lcs.tms_subset0[t]) {
					GdaPolyLine* p = new GdaPolyLine(x0, 0, x1, 0);
					if (lcs.compare_time_periods || lcs.compare_r_and_t) {
						p->setPen(wxPen(GdaConst::ln_cht_clr_tm1_light, 9));
					} else {
						p->setPen(wxPen(GdaConst::ln_cht_clr_regimes_hl, 9));
					}
					p->setNudge(0, 5);
					background_shps.push_back(p);
				}
				if ((lcs.compare_time_periods || lcs.compare_r_and_t)
						&& lcs.tms_subset1[t]) {
					GdaPolyLine* p = new GdaPolyLine(x0, 0, x1, 0);
					p->setPen(wxPen(GdaConst::ln_cht_clr_tm2_light, 9));
					p->setNudge(0, 5);
					background_shps.push_back(p);
				}
      
				// Create invisible selection rectangles
				{
					double x0_nudge = 0;
					double x1_nudge = 0;
					if (t == 0) {
						x0_nudge = -5;
					} else if (t+1 == tms) {
						x1_nudge = 5;
					}
					GdaRectangle* r = new GdaRectangle(wxRealPoint(x0, 0), wxRealPoint(x1, 100));
					tm_rects.push_back(r);
					r->setPen(*wxTRANSPARENT_PEN);
					r->setBrush(*wxTRANSPARENT_BRUSH);
					background_shps.push_back(r);
				}
			}
		}
		
		
		// Push subset circle highlights to the background first
		if ((lcs.compare_regimes || lcs.compare_r_and_t) && lcs.Y_excl_avg_valid) {
			for (size_t t=0; t<tms; ++t) {
				double fracX = ((double) t)/((double) (tms-1));
				double x = fracX * 100.0;
				double y = (lcs.Y_excl_avg[t] - axis_scale_y.scale_min) * scaleY;
				if (lcs.tms_subset0[t]) {
					GdaCircle* c = new GdaCircle(wxRealPoint(x, y), ss_circ_rad);
					if (lcs.compare_r_and_t) {
						c->setPen(GdaConst::ln_cht_clr_tm1_light);
						c->setBrush(GdaConst::ln_cht_clr_tm1_light);
					} else {
						c->setPen(GdaConst::ln_cht_clr_regimes_hl);
						c->setBrush(GdaConst::ln_cht_clr_regimes_hl);
					}
					background_shps.push_back(c);
				}
				if (lcs.compare_r_and_t && lcs.tms_subset1[t]) {
					GdaCircle* c = new GdaCircle(wxRealPoint(x, y), ss_circ_rad);
					c->setPen(GdaConst::ln_cht_clr_tm2_light);
					c->setBrush(GdaConst::ln_cht_clr_tm2_light);
					background_shps.push_back(c);
				}
			}
		}
		if ((lcs.compare_regimes || lcs.compare_r_and_t) && lcs.Y_sel_avg_valid) {
			for (size_t t=0; t<tms; ++t) {
				double fracX = ((double) t)/((double) (tms-1));
				double x = fracX * 100.0;	
				double y = (lcs.Y_sel_avg[t] - axis_scale_y.scale_min) * scaleY;
				if (lcs.tms_subset0[t]) {
					GdaCircle* c = new GdaCircle(wxRealPoint(x, y), ss_circ_rad);
					if (lcs.compare_r_and_t) {
						c->setPen(GdaConst::ln_cht_clr_tm1_light);
						c->setBrush(GdaConst::ln_cht_clr_tm1_light);
					} else {
						c->setPen(GdaConst::ln_cht_clr_regimes_hl);
						c->setBrush(GdaConst::ln_cht_clr_regimes_hl);
					}
					background_shps.push_back(c);
				}
				if (lcs.compare_r_and_t && lcs.tms_subset1[t]) {
					GdaCircle* c = new GdaCircle(wxRealPoint(x, y), ss_circ_rad);
					c->setPen(GdaConst::ln_cht_clr_tm2_light);
					c->setBrush(GdaConst::ln_cht_clr_tm2_light);
					background_shps.push_back(c);
				}
			}
		}
		if (lcs.Y_avg_valid && lcs.Y_excl_avg_valid == lcs.Y_sel_avg_valid) {
			for (size_t t=0; t<tms; ++t) {
				double fracX = ((double) t)/((double) (tms-1));
				double x = fracX * 100.0;
				double y = (lcs.Y_avg[t] - axis_scale_y.scale_min) * scaleY;
				if (lcs.tms_subset0[t]) {
					GdaCircle* c = new GdaCircle(wxRealPoint(x, y), ss_circ_rad);
					if (lcs.compare_time_periods || lcs.compare_r_and_t) {
						c->setPen(GdaConst::ln_cht_clr_tm1_light);
						c->setBrush(GdaConst::ln_cht_clr_tm1_light);
					} else {
						c->setPen(GdaConst::ln_cht_clr_regimes_hl);
						c->setBrush(GdaConst::ln_cht_clr_regimes_hl);
					}
					background_shps.push_back(c);
				}
				if ((lcs.compare_time_periods || lcs.compare_r_and_t)
						&& lcs.tms_subset1[t]) {
					GdaCircle* c = new GdaCircle(wxRealPoint(x, y), ss_circ_rad);
					c->setPen(GdaConst::ln_cht_clr_tm2_light);
					c->setBrush(GdaConst::ln_cht_clr_tm2_light);
					background_shps.push_back(c);
				}
			}
		}
		
		// Draw everything else
        if (lcs.Y_avg_valid) {
			for (size_t t=0; t<tms; ++t) {
				double fracX = ((double) t)/((double) (tms-1));
				double x = fracX * 100.0;
				double y = (lcs.Y_avg[t] - axis_scale_y.scale_min) * scaleY;
				y_pts[t].x = x;
				y_pts[t].y = y;
			}
			GdaPolyLine* p = new GdaPolyLine(num_points, y_pts);
            p->setPen(wxPen(*wxBLACK, 1, wxSHORT_DASH));
			background_shps.push_back(p);
			for (size_t t=0; t<tms; ++t) {
				GdaCircle* c = new GdaCircle(wxRealPoint(y_pts[t].x, y_pts[t].y), circ_rad);
				wxColour lc = *wxBLACK;
				wxColour dc = GdaColorUtils::ChangeBrightness(lc);
				c->setPen(lc);
				c->setBrush(dc);
				background_shps.push_back(c);
				comb_circs.push_back(c);
			}
		}
		if ((lcs.compare_regimes || lcs.compare_r_and_t) && lcs.Y_excl_avg_valid) {
			for (size_t t=0; t<tms; ++t) {
				double fracX = ((double) t)/((double) (tms-1));
				double x = fracX * 100.0;
				double y = (lcs.Y_excl_avg[t] - axis_scale_y.scale_min) * scaleY;
				y_pts[t].x = x;
				y_pts[t].y = y;
			}
			GdaPolyLine* p = new GdaPolyLine(num_points, y_pts);
			p->setPen(GdaConst::ln_cht_clr_exl_dark);
			background_shps.push_back(p);
			for (size_t t=0; t<tms; ++t) {
				GdaCircle* c = new GdaCircle(wxRealPoint(y_pts[t].x, y_pts[t].y),
																		 circ_rad);
				wxColour lc = GdaConst::ln_cht_clr_exl_dark;
				wxColour dc = GdaColorUtils::ChangeBrightness(lc);
				c->setPen(lc);
				c->setBrush(dc);
				background_shps.push_back(c);
				excl_circs.push_back(c);
			}
		}
		
		if ((lcs.compare_regimes || lcs.compare_r_and_t) && lcs.Y_sel_avg_valid) {
			for (size_t t=0; t<tms; ++t) {
				double fracX = ((double) t)/((double) (tms-1));
				double x = fracX * 100.0;	
				double y = (lcs.Y_sel_avg[t] - axis_scale_y.scale_min) * scaleY;
				y_pts[t].x = x;
				y_pts[t].y = y;
			}
			GdaPolyLine* p = new GdaPolyLine(num_points, y_pts);
			p->setPen(GdaConst::ln_cht_clr_sel_dark);
			background_shps.push_back(p);
			for (size_t t=0; t<tms; ++t) {
				GdaCircle* c = new GdaCircle(wxRealPoint(y_pts[t].x, y_pts[t].y), circ_rad);
				wxColour lc = GdaConst::ln_cht_clr_sel_dark;
				wxColour dc = GdaColorUtils::ChangeBrightness(lc);
				c->setPen(lc);
				c->setBrush(dc);
				background_shps.push_back(c);
				sel_circs.push_back(c);
			}
		}
	}
	
	if (!time_variant) {
		size_t t = 0;
		const double d=5.0;
		const double x = 50.0;
        
		if (lcs.Y_avg_valid && lcs.Y_excl_avg_valid == lcs.Y_sel_avg_valid) {
			double y = (lcs.Y_avg[t] - axis_scale_y.scale_min) * scaleY;
			GdaPolyLine* p = new GdaPolyLine(x-d, y, x+d, y);
			p->setPen(*wxBLACK_PEN);
			background_shps.push_back(p);
			GdaCircle* c = new GdaCircle(wxRealPoint(x,y), circ_rad);
			wxColour lc = *wxBLACK;
			wxColour dc = GdaColorUtils::ChangeBrightness(lc);
			c->setPen(lc);
			c->setBrush(dc);
			background_shps.push_back(c);
			comb_circs.push_back(c);
		}
		if ((lcs.compare_regimes || lcs.compare_r_and_t) && lcs.Y_excl_avg_valid) {
			double y = (lcs.Y_excl_avg[t] - axis_scale_y.scale_min) * scaleY;
			GdaPolyLine* p = new GdaPolyLine(x-d, y, x+d, y);
			p->setPen(GdaConst::ln_cht_clr_exl_dark);
			background_shps.push_back(p);
			GdaCircle* c = new GdaCircle(wxRealPoint(x,y), circ_rad);
			wxColour lc = GdaConst::ln_cht_clr_exl_dark;
			wxColour dc = GdaColorUtils::ChangeBrightness(lc);
			c->setPen(lc);
			c->setBrush(dc);
			background_shps.push_back(c);
			excl_circs.push_back(c);
		}
		if ((lcs.compare_regimes || lcs.compare_r_and_t) && lcs.Y_sel_avg_valid) {
			double y = (lcs.Y_sel_avg[t] - axis_scale_y.scale_min) * scaleY;
			GdaPolyLine* p = new GdaPolyLine(x-d, y, x+d, y);
			p->setPen(GdaConst::ln_cht_clr_sel_dark);
			background_shps.push_back(p);
			GdaCircle* c = new GdaCircle(wxRealPoint(x,y), circ_rad);
			wxColour lc = GdaConst::ln_cht_clr_sel_dark;
			wxColour dc = GdaColorUtils::ChangeBrightness(lc);
			c->setPen(lc);
			c->setBrush(dc);
			background_shps.push_back(c);
			sel_circs.push_back(c);
		}
	}

	GdaAxis* x_baseline = 0;
	if (time_variant) {
		x_baseline = new GdaAxis("", tm_strs, wxRealPoint(0,0), wxRealPoint(100, 0), 0, 5);
		x_baseline->hideCaption(true);
		x_baseline->setPen(*GdaConst::scatterplot_scale_pen);
		x_baseline->autoDropScaleValues(true);
		x_baseline->moveOuterValTextInwards(false);
		background_shps.push_back(x_baseline);
	}
	GdaAxis* y_baseline = new GdaAxis(lcs.Yname, axis_scale_y, wxRealPoint(0,0), wxRealPoint(0, 100), -5, 0);
	y_baseline->autoDropScaleValues(true);
	y_baseline->moveOuterValTextInwards(true);
	y_baseline->setPen(*GdaConst::scatterplot_scale_pen);
	background_shps.push_back(y_baseline);	
	
	if (y_pts) delete [] y_pts;
	
	ResizeSelectableShps();
    Refresh(false);
	LOG_MSG("Exiting LineChartCanvas::PopulateCanvas");
}
// The following function assumes that the set of selectable objects
// are all rectangles.
void ConditionalHistogramCanvas::UpdateSelection(bool shiftdown, bool pointsel)
{
	bool rect_sel = (!pointsel && (brushtype == rectangle));
	
	int t = var_info[HIST_VAR].time;
	std::vector<bool>& hs = highlight_state->GetHighlight();
    bool selection_changed = false;
	
	int total_sel_shps = selectable_shps.size();
	
	wxPoint lower_left;
	wxPoint upper_right;
	if (rect_sel) {
		GenGeomAlgs::StandardizeRect(sel1, sel2, lower_left, upper_right);
	}
	if (!shiftdown) {
		bool any_selected = false;
		for (int i=0; i<total_sel_shps; i++) {
			GdaRectangle* rec = (GdaRectangle*) selectable_shps[i];
			if ((pointsel && rec->pointWithin(sel1)) ||
				(rect_sel &&
				 GenGeomAlgs::RectsIntersect(rec->lower_left, rec->upper_right,
										  lower_left, upper_right)))
			{
				any_selected = true;
				break;
			}
		}
		if (!any_selected) {
			highlight_state->SetEventType(HLStateInt::unhighlight_all);
			highlight_state->notifyObservers();
			return;
		}
	}
	
	for (int i=0; i<total_sel_shps; i++) {
		int r, c, ival;
		sel_shp_to_cell(i, r, c, ival);
		GdaRectangle* rec = (GdaRectangle*) selectable_shps[i];
		bool selected = ((pointsel && rec->pointWithin(sel1)) ||
						 (rect_sel &&
						  GenGeomAlgs::RectsIntersect(rec->lower_left,
												   rec->upper_right,
												   lower_left, upper_right)));
		bool all_sel = (cell_data[t][r][c].ival_obs_cnt[ival] == 
						cell_data[t][r][c].ival_obs_sel_cnt[ival]);
		if (pointsel && all_sel && selected) {
			// unselect all in ival
			for (std::list<int>::iterator it =
					cell_data[t][r][c].ival_to_obs_ids[ival].begin();
				 it != cell_data[t][r][c].ival_to_obs_ids[ival].end(); it++) {
                hs[(*it)]= false;
                selection_changed = true;
			}
		} else if (!all_sel && selected) {
			// select currently unselected in ival
			for (std::list<int>::iterator it =
					cell_data[t][r][c].ival_to_obs_ids[ival].begin();
				 it != cell_data[t][r][c].ival_to_obs_ids[ival].end(); it++) {
				if (hs[*it]) continue;
                hs[(*it)]= true;
                selection_changed = true;
			}
		} else if (!selected && !shiftdown) {
			// unselect all selected in ival
			for (std::list<int>::iterator it =
					cell_data[t][r][c].ival_to_obs_ids[ival].begin();
				 it != cell_data[t][r][c].ival_to_obs_ids[ival].end(); it++) {
				if (!hs[*it]) continue;
                hs[(*it)]= false;
                selection_changed = true;
			}
		}
	}
	
	if ( selection_changed ) {
		highlight_state->SetEventType(HLStateInt::delta);
		highlight_state->notifyObservers();
	}
	UpdateStatusBar();
}