Linienzug: Abbruch wickelt Teil-Geometrie statt sie zu loeschen; Editieren per DCL-Dialog

Abbruch (ESC) im Linienzug-Baumodus loescht die bereits eingefuegte
Geometrie nicht mehr, sondern wickelt sie wie ein fertiger Bau zu einem
VF_n-Block und schreibt das volle Eingabe-Journal als XDATA drauf
(vfl-modus-abbruch-sichern, ersetzt das bisherige Loesch-Rollback). Die
Teil-Kette bleibt damit stehen und ist sofort per Doppelklick weiter
editierbar/fortsetzbar - unabhaengig davon, ob der Abbruch beim frischen
Bauen oder mitten in einem Editier-Neuaufbau passiert. Der bisherige
Menuepunkt "Letzten Abbruch fortsetzen" (Session-Variable) entfaellt
dadurch ersatzlos.

vfl-edit-ent (Doppelklick-Editieren) fragt die Sektion, auf die
zurueckgesetzt werden soll, jetzt per neuem DCL-Dialog ab
(dcl/vfl_edit.dcl, vfl-dlg-position: Combobox 1..N + OK/Abbrechen) statt
per Text-Prompt. Vorbelegung auf die letzte Sektion, damit Doppelklick +
sofort OK einen abgebrochenen Bau nahtlos fortsetzt.

