PlanView.qml 50.2 KB
Newer Older
1
2
3
4
5
6
7
8
/****************************************************************************
 *
 *   (c) 2009-2016 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/
Don Gagne's avatar
commit  
Don Gagne committed
9
10


11
12
import QtQuick          2.3
import QtQuick.Controls 1.2
Don Gagne's avatar
Don Gagne committed
13
import QtQuick.Dialogs  1.2
14
15
16
import QtLocation       5.3
import QtPositioning    5.3
import QtQuick.Layouts  1.2
17
import QtQuick.Window   2.2
Don Gagne's avatar
commit  
Don Gagne committed
18

19
20
21
22
23
24
25
26
27
28
29
import QGroundControl                   1.0
import QGroundControl.FlightMap         1.0
import QGroundControl.ScreenTools       1.0
import QGroundControl.Controls          1.0
import QGroundControl.FactSystem        1.0
import QGroundControl.FactControls      1.0
import QGroundControl.Palette           1.0
import QGroundControl.Controllers       1.0
import QGroundControl.ShapeFileHelper   1.0
import QGroundControl.Airspace          1.0
import QGroundControl.Airmap            1.0
Don Gagne's avatar
commit  
Don Gagne committed
30
31

/// Mission Editor
Don Gagne's avatar
Don Gagne committed
32

Don Gagne's avatar
Don Gagne committed
33
QGCView {
34
    id:         _qgcView
35
    viewPanel:  panel
36
    z:          QGroundControl.zOrderTopMost
37

Gus Grubba's avatar
Gus Grubba committed
38
39
    property bool planControlColapsed: false

Don Gagne's avatar
   
Don Gagne committed
40
41
42
    ///< This property is used to determine dirty state for prompting on QGC shutdown
    readonly property bool dirty: _planMasterController.dirty

43
    readonly property int   _decimalPlaces:             8
Gus Grubba's avatar
Gus Grubba committed
44
    readonly property real  _horizontalMargin:          ScreenTools.defaultFontPixelWidth  * 0.5
45
    readonly property real  _margin:                    ScreenTools.defaultFontPixelHeight * 0.5
Gus Grubba's avatar
Gus Grubba committed
46
    readonly property real  _radius:                    ScreenTools.defaultFontPixelWidth  * 0.5
47
48
49
50
51
    readonly property var   _activeVehicle:             QGroundControl.multiVehicleManager.activeVehicle
    readonly property real  _rightPanelWidth:           Math.min(parent.width / 3, ScreenTools.defaultFontPixelWidth * 30)
    readonly property real  _toolButtonTopMargin:       parent.height - ScreenTools.availableHeight + (ScreenTools.defaultFontPixelHeight / 2)
    readonly property var   _defaultVehicleCoordinate:  QtPositioning.coordinate(37.803784, -122.462276)
    readonly property bool  _waypointsOnlyMode:         QGroundControl.corePlugin.options.missionWaypointsOnly
52

Gus Grubba's avatar
Gus Grubba committed
53
    property bool   _airspaceEnabled:                    QGroundControl.airmapSupported ? (QGroundControl.settingsManager.airMapSettings.enableAirMap.rawValue && QGroundControl.airspaceManager.connected): false
Gus Grubba's avatar
Gus Grubba committed
54
55
56
57
58
59
60
61
62
63
64
65
    property var    _planMasterController:              masterController
    property var    _missionController:                 _planMasterController.missionController
    property var    _geoFenceController:                _planMasterController.geoFenceController
    property var    _rallyPointController:              _planMasterController.rallyPointController
    property var    _visualItems:                       _missionController.visualItems
    property bool   _lightWidgetBorders:                editorMap.isSatelliteMap
    property bool   _addWaypointOnClick:                false
    property bool   _addROIOnClick:                     false
    property bool   _singleComplexItem:                 _missionController.complexMissionItemNames.length === 1
    property real   _toolbarHeight:                     _qgcView.height - ScreenTools.availableHeight
    property int    _editingLayer:                      _layerMission
    property int    _toolStripBottom:                   toolStrip.height + toolStrip.y
66
    property var    _appSettings:                       QGroundControl.settingsManager.appSettings
Don Gagne's avatar
Don Gagne committed
67

DonLakeFlyer's avatar
DonLakeFlyer committed
68
69
70
71
    readonly property int       _layerMission:              1
    readonly property int       _layerGeoFence:             2
    readonly property int       _layerRallyPoints:          3
    readonly property string    _armedVehicleUploadPrompt:  qsTr("Vehicle is currently armed. Do you want to upload the mission to the vehicle?")
Don Gagne's avatar
Don Gagne committed
72

73
    Component.onCompleted: {
74
        toolbar.planMasterController =  Qt.binding(function () { return _planMasterController })
75
        toolbar.currentMissionItem =    Qt.binding(function () { return _missionController.currentPlanViewItem })
76
77
    }

78
79
    function addComplexItem(complexItemName) {
        var coordinate = editorMap.center
80
        coordinate.latitude  = coordinate.latitude.toFixed(_decimalPlaces)
81
        coordinate.longitude = coordinate.longitude.toFixed(_decimalPlaces)
82
        coordinate.altitude  = coordinate.altitude.toFixed(_decimalPlaces)
83
        insertComplexMissionItem(complexItemName, coordinate, _missionController.visualItems.count)
84
85
86
    }

    function insertComplexMissionItem(complexItemName, coordinate, index) {
87
        var sequenceNumber = _missionController.insertComplexMissionItem(complexItemName, coordinate, index)
88
        _missionController.setCurrentPlanViewIndex(sequenceNumber, true)
89
90
    }

91
92
    function insertComplexMissionItemFromKMLOrSHP(complexItemName, file, index) {
        var sequenceNumber = _missionController.insertComplexMissionItemFromKMLOrSHP(complexItemName, file, index)
DonLakeFlyer's avatar
DonLakeFlyer committed
93
94
95
        _missionController.setCurrentPlanViewIndex(sequenceNumber, true)
    }

96
    function updateAirspace(reset) {
97
98
99
100
        if(_airspaceEnabled) {
            var coordinateNW = editorMap.toCoordinate(Qt.point(0,0), false /* clipToViewPort */)
            var coordinateSE = editorMap.toCoordinate(Qt.point(width,height), false /* clipToViewPort */)
            if(coordinateNW.isValid && coordinateSE.isValid) {
101
                QGroundControl.airspaceManager.setROI(coordinateNW, coordinateSE, true /*planView*/, reset)
102
103
104
105
            }
        }
    }

