PlanView.qml 38.7 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
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
Don Gagne committed
18

19
import QGroundControl               1.0
Don Gagne's avatar
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
Don Gagne committed
25
import QGroundControl.Palette       1.0
26
import QGroundControl.Controllers   1.0
27
import QGroundControl.KMLFileHelper 1.0
Don Gagne's avatar
Don Gagne committed
28 29

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

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

36 37 38 39 40 41 42 43
    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)
    readonly property var   _defaultVehicleCoordinate:  QtPositioning.coordinate(37.803784, -122.462276)
    readonly property bool  _waypointsOnlyMode:         QGroundControl.corePlugin.options.missionWaypointsOnly
44

45 46 47 48 49
    property var    _planMasterController:      masterController
    property var    _missionController:         _planMasterController.missionController
    property var    _geoFenceController:        _planMasterController.geoFenceController
    property var    _rallyPointController:      _planMasterController.rallyPointController
    property var    _visualItems:               _missionController.visualItems
50 51
    property bool   _lightWidgetBorders:        editorMap.isSatelliteMap
    property bool   _addWaypointOnClick:        false
52
    property bool   _addROIOnClick:             false
53
    property bool   _singleComplexItem:         _missionController.complexMissionItemNames.length === 1
54 55
    property real   _toolbarHeight:             _qgcView.height - ScreenTools.availableHeight
    property int    _editingLayer:              _layerMission
56
    property int    _toolStripBottom:           toolStrip.height + toolStrip.y
57

58 59 60 61
    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?")
62

63
    Component.onCompleted: {
64
        toolbar.planMasterController =  Qt.binding(function () { return _planMasterController })
65
        toolbar.currentMissionItem =    Qt.binding(function () { return _missionController.currentPlanViewItem })
66 67
    }

68 69 70 71 72
    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)
73
        insertComplexMissionItem(complexItemName, coordinate, _missionController.visualItems.count)
74 75 76
    }

    function insertComplexMissionItem(complexItemName, coordinate, index) {
77
        var sequenceNumber = _missionController.insertComplexMissionItem(complexItemName, coordinate, index)
78
        _missionController.setCurrentPlanViewIndex(sequenceNumber, true)
79 80
    }

81 82 83 84 85
    function insertComplexMissionItemFromKML(complexItemName, kmlFile, index) {
        var sequenceNumber = _missionController.insertComplexMissionItemFromKML(complexItemName, kmlFile, index)
        _missionController.setCurrentPlanViewIndex(sequenceNumber, true)
    }

86 87 88 89 90
    property bool _firstMissionLoadComplete:    false
    property bool _firstFenceLoadComplete:      false
    property bool _firstRallyLoadComplete:      false
    property bool _firstLoadComplete:           false

91
    MapFitFunctions {
92
        id:                         mapFitFunctions  // The name for this id cannot be changed without breaking references outside of this code. Beware!
93 94
        map:                        editorMap
        usePlannedHomePosition:     true
95
        planMasterController:       _planMasterController
96 97
    }

DonLakeFlyer's avatar
DonLakeFlyer committed
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
    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()
116
                _missionController.applyDefaultMissionAltitude()
DonLakeFlyer's avatar
DonLakeFlyer committed
117 118 119 120
            }
        }
    }

121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
    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
146
                        _planMasterController.sendToVehicle()
147
                        hideDialog()
148 149 150 151 152 153
                    }
                }
            }
        }
    }

154 155 156 157 158 159 160 161
    Component {
        id: noItemForKML

        QGCViewMessage {
            message:    qsTr("You need at least one item to create a KML.")
        }
    }

