Hi Marco. My approach to your challenge was to pen a function that returned all the
references (direct / indirect) to a given block name,
not all the block names
hosted by a block name.
To illustrate, in your sample drawing block "C1" is referenced by "B1" and "B1" is referenced by "A1". Said another way, "A1" has an instance of "B1", and "B1" has an instance of "C1".
My rationale being that if one wished to update "C1" by adding selected objects, and the selection set included instances of either "A1", "B1" or "C1" it would be doomed to failure since any one one them would cause a circular reference -- directly or indirectly.
Thus if you select a block to modify, say "C1", a selection filter was created that prevents selecting any instances of "A1", "B1", "C1". Hope that makes sense.
In your sample drawing this is what each name would return using the function I penned:
(vlax-for b (vla-get-blocks (setq doc (vla-get-activedocument (vlax-get-acad-object))))
(if (eq :vlax-false (vla-get-islayout b))
(princ
(strcat
"\n" (setq n (vla-get-name b)) ": "
(vl-princ-to-string (mp-block-names-hosting doc n))
)
)
)
(princ)
)
B1: (A1) | "B1" is referenced by "A1", or "A1" has 1 or more instances of "B1". |
B2: (A1) | "B2" is referenced by "A1". |
A1: nil | "A1" is not referenced by any non-layout blocks. |
C1: (A1 B1) | "C1" is referenced by "A1" & "B1". |
In the latter case "B1" is direct, "A1" is indirect, as "B1" has 1 or more instances of "C1", "A1" has 1 or more instances of "B1".An aside, the original flavor I posted would fail if there were no instances of the block you wished to revise, as it was leveraging the instancing that can be quickly lifted from the block def via dxf 331 pairs. It's been updated to use the slower - but more reliable - "iterate all child objects" technique.
While seemingly verbose, it performs reasonably fast and will also find references within XREFs. The latter can be tricky when they're nested via cascaded attaches as the nesting does not manifest via AcDbBlockReference instances but rather AbDbBlockTableRecord instances at the root level of each XREF, flagged by dxf 332 sentinels. I know, a lot to digest - sorry - don't know how to say it clearer or more concisely.
Anyway, using:
(defun mp-block-names-hosting ( doc name / get-refs find-refs mp-main )
;; get nested block refs
(defun get-refs ( b / s n d result )
(setq s '((setq n (strcase (vla-get-name x)))))
(vlax-for x b
(and
(eq "AcDbBlockReference" (vla-get-objectname x))
(eq 'str (type (vl-catch-all-apply 'eval s)))
(or (member n result) (setq result (cons n result)))
)
)
(if (setq d (entget (vlax-vla-object->ename b)))
(foreach e (mapcar 'cdr (vl-remove-if-not '(lambda (p) (eq 332 (car p))) d))
(or
(member (setq n (strcase (cdr (assoc 2 (entget e))))) result)
(setq result (cons n result))
)
)
)
(if result (cons (strcase (vla-get-name b)) (reverse result)))
)
;; find nested block refs
(defun find-refs ( lst name / n! )
;; accesses lexical global var: result
(foreach n (setq n! (car lst) lst (cdr lst))
(cond
((and (/= n name) (null (member n result))))
((member n! result))
((setq result (cons n! result)))
)
)
)
;; wrap it up
(defun mp-main ( blocks name / x lst result )
(if (eq 'vla-object (type (vl-catch-all-apply 'vla-item (list blocks name))))
(progn
(vlax-for b blocks
(and
(eq :vlax-false (vla-get-islayout b))
(setq x (get-refs b))
(setq lst (cons x lst))
)
)
(setq lst (append (reverse lst) lst))
(while
(not
(equal
(length (cdr result))
(progn
(foreach x lst (find-refs x name))
(length (cdr result))
)
)
)
)
)
)
result
)
;; pull the trigger ...
(mp-main (vla-get-blocks doc) (strcase name))
)
A filter can be created preventing the user from selecting any blocks that would cause a circular reference to the original block insert selected:
(if
(and
;; let's assume insert "B1" selected:
(setq ent (car (entsel "Select block insert to update: ")))
(eq "INSERT" (cdr (assoc 0 (setq data (entget ent)))))
;; block-name = "B1"
(setq block-name (cdr (assoc 2 data)))
(setq doc (vla-get-activedocument (vlax-get-acad-object)))
;; nfg = ("B1" "A1"), the original block selected and "A1", since it references "B1"
(setq nfg (cons block-name (mp-block-names-hosting doc block-name)))
(setq nfg (substr (apply 'strcat (mapcar '(lambda (n) (strcat "," n)) nfg)) 2))
;; filter = ((-4 . "<or") (0 . "~INSERT") (-4 . "<not") (2 . "B1,A1") (-4 . "not>") (-4 . "or>"))
(setq filter (list '(-4 . "<or") '(0 . "~INSERT") '(-4 . "<not") (cons 2 nfg) '(-4 . "not>") '(-4 . "or>")))
(setq ss (ssget filter))
)
(progn
(princ
(strcat
"\nFilter: " (vl-prin1-to-string filter)
"\nAllowed: " (itoa (sslength ss)) " objects"
" that can be added to block def \"" block-name "\"."
)
)
;; ready to roll ...
(princ)
)
(princ)
)
Might report:
Filter: ((-4 . "<or") (0 . "~INSERT") (-4 . "<not") (2 . "B1,A1") (-4 . "not>") (-4 . "or>"))
Allowed: 9 objects that can be added to block def "B1".If you selected all visible entities.
Hope it makes sense - for what it's worth - Michael.
Revised per posts 25-27.