Don Gagne's avatar
Don Gagne committed
106
107
108
109
110
    property bool _firstMissionLoadComplete:    false
    property bool _firstFenceLoadComplete:      false
    property bool _firstRallyLoadComplete:      false
    property bool _firstLoadComplete:           false

111
    MapFitFunctions {
DonLakeFlyer's avatar
DonLakeFlyer committed
112
        id:                         mapFitFunctions  // The name for this id cannot be changed without breaking references outside of this code. Beware!
113
114
        map:                        editorMap
        usePlannedHomePosition:     true
115
        planMasterController:       _planMasterController
116
117
    }

118
    on_AirspaceEnabledChanged: {
119
        if(QGroundControl.airmapSupported) {
120
121
            if(_airspaceEnabled) {
                planControlColapsed = QGroundControl.airspaceManager.airspaceVisible
122
                updateAirspace(true)
123
124
125
            } else {
                planControlColapsed = false
            }
126
        } else {
Gus Grubba's avatar
Gus Grubba committed
127
128
129
130
            planControlColapsed = false
        }
    }

DonLakeFlyer's avatar
DonLakeFlyer committed
131
    Connections {
132
        target: _appSettings.defaultMissionItemAltitude
DonLakeFlyer's avatar
DonLakeFlyer committed
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148

        onRawValueChanged: {
            if (_visualItems.count > 1) {
                _qgcView.showDialog(applyNewAltitude, qsTr("Apply new alititude"), showDialogDefaultWidth, StandardButton.Yes | StandardButton.No)
            }
        }
    }

    Component {
        id: applyNewAltitude

        QGCViewMessage {
            message:    qsTr("You have changed the default altitude for mission items. Would you like to apply that altitude to all the items in the current mission?")

            function accept() {
                hideDialog()
149
                _missionController.applyDefaultMissionAltitude()
DonLakeFlyer's avatar
DonLakeFlyer committed
150
151
152
153
            }
        }
    }

154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
    Component {
        id: activeMissionUploadDialogComponent

        QGCViewDialog {

            Column {
                anchors.fill:   parent
                spacing:        ScreenTools.defaultFontPixelHeight

                QGCLabel {
                    width:      parent.width
                    wrapMode:   Text.WordWrap
                    text:       qsTr("Your vehicle is currently flying a mission. In order to upload a new or modified mission the current mission will be paused.")
                }

                QGCLabel {
                    width:      parent.width
                    wrapMode:   Text.WordWrap
                    text:       qsTr("After the mission is uploaded you can adjust the current waypoint and start the mission.")
                }

                QGCButton {
                    text:       qsTr("Pause and Upload")
                    onClicked: {
                        _activeVehicle.flightMode = _activeVehicle.pauseFlightMode
179
                        _planMasterController.sendToVehicle()
180
                        hideDialog()
181
182
183
184
185
186
                    }
                }
            }
        }
    }

Gus Grubba's avatar
Gus Grubba committed
187
    Connections {
188
        target: QGroundControl.airspaceManager
189
        onAirspaceVisibleChanged: {
190
            planControlColapsed = QGroundControl.airspaceManager.airspaceVisible
Gus Grubba's avatar
Gus Grubba committed
191
192
193
        }
    }

194
195
196
197
198
199
200
    Component {
        id: noItemForKML
        QGCViewMessage {
            message:    qsTr("You need at least one item to create a KML.")
        }
    }