162
    PlanMasterController {
163
        id: masterController
164

165
        Component.onCompleted: {
166
            start(false /* flyView */)
167
            _missionController.setCurrentPlanViewIndex(0, true)
168 169
        }

170 171 172 173
        function waitingOnDataMessage() {
            _qgcView.showMessage(qsTr("Unable to Save/Upload"), qsTr("Plan is waiting on terrain data from server for correct altitude values."), StandardButton.Ok)
        }

174
        function upload() {
175 176 177 178
            if (!readyForSaveSend()) {
                waitingOnDataMessage()
                return
            }
179
            if (_activeVehicle && _activeVehicle.armed && _activeVehicle.flightMode === _activeVehicle.missionFlightMode) {
180
                _qgcView.showDialog(activeMissionUploadDialogComponent, qsTr("Plan Upload"), _qgcView.showDialogDefaultWidth, StandardButton.Cancel)
181
            } else {
182 183
                sendToVehicle()
            }
DonLakeFlyer's avatar
DonLakeFlyer committed
184 185
        }

186
        function loadFromSelectedFile() {
187
            fileDialog.title =          qsTr("Select Plan File")
188
            fileDialog.selectExisting = true
189
            fileDialog.nameFilters =    masterController.loadNameFilters
190 191
            fileDialog.fileExtension =  QGroundControl.settingsManager.appSettings.planFileExtension
            fileDialog.fileExtension2 = QGroundControl.settingsManager.appSettings.missionFileExtension
192
            fileDialog.openForLoad()
193 194 195
        }

        function saveToSelectedFile() {
196 197 198 199
            if (!readyForSaveSend()) {
                waitingOnDataMessage()
                return
            }
200
            fileDialog.title =          qsTr("Save Plan")
201
            fileDialog.planFiles =      true
202
            fileDialog.selectExisting = false
203
            fileDialog.nameFilters =    masterController.saveNameFilters
204 205
            fileDialog.fileExtension =  QGroundControl.settingsManager.appSettings.planFileExtension
            fileDialog.fileExtension2 = QGroundControl.settingsManager.appSettings.missionFileExtension
206
            fileDialog.openForSave()
207 208
        }

209
        function fitViewportToItems() {
210
            mapFitFunctions.fitMapViewportToMissionItems()
211
        }
212

213 214 215 216 217 218 219 220 221 222
        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()
        }

223
        function saveKmlToSelectedFile() {
224 225 226 227
            if (!readyForSaveSend()) {
                waitingOnDataMessage()
                return
            }
228
            fileDialog.title =          qsTr("Save KML")
229
            fileDialog.planFiles =      false
230
            fileDialog.selectExisting = false
231
            fileDialog.nameFilters =    masterController.fileKmlFilters
232 233
            fileDialog.fileExtension =  QGroundControl.settingsManager.appSettings.kmlFileExtension
            fileDialog.fileExtension2 = ""
234 235
            fileDialog.openForSave()
        }
236
    }
237

238 239
    Connections {
        target: _missionController
240

241
        onNewItemsFromVehicle: {
242 243 244
            if (_visualItems && _visualItems.count != 1) {
                mapFitFunctions.fitMapViewportToMissionItems()
            }
245
            _missionController.setCurrentPlanViewIndex(0, true)
246 247
        }
    }
248

249
    QGCPalette { id: qgcPal; colorGroupEnabled: enabled }
Don Gagne's avatar
Don Gagne committed
250

251 252 253 254
    ExclusiveGroup {
        id: _mapTypeButtonsExclusiveGroup
    }

255 256 257 258
    /// Inserts a new simple mission item
    ///     @param coordinate Location to insert item
    ///     @param index Insert item at this index
    function insertSimpleMissionItem(coordinate, index) {
259
        var sequenceNumber = _missionController.insertSimpleMissionItem(coordinate, index)
260
        _missionController.setCurrentPlanViewIndex(sequenceNumber, true)
261 262
    }

263 264 265 266 267 268 269 270 271 272
    /// 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()
    }

273 274
    property int _moveDialogMissionItemIndex

