PlanView.qml 31.6 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
Don Gagne's avatar
commit  
Don Gagne committed
17

18
import QGroundControl               1.0
Don Gagne's avatar
commit  
Don Gagne committed
19
20
21
import QGroundControl.FlightMap     1.0
import QGroundControl.ScreenTools   1.0
import QGroundControl.Controls      1.0
22
import QGroundControl.FactSystem    1.0
23
import QGroundControl.FactControls  1.0
Don Gagne's avatar
commit  
Don Gagne committed
24
import QGroundControl.Palette       1.0
Don Gagne's avatar
Don Gagne committed
25
import QGroundControl.Mavlink       1.0
26
import QGroundControl.Controllers   1.0
Don Gagne's avatar
commit  
Don Gagne committed
27
28

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

Don Gagne's avatar
Don Gagne committed
30
QGCView {
31
    id:         _qgcView
32
    viewPanel:  panel
33
    z:          QGroundControl.zOrderTopMost
34

Don Gagne's avatar
Don Gagne committed
35
36
37
38
39
40
    readonly property int       _decimalPlaces:         8
    readonly property real      _horizontalMargin:      ScreenTools.defaultFontPixelWidth  / 2
    readonly property real      _margin:                ScreenTools.defaultFontPixelHeight * 0.5
    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)
Don Gagne's avatar
Don Gagne committed
41
    readonly property var       _defaultVehicleCoordinate:   QtPositioning.coordinate(37.803784, -122.462276)
42

43
44
45
46
47
    property var    _planMasterController:      masterController
    property var    _missionController:         _planMasterController.missionController
    property var    _geoFenceController:        _planMasterController.geoFenceController
    property var    _rallyPointController:      _planMasterController.rallyPointController
    property var    _visualItems:               _missionController.visualItems
Don Gagne's avatar
commit    
Don Gagne committed
48
    property var    _currentMissionItem
49
50
51
    property int    _currentMissionIndex:       0
    property bool   _lightWidgetBorders:        editorMap.isSatelliteMap
    property bool   _addWaypointOnClick:        false
52
    property bool   _singleComplexItem:         _missionController.complexMissionItemNames.length === 1
53
54
    property real   _toolbarHeight:             _qgcView.height - ScreenTools.availableHeight
    property int    _editingLayer:              _layerMission
Don Gagne's avatar
Don Gagne committed
55

DonLakeFlyer's avatar
DonLakeFlyer committed
56
57
58
59
    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
60

61
    Component.onCompleted: {
62
        toolbar.planMasterController =  Qt.binding(function () { return _planMasterController })
63
64
65
        toolbar.currentMissionItem =    Qt.binding(function () { return _currentMissionItem })
    }

66
67
68
69
70
    function addComplexItem(complexItemName) {
        var coordinate = editorMap.center
        coordinate.latitude = coordinate.latitude.toFixed(_decimalPlaces)
        coordinate.longitude = coordinate.longitude.toFixed(_decimalPlaces)
        coordinate.altitude = coordinate.altitude.toFixed(_decimalPlaces)
71
        insertComplexMissionItem(complexItemName, coordinate, _missionController.visualItems.count)
72
73
74
    }

    function insertComplexMissionItem(complexItemName, coordinate, index) {
75
        var sequenceNumber = _missionController.insertComplexMissionItem(complexItemName, coordinate, index)
76
        setCurrentItem(sequenceNumber, true)
77
78
    }

Don Gagne's avatar
Don Gagne committed
79
80
81
82
83
    property bool _firstMissionLoadComplete:    false
    property bool _firstFenceLoadComplete:      false
    property bool _firstRallyLoadComplete:      false
    property bool _firstLoadComplete:           false

84
85
86
87
    MapFitFunctions {
        id:                         mapFitFunctions
        map:                        editorMap
        usePlannedHomePosition:     true
88
        planMasterController:       _planMasterController
89
90
    }

DonLakeFlyer's avatar
DonLakeFlyer committed
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
    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()
109
                _missionController.applyDefaultMissionAltitude()