201
    PlanMasterController {
202
        id: masterController
203

dogmaphobic's avatar
dogmaphobic committed
204
        Component.onCompleted: {
205
            start(false /* flyView */)
206
            _missionController.setCurrentPlanViewIndex(0, true)
dogmaphobic's avatar
dogmaphobic committed
207
208
        }

DonLakeFlyer's avatar
DonLakeFlyer committed
209
210
211
212
        function waitingOnDataMessage() {
            _qgcView.showMessage(qsTr("Unable to Save/Upload"), qsTr("Plan is waiting on terrain data from server for correct altitude values."), StandardButton.Ok)
        }

213
        function upload() {
DonLakeFlyer's avatar
DonLakeFlyer committed
214
215
216
217
            if (!readyForSaveSend()) {
                waitingOnDataMessage()
                return
            }
218
            if (_activeVehicle && _activeVehicle.armed && _activeVehicle.flightMode === _activeVehicle.missionFlightMode) {
219
                _qgcView.showDialog(activeMissionUploadDialogComponent, qsTr("Plan Upload"), _qgcView.showDialogDefaultWidth, StandardButton.Cancel)
220
            } else {
221
222
                sendToVehicle()
            }
DonLakeFlyer's avatar
DonLakeFlyer committed
223
224
        }

Don Gagne's avatar
Don Gagne committed
225
        function loadFromSelectedFile() {
226
            fileDialog.title =          qsTr("Select Plan File")
DonLakeFlyer's avatar
DonLakeFlyer committed
227
            fileDialog.planFiles =      true
228
            fileDialog.selectExisting = true
229
            fileDialog.nameFilters =    masterController.loadNameFilters
230
231
            fileDialog.fileExtension =  _appSettings.planFileExtension
            fileDialog.fileExtension2 = _appSettings.missionFileExtension
232
            fileDialog.openForLoad()
Don Gagne's avatar
Don Gagne committed
233
234
235
        }

        function saveToSelectedFile() {
DonLakeFlyer's avatar
DonLakeFlyer committed
236
237
238
239
            if (!readyForSaveSend()) {
                waitingOnDataMessage()
                return
            }
240
            fileDialog.title =          qsTr("Save Plan")
DonLakeFlyer's avatar
DonLakeFlyer committed
241
            fileDialog.planFiles =      true
242
            fileDialog.selectExisting = false
243
            fileDialog.nameFilters =    masterController.saveNameFilters
244
245
            fileDialog.fileExtension =  _appSettings.planFileExtension
            fileDialog.fileExtension2 = _appSettings.missionFileExtension
246
            fileDialog.openForSave()
Don Gagne's avatar
Don Gagne committed
247
248
        }

Don Gagne's avatar
Don Gagne committed
249
        function fitViewportToItems() {
250
            mapFitFunctions.fitMapViewportToMissionItems()
Don Gagne's avatar
Don Gagne committed
251
        }
Patrick José Pereira's avatar
Patrick José Pereira committed
252

253
254
        function loadShapeFromSelectedFile() {
            fileDialog.title =          qsTr("Load Shape")
DonLakeFlyer's avatar
DonLakeFlyer committed
255
256
            fileDialog.planFiles =      false
            fileDialog.selectExisting = true
257
258
259
            fileDialog.nameFilters =    ShapeFileHelper.fileDialogKMLOrSHPFilters
            fileDialog.fileExtension =  _appSettings.kmlFileExtension
            fileDialog.fileExtension2 = _appSettings.shpFileExtension
DonLakeFlyer's avatar
DonLakeFlyer committed
260
261
262
            fileDialog.openForLoad()
        }

Patrick José Pereira's avatar
Patrick José Pereira committed
263
        function saveKmlToSelectedFile() {
DonLakeFlyer's avatar
DonLakeFlyer committed
264
265
266
267
            if (!readyForSaveSend()) {
                waitingOnDataMessage()
                return
            }
Patrick José Pereira's avatar
Patrick José Pereira committed
268
            fileDialog.title =          qsTr("Save KML")
DonLakeFlyer's avatar
DonLakeFlyer committed
269
            fileDialog.planFiles =      false
Patrick José Pereira's avatar
Patrick José Pereira committed
270
            fileDialog.selectExisting = false
271
272
            fileDialog.nameFilters =    ShapeFileHelper.fileDialogKMLFilters
            fileDialog.fileExtension =  _appSettings.kmlFileExtension
273
            fileDialog.fileExtension2 = ""
Patrick José Pereira's avatar
Patrick José Pereira committed
274
275
            fileDialog.openForSave()
        }
276
    }
Don Gagne's avatar
Don Gagne committed
277

278
279
    Connections {
        target: _missionController
280

Don Gagne's avatar
Don Gagne committed
281
        onNewItemsFromVehicle: {
282
283
284
            if (_visualItems && _visualItems.count != 1) {
                mapFitFunctions.fitMapViewportToMissionItems()
            }
285
            _missionController.setCurrentPlanViewIndex(0, true)
Don Gagne's avatar
Don Gagne committed
286
287
        }
    }
288

Don Gagne's avatar
Don Gagne committed
289
    QGCPalette { id: qgcPal; colorGroupEnabled: enabled }
Don Gagne's avatar
commit  
Don Gagne committed
290

Don Gagne's avatar
Don Gagne committed
291
292
293
294
    ExclusiveGroup {
        id: _mapTypeButtonsExclusiveGroup
    }

295
296
297
298
    /// Inserts a new simple mission item
    ///     @param coordinate Location to insert item
    ///     @param index Insert item at this index
    function insertSimpleMissionItem(coordinate, index) {
299
        var sequenceNumber = _missionController.insertSimpleMissionItem(coordinate, index)
300
        _missionController.setCurrentPlanViewIndex(sequenceNumber, true)
301
302
    }

303
304
305
306
307
308
309
310
311
312
    /// Inserts a new ROI mission item
    ///     @param coordinate Location to insert item
    ///     @param index Insert item at this index
    function insertROIMissionItem(coordinate, index) {
        var sequenceNumber = _missionController.insertROIMissionItem(coordinate, index)
        _missionController.setCurrentPlanViewIndex(sequenceNumber, true)
        _addROIOnClick = false
        toolStrip.uncheckAll()
    }

313
314
    property int _moveDialogMissionItemIndex

