From 32524195d474b74e48916867b2a6c2f022a40d98 Mon Sep 17 00:00:00 2001
From: 张世豪 <979909237@qq.com>
Date: 星期二, 09 十二月 2025 19:36:32 +0800
Subject: [PATCH] 20251209

---
 src/zhuye/Shouye.java |  755 +++++++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 690 insertions(+), 65 deletions(-)

diff --git a/src/zhuye/Shouye.java b/src/zhuye/Shouye.java
index 10f9ca0..1dfd462 100644
--- a/src/zhuye/Shouye.java
+++ b/src/zhuye/Shouye.java
@@ -92,6 +92,11 @@
     private JPanel floatingButtonPanel;
     private JPanel floatingButtonColumn;
     private Runnable endDrawingCallback;
+    private JButton pathPreviewReturnButton;
+    private boolean pathPreviewActive;
+    private Runnable pathPreviewReturnAction;
+    private String previewRestoreLandNumber;
+    private String previewRestoreLandName;
     private boolean drawingPaused;
     private ImageIcon pauseIcon;
     private ImageIcon pauseActiveIcon;
@@ -112,7 +117,6 @@
     private double[] circleBaseLatLon;
     private Timer circleDataMonitor;
     private Coordinate lastCapturedCoordinate;
-    private HandheldBoundaryCaptureDialog handheldCaptureDialog;
     private boolean handheldCaptureActive;
     private int handheldCapturedPoints;
     private final List<Point2D.Double> handheldTemporaryPoints = new ArrayList<>();
@@ -127,14 +131,26 @@
     private boolean stopButtonActive = false;
     private boolean bluetoothConnected = false;
     private Timer mowerSpeedRefreshTimer;
+    private boolean drawingControlModeActive;
+    private boolean storedStartButtonShowingPause;
+    private boolean storedStopButtonActive;
+    private String storedStatusBeforeDrawing;
+    private boolean handheldCaptureInlineUiActive;
+    private Timer handheldCaptureStatusTimer;
+    private String handheldCaptureStoredStatusText;
+    private Color handheldStartButtonOriginalBackground;
+    private Color handheldStartButtonOriginalForeground;
+    private Color handheldStopButtonOriginalBackground;
+    private Color handheldStopButtonOriginalForeground;
     
     public Shouye() {
         instance = this;
         baseStation = new BaseStation();
         baseStation.load();
-    dellmessage.registerLineListener(serialLineListener);
+        dellmessage.registerLineListener(serialLineListener);
         initializeUI();
         setupEventHandlers();
+        scheduleIdentifierCheck();
     }
 
     public static Shouye getInstance() {
@@ -174,6 +190,26 @@
         refreshMapForSelectedArea();
     }
 