DonLakeFlyer's avatar
DonLakeFlyer committed
110
111
112
113
            }
        }
    }

114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
    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
139
                        _planMasterController.sendToVehicle()
140
                        hideDialog()
141
142
143
144
145
146
                    }
                }
            }
        }
    }

147
148
    PlanElemementMasterController {
        id: masterController
149

dogmaphobic's avatar
dogmaphobic committed
150
151
        Component.onCompleted: {
            start(true /* editMode */)
152
            setCurrentItem(0, true)
dogmaphobic's avatar
dogmaphobic committed
153
154
        }

155
        function upload() {
156
            if (_activeVehicle && _activeVehicle.armed && _activeVehicle.flightMode === _activeVehicle.missionFlightMode) {
157
                _qgcView.showDialog(activeMissionUploadDialogComponent, qsTr("Plan Upload"), _qgcView.showDialogDefaultWidth, StandardButton.Cancel)
158
            } else {
159
160
                sendToVehicle()
            }
DonLakeFlyer's avatar
DonLakeFlyer committed
161
162
        }

Don Gagne's avatar
Don Gagne committed
163
        function loadFromSelectedFile() {
164
            fileDialog.title =          qsTr("Select Plan File")
165
            fileDialog.selectExisting = true
166
            fileDialog.nameFilters =    masterController.loadNameFilters
167
            fileDialog.openForLoad()
Don Gagne's avatar
Don Gagne committed
168
169
170
        }

        function saveToSelectedFile() {
171
            fileDialog.title =          qsTr("Save Plan")
172
            fileDialog.selectExisting = false
173
            fileDialog.nameFilters =    masterController.saveNameFilters
174
            fileDialog.openForSave()
Don Gagne's avatar
Don Gagne committed
175
176
        }

Don Gagne's avatar
Don Gagne committed
177
        function fitViewportToItems() {
178
            mapFitFunctions.fitMapViewportToMissionItems()
Don Gagne's avatar
Don Gagne committed
179
        }
180
    }
Don Gagne's avatar
Don Gagne committed
181

182
183
    Connections {
        target: _missionController
184

Don Gagne's avatar
Don Gagne committed
185
        onNewItemsFromVehicle: {
186
187
188
            if (_visualItems && _visualItems.count != 1) {
                mapFitFunctions.fitMapViewportToMissionItems()
            }
189
            setCurrentItem(0, true)
Don Gagne's avatar
Don Gagne committed
190
191
        }
    }
192

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

Don Gagne's avatar
Don Gagne committed
195
196
197
198
    ExclusiveGroup {
        id: _mapTypeButtonsExclusiveGroup
    }

199
200
    /// Sets a new current mission item
    ///     @param sequenceNumber - index for new item, -1 to clear current item
201
202
    function setCurrentItem(sequenceNumber, force) {
        if (force || sequenceNumber !== _currentMissionIndex) {
203
204
205
206
207
208
209
            _currentMissionItem = undefined
            _currentMissionIndex = -1
            for (var i=0; i<_visualItems.count; i++) {
                var visualItem = _visualItems.get(i)
                if (visualItem.sequenceNumber == sequenceNumber) {
                    _currentMissionItem = visualItem
                    _currentMissionItem.isCurrentItem = true
210
                    _currentMissionIndex = sequenceNumber
211
212
213
                } else {
                    visualItem.isCurrentItem = false
                }
Don Gagne's avatar
commit    
Don Gagne committed
214
            }
Don Gagne's avatar
Don Gagne committed
215
216
217
        }
    }

218
219
220
221
    /// Inserts a new simple mission item
    ///     @param coordinate Location to insert item
    ///     @param index Insert item at this index
    function insertSimpleMissionItem(coordinate, index) {
222
        var sequenceNumber = _missionController.insertSimpleMissionItem(coordinate, index)
223
        setCurrentItem(sequenceNumber, true)
224
225
    }

226
227
    property int _moveDialogMissionItemIndex

