1 /***************************************************************************
2 File : DataPickerTool.cpp
3 Project : SciDAVis
4 --------------------------------------------------------------------
5 Copyright : (C) 2006,2007 by Ion Vasilief,
6 Tilman Benkert, Knut Franke
7 Email (use @ for *) : ion_vasilief*yahoo.fr, thzs*gmx.net,
8 knut.franke*gmx.de
9 Description : Plot tool for selecting points on curves.
10
11 ***************************************************************************/
12
13 /***************************************************************************
14 * *
15 * This program is free software; you can redistribute it and/or modify *
16 * it under the terms of the GNU General Public License as published by *
17 * the Free Software Foundation; either version 2 of the License, or *
18 * (at your option) any later version. *
19 * *
20 * This program is distributed in the hope that it will be useful, *
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
23 * GNU General Public License for more details. *
24 * *
25 * You should have received a copy of the GNU General Public License *
26 * along with this program; if not, write to the Free Software *
27 * Foundation, Inc., 51 Franklin Street, Fifth Floor, *
28 * Boston, MA 02110-1301 USA *
29 * *
30 ***************************************************************************/
31 #include "DataPickerTool.h"
32 #include "Graph.h"
33 #include "Plot.h"
34 #include "FunctionCurve.h"
35 #include "PlotCurve.h"
36 #include "QwtErrorPlotCurve.h"
37 #include "ApplicationWindow.h"
38 #include "core/column/Column.h"
39
40 #include <qwt_symbol.h>
41 #include <qwt_plot_picker.h>
42 #include <qwt_plot_curve.h>
43 #include <qwt_scale_draw.h>
44 #include <QMessageBox>
45 #include <QLocale>
46 #include <QKeyEvent>
47 #include <QMouseEvent>
48
DataPickerTool(Graph * graph,ApplicationWindow * app,Mode mode,const QObject * status_target,const char * status_slot)49 DataPickerTool::DataPickerTool(Graph *graph, ApplicationWindow *app, Mode mode,
50 const QObject *status_target, const char *status_slot)
51 : QwtPlotPicker(graph->plotWidget()->canvas()),
52 PlotToolInterface(graph),
53 d_app(app),
54 d_mode(mode),
55 d_move_mode(Free)
56 {
57 d_selected_curve = NULL;
58
59 d_selection_marker.setSymbol(QwtSymbol(QwtSymbol::Ellipse, QBrush(QColor(255, 255, 0, 128)),
60 QPen(Qt::black, 2), QSize(20, 20)));
61 d_selection_marker.setLineStyle(QwtPlotMarker::Cross);
62 d_selection_marker.setLinePen(QPen(Qt::red, 1));
63
64 setTrackerMode(QwtPicker::AlwaysOn);
65 if (d_mode == Move) {
66 setSelectionFlags(QwtPicker::PointSelection | QwtPicker::DragSelection);
67 d_graph->plotWidget()->canvas()->setCursor(Qt::PointingHandCursor);
68 } else {
69 setSelectionFlags(QwtPicker::PointSelection | QwtPicker::ClickSelection);
70 d_graph->plotWidget()->canvas()->setCursor(QCursor(QPixmap(":/vizor.xpm"), -1, -1));
71 }
72
73 if (status_target)
74 connect(this, SIGNAL(statusText(const QString &)), status_target, status_slot);
75 switch (d_mode) {
76 case Display:
77 emit statusText(tr("Click on plot or move cursor to display coordinates!"));
78 break;
79 case Move:
80 emit statusText(tr("Please, click on plot and move cursor!"));
81 break;
82 case Remove:
83 emit statusText(tr("Select point and double click to remove it!"));
84 break;
85 }
86 }
87
~DataPickerTool()88 DataPickerTool::~DataPickerTool()
89 {
90 d_selection_marker.detach();
91 d_graph->plotWidget()->canvas()->unsetCursor();
92 }
93
append(const QPoint & pos)94 void DataPickerTool::append(const QPoint &pos)
95 {
96 int dist, point_index;
97 const int curve = d_graph->plotWidget()->closestCurve(pos.x(), pos.y(), dist, point_index);
98 if (curve <= 0 || dist >= 5) { // 5 pixels tolerance
99 setSelection(NULL, 0);
100 return;
101 }
102 setSelection((QwtPlotCurve *)d_graph->plotWidget()->curve(curve), point_index);
103 if (!d_selected_curve)
104 return;
105
106 QwtPlotPicker::append(transform(QwtDoublePoint(d_selected_curve->x(d_selected_point),
107 d_selected_curve->y(d_selected_point))));
108 }
109
setSelection(QwtPlotCurve * curve,int point_index)110 void DataPickerTool::setSelection(QwtPlotCurve *curve, int point_index)
111 {
112 if (curve == d_selected_curve && point_index == d_selected_point)
113 return;
114
115 d_selected_curve = curve;
116 d_selected_point = point_index;
117
118 if (!d_selected_curve) {
119 d_selection_marker.detach();
120 d_graph->plotWidget()->replot();
121 return;
122 }
123
124 setAxis(d_selected_curve->xAxis(), d_selected_curve->yAxis());
125
126 d_move_target_pos = QPoint(plot()->transform(xAxis(), d_selected_curve->x(d_selected_point)),
127 plot()->transform(yAxis(), d_selected_curve->y(d_selected_point)));
128
129 if (((PlotCurve *)d_selected_curve)->type() == Graph::Function) {
130 emit statusText(QString("%1[%2]: x=%3; y=%4")
131 .arg(d_selected_curve->title().text())
132 .arg(d_selected_point + 1)
133 .arg(QLocale().toString(d_selected_curve->x(d_selected_point), 'G',
134 d_app->d_decimal_digits))
135 .arg(QLocale().toString(d_selected_curve->y(d_selected_point), 'G',
136 d_app->d_decimal_digits)));
137 } else {
138 int row = ((DataCurve *)d_selected_curve)->tableRow(d_selected_point);
139
140 Table *t = ((DataCurve *)d_selected_curve)->table();
141 int xCol = t->colIndex(((DataCurve *)d_selected_curve)->xColumnName());
142 int yCol = t->colIndex(d_selected_curve->title().text());
143
144 emit statusText(QString("%1[%2]: x=%3; y=%4")
145 .arg(d_selected_curve->title().text())
146 .arg(row + 1)
147 .arg(t->text(row, xCol))
148 .arg(t->text(row, yCol)));
149 }
150
151 QwtDoublePoint selected_point_value(d_selected_curve->x(d_selected_point),
152 d_selected_curve->y(d_selected_point));
153 d_selection_marker.setValue(selected_point_value);
154 if (d_selection_marker.plot() == NULL)
155 d_selection_marker.attach(d_graph->plotWidget());
156 d_graph->plotWidget()->replot();
157 }
158
eventFilter(QObject * obj,QEvent * event)159 bool DataPickerTool::eventFilter(QObject *obj, QEvent *event)
160 {
161 switch (event->type()) {
162 case QEvent::MouseButtonDblClick:
163 switch (d_mode) {
164 case Remove:
165 removePoint();
166 return true;
167 default:
168 if (d_selected_curve)
169 emit selected(d_selected_curve, d_selected_point);
170 return true;
171 }
172 case QEvent::MouseMove:
173 if (((QMouseEvent *)event)->modifiers() == Qt::ControlModifier)
174 d_move_mode = Vertical;
175 else if (((QMouseEvent *)event)->modifiers() == Qt::AltModifier)
176 d_move_mode = Horizontal;
177 else
178 d_move_mode = Free;
179 break;
180
181 case QEvent::KeyPress:
182 if (keyEventFilter((QKeyEvent *)event))
183 return true;
184 break;
185 default:
186 break;
187 }
188 return QwtPlotPicker::eventFilter(obj, event);
189 }
190
keyEventFilter(QKeyEvent * ke)191 bool DataPickerTool::keyEventFilter(QKeyEvent *ke)
192 {
193 const int delta = 5;
194 switch (ke->key()) {
195 case Qt::Key_Enter:
196 case Qt::Key_Return:
197 if (d_selected_curve)
198 emit selected(d_selected_curve, d_selected_point);
199 return true;
200
201 case Qt::Key_Up:
202 if (d_graph && d_selected_curve) {
203 int n_curves = d_graph->curves();
204 int start = d_graph->curveIndex(d_selected_curve) + 1;
205 QwtPlotCurve *c;
206 for (int i = start; i < start + n_curves; ++i)
207 if ((c = d_graph->curve(i % n_curves))->dataSize() > 0) {
208 setSelection(c, qMin(c->dataSize() - 1, d_selected_point));
209 break;
210 }
211 d_graph->plotWidget()->replot();
212 }
213 return true;
214
215 case Qt::Key_Down:
216 if (d_graph && d_selected_curve) {
217 int n_curves = d_graph->curves();
218 int start = d_graph->curveIndex(d_selected_curve) + n_curves - 1;
219 QwtPlotCurve *c;
220 for (int i = start; i > start - n_curves; --i)
221 if ((c = d_graph->curve(i % n_curves))->dataSize() > 0) {
222 setSelection(c, qMin(c->dataSize() - 1, d_selected_point));
223 break;
224 }
225 d_graph->plotWidget()->replot();
226 }
227 return true;
228
229 case Qt::Key_Right:
230 case Qt::Key_Plus:
231 if (d_graph) {
232 if (d_selected_curve) {
233 int n_points = d_selected_curve->dataSize();
234 setSelection(d_selected_curve, (d_selected_point + 1) % n_points);
235 d_graph->plotWidget()->replot();
236 } else
237 setSelection(d_graph->curve(0), 0);
238 }
239 return true;
240
241 case Qt::Key_Left:
242 case Qt::Key_Minus:
243 if (d_graph) {
244 if (d_selected_curve) {
245 int n_points = d_selected_curve->dataSize();
246 setSelection(d_selected_curve, (d_selected_point - 1 + n_points) % n_points);
247 d_graph->plotWidget()->replot();
248 } else
249 setSelection(d_graph->curve(d_graph->curves() - 1), 0);
250 }
251 return true;
252
253 // The following keys represent a direction, they are
254 // organized on the keyboard.
255 case Qt::Key_1:
256 if (d_mode == Move) {
257 moveBy(-delta, delta);
258 return true;
259 }
260 break;
261 case Qt::Key_2:
262 if (d_mode == Move) {
263 moveBy(0, delta);
264 return true;
265 }
266 break;
267 case Qt::Key_3:
268 if (d_mode == Move) {
269 moveBy(delta, delta);
270 return true;
271 }
272 break;
273 case Qt::Key_4:
274 if (d_mode == Move) {
275 moveBy(-delta, 0);
276 return true;
277 }
278 break;
279 case Qt::Key_6:
280 if (d_mode == Move) {
281 moveBy(delta, 0);
282 return true;
283 }
284 break;
285 case Qt::Key_7:
286 if (d_mode == Move) {
287 moveBy(-delta, -delta);
288 return true;
289 }
290 break;
291 case Qt::Key_8:
292 if (d_mode == Move) {
293 moveBy(0, -delta);
294 return true;
295 }
296 break;
297 case Qt::Key_9:
298 if (d_mode == Move) {
299 moveBy(delta, -delta);
300 return true;
301 }
302 break;
303 default:
304 break;
305 }
306 return false;
307 }
308
removePoint()309 void DataPickerTool::removePoint()
310 {
311 if (!d_selected_curve)
312 return;
313 if (((PlotCurve *)d_selected_curve)->type() == Graph::Function) {
314 QMessageBox::critical(const_cast<Graph *>(d_graph), tr("Remove point error"),
315 tr("Sorry, but removing points of a function is not possible."));
316 return;
317 }
318
319 Table *t = ((DataCurve *)d_selected_curve)->table();
320 if (!t)
321 return;
322
323 int col = t->colIndex(d_selected_curve->title().text());
324 if (t->columnType(col) == SciDAVis::ColumnMode::Numeric) {
325 t->column(col)->setValueAt(((DataCurve *)d_selected_curve)->tableRow(d_selected_point),
326 0.0);
327 t->column(col)->setInvalid(((DataCurve *)d_selected_curve)->tableRow(d_selected_point),
328 true);
329 } else {
330 QMessageBox::warning(const_cast<Graph *>(d_graph), tr("Warning"),
331 tr("This operation cannot be performed on curves plotted from columns "
332 "having a non-numerical format."));
333 }
334
335 d_selection_marker.detach();
336 d_graph->plotWidget()->replot();
337 d_graph->setFocus();
338 d_selected_curve = NULL;
339 }
340
move(const QPoint & point)341 void DataPickerTool::move(const QPoint &point)
342 {
343 if (d_mode == Move && d_selected_curve) {
344 switch (d_move_mode) {
345 case Free:
346 d_move_target_pos = point;
347 break;
348 case Vertical:
349 d_move_target_pos.setY(point.y());
350 break;
351 case Horizontal:
352 d_move_target_pos.setX(point.x());
353 break;
354 }
355 double new_x_val = d_graph->plotWidget()->invTransform(d_selected_curve->xAxis(),
356 d_move_target_pos.x());
357 double new_y_val = d_graph->plotWidget()->invTransform(d_selected_curve->yAxis(),
358 d_move_target_pos.y());
359 d_selection_marker.setValue(new_x_val, new_y_val);
360 if (d_selection_marker.plot() == NULL)
361 d_selection_marker.attach(d_graph->plotWidget());
362 d_graph->replot();
363
364 int row = ((DataCurve *)d_selected_curve)->tableRow(d_selected_point);
365 emit statusText(QString("%1[%2]: x=%3; y=%4")
366 .arg(d_selected_curve->title().text())
367 .arg(row + 1)
368 .arg(QLocale().toString(new_x_val, 'G', d_app->d_decimal_digits))
369 .arg(QLocale().toString(new_y_val, 'G', d_app->d_decimal_digits)));
370 }
371
372 QwtPlotPicker::move(d_move_target_pos);
373 }
374
end(bool ok)375 bool DataPickerTool::end(bool ok)
376 {
377 if (d_mode == Move && d_selected_curve) {
378 if (((PlotCurve *)d_selected_curve)->type() == Graph::Function) {
379 QMessageBox::critical(d_graph, tr("Move point error"),
380 tr("Sorry, but moving points of a function is not possible."));
381 return QwtPlotPicker::end(ok);
382 }
383 Table *t = ((DataCurve *)d_selected_curve)->table();
384 if (!t)
385 return QwtPlotPicker::end(ok);
386 double new_x_val = d_graph->plotWidget()->invTransform(d_selected_curve->xAxis(),
387 d_move_target_pos.x());
388 double new_y_val = d_graph->plotWidget()->invTransform(d_selected_curve->yAxis(),
389 d_move_target_pos.y());
390 int row = ((DataCurve *)d_selected_curve)->tableRow(d_selected_point);
391 int xcol = t->colIndex(((DataCurve *)d_selected_curve)->xColumnName());
392 int ycol = t->colIndex(d_selected_curve->title().text());
393 if (t->columnType(xcol) == SciDAVis::ColumnMode::Numeric
394 && t->columnType(ycol) == SciDAVis::ColumnMode::Numeric) {
395 t->column(xcol)->setValueAt(row, new_x_val);
396 t->column(ycol)->setValueAt(row, new_y_val);
397 d_app->updateCurves(t, d_selected_curve->title().text());
398 d_app->modifiedProject();
399 } else
400 QMessageBox::warning(d_graph, tr("Warning"),
401 tr("This operation cannot be performed on curves plotted from "
402 "columns having a non-numerical format."));
403 }
404 return QwtPlotPicker::end(ok);
405 }
406
moveBy(int dx,int dy)407 void DataPickerTool::moveBy(int dx, int dy)
408 {
409 if (!d_selected_curve)
410 return;
411 move(d_move_target_pos + QPoint(dx, dy));
412 end(true);
413 }
414
trackerText(const QwtDoublePoint & point) const415 QwtText DataPickerTool::trackerText(const QwtDoublePoint &point) const
416 {
417 return plot()->axisScaleDraw(xAxis())->label(point.x()).text() + ", "
418 + plot()->axisScaleDraw(yAxis())->label(point.y()).text();
419 }
420