PlanView.qml 50.5 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
import QGroundControl               1.0
Don Gagne's avatar
commit  
Don Gagne committed
20
21
22
import QGroundControl.FlightMap     1.0
import QGroundControl.ScreenTools   1.0
import QGroundControl.Controls      1.0
23
import QGroundControl.FactSystem    1.0
24
import QGroundControl.FactControls  1.0
Don Gagne's avatar
commit  
Don Gagne committed
25
import QGroundControl.Palette       1.0
26
import QGroundControl.Controllers   1.0
DonLakeFlyer's avatar
DonLakeFlyer committed
27
import QGroundControl.KMLFileHelper 1.0
28
import QGroundControl.Airspace      1.0
Gus Grubba's avatar
Gus Grubba committed
29
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
Don Gagne's avatar
Don Gagne committed
66

DonLakeFlyer's avatar
DonLakeFlyer committed
67
68
69
70
    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
71

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

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

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

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

95
    function updateAirspace(reset) {
96
97
98
99
        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) {
100
                QGroundControl.airspaceManager.setROI(coordinateNW, coordinateSE, true /*planView*/, reset)
101
102
103
104
            }
        }
    }

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

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

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

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

        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()
148
                _missionController.applyDefaultMissionAltitude()
DonLakeFlyer's avatar
DonLakeFlyer committed
149
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
    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
178
                        _planMasterController.sendToVehicle()
179
                        hideDialog()
180
181
182
183
184
185
                    }
                }
            }
        }
    }

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

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

200
    PlanMasterController {
201
        id: masterController
202

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

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

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

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

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

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

DonLakeFlyer's avatar
DonLakeFlyer committed
252
253
254
255
256
257
258
259
260
261
        function loadKmlFromSelectedFile() {
            fileDialog.title =          qsTr("Load KML")
            fileDialog.planFiles =      false
            fileDialog.selectExisting = true
            fileDialog.nameFilters =    masterController.fileKmlFilters
            fileDialog.fileExtension =  QGroundControl.settingsManager.appSettings.kmlFileExtension
            fileDialog.fileExtension2 = ""
            fileDialog.openForLoad()
        }

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

277
278
    Connections {
        target: _missionController
279

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

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

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

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

302
303
304
305
306
307
308
309
310
311
    /// 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()
    }

312
313
    property int _moveDialogMissionItemIndex

314
315
316
317
    QGCFileDialog {
        id:             fileDialog
        qgcView:        _qgcView
        folder:         QGroundControl.settingsManager.appSettings.missionSavePath
318

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

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

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

DonLakeFlyer's avatar
DonLakeFlyer committed
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
    property string kmlPolygonSelectDialogKMLFile
    Component {
        id: kmlPolygonSelectDialog

        QGCViewDialog {
            function accept() {
                var complexItemName
                if (surveyRadio.checked) {
                    complexItemName = _missionController.surveyComplexItemName
                } else {
                    complexItemName = _missionController.structureScanComplexItemName
                }
                insertComplexMissionItemFromKML(complexItemName, kmlPolygonSelectDialogKMLFile, -1)
                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
                    text:           qsTr("What would you like to create from the polygon specified by the KML file?")
                }

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

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

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

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

Gus Grubba's avatar
Gus Grubba committed
409
                if (toIndex === 0) {
410
411
                    toIndex = 1
                }
412
                _missionController.moveMissionItem(_moveDialogMissionItemIndex, toIndex)
413
414
415
416
417
418
419
420
421
422
423
424
                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
425
                    text:           qsTr("Move the selected mission item to the be after following mission item:")
426
427
428
429
                }

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

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

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

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

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

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

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

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

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

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

473
474
475
476
477
            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
478
479
480
                    // Take focus to close any previous editing
                    editorMap.focus = true

481
482
483
484
                    //-- 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
485
486
                    }

487
488
489
490
                    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
491

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

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

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

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

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

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

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

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

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

577
578
579
580
581
582
583
584
585
            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
586
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 ]
                buttonVisible:      [ true, true, _waypointsOnlyMode, true, true, _showZoom, _showZoom ]
591
592
593
594
595
596
                maxHeight:          mapScale.y - toolStrip.y

                property bool _showZoom: !ScreenTools.isMobile

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

                onClicked: {
                    switch (index) {
DonLakeFlyer's avatar
DonLakeFlyer committed
634
                    case 1:
635
                        _addWaypointOnClick = checked
636
                        _addROIOnClick = false
637
                        break
DonLakeFlyer's avatar
DonLakeFlyer committed
638
                    case 2:
639
640
641
                        _addROIOnClick = checked
                        _addWaypointOnClick = false
                        break
DonLakeFlyer's avatar
DonLakeFlyer committed
642
                    case 3:
643
                        if (_singleComplexItem) {
644
                            addComplexItem(_missionController.complexMissionItemNames[0])
645
                        }
646
                        break
647
                    case 5:
648
649
                        editorMap.zoomLevel += 0.5
                        break
650
                    case 6:
651
652
653
654
655
                        editorMap.zoomLevel -= 0.5
                        break
                    }
                }
            }