315
316
317
    QGCFileDialog {
        id:             fileDialog
        qgcView:        _qgcView
318
        folder:         _appSettings.missionSavePath
319

DonLakeFlyer's avatar
DonLakeFlyer committed
320
321
        property bool planFiles: true    ///< true: working with plan files, false: working with kml file

322
        onAcceptedForSave: {
DonLakeFlyer's avatar
DonLakeFlyer committed
323
324
325
326
327
            if (planFiles) {
                masterController.saveToFile(file)
            } else {
                masterController.saveToKml(file)
            }
328
            close()
329
330
        }

331
        onAcceptedForLoad: {
DonLakeFlyer's avatar
DonLakeFlyer committed
332
333
334
335
336
            if (planFiles) {
                masterController.loadFromFile(file)
                masterController.fitViewportToItems()
                _missionController.setCurrentPlanViewIndex(0, true)
            } else {
337
338
                var retList = ShapeFileHelper.determineShapeType(file)
                if (retList[0] == ShapeFileHelper.Error) {
DonLakeFlyer's avatar
DonLakeFlyer committed
339
                    _qgcView.showMessage("Error", retList[1], StandardButton.Ok)
340
                } else if (retList[0] == ShapeFileHelper.Polygon) {
Don Gagne's avatar
   
Don Gagne committed
341
342
343
344
345
346
347
                     var editVehicle = _activeVehicle ? _activeVehicle : QGroundControl.multiVehicleManager.offlineEditingVehicle
                    if (editVehicle.fixedWing) {
                        insertComplexMissionItemFromKMLOrSHP(_missionController.surveyComplexItemName, file, -1)
                    } else {
                        polygonSelectPatternFile = file
                        _qgcView.showDialog(patternPolygonSelectDialog, fileDialog.title, _qgcView.showDialogDefaultWidth, StandardButton.Ok | StandardButton.Cancel)
                    }
348
349
                } else if (retList[0] == ShapeFileHelper.Polyline) {
                    insertComplexMissionItemFromKMLOrSHP(_missionController.corridorScanComplexItemName, file, -1)
DonLakeFlyer's avatar
DonLakeFlyer committed
350
351
                }
            }
352
            close()
353
354
355
        }
    }

356
    property string polygonSelectPatternFile
DonLakeFlyer's avatar
DonLakeFlyer committed
357
    Component {
358
        id: patternPolygonSelectDialog
DonLakeFlyer's avatar
DonLakeFlyer committed
359
360
361
362
363
364
365
366
367

        QGCViewDialog {
            function accept() {
                var complexItemName
                if (surveyRadio.checked) {
                    complexItemName = _missionController.surveyComplexItemName
                } else {
                    complexItemName = _missionController.structureScanComplexItemName
                }
368
                insertComplexMissionItemFromKMLOrSHP(complexItemName, polygonSelectPatternFile, -1)
DonLakeFlyer's avatar
DonLakeFlyer committed
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
                hideDialog()
            }

            ExclusiveGroup {
                id: radioGroup
            }

            Column {
                anchors.left:   parent.left
                anchors.right:  parent.right
                spacing:        ScreenTools.defaultFontPixelHeight

                QGCLabel {
                    anchors.left:   parent.left
                    anchors.right:  parent.right
                    wrapMode:       Text.WordWrap
385
                    text:           qsTr("Create which pattern type?")
DonLakeFlyer's avatar
DonLakeFlyer committed
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
                }

                QGCRadioButton {
                    id:             surveyRadio
                    text:           qsTr("Survey")
                    checked:        true
                    exclusiveGroup: radioGroup
                }

                QGCRadioButton {
                    text:           qsTr("Structure Scan")
                    exclusiveGroup: radioGroup
                }
            }
        }
    }

403
404
405
406
407
408
409
    Component {
        id: moveDialog

        QGCViewDialog {
            function accept() {
                var toIndex = toCombo.currentIndex

Gus Grubba's avatar
Gus Grubba committed
410
                if (toIndex === 0) {
411
412
                    toIndex = 1
                }
413
                _missionController.moveMissionItem(_moveDialogMissionItemIndex, toIndex)
414
415
416
417
418
419
420
421
422
423
424
425
                hideDialog()
            }

            Column {
                anchors.left:   parent.left
                anchors.right:  parent.right
                spacing:        ScreenTools.defaultFontPixelHeight

                QGCLabel {
                    anchors.left:   parent.left
                    anchors.right:  parent.right
                    wrapMode:       Text.WordWrap
426
                    text:           qsTr("Move the selected mission item to the be after following mission item:")
427
428
429
430
                }

                QGCComboBox {
                    id:             toCombo
Don Gagne's avatar
Don Gagne committed
431
                    model:          _visualItems.count
432
433
434
435
436
437
                    currentIndex:   _moveDialogMissionItemIndex
                }
            }
        }
    }