228
229
230
231
    QGCFileDialog {
        id:             fileDialog
        qgcView:        _qgcView
        folder:         QGroundControl.settingsManager.appSettings.missionSavePath
232
233
        fileExtension:  QGroundControl.settingsManager.appSettings.planFileExtension
        fileExtension2: QGroundControl.settingsManager.appSettings.missionFileExtension
234

235
        onAcceptedForSave: {
236
            masterController.saveToFile(file)
237
            close()
238
239
        }

240
        onAcceptedForLoad: {
241
242
            masterController.loadFromFile(file)
            masterController.fitViewportToItems()
243
            setCurrentItem(0, true)
244
            close()
245
246
247
        }
    }

248
249
250
251
252
253
254
255
256
257
    Component {
        id: moveDialog

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

                if (toIndex == 0) {
                    toIndex = 1
                }
258
                _missionController.moveMissionItem(_moveDialogMissionItemIndex, toIndex)
259
260
261
262
263
264
265
266
267
268
269
270
                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
271
                    text:           qsTr("Move the selected mission item to the be after following mission item:")
272
273
274
275
                }

                QGCComboBox {
                    id:             toCombo
Don Gagne's avatar
Don Gagne committed
276
                    model:          _visualItems.count
277
278
279
280
281
282
                    currentIndex:   _moveDialogMissionItemIndex
                }
            }
        }
    }

Don Gagne's avatar
Don Gagne committed
283
284
    QGCViewPanel {
        id:             panel
285
        anchors.fill:   parent
Don Gagne's avatar
commit  
Don Gagne committed
286

287
        FlightMap {
288
289
290
291
292
            id:                         editorMap
            anchors.fill:               parent
            mapName:                    "MissionEditor"
            allowGCSLocationCenter:     true
            allowVehicleLocationCenter: true
293
            planView:                   true
Don Gagne's avatar
commit  
Don Gagne committed
294

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

298
299
            property real _leftToolWidth:   toolStrip.x + toolStrip.width
            property real _statusHeight:    waypointValuesDisplay.visible ? editorMap.height - waypointValuesDisplay.y : 0
DonLakeFlyer's avatar
DonLakeFlyer committed
300

301
            readonly property real animationDuration: 500
DonLakeFlyer's avatar
DonLakeFlyer committed
302

303
304
            // Initial map position duplicates Fly view position
            Component.onCompleted: editorMap.center = QGroundControl.flightMapPosition
Don Gagne's avatar
Don Gagne committed
305

306
307
308
309
310
311
            Behavior on zoomLevel {
                NumberAnimation {
                    duration:       editorMap.animationDuration
                    easing.type:    Easing.InOutQuad
                }
            }
Don Gagne's avatar
Don Gagne committed
312

313
314
315
316
317
318
319
320
321
322
323
            QGCMapPalette { id: mapPal; lightColors: editorMap.isSatelliteMap }

            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: {
                    //-- 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
324
325
                    }

326
327
328
329
                    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
330

331
332
333
                    switch (_editingLayer) {
                    case _layerMission:
                        if (_addWaypointOnClick) {
334
                            insertSimpleMissionItem(coordinate, _missionController.visualItems.count)
Don Gagne's avatar
Don Gagne committed
335
                        }
336
337
                        break
                    case _layerRallyPoints:
338
339
                        if (_rallyPointController.rallyPointsSupported) {
                            _rallyPointController.addPoint(coordinate)
340
                        }
341
                        break
Don Gagne's avatar
Don Gagne committed
342
                    }
Don Gagne's avatar
Don Gagne committed
343
                }
344
            }
Don Gagne's avatar
Don Gagne committed
345

346
347
            // Add the mission item visuals to the map
            Repeater {
348
                model: _editingLayer == _layerMission ? _missionController.visualItems : undefined
349

350
351
                delegate: MissionItemMapVisual {
                    map:        editorMap
352
                    onClicked:  setCurrentItem(sequenceNumber, false)
353
                    visible:    _editingLayer == _layerMission
Don Gagne's avatar
Don Gagne committed
354
                }
355
            }
Don Gagne's avatar
Don Gagne committed
356

