10import QGroundControl.FlightMap
11import QGroundControl.Controls
12import QGroundControl.FactControls
13import QGroundControl.FlyView
14import QGroundControl.Geo
15import QGroundControl.Toolbar
20 readonly property int _decimalPlaces: 8
21 readonly property real _margin: ScreenTools.defaultFontPixelHeight * 0.5
22 readonly property real _toolsMargin: ScreenTools.defaultFontPixelWidth * 0.75
23 readonly property real _rightPanelWidth: Math.min(width / 3, ScreenTools.defaultFontPixelWidth * 30)
25 property var _planMasterController: planMasterController
26 property var _missionController: _planMasterController.missionController
27 property var _geoFenceController: _planMasterController.geoFenceController
28 property var _rallyPointController: _planMasterController.rallyPointController
29 property var _visualItems: _missionController.visualItems
30 property bool _singleComplexItem: _missionController.complexMissionItems.length === 1
31 property int _editingLayer: _layerMission
32 property var _appSettings: QGroundControl.settingsManager.appSettings
33 property var _planViewSettings: QGroundControl.settingsManager.planViewSettings
34 property bool _promptForPlanUsageShowing: false
35 property bool _addROIOnClick: false
36 property bool _addWaypointOnClick: false
38 readonly property int _layerMission: 1
39 readonly property int _layerFence: 2
40 readonly property int _layerRally: 3
44 editorMap.zoomLevel = QGroundControl.flightMapZoom
45 editorMap.center = QGroundControl.flightMapPosition
51 function onToolbarButtonClicked() {
52 _addWaypointOnClick = false
53 _addROIOnClick = false
57 function mapCenter() {
58 var coordinate = editorMap.center
59 coordinate.latitude = coordinate.latitude.toFixed(_decimalPlaces)
60 coordinate.longitude = coordinate.longitude.toFixed(_decimalPlaces)
61 coordinate.altitude = coordinate.altitude.toFixed(_decimalPlaces)
66 id: mapFitFunctions // The name for this id cannot be changed without breaking references outside of this code. Beware!
68 usePlannedHomePosition: true
69 planMasterController: _planMasterController
72 PlanMasterController {
73 id: planMasterController
76 Component.onCompleted: {
77 _planMasterController.start()
78 _missionController.setCurrentPlanViewSeqNum(0, true)
81 onPromptForPlanUsageOnVehicleChange: {
82 if (!_promptForPlanUsageShowing) {
83 _promptForPlanUsageShowing = true
84 promptForPlanUsageOnVehicleChangePopupFactory.open()
88 function waitingOnIncompleteDataMessage(save) {
89 var saveOrUpload = save ? qsTr("Save") : qsTr("Upload")
90 QGroundControl.showMessageDialog(_root, qsTr("Unable to %1").arg(saveOrUpload), qsTr("Plan has incomplete items. Complete all items and %1 again.").arg(saveOrUpload))
93 function waitingOnTerrainDataMessage(save) {
94 var saveOrUpload = save ? qsTr("Save") : qsTr("Upload")
95 QGroundControl.showMessageDialog(_root, qsTr("Unable to %1").arg(saveOrUpload), qsTr("Plan is waiting on terrain data from server for correct altitude values."))
98 function checkReadyForSaveUpload(save) {
99 if (readyForSaveState() == VisualMissionItem.NotReadyForSaveData) {
100 waitingOnIncompleteDataMessage(save)
102 } else if (readyForSaveState() == VisualMissionItem.NotReadyForSaveTerrain) {
103 waitingOnTerrainDataMessage(save)
110 if (!checkReadyForSaveUpload(false /* save */)) {
113 switch (_missionController.sendToVehiclePreCheck()) {
114 case MissionController.SendToVehiclePreCheckStateOk: sendToVehicle()
116 case MissionController.SendToVehiclePreCheckStateActiveMission: QGroundControl.showMessageDialog(_root, qsTr("Send To Vehicle"), qsTr("Current mission must be paused prior to uploading a new Plan"))
118 case MissionController.SendToVehiclePreCheckStateFirwmareVehicleMismatch: QGroundControl.showMessageDialog(_root, qsTr("Plan Upload"),
119 qsTr("This Plan was created for a different firmware or vehicle type than the firmware/vehicle type of vehicle you are uploading to. " +
120 "This can lead to errors or incorrect behavior. " +
121 "It is recommended to recreate the Plan for the correct firmware/vehicle type.\n\n" +
122 "Click 'Ok' to upload the Plan anyway."),
123 Dialog.Ok | Dialog.Cancel,
124 function() { _planMasterController.sendToVehicle() })
129 function loadFromSelectedFile() {
130 fileDialog.title = qsTr("Select Plan File")
131 fileDialog.planFiles = true
132 fileDialog.nameFilters = _planMasterController.loadNameFilters
133 fileDialog.openForLoad()
136 function saveToSelectedFile() {
137 if (!checkReadyForSaveUpload(true /* save */)) {
140 fileDialog.title = qsTr("Save Plan")
141 fileDialog.planFiles = true
142 fileDialog.nameFilters = _planMasterController.saveNameFilters
143 fileDialog.openForSave()
146 function fitViewportToItems() {
147 mapFitFunctions.fitMapViewportToMissionItems()
150 function saveKmlToSelectedFile() {
151 if (!checkReadyForSaveUpload(true /* save */)) {
154 fileDialog.title = qsTr("Save KML")
155 fileDialog.planFiles = false
156 fileDialog.nameFilters = ShapeFileHelper.fileDialogKMLFilters
157 fileDialog.openForSave()
162 target: _missionController
164 function onNewItemsFromVehicle() {
165 if (_visualItems && _visualItems.count !== 1) {
166 mapFitFunctions.fitMapViewportToMissionItems()
168 _missionController.setCurrentPlanViewSeqNum(0, true)
172 function insertSimpleItemAfterCurrent(coordinate) {
173 var nextIndex = _missionController.currentPlanViewVIIndex + 1
174 _missionController.insertSimpleMissionItem(coordinate, nextIndex, true /* makeCurrentItem */)
177 function insertROIAfterCurrent(coordinate) {
178 var nextIndex = _missionController.currentPlanViewVIIndex + 1
179 _missionController.insertROIMissionItem(coordinate, nextIndex, true /* makeCurrentItem */)
182 function insertCancelROIAfterCurrent() {
183 var nextIndex = _missionController.currentPlanViewVIIndex + 1
184 _missionController.insertCancelROIMissionItem(nextIndex, true /* makeCurrentItem */)
187 function insertComplexItemAfterCurrent(complexItemName) {
188 var nextIndex = _missionController.currentPlanViewVIIndex + 1
189 _missionController.insertComplexMissionItem(complexItemName, mapCenter(), nextIndex, true /* makeCurrentItem */)
192 function insertTakeoffItemAfterCurrent() {
193 var nextIndex = _missionController.currentPlanViewVIIndex + 1
194 _missionController.insertTakeoffItem(mapCenter(), nextIndex, true /* makeCurrentItem */)
197 function insertLandItemAfterCurrent() {
198 var nextIndex = _missionController.currentPlanViewVIIndex + 1
199 _missionController.insertLandItem(mapCenter(), nextIndex, true /* makeCurrentItem */)
204 folder: _appSettings ? _appSettings.missionSavePath : ""
206 property bool planFiles: true ///< true: working with plan files, false: working with kml file
208 onAcceptedForSave: (file) => {
210 if (_planMasterController.saveToFile(file)) {
214 _planMasterController.saveToKml(file)
219 onAcceptedForLoad: (file) => {
220 _planMasterController.loadFromFile(file)
221 _planMasterController.fitViewportToItems()
222 _missionController.setCurrentPlanViewSeqNum(0, true)
229 planMasterController: _planMasterController
230 showRallyPointsHelp: _editingLayer === _layerRally
235 anchors.left: parent.left
236 anchors.right: parent.right
237 anchors.top: planToolBar.bottom
238 anchors.bottom: parent.bottom
242 objectName: "planView_map"
244 mapName: "MissionEditor"
245 allowGCSLocationCenter: true
246 allowVehicleLocationCenter: true
249 zoomLevel: QGroundControl.flightMapZoom
250 center: QGroundControl.flightMapPosition
252 // This is the center rectangle of the map which is not obscured by tools
253 property rect centerViewport: Qt.rect(_leftToolWidth + _margin, _margin, editorMap.width - _leftToolWidth - _rightToolWidth - (_margin * 2), (missionStatus.visible ? missionStatus.y : height - _margin) - _margin)
255 property real _leftToolWidth: toolStrip.x + toolStrip.width
256 property real _rightToolWidth: rightPanel.width + rightPanel.anchors.rightMargin
257 property real _nonInteractiveOpacity: 0.5
259 // Initial map position duplicates Fly view position
260 Component.onCompleted: editorMap.center = QGroundControl.flightMapPosition
262 onZoomLevelChanged: {
263 QGroundControl.flightMapZoom = editorMap.zoomLevel
266 QGroundControl.flightMapPosition = editorMap.center
269 onMapClicked: (mouse) => {
270 // Take focus to close any previous editing
271 editorMap.focus = true
273 // Collapse layer switcher on any map click
274 layerSwitcher.expanded = false
277 if (!mainWindow.allowViewSwitch()) {
280 var coordinate = editorMap.toCoordinate(Qt.point(mouse.x, mouse.y), false /* clipToViewPort */)
281 coordinate.latitude = coordinate.latitude.toFixed(_decimalPlaces)
282 coordinate.longitude = coordinate.longitude.toFixed(_decimalPlaces)
283 coordinate.altitude = coordinate.altitude.toFixed(_decimalPlaces)
285 switch (_editingLayer) {
287 if (_planMasterController.showCreateFromTemplate) {
288 _missionController.setHomePosition(coordinate)
289 } else if (_addROIOnClick) {
290 _addROIOnClick = false
291 if (_missionController.isROIActive) {
292 var pos = Qt.point(mouse.x, mouse.y)
293 // For some strange reason using mainWindow in mapToItem doesn't work, so we use globals.parent instead which also gets us mainWindow
294 pos = editorMap.mapToItem(globals.parent, pos)
295 var dropPanel = insertOrCancelROIDropPanelComponent.createObject(mainWindow, { mapClickCoord: coordinate, clickRect: Qt.rect(pos.x, pos.y, 0, 0) })
298 insertROIAfterCurrent(coordinate)
300 } else if (_addWaypointOnClick) {
301 insertSimpleItemAfterCurrent(coordinate)
305 if (_rallyPointController.supported) {
306 _rallyPointController.addPoint(coordinate)
312 // Add the mission item visuals to the map
314 model: _missionController.visualItems
315 delegate: MissionItemMapVisual {
317 opacity: _editingLayer == _layerMission ? 1 : editorMap._nonInteractiveOpacity
318 interactive: _editingLayer == _layerMission
319 vehicle: _planMasterController.controllerVehicle
320 onClicked: (sequenceNumber) => { _missionController.setCurrentPlanViewSeqNum(sequenceNumber, false) }
324 // Add lines between waypoints
326 showSpecialVisual: _missionController.isROIBeginCurrentItem
327 model: _missionController.simpleFlightPathSegments
328 opacity: _editingLayer == _layerMission ? 1 : editorMap._nonInteractiveOpacity
331 // Direction arrows in waypoint lines
333 model: _editingLayer == _layerMission ? _missionController.directionArrows : undefined
335 delegate: MapLineArrow {
336 fromCoord: object ? object.coordinate1 : undefined
337 toCoord: object ? object.coordinate2 : undefined
339 z: QGroundControl.zOrderWaypointLines + 1
343 // UI for splitting the current segment
346 anchorPoint.x: sourceItem.width / 2
347 anchorPoint.y: sourceItem.height / 2
348 z: QGroundControl.zOrderWaypointLines + 1
349 visible: _editingLayer == _layerMission
351 sourceItem: SplitIndicator {
352 onClicked: _missionController.insertSimpleMissionItem(splitSegmentItem.coordinate,
353 _missionController.currentPlanViewVIIndex,
354 true /* makeCurrentItem */)
357 function _updateSplitCoord() {
358 if (_missionController.splitSegment) {
359 var distance = _missionController.splitSegment.coordinate1.distanceTo(_missionController.splitSegment.coordinate2)
360 var azimuth = _missionController.splitSegment.coordinate1.azimuthTo(_missionController.splitSegment.coordinate2)
361 splitSegmentItem.coordinate = _missionController.splitSegment.coordinate1.atDistanceAndAzimuth(distance / 2, azimuth)
363 coordinate = QtPositioning.coordinate()
368 target: _missionController
369 function onSplitSegmentChanged() { splitSegmentItem._updateSplitCoord() }
373 target: _missionController.splitSegment
374 function onCoordinate1Changed() { splitSegmentItem._updateSplitCoord() }
375 function onCoordinate2Changed() { splitSegmentItem._updateSplitCoord() }
379 // Add the vehicles to the map
381 model: QGroundControl.multiVehicleManager.vehicles
382 delegate: VehicleMapItem {
384 coordinate: object.coordinate
386 size: ScreenTools.defaultFontPixelHeight * 3
387 z: QGroundControl.zOrderMapItems - 1
393 myGeoFenceController: _geoFenceController
394 interactive: _editingLayer == _layerFence
395 homePosition: _missionController.plannedHomePosition
397 opacity: _editingLayer != _layerFence ? editorMap._nonInteractiveOpacity : 1
400 RallyPointMapVisuals {
402 myRallyPointController: _rallyPointController
403 interactive: _editingLayer == _layerRally
405 opacity: _editingLayer != _layerRally ? editorMap._nonInteractiveOpacity : 1
410 //-----------------------------------------------------------
414 anchors.margins: _toolsMargin
415 anchors.left: parent.left
416 anchors.top: parent.top
417 z: QGroundControl.zOrderWidgets
418 maxHeight: parent.height - toolStrip.y
419 visible: _editingLayer == _layerMission
421 property bool _isMissionLayer: _editingLayer == _layerMission
424 target: waypointButton
426 value: _addWaypointOnClick
432 value: _addROIOnClick
435 ToolStripActionList {
436 id: toolStripActionList
439 objectName: "planToolStrip_takeoffButton"
440 text: qsTr("Takeoff")
441 iconSource: "/res/takeoff.svg"
442 enabled: _missionController.isInsertTakeoffValid
443 visible: toolStrip._isMissionLayer && !_planMasterController.controllerVehicle.rover
445 insertTakeoffItemAfterCurrent()
449 objectName: "planToolStrip_patternButton"
450 text: _singleComplexItem ? _missionController.complexMissionItems[0].translatedName : qsTr("Pattern")
451 iconSource: "/qmlimages/MapDrawShape.svg"
452 enabled: _missionController.flyThroughCommandsAllowed
453 visible: toolStrip._isMissionLayer
454 dropPanelComponent: _singleComplexItem ? undefined : patternDropPanel
456 if (_singleComplexItem) {
457 insertComplexItemAfterCurrent(_missionController.complexMissionItems[0].canonicalName)
463 objectName: "planToolStrip_waypointButton"
464 text: qsTr("Waypoint")
465 iconSource: "/res/waypoint.svg"
466 enabled: _missionController.flyThroughCommandsAllowed
467 visible: toolStrip._isMissionLayer
469 onTriggered: { _addWaypointOnClick = !_addWaypointOnClick; if (_addWaypointOnClick) _addROIOnClick = false }
473 objectName: "planToolStrip_roiButton"
474 text: _missionController.isROIActive ? qsTr("Cancel ROI") : qsTr("ROI")
475 iconSource: "/qmlimages/roi.svg"
476 enabled: _missionController.isInsertROIValid
477 visible: toolStrip._isMissionLayer && _planMasterController.controllerVehicle.supports.roiMode
479 onTriggered: { _addROIOnClick = !_addROIOnClick; if (_addROIOnClick) _addWaypointOnClick = false }
482 objectName: "planToolStrip_landButton"
483 text: _planMasterController.controllerVehicle.multiRotor
485 : _missionController.isInsertLandValid && _missionController.hasLandItem
488 iconSource: "/res/rtl.svg"
489 enabled: _missionController.isInsertLandValid
490 visible: toolStrip._isMissionLayer
492 insertLandItemAfterCurrent()
497 iconSource: "/res/chevron-double-right.svg"
498 visible: missionStatus.hidden && QGroundControl.corePlugin.options.showMissionStatus
499 onTriggered: missionStatus.showMissionStatus()
504 model: toolStripActionList.model
508 anchors.margins: _toolsMargin
509 anchors.left: toolStrip.right
510 anchors.top: parent.top
511 mapControl: editorMap
517 anchors.top: parent.top
518 anchors.bottom: parent.bottom
519 anchors.right: parent.right
520 width: _rightPanelWidth
521 planMasterController: _planMasterController
523 onEditingLayerChangeRequested: (layer) => _editingLayer = layer
526 // Layer switching icons — only active icon visible; click to expand choices leftward
529 anchors.right: rightPanel.left
530 anchors.rightMargin: _toolsMargin
531 anchors.top: parent.top
532 anchors.topMargin: _toolsMargin
533 width: layerRow.width
534 height: _layerButtonSize
535 z: QGroundControl.zOrderWidgets
537 property bool expanded: false
538 property real _layerButtonSize: ScreenTools.defaultFontPixelHeight * 2.0
539 property real _spacing: ScreenTools.defaultFontPixelHeight * 0.25
541 readonly property var _layers: [
542 { layer: _layerMission, icon: "/res/waypoint.svg", nodeType: "missionGroup" },
543 { layer: _layerFence, icon: "/res/GeoFence.svg", nodeType: "fenceGroup" },
544 { layer: _layerRally, icon: "/res/RallyPoint.svg", nodeType: "rallyGroup" }
550 onTriggered: layerSwitcher.expanded = false
556 collapseTimer.restart()
562 function choose(nodeType) {
565 rightPanel.selectLayer(nodeType)
568 // Row laid out right-to-left: active icon on the right, choices expand left
571 anchors.right: parent.right
572 spacing: layerSwitcher._spacing
573 layoutDirection: Qt.RightToLeft
575 // Active layer button (always visible)
577 width: layerSwitcher._layerButtonSize
579 radius: ScreenTools.defaultBorderRadius
580 color: QGroundControl.globalPalette.buttonHighlight
583 anchors.centerIn: parent
584 width: parent.width * 0.6
586 source: layerSwitcher._layers.find(l => l.layer === _editingLayer)?.icon ?? "/res/waypoint.svg"
587 color: QGroundControl.globalPalette.buttonHighlightText
592 onClicked: layerSwitcher.toggle()
596 // Choice buttons (only layers that are NOT the current one)
598 model: layerSwitcher._layers.filter(l => l.layer !== _editingLayer)
601 required property var modelData
602 width: layerSwitcher._layerButtonSize
604 radius: ScreenTools.defaultBorderRadius
605 color: QGroundControl.globalPalette.button
607 opacity: layerSwitcher.expanded ? 1 : 0
609 Behavior on opacity { NumberAnimation { duration: 150 } }
612 anchors.centerIn: parent
613 width: parent.width * 0.6
615 source: modelData.icon
616 color: QGroundControl.globalPalette.buttonText
621 onClicked: layerSwitcher.choose(modelData.nodeType)
630 anchors.margins: _toolsMargin
631 anchors.left: _calcLeftAnchor()
632 anchors.right: rightPanel.left
633 anchors.bottom: parent.bottom
635 visible: !hidden && _editingLayer == _layerMission && QGroundControl.corePlugin.options.showMissionStatus
637 readonly property bool hidden: _planViewSettings.showMissionItemStatus.rawValue ? false : true
639 function showMissionStatus() {
640 _planViewSettings.showMissionItemStatus.rawValue = true
643 function _calcLeftAnchor() {
644 let bottomOfToolStrip = toolStrip.y + toolStrip.height
645 let largestStatsHeight = Math.max(terrainStatus.height, missionStats.height)
646 if (bottomOfToolStrip + largestStatsHeight > parent.height - missionStatus.anchors.margins) {
647 return toolStrip.right
652 function _toggleMissionStatusVisibility() {
653 _planViewSettings.showMissionItemStatus.rawValue = _planViewSettings.showMissionItemStatus.rawValue ? false : true
657 id: missionStatsButtonLayout
658 Layout.alignment: Qt.AlignBottom
661 property real _buttonImplicitWidth: ScreenTools.defaultFontPixelHeight * 1.5
662 property real _buttonImageMargins: _buttonImplicitWidth * 0.15
666 implicitWidth: missionStatsButtonLayout._buttonImplicitWidth
667 implicitHeight: implicitWidth
668 color: checked ? QGroundControl.globalPalette.buttonHighlight : QGroundControl.globalPalette.button
670 property bool checked: true
673 anchors.margins: missionStatsButtonLayout._buttonImageMargins
675 source: "/res/terrain.svg"
676 color: parent.checked ? QGroundControl.globalPalette.buttonHighlightText : QGroundControl.globalPalette.buttonText
682 terrainButton.checked = true
683 missionStatsButton.checked = false
689 id: missionStatsButton
690 implicitWidth: missionStatsButtonLayout._buttonImplicitWidth
691 implicitHeight: implicitWidth
692 color: checked ? QGroundControl.globalPalette.buttonHighlight : QGroundControl.globalPalette.button
694 property bool checked: false
697 anchors.margins: missionStatsButtonLayout._buttonImageMargins
699 source: "/res/sliders.svg"
700 color: parent.checked ? QGroundControl.globalPalette.buttonHighlightText : QGroundControl.globalPalette.buttonText
706 missionStatsButton.checked = true
707 terrainButton.checked = false
713 id: bottomStatusOpenCloseButton
714 implicitWidth: missionStatsButtonLayout._buttonImplicitWidth
715 implicitHeight: implicitWidth
716 color: QGroundControl.globalPalette.button
719 anchors.margins: missionStatsButtonLayout._buttonImageMargins
721 source: "/res/chevron-double-left.svg"
722 color: QGroundControl.globalPalette.buttonText
727 onClicked: missionStatus._toggleMissionStatusVisibility()
734 Layout.alignment: Qt.AlignBottom
735 Layout.fillWidth: true
736 height: ScreenTools.defaultFontPixelHeight * 7
737 missionController: _missionController
738 visible: terrainButton.checked
739 onSetCurrentSeqNum: _missionController.setCurrentPlanViewSeqNum(seqNum, true)
744 Layout.alignment: Qt.AlignBottom
745 Layout.fillWidth: true
746 visible: missionStatsButton.checked
747 planMasterController: _root._planMasterController
752 //- ToolStrip ToolStripDropPanel Components
758 spacing: ScreenTools.defaultFontPixelWidth * 0.5
760 QGCLabel { text: qsTr("Create complex pattern:") }
763 model: _missionController.complexMissionItems
766 text: modelData.translatedName
767 Layout.fillWidth: true
770 insertComplexItemAfterCurrent(modelData.canonicalName)
778 QGCPopupDialogFactory {
779 id: promptForPlanUsageOnVehicleChangePopupFactory
781 dialogComponent: promptForPlanUsageOnVehicleChangePopupComponent
785 id: promptForPlanUsageOnVehicleChangePopupComponent
787 title: _planMasterController.managerVehicle.isOfflineEditingVehicle ? qsTr("Plan View - Vehicle Disconnected") : qsTr("Plan View - Vehicle Changed")
788 buttons: Dialog.NoButton
792 Layout.maximumWidth: parent.width
793 wrapMode: QGCLabel.WordWrap
794 text: _planMasterController.managerVehicle.isOfflineEditingVehicle ?
795 qsTr("The vehicle associated with the plan in the Plan View is no longer available. What would you like to do with that plan?") : qsTr("The plan being worked on in the Plan View is not from the current vehicle. What would you like to do with that plan?")
799 Layout.fillWidth: true
800 text: (_planMasterController.dirtyForSave) ?
801 (_planMasterController.managerVehicle.isOfflineEditingVehicle ?
802 qsTr("Discard Unsaved Changes") : qsTr("Discard Unsaved Changes, Load New Plan From Vehicle")) : qsTr("Load New Plan From Vehicle")
804 _planMasterController.showPlanFromManagerVehicle()
805 _promptForPlanUsageShowing = false
811 Layout.fillWidth: true
812 text: _planMasterController.managerVehicle.isOfflineEditingVehicle ?
813 qsTr("Keep Current Plan") : qsTr("Keep Current Plan, Don't Update From Vehicle")
815 _promptForPlanUsageShowing = false
824 id: insertOrCancelROIDropPanelComponent
827 id: insertOrCancelROIDropPanel
830 property var mapClickCoord
832 sourceComponent: Component {
834 spacing: ScreenTools.defaultFontPixelWidth / 2
837 Layout.fillWidth: true
838 text: qsTr("Insert ROI")
841 insertOrCancelROIDropPanel.close()
842 insertROIAfterCurrent(mapClickCoord)
847 Layout.fillWidth: true
848 text: qsTr("Insert Cancel ROI")
851 insertOrCancelROIDropPanel.close()
852 insertCancelROIAfterCurrent()