275 276 277 278
    QGCFileDialog {
        id:             fileDialog
        qgcView:        _qgcView
        folder:         QGroundControl.settingsManager.appSettings.missionSavePath
279

280 281
        property bool planFiles: true    ///< true: working with plan files, false: working with kml file

282
        onAcceptedForSave: {
283 284 285 286 287
            if (planFiles) {
                masterController.saveToFile(file)
            } else {
                masterController.saveToKml(file)
            }
288
            close()
289 290
        }

291
        onAcceptedForLoad: {
292 293 294 295 296 297 298 299 300 301 302 303 304 305 306
            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) {
                    kmlPolygonSelectDialogKMLFile = file
                    _qgcView.showDialog(kmlPolygonSelectDialog, fileDialog.title, _qgcView.showDialogDefaultWidth, StandardButton.Ok | StandardButton.Cancel)
                } else if (retList[0] == KMLFileHelper.Polyline) {
                    insertComplexMissionItemFromKML(_missionController.corridorScanComplexItemName, file, -1)
                }
            }
307
            close()
308 309 310
        }
    }

311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367
    property string kmlPolygonSelectDialogKMLFile
    Component {
        id: kmlPolygonSelectDialog

        QGCViewDialog {
            property var editVehicle: _activeVehicle ? _activeVehicle : QGroundControl.multiVehicleManager.offlineEditingVehicle

            function accept() {
                var complexItemName
                if (surveyRadio.checked) {
                    complexItemName = _missionController.surveyComplexItemName
                } else {
                    complexItemName = _missionController.structureScanComplexItemName
                }
                insertComplexMissionItemFromKML(complexItemName, kmlPolygonSelectDialogKMLFile, -1)
                hideDialog()
            }

            Component.onCompleted: {
                if (editVehicle.fixedWing) {
                    // Only Survey available
                    accept()
                }
            }

            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
                    visible:        !editVehicle.fixedWing
                }
            }
        }
    }

368 369 370 371 372 373 374
    Component {
        id: moveDialog

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

375
                if (toIndex === 0) {
376 377
                    toIndex = 1
                }
378
                _missionController.moveMissionItem(_moveDialogMissionItemIndex, toIndex)
379 380 381 382 383 384 385 386 387 388 389 390
                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
391
                    text:           qsTr("Move the selected mission item to the be after following mission item:")
392 393 394 395
                }

                QGCComboBox {
                    id:             toCombo
396
                    model:          _visualItems.count
397 398 399 400 401 402
                    currentIndex:   _moveDialogMissionItemIndex
                }
            }
        }
    }

Don Gagne's avatar
Don Gagne committed
403 404
    QGCViewPanel {
        id:             panel
405
        anchors.fill:   parent
Don Gagne's avatar
Don Gagne committed
406

407
        FlightMap {
408 409 410 411 412
            id:                         editorMap
            anchors.fill:               parent
            mapName:                    "MissionEditor"
            allowGCSLocationCenter:     true
            allowVehicleLocationCenter: true
413
            planView:                   true
Don Gagne's avatar
Don Gagne committed
414

415 416
            // 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)
417

418 419
            property real _leftToolWidth:   toolStrip.x + toolStrip.width
            property real _statusHeight:    waypointValuesDisplay.visible ? editorMap.height - waypointValuesDisplay.y : 0
420

421
            readonly property real animationDuration: 500
422

423 424
            // Initial map position duplicates Fly view position
            Component.onCompleted: editorMap.center = QGroundControl.flightMapPosition
425

426 427 428 429 430 431
            Behavior on zoomLevel {
                NumberAnimation {
                    duration:       editorMap.animationDuration
                    easing.type:    Easing.InOutQuad
                }
            }
432

433 434 435 436 437 438 439
            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: {
440 441 442
                    // Take focus to close any previous editing
                    editorMap.focus = true

443 444 445 446
                    //-- Don't pay attention to items beneath the toolbar.
                    var topLimit = parent.height - ScreenTools.availableHeight
                    if(mouse.y < topLimit) {
                        return
447 448
                    }

449 450 451 452
                    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)
453

454 455 456
                    switch (_editingLayer) {
                    case _layerMission:
                        if (_addWaypointOnClick) {
457
                            insertSimpleMissionItem(coordinate, _missionController.visualItems.count)
458 459 460
                        } else if (_addROIOnClick) {
                            _addROIOnClick = false
                            insertROIMissionItem(coordinate, _missionController.visualItems.count)
461
                        }
462 463
                        break
                    case _layerRallyPoints:
464
                        if (_rallyPointController.supported) {
465
                            _rallyPointController.addPoint(coordinate)
466
                        }
467
                        break
Don Gagne's avatar
Don Gagne committed
468
                    }
Don Gagne's avatar
Don Gagne committed
469
                }
470
            }
