Can a variable be made to survive a macro?

Get help using and writing Nisus Writer Pro macros.
Post Reply
js
Posts: 229
Joined: 2007-04-12 14:59:36

Can a variable be made to survive a macro?

Post by js » 2009-02-28 10:34:42

Is it possible to create a variable that survives the macro and is kept for a whole Nisus session?
Can document properties by attributed by a macro and later read?

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

Re: Can a variable be made to survive a macro?

Post by martin » 2009-02-28 14:34:15

js wrote:Is it possible to create a variable that survives the macro and is kept for a whole Nisus session?
It is possible- you'll want to use the Registry object. Which registry you use depends on how long you want the value to stick around. If you just want something for a "session", which I imagine means the time from launch to quit, then use the "applicationRegistry", eg:

Code: Select all

$valueKey = 'com.nisus.example.favoriteColor'
$registry = Registry.applicationRegistry
$color = $registry.loadValueWithName($valueKey)
If $color == undefined
	$color = Prompt Input "What's your favorite color?"
	$registry.saveValueWithName($color, $valueKey)
	Prompt "Thanks, I'll remember that until you quit NWP."
Else
	Prompt "Probably your favorite color is still $color."
End
See the macro reference for more.
Can document properties by attributed by a macro and later read?
That's an interesting idea. There's currently no such feature, but I'll file a request, thanks.

js
Posts: 229
Joined: 2007-04-12 14:59:36

Re: Can a variable be made to survive a macro?

Post by js » 2009-03-02 08:57:22

Thanks for your help. Based on your example I created a pair of macros that set a flag into a text and jump to its position respectively. I gave them the shortcuts cd-FL and cd-FLL.

Code: Select all

#set flag
$valueKey = 'com.nisus.example.myLoc'
$registry = Registry.applicationRegistry
$flagLoc = $registry.loadValueWithName($valueKey)
$flagLoc = Selection Location
$registry.saveValueWithName($flagLoc, $valueKey)

Code: Select all

#jump to flag
$valueKey = 'com.nisus.example.myLoc'
$registry = Registry.applicationRegistry
$flagLoc = $registry.loadValueWithName($valueKey)
Set Selection Location $flagLoc
Scroll to selection
I find these very helpful to edit large documents. Of course the macros would be even better if they could also memorize the document-path, instead of only the selection. Could this be done in one go together with the selection, or would one need a separate pair: one for the location and one for the path?

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

Re: Can a variable be made to survive a macro?

Post by Kino » 2009-03-02 09:19:27

js wrote:Of course the macros would be even better if they could also memorize the document-path, instead of only the selection. Could this be done in one go together with the selection, or would one need a separate pair: one for the location and one for the path?
A while ago, I wrote such macros which might be of your interest. They use a hash -- $zeroMarker{$doc} -- to memorise a location per document.

Code: Select all

### Zero Marker (3.1.3) ###

#	This macro works as a toggle command and sets or clears the zero marker depending on the situation.

#	- If there is no zero marker, the macro sets the zero marker to the current selection (the last selection if there are multiple selections) on the active document. The selection may be zero-width (caret).

#	- If the active document has already the zero marker, the macro restore the selection stored in the zero marker.

#	- if the selection stored in the zero marker is exactly the same as the current selection (the last selection if there are multiple selections), the macro clears the zero marker. In other words, you can clear the zero marker by running the macro twice without modifying the current selection.

#	The zero marker consists of a Document object and a TextSelection object stored in the Application Registry (global variable). And a TextSelection object consists of a text object and a character range. So you may not be able to get back to the same position/selection if the portion of document preceding the zero marker has been modified after it was set.

Require Application Version '3.1'  # require NW Pro v1.1 (3.1 is an internal version number)
$doc = Document.active  # get the document object of this document and put it in $doc
if $doc == undefined
	Exit 'No document is open, exit...'
end

$selection = $doc.textSelections.lastValue  # get the current selection -- the last selection if there are multiple selections -- and put it in $selection

$identifier = 'com.nisususerkino.zeromarker.selection'  # you can use another identifier as far as it is unigue and the same as that of Zero Marker Select macro
$appRegistry = Registry.applicationRegistry  # use applicationRegistry which persists until NW Pro quits
$zeroMarker = $appRegistry.loadValueWithName $identifier  # load the value for $identifier from the Registry to $zeroMaker

