Jump to content

Select Similar LISP


broncos15

Recommended Posts

So I have a quick question on why this LISP routine isn't doing what I want it to. My goal is that I want to only grab multileaders that have a particular #. For example, when labeling I have leaders with blocks that have #'s inside of them. My code currently only grabs leaders on the layer and it seems to ignore my code to grab the group code 302. What am I doing incorrectly?

(defun c:ssleader ( / ss1 ss1data ss1302 ss18 ss2 cnt *error*)
 (defun *error* ( msg )
       (if (not (member msg '("Function cancelled" "quit / exit abort")))
           (princ (strcat "\nError: " msg))
       )
       (princ)
   )
 (if (setq ss1 (LM:ssget "\nSelect MLEADER with #: "
      '("_+.:E:S" ((0 . "MULTILEADER")))
   ))
   (progn
     (setq ss1data (ssname ss1 0)
    ss1302 (cdr (assoc 302 (entget ss1data)))
    ss18 (cdr (assoc 8 (entget ss1data))))
     (setq ss2 (ssget "_A" (list '(0 . "MULTILEADER") (cons 302 ss1302) (cons 8 ss18))))
     (setq cnt 0)
     (repeat (sslength ss2)
         (if (sslength ss2)
          (sssetfirst nil ss2)
           )
           (setq cnt (+ cnt 1))
       )
     )
   )
 (princ)
 )

Link to comment
Share on other sites

So I have a quick question on why this LISP routine isn't doing what I want it to. My goal is that I want to only grab multileaders that have a particular #. For example, when labeling I have leaders with blocks that have #'s inside of them. My code currently only grabs leaders on the layer and it seems to ignore my code to grab the group code 302. What am I doing incorrectly?
(defun c:ssleader ( / ss1 ss1data ss1302 ss18 ss2 cnt *error*)

ac2007 does not have multileader, maybe others can help

 

FWIW:

1. (setq cnt (+ cnt 1)) in repeat loop, just increases but does nothing ?

2. (sssetfirst nil ss2) enough for selection ss2 (if qualified), no need repeat

3. try (setq ss18 (assoc 8 (entget ss1data))) ;dotted pair so no need cdr then cons in ssget list

Link to comment
Share on other sites

ac2007 does not have multileader, maybe others can help

 

FWIW:

1. (setq cnt (+ cnt 1)) in repeat loop, just increases but does nothing ?

2. (sssetfirst nil ss2) enough for selection ss2 (if qualified), no need repeat

3. try (setq ss18 (assoc 8 (entget ss1data))) ;dotted pair so no need cdr then cons in ssget list

So I made the above changes and a few other changes as well. for some reason, it seems like it is completely ignoring my 302 group code though. Does it have something to do with the fact that 302 is a string so for example, 4 reads 302 . "4"? My updated code is:
(defun c:ssleader ( / entlist  ent302 ent8 ss *error*)
 (defun *error* ( msg )
       (if (not (member msg '("Function cancelled" "quit / exit abort")))
           (princ (strcat "\nError: " msg))
       )
       (princ)
   )
 (if (and (setq entlist (entget (car (entsel "\nSelect MLEADER with # to select: "))))
   (= (assoc 0 entlist) "MULTILEADER"))
   (progn
     (setq ent302 (assoc 302 entlist)
    ent8 (assoc 8 entlist))
     (setq ss (ssget "_A" (list '(0 . "MULTILEADER") (cons 302 ent302) (cons 8 ent8))))
(if (sslength ss)
  (sssetfirst nil ss)
  )
     )
   )
 (princ)
 )

Link to comment
Share on other sites

Hi broncos.

 

So I made the above changes and a few other changes as well. for some reason, it seems like it is completely ignoring my 302 group code though. Does it have something to do with the fact that 302 is a string so for example, 4 reads 302 . "4"?

 

Well to start, there is the following issue with the code you posted

You store in a variable "ent8" (assoc 8 entlist), so when later on to build your ssget you use (cons 8 ent8), the result looks like that (8 8 . "DIM") instead of a dotted pair. So the first thing is to look at (cdr (assoc 8 entlist)) to only retrieve the assoc value instead of the dotted pair for both 8 and 302.

 

The other thing, is that: Welcome into the world of multiple assoc! You just opened a fun can of worms. Let me show you. Here I (entget(car(entsel))) a mleader. (I'll crop some of the result)

Command: (entget (car(entsel)))

Select object: ((-1 . ) (0 . "MULTILEADER") (330 . ) (5 . "83F9B") (100 . "AcDbEntity") (67 . 1) (410 . "01") (..cropped..) (297 . 0) (302 . "LEADER{") (290 . 1) (...cropped...) (44 . 0.0) (302 . "1") (330 . ) (177 . 2) (44 . 0.0) (302 . "") (294 . 0) (178 . 0) (179 . 1) (45 . 1.0) (271 . 0) (272 . 9) (273 . 9) (295 . 1))

So, as you can see, you have multiple assoc 302 dotted pairs. With (assoc 302), it retrieves the first one it encounters and returns it. In that precise case, that is not the assoc 302 we want. In a mleader made of a block, there is that one (302 . "LEADER{"), and 1 more assoc 302 for every attribute def (even if ther are setted to invisible, like my 3rd one which cant be modified by the attribute editor). Basically, you have to iterate thru your entlist using a (part in green in the below code) for each statement and use if/cond to find and focus on the specific dotted pair you're looking for and/or ignore the one(s) that you dont need. As you will see, i've manage to do that by verifying that the car was 302 AND the cdr didn't correspond to either "LEADER{" / "" (empty string). When these conditions were met in a dotted pair, I bound the ent302 variable to the cdr of that specific pair.

 

(defun c:ssleader ( / entlist ent302 ent8 ss *error*)
 (defun *error* (msg)
   (if	(not
  (member msg '("Function cancelled" "quit / exit abort"))
)
     (princ (strcat "\nError: " msg))
   )
   (princ)
 )
 (if (and (setq
     entlist (entget
	       (car (entsel "\nSelect MLEADER with # to select: "))
	     )
   )
   (= [color="red"](cdr[/color](assoc 0 entlist)[color="red"])[/color] "MULTILEADER"); [color="red"]CDR![/color]
     )
     (progn
       (setq ent8   [color="red"](cdr [/color](assoc 8 entlist)[color="red"])[/color]; [color="red"]CDR![/color]
       )
       [color="seagreen"][b](foreach x entlist
          (if (and (eq 302 (car x))
                   (not (or (wcmatch "LEADER{" (cdr x)) (wcmatch "" (cdr x))))
              )
              (setq ent302 (cdr x))
          )
       )[/b][/color]
       (setq ss (ssget "_A" (list '(0 . "MULTILEADER") (cons 302 ent302) (cons 8 ent8)))
       )
       (if (sslength ss)
           (sssetfirst nil ss)
       )
     )
 )
 (princ)
)

That being said, you will get in trouble if

-You select a mleader have more than 1 attribute in the mleader's block (the code retrieves the first non empty attribute value)

-You have mleaders made from any other block which contain attributes.

As is the code make a selection of all the mleaders as long any attribute hold the same information as the 1rst non empty attribute value of the selected mleader.)

Its far from being fail safe, and a lot could go wrong but it might suit your needs if all mleaders contain the same unique block, and they have only 1 attribute. Even if it doesn't quite suit your needs, i hope that I helped you learn something.

 

ps.: could you please go back and edit your reply in your lentityp-nil-error thread to give me some feedback? Your reply only contains a quote of my own message (with a missing closing quote tag furthermore..) :D

Thanks

 

Cheers

Jef!

Link to comment
Share on other sites

Jef!, thank you so much! That was super informative, especially about the multiple assoc. I haven't really dealt with that yet in coding, so that is really cool to learn about. I haven't used a for each loop before, so again, thank you again. I know I really need to study up on about when to use car, assoc, and cdr. I often mess up when using them, but your information was super useful. My next code is going to be editing the attributes in the mleader blocks, so I know that all this information will be helpful when I attempt to accomplish that. I changed the quote tags in the other post (somehow that and my question got deleted). Thanks again for the help and the information, I really appreciate it!

Link to comment
Share on other sites

You should be safe to use the DXF groups following the CONTEXT_DATA block, e.g.:

(defun c:test ( / ent enx val )
   (while
       (progn
           (setvar 'errno 0)
           (setq ent (car (entsel "\nSelect mleader: ")))
           (/= 52 (getvar 'errno))
       )
       (cond
           (   (null ent)
               (princ "\nMissed, try again.")
           )
           (   (/= "MULTILEADER" (cdr (assoc 0 (setq enx (entget ent)))))
               (princ "\nSelected object is not a multileader.")
           )
           (   (/= acblockcontent (cdr (assoc 172 (reverse enx))))
               (princ "\nSelected multileader does not contain an attributed block.")
           )
           (   (null (setq val (vl-remove-if-not '(lambda ( x ) (and (= 302 (car x)) (wcmatch (cdr x) "~*[~0-9]*"))) (member '(301 . "}") enx))))
               (princ "\nMultileader attributes do not contain integer values.")
           )
           (   (null (sssetfirst nil (ssget "_A" (list '(0 . "MULTILEADER") (assoc 8 enx) (car val))))))
       )
   )
   (princ)
)

Link to comment
Share on other sites

Hi Lee!

 

I setuped few different mleaders to do my tests and i gave a try to your code.

It seems to do the same thing as my code when selecting a mleader containing a digit, it adds to the selection any other mleaders with the same info in any attribute of any mleader made of any other block.. (hope you understood that one! :D ), with some nice added check ups (like "does not contain an attributed block") but when I use it selecting a mleader that contains "cc", it seems to add up any mleaders (made of the same block) to the selection set as long as they are on the same layer..

 

I'm also curious on one thing: what is the var 'errno?

Link to comment
Share on other sites

Lee, like usual, your coding is much more sophisticated than mine. Do I understand correctly:

(   (/= acblockcontent (cdr (assoc 172 (reverse enx))))
               (princ "\nSelected multileader does not contain an attributed block.")
           )

This is checking to see if the entity is an attributed block. When I print the dxf data for a mleader with a block, I get (172 . 1 ). What does the reverse enx portion of the code do? Also, I have a really hard time with the lambda function. How exactly is it passing the arguments? I understand that the

(wcmatch (cdr x) "~*[~0-9]*")))

Portion checks to see if it is an integer. However, How is the lambda function passing everything to the vl-remove-if-not function? Thank you again for posting this! I always learn so much from studying your codes.

Link to comment
Share on other sites

when I use it selecting a mleader that contains "cc", it seems to add up any mleaders (made of the same block) to the selection set as long as they are on the same layer..

 

I'm unsure why this would be the case, as the wcmatch test within the vl-remove-if-not expression is specifically excluding any attribute values unless they contain only integer values.

 

I'm also curious on one thing: what is the var 'errno?

 

ERRNO Reference - it can be very useful for determining additional information about a user's response when a function returns nil for one than one type of invalid input.

 

Do I understand correctly:
(   (/= acblockcontent (cdr (assoc 172 (reverse enx))))
               (princ "\nSelected multileader does not contain an attributed block.")
           )

This is checking to see if the entity is an attributed block.

 

Yes, checking to see whether the selected multileader has block content (as opposed to MText or None).

 

When I print the dxf data for a mleader with a block, I get (172 . 1). What does the reverse enx portion of the code do?

 

DXF Group 172 represent different properties of the MLeader depending on the position of the dotted pair within the subgroups of the MLeader DXF data (CONTEXT_DATA / LEADER / LEADER_LINE). The reverse ensures that the appropriate dotted pair is obtained by assoc.

 

Also, I have a really hard time with the lambda function. How exactly is it passing the arguments? I understand that the
(wcmatch (cdr x) "~*[~0-9]*")))

Portion checks to see if it is an integer. However, How is the lambda function passing everything to the vl-remove-if-not function?

 

The second argument for the vl-remove-if-not expression is a predicate function - this is a function which typically returns a boolean value, however, in the context of AutoLISP, this can be any function since any non-nil value is interpreted as True (T).

 

The vl-remove-if-not function will iterate over every item in the given list and will supply each list item as an argument to the given predicate function. The predicate function is evaluated with the list item, and, if the result if the evaluation is a non-nil value then the item is retained in the output list, otherwise, the item is omitted from the output list.

 

The predicate function need not necessary be a lambda function, but could be any defined function which will accept one or more arguments, observe:

_$ (defun IsTheNumberTwo ( x ) (= x 2))
ISTHENUMBERTWO
_$ (vl-remove-if-not 'IsTheNumberTwo '(1 2 3 2 1))
(2 2)

_$ (vl-remove-if-not '(lambda ( x ) (= x 2)) '(1 2 3 2 1))
(2 2)

_$ (vl-remove-if-not 'zerop '(0 1 2 2 1 0))
(0 0)

Thank you again for posting this! I always learn so much from studying your codes.

 

You're most welcome! :)

Link to comment
Share on other sites

3. try (setq ss18 (assoc 8 (entget ss1data))) ;dotted pair so no need cdr then cons in ssget list

 

hi broncos,

what i meant was : since your ss18 already assoc dotted pair , so in ssget list no need cons

 

example:

if ss18= '(8 . "layer1")

(setq ss (ssget "_A" (list '(0 . "MULTILEADER") [color="red"]ss18[/color] )) [color="green"];not (cons 8 ss18)[/color]

 

whereas,

(cdr ss18) ; returns "layer1"

i hope u won't get confused

 

Thanks jef & Lee for the assist during absent :thumbsup:

though without multileader, i still got bonus lesson from this thread :)

Link to comment
Share on other sites

Thank you everyone for the help and the information! So I tried rewriting it to gain some more practice, but I am having difficulty grabbing only one type of style. For example, if I had a style called Hex and another called square and both had the number 1 in them, it would grab both the styles with the 1 instead of just the style that was selected. My new code is.

(defun c:ssleader (/ entlist ent302 ent8 ss  whilestop *error*)
 (defun *error* (msg)
   (if (not
  (member msg '("Function cancelled" "quit / exit abort"))
)
     (princ (strcat "\nError: " msg))
   )
   (princ)
 )
 (setq whilestop t)
 (setvar 'errno 0)
 (while whilestop
   (if
     (setq ent (car (entsel "\nSelect MLEADER with # to select: ")))
      (progn
 (setq entlist (entget ent))
 (cond
   ((/= (cdr (assoc 0 entlist)) "MULTILEADER")
    (princ "\nSelected object is not a multileader.")
   )
   ((/= acblockcontent (cdr (assoc 172 (reverse entlist))))
    (princ
      "\nSelected multileader does not contain an attributed block."
    )
   )
   (t
    (setq ent8 (cdr (assoc 8 entlist)))
    (foreach x entlist
      (if
 (and
   (eq 302 (car x))
   (not
     (or (wcmatch "LEADER{" (cdr x)) (wcmatch "" (cdr x)))
   )
 )
  (setq ent302 (cdr x))
      )
    )
    (if (/= ent302 nil)
      (progn
 (setq ss (ssget "_A"
   (list '(0 . "MULTILEADER")
         (cons 8 ent8)
         (cons 302 ent302)
   )
   )
 )
 (if (sslength ss)
   (sssetfirst nil ss)
 )
 (setq whilestop nil)
      )
      (princ "\nMultileader attribute is blank.")
    )
   )
 )
      )
     (if (/= (getvar 'errno) 52)
(princ "\nYou missed. Try again.")
(setq whilestop nil))
   )
 )
 (princ)
)

Link to comment
Share on other sites

So I have tried to append everything I grabbed in ssget, but I am getting the error "extra cdrs in dotted pair on input". What am I doing wrong?

(defun c:ssleader (/ entlist ent302 ent8 ss *error* whilestop)
 (defun *error* (msg)
   (if (not
  (member msg '("Function cancelled" "quit / exit abort"))
)
     (princ (strcat "\nError: " msg))
   )
   (princ)
 )
 (setq whilestop t)
 (setvar 'errno 0)
 (while whilestop
   (if
     (setq ent (car (entsel "\nSelect MLEADER with # to select: ")))
      (progn
 (setq entlist (entget ent))
 (cond
   ((/= (cdr (assoc 0 entlist)) "MULTILEADER")
    (princ "\nSelected object is not a multileader.")
   )
   ((/= acblockcontent (cdr (assoc 172 (reverse entlist))))
    (princ
      "\nSelected multileader does not contain an attributed block."
    )
   )
   (t
    (ssleader:ssgrab)
    )
 )
      )
      (if (/= (getvar 'errno) 52)
 (princ "\nYou missed. Try again.")
 (setq whilestop nil)
      )
   )
 )
 (princ)
)
(defun ssleader:ssgrab ()
 (setq obj  (vlax-ename->vla-object (entlist))
objstyle (vla-get-StyleName obj)
 )
 (setq ent8 (cdr (assoc 8 entlist)))
 (foreach x entlist
   (if
     (and
(eq 302 (car x))
(not
  (or (wcmatch "LEADER{" (cdr x)) (wcmatch "" (cdr x)))
)
     )
      (setq ent302 (cdr x))
   )
 )
 (if (/= ent302 nil)
   (progn
     (setq ss (ssget "_A"
       (list '(0 . "MULTILEADER")
      (cons 8 ent8)
      (cons 302 ent302)
       )
       )
     )
     (setq cnt 0)
     (repeat (sslength ss)
(if
  (/=
    (vla-get-stylename (vlax-ename->vla-object (ssname ss cnt)))
    (objstyle)
  )
   (ssdel (ssname ss cnt) ss)
)
(setq cnt (+ cnt 1))
     )
     (if (sslength ss)
(sssetfirst nil ss)
     )
     (setq whilestop nil)
   )
   (princ "\nMultileader attribute is blank.")
 )
)

Edited by broncos15
Link to comment
Share on other sites

So I have tried to append everything I grabbed in ssget, but I am getting the error "extra cdrs in dotted pair on input". What am I doing wrong?


 (setq obj  ([color="blue"]vlax-ename->vla-object[/color] [color="red"](entlist)[/color]) [color="green"];<-- argument should be an entity/ename neither a function nor a list [/color]
[color="#ff00ff"]objstyle[/color] (vla-get-StyleName obj)
 )
...
...
...
(if
  (/=
    (vla-get-stylename (vlax-ename->vla-object (ssname ss cnt)))
    [color="red"][color="red"]([/color][color="#ff00ff"]objstyle[/color][color="red"])[/color][/color] [color="green"];<-- no need parentheses [/color]
  )
...
...

 

FWIW, same logic

(if (/= ent302 nil) ...  ) 

(if ent302 ... ) [color="green"];if ent302 not nil[/color]

Link to comment
Share on other sites

Thank you so much for the help! I am still getting errors, even after making your suggested edits. So my new code is

(defun ssleader:ssgrab ()
 (setq obj      (vlax-ename->vla-object (ent))
       objstyle (vla-get-StyleName obj)
 )
 (setq ent8 (cdr (assoc 8 entlist)))
 (foreach x entlist
   (if
     (and
       (eq 302 (car x))
       (not
         (or (wcmatch "LEADER{" (cdr x)) (wcmatch "" (cdr x)))
       )
     )
      (setq ent302 (cdr x))
   )
 )
 (if ent302
   (progn
     (setq ss (ssget "_A"
                     (list '(0 . "MULTILEADER")
                           (cons 8 ent8)
                           (cons 302 ent302)
                     )
              )
     )
     (setq cnt 0)
     (repeat (sslength ss)
       (if
         (/=
           (vla-get-stylename (vlax-ename->vla-object (ssname ss cnt)))
           objstyle
         )
          (ssdel (ssname ss cnt) ss)
       )
       (setq cnt (+ cnt 1))
     )
     (if (sslength ss)
       (sssetfirst nil ss)
     )
     (setq whilestop nil)
   )
   (princ "\nMultileader attribute is blank.")
 )
)

Now it is breaking on the foreach statement, which seems weird to me.

Link to comment
Share on other sites

I have tried writing it slightly different, and I still can't get it to work. Am I doing something incorrectly with my ssdel function, or is it something else?

(setq MLeaderStyle (vla-get-stylename (vlax-ename->vla-object ent)))
(setq cnt 0)
(repeat (sslength ss)
  (if (/= (vla-get-stylename (vlax-ename->vla-object (ssname ss cnt))) MLeaderStyle)
       (ssdel (ssname ss cnt) ss)
    )
   (setq cnt (+ cnt 1))
)

Link to comment
Share on other sites

I have tried writing it slightly different, and I still can't get it to work. Am I doing something incorrectly with my ssdel function, or is it something else?
(setq MLeaderStyle (vla-get-stylename (vlax-ename->vla-object ent)))
(setq cnt 0)
(repeat (sslength ss)
  (if (/= (vla-get-stylename (vlax-ename->vla-object (ssname ss cnt))) MLeaderStyle)
       (ssdel (ssname ss cnt) ss)
    )
   (setq cnt (+ cnt 1))
)

 

You found a workaround, so just for information, yes there was something wrong with the way you used ssdel.

lets say you have the following selection set

index 0 object A mleader with correct style

index 1 object B circle

index 2 object C mleader with correct style

index 3 object D mleader with correct style

counter set to 0...

you retrieve index 0, object A mleader with correct style, leave it, increase the counter by one, counter is now set to 1...

you retrieve index 1, object B circle. You don't want it, and delete it from the selection set, increase the counter by one, counter is now set to 2...

After deleting an entry in the selection set, it now looks like that

index 0 object A mleader with correct style

index 1 object C mleader with correct style

index 2 object D mleader with correct style

You retrieve index 2, which is object D, increase the counter by one, counter is now set to 3... (problem #1, You stepped over object C)

You try to retrieve index 3 which no longer exists (problem #2)

 

You cannot iterate thru the selection and systematically increment the counter number... Wether

-You include the counter incrementation in the if statement (if you delete you dont increment, if you dont delete then you increment)

(repeat (sslength ss)
  (if (/= (vla-get-stylename (vlax-ename->vla-object (ssname ss cnt))) MLeaderStyle)
       (ssdel (ssname ss cnt) ss)
       (setq cnt (+ cnt 1)); <==
    )
   
)

OR you dont bother with that and just iterate thru the selection backward, like that

(setq MLeaderStyle (vla-get-stylename (vlax-ename->vla-object ent)))
(setq cnt (1- (sslength ss)));for a selection of 4 items you start at index 3
(repeat (sslength ss)
  (if (/= (vla-get-stylename (vlax-ename->vla-object (ssname ss cnt))) MLeaderStyle)
       (ssdel (ssname ss cnt) ss)
    )
   (setq cnt (1- cnt));and decrement counter
)

 

Late reply, but I hope you will learn something of this

Regards

Jef!

Link to comment
Share on other sites

You found a workaround, so just for information, yes there was something wrong with the way you used ssdel.

lets say you have the following selection set

 

counter set to 0...

you retrieve index 0, object A mleader with correct style, leave it, increase the counter by one, counter is now set to 1...

you retrieve index 1, object B circle. You don't want it, and delete it from the selection set, increase the counter by one, counter is now set to 2...

After deleting an entry in the selection set, it now looks like that

 

You retrieve index 2, which is object D, increase the counter by one, counter is now set to 3... (problem #1, You stepped over object C)

You try to retrieve index 3 which no longer exists (problem #2)

 

You cannot iterate thru the selection and systematically increment the counter number... Wether

-You include the counter incrementation in the if statement (if you delete you dont increment, if you dont delete then you increment)

(repeat (sslength ss)
  (if (/= (vla-get-stylename (vlax-ename->vla-object (ssname ss cnt))) MLeaderStyle)
       (ssdel (ssname ss cnt) ss)
       (setq cnt (+ cnt 1)); <==
    )

)

OR you dont bother with that and just iterate thru the selection backward, like that

(setq MLeaderStyle (vla-get-stylename (vlax-ename->vla-object ent)))
(setq cnt (1- (sslength ss)));for a selection of 4 items you start at index 3
(repeat (sslength ss)
  (if (/= (vla-get-stylename (vlax-ename->vla-object (ssname ss cnt))) MLeaderStyle)
       (ssdel (ssname ss cnt) ss)
    )
   (setq cnt (1- cnt));and decrement counter
)

 

Late reply, but I hope you will learn something of this

Regards

Jef!

Jef!, thank you so much for that information/tutorial. That logic makes a lot of sense in regards to when I need to actually step the counter. I made the same mistake coding an Excel spreadsheet a few weeks ago in regards to deleting rows, so I should have recognized it.
Link to comment
Share on other sites

Jef!, thank you so much for that information/tutorial. That logic makes a lot of sense in regards to when I need to actually step the counter. I made the same mistake coding an Excel spreadsheet a few weeks ago in regards to deleting rows, so I should have recognized it.

 

I try to avoid destructive approach (like ssdel) of selection sets (and lists as well) when I can, and if I have to do it I proceed backward. That way, ne matter if you remove or not the 4th entry of the index, the 3rd remains the 3rd. While I'm talking about 3rd, isn't there a saying that goes as follow "Third time's a charm!"? :D

I'm glad I could help!

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...