357
358
            // Add lines between waypoints
            MissionLineView {
359
                model: _editingLayer == _layerMission ? _missionController.waypointLines : undefined
360
            }
361

362
363
364
365
366
367
368
            // Add the vehicles to the map
            MapItemView {
                model: QGroundControl.multiVehicleManager.vehicles
                delegate:
                    VehicleMapItem {
                    vehicle:        object
                    coordinate:     object.coordinate
369
                    map:            editorMap
370
371
                    size:           ScreenTools.defaultFontPixelHeight * 3
                    z:              QGroundControl.zOrderMapItems - 1
372
                }
373
            }
374

375
376
            GeoFenceMapVisuals {
                map:                    editorMap
377
                myGeoFenceController:   _geoFenceController
378
                interactive:            _editingLayer == _layerGeoFence
379
                homePosition:           _missionController.plannedHomePosition
380
381
                planView:               true
            }
382

383
384
            RallyPointMapVisuals {
                map:                    editorMap
385
                myRallyPointController: _rallyPointController
386
387
                interactive:            _editingLayer == _layerRallyPoints
                planView:               true
388
            }
389

390
391
392
393
394
395
396
397
398
            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
399
                showAlternateIcon:  [ false, false, masterController.dirty, false, false, false ]
400
                rotateImage:        [ false, false, masterController.syncInProgress, false, false, false ]
401
                animateImage:       [ false, false, masterController.dirty, false, false, false ]
402
                buttonEnabled:      [ true, true, !masterController.syncInProgress, true, true, true ]
403
                buttonVisible:      [ true, true, true, true, _showZoom, _showZoom ]