Gus Grubba's avatar
Gus Grubba committed
656
657
        }
        //-----------------------------------------------------------
658
659
660
        // Right pane for mission editing controls
        Rectangle {
            id:                 rightPanel
661
            height:             ScreenTools.availableHeight
662
663
            width:              _rightPanelWidth
            color:              qgcPal.window
664
            opacity:            planExpanded.visible ? 0.2 : 0
Gus Grubba's avatar
Gus Grubba committed
665
666
667
            anchors.bottom:     parent.bottom
            anchors.right:      parent.right
            anchors.rightMargin: ScreenTools.defaultFontPixelWidth
668
        }
Gus Grubba's avatar
Gus Grubba committed
669
670
        //-------------------------------------------------------
        // Right Panel Controls
671
        Item {
Gus Grubba's avatar
Gus Grubba committed
672
673
674
            anchors.fill:           rightPanel
            Column {
                id:                 rightControls
Gus Grubba's avatar
Gus Grubba committed
675
                spacing:            ScreenTools.defaultFontPixelHeight * 0.5
676
677
                anchors.left:       parent.left
                anchors.right:      parent.right
Gus Grubba's avatar
Gus Grubba committed
678
679
680
681
682
                anchors.top:        parent.top
                anchors.topMargin:  ScreenTools.defaultFontPixelHeight * 0.25
                //-------------------------------------------------------
                // Airmap Airspace Control
                AirspaceControl {
Gus Grubba's avatar
Gus Grubba committed
683
684
                    id:             airspaceControl
                    width:          parent.width
685
                    visible:        _airspaceEnabled
686
                    planView:       true
687
                    showColapse:    true
688
                }
Gus Grubba's avatar
Gus Grubba committed
689
690
691
692
                //-------------------------------------------------------
                // Mission Controls (Colapsed)
                Rectangle {
                    width:      parent.width
Gus Grubba's avatar
Gus Grubba committed
693
                    height:     planControlColapsed ? colapsedRow.height + ScreenTools.defaultFontPixelHeight : 0
Gus Grubba's avatar
Gus Grubba committed
694
695
                    color:      qgcPal.missionItemEditor
                    radius:     _radius
696
                    visible:    planControlColapsed && _airspaceEnabled
Gus Grubba's avatar
Gus Grubba committed
697
698
699
700
701
702
703
                    Row {
                        id:                     colapsedRow
                        spacing:                ScreenTools.defaultFontPixelWidth
                        anchors.left:           parent.left
                        anchors.leftMargin:     ScreenTools.defaultFontPixelWidth
                        anchors.verticalCenter: parent.verticalCenter
                        QGCColoredImage {
704
705
706
707
708
                            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
709
710
711
                            anchors.verticalCenter: parent.verticalCenter
                        }
                        QGCLabel {
712
713
                            text:               qsTr("Plan")
                            color:              qgcPal.text
Gus Grubba's avatar
Gus Grubba committed
714
                            anchors.verticalCenter: parent.verticalCenter
715
716
                        }
                    }
Gus Grubba's avatar
Gus Grubba committed
717
718
719
720
                    QGCColoredImage {
                        width:                  height
                        height:                 ScreenTools.defaultFontPixelWidth * 2.5
                        sourceSize.height:      height
721
                        source:                 QGroundControl.airmapSupported ? "qrc:/airmap/expand.svg" : ""
722
                        color:                  "white"
723
                        visible:                QGroundControl.airmapSupported
Gus Grubba's avatar
Gus Grubba committed
724
725
726
727
728
729
                        anchors.right:          parent.right
                        anchors.rightMargin:    ScreenTools.defaultFontPixelWidth
                        anchors.verticalCenter: parent.verticalCenter
                    }
                    MouseArea {
                        anchors.fill:   parent
730
                        enabled:        QGroundControl.airmapSupported
Gus Grubba's avatar
Gus Grubba committed
731
                        onClicked: {
732
                            QGroundControl.airspaceManager.airspaceVisible = false
Gus Grubba's avatar
Gus Grubba committed
733
734
                        }
                    }
Gus Grubba's avatar
Gus Grubba committed
735
                }
Gus Grubba's avatar
Gus Grubba committed
736
737
738
739
740
                //-------------------------------------------------------
                // Mission Controls (Expanded)
                Rectangle {
                    id:         planExpanded
                    width:      parent.width
741
                    height:     (!planControlColapsed || !_airspaceEnabled) ? expandedCol.height + ScreenTools.defaultFontPixelHeight : 0
Gus Grubba's avatar
Gus Grubba committed
742
743
                    color:      qgcPal.missionItemEditor
                    radius:     _radius
744
                    visible:    !planControlColapsed || !_airspaceEnabled
Gus Grubba's avatar
Gus Grubba committed
745
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
826
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
860
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
886
887
888
889
890
891
892
                    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
                                QGCColoredImage {
                                    width:                  height
                                    height:                 ScreenTools.defaultFontPixelWidth * 2.5
                                    sourceSize.height:      height
                                    source:                 "qrc:/res/waypoint.svg"
                                    color:                  qgcPal.text
                                    anchors.verticalCenter: parent.verticalCenter
                                }
                                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
                    spacing:        ScreenTools.defaultFontPixelHeight * 0.5
                    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
                anchors.left:           parent.left
                anchors.right:          parent.right
                availableHeight:        ScreenTools.availableHeight
                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
893
            }
Gus Grubba's avatar
Gus Grubba committed
894
        }
895
896
897
898
899
900
901

        MapScale {
            id:                 mapScale
            anchors.margins:    ScreenTools.defaultFontPixelHeight * (0.66)
            anchors.bottom:     waypointValuesDisplay.visible ? waypointValuesDisplay.top : parent.bottom
            anchors.left:       parent.left
            mapControl:         editorMap
902
            visible:            _toolStripBottom < y
903
904
905
906
907
908
        }

        MissionItemStatus {
            id:                 waypointValuesDisplay
            anchors.margins:    ScreenTools.defaultFontPixelWidth
            anchors.left:       parent.left
909
            height:             ScreenTools.defaultFontPixelHeight * 7
910
            maxWidth:           parent.width - rightPanel.width - x
911
            anchors.bottom:     parent.bottom
912
            missionItems:       _missionController.visualItems
913
            visible:            _editingLayer === _layerMission && (_toolStripBottom + mapScale.height) < y && QGroundControl.corePlugin.options.showMissionStatus
914
        }
Gus Grubba's avatar
Gus Grubba committed
915
    }