if $zeroMarker == undefined  # which means that the Registry has no value corresponding to $identifier
	$zeroMarker = Hash.new $doc, $selection  # re-initialize $zeroMaker as a new hash with "key = this document" and "value = the current selection"
else
	if $zeroMarker{$doc} == undefined  # which means that $zeroMarker has no value for this document
		$zeroMarker{$doc} = $selection  # define the value of $zeroMarker for this document as the current selection
	else
		$zeroMarkerRange = $zeroMarker{$doc}.range
		if $zeroMarkerRange.bound > $doc.text.length
			if $zeroMarkerRange.location > $doc.text.length
				$zeroMarkerRange.location = $doc.text.length
			end
			$zeroMarkerRange.bound = $doc.text.length
			$zeroMarker{$doc} = TextSelection.new $zeroMarker{$doc}.text, $zeroMarkerRange
		end
		if $zeroMarker{$doc} == $selection  # if the value of $zeroMarker for this document is the same as the current selection
			$zeroMarker.removeKey $doc  # make the value of $zeroMarker for this document undefined
		else
			$doc.setSelection $zeroMarker{$doc}  # restore the selection stored in $zeroMarker
		end
	end
end

$appRegistry.saveValueWithName $zeroMarker, $identifier  # save the updated $zeroMarker to the Registry as the value of $identifier

### end of macro ###

Code: Select all

### Zero Marker Select (3.1.2) ###

#	This macro selects from the active selection (the last selection if there are multiple selections) to the zero marker. The selection may be zero-width (caret).

#	This macro works as a toggle command and sets or clears the zero marker depending on the situation.

#	- If there is no zero marker, the macro sets the zero marker to the current selection (the last selection if there are multiple selections) on the active document. The selection may be zero-width (caret).

#	- If the active document has already the zero marker and if the text object is the same for the zero marker and for the active selection, the macro selects from the active selection to the zero marker.

#	- If the active document has already the zero marker and if the text object is different for the zero marker and for the active selection (for example, the zero marker is in the footnotes area and the active selection in the body), the macro resets the zero marker to the active selection without modifying the latter.

#	- If the selection stored in the zero marker or the new selection which will cover from the zero marker to the current selection is exactly the same as the current selection (the last selection if there are multiple selections), the macro clears the zero marker and put the caret at the end of the current selection. In other words, you can clear the zero marker by running the macro twice without modifying the current selection.

#	The zero marker consists of a Document object and a TextSelection object stored in the Application Registry (global variable). And a TextSelection object consists of a text object and a character range. So you may not be able to get back to the same position/selection if the portion of document preceding the zero marker has been modified after it was set.

Require Application Version '3.1'  # require NW Pro v1.1 (3.1 is an internal version number)
$doc = Document.active  # get the document object of this document and put it in $doc
if $doc == undefined
	Exit 'No document is open, exit...'
end

$selection = $doc.textSelections.lastValue  # get the current and the last selection and put it in $selection

$identifier = 'com.nisususerkino.zeromarker.selection'  # you can use another identifier as far as it is unigue and the same as that of Zero Marker macro
$appRegistry = Registry.applicationRegistry  # use applicationRegistry which persists until NW Pro quits
$zeroMarker = $appRegistry.loadValueWithName $identifier  # load the value for $identifier from the Registry to $zeroMaker

if $zeroMarker == undefined  # it means that the Registry has no value corresponding to $identifier
	$zeroMarker = Hash.new $doc, $selection  # re-initialize $zeroMaker as a new hash with this document as key and the current selection as value
elsif $zeroMarker{$doc} == undefined  # then, the Registry has $zeroMarker which, however, has no value for this document
	$zeroMarker{$doc} = $selection  # define the value of $zeroMarker for this document as the current selection