Die *error*-Handler-Installation wandert vor den allerersten Prompt
(vorher: nach Startpunkt/AS-Frage), damit ein ESC in diesem Fenster nicht
mehr auf den ungeschuetzten vorherigen Handler faellt. Das Wickeln im
Abbruch-Pfad ist per vl-catch-all-apply abgesichert, da vfl-block-erstellen
intern einen BKS-Wechsel per (command "_.UCS" ...) macht.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-25 22:06:17 +02:00
parent 236808f4dc
commit 82ce795b53
6 changed files with 164 additions and 108 deletions
+118 -84
View File
@@ -132,7 +132,7 @@
;; *vfl-journal* wird in UMGEKEHRTER Reihenfolge gehalten (neuestes zuerst, cons).
(defun vfl-journal-reset ()
(setq *vfl-journal* '() *vfl-replay-queue* nil *vfl-edit-orig-ent* nil))
(setq *vfl-journal* '() *vfl-replay-queue* nil))
;; Eintrag anhaengen (vorn, da Reverse-Reihenfolge).
(defun vfl-journal-record (kind val)
@@ -285,33 +285,44 @@
(vfl-string->journal (apply 'strcat (cdr xd)))
nil))
;; --- Abbruch-Rollback ---
;; Alle nach lastEnt erzeugten Entities loeschen (Transaktions-Rollback der
;; Teil-Geometrie) und das Journal der vollstaendig abgeschlossenen Glieder
;; (*vfl-journal-commit*) fuer die Wiederaufnahme sichern. Aufgerufen aus dem
;; *error*-Handler von vf-linienzug-modus.
(defun vfl-modus-cleanup (lastEnt / e nxt cnt)
;; --- Abbruch-Sicherung ---
;; Alle nach lastEnt erzeugten Entities werden NICHT geloescht, sondern
;; GENAUSO wie beim erfolgreichen Abschluss (vfl-block-erstellen) zu einem
;; neuen VF_n-Block gewickelt, mit dem VOLLEN aktuellen Eingabe-Journal
;; (inkl. des angefangenen, noch unfertigen letzten Gliedes) als XDATA. Die
;; Geometrie bleibt in der Zeichnung stehen und ist sofort per Doppelklick
;; weiter editierbar/fortsetzbar (vfl-edit-ent) - unabhaengig davon, ob es
;; sich um einen frischen Bau oder einen Editier-Neuaufbau handelte (kein
;; Sonderfall mehr noetig). Aufgerufen aus dem *error*-Handler von
;; vf-linienzug-modus.
(defun vfl-modus-abbruch-sichern (lastEnt vfl-nummer anzahl-gf anzahl-vf
startpunkt frame as-seite es-seite /
e cnt vfl-ins wickel-erg)
(setq cnt 0 e (if lastEnt (entnext lastEnt) (entnext)))
(while e
(setq nxt (entnext e))
(if (not (vl-catch-all-error-p (vl-catch-all-apply 'entdel (list e))))
(setq cnt (1+ cnt)))
(setq e nxt))
(if (and (boundp '*vfl-edit-orig-ent*) *vfl-edit-orig-ent*)
;; EDIT-Abbruch: den beim Edit vorab geloeschten Original-Block
;; wiederherstellen (entdel ist im laufenden Befehl umkehrbar) und KEIN
;; Resume anbieten - der Ausgangszustand ist ja komplett zurueck.
(progn
(vl-catch-all-apply 'entdel (list *vfl-edit-orig-ent*))
(setq *vfl-edit-orig-ent* nil)
(princ (ssg-textf "vfl-abbruch-edit" (list (itoa cnt)))))
;; FRESH/RESUME-Abbruch: Journal der abgeschlossenen Glieder fuer die
;; Wiederaufnahme sichern.
(if *vfl-journal-commit*
(progn
(setq *vfl-journal-letzter-abbruch* (reverse *vfl-journal-commit*))
(princ (ssg-textf "vfl-abbruch-resume" (list (itoa cnt)))))
(princ (ssg-textf "vfl-abbruch-simpel" (list (itoa cnt))))))
(while e (setq cnt (1+ cnt)) (setq e (entnext e)))
(cond
((or (= cnt 0) (null frame))
(princ (ssg-text "vfl-abbruch-nichts-gebaut")))
(t
;; vfl-block-erstellen nutzt intern (command "_.UCS" ...) fuer den
;; BKS-Wechsel (ssg-block-wrap-welt) - im *error*-Handler-Kontext
;; riskanter als eine einfache Loesch-Schleife, daher abgesichert.
(setq wickel-erg
(vl-catch-all-apply
(function (lambda ()
(setq vfl-ins
(vfl-block-erstellen vfl-nummer anzahl-gf anzahl-vf
(caddr startpunkt) (caddr (car frame))
(vfl-planar-dist startpunkt (car frame))
as-seite es-seite startpunkt lastEnt))
(if vfl-ins (vfl-journal-xdata-schreiben vfl-ins))))
nil))
(if (vl-catch-all-error-p wickel-erg)
(princ (ssg-textf "vfl-abbruch-wickeln-fehler"
(list (vl-catch-all-error-message wickel-erg))))
(princ (ssg-textf "vfl-abbruch-gesichert" (list (itoa cnt)))))
)
)
(princ))
;; Replay-Modus scharf schalten: die uebergebene (Vorwaerts-)Journalliste wird
@@ -1495,6 +1506,43 @@
(if (or (not *lib-initialized*) (null bogen-auf)) (init-bibliothek))
;; --- Abbruch-Sicherung scharf schalten (VOR dem ersten Prompt!) ---
;; Ab hier kann jederzeit Geometrie entstehen. Ein *error*-Handler faengt
;; jeden Abbruch (ESC / (exit) / Laufzeitfehler) ab und WICKELT die bereits
;; eingefuegte Teil-Geometrie (alles nach lastEnt) zu einem VF_n-Block mit
;; dem vollen aktuellen Eingabe-Journal als XDATA (vfl-modus-abbruch-sichern)
;; - die Geometrie bleibt stehen und ist per Doppelklick sofort weiter
;; editierbar/fortsetzbar (kein Rollback mehr, kein separater
;; "Fortsetzen"-Mechanismus noetig).
;; Die Installation MUSS vor dem allerersten vfl-in-*-Aufruf erfolgen: ein
;; echtes ESC (nicht ein leeres Enter) loest bei JEDEM get*-Aufruf sofort
;; *error* aus (nicht nil) - ohne den Handler wuerde ein ESC in diesem
;; Fenster auf den vorherigen Handler zurueckfallen (im Editier-Pfad: der
;; ssg-start-Handler ohne Wickeln - der bereits geloeschte Original-Block
;; waere dann ersatzlos weg). lastEnt/old-error/vfl-nummer/anzahl-gf/
;; anzahl-vf/startpunkt/frame/as-seite/es-seite sind zur Aufrufzeit
;; dynamisch gebunden und daher im Handler-Lambda sichtbar (AutoLISP
;; dynamic scoping, gilt fuer die gesamte Laufzeit dieses Aufrufs, auch
;; tief verschachtelt z.B. in vfl-vf-einheit).
(setq vfl-nummer (vf-next-number))
(setq lastEnt (vf-lastent-ohne-attribute))
(setq anzahl-gf 0 anzahl-vf 0 frame nil)
(setq old-error *error*)
;; Abbruch-Handler: zuerst *error* zuruecksetzen (kein rekursiver
;; Wiedereintritt bei einem Fehler waehrend des Wickelns), dann wickeln.
;; Laeuft der Aufruf innerhalb einer ssg-start-Sitzung (Editier-Pfad via
;; c:VARIOFOERDERER_EDIT -> vfl-edit-ent), wird diese mit ssg-end sauber
;; geschlossen (Undo-Gruppe, gesicherte Systemvariablen, *error* aus dem
;; Sitzungs-Frame) - sonst bliebe die von ssg-start geoeffnete Undo-Gruppe
;; offen.
(setq *error*
(function (lambda (msg)
(setq *error* old-error)
(vfl-modus-abbruch-sichern lastEnt vfl-nummer anzahl-gf anzahl-vf
startpunkt frame as-seite es-seite)
(if (and (boundp '*ssg-start-stack*) *ssg-start-stack*) (ssg-end))
(princ))))
(setq startpunkt (vfl-in-point nil (ssg-text "vfl-prompt-startpunkt-kette")))
(if (null startpunkt) (progn (princ (ssg-text "vfl-abgebrochen")) (exit)))
;; Hoehe (Z) des Startpunkts abfragen und in die Z-Koordinate uebernehmen.
@@ -1524,29 +1572,6 @@
;; ES-Seite wird erst am Kettenende abgefragt (siehe vfl-frage-es-seite),
;; da das ES-Element erst dort eingefuegt wird.
(setq vfl-nummer (vf-next-number))
(setq lastEnt (vf-lastent-ohne-attribute))
;; --- Abbruch-Rollback + Wiederaufnahme scharf schalten ---
;; Ab hier wird Geometrie gebaut. Ein *error*-Handler faengt jeden Abbruch
;; (ESC / (exit) / Laufzeitfehler) ab, entfernt die bereits eingefuegte
;; Teil-Geometrie (alles nach lastEnt, siehe vfl-modus-cleanup) und sichert
;; das Journal der bis dahin VOLLSTAENDIG abgeschlossenen Glieder
;; (*vfl-journal-commit*) fuer die Wiederaufnahme. lastEnt/old-error sind zur
;; Aufrufzeit dynamisch gebunden und daher im Handler sichtbar.
(setq *vfl-journal-commit* nil)
(setq old-error *error*)
;; Abbruch-Handler: zuerst *error* zuruecksetzen (kein rekursiver Wiedereintritt
;; bei einem Cleanup-Fehler), dann Teil-Geometrie zuruecknehmen. Laeuft der
;; Aufruf innerhalb einer ssg-start-Sitzung (Edit-Pfad via c:VARIOFOERDERER_EDIT
;; -> vfl-edit-ent), wird diese mit ssg-end sauber geschlossen (Undo-Gruppe,
;; gesicherte Systemvariablen, *error* aus dem Sitzungs-Frame) - sonst bliebe
;; die von ssg-start geoeffnete Undo-Gruppe offen.
(setq *error*
(function (lambda (msg)
(setq *error* old-error)
(vfl-modus-cleanup lastEnt)
(if (and (boundp '*ssg-start-stack*) *ssg-start-stack*) (ssg-end))
(princ))))
(setq p-aktuell startpunkt)
(setq letzter-typ nil fertig nil frame nil)
(setq anzahl-gf 0 anzahl-vf 0)
@@ -1568,11 +1593,6 @@
;; nutzt weiterhin vfl-segment-entscheidung).
(princ (ssg-text "vfl-naechstes-element-header"))
(setq linie-ende-modus nil)
;; Commit-Schnappschuss: Journalstand NACH dem letzten vollstaendig
;; abgeschlossenen Glied (vor dem Marker der neuen, noch offenen Iteration).
;; Bei Abbruch wird genau dieser Stand als Wiederaufnahme-Journal gesichert
;; (das angefangene Glied faellt weg).
(setq *vfl-journal-commit* *vfl-journal*)
;; Glied-Checkpoint am Iterationskopf setzen; Label wird nach der Auswahl
;; nachgetragen (vfl-journal-steplabel), damit jedes Glied seine eigene
;; Menue-Auswahl + Segment-Eingaben vollstaendig umschliesst (fuer den
@@ -1930,12 +1950,8 @@
;; Eingabe-Journal am fertigen Block persistieren (Marker "linienzug" +
;; Chunks) -> spaeter per Doppelklick editierbar (vfl-edit-ent).
(if vfl-ins (vfl-journal-xdata-schreiben vfl-ins))
;; Erfolgreicher Abschluss: *error* zuruecksetzen, evtl. offenes Abbruch-
;; Journal verwerfen (die Kette ist fertiggestellt), Editier-Marker loeschen
;; (der Original-Block bleibt im Edit-Fall geloescht - der Neuaufbau ersetzt ihn).
;; Erfolgreicher Abschluss: *error* zuruecksetzen.
(setq *error* old-error)
(setq *vfl-journal-letzter-abbruch* nil)
(setq *vfl-edit-orig-ent* nil)
(princ "\n\n=========================================")
(princ (ssg-text "vfl-kette-eingefuegt"))
@@ -1947,11 +1963,14 @@
;; EDITIEREN: gliedweise zuruecknehmen + Neuaufbau per Replay
;; ============================================================
;; Aufgerufen aus c:VARIOFOERDERER_EDIT (Doppelklick auf VF_n mit XDATA-Marker
;; "linienzug"). Liest das Eingabe-Journal vom Block, listet die Glieder, laesst
;; k Glieder vom Ende zuruecknehmen, loescht den alten Block und baut die Kette
;; neu: die behaltenen Glieder werden stumm abgespielt, danach laeuft die
;; Eingabe interaktiv fuer die geaenderten/neuen Glieder weiter.
(defun vfl-edit-ent (ent / journal glieder n i k trunc)
;; "linienzug", egal ob fertiggestellt oder durch einen Abbruch gewickelt).
;; Liest das Eingabe-Journal vom Block, listet die Glieder, laesst per
;; DCL-Dialog (vfl-dlg-position) eine Sektion waehlen, auf die zurueckgesetzt
;; wird, loescht den alten Block und baut die Kette neu: die behaltenen
;; Glieder werden stumm abgespielt, danach laeuft die Eingabe interaktiv fuer
;; die geaenderten/neuen Glieder weiter. Vorbelegung im Dialog = voller Stand
;; -> Doppelklick + sofort OK setzt einen abgebrochenen Bau nahtlos fort.
(defun vfl-edit-ent (ent / journal glieder n i pos trunc)
(setq journal (vfl-journal-xdata-lesen ent))
(if (null journal)
(progn (alert (ssg-text "vfl-edit-kein-journal")) (exit)))
@@ -1962,33 +1981,48 @@
(foreach g glieder
(princ (strcat "\n " (itoa i) ") " (vfl-glied-label-text g)))
(setq i (1+ i)))
(setq k (getint (ssg-textf "vfl-edit-wieviele" (list (itoa n)))))
(if (or (null k) (< k 0)) (setq k 0))
(if (> k n) (setq k n))
(setq trunc (vfl-journal-truncate journal (- n k)))
(setq pos (vfl-dlg-position glieder))
(if (null pos)
(progn (princ (ssg-text "vfl-edit-abgebrochen")) (exit)))
(setq trunc (vfl-journal-truncate journal pos))
;; Alten Block entfernen (wie Standard/Etage: entdel + kompletter Neuaufbau).
;; *vfl-edit-orig-ent* merken, damit ein Abbruch waehrend des Neuaufbaus den
;; Original-Block wiederherstellen kann (vfl-modus-cleanup).
(setq *vfl-edit-orig-ent* ent)
;; Bricht der Neuaufbau selbst ab, wickelt vfl-modus-abbruch-sichern den
;; Zwischenstand automatisch zu einem neuen Block - kein Sonderfall noetig.
(entdel ent)
(princ (ssg-textf "vfl-edit-abgespielt" (list (itoa (- n k)) (itoa k))))
(princ (ssg-textf "vfl-edit-abgespielt" (list (itoa pos) (itoa (- n pos)))))
;; Behaltene Glieder abspielen, dann live weiterbauen.
(vfl-journal-replay-start trunc)
(vf-linienzug-modus)
(princ))
;; Wiederaufnahme des zuletzt abgebrochenen Linienzugs: das beim Abbruch
;; gesicherte Journal abspielen und danach interaktiv weiterbauen.
(defun vfl-modus-fortsetzen ( / )
(if (and (boundp '*vfl-journal-letzter-abbruch*) *vfl-journal-letzter-abbruch*)
;; DCL-Dialog: Sektion waehlen, auf die die Kette zurueckgesetzt wird.
;; glieder = Liste der Glied-Labels (interne Tokens, vfl-journal-glieder), in
;; Bau-Reihenfolge. Vorbelegung auf den letzten Eintrag (voller Stand = reines
;; Fortsetzen ohne Kuerzung). Rueckgabe: gewaehlte Position (1-basiert) oder
;; nil bei Abbrechen/Escape.
(defun vfl-dlg-position (glieder / n dcl-pfad dat idx ergebnis i)
(setq n (length glieder))
(setq dcl-pfad (strcat (getenv "DXFM_DCL") "/vfl_edit.dcl"))
(setq dat (load_dialog dcl-pfad))
(if (not (new_dialog "vfl_edit" dat))
(progn (alert (ssg-textf "vfl-edit-dialog-fehlt" (list dcl-pfad))) nil)
(progn
(princ (ssg-text "vfl-fortsetzen-start"))
(setq *vfl-edit-orig-ent* nil) ; Wiederaufnahme ist kein Edit -> kein Original restaurieren
(vfl-journal-replay-start *vfl-journal-letzter-abbruch*)
(vf-linienzug-modus))
(progn
(princ (ssg-text "vfl-fortsetzen-keiner"))
(princ))))
(set_tile "kopf" (ssg-textf "vfl-dlg-kopf-sektionen" (list (itoa n))))
(start_list "position")
(setq i 1)
(foreach g glieder
(add_list (strcat (itoa i) " - " (vfl-glied-label-text g)))
(setq i (1+ i)))
(end_list)
(set_tile "position" (itoa (1- n))) ; letzter Eintrag vorbelegt (0-basierter Index)
(action_tile "accept" "(setq idx (atoi (get_tile \"position\"))) (done_dialog 1)")
(action_tile "cancel" "(done_dialog 0)")
(setq ergebnis (start_dialog))
(unload_dialog dat)
(if (= ergebnis 1) (1+ idx) nil)
)
)
)
;; ============================================================
;; DIAGNOSE: Blockstruktur (KS_EIN/KS_AUS) untersuchen