in Indexing

Automated Letter-by-Letter Index Sorting

Last week's newsletter explained how to sort Word's index entries letter by letter rather than word by word:

Using "Found" Macros

But it didn't explain how to do it in an automated way. So here's an automated way, using wildcard Find and Replace. You can learn more about using wildcards in some of our past newsletters:

http://www.topica.com/lists/editorium/read/message.html?mid=1705963026

http://www.topica.com/lists/editorium/read/message.html?mid=1706069286

http://www.topica.com/lists/editorium/read/message.html?mid=1706069286

http://www.topica.com/lists/editorium/read/message.html?mid=1706069286

http://www.topica.com/lists/editorium/read/message.html?mid=1706365638

http://www.topica.com/lists/editorium/read/message.html?mid=1706458823

Here's the procedure, which you can record as a macro for future use. Or, better yet, just use the prerecorded macro I've included at the end of this article. Work through the procedure if you want to know more about using complicated searches, or if you just want to see how my devious little mind works. (There's probably a better way to do all this using Visual Basic for Applications, but that's a subject for another day.)

1. Make sure your index entries are visible by showing hidden text (Tools > Options > View > Hidden text).

2. Find the index entries and replace them with themselves colored as, say, plum, so your Find and Replace won't move across entry borders later:

Find What:

(XE "*")

Replace With:

1 (formatted as plum)

Use Wildcards:

Checked

3. Replace escaped colons and quotation marks with arbitrary symbols to be changed back later ("escaped" means they have a backslash in front of them, telling Word to treat them as characters, which is how you can use colons and quotation marks in your index entries!):

Find What:

:

Replace With:

&&&

Use Wildcards:

Unchecked

Find What:

"

Replace With:

@@@

Use Wildcards:

Unchecked

4. Put a colon after main-only (but actually, after all) entries:

Find What:

(XE "*)(")

Replace With:

1:2

Use Wildcards:

Checked

5. Find plum-colored, multiple-word index entries and enter semicolon entries, going from three spaces to one space, which ought to be enough for anybody (and besides, Word can only handle up to five "Find What Expression" wildcards):

Find What:

(XE ")([! :]@) ([! :]@) ([! :]@) ([! :]@): (formatted as plum)

Replace With:

12 3 4 5;2345:

Use Wildcards:

Checked

Find What:

(XE ")([! :]@) ([! :]@) ([! :]@): (formatted as plum)

Replace With:

12 3 4;234:

Use Wildcards:

Checked

Find What:

(XE ")([! :]@) ([! :]@): (formatted as plum)

Replace With:

12 3;23:

Use Wildcards:

Checked

6. Clean up colons at ends of entries:

Find What:

(XE "*):(") (formatted as plum)

Replace With:

12 (formatted as Automatic, which gets rid of all plum)

Use Wildcards:

Checked

7. Restore escaped colons and quotation marks, if any:

Find What:

&&&

Replace With:

:

Use Wildcards:

Unchecked

Find What:

@@@

Replace With:

"

Use Wildcards:

Unchecked

Now move to the bottom of your document and have Word generate your index (Insert > Index and Tables > Index). Well, look at that: The entries are sorted letter by letter. Neat!

If you want to work manually, you can insert a semicolon and alternate spelling after a main index entry to force Word to sort in any way you like. For example, let's say you've got some numbers in your index, ordered like this:

8123

835

86

Ordinarily, that's how they'd sort. But if you edit your index entries like this--

{XE "835;0835"}

{XE "86;0086"}

{XE "8123;8123"}

--you'll make them sort like this:

86

835

8123

If you really wanted to, you could even do something weird like this--

{XE "Zebra;1"}

--and force "Zebra" to the top of your index.

Pretty handy, no? At any rate, you now have an automated way to sort Word's index entries letter by letter. Enjoy!

Here's the prerecorded macro to make index entries sort letter by letter. If you don't know how to use such macros, you can find out here.

'THE MACRO STARTS HERE
Sub IndexEntriesLetterByLetter()
Selection.HomeKey Unit:=wdStory
Selection.Find.ClearFormatting
Selection.Find.Replacement.ClearFormatting
Selection.Find.Replacement.Font.Color = wdColorPlum
With Selection.Find
.Text = "(XE "")([! :]@) ([! :]@):"
.Replacement.Text = "12 3;23:"
.Forward = True
.Wrap = wdFindContinue
.Format = True
.MatchCase = False
.MatchWholeWord = False
.MatchAllWordForms = False
.MatchSoundsLike = False
.MatchWildcards = True
End With
Selection.Find.ClearFormatting
Selection.Find.Replacement.ClearFormatting
Selection.Find.Replacement.Font.Color = wdColorPlum
With Selection.Find
.Text = "(XE ""*"")"
.Replacement.Text = "1"
.Forward = True
.Wrap = wdFindContinue
.Format = True
.MatchCase = False
.MatchWholeWord = False
.MatchAllWordForms = False
.MatchSoundsLike = False
.MatchWildcards = True
End With
Selection.Find.Execute Replace:=wdReplaceAll
Selection.Find.ClearFormatting
Selection.Find.Replacement.ClearFormatting
With Selection.Find
.Text = ":"
.Replacement.Text = "&&&"
.Forward = True
.Wrap = wdFindContinue
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
End With
Selection.Find.Execute Replace:=wdReplaceAll
With Selection.Find
.Text = """"
.Replacement.Text = "@@@"
.Forward = True
.Wrap = wdFindContinue
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
End With
Selection.Find.Execute Replace:=wdReplaceAll
Selection.Find.ClearFormatting
Selection.Find.Replacement.ClearFormatting
With Selection.Find
.Text = "(XE ""*)("")"
.Replacement.Text = "1:2"
.Forward = True
.Wrap = wdFindContinue
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchAllWordForms = False
.MatchSoundsLike = False
.MatchWildcards = True
End With
Selection.Find.Execute Replace:=wdReplaceAll
Selection.Find.ClearFormatting
Selection.Find.Font.Color = wdColorPlum
Selection.Find.Replacement.ClearFormatting
With Selection.Find
.Text = "(XE "")([! :]@) ([! :]@) ([! :]@) ([! :]@):"
.Replacement.Text = "12 3 4 5;2345:"
.Forward = True
.Wrap = wdFindContinue
.Format = True
.MatchCase = False
.MatchWholeWord = False
.MatchAllWordForms = False
.MatchSoundsLike = False
.MatchWildcards = True
End With
Selection.Find.Execute Replace:=wdReplaceAll
Selection.Find.ClearFormatting
Selection.Find.Font.Color = wdColorPlum
Selection.Find.Replacement.ClearFormatting
With Selection.Find
.Text = "(XE "")([! :]@) ([! :]@) ([! :]@):"
.Replacement.Text = "12 3 4;234:"
.Forward = True
.Wrap = wdFindContinue
.Format = True
.MatchCase = False
.MatchWholeWord = False
.MatchAllWordForms = False
.MatchSoundsLike = False
.MatchWildcards = True
End With
Selection.Find.Execute Replace:=wdReplaceAll
Selection.Find.ClearFormatting
Selection.Find.Font.Color = wdColorPlum
Selection.Find.Replacement.ClearFormatting
With Selection.Find
.Text = "(XE "")([! :]@) ([! :]@):"
.Replacement.Text = "12 3;23:"
.Forward = True
.Wrap = wdFindContinue
.Format = True
.MatchCase = False
.MatchWholeWord = False
.MatchAllWordForms = False
.MatchSoundsLike = False
.MatchWildcards = True
End With
Selection.Find.Execute Replace:=wdReplaceAll
Selection.Find.ClearFormatting
Selection.Find.Font.Color = wdColorPlum
Selection.Find.Replacement.ClearFormatting
Selection.Find.Replacement.Font.Color = wdColorAutomatic
With Selection.Find
.Text = "(XE ""*):("")"
.Replacement.Text = "12"
.Forward = True
.Wrap = wdFindContinue
.Format = True
.MatchCase = False
.MatchWholeWord = False
.MatchAllWordForms = False
.MatchSoundsLike = False
.MatchWildcards = True
End With
Selection.Find.Execute Replace:=wdReplaceAll
Selection.Find.ClearFormatting
Selection.Find.Replacement.ClearFormatting
With Selection.Find
.Text = "(XE ""*):("")"
.Replacement.Text = "12"
.Forward = True
.Wrap = wdFindContinue
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
End With
Selection.Find.ClearFormatting
Selection.Find.Replacement.ClearFormatting
With Selection.Find
.Text = "&&&"
.Replacement.Text = ":"
.Forward = True
.Wrap = wdFindContinue
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
End With
Selection.Find.Execute Replace:=wdReplaceAll
With Selection.Find
.Text = "@@@"
.Replacement.Text = """"
.Forward = True
.Wrap = wdFindContinue
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
End With
Selection.Find.Execute Replace:=wdReplaceAll
End Sub
'THE MACRO ENDS HERE

_________________________________________

READERS WRITE

The newsletter for July 31 explained how to make punctuation easier to see by formatting it as big, bold, and red; the following newsletter offered some reader tips on doing so; and last week's newsletter included a terrific macro by Phil Rabichow to automate the process.

This week, Steve Hudson provides a similar macro as an exercise in Visual Basic programming (and of course, it's useful, too!). It's a toggle, so you'll probably want to put the subroutine named SomeToolbarNameToggleBigPunctuation() on a toolbar button for easy access. You can learn how to do this here:

http://www.topica.com/lists/editorium/read/message.html?mid=1707286867

As you look through the macro, pay particular attention to Steve's comments, which explain what's going on. The macro is an excellent example of VBA, including find loops, wildcard matching, range objects, optional parameters, design analysis, and much more. Actually, the whole macro has many left-of-center concepts--for example, a Find that can return nothing yet still not have finished (it picks up the Chr$(7) that marks a table start, which can't be included in the wildcard entry). The macro also nicely changes the cursor and screen updating, and it backs out formatting changes. Steve is a master at this kind of stuff, so get ready to learn something. And why not contact Steve to take care of your custom programming needs?

By the way, you'll note Steve's humor throughout. Here is Steve's commentary, edited slightly by me to fit the context of this newsletter:

The past newsletters found and formatted punctuation by inclusion, which rules out Unicode and the like. So I figured a macro would be better with punctuation by exclusion in order to show up all sorts of strange dweebs.

The second problem was your suggestion to create a temporary style that mucks my document up with no subsequent hint of destruction.

The third was "How do I reverse this?" If I have a formula in character styling and another elsewhere in terminal screen, I can't find this style and kill it dead with a known something else. So I have to transpose my edits to have a safe working practice. Ack.

The fourth problem was not acknowledging No Proofing on styles.

So the first one is easy enough, we simply start the square brackets formula for the find with a ! and then what we are not interested in. Thus the string for a trivial solution is [!A-Za-z0-9^160]. Note the ^160 for the nonbreaking space; Word honours the ole caret at a higher priority than anything else. Basically, this finds anything *except* a letter, a number, or a nonbreaking space, but I've added additional characters in the macro itself.

The second is also easy. We all use STYLES like good little folk, so let's keep those in place and add--oooo--MANUAL FORMATTING. We can hunt it down and undo it easy enough later by resetting that range to its underlying style again, so it's a temporary aberration for our temporary aberration. That's the third taken care of as well.

Now, the fourth and last--is there any way we can tell the Find to do only those styles with proofing set? Err, no. So we have to check this ourselves manually. 🙁 Word's find falls down spectacularly with stuff like this. Easy enough, and should be faster as we are doing less styling work, which is expensive.

Now some little style notes--I prefer to use ranges and let the users have their selection object. It may be faster running selection.finds, dunno, duncare 🙂 It makes the resulting code smaller and easier to read and allows me sick puns--the last being the most important reason of course. What's the use of knowing all this junk if you can't have fun with it, I say. I know that some folks hate nonstandard variable naming, but I like code that is human readable.

Next, always reset stuff back to the way it was. If users do a find, do they want your settings?

'THE MACRO STARTS HERE
Public Sub SomeToolbarNameToggleBigPunctuation()
'Attach this to a button on a toolbar
'we use the toolbar name to start the sub so we know where
'this sub is called from
Static Toggle As Boolean
Toggle = Not Toggle
ProofReadBigPunctuation Toggle
End Sub
Public Sub ProofReadBigPunctuation(MakeBig As Boolean, Optional Scope As Range)
'$--------
Author: heretic [at symbol] tdfa.com
'$Short: If makebig is true, it makes punctuation marks BIG
'to aid proofreading as discussed by Editmeister Jack.
'If makebig is false, it removes this formatting
'$Known issues: Destroys original doc highlighting IF
'highlighting is used in the formatting process.
Dim HomeOnThe As Range 'Our findermatic
Dim Finished As Boolean
Dim Progress As Double 'enough space for calcs in big dox
'Be nice, don't assume system settings
Dim Cursor_pholder As WdCursorType
Dim ScreenUpdating_pholder As Boolean
'frilly bits: change the cursor whilst we work!
Cursor_pholder = System.Cursor
System.Cursor = wdCursorWait
'freeze the screen to speed things up
ScreenUpdating_pholder = Application.ScreenUpdating
Application.ScreenUpdating = False
'This basic technique can be used in as many ways as Bill
'has bux.
'________________________________
'Give our range a document range to hang off.
'The first .dupe is strictly speaking unnecc but a good habit
'to get into with range objects--the second one prevents us
'changing the passed scope without realising it!
'We avoid the other parts of the document by setting our
'ranges parent to be the range that is the content only.
'I regularly use Activedocument.Content too.
'Also note how we deal with no explicit scope being passed
'from that Optional parameter
If Scope Is Nothing Then Set Scope = _
ActiveDocument.StoryRanges(wdMainTextStory).Duplicate
Set HomeOnThe = Scope.Duplicate
'Collapse our range to a point at the start of the doc
'main body content, just like a cursor in a virgin document.
HomeOnThe.Collapse
'We don't have to clearformatting or anything as it's
'a whole new range. Just set up the find
'$Customize: the ! means NOT anything in the following list
With HomeOnThe.Find
'note we don't use If MakeBig = True then
If MakeBig Then 'errant chars
.Text = "[! 0-9A-Za-z^9^12^13^160]" '$Customize
.MatchWildcards = True
Else '$Customize: formatting
With .Font
.Color = wdColorRed
.Bold = True
End With
.Highlight = True
End If
End With
'Get started on a standard manual processing find loop.
While Not Finished
'let the poor user know where we are at
Progress = Int(HomeOnThe.End * 100 / Scope.End)
Application.StatusBar = "Restyling " & Format(Progress) & "%"
'ensure the statusbar change gets through
DoEvents
'the find!
With HomeOnThe.Find
.Execute Replace:=wdReplaceNone
Finished = Not .Found
End With
'Our range is now either null, meaning nothing found
'or it contains a range for us to examine
If Not Finished Then 'we caught one!
If MakeBig Then 'style if proofing on
If Not ActiveDocument.Styles(HomeOnThe.Style).NoProofing Then _
FormatFontGruesome HomeOnThe
Else 'unstyle
HomeOnThe.Font.Reset
HomeOnThe.HighlightColorIndex = wdNoHighlight
End If
End If
HomeOnThe.Collapse wdCollapseEnd 'so we keep moving along
Wend 'finished
'destroy our objects
Set HomeOnThe = Nothing
'reset our changes
Application.ScreenUpdating = ScreenUpdating_pholder
System.Cursor = Cursor_pholder
Application.StatusBar = "Finished"
End Sub
Private Sub FormatFontGruesome(Scope As Range)
'$Short: to make shtuff shtand out shorty, you
'gotta problem wid dat?
'$Customize: don't forget to keep this matched with the
'find requirements for the undo
'I don't like the thought of changing font name
'as that could change the displayed character
'Lets just make it BIG, BOLD and RED on a YELLOW background
With Scope.Font
.Color = wdColorRed
.Bold = True
.Grow
.Grow
.Grow
End With
Scope.HighlightColorIndex = wdYellow
End Sub
Sub TestProofReadBigPunctuationOn()
Dim i As Long
Application.ScreenUpdating = False
Documents.Add
DoEvents
For i = 27 To 1200
Selection.InsertAfter ChrW(i)
Next i
ProofReadBigPunctuation True
Application.ScreenUpdating = True
End Sub
Sub TestProofReadBigPunctuationOnSimple()
'run this on any trial doc to be sure
ProofReadBigPunctuation True
End Sub
Sub TestProofReadBigPunctuationOff()
ProofReadBigPunctuation False
End Sub
'THE MACRO ENDS HERE

Many thanks to Steve for the macro and for the tutorial.

_________________________________________

RESOURCES

If you're looking for indexing resources, don't miss those supplied by the School of Library, Archival and Information Studies at the University of British Columbia:

http://www.slais.ubc.ca/resources/indexing/index.htm