+    private void scheduleIdentifierCheck() {
+        HierarchyListener listener = new HierarchyListener() {
+            @Override
+            public void hierarchyChanged(HierarchyEvent e) {
+                if ((e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0 && Shouye.this.isShowing()) {
+                    Shouye.this.removeHierarchyListener(this);
+                    SwingUtilities.invokeLater(() -> {
+                        Shouye.this.checkIdentifiersAndPromptIfNeeded();
+                        Shouye.this.showInitialMowerSelfCheckDialogIfNeeded();
+                    });
+                }
+            }
+        };
+        addHierarchyListener(listener);
+    }
+
+    private void showInitialMowerSelfCheckDialogIfNeeded() {
+        zijian.showInitialPromptIfNeeded(this, this::showRemoteControlDialog);
+    }
+
     private void applyIdleTrailDurationFromSettings() {
         if (mapRenderer == null) {
             return;
@@ -584,8 +620,9 @@
         }
         if (remoteDialog != null) {
             positionRemoteDialogBottomCenter(remoteDialog);
+            zijian.markSelfCheckCompleted();
+            remoteDialog.setVisible(true);
         }
-        remoteDialog.setVisible(true);
     }
 
     private void positionRemoteDialogBottomCenter(RemoteControlDialog dialog) {
@@ -663,6 +700,142 @@
         baseStationDialog.setVisible(true);
     }
 
+    private void checkIdentifiersAndPromptIfNeeded() {
+        if (baseStation == null) {
+            baseStation = new BaseStation();
+        }
+        baseStation.load();
+
+        String currentMowerId = Setsys.getPropertyValue("mowerId");
+        String currentBaseStationId = baseStation.getDeviceId();
+
+        if (!isIdentifierMissing(currentMowerId) && !isIdentifierMissing(currentBaseStationId)) {
+            return;
+        }
+
+        Window owner = SwingUtilities.getWindowAncestor(this);
+        promptForMissingIdentifiers(owner, currentMowerId, currentBaseStationId);
+    }
+
+    private void promptForMissingIdentifiers(Window owner, String currentMowerId, String currentBaseStationId) {
+        while (true) {
+            JTextField mowerField = new JTextField(10);
+            JTextField baseField = new JTextField(10);
+
+            if (!isIdentifierMissing(currentMowerId)) {
+                mowerField.setText(currentMowerId.trim());
+            }
+            if (!isIdentifierMissing(currentBaseStationId)) {
+                baseField.setText(currentBaseStationId.trim());
+            }
+
+            JPanel panel = new JPanel();
+            panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
+            panel.setBorder(BorderFactory.createEmptyBorder(6, 6, 6, 6));
+
+            JLabel mowerLabel = new JLabel("鍓茶崏鏈虹紪鍙�");
+            JLabel baseLabel = new JLabel("宸垎鍩哄噯绔欑紪鍙�");
+
+            mowerField.setMaximumSize(new Dimension(Integer.MAX_VALUE, mowerField.getPreferredSize().height));
+            baseField.setMaximumSize(new Dimension(Integer.MAX_VALUE, baseField.getPreferredSize().height));
+
+            panel.add(mowerLabel);
+            panel.add(Box.createVerticalStrut(4));
+            panel.add(mowerField);
+            panel.add(Box.createVerticalStrut(10));
+            panel.add(baseLabel);
+            panel.add(Box.createVerticalStrut(4));
+            panel.add(baseField);
+
+            Object[] options = {"淇濆瓨", "鍙栨秷"};
+            int result = JOptionPane.showOptionDialog(owner, panel, "瀹屽杽璁惧淇℃伅",
+                    JOptionPane.DEFAULT_OPTION, JOptionPane.PLAIN_MESSAGE, null, options, options[0]);
+
+            if (result != 0) {
+                break;
+            }
+
+            String mowerInput = mowerField.getText().trim();
+            String baseInput = baseField.getText().trim();
+
+            if (mowerInput.isEmpty()) {
+                JOptionPane.showMessageDialog(owner, "鍓茶崏鏈虹紪鍙蜂笉鑳戒负绌恒��", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+                continue;
+            }
+
+            if (baseInput.isEmpty()) {
+                JOptionPane.showMessageDialog(owner, "宸垎鍩哄噯绔欑紪鍙蜂笉鑳戒负绌恒��", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+                continue;
+            }
+
+            boolean mowerSaved = persistMowerIdentifier(mowerInput);
+            boolean baseSaved = persistBaseStationIdentifier(baseInput);
+
+            if (mowerSaved && baseSaved) {
+                JOptionPane.showMessageDialog(owner, "缂栧彿宸蹭繚瀛樸��", "鎴愬姛", JOptionPane.INFORMATION_MESSAGE);
+                break;
+            }
+
+            StringBuilder errorBuilder = new StringBuilder();
+            if (!mowerSaved) {
+                errorBuilder.append("鍓茶崏鏈虹紪鍙蜂繚瀛樺け璐ャ��");
+            }
+            if (!baseSaved) {
+                if (errorBuilder.length() > 0) {
+                    errorBuilder.append('\n');
+                }
+                errorBuilder.append("宸垎鍩哄噯绔欑紪鍙蜂繚瀛樺け璐ャ��");
+            }
+
+            JOptionPane.showMessageDialog(owner, errorBuilder.toString(), "淇濆瓨澶辫触", JOptionPane.ERROR_MESSAGE);
+
+            currentMowerId = Setsys.getPropertyValue("mowerId");
+            baseStation.load();
+            currentBaseStationId = baseStation.getDeviceId();
+        }
+    }
+
+    private boolean isIdentifierMissing(String value) {
+        if (value == null) {
+            return true;
+        }
+        String trimmed = value.trim();
+        return trimmed.isEmpty() || "-1".equals(trimmed);
+    }
+
+    private boolean persistMowerIdentifier(String mowerId) {
+        try {
+            Setsys setsys = new Setsys();
+            setsys.initializeFromProperties();
+            boolean updated = setsys.updateProperty("mowerId", mowerId);
+            if (updated) {
+                Device.initializeActiveDevice(mowerId);
+            }
+            return updated;
+        } catch (Exception ex) {
+            ex.printStackTrace();
+            return false;
+        }
+    }
+
+    private boolean persistBaseStationIdentifier(String baseStationId) {
+        if (baseStation == null) {
+            baseStation = new BaseStation();
+        }
+        try {
+            baseStation.updateByDeviceId(baseStationId,
+                    baseStation.getInstallationCoordinates(),
+                    baseStation.getIotSimCardNumber(),
+                    baseStation.getDeviceActivationTime(),
+                    baseStation.getDataUpdateTime());
+            baseStation.load();
+            return true;
+        } catch (Exception ex) {
+            ex.printStackTrace();
+            return false;
+        }
+    }
+
     private boolean hasValidBaseStationId() {
         if (baseStation == null) {
             return false;
@@ -713,9 +886,22 @@
     }
     
     private void toggleStartPause() {
+        if (handheldCaptureInlineUiActive) {
+            handleHandheldConfirmAction();
+            return;
+        }
+        if (drawingControlModeActive) {
+            toggleDrawingPause();
+            return;
+        }
         if (startBtn == null) {
             return;
         }
+        if (startButtonShowingPause) {
+            if (!zijian.ensureBeforeMowing(this, this::showRemoteControlDialog)) {
+                return;
+            }
+        }
         startButtonShowingPause = !startButtonShowingPause;
         if (!startButtonShowingPause) {
             statusLabel.setText("浣滀笟涓�");
@@ -737,6 +923,14 @@
     }
 
     private void handleStopAction() {
+        if (handheldCaptureInlineUiActive) {
+            handleHandheldFinishAction();
+            return;
+        }
+        if (drawingControlModeActive) {
+            handleDrawingStopFromControlPanel();
+            return;
+        }
         stopButtonActive = !stopButtonActive;
         updateStopButtonIcon();
         if (stopButtonActive) {
@@ -751,6 +945,268 @@
         updateStartButtonAppearance();
     }
 
+    private void handleDrawingStopFromControlPanel() {
+        if (endDrawingCallback != null) {
+            endDrawingCallback.run();
+        } else {
+            addzhangaiwu.finishDrawingSession();
+        }
+    }
+
+    private void handleHandheldConfirmAction() {
+        if (!handheldCaptureInlineUiActive) {
+            return;
+        }
+        if (!canConfirmHandheldPoint()) {
+            refreshHandheldCaptureUiState();
+            return;
+        }
+        int count = captureHandheldBoundaryPoint();
+        if (count <= 0) {
+            refreshHandheldCaptureUiState();
+            return;
+        }
+        refreshHandheldCaptureUiState();
+    }
+
+    private void handleHandheldFinishAction() {
+        if (!handheldCaptureInlineUiActive) {
+            return;
+        }
+        if (stopBtn != null && !stopBtn.isEnabled()) {
+            refreshHandheldCaptureUiState();
+            return;
+        }
+        if (!finishHandheldBoundaryCapture()) {
+            refreshHandheldCaptureUiState();
+        }
+    }
+
+    private void enterHandheldCaptureInlineUi() {
+        if (handheldCaptureInlineUiActive) {
+            refreshHandheldCaptureUiState();
+            return;
+        }
+        handheldCaptureInlineUiActive = true;
+        handheldCaptureStoredStatusText = statusLabel != null ? statusLabel.getText() : null;
+        if (statusLabel != null) {
+            statusLabel.setText("鎵嬫寔閲囬泦涓�");
+        }
+        if (startBtn != null) {
+            handheldStartButtonOriginalBackground = startBtn.getBackground();
+            handheldStartButtonOriginalForeground = startBtn.getForeground();
+            startBtn.setIcon(null);
+            startBtn.setIconTextGap(0);
+            startBtn.setHorizontalAlignment(SwingConstants.CENTER);
+            startBtn.setHorizontalTextPosition(SwingConstants.CENTER);
+            startBtn.setVerticalTextPosition(SwingConstants.CENTER);
+        }
+        if (stopBtn != null) {
+            handheldStopButtonOriginalBackground = stopBtn.getBackground();
+            handheldStopButtonOriginalForeground = stopBtn.getForeground();
+            stopBtn.setIcon(null);
+            stopBtn.setIconTextGap(0);
+            stopBtn.setHorizontalAlignment(SwingConstants.CENTER);
+            stopBtn.setHorizontalTextPosition(SwingConstants.CENTER);
+            stopBtn.setVerticalTextPosition(SwingConstants.CENTER);
+            stopBtn.setText("缁撴潫");
+        }
+        startHandheldCaptureStatusTimer();
+        refreshHandheldCaptureUiState();
+    }
+
+    private void exitHandheldCaptureInlineUi() {
+        if (!handheldCaptureInlineUiActive) {
+            return;
+        }
+        handheldCaptureInlineUiActive = false;
+        stopHandheldCaptureStatusTimer();
+        if (statusLabel != null) {
+            statusLabel.setText(handheldCaptureStoredStatusText != null ? handheldCaptureStoredStatusText : "寰呮満");
+        }
+        if (startBtn != null) {
+            startBtn.setToolTipText(null);
+            if (handheldStartButtonOriginalBackground != null) {
+                startBtn.setBackground(handheldStartButtonOriginalBackground);
+            }
+            if (handheldStartButtonOriginalForeground != null) {
+                startBtn.setForeground(handheldStartButtonOriginalForeground);
+            }
+            startBtn.setEnabled(true);
+            updateStartButtonAppearance();
+        }
+        if (stopBtn != null) {
+            stopBtn.setToolTipText(null);
+            if (handheldStopButtonOriginalBackground != null) {
+                stopBtn.setBackground(handheldStopButtonOriginalBackground);
+            }
+            if (handheldStopButtonOriginalForeground != null) {
+                stopBtn.setForeground(handheldStopButtonOriginalForeground);
+            }
+            stopBtn.setEnabled(true);
+            stopBtn.setText("缁撴潫");
+            updateStopButtonIcon();
+        }
+        handheldCaptureStoredStatusText = null;
+        handheldStartButtonOriginalBackground = null;
+        handheldStartButtonOriginalForeground = null;
+        handheldStopButtonOriginalBackground = null;
+        handheldStopButtonOriginalForeground = null;
+    }
+
+    private void startHandheldCaptureStatusTimer() {
+        if (handheldCaptureStatusTimer == null) {
+            handheldCaptureStatusTimer = new Timer(400, e -> refreshHandheldCaptureUiState());
+            handheldCaptureStatusTimer.setRepeats(true);
+        }
+        if (!handheldCaptureStatusTimer.isRunning()) {
+            handheldCaptureStatusTimer.start();
+        }
+    }
+
+    private void stopHandheldCaptureStatusTimer() {
+        if (handheldCaptureStatusTimer != null && handheldCaptureStatusTimer.isRunning()) {
+            handheldCaptureStatusTimer.stop();
+        }
+    }
+
+    // Update inline handheld capture buttons based on the current device reading.
+    private void refreshHandheldCaptureUiState() {
+        if (!handheldCaptureInlineUiActive) {
+            return;
+        }
+        int nextIndex = handheldCapturedPoints + 1;
+        boolean hasFix = hasHighPrecisionFix();
+        boolean hasValid = hasValidRealtimeHandheldPosition();
+        boolean duplicate = hasValid && isCurrentHandheldPointDuplicate();
+        boolean canConfirm = handheldCaptureActive && hasFix && hasValid && !duplicate;
+
+        if (startBtn != null) {
+            String prompt = "<html><center>閲囬泦鐐�" + nextIndex + "<br>纭畾</center></html>";
+            startBtn.setText(prompt);
+            startBtn.setEnabled(canConfirm);
+            if (canConfirm) {
+                if (handheldStartButtonOriginalBackground != null) {
+                    startBtn.setBackground(handheldStartButtonOriginalBackground);
+                }
+                if (handheldStartButtonOriginalForeground != null) {
+                    startBtn.setForeground(handheldStartButtonOriginalForeground);
+                }
+                startBtn.setToolTipText(null);
+            } else {
+                startBtn.setBackground(new Color(200, 200, 200));
+                startBtn.setForeground(new Color(130, 130, 130));
+                startBtn.setToolTipText(resolveHandheldConfirmTooltip(hasFix, hasValid, duplicate));
+            }
+        }
+
+        if (stopBtn != null) {
+            boolean canFinish = handheldCapturedPoints >= 3;
+            stopBtn.setText("缁撴潫");
+            stopBtn.setEnabled(canFinish);
+            if (canFinish) {
+                if (handheldStopButtonOriginalBackground != null) {
+                    stopBtn.setBackground(handheldStopButtonOriginalBackground);
+                }
+                if (handheldStopButtonOriginalForeground != null) {
+                    stopBtn.setForeground(handheldStopButtonOriginalForeground);
+                }
+                stopBtn.setToolTipText("缁撴潫閲囬泦骞惰繑鍥炴柊澧炲湴鍧�");
+            } else {
+                stopBtn.setBackground(new Color(220, 220, 220));
+                stopBtn.setForeground(new Color(130, 130, 130));
+                stopBtn.setToolTipText("鑷冲皯閲囬泦涓変釜鐐规墠鑳界粨鏉�");
+            }
+        }
+    }
+
+    private String resolveHandheldConfirmTooltip(boolean hasFix, boolean hasValidPosition, boolean duplicate) {
+        if (!hasFix) {
+            return "褰撳墠瀹氫綅璐ㄩ噺涓嶈冻锛屾棤娉曢噰闆�";
+        }
+        if (!hasValidPosition) {
+            return "褰撳墠瀹氫綅鏁版嵁鏃犳晥锛岃绋嶅悗鍐嶈瘯";
+        }
+        if (duplicate) {
+            return "褰撳墠鍧愭爣宸查噰闆嗭紝璇风Щ鍔ㄥ埌鏂扮殑浣嶇疆";
+        }
+        return null;
+    }
+
+    private boolean hasHighPrecisionFix() {
+        Device device = Device.getGecaoji();
+        if (device == null) {
+            return false;
+        }
+        String status = device.getPositioningStatus();
+        return status != null && "4".equals(status.trim());
+    }
+
+    private boolean canConfirmHandheldPoint() {
+        return handheldCaptureActive
+            && hasHighPrecisionFix()
+            && hasValidRealtimeHandheldPosition()
+            && !isCurrentHandheldPointDuplicate();
+    }
+
+    private void enterDrawingControlMode() {
+        if (drawingControlModeActive) {
+            return;
+        }
+        storedStartButtonShowingPause = startButtonShowingPause;
+        storedStopButtonActive = stopButtonActive;
+        storedStatusBeforeDrawing = statusLabel != null ? statusLabel.getText() : null;
+        drawingControlModeActive = true;
+        applyDrawingPauseState(false, false);
+        updateDrawingControlButtonLabels();
+    }
+
+    private void exitDrawingControlMode() {
+        if (!drawingControlModeActive) {
+            return;
+        }
+        drawingControlModeActive = false;
+        applyDrawingPauseState(false, false);
+        drawingPaused = false;
+        stopButtonActive = storedStopButtonActive;
+        startButtonShowingPause = storedStartButtonShowingPause;
+        if (startBtn != null) {
+            updateStartButtonAppearance();
+        }
+        if (stopBtn != null) {
+            stopBtn.setText("缁撴潫");
+            updateStopButtonIcon();
+        }
+        if (statusLabel != null) {
+            statusLabel.setText(storedStatusBeforeDrawing != null ? storedStatusBeforeDrawing : "寰呮満");
+        }
+        storedStatusBeforeDrawing = null;
+    }
+
+    private void updateDrawingControlButtonLabels() {
+        if (!drawingControlModeActive) {
+            return;
+        }
+        configureButtonForDrawingMode(startBtn);
+        configureButtonForDrawingMode(stopBtn);
+        if (startBtn != null) {
+            startBtn.setText(drawingPaused ? "寮�濮嬬粯鍒�" : "鏆傚仠缁樺埗");
+        }
+        if (stopBtn != null) {
+            stopBtn.setText("缁撴潫缁樺埗");
+        }
+    }
+
+    private void configureButtonForDrawingMode(JButton button) {
+        if (button == null) {
+            return;
+        }
+        button.setIcon(null);
+        button.setIconTextGap(0);
+        button.setHorizontalAlignment(SwingConstants.CENTER);
+        button.setHorizontalTextPosition(SwingConstants.CENTER);
+    }
+
     private void updateStartButtonAppearance() {
         if (startBtn == null) {
             return;
@@ -1028,6 +1484,13 @@
         refreshMowerSpeedLabel();
     }
 
+    public void setHandheldMowerIconActive(boolean active) {
+        if (mapRenderer == null) {
+            return;
+        }
+        mapRenderer.setHandheldMowerIconActive(active);
+    }
+
     public boolean startMowerBoundaryCapture() {
         if (mapRenderer == null) {
             return false;
@@ -1037,6 +1500,8 @@
             return false;
         }
 
+        mapRenderer.clearIdleTrail();
+
         activeBoundaryMode = BoundaryCaptureMode.MOWER;
         mowerBoundaryCaptureActive = true;
         mowerBaseLatLon = baseLatLonCandidate;
@@ -1053,9 +1518,12 @@
         Coordinate.setStartSaveGngga(true);
 
         if (mapRenderer != null) {
+            mapRenderer.setBoundaryPreviewMarkerScale(2.0d);
             mapRenderer.beginHandheldBoundaryPreview();
         }
 
+        setHandheldMowerIconActive(false);
+
         startMowerBoundaryMonitor();
         return true;
     }
@@ -1064,15 +1532,12 @@
         if (mapRenderer == null) {
             return false;
         }
-        if (handheldCaptureDialog != null && handheldCaptureDialog.isShowing()) {
-            handheldCaptureDialog.toFront();
-            return true;
-        }
-
         if (activeBoundaryMode == BoundaryCaptureMode.MOWER) {
             stopMowerBoundaryCapture();
         }
 
+        mapRenderer.clearIdleTrail();
+
         activeBoundaryMode = BoundaryCaptureMode.HANDHELD;
         handheldCaptureActive = true;
         handheldCapturedPoints = 0;
@@ -1084,19 +1549,10 @@
             handheldTemporaryPoints.clear();
         }
         AddDikuai.recordTemporaryBoundaryPoints(Collections.emptyList());
-        mapRenderer.beginHandheldBoundaryPreview();
-
-        Window ownerWindow = SwingUtilities.getWindowAncestor(this);
-        SwingUtilities.invokeLater(() -> {
-            Window targetOwner = ownerWindow;
-            if (targetOwner == null) {
-                targetOwner = SwingUtilities.getWindowAncestor(Shouye.this);
-            }
-            HandheldBoundaryCaptureDialog dialog = new HandheldBoundaryCaptureDialog(targetOwner, Shouye.this, visualizationPanel, THEME_COLOR);
-            handheldCaptureDialog = dialog;
-            dialog.setVisible(true);
-        });
-
+    mapRenderer.setBoundaryPreviewMarkerScale(1.0d);
+    mapRenderer.beginHandheldBoundaryPreview();
+        setHandheldMowerIconActive(true);
+        enterHandheldCaptureInlineUi();
         return true;
     }
 
@@ -1188,6 +1644,7 @@
         if (activeBoundaryMode == BoundaryCaptureMode.MOWER) {
             activeBoundaryMode = BoundaryCaptureMode.NONE;
         }
+        setHandheldMowerIconActive(false);
     }
 
     private void discardLatestCoordinate(Coordinate coordinate) {
@@ -1282,6 +1739,7 @@
 
         List<Point2D.Double> closedSnapshot = createClosedHandheldPointSnapshot();
         handheldCaptureActive = false;
+        activeBoundaryMode = BoundaryCaptureMode.NONE;
         Coordinate.setStartSaveGngga(false);
         if (mapRenderer != null) {
             mapRenderer.clearHandheldBoundaryPreview();
@@ -1289,20 +1747,12 @@
 
     AddDikuai.recordTemporaryBoundaryPoints(closedSnapshot);
 
+        exitHandheldCaptureInlineUi();
+
         SwingUtilities.invokeLater(AddDikuai::finishDrawingSession);
         return true;
     }
 
-    void handheldBoundaryCaptureDialogClosed(HandheldBoundaryCaptureDialog dialog) {
-        if (handheldCaptureDialog == dialog) {
-            handheldCaptureDialog = null;
-        }
-        handheldCaptureActive = false;
-        if (activeBoundaryMode == BoundaryCaptureMode.HANDHELD) {
-            activeBoundaryMode = BoundaryCaptureMode.NONE;
-        }
-    }
-
     int getHandheldCapturedPointCount() {
         return handheldCapturedPoints;
     }
@@ -1571,9 +2021,9 @@
         if (statusLabel == null) {
             return;
         }
-        if ("浣滀笟涓�".equals(statusText)) {
+        if ("浣滀笟涓�".equals(statusText) || "缁樺埗涓�".equals(statusText)) {
             statusLabel.setForeground(THEME_COLOR);
-        } else if ("鏆傚仠涓�".equals(statusText)) {
+        } else if ("鏆傚仠涓�".equals(statusText) || "缁樺埗鏆傚仠".equals(statusText)) {
             statusLabel.setForeground(STATUS_PAUSE_COLOR);
         } else {
             statusLabel.setForeground(Color.GRAY);
@@ -1601,6 +2051,29 @@
         return button;
     }
 
+    private JButton createFloatingTextButton(String text) {
+        JButton button = new JButton(text);
+        button.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 15));
+        button.setForeground(Color.WHITE);
+        button.setBackground(THEME_COLOR);
+        button.setBorder(BorderFactory.createEmptyBorder(10, 18, 10, 18));
+        button.setFocusPainted(false);
+        button.setOpaque(true);
+        button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
+        button.addMouseListener(new MouseAdapter() {
+            @Override
+            public void mouseEntered(MouseEvent e) {
+                button.setBackground(THEME_HOVER_COLOR);
+            }
+
+            @Override
+            public void mouseExited(MouseEvent e) {
+                button.setBackground(THEME_COLOR);
+            }
+        });
+        return button;
+    }
+
     private ImageIcon loadScaledIcon(String path, int width, int height) {
         try {
             ImageIcon icon = new ImageIcon(path);
@@ -1658,6 +2131,12 @@
         if (notifyCoordinate) {
             Coordinate.setStartSaveGngga(!paused);
         }
+        if (drawingControlModeActive) {
+            updateDrawingControlButtonLabels();
+            if (statusLabel != null) {
+                statusLabel.setText(paused ? "缁樺埗鏆傚仠" : "缁樺埗涓�");
+            }
+        }
     }
 
     private void toggleDrawingPause() {
@@ -1670,36 +2149,33 @@
 
     public void showEndDrawingButton(Runnable callback, String drawingShape) {
         endDrawingCallback = callback;
-        applyDrawingPauseState(false, false);
         circleDialogMode = false;
         hideCircleGuidancePanel();
-
-        ensureFloatingIconsLoaded();
-        ensureFloatingButtonInfrastructure();
+        enterDrawingControlMode();
 
         boolean enableCircleGuidance = drawingShape != null
                 && "circle".equalsIgnoreCase(drawingShape.trim());
         if (enableCircleGuidance) {
-            prepareCircleGuidanceState();
-            showCircleGuidanceStep(1);
-            endDrawingButton.setVisible(false);
+            ensureFloatingIconsLoaded();
+            ensureFloatingButtonInfrastructure();
             if (drawingPauseButton != null) {
                 drawingPauseButton.setVisible(false);
             }
+            if (endDrawingButton != null) {
+                endDrawingButton.setVisible(false);
+            }
+            prepareCircleGuidanceState();
+            showCircleGuidanceStep(1);
+            floatingButtonPanel.setVisible(true);
+            if (floatingButtonPanel.getParent() != visualizationPanel) {
+                visualizationPanel.add(floatingButtonPanel, BorderLayout.SOUTH);
+            }
+            rebuildFloatingButtonColumn();
         } else {
             clearCircleGuidanceArtifacts();
-            endDrawingButton.setVisible(true);
-            if (drawingPauseButton != null) {
-                drawingPauseButton.setVisible(true);
-            }
+            hideFloatingDrawingControls();
         }
 
-        floatingButtonPanel.setVisible(true);
-        if (floatingButtonPanel.getParent() != visualizationPanel) {
-            visualizationPanel.add(floatingButtonPanel, BorderLayout.SOUTH);
-        }
-
-        rebuildFloatingButtonColumn();
         visualizationPanel.revalidate();
         visualizationPanel.repaint();
     }
@@ -1776,6 +2252,14 @@
                 floatingButtonColumn.add(Box.createRigidArea(new Dimension(0, 10)));
             }
             floatingButtonColumn.add(endDrawingButton);
+            added = true;
+        }
+        if (pathPreviewReturnButton != null && pathPreviewReturnButton.isVisible()) {
+            if (added) {
+                floatingButtonColumn.add(Box.createRigidArea(new Dimension(0, 10)));
+            }
+            floatingButtonColumn.add(pathPreviewReturnButton);
+            added = true;
         }
         floatingButtonColumn.revalidate();
         floatingButtonColumn.repaint();
@@ -2184,21 +2668,27 @@
 
     private double[] resolveCircleBaseLatLon() {
         String coords = null;
-        String landNumber = Dikuaiguanli.getCurrentWorkLandNumber();
-        if (isMeaningfulValue(landNumber)) {
-            Dikuai current = Dikuai.getDikuai(landNumber);
-            if (current != null) {
-                coords = current.getBaseStationCoordinates();
+
+        if (baseStation == null) {
+            baseStation = new BaseStation();
+        }
+        baseStation.load();
+        coords = baseStation.getInstallationCoordinates();
+
+        if (!isMeaningfulValue(coords)) {
+            String landNumber = Dikuaiguanli.getCurrentWorkLandNumber();
+            if (isMeaningfulValue(landNumber)) {
+                Dikuai current = Dikuai.getDikuai(landNumber);
+                if (current != null) {
+                    coords = current.getBaseStationCoordinates();
+                }
             }
-        }
-        if (!isMeaningfulValue(coords)) {
-            coords = addzhangaiwu.getActiveSessionBaseStation();
-        }
-        if (!isMeaningfulValue(coords) && baseStation != null) {
-            coords = baseStation.getInstallationCoordinates();
-        }
-        if (!isMeaningfulValue(coords)) {
-            return null;
+            if (!isMeaningfulValue(coords)) {
+                coords = addzhangaiwu.getActiveSessionBaseStation();
+            }
+            if (!isMeaningfulValue(coords)) {
+                return null;
+            }
         }
         String[] parts = coords.split(",");
         if (parts.length < 4) {
@@ -2330,7 +2820,9 @@
         clearCircleGuidanceArtifacts();
         hideFloatingDrawingControls();
         circleDialogMode = false;
-        applyDrawingPauseState(false, false);
+        exitHandheldCaptureInlineUi();
+        handheldCaptureActive = false;
+        exitDrawingControlMode();
         if (activeBoundaryMode == BoundaryCaptureMode.MOWER) {
             stopMowerBoundaryCapture();
         } else if (activeBoundaryMode == BoundaryCaptureMode.HANDHELD && !handheldCaptureActive) {
@@ -2339,6 +2831,129 @@
         endDrawingCallback = null;
         visualizationPanel.revalidate();
         visualizationPanel.repaint();
+        setHandheldMowerIconActive(false);
+    }
+
+    private void showPathPreviewReturnControls() {
+        ensureFloatingButtonInfrastructure();
+        if (drawingPauseButton != null) {
+            drawingPauseButton.setVisible(false);
+        }
+        if (endDrawingButton != null) {
+            endDrawingButton.setVisible(false);
+        }
+        if (pathPreviewReturnButton == null) {
+            pathPreviewReturnButton = createFloatingTextButton("杩斿洖");
+            pathPreviewReturnButton.setToolTipText("杩斿洖鏂板鍦板潡姝ラ");
+            pathPreviewReturnButton.addActionListener(e -> handlePathPreviewReturn());
+        }
+        pathPreviewReturnButton.setVisible(true);
+        if (floatingButtonPanel != null) {
+            floatingButtonPanel.setVisible(true);
+            if (floatingButtonPanel.getParent() != visualizationPanel) {
+                visualizationPanel.add(floatingButtonPanel, BorderLayout.SOUTH);
+            }
+        }
+        rebuildFloatingButtonColumn();
+    }
+
+    private void hidePathPreviewReturnControls() {
+        if (pathPreviewReturnButton != null) {
+            pathPreviewReturnButton.setVisible(false);
+        }
+        rebuildFloatingButtonColumn();
+        if (floatingButtonPanel != null && floatingButtonColumn != null
+                && floatingButtonColumn.getComponentCount() == 0) {
+            floatingButtonPanel.setVisible(false);
+        }
+    }
+
+    private void handlePathPreviewReturn() {
+        Runnable callback = pathPreviewReturnAction;
+        exitMowingPathPreview();
+        if (callback != null) {
+            callback.run();
+        }
+    }
+
+    public boolean startMowingPathPreview(String landNumber,
+                                          String landName,
+                                          String boundary,
+                                          String obstacles,
+                                          String plannedPath,
+                                          Runnable returnAction) {
+        if (mapRenderer == null || !isMeaningfulValue(plannedPath)) {
+            return false;
+        }
+
+        if (pathPreviewActive) {
+            exitMowingPathPreview();
+        }
+
+        exitDrawingControlMode();
+        hideCircleGuidancePanel();
+        clearCircleGuidanceArtifacts();
+
+        pathPreviewReturnAction = returnAction;
+        pathPreviewActive = true;
+    mapRenderer.setPathPreviewSizingEnabled(true);
+
+        previewRestoreLandNumber = Dikuaiguanli.getCurrentWorkLandNumber();
+        previewRestoreLandName = null;
+        if (isMeaningfulValue(previewRestoreLandNumber)) {
+            Dikuai existing = Dikuai.getDikuai(previewRestoreLandNumber);
+            if (existing != null) {
+                previewRestoreLandName = existing.getLandName();
+            }
+        }
+
+        mapRenderer.setCurrentBoundary(boundary, landNumber, landName);
+        mapRenderer.setCurrentObstacles(obstacles, landNumber);
+        mapRenderer.setCurrentPlannedPath(plannedPath);
+        mapRenderer.clearHandheldBoundaryPreview();
+    mapRenderer.setBoundaryPointSizeScale(1.0d);
+        mapRenderer.setBoundaryPointsVisible(isMeaningfulValue(boundary));
+
+        String displayName = isMeaningfulValue(landName) ? landName : landNumber;
+        updateCurrentAreaName(displayName);
+
+        showPathPreviewReturnControls();
+        visualizationPanel.revalidate();
+        visualizationPanel.repaint();
+        return true;
+    }
+
+    public void exitMowingPathPreview() {
+        if (!pathPreviewActive) {
+            return;
+        }
+        pathPreviewActive = false;
+        if (mapRenderer != null) {
+            mapRenderer.setPathPreviewSizingEnabled(false);
+        }
+        hidePathPreviewReturnControls();
+
+        String restoreNumber = previewRestoreLandNumber;
+        String restoreName = previewRestoreLandName;
+        previewRestoreLandNumber = null;
+        previewRestoreLandName = null;
+        pathPreviewReturnAction = null;
+
+        if (restoreNumber != null) {
+            Dikuaiguanli.setCurrentWorkLand(restoreNumber, restoreName);
+        } else if (mapRenderer != null) {
+            mapRenderer.setCurrentBoundary(null, null, null);
+            mapRenderer.setCurrentObstacles((String) null, null);
+            mapRenderer.setCurrentPlannedPath(null);
+            mapRenderer.setBoundaryPointsVisible(false);
+            mapRenderer.setBoundaryPointSizeScale(1.0d);
+            mapRenderer.clearHandheldBoundaryPreview();
+            mapRenderer.resetView();
+            updateCurrentAreaName(null);
+        }
+
+        visualizationPanel.revalidate();
+        visualizationPanel.repaint();
     }
     
     /**
@@ -2370,6 +2985,16 @@
 
     private void initializeDefaultAreaSelection() {
         Dikuai.initFromProperties();
+        String persistedLandNumber = Dikuaiguanli.getPersistedWorkLandNumber();
+        if (persistedLandNumber != null) {
+            Dikuai stored = Dikuai.getDikuai(persistedLandNumber);
+            if (stored != null) {
+                Dikuaiguanli.setCurrentWorkLand(persistedLandNumber, stored.getLandName());
+                return;
+            }
+            Dikuaiguanli.setCurrentWorkLand(null, null);
+        }
+
         Map<String, Dikuai> all = Dikuai.getAllDikuai();
         if (all.isEmpty()) {
             Dikuaiguanli.setCurrentWorkLand(null, null);

--
Gitblit v1.10.0