404
405
406
407
408
409
410
411
412
413
414
                maxHeight:          mapScale.y - toolStrip.y

                property bool _showZoom: !ScreenTools.isMobile

                model: [
                    {
                        name:       "Waypoint",
                        iconSource: "/qmlimages/MapAddMission.svg",
                        toggle:     true
                    },
                    {
415
                        name:               _singleComplexItem ? _missionController.complexMissionItemNames[0] : "Pattern",
416
417
418
                        iconSource:         "/qmlimages/MapDrawShape.svg",
                        dropPanelComponent: _singleComplexItem ? undefined : patternDropPanel
                    },
419
420
421
422
423
424
                    {
                        name:                   "Sync",
                        iconSource:             "/qmlimages/MapSync.svg",
                        alternateIconSource:    "/qmlimages/MapSyncChanged.svg",
                        dropPanelComponent:     syncDropPanel
                    },
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
                    {
                        name:               "Center",
                        iconSource:         "/qmlimages/MapCenter.svg",
                        dropPanelComponent: centerMapDropPanel
                    },
                    {
                        name:               "In",
                        iconSource:         "/qmlimages/ZoomPlus.svg"
                    },
                    {
                        name:               "Out",
                        iconSource:         "/qmlimages/ZoomMinus.svg"
                    }
                ]

                onClicked: {
                    switch (index) {
                    case 0:
                        _addWaypointOnClick = checked
                        break
                    case 1:
                        if (_singleComplexItem) {
447
                            addComplexItem(_missionController.complexMissionItemNames[0])
448
                        }
449
                        break
450
                    case 4:
451
452
                        editorMap.zoomLevel += 0.5
                        break
453
                    case 5:
454
455
456
457
458
459
460
461
462
463
464
465
                        editorMap.zoomLevel -= 0.5
                        break
                    }
                }
            }
        } // FlightMap

        // Right pane for mission editing controls
        Rectangle {
            id:                 rightPanel
            anchors.bottom:     parent.bottom
            anchors.right:      parent.right
466
            height:             ScreenTools.availableHeight
467
468
            width:              _rightPanelWidth
            color:              qgcPal.window
469
470
471
472
473
            opacity:            0.2
        }

        Item {
            anchors.fill:   rightPanel
474
475
476
477

            // Plan Element selector (Mission/Fence/Rally)
            Row {
                id:                 planElementSelectorRow
478
                anchors.topMargin:  Math.round(ScreenTools.defaultFontPixelHeight / 3)
479
480
481
482
                anchors.top:        parent.top
                anchors.left:       parent.left
                anchors.right:      parent.right
                spacing:            _horizontalMargin
483
                visible:            QGroundControl.corePlugin.options.enablePlanViewSelector
484
485
486
487
488
489
490
491
492

                readonly property real _buttonRadius: ScreenTools.defaultFontPixelHeight * 0.75

                ExclusiveGroup {
                    id: planElementSelectorGroup
                    onCurrentChanged: {
                        switch (current) {
                        case planElementMission:
                            _editingLayer = _layerMission
493
                            break
494
495
                        case planElementGeoFence:
                            _editingLayer = _layerGeoFence
Don Gagne's avatar
Don Gagne committed
496
                            break
497
498
                        case planElementRallyPoints:
                            _editingLayer = _layerRallyPoints
Don Gagne's avatar
Don Gagne committed
499
                            break
500
501
502
503
                        }
                    }
                }

504
505
506
507
508
509
510
511
                QGCRadioButton {
                    id:             planElementMission
                    exclusiveGroup: planElementSelectorGroup
                    text:           qsTr("Mission")
                    checked:        true
                    color:          mapPal.text
                    textStyle:      Text.Outline
                    textStyleColor: mapPal.textOutline
512
513
                }

514
515
516
517
518
519
520
521
522
                Item { height: 1; width: 1 }

                QGCRadioButton {
                    id:             planElementGeoFence
                    exclusiveGroup: planElementSelectorGroup
                    text:           qsTr("Fence")
                    color:          mapPal.text
                    textStyle:      Text.Outline
                    textStyleColor: mapPal.textOutline
523
                }
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539

                Item { height: 1; width: 1 }

                QGCRadioButton {
                    id:             planElementRallyPoints
                    exclusiveGroup: planElementSelectorGroup
                    text:           qsTr("Rally")
                    color:          mapPal.text
                    textStyle:      Text.Outline
                    textStyleColor: mapPal.textOutline
                }
            } // Row - Plan Element Selector

            // Mission Item Editor
            Item {
                id:                 missionItemEditor
Don Gagne's avatar
Don Gagne committed
540
                anchors.topMargin:  ScreenTools.defaultFontPixelHeight / 2
541
542
543
544
545
546
547
548
549
550
551
                anchors.top:        planElementSelectorRow.visible ? planElementSelectorRow.bottom : planElementSelectorRow.top
                anchors.left:       parent.left
                anchors.right:      parent.right
                anchors.bottom:     parent.bottom
                visible:            _editingLayer == _layerMission

                QGCListView {
                    id:             missionItemEditorListView
                    anchors.fill:   parent
                    spacing:        _margin / 2
                    orientation:    ListView.Vertical
552
                    model:          _missionController.visualItems
553
554
555
556
557
558
                    cacheBuffer:    Math.max(height * 2, 0)
                    clip:           true
                    currentIndex:   _currentMissionIndex
                    highlightMoveDuration: 250

                    delegate: MissionItemEditor {
559
                        map:                editorMap
560
                        masterController:  _planMasterController
561
562
563
564
                        missionItem:        object
                        width:              parent.width
                        readOnly:           false
                        rootQgcView:        _qgcView
565

566
                        onClicked:  setCurrentItem(object.sequenceNumber, false)
567
568
569

                        onRemove: {
                            var removeIndex = index
570
571
                            _missionController.removeMissionItem(removeIndex)
                            if (removeIndex >= _missionController.visualItems.count) {
572
573
                                removeIndex--
                            }
574
                            _currentMissionIndex = -1
575
                            rootQgcView.setCurrentItem(removeIndex, true)
576
577
                        }

578
579
                        onInsertWaypoint:       insertSimpleMissionItem(editorMap.center, index)
                        onInsertComplexItem:    insertComplexMissionItem(complexItemName, editorMap.center, index)
580
581
582
583
584
                    }
                } // QGCListView
            } // Item - Mission Item editor

            // GeoFence Editor
585
586
587
588
589
590
            GeoFenceEditor {
                anchors.topMargin:      ScreenTools.defaultFontPixelHeight / 2
                anchors.top:            planElementSelectorRow.bottom
                anchors.left:           parent.left
                anchors.right:          parent.right
                availableHeight:        ScreenTools.availableHeight
591
                myGeoFenceController:   _geoFenceController
592
593
                flightMap:              editorMap
                visible:                _editingLayer == _layerGeoFence
594
595
596
597
598
599
            }

            // Rally Point Editor

            RallyPointEditorHeader {
                id:                 rallyPointHeader
600
601
                anchors.topMargin:  ScreenTools.defaultFontPixelHeight / 2
                anchors.top:        planElementSelectorRow.bottom
602
603
604
                anchors.left:       parent.left
                anchors.right:      parent.right
                visible:            _editingLayer == _layerRallyPoints
605
                controller:         _rallyPointController
606
607
608
609
            }

            RallyPointItemEditor {
                id:                 rallyPointEditor
610
611
                anchors.topMargin:  ScreenTools.defaultFontPixelHeight / 2
                anchors.top:        rallyPointHeader.bottom
612
613
                anchors.left:       parent.left
                anchors.right:      parent.right
614
615
616
                visible:            _editingLayer == _layerRallyPoints && _rallyPointController.points.count
                rallyPoint:         _rallyPointController.currentRallyPoint
                controller:         _rallyPointController
617
618
            }
        } // Right panel
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634

        MapScale {
            id:                 mapScale
            anchors.margins:    ScreenTools.defaultFontPixelHeight * (0.66)
            anchors.bottom:     waypointValuesDisplay.visible ? waypointValuesDisplay.top : parent.bottom
            anchors.left:       parent.left
            mapControl:         editorMap
            visible:            !ScreenTools.isTinyScreen
        }

        MissionItemStatus {
            id:                 waypointValuesDisplay
            anchors.margins:    ScreenTools.defaultFontPixelWidth
            anchors.left:       parent.left
            anchors.right:      rightPanel.left
            anchors.bottom:     parent.bottom
635
            missionItems:       _missionController.visualItems
636
            visible:            _editingLayer === _layerMission && !ScreenTools.isShortScreen
637
        }
