;;-------------------------=={  Batch Find & Replace  }==------------------------;;
;;                                                                               ;;
;;  Will Find and Replace multiple strings entered by the user within Text,      ;;
;;  MText, Block Attributes, MLeader Text, Table Text and Dimensions within the  ;;
;;  current drawing, all currently open drawings and/or a directory              ;;
;;  (and subdirectories) of drawings.                                            ;;
;;                                                                               ;;
;;  If the 'Block Definitions' option is selected, all instances of the          ;;
;;  aforementioned text objects residing within block definitions are included   ;;
;;  in the search domain.                                                        ;;
;;                                                                               ;;
;;  Furthermore the user may choose which objects to search, whether the find    ;;
;;  string is case sensitive, and whether to restrict the search to whole words  ;;
;;  only.                                                                        ;;
;;                                                                               ;;
;;                       ----------------------------------                      ;;
;;  Example:                                                                     ;;
;;  ------------                                                                 ;;
;;  Find:         "is"                                                           ;;
;;  Replace With: "be"                                                           ;;
;;  String:       "This Is a test"                                               ;;
;;                                                                               ;;
;;  |  [x] Match Case        |  [ ] Match Case         |                         ;;
;;  |  [ ] Whole words only  |  [ ] Whole words only   |                         ;;
;;  |  This Is a test        |  This Is a test         |                         ;;
;;  |  Thbe Is a test        |  Thbe be a test         |                         ;;
;;  |------------------------|-------------------------|                         ;;
;;  |  [x] Match Case        |  [ ] Match Case         |                         ;;
;;  |  [x] Whole words only  |  [x] Whole words only   |                         ;;
;;  |  This Is a test        |  This Is a test         |                         ;;
;;  |  This Is a test        |  This be a test         |                         ;;
;;                                                                               ;;
;;                       ----------------------------------                      ;;
;;                                                                               ;;
;;  The user can also choose to 'Ignore Locked Layers'; with this option set,    ;;
;;  all objects residing on Locked Layers are excluded from the search.          ;;
;;                                                                               ;;
;;  The 'Where to Search' panel offers the ability to perform the find & replace ;;
;;  operation througout the entire drawing, or restrict the search to model      ;;
;;  space or layout space only.                                                  ;;
;;                                                                               ;;
;;  If 'Search Only' is checked, drawings will be searched for each 'Find'       ;;
;;  string in the list, but no replacements will be made. Upon completion of     ;;
;;  the search, a CSV report will be displayed should a match be found.          ;;
;;                                                                               ;;
;;  When performing replacements, the creation of a 'Replacement Report' may be  ;;
;;  toggled using the relevant setting on the 'Options' dialog.                  ;;
;;                                                                               ;;
;;  Note that a Regen may be required to view changes to open drawings.          ;;
;;  This could be accomplished programmatically, but has been omitted for better ;;
;;  performance.                                                                 ;;
;;                                                                               ;;
;;-------------------------------------------------------------------------------;;
;;                                                                               ;;
;;  FUNCTION SYNTAX:  BFind                                                      ;;
;;                                                                               ;;
;;  Notes:                                                                       ;;
;;  ---------                                                                    ;;
;;  As expected, the known bug with ObjectDBX arises with regards to attribute   ;;
;;  alignment following modification. A sub-function has been included in an     ;;
;;  attempt to correct the change in position, however, a slight shift in        ;;
;;  position may be noticed. The attribute is realigned when the block is moved  ;;
;;  manually.                                                                    ;;
;;                                                                               ;;
;;  Also, when using the Batch Find and Replace, drawing thumbnails are lost     ;;
;;  upon saving with ObjectDBX. These return when the drawing is saved manually. ;;
;;                                                                               ;;
;;-------------------------------------------------------------------------------;;
;;                                                                               ;;
;;  Author: Lee Mac, Copyright ?2011 - www.lee-mac.com                          ;;
;;                                                                               ;;
;;-------------------------------------------------------------------------------;;
;;                                                                               ;;
;;  With additional thanks to Joe Burke for his tremendous help towards using    ;;
;;  Regular Expressions to eliminate the problem of MText formatting codes when  ;;
;;  performing replacements.                                                     ;;
;;                                                                               ;;
;;-------------------------------------------------------------------------------;;
;;  Version:                                                                     ;;
;;                                                                               ;;
;;  1.0:  24/05/2010  -  First Release                                           ;;
;;-------------------------------------------------------------------------------;;
;;  1.1:  26/05/2010  -  Added Options Button.                                   ;;
;;                    -  Updated String Replacement functions to allow for case  ;;
;;                       sensitivity options.                                    ;;
;;                    -  Upgraded StringRep function to allow replacement string ;;
;;                       to contain find string.                                 ;;
;;                    -  Fixed bug causing an unsaved drawing to be saved.       ;;
;;-------------------------------------------------------------------------------;;
;;  1.2:  29/05/2010  -  Added ability to perform multiple replacements.         ;;
;;-------------------------------------------------------------------------------;;
;;  1.3:  31/05/2010  -  Added ability to edit find and replace entry by         ;;
;;                       double-clicking.                                        ;;
;;                    -  Added 'Table Text' object to Options Dialog.            ;;
;;                    -  Added 'Find Whole Words Only' option to Options Dialog. ;;
;;                    -  Redesigned Options Dialog to accommodate new toggles,   ;;
;;                       added mnemonics.                                        ;;
;;-------------------------------------------------------------------------------;;
;;  1.4:  27/04/2010  -  Replaced 'StringRep' function with functions utilising  ;;
;;                       Regular Expressions to allow for MText formatting.      ;;
;;-------------------------------------------------------------------------------;;
;;  1.5:  29/09/2010  -  Reformatted code.                                       ;;
;;                    -  Drawings only saved if a replacement is made.           ;;
;;-------------------------------------------------------------------------------;;
;;  1.6:  03/10/2010  -  Added pick buttons to select text for Find/Replace      ;;
;;                       strings.                                                ;;
;;                    -  Added 'Match Case' option                               ;;
;;                    -  Added 'Find Whole Words Only' option                    ;;
;;-------------------------------------------------------------------------------;;
;;  1.7:  05/10/2010  -  Added 'Search Only' option in which the program will    ;;
;;                       search for multiple strings within the current drawing  ;;
;;                       and/or a directory (and subdirectories) of drawings,    ;;
;;                       subsequently producing a report detailing the number    ;;
;;                       of occurrences of each string and list of possible      ;;
;;                       changes should a replacement be performed.              ;;
;;                    -  Added error trap to drawing processing loop to prevent  ;;
;;                       crash should one drawing error.                         ;;
;;                    -  Added delay to DCL creation.                            ;;
;;-------------------------------------------------------------------------------;;
;;  1.8:  08/10/2010  -  Added Load & Save buttons to allow a set of search      ;;
;;                       items to be loaded and saved.                           ;;
;;-------------------------------------------------------------------------------;;
;;  1.9:  09/10/2010  -  Fixed bug involving capital 'S' being removed from      ;;
;;                       textstring during stacking formatting code removal.     ;;
;;                    -  Added code to allow 'special' characters to be used in  ;;
;;                       Find string.                                            ;;
;;-------------------------------------------------------------------------------;;
;;  2.0:  22/07/2011  -  Majority of program rewritten and reformatted to        ;;
;;                       include the following enhancements:                     ;;
;;                    -  Fixed tab stops in dialog to allow the user to use the  ;;
;;                       tab key to navigate between edit boxes.                 ;;
;;                    -  Added 'Where to Search' panel to allow the user to      ;;
;;                       restrict the search to Modelspace, Layout space or Both.;;
;;                    -  Added 'Block Definitions' to list of objects to search  ;;
;;                       to allow users to search text objects in blocks.        ;;
;;                    -  Added 'Current Directory' toggle to allow the user to   ;;
;;                       quickly select the current working directory to be      ;;
;;                       processed.                                              ;;
;;                    -  Added ability to add items to find/replace list by      ;;
;;                       pressing Enter from within either edit box.             ;;
;;                    -  Added ability to press Enter after entering Save        ;;
;;                       Reference without needing to click the OK button.       ;;
;;                    -  Added ability to press Enter after editing entry        ;;
;;                       without needing to click the OK button.                 ;;
;;                    -  Changed format of Saved Searches file for easier        ;;
;;                       manual editing.                                         ;;
;;                    -  Added ability to process 'All Open Drawings'.           ;;
;;-------------------------------------------------------------------------------;;

