<?xml version="1.0" encoding="UTF-8"?>
<rename_preset case="none" ignoreext="yes" preset="From File" script="yes" type="normal" version="13">
	<from>*</from>
	<to>MARKER</to>
	<script>@script vbscript
option explicit

&apos; ************************** Script Information **************************
&apos; From clipboard or file rename script version 4.1
&apos; (tested with Directory Opus 13.1 x64)
&apos; by Henrik Ripa (henrik@gieron.info) 2024-02-05
&apos; ************************************************************************

&apos; ****************************** Changelog *******************************
&apos; Version 4.1
&apos; - Removed the dropdown in the dialog for selecting where to get
&apos;   filenames from, use the From Clipboard or From File presets instead
&apos; - Removed the dropdown in the dialog for selecting what to do with
&apos;   empty lines, the option to skip empty filenames is now a checkbox
&apos;  (Both changes were done to avoid some problems with the dropdowns)
&apos; - Removed the option to use empty filenames since I can&apos;t see anyone
&apos;   wanting this (the option is still present in the script itself)
&apos; + New option to trim and collapse spaces in filenames (on by default)
&apos; + Now determines if line is empty after replacing illegal characters
&apos;   and trimming spaces
&apos; + The file to get filenames from can now have an absolute path
&apos; + Files less than three bytes long now works properly
&apos; Version 4.0
&apos; - Now works only in Directory Opus 12 and above
&apos; + The settings are now accessible from the rename dialog
&apos; + Whether to use the clipboard or a file is now a setting instead of the
&apos;   old way of falling back to file if there was nothing in the clipboard
&apos; + There are now two rename presets, one for clipboard and one for file
&apos; Version 3.1
&apos; + Added option to skip empty lines
&apos; Version 3.0
&apos; + Will now get text from the clipboard if there are no text files to use
&apos; + Now supports text files in UTF-8 and UTF-16 (LE and BE) if they have
&apos;   a correct BOM (Byte Order Mark)
&apos; + Added an option to read lines in reverse order
&apos; + The default regexp works better for file names with dots in them
&apos; Version 2.1
&apos; + Better regexp that works for renaming of directories and files
&apos;   without extension.
&apos; + Will now remove characters that are illegal in filenames.
&apos; + Now looks for a &apos;Clipboard Text.txt&apos; file if the &apos;filenames.txt&apos;
&apos;   file is missing.
&apos; Version 2.0
&apos; + Rewritten to use magic markers in new filenames.
&apos; Version 1.0
&apos; + Initial version. Worked entirely differently.
&apos; ************************************************************************

&apos; ***************************** Installation *****************************
&apos; Download and unzip the .orp files.
&apos;
&apos; Open the Directory Opus rename dialog. Select Import Preset File from
&apos; the Preset Management menu and open &apos;From Clipboard.orp&apos;. Close the
&apos; Directory Opus rename dialog.
&apos;
&apos; Open the Directory Opus rename dialog. Select Import Preset File from
&apos; the Preset Management menu and open &apos;From File.orp&apos;. Close the
&apos; Directory Opus rename dialog.
&apos; ************************************************************************

&apos; **************************** Instructions ******************************
&apos; This script replaces the string &apos;MARKER&apos; in new file names with lines
&apos; taken from the clipboard or from a text file. If the lines include any
&apos; characters that are illegal in filenames they will be removed.
&apos;
&apos; If a text file is used it must be located in the same directory as the
&apos; files that you want to rename.
&apos; 如果要从文件中获取文件名，含有文件名的文件必须放在与被重命名文件相同的路径下
&apos;
&apos; When the text file is in place, or you have copied some text to the
&apos; clipboard, select the files you want to rename. The order of the
&apos; selected files in the lister must be the same as the order in the text
&apos; file. Then click the rename button and select this script under presets.
&apos;
&apos; Set up the old name and new name fields so that the the string &apos;MARKER&apos;
&apos; is present in the new file names. You can use standard rename, regular
&apos; expressions or find and replace. Inspect the results in the preview
&apos; pane and click OK.
&apos;
&apos; If you have problems you can start by examining the script output
&apos; below this editor. If you find a bug or have a feature request you can
&apos; either go to the Dopus Resource Centre forum thread where you got this
&apos; script from or send me an email.
&apos; ************************************************************************

&apos; ******************************* Options ********************************
dim optClipboardMode, optIllegalReplacement, optIllegalCharacters, _
  optUseEmptyFilenames