Don Gagne's avatar
Don Gagne committed
638
    } // QGCViewPanel
639

640
641
642
643
    Component {
        id: syncLoadFromVehicleOverwrite
        QGCViewMessage {
            id:         syncLoadFromVehicleCheck
Don Gagne's avatar
Don Gagne committed
644
            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?")
645
646
            function accept() {
                hideDialog()
647
                masterController.loadFromVehicle()
648
649
650
651
652
653
654
655
            }
        }
    }

    Component {
        id: syncLoadFromFileOverwrite
        QGCViewMessage {
            id:         syncLoadFromVehicleCheck
DonLakeFlyer's avatar
DonLakeFlyer committed
656
            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?")
657
658
            function accept() {
                hideDialog()
659
                masterController.loadFromSelectedFile()
660
661
662
663
            }
        }
    }

664
665
666
    Component {
        id: removeAllPromptDialog
        QGCViewMessage {
667
668
            message: qsTr("Are you sure you want to remove all items? ") +
                     (_planMasterController.offline ? "" : qsTr("This will also remove all items from the vehicle."))
669
            function accept() {
670
671
672
673
674
                if (_planMasterController.offline) {
                    masterController.removeAll()
                } else {
                    masterController.removeAllFromVehicle()
                }
675
676
677
678
679
                hideDialog()
            }
        }
    }

680
681
682
683
684
685
686
687
688
689
690
    //- ToolStrip DropPanel Components

    Component {
        id: centerMapDropPanel

        CenterMapDropPanel {
            map:            editorMap
            fitFunctions:   mapFitFunctions
        }
    }

691
692
693
694
695
696
697
698
699
    Component {
        id: patternDropPanel

        ColumnLayout {
            spacing:    ScreenTools.defaultFontPixelWidth * 0.5

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

            Repeater {
700
                model: _missionController.complexMissionItemNames
701
702
703
704
705
706

                QGCButton {
                    text:               modelData
                    Layout.fillWidth:   true

                    onClicked: {
707
                        addComplexItem(modelData)
708
709
710
711
712
713
                        dropPanel.hide()
                    }
                }
            }
        } // Column
    }