Don Gagne's avatar
Don Gagne committed
438
439
    QGCViewPanel {
        id:             panel
440
        anchors.fill:   parent
Don Gagne's avatar
commit  
Don Gagne committed
441

442
        FlightMap {
443
444
445
446
447
            id:                         editorMap
            anchors.fill:               parent
            mapName:                    "MissionEditor"
            allowGCSLocationCenter:     true
            allowVehicleLocationCenter: true
448
            planView:                   true
449
            qgcView:                    _qgcView
Don Gagne's avatar
commit  
Don Gagne committed
450

451
            // This is the center rectangle of the map which is not obscured by tools
Gus Grubba's avatar
Gus Grubba committed
452
            property rect centerViewport:   Qt.rect(_leftToolWidth, _toolbarHeight, editorMap.width - _leftToolWidth - _rightPanelWidth, editorMap.height - _statusHeight - _toolbarHeight)
dogmaphobic's avatar
dogmaphobic committed
453

454
455
            property real _leftToolWidth:   toolStrip.x + toolStrip.width
            property real _statusHeight:    waypointValuesDisplay.visible ? editorMap.height - waypointValuesDisplay.y : 0
DonLakeFlyer's avatar
DonLakeFlyer committed
456

457
            readonly property real animationDuration: 500
DonLakeFlyer's avatar
DonLakeFlyer committed
458

459
460
            // Initial map position duplicates Fly view position
            Component.onCompleted: editorMap.center = QGroundControl.flightMapPosition
Don Gagne's avatar
Don Gagne committed
461

462
463
464
465
466
467
            Behavior on zoomLevel {
                NumberAnimation {
                    duration:       editorMap.animationDuration
                    easing.type:    Easing.InOutQuad
                }
            }
Don Gagne's avatar
Don Gagne committed
468

469
470
            QGCMapPalette { id: mapPal; lightColors: editorMap.isSatelliteMap }

471
472
            onZoomLevelChanged: updateAirspace(false)
            onCenterChanged:    updateAirspace(false)
473

474
475
476
477
478
            MouseArea {
                //-- It's a whole lot faster to just fill parent and deal with top offset below
                //   than computing the coordinate offset.
                anchors.fill: parent
                onClicked: {
DonLakeFlyer's avatar
DonLakeFlyer committed
479
480
481
                    // Take focus to close any previous editing
                    editorMap.focus = true

482
483
484
485
                    //-- Don't pay attention to items beneath the toolbar.
                    var topLimit = parent.height - ScreenTools.availableHeight
                    if(mouse.y < topLimit) {
                        return
Don Gagne's avatar
Don Gagne committed
486
487
                    }

488
489
490
491
                    var coordinate = editorMap.toCoordinate(Qt.point(mouse.x, mouse.y), false /* clipToViewPort */)
                    coordinate.latitude = coordinate.latitude.toFixed(_decimalPlaces)
                    coordinate.longitude = coordinate.longitude.toFixed(_decimalPlaces)
                    coordinate.altitude = coordinate.altitude.toFixed(_decimalPlaces)
Don Gagne's avatar
Don Gagne committed
492

493
494
495
                    switch (_editingLayer) {
                    case _layerMission:
                        if (_addWaypointOnClick) {
496
                            insertSimpleMissionItem(coordinate, _missionController.visualItems.count)
497
498
499
                        } else if (_addROIOnClick) {
                            _addROIOnClick = false
                            insertROIMissionItem(coordinate, _missionController.visualItems.count)
Don Gagne's avatar
Don Gagne committed
500
                        }
501
502
                        break
                    case _layerRallyPoints:
503
                        if (_rallyPointController.supported) {
504
                            _rallyPointController.addPoint(coordinate)
505
                        }
506
                        break
Don Gagne's avatar
Don Gagne committed
507
                    }
Don Gagne's avatar
Don Gagne committed
508
                }
509
            }
Don Gagne's avatar
Don Gagne committed
510

511
512
            // Add the mission item visuals to the map
            Repeater {
513
                model: _editingLayer == _layerMission ? _missionController.visualItems : undefined
514

515
516
                delegate: MissionItemMapVisual {
                    map:        editorMap
DonLakeFlyer's avatar
DonLakeFlyer committed
517
                    qgcView:    _qgcView
518
                    onClicked:  _missionController.setCurrentPlanViewIndex(sequenceNumber, false)
519
                    visible:    _editingLayer == _layerMission
Don Gagne's avatar
Don Gagne committed
520
                }
521
            }
Don Gagne's avatar
Don Gagne committed
522

523
524
            // Add lines between waypoints
            MissionLineView {
525
                model: _editingLayer == _layerMission ? _missionController.waypointLines : undefined
526
            }
527

528
529
530
531
532
533
534
            // Add the vehicles to the map
            MapItemView {
                model: QGroundControl.multiVehicleManager.vehicles
                delegate:
                    VehicleMapItem {
                    vehicle:        object
                    coordinate:     object.coordinate
535
                    map:            editorMap
536
537
                    size:           ScreenTools.defaultFontPixelHeight * 3
                    z:              QGroundControl.zOrderMapItems - 1
538
                }
539
            }
540

541
542
            GeoFenceMapVisuals {
                map:                    editorMap
543
                myGeoFenceController:   _geoFenceController
544
                interactive:            _editingLayer == _layerGeoFence
545
                homePosition:           _missionController.plannedHomePosition
546
547
                planView:               true
            }
548

549
550
            RallyPointMapVisuals {
                map:                    editorMap
551
                myRallyPointController: _rallyPointController
552
553
                interactive:            _editingLayer == _layerRallyPoints
                planView:               true
554
            }
555

556
557
            // Airspace overlap support
            MapItemView {
558
                model:              _airspaceEnabled && QGroundControl.airspaceManager.airspaceVisible ? QGroundControl.airspaceManager.airspaces.circles : []
559
560
561
                delegate: MapCircle {
                    center:         object.center
                    radius:         object.radius
562
                    color:          object.color
Gus Grubba's avatar
Gus Grubba committed
563
564
                    border.color:   object.lineColor
                    border.width:   object.lineWidth
565
566
567
568
                }
            }

            MapItemView {
569
                model:              _airspaceEnabled && QGroundControl.airspaceManager.airspaceVisible ? QGroundControl.airspaceManager.airspaces.polygons : []
570
571
                delegate: MapPolygon {
                    path:           object.polygon
572
                    color:          object.color
Gus Grubba's avatar
Gus Grubba committed
573
574
                    border.color:   object.lineColor
                    border.width:   object.lineWidth
575
576
577
                }
            }

578
579
580
581
582
583
584
585
586
            ToolStrip {
                id:                 toolStrip
                anchors.leftMargin: ScreenTools.defaultFontPixelWidth
                anchors.left:       parent.left
                anchors.topMargin:  _toolButtonTopMargin
                anchors.top:        parent.top
                color:              qgcPal.window
                title:              qsTr("Plan")
                z:                  QGroundControl.zOrderWidgets
587
588
589
590
                showAlternateIcon:  [ masterController.dirty, false, false, false, false, false, false ]
                rotateImage:        [ masterController.syncInProgress, false, false, false, false, false, false ]
                animateImage:       [ masterController.dirty, false, false, false, false, false, false ]
                buttonEnabled:      [ !masterController.syncInProgress, true, true, true, true, true, true ]
Don Gagne's avatar
   
Don Gagne committed
591
                buttonVisible:      [ true, true, _waypointsOnlyMode, true, true, _showZoom, _showZoom ]
592
593
594
595
596
597
                maxHeight:          mapScale.y - toolStrip.y

                property bool _showZoom: !ScreenTools.isMobile

                model: [
                    {
598
                        name:                   qsTr("File"),
599
600
601
                        iconSource:             "/qmlimages/MapSync.svg",
                        alternateIconSource:    "/qmlimages/MapSyncChanged.svg",
                        dropPanelComponent:     syncDropPanel
602
                    },
603
                    {
604
                        name:                   qsTr("Waypoint"),
605
606
                        iconSource:             "/qmlimages/MapAddMission.svg",
                        toggle:                 true
607
                    },
608
                    {
609
                        name:                   qsTr("ROI"),
610
611
                        iconSource:             "/qmlimages/MapAddMission.svg",
                        toggle:                 true
612
                    },
613
                    {
614
                        name:               _singleComplexItem ? _missionController.complexMissionItemNames[0] : qsTr("Pattern"),
615
616
                        iconSource:         "/qmlimages/MapDrawShape.svg",
                        dropPanelComponent: _singleComplexItem ? undefined : patternDropPanel
617
                    },
618
                    {
619
                        name:               qsTr("Center"),
620
621
622
623
                        iconSource:         "/qmlimages/MapCenter.svg",
                        dropPanelComponent: centerMapDropPanel
                    },
                    {
624
                        name:               qsTr("In"),
625
626
627
                        iconSource:         "/qmlimages/ZoomPlus.svg"
                    },
                    {
628
                        name:               qsTr("Out"),
629
630
631
632
633
634
                        iconSource:         "/qmlimages/ZoomMinus.svg"
                    }
                ]

                onClicked: {
                    switch (index) {
DonLakeFlyer's avatar
DonLakeFlyer committed
635
                    case 1:
636
                        _addWaypointOnClick = checked
637
                        _addROIOnClick = false
638
                        break
DonLakeFlyer's avatar
DonLakeFlyer committed
639
                    case 2:
640
641
642
                        _addROIOnClick = checked
                        _addWaypointOnClick = false
                        break
DonLakeFlyer's avatar
DonLakeFlyer committed
643
                    case 3:
644
                        if (_singleComplexItem) {
645
                            addComplexItem(_missionController.complexMissionItemNames[0])
646
                        }
647
                        break
648
                    case 5:
649
650
                        editorMap.zoomLevel += 0.5
                        break
651
                    case 6:
652
653
654
655
656
                        editorMap.zoomLevel -= 0.5
                        break
                    }
                }
            }
Gus Grubba's avatar
Gus Grubba committed
657
658
        }
        //-----------------------------------------------------------
659
660
661
        // Right pane for mission editing controls
        Rectangle {
            id:                 rightPanel
662
            height:             ScreenTools.availableHeight
663
664
            width:              _rightPanelWidth
            color:              qgcPal.window
665
            opacity:            planExpanded.visible ? 0.2 : 0
Gus Grubba's avatar
Gus Grubba committed
666
667
668
            anchors.bottom:     parent.bottom
            anchors.right:      parent.right
            anchors.rightMargin: ScreenTools.defaultFontPixelWidth
669
        }
Gus Grubba's avatar
Gus Grubba committed
670
671
        //-------------------------------------------------------
        // Right Panel Controls
672
        Item {
Gus Grubba's avatar
Gus Grubba committed
673
674
675
            anchors.fill:           rightPanel
            Column {
                id:                 rightControls
Gus Grubba's avatar
Gus Grubba committed
676
                spacing:            ScreenTools.defaultFontPixelHeight * 0.5
677
678
                anchors.left:       parent.left
                anchors.right:      parent.right
Gus Grubba's avatar
Gus Grubba committed
679
680
681
682
683
                anchors.top:        parent.top
                anchors.topMargin:  ScreenTools.defaultFontPixelHeight * 0.25
                //-------------------------------------------------------
                // Airmap Airspace Control
                AirspaceControl {
Gus Grubba's avatar
Gus Grubba committed
684
685
                    id:             airspaceControl
                    width:          parent.width
686
                    visible:        _airspaceEnabled
687
                    planView:       true
688
                    showColapse:    true
689
                }
Gus Grubba's avatar
Gus Grubba committed
690
691
692
693
                //-------------------------------------------------------
                // Mission Controls (Colapsed)
                Rectangle {
                    width:      parent.width
Gus Grubba's avatar
Gus Grubba committed
694
                    height:     planControlColapsed ? colapsedRow.height + ScreenTools.defaultFontPixelHeight : 0
Gus Grubba's avatar
Gus Grubba committed
695
696
                    color:      qgcPal.missionItemEditor
                    radius:     _radius
697
                    visible:    planControlColapsed && _airspaceEnabled
Gus Grubba's avatar
Gus Grubba committed
698
699
700
701
702
703
704
                    Row {
                        id:                     colapsedRow
                        spacing:                ScreenTools.defaultFontPixelWidth
                        anchors.left:           parent.left
                        anchors.leftMargin:     ScreenTools.defaultFontPixelWidth
                        anchors.verticalCenter: parent.verticalCenter
                        QGCColoredImage {
705
706
707
708
709
                            width:              height
                            height:             ScreenTools.defaultFontPixelWidth * 2.5
                            sourceSize.height:  height
                            source:             "qrc:/res/waypoint.svg"
                            color:              qgcPal.text
Gus Grubba's avatar
Gus Grubba committed
710
711
712
                            anchors.verticalCenter: parent.verticalCenter
                        }
                        QGCLabel {
713
714
                            text:               qsTr("Plan")
                            color:              qgcPal.text
Gus Grubba's avatar
Gus Grubba committed
715
                            anchors.verticalCenter: parent.verticalCenter
716
717
                        }
                    }
Gus Grubba's avatar
Gus Grubba committed
718
719
720
721
                    QGCColoredImage {
                        width:                  height
                        height:                 ScreenTools.defaultFontPixelWidth * 2.5
                        sourceSize.height:      height
722
                        source:                 QGroundControl.airmapSupported ? "qrc:/airmap/expand.svg" : ""
723
                        color:                  "white"
724
                        visible:                QGroundControl.airmapSupported
Gus Grubba's avatar
Gus Grubba committed
725
726
727
728
729
730
                        anchors.right:          parent.right
                        anchors.rightMargin:    ScreenTools.defaultFontPixelWidth
                        anchors.verticalCenter: parent.verticalCenter
                    }
                    MouseArea {
                        anchors.fill:   parent
731
                        enabled:        QGroundControl.airmapSupported
Gus Grubba's avatar
Gus Grubba committed
732
                        onClicked: {
733
                            QGroundControl.airspaceManager.airspaceVisible = false
Gus Grubba's avatar
Gus Grubba committed
734
735
                        }
                    }
Gus Grubba's avatar
Gus Grubba committed
736
                }
Gus Grubba's avatar
Gus Grubba committed
737
738
739
740
741
                //-------------------------------------------------------
                // Mission Controls (Expanded)
                Rectangle {
                    id:         planExpanded
                    width:      parent.width
742
                    height:     (!planControlColapsed || !_airspaceEnabled) ? expandedCol.height + ScreenTools.defaultFontPixelHeight : 0
Gus Grubba's avatar
Gus Grubba committed
743
744
                    color:      qgcPal.missionItemEditor
                    radius:     _radius
745
                    visible:    !planControlColapsed || !_airspaceEnabled
Gus Grubba's avatar
Gus Grubba committed
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
                    Item {
                        height:             expandedCol.height
                        anchors.left:       parent.left
                        anchors.right:      parent.right
                        anchors.verticalCenter: parent.verticalCenter
                        Column {
                            id:                     expandedCol
                            spacing:                ScreenTools.defaultFontPixelHeight * 0.5
                            anchors.left:           parent.left
                            anchors.right:          parent.right
                            //-- Header
                            Row {
                                id:                     expandedRow
                                spacing:                ScreenTools.defaultFontPixelWidth
                                anchors.left:           parent.left
                                anchors.leftMargin:     ScreenTools.defaultFontPixelWidth
                                readonly property real _buttonRadius: ScreenTools.defaultFontPixelHeight * 0.75
                                QGCLabel {
                                    text:           qsTr("Plan")
                                    color:          qgcPal.text
                                    visible:        !QGroundControl.corePlugin.options.enablePlanViewSelector
                                    anchors.verticalCenter: parent.verticalCenter
                                }
                                ExclusiveGroup {
                                    id: planElementSelectorGroup
                                    onCurrentChanged: {
                                        switch (current) {
                                        case planElementMission:
                                            _editingLayer = _layerMission
                                            break
                                        case planElementGeoFence:
                                            _editingLayer = _layerGeoFence
                                            break
                                        case planElementRallyPoints:
                                            _editingLayer = _layerRallyPoints
                                            break
                                        }
                                    }
                                }
                                QGCRadioButton {
                                    id:             planElementMission
                                    exclusiveGroup: planElementSelectorGroup
                                    text:           qsTr("Mission")
                                    checked:        true
                                    visible:        QGroundControl.corePlugin.options.enablePlanViewSelector
                                    anchors.verticalCenter: parent.verticalCenter
                                }
                                QGCRadioButton {
                                    id:             planElementGeoFence
                                    exclusiveGroup: planElementSelectorGroup
                                    text:           qsTr("Fence")
                                    visible:        QGroundControl.corePlugin.options.enablePlanViewSelector
                                    anchors.verticalCenter: parent.verticalCenter
                                }
                                QGCRadioButton {
                                    id:             planElementRallyPoints
                                    exclusiveGroup: planElementSelectorGroup
                                    text:           qsTr("Rally")
                                    visible:        QGroundControl.corePlugin.options.enablePlanViewSelector
                                    anchors.verticalCenter: parent.verticalCenter
                                }
                            }
                        }
                    }
                }
            }
            //-------------------------------------------------------
            // Mission Item Editor
            Item {
                id:                     missionItemEditor
                anchors.left:           parent.left
                anchors.right:          parent.right
                anchors.top:            rightControls.bottom
                anchors.topMargin:      ScreenTools.defaultFontPixelHeight * 0.5
                anchors.bottom:         parent.bottom
                anchors.bottomMargin:   ScreenTools.defaultFontPixelHeight * 0.25
                visible:                _editingLayer == _layerMission && !planControlColapsed
                QGCListView {
                    id:             missionItemEditorListView
                    anchors.fill:   parent
Don Gagne's avatar
   
Don Gagne committed
826
                    spacing:        ScreenTools.defaultFontPixelHeight / 4
Gus Grubba's avatar
Gus Grubba committed
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
                    orientation:    ListView.Vertical
                    model:          _missionController.visualItems
                    cacheBuffer:    Math.max(height * 2, 0)
                    clip:           true
                    currentIndex:   _missionController.currentPlanViewIndex
                    highlightMoveDuration: 250
                    visible:        _editingLayer == _layerMission && !planControlColapsed
                    //-- List Elements
                    delegate: MissionItemEditor {
                        map:                editorMap
                        masterController:  _planMasterController
                        missionItem:        object
                        width:              parent.width
                        readOnly:           false
                        rootQgcView:        _qgcView
                        onClicked:  _missionController.setCurrentPlanViewIndex(object.sequenceNumber, false)
                        onRemove: {
                            var removeIndex = index
                            _missionController.removeMissionItem(removeIndex)
                            if (removeIndex >= _missionController.visualItems.count) {
                                removeIndex--
                            }
                            _missionController.setCurrentPlanViewIndex(removeIndex, true)
                        }
                        onInsertWaypoint:       insertSimpleMissionItem(editorMap.center, index)
                        onInsertComplexItem:    insertComplexMissionItem(complexItemName, editorMap.center, index)
                    }
                }
            }
            // GeoFence Editor
            GeoFenceEditor {
                anchors.top:            rightControls.bottom
                anchors.topMargin:      ScreenTools.defaultFontPixelHeight * 0.5
Don Gagne's avatar
   
Don Gagne committed
860
                anchors.bottom:         parent.bottom
Gus Grubba's avatar
Gus Grubba committed
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
                anchors.left:           parent.left
                anchors.right:          parent.right
                myGeoFenceController:   _geoFenceController
                flightMap:              editorMap
                visible:                _editingLayer == _layerGeoFence
            }
            // Rally Point Editor
            RallyPointEditorHeader {
                id:                     rallyPointHeader
                anchors.top:            rightControls.bottom
                anchors.topMargin:      ScreenTools.defaultFontPixelHeight * 0.5
                anchors.left:           parent.left
                anchors.right:          parent.right
                visible:                _editingLayer == _layerRallyPoints
                controller:             _rallyPointController
            }
            RallyPointItemEditor {
                id:                     rallyPointEditor
                anchors.top:            rallyPointHeader.bottom
                anchors.topMargin:      ScreenTools.defaultFontPixelHeight * 0.5
                anchors.left:           parent.left
                anchors.right:          parent.right
                visible:                _editingLayer == _layerRallyPoints && _rallyPointController.points.count
                rallyPoint:             _rallyPointController.currentRallyPoint
                controller:             _rallyPointController
886
            }
Gus Grubba's avatar
Gus Grubba committed
887
        }
888
889
890
891
892
893
894

        MapScale {
            id:                 mapScale
            anchors.margins:    ScreenTools.defaultFontPixelHeight * (0.66)
            anchors.bottom:     waypointValuesDisplay.visible ? waypointValuesDisplay.top : parent.bottom
            anchors.left:       parent.left
            mapControl:         editorMap
895
            visible:            _toolStripBottom < y
896
897
898
899
900
901
        }

        MissionItemStatus {
            id:                 waypointValuesDisplay
            anchors.margins:    ScreenTools.defaultFontPixelWidth
            anchors.left:       parent.left
902
            height:             ScreenTools.defaultFontPixelHeight * 7
903
            maxWidth:           parent.width - rightPanel.width - x
904
            anchors.bottom:     parent.bottom
905
            missionItems:       _missionController.visualItems
906
            visible:            _editingLayer === _layerMission && (_toolStripBottom + mapScale.height) < y && QGroundControl.corePlugin.options.showMissionStatus
907
        }
Gus Grubba's avatar
Gus Grubba committed
908
    }