Don Gagne's avatar
Don Gagne committed
471

472 473
            // Add the mission item visuals to the map
            Repeater {
474
                model: _editingLayer == _layerMission ? _missionController.visualItems : undefined
475

476 477
                delegate: MissionItemMapVisual {
                    map:        editorMap
DonLakeFlyer's avatar
DonLakeFlyer committed
478
                    qgcView:    _qgcView
479
                    onClicked:  _missionController.setCurrentPlanViewIndex(sequenceNumber, false)
480
                    visible:    _editingLayer == _layerMission
481
                }
482
            }
483

484 485
            // Add lines between waypoints
            MissionLineView {
486
                model: _editingLayer == _layerMission ? _missionController.waypointLines : undefined
487
            }
488

489 490 491 492 493 494 495
            // Add the vehicles to the map
            MapItemView {
                model: QGroundControl.multiVehicleManager.vehicles
                delegate:
                    VehicleMapItem {
                    vehicle:        object
                    coordinate:     object.coordinate
496
                    map:            editorMap
497 498
                    size:           ScreenTools.defaultFontPixelHeight * 3
                    z:              QGroundControl.zOrderMapItems - 1
499
                }
500
            }
501

502 503
            GeoFenceMapVisuals {
                map:                    editorMap
504
                myGeoFenceController:   _geoFenceController
505
                interactive:            _editingLayer == _layerGeoFence
506
                homePosition:           _missionController.plannedHomePosition
507 508
                planView:               true
            }
509

510 511
            RallyPointMapVisuals {
                map:                    editorMap
512
                myRallyPointController: _rallyPointController
513 514
                interactive:            _editingLayer == _layerRallyPoints
                planView:               true
515
            }
516

517 518 519 520 521 522 523 524 525
            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
526 527 528 529 530
                showAlternateIcon:  [ false, false, false, masterController.dirty, false, false, false ]
                rotateImage:        [ false, false, false, masterController.syncInProgress, false, false, false ]
                animateImage:       [ false, false, false, masterController.dirty, false, false, false ]
                buttonEnabled:      [ true, true, true, !masterController.syncInProgress, true, true, true ]
                buttonVisible:      [ true, _waypointsOnlyMode, true, true, true, _showZoom, _showZoom ]