714
715

    Component {
716
        id: syncDropPanel
DonLakeFlyer's avatar
DonLakeFlyer committed
717

718
719
720
        Column {
            id:         columnHolder
            spacing:    _margin
DonLakeFlyer's avatar
DonLakeFlyer committed
721

722
            property string _overwriteText: (_editingLayer == _layerMission) ? qsTr("Mission overwrite") : ((_editingLayer == _layerGeoFence) ? qsTr("GeoFence overwrite") : qsTr("Rally Points overwrite"))
DonLakeFlyer's avatar
DonLakeFlyer committed
723

724
725
726
            QGCLabel {
                width:      sendSaveGrid.width
                wrapMode:   Text.WordWrap
727
                text:       masterController.dirty ?
728
729
                                qsTr("You have unsaved changes. You should upload to your vehicle, or save to a file:") :
                                qsTr("Sync:")
DonLakeFlyer's avatar
DonLakeFlyer committed
730
731
            }

732
733
734
735
736
737
            GridLayout {
                id:                 sendSaveGrid
                columns:            2
                anchors.margins:    _margin
                rowSpacing:         _margin
                columnSpacing:      ScreenTools.defaultFontPixelWidth
DonLakeFlyer's avatar
DonLakeFlyer committed
738

739
740
741
                QGCButton {
                    text:               qsTr("Upload")
                    Layout.fillWidth:   true
742
                    enabled:            !masterController.offline && !masterController.syncInProgress
743
744
                    onClicked: {
                        dropPanel.hide()
745
                        masterController.upload()
746
747
                    }
                }
DonLakeFlyer's avatar
DonLakeFlyer committed
748

749
750
751
                QGCButton {
                    text:               qsTr("Download")
                    Layout.fillWidth:   true
752
                    enabled:            !masterController.offline && !masterController.syncInProgress
753
754
                    onClicked: {
                        dropPanel.hide()
755
                        if (masterController.dirty) {
756
757
                            _qgcView.showDialog(syncLoadFromVehicleOverwrite, columnHolder._overwriteText, _qgcView.showDialogDefaultWidth, StandardButton.Yes | StandardButton.Cancel)
                        } else {
758
                            masterController.loadFromVehicle()
759
760
761
                        }
                    }
                }
DonLakeFlyer's avatar
DonLakeFlyer committed
762

763
764
765
                QGCButton {
                    text:               qsTr("Save To File...")
                    Layout.fillWidth:   true
766
                    enabled:            !masterController.syncInProgress
767
768
                    onClicked: {
                        dropPanel.hide()
769
                        masterController.saveToSelectedFile()
770
771
772
773
774
775
                    }
                }

                QGCButton {
                    text:               qsTr("Load From File...")
                    Layout.fillWidth:   true
776
                    enabled:            !masterController.syncInProgress
777
778
                    onClicked: {
                        dropPanel.hide()
779
                        if (masterController.dirty) {
780
781
                            _qgcView.showDialog(syncLoadFromFileOverwrite, columnHolder._overwriteText, _qgcView.showDialogDefaultWidth, StandardButton.Yes | StandardButton.Cancel)
                        } else {
782
                            masterController.loadFromSelectedFile()
783
784
785
786
787
788
789
790
791
792
793
794
795
                        }
                    }
                }

                QGCButton {
                    text:               qsTr("Remove All")
                    Layout.fillWidth:   true
                    onClicked:  {
                        dropPanel.hide()
                        _qgcView.showDialog(removeAllPromptDialog, qsTr("Remove all"), _qgcView.showDialogDefaultWidth, StandardButton.Yes | StandardButton.No)
                    }
                }
            }
DonLakeFlyer's avatar
DonLakeFlyer committed
796
797
        }
    }
Don Gagne's avatar
Don Gagne committed
798
} // QGCVIew