&apos; Which mode to use; clipboard (True) or file (False)
&apos; 从剪贴板还是文本获取
optClipboardMode = False

&apos; What to replace illegal file name characters with
optIllegalReplacement = &quot;&quot;

&apos; Pattern of illegal file name characters
optIllegalCharacters = &quot;[/\\:*?&lt;&gt;\|&quot;&quot;]&quot;

&apos; Whether to use empty filenames instead of not renaming the file
optUseEmptyFilenames = False
&apos; ************************************************************************

&apos; *********************** DOpus custom fields ****************************
Function OnGetCustomFields(ByRef getFieldData)

  if (not optClipboardMode) then
    getFieldData.field_labels(&quot;file&quot;) = DOpus.strings.Get(&quot;file&quot;)
	getFieldData.field_tips(&quot;file&quot;) = DOpus.strings.Get(&quot;file_tip&quot;)
    getFieldData.fields.file = &quot;filenames.txt&quot;
  end if

  getFieldData.field_labels(&quot;marker&quot;) = DOpus.strings.Get(&quot;marker&quot;)
  getFieldData.fields.marker = &quot;MARKER&quot;

  getFieldData.field_labels(&quot;trim_spaces&quot;) = DOpus.strings.Get(&quot;trim_spaces&quot;)
  getFieldData.fields.trim_spaces = True

  getFieldData.field_labels(&quot;skip_empty&quot;) = DOpus.strings.Get(&quot;skip_empty&quot;)
  getFieldData.fields.skip_empty = False

  getFieldData.field_labels(&quot;reverse&quot;) = DOpus.strings.Get(&quot;reverse&quot;)
  getFieldData.fields.reverse = False
End Function
&apos; ************************************************************************

&apos; ************************ DOpus rename function *************************
&apos; This is the function that is called by DOpus to get a new file name,
&apos; it is called once for each file that will be renamed
Function OnGetNewName(ByRef getNewNameData)

  dim file

  if (not optClipboardMode) then
    file = getNewNameData.custom.file
  else
    file = &quot;&quot;
  end if

  dorInitialize _
    getNewNameData.item.path, _
    file, _
    optClipboardMode, _
    getNewNameData.custom.reverse

  OnGetNewName = dorGetNewName( _
    getNewNameData.item.name, _
    getNewNameData.newname, _
    getNewNameData.custom.marker, _
    optUseEmptyFilenames, _
    getNewNameData.custom.skip_empty, _
    getNewNameData.custom.trim_spaces)
End Function
&apos; ************************************************************************

&apos; ********************* Directory Opus rename module *********************
dim dorInitialized
dorInitialized = False

&apos; Initalizes the module by initializing all the modules it depends on
Function dorInitialize( strPath, strFileName, _
                        bolUseClipboard, bolReverseOrder )
  &apos; Only run this initialization once
  if (dorInitialized) then exit function
  dorInitialized = True

  &apos; Print script identification
  eioPrint &quot;From file rename script by Henrik Ripa&quot;

  &apos; Initialize the file names source, remove illegal characters
  &apos; and trim and collapse spaces modules
  fnsInitialize strPath, strFileName, bolUseClipboard, bolReverseOrder
  ricInitialize optIllegalCharacters, optIllegalReplacement
  tcsInitialize
End Function

&apos; Returns the new name for the given file, the input is the files old
&apos; name and the name it gets after the preprocessing that is done in DOpus
Function dorGetNewName ( strOldName, strMidName, strMagicMarker, _
                         bolUseEmptyLines, bolSkipEmptyLines, _
                         bolTrimSpaces )
  dim line

  &apos; Set new name to the original name to undo the preprocessing
  &apos; in case we do not come up with a new name
  dorGetNewName = strOldName

  &apos; Exit if there are nothing to rename with
  if (fnsNoMoreNames()) then exit function

  &apos; Ignore anything that does not have the magic marker
  if (instr(strMidName, strMagicMarker) = 0) then exit function

  &apos; Ignore the filenames file if the user has selected it
  if (strOldName = focGetUsedFile()) then exit function

  &apos; Get next file name from source
  line = fnsGetNextName( bolSkipEmptyLines, bolTrimSpaces )

  &apos; Assemble new file name, if it isn&apos;t empty
  if (bolUseEmptyLines or line &lt;&gt; &quot;&quot;) then
    dorGetNewName = Replace(strMidName, strMagicMarker, line)
  end if
End Function
&apos; ************************************************************************

&apos; ****************** Replace illegal characters module *******************
dim ricRegexp, ricReplacement

