OvationPro2.56(05-Nov-99)Xz  Pd6HSVnddB $  L L k @0 8  tn L  GenericBlack!fWhitefTransparent fRedfGreenfBluefCyan!fMagenta!fYellow !fRegistration Af `.HSVn:Pff.- @DkP-^0zv/;Pn'Homerton.BoldHomerton.Medium.ObliqueHomerton.MediumTrinity.MediumCorpus.Mediumy  Bodytext#/z''-6P33$ Section-Dk.. 70P33, Titlext-Dk]]@n6P33pY SmallTitle/z66?6P333 FunctionName-Dk''-6P33D% MacroName0''-6P33$ Codeext''8.0P33% Listext( /z''-6P33$ nFCaption0@@$0P33     Meg8/z''-6P33$ V-Dk]]@n6P33pY 3Q"/z66?6P333 NV-Dk.. 70P33, eg8-Dk''-6P33D% *X ''8.0P33% *X  ''8.0P33% W_0@@$0P33 *X  ''8.0P33% *X . ''8.0P33% ME/z ''-6P33$ MF/z''-6P33$ *X G''8.0P33% *X J&'8.0P33% *X K@''8.0P33% *X L@''8.0P33% AL:S>Z3K|y.S~KVGFR JNVp3Q"xV*X W_Meg8 NVp3Q"xV*X W_Meg8 L$L  !MainDict!UserDict!Ignore6   @  L A4  tt  L  44   nn7. nn7. l`yy! #0ccMR l X]7:  @ 0(XP ?B F- HH   4$ HH Ø 4$ HF  4$JpP L A4  t ttzL J<64 'nn7. nn7. l`yy! #0ccMR l  X]7{tFG EH& 55444Last month, I gave a brief introduction to some of the simpler things that are possible with scripts in Ovation Pro. However, as with most such overviews, this doesnt give much of an idea of the extent of the possibilities. To rectify this, Im now going to show how to add two new commands, both of which are actually useful; in doing this, some of the features I didnt cover last month will also crop up. nOvPro has built-in case- changing abilities, but these are limited to all capitals and title case, the first letter of each word in the selection is changed to upper case. However, it- is possible to have other variants, such as sentence case, in which the first letter of the first word in the selection is capitalised. nIn order to do this, we run into one of the limitations of macros - the 256-character limit of the icon used to enter the text. This macro will need more than this, meaning that we must break it up into smaller steps. Of course, it is also possible to get past this limit by using a different form of script, such as a separate file (run by double-clicking) or an applet, but this is not necessary here. nAlthough splitting the macro is a necessity, it is also a useful thing to do, for two important reasons: first, it makes it easier to follow the macro, because it is essentially broken up into functions; second, it means that we can reuse these component functions in other macros without having to rewrite them. every time. User macros In order to break up the large macro, we make use of user macros. These areI basically the same as button and key macros except that they have no associated button or keypress. This means that they cannot be run directly but instead are called from other macros. Just as, last month, we had to call {NLock} to flush the effect buffer so we could reset the normal text, we can call our own user macros by name from within other macros. nTo decide how we should break up the SentenceCase macro, we must analyse it to see what tasks are needed. First, we need to mark the beginning and end of our selected zone. Second, we must select (highlight) the word at the beginning of the selected zone. Third, we should select the first letter of this word and then capitalise it. Last, we must delete all the bookmarks we have used. Marking the selection Although we only need to know the beginning of the selected zone in SelectZone user macro will to use in other macros, too. This means that it will pay to write it with possible future uses in mind. Thus, Figure I shows a macro that will set bookmarks (ZoneStart and ZoneEnd) to the start and end of the selection, respectively. bG nFirst, the macro creates the bookmarks. Then, the position of each is set to the beginning or end of the selected zone. Finally, the caret is set to the position of the bookmark ZoneStart, which is at the start of the selected zone, so that we can be sure of where it is. Selecting the word This involves two more user macros, purely because of the length restriction on macros. Figure II shows these two macros, and also shows how one is called from the other. It is important to remember that we cant be sure whether or not the selected zone contained the whole of the first word, so we need to check this. cMakeWdBM is a separate function that makes the bookmarks for the word selection. These are first defined, with names WordStart and WordEnd, and then each is set to the position of the caret. This macro is called from the SelectWord macro simply by using its name in curly brackets. Once this is done, we can start using these bookmarks. However, we must create new variables to handle them with, because variables in OvPro scripts are local to the macro in which they are defined. Ή nHaving created handles for the bookmarks, we can then move them until they are at the beginning and end of the word. Thus, the first bookmark is moved backwards and the second is moved forwards. The movement is controlled by a while statement, because we want the bookmarks to move only until they run into one of the characters that indicate the start or end of a word. nThe characters we are looking for are, in the order they appear in Figure II, the start of the story, a new line or a space. The characters are referred to by their ASCII numbers, with the start of the story being indicated by -1 (not 0 as stated in the documentation). The ! in the while statement indicates a logical Not, and the ll characters indicate a logical Or. Thus, the first while statement can be read as: while the character before the bookmark does not have the value -1 or 10 or 32, move the bookmark back one character. nThe second while statement does the same for the WordEnd bookmark, although here, it is looking for whether the next character is. the end of the story (confusingly indicated by a 0 from bmchar - the documentation is correct here) in addition to a new line or a space. While the next character is not one of these, the bookmark is moved forward one character. nFinally, the caret is set to the beginning of the I word (via the bookmark positioned there) and the selected zone set to extend to the second bookmark, at the word. Deleting the bookmarks The final user macro we need for this is one to delete the bookmarks we have used. Again, to make it as generally useful as possible, it would be good if we didnt just delete the named bookmarks but all of them. Figure Ill shows the DelBM macro, which uses the bmname function to scan all the bookmark handles that have been used and then delete the relevant bookmark; bmname returns in s the name of the bookmark whose handle was passed in a. The function returns 0 when there are no more bookmarks, terminating the while loop. WyΉ nThe important point to notice in Figure III is the use of the ++ operator. As described last month, this adds one to the value of the variable each time the statement is executed. When placed after the variable, as here, the incrementing is done after the variable is used in the statement. Thus, we must subtract 1 from a before deleting the bookmark, to return it to the value it had when the while statement was executed. nThis macro deletes all the bookmarks in the current document, which may not be what is wanted there may be bookmarks left from other macros that need to be kept. If this is the case, you can easily add a check for these with an If statement, along the lines: "If(!((s==markl)\ \(s==mark2))) {bmdelete(a1) ;};"  The final macro Figure IV shows the macro that we end up with in order to achieve our sentence-case macro. Once all these tasks have been removed to user macros, it actually looks fairly simple. First, we call the SelectZone macro, which sets a bookmark to the beginning of the selected region. Then the SelectWord macro scans the text around that bookmark and sets further bookmarks to surround the word containing it (the start and end of a word being defined as the start or end of the story, a new line, or a space). Ή nOnce the word is selected, the SentenceCase macro creates a new bookmark and defines another variable to point to the bookmark that indicates the start of the word. This is because it needs to use that bookmark and it cannot use the previous variable because that was assigned in a different macro. The new bookmark is set to the beginning of the word by putting it in the same place as the WordStart bookmark. nThis new bookmark is then moved one character to the right so that the two bookmarks surround the first character of the word. Setting the zone to extend to the new bookmark means that this character is now highlighted, because the SelectWord macro sets the caret to the start of the word. Setting the case to 2 means all capitals, giving us our initial capital letter. Finally, the bookmarks are deleted from the document by calling DelBM. nIt would be possible to extend this macro to true sentence case by, for example, checking that there was a full stop before the first word or by setting all the letters except the first to lower case. The first of these is fairly easy, but unnecessary (I usually assume that people want macros to do what they demand, rather than second-guessing them). The second is rather harder, because there is no lower case command available by default, so we would have to add it by hand by manipulating the ASCII codes. Switching words OvPro has a very useful facility built in if two letters are the wrong way round (for example, I might type this worngly), Ctrl- Shift-Q will swap them around (worngly becomes wrongly). However, it is also possible to write a macro that will do the same thing for whole words. This will reuse two of the user macros we used above and implement one more. nIn order to do this, we must select the word containing the caret, setting bookmarks to its beginning and end (using SelectWord). Then, we need to move to select the next word and set bookmarks to its beginning and end. After this, we must cut the first word out and paste it in on the other side of the second word, preferably taking the spaces with us so that the words remain separate. Finally, we must delete all these bookmarks. nSelecting the next word is similar to the SelectWord macro but with a few additions, and is shown in Figure V. First, we create two bookmarks to be set to the two ends of the new word. One of these is set to the end of the first word by using bmfind within the setbmtobm function, to avoid defining a variable to handle a bookmark we only need once in this macro. nThe new bookmark is then moved to the start of the next word using a do...while loop, which is exactly the same as a normal while loop except that the condition comes at the end of the loop. This means that the statement in the middle will always be executed at least once, whereas the statement in the normal while loop may never get executed. In this instance, we are definitely going to want the bookmark to move at least one character (so that it is past the space between the words). Ή nThe bookmark is moved until the character after it is not a new line or a space. Once the start of the new word has been located, the second new bookmarks is also set to that position. Now, one bookmark is moved through the word until the end is reached, using the same conditions as before (the character following the end of the word must be the end of the story, a new line or a space). nFinally, the caret is set to the beginning of the word and the selected zone set to extend from there to the end of the word, as we did in the SelectWord macro (it is useful to have the two macros behaving in the same way!). Switching the two words Figure VI shows the Switch words macro. As before the unique body of the macro is relatively simple once the separate user macros have removed much of the complication. nFirst, bookmarks are set to the beginning and end of the word containing the caret using SelectWord this is why we made that macro more general than we needed at the time - user macros may be used by several master macros, so it pays to make them general, to save writing two or three that are very similar. Next, bookmarks are also set around the following word. nThe two macros SelectWord and NextWord select only the text of the word itself, not the trailing spaces, largely because there is no space left to add this code. For the SentenceCase macro, this was irrelevant, but here, we need them to avoid having the words running into one another. Thus, we need to add two If statements if there is a space after either of the bookmarks that denote the end of a word, move that bookmark one character forwards. nHaving done this, all that remains is to cut and paste the first word. Thus, we need to select that word (set the caret to the start and then set the zone to extend to its end) and cut it, then move the caret to the end of the second word and paste the first word in there. Finally, we again need to delete all the bookmarks that we have used, using the DelBM macro. Do it yourself-.. I hope that this pair of articles has given you a flavour of the sort of thing that can be achieved using macros in Ovation Pro. They can extend from saving a little bit of typing to adding whole new functions and options. The macros Ive described in this article and the last one are on the cover disc; if you dont want to type them in from the magazine, you can have a look at this file for some tips. Drag it onto the OvPro iconbar icon and the buttons will appear on the toolbar. nThere are some extremely useful third- party macros and applets around on the Net and In other places. For example, me of my personal favourites is SRWE by Stephen Brown, which extends the normal find and replace option to allow you to specify the effect or style of the text you wish to find, or to use macros in tour replace text - so that you can insert text in superscript, for example. nHowever, even fairly simple macros can produce useful functions and I hope Ive encouraged some of you out there to have a go. Its easy, fun and even saves effort in the long run. Ήng tha ?HsF-ȸ ?(F-U ?сF- ?F-l ?.F-8 ?]F-bd ?F-a ?hF-U ?HF-z ?(F-20 ?HF-u ?vF-~< ?ȥF-t ?ԃF-V% ?F-^Y ?h2F-6 ?HaF-Nd ?(F-n ?F-_ ?F-`f ?F- ?KF-|s ?zF-u ?hF-T ?H؅F-] ?(F-T ?6F-0 ?dF-vs ?ȓF-`f ?†F-H~ ?F-"| ?h F-~\ ?HOF-w ?(~F-؄ ?F-w ?ۇF-0d ? F-R ?9F-*o ?hF-t ?hF-: ?HƈF-T ?(F- ?$F 7@ ?H\F-y ?(F-u ?F- ?F-,| ?F-jf ?FF-x ?uF-R ?hF- ?HӊF-z ?(F-b ?1F-p ?_F-$W ?ȎF-x'p ?F-tfςaHsL-yςa(L-]ςaL-ςaL-tςa.L-jςa]L-tfςaL-FqςahL- ςaHL-8ςa(L-ςaHL 7LhςaHL-]ςa(L-;ςaL-bsςa L-yςa;L-dςajL-|ςaL-txςaL-ςaHL-ςa#L"PCςaZuL-ςa:L-(JςaL-:ςaL-2bςa0L-Tςa_L-$ςaL-,|ςazL-ςaZL-@ςa: L-ςaJ L 7Ġ\ςaZ L-yςa: L-tςa L-ςa L-Uςa= L-"|ςal L-F-lF@BmF-H4DF@"F-+|F@ˉF-jF@F-wF@(F-|F@WF-sF@F-*oF@bF-_F@BF-&pF@"F-*oF@BF-hF@pF-GPF@ŸF-8t ?B F-rY ?" L-ςaBm L-΄ςa" L-Ndςa L-Dpςa L-6tςa( L-zςaW L-|ςa L-xςab L-8%|ςaB L-rYςa" L-8ςaB L-ςap L-ZqςaŸ L-:dF@B F-lsF@"bGKv> _Ig =  e3S | t X]7҆ /* SelectZone */ { int a=bmcreate("ZoneStart") int b=bmcreate("ZoneEnd"); setbmtozone(a,b); setcarettobm(a); } Figure I: A macro to set bookmarks to the start and end of the currently selected zone of text. , 7 A8.ph 7;A8. 7jA8.p 7A8.xp 7A8.`al 7kA8.pH 7K&A8.wD 7+UA8.p 7 A8. 7A$(= 7.A$z 7A$; 0 HH  o>1t t rΉcr<v> _I!   #3 m,t X]7%0 `%%%/* MakeWdBM */ { .int a=bmcreate("WordStart"); int b=bmcreate("WordEnd"); setbmtocaret(a); setbmtocaret(b); } /* SelectWord */ {MakeWdBM} {  int a=bmfind("WordStart"); int b=bmfind("WordEnd"); . while(!((bmprevchar(a)==-1)  || (bmprevchar(a)==10) || (bmprevchar(a)==32))) { bmmove(a,0,0); } while(!((bmchar(b)==0) || (bmchar(b)==10) || (bmchar(b)==32))) { bmmove(b,1,0); } setcarettobm(a); setcarettobm(b); } Figure II: The two macros that together select the word around the caret.  U7 98._` U<98. Uj98.p Uי98.r. UȀ98.`al U98.wD Uw&98.wD UWU98.p U798. U98.wd U98.`, U98.p U?98.r Un98.Ih Uw98.r. UŴ98.p U798.Ih U*98..  UX98.wD Uׇ98..  U98.`al U98. \ Uw98.T UWC98..  U7r98.wD U98..  Uτ98.pH U98.pH U-98.p U\98. U:9$p U9$  HH : st  {yL ΉWycMhMv> _I C  #3 Dt X]7V;l E/* DelBM */F G{ int a=0; string s; while(bmname(a++,s)) { bmdelete(a-1); } } Figure III: A short macro to delete all bookmarks from the document.@J@ U8 9-ET U<9-F Uj98.pG U98.G( UȀ98.`G, Ue98.0GX UE&98..G  U%U98.wGD U98..G  U岁98.pG U98.G Uh9$ U59$ t U HH  s1ct 0 {D>Ήcfv> _I L  #3  ߃t X]7, /* SentenceCase */G {SelectZone} {SelectWord} G{ int a=bmcreate("A"); J int b=bmfind("WordStart");G setbmtobm(a,b); setzonetobm(a); setcase(2); } {DelBM} Figure IV: The finished SentenceCase macro. h,,, U 98.Pp U;98.G Uj98.@G4 U98.@G4 UȀ98.pG Ut98.0GX UT&98.pJ U4U98.wGD U98.wGD U98.@G4 U98.pG U98.G  U?98.G U7n9$ U9$_  HH  st P y ΉcM*v> _I /  #3 T X]7V;,#1 /* NextWord */G G{ int a=bmcreate("New1"); G int b=bmcreate("New2");G setbmtobm(b, bmfind("WordEnd"K))G; J do { bmmove(b,1,0); } while(bmchar(b)==10) || bmchar(b)==32)); G setbmtobm(a,b); while(!((bmchar(b)==0) || (bmchar(b)==10) || (bmchar(b)==32))) { bmmove(b,1,0); } setcarettobm(a); setzonetobm(b); } Figure V: Selecting the word following the one last selected with SelectWord.X 4t|D U& 98._` U<98.G Uj98.pG Uƙ98.2Gd UȀ98.2G U98.0G8 Ui$9 2`aG U1W98.oEJ  U98.J.J  U98.PrJD U98.J.J  U98.x+Jd UA98.SJ` Uqp98.wGT UQ98.`aGl U1΂98.`aGl U98.IGh U+98..G  UZ98.wGD U98..G  U98.pGH Uq98.wGD UQ98.pG U1E98.G Us9$G UT9$  U HH ) st  Ήcʮv> _I /  #3 t X]73 /* SwitchWords */G {SelectWord} {NextWord} G{ int a=bmfind("WordEnd"); G int b=bmfind("New2");G GJG if(bmchar(a)==32) { bmmove(a,1,0); } if(bmchar(b)==32) { bmmove(b,1,0); } setcarettobm(bmfind("WordStart"L)G); setzonetobm(a); cutselection(); setcarettobm(b); pasteselection(); } {delBM} Figure VI: Moving the word containing the caret one word to the rightcontDD U& 98.l U<98.G Uj98.@G4 Uƙ98.`G, UȀ98.pG U98.IGh Uf&98.G| UFU98.G| U&98..G  U98.wGD U98..G  U98.GL U?98..G  Un98.wGD Uf98..G  UF̂98.`aGh U29,0GH U*98.wGD UX98.wGD UƇ98.pGH U98.GL U98.pG Uf98.G  UFC98.G Uq9$XI Ui9$k p U H ) st