909

910
911
912
913
    Component {
        id: syncLoadFromVehicleOverwrite
        QGCViewMessage {
            id:         syncLoadFromVehicleCheck
Don Gagne's avatar
Don Gagne committed
914
            message:   qsTr("You have unsaved/unsent changes. Loading from the Vehicle will lose these changes. Are you sure you want to load from the Vehicle?")
915
916
            function accept() {
                hideDialog()
917
                masterController.loadFromVehicle()
918
919
920
921
922
923
924
925
            }
        }
    }

    Component {
        id: syncLoadFromFileOverwrite
        QGCViewMessage {
            id:         syncLoadFromVehicleCheck
DonLakeFlyer's avatar
DonLakeFlyer committed
926
            message:   qsTr("You have unsaved/unsent changes. Loading from a file will lose these changes. Are you sure you want to load from a file?")
927
928
            function accept() {
                hideDialog()
929
                masterController.loadFromSelectedFile()
930
931
932
933
            }
        }
    }

934
935
936
    Component {
        id: removeAllPromptDialog
        QGCViewMessage {
937
            message: qsTr("Are you sure you want to remove all items and create a new plan? ") +
938
                     (_planMasterController.offline ? "" : qsTr("This will also remove all items from the vehicle."))
939
            function accept() {
940
941
942
943
944
                if (_planMasterController.offline) {
                    masterController.removeAll()
                } else {
                    masterController.removeAllFromVehicle()
                }
945
946
947
948
949
                hideDialog()
            }
        }
    }