&apos; Takes arguments for the module and creates the regexp object
Function ricInitialize( strPattern, strReplacement )
  set ricRegexp = new Regexp
  ricRegexp.Global = True
  ricRegexp.IgnoreCase = False
  ricRegexp.Pattern = strPattern
  ricReplacement = strReplacement
End Function

&apos; Replaces illegal characters in the text with the replacement string
Function ricReplaceCharacters( strText )
  dim text
  text = ricRegexp.Replace(strText, ricReplacement)
  if (strText &lt;&gt; text) then
    eioPrint &quot;Replaced illegal characters in &quot;&quot;&quot; &amp; strText &amp; &quot;&quot;&quot;&quot;
  end if
  ricReplaceCharacters = text
End Function
&apos; ************************************************************************

&apos; ******************* Trim and collapse spaces module ********************
dim tcsRegexp, tcsReplacement

&apos; Creates the regexp object
Function tcsInitialize( )
  set tcsRegexp = new Regexp
  tcsRegexp.Global = True
  tcsRegexp.IgnoreCase = False
  tcsRegexp.Pattern = &quot;[ \t]+&quot;
  tcsReplacement = &quot; &quot;
End Function

&apos; Replaces multiple spaces or tabs with one space and removes
&apos; leading and/or trailing spaces from the text
Function tcsTrimAndCollapseSpaces( strText )
  dim text
  text = tcsRegexp.Replace(strText, tcsReplacement)
  text = Trim(text)
  if (strText &lt;&gt; text) then
    eioPrint &quot;Trimmed/collapsed spaces in &quot;&quot;&quot; &amp; strText &amp; &quot;&quot;&quot;&quot;
  end if
  tcsTrimAndCollapseSpaces = text
End Function
&apos; ************************************************************************

&apos; *********************** File names source module ***********************
dim fnsSourceLines, fnsCurrentLine

&apos; Initializes the file names source by getting the source text
&apos; from file or clipboard
Function fnsInitialize( strPath, strFileName, _
                        bolUseClipboard, bolReverseOrder )
  dim lines, text
  fnsCurrentLine = 0
  text = focGetText(strPath, strFileName, bolUseClipboard)
  lines = arhSplitString(text)
  if (bolReverseOrder) then
    lines = arhRemoveLastIfEmpty(lines)
    lines = arhReverse(lines)
  end if
  fnsSourceLines = lines
End Function

&apos; Returns true if there are no lines left, i.e. if the source text
&apos; is empty or non existant or if all lines have been returned,
&apos; false otherwise
Function fnsNoMoreNames( )
  dim nomore
  nomore = (fnsCurrentLine &gt; Ubound(fnsSourceLines))
  if (nomore) then
    eioPrint DOpus.strings.Get(&quot;noMoreText&quot;)
  end if
  fnsNoMoreNames = nomore
End Function

&apos; Returns the next line from the source text
Function fnsGetNextName( bolSkipEmptyLines, bolTrimSpaces )
  if (fnsCurrentLine &gt; Ubound(fnsSourceLines)) then
    fnsGetNextName = &quot;&quot;
  else
    fnsGetNextName = fnsSourceLines(fnsCurrentLine)
    fnsCurrentLine = fnsCurrentLine + 1

    &apos; Clean file name from unwanted characters
    fnsGetNextName = ricReplaceCharacters(fnsGetNextName)

    if (bolTrimSpaces) then
      &apos; Trim and collapse spaces in file name
      fnsGetNextName = tcsTrimAndCollapseSpaces(fnsGetNextName)
    end if

    if (bolSkipEmptyLines and fnsGetNextName = &quot;&quot;) then
      fnsGetNextName = fnsGetNextName( bolSkipEmptyLines, bolTrimSpaces )
    end if
  end if
End Function
&apos; ************************************************************************

&apos; *********************** File or clipboard module ***********************
dim focFileNameUsed

