1 /*
2 * Copyright 2012, 2013 Thomas Schöps
3 * Copyright 2012-2015, 2017 Kai Pastor
4 *
5 * This file is part of OpenOrienteering.
6 *
7 * OpenOrienteering is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * OpenOrienteering is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with OpenOrienteering. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21
22 #include "template_adjust.h"
23
24 #include <QAbstractItemView>
25 #include <QAction>
26 #include <QCheckBox>
27 #include <QHeaderView>
28 #include <QLabel>
29 #include <QMessageBox>
30 #include <QMouseEvent>
31 #include <QPainter>
32 #include <QPushButton>
33 #include <QTableWidget>
34 #include <QToolBar>
35 #include <QVBoxLayout>
36
37 #include "core/map.h"
38 #include "gui/main_window.h"
39 #include "gui/modifier_key.h"
40 #include "gui/util_gui.h"
41 #include "gui/map/map_editor.h"
42 #include "gui/map/map_widget.h"
43 #include "templates/template.h"
44 #include "util/transformation.h"
45 #include "util/util.h"
46
47
48 namespace OpenOrienteering {
49
50 float TemplateAdjustActivity::cross_radius = 4;
51
TemplateAdjustActivity(Template * temp,MapEditorController * controller)52 TemplateAdjustActivity::TemplateAdjustActivity(Template* temp, MapEditorController* controller) : controller(controller)
53 {
54 setActivityObject(temp);
55 connect(controller->getMap(), &Map::templateChanged, this, &TemplateAdjustActivity::templateChanged);
56 connect(controller->getMap(), &Map::templateDeleted, this, &TemplateAdjustActivity::templateDeleted);
57 }
58
~TemplateAdjustActivity()59 TemplateAdjustActivity::~TemplateAdjustActivity()
60 {
61 widget->stopTemplateAdjust();
62 delete dock;
63 }
64
init()65 void TemplateAdjustActivity::init()
66 {
67 auto temp = reinterpret_cast<Template*>(activity_object);
68
69 dock = new TemplateAdjustDockWidget(tr("Template adjustment"), controller, controller->getWindow());
70 widget = new TemplateAdjustWidget(temp, controller, dock);
71 dock->setWidget(widget);
72
73 // Show dock in floating state
74 dock->setFloating(true);
75 dock->show();
76 dock->setGeometry(controller->getWindow()->geometry().left() + 40, controller->getWindow()->geometry().top() + 100, dock->width(), dock->height());
77 }
78
draw(QPainter * painter,MapWidget * widget)79 void TemplateAdjustActivity::draw(QPainter* painter, MapWidget* widget)
80 {
81 auto temp = reinterpret_cast<Template*>(activity_object);
82 bool adjusted = temp->isAdjustmentApplied();
83
84 for (int i = 0; i < temp->getNumPassPoints(); ++i)
85 {
86 auto point = temp->getPassPoint(i);
87 QPointF start = widget->mapToViewport(adjusted ? point->calculated_coords : point->src_coords);
88 QPointF end = widget->mapToViewport(point->dest_coords);
89
90 drawCross(painter, start.toPoint(), QColor(Qt::red));
91 painter->drawLine(start, end);
92 drawCross(painter, end.toPoint(), QColor(Qt::green));
93 }
94 }
95
drawCross(QPainter * painter,const QPoint & midpoint,QColor color)96 void TemplateAdjustActivity::drawCross(QPainter* painter, const QPoint& midpoint, QColor color)
97 {
98 painter->setPen(color);
99 painter->drawLine(midpoint + QPoint(0, -TemplateAdjustActivity::cross_radius), midpoint + QPoint(0, TemplateAdjustActivity::cross_radius));
100 painter->drawLine(midpoint + QPoint(-TemplateAdjustActivity::cross_radius, 0), midpoint + QPoint(TemplateAdjustActivity::cross_radius, 0));
101 }
102
findHoverPoint(Template * temp,const QPoint & mouse_pos,MapWidget * widget,bool & point_src)103 int TemplateAdjustActivity::findHoverPoint(Template* temp, const QPoint& mouse_pos, MapWidget* widget, bool& point_src)
104 {
105 bool adjusted = temp->isAdjustmentApplied();
106 const float hover_distance_sq = 10*10;
107
108 float minimum_distance_sq = 999999;
109 int point_number = -1;
110
111 for (int i = 0; i < temp->getNumPassPoints(); ++i)
112 {
113 auto point = temp->getPassPoint(i);
114
115 QPointF display_pos_src = adjusted ? widget->mapToViewport(point->calculated_coords) : widget->mapToViewport(point->src_coords);
116 float distance_sq = (display_pos_src.x() - mouse_pos.x())*(display_pos_src.x() - mouse_pos.x()) + (display_pos_src.y() - mouse_pos.y())*(display_pos_src.y() - mouse_pos.y());
117 if (distance_sq < hover_distance_sq && distance_sq < minimum_distance_sq)
118 {
119 minimum_distance_sq = distance_sq;
120 point_number = i;
121 point_src = true;
122 }
123
124 QPointF display_pos_dest = widget->mapToViewport(point->dest_coords);
125 distance_sq = (display_pos_dest.x() - mouse_pos.x())*(display_pos_dest.x() - mouse_pos.x()) + (display_pos_dest.y() - mouse_pos.y())*(display_pos_dest.y() - mouse_pos.y());
126 if (distance_sq < hover_distance_sq && distance_sq <= minimum_distance_sq) // NOTE: using <= here so dest positions have priority above other positions when at the same position
127 {
128 minimum_distance_sq = distance_sq;
129 point_number = i;
130 point_src = false;
131 }
132 }
133
134 return point_number;
135 }
136
calculateTemplateAdjust(Template * temp,TemplateTransform & out,QWidget * dialog_parent)137 bool TemplateAdjustActivity::calculateTemplateAdjust(Template* temp, TemplateTransform& out, QWidget* dialog_parent)
138 {
139 // Get original transformation
140 if (temp->isAdjustmentApplied())
141 temp->getOtherTransform(out);
142 else
143 temp->getTransform(out);
144
145 if (!temp->getPassPointList().estimateSimilarityTransformation(&out))
146 {
147 QMessageBox::warning(dialog_parent, tr("Error"), tr("Failed to calculate adjustment!"));
148 return false;
149 }
150
151 return true;
152 }
153
templateChanged(int index,const Template * temp)154 void TemplateAdjustActivity::templateChanged(int index, const Template* temp)
155 {
156 Q_UNUSED(index);
157 if (static_cast<Template*>(activity_object) == temp)
158 {
159 widget->updateDirtyRect(true);
160 widget->updateAllRows();
161 }
162 }
templateDeleted(int index,const Template * temp)163 void TemplateAdjustActivity::templateDeleted(int index, const Template* temp)
164 {
165 Q_UNUSED(index);
166 if (static_cast<Template*>(activity_object) == temp)
167 controller->setEditorActivity(nullptr);
168 }
169
170
171
172 // ### TemplateAdjustDockWidget ###
173
TemplateAdjustDockWidget(const QString & title,MapEditorController * controller,QWidget * parent)174 TemplateAdjustDockWidget::TemplateAdjustDockWidget(const QString& title, MapEditorController* controller, QWidget* parent)
175 : QDockWidget(title, parent)
176 , controller(controller)
177 {
178 // nothing else
179 }
180
event(QEvent * event)181 bool TemplateAdjustDockWidget::event(QEvent* event)
182 {
183 if (event->type() == QEvent::ShortcutOverride && controller->getWindow()->shortcutsBlocked())
184 event->accept();
185 return QDockWidget::event(event);
186 }
187
closeEvent(QCloseEvent * event)188 void TemplateAdjustDockWidget::closeEvent(QCloseEvent* event)
189 {
190 Q_UNUSED(event);
191 emit closed();
192 controller->setEditorActivity(nullptr);
193 }
194
TemplateAdjustWidget(Template * temp,MapEditorController * controller,QWidget * parent)195 TemplateAdjustWidget::TemplateAdjustWidget(Template* temp, MapEditorController* controller, QWidget* parent): QWidget(parent), temp(temp), controller(controller)
196 {
197 react_to_changes = true;
198
199 auto toolbar = new QToolBar();
200 toolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
201 toolbar->setFloatable(false);
202
203 auto passpoint_label = new QLabel(tr("Pass points:"));
204
205 new_act = new QAction(QIcon(QString::fromLatin1(":/images/cursor-georeferencing-add.png")), tr("New"), this);
206 new_act->setCheckable(true);
207 toolbar->addAction(new_act);
208
209 move_act = new QAction(QIcon(QString::fromLatin1(":/images/move.png")), tr("Move"), this);
210 move_act->setCheckable(true);
211 toolbar->addAction(move_act);
212
213 delete_act = new QAction(QIcon(QString::fromLatin1(":/images/delete.png")), tr("Delete"), this);
214 delete_act->setCheckable(true);
215 toolbar->addAction(delete_act);
216
217 table = new QTableWidget(temp->getNumPassPoints(), 5);
218 table->setEditTriggers(QAbstractItemView::AllEditTriggers);
219 table->setSelectionBehavior(QAbstractItemView::SelectRows);
220 table->setHorizontalHeaderLabels(QStringList() << tr("Template X") << tr("Template Y") << tr("Map X") << tr("Map Y") << tr("Error"));
221 table->verticalHeader()->setVisible(false);
222
223 auto header_view = table->horizontalHeader();
224 for (int i = 0; i < 5; ++i)
225 header_view->setSectionResizeMode(i, QHeaderView::ResizeToContents);
226 header_view->setSectionsClickable(false);
227
228 for (int i = 0; i < temp->getNumPassPoints(); ++i)
229 addRow(i);
230
231 apply_check = new QCheckBox(tr("Apply pass points"));
232 apply_check->setChecked(temp->isAdjustmentApplied());
233 auto help_button = new QPushButton(QIcon(QString::fromLatin1(":/images/help.png")), tr("Help"));
234 clear_and_apply_button = new QPushButton(tr("Apply && clear all"));
235 clear_and_revert_button = new QPushButton(tr("Clear all"));
236
237 auto buttons_layout = new QHBoxLayout();
238 buttons_layout->addWidget(help_button);
239 buttons_layout->addStretch(1);
240 buttons_layout->addWidget(clear_and_revert_button);
241 buttons_layout->addWidget(clear_and_apply_button);
242
243
244 auto layout = new QVBoxLayout();
245 layout->addWidget(passpoint_label);
246 layout->addWidget(toolbar);
247 layout->addWidget(table, 1);
248 layout->addWidget(apply_check);
249 layout->addSpacing(16);
250 layout->addLayout(buttons_layout);
251 setLayout(layout);
252
253 updateActions();
254
255 connect(new_act, &QAction::triggered, this, &TemplateAdjustWidget::newClicked);
256 connect(move_act, &QAction::triggered, this, &TemplateAdjustWidget::moveClicked);
257 connect(delete_act, &QAction::triggered, this, &TemplateAdjustWidget::deleteClicked);
258
259 connect(apply_check, &QAbstractButton::clicked, this, &TemplateAdjustWidget::applyClicked);
260 connect(help_button, &QAbstractButton::clicked, this, &TemplateAdjustWidget::showHelp);
261 connect(clear_and_apply_button, &QAbstractButton::clicked, this, &TemplateAdjustWidget::clearAndApplyClicked);
262 connect(clear_and_revert_button, &QAbstractButton::clicked, this, &TemplateAdjustWidget::clearAndRevertClicked);
263
264 updateDirtyRect();
265 }
266
267 TemplateAdjustWidget::~TemplateAdjustWidget() = default;
268
269
270
addPassPoint(const MapCoordF & src,const MapCoordF & dest)271 void TemplateAdjustWidget::addPassPoint(const MapCoordF& src, const MapCoordF& dest)
272 {
273 bool adjusted = temp->isAdjustmentApplied();
274 PassPoint new_point;
275
276 if (adjusted)
277 {
278 MapCoordF src_coords_template = temp->mapToTemplate(src);
279 new_point.src_coords = temp->templateToMapOther(src_coords_template);
280 }
281 else
282 {
283 new_point.src_coords = src;
284 }
285 new_point.dest_coords = dest;
286 new_point.error = -1;
287
288 int row = temp->getNumPassPoints();
289 temp->addPassPoint(new_point, row);
290 table->insertRow(row);
291 addRow(row);
292
293 temp->setAdjustmentDirty(true);
294 if (adjusted)
295 {
296 TemplateTransform transformation;
297 if (TemplateAdjustActivity::calculateTemplateAdjust(temp, transformation, this))
298 {
299 updatePointErrors();
300 temp->setTransform(transformation);
301 temp->setAdjustmentDirty(false);
302 }
303 }
304 updateDirtyRect();
305 updateActions();
306 }
307
deletePassPoint(int number)308 void TemplateAdjustWidget::deletePassPoint(int number)
309 {
310 Q_ASSERT(number >= 0 && number < temp->getNumPassPoints());
311
312 temp->deletePassPoint(number);
313 table->removeRow(number);
314
315 temp->setAdjustmentDirty(true);
316 if (temp->isAdjustmentApplied())
317 {
318 TemplateTransform transformation;
319 if (TemplateAdjustActivity::calculateTemplateAdjust(temp, transformation, this))
320 {
321 updatePointErrors();
322 temp->setTransform(transformation);
323 temp->setAdjustmentDirty(false);
324 }
325 }
326 updateDirtyRect(false);
327 updateActions();
328 }
329
stopTemplateAdjust()330 void TemplateAdjustWidget::stopTemplateAdjust()
331 {
332 // If one of these is checked, the corresponding tool should be set. The last condition is just to be sure.
333 if ((new_act->isChecked() || move_act->isChecked() || delete_act->isChecked()) && controller->getTool())
334 {
335 controller->setTool(nullptr);
336 }
337 }
338
updateActions()339 void TemplateAdjustWidget::updateActions()
340 {
341 bool has_pass_points = temp->getNumPassPoints() > 0;
342
343 clear_and_apply_button->setEnabled(has_pass_points);
344 clear_and_revert_button->setEnabled(has_pass_points);
345 move_act->setEnabled(has_pass_points);
346 delete_act->setEnabled(has_pass_points);
347 if (! has_pass_points)
348 {
349 if (move_act->isChecked())
350 {
351 move_act->setChecked(false);
352 moveClicked(false);
353 }
354 if (delete_act->isChecked())
355 {
356 delete_act->setChecked(false);
357 deleteClicked(false);
358 }
359 }
360 }
361
newClicked(bool checked)362 void TemplateAdjustWidget::newClicked(bool checked)
363 {
364 if (checked)
365 controller->setTool(new TemplateAdjustAddTool(controller, new_act, this));
366 else
367 new_act->setChecked(true);
368 }
369
moveClicked(bool checked)370 void TemplateAdjustWidget::moveClicked(bool checked)
371 {
372 if (checked)
373 controller->setTool(new TemplateAdjustMoveTool(controller, move_act, this));
374 else
375 move_act->setChecked(true);
376 }
377
deleteClicked(bool checked)378 void TemplateAdjustWidget::deleteClicked(bool checked)
379 {
380 if (checked)
381 controller->setTool(new TemplateAdjustDeleteTool(controller, delete_act, this));
382 else
383 delete_act->setChecked(true);
384 }
385
applyClicked(bool checked)386 void TemplateAdjustWidget::applyClicked(bool checked)
387 {
388 if (checked)
389 {
390 if (temp->isAdjustmentDirty())
391 {
392 TemplateTransform transformation;
393 if (!TemplateAdjustActivity::calculateTemplateAdjust(temp, transformation, this))
394 {
395 apply_check->setChecked(false);
396 return;
397 }
398 updatePointErrors();
399 temp->setOtherTransform(transformation);
400 temp->setAdjustmentDirty(false);
401 }
402 }
403
404 temp->switchTransforms();
405 react_to_changes = false;
406 controller->getMap()->emitTemplateChanged(temp);
407 react_to_changes = true;
408 updateDirtyRect();
409 }
410
clearAndApplyClicked(bool checked)411 void TemplateAdjustWidget::clearAndApplyClicked(bool checked)
412 {
413 Q_UNUSED(checked);
414
415 if (!temp->isAdjustmentApplied())
416 applyClicked(true);
417
418 clearPassPoints();
419 }
420
clearAndRevertClicked(bool checked)421 void TemplateAdjustWidget::clearAndRevertClicked(bool checked)
422 {
423 Q_UNUSED(checked);
424
425 if (temp->isAdjustmentApplied())
426 applyClicked(false);
427
428 clearPassPoints();
429 }
430
showHelp()431 void TemplateAdjustWidget::showHelp()
432 {
433 Util::showHelp(controller->getWindow(), "template_adjust.html");
434 }
435
clearPassPoints()436 void TemplateAdjustWidget::clearPassPoints()
437 {
438 temp->clearPassPoints();
439 apply_check->setChecked(false);
440 table->clearContents();
441 table->setRowCount(0);
442 updateDirtyRect();
443 updateActions();
444 }
445
addRow(int row)446 void TemplateAdjustWidget::addRow(int row)
447 {
448 react_to_changes = false;
449
450 for (int i = 0; i < 4; ++i)
451 {
452 auto item = new QTableWidgetItem();
453 item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); // TODO: make editable Qt::ItemIsEditable |
454 table->setItem(row, i, item);
455 }
456
457 auto item = new QTableWidgetItem();
458 item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
459 table->setItem(row, 4, item);
460
461 updateRow(row);
462
463 react_to_changes = true;
464 }
465
updatePointErrors()466 void TemplateAdjustWidget::updatePointErrors()
467 {
468 react_to_changes = false;
469
470 for (int row = 0; row < temp->getNumPassPoints(); ++row)
471 {
472 PassPoint& point = *temp->getPassPoint(row);
473 table->item(row, 4)->setText((point.error > 0) ? QString::number(point.error) : QString(QLatin1Char{'?'}));
474 }
475
476 react_to_changes = true;
477 }
478
updateAllRows()479 void TemplateAdjustWidget::updateAllRows()
480 {
481 if (!react_to_changes) return;
482 for (int row = 0; row < temp->getNumPassPoints(); ++row)
483 updateRow(row);
484 }
485
updateRow(int row)486 void TemplateAdjustWidget::updateRow(int row)
487 {
488 react_to_changes = false;
489
490 PassPoint& point = *temp->getPassPoint(row);
491 MapCoordF src_coords_template;
492 if (temp->isAdjustmentApplied())
493 src_coords_template = temp->mapToTemplateOther(point.src_coords);
494 else
495 src_coords_template = temp->mapToTemplate(point.src_coords);
496
497 table->item(row, 0)->setText(QString::number(src_coords_template.x()));
498 table->item(row, 1)->setText(QString::number(src_coords_template.y()));
499 table->item(row, 2)->setText(QString::number(point.dest_coords.x()));
500 table->item(row, 3)->setText(QString::number(point.dest_coords.y()));
501 table->item(row, 4)->setText((point.error > 0) ? QString::number(point.error) : QString(QLatin1Char{'?'}));
502
503 react_to_changes = true;
504 }
505
updateDirtyRect(bool redraw)506 void TemplateAdjustWidget::updateDirtyRect(bool redraw)
507 {
508 bool adjusted = temp->isAdjustmentApplied();
509
510 if (temp->getNumPassPoints() == 0)
511 temp->getMap()->clearActivityBoundingBox();
512 else
513 {
514 QRectF rect = QRectF(adjusted ? temp->getPassPoint(0)->calculated_coords : temp->getPassPoint(0)->src_coords, QSizeF(0, 0));
515 rectInclude(rect, temp->getPassPoint(0)->dest_coords);
516 for (int i = 1; i < temp->getNumPassPoints(); ++i)
517 {
518 rectInclude(rect, adjusted ? temp->getPassPoint(i)->calculated_coords : temp->getPassPoint(i)->src_coords);
519 rectInclude(rect, temp->getPassPoint(i)->dest_coords);
520 }
521 temp->getMap()->setActivityBoundingBox(rect, TemplateAdjustActivity::cross_radius, redraw);
522 }
523 }
524
525
526
527 // ### TemplateAdjustEditTool ###
528
TemplateAdjustEditTool(MapEditorController * editor,QAction * tool_button,TemplateAdjustWidget * widget)529 TemplateAdjustEditTool::TemplateAdjustEditTool(MapEditorController* editor, QAction* tool_button, TemplateAdjustWidget* widget): MapEditorTool(editor, Other, tool_button), widget(widget)
530 {
531 active_point = -1;
532 }
533
draw(QPainter * painter,MapWidget * widget)534 void TemplateAdjustEditTool::draw(QPainter* painter, MapWidget* widget)
535 {
536 bool adjusted = this->widget->getTemplate()->isAdjustmentApplied();
537
538 if (active_point >= 0)
539 {
540 auto point = this->widget->getTemplate()->getPassPoint(active_point);
541 MapCoordF position = active_point_is_src ? (adjusted ? point->calculated_coords : point->src_coords) : point->dest_coords;
542 QPoint viewport_pos = widget->mapToViewport(position).toPoint();
543
544 painter->setPen(active_point_is_src ? Qt::red : Qt::green);
545 painter->setBrush(Qt::NoBrush);
546 painter->drawRect(viewport_pos.x() - TemplateAdjustActivity::cross_radius, viewport_pos.y() - TemplateAdjustActivity::cross_radius,
547 2*TemplateAdjustActivity::cross_radius, 2*TemplateAdjustActivity::cross_radius);
548 }
549 }
550
findHoverPoint(const QPoint & mouse_pos,MapWidget * map_widget)551 void TemplateAdjustEditTool::findHoverPoint(const QPoint& mouse_pos, MapWidget* map_widget)
552 {
553 bool adjusted = this->widget->getTemplate()->isAdjustmentApplied();
554 bool new_active_point_is_src;
555 int new_active_point = TemplateAdjustActivity::findHoverPoint(this->widget->getTemplate(), mouse_pos, map_widget, new_active_point_is_src);
556
557 if (new_active_point != active_point || (new_active_point >= 0 && new_active_point_is_src != active_point_is_src))
558 {
559 // Hovering over a different point than before (or none)
560 active_point = new_active_point;
561 active_point_is_src = new_active_point_is_src;
562
563 if (active_point >= 0)
564 {
565 auto point = this->widget->getTemplate()->getPassPoint(active_point);
566 if (active_point_is_src)
567 {
568 if (adjusted)
569 map()->setDrawingBoundingBox(QRectF(point->calculated_coords.x(), point->calculated_coords.y(), 0, 0), TemplateAdjustActivity::cross_radius);
570 else
571 map()->setDrawingBoundingBox(QRectF(point->src_coords.x(), point->src_coords.y(), 0, 0), TemplateAdjustActivity::cross_radius);
572 }
573 else
574 map()->setDrawingBoundingBox(QRectF(point->dest_coords.x(), point->dest_coords.y(), 0, 0), TemplateAdjustActivity::cross_radius);
575 }
576 else
577 map()->clearDrawingBoundingBox();
578 }
579 }
580
581
582
583 // ### TemplateAdjustAddTool ###
584
TemplateAdjustAddTool(MapEditorController * editor,QAction * tool_button,TemplateAdjustWidget * widget)585 TemplateAdjustAddTool::TemplateAdjustAddTool(MapEditorController* editor, QAction* tool_button, TemplateAdjustWidget* widget): MapEditorTool(editor, Other, tool_button), widget(widget)
586 {
587 first_point_set = false;
588 }
589
init()590 void TemplateAdjustAddTool::init()
591 {
592 // NOTE: this is called by other methods to set this text again. Change that behavior if adding stuff here
593 setStatusBarText(tr("<b>Click</b>: Set the template position of the pass point. "));
594
595 MapEditorTool::init();
596 }
597
getCursor() const598 const QCursor& TemplateAdjustAddTool::getCursor() const
599 {
600 static auto const cursor = scaledToScreen(QCursor{ QPixmap(QString::fromLatin1(":/images/cursor-georeferencing-add.png")), 11, 11 });
601 return cursor;
602 }
603
mousePressEvent(QMouseEvent * event,const MapCoordF & map_coord,MapWidget * widget)604 bool TemplateAdjustAddTool::mousePressEvent(QMouseEvent* event, const MapCoordF& map_coord, MapWidget* widget)
605 {
606 Q_UNUSED(widget);
607
608 if (event->button() != Qt::LeftButton)
609 return false;
610
611 if (!first_point_set)
612 {
613 first_point = map_coord;
614 mouse_pos = map_coord;
615 first_point_set = true;
616 setDirtyRect(map_coord);
617
618 setStatusBarText(tr("<b>Click</b>: Set the map position of the pass point. ") +
619 OpenOrienteering::MapEditorTool::tr("<b>%1</b>: Abort. ").arg(ModifierKey::escape()) );
620 }
621 else
622 {
623 this->widget->addPassPoint(first_point, map_coord);
624
625 first_point_set = false;
626 map()->clearDrawingBoundingBox();
627
628 init();
629 }
630
631 return true;
632 }
mouseMoveEvent(QMouseEvent * event,const MapCoordF & map_coord,MapWidget * widget)633 bool TemplateAdjustAddTool::mouseMoveEvent(QMouseEvent* event, const MapCoordF& map_coord, MapWidget* widget)
634 {
635 Q_UNUSED(event);
636 Q_UNUSED(widget);
637
638 if (first_point_set)
639 {
640 mouse_pos = map_coord;
641 setDirtyRect(map_coord);
642 }
643
644 return true;
645 }
keyPressEvent(QKeyEvent * event)646 bool TemplateAdjustAddTool::keyPressEvent(QKeyEvent* event)
647 {
648 if (first_point_set && event->key() == Qt::Key_Escape)
649 {
650 first_point_set = false;
651 map()->clearDrawingBoundingBox();
652
653 init();
654 return true;
655 }
656 return false;
657 }
658
draw(QPainter * painter,MapWidget * widget)659 void TemplateAdjustAddTool::draw(QPainter* painter, MapWidget* widget)
660 {
661 if (first_point_set)
662 {
663 QPointF start = widget->mapToViewport(first_point);
664 QPointF end = widget->mapToViewport(mouse_pos);
665
666 TemplateAdjustActivity::drawCross(painter, start.toPoint(), Qt::red);
667
668 MapCoordF to_end = MapCoordF((end - start).x(), (end - start).y());
669 if (to_end.lengthSquared() > 3*3)
670 {
671 auto length = to_end.length();
672 to_end *= (length - 3) / length;
673 painter->drawLine(start, start + to_end);
674 }
675 }
676 }
677
setDirtyRect(const MapCoordF & mouse_pos)678 void TemplateAdjustAddTool::setDirtyRect(const MapCoordF& mouse_pos)
679 {
680 QRectF rect = QRectF(first_point.x(), first_point.y(), 0, 0);
681 rectInclude(rect, mouse_pos);
682 map()->setDrawingBoundingBox(rect, TemplateAdjustActivity::cross_radius);
683 }
684
685
686
687 // ### TemplateAdjustMoveTool ###
688
689 QCursor* TemplateAdjustMoveTool::cursor = nullptr;
690 QCursor* TemplateAdjustMoveTool::cursor_invisible = nullptr;
691
TemplateAdjustMoveTool(MapEditorController * editor,QAction * tool_button,TemplateAdjustWidget * widget)692 TemplateAdjustMoveTool::TemplateAdjustMoveTool(MapEditorController* editor, QAction* tool_button, TemplateAdjustWidget* widget): TemplateAdjustEditTool(editor, tool_button, widget)
693 {
694 dragging = false;
695
696 if (!cursor)
697 {
698 cursor = new QCursor(QPixmap(QString::fromLatin1(":/images/cursor-georeferencing-move.png")), 1, 1);
699 cursor_invisible = new QCursor(QPixmap(QString::fromLatin1(":/images/cursor-invisible.png")), 0, 0);
700 }
701 }
702
init()703 void TemplateAdjustMoveTool::init()
704 {
705 setStatusBarText(tr("<b>Drag</b>: Move pass points. "));
706
707 MapEditorTool::init();
708 }
709
getCursor() const710 const QCursor& TemplateAdjustMoveTool::getCursor() const
711 {
712 return *cursor;
713 }
714
mousePressEvent(QMouseEvent * event,const MapCoordF & map_coord,MapWidget * widget)715 bool TemplateAdjustMoveTool::mousePressEvent(QMouseEvent* event, const MapCoordF& map_coord, MapWidget* widget)
716 {
717 if (event->button() != Qt::LeftButton)
718 return false;
719
720 bool adjusted = this->widget->getTemplate()->isAdjustmentApplied();
721
722 active_point = TemplateAdjustActivity::findHoverPoint(this->widget->getTemplate(), event->pos(), widget, active_point_is_src);
723 if (active_point >= 0)
724 {
725 auto point = this->widget->getTemplate()->getPassPoint(active_point);
726 MapCoordF* point_coords;
727 if (active_point_is_src)
728 {
729 if (adjusted)
730 point_coords = &point->calculated_coords;
731 else
732 point_coords = &point->src_coords;
733 }
734 else
735 point_coords = &point->dest_coords;
736
737 dragging = true;
738 dragging_offset = MapCoordF(point_coords->x() - map_coord.x(), point_coords->y() - map_coord.y());
739
740 widget->setCursor(*cursor_invisible);
741 }
742
743 return false;
744 }
745
mouseMoveEvent(QMouseEvent * event,const MapCoordF & map_coord,MapWidget * widget)746 bool TemplateAdjustMoveTool::mouseMoveEvent(QMouseEvent* event, const MapCoordF& map_coord, MapWidget* widget)
747 {
748 if (!dragging)
749 findHoverPoint(event->pos(), widget);
750 else
751 setActivePointPosition(map_coord);
752
753 return true;
754 }
755
mouseReleaseEvent(QMouseEvent * event,const MapCoordF & map_coord,MapWidget * widget)756 bool TemplateAdjustMoveTool::mouseReleaseEvent(QMouseEvent* event, const MapCoordF& map_coord, MapWidget* widget)
757 {
758 Q_UNUSED(event);
759
760 auto temp = this->widget->getTemplate();
761
762 if (dragging)
763 {
764 setActivePointPosition(map_coord);
765
766 if (temp->isAdjustmentApplied())
767 {
768 TemplateTransform transformation;
769 if (TemplateAdjustActivity::calculateTemplateAdjust(temp, transformation, this->widget))
770 {
771 this->widget->updatePointErrors();
772 this->widget->updateDirtyRect();
773 temp->setTransform(transformation);
774 temp->setAdjustmentDirty(false);
775 }
776 }
777
778 dragging = false;
779 widget->setCursor(*cursor);
780 }
781 return false;
782 }
783
setActivePointPosition(const MapCoordF & map_coord)784 void TemplateAdjustMoveTool::setActivePointPosition(const MapCoordF& map_coord)
785 {
786 bool adjusted = this->widget->getTemplate()->isAdjustmentApplied();
787
788 auto point = this->widget->getTemplate()->getPassPoint(active_point);
789 MapCoordF* changed_coords;
790 if (active_point_is_src)
791 {
792 if (adjusted)
793 changed_coords = &point->calculated_coords;
794 else
795 changed_coords = &point->src_coords;
796 }
797 else
798 changed_coords = &point->dest_coords;
799 QRectF changed_rect = QRectF(adjusted ? point->calculated_coords : point->src_coords, QSizeF(0, 0));
800 rectInclude(changed_rect, point->dest_coords);
801 rectInclude(changed_rect, map_coord);
802
803 *changed_coords = MapCoordF(map_coord.x() + dragging_offset.x(), map_coord.y() + dragging_offset.y());
804 if (active_point_is_src)
805 {
806 if (adjusted)
807 {
808 MapCoordF src_coords_template = this->widget->getTemplate()->mapToTemplate(point->calculated_coords);
809 point->src_coords = this->widget->getTemplate()->templateToMapOther(src_coords_template);
810 }
811 }
812 point->error = -1;
813
814 this->widget->updateRow(active_point);
815
816 map()->setDrawingBoundingBox(QRectF(changed_coords->x(), changed_coords->y(), 0, 0), TemplateAdjustActivity::cross_radius + 1, false);
817 map()->updateDrawing(changed_rect, TemplateAdjustActivity::cross_radius + 1);
818 widget->updateDirtyRect();
819
820 this->widget->getTemplate()->setAdjustmentDirty(true);
821 }
822
823
824
825 // ### TemplateAdjustDeleteTool ###
826
TemplateAdjustDeleteTool(MapEditorController * editor,QAction * tool_button,TemplateAdjustWidget * widget)827 TemplateAdjustDeleteTool::TemplateAdjustDeleteTool(MapEditorController* editor, QAction* tool_button, TemplateAdjustWidget* widget): TemplateAdjustEditTool(editor, tool_button, widget)
828 {
829 // nothing
830 }
831
init()832 void TemplateAdjustDeleteTool::init()
833 {
834 setStatusBarText(tr("<b>Click</b>: Delete pass points. "));
835
836 MapEditorTool::init();
837 }
838
getCursor() const839 const QCursor& TemplateAdjustDeleteTool::getCursor() const
840 {
841 static auto const cursor = scaledToScreen(QCursor{ QPixmap(QString::fromLatin1(":/images/cursor-delete.png")), 1, 1});
842 return cursor;
843 }
844
mousePressEvent(QMouseEvent * event,const MapCoordF &,MapWidget * widget)845 bool TemplateAdjustDeleteTool::mousePressEvent(QMouseEvent* event, const MapCoordF& /*map_coord*/, MapWidget* widget)
846 {
847 if (event->button() != Qt::LeftButton)
848 return false;
849
850 bool adjusted = this->widget->getTemplate()->isAdjustmentApplied();
851
852 active_point = TemplateAdjustActivity::findHoverPoint(this->widget->getTemplate(), event->pos(), widget, active_point_is_src);
853 if (active_point >= 0)
854 {
855 auto point = this->widget->getTemplate()->getPassPoint(active_point);
856 QRectF changed_rect = QRectF(adjusted ? point->calculated_coords : point->src_coords, QSizeF(0, 0));
857 rectInclude(changed_rect, point->dest_coords);
858
859 this->widget->deletePassPoint(active_point);
860 findHoverPoint(event->pos(), widget);
861 map()->updateDrawing(changed_rect, TemplateAdjustActivity::cross_radius + 1);
862 }
863 return true;
864 }
865
mouseMoveEvent(QMouseEvent * event,const MapCoordF &,MapWidget * widget)866 bool TemplateAdjustDeleteTool::mouseMoveEvent(QMouseEvent* event, const MapCoordF& /*map_coord*/, MapWidget* widget)
867 {
868 findHoverPoint(event->pos(), widget);
869 return true;
870 }
871
872
873 } // namespace OpenOrienteering
874