1 /*
2 * Copyright 2012, 2013 Thomas Schöps
3 * Copyright 2012-2020 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_list_widget.h"
23
24 #include <vector>
25
26 #include <Qt>
27 #include <QtGlobal>
28 #include <QAbstractButton>
29 #include <QAbstractItemView>
30 #include <QAbstractSlider>
31 #include <QAction>
32 #include <QBoxLayout>
33 #include <QBrush>
34 #include <QByteArray>
35 #include <QCheckBox>
36 #include <QColor>
37 #include <QCoreApplication>
38 #include <QDialog>
39 #include <QDir>
40 #include <QEvent>
41 #include <QEventLoop>
42 #include <QFileInfo>
43 #include <QFlags>
44 #include <QHBoxLayout>
45 #include <QHeaderView>
46 #include <QIcon>
47 #include <QInputDialog>
48 #include <QItemSelectionModel>
49 #include <QKeyEvent>
50 #include <QLabel>
51 #include <QLatin1Char>
52 #include <QLatin1String>
53 #include <QList>
54 #include <QLocale>
55 #include <QMenu>
56 #include <QMessageBox>
57 #include <QPainter>
58 #include <QPalette>
59 #include <QPixmap>
60 #include <QRect>
61 #include <QRectF>
62 #include <QScroller>
63 #include <QSettings>
64 #include <QSignalBlocker>
65 #include <QSize>
66 #include <QSlider>
67 #include <QStringList>
68 #include <QStyle>
69 #include <QStyleOption>
70 #include <QStyleOptionButton>
71 #include <QStyleOptionViewItem>
72 #include <QTableWidget>
73 #include <QTableWidgetItem>
74 #include <QToolButton>
75 #include <QToolTip>
76 #include <QVBoxLayout>
77 #include <QVariant>
78
79 #ifdef WITH_COVE
80 #include <app/coverunner.h>
81 #endif /* WITH_COVE */
82
83 #include "settings.h"
84 #include "core/georeferencing.h"
85 #include "core/map.h"
86 #include "core/map_coord.h"
87 #include "fileformats/file_format_registry.h"
88 #include "fileformats/file_import_export.h"
89 #include "gui/file_dialog.h"
90 #include "gui/main_window.h"
91 #include "gui/util_gui.h"
92 #include "gui/map/map_editor.h"
93 #include "gui/map/map_editor_activity.h"
94 #include "gui/map/map_widget.h"
95 #include "gui/widgets/segmented_button_layout.h"
96 #include "templates/template.h"
97 #include "templates/template_adjust.h"
98 #include "templates/template_image.h"
99 #include "templates/template_map.h"
100 #include "templates/template_tool_move.h"
101 #include "util/item_delegates.h"
102
103
104 namespace OpenOrienteering {
105
106 // ### TemplateListWidget ###
107
108 // Template grouping implementation is incomplete
109 #define NO_TEMPLATE_GROUP_SUPPORT
110
TemplateListWidget(Map * map,MapView * main_view,MapEditorController * controller,QWidget * parent)111 TemplateListWidget::TemplateListWidget(Map* map, MapView* main_view, MapEditorController* controller, QWidget* parent)
112 : QWidget(parent)
113 , map(map)
114 , main_view(main_view)
115 , controller(controller)
116 , mobile_mode(controller->isInMobileMode())
117 , name_column(3)
118 {
119 Q_ASSERT(main_view);
120 Q_ASSERT(controller);
121
122 setWhatsThis(Util::makeWhatThis("templates.html#setup"));
123
124 QStyleOption style_option(QStyleOption::Version, QStyleOption::SO_DockWidget);
125
126 // Wrap the checkbox in a widget and layout to force a margin.
127 auto top_bar_widget = new QWidget();
128 auto top_bar_layout = new QHBoxLayout(top_bar_widget);
129
130 // Reuse the translation from MapEditorController action.
131 all_hidden_check = new QCheckBox(::OpenOrienteering::MapEditorController::tr("Hide all templates"));
132 top_bar_layout->addWidget(all_hidden_check);
133
134 if (mobile_mode)
135 {
136 auto close_action = new QAction(QIcon(QString::fromLatin1(":/images/close.png")), ::OpenOrienteering::MainWindow::tr("Close"), this);
137 connect(close_action, &QAction::triggered, this, &TemplateListWidget::closeClicked );
138
139 auto close_button = new QToolButton();
140 close_button->setDefaultAction(close_action);
141 close_button->setAutoRaise(true);
142
143 top_bar_layout->addWidget(close_button);
144 }
145
146 top_bar_layout->setContentsMargins(
147 style()->pixelMetric(QStyle::PM_LayoutLeftMargin, &style_option) / 2,
148 style()->pixelMetric(QStyle::PM_LayoutTopMargin, &style_option) / 2,
149 style()->pixelMetric(QStyle::PM_LayoutRightMargin, &style_option) / 2,
150 0 // Covered by the main layout's spacing.
151 );
152
153 // Template table
154 template_table = new QTableWidget(map->getNumTemplates() + 1, 4);
155 QScroller::grabGesture(template_table->viewport(), QScroller::TouchGesture);
156 template_table->installEventFilter(this);
157 template_table->setEditTriggers(QAbstractItemView::AllEditTriggers);
158 template_table->setSelectionBehavior(QAbstractItemView::SelectRows);
159 template_table->setSelectionMode(QAbstractItemView::SingleSelection);
160 template_table->verticalHeader()->setVisible(false);
161 #ifdef NO_TEMPLATE_GROUP_SUPPORT
162 // Template grouping is not yet implemented.
163 template_table->hideColumn(2);
164 #endif
165
166 auto header_view = template_table->horizontalHeader();
167 if (mobile_mode)
168 {
169 header_view->setVisible(false);
170 template_table->setShowGrid(false);
171 template_table->hideColumn(3);
172 name_column = 0;
173 }
174 else
175 {
176 template_table->setHorizontalHeaderLabels(QStringList() << QString{} << tr("Opacity") << tr("Group") << tr("Filename"));
177 template_table->horizontalHeaderItem(0)->setData(Qt::ToolTipRole, tr("Show"));
178
179 header_view->setSectionResizeMode(0, QHeaderView::Fixed);
180
181 QStyleOptionButton option;
182 auto geometry = style()->subElementRect(QStyle::SE_ItemViewItemCheckIndicator, &option, nullptr);
183 template_table->setColumnWidth(0, geometry.width() * 14 / 10);
184
185 auto header_check_size = geometry.size();
186 if (header_check_size.isValid())
187 {
188 QCheckBox header_check;
189 header_check.setChecked(true);
190 header_check.setEnabled(false);
191 QPixmap pixmap(header_check_size);
192 pixmap.fill(Qt::transparent);
193 QPainter painter(&pixmap);
194 QStyleOptionViewItem option;
195 option.rect = { {0, 0}, geometry.size() };
196 style()->drawPrimitive(QStyle::PE_IndicatorViewItemCheck, &option, &painter, nullptr);
197 painter.end();
198 template_table->horizontalHeaderItem(0)->setData(Qt::DecorationRole, pixmap);
199 }
200 }
201
202 auto percentage_delegate = new PercentageDelegate(this, 5);
203 template_table->setItemDelegateForColumn(1, percentage_delegate);
204
205 for (int i = 1; i < 3; ++i)
206 header_view->setSectionResizeMode(i, QHeaderView::ResizeToContents);
207 header_view->setSectionResizeMode(name_column, QHeaderView::Stretch);
208 header_view->setSectionsClickable(false);
209
210 for (int i = 0; i < map->getNumTemplates() + 1; ++i)
211 addRowItems(i);
212
213 all_templates_layout = new QVBoxLayout();
214 all_templates_layout->setMargin(0);
215 all_templates_layout->addWidget(top_bar_widget);
216 all_templates_layout->addWidget(template_table, 1);
217
218 auto new_button_menu = new QMenu(this);
219 if (!mobile_mode)
220 {
221 new_button_menu->addAction(QIcon(QString::fromLatin1(":/images/open.png")), tr("Open..."), this, SLOT(openTemplate()));
222 new_button_menu->addAction(controller->getAction("reopentemplate"));
223 }
224 duplicate_action = new_button_menu->addAction(QIcon(QString::fromLatin1(":/images/tool-duplicate.png")), tr("Duplicate"), this, SLOT(duplicateTemplate()));
225 #if 0
226 current_action = new_button_menu->addAction(tr("Sketch"));
227 current_action->setDisabled(true);
228 current_action = new_button_menu->addAction(tr("GPS"));
229 current_action->setDisabled(true);
230 #endif
231
232 auto new_button = newToolButton(QIcon(QString::fromLatin1(":/images/plus.png")), tr("Add template..."));
233 new_button->setPopupMode(QToolButton::InstantPopup);
234 new_button->setMenu(new_button_menu);
235
236 delete_button = newToolButton(QIcon(QString::fromLatin1(":/images/minus.png")), tr("Remove"));
237
238 auto add_remove_layout = new SegmentedButtonLayout();
239 add_remove_layout->addWidget(new_button);
240 add_remove_layout->addWidget(delete_button);
241
242 move_up_button = newToolButton(QIcon(QString::fromLatin1(":/images/arrow-up.png")), tr("Move Up"));
243 move_up_button->setAutoRepeat(true);
244 move_down_button = newToolButton(QIcon(QString::fromLatin1(":/images/arrow-down.png")), tr("Move Down"));
245 move_down_button->setAutoRepeat(true);
246
247 auto up_down_layout = new SegmentedButtonLayout();
248 up_down_layout->addWidget(move_up_button);
249 up_down_layout->addWidget(move_down_button);
250
251 move_by_hand_action = new QAction(QIcon(QString::fromLatin1(":/images/move.png")), tr("Move by hand"), this);
252 move_by_hand_action->setCheckable(true);
253 move_by_hand_button = newToolButton(move_by_hand_action->icon(), move_by_hand_action->text());
254 move_by_hand_button->setDefaultAction(move_by_hand_action);
255 move_by_hand_button->setVisible(!mobile_mode);
256 adjust_button = newToolButton(QIcon(QString::fromLatin1(":/images/georeferencing.png")), tr("Adjust..."));
257 adjust_button->setCheckable(true);
258 adjust_button->setVisible(!mobile_mode);
259
260 auto edit_menu = new QMenu(this);
261 georef_action = edit_menu->addAction(tr("Georeferenced"), this, SLOT(changeGeorefClicked()));
262 georef_action->setCheckable(true);
263 position_action = edit_menu->addAction(tr("Positioning..."));
264 position_action->setCheckable(true);
265 edit_menu->addSeparator();
266 import_action = edit_menu->addAction(tr("Import and remove"), this, SLOT(importClicked()));
267
268 #if WITH_COVE
269 vectorize_action = edit_menu->addAction(tr("Vectorize lines"), this, SLOT(vectorizeClicked()));
270 #else
271 vectorize_action = nullptr;
272 #endif /* WITH_COVE */
273
274 edit_button = newToolButton(QIcon(QString::fromLatin1(":/images/settings.png")),
275 ::OpenOrienteering::MapEditorController::tr("&Edit").remove(QLatin1Char('&')));
276 edit_button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
277 edit_button->setPopupMode(QToolButton::InstantPopup);
278 edit_button->setMenu(edit_menu);
279 edit_button->setVisible(!mobile_mode);
280
281 // The buttons row layout
282 auto list_buttons_layout = new QHBoxLayout();
283 list_buttons_layout->setContentsMargins(0,0,0,0);
284 list_buttons_layout->addLayout(add_remove_layout);
285 list_buttons_layout->addLayout(up_down_layout);
286 list_buttons_layout->addWidget(adjust_button);
287 list_buttons_layout->addWidget(move_by_hand_button);
288 list_buttons_layout->addWidget(edit_button);
289
290 list_buttons_group = new QWidget();
291 list_buttons_group->setLayout(list_buttons_layout);
292
293 auto all_buttons_layout = new QHBoxLayout();
294 all_buttons_layout->setContentsMargins(
295 style()->pixelMetric(QStyle::PM_LayoutLeftMargin, &style_option) / 2,
296 0, // Covered by the main layout's spacing.
297 style()->pixelMetric(QStyle::PM_LayoutRightMargin, &style_option) / 2,
298 style()->pixelMetric(QStyle::PM_LayoutBottomMargin, &style_option) / 2
299 );
300 all_buttons_layout->addWidget(list_buttons_group);
301 all_buttons_layout->addWidget(new QLabel(QString::fromLatin1(" ")), 1);
302
303 if (!mobile_mode)
304 {
305 auto help_button = newToolButton(QIcon(QString::fromLatin1(":/images/help.png")), tr("Help"));
306 help_button->setAutoRaise(true);
307 all_buttons_layout->addWidget(help_button);
308 connect(help_button, &QAbstractButton::clicked, this, &TemplateListWidget::showHelp);
309 }
310
311 all_templates_layout->addLayout(all_buttons_layout);
312
313 setLayout(all_templates_layout);
314
315 //group_button = new QPushButton(QIcon(QString::fromLatin1(":/images/group.png")), tr("(Un)group"));
316 /*more_button = new QToolButton();
317 more_button->setText(tr("More..."));
318 more_button->setPopupMode(QToolButton::InstantPopup);
319 more_button->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed));
320 QMenu* more_button_menu = new QMenu(more_button);
321 more_button_menu->addAction(QIcon(QString::fromLatin1(":/images/window-new.png")), tr("Numeric transformation window"));
322 more_button_menu->addAction(tr("Set transparent color..."));
323 more_button_menu->addAction(tr("Trace lines..."));
324 more_button->setMenu(more_button_menu);*/
325
326 updateButtons();
327
328 setAllTemplatesHidden(main_view->areAllTemplatesHidden());
329
330 // Connections
331 connect(all_hidden_check, &QAbstractButton::toggled, controller, &MapEditorController::hideAllTemplates);
332
333 connect(template_table, &QTableWidget::cellChanged, this, &TemplateListWidget::cellChange);
334 connect(template_table->selectionModel(), &QItemSelectionModel::selectionChanged, this, &TemplateListWidget::updateButtons);
335 connect(template_table, &QTableWidget::cellClicked, this, &TemplateListWidget::cellClicked, Qt::QueuedConnection);
336 connect(template_table, &QTableWidget::cellDoubleClicked, this, &TemplateListWidget::cellDoubleClicked, Qt::QueuedConnection);
337
338 connect(delete_button, &QAbstractButton::clicked, this, &TemplateListWidget::deleteTemplate);
339 connect(move_up_button, &QAbstractButton::clicked, this, &TemplateListWidget::moveTemplateUp);
340 connect(move_down_button, &QAbstractButton::clicked, this, &TemplateListWidget::moveTemplateDown);
341
342 connect(move_by_hand_action, &QAction::triggered, this, &TemplateListWidget::moveByHandClicked);
343 connect(adjust_button, &QAbstractButton::clicked, this, &TemplateListWidget::adjustClicked);
344 connect(position_action, &QAction::triggered, this, &TemplateListWidget::positionClicked);
345
346 //connect(group_button, SIGNAL(clicked(bool)), this, SLOT(groupClicked()));
347 //connect(more_button_menu, SIGNAL(triggered(QAction*)), this, SLOT(moreActionClicked(QAction*)));
348
349 connect(main_view, &MapView::visibilityChanged, this, &TemplateListWidget::updateVisibility);
350 connect(controller, &MapEditorController::templatePositionDockWidgetClosed, this, &TemplateListWidget::templatePositionDockWidgetClosed);
351 }
352
353 TemplateListWidget::~TemplateListWidget() = default;
354
355
356
newToolButton(const QIcon & icon,const QString & text)357 QToolButton* TemplateListWidget::newToolButton(const QIcon& icon, const QString& text)
358 {
359 auto button = new QToolButton();
360 button->setToolButtonStyle(Qt::ToolButtonIconOnly);
361 button->setToolTip(text);
362 button->setIcon(icon);
363 button->setText(text);
364 button->setWhatsThis(Util::makeWhatThis("templates.html#setup"));
365 return button;
366 }
367
368 // slot
setAllTemplatesHidden(bool value)369 void TemplateListWidget::setAllTemplatesHidden(bool value)
370 {
371 all_hidden_check->setChecked(value);
372
373 bool enabled = !value;
374 template_table->setEnabled(enabled);
375 list_buttons_group->setEnabled(enabled);
376 updateButtons();
377 }
378
addTemplateAt(Template * new_template,int pos)379 void TemplateListWidget::addTemplateAt(Template* new_template, int pos)
380 {
381 /*int row;
382 if (pos >= 0)
383 row = template_table->rowCount() - 1 - ((pos >= map->getFirstFrontTemplate()) ? (pos + 1) : pos);
384 else
385 row = template_table->rowCount() - 1 - map->getFirstFrontTemplate();*/
386
387 if (pos < map->getFirstFrontTemplate())
388 map->setFirstFrontTemplate(map->getFirstFrontTemplate() + 1);
389 if (pos < 0)
390 pos = map->getFirstFrontTemplate() - 1;
391
392 map->addTemplate(new_template, pos);
393 map->setTemplateAreaDirty(pos);
394
395 map->setTemplatesDirty();
396 }
397
showOpenTemplateDialog(QWidget * dialog_parent,MapEditorController * controller)398 std::unique_ptr<Template> TemplateListWidget::showOpenTemplateDialog(QWidget* dialog_parent, MapEditorController* controller)
399 {
400 QSettings settings;
401 QString template_directory = settings.value(QString::fromLatin1("templateFileDirectory"), QDir::homePath()).toString();
402
403 QString pattern;
404 for (const auto& extension : Template::supportedExtensions())
405 {
406 pattern.append(QLatin1String(" *."));
407 pattern.append(QLatin1String(extension));
408 }
409 pattern.remove(0, 1);
410 QString path = FileDialog::getOpenFileName(dialog_parent,
411 tr("Open image, GPS track or DXF file"),
412 template_directory,
413 QString::fromLatin1("%1 (%2);;%3 (*.*)").arg(
414 tr("Template files"), pattern, tr("All files")));
415 auto canonical_path = QFileInfo(path).canonicalFilePath();
416 if (!canonical_path.isEmpty())
417 {
418 path = canonical_path;
419 settings.setValue(QString::fromLatin1("templateFileDirectory"), QFileInfo(path).canonicalPath());
420 }
421 else if (path.isEmpty())
422 {
423 return {};
424 }
425
426 bool center_in_view = true;
427 QString error;
428 auto new_temp = Template::templateForFile(path, controller->getMap());
429 if (!new_temp)
430 {
431 error = tr("File format not recognized.");
432 }
433 else if (!new_temp->preLoadConfiguration(dialog_parent))
434 {
435 // For now, an empty error string means the step was canceled by the user.
436 error = new_temp->errorString();
437 new_temp.reset();
438 }
439 else if (!new_temp->loadTemplateFile(true))
440 {
441 error = new_temp->errorString();
442 /// \todo Review the default error message. Don't use question mark.
443 if (error.isEmpty())
444 error = tr("Failed to load template. Does the file exist and is it valid?");
445 new_temp.reset();
446 }
447 else if (!new_temp->postLoadConfiguration(dialog_parent, center_in_view))
448 {
449 // For now, an empty error string means the step was canceled by the user.
450 error = new_temp->errorString();
451 new_temp.reset();
452 }
453 // If the template is not georeferenced, position it at the viewport midpoint
454 else if (!new_temp->isTemplateGeoreferenced() && center_in_view)
455 {
456 auto main_view = controller->getMainWidget()->getMapView();
457 auto view_pos = main_view->center();
458 auto offset = MapCoord { new_temp->calculateTemplateBoundingBox().center() };
459 new_temp->setTemplatePosition(view_pos - offset);
460 }
461
462 if (!new_temp && !error.isEmpty())
463 {
464 auto const error_template = tr("Cannot open template\n%1:\n%2");
465 QMessageBox::warning(dialog_parent, tr("Error"), error_template.arg(path, error));
466 }
467
468 return new_temp;
469 }
470
eventFilter(QObject * watched,QEvent * event)471 bool TemplateListWidget::eventFilter(QObject* watched, QEvent* event)
472 {
473 if (watched == template_table)
474 {
475 switch (event->type())
476 {
477 case QEvent::KeyPress:
478 if (static_cast<QKeyEvent*>(event)->key() == Qt::Key_Space)
479 {
480 int row = template_table->currentRow();
481 if (row >= 0 && template_table->item(row, 1)->flags().testFlag(Qt::ItemIsEnabled))
482 {
483 bool is_checked = template_table->item(row, 0)->checkState() != Qt::Unchecked;
484 template_table->item(row, 0)->setCheckState(is_checked ? Qt::Unchecked : Qt::Checked);
485 }
486 return true;
487 }
488 break;
489
490 case QEvent::KeyRelease:
491 if (static_cast<QKeyEvent*>(event)->key() == Qt::Key_Space)
492 return true;
493 break;
494
495 #ifdef Q_OS_ANDROID
496 case QEvent::Show:
497 {
498 auto map_row = rowFromPos(map->getFirstFrontTemplate()) + 1;
499 template_table->resizeRowToContents(map_row);
500 template_table->verticalHeader()->setDefaultSectionSize(template_table->verticalHeader()->sectionSize(map_row));
501 }
502 break;
503 #endif
504
505 default:
506 ; //nothing
507 }
508 }
509
510 return false;
511 }
512
newTemplate(QAction * action)513 void TemplateListWidget::newTemplate(QAction* action)
514 {
515 if (action->text() == tr("Sketch"))
516 {
517 // TODO
518 }
519 else if (action->text() == tr("GPS"))
520 {
521 // TODO
522 }
523 }
524
openTemplate()525 void TemplateListWidget::openTemplate()
526 {
527 auto new_template = showOpenTemplateDialog(window(), controller);
528 if (new_template)
529 {
530 int pos = -1;
531 int row = template_table->currentRow();
532 if (row >= 0)
533 pos = posFromRow(row);
534
535 addTemplateAt(new_template.release(), pos);
536 }
537 }
538
deleteTemplate()539 void TemplateListWidget::deleteTemplate()
540 {
541 int pos = posFromRow(template_table->currentRow());
542 Q_ASSERT(pos >= 0);
543
544 map->setTemplateAreaDirty(pos);
545
546 if (Settings::getInstance().getSettingCached(Settings::Templates_KeepSettingsOfClosed).toBool())
547 map->closeTemplate(pos);
548 else
549 map->deleteTemplate(pos);
550
551 {
552 QSignalBlocker block(template_table);
553 template_table->removeRow(template_table->currentRow());
554 }
555
556 if (pos < map->getFirstFrontTemplate())
557 map->setFirstFrontTemplate(map->getFirstFrontTemplate() - 1);
558
559 map->setTemplatesDirty();
560
561 // Do a change of selection to trigger a button update
562 int current_row = template_table->currentRow();
563 template_table->clearSelection();
564 template_table->selectRow(current_row);
565 }
566
duplicateTemplate()567 void TemplateListWidget::duplicateTemplate()
568 {
569 int row = template_table->currentRow();
570 Q_ASSERT(row >= 0);
571 int pos = posFromRow(row);
572 Q_ASSERT(pos >= 0);
573
574 const auto* prototype = map->getTemplate(pos);
575 const auto visibility = main_view->getTemplateVisibility(prototype);
576
577 auto new_template = prototype->duplicate();
578 addTemplateAt(new_template, pos);
579 main_view->setTemplateVisibility(new_template, visibility);
580 updateRow(row+1);
581 }
582
moveTemplateUp()583 void TemplateListWidget::moveTemplateUp()
584 {
585 int row = template_table->currentRow();
586 Q_ASSERT(row >= 1);
587 if (!(row >= 1)) return; // in release mode
588
589 int cur_pos = posFromRow(row);
590 int above_pos = posFromRow(row - 1);
591 map->setTemplateAreaDirty(cur_pos);
592 map->setTemplateAreaDirty(above_pos);
593
594 if (cur_pos < 0)
595 {
596 // Moving the map layer up
597 map->setFirstFrontTemplate(map->getFirstFrontTemplate() + 1);
598 }
599 else if (above_pos < 0)
600 {
601 // Moving something above the map layer
602 map->setFirstFrontTemplate(map->getFirstFrontTemplate() - 1);
603 }
604 else
605 {
606 // Exchanging two templates
607 auto above_template = map->getTemplate(above_pos);
608 auto cur_template = map->getTemplate(cur_pos);
609 map->setTemplate(cur_template, above_pos);
610 map->setTemplate(above_template, cur_pos);
611 }
612
613 map->setTemplateAreaDirty(cur_pos);
614 map->setTemplateAreaDirty(above_pos);
615 updateRow(row - 1);
616 updateRow(row);
617
618 {
619 QSignalBlocker block(template_table);
620 template_table->setCurrentCell(row - 1, template_table->currentColumn());
621 }
622 //updateButtons();
623 map->setTemplatesDirty();
624 }
625
moveTemplateDown()626 void TemplateListWidget::moveTemplateDown()
627 {
628 int row = template_table->currentRow();
629 Q_ASSERT(row >= 0);
630 if (!(row >= 0)) return; // in release mode
631 Q_ASSERT(row < template_table->rowCount() - 1);
632 if (!(row < template_table->rowCount() - 1)) return; // in release mode
633
634 int cur_pos = posFromRow(row);
635 int below_pos = posFromRow(row + 1);
636 map->setTemplateAreaDirty(cur_pos);
637 map->setTemplateAreaDirty(below_pos);
638
639 if (cur_pos < 0)
640 {
641 // Moving the map layer down
642 map->setFirstFrontTemplate(map->getFirstFrontTemplate() - 1);
643 }
644 else if (below_pos < 0)
645 {
646 // Moving something below the map layer
647 map->setFirstFrontTemplate(map->getFirstFrontTemplate() + 1);
648 }
649 else
650 {
651 // Exchanging two templates
652 auto below_template = map->getTemplate(below_pos);
653 auto cur_template = map->getTemplate(cur_pos);
654 map->setTemplate(cur_template, below_pos);
655 map->setTemplate(below_template, cur_pos);
656 }
657
658 map->setTemplateAreaDirty(cur_pos);
659 map->setTemplateAreaDirty(below_pos);
660 updateRow(row + 1);
661 updateRow(row);
662
663 {
664 QSignalBlocker block(template_table);
665 template_table->setCurrentCell(row + 1, template_table->currentColumn());
666 }
667 updateButtons();
668 map->setTemplatesDirty();
669 }
670
showHelp()671 void TemplateListWidget::showHelp()
672 {
673 Util::showHelp(controller->getWindow(), "templates.html", "setup");
674 }
675
cellChange(int row,int column)676 void TemplateListWidget::cellChange(int row, int column)
677 {
678 int pos = posFromRow(row);
679
680 Template* temp = nullptr;
681 auto state = Template::Loaded;
682 auto visibility = main_view->getMapVisibility();
683 if (pos >= 0)
684 {
685 // Template row, not map row
686 temp = map->getTemplate(pos);
687 state = temp->getTemplateState();
688 visibility = main_view->getTemplateVisibility(temp);
689 }
690
691 auto updateVisibility = [this](Template* temp, TemplateVisibility vis)
692 {
693 if (temp)
694 main_view->setTemplateVisibility(temp, vis);
695 else
696 main_view->setMapVisibility(vis);
697 };
698
699 if (state != Template::Invalid)
700 {
701 auto setAreaDirty = [this, pos]()
702 {
703 if (pos >= 0)
704 {
705 map->setTemplateAreaDirty(pos);
706 }
707 else
708 {
709 //QRectF map_bounds = map->calculateExtent(true, false, nullptr);
710 //map->setObjectAreaDirty(map_bounds);
711 main_view->updateAllMapWidgets(); // Map change - doesn't need to update the map cache
712 }
713 };
714
715 switch (column)
716 {
717 case 0: // Visibility checkbox
718 {
719 bool visible = template_table->item(row, column)->checkState() == Qt::Checked;
720 if (visibility.visible != visible)
721 {
722 if (!visible)
723 {
724 setAreaDirty();
725 visibility.visible = false;
726 updateVisibility(temp, visibility);
727 }
728 else
729 {
730 if (state != Template::Loaded)
731 {
732 // Ensure feedback before slow loading/drawing
733 QSignalBlocker block(template_table);
734 template_table->item(row, 0)->setCheckState(Qt::PartiallyChecked);
735 auto item_rect = template_table->visualItemRect(template_table->item(row, 1));
736 QToolTip::showText(template_table->mapToGlobal(item_rect.bottomLeft()),
737 qApp->translate("OpenOrienteering::MainWindow", "Opening %1").arg(temp->getTemplateFilename()) );
738 QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 100 /* ms */);
739 }
740 visibility.visible = true;
741 updateVisibility(temp, visibility);
742 setAreaDirty();
743 if (state != Template::Loaded)
744 {
745 QToolTip::hideText();
746 if (temp->getTemplateState() != Template::Loaded)
747 {
748 QMessageBox::warning(this,
749 qApp->translate("OpenOrienteering::MainWindow", "Error"),
750 qApp->translate("OpenOrienteering::Importer", "Failed to load template '%1', reason: %2")
751 .arg(temp->getTemplateFilename(), temp->errorString()) );
752 }
753 }
754 }
755 updateRow(row);
756 updateButtons();
757 }
758 }
759 break;
760
761 case 1: // Opacity spinbox or slider
762 {
763 auto const opacity = template_table->item(row, column)->data(Qt::DisplayRole).toReal();
764 if (!qFuzzyCompare(1.0+opacity, 1.0+visibility.opacity))
765 {
766 visibility.opacity = qBound(0.0, opacity, 1.0);
767 updateVisibility(temp, visibility);
768 setAreaDirty();
769 template_table->item(row, 1)->setData(Qt::DecorationRole, QColor::fromCmykF(0.0, 0.0, 0.0, visibility.opacity));
770 }
771 }
772 break;
773
774 #ifndef NO_TEMPLATE_GROUP_SUPPORT
775 case 2:
776 {
777 QString text = template_table->item(row, column)->text().trimmed();
778 bool ok = true;
779 int ivalue = text.toInt(&ok);
780
781 if (text.isEmpty())
782 {
783 temp->setTemplateGroup(-1);
784 }
785 else if (!ok)
786 {
787 QMessageBox::warning(window(), tr("Error"), tr("Please enter a valid integer number to set a group or leave the field empty to ungroup the template!"));
788 template_table->item(row, column)->setText(QString::number(temp->getTemplateGroup()));
789 }
790 else
791 temp->setTemplateGroup(ivalue);
792 }
793 #endif
794 default:
795 ; // nothing
796 }
797 }
798 }
799
updateButtons()800 void TemplateListWidget::updateButtons()
801 {
802 bool map_row_selected = false; // does the selection contain the map row?
803 bool first_row_selected = false;
804 bool last_row_selected = false;
805 int num_rows_selected = 0;
806 int visited_row = -1;
807 for (auto&& item : template_table->selectedItems())
808 {
809 const int row = item->row();
810 if (row == visited_row)
811 continue;
812
813 visited_row = row;
814 ++num_rows_selected;
815
816 if (posFromRow(row) < 0)
817 map_row_selected = true;
818
819 if (row == 0)
820 first_row_selected = true;
821 if (row == template_table->rowCount() - 1)
822 last_row_selected = true;
823 }
824 bool single_row_selected = (num_rows_selected == 1);
825
826 auto single_template_selected = single_row_selected && !map_row_selected;
827 duplicate_action->setEnabled(single_template_selected);
828 delete_button->setEnabled(single_template_selected); /// \todo Make it possible to delete multiple templates at once
829 move_up_button->setEnabled(single_row_selected && !first_row_selected);
830 move_down_button->setEnabled(single_row_selected && !last_row_selected);
831
832 if (!mobile_mode)
833 {
834 // Update and show other buttons
835
836 bool is_georeferenced = false;
837 bool edit_enabled = false;
838 bool georef_enabled = false;
839 bool custom_enabled = false;
840 bool import_enabled = false;
841 bool vectorize_enabled = false;
842 if (single_template_selected)
843 {
844 auto temp = map->getTemplate(posFromRow(visited_row));
845 is_georeferenced = temp->isTemplateGeoreferenced();
846 if (template_table->item(visited_row, 0)->checkState() == Qt::Checked)
847 {
848 edit_enabled = true;
849 georef_enabled = temp->canChangeTemplateGeoreferenced();
850 custom_enabled = !is_georeferenced;
851 import_enabled = bool(qobject_cast<TemplateMap*>(getCurrentTemplate()));
852 vectorize_enabled = qobject_cast<TemplateImage*>(getCurrentTemplate())
853 && getCurrentTemplate()->getTemplateState() == Template::Loaded;
854 }
855 }
856 else if (single_row_selected)
857 {
858 Q_ASSERT(map_row_selected);
859 is_georeferenced = map->getGeoreferencing().isValid() && !map->getGeoreferencing().isLocal();
860 }
861
862 edit_button->setEnabled(edit_enabled);
863 georef_action->setChecked(is_georeferenced);
864 georef_action->setEnabled(georef_enabled);
865 move_by_hand_button->setEnabled(custom_enabled);
866 adjust_button->setEnabled(custom_enabled);
867 position_action->setEnabled(custom_enabled);
868 import_action->setEnabled(import_enabled);
869 if (vectorize_action)
870 vectorize_action->setEnabled(vectorize_enabled);
871 }
872 }
873
cellClicked(int row,int column)874 void TemplateListWidget::cellClicked(int row, int column)
875 {
876 auto pos = posFromRow(qMax(0, row));
877
878 switch (column)
879 {
880 case 1:
881 if (mobile_mode
882 && row >= 0
883 && template_table->item(row, 0)->checkState() == Qt::Checked)
884 {
885 showOpacitySlider(row);
886 }
887 break;
888
889 case 3:
890 if (!mobile_mode
891 && row >= 0 && pos >= 0
892 && map->getTemplate(pos)->getTemplateState() == Template::Invalid)
893 {
894 changeTemplateFile(pos);
895 }
896 break;
897
898 default:
899 break;
900 }
901 }
902
cellDoubleClicked(int row,int column)903 void TemplateListWidget::cellDoubleClicked(int row, int column)
904 {
905 auto pos = posFromRow(qMax(0, row));
906
907 switch (column)
908 {
909 default:
910 if (! (row >= 0 && pos >= 0
911 && map->getTemplate(pos)->getTemplateState() == Template::Invalid))
912 break;
913 // Invalid template:
914 Q_FALLTHROUGH();
915 case 3:
916 if (!mobile_mode
917 && row >= 0 && pos >= 0)
918 {
919 changeTemplateFile(pos);
920 }
921 }
922 }
923
moveByHandClicked(bool checked)924 void TemplateListWidget::moveByHandClicked(bool checked)
925 {
926 auto temp = getCurrentTemplate();
927 Q_ASSERT(temp);
928 controller->setTool(checked ? new TemplateMoveTool(temp, controller, move_by_hand_action) : nullptr);
929 }
930
adjustClicked(bool checked)931 void TemplateListWidget::adjustClicked(bool checked)
932 {
933 if (checked)
934 {
935 auto temp = getCurrentTemplate();
936 Q_ASSERT(temp);
937 auto activity = new TemplateAdjustActivity(temp, controller);
938 controller->setEditorActivity(activity);
939 connect(activity->getDockWidget(), &TemplateAdjustDockWidget::closed, this, &TemplateListWidget::adjustWindowClosed);
940 }
941 else
942 {
943 controller->setEditorActivity(nullptr); // TODO: default activity?!
944 }
945 }
946
adjustWindowClosed()947 void TemplateListWidget::adjustWindowClosed()
948 {
949 auto current_template = getCurrentTemplate();
950 if (!current_template)
951 return;
952
953 if (controller->getEditorActivity() && controller->getEditorActivity()->getActivityObject() == current_template)
954 adjust_button->setChecked(false);
955 }
956
957 #ifndef NO_TEMPLATE_GROUP_SUPPORT
groupClicked()958 void TemplateListWidget::groupClicked()
959 {
960 // TODO
961 }
962 #endif
963
positionClicked(bool checked)964 void TemplateListWidget::positionClicked(bool checked)
965 {
966 Q_UNUSED(checked);
967
968 auto temp = getCurrentTemplate();
969 if (!temp)
970 return;
971
972 if (controller->existsTemplatePositionDockWidget(temp))
973 controller->removeTemplatePositionDockWidget(temp);
974 else
975 controller->addTemplatePositionDockWidget(temp);
976 }
977
importClicked()978 void TemplateListWidget::importClicked()
979 {
980 auto prototype = qobject_cast<const TemplateMap*>(getCurrentTemplate());
981 if (!prototype)
982 return;
983
984 TemplateTransform transform;
985 if (!prototype->isTemplateGeoreferenced())
986 prototype->getTransform(transform);
987
988 Map template_map;
989 bool ok = true;
990 if (qstrcmp(prototype->getTemplateType(), "OgrTemplate") == 0)
991 {
992 template_map.importMap(*prototype->templateMap(), Map::MinimalObjectImport);
993 if (!prototype->isTemplateGeoreferenced())
994 {
995 template_map.applyOnAllObjects(transform.makeObjectTransform());
996 template_map.setGeoreferencing(map->getGeoreferencing());
997 }
998 auto template_scale = (transform.template_scale_x + transform.template_scale_y) / 2;
999 template_scale *= double(prototype->templateMap()->getScaleDenominator()) / map->getScaleDenominator();
1000 if (!qFuzzyCompare(template_scale, 1))
1001 {
1002 template_map.scaleAllSymbols(template_scale);
1003 }
1004 }
1005 else if (qstrcmp(prototype->getTemplateType(), "TemplateMap") == 0)
1006 {
1007 auto importer = FileFormats.makeImporter(prototype->getTemplatePath(), template_map, nullptr);
1008 if (!importer)
1009 {
1010 QMessageBox::warning(this, tr("Error"), tr("Cannot load map file, aborting."));
1011 return;
1012 }
1013 if (!importer->doImport())
1014 {
1015 QMessageBox::warning(this, tr("Error"), importer->warnings().back());
1016 return;
1017 }
1018 if (!importer->warnings().empty())
1019 {
1020 MainWindow::showMessageBox(this, tr("Warning"), tr("The map import generated warnings."), importer->warnings());
1021 }
1022
1023 if (!prototype->isTemplateGeoreferenced())
1024 template_map.applyOnAllObjects(transform.makeObjectTransform());
1025
1026 auto nominal_scale = double(template_map.getScaleDenominator()) / map->getScaleDenominator();
1027 auto current_scale = 0.5 * (transform.template_scale_x + transform.template_scale_y);
1028 auto scale = 1.0;
1029 QStringList scale_options;
1030 if (qAbs(nominal_scale - 1.0) > 0.009)
1031 scale_options.append(tr("Scale by nominal map scale ratio (%1 %)").arg(locale().toString(nominal_scale * 100.0, 'f', 1)));
1032 if (qAbs(current_scale - 1.0) > 0.009 && qAbs(current_scale - nominal_scale) > 0.009)
1033 scale_options.append(tr("Scale by current template scaling (%1 %)").arg(locale().toString(current_scale * 100.0, 'f', 1)));
1034 if (!scale_options.isEmpty())
1035 {
1036 scale_options.prepend(tr("Don't scale"));
1037 QString option = QInputDialog::getItem( window(),
1038 tr("Template import"),
1039 tr("How shall the symbols of the imported template map be scaled?"),
1040 scale_options, 0, false, &ok );
1041 if (option.isEmpty())
1042 return;
1043 else if (option == scale_options[0])
1044 Q_ASSERT(scale == 1.0);
1045 else if (option == scale_options[1])
1046 scale = nominal_scale;
1047 else // This option may have been omitted.
1048 scale = current_scale;
1049 }
1050
1051 if (ok && scale != 1.0)
1052 template_map.scaleAllSymbols(scale);
1053 }
1054 else
1055 {
1056 QMessageBox::warning(this, tr("Error"), tr("Cannot load map file, aborting."));
1057 return;
1058 }
1059
1060 map->importMap(template_map, Map::MinimalObjectImport);
1061 deleteTemplate();
1062
1063 if (main_view->isOverprintingSimulationEnabled()
1064 && !template_map.hasSpotColors())
1065 {
1066 auto answer = QMessageBox::question(
1067 window(),
1068 tr("Template import"),
1069 tr("The template will be invisible in the overprinting simulation. "
1070 "Switch to normal view?"),
1071 QMessageBox::StandardButtons(QMessageBox::Yes | QMessageBox::No),
1072 QMessageBox::Yes );
1073 if (answer == QMessageBox::Yes)
1074 {
1075 if (auto action = controller->getAction("overprintsimulation"))
1076 action->trigger();
1077 }
1078 }
1079
1080
1081 auto map_visibility = main_view->getMapVisibility();
1082 if (!map_visibility.visible)
1083 {
1084 map_visibility.visible = true;
1085 updateRow(map->getNumTemplates() - map->getFirstFrontTemplate());
1086 }
1087 }
1088
changeGeorefClicked()1089 void TemplateListWidget::changeGeorefClicked()
1090 {
1091 auto* templ = getCurrentTemplate();
1092 if (templ && templ->canChangeTemplateGeoreferenced())
1093 {
1094 auto new_value = !templ->isTemplateGeoreferenced();
1095 if (new_value)
1096 {
1097 // Properly tear down positioning activities
1098 if (move_by_hand_action->isChecked())
1099 move_by_hand_action->trigger();
1100 if (adjust_button->isChecked())
1101 adjust_button->click();
1102 if (position_action->isChecked())
1103 position_action->trigger();
1104 }
1105 if (!templ->trySetTemplateGeoreferenced(new_value, this))
1106 {
1107 QMessageBox::warning(this, tr("Error"), tr("Cannot change the georeferencing state."));
1108 georef_action->setChecked(templ->isTemplateGeoreferenced());
1109 }
1110 updateButtons();
1111 }
1112 }
1113
vectorizeClicked()1114 void TemplateListWidget::vectorizeClicked()
1115 {
1116 #ifdef WITH_COVE
1117 cove::CoveRunner cr;
1118 auto templ = qobject_cast<TemplateImage*>(getCurrentTemplate());
1119 cr.run(controller->getWindow(), map, templ);
1120 #endif /* WITH_COVE */
1121 }
1122
moreActionClicked(QAction * action)1123 void TemplateListWidget::moreActionClicked(QAction* action)
1124 {
1125 Q_UNUSED(action);
1126 // TODO
1127 }
1128
templateAdded(int pos,const Template * temp)1129 void TemplateListWidget::templateAdded(int pos, const Template* temp)
1130 {
1131 Q_UNUSED(temp);
1132 int row = rowFromPos(pos);
1133 template_table->insertRow(row);
1134 addRowItems(row);
1135 template_table->setCurrentCell(row, 0);
1136 }
1137
templatePositionDockWidgetClosed(Template * temp)1138 void TemplateListWidget::templatePositionDockWidgetClosed(Template* temp)
1139 {
1140 auto current_temp = getCurrentTemplate();
1141 if (current_temp == temp)
1142 position_action->setChecked(false);
1143 }
1144
updateVisibility(MapView::VisibilityFeature feature,bool active,const Template * temp)1145 void TemplateListWidget::updateVisibility(MapView::VisibilityFeature feature, bool active, const Template* temp)
1146 {
1147 switch (feature)
1148 {
1149 case MapView::AllTemplatesHidden:
1150 setAllTemplatesHidden(active);
1151 break;
1152
1153 case MapView::MapVisible:
1154 updateRow(map->getFirstFrontTemplate());
1155 break;
1156
1157 case MapView::TemplateVisible:
1158 if (map->getNumTemplates() == template_table->rowCount()+1)
1159 {
1160 auto row = map->findTemplateIndex(temp);
1161 if (row >= 0)
1162 updateRow(posFromRow(row));
1163 break;
1164 }
1165 Q_FALLTHROUGH();
1166 case MapView::MultipleFeatures:
1167 updateAll();
1168 break;
1169
1170 default:
1171 ; // nothing
1172 }
1173 }
1174
updateAll()1175 void TemplateListWidget::updateAll()
1176 {
1177 auto templates_hidden = main_view->areAllTemplatesHidden();
1178 template_table->setEnabled(!templates_hidden); // Color scheme depends on state
1179
1180 auto old_size = template_table->rowCount();
1181 template_table->setRowCount(map->getNumTemplates() + 1);
1182 for (auto i = 0; i < old_size; ++i)
1183 updateRow(i);
1184 for (auto i = old_size; i < template_table->rowCount(); ++i)
1185 addRowItems(i);
1186
1187 setAllTemplatesHidden(templates_hidden); // implicit updateButtons
1188 }
1189
addRowItems(int row)1190 void TemplateListWidget::addRowItems(int row)
1191 {
1192 QSignalBlocker block(template_table);
1193
1194 for (int i = 0; i < 4; ++i)
1195 {
1196 auto item = new QTableWidgetItem();
1197 template_table->setItem(row, i, item);
1198 }
1199 template_table->item(row, 0)->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled /* | Qt::ItemIsSelectable*/);
1200 template_table->item(row, 1)->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
1201 template_table->item(row, 2)->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
1202
1203 updateRow(row);
1204 }
1205
updateRow(int row)1206 void TemplateListWidget::updateRow(int row)
1207 {
1208 int pos = posFromRow(row);
1209 //int group = -1;
1210 QString name;
1211 QString path;
1212 bool valid = true;
1213
1214 TemplateVisibility vis;
1215
1216 QPalette::ColorGroup color_group = template_table->isEnabled() ? QPalette::Active : QPalette::Disabled;
1217 #ifdef Q_OS_ANDROID
1218 auto background_color = QPalette().color(color_group, QPalette::Background);
1219 #else
1220 auto background_color = QPalette().color(color_group, QPalette::Base);
1221 #endif
1222
1223 if (pos >= 0)
1224 {
1225 auto temp = map->getTemplate(pos);
1226 //group = temp->getTemplateGroup();
1227 name = temp->getTemplateFilename();
1228 path = temp->getTemplatePath();
1229 valid = temp->getTemplateState() != Template::Invalid;
1230 /// @todo Get visibility values from the MapView of the active MapWidget (instead of always main_view)
1231 vis = main_view->getTemplateVisibility(temp);
1232 }
1233 else
1234 {
1235 name = tr("- Map -");
1236 vis = main_view->getMapVisibility();
1237 #ifdef Q_OS_ANDROID
1238 auto r = (128 + 5 * background_color.red()) / 6;
1239 auto g = (128 + 5 * background_color.green()) / 6;
1240 auto b = (128 + 5 * background_color.blue()) / 6;
1241 background_color = QColor(r, g, b);
1242 #else
1243 background_color = QPalette().color(color_group, QPalette::AlternateBase);
1244 #endif
1245 }
1246
1247 // Cheep defaults, mostly for !vis.visible
1248 auto check_state = Qt::Unchecked;
1249 auto opacity_color = QColor{ Qt::transparent };
1250 auto text_color = QColor::fromRgb(255, 51, 51);
1251 auto decoration = QVariant{ };
1252 auto checkable = Qt::ItemIsUserCheckable;
1253 auto editable = Qt::NoItemFlags;
1254 //auto group_editable = Qt::NoItemFlags;
1255
1256 if (valid)
1257 {
1258 if (vis.visible)
1259 {
1260 check_state = Qt::Checked;
1261 opacity_color = QColor::fromCmykF(0.0, 0.0, 0.0, vis.opacity);
1262 text_color = QPalette().color(color_group, QPalette::Foreground);
1263 if (!mobile_mode)
1264 {
1265 editable = Qt::ItemIsEditable;
1266 if (pos >= 0)
1267 {
1268 //group_editable = Qt::ItemIsEditable;
1269 }
1270 }
1271 }
1272 else
1273 {
1274 text_color = QPalette().color(QPalette::Disabled, QPalette::Foreground);
1275 }
1276 decoration = QVariant{ opacity_color };
1277 }
1278 else
1279 {
1280 if (vis.visible)
1281 {
1282 check_state = Qt::PartiallyChecked;
1283 text_color = text_color.darker();
1284 }
1285 decoration = QIcon::fromTheme(QLatin1String("image-missing"), QIcon{QLatin1String(":/images/close.png")});
1286 checkable = Qt::NoItemFlags;
1287 editable = Qt::NoItemFlags;
1288 }
1289
1290 auto foreground = QBrush(text_color);
1291 auto background = QBrush(background_color);
1292
1293 QSignalBlocker block(template_table);
1294 {
1295 auto item0 = template_table->item(row, 0);
1296 item0->setBackground(background);
1297 item0->setCheckState(check_state);
1298 item0->setFlags(checkable | Qt::ItemIsEnabled);
1299 #ifdef Q_OS_ANDROID
1300 // Some combinations not working well in Android style
1301 if (!valid)
1302 {
1303 // item0->setData(Qt::CheckStateRole, {});
1304 // item0->setFlags(enabled);
1305 }
1306 #endif
1307 }
1308 {
1309 auto item1 = template_table->item(row, 1);
1310 item1->setBackground(background);
1311 item1->setForeground(foreground);
1312 item1->setData(Qt::DisplayRole, vis.opacity);
1313 item1->setData(Qt::DecorationRole, decoration);
1314 item1->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | editable);
1315 }
1316 #ifndef NO_TEMPLATE_GROUP_SUPPORT
1317 {
1318 auto item2 = template_table->item(row, 2);
1319 item->setBackground(background);
1320 item->setForeground(foreground);
1321 item->setText((group < 0) ? "" : QString::number(group));
1322 item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | groupable);
1323 }
1324 #endif
1325 {
1326 auto name_item = template_table->item(row, name_column);
1327 name_item->setBackground(background);
1328 name_item->setForeground(foreground);
1329 name_item->setText(name);
1330 name_item->setData(Qt::ToolTipRole, path);
1331 name_item->setData(Qt::DecorationRole, {});
1332 auto prev_checkable = name_item->flags() & Qt::ItemIsUserCheckable;
1333 name_item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | prev_checkable);
1334 }
1335 }
1336
posFromRow(int row)1337 int TemplateListWidget::posFromRow(int row)
1338 {
1339 int pos = template_table->rowCount() - 1 - row;
1340
1341 if (pos == map->getFirstFrontTemplate())
1342 pos = -1; // the map row
1343 else if (pos > map->getFirstFrontTemplate())
1344 pos = pos - 1; // before the map row
1345
1346 return pos;
1347 }
1348
rowFromPos(int pos)1349 int TemplateListWidget::rowFromPos(int pos)
1350 {
1351 Q_ASSERT(pos >= 0);
1352 return map->getNumTemplates() - 1 - ((pos >= map->getFirstFrontTemplate()) ? pos : (pos - 1));
1353 }
1354
getCurrentTemplate()1355 Template* TemplateListWidget::getCurrentTemplate()
1356 {
1357 int current_row = template_table->currentRow();
1358 if (current_row < 0)
1359 return nullptr;
1360 int pos = posFromRow(current_row);
1361 if (pos < 0)
1362 return nullptr;
1363 return map->getTemplate(pos);
1364 }
1365
changeTemplateFile(int pos)1366 void TemplateListWidget::changeTemplateFile(int pos)
1367 {
1368 auto temp = map->getTemplate(pos);
1369 Q_ASSERT(temp);
1370 temp->execSwitchTemplateFileDialog(this);
1371 updateRow(rowFromPos(pos));
1372 updateButtons();
1373 temp->setTemplateAreaDirty();
1374 map->setTemplatesDirty();
1375 }
1376
showOpacitySlider(int row)1377 void TemplateListWidget::showOpacitySlider(int row)
1378 {
1379 auto geometry = template_table->visualItemRect(template_table->item(row, name_column));
1380 geometry.translate(0, geometry.height());
1381
1382 QDialog dialog(this);
1383 dialog.move(template_table->viewport()->mapToGlobal(geometry.topLeft()));
1384
1385 auto slider = new QSlider();
1386 slider->setOrientation(Qt::Horizontal);
1387 slider->setRange(0, 20);
1388 slider->setMinimumWidth(geometry.width());
1389
1390 auto opacity_item = template_table->item(row, 1);
1391 slider->setValue(qRound(opacity_item->data(Qt::DisplayRole).toFloat() * 20));
1392 connect(slider, &QSlider::valueChanged, [opacity_item](int value) {
1393 opacity_item->setData(Qt::DisplayRole, 0.05f * value);
1394 } );
1395
1396 auto close_button = new QToolButton();
1397 close_button->setIcon(style()->standardIcon(QStyle::SP_DialogApplyButton, nullptr, this));
1398 close_button->setAutoRaise(true);
1399 connect(close_button, &QToolButton::clicked, &dialog, &QDialog::accept);
1400
1401 auto layout = new QHBoxLayout(&dialog);
1402 layout->addWidget(slider);
1403 layout->addWidget(close_button);
1404
1405 dialog.exec();
1406 }
1407
1408
1409 } // namespace OpenOrienteering
1410