&apos; Reads text from files in the given string array or from clipboard
Function focGetText( strPath, strFileName, bolUseClipboard )
  dim text
  focFileNameUsed = &quot;&quot;
  if (bolUseClipboard) then
    text = focGetTextFromClipboard()
    if (text &lt;&gt; &quot;&quot;) then
      eioPrint DOpus.strings.Get(&quot;clipUse&quot;)
    else
      eioPrint DOpus.strings.Get(&quot;clipNoText&quot;)
    end if
  else
    text = focGetTextFromFile(strPath &amp; &quot;\&quot; &amp; strFileName)
    if (text &lt;&gt; &quot;&quot;) then
      focFileNameUsed = strFileName
      eioPrint &quot;Using the file &apos;&quot; &amp; strFileName &amp; &quot;&apos; as source text&quot;
    else
      text = focGetTextFromFile(strFileName)
        if (text &lt;&gt; &quot;&quot;) then
          focFileNameUsed = strFileName
          eioPrint &quot;Using the file &apos;&quot; &amp; strFileName &amp; &quot;&apos; as source text&quot;
        else
          eioPrint &quot;The file &apos;&quot; &amp; strFileName &amp; &quot;&apos; is missing or empty&quot; &amp; _
            &quot;, nothing to use as source text&quot;
      end if
    end if
  end if
  focGetText = text
End Function

&apos; Returns the file name used for getting the text, if any
Function focGetUsedFile( )
  focGetUsedFile = focFileNameUsed
End Function

&apos; Reads text from clipboard
Function focGetTextFromClipboard( )
&apos;  dim hto, text
&apos;  set hto = CreateObject(&quot;htmlfile&quot;)
&apos;  text = hto.parentwindow.clipboardData.GetData(&quot;text&quot;)
  dim text, objClipFiles, x
  If DOpus.GetClipFormat() = &quot;files&quot; Then
    Set objClipFiles = DOpus.GetClip(&quot;files&quot;)
	For each x in objClipFiles
		&apos;eioPrint x.display_name &amp; &quot; 1&quot;
		If x.display_name &lt;&gt; &quot;&quot; and x.display_name &lt;&gt; x.name Then
			text = text &amp; vbLf &amp; x.display_name
		Else
			&apos;eioPrint x.name &amp; &quot; 2&quot;
			text = text &amp; vbLf &amp; x.name_stem_m
		end IF
	Next
	text = Replace(text, vbLf, &quot;&quot;, 1, 1)
	&apos;eioPrint text
  Else
    text = DOpus.GetClip(&quot;text&quot;)
	&apos;eioPrint text
  end If			
  &apos;if (VarType(text) &lt;&gt; vbString) then text = &quot;&quot;
  focGetTextFromClipboard = text
End Function

&apos; Reads text from file, supports UTF-8, UTF-16 and UTF-16 (BE) files
&apos; with proper BOM (Byte Order Mark)
Function focGetTextFromFile( strTextFile )
  dim ado, fso, tmp, bom(3), encoding, file, i
  const adTypeBynary = 1
  const adTypeText = 2
  const adReadAll = -1
  on error resume next
  set ado = CreateObject(&quot;ADODB.Stream&quot;)
  ado.Open
  ado.Type = adTypeBynary
  ado.LoadFromFile strTextFile
  tmp = ado.Read(3)
  if (Ubound(tmp) = 2) then
    for i = 0 To 2
      bom(i) = AscB(MidB(tmp, i+1, 1))
    next
  end if
  ado.Close
  if err.number &gt; 0 then
    focGetTextFromFile = &quot;&quot;
  else
    if (bom(0) = &amp;hEF and bom(1) = &amp;hBB and bom(2) = &amp;hBF) then
      encoding = &quot;utf-8&quot; &apos; UTF-8
    elseif (bom(0) = &amp;hFE and bom(1) = &amp;hFF) then
      encoding = &quot;unicode&quot; &apos; UTF-16 big endian
    elseif (bom(0) = &amp;hFF and bom(1) = &amp;hFE) then
      encoding = &quot;unicodeFFFE&quot; &apos; UTF-16 little endian
    else
      encoding = &quot;default&quot;
    end if
    eioPrint &quot;Loading file using &quot; &amp; encoding &amp; &quot; encoding&quot;
    if (encoding = &quot;default&quot;) then
      set fso = CreateObject(&quot;Scripting.FileSystemObject&quot;)
      set file = fso.OpenTextFile(strTextFile, 1, False)
      if err.number &gt; 0 then
        focGetTextFromFile = &quot;&quot;
      else
        focGetTextFromFile = file.ReadAll()
      end if
    else
      set ado = CreateObject(&quot;ADODB.Stream&quot;)
      ado.Open
      ado.Type = adTypeText
      ado.CharSet = encoding
      ado.LoadFromFile strTextFile
      if err.number &gt; 0 then
        focGetTextFromFile = &quot;&quot;
      else
        focGetTextFromFile = ado.ReadText(adReadAll)
      end if
    end if
  end if
End Function
&apos; ************************************************************************