950
951
952
953
954
955
956
957
958
959
960
    Component {
        id: clearVehicleMissionDialog
        QGCViewMessage {
            message: qsTr("Are you sure you want to remove all mission items and clear the mission from the vehicle?")
            function accept() {
                masterController.removeAllFromVehicle()
                hideDialog()
            }
        }
    }

961
962
963
964
965
966
967
968
969
970
971
    //- ToolStrip DropPanel Components

    Component {
        id: centerMapDropPanel

        CenterMapDropPanel {
            map:            editorMap
            fitFunctions:   mapFitFunctions
        }
    }

972
973
974
975
976
977
978
979
980
    Component {
        id: patternDropPanel

        ColumnLayout {
            spacing:    ScreenTools.defaultFontPixelWidth * 0.5

            QGCLabel { text: qsTr("Create complex pattern:") }

            Repeater {
981
                model: _missionController.complexMissionItemNames
982
983
984
985
986
987

                QGCButton {
                    text:               modelData
                    Layout.fillWidth:   true

                    onClicked: {
988
                        addComplexItem(modelData)
989
990
991
992
                        dropPanel.hide()
                    }
                }
            }
993
994
995
996
997
998
999
1000

            Rectangle {
                width:              parent.width * 0.8
                height:             1
                color:              qgcPal.text
                opacity:            0.5
                Layout.fillWidth:   true
                Layout.columnSpan:  2
For faster browsing, not all history is shown. View entire blame