531 532 533 534 535 536 537 538 539 540
                maxHeight:          mapScale.y - toolStrip.y

                property bool _showZoom: !ScreenTools.isMobile

                model: [
                    {
                        name:       "Waypoint",
                        iconSource: "/qmlimages/MapAddMission.svg",
                        toggle:     true
                    },
541 542 543 544 545
                    {
                        name:       "ROI",
                        iconSource: "/qmlimages/MapAddMission.svg",
                        toggle:     true
                    },
546
                    {
547
                        name:               _singleComplexItem ? _missionController.complexMissionItemNames[0] : "Pattern",
548 549 550
                        iconSource:         "/qmlimages/MapDrawShape.svg",
                        dropPanelComponent: _singleComplexItem ? undefined : patternDropPanel
                    },
551 552 553 554 555 556
                    {
                        name:                   "Sync",
                        iconSource:             "/qmlimages/MapSync.svg",
                        alternateIconSource:    "/qmlimages/MapSyncChanged.svg",
                        dropPanelComponent:     syncDropPanel
                    },
557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575
                    {
                        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
576
                        _addROIOnClick = false
577 578
                        break
                    case 1:
579 580 581 582
                        _addROIOnClick = checked
                        _addWaypointOnClick = false
                        break
                    case 2:
583
                        if (_singleComplexItem) {
584
                            addComplexItem(_missionController.complexMissionItemNames[0])
585
                        }
586
                        break
587
                    case 5:
588 589
                        editorMap.zoomLevel += 0.5
                        break
590
                    case 6:
591 592 593 594 595 596 597 598 599 600 601 602
                        editorMap.zoomLevel -= 0.5
                        break
                    }
                }
            }
        } // FlightMap

        // Right pane for mission editing controls
        Rectangle {
            id:                 rightPanel
            anchors.bottom:     parent.bottom
            anchors.right:      parent.right
603
            height:             ScreenTools.availableHeight
604 605
            width:              _rightPanelWidth
            color:              qgcPal.window
606 607 608 609 610
            opacity:            0.2
        }

        Item {
            anchors.fill:   rightPanel
611 612 613 614

            // Plan Element selector (Mission/Fence/Rally)
            Row {
                id:                 planElementSelectorRow
615
                anchors.topMargin:  Math.round(ScreenTools.defaultFontPixelHeight / 3)
616 617 618 619
                anchors.top:        parent.top
                anchors.left:       parent.left
                anchors.right:      parent.right
                spacing:            _horizontalMargin
620
                visible:            QGroundControl.corePlugin.options.enablePlanViewSelector
621 622 623 624 625 626 627 628 629

                readonly property real _buttonRadius: ScreenTools.defaultFontPixelHeight * 0.75

                ExclusiveGroup {
                    id: planElementSelectorGroup
                    onCurrentChanged: {
                        switch (current) {
                        case planElementMission:
                            _editingLayer = _layerMission
630
                            break
631 632
                        case planElementGeoFence:
                            _editingLayer = _layerGeoFence
Don Gagne's avatar
Don Gagne committed
633
                            break
634 635
                        case planElementRallyPoints:
                            _editingLayer = _layerRallyPoints
Don Gagne's avatar
Don Gagne committed
636
                            break
637 638 639 640
                        }
                    }
                }

641 642 643 644 645 646 647 648
                QGCRadioButton {
                    id:             planElementMission
                    exclusiveGroup: planElementSelectorGroup
                    text:           qsTr("Mission")
                    checked:        true
                    color:          mapPal.text
                    textStyle:      Text.Outline
                    textStyleColor: mapPal.textOutline
649 650
                }

651 652 653 654 655 656 657 658 659
                Item { height: 1; width: 1 }

                QGCRadioButton {
                    id:             planElementGeoFence
                    exclusiveGroup: planElementSelectorGroup
                    text:           qsTr("Fence")
                    color:          mapPal.text
                    textStyle:      Text.Outline
                    textStyleColor: mapPal.textOutline
660
                }
661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676

                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
677
                anchors.topMargin:  ScreenTools.defaultFontPixelHeight / 2
678 679 680 681 682 683 684 685 686 687 688
                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
689
                    model:          _missionController.visualItems
690 691
                    cacheBuffer:    Math.max(height * 2, 0)
                    clip:           true
692
                    currentIndex:   _missionController.currentPlanViewIndex
693 694 695
                    highlightMoveDuration: 250

                    delegate: MissionItemEditor {
696
                        map:                editorMap
697
                        masterController:  _planMasterController
698 699 700 701
                        missionItem:        object
                        width:              parent.width
                        readOnly:           false
                        rootQgcView:        _qgcView
702

703
                        onClicked:  _missionController.setCurrentPlanViewIndex(object.sequenceNumber, false)
704 705 706

                        onRemove: {
                            var removeIndex = index
707 708
                            _missionController.removeMissionItem(removeIndex)
                            if (removeIndex >= _missionController.visualItems.count) {
709 710
                                removeIndex--
                            }
711
                            _missionController.setCurrentPlanViewIndex(removeIndex, true)
712 713
                        }

714 715
                        onInsertWaypoint:       insertSimpleMissionItem(editorMap.center, index)
                        onInsertComplexItem:    insertComplexMissionItem(complexItemName, editorMap.center, index)
716 717 718 719 720
                    }
                } // QGCListView
            } // Item - Mission Item editor

            // GeoFence Editor