&apos; ************************* Array helper module **************************
&apos; Splits a string into an array of strings on line breaks (\r\n, \r or \n)
Function arhSplitString( strText )
  dim text
  text = strText
  text = Replace(text, vbCrLf, vbLf)
  text = Replace(text, vbCr, vbLf)
  arhSplitString = Split(text, vbLf)
End Function

&apos; Returns an array that is in reverse order to the input array
Function arhReverse( ByVal arrIn )
  dim index, maxIndex, out
  maxIndex = Ubound(arrIn)
  if (maxIndex &lt; 1) then
    arhReverse = arrIn
    exit function
  end if
  redim out(maxIndex)
  for index = 0 to maxIndex
    out(maxIndex - index) = arrIn(index)
  next
  arhReverse = out
End Function

&apos; Returns an array where the last element of the input array has
&apos; been removed if it was an empty string
Function arhRemoveLastIfEmpty( ByVal arrIn )
  dim index, maxIndex, out
  maxIndex = Ubound(arrIn)
  if (maxIndex &gt; -1) then
    if (arrIn(maxIndex) = &quot;&quot;) then
      maxIndex = maxIndex - 1
      redim out(maxIndex)
      for index = 0 to maxIndex
        out(index) = arrIn(index)
      next
      arhRemoveLastIfEmpty = out
    else
      arhRemoveLastIfEmpty = arrIn
    end if
  else
    arhRemoveLastIfEmpty = arrIn
  end if
End Function
&apos; ************************************************************************

&apos; ****************** Error &amp; information output module *******************
&apos; Prints message to the appropriate output channel
Function eioPrint( strMessage )
  Dopus.OutputString strMessage
  &apos;WScript.Echo strMessage
End Function
&apos; ************************************************************************
==SCRIPT RESOURCES
&lt;resources&gt;
    &lt;resource type=&quot;strings&quot;&gt;
        &lt;strings lang=&quot;english&quot;&gt;
            &lt;string id=&quot;file&quot; text=&quot;File to get filenames from&quot; /&gt;
            &lt;string id=&quot;file_tip&quot; text=&quot;it must be located in the same directory as the
&apos; files that you want to rename, or use an absolute path.&quot; /&gt;
            &lt;string id=&quot;marker&quot; text=&quot;Text to replace with filename&quot; /&gt;
            &lt;string id=&quot;trim_spaces&quot; text=&quot;Trim and collapse spaces in filenames&quot; /&gt;
			&lt;string id=&quot;skip_empty&quot; text=&quot;Skip empty filenames&quot; /&gt;
            &lt;string id=&quot;reverse&quot; text=&quot;Reverse the order of filenames&quot; /&gt;
			&lt;string id=&quot;noMoreText&quot; text=&quot;No more names in source text&quot; /&gt;
			&lt;string id=&quot;clipUse&quot; text=&quot;Using the clipboard as source text&quot; /&gt;
			&lt;string id=&quot;clipNoText&quot; text=&quot;Nothing in clipboard to use as source text&quot; /&gt;
        &lt;/strings&gt;
        &lt;strings lang=&quot;chs&quot;&gt;
            &lt;string id=&quot;file&quot; text=&quot;从中获取文件名的文件&quot; /&gt;
            &lt;string id=&quot;file_tip&quot; text=&quot;必须与被重命名的文件在同一路径下，或者使用绝对路径&quot; /&gt;
            &lt;string id=&quot;marker&quot; text=&quot;文本替换标记&quot; /&gt;
            &lt;string id=&quot;trim_spaces&quot; text=&quot;去除首尾空格，合并连续空格&quot; /&gt;
			&lt;string id=&quot;skip_empty&quot; text=&quot;跳过空行&quot; /&gt;
            &lt;string id=&quot;reverse&quot; text=&quot;文件名行 反序&quot; /&gt;
			&lt;string id=&quot;noMoreText&quot; text=&quot;源文本中已无更多名称&quot; /&gt;
			&lt;string id=&quot;clipUse&quot; text=&quot;使用剪贴板作为源文本&quot; /&gt;
			&lt;string id=&quot;clipNoText&quot; text=&quot;剪贴板中无文本可用&quot; /&gt;
        &lt;/strings&gt;
    &lt;/resource&gt;
&lt;/resources&gt;</script>
	<custom>
		<var name="file" type="8">filenames.txt</var>
		<var name="marker" type="8">MARKER</var>
		<var name="reverse" type="11">no</var>
		<var name="skip_empty" type="11">no</var>
		<var name="trim_spaces" type="11">yes</var>
	</custom>
</rename_preset>