else  # then, the Registry has $zeroMarker which has a value for this document
	if $zeroMarker{$doc} == $selection  # if the value of $zeroMarker for this document is the same as the current selection
		$zeroMarker.removeKey $doc  # make the value of $zeroMarker for this document undefined
		$range = Range.new $selection.bound, 0  # create a zero-width range corresponding to the selection end
	elsif $zeroMarker{$doc}.text == $selection.text  # if the value of $zeroMarker for this document is differnt from the current selection but belongs to the same text object
		$allSelections = Array.new $zeroMarker{$doc}, $selection  # create an array containing both selections
		$allSelections.sort  # sort $allSelections by location
		$bound = $allSelections[1].bound  # which corresponds to the boundary of the selection closer to the document end
		if $bound > $selection.text.length  # if it is beyond the document end
			$bound = $selection.text.length  # reset $bound to the document end
		end
		$range = Range.newWithLocationAndBound $allSelections[0].location, $bound  # create a range covering both selections
		if $range == $selection.range  # if the range covering both selections is identical to that of the current selection
			$range = Range.new $selection.bound, 0  # re-initialize $range as a zero-width range corresponding to the selection end
			$zeroMarker.removeKey $doc  # make the value of $zeroMarker for this document undefined
		end
		$newSelection = TextSelection.new $selection.text, $range  # create a TextSelection object corresponding to $range
		$doc.setSelection $newSelection  # select $newSelection
	else  # if the value of $zeroMarker for this document is differnt from the current selection and belongs to a different text object
		$zeroMarker{$doc} = $selection  # redefine the value of $zeroMarker for this document as the current selection
	end
end

$appRegistry.saveValueWithName $zeroMarker, $identifier  # save the updated $zeroMarker to the Registry as the value of $identifier

### end of macro ###

js
Posts: 229
Joined: 2007-04-12 14:59:36

Re: Can a variable be made to survive a macro?

Post by js » 2009-03-02 14:21:07

Thanks, Kino. Your macros are obviously far more sophisticated, and I find they work very well. Still, in some circumstances we might want a simple macro that finds the spot of it's origin, in whatever document you are.

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

Re: Can a variable be made to survive a macro?

Post by Kino » 2009-03-02 23:27:52

js wrote:Still, in some circumstances we might want a simple macro that finds the spot of it's origin, in whatever document you are.
How about this one?

Code: Select all

#	This macro works as a toggle command and defines or remove $markedSelection (document object and text selection object) depending on the situation.
#	if $markedSelection is not defined, use the current selection to define it.
#	if $markedSelection is defined, the macro restores the selection and removes it except that...
#		if the document has become too short to restore the selection, the macro puts the caret at the end of the text object (document end if it is body text).
#		if the document is not open anymore, the macro will not try to open it.

Require Application Version '3.1'
$doc = Document.active
if $doc == undefined
	Exit 'No document is open, exit...'
end
$sel = $doc.textSelection

$message = ''
$identifier = 'com.usermacro.markedSelection'
$appRegistry = Registry.applicationRegistry
$markedSelection = $appRegistry.loadValueWithName $identifier

if $markedSelection == undefined
	$markedSelection = Array.new $doc, $sel
	# create an array with the active document and text selection objects
	$appRegistry.saveValueWithName $markedSelection, $identifier
	# save $markedSelection as $identifier in $appRegistry
	$message = 'This selection has been saved as marked.'
else  # i.e. if $markedSelection is defined
	$markedDoc = $markedSelection.firstValue  # put the document object in $markedDoc
	$markedSel = $markedSelection.lastValue  # put the text selection object in $markedSel
	$docs = Document.openDocuments  # put document objects of all open documents in $doc (array)
	$check = $docs.indexOfValue $markedDoc
	if $check == -1  # i.e. if $markedDoc is not found in $docs
		$message = 'Marked document is not open anymore.'
	else
		Document.setActive $markedDoc
		$len = $markedSel.text.length
		if $markedSel.bound > $len
			$message = 'This document has become too short to restore the original selection.'
			$markedSel = TextSelection.newWithLocationAndLength $markedSel.text, $len, 0
			# redefine $markedSel so as to make a zero-width selection at the document end
		end
		$markedDoc.setSelection $markedSel
	end
	$appRegistry.removeValueWithName $identifier  # clear $markedSelection
end

if $message  # i.e. if $message is not empty...
	exit $message
end

### end of macro ###
If you find annoying some of the messages, just remove the corresponding $message = '...' or the last "if ... end" block.

I think it would be great if Nisus Writer Pro Macro has a way of notification which does not force the user to click OK button, something like what Growl does.

Post Reply