916

917
918
919
920
    Component {
        id: syncLoadFromVehicleOverwrite
        QGCViewMessage {
            id:         syncLoadFromVehicleCheck
Don Gagne's avatar
Don Gagne committed
921
            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?")
922
923
            function accept() {
                hideDialog()
924
                masterController.loadFromVehicle()
925
926
927
928
929
930
931
932
            }
        }
    }

    Component {
        id: syncLoadFromFileOverwrite
        QGCViewMessage {
            id:         syncLoadFromVehicleCheck
DonLakeFlyer's avatar
DonLakeFlyer committed
933
            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?")
934
935
            function accept() {
                hideDialog()
936
                masterController.loadFromSelectedFile()
937
938
939
940
            }
        }
    }

941
942
943
    Component {
        id: removeAllPromptDialog
        QGCViewMessage {
944
            message: qsTr("Are you sure you want to remove all items and create a new plan? ") +
945
                     (_planMasterController.offline ? "" : qsTr("This will also remove all items from the vehicle."))
946
            function accept() {
947
948
949
950
951
                if (_planMasterController.offline) {
                    masterController.removeAll()
                } else {
                    masterController.removeAllFromVehicle()
                }
952
953
954
955
956
                hideDialog()
            }
        }
    }

957
958
959
960
961
962
963
964
965
966
967
    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()
            }
        }
    }

968
969
970
971
972
973
974
975
976
977
978
    //- ToolStrip DropPanel Components

    Component {
        id: centerMapDropPanel

        CenterMapDropPanel {
            map:            editorMap
            fitFunctions:   mapFitFunctions
        }
    }

979
980
981
982
983
984
985
986
987
    Component {
        id: patternDropPanel

        ColumnLayout {
            spacing:    ScreenTools.defaultFontPixelWidth * 0.5

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

            Repeater {
988
                model: _missionController.complexMissionItemNames
989
990
991
992
993
994

                QGCButton {
                    text:               modelData
                    Layout.fillWidth:   true

                    onClicked: {
995
                        addComplexItem(modelData)
996
997
998
999
1000
                        dropPanel.hide()
                    }
                }
            }
        } // Column
For faster browsing, not all history is shown. View entire blame