Restricting macros to operate on specific paragraph styles

Get help using and writing Nisus Writer Pro macros.
Post Reply
djmacd
Posts: 3
Joined: 2010-09-29 03:28:07
Location: Scotland

Restricting macros to operate on specific paragraph styles

Post by djmacd » 2010-09-29 11:24:46

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.

Kino
Posts: 400
Joined: 2008-05-17 04:02:32

Re: Restricting macros to operate on specific paragraph styles

Post by Kino » 2010-09-30 02:01:43

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).
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, . . .

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
Or, if you prefer Replace All in Selection like me:

Code: Select all

Replace All in Selection '^[\x20\t]+|[\x20\t]+$', '', 'ES'
Replace All in Selection '\x20{2,}', '\x20', 'ES'
Then, all you need to do is to select all texts in specific styles before running your macro. For that, try this one.

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
When you need to make negative selections, use Edit > Select > Invert Selection along with the macro above.

djmacd
Posts: 3
Joined: 2010-09-29 03:28:07
Location: Scotland

Re: Restricting macros to operate on specific paragraph styles

Post by djmacd » 2010-09-30 08:08:35

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.

djmacd
Posts: 3
Joined: 2010-09-29 03:28:07
Location: Scotland

Re: Restricting macros to operate on specific paragraph styles

Post by djmacd » 2010-10-01 14:43:24

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.

User avatar
martin
Official Nisus Person
Posts: 4273
Joined: 2002-07-11 17:14:10
Location: San Diego, CA
Contact:

Re: Restricting macros to operate on specific paragraph styles

Post by martin » 2010-10-04 13:39:02

That is a very useful macro, thanks Kino. I'll have to add it to the macro repository!

Kino
Posts: 400
Joined: 2008-05-17 04:02:32

Re: Restricting macros to operate on specific paragraph styl

Post by Kino » 2011-01-13 09:55:02

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.
SelectByStyle_20110110_nwm.zip
(5.29 KiB) Downloaded 271 times

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 ###

Kino
Posts: 400
Joined: 2008-05-17 04:02:32

Re: Restricting macros to operate on specific paragraph styl

Post by Kino » 2011-01-15 10:09:16

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.

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
SelectByStyle_20110116_nwm.zip
(5.41 KiB) Downloaded 259 times

Post Reply