1 /**********************************************************************************************
2 Copyright (C) 2014 Oliver Eichler <oliver.eichler@gmx.de>
3 Copyright (C) 2017 Norbert Truchsess <norbert.truchsess@t-online.de>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 **********************************************************************************************/
19
20 #include "canvas/CCanvas.h"
21 #include "canvas/CCanvasSelect.h"
22 #include "CMainWindow.h"
23 #include "device/IDevice.h"
24 #include "gis/CGisDatabase.h"
25 #include "gis/CGisDraw.h"
26 #include "gis/CGisItemRate.h"
27 #include "gis/CGisWorkspace.h"
28 #include "gis/db/CDBProject.h"
29 #include "gis/db/CSelectDBFolder.h"
30 #include "gis/db/CSetupFolder.h"
31 #include "gis/gpx/CGpxProject.h"
32 #include "gis/IGisItem.h"
33 #include "gis/ovl/CGisItemOvlArea.h"
34 #include "gis/prj/IGisProject.h"
35 #include "gis/Poi.h"
36 #include "gis/qms/CQmsProject.h"
37 #include "gis/rte/CCreateRouteFromWpt.h"
38 #include "gis/rte/CGisItemRte.h"
39 #include "gis/rte/router/IRouter.h"
40 #include "gis/search/CGeoSearchWeb.h"
41 #include "gis/search/CSearch.h"
42 #include "gis/search/CSearchExplanationDialog.h"
43 #include "gis/trk/CCombineTrk.h"
44 #include "gis/trk/CGisItemTrk.h"
45 #include "gis/wpt/CGisItemWpt.h"
46 #include "gis/wpt/CProjWpt.h"
47 #include "helpers/CInputDialog.h"
48 #include "helpers/CProgressDialog.h"
49 #include "helpers/CSelectCopyAction.h"
50 #include "helpers/CSelectProjectDialog.h"
51 #include "helpers/CSettings.h"
52
53 #include <QtWidgets>
54 #include <QtXml>
55
56 CGisWorkspace* CGisWorkspace::pSelf = nullptr;
57
CGisWorkspace(QMenu * menuProject,QWidget * parent)58 CGisWorkspace::CGisWorkspace(QMenu* menuProject, QWidget* parent)
59 : QWidget(parent), currentSearch("")
60 {
61 pSelf = this;
62 setupUi(this);
63
64 treeWks->setExternalMenu(menuProject);
65
66 SETTINGS;
67 treeWks->header()->restoreState(cfg.value("Workspace/treeWks/state", treeWks->header()->saveState()).toByteArray());
68
69 tags_hidden_e tagsHidden = (tags_hidden_e)cfg.value("Workspace/treeWks/tagsHidden", eTagsHiddenUnknown).toInt();
70 // Limit the column width to half the table width to avoid the name column to be pushed out the window.
71 if(tagsHidden == eTagsHiddenUnknown || treeWks->columnWidth(CGisListWks::eColumnRating) > 0.4 * this->width())
72 {
73 treeWks->setColumnWidth(CGisListWks::eColumnRating, 0.2 * this->width());
74 }
75
76 //Only show tags if they are explcitely not hidden
77 setTagsHidden(tagsHidden != eTagsHiddenFalse);
78
79
80 CSearch::setSearchMode(CSearch::search_mode_e(cfg.value("Workspace/projects/filterMode", CSearch::getSearchMode()).toInt()));
81 CSearch::setCaseSensitivity(Qt::CaseSensitivity(cfg.value("Workspace/projects/CaseSensitivity", CSearch::getCaseSensitivity()).toInt()));
82
83 connect(treeWks, &CGisListWks::sigChanged, this, &CGisWorkspace::sigChanged);
84 connect(sliderOpacity, &QSlider::valueChanged, this, &CGisWorkspace::slotSetGisLayerOpacity);
85 connect(lineFilter, &CSearchLineEdit::sigWorkspaceSearchChanged, this, &CGisWorkspace::slotSearch);
86 connect(treeWks, &CGisListWks::itemPressed, this, &CGisWorkspace::slotWksItemPressed);
87 connect(treeWks, &CGisListWks::itemSelectionChanged, this, &CGisWorkspace::slotWksItemSelectionChanged);
88 connect(treeWks, &CGisListWks::sigItemDeleted, this, &CGisWorkspace::slotWksItemSelectionChanged);
89 }
90
~CGisWorkspace()91 CGisWorkspace::~CGisWorkspace()
92 {
93 SETTINGS;
94 //To ensure backwards compatibility first it is saved wether the tags are hidden,
95 //then the column is made visible and then the header is saved. This way the name column is in no case hidden.
96 cfg.setValue("Workspace/treeWks/tagsHidden", areTagsHidden() ? eTagsHiddenTrue : eTagsHiddenFalse);
97 setTagsHidden(false);
98 cfg.setValue("Workspace/treeWks/state", treeWks->header()->saveState());
99
100 cfg.setValue("Workspace/projects/filterMode", CSearch::getSearchMode());
101 cfg.setValue("Workspace/projects/CaseSensitivity", CSearch::getCaseSensitivity());
102 /*
103 Explicitly delete workspace here, as database projects use
104 CGisWorkspace upon destruction to signal the database their destruction.
105
106 */
107 delete treeWks;
108 }
109
slotLateInit()110 void CGisWorkspace::slotLateInit()
111 {
112 // [Issue #265] Delay the loading of the workspace to make sure the complete IUnit system
113 // is up and running.
114 QTimer::singleShot(1000, treeWks, &CGisListWks::slotLoadWorkspace);
115 }
116
setOpacity(qreal val)117 void CGisWorkspace::setOpacity(qreal val)
118 {
119 sliderOpacity->setValue(val * 100);
120 }
121
postEventForWks(QEvent * event)122 void CGisWorkspace::postEventForWks(QEvent* event)
123 {
124 QCoreApplication::postEvent(treeWks, event);
125 }
126
loadGisProject(const QString & filename)127 void CGisWorkspace::loadGisProject(const QString& filename)
128 {
129 // add project to workspace
130 {
131 CCanvasCursorLock cursorLock(Qt::WaitCursor, __func__);
132 treeWks->blockSignals(true);
133
134 QMutexLocker lock(&IGisItem::mutexItems);
135
136 IGisProject* item = IGisProject::create(filename, treeWks);
137 // skip if project is already loaded
138 if(item && treeWks->hasProject(item))
139 {
140 QMessageBox::information(this, tr("Load project..."), tr("The project \"%1\" is already in the workspace.").arg(item->getName()), QMessageBox::Abort);
141
142 delete item;
143 item = nullptr;
144 }
145
146 treeWks->blockSignals(false);
147
148 if(item != nullptr)
149 {
150 item->setWorkspaceFilter(currentSearch);
151 }
152 }
153
154 emit sigChanged();
155 }
156
157
slotSetGisLayerOpacity(int val)158 void CGisWorkspace::slotSetGisLayerOpacity(int val)
159 {
160 CCanvas::gisLayerOpacity = qreal(val) / 100;
161 CCanvas* canvas = CMainWindow::self().getVisibleCanvas();
162 if(canvas != nullptr)
163 {
164 canvas->update();
165 }
166 }
167
slotSearch(const CSearch & currentSearch)168 void CGisWorkspace::slotSearch(const CSearch& currentSearch)
169 {
170 this->currentSearch = currentSearch;
171 {
172 CCanvasCursorLock cursorLock(Qt::WaitCursor, __func__);
173 QMutexLocker lock(&IGisItem::mutexItems);
174
175 const int N = treeWks->topLevelItemCount();
176 for(int n = 0; n < N; n++)
177 {
178 IGisProject* item = dynamic_cast<IGisProject*>(treeWks->topLevelItem(n));
179 if(item == nullptr)
180 {
181 continue;
182 }
183
184 item->setWorkspaceFilter(currentSearch);
185 item->setExpanded(!lineFilter->text().isEmpty());
186 }
187 }
188 CCanvas::triggerCompleteUpdate(CCanvas::eRedrawGis);
189 }
190
slotSaveAll()191 void CGisWorkspace::slotSaveAll()
192 {
193 CCanvasCursorLock cursorLock(Qt::WaitCursor, __func__);
194 QMutexLocker lock(&IGisItem::mutexItems);
195 for(int i = 0; i < treeWks->topLevelItemCount(); i++)
196 {
197 IGisProject* item = dynamic_cast<IGisProject*>(treeWks->topLevelItem(i));
198 if(nullptr == item)
199 {
200 continue;
201 }
202
203 if(item->skipSave())
204 {
205 continue;
206 }
207
208 if(item->canSave())
209 {
210 item->save();
211 }
212 else
213 {
214 item->saveAs();
215 }
216 }
217 }
218
219
slotWksItemSelectionChanged()220 void CGisWorkspace::slotWksItemSelectionChanged()
221 {
222 slotWksItemPressed(treeWks->currentItem());
223 }
224
slotWksItemPressed(QTreeWidgetItem * i)225 void CGisWorkspace::slotWksItemPressed(QTreeWidgetItem* i)
226 {
227 IGisItem* item = dynamic_cast<IGisItem*>(i);
228 if(item != nullptr)
229 {
230 IGisProject* project = item->getParentProject();
231 if (project != nullptr && project->isVisible())
232 {
233 keyWksSelection = item->getKey();
234 const QList<CCanvas*>& allCanvas = CMainWindow::self().getCanvas();
235 for(CCanvas* canvas : allCanvas)
236 {
237 canvas->reportStatus("WksSelection", tr("<b>Item Selection: </b>Item selected from workspace list. Click on the map to switch back to normal mouse selection behavior."));
238 canvas->abortMouse();
239 }
240 }
241 }
242 else
243 {
244 slotWksItemSelectionReset();
245 }
246 }
247
slotWksItemSelectionReset()248 void CGisWorkspace::slotWksItemSelectionReset()
249 {
250 keyWksSelection.clear();
251 const QList<CCanvas*>& allCanvas = CMainWindow::self().getCanvas();
252 for(CCanvas* canvas : allCanvas)
253 {
254 canvas->reportStatus("WksSelection", "");
255 canvas->abortMouse();
256 }
257 }
258
slotActivityTrkByKey(const QList<IGisItem::key_t> & keys,trkact_t act)259 void CGisWorkspace::slotActivityTrkByKey(const QList<IGisItem::key_t>& keys, trkact_t act)
260 {
261 if(keys.isEmpty())
262 {
263 return;
264 }
265
266 if(CTrackData::trkpt_t::eAct20Bad != act)
267 {
268 QMutexLocker lock(&IGisItem::mutexItems);
269
270 QSet<IGisProject*> projects;
271 for(const IGisItem::key_t& key : keys)
272 {
273 CGisItemTrk* trk = dynamic_cast<CGisItemTrk*>(getItemByKey(key));
274 if(trk == nullptr)
275 {
276 continue;
277 }
278
279 IGisProject* project = trk->getParentProject();
280 if(!projects.contains(project))
281 {
282 project->blockUpdateItems(true);
283 projects << project;
284 }
285
286 if(trk->isRangeSelected())
287 {
288 trk->setActivityRange(act);
289 }
290 else
291 {
292 trk->setActivity(act);
293 }
294 }
295
296 for(IGisProject* project : qAsConst(projects))
297 {
298 project->blockUpdateItems(false);
299 }
300 }
301 }
302
selectProject(bool forceSelect)303 IGisProject* CGisWorkspace::selectProject(bool forceSelect)
304 {
305 QString key = IGisProject::getUserFocus();
306 QString name;
307 IGisProject::type_e type = IGisProject::eTypeQms;
308
309 if(key.isEmpty() || forceSelect)
310 {
311 CSelectProjectDialog dlg(key, name, type, treeWks);
312 if(dlg.exec() == QDialog::Rejected)
313 {
314 return nullptr;
315 }
316 }
317
318 IGisProject* project = nullptr;
319 if(!key.isEmpty())
320 {
321 QMutexLocker lock(&IGisItem::mutexItems);
322 for(int i = 0; i < treeWks->topLevelItemCount(); i++)
323 {
324 project = dynamic_cast<IGisProject*>(treeWks->topLevelItem(i));
325 if(nullptr == project)
326 {
327 continue;
328 }
329 if(key == project->getKey())
330 {
331 break;
332 }
333 }
334 }
335 else if(type == IGisProject::eTypeDb)
336 {
337 QList<quint64> ids;
338 QString db;
339 QString host;
340 IDBFolder::type_e type;
341
342 CSelectDBFolder dlg1(ids, db, host, this);
343 if((dlg1.exec() == QDialog::Rejected) || ids.isEmpty())
344 {
345 return nullptr;
346 }
347
348 CSetupFolder dlg2(type, name, false, this);
349 if(dlg2.exec() == QDialog::Rejected)
350 {
351 return nullptr;
352 }
353
354 QMutexLocker lock(&IGisItem::mutexItems);
355 CEvtW2DCreate evt(name, type, ids[0], db, host);
356 CGisDatabase::self().sendEventForDb(&evt);
357
358 if(evt.idChild)
359 {
360 CDBProject* p = nullptr;
361 while(nullptr == p)
362 {
363 QApplication::processEvents(QEventLoop::WaitForMoreEvents | QEventLoop::ExcludeUserInputEvents, 100);
364 p = dynamic_cast<CDBProject*>(treeWks->getProjectById(evt.idChild, db));
365 }
366 /*
367 Creating a project usually does initiate an info request. However as the project isn't in the workspace
368 the moment we create it, the request will fail. That is why we send the info now.
369 */
370 p->postStatus(false);
371 project = p;
372 }
373 }
374 else if(!name.isEmpty())
375 {
376 QMutexLocker lock(&IGisItem::mutexItems);
377 if(type == IGisProject::eTypeGpx)
378 {
379 project = new CGpxProject(name, treeWks);
380 }
381 else if (type == IGisProject::eTypeQms)
382 {
383 project = new CQmsProject(name, treeWks);
384 }
385 }
386
387 return project;
388 }
389
getItemsByPos(const QPointF & pos,QList<IGisItem * > & items)390 void CGisWorkspace::getItemsByPos(const QPointF& pos, QList<IGisItem*>& items)
391 {
392 QMutexLocker lock(&IGisItem::mutexItems);
393
394 for(int i = 0; i < treeWks->topLevelItemCount(); i++)
395 {
396 QTreeWidgetItem* item = treeWks->topLevelItem(i);
397 IGisProject* project = dynamic_cast<IGisProject*>(item);
398 if(project)
399 {
400 project->getItemsByPos(pos, items);
401 continue;
402 }
403 IDevice* device = dynamic_cast<IDevice*>(item);
404 if(device)
405 {
406 device->getItemsByPos(pos, items);
407 continue;
408 }
409 }
410
411 /*
412 If there is an item selected by the workspace limit
413 the list of items to this item. But only if the item
414 is part of the items close to position.
415 */
416 if(!keyWksSelection.item.isEmpty() && !items.isEmpty())
417 {
418 IGisItem* item = getItemByKey(keyWksSelection);
419 if(item && items.contains(item))
420 {
421 items.clear();
422 items << item;
423 }
424 else
425 {
426 items.clear();
427 }
428 }
429 }
430
getItemsByKeys(const QList<IGisItem::key_t> & keys,QList<IGisItem * > & items)431 void CGisWorkspace::getItemsByKeys(const QList<IGisItem::key_t>& keys, QList<IGisItem*>& items)
432 {
433 QMutexLocker lock(&IGisItem::mutexItems);
434 for(int i = 0; i < treeWks->topLevelItemCount(); i++)
435 {
436 QTreeWidgetItem* item = treeWks->topLevelItem(i);
437 IGisProject* project = dynamic_cast<IGisProject*>(item);
438 if(project)
439 {
440 project->getItemsByKeys(keys, items);
441 continue;
442 }
443 IDevice* device = dynamic_cast<IDevice*>(item);
444 if(device)
445 {
446 device->getItemsByKeys(keys, items);
447 continue;
448 }
449 }
450 }
451
getItemsByArea(const QRectF & area,IGisItem::selflags_t flags,QList<IGisItem * > & items)452 void CGisWorkspace::getItemsByArea(const QRectF& area, IGisItem::selflags_t flags, QList<IGisItem*>& items)
453 {
454 QMutexLocker lock(&IGisItem::mutexItems);
455 for(int i = 0; i < treeWks->topLevelItemCount(); i++)
456 {
457 QTreeWidgetItem* item = treeWks->topLevelItem(i);
458 IGisProject* project = dynamic_cast<IGisProject*>(item);
459 if(project)
460 {
461 project->getItemsByArea(area, flags, items);
462 continue;
463 }
464 IDevice* device = dynamic_cast<IDevice*>(item);
465 if(device)
466 {
467 device->getItemsByArea(area, flags, items);
468 continue;
469 }
470 }
471 }
472
getNogoAreas(QList<IGisItem * > & nogos)473 void CGisWorkspace::getNogoAreas(QList<IGisItem*>& nogos)
474 {
475 QMutexLocker lock(&IGisItem::mutexItems);
476 for(int i = 0; i < treeWks->topLevelItemCount(); i++)
477 {
478 QTreeWidgetItem* item = treeWks->topLevelItem(i);
479 IGisProject* project = dynamic_cast<IGisProject*>(item);
480 if(project)
481 {
482 project->getNogoAreas(nogos);
483 continue;
484 }
485 IDevice* device = dynamic_cast<IDevice*>(item);
486 if(device)
487 {
488 device->getNogoAreas(nogos);
489 continue;
490 }
491 }
492 }
493
mouseMove(const QPointF & pos)494 void CGisWorkspace::mouseMove(const QPointF& pos)
495 {
496 QMutexLocker lock(&IGisItem::mutexItems);
497 for(int i = 0; i < treeWks->topLevelItemCount(); i++)
498 {
499 QTreeWidgetItem* item = treeWks->topLevelItem(i);
500 IGisProject* project = dynamic_cast<IGisProject*>(item);
501 if(project)
502 {
503 project->mouseMove(pos);
504 continue;
505 }
506 }
507 }
508
getItemByKey(const IGisItem::key_t & key)509 IGisItem* CGisWorkspace::getItemByKey(const IGisItem::key_t& key)
510 {
511 IGisItem* item = nullptr;
512 QMutexLocker lock(&IGisItem::mutexItems);
513 for(int i = 0; i < treeWks->topLevelItemCount(); i++)
514 {
515 QTreeWidgetItem* item1 = treeWks->topLevelItem(i);
516 IGisProject* project = dynamic_cast<IGisProject*>(item1);
517 if(project)
518 {
519 if(project->getKey() != key.project)
520 {
521 continue;
522 }
523
524 item = project->getItemByKey(key);
525 if(nullptr != item)
526 {
527 break;
528 }
529
530 continue;
531 }
532
533 IDevice* device = dynamic_cast<IDevice*>(item1);
534 if(device)
535 {
536 if(device->getKey() != key.device)
537 {
538 continue;
539 }
540
541 item = device->getItemByKey(key);
542 if(nullptr != item)
543 {
544 break;
545 }
546 }
547 }
548
549 return item;
550 }
551
delItemByKey(const IGisItem::key_t & key)552 void CGisWorkspace::delItemByKey(const IGisItem::key_t& key)
553 {
554 QMutexLocker lock(&IGisItem::mutexItems);
555 QMessageBox::StandardButtons last = QMessageBox::NoButton;
556 for(int i = 0; i < treeWks->topLevelItemCount(); i++)
557 {
558 IGisProject* project = dynamic_cast<IGisProject*>(treeWks->topLevelItem(i));
559 if(nullptr == project)
560 {
561 continue;
562 }
563
564 if(project->delItemByKey(key, last))
565 {
566 // update database tree if that is a database project
567 CDBProject* dbp = dynamic_cast<CDBProject*>(project);
568 if(dbp)
569 {
570 dbp->postStatus(true);
571 }
572 }
573
574 if(last == QMessageBox::Cancel)
575 {
576 break;
577 }
578 }
579
580
581 emit sigChanged();
582 }
583
delItemsByKey(const QList<IGisItem::key_t> & keys)584 void CGisWorkspace::delItemsByKey(const QList<IGisItem::key_t>& keys)
585 {
586 QMessageBox::StandardButtons last = QMessageBox::NoButton;
587
588 QSet<CDBProject*> projects;
589 QSet<IGisProject*> projectsAll;
590
591 for(const IGisItem::key_t& key : keys)
592 {
593 IGisItem* gisItem = getItemByKey(key);
594 if(nullptr != gisItem)
595 {
596 bool yes = false;
597 IGisProject* project = dynamic_cast<IGisProject*>(gisItem->parent());
598 if(nullptr != project)
599 {
600 project->blockUpdateItems(true);
601 yes = project->delItemByKey(gisItem->getKey(), last);
602
603
604 /*
605 collect database projects to update their counterpart in
606 the database view, after all operations are done.
607 */
608 if(yes && project->getType() == IGisProject::eTypeDb)
609 {
610 projects << dynamic_cast<CDBProject*>(project);
611 }
612
613 /*
614 Collect all projects to unblock update later on.
615 */
616 projectsAll << project;
617 }
618
619 if(last == QMessageBox::Cancel)
620 {
621 break;
622 }
623 }
624 }
625
626 // make all database projects that are changed to post their new status
627 // this will update the database view.
628 for(CDBProject* project : qAsConst(projects))
629 {
630 project->postStatus(true);
631 }
632 // unblock update for all projects seen
633 for(IGisProject* project : qAsConst(projectsAll))
634 {
635 project->blockUpdateItems(false);
636 }
637
638 CCanvas::triggerCompleteUpdate(CCanvas::eRedrawGis);
639 }
640
editItemByKey(const IGisItem::key_t & key)641 void CGisWorkspace::editItemByKey(const IGisItem::key_t& key)
642 {
643 QMutexLocker lock(&IGisItem::mutexItems);
644 for(int i = 0; i < treeWks->topLevelItemCount(); i++)
645 {
646 QTreeWidgetItem* item = treeWks->topLevelItem(i);
647 IGisProject* project = dynamic_cast<IGisProject*>(item);
648 if(nullptr != project)
649 {
650 project->editItemByKey(key);
651 continue;
652 }
653 IDevice* device = dynamic_cast<IDevice*>(item);
654 if(nullptr != device)
655 {
656 device->editItemByKey(key);
657 continue;
658 }
659 }
660
661 emit sigChanged();
662 }
663
copyItemByKey(const IGisItem::key_t & key)664 void CGisWorkspace::copyItemByKey(const IGisItem::key_t& key)
665 {
666 QMutexLocker lock(&IGisItem::mutexItems);
667
668 IGisItem* item = getItemByKey(key);
669 if(nullptr == item)
670 {
671 return;
672 }
673
674 IGisProject* project = selectProject(true);
675 if(nullptr == project)
676 {
677 return;
678 }
679 CSelectCopyAction::result_e actionForAll = CSelectCopyAction::eResultNone;
680 project->insertCopyOfItem(item, NOIDX, actionForAll);
681
682
683 emit sigChanged();
684 }
685
copyItemsByKey(const QList<IGisItem::key_t> & keys)686 IGisProject* CGisWorkspace::copyItemsByKey(const QList<IGisItem::key_t>& keys)
687 {
688 QMutexLocker lock(&IGisItem::mutexItems);
689
690 IGisProject* project = selectProject(true);
691 if(nullptr == project)
692 {
693 return nullptr;
694 }
695
696 CSelectCopyAction::result_e lastResult = CSelectCopyAction::eResultNone;
697
698 project->blockUpdateItems(true);
699 int cnt = 1;
700 PROGRESS_SETUP(tr("Copy items..."), 0, keys.count(), this);
701 for(const IGisItem::key_t& key : keys)
702 {
703 PROGRESS(cnt++, break);
704 IGisItem* gisItem = getItemByKey(key);
705 if(nullptr != gisItem)
706 {
707 project->insertCopyOfItem(gisItem, NOIDX, lastResult);
708 }
709 }
710 project->blockUpdateItems(false);
711
712 CCanvas::triggerCompleteUpdate(CCanvas::eRedrawGis);
713 return project;
714 }
715
searchWebByKey(const IGisItem::key_t & key)716 void CGisWorkspace::searchWebByKey(const IGisItem::key_t& key)
717 {
718 QMutexLocker lock(&IGisItem::mutexItems);
719
720 CGisItemWpt* wpt = dynamic_cast<CGisItemWpt*>(getItemByKey(key));
721 if(wpt != nullptr)
722 {
723 CGeoSearchWeb::self().getMenu(wpt->getPosition(), this, true);
724 }
725 }
726
changeWptSymByKey(const QList<IGisItem::key_t> & keys,const QString & sym)727 void CGisWorkspace::changeWptSymByKey(const QList<IGisItem::key_t>& keys, const QString& sym)
728 {
729 QMutexLocker lock(&IGisItem::mutexItems);
730
731 PROGRESS_SETUP(tr("Change waypoint symbols."), 0, keys.count(), this);
732 int cnt = 0;
733
734 QSet<IGisProject*> projects;
735 for(const IGisItem::key_t& key : keys)
736 {
737 PROGRESS(cnt++, break);
738 CGisItemWpt* wpt = dynamic_cast<CGisItemWpt*>(getItemByKey(key));
739 if(nullptr != wpt)
740 {
741 IGisProject* project = wpt->getParentProject();
742 if(!projects.contains(project))
743 {
744 project->blockUpdateItems(true);
745 projects << project;
746 }
747 wpt->setIcon(sym);
748 }
749 }
750
751 for(IGisProject* project : qAsConst(projects))
752 {
753 project->blockUpdateItems(false);
754 }
755
756 emit sigChanged();
757 }
758
759
addEleToWptTrkByKey(const QList<IGisItem::key_t> & keys)760 void CGisWorkspace::addEleToWptTrkByKey(const QList<IGisItem::key_t>& keys)
761 {
762 CCanvas* canvas = nullptr;
763 CCanvasSelect dlg(canvas, this);
764 dlg.exec();
765
766 if(canvas == nullptr)
767 {
768 return;
769 }
770
771 QMutexLocker lock(&IGisItem::mutexItems);
772
773 QSet<IGisProject*> projects;
774 for(const IGisItem::key_t& key : keys)
775 {
776 IGisItem* item = dynamic_cast<IGisItem*>(getItemByKey(key));
777
778 IGisProject* project = item->getParentProject();
779 if(!projects.contains(project))
780 {
781 project->blockUpdateItems(true);
782 projects << project;
783 }
784
785 switch(item->type())
786 {
787 case IGisItem::eTypeTrk:
788 {
789 CGisItemTrk* trk = dynamic_cast<CGisItemTrk*>(item);
790 if(trk != nullptr)
791 {
792 trk->filterReplaceElevation(canvas);
793 }
794 break;
795 }
796
797 case IGisItem::eTypeWpt:
798 {
799 CGisItemWpt* wpt = dynamic_cast<CGisItemWpt*>(item);
800 if(wpt != nullptr)
801 {
802 qreal ele = canvas->getElevationAt(wpt->getPosition() * DEG_TO_RAD);
803 wpt->setElevation(ele == NOFLOAT ? NOINT : ele);
804 }
805 break;
806 }
807 }
808 }
809
810 for(IGisProject* project : qAsConst(projects))
811 {
812 project->blockUpdateItems(false);
813 }
814
815 emit sigChanged();
816 }
817
projWptByKey(const IGisItem::key_t & key)818 void CGisWorkspace::projWptByKey(const IGisItem::key_t& key)
819 {
820 QMutexLocker lock(&IGisItem::mutexItems);
821
822 CGisItemWpt* wpt = dynamic_cast<CGisItemWpt*>(getItemByKey(key));
823 if(nullptr != wpt)
824 {
825 CProjWpt dlg(*wpt, 0);
826 dlg.exec();
827 }
828
829
830 emit sigChanged();
831 }
832
moveWptByKey(const IGisItem::key_t & key)833 void CGisWorkspace::moveWptByKey(const IGisItem::key_t& key)
834 {
835 QMutexLocker lock(&IGisItem::mutexItems);
836 CGisItemWpt* wpt = dynamic_cast<CGisItemWpt*>(getItemByKey(key));
837 if(nullptr != wpt)
838 {
839 if(!wpt->setReadOnlyMode(false))
840 {
841 return;
842 }
843
844 CCanvas* canvas = CMainWindow::self().getVisibleCanvas();
845 if(nullptr != canvas)
846 {
847 canvas->setMouseMoveWpt(*wpt);
848 }
849 }
850 }
851
toggleWptBubble(const IGisItem::key_t & key)852 void CGisWorkspace::toggleWptBubble(const IGisItem::key_t& key)
853 {
854 QMutexLocker lock(&IGisItem::mutexItems);
855 CGisItemWpt* wpt = dynamic_cast<CGisItemWpt*>(getItemByKey(key));
856 if(nullptr != wpt)
857 {
858 wpt->toggleBubble();
859 }
860 }
861
deleteWptRadius(const IGisItem::key_t & key)862 void CGisWorkspace::deleteWptRadius(const IGisItem::key_t& key)
863 {
864 IGisItem* item = getItemByKey(key);
865 if(nullptr != item)
866 {
867 CGisItemWpt* wpt = dynamic_cast<CGisItemWpt*>(item);
868 wpt->setProximity(NOFLOAT);
869 }
870 }
871
toggleNogoItem(const IGisItem::key_t & key)872 void CGisWorkspace::toggleNogoItem(const IGisItem::key_t& key)
873 {
874 QMutexLocker lock(&IGisItem::mutexItems);
875 IGisItem* item = getItemByKey(key);
876 if(nullptr != item)
877 {
878 item->setNogo(!item->isNogo());
879 }
880 }
881
editWptRadius(const IGisItem::key_t & key)882 void CGisWorkspace::editWptRadius(const IGisItem::key_t& key)
883 {
884 QMutexLocker lock(&IGisItem::mutexItems);
885 CGisItemWpt* wpt = dynamic_cast<CGisItemWpt*>(getItemByKey(key));
886 if(nullptr != wpt)
887 {
888 if(!wpt->setReadOnlyMode(false))
889 {
890 return;
891 }
892
893 CCanvas* canvas = CMainWindow::self().getVisibleCanvas();
894 if(nullptr != canvas)
895 {
896 canvas->setMouseRadiusWpt(*wpt);
897 }
898 }
899 }
900
copyWptCoordByKey(const IGisItem::key_t & key)901 void CGisWorkspace::copyWptCoordByKey(const IGisItem::key_t& key)
902 {
903 QMutexLocker lock(&IGisItem::mutexItems);
904 CGisItemWpt* wpt = dynamic_cast<CGisItemWpt*>(getItemByKey(key));
905 if(nullptr != wpt)
906 {
907 QString strPos;
908 QPointF pos = wpt->getPosition();
909 IUnit::degToStr(pos.x(), pos.y(), strPos);
910 QClipboard* clipboard = QApplication::clipboard();
911 clipboard->setText(strPos);
912 }
913 }
914
addWptByPos(const QPointF & pt,const QString & name,const QString & desc) const915 void CGisWorkspace::addWptByPos(const QPointF& pt, const QString& name, const QString& desc) const
916 {
917 QMutexLocker lock(&IGisItem::mutexItems);
918
919 IGisProject* project = CGisWorkspace::self().selectProject(false);
920 if(nullptr == project)
921 {
922 return;
923 }
924
925 CGisItemWpt::newWpt(pt, name, desc, project);
926 }
927
addPoisAsWpt(const QSet<poi_t> & pois,IGisProject * project) const928 void CGisWorkspace::addPoisAsWpt(const QSet<poi_t>& pois, IGisProject* project) const
929 {
930 if(nullptr == project)
931 {
932 project = CGisWorkspace::self().selectProject(false);
933 }
934 if(nullptr == project)
935 {
936 return;
937 }
938 tristate_e openEditWindow = eTristateUndefined;
939 for(const poi_t& poi : pois)
940 {
941 addPoiAsWpt(poi, openEditWindow, project);
942 }
943 }
944
addPoiAsWpt(const poi_t & poi,IGisProject * project) const945 void CGisWorkspace::addPoiAsWpt(const poi_t& poi, IGisProject* project) const
946 {
947 tristate_e tmp = eTristateUndefined;
948 addPoiAsWpt(poi, tmp, project);
949 }
950
addPoiAsWpt(const poi_t & poi,tristate_e & openEditWindow,IGisProject * project) const951 void CGisWorkspace::addPoiAsWpt(const poi_t& poi, tristate_e& openEditWindow, IGisProject* project) const
952 {
953 QMutexLocker lock(&IGisItem::mutexItems);
954
955 if(nullptr == project)
956 {
957 project = CGisWorkspace::self().selectProject(false);
958 }
959 if(nullptr == project)
960 {
961 return;
962 }
963 if(openEditWindow == eTristateUndefined && poi.icon.isEmpty())
964 {
965 int answer = QMessageBox(QMessageBox::Icon::Question,
966 tr("Undefined Waypoint Symbol"),
967 tr("QMapShack couldn't automatically assign a waypoint icon to one of the POIs you want to convert to a waypoint.\n\n"
968 "Do you want to choose an icon for each new waypoint for which no icon could be found?\n"
969 "If you choose 'No' the respective last used waypoint icon is applied."),
970 QMessageBox::Yes | QMessageBox::No).exec();
971
972 if(answer == QMessageBox::Yes)
973 {
974 openEditWindow = eTristateTrue;
975 }
976 else
977 {
978 openEditWindow = eTristateFalse;
979 }
980 }
981 CGisItemWpt::newWpt(poi, project, openEditWindow == eTristateTrue && poi.icon.isEmpty());
982 }
983
focusTrkByKey(bool yes,const IGisItem::key_t & key)984 void CGisWorkspace::focusTrkByKey(bool yes, const IGisItem::key_t& key)
985 {
986 QMutexLocker lock(&IGisItem::mutexItems);
987
988 CGisItemTrk* trk = dynamic_cast<CGisItemTrk*>(getItemByKey(key));
989 if(nullptr != trk)
990 {
991 trk->gainUserFocus(yes);
992 }
993
994 emit sigChanged();
995 }
996
focusRteByKey(bool yes,const IGisItem::key_t & key)997 void CGisWorkspace::focusRteByKey(bool yes, const IGisItem::key_t& key)
998 {
999 QMutexLocker lock(&IGisItem::mutexItems);
1000
1001 CGisItemRte* rte = dynamic_cast<CGisItemRte*>(getItemByKey(key));
1002 if(nullptr != rte)
1003 {
1004 rte->gainUserFocus(yes);
1005 }
1006
1007 emit sigChanged();
1008 }
1009
convertRouteToTrack(const IGisItem::key_t & key)1010 void CGisWorkspace::convertRouteToTrack(const IGisItem::key_t& key)
1011 {
1012 QMutexLocker lock(&IGisItem::mutexItems);
1013 CGisItemRte* rte = dynamic_cast<CGisItemRte*>(getItemByKey(key));
1014 if(nullptr != rte)
1015 {
1016 rte->toTrack();
1017 }
1018
1019 emit sigChanged();
1020 }
1021
convertTrackToRoute(const IGisItem::key_t & key)1022 void CGisWorkspace::convertTrackToRoute(const IGisItem::key_t& key)
1023 {
1024 QMutexLocker lock(&IGisItem::mutexItems);
1025 CGisItemTrk* trk = dynamic_cast<CGisItemTrk*>(getItemByKey(key));
1026 if(nullptr != trk)
1027 {
1028 trk->toRoute();
1029 }
1030
1031 emit sigChanged();
1032 }
1033
cutTrkByKey(const IGisItem::key_t & key)1034 void CGisWorkspace::cutTrkByKey(const IGisItem::key_t& key)
1035 {
1036 QMutexLocker lock(&IGisItem::mutexItems);
1037
1038 CGisItemTrk* trk = dynamic_cast<CGisItemTrk*>(getItemByKey(key));
1039 if(nullptr != trk && trk->cut())
1040 {
1041 int res = QMessageBox::question(this, tr("Cut Track..."), tr("Do you want to delete the original track?"), QMessageBox::Ok | QMessageBox::No, QMessageBox::Ok);
1042 if(res == QMessageBox::Ok)
1043 {
1044 delete trk;
1045 }
1046 }
1047
1048 emit sigChanged();
1049 }
1050
addTrkInfoByKey(const IGisItem::key_t & key)1051 void CGisWorkspace::addTrkInfoByKey(const IGisItem::key_t& key)
1052 {
1053 QMutexLocker lock(&IGisItem::mutexItems);
1054
1055 CGisItemTrk* trk = dynamic_cast<CGisItemTrk*>(getItemByKey(key));
1056 if(nullptr != trk)
1057 {
1058 trk->addTrkPtDesc();
1059 }
1060
1061 emit sigChanged();
1062 }
1063
reverseTrkByKey(const IGisItem::key_t & key)1064 void CGisWorkspace::reverseTrkByKey(const IGisItem::key_t& key)
1065 {
1066 QMutexLocker lock(&IGisItem::mutexItems);
1067
1068 CGisItemTrk* trk = dynamic_cast<CGisItemTrk*>(getItemByKey(key));
1069 if(nullptr != trk)
1070 {
1071 trk->reverse();
1072 }
1073
1074 emit sigChanged();
1075 }
1076
combineTrkByKey(const IGisItem::key_t & keyTrk)1077 void CGisWorkspace::combineTrkByKey(const IGisItem::key_t& keyTrk)
1078 {
1079 QMutexLocker lock(&IGisItem::mutexItems);
1080
1081 QList<IGisItem::key_t> keys;
1082 IGisItem* item = dynamic_cast<IGisItem*>(getItemByKey(keyTrk));
1083 if(item == nullptr)
1084 {
1085 return;
1086 }
1087
1088 keys << keyTrk;
1089
1090 IGisProject* project = dynamic_cast<IGisProject*>(item->parent());
1091 if(project == nullptr)
1092 {
1093 return;
1094 }
1095
1096 const int N = project->childCount();
1097 for(int i = 0; i < N; i++)
1098 {
1099 CGisItemTrk* trk = dynamic_cast<CGisItemTrk*>(project->child(i));
1100 if(trk != nullptr)
1101 {
1102 const IGisItem::key_t& key = trk->getKey();
1103 if(key != keyTrk)
1104 {
1105 keys << key;
1106 }
1107 }
1108 }
1109
1110 combineTrkByKey(keys, {keyTrk});
1111 }
1112
combineTrkByKey(const QList<IGisItem::key_t> & keys,const QList<IGisItem::key_t> & keysPreSel)1113 void CGisWorkspace::combineTrkByKey(const QList<IGisItem::key_t>& keys, const QList<IGisItem::key_t>& keysPreSel)
1114 {
1115 if(keys.isEmpty())
1116 {
1117 return;
1118 }
1119
1120 QMutexLocker lock(&IGisItem::mutexItems);
1121
1122 CCombineTrk dlg(keys, keysPreSel, this);
1123 dlg.exec();
1124
1125 emit sigChanged();
1126 }
1127
colorTrkByKey(const QList<IGisItem::key_t> & keys)1128 void CGisWorkspace::colorTrkByKey(const QList<IGisItem::key_t>& keys)
1129 {
1130 if(keys.isEmpty())
1131 {
1132 return;
1133 }
1134
1135 qint32 colorIdx = IGisItem::selectColor(this);
1136 if(colorIdx != NOIDX)
1137 {
1138 QMutexLocker lock(&IGisItem::mutexItems);
1139
1140 QSet<IGisProject*> projects;
1141 for(const IGisItem::key_t& key : keys)
1142 {
1143 CGisItemTrk* trk = dynamic_cast<CGisItemTrk*>(getItemByKey(key));
1144 if(trk != nullptr)
1145 {
1146 IGisProject* project = trk->getParentProject();
1147 if(!projects.contains(project))
1148 {
1149 project->blockUpdateItems(true);
1150 projects << project;
1151 }
1152
1153 trk->setColor(colorIdx);
1154 }
1155 }
1156
1157 for(IGisProject* project : qAsConst(projects))
1158 {
1159 project->blockUpdateItems(false);
1160 }
1161 }
1162 }
1163
editTrkByKey(const IGisItem::key_t & key)1164 void CGisWorkspace::editTrkByKey(const IGisItem::key_t& key)
1165 {
1166 QMutexLocker lock(&IGisItem::mutexItems);
1167
1168 CGisItemTrk* trk = dynamic_cast<CGisItemTrk*>(getItemByKey(key));
1169 if(nullptr != trk)
1170 {
1171 if(!trk->setReadOnlyMode(false))
1172 {
1173 return;
1174 }
1175
1176 CCanvas* canvas = CMainWindow::self().getVisibleCanvas();
1177 if(nullptr != canvas)
1178 {
1179 canvas->setMouseEditTrk(*trk);
1180 }
1181 }
1182 }
1183
rangeTrkByKey(const IGisItem::key_t & key)1184 void CGisWorkspace::rangeTrkByKey(const IGisItem::key_t& key)
1185 {
1186 QMutexLocker lock(&IGisItem::mutexItems);
1187
1188 CGisItemTrk* trk = dynamic_cast<CGisItemTrk*>(getItemByKey(key));
1189 if(nullptr != trk)
1190 {
1191 CCanvas* canvas = CMainWindow::self().getVisibleCanvas();
1192 if(nullptr != canvas)
1193 {
1194 canvas->setMouseRangeTrk(*trk);
1195 }
1196 }
1197 }
1198
copyTrkWithWptByKey(const IGisItem::key_t & key)1199 void CGisWorkspace::copyTrkWithWptByKey(const IGisItem::key_t& key)
1200 {
1201 QList<IGisItem::key_t> keys;
1202
1203 CGisItemTrk* trk = dynamic_cast<CGisItemTrk*>(CGisWorkspace::self().getItemByKey(key));
1204 if(nullptr != trk)
1205 {
1206 keys << key;
1207
1208 const CTrackData& t = trk->getTrackData();
1209 for(const CTrackData::trkpt_t& trkpt : t)
1210 {
1211 if(trkpt.isHidden() || trkpt.keyWpt.item.isEmpty())
1212 {
1213 continue;
1214 }
1215
1216 keys << trkpt.keyWpt;
1217 }
1218
1219 copyItemsByKey(keys);
1220 }
1221 }
1222
editRteByKey(const IGisItem::key_t & key)1223 void CGisWorkspace::editRteByKey(const IGisItem::key_t& key)
1224 {
1225 QMutexLocker lock(&IGisItem::mutexItems);
1226
1227 CGisItemRte* rte = dynamic_cast<CGisItemRte*>(getItemByKey(key));
1228 if(nullptr != rte)
1229 {
1230 if(!rte->setReadOnlyMode(false))
1231 {
1232 return;
1233 }
1234
1235 CCanvas* canvas = CMainWindow::self().getVisibleCanvas();
1236 if(nullptr != canvas)
1237 {
1238 canvas->setMouseEditRte(*rte);
1239 }
1240 }
1241 }
1242
reverseRteByKey(const IGisItem::key_t & key)1243 void CGisWorkspace::reverseRteByKey(const IGisItem::key_t& key)
1244 {
1245 QMutexLocker lock(&IGisItem::mutexItems);
1246
1247 CGisItemRte* rte = dynamic_cast<CGisItemRte*>(getItemByKey(key));
1248 if(nullptr != rte)
1249 {
1250 rte->reverse();
1251 }
1252 }
1253
calcRteByKey(const IGisItem::key_t & key)1254 void CGisWorkspace::calcRteByKey(const IGisItem::key_t& key)
1255 {
1256 QMutexLocker lock(&IGisItem::mutexItems);
1257
1258 CGisItemRte* rte = dynamic_cast<CGisItemRte*>(getItemByKey(key));
1259 if(nullptr != rte)
1260 {
1261 rte->calc();
1262 }
1263 }
1264
resetRteByKey(const IGisItem::key_t & key)1265 void CGisWorkspace::resetRteByKey(const IGisItem::key_t& key)
1266 {
1267 QMutexLocker lock(&IGisItem::mutexItems);
1268
1269 CGisItemRte* rte = dynamic_cast<CGisItemRte*>(getItemByKey(key));
1270 if(rte != nullptr)
1271 {
1272 rte->reset();
1273 }
1274 }
1275
1276
editAreaByKey(const IGisItem::key_t & key)1277 void CGisWorkspace::editAreaByKey(const IGisItem::key_t& key)
1278 {
1279 QMutexLocker lock(&IGisItem::mutexItems);
1280
1281 CGisItemOvlArea* area = dynamic_cast<CGisItemOvlArea*>(getItemByKey(key));
1282 if(area != nullptr)
1283 {
1284 if(!area->setReadOnlyMode(false))
1285 {
1286 return;
1287 }
1288
1289 CCanvas* canvas = CMainWindow::self().getVisibleCanvas();
1290 if(canvas != nullptr)
1291 {
1292 canvas->setMouseEditArea(*area);
1293 }
1294 }
1295 }
1296
makeRteFromWpt(const QList<IGisItem::key_t> & keys)1297 void CGisWorkspace::makeRteFromWpt(const QList<IGisItem::key_t>& keys)
1298 {
1299 QMutexLocker lock(&IGisItem::mutexItems);
1300
1301 CCreateRouteFromWpt dlg(keys, this);
1302 dlg.exec();
1303 }
1304
editPrxWpt(const QList<IGisItem::key_t> & keys)1305 void CGisWorkspace::editPrxWpt(const QList<IGisItem::key_t>& keys)
1306 {
1307 QMutexLocker lock(&IGisItem::mutexItems);
1308
1309 QVariant var;
1310 CInputDialog dlg(this, tr("Enter new proximity range."), var, QVariant(NOFLOAT), IUnit::self().baseUnit);
1311 dlg.setOption(tr("Is no-go area"), false);
1312 if(dlg.exec() != QDialog::Accepted)
1313 {
1314 return;
1315 }
1316
1317 qreal proximity = var.toDouble() / IUnit::self().baseFactor;
1318 bool isNoGo = dlg.optionIsChecked();
1319 for(const IGisItem::key_t& key : keys)
1320 {
1321 CGisItemWpt* wpt = dynamic_cast<CGisItemWpt*>(getItemByKey(key));
1322 if(wpt != nullptr)
1323 {
1324 wpt->setProximity(proximity);
1325 if(wpt->isNogo() != isNoGo)
1326 {
1327 wpt->setNogo(isNoGo);
1328 }
1329 }
1330 }
1331 }
1332
1333
draw(QPainter & p,const QPolygonF & viewport,CGisDraw * gis)1334 void CGisWorkspace::draw(QPainter& p, const QPolygonF& viewport, CGisDraw* gis)
1335 {
1336 QFontMetricsF fm(CMainWindow::self().getMapFont());
1337 QList<QRectF> blockedAreas;
1338
1339 QMutexLocker lock(&IGisItem::mutexItems);
1340 // draw mandatory stuff first
1341 for(int i = 0; i < treeWks->topLevelItemCount(); i++)
1342 {
1343 if(gis->needsRedraw())
1344 {
1345 break;
1346 }
1347
1348 QTreeWidgetItem* item = treeWks->topLevelItem(i);
1349
1350 IGisProject* project = dynamic_cast<IGisProject*>(item);
1351 if(nullptr != project)
1352 {
1353 project->drawItem(p, viewport, blockedAreas, gis);
1354 continue;
1355 }
1356 IDevice* device = dynamic_cast<IDevice*>(item);
1357 if(nullptr != device)
1358 {
1359 device->drawItem(p, viewport, blockedAreas, gis);
1360 continue;
1361 }
1362 }
1363
1364 // draw optional labels second
1365 for(int i = 0; i < treeWks->topLevelItemCount(); i++)
1366 {
1367 if(gis->needsRedraw())
1368 {
1369 break;
1370 }
1371
1372 QTreeWidgetItem* item = treeWks->topLevelItem(i);
1373
1374 IGisProject* project = dynamic_cast<IGisProject*>(item);
1375 if(nullptr != project)
1376 {
1377 project->drawLabel(p, viewport, blockedAreas, fm, gis);
1378 continue;
1379 }
1380 IDevice* device = dynamic_cast<IDevice*>(item);
1381 if(nullptr != device)
1382 {
1383 device->drawLabel(p, viewport, blockedAreas, fm, gis);
1384 continue;
1385 }
1386 }
1387 }
1388
fastDraw(QPainter & p,const QRectF & viewport,CGisDraw * gis)1389 void CGisWorkspace::fastDraw(QPainter& p, const QRectF& viewport, CGisDraw* gis)
1390 {
1391 /*
1392 Mutex locking will make map moving very slow if there are many GIS items
1393 visible. Remove it for now. But I am not sure if that is a good idea.
1394 */
1395 //QMutexLocker lock(&IGisItem::mutexItems);
1396 for(int i = 0; i < treeWks->topLevelItemCount(); i++)
1397 {
1398 QTreeWidgetItem* item = treeWks->topLevelItem(i);
1399
1400 IGisProject* project = dynamic_cast<IGisProject*>(item);
1401 if(nullptr != project)
1402 {
1403 project->drawItem(p, viewport, gis);
1404 continue;
1405 }
1406 IDevice* device = dynamic_cast<IDevice*>(item);
1407 if(nullptr != device)
1408 {
1409 device->drawItem(p, viewport, gis);
1410 continue;
1411 }
1412 }
1413
1414
1415 IGisItem* item = getItemByKey(keyWksSelection);
1416 if(item != nullptr)
1417 {
1418 IGisProject* project = item->getParentProject();
1419 if (project != nullptr && project->isVisible())
1420 {
1421 item->drawHighlight(p);
1422 }
1423 }
1424 }
1425
1426
findPolylineCloseBy(const QPointF & pt1,const QPointF & pt2,qint32 threshold,QPolygonF & polyline)1427 bool CGisWorkspace::findPolylineCloseBy(const QPointF& pt1, const QPointF& pt2, qint32 threshold, QPolygonF& polyline)
1428 {
1429 QMutexLocker lock(&IGisItem::mutexItems);
1430 for(int i = 0; i < treeWks->topLevelItemCount(); i++)
1431 {
1432 QTreeWidgetItem* item1 = treeWks->topLevelItem(i);
1433 IGisProject* project = dynamic_cast<IGisProject*>(item1);
1434 if(project)
1435 {
1436 project->findPolylineCloseBy(pt1, pt2, threshold, polyline);
1437 }
1438 }
1439
1440 return !polyline.isEmpty();
1441 }
1442
areTagsHidden() const1443 bool CGisWorkspace::areTagsHidden() const
1444 {
1445 return treeWks->isColumnHidden(CGisListWks::eColumnRating);
1446 }
1447
setTagsHidden(bool hidden)1448 void CGisWorkspace::setTagsHidden(bool hidden)
1449 {
1450 treeWks->setColumnHidden(CGisListWks::eColumnRating, hidden);
1451 }
1452
tagItemsByKey(const QList<IGisItem::key_t> & keys)1453 void CGisWorkspace::tagItemsByKey(const QList<IGisItem::key_t>& keys)
1454 {
1455 QSet<QString> commonKeywords;
1456
1457 QList<IGisItem*> items;
1458 bool firstItem = true;
1459 qreal rating = 0;
1460 for(const IGisItem::key_t& key : keys)
1461 {
1462 IGisItem* gisItem = getItemByKey(key);
1463 if(gisItem != nullptr)
1464 {
1465 if(firstItem)
1466 {
1467 commonKeywords = gisItem->getKeywords();
1468 rating = gisItem->getRating();
1469 firstItem = false;
1470 }
1471 else
1472 {
1473 commonKeywords = commonKeywords.intersect(gisItem->getKeywords());
1474 }
1475
1476 items << gisItem;
1477 }
1478 }
1479
1480 CGisItemRate dlg (this, commonKeywords, items.size() > 1 ? 0 : rating);
1481 dlg.exec();
1482
1483 if(dlg.result() == QDialog::Accepted)
1484 {
1485 for(IGisItem* gisItem : qAsConst(items))
1486 {
1487 if(dlg.getRatingChanged())
1488 {
1489 gisItem->setRating(dlg.getRating());
1490 }
1491 gisItem->removeKeywords(dlg.getRemovedKeywords());
1492 gisItem->addKeywords(dlg.getAddedKeywords());
1493 }
1494 }
1495 }
1496