1 #include "Global.h"
2 #include "MainWindow.h"
3 
4 #include "LayerDock.h"
5 #include "MapView.h"
6 #include "PropertiesDock.h"
7 #include "InfoDock.h"
8 #include "DirtyDock.h"
9 #include "StyleDock.h"
10 #include "FeaturesDock.h"
11 #include "Command.h"
12 #include "DocumentCommands.h"
13 #include "FeatureCommands.h"
14 #include "RelationCommands.h"
15 #include "ImportExportOSC.h"
16 #include "ExportGPX.h"
17 #include "ImportExportKML.h"
18 #ifdef USE_PROTOBUF
19 #include "ImportExportPBF.h"
20 #endif
21 #include "ImportExportGdal.h"
22 #include "CreateAreaInteraction.h"
23 #include "CreateDoubleWayInteraction.h"
24 #include "CreateNodeInteraction.h"
25 #include "BuildBridgeInteraction.h"
26 #include "CreateRoundaboutInteraction.h"
27 #include "CreatePolygonInteraction.h"
28 #include "CreateSingleWayInteraction.h"
29 #include "EditInteraction.h"
30 #include "MoveNodeInteraction.h"
31 #include "RotateInteraction.h"
32 #include "ScaleInteraction.h"
33 #include "ZoomInteraction.h"
34 #include "ExtrudeInteraction.h"
35 #include "Coord.h"
36 #include "DownloadOSM.h"
37 #include "ImportGPX.h"
38 #include "ImportNGT.h"
39 #include "ImportOSM.h"
40 #include "Document.h"
41 #include "Layer.h"
42 #include "ImageMapLayer.h"
43 #include "Features.h"
44 #include "FeatureManipulations.h"
45 #include "LayerIterator.h"
46 #include "MasPaintStyle.h"
47 #include "MapCSSPaintstyle.h"
48 #include "PaintStyleEditor.h"
49 #include "Utils/Utils.h"
50 #include "DirtyList.h"
51 #include "DirtyListExecutorOSC.h"
52 #include "RemoteControlServer.hpp"
53 
54 #include <ui_MainWindow.h>
55 #include <ui_AboutDialog.h>
56 #include <ui_UploadMapDialog.h>
57 #include <ui_SelectionDialog.h>
58 #include <ui_ExportDialog.h>
59 #include <ui_PropertiesDialog.h>
60 
61 #include "Preferences/PreferencesDialog.h"
62 #include "Preferences/MerkaartorPreferences.h"
63 #include "Preferences/ProjectionsList.h"
64 #include "Preferences/WMSPreferencesDialog.h"
65 #include "Preferences/TMSPreferencesDialog.h"
66 #include "Preferences/ProjPreferencesDialog.h"
67 #include "Preferences/FilterPreferencesDialog.h"
68 #include "Utils/SelectionDialog.h"
69 #include "Utils/MDiscardableDialog.h"
70 #include "QMapControl/imagemanager.h"
71 #ifdef USE_WEBKIT
72     #include "QMapControl/browserimagemanager.h"
73 #endif
74 #include "QMapControl/mapadapter.h"
75 #include "QMapControl/wmsmapadapter.h"
76 #include "Tools/ActionsDialog.h"
77 #include "GotoDialog.h"
78 #include "TerraceDialog.h"
79 
80 #ifdef GEOIMAGE
81 #include "GeoImageDock.h"
82 #endif
83 
84 #include "Render/NativeRenderDialog.h"
85 
86 #include "qgps.h"
87 #include "qgpsdevice.h"
88 
89 #include <QtCore>
90 #include <QtGui>
91 #include <QTcpServer>
92 #include <QTcpSocket>
93 #include <QXmlStreamReader>
94 #include <QStyleFactory>
95 #include <QMessageBox>
96 #include <QFileDialog>
97 #include <QInputDialog>
98 #include <QToolTip>
99 
100 #include "qttoolbardialog.h"
101 
102 #include <locale.h>
103 #include <limits.h>
104 
105 #include "proj.h"
106 #include "gdal_version.h"
107 
108 #include "Utils/SlippyMapWidget.h"
109 
110 namespace {
111 
112 const QString MIME_OPENSTREETMAP_XML = "application/x-openstreetmap+xml";
113 const QString MIME_GOOGLE_EARTH_KML = "application/vnd.google-earth.kml+xml";
114 const QString MIME_GPX = "application/gpx+xml";
115 const QString MIME_MERKAARTOR_UNDO_XML = "application/x-merkaartor-undo+xml";
116 
117 }  // namespace
118 
119 using namespace Merkaartor;
120 
121 class MainWindowPrivate
122 {
123     public:
MainWindowPrivate()124         MainWindowPrivate()
125             : lastPrefTabIndex(0)
126             , projActgrp(0)
127             , theListeningServer(0)
128             , latSaveDirtyLevel(0)
129     #ifdef GEOIMAGE
130             , dropTarget(0)
131     #endif
132             , numImages(0)
133         {
134             title = QString("%1 v%2").arg(STRINGIFY(PRODUCT)).arg(STRINGIFY(REVISION));
135         }
136 
137         QString FILTER_OPEN_NATIVE;
138         QString FILTER_OPEN_SUPPORTED;
139         QString FILTER_IMPORT_SUPPORTED;
140         int lastPrefTabIndex;
141         QString defStyle;
142         StyleDock* theStyle;
143         FeaturesDock* theFeats;
144         QString title;
145         QActionGroup* projActgrp;
146         RemoteControlServer* theListeningServer;
147         PropertiesDock* theProperties;
148         RendererOptions renderOptions;
149         int latSaveDirtyLevel;
150 #ifdef GEOIMAGE
151         Node *dropTarget;
152 #endif
153         int numImages;
154 };
155 
156 namespace {
157 
SetOptionValue(RendererOptions & options,RendererOptions::RenderOption option,bool on)158 void SetOptionValue(RendererOptions& options,
159 		RendererOptions::RenderOption option, bool on) {
160     if (on) {
161 	options.options |= option;
162     } else {
163 	options.options &= ~option;
164     }
165 }
166 
167 }  // namespace
168 
MainWindow(QWidget * parent)169 MainWindow::MainWindow(QWidget *parent)
170         : QMainWindow(parent)
171         , ui(new Ui::MainWindow)
172         , theDocument(0)
173         , gpsRecLayer(0)
174         , curGpsTrackSegment(0)
175         , qtTranslator(0)
176         , merkaartorTranslator(0)
177         , toolBarManager(0)
178 {
179     setlocale(LC_NUMERIC, "C");  // impose decimal point separator
180     qsrand(QDateTime::currentDateTime().toTime_t());  //initialize random generator
181 
182     p = new MainWindowPrivate;
183 
184     QString supported_import_formats("*.gpx *.osm *.osc *.ngt *.nmea *.nma *.kml *.csv");
185 #ifdef GEOIMAGE
186     supported_import_formats += " *.jpg";
187 #endif
188     supported_import_formats += " *.shp *.gml";
189 #ifdef USE_PROTOBUF
190     supported_import_formats += " *.pbf";
191 #endif
192     QString supported_import_formats_desc =
193             tr("GPS Exchange format (*.gpx)\n") \
194             +tr("OpenStreetMap format (*.osm)\n") \
195             +tr("OpenStreetMap change format (*.osc)\n") \
196             +tr("Noni GPSPlot format (*.ngt)\n") \
197             +tr("NMEA GPS log format (*.nmea *.nma)\n") \
198             +tr("KML file (*.kml)\n") \
199             +tr("Comma delimited format (*.csv)\n");
200 
201 #ifdef GEOIMAGE
202     supported_import_formats_desc += tr("Geotagged images (*.jpg)\n");
203 #endif
204     supported_import_formats_desc += tr("ESRI Shapefile (*.shp)\n") + tr("Geography Markup Language (*.gml)\n");
205 #ifdef USE_PROTOBUF
206     supported_import_formats_desc += tr("Protobuf Binary Format (*.pbf)\n");
207 #endif
208 
209     p->FILTER_OPEN_NATIVE = tr("Merkaartor document (*.mdc)\n");
210 
211     p->FILTER_OPEN_SUPPORTED = QString(tr("Supported formats") + " (*.mdc %1)\n").arg(supported_import_formats);
212     p->FILTER_OPEN_SUPPORTED += tr("Merkaartor document (*.mdc)\n") + supported_import_formats_desc;
213     p->FILTER_OPEN_SUPPORTED += tr("All Files (*)");
214 
215     p->FILTER_IMPORT_SUPPORTED = QString(tr("Supported formats") + " (%1)\n").arg(supported_import_formats);
216     p->FILTER_IMPORT_SUPPORTED += supported_import_formats_desc;
217     p->FILTER_IMPORT_SUPPORTED += tr("All Files (*)");
218 
219     theProgressDialog = NULL;
220     theProgressBar = NULL;
221     theProgressLabel = NULL;
222 
223     if (M_PREFS->getMerkaartorStyle())
224         QApplication::setStyle(QStyleFactory::create(M_PREFS->getMerkaartorStyleString()));
225 
226     ui->setupUi(this);
227     M_STYLE->loadPainters(M_PREFS->getDefaultStyle());
228 
229     blockSignals(true);
230 
231     ViewportStatusLabel = new QLabel(this);
232     MeterPerPixelLabel = new QLabel(this);
233     AdjusmentMeterLabel = new QLabel(this);
234 
235     pbImages = new QProgressBar(this);
236     statusBar()->addPermanentWidget(ViewportStatusLabel);
237     statusBar()->addPermanentWidget(pbImages);
238     statusBar()->addPermanentWidget(MeterPerPixelLabel);
239     statusBar()->addPermanentWidget(AdjusmentMeterLabel);
240 #ifndef NDEBUG
241     PaintTimeLabel = new QLabel(this);
242     PaintTimeLabel->setMinimumWidth(23);
243     statusBar()->addPermanentWidget(PaintTimeLabel);
244 #endif
245 
246     updateLanguage();
247 
248     ViewportStatusLabel->setTextInteractionFlags(Qt::TextSelectableByMouse);
249     pbImages->setMaximumWidth(200);
250     pbImages->setFormat(tr("tile %v / %m"));
251 #ifdef _MOBILE
252     pbImages->setVisible(false);
253 #endif
254 
255     SlippyMapWidget::theSlippyCache = new SlippyMapCache;
256 
257     theView = new MapView(this);
258     setCentralWidget(theView);
259 
260     theInfo = new InfoDock(this);
261     connect(theInfo, SIGNAL(visibilityChanged(bool)), this, SLOT(updateWindowMenu(bool)));
262 
263     theLayers = new LayerDock(this);
264     connect(theLayers, SIGNAL(visibilityChanged(bool)), this, SLOT(updateWindowMenu(bool)));
265 
266     p->theProperties = new PropertiesDock(this);
267     connect(p->theProperties, SIGNAL(visibilityChanged(bool)), this, SLOT(updateWindowMenu(bool)));
268     on_editPropertiesAction_triggered();
269 
270     theDirty = new DirtyDock(this);
271     connect(theDirty, SIGNAL(visibilityChanged(bool)), this, SLOT(updateWindowMenu(bool)));
272 
273     p->theStyle = new StyleDock(this);
274     connect(p->theStyle, SIGNAL(visibilityChanged(bool)), this, SLOT(updateWindowMenu(bool)));
275 
276     p->theFeats = new FeaturesDock(this);
277     connect(p->theFeats, SIGNAL(visibilityChanged(bool)), this, SLOT(updateWindowMenu(bool)));
278     connect(theView, SIGNAL(viewportChanged()), p->theFeats, SLOT(on_Viewport_changed()), Qt::QueuedConnection);
279     connect(this, SIGNAL(content_changed()), p->theFeats, SLOT(on_Viewport_changed()), Qt::QueuedConnection);
280     connect(this, SIGNAL(content_changed()), p->theProperties, SLOT(adjustSelection()), Qt::QueuedConnection);
281 
282     theGPS = new QGPS(this);
283     connect(theGPS, SIGNAL(visibilityChanged(bool)), this, SLOT(updateWindowMenu(bool)));
284 
285 #ifdef GEOIMAGE
286     theGeoImage = new GeoImageDock(this);
287     theGeoImage->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
288     addDockWidget(Qt::RightDockWidgetArea, theGeoImage);
289     connect(theGeoImage, SIGNAL(visibilityChanged(bool)), this, SLOT(updateWindowMenu(bool)));
290 #endif
291 
292     connect(theLayers, SIGNAL(layersChanged(bool)), this, SLOT(adjustLayers(bool)));
293     connect(theLayers, SIGNAL(layersCleared()), this, SIGNAL(content_changed()));
294     connect(theLayers, SIGNAL(layersClosed()), this, SIGNAL(content_changed()));
295     connect(theLayers, SIGNAL(layersProjection(const QString&)), this, SLOT(projectionSet(const QString&)));
296 
297     connect (M_PREFS, SIGNAL(bookmarkChanged()), this, SLOT(updateBookmarksMenu()));
298     updateBookmarksMenu();
299     connect (ui->menuBookmarks, SIGNAL(triggered(QAction *)), this, SLOT(bookmarkTriggered(QAction *)));
300 
301     updateRecentOpenMenu();
302     connect (ui->menuRecentOpen, SIGNAL(triggered(QAction *)), this, SLOT(recentOpenTriggered(QAction *)));
303 
304     updateRecentImportMenu();
305     connect (ui->menuRecentImport, SIGNAL(triggered(QAction *)), this, SLOT(recentImportTriggered(QAction *)));
306 
307     updateStyleMenu();
308     connect (ui->menuStyles, SIGNAL(triggered(QAction *)), this, SLOT(styleTriggered(QAction *)));
309 
310     ui->viewDownloadedAction->setChecked(M_PREFS->getDownloadedVisible());
311     ui->viewDirtyAction->setChecked(M_PREFS->getDirtyVisible());
312     ui->viewScaleAction->setChecked(M_PREFS->getScaleVisible());
313     ui->viewPhotosAction->setChecked(M_PREFS->getPhotosVisible());
314     ui->viewShowLatLonGridAction->setChecked(M_PREFS->getLatLonGridVisible());
315     ui->viewStyleBackgroundAction->setChecked(M_PREFS->getBackgroundVisible());
316     ui->viewStyleForegroundAction->setChecked(M_PREFS->getForegroundVisible());
317     ui->viewStyleTouchupAction->setChecked(M_PREFS->getTouchupVisible());
318     ui->viewNamesAction->setChecked(M_PREFS->getNamesVisible());
319     ui->viewTrackPointsAction->setChecked(M_PREFS->getTrackPointsVisible());
320     ui->viewTrackSegmentsAction->setChecked(M_PREFS->getTrackSegmentsVisible());
321     ui->viewRelationsAction->setChecked(M_PREFS->getRelationsVisible());
322     ui->viewVirtualNodesAction->setChecked(M_PREFS->getVirtualNodesVisible());
323     ui->viewLockZoomAction->setChecked(M_PREFS->getZoomBoris());
324     ui->viewWireframeAction->setChecked(M_PREFS->getWireframeView());
325 
326     SetOptionValue(p->renderOptions, RendererOptions::BackgroundVisible, M_PREFS->getBackgroundVisible());
327     SetOptionValue(p->renderOptions, RendererOptions::ForegroundVisible, M_PREFS->getForegroundVisible());
328     SetOptionValue(p->renderOptions, RendererOptions::TouchupVisible, M_PREFS->getTouchupVisible());
329     SetOptionValue(p->renderOptions, RendererOptions::NamesVisible, M_PREFS->getNamesVisible());
330     SetOptionValue(p->renderOptions, RendererOptions::PhotosVisible, M_PREFS->getPhotosVisible());
331     SetOptionValue(p->renderOptions, RendererOptions::VirtualNodesVisible, M_PREFS->getVirtualNodesVisible());
332     SetOptionValue(p->renderOptions, RendererOptions::NodesVisible, M_PREFS->getTrackPointsVisible());
333     SetOptionValue(p->renderOptions, RendererOptions::TrackSegmentVisible, M_PREFS->getTrackSegmentsVisible());
334     SetOptionValue(p->renderOptions, RendererOptions::RelationsVisible, M_PREFS->getRelationsVisible());
335     SetOptionValue(p->renderOptions, RendererOptions::DownloadedVisible, M_PREFS->getDownloadedVisible());
336     SetOptionValue(p->renderOptions, RendererOptions::ScaleVisible, M_PREFS->getScaleVisible());
337     SetOptionValue(p->renderOptions, RendererOptions::LatLonGridVisible, M_PREFS->getLatLonGridVisible());
338     SetOptionValue(p->renderOptions, RendererOptions::LockZoom, M_PREFS->getZoomBoris());
339 
340     updateMenu();
341 
342     QActionGroup* actgrpArrows = new QActionGroup(this);
343     actgrpArrows->addAction(ui->viewArrowsNeverAction);
344     actgrpArrows->addAction(ui->viewArrowsOnewayAction);
345     actgrpArrows->addAction(ui->viewArrowsAlwaysAction);
346     switch (M_PREFS->getDirectionalArrowsVisible()) {
347         case RendererOptions::ArrowsNever:
348             ui->viewArrowsNeverAction->setChecked(true);
349             p->renderOptions.arrowOptions = RendererOptions::ArrowsNever;
350             break;
351         case RendererOptions::ArrowsOneway:
352             ui->viewArrowsOnewayAction->setChecked(true);
353             p->renderOptions.arrowOptions = RendererOptions::ArrowsOneway;
354             break;
355         case RendererOptions::ArrowsAlways:
356             ui->viewArrowsAlwaysAction->setChecked(true);
357             p->renderOptions.arrowOptions = RendererOptions::ArrowsAlways;
358             break;
359     }
360 
361     theView->setRenderOptions(p->renderOptions);
362 
363     ui->gpsCenterAction->setChecked(M_PREFS->getGpsMapCenter());
364 
365     connect(QApplication::clipboard(), SIGNAL(dataChanged()), this, SLOT(clipboardChanged()));
366 
367 #ifndef _MOBILE
368     theLayers->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
369     addDockWidget(Qt::LeftDockWidgetArea, theLayers);
370 
371     p->theProperties->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
372     addDockWidget(Qt::LeftDockWidgetArea, p->theProperties);
373 
374     theInfo->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
375     addDockWidget(Qt::RightDockWidgetArea, theInfo);
376 
377     theDirty->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
378     addDockWidget(Qt::RightDockWidgetArea, theDirty);
379 
380     p->theStyle->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
381     addDockWidget(Qt::RightDockWidgetArea, p->theStyle);
382 
383     p->theFeats->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
384     addDockWidget(Qt::RightDockWidgetArea, p->theFeats);
385 
386     theGPS->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
387     addDockWidget(Qt::RightDockWidgetArea, theGPS);
388 
389 #else
390     p->theProperties->setVisible(false);
391     theInfo->setVisible(false);
392     theLayers->setVisible(false);
393     theDirty->setVisible(false);
394     theFeats->setVisible(false);
395     theGPS->setVisible(false);
396 
397     toolBar->setVisible(false);
398     mobileToolBar->setVisible(true);
399 
400     gpsPopupAction->setMenu(menuGps);
401     dynamic_cast<QToolButton*>(mobileToolBar->widgetForAction(gpsPopupAction))->setPopupMode(QToolButton::InstantPopup);
402 
403 #ifdef _WINCE
404     windowPropertiesAction->setText(tr("Properties..."));
405     menuBar()->setDefaultAction(windowPropertiesAction);
406 #endif // _WINCE
407 #endif // _MOBILE
408 
409 #ifndef OSMARENDER
410     ui->renderSVGAction->setVisible(false);
411 #endif
412 
413 #ifndef GEOIMAGE
414     ui->windowGeoimageAction->setVisible(false);
415     ui->viewPhotosAction->setVisible(false);
416 #endif
417 
418     ui->viewStyleBackgroundAction->setVisible(false);
419     ui->viewStyleForegroundAction->setVisible(false);
420     ui->viewStyleTouchupAction->setVisible(false);
421 
422     createToolBarManager();  // has to be before restorestate
423     M_PREFS->restoreMainWindowState( this );
424 
425     if (!M_PREFS->getProjectionsList()->getProjections()->size()) {
426         qWarning() << "Projection list empty. (Projections.xml file could not be opened?).";
427     }
428 
429 #define NUMOP 3
430     static const char *opStr[NUMOP] = {
431         QT_TR_NOOP("Low"), QT_TR_NOOP("High"), QT_TR_NOOP("Opaque")};
432 
433     int o = M_PREFS->getAreaOpacity();
434     QActionGroup* actgrp = new QActionGroup(this);
435     for (int i=0; i<NUMOP; i++) {
436         QAction* act = new QAction(tr(opStr[i]), ui->mnuAreaOpacity);
437         actgrp->addAction(act);
438         qreal a = M_PREFS->getAlpha(opStr[i]);
439         act->setData(a);
440         act->setCheckable(true);
441         ui->mnuAreaOpacity->addAction(act);
442         if (o == int(a*100))
443             act->setChecked(true);
444     }
445     connect(ui->mnuAreaOpacity, SIGNAL(triggered(QAction*)), this, SLOT(setAreaOpacity(QAction*)));
446 
447     if (!g_Merk_Frisius) {
448         ui->layersNewDrawingAction->setVisible(false);
449     }
450 
451     blockSignals(false);
452 
453     QTimer::singleShot( 0, this, SLOT(delayedInit()) );
454 }
455 
delayedInit()456 void MainWindow::delayedInit()
457 {
458     QList<QAction*> actions = findChildren<QAction*>();
459     for (int i=0; i<actions.size(); i++) {
460         shortcutsDefault[actions[i]->objectName()] = actions[i]->shortcut().toString();
461     }
462     QStringList shortcuts = M_PREFS->getShortcuts();
463     for (int i=0; i<shortcuts.size(); i+=2) {
464         QAction* act = findChild<QAction*>(shortcuts[i]);
465         if (act)
466             act->setShortcut(QKeySequence(shortcuts[i+1]));
467     }
468 
469     updateWindowMenu();
470 
471     p->theListeningServer = new RemoteControlServer(this);
472     connect( p->theListeningServer, &RemoteControlServer::requestReceived,
473             this, [this](QString url) { loadUrl(url); } );
474 
475     if (M_PREFS->getLocalServer()) {
476         p->theListeningServer->listen();
477     }
478 
479     if (M_PREFS->getHideToolbarLabels()) {
480         setToolButtonStyle(Qt::ToolButtonIconOnly);
481     } else {
482         setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
483     }
484 
485 //    M_PREFS->initialPosition(theView);
486     on_fileNewAction_triggered();
487     invalidateView();
488 }
489 
handleMessage(const QString & msg)490 void MainWindow::handleMessage(const QString &msg)
491 {
492     QStringList args = msg.split("$", QString::SkipEmptyParts);
493     QStringList fileNames;
494     for (int i=0; i < args.size(); ++i) {
495         if (args[i] == "-l" || args[i] == "--log") {
496             ++i;
497         } else {
498             QUrl u(args[i]);
499             if (u.scheme() == "osm") {
500                 loadUrl(u);
501                 continue;
502             }
503             if (args[i].endsWith(".mdc", Qt::CaseInsensitive))
504                 loadDocument(args[i]);
505             else
506                 fileNames.append(args[i]);
507         }
508     }
509 
510     if (fileNames.size() > 0) {
511         importFiles(theDocument, fileNames, NULL);
512     }
513 }
514 
~MainWindow(void)515 MainWindow::~MainWindow(void)
516 {
517     p->theProperties->setSelection(NULL);
518 
519     delete M_STYLE;
520     delete theDocument;
521     delete theView;
522     delete p->theProperties;
523 
524     delete qtTranslator;
525     delete merkaartorTranslator;
526 
527     delete SlippyMapWidget::theSlippyCache;
528 
529     delete p;
530 
531     delete M_PREFS;
532     delete ui;
533 }
534 
launchInteraction(Interaction * anInteraction)535 void MainWindow::launchInteraction(Interaction* anInteraction)
536 {
537     QList<Feature*> theSnapList;
538     EditInteraction* EI = dynamic_cast<EditInteraction*>(theView->interaction());
539     if (EI) {
540         theSnapList = EI->snapList();
541         ui->editRemoveAction->setEnabled(false);
542         ui->editReverseAction->setEnabled(false);
543     }
544     if (!theSnapList.size())
545         if (p->theProperties)
546             theSnapList = p->theProperties->selection();
547     if (theView->interaction())
548         delete theView->interaction();
549     theView->setInteraction(anInteraction);
550     if (anInteraction) {
551         ui->editPropertiesAction->setChecked(dynamic_cast<EditInteraction*>(anInteraction) != NULL);
552         ui->editMoveAction->setChecked(dynamic_cast<MoveNodeInteraction*>(anInteraction) != NULL);
553         ui->editRotateAction->setChecked(dynamic_cast<RotateInteraction*>(anInteraction) != NULL);
554         ui->editScaleAction->setChecked(dynamic_cast<ScaleInteraction*>(anInteraction) != NULL);
555         ui->createNodeAction->setChecked(dynamic_cast<CreateNodeInteraction*>(anInteraction) != NULL);
556         ui->createRoadAction->setChecked(dynamic_cast<CreateSingleWayInteraction*>(anInteraction) != NULL);
557         ui->createAreaAction->setChecked(dynamic_cast<CreateAreaInteraction*>(anInteraction) != NULL);
558         ui->roadExtrudeAction->setChecked(dynamic_cast<ExtrudeInteraction*>(anInteraction) != NULL);
559         ui->markBridgeAction->setChecked(dynamic_cast<BuildBridgeInteraction*>(anInteraction) != NULL);
560 
561         EditInteraction* EI = dynamic_cast<EditInteraction*>(anInteraction);
562         if (EI)
563             EI->setSnap(theSnapList);
564     } else {
565 #ifndef _MOBILE
566         theView->setCursor(QCursor(Qt::ArrowCursor));
567 #endif
568         launchInteraction(new EditInteraction(this));
569         //Q_ASSERT(theView->interaction());
570     }
571 }
572 
onCustomcontextmenurequested(const QPoint & pos)573 void MainWindow::onCustomcontextmenurequested(const QPoint & pos)
574 {
575 #ifndef _MOBILE
576     if (/*EditInteraction* ei = */dynamic_cast<EditInteraction*>(theView->interaction()) || dynamic_cast<MoveNodeInteraction*>(theView->interaction())) {
577         QMenu menu;
578 
579         //FIXME Some of these actions on WIN32-MSVC corrupts the heap.
580 
581         //QMenu editMenu(tr("Edit"));
582         //for(int i=0; i<Main->menuEdit->actions().size(); ++i) {
583         //	if (Main->menuEdit->actions()[i]->isEnabled())
584         //		editMenu.addAction(Main->menuEdit->actions()[i]);
585         //}
586         //if (editMenu.actions().size())
587         //	menu.addMenu(&editMenu);
588 
589         //QMenu createMenu(tr("Create"));
590         //for(int i=0; i<Main->menuCreate->actions().size(); ++i) {
591         //	if (Main->menuCreate->actions()[i]->isEnabled())
592         //		createMenu.addAction(Main->menuCreate->actions()[i]);
593         //}
594         //if (createMenu.actions().size())
595         //	menu.addMenu(&createMenu);
596 
597         menu.addAction(ui->viewZoomOutAction);
598         menu.addAction(ui->viewZoomWindowAction);
599         menu.addAction(ui->viewZoomInAction);
600 
601         QMenu featureMenu(tr("Feature"));
602         for(int i=0; i<ui->menu_Feature->actions().size(); ++i) {
603             if (ui->menu_Feature->actions()[i]->isEnabled())
604                 featureMenu.addAction(ui->menu_Feature->actions()[i]);
605         }
606         if (featureMenu.actions().size())
607             menu.addMenu(&featureMenu);
608 
609 
610         QMenu nodeMenu(tr("Node"));
611         for(int i=0; i<ui->menu_Node->actions().size(); ++i) {
612             if (ui->menu_Node->actions()[i]->isEnabled())
613                 nodeMenu.addAction(ui->menu_Node->actions()[i]);
614         }
615         if (nodeMenu.actions().size())
616             menu.addMenu(&nodeMenu);
617 
618         QMenu roadMenu(tr("Way"));
619         for(int i=0; i<ui->menuRoad->actions().size(); ++i) {
620             if (ui->menuRoad->actions()[i]->isEnabled())
621                 roadMenu.addAction(ui->menuRoad->actions()[i]);
622         }
623         if (roadMenu.actions().size())
624             menu.addMenu(&roadMenu);
625 
626         QMenu relationMenu(tr("Relation"));
627         for(int i=0; i<ui->menuRelation->actions().size(); ++i) {
628             if (ui->menuRelation->actions()[i]->isEnabled())
629                 relationMenu.addAction(ui->menuRelation->actions()[i]);
630         }
631         if (relationMenu.actions().size())
632             menu.addMenu(&relationMenu);
633 
634         if (menu.actions().size()) {
635             if (menu.actions().size() == 1) {
636                 for (int i=0; i < menu.actions()[0]->menu()->actions().size(); ++i) {
637                     menu.addAction(menu.actions()[0]->menu()->actions()[i]);
638                 }
639                 menu.removeAction(menu.actions()[0]);
640             }
641             menu.exec(theView->mapToGlobal(pos));
642         }
643     }
644 #endif
645 }
646 
onImagerequested(ImageMapLayer *)647 void MainWindow::onImagerequested(ImageMapLayer *)
648 {
649 #ifndef _MOBILE
650     ++p->numImages;
651     pbImages->setRange(0, p->numImages);
652     //pbImages->setValue(0);
653     pbImages->update();
654     if (pbImages->value() < 0)
655         pbImages->setValue(0);
656 #endif
657 }
658 
onImagereceived(ImageMapLayer * aLayer)659 void MainWindow::onImagereceived(ImageMapLayer *aLayer)
660 {
661 #ifndef _MOBILE
662     if (pbImages->value() < pbImages->maximum())
663         pbImages->setValue(pbImages->value()+1);
664 #endif
665     theView->on_imageReceived(aLayer);
666 }
667 
onLoadingfinished(ImageMapLayer *)668 void MainWindow::onLoadingfinished(ImageMapLayer *)
669 {
670     p->numImages = 0;
671 #ifndef _MOBILE
672     pbImages->reset();
673 #endif
674 }
675 
updateSegmentMode(QMouseEvent * mouseEvent)676 void MainWindow::updateSegmentMode(QMouseEvent* mouseEvent)
677 {
678     g_Merk_Segment_Mode = (mouseEvent->modifiers() & Qt::AltModifier) || dynamic_cast<ExtrudeInteraction*>(theView->interaction());
679 }
680 
eventFilter(QObject *,QEvent * event)681 bool MainWindow::eventFilter(QObject */* watched */, QEvent *event)
682 {
683     switch (event->type()) {
684 
685 #ifdef GEOIMAGE
686     case QEvent::DragEnter: {
687         QDragEnterEvent* devent = static_cast<QDragEnterEvent*>(event);
688 
689         if (devent->mimeData()->hasUrls() && devent->mimeData()->urls().first().toLocalFile().endsWith(".jpg", Qt::CaseInsensitive)) {
690             p->dropTarget = NULL;
691             event->accept();
692             return true;
693         } else
694             event->ignore();
695 
696         return false;
697     }
698 
699     case QEvent::DragMove: {
700         QDragMoveEvent* devent = static_cast<QDragMoveEvent*>(event);
701 
702         QMouseEvent mE(QEvent::MouseMove, devent->pos(), Qt::LeftButton, Qt::LeftButton, qApp->keyboardModifiers());
703         theView->mouseMoveEvent(&mE);
704 
705         Node *tP;
706         for (VisibleFeatureIterator it(document()); !it.isEnd(); ++it) {
707             QList<Feature*> NoSnap;
708             if ((tP = CAST_NODE(it.get())) && tP->pixelDistance(devent->pos(), 5.01, NoSnap, theView) < 5.01) {
709                 p->dropTarget = tP;
710                 QRect acceptedRect(tP->projected().toPoint() - QPoint(3, 3), tP->projected().toPoint() + QPoint(3, 3));
711                 devent->acceptProposedAction();
712                 devent->accept(acceptedRect);
713                 return true;
714             }
715         }
716         devent->acceptProposedAction();
717         devent->accept();
718         return true;
719     }
720 
721     case QEvent::Drop: {
722         QDropEvent* devent = static_cast<QDropEvent*>(event);
723 
724         // first save the image url because the even->mimeData() releases its data very soon
725         // this is probably because the drop action ends with calling of this event
726         // so the program that started the drag-action thinks the data isn't needed anymore
727         QList<QUrl> imageUrls = devent->mimeData()->urls();
728         QStringList locFiles;
729         foreach(QUrl url, imageUrls)
730             locFiles << url.toLocalFile();
731 
732         if (locFiles.size() > 1)
733             geoImage()->loadImages(locFiles);
734         else {
735             QMenu menu(this);
736             QString imageFn = locFiles[0];
737             Coord mapC = theView->fromView(devent->pos());
738             Coord pos = GeoImageDock::getGeoDataFromImage(imageFn);
739 
740             if (pos.isNull()) {
741                 QAction *add, *load;
742                 load = menu.addAction(tr("Load image"));
743                 if (p->dropTarget)
744                     add = menu.addAction(tr("Add node position to image"));
745                 else
746                     add = menu.addAction(tr("Geotag image with this position"));
747                 menu.addSeparator();
748                 menu.addAction(tr("Cancel"));
749                 QAction* res = menu.exec(QCursor::pos());
750                 if (res == add) {
751                     if (p->dropTarget)
752                         geoImage()->addGeoDataToImage(p->dropTarget->position(), imageFn);
753                     else
754                         geoImage()->addGeoDataToImage(mapC,imageFn);
755                     geoImage()->loadImages(locFiles);
756                 } else if (res == load)
757                     geoImage()->loadImage(imageFn, mapC);
758             } else
759                 geoImage()->loadImages(locFiles);
760         }
761 
762         return false;
763     }
764 #endif // GEOIMAGE
765 
766     case QEvent::MouseButtonDblClick:  {
767         if (!theDocument)
768             return true;
769 
770         if (!theView->updatesEnabled())
771             return true;
772 
773         if (!theView->interaction())
774             return true;
775 
776         QMouseEvent* mevent = static_cast<QMouseEvent*>(event);
777         updateSegmentMode(mevent);
778 
779         theView->interaction()->mouseEvent(mevent);
780 
781         if (M_PREFS->getSelectModeCreation()) {
782             MoveNodeInteraction* MI = NULL;
783             if (!M_PREFS->getSeparateMoveMode()) {
784                 MI = dynamic_cast<MoveNodeInteraction*>(theView->interaction());
785             }
786             EditInteraction* EI = dynamic_cast<EditInteraction*>(theView->interaction());
787             if ((EI && EI->isIdle()) || (MI && MI->isIdle())) {
788                 if ((EI->lastSnap() && EI->lastSnap()->getType() & IFeature::LineString) || !EI->lastSnap())
789                     CreateNodeInteraction::createNode(theView->fromView(mevent->pos()), EI->lastSnap());
790                 else if (EI->lastSnap() && EI->lastSnap()->getType() == IFeature::Point) {
791                     Node* N = CAST_NODE(EI->lastSnap());
792                     CreateSingleWayInteraction* CI = new CreateSingleWayInteraction(this, N, false);
793                     N->invalidatePainter();
794                     launchInteraction(CI);
795                     theInfo->setHtml(theView->interaction()->toHtml());
796 #ifndef _MOBILE
797                     theView->setCursor(CI->cursor());
798 #endif
799                     theView->update();
800                     return true;
801                 }
802             }
803         }
804 
805         return false;
806     }
807 
808     case QEvent::MouseButtonPress: {
809         if (!theDocument)
810             return true;
811 
812         if (!theView->interaction())
813             return true;
814 
815         QMouseEvent* mevent = static_cast<QMouseEvent*>(event);
816         updateSegmentMode(mevent);
817 
818         theView->interaction()->mouseEvent(mevent);
819         if (theInfo)
820             theInfo->setHtml(theView->interaction()->toHtml());
821 
822         return false;
823     }
824 
825     case QEvent::MouseButtonRelease: {
826         if (!document())
827             return true;
828 
829         if (!theView->interaction())
830             return true;
831 
832         QMouseEvent* mevent = static_cast<QMouseEvent*>(event);
833         updateSegmentMode(mevent);
834 
835         theView->interaction()->mouseEvent(mevent);
836 
837         return false;
838     }
839 
840     case QEvent::MouseMove: {
841         if (!theDocument)
842             return true;
843 
844         if (!theView->updatesEnabled())
845             return true;
846 
847         if (!theView->interaction())
848             return false;
849 
850         QMouseEvent* mevent = static_cast<QMouseEvent*>(event);
851         updateSegmentMode(mevent);
852 
853         theView->interaction()->mouseEvent(mevent);
854 
855         if (!M_PREFS->getSeparateMoveMode()) {
856             EditInteraction* EI = dynamic_cast<EditInteraction*>(theView->interaction());
857             if (EI && EI->isIdle()) {
858                 if (EI->lastSnap() && p->theProperties->isSelected(EI->lastSnap())) {
859                     MoveNodeInteraction* MI = new MoveNodeInteraction(this);
860                     launchInteraction(MI);
861                     //                    main()->info()->setHtml(interaction()->toHtml());
862 #ifndef _MOBILE
863                     theView->setCursor(MI->cursor());
864 #endif
865                     theView->update();
866                     return false;
867                 }
868             }
869             MoveNodeInteraction* MI = dynamic_cast<MoveNodeInteraction*>(theView->interaction());
870             if (MI && !MI->lastSnap() && MI->isIdle()) {
871                 EditInteraction* EI = new EditInteraction(this);
872                 launchInteraction(EI);
873 #ifndef _MOBILE
874                 theView->setCursor(EI->cursor());
875 #endif
876                 theView->update();
877                 return false;
878             }
879 
880         }
881 
882         return false;
883     }
884 
885     case QEvent::Wheel: {
886         if (!theDocument)
887             return true;
888 
889         return false;
890     }
891 
892     case QEvent::ToolTip: {
893             QHelpEvent *helpEvent = static_cast<QHelpEvent *>(event);
894             //Coord p = p->theProjection.inverse(helpEvent->pos());
895             if (M_PREFS->getMapTooltip()) {
896                 if (!toolTip().isEmpty())
897                     QToolTip::showText(helpEvent->globalPos(), toolTip());
898                 else
899                     QToolTip::hideText();
900             }
901             return true;
902         }
903 
904     case QEvent::KeyPress: {
905             QKeyEvent *ke = static_cast< QKeyEvent* >( event );
906             switch ( ke->key() ) {
907             case Qt::Key_Space:
908                 ke->accept();
909                 theView->setBackgroundOnlyPanZoom(true);
910                 return true;
911 
912             case Qt::Key_Tab:
913                 theView->setFocus();
914                 ke->accept();
915 
916                 //            if (!isSelectionLocked())
917                 //                lockSelection();
918                 //            else
919                 {
920                     FeatureSnapInteraction* intr = dynamic_cast<FeatureSnapInteraction*>(theView->interaction());
921                     if (intr)
922                         intr->nextSnap();
923                 }
924                 return true;
925 
926             case Qt::Key_Backtab:
927                 theView->setFocus();
928                 ke->accept();
929 
930                 //            if (!isSelectionLocked())
931                 //                lockSelection();
932                 //            else
933                 {
934                     FeatureSnapInteraction* intr = dynamic_cast<FeatureSnapInteraction*>(theView->interaction());
935                     if (intr)
936                         intr->nextSnap();
937                 }
938                 return true;
939 
940             case Qt::Key_T:
941                 {
942                     theView->rotateScreen(theView->rect().center(), 15.);
943                     return true;
944                 }
945 
946             case Qt::Key_O:
947                 {
948                     CreateSingleWayInteraction* intr = dynamic_cast<CreateSingleWayInteraction*>(theView->interaction());
949                     if (!intr)
950                         return false;
951 
952                     theView->setFocus();
953                     ke->accept();
954                     intr->setSnapAngle(45.);
955 
956                     return true;
957                 }
958 
959             case Qt::Key_H:
960                 {
961                     CreateSingleWayInteraction* intr = dynamic_cast<CreateSingleWayInteraction*>(theView->interaction());
962                     if (!intr)
963                         return false;
964 
965                     theView->setFocus();
966                     ke->accept();
967                     intr->setSnapAngle(30.);
968 
969                     return true;
970                 }
971 
972             case Qt::Key_P:
973                 {
974                     CreateSingleWayInteraction* intr = dynamic_cast<CreateSingleWayInteraction*>(theView->interaction());
975                     if (!intr)
976                         return false;
977 
978                     theView->setFocus();
979                     ke->accept();
980                     intr->setParallelMode(true);
981 
982                     return true;
983                 }
984 
985             case Qt::Key_C:
986                 {
987                     CreateSingleWayInteraction* CI = dynamic_cast<CreateSingleWayInteraction*>(theView->interaction());
988                     if (CI) {
989                         theView->setFocus();
990                         ke->accept();
991                         CI->closeAndFinish();
992                     } else {
993                         CreateAreaInteraction* AI = dynamic_cast<CreateAreaInteraction*>(theView->interaction());
994                         if (AI) {
995                             theView->setFocus();
996                             ke->accept();
997                             AI->closeAndFinish();
998                         }
999                         else
1000                             return false;
1001                     }
1002                     return true;
1003                 }
1004 
1005             default:
1006                 break;
1007 
1008             }
1009         }
1010         break;
1011 
1012     case QEvent::KeyRelease: {
1013             QKeyEvent *ke = static_cast< QKeyEvent* >( event );
1014             switch ( ke->key() ) {
1015             case Qt::Key_Space:
1016                 ke->accept();
1017                 theView->setBackgroundOnlyPanZoom(false);
1018                 return true;
1019 
1020             case Qt::Key_O:
1021             case Qt::Key_H:
1022                 {
1023                     CreateSingleWayInteraction* intr = dynamic_cast<CreateSingleWayInteraction*>(theView->interaction());
1024                     if (!intr)
1025                         return false;
1026 
1027                     ke->accept();
1028                     intr->setSnapAngle(0);
1029 
1030                     return true;
1031                 }
1032 
1033             case Qt::Key_P:
1034                 {
1035                     CreateSingleWayInteraction* intr = dynamic_cast<CreateSingleWayInteraction*>(theView->interaction());
1036                     if (!intr)
1037                         return false;
1038 
1039                     ke->accept();
1040                     intr->setParallelMode(false);
1041 
1042                     return true;
1043                 }
1044 
1045             default:
1046                 break;
1047             }
1048         }
1049         break;
1050 
1051     case QEvent::Leave: {
1052             if (theInfo)
1053                 theInfo->unsetHoverHtml();
1054             FeatureSnapInteraction* intr = dynamic_cast<FeatureSnapInteraction*>(theView->interaction());
1055             if (intr)
1056                 intr->clearLastSnap();
1057             theView->update();
1058         }
1059 
1060     default:
1061         break;
1062     }
1063 
1064     return false;
1065 }
1066 
1067 namespace {
1068 
AddActionsIntoManager(QtToolBarManager * manager,QWidget * widget,const QString & label)1069 void AddActionsIntoManager(QtToolBarManager* manager, QWidget* widget,
1070 		const QString& label) {
1071     foreach(QAction* a, widget->actions()) {
1072         if (!a->isSeparator() && !a->menu())
1073             manager->addAction(a, label);
1074     }
1075 }
1076 
1077 }  // namespace
1078 
createToolBarManager()1079 void MainWindow::createToolBarManager()
1080 {
1081     toolBarManager = new QtToolBarManager(this);
1082     toolBarManager->setMainWindow(this);
1083 
1084     AddActionsIntoManager(toolBarManager, ui->menuFile, tr("File"));
1085     AddActionsIntoManager(toolBarManager, ui->menuEdit, tr("Edit"));
1086     AddActionsIntoManager(toolBarManager, ui->menuView, tr("View"));
1087     AddActionsIntoManager(toolBarManager, ui->menu_Show, tr("Show"));
1088     AddActionsIntoManager(toolBarManager, ui->menuShow_directional_Arrows, tr("Directional Arrows"));
1089     AddActionsIntoManager(toolBarManager, ui->menuGps, tr("GPS"));
1090     AddActionsIntoManager(toolBarManager, ui->menuLayers, tr("Layers"));
1091     AddActionsIntoManager(toolBarManager, ui->menuCreate, tr("Create"));
1092     AddActionsIntoManager(toolBarManager, ui->menu_Feature, tr("Feature"));
1093     AddActionsIntoManager(toolBarManager, ui->menu_Node, tr("Node"));
1094     AddActionsIntoManager(toolBarManager, ui->menuRoad, tr("Way"));
1095     AddActionsIntoManager(toolBarManager, ui->menuRelation, tr("Relation"));
1096     AddActionsIntoManager(toolBarManager, ui->menuTools, tr("Tools"));
1097     AddActionsIntoManager(toolBarManager, ui->menuWindow, tr("Windows"));
1098     AddActionsIntoManager(toolBarManager, ui->menuHelp, tr("Help"));
1099 
1100     toolBarManager->addToolBar(ui->toolBar, QString());
1101 
1102     QSettings* Sets = M_PREFS->getQSettings();
1103     if (!g_Merk_Ignore_Preferences && !g_Merk_Reset_Preferences)
1104             if (Sets->contains("MainWindow/Toolbars"))
1105                 toolBarManager->restoreState(Sets->value("MainWindow/Toolbars").toByteArray());
1106 }
1107 
on_toolsToolbarsAction_triggered()1108 void MainWindow::on_toolsToolbarsAction_triggered()
1109 {
1110     QtToolBarDialog dlg(this);
1111     dlg.setToolBarManager(toolBarManager);
1112     dlg.exec();
1113 
1114     QSettings* Sets = M_PREFS->getQSettings();
1115     Sets->setValue("MainWindow/Toolbars", toolBarManager->saveState());
1116 }
1117 
createProgressDialog()1118 void MainWindow::createProgressDialog()
1119 {
1120     theProgressDialog = new QProgressDialog(this);
1121     theProgressDialog->setWindowModality(Qt::ApplicationModal);
1122     theProgressDialog->setMinimumDuration(0);
1123 
1124     theProgressBar = new QProgressBar(theProgressDialog);
1125     theProgressBar->setTextVisible(false);
1126     theProgressDialog->setBar(theProgressBar);
1127 
1128     theProgressLabel = new QLabel();
1129     theProgressLabel->setAlignment(Qt::AlignCenter);
1130     theProgressDialog->setLabel(theProgressLabel);
1131 
1132     theProgressDialog->setMaximum(11);
1133     theProgressDialog->reset();
1134 }
1135 
deleteProgressDialog()1136 void MainWindow::deleteProgressDialog()
1137 {
1138     SAFE_DELETE(theProgressDialog)
1139     theProgressBar = NULL;
1140     theProgressLabel = NULL;
1141 }
1142 
setAreaOpacity(QAction * act)1143 void MainWindow::setAreaOpacity(QAction *act)
1144 {
1145     qreal a = act->data().toDouble();
1146     M_PREFS->setAreaOpacity(int(a*100));
1147 
1148     theView->invalidate(true, true, false);
1149 }
1150 
adjustLayers(bool adjustViewport)1151 void MainWindow::adjustLayers(bool adjustViewport)
1152 {
1153     if (M_PREFS->getZoomBoris()) {
1154         ImageMapLayer* l = NULL;
1155         for (LayerIterator<ImageMapLayer*> ImgIt(theDocument); !ImgIt.isEnd(); ++ImgIt) {
1156             l = ImgIt.get();
1157             break;
1158         }
1159         if (l && l->isTiled()) {
1160             theView->projection().setProjectionType(l->projection());
1161             updateProjectionMenu();
1162         }
1163     }
1164     if (adjustViewport) {
1165         CoordBox theVp;
1166         theVp = theView->viewport();
1167         theView->setViewport(theVp, theView->rect());
1168     }
1169     invalidateView(true);
1170 }
1171 
invalidateView(bool UpdateDock)1172 void MainWindow::invalidateView(bool UpdateDock)
1173 {
1174     theView->setRenderOptions(p->renderOptions);
1175     theView->invalidate(true, true, true);
1176     //theLayers->updateContent();
1177     if (UpdateDock)
1178         p->theProperties->resetValues();
1179 }
1180 
toggleButtonStyle()1181 void MainWindow::toggleButtonStyle()
1182 {
1183     if (M_PREFS->getHideToolbarLabels()) {
1184         M_PREFS->setHideToolbarLabels(false);
1185         setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
1186     } else {
1187         M_PREFS->setHideToolbarLabels(true);
1188         setToolButtonStyle(Qt::ToolButtonIconOnly);
1189     }
1190 }
1191 
properties()1192 PropertiesDock* MainWindow::properties()
1193 {
1194     return p->theProperties;
1195 }
1196 
info()1197 InfoDock* MainWindow::info()
1198 {
1199     return theInfo;
1200 }
1201 
1202 #ifdef GEOIMAGE
geoImage()1203 GeoImageDock* MainWindow::geoImage()
1204 {
1205     return theGeoImage;
1206 }
1207 #endif
1208 
features()1209 FeaturesDock* MainWindow::features()
1210 {
1211     return p->theFeats;
1212 }
1213 
document()1214 Document* MainWindow::document()
1215 {
1216     return theDocument;
1217 }
1218 
on_editCutAction_triggered()1219 void MainWindow::on_editCutAction_triggered()
1220 {
1221     // Export
1222     QClipboard *clipboard = QApplication::clipboard();
1223     QMimeData* md = new QMimeData();
1224 
1225     QBuffer osmBuf;
1226     osmBuf.open(QIODevice::WriteOnly);
1227 
1228     QList<Feature*> exportedFeatures = document()->exportCoreOSM(p->theProperties->selection());
1229     theDocument->exportOSM(this, &osmBuf, exportedFeatures);
1230     md->setText(QString(osmBuf.data()));
1231     md->setData(MIME_OPENSTREETMAP_XML, osmBuf.data());
1232 
1233     ImportExportKML kmlexp(theDocument);
1234     QBuffer kmlBuf;
1235     kmlBuf.open(QIODevice::WriteOnly);
1236     if (kmlexp.setDevice(&kmlBuf)) {
1237         kmlexp.export_(p->theProperties->selection());
1238         md->setData(MIME_GOOGLE_EARTH_KML, kmlBuf.data());
1239     }
1240 
1241     ExportGPX gpxexp(theDocument);
1242     QBuffer gpxBuf;
1243     gpxBuf.open(QIODevice::WriteOnly);
1244     if (gpxexp.setDevice(&gpxBuf)) {
1245         gpxexp.export_(p->theProperties->selection());
1246         md->setData(MIME_GPX, gpxBuf.data());
1247     }
1248 
1249     //Deletion
1250     QList<Feature*> Sel;
1251     for (int i=0; i<p->theProperties->selectionSize(); ++i)
1252         Sel.push_back(p->theProperties->selection(i));
1253     if (Sel.size() == 0) return;
1254 
1255     CommandList* theList  = new CommandList(MainWindow::tr("Cut Features"), Sel[0]);
1256     for (int i=0; i<Sel.size(); ++i) {
1257         QList<Feature*> Alternatives;
1258         theList->add(new RemoveFeatureCommand(document(), Sel[i], Alternatives));
1259     }
1260 
1261     if (theList->size()) {
1262         document()->addHistory(theList);
1263     }
1264     else {
1265         delete theList;
1266         return;
1267     }
1268 
1269     QString xml;
1270     QXmlStreamWriter stream(&xml);
1271     stream.setAutoFormatting(true);
1272     stream.setAutoFormattingIndent(2);
1273     stream.writeStartDocument();
1274     stream.writeStartElement("MerkaartorUndo");
1275     stream.writeAttribute("documentid", theDocument->id());
1276     theList->toXML(stream);
1277     stream.writeEndElement();
1278     stream.writeEndDocument();
1279     md->setData(MIME_MERKAARTOR_UNDO_XML, xml.toUtf8());
1280 //    qDebug() << doc.toString(2);
1281 
1282     clipboard->setMimeData(md);
1283 
1284     properties()->setSelection(0);
1285     properties()->checkMenuStatus();
1286     view()->invalidate(true, true, false);
1287 }
1288 
on_editCopyAction_triggered()1289 void MainWindow::on_editCopyAction_triggered()
1290 {
1291     QClipboard *clipboard = QApplication::clipboard();
1292     QMimeData* md = new QMimeData();
1293 
1294     QBuffer osmBuf;
1295     osmBuf.open(QIODevice::WriteOnly);
1296 
1297     QList<Feature*> exportedFeatures = document()->exportCoreOSM(p->theProperties->selection(), true);
1298     theDocument->exportOSM(this, &osmBuf, exportedFeatures);
1299     md->setText(QString(osmBuf.data()));
1300     md->setData(MIME_OPENSTREETMAP_XML, osmBuf.data());
1301 
1302     ImportExportKML kmlexp(theDocument);
1303     QBuffer kmlBuf;
1304     kmlBuf.open(QIODevice::WriteOnly);
1305     if (kmlexp.setDevice(&kmlBuf)) {
1306         kmlexp.export_(p->theProperties->selection());
1307         md->setData(MIME_GOOGLE_EARTH_KML, kmlBuf.data());
1308     }
1309 
1310     ExportGPX gpxexp(theDocument);
1311     QBuffer gpxBuf;
1312     gpxBuf.open(QIODevice::WriteOnly);
1313     if (gpxexp.setDevice(&gpxBuf)) {
1314         gpxexp.export_(p->theProperties->selection());
1315         md->setData(MIME_GPX, gpxBuf.data());
1316     }
1317 
1318     clipboard->setMimeData(md);
1319     invalidateView();
1320 }
1321 
on_editPasteFeatureAction_triggered()1322 void MainWindow::on_editPasteFeatureAction_triggered()
1323 {
1324     DrawingLayer* l = dynamic_cast<DrawingLayer*>(theDocument->getDirtyOrOriginLayer());
1325     if (!l)
1326         return;
1327 
1328     Document* doc;
1329 
1330     QClipboard *clipboard = QApplication::clipboard();
1331     if (clipboard->mimeData()->hasFormat(MIME_MERKAARTOR_UNDO_XML)) {
1332         QDomDocument theXmlDoc;
1333         if (!theXmlDoc.setContent(clipboard->mimeData()->data(MIME_MERKAARTOR_UNDO_XML))) {
1334             // Do nothing.
1335         } else {
1336             QDomElement root = theXmlDoc.firstChildElement("MerkaartorUndo");
1337             if (!root.isNull()) {
1338                 QString docId = root.attribute("documentid");
1339                 if (theDocument->id() == docId) {
1340 
1341                     QDomNodeList nl = theXmlDoc.elementsByTagName("RemoveFeatureCommand");
1342                     for (int i=0; i<nl.size(); ++i) {
1343                         nl.at(i).toElement().setAttribute("layer", l->id());
1344                     }
1345 
1346                     QString xml;
1347                     QTextStream tstr(&xml, QIODevice::ReadOnly);
1348                     root.firstChildElement("CommandList").save(tstr, 2);
1349                     QXmlStreamReader stream(xml);
1350 
1351                     CommandList* theList = CommandList::fromXML(theDocument, stream);
1352                     theList->setReversed(true);
1353                     theList->redo();
1354                     theList->setDescription("Paste Features");
1355                     theDocument->addHistory(theList);
1356 
1357                     view()->invalidate(true, true, false);
1358 
1359                     return;
1360                 }
1361             }
1362         }
1363     }
1364 
1365     if (!(doc = Document::getDocumentFromClipboard())) {
1366         dieClipboardInvalid();
1367         return;
1368     }
1369 
1370     CommandList* theList = new CommandList();
1371     theList->setDescription("Paste Features");
1372     QList<Feature*> theFeats = theDocument->mergeDocument(doc, theDocument->getDirtyOrOriginLayer(), theList);
1373 
1374     if (theList->size())
1375         document()->addHistory(theList);
1376     else
1377         delete theList;
1378 
1379     delete doc;
1380 
1381     p->theProperties->setSelection(theFeats);
1382     view()->invalidate(true, true, false);
1383 }
1384 
dieClipboardInvalid()1385 void MainWindow::dieClipboardInvalid()
1386 {
1387     QMessageBox::critical(this, tr("Clipboard invalid"), tr("Clipboard do not contain valid data."));
1388 }
1389 
on_editPasteOverwriteAction_triggered()1390 void MainWindow::on_editPasteOverwriteAction_triggered()
1391 {
1392     QList<Feature*> sel = properties()->selection();
1393     if (!sel.size())
1394         return;
1395 
1396     Document* doc;
1397     if (!(doc = Document::getDocumentFromClipboard())) {
1398         dieClipboardInvalid();
1399         return;
1400     }
1401 
1402     CommandList* theList = new CommandList();
1403     theList->setDescription("Paste tags (overwrite)");
1404 
1405     for(int i=0; i < sel.size(); ++i) {
1406         theList->add(new ClearTagsCommand(sel[i], theDocument->getDirtyOrOriginLayer(sel[i]->layer())));
1407         for (FeatureIterator k(doc); !k.isEnd(); ++k) {
1408             // Allow any<->any pasting but only takes top level feature into account
1409             if (k.get()->sizeParents())
1410                 continue;
1411             Feature::mergeTags(theDocument, theList, sel[i], k.get());
1412         }
1413     }
1414 
1415     if (theList->size())
1416         document()->addHistory(theList);
1417     else
1418         delete theList;
1419 
1420     delete doc;
1421     invalidateView();
1422 }
1423 
on_editPasteMergeAction_triggered()1424 void MainWindow::on_editPasteMergeAction_triggered()
1425 {
1426     QList<Feature*> sel = properties()->selection();
1427     if (!sel.size())
1428         return;
1429 
1430     Document* doc;
1431     if (!(doc = Document::getDocumentFromClipboard())) {
1432         dieClipboardInvalid();
1433         return;
1434     }
1435 
1436     CommandList* theList = new CommandList();
1437     theList->setDescription("Paste tags (merge)");
1438 
1439     for(int i=0; i < sel.size(); ++i) {
1440         for (FeatureIterator k(doc); !k.isEnd(); ++k) {
1441             // Allow any<->any pasting but only takes top level feature into account
1442             if (k.get()->sizeParents())
1443                 continue;
1444             Feature::mergeTags(theDocument, theList, sel[i], k.get());
1445         }
1446     }
1447 
1448     if (theList->size())
1449         document()->addHistory(theList);
1450     else
1451         delete theList;
1452 
1453     delete doc;
1454     invalidateView();
1455 }
1456 
clipboardChanged()1457 void MainWindow::clipboardChanged()
1458 {
1459     ui->editPasteFeatureAction->setEnabled(false);
1460     ui->editPasteMergeAction->setEnabled(false);
1461     ui->editPasteOverwriteAction->setEnabled(false);
1462 
1463     QClipboard *clipboard = QApplication::clipboard();
1464     //qDebug() << "Clipboard mime: " << clipboard->mimeData()->formats();
1465     QDomDocument theXmlDoc;
1466     bool ok = false;
1467     // mimeData() returns nullptr if not supported by the platform
1468     if (clipboard->mimeData()) {
1469         if (clipboard->mimeData()->hasFormat(MIME_OPENSTREETMAP_XML))
1470             ok = theXmlDoc.setContent(clipboard->mimeData()->data(MIME_OPENSTREETMAP_XML));
1471         else if (clipboard->mimeData()->hasText())
1472             ok = theXmlDoc.setContent(clipboard->text());
1473     }
1474 
1475     if (!ok) {
1476         return;
1477     }
1478 
1479     QDomElement c = theXmlDoc.documentElement();
1480 
1481     if (c.tagName() != "osm" && c.tagName() != "kml") {
1482         return;
1483     }
1484 
1485     ui->editPasteFeatureAction->setEnabled(true);
1486     ui->editPasteMergeAction->setEnabled(true);
1487     ui->editPasteOverwriteAction->setEnabled(true);
1488 }
1489 
on_editRedoAction_triggered()1490 void MainWindow::on_editRedoAction_triggered()
1491 {
1492     theDocument->redoHistory();
1493     p->theProperties->adjustSelection();
1494     invalidateView();
1495 }
1496 
on_editUndoAction_triggered()1497 void MainWindow::on_editUndoAction_triggered()
1498 {
1499     theDocument->undoHistory();
1500     p->theProperties->adjustSelection();
1501     invalidateView();
1502 }
1503 
on_editPropertiesAction_triggered()1504 void MainWindow::on_editPropertiesAction_triggered()
1505 {
1506     if (theView->interaction() && dynamic_cast<EditInteraction*>(theView->interaction()))
1507         p->theProperties->setSelection(0);
1508     theView->unlockSelection();
1509 //    theView->invalidate(true, true, false);
1510     launchInteraction(new EditInteraction(this));
1511     view()->setInteracting(false);
1512     theInfo->setHtml(theView->interaction()->toHtml());
1513 }
1514 
on_editRemoveAction_triggered()1515 void MainWindow::on_editRemoveAction_triggered()
1516 {
1517     emit remove_triggered();
1518     emit content_changed();
1519 }
1520 
on_editMoveAction_triggered()1521 void MainWindow::on_editMoveAction_triggered()
1522 {
1523     if (M_PREFS->getSeparateMoveMode()) {
1524         launchInteraction(new MoveNodeInteraction(this));
1525         theInfo->setHtml(theView->interaction()->toHtml());
1526     }
1527 }
1528 
on_editRotateAction_triggered()1529 void MainWindow::on_editRotateAction_triggered()
1530 {
1531     launchInteraction(new RotateInteraction(this));
1532     theInfo->setHtml(theView->interaction()->toHtml());
1533 }
1534 
on_editScaleAction_triggered()1535 void MainWindow::on_editScaleAction_triggered()
1536 {
1537     launchInteraction(new ScaleInteraction(this));
1538     theInfo->setHtml(theView->interaction()->toHtml());
1539 }
1540 
on_editReverseAction_triggered()1541 void MainWindow::on_editReverseAction_triggered()
1542 {
1543     emit reverse_triggered();
1544 }
1545 
on_fileImportGDALAction_triggered()1546 void MainWindow::on_fileImportGDALAction_triggered() {
1547     if (QMessageBox::warning(this,
1548         MainWindow::tr("GDAL import warning"),
1549         MainWindow::tr(
1550             "You are about to import file(s) using GDAL. This feature is rather experimental, and may or may NOT work, possibly causing a crash. Make sure you won't loose any data in case it doesn't work out.\n"
1551             "Please do report bugs in case of a crash, but don't be surprised about it.\n"
1552             "\n"
1553             "Continue with import?\n"
1554             ),
1555             QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes)
1556     {
1557         importAction(true);
1558     }
1559 }
1560 
on_fileImportAction_triggered()1561 void MainWindow::on_fileImportAction_triggered() {
1562     importAction(false);
1563 
1564 }
1565 
importAction(bool useGdal)1566 void MainWindow::importAction( bool useGdal ) {
1567     QStringList fileNames = QFileDialog::getOpenFileNames(
1568                     this,
1569                     tr("Import file"),
1570                     QString(), p->FILTER_IMPORT_SUPPORTED);
1571 
1572     if (fileNames.isEmpty())
1573         return;
1574 
1575     view()->setUpdatesEnabled(false);
1576     theLayers->setUpdatesEnabled(false);
1577 
1578     QStringList importedFiles;
1579     importFiles(theDocument, fileNames, &importedFiles, useGdal);
1580 
1581     foreach (QString currentFileName, importedFiles)
1582         M_PREFS->addRecentImport(currentFileName);
1583 
1584     updateRecentImportMenu();
1585 
1586     view()->setUpdatesEnabled(true);
1587     theLayers->setUpdatesEnabled(true);
1588 
1589     on_editPropertiesAction_triggered();
1590     theDocument->history().setActions(ui->editUndoAction, ui->editRedoAction, ui->fileUploadAction);
1591 }
1592 
mayDiscardUnsavedChanges(QWidget * aWidget)1593 static bool mayDiscardUnsavedChanges(QWidget* aWidget)
1594 {
1595     return QMessageBox::question(aWidget, MainWindow::tr("Unsaved changes"),
1596                                  MainWindow::tr("The current map contains unsaved changes that will be lost when starting a new one.\n"
1597                                                 "Do you want to cancel starting a new map or continue and discard the old changes?"),
1598                                  QMessageBox::Discard | QMessageBox::Cancel, QMessageBox::Cancel) == QMessageBox::Discard;
1599 }
1600 
mayDiscardStyleChanges(QWidget * aWidget)1601 static bool mayDiscardStyleChanges(QWidget* aWidget)
1602 {
1603     return QMessageBox::question(aWidget, MainWindow::tr("Unsaved Style changes"),
1604                                  MainWindow::tr("You have modified the current style.\n"
1605                                                 "Do you want to save your changes?"),
1606                                  QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) == QMessageBox::No;
1607 }
1608 
startBusyCursor()1609 void MainWindow::startBusyCursor() {
1610 #ifndef Q_OS_SYMBIAN
1611     QApplication::setOverrideCursor(Qt::BusyCursor);
1612 #endif
1613 }
1614 
endBusyCursor()1615 void MainWindow::endBusyCursor() {
1616 #ifndef Q_OS_SYMBIAN
1617     QApplication::restoreOverrideCursor();
1618 #endif
1619 }
1620 
importFile(Document * mapDocument,const QString & fileName,Layer * & newLayer)1621 MainWindow::ImportStatus MainWindow::importFile(Document * mapDocument, const QString& fileName, Layer*& newLayer) {
1622     MainWindow::ImportStatus result;
1623 
1624     qDebug() << "Importing file" << fileName << "using native parsers.";
1625 
1626     QString baseFileName = fileName.section('/', - 1);
1627     if (fileName.toLower().endsWith(".gpx")) {
1628         QList<TrackLayer*> theTracklayers;
1629         newLayer = new TrackLayer( baseFileName + " - " + tr("Waypoints"), baseFileName);
1630         mapDocument->add(newLayer);
1631         theTracklayers.append((TrackLayer*) newLayer);
1632         bool importOK = ImportGPX::import(this, fileName, mapDocument, theTracklayers);
1633         if (!importOK) {
1634             for (int i=0; i<theTracklayers.size(); i++) {
1635                 mapDocument->remove(theTracklayers[i]);
1636                 SAFE_DELETE(theTracklayers[i]);
1637             }
1638             newLayer = NULL;
1639         } else {
1640             if (!newLayer->size()) {
1641                 mapDocument->remove(newLayer);
1642                 SAFE_DELETE(newLayer);
1643             }
1644             for (int i=1; i<theTracklayers.size(); i++) {
1645                 if (theTracklayers[i]->name().isEmpty())
1646                     theTracklayers[i]->setName(QString(baseFileName + " - " + tr("Track %1").arg(i)));
1647                 if (importOK && M_PREFS->getAutoExtractTracks()) {
1648                     theTracklayers[i]->extractLayer();
1649                 }
1650             }
1651         }
1652 	    result = importOK ? IMPORT_OK : IMPORT_ERROR;
1653     }
1654     else if (fileName.toLower().endsWith(".osm")) {
1655         newLayer = new DrawingLayer( baseFileName );
1656         mapDocument->add(newLayer);
1657         result = importOSM(this, fileName, mapDocument, newLayer) ? IMPORT_OK : IMPORT_ERROR;
1658     }
1659 #ifndef FRISIUS_BUILD
1660     else if (fileName.toLower().endsWith(".osc")) {
1661         if (g_Merk_Frisius) {
1662             newLayer = new DrawingLayer( baseFileName );
1663             mapDocument->add(newLayer);
1664         } else {
1665             newLayer = mapDocument->getDirtyOrOriginLayer();
1666         }
1667         result = mapDocument->importOSC(fileName, (DrawingLayer*)newLayer) ? IMPORT_OK : IMPORT_ERROR;
1668     }
1669 #endif
1670     else if (fileName.toLower().endsWith(".ngt")) {
1671         newLayer = new TrackLayer( baseFileName );
1672         newLayer->setUploadable(false);
1673         mapDocument->add(newLayer);
1674         bool importOK = importNGT(this, fileName, mapDocument, newLayer);
1675         if (importOK && M_PREFS->getAutoExtractTracks()) {
1676             ((TrackLayer *)newLayer)->extractLayer();
1677         }
1678         result = importOK ? IMPORT_OK : IMPORT_ERROR;
1679     }
1680     else if (fileName.toLower().endsWith(".nmea") || (fileName.toLower().endsWith(".nma"))) {
1681         newLayer = new TrackLayer( baseFileName );
1682         newLayer->setUploadable(false);
1683         mapDocument->add(newLayer);
1684         bool importOK = mapDocument->importNMEA(fileName, (TrackLayer *)newLayer);
1685         if (importOK && M_PREFS->getAutoExtractTracks()) {
1686             ((TrackLayer *)newLayer)->extractLayer();
1687         }
1688         result = importOK ? IMPORT_OK : IMPORT_ERROR;
1689     }
1690     else if (fileName.toLower().endsWith(".kml")) {
1691         if (QMessageBox::warning(this, MainWindow::tr("Big Fat Copyright Warning"),
1692                  MainWindow::tr(
1693                  "You are trying to import a KML file. Please be aware that:\n"
1694                  "\n"
1695                  " - You cannot import to OSM a KML file created from Google Earth. While you might\n"
1696                  "   think that nodes you created from GE are yours, they are not!\n"
1697                  "   They are still a derivative work from GE, and, as such, cannot be used in OSM.\n"
1698                  "\n"
1699                  " - If you downloaded it from the Internet, chances are that there is a copyright on it.\n"
1700                  "   Please be absolutely sure that using those data in OSM is permitted by the author, or\n"
1701                  "   that the data is public domain.\n"
1702                  "\n"
1703                  "If unsure, please seek advice on the \"legal\" or \"talk\" openstreetmap mailing lists.\n"
1704                  "\n"
1705                  "Are you absolutely sure this KML can legally be imported in OSM?"),
1706                  QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes)
1707         {
1708             newLayer = new DrawingLayer( baseFileName );
1709             newLayer->setUploadable(false);
1710             mapDocument->add(newLayer);
1711             result = mapDocument->importKML(fileName, (TrackLayer *)newLayer) ? IMPORT_OK : IMPORT_ERROR;
1712         } else
1713             result = IMPORT_ABORTED;
1714     }
1715     else if (fileName.toLower().endsWith(".csv")) {
1716 #ifndef Q_OS_SYMBIAN
1717         QApplication::restoreOverrideCursor();
1718 #endif
1719         newLayer = new DrawingLayer( baseFileName );
1720         newLayer->setUploadable(false);
1721         mapDocument->add(newLayer);
1722         result = mapDocument->importCSV(fileName, (DrawingLayer*)newLayer) ? IMPORT_OK : IMPORT_ERROR;
1723     }
1724 #ifdef USE_PROTOBUF
1725     else if (fileName.toLower().endsWith(".pbf")) {
1726         newLayer = new DrawingLayer( baseFileName );
1727         mapDocument->add(newLayer);
1728         result = mapDocument->importPBF(fileName, (DrawingLayer*)newLayer) ? IMPORT_OK : IMPORT_ERROR;
1729     }
1730 #endif
1731     else {
1732         qDebug() << "File type not recognized. If this file was recognized by previous versions, use Import GDAL function.";
1733         result = IMPORT_ERROR;
1734     }
1735 
1736     if (result == IMPORT_ERROR && newLayer) {
1737         mapDocument->remove(newLayer);
1738         SAFE_DELETE(newLayer);
1739     }
1740 
1741     return result;
1742 }
1743 
importFileUsingGDAL(Document * mapDocument,const QString & fileName,Layer * & newLayer)1744 MainWindow::ImportStatus MainWindow::importFileUsingGDAL( Document* mapDocument, const QString& fileName, Layer*& newLayer ) {
1745     MainWindow::ImportStatus result;
1746 
1747     qDebug() << "Importing file" << fileName << "using GDAL.";
1748 
1749     QString baseFileName = fileName.section('/', - 1);
1750     newLayer = new DrawingLayer( baseFileName );
1751     newLayer->setUploadable(false);
1752     mapDocument->add(newLayer);
1753     result = mapDocument->importGDAL(fileName, (DrawingLayer*)newLayer) ? IMPORT_OK : IMPORT_ERROR;
1754     if (result == IMPORT_ERROR) {
1755         mapDocument->remove(newLayer);
1756         SAFE_DELETE(newLayer);
1757     }
1758 
1759     return result;
1760 }
1761 
1762 /**
1763  * Auxilary function to test the import functionality. May be removed when the
1764  * import is decoupled from the whole app, but as of now that is too much work.
1765  */
testImport(const QString filePath)1766 bool MainWindow::testImport(const QString filePath)
1767 {
1768     qDebug() << "Testing import with file: " << filePath;
1769     Layer* newLayer = nullptr;
1770     importFileUsingGDAL(theDocument, filePath, newLayer);
1771     if (newLayer == nullptr) {
1772         qDebug() << "FAILED";
1773         return false;
1774     } else {
1775         QRectF bbox = newLayer->boundingBox();
1776         int elements = newLayer->size();
1777         // Our test data are contained in this rectangle; only rough location is tested, mostly to check for swapper coordinates
1778         QRectF testRect = QRectF(QPointF(4,50) ,  QPointF(5,51) );
1779         // Return true if nonzero elements were imported and they are contained in our testRect
1780         return (elements > 0) && testRect.contains(bbox);
1781     }
1782 }
1783 
1784 
importFiles(Document * mapDocument,const QStringList & fileNames,QStringList * importedFileNames,bool useGdal)1785 bool MainWindow::importFiles(Document * mapDocument, const QStringList & fileNames, QStringList * importedFileNames, bool useGdal )
1786 {
1787     createProgressDialog();
1788 #ifndef Q_OS_SYMBIAN
1789     QApplication::setOverrideCursor(Qt::BusyCursor);
1790 #endif
1791 
1792     bool foundImport = false;
1793 
1794     QStringList filesToProcess = fileNames;
1795 
1796 #ifdef GEOIMAGE
1797     /* Geotagged images should be imported as a group, let's do it first. */
1798     QStringList images = fileNames.filter(".jpg", Qt::CaseInsensitive);
1799     if (!images.isEmpty()) {
1800         theGeoImage->loadImages(images);
1801         QString cur;
1802         foreach (cur, images)
1803             filesToProcess.removeAll(cur);
1804         foundImport = true;
1805     }
1806 #endif
1807 
1808     QStringListIterator it(filesToProcess);
1809     while (it.hasNext())
1810     {
1811         const QString & fn = it.next();
1812 
1813         Layer* newLayer = NULL;
1814         // TODO: The passing mechanism of newLayer is evil black magic.
1815         ImportStatus fileImportResult;
1816         if (useGdal)
1817             fileImportResult = importFileUsingGDAL(mapDocument, fn, newLayer);
1818         else
1819             fileImportResult = importFile(mapDocument, fn, newLayer);
1820 
1821         switch (fileImportResult) {
1822             case IMPORT_OK:
1823                 foundImport = true;
1824 
1825                 if (importedFileNames)
1826                     importedFileNames->append(fn);
1827 
1828                 emit content_changed();
1829                 break;
1830             case IMPORT_ERROR:
1831                 QMessageBox::warning(this, tr("No valid file"), tr("%1 could not be opened.").arg(fn));
1832             case IMPORT_ABORTED:
1833                 // noop
1834                 break;
1835         }
1836     }
1837     endBusyCursor();
1838     deleteProgressDialog();
1839     invalidateView(false);
1840 
1841     return foundImport;
1842 }
1843 
loadUrl(const QString & urlString)1844 void MainWindow::loadUrl(const QString& urlString)
1845 {
1846     qDebug() << "Loading url: " << urlString;
1847     loadUrl(QUrl(urlString));
1848 }
1849 
loadUrl(const QUrl & theUrl)1850 void MainWindow::loadUrl(const QUrl& theUrl)
1851 {
1852     activateWindow();
1853 
1854 #ifdef QT5
1855     QUrlQuery theQuery(theUrl);
1856 #define theQuery theQuery
1857 #else
1858 #define theQuery theUrl
1859 #endif
1860     if (theUrl.path() == "/load_object") {
1861         QString obj = theQuery.queryItemValue("objects");
1862         IFeature::FId mId;
1863         if (obj.startsWith("n")) {
1864             obj.remove("n");
1865             mId.type = IFeature::Point;
1866             mId.numId = obj.toLongLong();
1867         } else if (obj.startsWith("w")) {
1868             obj.remove("w");
1869             mId.type = IFeature::LineString;
1870             mId.numId = obj.toLongLong();
1871         } else if (obj.startsWith("r")) {
1872             obj.remove("r");
1873             mId.type = IFeature::LineString;
1874             mId.numId = obj.toLongLong();
1875         } else {
1876             QMessageBox::critical(this, tr("Incoming Remote Control Request"),
1877                 tr("Wanted to load object '%1', but don't know how to do that. Sorry.").arg(theUrl.toString()));
1878             return;
1879         }
1880         Feature* F = theDocument->getFeature(mId);
1881         if (!F) {
1882             /* The feature is missing, download it first. */
1883             createProgressDialog();
1884             downloadFeature(this, mId, theDocument, NULL);
1885             deleteProgressDialog();
1886             F = theDocument->getFeature(mId);
1887         }
1888         /* The feature is on our map, just select it. */
1889         if (F) { /* If F existed, e.g., was not deleted*/
1890           if (theView) {
1891             theView->setViewport(F->boundingBox(), theView->rect());
1892             on_fileDownloadMoreAction_triggered();
1893           }
1894           properties()->setSelection(0);
1895           properties()->addSelection(F);
1896           emit content_changed();
1897         }
1898     } else if (theUrl.path() == "/add_node") {
1899         qreal lat = theQuery.queryItemValue("lat").toDouble();
1900         qreal lon = theQuery.queryItemValue("lon").toDouble();
1901 	QString addtagsstring=theQuery.queryItemValue("addtags");
1902 	Coord pos=Coord(lon,lat);
1903 	Node* N;
1904 	CommandList* theList;
1905 	Layer* l=document()->getDirtyOrOriginLayer();
1906         N = g_backend.allocNode(g_Merk_MainWindow->document()->getDirtyOrOriginLayer(), pos);
1907 	N->setDirtyLevel(1);
1908 	QString poiName=".";
1909 	if (addtagsstring.size()>0) {
1910 	  QStringList addtags = QUrl::fromPercentEncoding(addtagsstring.toUtf8()).split("|");
1911 	  foreach (const QString &tag, addtags) {
1912 	    QStringList kv = tag.split("=");
1913 	    QString k=kv[0];
1914 	    QString v;
1915 	    if (kv.size()>0) {
1916 	      v=kv[1];
1917 	      if (k=="name") poiName=v;
1918 	    }
1919 	    N->setTag(k,v);
1920 	  }
1921 	}
1922 	theList  = new CommandList(MainWindow::tr("Remote add POI %1 %2").arg(N->id().numId).arg(poiName), N);
1923 	theList->add(new AddFeatureCommand(l,N,true));
1924 	g_Merk_MainWindow->properties()->setSelection(0);
1925 	N->updateMeta();
1926 	document()->addHistory(theList);
1927         properties()->setSelection(0);
1928 	properties()->addSelection(N);
1929 	theView->invalidate(true, true, false);
1930 	CoordBox cb;
1931 	cb = N->boundingBox();
1932 	if (!cb.isNull()) {
1933 	  CoordBox mini(cb.center()-COORD_ENLARGE, cb.center()+COORD_ENLARGE);
1934 	  cb.merge(mini);
1935 	  cb = cb.zoomed(1.1);
1936 	  theView->setViewport(cb, theView->rect());
1937 	}
1938         emit content_changed();
1939     } else if (theUrl.path() == "/load_and_zoom") {
1940         qreal t = theQuery.queryItemValue("top").toDouble();
1941         qreal b = theQuery.queryItemValue("bottom").toDouble();
1942         qreal r = theQuery.queryItemValue("right").toDouble();
1943         qreal l = theQuery.queryItemValue("left").toDouble();
1944 
1945         if (theView) {
1946             CoordBox vp(Coord(l,b), Coord(r,t));
1947             theView->setViewport(vp, theView->rect());
1948             on_fileDownloadMoreAction_triggered();
1949         }
1950         properties()->setSelection(0);
1951 
1952         Feature* F;
1953         IFeature::FId mId;
1954         QString sel = theQuery.queryItemValue("select");
1955         if (!sel.isNull()) {
1956             QStringList sl = sel.split(",");
1957             foreach (QString f, sl) {
1958                 if (f.startsWith("node")) {
1959                     f.remove("node");
1960                     mId.type = IFeature::Point;
1961                     mId.numId = f.toLongLong();
1962                 } else if (f.startsWith("way")) {
1963                     f.remove("way");
1964                     mId.type = IFeature::LineString;
1965                     mId.numId = f.toLongLong();
1966                 } else if (f.startsWith("relation")) {
1967                     f.remove("relation");
1968                     mId.type = IFeature::OsmRelation;
1969                     mId.numId = f.toLongLong();
1970                 }
1971                 F = theDocument->getFeature(mId);
1972                 if (F)
1973                     properties()->addSelection(F);
1974             }
1975         }
1976     } else {
1977         QMessageBox::critical(this, tr("Incoming Remote control request"), tr("Unknown action url: %1").arg(theUrl.toString()));
1978     }
1979 #undef theQuery
1980 }
1981 
on_fileOpenAction_triggered()1982 void MainWindow::on_fileOpenAction_triggered()
1983 {
1984     if (hasUnsavedChanges() && !mayDiscardUnsavedChanges(this))
1985         return;
1986 
1987     QString fileName = QFileDialog::getOpenFileName(this, tr("Open file"), QString(), p->FILTER_OPEN_NATIVE );
1988 
1989     if (!fileName.isNull())
1990         loadDocument(fileName);
1991 }
1992 
on_fileUploadAction_triggered()1993 void MainWindow::on_fileUploadAction_triggered()
1994 {
1995 
1996     if (QString(qVersion()) < "4.3.3")
1997     {
1998         if (QMessageBox::question(this,
1999             tr("Old Qt version detected"),
2000             tr("Your setup uses Qt %1, which contains various known errors in uploading "
2001             "data to OpenStreetMap leading to 401 server response codes. Are you sure you want to continue (which is not "
2002             "recommended).\n"
2003             "For more information see http://wiki.openstreetmap.org/index.php/Problem_uploading_with_Merkaartor").arg(qVersion()),QMessageBox::Yes|QMessageBox::No) != QMessageBox::Yes)
2004             return;
2005     }
2006 
2007     while (M_PREFS->getOsmUser().isEmpty()) {
2008         int ret = QMessageBox::warning(this, tr("Upload OSM"), tr("You don't seem to have specified your\n"
2009             "OpenStreetMap username and password.\nDo you want to do this now?"), QMessageBox::Yes | QMessageBox::No);
2010         if (ret == QMessageBox::Yes) {
2011             toolsPreferencesAction_triggered(true);
2012         } else
2013             return;
2014     }
2015     on_editPropertiesAction_triggered();
2016     syncOSM(M_PREFS->getOsmApiUrl(), M_PREFS->getOsmUser(), M_PREFS->getOsmPassword());
2017 
2018     theDocument->history().updateActions();
2019     theDirty->updateList();
2020     invalidateView();
2021 }
2022 
warnMapDownloadFailed()2023 void MainWindow::warnMapDownloadFailed() {
2024     QMessageBox::warning(this, tr("Error downloading"), tr("The map could not be downloaded"));
2025 }
2026 
on_fileDownloadAction_triggered()2027 void MainWindow::on_fileDownloadAction_triggered()
2028 {
2029     createProgressDialog();
2030 
2031     if (downloadOSM(this, theView->viewport(), theDocument)) {
2032         on_editPropertiesAction_triggered();
2033     } else
2034         warnMapDownloadFailed();
2035 
2036     deleteProgressDialog();
2037 
2038     updateBookmarksMenu();
2039 
2040     emit content_changed();
2041 }
2042 
on_fileDownloadMoreAction_triggered()2043 void MainWindow::on_fileDownloadMoreAction_triggered()
2044 {
2045     createProgressDialog();
2046 
2047     if (!downloadMoreOSM(this, theView->viewport(), theDocument)) {
2048         warnMapDownloadFailed();
2049     }
2050 
2051     deleteProgressDialog();
2052 
2053     emit content_changed();
2054 }
2055 
on_layersMapdustAction_triggered()2056 void MainWindow::on_layersMapdustAction_triggered()
2057 {
2058     SpecialLayer* sl = NULL;
2059     for (int i=0; i<theDocument->layerSize(); ++i) {
2060         if (theDocument->getLayer(i)->classType() == Layer::MapDustLayer) {
2061             sl = dynamic_cast<SpecialLayer*>(theDocument->getLayer(i));
2062             while (sl->size())
2063             {
2064                 sl->deleteFeature(sl->get(0));
2065             }
2066         }
2067     }
2068 
2069     createProgressDialog();
2070 
2071     if (!::downloadMapdust(this, theView->viewport(), theDocument, sl)) {
2072         QMessageBox::warning(this, tr("Error downloading MapDust"), tr("The MapDust bugs could not be downloaded"));
2073     }
2074 
2075     deleteProgressDialog();
2076 }
2077 
downloadFeatures(const QList<Feature * > & aDownloadList)2078 void MainWindow::downloadFeatures(const QList<Feature*>& aDownloadList)
2079 {
2080     createProgressDialog();
2081 
2082     if (!::downloadFeatures(this, aDownloadList, theDocument)) {
2083         QMessageBox::warning(this, tr("Error downloading"), tr("The map could not be downloaded"));
2084     }
2085 
2086     deleteProgressDialog();
2087 
2088     emit content_changed();
2089 
2090 }
2091 
on_fileWorkOfflineAction_triggered()2092 void MainWindow::on_fileWorkOfflineAction_triggered()
2093 {
2094     M_PREFS->setOfflineMode(!M_PREFS->getOfflineMode());
2095     updateMenu();
2096 }
2097 
on_filePrintAction_triggered()2098 void MainWindow::on_filePrintAction_triggered()
2099 {
2100     NativeRenderDialog osmR(theDocument, theView->viewport(), this);
2101     osmR.exec();
2102 }
2103 
on_filePropertiesAction_triggered()2104 void MainWindow::on_filePropertiesAction_triggered()
2105 {
2106     QDialog dlg(this);
2107     Ui::PropertiesDialog Prop;
2108     Prop.setupUi(&dlg);
2109 
2110     QString h;
2111     h += theView->toPropertiesHtml();
2112     h += "<br/>";
2113     h += theDocument->toPropertiesHtml();
2114     Prop.textBrowser->setHtml(h);
2115 
2116     dlg.exec();
2117 }
2118 
on_helpAboutAction_triggered()2119 void MainWindow::on_helpAboutAction_triggered()
2120 {
2121     QDialog dlg(this);
2122     Ui::AboutDialog About;
2123     About.setupUi(&dlg);
2124     dlg.setWindowFlags(dlg.windowFlags() & ~Qt::WindowContextHelpButtonHint);
2125     dlg.setWindowFlags(dlg.windowFlags() | Qt::MSWindowsFixedSizeDialogHint);
2126     About.Version->setText(About.Version->text().arg(STRINGIFY(REVISION)));
2127     About.QTVersion->setText(About.QTVersion->text().arg(qVersion()).arg(QT_VERSION_STR));
2128     PJ_INFO projVer = proj_info();
2129     About.ProjVersion->setText(About.ProjVersion->text().arg(QString("%1.%2.%3").arg(projVer.major).arg(projVer.minor).arg(projVer.patch)));
2130     About.GdalVersion->setText(About.GdalVersion->text().arg(GDAL_RELEASE_NAME));
2131 
2132     QFile ct(":/Utils/CHANGELOG");
2133     ct.open(QIODevice::ReadOnly);
2134     QTextStream cl(&ct);
2135     About.txtChangelog->setPlainText(cl.readAll());
2136     QPixmap px(":/Utils/Merkaartor_About.png");
2137     About.pxIcon->setPixmap(px);
2138     About.lblUrl->setOpenExternalLinks(true);
2139     dlg.exec();
2140 }
2141 
on_viewZoomAllAction_triggered()2142 void MainWindow::on_viewZoomAllAction_triggered()
2143 {
2144     QPair<bool, CoordBox> BBox(theDocument->boundingBox());
2145     if (BBox.first) {
2146         BBox.second.resize(1.01);
2147         theView->setViewport(BBox.second, theView->rect());
2148         invalidateView();
2149     }
2150 }
2151 
on_viewZoomInAction_triggered()2152 void MainWindow::on_viewZoomInAction_triggered()
2153 {
2154     theView->zoomIn();
2155 }
2156 
on_viewZoomOutAction_triggered()2157 void MainWindow::on_viewZoomOutAction_triggered()
2158 {
2159     theView->zoomOut();
2160 }
2161 
on_viewZoomWindowAction_triggered()2162 void MainWindow::on_viewZoomWindowAction_triggered()
2163 {
2164     launchInteraction(new ZoomInteraction(this));
2165 }
2166 
on_viewLockZoomAction_triggered()2167 void MainWindow::on_viewLockZoomAction_triggered()
2168 {
2169     M_PREFS->setZoomBoris(!M_PREFS->getZoomBoris());
2170     SetOptionValue(p->renderOptions, RendererOptions::LockZoom, M_PREFS->getZoomBoris());
2171     ui->viewLockZoomAction->setChecked(M_PREFS->getZoomBoris());
2172     ImageMapLayer* l = NULL;
2173     for (LayerIterator<ImageMapLayer*> ImgIt(theDocument); !ImgIt.isEnd(); ++ImgIt) {
2174         l = ImgIt.get();
2175         break;
2176     }
2177     if (l && l->isTiled()) {
2178         theView->projection().setProjectionType(l->projection());
2179         theView->setViewport(theView->viewport(), theView->rect());
2180     }
2181     theView->adjustZoomToBoris();
2182     updateProjectionMenu();
2183     invalidateView();
2184 }
2185 
on_viewDownloadedAction_triggered()2186 void MainWindow::on_viewDownloadedAction_triggered()
2187 {
2188     M_PREFS->setDownloadedVisible(!M_PREFS->getDownloadedVisible());
2189     SetOptionValue(p->renderOptions, RendererOptions::DownloadedVisible, M_PREFS->getDownloadedVisible());
2190     ui->viewDownloadedAction->setChecked(M_PREFS->getDownloadedVisible());
2191     invalidateView();
2192 }
2193 
on_viewDirtyAction_triggered()2194 void MainWindow::on_viewDirtyAction_triggered()
2195 {
2196     M_PREFS->setDirtyVisible(!M_PREFS->getDirtyVisible());
2197     SetOptionValue(p->renderOptions, RendererOptions::DirtyVisible, M_PREFS->getDirtyVisible());
2198     ui->viewDirtyAction->setChecked(M_PREFS->getDirtyVisible());
2199     invalidateView();
2200 }
2201 
on_viewScaleAction_triggered()2202 void MainWindow::on_viewScaleAction_triggered()
2203 {
2204     M_PREFS->setScaleVisible(!M_PREFS->getScaleVisible());
2205     SetOptionValue(p->renderOptions, RendererOptions::ScaleVisible, M_PREFS->getScaleVisible());
2206     ui->viewScaleAction->setChecked(M_PREFS->getScaleVisible());
2207     invalidateView();
2208 }
2209 
on_viewPhotosAction_triggered()2210 void MainWindow::on_viewPhotosAction_triggered()
2211 {
2212     M_PREFS->setPhotosVisible(!M_PREFS->getPhotosVisible());
2213     SetOptionValue(p->renderOptions, RendererOptions::PhotosVisible, M_PREFS->getPhotosVisible());
2214     ui->viewPhotosAction->setChecked(M_PREFS->getPhotosVisible());
2215     invalidateView();
2216 }
2217 
on_viewShowLatLonGridAction_triggered()2218 void MainWindow::on_viewShowLatLonGridAction_triggered()
2219 {
2220     M_PREFS->setLatLonGridVisible(!M_PREFS->getLatLonGridVisible());
2221     SetOptionValue(p->renderOptions, RendererOptions::LatLonGridVisible, M_PREFS->getLatLonGridVisible());
2222     ui->viewShowLatLonGridAction->setChecked(M_PREFS->getLatLonGridVisible());
2223     invalidateView();
2224 }
2225 
on_viewStyleBackgroundAction_triggered()2226 void MainWindow::on_viewStyleBackgroundAction_triggered()
2227 {
2228     M_PREFS->setBackgroundVisible(!M_PREFS->getBackgroundVisible());
2229     SetOptionValue(p->renderOptions, RendererOptions::BackgroundVisible, M_PREFS->getBackgroundVisible());
2230     ui->viewStyleBackgroundAction->setChecked(M_PREFS->getBackgroundVisible());
2231     invalidateView();
2232 }
2233 
on_viewStyleForegroundAction_triggered()2234 void MainWindow::on_viewStyleForegroundAction_triggered()
2235 {
2236     M_PREFS->setForegroundVisible(!M_PREFS->getForegroundVisible());
2237     SetOptionValue(p->renderOptions, RendererOptions::ForegroundVisible, M_PREFS->getForegroundVisible());
2238     ui->viewStyleForegroundAction->setChecked(M_PREFS->getForegroundVisible());
2239     invalidateView();
2240 }
2241 
on_viewStyleTouchupAction_triggered()2242 void MainWindow::on_viewStyleTouchupAction_triggered()
2243 {
2244     M_PREFS->setTouchupVisible(!M_PREFS->getTouchupVisible());
2245     SetOptionValue(p->renderOptions, RendererOptions::TouchupVisible, M_PREFS->getTouchupVisible());
2246     ui->viewStyleTouchupAction->setChecked(M_PREFS->getTouchupVisible());
2247     invalidateView();
2248 }
2249 
on_viewNamesAction_triggered()2250 void MainWindow::on_viewNamesAction_triggered()
2251 {
2252     M_PREFS->setNamesVisible(!M_PREFS->getNamesVisible());
2253     SetOptionValue(p->renderOptions, RendererOptions::NamesVisible, M_PREFS->getNamesVisible());
2254     ui->viewNamesAction->setChecked(M_PREFS->getNamesVisible());
2255     invalidateView();
2256 }
2257 
on_viewVirtualNodesAction_triggered()2258 void MainWindow::on_viewVirtualNodesAction_triggered()
2259 {
2260     M_PREFS->setVirtualNodesVisible(!M_PREFS->getVirtualNodesVisible());
2261     SetOptionValue(p->renderOptions, RendererOptions::VirtualNodesVisible, M_PREFS->getVirtualNodesVisible());
2262     ui->viewVirtualNodesAction->setChecked(M_PREFS->getVirtualNodesVisible());
2263     invalidateView();
2264 }
2265 
on_viewTrackPointsAction_triggered()2266 void MainWindow::on_viewTrackPointsAction_triggered()
2267 {
2268     M_PREFS->setTrackPointsVisible(!M_PREFS->getTrackPointsVisible());
2269     SetOptionValue(p->renderOptions, RendererOptions::NodesVisible, M_PREFS->getTrackPointsVisible());
2270     ui->viewTrackPointsAction->setChecked(M_PREFS->getTrackPointsVisible());
2271     invalidateView();
2272 }
2273 
on_viewTrackSegmentsAction_triggered()2274 void MainWindow::on_viewTrackSegmentsAction_triggered()
2275 {
2276     M_PREFS->setTrackSegmentsVisible(!M_PREFS->getTrackSegmentsVisible());
2277     SetOptionValue(p->renderOptions, RendererOptions::TrackSegmentVisible, M_PREFS->getTrackSegmentsVisible());
2278     ui->viewTrackSegmentsAction->setChecked(M_PREFS->getTrackSegmentsVisible());
2279     invalidateView();
2280 }
2281 
on_viewRelationsAction_triggered()2282 void MainWindow::on_viewRelationsAction_triggered()
2283 {
2284     M_PREFS->setRelationsVisible(!M_PREFS->getRelationsVisible());
2285     SetOptionValue(p->renderOptions, RendererOptions::RelationsVisible, M_PREFS->getRelationsVisible());
2286     ui->viewRelationsAction->setChecked(M_PREFS->getRelationsVisible());
2287     invalidateView();
2288 }
2289 
on_viewGotoAction_triggered()2290 void MainWindow::on_viewGotoAction_triggered()
2291 {
2292     GotoDialog* Dlg = new GotoDialog(theView, this);
2293     if (Dlg->exec() == QDialog::Accepted) {
2294         if (!Dlg->newViewport().isNull() && !Dlg->newViewport().isEmpty()) {
2295             theView->setViewport(Dlg->newViewport(), theView->rect());
2296             invalidateView();
2297         }
2298     }
2299     delete Dlg;
2300 }
2301 
on_viewArrowsNeverAction_triggered(bool checked)2302 void MainWindow::on_viewArrowsNeverAction_triggered(bool checked)
2303 {
2304     if (checked) {
2305         M_PREFS->setDirectionalArrowsVisible(RendererOptions::ArrowsNever);
2306         p->renderOptions.arrowOptions = RendererOptions::ArrowsNever;
2307         invalidateView();
2308     }
2309 }
2310 
on_viewArrowsOnewayAction_triggered(bool checked)2311 void MainWindow::on_viewArrowsOnewayAction_triggered(bool checked)
2312 {
2313     if (checked) {
2314         M_PREFS->setDirectionalArrowsVisible(RendererOptions::ArrowsOneway);
2315         p->renderOptions.arrowOptions = RendererOptions::ArrowsOneway;
2316         invalidateView();
2317     }
2318 }
2319 
on_viewArrowsAlwaysAction_triggered(bool checked)2320 void MainWindow::on_viewArrowsAlwaysAction_triggered(bool checked)
2321 {
2322     if (checked) {
2323         M_PREFS->setDirectionalArrowsVisible(RendererOptions::ArrowsAlways);
2324         p->renderOptions.arrowOptions = RendererOptions::ArrowsAlways;
2325         invalidateView();
2326     }
2327 }
2328 
on_fileNewAction_triggered()2329 void MainWindow::on_fileNewAction_triggered()
2330 {
2331     launchInteraction(0);
2332     p->theProperties->setSelection(0);
2333 
2334     if (theDocument)
2335         saveTemplateDocument(TEMPLATE_DOCUMENT);
2336 
2337     if (!theDocument || !hasUnsavedChanges() || mayDiscardUnsavedChanges(this)) {
2338         p->theFeats->invalidate();
2339         SAFE_DELETE(theDocument);
2340         theView->setDocument(NULL);
2341         p->latSaveDirtyLevel = 0;
2342         g_feat_rndId = 0;
2343 
2344         if (M_PREFS->getHasAutoLoadDocument())
2345             loadTemplateDocument(M_PREFS->getAutoLoadDocumentFilename());
2346         else if (!g_Merk_IgnoreStartupTemplate)
2347             loadTemplateDocument(TEMPLATE_DOCUMENT);
2348 
2349         if (!theDocument) {
2350             theDocument = new Document(theLayers);
2351             theDocument->addDefaultLayers();
2352             theView->projection().setProjectionType(M_PREFS->getProjectionType());
2353             theView->setViewport(WORLD_COORDBOX, theView->rect());
2354         }
2355         theView->setDocument(theDocument);
2356         theDocument->history().setActions(ui->editUndoAction, ui->editRedoAction, ui->fileUploadAction);
2357         connect (theDocument, SIGNAL(historyChanged()), theDirty, SLOT(updateList()));
2358         connect (theDocument, SIGNAL(historyChanged()), this, SIGNAL(content_changed()));
2359         connect(theDocument, SIGNAL(imageRequested(ImageMapLayer*)),
2360                 this, SLOT(onImagerequested(ImageMapLayer*)), Qt::QueuedConnection);
2361         connect(theDocument, SIGNAL(imageReceived(ImageMapLayer*)),
2362                 this, SLOT(onImagereceived(ImageMapLayer*)), Qt::QueuedConnection);
2363         connect(theDocument, SIGNAL(loadingFinished(ImageMapLayer*)),
2364                 this, SLOT(onLoadingfinished(ImageMapLayer*)), Qt::QueuedConnection);
2365         theDirty->updateList();
2366 
2367         currentProjectFile.clear();
2368         setWindowTitle(QString("%1 - %2").arg(theDocument->title()).arg(p->title));
2369 
2370         updateProjectionMenu();
2371 
2372         emit content_changed();
2373         on_editPropertiesAction_triggered();
2374         adjustLayers(true);
2375     }
2376 
2377 #ifdef GEOIMAGE
2378     if (theGeoImage)
2379         theGeoImage->clear();
2380 #endif
2381 }
2382 
on_createDoubleWayAction_triggered()2383 void MainWindow::on_createDoubleWayAction_triggered()
2384 {
2385     launchInteraction(new CreateDoubleWayInteraction(this));
2386     theInfo->setHtml(theView->interaction()->toHtml());
2387 }
2388 
on_createRoundaboutAction_triggered()2389 void MainWindow::on_createRoundaboutAction_triggered()
2390 {
2391     launchInteraction(new CreateRoundaboutInteraction(this));
2392     theInfo->setHtml(theView->interaction()->toHtml());
2393 }
2394 
2395 namespace {
2396 
2397 // TODO: Move Qt4/Qt5 wrappers to a separate file.
getInteger(QWidget * parent,const QString & title,const QString & label,int value=0,int minimum=INT_MIN,int maximum=INT_MAX,int step=1,bool * ok=NULL)2398 int getInteger(QWidget* parent, const QString& title, const QString& label, int value = 0, int minimum = INT_MIN, int maximum = INT_MAX, int step = 1, bool* ok = NULL) {
2399 #ifdef QT5
2400     return QInputDialog::getInt(parent, title, label, value, minimum, maximum, step, ok);
2401 #else
2402     return QInputDialog::getInteger(parent, title, label, value, minimum, maximum, step, ok);
2403 #endif
2404 }
2405 
2406 }
2407 
on_createPolygonAction_triggered()2408 void MainWindow::on_createPolygonAction_triggered()
2409 {
2410     QList< QPair <QString, QString> > tags;
2411     int Sides = getInteger(this, tr("Create Polygon"), tr("Specify the number of sides"), M_PREFS->getPolygonSides(), 3);
2412     M_PREFS->setPolygonSides(Sides);
2413     launchInteraction(new CreatePolygonInteraction(this, Sides, tags));
2414     theInfo->setHtml(theView->interaction()->toHtml());
2415 }
2416 
on_createRectangleAction_triggered()2417 void MainWindow::on_createRectangleAction_triggered()
2418 {
2419     QList< QPair <QString, QString> > tags;
2420     tags << qMakePair(QString("building"), QString("yes"));
2421     launchInteraction(new CreatePolygonInteraction(this, 4, tags));
2422     theInfo->setHtml(theView->interaction()->toHtml());
2423 }
2424 
on_createRoadAction_triggered()2425 void MainWindow::on_createRoadAction_triggered()
2426 {
2427     Node * firstPoint = NULL;
2428 
2429     if (p->theProperties->selectionSize() == 1)
2430     {
2431         Feature * feature = p->theProperties->selection(0);
2432         firstPoint = dynamic_cast<Node*>(feature);
2433     }
2434 
2435     launchInteraction(new CreateSingleWayInteraction(this, firstPoint, false));
2436     theInfo->setHtml(theView->interaction()->toHtml());
2437 }
2438 
on_createCurvedRoadAction_triggered()2439 void MainWindow::on_createCurvedRoadAction_triggered()
2440 {
2441     launchInteraction(new CreateSingleWayInteraction(this, NULL, true));
2442     theInfo->setHtml(theView->interaction()->toHtml());
2443 }
2444 
on_createAreaAction_triggered()2445 void MainWindow::on_createAreaAction_triggered()
2446 {
2447     launchInteraction(new CreateAreaInteraction(this));
2448     theInfo->setHtml(theView->interaction()->toHtml());
2449 }
2450 
on_createNodeAction_triggered()2451 void MainWindow::on_createNodeAction_triggered()
2452 {
2453     launchInteraction(new CreateNodeInteraction(this));
2454     theInfo->setHtml(theView->interaction()->toHtml());
2455 }
2456 
on_markBridgeAction_triggered()2457 void MainWindow::on_markBridgeAction_triggered()
2458 {
2459     launchInteraction(new BuildBridgeInteraction(this));
2460     theInfo->setHtml(theView->interaction()->toHtml());
2461 }
2462 
on_roadJoinAction_triggered()2463 void MainWindow::on_roadJoinAction_triggered()
2464 {
2465     CommandList* theList = new CommandList(MainWindow::tr("Join Ways"), NULL);
2466     joinRoads(theDocument, theList, p->theProperties);
2467     if (theList->empty())
2468         delete theList;
2469     else
2470     {
2471         theDocument->addHistory(theList);
2472         emit content_changed();
2473         invalidateView();
2474     }
2475 }
2476 
on_roadSplitAction_triggered()2477 void MainWindow::on_roadSplitAction_triggered()
2478 {
2479     CommandList* theList = new CommandList(MainWindow::tr("Split Ways"), NULL);
2480     splitRoads(theDocument, theList, p->theProperties);
2481     if (theList->empty())
2482         delete theList;
2483     else
2484     {
2485         theDocument->addHistory(theList);
2486         emit content_changed();
2487         invalidateView();
2488     }
2489 }
2490 
on_roadBreakAction_triggered()2491 void MainWindow::on_roadBreakAction_triggered()
2492 {
2493     CommandList* theList = new CommandList(MainWindow::tr("Break Ways"), NULL);
2494     breakRoads(theDocument, theList, p->theProperties);
2495     if (theList->empty())
2496         delete theList;
2497     else
2498     {
2499         theDocument->addHistory(theList);
2500         emit content_changed();
2501         invalidateView();
2502     }
2503 }
2504 
on_roadSimplifyAction_triggered()2505 void MainWindow::on_roadSimplifyAction_triggered()
2506 {
2507     CommandList* theList = new CommandList(MainWindow::tr("Simplify Ways"), NULL);
2508     qreal threshold = 3.0; // in metres; TODO: allow user-specified threshold
2509     simplifyRoads(theDocument, theList, p->theProperties, threshold);
2510     if (theList->empty())
2511         delete theList;
2512     else
2513     {
2514         theDocument->addHistory(theList);
2515         emit content_changed();
2516         invalidateView();
2517     }
2518 }
2519 
on_featureSelectChildrenAction_triggered()2520 void MainWindow::on_featureSelectChildrenAction_triggered()
2521 {
2522     QList<Feature*> theFeatures;
2523     foreach (Feature* F, p->theProperties->selection()) {
2524         theFeatures << F;
2525         for (int i=0; i<F->size(); ++i)
2526             theFeatures << F->get(i);
2527     }
2528     p->theProperties->setSelection(theFeatures);
2529     p->theProperties->checkMenuStatus();
2530     invalidateView();
2531 }
2532 
on_featureSelectParentsAction_triggered()2533 void MainWindow::on_featureSelectParentsAction_triggered()
2534 {
2535     QList<Feature*> theFeatures;
2536     foreach (Feature* F, p->theProperties->selection()) {
2537         for (int i=0; i<F->sizeParents(); ++i) {
2538             Feature* Feat = STATIC_CAST_FEATURE(F->getParent(i));
2539             theFeatures << Feat;
2540         }
2541     }
2542     p->theProperties->setSelection(theFeatures);
2543     p->theProperties->checkMenuStatus();
2544     invalidateView();
2545 }
2546 
on_featureDownloadMissingChildrenAction_triggered()2547 void MainWindow::on_featureDownloadMissingChildrenAction_triggered()
2548 {
2549 #ifndef _MOBILE
2550     QList<Feature*> toResolve;
2551     foreach (Feature* F, p->theProperties->selection()) {
2552         if (F->notEverythingDownloaded()) {
2553             toResolve.push_back(F);
2554         }
2555     }
2556     downloadFeatures(toResolve);
2557 #endif
2558 }
2559 
on_featureDeleteAction_triggered()2560 void MainWindow::on_featureDeleteAction_triggered()
2561 {
2562     Feature* F = p->theProperties->selection(0);
2563     if (!F)
2564         return;
2565 
2566     while (F->sizeParents()) {
2567         Feature* p = (Feature*)(F->getParent(0));
2568         if (p)
2569             p->remove(F);
2570     }
2571     F->layer()->deleteFeature(F);
2572     p->theProperties->setSelection(0);
2573 
2574     emit content_changed();
2575     invalidateView();
2576 }
2577 
on_featureCommitAction_triggered()2578 void MainWindow::on_featureCommitAction_triggered()
2579 {
2580     CommandList* theList = new CommandList(MainWindow::tr("Force Feature upload"), NULL);
2581     commitFeatures(theDocument, theList, p->theProperties);
2582     if (theList->empty())
2583         delete theList;
2584     else
2585     {
2586         theDocument->addHistory(theList);
2587         invalidateView();
2588     }
2589 }
2590 
on_roadCreateJunctionAction_triggered()2591 void MainWindow::on_roadCreateJunctionAction_triggered()
2592 {
2593     CommandList* theList = new CommandList(MainWindow::tr("Create Junction"), NULL);
2594     int n = createJunction(theDocument, theList, p->theProperties, false);
2595     if (n > 1) {
2596         MDiscardableMessage dlg(view(),
2597             MainWindow::tr("Multiple intersection."),
2598             MainWindow::tr("Those roads have multiple intersections.\nDo you still want to create a junction for each one (Unwanted junctions can still be deleted afterhand)?"));
2599         if (dlg.check() != QDialog::Accepted)
2600             return;
2601     }
2602     createJunction(theDocument, theList, p->theProperties, true);
2603 
2604     if (theList->empty())
2605         delete theList;
2606     else
2607     {
2608         theDocument->addHistory(theList);
2609         invalidateView();
2610     }
2611 }
2612 
on_roadAddStreetNumbersAction_triggered()2613 void MainWindow::on_roadAddStreetNumbersAction_triggered()
2614 {
2615     CommandList* theList = new CommandList(MainWindow::tr("Add Street Numbers"), NULL);
2616 
2617     addStreetNumbers(theDocument, theList, p->theProperties);
2618 
2619     if (theList->empty())
2620         delete theList;
2621     else
2622     {
2623         theDocument->addHistory(theList);
2624         invalidateView();
2625     }
2626 }
2627 
on_roadSubdivideAction_triggered()2628 void MainWindow::on_roadSubdivideAction_triggered()
2629 {
2630 #if QT_VERSION < 0x040500
2631     {
2632         int divisions = getInteger(this, tr("Number of segments to divide into"), tr("Specify the number of segments"), 2, 99);
2633 #else
2634     QInputDialog *Dlg = new QInputDialog(this);
2635     Dlg->setInputMode(QInputDialog::IntInput);
2636     Dlg->setIntRange(2, 99);
2637     Dlg->setLabelText(tr("Number of segments to divide into"));
2638     if (Dlg->exec() == QDialog::Accepted) {
2639         int divisions = Dlg->intValue();
2640 #endif
2641         CommandList* theList = new CommandList(MainWindow::tr("Subdivide way into %1").arg(divisions), NULL);
2642         subdivideRoad(theDocument, theList, p->theProperties, divisions);
2643         if (theList->empty())
2644             delete theList;
2645         else {
2646             theDocument->addHistory(theList);
2647             invalidateView();
2648         }
2649     }
2650 #if QT_VERSION > 0x040499
2651     delete Dlg;
2652 #endif
2653 }
2654 
2655 void MainWindow::on_roadAxisAlignAction_triggered()
2656 {
2657     const unsigned int max_axes = 16;
2658     bool ok;
2659     unsigned int axes;
2660 
2661     axes = axisAlignGuessAxes(p->theProperties, view()->projection(), max_axes);
2662     if (!axes)
2663         axes = 4;
2664     axes = getInteger(this, tr("Axis Align"),
2665                                     tr("Specify the number of regular axes to align edges on (e.g. 4 for rectangular)"),
2666                                     axes, 3, max_axes, 1, &ok);
2667     if (!ok)
2668         return;
2669 
2670     // Create a command description
2671     const QString special_names[] = {
2672         tr("triangular"),
2673         tr("rectangular"),
2674         tr("pentagonal"),
2675         tr("hexagonal"),
2676         tr("heptagonal"),
2677         tr("octagonal"),
2678     };
2679     QString command_name;
2680     if (axes < 3 + (sizeof(special_names)/sizeof(special_names[0])))
2681         command_name = tr("Align onto %1 axes").arg(special_names[axes-3]);
2682     else
2683         command_name = tr("Align onto %1 regular axes").arg(axes);
2684 
2685     // Do the manipulation
2686     CommandList* theList = new CommandList(command_name, NULL);
2687     AxisAlignResult result = axisAlignRoads(theDocument, theList, p->theProperties, view()->projection(), axes);
2688     if (result != AxisAlignSuccess || theList->empty()) {
2689         if (result == AxisAlignSharpAngles)
2690             QMessageBox::critical(this, tr("Unable to align to axes"),
2691                                   tr("Align to axes operation failed. Please adjust any sharp corners and try again."));
2692         else if (result == AxisAlignFail)
2693             QMessageBox::critical(this, tr("Unable to align to axes"),
2694                                   tr("Align to axes operation failed and did not converge on a solution."));
2695         delete theList;
2696     } else {
2697         theDocument->addHistory(theList);
2698         invalidateView();
2699     }
2700 }
2701 
2702 void MainWindow::on_roadExtrudeAction_triggered()
2703 {
2704     launchInteraction(new ExtrudeInteraction(this));
2705     theInfo->setHtml(theView->interaction()->toHtml());
2706 }
2707 
2708 void MainWindow::on_roadBingExtractAction_triggered()
2709 {
2710     CommandList* theList = new CommandList(MainWindow::tr("Bing Extract"), NULL);
2711     bingExtract(theDocument, theList, p->theProperties, theView->viewport());
2712     if (theList->empty())
2713         delete theList;
2714     else
2715     {
2716         theDocument->addHistory(theList);
2717     //	theView->properties()->setSelection(F);
2718         invalidateView();
2719     }
2720 }
2721 
2722 void MainWindow::on_nodeAlignAction_triggered()
2723 {
2724     //MapFeature* F = theView->properties()->selection(0);
2725     CommandList* theList = new CommandList(MainWindow::tr("Align Nodes"), NULL);
2726     alignNodes(theDocument, theList, p->theProperties);
2727     if (theList->empty())
2728         delete theList;
2729     else
2730     {
2731         theDocument->addHistory(theList);
2732     //	theView->properties()->setSelection(F);
2733         invalidateView();
2734     }
2735 }
2736 
2737 void MainWindow::on_nodeSpreadAction_triggered()
2738 {
2739     CommandList* theList = new CommandList(MainWindow::tr("Spread Nodes"), NULL);
2740     spreadNodes(theDocument, theList, p->theProperties);
2741     if (theList->empty())
2742         delete theList;
2743     else
2744     {
2745         theDocument->addHistory(theList);
2746         invalidateView();
2747     }
2748 }
2749 
2750 void MainWindow::on_nodeMergeAction_triggered()
2751 {
2752     Feature* F = p->theProperties->selection(0);
2753     CommandList* theList = new CommandList(MainWindow::tr("Merge Nodes into %1").arg(F->id().numId), F);
2754     mergeNodes(theDocument, theList, p->theProperties);
2755     if (theList->empty())
2756         delete theList;
2757     else
2758     {
2759         theDocument->addHistory(theList);
2760         p->theProperties->setSelection(F);
2761         invalidateView();
2762     }
2763 }
2764 
2765 void MainWindow::on_nodeDetachAction_triggered()
2766 {
2767     Feature* F = p->theProperties->selection(0);
2768     CommandList* theList = new CommandList(MainWindow::tr("Detach Node %1").arg(F->id().numId), F);
2769     detachNode(theDocument, theList, p->theProperties);
2770     if (theList->empty())
2771         delete theList;
2772     else
2773     {
2774         theDocument->addHistory(theList);
2775         p->theProperties->setSelection(F);
2776         invalidateView();
2777     }
2778 }
2779 
2780 void MainWindow::on_relationAddMemberAction_triggered()
2781 {
2782     CommandList* theList = new CommandList(MainWindow::tr("Add member to relation"), NULL);
2783     addRelationMember(theDocument, theList, p->theProperties);
2784     if (theList->empty())
2785         delete theList;
2786     else {
2787         theDocument->addHistory(theList);
2788         invalidateView();
2789     }
2790 }
2791 
2792 void MainWindow::on_relationRemoveMemberAction_triggered()
2793 {
2794     CommandList* theList = new CommandList(MainWindow::tr("Remove member from relation"), NULL);
2795     removeRelationMember(theDocument, theList, p->theProperties);
2796     if (theList->empty())
2797         delete theList;
2798     else {
2799         theDocument->addHistory(theList);
2800         invalidateView();
2801     }
2802 }
2803 
2804 void MainWindow::on_relationAddToMultipolygonAction_triggered()
2805 {
2806     CommandList* theList = new CommandList(MainWindow::tr("Add to Multipolygon"), NULL);
2807     addToMultipolygon(theDocument, theList, p->theProperties);
2808     if (theList->empty())
2809         delete theList;
2810     else {
2811         theDocument->addHistory(theList);
2812         invalidateView();
2813     }
2814 }
2815 
2816 void MainWindow::on_areaJoinAction_triggered()
2817 {
2818     CommandList* theList = new CommandList(MainWindow::tr("Join areas"), NULL);
2819     joinAreas(theDocument, theList, p->theProperties);
2820     if (theList->empty())
2821         delete theList;
2822     else {
2823         theDocument->addHistory(theList);
2824         invalidateView();
2825     }
2826 }
2827 
2828 void MainWindow::on_areaSplitAction_triggered()
2829 {
2830     CommandList* theList = new CommandList(MainWindow::tr("Split area"), NULL);
2831     splitArea(theDocument, theList, p->theProperties);
2832     if (theList->empty())
2833         delete theList;
2834     else {
2835         theDocument->addHistory(theList);
2836         invalidateView();
2837     }
2838 }
2839 
2840 void MainWindow::on_areaTerraceAction_triggered()
2841 {
2842     TerraceDialog* Dlg = new TerraceDialog(this);
2843     if (Dlg->exec() == QDialog::Accepted) {
2844         int divisions = Dlg->numHouses();
2845         CommandList* theList = new CommandList(MainWindow::tr("Terrace area into %1").arg(divisions), NULL);
2846         terraceArea(theDocument, theList, p->theProperties, divisions);
2847         // Add the house numbers to the houses in the selection
2848         if (Dlg->hasHouseNumbers()) {
2849             QStringList numbers = Dlg->houseNumbers();
2850             QList<Feature*> areas = p->theProperties->selection();
2851             int i = 0;
2852             foreach (Feature* area, areas) {
2853                 if (i >= numbers.size())
2854                     break;
2855                 if (!numbers[i].isEmpty())
2856                     theList->add(new SetTagCommand(area, "addr:housenumber", numbers[i]));
2857                 ++i;
2858             }
2859         }
2860         if (theList->empty())
2861             delete theList;
2862         else {
2863             theDocument->addHistory(theList);
2864             invalidateView();
2865         }
2866     }
2867     delete Dlg;
2868 }
2869 
2870 void MainWindow::on_createRelationAction_triggered()
2871 {
2872     Relation* R = g_backend.allocRelation(document()->getDirtyOrOriginLayer());
2873     CommandList* theList = new CommandList(MainWindow::tr("Create Relation %1").arg(R->description()), R);
2874     theList->add(
2875         new AddFeatureCommand(document()->getDirtyOrOriginLayer(), R, true));
2876     for (int i = 0; i < p->theProperties->selectionSize(); ++i)
2877         theList->add(new RelationAddFeatureCommand(R, QString(), p->theProperties->selection(i)));
2878     theDocument->addHistory(theList);
2879     p->theProperties->setSelection(R);
2880     invalidateView();
2881 }
2882 
2883 void MainWindow::on_editMapStyleAction_triggered()
2884 {
2885     PaintStyleEditor* dlg = new PaintStyleEditor(this, M_STYLE->getGlobalPainter(), M_STYLE->getPainters());
2886     connect(dlg, SIGNAL(stylesApplied(GlobalPainter*, QList<Painter>* )), this, SLOT(applyPainters(GlobalPainter*, QList<Painter>* )));
2887     GlobalPainter saveGlobalPainter = M_STYLE->getGlobalPainter();
2888     QList<Painter> savePainters = M_STYLE->getPainters();
2889     if (dlg->exec() == QDialog::Accepted) {
2890         applyPainters(&dlg->theGlobalPainter, &dlg->thePainters);
2891     } else {
2892         /* FIXME: Is it really needed? The painter should be already set up. */
2893         applyPainters(&saveGlobalPainter, &savePainters);
2894     }
2895 
2896     delete dlg;
2897 }
2898 
2899 void MainWindow::applyStyles(QString NewStyle)
2900 {
2901     if (NewStyle != M_PREFS->getDefaultStyle())
2902     {
2903         if (M_STYLE->isDirty() && !mayDiscardStyleChanges(this)) {
2904             on_mapStyleSaveAction_triggered();
2905         }
2906         M_PREFS->setDefaultStyle(NewStyle);
2907         M_STYLE->loadPainters(M_PREFS->getDefaultStyle());
2908         theDocument->setPainters(M_STYLE->getPainters());
2909         invalidateView(false);
2910     }
2911 }
2912 
2913 void MainWindow::applyPainters(GlobalPainter* theGlobalPainter, QList<Painter>* thePainters)
2914 {
2915     M_STYLE->setGlobalPainter(*theGlobalPainter);
2916     M_STYLE->setPainters(*thePainters);
2917 
2918     theDocument->setPainters(*thePainters);
2919     invalidateView(false);
2920 }
2921 
2922 //MapLayer* MainWindow::activeLayer()
2923 //{
2924 ////	return theLayers->activeLayer();
2925 //	//The "active" layer is always the dirty layer
2926 //	return theDocument->getDirtyLayer();
2927 //}
2928 
2929 MapView* MainWindow::view()
2930 {
2931     return theView;
2932 }
2933 
2934 void MainWindow::on_mapStyleSaveAction_triggered()
2935 {
2936     QString f = M_STYLE->getFilename();
2937     if (f.isEmpty() || f.startsWith(":") || f.startsWith("qrc:")) {
2938         on_mapStyleSaveAsAction_triggered();
2939         return;
2940     }
2941     M_STYLE->savePainters(f);
2942 }
2943 
2944 void MainWindow::on_mapStyleSaveAsAction_triggered()
2945 {
2946     QString f;
2947     QFileDialog dlg(this, tr("Save map style"), M_PREFS->getCustomStyle(), tr("Merkaartor map style (*.mas)"));
2948     dlg.setFileMode(QFileDialog::AnyFile);
2949     dlg.setDefaultSuffix("mas");
2950     dlg.setAcceptMode(QFileDialog::AcceptSave);
2951 
2952     if (dlg.exec()) {
2953         if (dlg.selectedFiles().size())
2954             f = dlg.selectedFiles()[0];
2955     }
2956     if (!f.isNull()) {
2957         M_STYLE->savePainters(f);
2958     }
2959     updateStyleMenu();
2960 }
2961 
2962 void MainWindow::on_mapStyleLoadAction_triggered()
2963 {
2964     if (M_STYLE->isDirty() && !mayDiscardStyleChanges(this)) {
2965         on_mapStyleSaveAction_triggered();
2966     }
2967 
2968     QString f = QFileDialog::getOpenFileName(this, tr("Load map style"), QString(),
2969                                              tr("Supported formats")+" (*.mas *.css)\n" \
2970                                              + tr("Merkaartor map style (*.mas)\n")
2971                                              + tr("MapCSS stylesheet (*.css)"));
2972     if (!f.isNull()) {
2973         if (f.endsWith("css"))
2974             MapCSSPaintstyle::instance()->loadPainters(f);
2975         else {
2976             M_STYLE->loadPainters(f);
2977             document()->setPainters(M_STYLE->getPainters());
2978             invalidateView();
2979         }
2980     }
2981 }
2982 
2983 void MainWindow::on_toolsWMSServersAction_triggered()
2984 {
2985     WMSPreferencesDialog* WMSPref;
2986     WMSPref = new WMSPreferencesDialog();
2987     if (WMSPref->exec() == QDialog::Accepted) {
2988         for (LayerIterator<ImageMapLayer*> ImgIt(theDocument); !ImgIt.isEnd(); ++ImgIt)
2989             ImgIt.get()->updateWidget();
2990         adjustLayers(true);
2991     }
2992 }
2993 
2994 void MainWindow::on_toolsTMSServersAction_triggered()
2995 {
2996     TMSPreferencesDialog* TMSPref;
2997     TMSPref = new TMSPreferencesDialog();
2998     if (TMSPref->exec() == QDialog::Accepted) {
2999         for (LayerIterator<ImageMapLayer*> ImgIt(theDocument); !ImgIt.isEnd(); ++ImgIt)
3000             ImgIt.get()->updateWidget();
3001         adjustLayers(true);
3002     }
3003 }
3004 
3005 void MainWindow::on_toolsProjectionsAction_triggered()
3006 {
3007     ProjPreferencesDialog* prefDlg = new ProjPreferencesDialog();
3008     if (prefDlg->exec() == QDialog::Accepted) {
3009         updateProjectionMenu();
3010     }
3011 }
3012 
3013 void MainWindow::on_toolsFiltersAction_triggered()
3014 {
3015     FilterPreferencesDialog* prefDlg = new FilterPreferencesDialog();
3016     prefDlg->exec();
3017 //    if (prefDlg->exec() == QDialog::Accepted) {
3018 //        updateFilterMenu();
3019 //    }
3020 }
3021 
3022 void MainWindow::on_toolsResetDiscardableAction_triggered()
3023 {
3024     QSettings* Sets = M_PREFS->getQSettings();
3025     Sets->remove("DiscardableDialogs");
3026 }
3027 
3028 void MainWindow::on_toolsRebuildHistoryAction_triggered()
3029 {
3030     QMessageBox::StandardButton ret = QMessageBox::warning(this, tr("Rebuild History"), tr("An attempt will be made to rebuild the history.\nNo guarantee, though, and no Undo.\nAre you sure you want to try this? ")
3031                                                          , QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
3032     if (ret == QMessageBox::Yes) {
3033         theDocument->rebuildHistory();
3034         theDirty->updateList();
3035     }
3036 }
3037 
3038 namespace {
3039 
3040 void CollectActions(QList<QAction*>& collectedActions, const QWidget* widget) {
3041     foreach(QAction* a, widget->actions()) {
3042         if (!a->isSeparator() && !a->menu())
3043             collectedActions << a;
3044     }
3045 }
3046 
3047 }  // namespace
3048 
3049 void MainWindow::on_toolsShortcutsAction_triggered()
3050 {
3051     QList<QAction*> theActions;
3052 
3053     CollectActions(theActions, ui->menuFile);
3054     CollectActions(theActions, ui->menuEdit);
3055     CollectActions(theActions, ui->menuView);
3056     CollectActions(theActions, ui->menu_Show);
3057     CollectActions(theActions, ui->menuShow_directional_Arrows);
3058     CollectActions(theActions, ui->menuGps);
3059     CollectActions(theActions, ui->menuLayers);
3060     CollectActions(theActions, ui->menuCreate);
3061     CollectActions(theActions, ui->menu_Feature);
3062     CollectActions(theActions, ui->menu_Node);
3063     CollectActions(theActions, ui->menuRoad);
3064     CollectActions(theActions, ui->menuRelation);
3065     CollectActions(theActions, ui->menuTools);
3066     CollectActions(theActions, ui->menuWindow);
3067     CollectActions(theActions, ui->menuHelp);
3068 
3069     ActionsDialog(theActions, this).exec();
3070 }
3071 
3072 void MainWindow::toolsPreferencesAction_triggered(bool focusData)
3073 {
3074     PreferencesDialog* Pref = new PreferencesDialog(this);
3075     if (focusData)
3076         Pref->tabPref->setCurrentWidget(Pref->tabData);
3077     else
3078         Pref->tabPref->setCurrentIndex(p->lastPrefTabIndex);
3079     connect (Pref, SIGNAL(preferencesChanged(PreferencesDialog*)), this, SLOT(preferencesChanged(PreferencesDialog*)));
3080     Pref->exec();
3081     p->lastPrefTabIndex = Pref->tabPref->currentIndex();
3082 }
3083 
3084 void MainWindow::preferencesChanged(PreferencesDialog* prefs)
3085 {
3086     QString qVer = QString(qVersion()).replace('.', QChar());
3087     int iQVer = qVer.toInt();
3088     if (iQVer < 451) {
3089         QApplication::setStyle(QStyleFactory::create("skulpture"));
3090     } else {
3091         if (!M_PREFS->getMerkaartorStyle()) {
3092             if (QApplication::style()->objectName() != p->defStyle)
3093                 QApplication::setStyle(p->defStyle);
3094         } else {
3095             QApplication::setStyle(QStyleFactory::create(M_PREFS->getMerkaartorStyleString()));
3096         }
3097     }
3098     ui->mnuProjections->menuAction()->setEnabled(true);
3099     if (M_PREFS->getZoomBoris()) {
3100         ImageMapLayer* l = NULL;
3101         for (LayerIterator<ImageMapLayer*> ImgIt(theDocument); !ImgIt.isEnd(); ++ImgIt) {
3102             l = ImgIt.get();
3103             break;
3104         }
3105         if (l && l->isTiled()) {
3106             ui->mnuProjections->menuAction()->setEnabled(false);
3107             view()->projection().setProjectionType(l->projection());
3108             view()->zoom(0.99, view()->rect().center());
3109         }
3110     }
3111     if (M_PREFS->getLocalServer()) {
3112         p->theListeningServer->listen();
3113     } else {
3114         p->theListeningServer->close();
3115     }
3116 
3117     applyStyles(prefs->cbStyles->itemData(prefs->cbStyles->currentIndex()).toString());
3118     updateStyleMenu();
3119 
3120     updateMenu();
3121     launchInteraction(new EditInteraction(this));
3122     invalidateView(false);
3123 }
3124 
3125 bool MainWindow::getPathToSave(const QString& title, const QString& extension, const QString& allowedTypes, QString* path) {
3126     const QString defaultFile = QString("%1/%2.%3").arg(M_PREFS->getworkingdir()).arg(tr("untitled")).arg(extension);
3127     QFileDialog dlg(this, title, defaultFile, allowedTypes);
3128     dlg.setFileMode(QFileDialog::AnyFile);
3129     dlg.setDefaultSuffix(extension);
3130     dlg.setAcceptMode(QFileDialog::AcceptSave);
3131 
3132     if (dlg.exec() && dlg.selectedFiles().size() && !dlg.selectedFiles()[0].isEmpty()) {
3133 	*path = dlg.selectedFiles()[0];
3134         return true;
3135     }
3136     return false;
3137 }
3138 
3139 void MainWindow::on_fileSaveAsAction_triggered()
3140 {
3141     QString path;
3142     if (getPathToSave(tr("Save Merkaartor document"), "mdc", tr("Merkaartor documents Files (*.mdc)") + "\n" + tr("All Files (*)"), &path)) {
3143         saveDocument(path);
3144         M_PREFS->addRecentOpen(path);
3145         updateRecentOpenMenu();
3146     }
3147 }
3148 
3149 void MainWindow::on_fileSaveAsTemplateAction_triggered()
3150 {
3151     QString path;
3152     if (getPathToSave(tr("Save Merkaartor template document"), "mdc", tr("Merkaartor documents Files (*.mdc)") + "\n" + tr("All Files (*)"), &path)) {
3153         saveTemplateDocument(path);
3154     }
3155 }
3156 
3157 void MainWindow::on_fileSaveAction_triggered()
3158 {
3159     if (!currentProjectFile.isEmpty()) {
3160         saveDocument(currentProjectFile);
3161     } else {
3162         on_fileSaveAsAction_triggered();
3163     }
3164 }
3165 
3166 void MainWindow::doSaveDocument(QFile* file, bool asTemplate)
3167 {
3168     startBusyCursor();
3169     QXmlStreamWriter stream(file);
3170     stream.setAutoFormatting(true);
3171     stream.setAutoFormattingIndent(2);
3172     stream.writeStartDocument();
3173     stream.writeStartElement("MerkaartorDocument");
3174     stream.writeAttribute("version", "1.2");
3175     stream.writeAttribute("creator", QString("%1").arg(p->title));
3176 
3177     QProgressDialog progress("Saving document...", "Cancel", 0, 0);
3178     progress.setWindowModality(Qt::WindowModal);
3179 
3180     theDocument->toXML(stream, asTemplate, &progress);
3181     theView->toXML(stream);
3182 
3183     stream.writeEndDocument();
3184 
3185     progress.setValue(progress.maximum());
3186 
3187     theDocument->setTitle(QFileInfo(currentProjectFile).fileName());
3188     setWindowTitle(QString("%1 - %2").arg(theDocument->title()).arg(p->title));
3189 
3190     endBusyCursor();
3191 }
3192 
3193 void MainWindow::saveDocument(const QString& fn)
3194 {
3195     QFile file(fn);
3196     if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
3197         QMessageBox::critical(this, tr("Unable to open save file"), tr("%1 could not be opened for writing.").arg(fn));
3198         on_fileSaveAsAction_triggered();
3199         return;
3200     }
3201 
3202     doSaveDocument(&file);
3203     file.close();
3204     currentProjectFile = fn;
3205 
3206     p->latSaveDirtyLevel = theDocument->getDirtySize();
3207 }
3208 
3209 void MainWindow::saveTemplateDocument(const QString& fn)
3210 {
3211     QFile file(fn);
3212     if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
3213         QMessageBox::critical(this, tr("Unable to open save template document"), tr("%1 could not be opened for writing.").arg(fn));
3214         return;
3215     }
3216 
3217     doSaveDocument(&file, true);
3218     file.close();
3219 }
3220 
3221 Document* MainWindow::doLoadDocument(QFile* file)
3222 {
3223     QProgressDialog progress("Loading document...", "Cancel", 0, 0, this);
3224     progress.setWindowModality(Qt::WindowModal);
3225 
3226     QXmlStreamReader stream(file);
3227     while (stream.readNext() && stream.tokenType() != QXmlStreamReader::Invalid && stream.tokenType() != QXmlStreamReader::StartElement)
3228         ;
3229     if (stream.tokenType() != QXmlStreamReader::StartElement || stream.name() != "MerkaartorDocument") {
3230         QMessageBox::critical(this, tr("Invalid file"), tr("%1 is not a valid Merkaartor document.").arg(file->fileName()));
3231         return NULL;
3232     }
3233     double version = stream.attributes().value("version").toString().toDouble();
3234 
3235     progress.setMaximum(file->size());
3236 
3237     Document* newDoc = NULL;
3238 
3239     if (version < 2.) {
3240         stream.readNext();
3241         while(!stream.atEnd() && !stream.isEndElement()) {
3242             if (stream.name() == "MapDocument") {
3243                 newDoc = Document::fromXML(QFileInfo(*file).fileName(), stream, version, theLayers, &progress);
3244 
3245                 if (progress.wasCanceled())
3246                     break;
3247             } else if (stream.name() == "MapView") {
3248                 view()->fromXML(stream);
3249             } else if (!stream.isWhitespace()) {
3250                 qDebug() << "Main: logic error: " << stream.name() << " : " << stream.tokenType() << " (" << stream.lineNumber() << ")";
3251                 stream.skipCurrentElement();
3252             }
3253 
3254             if (progress.wasCanceled())
3255                 break;
3256 
3257             stream.readNext();
3258         }
3259     }
3260     progress.reset();
3261 
3262     updateProjectionMenu();
3263 
3264 #ifdef GEOIMAGE
3265     if (theGeoImage)
3266         theGeoImage->clear();
3267 #endif
3268     return newDoc;
3269 }
3270 
3271 void MainWindow::loadDocument(QString fn)
3272 {
3273     QFile file(fn);
3274     if (!file.open(QIODevice::ReadOnly)) {
3275         QMessageBox::critical(this, tr("Invalid file"), tr("%1 could not be opened.").arg(fn));
3276         return;
3277     }
3278 
3279     Document* newDoc = doLoadDocument(&file);
3280     file.close();
3281 
3282     if (newDoc) {
3283         theView->stopRendering();
3284         p->theProperties->setSelection(0);
3285         p->theFeats->invalidate();
3286         delete theDocument;
3287         theDocument = newDoc;
3288         theView->setDocument(theDocument);
3289         on_editPropertiesAction_triggered();
3290         theDocument->history().setActions(ui->editUndoAction, ui->editRedoAction, ui->fileUploadAction);
3291         connect (theDocument, SIGNAL(historyChanged()), theDirty, SLOT(updateList()));
3292         connect (theDocument, SIGNAL(historyChanged()), this, SIGNAL(content_changed()));
3293         connect(theDocument, SIGNAL(imageRequested(ImageMapLayer*)),
3294                 this, SLOT(onImagerequested(ImageMapLayer*)), Qt::QueuedConnection);
3295         connect(theDocument, SIGNAL(imageReceived(ImageMapLayer*)),
3296                 this, SLOT(onImagereceived(ImageMapLayer*)), Qt::QueuedConnection);
3297         connect(theDocument, SIGNAL(loadingFinished(ImageMapLayer*)),
3298                 this, SLOT(onLoadingfinished(ImageMapLayer*)), Qt::QueuedConnection);
3299         theDirty->updateList();
3300         currentProjectFile = fn;
3301         setWindowTitle(QString("%1 - %2").arg(theDocument->title()).arg(p->title));
3302         p->latSaveDirtyLevel = theDocument->getDirtySize();
3303         theView->resumeRendering();
3304     }
3305 
3306     M_PREFS->addRecentOpen(fn);
3307     updateRecentOpenMenu();
3308 
3309     emit content_changed();
3310 }
3311 
3312 void MainWindow::loadTemplateDocument(QString fn)
3313 {
3314     Document* newDoc = NULL;
3315     QFile file(fn);
3316     if (file.open(QIODevice::ReadOnly)) {
3317         newDoc = doLoadDocument(&file);
3318         file.close();
3319     }
3320 
3321     if (newDoc) {
3322         theDocument = newDoc;
3323         theDocument->setTitle(tr("untitled"));
3324     }
3325 }
3326 
3327 void MainWindow::on_exportOSMAction_triggered()
3328 {
3329     QList<Feature*> theFeatures;
3330 
3331     createProgressDialog();
3332     if (!selectExportedFeatures(theFeatures))
3333         return;
3334 
3335     QString path;
3336     if (getPathToSave(tr("Export OSM"), "osm", tr("OSM Files (*.osm)") + "\n" + tr("All Files (*)"), &path)) {
3337         QFile file(path);
3338         if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
3339             return;
3340 
3341         theDocument->exportOSM(this, &file, theFeatures);
3342         file.close();
3343     }
3344     deleteProgressDialog();
3345 }
3346 
3347 void MainWindow::on_exportOSCAction_triggered()
3348 {
3349 #ifndef FRISIUS_BUILD
3350     QString path;
3351     if (getPathToSave(tr("Export osmChange"), "osc", tr("osmChange Files (*.osc)") + "\n" + tr("All Files (*)"), &path)) {
3352         startBusyCursor();
3353         ImportExportOSC osc(document());
3354         if (osc.saveFile(path)) {
3355             osc.export_();
3356         }
3357         endBusyCursor();
3358     }
3359 #endif
3360 }
3361 
3362 
3363 void MainWindow::on_exportGPXAction_triggered()
3364 {
3365     QList<Feature*> theFeatures;
3366 
3367     createProgressDialog();
3368     if (!selectExportedFeatures(theFeatures))
3369         return;
3370 
3371     QString path;
3372     if (getPathToSave(tr("Export GPX"), "gpx", tr("GPX Files (*.gpx)") + "\n" + tr("All Files (*)"), &path)) {
3373         startBusyCursor();
3374         ExportGPX gpx(document());
3375         if (gpx.saveFile(path)) {
3376             gpx.export_(theFeatures);
3377         }
3378         endBusyCursor();
3379     }
3380     deleteProgressDialog();
3381 }
3382 
3383 void MainWindow::on_exportGDALAction_triggered()
3384 {
3385     QList<Feature*> theFeatures;
3386 
3387     createProgressDialog();
3388     if (!selectExportedFeatures(theFeatures))
3389         return;
3390     startBusyCursor();
3391     ImportExportGdal gdal(document());
3392     gdal.export_(theFeatures);
3393     endBusyCursor();
3394 
3395     deleteProgressDialog();
3396 }
3397 
3398 void MainWindow::on_exportKMLAction_triggered()
3399 {
3400     QList<Feature*> theFeatures;
3401 
3402     createProgressDialog();
3403     if (!selectExportedFeatures(theFeatures))
3404         return;
3405 
3406     QString path;
3407     if (getPathToSave(tr("Export KML"), "kml", tr("KML Files (*.kml)") + "\n" + tr("All Files (*)"), &path)) {
3408         startBusyCursor();
3409         ImportExportKML kml(document());
3410         if (kml.saveFile(path)) {
3411             kml.export_(theFeatures);
3412         }
3413         endBusyCursor();
3414     }
3415     deleteProgressDialog();
3416 }
3417 
3418 bool MainWindow::selectExportedFeatures(QList<Feature*>& theFeatures)
3419 {
3420     QDialog dlg(this);
3421     Ui::ExportDialog dlgExport;
3422     dlgExport.setupUi(&dlg);
3423     switch(M_PREFS->getExportType()) {
3424         case Export_All:
3425             dlgExport.rbAll->setChecked(true);
3426             break;
3427         case Export_Viewport:
3428             dlgExport.rbViewport->setChecked(true);
3429             break;
3430         case Export_Selected:
3431             dlgExport.rbSelected->setChecked(true);
3432             break;
3433         default:
3434             return false;
3435 
3436     }
3437     if (dlg.exec()) {
3438         if (dlgExport.rbAll->isChecked()) {
3439             for (VisibleFeatureIterator i(document()); !i.isEnd(); ++i) {
3440                 if (i.get()->notEverythingDownloaded())
3441                     continue;
3442 
3443                 theFeatures.append(i.get());
3444             }
3445             M_PREFS->setExportType(Export_All);
3446             return true;
3447         }
3448         else if (dlgExport.rbViewport->isChecked()) {
3449             CoordBox aCoordBox = view()->viewport();
3450 
3451             theFeatures.clear();
3452             for (VisibleFeatureIterator i(document()); !i.isEnd(); ++i) {
3453                 if (i.get()->notEverythingDownloaded())
3454                     continue;
3455 
3456                 if (Node* P = dynamic_cast<Node*>(i.get())) {
3457                     if (aCoordBox.contains(P->position())) {
3458                         theFeatures.append(P);
3459                     }
3460                 } else
3461                     if (Way* G = dynamic_cast<Way*>(i.get())) {
3462                         if (aCoordBox.intersects(G->boundingBox())) {
3463                             for (int j=0; j < G->size(); j++) {
3464                                 if (Node* P = dynamic_cast<Node*>(G->get(j)))
3465                                     if (!aCoordBox.contains(P->position()))
3466                                         theFeatures.append(P);
3467                             }
3468                             theFeatures.append(G);
3469                         }
3470                     } else
3471                         //FIXME Not working for relation (not made of point?)
3472                         if (Relation* G = dynamic_cast<Relation*>(i.get())) {
3473                             if (aCoordBox.intersects(G->boundingBox())) {
3474                                 for (int j=0; j < G->size(); j++) {
3475                                     if (Way* R = dynamic_cast<Way*>(G->get(j))) {
3476                                         if (!aCoordBox.contains(R->boundingBox())) {
3477                                             for (int k=0; k < R->size(); k++) {
3478                                                 if (Node* P = dynamic_cast<Node*>(R->get(k)))
3479                                                     if (!aCoordBox.contains(P->position()))
3480                                                         theFeatures.append(P);
3481                                             }
3482                                             theFeatures.append(R);
3483                                         }
3484                                     }
3485                                 }
3486                                 theFeatures.append(G);
3487                             }
3488                         }
3489             }
3490             M_PREFS->setExportType(Export_Viewport);
3491         }
3492         else if (dlgExport.rbSelected->isChecked()) {
3493             theFeatures = p->theProperties->selection();
3494             M_PREFS->setExportType(Export_Selected);
3495         }
3496 
3497         QProgressDialog* dlg = getProgressDialog();
3498         if (dlg)
3499             dlg->setWindowTitle(tr("Feature extraction"));
3500 
3501         QProgressBar* Bar = getProgressBar();
3502         if (Bar) {
3503             Bar->setTextVisible(false);
3504             Bar->setMaximum(theFeatures.size());
3505         }
3506 
3507         QLabel* Lbl = getProgressLabel();
3508         if (Lbl)
3509             Lbl->setText(tr("Extracting features..."));
3510 
3511         if (dlg)
3512             dlg->show();
3513 
3514         theFeatures = document()->exportCoreOSM(theFeatures, false, dlg);
3515         return true;
3516     }
3517     return false;
3518 }
3519 
3520 void MainWindow::on_editSelectAction_triggered()
3521 {
3522     SelectionDialog* Sel = new SelectionDialog(this);
3523 
3524     if (Sel->exec() == QDialog::Accepted) {
3525         QString out;
3526         int idx = 0;
3527         QString in = Sel->edTagQuery->text();
3528         QList<TagSelector*> terms;
3529         while (idx < in.length()) {
3530             TagSelector* t = TagSelector::parse(in, idx);
3531             if (!t) break;
3532             terms.append(t);
3533         }
3534 
3535         if (terms.count()) {
3536             out += terms[terms.count()-1]->asExpression(true);
3537             for (int i=terms.count()-2; i>=0; --i) {
3538                 out += " and parent(";
3539                 out += terms[i]->asExpression(true);
3540                 out += ") ";
3541             }
3542         } else
3543             return;
3544 
3545         qDebug() << out;
3546         TagSelector* tsel = TagSelector::parse(out);
3547         if (!tsel)
3548             return;
3549         qDebug() << tsel->asExpression(false);
3550 
3551         int selMaxResult = Sel->sbMaxResult->value();
3552 
3553         QList <Feature *> selection;
3554         int added = 0;
3555         for (VisibleFeatureIterator i(theDocument); !i.isEnd() && (!selMaxResult || added < selMaxResult); ++i) {
3556             Feature* F = i.get();
3557             if (tsel->matches(F, theView->pixelPerM())) {
3558                 selection.push_back(F);
3559                 ++added;
3560             }
3561         }
3562         p->theProperties->setMultiSelection(selection);
3563         p->theProperties->checkMenuStatus();
3564     }
3565 }
3566 
3567 void MainWindow::closeEvent(QCloseEvent * event)
3568 {
3569     if (hasUnsavedChanges() && !mayDiscardUnsavedChanges(this)) {
3570         event->ignore();
3571         return;
3572     }
3573 
3574     if (M_STYLE->isDirty() && !mayDiscardStyleChanges(this)) {
3575         on_mapStyleSaveAction_triggered();
3576     }
3577 
3578     M_PREFS->saveMainWindowState( this );
3579 //    M_PREFS->setInitialPosition(theView);
3580     M_PREFS->setworkingdir(QDir::currentPath());
3581 
3582     saveTemplateDocument(TEMPLATE_DOCUMENT);
3583     M_PREFS->save();
3584     QMainWindow::closeEvent(event);
3585 }
3586 
3587 QMenu *MainWindow::createPopupMenu()
3588 {
3589     QMenu* mnu = QMainWindow::createPopupMenu();
3590     mnu->addSeparator();
3591 
3592     QAction* toolbarstyle = new QAction(tr("Hide tool buttons labels"), mnu);
3593     toolbarstyle->setCheckable(true);
3594     toolbarstyle->setChecked(M_PREFS->getHideToolbarLabels());
3595     connect(toolbarstyle, SIGNAL(triggered()), SLOT(toggleButtonStyle()));
3596     mnu->addAction(toolbarstyle);
3597 
3598     return mnu;
3599 }
3600 
3601 void MainWindow::updateBookmarksMenu()
3602 {
3603     for(int i=ui->menuBookmarks->actions().count()-1; i > 2 ; i--) {
3604         ui->menuBookmarks->removeAction(ui->menuBookmarks->actions()[3]);
3605     }
3606 
3607     BookmarkListIterator it(*(M_PREFS->getBookmarks()));
3608     while (it.hasNext()) {
3609         it.next();
3610         if (it.value().deleted == false) {
3611             QAction* a = new QAction(it.key(), ui->menuBookmarks);
3612             ui->menuBookmarks->addAction(a);
3613         }
3614     }
3615 }
3616 
3617 void MainWindow::updateRecentOpenMenu()
3618 {
3619     for(int i=ui->menuRecentOpen->actions().count()-1; i >= 0; i--) {
3620         ui->menuRecentOpen->removeAction(ui->menuRecentOpen->actions()[0]);
3621     }
3622 
3623     if (!M_PREFS->getRecentOpen().size()) {
3624         ui->menuRecentOpen->setEnabled(false);
3625         return;
3626     }
3627 
3628     ui->menuRecentOpen->setEnabled(true);
3629     QStringList RecentOpen = M_PREFS->getRecentOpen();
3630     for (int i=0; i<RecentOpen.size(); i++) {
3631         QAction* a = new QAction(RecentOpen[i], ui->menuRecentOpen);
3632         ui->menuRecentOpen->addAction(a);
3633     }
3634 }
3635 
3636 void MainWindow::updateRecentImportMenu()
3637 {
3638     for(int i=ui->menuRecentImport->actions().count()-1; i >= 0; i--) {
3639         ui->menuRecentImport->removeAction(ui->menuRecentImport->actions()[0]);
3640     }
3641 
3642     if (!M_PREFS->getRecentImport().size()) {
3643         ui->menuRecentImport->setEnabled(false);
3644         return;
3645     }
3646 
3647     ui->menuRecentImport->setEnabled(true);
3648     QStringList RecentImport = M_PREFS->getRecentImport();
3649     for (int i=0; i<RecentImport.size(); i++) {
3650         QAction* a = new QAction(RecentImport[i], ui->menuRecentImport);
3651         ui->menuRecentImport->addAction(a);
3652     }
3653 }
3654 
3655 void MainWindow::updateProjectionMenu()
3656 {
3657 #ifndef _MOBILE
3658     SAFE_DELETE(p->projActgrp)
3659     p->projActgrp = new QActionGroup(this);
3660     bool projFound = false;
3661     foreach (ProjectionItem it, *M_PREFS->getProjectionsList()->getProjections()) {
3662         if (it.deleted)
3663             continue;
3664         QAction* a = new QAction(it.name, p->projActgrp);
3665         a->setCheckable (true);
3666         if (it.name.contains(theView->projection().getProjectionType(), Qt::CaseInsensitive)) {
3667             a->setChecked(true);
3668             projFound = true;
3669         }
3670         ui->mnuProjections->addAction(a);
3671     }
3672     if (!projFound) {
3673         QAction* a = new QAction(theView->projection().getProjectionType(), p->projActgrp);
3674         a->setCheckable (true);
3675         a->setChecked(true);
3676         ui->mnuProjections->addAction(a);
3677         M_PREFS->getProjectionsList()->addProjection(ProjectionItem(theView->projection().getProjectionType(), theView->projection().getProjectionProj4()));
3678     }
3679     connect (ui->mnuProjections, SIGNAL(triggered(QAction *)), this, SLOT(projectionTriggered(QAction *)));
3680 #endif
3681     ui->mnuProjections->menuAction()->setEnabled(true);
3682     if (M_PREFS->getZoomBoris() && theDocument) {
3683         ImageMapLayer* l = NULL;
3684         for (LayerIterator<ImageMapLayer*> ImgIt(theDocument); !ImgIt.isEnd(); ++ImgIt) {
3685             l = ImgIt.get();
3686             break;
3687         }
3688         if (l && l->isTiled())
3689             ui->mnuProjections->menuAction()->setEnabled(false);
3690     }
3691 }
3692 
3693 void MainWindow::updateStyleMenu()
3694 {
3695     for(int i=ui->menuStyles->actions().count()-1; i > 4 ; i--) {
3696         ui->menuStyles->removeAction(ui->menuStyles->actions()[5]);
3697     }
3698     p->theStyle->clearItems();
3699 
3700     QActionGroup* actgrp = new QActionGroup(this);
3701     QDir intStyles(BUILTIN_STYLES_DIR);
3702     for (int i=0; i < intStyles.entryList().size(); ++i) {
3703         QAction* a = new QAction(QString(tr("%1 (int)")).arg(intStyles.entryList().at(i)), ui->menuStyles);
3704         actgrp->addAction(a);
3705         a->setCheckable(true);
3706         a->setData(QVariant(intStyles.entryInfoList().at(i).absoluteFilePath()));
3707         ui->menuStyles->addAction(a);
3708         if (intStyles.entryInfoList().at(i).absoluteFilePath() == M_PREFS->getDefaultStyle())
3709             a->setChecked(true);
3710         p->theStyle->addItem(a);
3711     }
3712     if (!M_PREFS->getCustomStyle().isEmpty()) {
3713         QDir customStyles(M_PREFS->getCustomStyle(), "*.mas *.msz");
3714         for (int i=0; i < customStyles.entryList().size(); ++i) {
3715             QAction* a = new QAction(customStyles.entryList().at(i), ui->menuStyles);
3716             actgrp->addAction(a);
3717             a->setCheckable(true);
3718             a->setData(QVariant(customStyles.entryInfoList().at(i).absoluteFilePath()));
3719             ui->menuStyles->addAction(a);
3720             if (customStyles.entryInfoList().at(i).absoluteFilePath() == M_PREFS->getDefaultStyle())
3721                 a->setChecked(true);
3722             p->theStyle->addItem(a);
3723        }
3724     }
3725 }
3726 
3727 void MainWindow::updateWindowMenu(bool)
3728 {
3729     ui->windowPropertiesAction->setChecked(p->theProperties->isVisible());
3730     ui->windowLayersAction->setChecked(theLayers->isVisible());
3731     ui->windowInfoAction->setChecked(theInfo->isVisible());
3732     ui->windowDirtyAction->setChecked(theDirty->isVisible());
3733     ui->windowFeatsAction->setChecked(p->theFeats->isVisible());
3734     ui->windowGPSAction->setChecked(theGPS->isVisible());
3735 #ifdef GEOIMAGE
3736     ui->windowGeoimageAction->setChecked(theGeoImage->isVisible());
3737 #endif
3738     ui->windowStylesAction->setChecked(p->theStyle->isVisible());
3739 }
3740 
3741 void MainWindow::on_bookmarkAddAction_triggered()
3742 {
3743     bool ok = true;
3744     QString text;
3745 
3746     BookmarkList* Bookmarks = M_PREFS->getBookmarks();
3747     QStringList bkName;
3748     BookmarkListIterator i(*Bookmarks);
3749     while (i.hasNext()) {
3750         i.next();
3751         if (i.value().deleted == false)
3752             bkName.append(i.key());
3753     }
3754     while (ok) {
3755         text = QInputDialog::getItem(this, MainWindow::tr("Add Bookmark"),
3756                         MainWindow::tr("Specify the name of the bookmark."), bkName, 0, true, &ok);
3757         if (ok) {
3758             if (text.isEmpty()) {
3759                 QMessageBox::critical(this, tr("Invalid bookmark name"),
3760                     tr("Bookmark cannot be blank."), QMessageBox::Ok);
3761                 continue;
3762             }
3763             if (Bookmarks->contains(text)) {
3764                 QString newBk = QInputDialog::getText(this, MainWindow::tr("Warning: Bookmark name already exists"),
3765                         MainWindow::tr("Enter a new one, keep the same to overwrite or cancel."), QLineEdit::Normal,
3766                                        text, &ok);
3767                 if (ok && Bookmarks->contains(newBk)) {
3768                     for(int i=2; i < ui->menuBookmarks->actions().count(); i++) {
3769                         if (ui->menuBookmarks->actions()[i]->text() == newBk) {
3770                             ui->menuBookmarks->removeAction(ui->menuBookmarks->actions()[i]);
3771                             break;
3772                         }
3773                     }
3774                 }
3775                 text = newBk;
3776             }
3777             break;
3778         }
3779     }
3780     if (ok) {
3781         CoordBox Clip = view()->viewport();
3782         Bookmark B(text, Clip);
3783         Bookmarks->insert(text, B);
3784         M_PREFS->save();
3785 
3786         QAction* a = new QAction(text,ui-> menuBookmarks);
3787         ui->menuBookmarks->addAction(a);
3788     }
3789 }
3790 
3791 void MainWindow::on_bookmarkRemoveAction_triggered()
3792 {
3793     bool ok;
3794 
3795     BookmarkList* Bookmarks = M_PREFS->getBookmarks();
3796     QStringList bkName;
3797     BookmarkListIterator i(*Bookmarks);
3798     while (i.hasNext()) {
3799         i.next();
3800         if (i.value().deleted == false)
3801             bkName.append(i.key());
3802     }
3803     QString item = QInputDialog::getItem(this, MainWindow::tr("Remove Bookmark"),
3804                         MainWindow::tr("Select the bookmark to remove."), bkName, 0, false, &ok);
3805     if (ok) {
3806         Bookmark B = Bookmarks->value(item);
3807         B.deleted = true;
3808         Bookmarks->insert(item, B);
3809         M_PREFS->save();
3810 
3811         for(int i=2; i < ui->menuBookmarks->actions().count(); i++) {
3812             if (ui->menuBookmarks->actions()[i]->text() == item) {
3813                 ui->menuBookmarks->removeAction(ui->menuBookmarks->actions()[i]);
3814                 break;
3815             }
3816         }
3817     }
3818 }
3819 
3820 void MainWindow::bookmarkTriggered(QAction* anAction)
3821 {
3822     if (anAction == ui->bookmarkAddAction || anAction == ui->bookmarkRemoveAction)
3823         return;
3824     BookmarkList* Bookmarks = M_PREFS->getBookmarks();
3825     theView->setViewport(Bookmarks->value(anAction->text()).Coordinates, theView->rect());
3826 
3827     invalidateView();
3828 }
3829 
3830 void MainWindow::recentOpenTriggered(QAction* anAction)
3831 {
3832     if (hasUnsavedChanges() && !mayDiscardUnsavedChanges(this))
3833         return;
3834 
3835     QString fileName(anAction->text());
3836     loadDocument(fileName);
3837 }
3838 
3839 void MainWindow::recentImportTriggered(QAction* anAction)
3840 {
3841     view()->setUpdatesEnabled(false);
3842     theLayers->setUpdatesEnabled(false);
3843 
3844     QStringList fileNames(anAction->text());
3845     QStringList importedFiles;
3846     importFiles(theDocument, fileNames, &importedFiles);
3847 
3848     foreach (QString currentFileName, importedFiles)
3849         M_PREFS->addRecentImport(currentFileName);
3850 
3851     updateRecentImportMenu();
3852 
3853     view()->setUpdatesEnabled(true);
3854     theLayers->setUpdatesEnabled(true);
3855 
3856     on_editPropertiesAction_triggered();
3857     theDocument->history().setActions(ui->editUndoAction, ui->editRedoAction, ui->fileUploadAction);
3858 }
3859 
3860 #ifndef _MOBILE
3861 void MainWindow::projectionSet(const QString& prj)
3862 {
3863     if(false == theView->projection().setProjectionType(prj))
3864         QMessageBox::critical(this, tr("Invalid projection"), tr("Unable to set projection \"%1\".").arg(prj));
3865     updateProjectionMenu();
3866     theView->setViewport(theView->viewport(), theView->rect());
3867     invalidateView();
3868 }
3869 
3870 void MainWindow::projectionTriggered(QAction* anAction)
3871 {
3872     if(false == theView->projection().setProjectionType(anAction->text()))
3873         QMessageBox::critical(this, tr("Invalid projection"), tr("Unable to set projection \"%1\".").arg(anAction->text()));
3874     else
3875         M_PREFS->setProjectionType(anAction->text());
3876     theView->setViewport(theView->viewport(), theView->rect());
3877     invalidateView();
3878 }
3879 #endif
3880 
3881 void MainWindow::styleTriggered(QAction* anAction)
3882 {
3883     if (!anAction->isCheckable())
3884         return;
3885 
3886     QString NewStyle = anAction->data().toString();
3887     p->theStyle->setCurrent(anAction);
3888     applyStyles(NewStyle);
3889 }
3890 
3891 void MainWindow::on_windowPropertiesAction_triggered()
3892 {
3893     p->theProperties->setVisible(!p->theProperties->isVisible());
3894     ui->windowPropertiesAction->setChecked(p->theProperties->isVisible());
3895 }
3896 
3897 void MainWindow::on_windowLayersAction_triggered()
3898 {
3899     theLayers->setVisible(!theLayers->isVisible());
3900     ui->windowLayersAction->setChecked(theLayers->isVisible());
3901 }
3902 
3903 void MainWindow::on_windowInfoAction_triggered()
3904 {
3905     theInfo->setVisible(!theInfo->isVisible());
3906     ui->windowInfoAction->setChecked(theInfo->isVisible());
3907 }
3908 
3909 void MainWindow::on_windowDirtyAction_triggered()
3910 {
3911     theDirty->setVisible(!theDirty->isVisible());
3912     ui->windowDirtyAction->setChecked(theDirty->isVisible());
3913 }
3914 
3915 void MainWindow::on_windowFeatsAction_triggered()
3916 {
3917     p->theFeats->setVisible(!p->theFeats->isVisible());
3918     ui->windowFeatsAction->setChecked(p->theFeats->isVisible());
3919 }
3920 
3921 void MainWindow::on_windowToolbarAction_triggered()
3922 {
3923     foreach (QObject* child, children()) {
3924         if (QToolBar* tb = qobject_cast<QToolBar*>(child))
3925             tb->setVisible(!tb->isVisible());
3926     }
3927 }
3928 
3929 void MainWindow::on_windowGPSAction_triggered()
3930 {
3931     theGPS->setVisible(!theGPS->isVisible());
3932     ui->windowGPSAction->setChecked(theGPS->isVisible());
3933 }
3934 
3935 #ifdef GEOIMAGE
3936 void MainWindow::on_windowGeoimageAction_triggered()
3937 {
3938     theGeoImage->setVisible(!theGeoImage->isVisible());
3939     ui->windowGeoimageAction->setChecked(theGeoImage->isVisible());
3940 }
3941 #endif
3942 
3943 void MainWindow::on_windowStylesAction_triggered()
3944 {
3945     p->theStyle->setVisible(!p->theStyle->isVisible());
3946     ui->windowStylesAction->setChecked(p->theStyle->isVisible());
3947 }
3948 
3949 void MainWindow::on_windowHideAllAction_triggered()
3950 {
3951     fullscreenState = saveState(1);
3952 
3953     ui->windowHideAllAction->setEnabled(false);
3954     ui->windowHideAllAction->setVisible(false);
3955     ui->windowShowAllAction->setEnabled(true);
3956     ui->windowShowAllAction->setVisible(true);
3957 
3958 //	ui->toolBar->setVisible(false);
3959     theInfo->setVisible(false);
3960     theDirty->setVisible(false);
3961     p->theFeats->setVisible(false);
3962     theLayers->setVisible(false);
3963     p->theProperties->setVisible(false);
3964     theGPS->setVisible(false);
3965     p->theStyle->setVisible(false);
3966 #ifdef GEOIMAGE
3967     theGeoImage->setVisible(false);
3968 #endif
3969 }
3970 
3971 void MainWindow::on_windowShowAllAction_triggered()
3972 {
3973     restoreState(fullscreenState, 1);
3974 
3975     ui->windowHideAllAction->setEnabled(true);
3976     ui->windowHideAllAction->setVisible(true);
3977     ui->windowShowAllAction->setEnabled(false);
3978     ui->windowShowAllAction->setVisible(false);
3979 }
3980 
3981 void MainWindow::on_gpsConnectAction_triggered()
3982 {
3983 #ifndef Q_OS_SYMBIAN
3984     QGPSDevice* aGps;
3985     if (M_PREFS->getGpsUseGpsd())
3986         aGps = new QGPSDDevice("gpsd");
3987     else
3988         aGps = new QGPSComDevice(M_PREFS->getGpsPort());
3989 #else
3990     QGPSS60Device* aGps = new QGPSS60Device();
3991 #endif
3992     if (aGps->openDevice()) {
3993         connect(aGps, SIGNAL(updatePosition(qreal, qreal, QDateTime, qreal, qreal, qreal)),
3994             this, SLOT(updateGpsPosition(qreal, qreal, QDateTime, qreal, qreal, qreal)));
3995 
3996         ui->gpsConnectAction->setEnabled(false);
3997         ui->gpsReplayAction->setEnabled(false);
3998         ui->gpsDisconnectAction->setEnabled(true);
3999         ui->gpsRecordAction->setEnabled(true);
4000         ui->gpsPauseAction->setEnabled(true);
4001         theGPS->setGpsDevice(aGps);
4002         theGPS->resetGpsStatus();
4003         theGPS->startGps();
4004     } else {
4005         QMessageBox::critical(this, tr("GPS error"),
4006             tr("Unable to open GPS port."), QMessageBox::Ok);
4007         delete aGps;
4008     }
4009 }
4010 
4011 void MainWindow::on_gpsReplayAction_triggered()
4012 {
4013     QString fileName = QFileDialog::getOpenFileName(
4014                     this,
4015                     tr("Open NMEA log file"),
4016                     "", "NMEA GPS log format (*.nmea *.nma)" );
4017 
4018     if (fileName.isEmpty())
4019         return;
4020 
4021     QGPSFileDevice* aGps = new QGPSFileDevice(fileName);
4022     if (aGps->openDevice()) {
4023         connect(aGps, SIGNAL(updatePosition(qreal, qreal, QDateTime, qreal, qreal, qreal)),
4024             this, SLOT(updateGpsPosition(qreal, qreal, QDateTime, qreal, qreal, qreal)));
4025 
4026         ui->gpsConnectAction->setEnabled(false);
4027         ui->gpsReplayAction->setEnabled(false);
4028         ui->gpsDisconnectAction->setEnabled(true);
4029         ui->gpsRecordAction->setEnabled(true);
4030         ui->gpsPauseAction->setEnabled(true);
4031 
4032         theGPS->setGpsDevice(aGps);
4033         theGPS->resetGpsStatus();
4034         theGPS->startGps();
4035     }
4036 }
4037 
4038 void MainWindow::on_gpsDisconnectAction_triggered()
4039 {
4040     ui->gpsConnectAction->setEnabled(true);
4041     ui->gpsReplayAction->setEnabled(true);
4042     ui->gpsDisconnectAction->setEnabled(false);
4043     ui->gpsRecordAction->setEnabled(false);
4044     ui->gpsPauseAction->setEnabled(false);
4045     ui->gpsRecordAction->setChecked(false);
4046     ui->gpsPauseAction->setChecked(false);
4047 
4048     disconnect(theGPS->getGpsDevice(), SIGNAL(updatePosition(qreal, qreal, QDateTime, qreal, qreal, qreal)),
4049         this, SLOT(updateGpsPosition(qreal, qreal, QDateTime, qreal, qreal, qreal)));
4050     theGPS->stopGps();
4051     theGPS->resetGpsStatus();
4052 }
4053 
4054 void MainWindow::updateGpsPosition(qreal latitude, qreal longitude, QDateTime time, qreal altitude, qreal speed, qreal heading)
4055 {
4056     Q_UNUSED(heading)
4057     if (theGPS->getGpsDevice()) {
4058         Coord gpsCoord(longitude,latitude);
4059         if (M_PREFS->getGpsMapCenter()) {
4060             CoordBox vp = theView->viewport();
4061             qreal lonDiff = vp.lonDiff();
4062             qreal latDiff = vp.latDiff();
4063             QRectF vpr = vp.adjusted(lonDiff / 4, -latDiff / 4, -lonDiff / 4, latDiff / 4);
4064             if (!vpr.contains(gpsCoord)) {
4065                 theView->setCenter(gpsCoord, theView->rect());
4066                 theView->invalidate(false, false, true);
4067             }
4068         }
4069 
4070         if (ui->gpsRecordAction->isChecked() && !ui->gpsPauseAction->isChecked()) {
4071             TrackNode* pt = g_backend.allocTrackNode(gpsRecLayer, gpsCoord);
4072             pt->setTime(time);
4073             pt->setElevation(altitude);
4074             pt->setSpeed(speed);
4075             gpsRecLayer->add(pt);
4076             curGpsTrackSegment->add(pt);
4077         }
4078     }
4079     theView->update();
4080 }
4081 
4082 QGPS* MainWindow::gps()
4083 {
4084     return theGPS;
4085 }
4086 
4087 void MainWindow::on_gpsCenterAction_triggered()
4088 {
4089     M_PREFS->setGpsMapCenter(!M_PREFS->getGpsMapCenter());
4090     ui->gpsCenterAction->setChecked(M_PREFS->getGpsMapCenter());
4091     invalidateView();
4092 }
4093 
4094 void MainWindow::on_gpsRecordAction_triggered()
4095 {
4096     if (ui->gpsRecordAction->isChecked()) {
4097         if (theDocument) {
4098             QString fn = "log-" + QDateTime::currentDateTime().toString(Qt::ISODate);
4099             fn.replace(':', '-');
4100 
4101             gpsRecLayer = new TrackLayer();
4102             gpsRecLayer->setName(fn);
4103             theDocument->add(gpsRecLayer);
4104 
4105             curGpsTrackSegment = g_backend.allocSegment(gpsRecLayer);
4106             gpsRecLayer->add(curGpsTrackSegment);
4107         } else {
4108             ui->gpsRecordAction->setChecked(false);
4109         }
4110     } else {
4111         ui->gpsPauseAction->setChecked(false);
4112     }
4113 }
4114 void MainWindow::on_gpsPauseAction_triggered()
4115 {
4116     if (ui->gpsPauseAction->isChecked()) {
4117         if (!ui->gpsRecordAction->isChecked()) {
4118             ui->gpsPauseAction->setChecked(false);
4119         }
4120     } else {
4121         if (theDocument && ui->gpsRecordAction->isChecked()) {
4122             curGpsTrackSegment = g_backend.allocSegment(gpsRecLayer);
4123             gpsRecLayer->add(curGpsTrackSegment);
4124         }
4125     }
4126 }
4127 
4128 void MainWindow::on_toolTemplatesSaveAction_triggered()
4129 {
4130     QString path;
4131     if (getPathToSave(tr("Save Tag Templates"), "mat", tr("Merkaartor tag templates (*.mat)") + "\n" + tr("All Files (*)"), &path)) {
4132 	p->theProperties->saveTemplates(path);
4133     }
4134 }
4135 
4136 void MainWindow::on_toolTemplatesMergeAction_triggered()
4137 {
4138     QString fileName = QFileDialog::getOpenFileName(
4139                     this,
4140                     tr("Open Tag Templates"),
4141                     "", "Merkaartor tag templates (*.mat)" );
4142 
4143     if (fileName.isEmpty())
4144         return;
4145 
4146     p->theProperties->mergeTemplates(fileName);
4147     p->theProperties->resetValues();
4148 }
4149 
4150 void MainWindow::on_toolTemplatesLoadAction_triggered()
4151 {
4152     QString fileName = QFileDialog::getOpenFileName(
4153                     this,
4154                     tr("Open Tag Templates"),
4155                     "", "Merkaartor tag templates (*.mat)" );
4156 
4157     if (fileName.isEmpty())
4158         return;
4159 
4160     p->theProperties->loadTemplates(fileName);
4161     p->theProperties->resetValues();
4162 }
4163 
4164 
4165 #if defined(Q_OS_MAC)
4166 QString MainWindow::macOsTranslationsPath() {
4167     QDir resources = QDir(QCoreApplication::applicationDirPath());
4168     resources.cdUp();
4169     resources.cd("Resources");
4170     return resources.absolutePath();
4171 }
4172 #endif
4173 
4174 QString MainWindow::makeAbsolute(const QString& path) {
4175     if (QDir::isAbsolutePath(path))
4176         return path;
4177     else
4178         return QCoreApplication::applicationDirPath() + "/" + path;
4179 }
4180 
4181 /* Compose the list of translation paths. */
4182 QStringList MainWindow::translationPaths() {
4183     QStringList paths;
4184     /* Try the macros first, as they are defined by the user. */
4185     paths << makeAbsolute(STRINGIFY(TRANSDIR_SYSTEM));
4186     paths << makeAbsolute(STRINGIFY(TRANSDIR_MERKAARTOR));
4187     paths << QCoreApplication::applicationDirPath();
4188     paths << QCoreApplication::applicationDirPath() + "/translations";
4189 #if defined(Q_OS_MAC)
4190     paths << macOsTranslationsPath();
4191 #endif
4192     paths << QLibraryInfo::location(QLibraryInfo::TranslationsPath);
4193     return paths;
4194 }
4195 
4196 bool MainWindow::tryLoadTranslator(const QString& languageFile, QTranslator* theTranslator)
4197 {
4198     qDebug() << "Looking for translations file: " << languageFile;
4199     QStringList paths = translationPaths();
4200     foreach (const QString &path, paths) {
4201         qDebug() << "  Trying directory " << path;
4202         if (theTranslator->load(languageFile, path)) {
4203             qDebug() << "  Found it.";
4204             return true;
4205         }
4206     }
4207     qDebug() << "  Not found.";
4208     return false;
4209 }
4210 
4211 void MainWindow::updateLanguage()
4212 {
4213     if (qtTranslator) {
4214         QCoreApplication::removeTranslator(qtTranslator);
4215     }
4216     if (merkaartorTranslator) {
4217         QCoreApplication::removeTranslator(merkaartorTranslator);
4218     }
4219     QString language = getDefaultLanguage();
4220     if (language != "-" && language != "en") {
4221         qtTranslator = new QTranslator;
4222         const QString languagePrefix = language.left(2);
4223         if (tryLoadTranslator("qt_"+languagePrefix, qtTranslator))
4224             QCoreApplication::installTranslator(qtTranslator);
4225 
4226         // Do not prevent Merkaartor translations to be loaded, even if there is no Qt translation for the language.
4227         merkaartorTranslator = new QTranslator;
4228         if (tryLoadTranslator("merkaartor_"+language, merkaartorTranslator))
4229             QCoreApplication::installTranslator(merkaartorTranslator);
4230         else
4231             statusBar()->showMessage(tr("Warning! Could not load the Merkaartor translations for the \"%1\" language. Switching to default English.").arg(language), 15000);
4232     }
4233     ui->retranslateUi(this);
4234 }
4235 
4236 void MainWindow::updateMenu()
4237 {
4238     if (M_PREFS->getOfflineMode()) {
4239         ui->fileWorkOfflineAction->setChecked(true);
4240         ui->fileDownloadAction->setEnabled(false);
4241         ui->fileDownloadMoreAction->setEnabled(false);
4242         ui->fileUploadAction->setEnabled(false);
4243     } else {
4244         ui->fileWorkOfflineAction->setChecked(false);
4245         ui->fileDownloadAction->setEnabled(true);
4246         ui->fileDownloadMoreAction->setEnabled(true);
4247         ui->fileUploadAction->setEnabled(true);
4248     }
4249 
4250     if (M_PREFS->getSeparateMoveMode())
4251         ui->editMoveAction->setVisible(true);
4252     else
4253         ui->editMoveAction->setVisible(false);
4254 }
4255 
4256 void MainWindow::on_layersNewImageAction_triggered()
4257 {
4258     if (theDocument)
4259         theDocument->addImageLayer();
4260 }
4261 
4262 void MainWindow::on_layersNewDrawingAction_triggered()
4263 {
4264     if (theDocument)
4265         theDocument->addDrawingLayer();
4266 }
4267 
4268 void MainWindow::on_layersNewFilterAction_triggered()
4269 {
4270     if (theDocument)
4271         theDocument->addFilterLayer();
4272 }
4273 
4274 bool MainWindow::hasUnsavedChanges()
4275 {
4276     if (!theDocument)
4277         return false;
4278 
4279     if (theDocument->getDirtySize() == p->latSaveDirtyLevel)
4280         return false;
4281 
4282     return true;
4283 }
4284 
4285 void MainWindow::syncOSM(const QString& aWeb, const QString& aUser, const QString& aPwd)
4286 {
4287 #ifndef FRISIUS_BUILD
4288     if (checkForConflicts(theDocument)) {
4289         QMessageBox::warning(this,tr("Unresolved conflicts"), tr("Please resolve existing conflicts first"));
4290         return;
4291     }
4292 
4293     DirtyListBuild Future;
4294     theDocument->history().buildDirtyList(Future);
4295     DirtyListDescriber Describer(theDocument,Future);
4296     if (Describer.showChanges(this) && Describer.tasks()) {
4297         Future.resetUpdates();
4298         DirtyListExecutorOSC Exec(theDocument,Future,aWeb,aUser,aPwd,Describer.tasks());
4299         if (Exec.executeChanges(this)) {
4300             if (M_PREFS->getAutoHistoryCleanup() && !theDocument->getDirtyOrOriginLayer()->getDirtySize())
4301                 theDocument->history().cleanup();
4302 
4303             p->latSaveDirtyLevel = theDocument->getDirtySize();
4304 
4305             if (!currentProjectFile.isEmpty()) {
4306                 if (M_PREFS->getAutoSaveDoc()) {
4307                     saveDocument(currentProjectFile);
4308                 } else {
4309                     if (QMessageBox::warning(this,tr("Unsaved changes"),
4310                                              tr("It is strongly recommended to save the changes to your document after an upload.\nDo you want to do this now?"),
4311                                              QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) == QMessageBox::Yes) {
4312                         saveDocument(currentProjectFile);
4313 
4314                     }
4315                 }
4316             }
4317         }
4318     }
4319 #endif
4320 }
4321 
4322 void MainWindow::on_viewWireframeAction_toggled(bool val)
4323 {
4324     M_PREFS->setWireframeView(val);
4325     invalidateView();
4326 }
4327