;;; ---------------------------------------------------------------------------
;;; XRay.lsp
;;; code compiled by YZ August 2026
;;; ---------------------------------------------------------------------------
;;; Dumps every ActiveX property and method of a picked object to the text
;;; window.
;;;
;;; This is the single most useful tool there is when writing your own
;;; routines. When you need to know what a piece of geometry actually exposes -
;;; what a dynamic block calls its parameters, whether a leader stores its
;;; landing point as a property or a DXF group, what an object's real class
;;; name is - you point this at it and read the answer, instead of guessing.
;;;
;;; TWO COMMANDS, ONE DIFFERENCE
;;;   XRAY   - inspects the top-level object you pick. Picking anything inside
;;;            a block reports the block reference itself.
;;;   XRAYN  - inspects the NESTED object. Picking inside a block or xref
;;;            reports the individual line, arc or text you actually clicked,
;;;            not its container.
;;;
;;; Output goes to the text window; press F2 if it is not already visible.
;;;
;;; This routine is read-only. It inspects and reports, and never modifies the
;;; drawing, so it deliberately changes no system variables and opens no undo
;;; group - there is nothing to undo, and an empty undo entry would only get in
;;; the user's way.
;;; ---------------------------------------------------------------------------

;; The vlax- family of functions used below live in the ActiveX layer, which is
;; not loaded into a fresh drawing session by default. Loading it at file scope
;; means it is guaranteed available however the routine is later invoked.
(vl-load-com)

;; ---------------------------------------------------------------------------
;; XRay:Dump
;; ---------------------------------------------------------------------------
;; Dumps whatever it is handed, in whichever form it arrives.
;;
;; Object references turn up in four different guises depending on where they
;; came from, and rather than forcing the caller to convert, this reduces each
;; form to a VLA-object by recursing on itself:
;;
;;   vla-object - already what we need; dump it and stop
;;   ename      - a plain entity name, as returned by entsel/ssname. Converted.
;;   list       - a DXF data list from entget. Group -1 holds its entity name,
;;                so that is pulled out and fed back through.
;;   str        - an object handle, e.g. "2AF". handent resolves it.
;;
;; The t argument to vlax-dump-object is what makes this worth using: it
;; expands the methods as well as the properties. Without it you see only half
;; the picture.
;; ---------------------------------------------------------------------------
(defun XRay:Dump ( arg )
    (cond
        (   (= 'vla-object (type arg))
            (vlax-dump-object arg t)
        )
        (   (= 'ename (type arg))
            (XRay:Dump (vlax-ename->vla-object arg))
        )
        (   (= 'list (type arg))
            (XRay:Dump (cdr (assoc -1 arg)))
        )
        (   (= 'str (type arg))
            (XRay:Dump (handent arg))
        )
        (   t
            ;; Reached only if the argument was nil or some type we cannot
            ;; resolve. Reported rather than failing silently, so the user is
            ;; not left wondering why nothing appeared.
            (princ "\nNothing to inspect - selection was empty or unrecognised.")
        )
    )
    (princ)
)

;; ---------------------------------------------------------------------------
;; c:XRAY  -  inspect the top-level object
;; ---------------------------------------------------------------------------
(defun c:XRAY ( / *error* sel )

    ;; No state is altered by this routine, so the handler has nothing to
    ;; restore. It exists purely so that an unexpected failure prints a clear
    ;; message instead of dropping raw LISP error text on the user.
    (defun *error* ( msg )
        (if (and msg (not (wcmatch (strcase msg t) "*break*,*cancel*,*exit*")))
            (princ (strcat "\n** XRAY error: " msg " **"))
        )
        (princ)
    )

    ;; entsel returns (<entity name> <pick point>), or nil if the user pressed
    ;; Enter or Escape, so car is only taken once the result is known good.
    (if (setq sel (entsel "\nSelect object to inspect: "))
        (progn
            (textscr)                 ; bring the text window forward
            (XRay:Dump (car sel))
        )
        (princ "\n*Cancelled* - nothing selected.")
    )

    (princ)
)

;; ---------------------------------------------------------------------------
;; c:XRAYN  -  inspect the nested object inside a block or xref
;; ---------------------------------------------------------------------------
(defun c:XRAYN ( / *error* sel )

    (defun *error* ( msg )
        (if (and msg (not (wcmatch (strcase msg t) "*break*,*cancel*,*exit*")))
            (princ (strcat "\n** XRAYN error: " msg " **"))
        )
        (princ)
    )

    ;; nentsel is the nested equivalent of entsel: it reaches through block and
    ;; xref references and returns the primitive actually under the cursor.
    (if (setq sel (nentsel "\nSelect nested object to inspect: "))
        (progn
            (textscr)
            (XRay:Dump (car sel))
        )
        (princ "\n*Cancelled* - nothing selected.")
    )

    (princ)
)

(princ)
