Is it possible/easy to limit NW macros such that they will operate on specific paragraph (or character) styles?
I have an NW Pro macro that carries out a series of grep Finds and Replaces on documents (styled MS Word documents, typically several hundred pages long). I want this macro to operate on a specific paragraph style (or, perhaps, list of paragraph styles). Is there an easy way to create an array or subset of styles that the macro can can operate on? Alternatively, is it easy to create an exclusion list, i.e. paragraph styles that it should not operate on?
The macro operates perfectly by the way. I just want it restrict it to specific styles.
Using NW Pro 1.4.1 and Mac OS X 10.6.4.
Restricting macros to operate on specific paragraph styles
Re: Restricting macros to operate on specific paragraph styles
If your macro consists exclusively of Find and Replace commands, you can make them work on selections preserving the selections by adding ‘sS’ to their options. For example, . . .djmacd wrote:I have an NW Pro macro that carries out a series of grep Finds and Replaces on documents [ . . .]. I want this macro to operate on a specific paragraph style (or, perhaps, list of paragraph styles).
Code: Select all
Find and Replace '^[\x20\t]+|[\x20\t]+$', '', 'EasS' # remove leading/trailing space and tab
Find and Replace '\x20{2,}', '\x20', 'EasS' #shorten two or more spaces to one
Code: Select all
Replace All in Selection '^[\x20\t]+|[\x20\t]+$', '', 'ES'
Replace All in Selection '\x20{2,}', '\x20', 'ES'
Code: Select all
### Select by Styles ###
$charStyleMark = Cast to String ' [a̱]'
$paraStyleMark = Cast to String ' [¶]'
$doc = Document.active
if $doc == undefined
exit # no open document
end
$charStyles = $paraStyles = Hash.new
foreach $text in $doc.allTexts
$i = 0
while $i < $text.length
$attr = $text.attributesAtIndex $i
$range = $text.rangeOfAttributesAtIndex $i
$sel = TextSelection.new $text, $range
if Defined $attr.characterStyleName
if $charStyles{$attr.characterStyleName} == undefined
$charStyles{$attr.characterStyleName} = Array.new
end
$charStyles{$attr.characterStyleName}.appendValue $sel
end
if Defined $attr.paragraphStyleName
if $paraStyles{$attr.paragraphStyleName} == undefined
$paraStyles{$attr.paragraphStyleName} = Array.new
end
$paraStyles{$attr.paragraphStyleName}.appendValue $sel
end
$i = $range.bound
end
end
$styleNames = $charStyles.keys
$styleNames.sort 'li'
foreach $i, $name in $styleNames
$name &= $charStyleMark
$styleNames.setValueAtIndex $name, $i
end
$paraStyleNames = $paraStyles.keys
$paraStyleNames.sort 'li'
foreach $name in $paraStyleNames
$name &= $paraStyleMark
$styleNames.appendValue $name
end
if ! $styleNames.count
exit 'No applied style fouond, exiting . . .'
end
$styleNames = Prompt Checkboxes 'Select all texts in specific styles . . .', '', '', $styleNames
if ! $styleNames.count
exit 'Nothing checked, exiting . . .'
end
$sels = Array.new
$charStyleMark = '\Q' & $charStyleMark
$charStyleMark &= '\E$'
$paraStyleMark = '\Q' & $paraStyleMark
$paraStyleMark &= '\E$'
foreach $name in $styleNames
$check = $name.findAndReplace $charStyleMark, '', 'E-i'
if Defined $check
$sels.appendValuesFromArray $charStyles{$name}
else
$name.findAndReplace $paraStyleMark, '', 'E-i'
$sels.appendValuesFromArray $paraStyles{$name}
end
end
$doc.setSelections $sels
Re: Restricting macros to operate on specific paragraph styles
That is remarkably helpful. I don't know the NW macro language well yet, but I think I follow the code you have provided.
I hope that I will be in a position to return the favour one day.
I hope that I will be in a position to return the favour one day.
Re: Restricting macros to operate on specific paragraph styles
I now understand the code. It's elegant and very useful, and not just for the macro I asked about, but generally useful for selecting styles before running any macro. Thank you again.
- martin
- Official Nisus Person
- Posts: 5230
- Joined: 2002-07-11 17:14:10
- Location: San Diego, CA
- Contact:
Re: Restricting macros to operate on specific paragraph styles
That is a very useful macro, thanks Kino. I'll have to add it to the macro repository!
Re: Restricting macros to operate on specific paragraph styl
The macro posted above was unnecessarily complicated. The cause of my writing such crappy code is that, for some reason I don’t remember, I thought it was necessary to add [a̱] and [¶] at the end of each style name. But why not add them at the top of style names??? Then, you don’t need using different hashes for character styles and paragraph styles respectively. Just a single hash is sufficient because names beginning with [a̱] and [¶] are sorted by kind naturally, without your doing anything special. And, in this way, names look better in the Macro Prompt, at least to my eyes. Also, why not use “[a̱] style name” and “[¶] style name” as hash keys instead of just “style name”??? Then, you can get rid of the last foreach loop and related code.
So here is a revised version, which supports list styles ([#] style name) too. Also, it allows you to select paragraphs having no paragraph style directly. For that, I had to add a routine to exclude special characters between tables and other text objects. Otherwise, the macro will pick up such characters as “[¶] <no style>” on which, however, you cannot apply any paragraph style. You can do nothing against them. That should be confusing and irritating.
So here is a revised version, which supports list styles ([#] style name) too. Also, it allows you to select paragraphs having no paragraph style directly. For that, I had to add a routine to exclude special characters between tables and other text objects. Otherwise, the macro will pick up such characters as “[¶] <no style>” on which, however, you cannot apply any paragraph style. You can do nothing against them. That should be confusing and irritating.
Code: Select all
### Select by Style (rev. 3) ###
# This macro lists up all Paragraph/Character/List styles
# used in the frontmost document and enables you to select
# all text portions in specific style(s).
$charStyleMark = Cast to String '[a̱] ' # customizable
$paraStyleMark = Cast to String '[¶] ' # customizable
$listStyleMark = Cast to String '[#] ' # customizable
$noStyleName = Cast to String '<no style>' # customizable
Require Pro Version 1.3
$doc = Document.active
if $doc == undefined
exit # no open document
end
$language = Language.systemLanguage
$selectionsByStyle = Hash.new
foreach $text in $doc.allTexts
$textRanges = Array.new
if $text.tables.count # then, exclude special chars enclosing tables
$tableRanges = Array.new
foreach $table in $text.tables
$tableRanges.appendValue $table.enclosingTextRange
end
$tableRanges.sort
$i = 0
foreach $tableRange in $tableRanges
$range = Range.newWithLocationAndBound $i, $tableRange.location
$textRanges.appendValue $range
$i = $tableRange.bound
end
$range = Range.newWithLocationAndBound $i, $text.length
else
$range = Range.new 0, $text.length
end
$textRanges.appendValue $range
foreach $textRange in $textRanges
$i = $textRange.location
while $i < $textRange.bound
$attr = $text.attributesAtIndex $i
$range = $text.rangeOfAttributesAtIndex $i
$sel = TextSelection.new $text, $range
if Defined $attr.characterStyleName
$name = $charStyleMark & $attr.characterStyleName
if $selectionsByStyle{$name} == undefined
$selectionsByStyle{$name} = Array.new
end
$selectionsByStyle{$name}.appendValue $sel
end
if Defined $attr.paragraphStyleName
$name = $paraStyleMark & $attr.paragraphStyleName
if $selectionsByStyle{$name} == undefined
$selectionsByStyle{$name} = Array.new
end
$selectionsByStyle{$name}.appendValue $sel
else
$name = $paraStyleMark & $noStyleName
if $selectionsByStyle{$name} == undefined
$selectionsByStyle{$name} = Array.new
end
$selectionsByStyle{$name}.appendValue $sel
end
if Defined $attr.listStyle
$name = $listStyleMark & $attr.listStyle.name
if $selectionsByStyle{$name} == undefined
$selectionsByStyle{$name} = Array.new
end
$selectionsByStyle{$name}.appendValue $sel
end
$i = $range.bound
end
end
end
$styleNames = $selectionsByStyle.keys
$styleNames.sort 'li', $language
$styleNames = Prompt Checkboxes 'Select all texts in specific styles . . .', '', '', $styleNames
if ! $styleNames.count
exit 'Nothing checked, exiting . . .'
end
$sels = Array.new
foreach $name in $styleNames
$sels.appendValuesFromArray $selectionsByStyle{$name}
end
$doc.setSelections $sels
### end of macro ###
Re: Restricting macros to operate on specific paragraph styl
I modified the macro again. Thanks to getProperty command, I could simplify the code in the first “foreach . . . end” loop. This makes it easier to maintain the macro, e.g. fix a potential/real bug. As a side effect, not only for Paragraph Style but also for Character and List Styles, selections not having any style are listed up. Useless, I think, but harmless. And perhaps there may be situations in which “[a̱] <no style>” or “[#] <no style>” are useful.
In spite of that, this version is longer than the previous ones because...
• It checks for empty document (i.e. not a singe character in it).
• The Macro Prompt is more informative (addition of $detail).
• More easily customizable/localizable because of variables.
The last reason mentioned above applies especially to an array “$styleCategories” and hashes declared in “$styleSymbols = $properties = $selectionsByStyle = Hash.new”. The macro works without problem even if the definition of $styleCategories has been changed to “$styleCategories = Array.new '○', '▴', '□'”, for example ;-)
IMO, one of the most fantastic features of Nisus Writer Pro Macro language is hash. It is very strong and versatile. You can put in it anything: normal Text object ($styleSymbols and $properties although the latter will be treated as Attributes Object properties by getProperty), array ($selectionsByStyle) and/or anything else.
In spite of that, this version is longer than the previous ones because...
• It checks for empty document (i.e. not a singe character in it).
• The Macro Prompt is more informative (addition of $detail).
• More easily customizable/localizable because of variables.
The last reason mentioned above applies especially to an array “$styleCategories” and hashes declared in “$styleSymbols = $properties = $selectionsByStyle = Hash.new”. The macro works without problem even if the definition of $styleCategories has been changed to “$styleCategories = Array.new '○', '▴', '□'”, for example ;-)
IMO, one of the most fantastic features of Nisus Writer Pro Macro language is hash. It is very strong and versatile. You can put in it anything: normal Text object ($styleSymbols and $properties although the latter will be treated as Attributes Object properties by getProperty), array ($selectionsByStyle) and/or anything else.
Code: Select all
$noStyleName = Cast to String '<no style>' # customizable
$styleString = 'Style' # customizable (used in Macro Prompt's $detail)
$promptMessage = 'Select all texts in styles of your choice...' # customizable
$emptyDocument = 'Empty document, exiting...' # customizable (error message)
$nothingChecked = 'Nothing checked, exiting...' # customizable (error message)
$sp = Cast to String "\x20" # customizable but...
$styleCategories = Array.new 'Character', 'Paragraph', 'List' # customizable
$styleSymbols = $properties = $selectionsByStyle = Hash.new
$styleSymbols{$styleCategories[0]} = Cast to String '[a̱]' # customizable (for Character Style)
$styleSymbols{$styleCategories[1]} = Cast to String '[¶]' # customizable (for Paragraph Style)
$styleSymbols{$styleCategories[2]} = Cast to String '[#]' # customizable (for List Style)
$properties{$styleCategories[0]} = 'characterStyle'
$properties{$styleCategories[1]} = 'paragraphStyle'
$properties{$styleCategories[2]} = 'listStyle'
Require Pro Version 1.3
$doc = Document.active
if $doc == undefined
exit # no open document
end
$language = Language.systemLanguage
foreach $text in $doc.allTexts
if $text.length
$textRanges = Array.new
if $text.tables.count # then, exclude special chars enclosing tables
$tableRanges = Array.new
foreach $table in $text.tables
$tableRanges.appendValue $table.enclosingTextRange
end
$tableRanges.sort
$i = 0
foreach $tableRange in $tableRanges
$range = Range.newWithLocationAndBound $i, $tableRange.location
$textRanges.appendValue $range
$i = $tableRange.bound
end
$range = Range.newWithLocationAndBound $i, $text.length
else
$range = Range.new 0, $text.length
end
$textRanges.appendValue $range
foreach $textRange in $textRanges
$i = $textRange.location
while $i < $textRange.bound
$attr = $text.displayAttributesAtIndex $i
$range = $text.rangeOfDisplayAttributesAtIndex $i
$sel = TextSelection.new $text, $range
foreach $property in $properties.keys
$styleObject = $attr.getProperty $properties{$property}
$name = $styleSymbols{$property}
if Defined $styleObject
$name &= $sp & $styleObject.name
else
$name &= $sp & $noStyleName
end
if $selectionsByStyle{$name} == undefined
$selectionsByStyle{$name} = Array.new
end
$selectionsByStyle{$name}.appendValue $sel
end
$i = $range.bound
end
end
end
end
if ! $selectionsByStyle.count
exit $emptyDocument
end
$styleNames = $selectionsByStyle.keys
$styleNames.sort 'li', $language
$detail = Array.new
foreach $key, $value in $styleSymbols
$temp = ''
$temp = $temp.textByAppending $value, $sp, $key, $sp, $styleString
$detail.appendValue $temp
end
$detail.sort 'li', $language
$detail = $detail.join ', '
$styleNames = Prompt Checkboxes $promptMessage, $detail, 'Select!', $styleNames
if ! $styleNames.count
exit $nothingChecked
end
$sels = Array.new
foreach $name in $styleNames
$sels.appendValuesFromArray $selectionsByStyle{$name}
end
$doc.setSelections $sels