721 722 723 724 725 726
            GeoFenceEditor {
                anchors.topMargin:      ScreenTools.defaultFontPixelHeight / 2
                anchors.top:            planElementSelectorRow.bottom
                anchors.left:           parent.left
                anchors.right:          parent.right
                availableHeight:        ScreenTools.availableHeight
727
                myGeoFenceController:   _geoFenceController
728 729
                flightMap:              editorMap
                visible:                _editingLayer == _layerGeoFence
730 731 732 733 734 735
            }

            // Rally Point Editor

            RallyPointEditorHeader {
                id:                 rallyPointHeader
736 737
                anchors.topMargin:  ScreenTools.defaultFontPixelHeight / 2
                anchors.top:        planElementSelectorRow.bottom
738 739 740
                anchors.left:       parent.left
                anchors.right:      parent.right
                visible:            _editingLayer == _layerRallyPoints
741
                controller:         _rallyPointController
742 743 744 745
            }

            RallyPointItemEditor {
                id:                 rallyPointEditor
746 747
                anchors.topMargin:  ScreenTools.defaultFontPixelHeight / 2
                anchors.top:        rallyPointHeader.bottom
748 749
                anchors.left:       parent.left
                anchors.right:      parent.right
750 751 752
                visible:            _editingLayer == _layerRallyPoints && _rallyPointController.points.count
                rallyPoint:         _rallyPointController.currentRallyPoint
                controller:         _rallyPointController
753 754
            }
        } // Right panel
755 756 757 758 759 760 761

        MapScale {
            id:                 mapScale
            anchors.margins:    ScreenTools.defaultFontPixelHeight * (0.66)
            anchors.bottom:     waypointValuesDisplay.visible ? waypointValuesDisplay.top : parent.bottom
            anchors.left:       parent.left
            mapControl:         editorMap
762
            visible:            _toolStripBottom < y
763 764 765 766 767 768
        }

        MissionItemStatus {
            id:                 waypointValuesDisplay
            anchors.margins:    ScreenTools.defaultFontPixelWidth
            anchors.left:       parent.left
769
            height:             ScreenTools.defaultFontPixelHeight * 7
770
            maxWidth:           parent.width - rightPanel.width - x
771
            anchors.bottom:     parent.bottom
772
            missionItems:       _missionController.visualItems
773
            visible:            _editingLayer === _layerMission && (_toolStripBottom + mapScale.height) < y && QGroundControl.corePlugin.options.showMissionStatus
774
        }
Don Gagne's avatar
Don Gagne committed
775
    } // QGCViewPanel
776

777 778 779 780
    Component {
        id: syncLoadFromVehicleOverwrite
        QGCViewMessage {
            id:         syncLoadFromVehicleCheck
Don Gagne's avatar
Don Gagne committed
781
            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?")
782 783
            function accept() {
                hideDialog()
784
                masterController.loadFromVehicle()
785 786 787 788 789 790 791 792
            }
        }
    }

    Component {
        id: syncLoadFromFileOverwrite
        QGCViewMessage {
            id:         syncLoadFromVehicleCheck
DonLakeFlyer's avatar
DonLakeFlyer committed
793
            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?")
794 795
            function accept() {
                hideDialog()
796
                masterController.loadFromSelectedFile()
797 798 799 800
            }
        }
    }

801 802 803
    Component {
        id: removeAllPromptDialog
        QGCViewMessage {
804 805
            message: qsTr("Are you sure you want to remove all items? ") +
                     (_planMasterController.offline ? "" : qsTr("This will also remove all items from the vehicle."))
806
            function accept() {
807 808 809 810 811
                if (_planMasterController.offline) {
                    masterController.removeAll()
                } else {
                    masterController.removeAllFromVehicle()
                }
812 813 814 815 816
                hideDialog()
            }
        }
    }

817 818 819 820 821 822 823 824 825 826 827
    //- ToolStrip DropPanel Components

    Component {
        id: centerMapDropPanel

        CenterMapDropPanel {
            map:            editorMap
            fitFunctions:   mapFitFunctions
        }
    }