(defun c:LM_FIND

;;-------------------------------------------------------------------------------;;

  (
    /

    ;;         --=={ Local Functions }==--          ;
    tmp ref
    *error*
    _calcinspt
    _directorymode
    _directorytext
    _endundo
    _entryedit
    _formatdate
    _generatereport
    _getallfiles
    _gettextstring
    _list->value
    _loaddialog
    ;~ _locklayers
    _objectdbxdocument
    _openfile
    _optionsdialog
    _populatelistbox
    _readconfig
    _readsearchitems
    _regexexecute
    _regexreplace
    _releaseobject
    _removeitems
    _replace
    _replaceobject
    _replacetext
    _savedialog
    _sortbyfirst
    _startundo
    _value->list
    _writeconfig
    _writedcl
    _writesearchitems

    ;;         --=={ Local Variables }==--          ;

    *bfind_cur*
    *bfind_lst*
    *bfind_opn*
    *bfind_opt*
    *bfind_pat*
    *bfind_ser*
    *bfind_sub*
    *bfind_whr*
    *replaceflag*
    acapp
    acdoc
    bit
    cdir
    cfgfname
    dbdoc
    dcflag
    dcfname
    dir
    doc
    doclst
    dwg
    dwlst
    elist
    err
    express
    filepath
    filetype
    flag
    flags
    fn
    fo
    fold
    folder
    fs
    fstr
    i
    id
    items
    l
    lst
    mode
    msg
    ns
    obj
    os
    path
    progbar
    ptr
    rep
    reportlst
    res
    rname
    row
    rs
    rstr
    savelist
    savfname
    self
    shell
    str
    string
    sym
    symlist
    title
    tmp1
    tmp2
    val
    vallist
    where
    wsh

    ;;         --=={ Global Variables }==--         ;

    ; --None--

  )


;;-------------------------------------------------------------------------------;;
;;                           --=={  Local Functions  }==--                       ;;
;;-------------------------------------------------------------------------------;;

  (defun *error* ( msg )
    (if acdoc (_EndUndo acdoc))
    (if (< 0 id) (setq id (unload_dialog id)))
    (and fo (eq 'FILE (type fo)) (setq fo (close fo)))
    (and Express ProgBar (acet-ui-progress))
    (mapcar '_ReleaseObject (list acapp acdoc shell wsh dbdoc doc))
    (_ReleaseObject (vl-bb-ref '*REX*))
    (vl-bb-set '*REX* nil)
    (or (wcmatch (strcase msg) "*BREAK,*CANCEL*,*EXIT*")
        (princ (strcat "\n** Error: " msg " **"))
    )
    (princ)
  )

;;-------------------------------------------------------------------------------;;

  (defun _StartUndo ( doc ) (_EndUndo doc)
    (vla-StartUndoMark doc)
  )

;;-------------------------------------------------------------------------------;;

  (defun _EndUndo ( doc )
    (if (= 8 (logand 8 (getvar 'UNDOCTL)))
      (vla-EndUndoMark doc)
    )
  )



;;-------------------------------------------------------------------------------;;

  (defun _WriteConfig ( fn lst / fo x )
    (if (setq fo (open fn "w"))
      (progn
        (foreach x lst (write-line (vl-prin1-to-string x) fo))
        (setq fo (close fo))
        T
      )
    )
  )

;;-------------------------------------------------------------------------------;;

  (defun _ReadConfig ( fn lst / fo x )
    (and
        (setq fn (findfile fn))
        (setq fo (open fn "r"))
        (progn
            (foreach x lst (set x (read (read-line fo))))
            (setq fo (close fo))
            T
        )
    )
  )

;;-------------------------------------------------------------------------------;;

  (defun _ReleaseObject ( obj )
    (and obj (eq 'VLA-OBJECT (type obj)) (not (vlax-object-released-p obj))
      (not
        (vl-catch-all-error-p
          (vl-catch-all-apply 'vlax-release-object (list obj))
        )
      )
    )
  )

;;-------------------------------------------------------------------------------;;

  (defun _GetAllFiles ( Dir Subs Filetype / _GetSubFolders )

    (defun _GetSubFolders ( folder )
      (apply 'append
        (mapcar
            '(lambda ( f )
              (cons (setq f (strcat folder "\\" f)) (_GetSubFolders f))
            )
            (vl-remove "." (vl-remove ".." (vl-directory-files folder nil -1)))
        )
      )
    )

    (apply 'append
      (mapcar
         '(lambda ( Filepath )
            (mapcar
              '(lambda ( Filename ) (strcat Filepath "\\" Filename))
              (vl-directory-files Filepath Filetype 1)
            )
        )
        (cons Dir (if subs (_GetSubFolders Dir)))
      )
    )
  )

;;-------------------------------------------------------------------------------;;

  (defun _ObjectDBXDocument ( acapp / acVer )
    (vla-GetInterfaceObject acapp
      (if (< (setq acVer (atoi (getvar "ACADVER"))) 16)
        "ObjectDBX.AxDbDocument" (strcat "ObjectDBX.AxDbDocument." (itoa acVer))
      )
    )
  )

;;-------------------------------------------------------------------------------;;


;;-------------------------------------------------------------------------------;;

  (defun _WriteDCL ( fn / fo )
    (cond
      ( (findfile fn)
      )
      ( (setq fo (open fn "w"))
        (foreach str
         '(
            "//-------------------------=={  Batch Find & Replace  }==------------------------//"
            "//                                                                               //"
            "//  BFind.dcl to be used in conjunction with BFind.lsp.                          //"
            "//-------------------------------------------------------------------------------//"
            "//-------------------------------------------------------------------------------//"
            ""
            "//-------------------------------------------------------------------------------//"
            "//                        --=={ Sub-Assembly Definitions }==--                   //"
            "//-------------------------------------------------------------------------------//"
            ""
            "boxcol : boxed_column {  width =  60; fixed_width  = true; alignment = centered; }"
            "edit53 :     edit_box {  width =  53; fixed_width  = true; alignment = left;     }"
            "space1 :       spacer { height = 0.1; fixed_height = true;                       }"
            "butt10 :       button {  width =  10; fixed_width  = true; alignment = centered; }"
            "butt12 :       button {  width =  12; fixed_width  = true; alignment = centered; }"
            "butt20 :       button {  width =  20; fixed_width  = true; alignment = centered; }"
            "butt3  :       button {  width =   3; fixed_width  = true; alignment = centered; }"
            ""
            "//-------------------------------------------------------------------------------//"
            "//                         --=={ Main Dialog Definition }==--                    //"
            "//-------------------------------------------------------------------------------//"
            ""
            "bfind : dialog { key = \"dcl_title\";"
            ; "  spacer;"
            ; "  : text { label = \"Copyright (c) 2011 Lee Mac\"; alignment = right; }"
            "  : column {  "
            "    : text { label = \"&W 需替换的文字:\"; }"
            "    : row {"
            "      : edit53 { key = \"fstr\"; mnemonic = \"W\"; }"
            "      : butt3  { key = \"fpick\"; label = \"图中选文字>>\"; is_tab_stop = false; }"
            "    }"
            "    spacer;"
            "    : text { label = \"&R 替换为:\"; }"
            "    : row {"
            "      : edit53 { key = \"rstr\"; mnemonic = \"R\"; }"
            "      : butt3  { key = \"rpick\"; label = \"图中选文字>>\"; is_tab_stop = false; }"
            "    }"
            "    : toggle { key = \"search\"; label = \"Search Only\"; is_tab_stop = false; }"
            "    spacer;"
            "  }"
            "  spacer;"
            "  : row {"
            "    spacer;"
            "    : butt20 { key = \"add\"; label = \"增加\"; }"
            "    : butt10 { key = \"clr\"; label = \"全清\"; }"
            "    : butt20 { key = \"rem\"; label = \"去除\"; }"
            "    spacer;"
            "  }"
            "  spacer;"
            "  : list_box { key = \"rep_list\"; multiple_select = true;"
            "               fixed_width = false; alignment = centered; tabs = \"30\";"
            "  }"
            "  spacer;"
            "  : row { alignment = centered; fixed_width = true;"
            "    : butt20 { key = \"load\"; label = \"加载\"; }"
            "    : butt20 { key = \"save\"; label = \"保存\"; }"
            "  }"
            "  : text   { label = \"双击编辑实体  \"; alignment = right; }"
            "  : boxcol { label = \"图形所在文件夹\";"
            "    : row {"
            "      : column {"
            "        space1;"
            "        : text   { key = \"dir_text\"; alignment = left; }"
            "        space1;"
            "      }"
            "      : butt10 { label = \"选择文件夹...\"; key = \"dir\"; }"
            "    }"
            "    : row {"
            "      : column {"
            "        : toggle { key = \"cur_dir\"; label = \"当前文件夹\"      ; }"
            "        : toggle { key = \"sub_dir\"; label = \"包含所有子文件夹\"; }"
            "      }"
            "      : column {"
            "        : toggle { key = \"cur_dwg\"; label = \"仅当前图形\"   ; }"
            "        : toggle { key = \"opn_dwg\"; label = \"所有打开的图形\"      ; }"
            "      }"
            "    }"
            "  }"
            "  spacer;"
            "  : row {"
            "    : butt12 { key = \"option\"; label = \"设置\"; }"
            "    : butt12 { key = \"accept\"; label = \"确定\"; is_default = true; }"
            "    : butt12 { key = \"cancel\"; label = \"取消\"; is_cancel = true; }"
            ;"    : image  { key = \"logo\"; alignment = centered;"
            ;"               width = 16.06 ; fixed_width  = true;"
           ; "               height = 2.06 ; fixed_height = true; color = -15;"
            ;"    }"
            "  }"
            "}"
            ""
            "//-------------------------------------------------------------------------------//"
            "//                       --=={ Options Dialog Definition }==--                   //"
            "//-------------------------------------------------------------------------------//"
            ""
            "bfind_opt : dialog { key = \"dcl_title_opt\";"
            "  spacer;"
            "  : boxed_column { label = \"搜索选项\";"
            "    : row {"
            "      : toggle { key = \"case\"; label = \"&C 匹配大小写\"; mnemonic = \"C\"; }"
            "      : toggle { key = \"whol\"; label = \"&W 仅整句匹配\" ; mnemonic = \"W\"; }"
            "    }"
            "    : toggle { key = \"lock\"; label = \" 不处理在锁定图层的实体&I\" ; mnemonic = \"I\"; }"
            "    spacer;"
            "  }"
            "  spacer;"
            "  : boxed_column { label = \"要搜索的实体\";"
            "    : row {"
            "      : column {"
            "        : toggle { key = \"dtxt\"; label = \"&S 单行文字\" ; mnemonic = \"S\"; }"
            "        : toggle { key = \"mtxt\"; label = \"&M 多行文字\"   ; mnemonic = \"M\"; }"
            "        : toggle { key = \"att\" ; label = \"&B 图块属性\" ; mnemonic = \"B\"; }"
            "        : toggle { key = \"blk\" ; label = \"&l 属性定义\"; mnemonic = \"l\"; }"
            "      }"
            "      : column {"
            "        : toggle { key = \"dim\" ; label = \"&D 尺寸文字\"   ; mnemonic = \"D\"; }"
            "        : toggle { key = \"mld\" ; label = \"&U 多重引线文字\" ; mnemonic = \"u\"; }"
            "        : toggle { key = \"tab\" ; label = \"&T 表格文字\"  ; mnemonic = \"T\"; }"
            "        : spacer { height = 1.2; fixed_height = true; }"
            "      }"
            "    }"
            "    spacer;"
            "  }"
            "  spacer;"
            "  : boxed_column { label = \"生成记录选项\";"
            "    : toggle { key = \"report\"; label = \"&A 总是生成替换记录\"; mnemonic = \"A\"; }"
            "    spacer;"
            "  }"
            "  spacer;"
            "  : boxed_column { label = \"在哪里搜索\";"
            "    : popup_list { key = \"哪里\"; }"
            "    spacer;"
            "  }"
            "  spacer; ok_cancel;"
            "}"
            ""
            "//-------------------------------------------------------------------------------//"
            "//                   --=={ Entry Editing Dialog Definition }==--                 //"
            "//-------------------------------------------------------------------------------//"
            ""
            "bfind_edit : dialog { key = \"dcl_title_edi\";"
            "  spacer;"
            "  : column {"
            "    : text   { label = \"&W 查找的文字:\"; is_tab_stop = false; }"
            "    : edit53 { key = \"fstr\"; mnemonic = \"W\"; allow_accept = true; }"
            "    spacer;"
            "    : text   { label = \"&R用来替换的文字 :\"; is_tab_stop = false; }"
            "    : edit53 { key = \"rstr\"; mnemonic = \"R\"; allow_accept = true; }"
            "    spacer;"
            "  }"
            "  spacer; ok_cancel;"
            "}"
            ""
            "//-------------------------------------------------------------------------------//"
            "//                       --=={ Save Dialog Definition }==--                      //"
            "//-------------------------------------------------------------------------------//"
            ""
            "bfind_save : dialog { label = \"另存为\"; initial_focus = \"saveas\";"
            "  spacer;"
            "  : text { label = \"Enter Reference for Search Items List:\"; }"
            "  : edit53 { key = \"saveas\"; allow_accept = true; }"
            "  spacer; ok_cancel;"
            "}"
            ""
            "//-------------------------------------------------------------------------------//"
            "//                        --=={ Load Dialog Definition }==--                     //"
            "//-------------------------------------------------------------------------------//"
            ""
            "bfind_load : dialog { label = \"加载搜索条目\";"
            "  spacer;"
            "  : list_box { key = \"items\"; alignment = centered; fixed_width =true;"
            "               multiple_select = false; width = 50;"
            "  }"
            "  spacer;"
            "  : row { fixed_width = true; alignment = centered;"
            "    : butt12 { label = \"加载\"; key = \"accept\"; is_default = true; }"
            "    : butt12 { label = \"取消\"; key = \"cancel\"; is_cancel  = true; }"
            "    : butt12 { label = \"删除\"; key = \"delete\"; }"
            "  }"
            "}"
            "//                           End of Dialog Definition                            //"
          )
          (write-line str fo)
        )
        (setq fo (close fo))
        (while (not (findfile fn)))
        fn
      )
    )
  )

;;-------------------------------------------------------------------------------;;


  (defun _DirectoryText ( key str )
    (set_tile key
      (if str
        (if (< 40 (strlen str))
			str
            (strcat (substr str 1 37) "...")
        )
        ""
      )
    )
  )

;;-------------------------------------------------------------------------------;;

  (defun _DirectoryMode ( cur opn / val box x )
    (setq val (if (or (eq "1" cur) (eq "1" opn)) 1 0)
          box (cond ( (eq "1" val) '("opn_dwg" "cur_dir" "sub_dir" "dir" "dir_text"))
                    ( (eq "1" opn) '("cur_dwg" "cur_dir" "sub_dir" "dir" "dir_text"))
                    ('("opn_dwg" "cur_dwg" "cur_dir" "sub_dir" "dir" "dir_text"))
              )
    )
    (foreach x box (mode_tile x val))
  )

;;-------------------------------------------------------------------------------;;

  (defun _SortByFirst ( lst )
    (vl-sort lst '(lambda ( a b ) (< (car a) (car b))))
  )

;;-------------------------------------------------------------------------------;;

  (defun _RemoveItems ( items lst / i )
    (setq i -1)
    (vl-remove-if '(lambda ( x ) (vl-position (setq i (1+ i)) items)) lst)
  )

;;-------------------------------------------------------------------------------;;

  (defun _PopulateListBox ( key lst / x )
    (start_list key)
    (foreach x lst
      (add_list
        (strcat
          (if (< 29 (strlen (car x)))
              (strcat (substr (car x) 1 26) "...")
              (car x)
          )
          "\t" (cdr x)
        )
      )
    )
    (end_list)
    lst
  )

;;-------------------------------------------------------------------------------;;

  (defun _GetTextString ( entity )
    (
      (lambda ( entity / _type elist )
        (setq elist (entget entity)
              _type (mai_dxf 0 elist)
        )
        (cond
          ( (wcmatch _type "TEXT,*DIMENSION")
            (mai_dxf 1 (reverse elist))
          )
          ( (eq _type "MULTILEADER")
            (mai_dxf 304 elist)
          )
          ( (wcmatch _type "ATTRIB,MTEXT")
            (
              (lambda ( string )
                (mapcar
                   '(lambda ( pair )
                      (if (member (car pair) '(1 3))
                          (setq string (strcat string (cdr pair)))
                      )
                    )
                    elist
                )
                string
              )
              ""
            )
          )
        )
      )
      (if (eq 'VLA-OBJECT (type entity))
          (vlax-vla-object->ename entity)
          entity
      )
    )
  )

;;-------------------------------------------------------------------------------;;

  (defun _CalcInsPt ( obj str / e a )
    (setq e (entget (vlax-vla-object->ename obj))
          a (mai_dxf 72 e)
    )
    (polar
      (vlax-get obj 'InsertionPoint)
      (vlax-get obj 'Rotation)
      (*
        (apply '+
          (mapcar
            '(lambda ( e1 e2 ) (- (car e1) (car e2)))
            (textbox e)
            (textbox (subst (cons 1 str) (assoc 1 e) e))
          )
        )
        (cond
          ( (= a 2) 1.0)
          ( (or (= a 1) (= a 4)) 0.5)
          ( 0.0 )
        )
      )
    )
  )

;;-------------------------------------------------------------------------------;;

  ;~ (defun _UnlockLayers ( doc / lst )
    ;~ (vlax-for l (vla-get-layers doc)
      ;~ (if (eq :vlax-true (vla-get-lock l))
        ;~ (vla-put-lock (car (setq lst (cons l lst))) :vlax-false)
      ;~ )
    ;~ )
    ;~ lst
  ;~ )

;;-------------------------------------------------------------------------------;;

  ;~ (defun _LockLayers ( lst )
    ;~ (foreach l lst (vla-put-lock l :vlax-true))
  ;~ )

;;-------------------------------------------------------------------------------;;

  (defun _OptionsDialog ( id mode where / tmp1 tmp2 i x )
    (cond
      ( (not (new_dialog "bfind_opt" id))
        (mai_dos_Popup "注意：" 16 "批量替换对话框无法被加载")
        (princ "\n** 批量替换对话框无法被加载 **")
      )
      ( t
        (set_tile "dcl_title_opt" "选项")
        (mai_filldcl_list "where"
         '(
            "全图搜索";"Entire Drawing"
            "仅限模型空间";"Model Space Only"
            "仅限布局空间";"Layout Space Only"
          )
        )
        (setq tmp1 mode
              tmp2 where
              i 1
        )
        (foreach x '("case" "lock" "whol" "dtxt" "mtxt" "att" "dim" "mld" "tab" "report" "blk")
          (set_tile x (if (= i (logand i tmp1)) "1" "0"))
          (action_tile x (strcat "(setq tmp1 ((if (eq \"1\" $value) + -) tmp1 " (itoa i) "))"))
          (setq i (lsh i 1))
        )
        (set_tile "where" tmp2)
        (action_tile "where"  "(setq tmp2 $value)")
        (action_tile "accept" "(setq mode tmp1 where tmp2) (done_dialog)")
        (action_tile "cancel" "(done_dialog)")
        (start_dialog)
      )
    )
    (list mode where)
  )

;;-------------------------------------------------------------------------------;;

  (defun _EntryEdit ( id entry / fs rs )
    (cond
      ( (not (new_dialog "bfind_edit" id))
        (mai_dos_Popup "注意：" 16 "批量替换对话框无法被加载")
        (princ "\n** 批量替换对话框无法被加载 **")
      )
      ( t
        (set_tile "dcl_title_edi" "编辑实体")
        (mapcar 'set '(fs rs) (mapcar 'set_tile '("fstr" "rstr") (list (car entry) (cdr entry))))
        (mode_tile "fstr" 2)
        (action_tile "fstr" "(setq fs $value)")
        (action_tile "rstr" "(setq rs $value)")
        (action_tile "accept"
          (vl-prin1-to-string
            (quote
              (cond
                ( (or (not fs) (eq "" fs))
                  (mai_dos_Popup "注意：" 64 "输入需要查找的文字")
                )
                ( (setq entry (cons fs rs))
                  (done_dialog)
                )
              )
            )
          )
        )
        (action_tile "cancel" "(done_dialog)")
        (start_dialog)
      )
    )
    entry
  )

;;-------------------------------------------------------------------------------;;

  (defun _GenerateReport ( fn data / fo line )
    (cond
      ( (setq fo (open fn "a"))
        (write-line
          (strcat "Replacement Report,"
            (_FormatDate "DD.MO.YYYY HH:MM") (if (eq "1" *BFind_ser*) ",SEARCH ONLY" "")
          )
          fo
        )
        (write-line "" fo)
        (foreach l (cons '("Search String" . "Replace String") *BFind_lst*)
          (write-line (strcat (car l) "," (cdr l)) fo)
        )
        (write-line "" fo)
        (write-line (strcat "Parent Directory," *BFind_pat*) fo)
        (write-line "" fo)
        (write-line
          (strcat "Drawing,Old String,New String,Object,Handle"
            (if (vl-some '(lambda ( x ) (= 6 (length x))) data) ",TagString" "")
          )
          fo
        )
        (foreach line data (write-line (mai_lst2str line ",") fo))
        (write-line "" fo)
        (write-line (strcat "Total Replacements:," (itoa (length data))) fo)
        (write-line "" fo)
        (setq fo (close fo))
        T
      )
    )
  )

;;-------------------------------------------------------------------------------;;

  (defun _OpenFile ( fn / shell result )
    (setq result
      (vl-catch-all-apply
        (function
          (lambda nil
            (setq shell (vla-getinterfaceobject (vlax-get-acad-object) "Shell.Application"))
            (vlax-invoke shell 'open fn)
          )
        )
      )
    )
    (if shell (vlax-release-object shell))
    (not (vl-catch-all-error-p result))
  )


;;-------------------------------------------------------------------------------;;

  (defun _FormatDate ( format )
    (menucmd (strcat "m=$(edtime,$(getvar,DATE)," format ")"))
  )

;;-------------------------------------------------------------------------------;;

  (defun _value->list ( ptr ) (read (strcat "(" ptr ")")))

;;-------------------------------------------------------------------------------;;

  (defun _list->value ( lst ) (vl-string-trim "()" (vl-princ-to-string lst)))

;;-------------------------------------------------------------------------------;;


  (defun _WriteSearchItems ( fn lst / fo ref item )
    (if (setq fo (open fn "w"))
      (progn
        (foreach ref lst
          (write-line (strcat "[" (car ref) "]") fo)
          (foreach item (cdr ref)
            (write-line (strcat (car item) "\t" (cdr item)) fo)
          )
          (write-line "" fo)
        )
        (setq fo (close fo))
        T
      )
    )
  )

;;-------------------------------------------------------------------------------;;

  (defun _ReadSearchItems ( fn / fo l1 l2 ln ps )
    (if
      (and
        (setq fn (findfile fn))
        (setq fo (open fn "r"))
      )
      (progn
        (while (setq ln (read-line fo))
          (cond
            ( (and (wcmatch ln "`[*`]") (not (wcmatch ln "*\t*")))
              (if l2
                (setq l1 (cons (reverse l2) l1))
              )
              (setq l2 (list (substr ln 2 (- (strlen ln) 2))))
            )
            ( (wcmatch ln "*\t*")
              (setq ps (vl-string-position 9 ln)
                    l2 (cons (cons (substr ln 1 ps) (substr ln (+ ps 2))) l2)
              )
            )
          )
        )
        (setq fo (close fo))
        (if l2
            (setq l1 (cons (reverse l2) l1))
        )
        (reverse l1)
      )
    )
  )

;;-------------------------------------------------------------------------------;;

  (defun _SaveDialog ( id lst / str )
    (cond
      ( (not (new_dialog "bfind_save" id))
        (mai_dos_Popup "注意：" 16 "保存的对话框无法被加载")
        (princ "\n** 保存的对话框无法被加载 **")
      )
      ( t
        (action_tile "saveas" "(setq str $value)")
        (action_tile "accept"
          (vl-prin1-to-string
            (quote
              (cond
                ( (or (not str) (eq "" str))
                  (mai_dos_Popup "注意：" 64 "输入保存的文件")
                )
                ( (member str lst)
                  (if (= 6 (mai_dos_Popup "注意：" 68 "输入保存的文件已存在，是否覆盖?"))
                    (done_dialog)
                  )
                )
                ( (done_dialog) )
              )
            )
          )
        )
        (action_tile "cancel" "(setq str nil) (done_dialog)")
        (start_dialog)
      )
    )
    str
  )

;;-------------------------------------------------------------------------------;;

  (defun _LoadDialog ( id lst / ptr item )
    (cond
      ( (not (new_dialog "bfind_load" id))
        (mai_dos_Popup "注意：" 16 "对话框无法被加载")
        (princ "\n** 对话框无法被加载 **")
      )
      ( t
        (mai_filldcl_list "items" (setq lst (acad_strlsort lst)))
        (setq ptr (set_tile "items" "0"))

        (action_tile "items" "(setq ptr $value)")
        (action_tile "delete"
          (vl-prin1-to-string
            (quote
              (progn
                (cond
                  ( (not ptr)
                    (mai_dos_Popup "注意：" 48 "选择需要删除的条目")
                  )
                  ( (= 6
                      (mai_dos_Popup "注意：" 68
                        (strcat "关于删除已保存的搜索条目: '"
                          (nth (atoi ptr) lst) "'\n这个过程无法回退.\n\n是否继续?"
                        )
                      )
                    )
                    (mai_filldcl_list "items" (setq lst (_RemoveItems (list (atoi ptr)) lst)))
                    (if lst
                      (setq ptr
                        (set_tile "items"
                          (if (< (atoi ptr) (length lst)) ptr "0")
                        )
                      )
                      (mapcar 'mode_tile '("delete" "accept") '(1 1))
                    )
                  )
                )
              )
            )
          )
        )
        (action_tile "accept"
          (vl-prin1-to-string
            (quote
              (progn
                (cond
                  ( (not ptr)
                    (mai_dos_Popup "注意：" 48 "选择需要加载的搜索条目")
                  )
                  ( t
                    (setq item (nth (atoi ptr) lst))
                    (done_dialog)
                  )
                )
              )
            )
          )
        )
        (start_dialog)
      )
    )
    (list item lst)
  )

;;-------------------------------------------------------------------------------;;
;;                     --=={  String Replacement Engine  }==--                   ;;
;;-------------------------------------------------------------------------------;;

  (or (vl-bb-ref '*REX*)
      (vl-bb-set '*REX* (vlax-create-object "VBScript.RegExp"))
  )

;;-------------------------------------------------------------------------------;;

  (defun _RegExReplace ( newstr pat string )
    (vlax-put    (vl-bb-ref '*REX*) 'Pattern pat)
    (vlax-put    (vl-bb-ref '*REX*) 'Global actrue)
    (vlax-put    (vl-bb-ref '*REX*) 'IgnoreCase acfalse)
    (vlax-invoke (vl-bb-ref '*REX*) 'Replace string newstr)
  )

;;-------------------------------------------------------------------------------;;

  (defun _RegExExecute ( pat string case / lst x )
    (vlax-put    (vl-bb-ref '*REX*) 'Pattern pat)
    (vlax-put    (vl-bb-ref '*REX*) 'Global actrue)
    (vlax-put    (vl-bb-ref '*REX*) 'IgnoreCase (if case acfalse actrue))
    (vlax-for x  (vlax-invoke (vl-bb-ref '*REX*) 'Execute string)
      (setq lst
        (cons (list (vlax-get x 'Value) (vlax-get x 'FirstIndex)) lst)
      )
    )
    lst
  )

;;-------------------------------------------------------------------------------;;

  (defun _ReplaceText ( newstr oldstr str case whole / x patterm origstr oldlst lst s offset nlst res xstr replst origlen slashflag lbrace rbrace )

    ;; Temporarily replace literal backslashes with a unique string.
    ;; Literal backslashes are restored at end of function. By Steve Doman.
    (setq slashflag (strcat "<"  (substr (rtos (getvar "CDATE") 2 8) 14)  ">"))
    (setq str (_RegExReplace slashflag "\\\\\\\\" str))

    ;; Temporarily replace literal left curly brace.
    (setq lbrace    (strcat "<L" (substr (rtos (getvar "CDATE") 2 8) 14)  ">"))
    (setq str (_RegExReplace lbrace "\\\\{" str))

    ;; Temporarily replace literal right curly brace.
    (setq rbrace    (strcat "<"  (substr (rtos (getvar "CDATE") 2 8) 14) "R>"))
    (setq str (_RegExReplace rbrace "\\\\}" str))

    ;; Replace linefeeds using this format "\n" with the AutoCAD
    ;; standard format "\P". The "\n" format occurs when text is
    ;; copied to ACAD from some other application.

    (setq str  (_RegExReplace "\\P" "\\n" str) origstr str
          nlst (vl-string->list oldstr)
    )

    (foreach x (cdr (reverse nlst))
      (setq oldlst (vl-list* x 208 42 oldlst))
    )

    (setq oldlst (append oldlst (list (last nlst)))
          oldstr (vl-list->string oldlst)
    )

    ;; oblique formatting in an mtext object
    (setq lst (_RegExExecute "\\\\Q[-]?[0-9]*?[.]?[0-9]+;" str t)
          ;; height
          lst (append lst (_RegExExecute "\\\\H[0-9]?[.]?[0-9]+x;" str t))
          ;; color
          lst (append lst (_RegExExecute "\\\\[Cc][0-9]?[.]?[0-9]+;" str t))
          ;; alignment
          lst (append lst (_RegExExecute "\\\\A[012];" str t))
          ;; font
          lst (append lst (_RegExExecute  "\\\\[Ff].*?;" str t))
          ;; overline
          lst (append lst (_RegExExecute "\\\\[Oo]" str t))
          ;; underline
          lst (append lst (_RegExExecute "\\\\[Ll]" str t))
          ;; width
          lst (append lst (_RegExExecute "\\\\W[0-9]?[.]?[0-9]+;" str t))
          ;; tracking
          lst (append lst (_RegExExecute "\\\\T[0-9]?[.]?[0-9]+;" str t))
          ;; stacking
          lst (append lst (_RegExExecute "\\\\S.*?;" str t))
          ;; linefeed
          lst (append lst (_RegExExecute "\\\\P" str t))
          ;; paragraph
          lst (append lst (_RegExExecute "\\\\p.*?;" str t))
          ;; tabs
          ;; from StripFormat
          ;; lst (append lst (_RegExExecute "\\\\P\\t|[0-9]+;\\t" str))
          lst (append lst (_RegExExecute "\\\\P\\t|[0-9]+;\\t|\\t" str t))
          ;; underline and overline formatting in a text object
          lst (append lst (_RegExExecute "%%[uUoO]" str t))
    )

    ;; Replace formatting codes with (chr 208) within str.
    (foreach x lst
      (setq str (vl-string-subst (_RegExReplace (chr 208) "(.)" (car x)) (car x) str (cadr x)))
    )

    ;; Replace curly braces with (chr 208) within str.
    (setq str (_RegExReplace (chr 208) "{|}" str))

    (foreach pattern '("\\\\" "\\^" "\\$" "\\+" "\\?" "\\." "\\(" "\\)" "\\|" "\\{" "\\}" "\\," "\\[" "\\]")
      (setq oldstr (_RegExReplace pattern pattern oldstr))
    )
    (setq oldstr (_RegExReplace "$1\\*" "([^衇|^)\\*" oldstr))

    (foreach x (reverse (_RegExExecute (if whole (strcat "\\b" oldstr "\\b") oldstr) str case))
      (setq xstr   (substr origstr (1+ (cadr x)) (strlen (car x)))
            replst (cons (list xstr (cadr x)) replst)
      )
    )

    (setq origlen (strlen origstr)
          offset  0
    )
    (foreach x (reverse replst)
      (setq *ReplaceFlag* T
            res (vl-string-subst newstr (car x) origstr (+ offset (cadr x)))
            origstr res
            offset (- (strlen res) origlen)
      )
    )
    (or res (setq res origstr))
    (setq res (_RegExReplace "\\\\" slashflag res)
          res (_RegExReplace "\\{"     lbrace res)
          res (_RegExReplace "\\}"     rbrace res)
    )
  )

;;-------------------------------------------------------------------------------;;

  (defun _Replace ( replacements string case whole / x )
    (foreach x replacements
      (setq string (_ReplaceText (cdr x) (car x) string case whole))
    )
    string
  )

;;-------------------------------------------------------------------------------;;

  (defun _ReplaceObject ( dwg obj lst bit lkl rep / nme os ns )
    (setq nme (vla-get-Objectname obj))
    (cond
      (
        (not
          (and (= 2 (logand 2 bit))
            (vl-position (strcase (vla-get-layer obj)) lkl)
          )
        )
        (cond
          (
            (and
              (eq "AcDbBlockReference" nme)
              (eq :vlax-true (vla-get-HasAttributes obj))
              (= 32 (logand 32 bit))
            )
            (foreach att (vlax-invoke obj 'GetAttributes)
                (setq os (_GetTextString att)
                      ns (_Replace lst os (= 1 (logand 1 bit)) (= 4 (logand 4 bit)))
                )
              (or
                (eq os ns)
                (progn
                  (setq ReportLst
                    (cons
                      (list dwg os ns "Attribute" (vl-prin1-to-string (vla-get-handle att)) (vla-get-tagstring att))
                      ReportLst
                    )
                  )
                  (if rep
                    (progn
                      (vla-put-insertionpoint att (vlax-3d-point (_calcinspt att ns)))
                      (vla-put-textstring att ns)
                    )
                  )
                )
              )
            )
          )
          (
            (and
              (wcmatch (strcase nme) "*DIMENSION*")
              (= 64 (logand 64 bit))
            )
             (and
                (not (eq "" (setq os (_GetTextString obj))))
                (not (eq os (setq ns (_Replace lst os (= 1 (logand 1 bit)) (= 4 (logand 4 bit))))))
              (progn
                (setq ReportLst
                  (cons
                    (list dwg os ns "Dimension" (vl-prin1-to-string (vla-get-Handle obj)))
                    ReportLst
                  )
                )
                (if rep (vla-put-TextOverride obj ns))
              )
            )
          )
          (
            (or
              (and
                (eq "AcDbMText" nme)
                (= 16 (logand 16 bit))
              )
              (and
                (eq "AcDbText" nme)
                (= 8 (logand 8 bit))
              )
              (and
                (eq "AcDbMLeader" nme)
                (= 128 (logand 128 bit))
              )
            )
            (if
              (not
                (eq
                  (setq os (_GetTextString obj))
                  (setq ns (_Replace lst os (= 1 (logand 1 bit)) (= 4 (logand 4 bit))))
                )
              )
              (progn
                (setq ReportLst
                  (cons
                    (list dwg os ns
                      (cdr
                        (assoc nme
                         '(
                            ("AcDbMText"   . "MText")
                            ("AcDbText"    . "Text")
                            ("AcDbMLeader" . "Multileader")
                          )
                        )
                      )
                      (vl-prin1-to-string (vla-get-Handle obj))
                    )
                    ReportLst
                  )
                )
                (if rep (vla-put-TextString obj ns))
              )
            )
          )
          (
            (and
              (eq "AcDbTable" nme)
              (= 256 (logand 256 bit))
            )
            (
              (lambda ( row )
                (while (not (minusp (setq row (1- row))))
                  (
                    (lambda ( column )
                      (while (not (minusp (setq column (1- column))))
                        (if
                          (not
                            (eq
                              (setq os (vla-GetText obj row column))
                              (setq ns (_Replace lst os (= 1 (logand 1 bit)) (= 4 (logand 4 bit))))
                            )
                          )
                          (progn
                            (setq ReportLst
                              (cons
                                (list dwg os ns "Table" (vl-prin1-to-string (vla-get-Handle obj)))
                                ReportLst
                              )
                            )
                            (if rep (vla-SetText obj row column ns))
                          )
                        )
                      )
                    )
                    (vla-get-Columns obj)
                  )
                )
              )
              (vla-get-Rows obj)
            )
          )
        )
      )
    )
  )

;;-------------------------------------------------------------------------------;;
;;                           --=={  Preliminaries  }==--                         ;;
;;-------------------------------------------------------------------------------;;

  (setq dcfname    (strcat mai_mini_path "用户设置\\LMAC_BFind.dcl")
        cfgfname   (strcat mai_mini_path "用户设置\\LMAC_BFind.cfg")
        savfname   (strcat mai_mini_path "用户设置\\LMAC_BFind_SavedSearches.txt")
        acapp (vlax-get-acad-object)
        acdoc (vla-get-ActiveDocument acapp)
        cdir  (vl-string-right-trim "\\" (getvar 'DWGPREFIX))
  )

  (vlax-for doc (vla-get-documents acapp)
    (setq doclst
      (cons
        (cons
          (strcase
            (if (eq "" (vla-get-fullname doc))
              (strcat (vl-string-right-trim "\\" (vla-get-path doc)) "\\" (vla-get-name doc))
              (vla-get-fullname doc)
            )
          )
          doc
        )
        doclst
      )
    )
  )

  (setq Express
    (and (vl-position "acetutil.arx" (arx))
         (not (vl-catch-all-error-p (vl-catch-all-apply '(lambda nil (acet-sys-shift-down)))))
    )
  )

;;-------------------------------------------------------------------------------;;
;;                           --=={  Main Function  }==--                         ;;
;;-------------------------------------------------------------------------------;;

  ;; Option Bit-Codes

  ;  Match Case            =    1
  ;  Ignore Locked Layers  =    2
  ;  Whole Words Only      =    4
  ;  Single-line Text      =    8
  ;  Multiline Text        =   16
  ;  Block Attributes      =   32
  ;  Dimension Text        =   64
  ;  Multileader Text      =  128
  ;  Table Text            =  256
  ;  Generate Report       =  512
  ;  Block Definitions     = 1024

;;-------------------------------------------------------------------------------;;
;;                          --=={  Setup Defaults  }==--                         ;;
;;-------------------------------------------------------------------------------;;

  (setq SymList '(*BFind_lst* *BFind_pat* *BFind_cur* *BFind_opn* *BFind_sub* *BFind_opt* *BFind_ser* *BFind_whr*)
        ValList  (list 'nil cdir "0" "0" "1" (+ 1 8 16 32 64 128 256 512) "0" "0")
  )

  (or (findfile cfgfname)
      (_WriteConfig cfgfname ValList)
  )
  (_ReadConfig cfgfname SymList)

  (mapcar '(lambda ( sym val ) (or (boundp sym) (set sym val))) SymList ValList)

  (setq SaveList (_ReadSearchItems savfname))

;;-------------------------------------------------------------------------------;;

  (cond
    ( (not (_WriteDCL dcfname))
      (mai_dos_Popup "注意：" 16 "无法写出DCL对话框定义文件")
      (princ "\n** 无法写出DCL对话框定义文件 **")
    )
    ( (<= (setq id (load_dialog dcfname)) 0)
      (mai_dos_Popup "注意：" 16 "没找到对话框定义文件")
      (princ "\n**  没找到DCL对话框定义文件 **")
    )
    ( t
      (while (not (member dcFlag '(1 0)))
        (cond
          ( (not (new_dialog "bfind" id))
            (mai_dos_Popup "注意：" 16 "批量查找文字对话框无法被加载")
            (princ "\n** DCL could not be Loaded **")
            (setq dcFlag 0)
          )
          ( t

            (if fstr (set_tile "fstr" fstr))
            (if rstr (set_tile "rstr" rstr))

            (set_tile  "dcl_title"  "批量查找和替换")
            (set_tile  "sub_dir"    *BFind_sub*)
            (set_tile  "search"     *BFind_ser*)


            (_DirectoryText  "dir_text"
              (setq *BFind_pat*
                (vl-string-right-trim "\\"
                  (if (vl-file-directory-p *BFind_pat*) *BFind_pat* cdir)
                )
              )
            )
            (_DirectoryMode (set_tile "cur_dwg" *BFind_cur*) (set_tile "opn_dwg" *BFind_opn*))

            (if (eq (strcase *BFind_pat*) (strcase cdir))
              (set_tile "cur_dir" "1")
            )
            (_PopulateListBox "rep_list" *BFind_lst*)

            (action_tile "cur_dir"
              (vl-prin1-to-string
                (quote
                  (if (= "1" $value)
                    (_DirectoryText "dir_text" (setq *BFind_pat* cdir))
                  )
                )
              )
            )

            (action_tile "cur_dwg"
              (vl-prin1-to-string
                (quote
                  (progn
                    (if (eq "1" (setq *BFind_cur* $value))
                      (set_tile "opn_dwg" (setq *BFind_opn* "0"))
                    )
                    (_DirectoryMode *BFind_cur* *BFind_opn*)
                  )
                )
              )
            )

            (action_tile "opn_dwg"
              (vl-prin1-to-string
                (quote
                  (progn
                    (if (eq "1" (setq *BFind_opn* $value))
                      (set_tile "cur_dwg" (setq *BFind_cur* "0"))
                    )
                    (_DirectoryMode *BFind_cur* *BFind_opn*)
                  )
                )
              )
            )

            (action_tile "dir"
              (vl-prin1-to-string
                (quote
                  (progn
                    (if (setq tmp (mai_dos_getdir "多选要处理的dwg文件..." nil))
                      (progn
                        (_DirectoryText "dir_text" (setq *BFind_pat* tmp))
                        (set_tile "cur_dir" (if (eq (strcase *BFind_pat*) (strcase cdir)) "1" "0"))
                      )
                    )
                  )
                )
              )
            )

            (action_tile "sub_dir" "(setq *BFind_sub* $value)")

            (action_tile "fstr"
              (vl-prin1-to-string
                (quote
                  (progn
                    (setq fstr $value)
                    (if (= 1 $reason)
                      (cond
                        ( (or (not fstr) (eq "" fstr))
                          (mai_dos_Popup "注意：" 64 "需要输入查找的文字")
                        )
                        ( (or rstr (setq rstr ""))
                          (mapcar 'set_tile '("fstr" "rstr") '("" ""))
                          (setq *BFind_lst* (_PopulateListBox "rep_list" (_SortByFirst (cons (cons fstr rstr) *BFind_lst*)))
                                 fstr nil
                                 rstr nil
                          )
                        )
                      )
                    )
                  )
                )
              )
            )

            (action_tile "rstr"
              (vl-prin1-to-string
                (quote
                  (progn
                    (setq rstr $value)
                    (if (= 1 $reason)
                      (cond
                        ( (or (not fstr) (eq "" fstr))
                          (mai_dos_Popup "注意：" 64 "输入要查找的文字")
                        )
                        ( (or rstr (setq rstr ""))
                          (mapcar 'set_tile '("fstr" "rstr") '("" ""))
                          (setq *BFind_lst* (_PopulateListBox "rep_list" (_SortByFirst (cons (cons fstr rstr) *BFind_lst*)))
                                fstr nil
                                rstr nil
                          )
                        )
                      )
                    )
                  )
                )
              )
            )

            (action_tile "search"  "(setq *BFind_ser* $value)")
            (action_tile "option"
              (vl-prin1-to-string
                (quote
                  (setq tmp (_OptionsDialog id *BFind_opt* *BFind_whr*)
                        *BFind_opt* (car  tmp)
                        *BFind_whr* (cadr tmp)
                  )
                )
              )
            )

            (action_tile "add"
              (vl-prin1-to-string
                (quote
                  (progn
                    (cond
                      ( (or (not fstr) (eq "" fstr))
                        (mai_dos_Popup "注意：" 64 "输入要查找的文字")
                      )
                      ( (or rstr (setq rstr ""))
                        (mapcar 'set_tile '("fstr" "rstr") '("" ""))
                        (setq *BFind_lst* (_PopulateListBox "rep_list" (_SortByFirst (cons (cons fstr rstr) *BFind_lst*)))
                              fstr nil
                              rstr nil
                        )
                      )
                    )
                  )
                )
              )
            )

            (action_tile "rem"
              (vl-prin1-to-string
                (quote
                  (progn
                    (if *BFind_lst*
                        (if (and ptr (listp (setq items (_value->list ptr))))
                            (setq *BFind_lst* (_PopulateListBox "rep_list" (_SortByFirst (_RemoveItems items *BFind_lst*)))
                                   ptr nil
                            )
                            (mai_dos_Popup "注意：" 48 "在列表中选择需要去除的图元")
                        )
                        (mai_dos_Popup "注意：" 64 "没有找到需要去除的图元")
                    )
                  )
                )
              )
            )

            (action_tile "rep_list"
              (vl-prin1-to-string
                (quote
                  (progn
                    (setq ptr $value)
                    (and (= 4 $reason)
                         (setq ptr (car (_value->list ptr)))
                          (progn
                            (setq *BFind_lst* (_SortByFirst (mai_replace_nth *BFind_lst* ptr (_EntryEdit id (nth ptr *BFind_lst*)))))
                            (_PopulateListBox "rep_list" *BFind_lst*)
                            (set_tile "rep_list" (setq ptr (itoa ptr)))
                          )
                    )
                  )
                )
              )
            )

            (action_tile "clr" "(_PopulateListBox \"rep_list\" (setq *BFind_lst* nil))")

            (action_tile "save"
              (vl-prin1-to-string
                (quote
                  (progn
                    (cond
                      ( (null *BFind_lst*)
                        (mai_dos_Popup "注意：" 48 "输入需要保存的搜索条目")
                      )
                      ( (setq ref (_SaveDialog id (mapcar 'car SaveList)))
                        (setq SaveList
                             (if (assoc ref SaveList)
                                 (subst (cons ref *BFind_lst*) (assoc ref SaveList) SaveList)
                                 (cons  (cons ref *BFind_lst*) SaveList)
                             )
                        )
                        (mai_dos_Popup "注意：" 64 "搜索条目已被保存.")
                      )
                    )
                  )
                )
              )
            )

            (action_tile "load"
              (vl-prin1-to-string
                (quote
                  (progn
                    (cond
                      ( (null SaveList)
                        (mai_dos_Popup "注意：" 48 "没找到保存的搜索条目")
                      )
                      ( (setq ref (_LoadDialog id (mapcar 'car SaveList)))
                        (setq SaveList (vl-remove-if-not '(lambda(x) (member (car x) (cadr ref))) SaveList))
                        (if (car ref)
                            (_PopulateListBox "rep_list"
                                (setq *BFind_lst* (_SortByFirst (cdr (assoc (car ref) SaveList))))
                            )
                        )
                      )
                    )
                  )
                )
              )
            )

            (action_tile "fpick" "(done_dialog 2)")
            (action_tile "rpick" "(done_dialog 3)")

            (action_tile "accept"
              (vl-prin1-to-string
                (quote
                    (if *BFind_lst*
                        (done_dialog 1)
                        (mai_dos_Popup "注意：" 48 "在列表中添加子项")
                    )
                )
              )
            )

            (action_tile "cancel" "(done_dialog 0)")
            (setq dcFlag (start_dialog))
          )
        )

        (if (member dcFlag '(2 3))
          (
            (lambda ( / e )
              (while
                (progn (setvar 'ERRNO 0) (setq e (car (nentsel "\n选择实体:")))
                  (cond
                    ( (= 7 (getvar 'ERRNO)) (princ "\n空选，再次选择:") )
                    ( (eq 'ENAME (type e))
                      (if (wcmatch (cdr (assoc 0 (entget e))) "ATTRIB,TEXT,MTEXT,MULTILEADER")
                          (not (set (if (= 2 dcFlag) 'fstr 'rstr) (_GetTextString e)))
                          (princ "\n选择的实体必须包含文字.")
                      )
                    )
                  )
                )
              )
            )
          )
        )
      )

      (setq id (unload_dialog id))
      (_WriteSearchItems savfname SaveList)

;;-------------------------------------------------------------------------------;;

      (if (= 1 dcFlag)
        (progn
          (_StartUndo acdoc)
          (princ "\n处理中，请耐心等候...") (princ)
          (setq dbdoc (_ObjectDBXDocument acapp))
          (setq dwLst
            (cond
              ( (eq "1" *BFind_cur*)
                (list
                  (cond
                    ( (eq "" (vla-get-FullName acdoc))
                      (strcat (vl-string-right-trim "\\" (vla-get-Path acdoc)) "\\" (vla-get-Name acdoc))
                    )
                    ( (vla-get-FullName acdoc) )
                  )
                )
              )
              ( (eq "1" *BFind_opn*)
                (vlax-for doc (vla-get-documents acapp)
                  (setq dwlst
                    (cons
                      (if (eq "" (vla-get-fullname doc))
                          (strcat (vl-string-right-trim "\\" (vla-get-path doc)) "\\" (vla-get-name doc))
                          (vla-get-fullname doc)
                      )
                      dwlst
                    )
                  )
                )
                dwlst
              )
              ( t (_GetAllFiles *BFind_pat* (eq "1" *BFind_sub*) "*.dwg"))
            )
          )

          (if Express (setq ProgBar (acet-ui-progress "替换中..." (length dwLst))))
          (foreach dwg dwLst
            (setq *ReplaceFlag* nil)
            (princ ".")
            (if Express (acet-ui-progress -1))
            (if
              (setq doc
                (cond
                  ( (eq "1" *BFind_cur*) acdoc )
                  ( (cdr (assoc (strcase dwg) DocLst)) )
                  ( (not (vl-catch-all-error-p (vl-catch-all-apply 'vla-open (list dbdoc dwg)))) dbdoc)
                )
              )
              (if
                (vl-catch-all-error-p
                  (setq err
                    (vl-catch-all-apply
                      (function
                        (lambda ( / lckd lckl dwgn lay blk )
                          (setq lckd (mai_unlock_alllayer)
                                lckl (mapcar '(lambda(x) (strcase (vla-get-name x))) lckd)
                                dwgn (strcat (vl-filename-base dwg) ".dwg")
                          )
                          (vlax-for lay (vla-get-Layouts doc)
                            (if
                              (or
                                (eq "0" *BFind_whr*)
                                (and (eq "1" *BFind_whr*) (eq "MODEL" (strcase (vla-get-name lay))))
                                (and (eq "2" *Bfind_whr*) (not (eq "MODEL" (strcase (vla-get-name lay)))))
                              )
                              (vlax-for obj (vla-get-Block lay)
                                (_ReplaceObject dwgn obj *BFind_lst* *BFind_opt* lckl (eq "0" *BFind_ser*))
                              )
                            )
                          )
                          (if (= 1024 (logand 1024 *BFind_opt*))
                            (vlax-for blk (vla-get-blocks doc)
                              (and
                                  (eq :vlax-false (vla-get-islayout blk))
                                  (eq :vlax-false (vla-get-isxref blk))
                                  (not (wcmatch (vla-get-name blk) "`*D*"))
                                  (vlax-for obj blk (_ReplaceObject dwgn obj *BFind_lst* *BFind_opt* lckl (eq "0" *BFind_ser*)))
                              )
                            )
                          )
                          (mai_relock_layers lckd)
                          (and  *ReplaceFlag*
                                (eq "0" *BFind_ser*)
                                (not (and (vlax-property-available-p doc 'FullName)
                                          (eq "" (vla-get-FullName doc))
                                     )
                                )
                               (vla-saveas doc dwg)
                          )
                        )
                      )
                    )
                  )
                )
                (princ
                  (strcat
                    "\n** 处理dwg文件出错: " (vl-filename-base dwg) ".dwg **"
                    "\n** 出错详细信息: " (vl-catch-all-error-message err) " **"
                  )
                )
              )
              (princ (strcat "\n** 打开dwg文件出错: " (vl-filename-base dwg)  ".dwg **"))
            )
          )
          (if Express (setq ProgBar (acet-ui-progress)))

          (and
              (eq "1" *BFind_cur*)
              (= 1024 (logand 1024 *BFind_opt*))
              (vla-regen acdoc acallviewports)
          )

        ;;-------------------------------------------------------------------------------;;

          (princ (strcat "\n<< " (itoa (length dwLst)) " 文件被处理 >>"))

          (if ReportLst
              (and (or (eq "1" *BFind_ser*) (= 512 (logand 512 *BFind_opt*)))
                   (_GenerateReport (setq rname (strcat *BFind_pat* "\\BFindReport" (_FormatDate "YYYYMODD") ".csv"))
                      (vl-sort
                         (vl-sort ReportLst '(lambda ( a b ) (< (cadddr a) (cadddr b))))
                         '(lambda ( a b ) (< (car a) (car b)))
                      )
                   )
                   (null (_OpenFile rname))
                   (princ "\n--> 打开文件错误报告.")
              )
              (princ "\n<< 没有替换 >>")
          )
          (_WriteConfig cfgfname (mapcar 'eval SymList))
          (_EndUndo acdoc)
        )
        (princ "\n*Cancel*")
      )
      (mapcar '_ReleaseObject (list dbdoc doc))
      (_ReleaseObject (vl-bb-ref '*REX*))
      (vl-bb-set '*REX* nil)
      (gc) (gc) (gc)
    )
  )
  (princ)
)

;;-------------------------------------------------------------------------------;;
;;                                  End of File                                  ;;
;;-------------------------------------------------------------------------------;;