828 829 830 831 832 833 834 835 836
    Component {
        id: patternDropPanel

        ColumnLayout {
            spacing:    ScreenTools.defaultFontPixelWidth * 0.5

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

            Repeater {
837
                model: _missionController.complexMissionItemNames
838 839 840 841 842 843

                QGCButton {
                    text:               modelData
                    Layout.fillWidth:   true

                    onClicked: {
844
                        addComplexItem(modelData)
845 846 847 848 849 850
                        dropPanel.hide()
                    }
                }
            }
        } // Column
    }
851 852

    Component {
853
        id: syncDropPanel
854

855 856 857
        Column {
            id:         columnHolder
            spacing:    _margin
858

859
            property string _overwriteText: (_editingLayer == _layerMission) ? qsTr("Mission overwrite") : ((_editingLayer == _layerGeoFence) ? qsTr("GeoFence overwrite") : qsTr("Rally Points overwrite"))
860

861 862 863
            QGCLabel {
                width:      sendSaveGrid.width
                wrapMode:   Text.WordWrap
864
                text:       masterController.dirty ?
865 866
                                qsTr("You have unsaved changes. You should upload to your vehicle, or save to a file:") :
                                qsTr("Sync:")
867 868
            }

869 870 871 872 873 874
            GridLayout {
                id:                 sendSaveGrid
                columns:            2
                anchors.margins:    _margin
                rowSpacing:         _margin
                columnSpacing:      ScreenTools.defaultFontPixelWidth
875

876 877 878
                QGCButton {
                    text:               qsTr("Upload")
                    Layout.fillWidth:   true
879
                    enabled:            !masterController.offline && !masterController.syncInProgress
880 881
                    onClicked: {
                        dropPanel.hide()
882
                        masterController.upload()
883 884
                    }
                }
885

886 887 888
                QGCButton {
                    text:               qsTr("Download")
                    Layout.fillWidth:   true
889
                    enabled:            !masterController.offline && !masterController.syncInProgress
890 891
                    onClicked: {
                        dropPanel.hide()
892
                        if (masterController.dirty) {
893 894
                            _qgcView.showDialog(syncLoadFromVehicleOverwrite, columnHolder._overwriteText, _qgcView.showDialogDefaultWidth, StandardButton.Yes | StandardButton.Cancel)
                        } else {
895
                            masterController.loadFromVehicle()
896 897 898
                        }
                    }
                }
899

900
                QGCButton {
901
                    text:               qsTr("Save Plan...")
902
                    Layout.fillWidth:   true
903
                    enabled:            !masterController.syncInProgress
904 905
                    onClicked: {
                        dropPanel.hide()
906
                        masterController.saveToSelectedFile()
907 908 909 910
                    }
                }

                QGCButton {
911
                    text:               qsTr("Load Plan...")
912
                    Layout.fillWidth:   true
913
                    enabled:            !masterController.syncInProgress
914 915
                    onClicked: {
                        dropPanel.hide()
916
                        if (masterController.dirty) {
917 918
                            _qgcView.showDialog(syncLoadFromFileOverwrite, columnHolder._overwriteText, _qgcView.showDialogDefaultWidth, StandardButton.Yes | StandardButton.Cancel)
                        } else {
919
                            masterController.loadFromSelectedFile()
920 921 922 923 924
                        }
                    }
                }

                QGCButton {
925
                    text:               qsTr("Load KML...")
926
                    Layout.fillWidth:   true
927 928
                    enabled:            !masterController.syncInProgress
                    onClicked: {
929
                        dropPanel.hide()
930
                        masterController.loadKmlFromSelectedFile()
931 932
                    }
                }
933

934

935 936 937 938 939
                QGCButton {
                    text:               qsTr("Save KML...")
                    Layout.fillWidth:   true
                    enabled:            !masterController.syncInProgress
                    onClicked: {
940 941 942 943 944
                        // First point do not count
                        if (_visualItems.count < 2) {
                            _qgcView.showDialog(noItemForKML, qsTr("KML"), _qgcView.showDialogDefaultWidth, StandardButton.Cancel)
                            return
                        }
945 946 947 948
                        dropPanel.hide()
                        masterController.saveKmlToSelectedFile()
                    }
                }
949 950 951 952 953 954 955 956 957

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