Dragon Drop
Dragon Drop - A Software Consultancy
Home      Our Products      Consultancy      Web Page Development      Services      Coding      Windows      External Resources     
Links      Newsletter      News And Issues      Books etc.      About Us     
CODING:   Exchange      Visual Basic      VBA      HomeSite     |     Coding Tools      Software Clinic     

Word Tips

A Glossary Builder

A fun little assignment. Someone had, via the Record Macro, functionality had written some code which would copy the highlighted text from one document and into another. The idea was that the second document would be some form of Glossary Document. However, after a few days the recorded macro started to fail and so the call went out to see if the code could be improved.

One of the great problems about using the code from the Record Macro function in a production environment is that things in Real Life rarely mimic what the conditions which were present when the macro was recorded. This is why we always say that anything that comes from Record Macro has to be rewritten entirely after the general idea has be taken.

The first thing that must be understood is that the Record Macro does just what it says; it records what you do. The code it produces should be considered to be more of a log of what has happened rather than real useable code. Certainly it comes nowhere close to what we would call good VBA standard code.

The second thing is to realise is that in recorded code there is no error handling and there is no form of defensive programming at all. Neither do we expect there to be. In this scenario, we have a requirement to copy the selected text from one document to another. What happens if there are no documents open? Or just one document? What happens if the glossary document is closed, or will opening another document cause problems and, also, what happens if one tries to copy text from the glossary document to itself?

All of these are Real World issues and have to be addresed by the code. Clearly the Record Macro function cannot cater for these problems and this is why we stress that by all means use the Record Macro function but then be prepared to work on the code until it does what is really required.

In this case we were told of another requirement; if the highlighted text is only a point and not a range then the remainder of the word to the selected text must be included. Now we come to conditional requirements which, of course, no Record Macro would ever cope with. So, this is simple, if the selection is a point and is on the left hand side of a word then the word gets expanded. Of course, if the selection point is at the end of the word then it has to move to the right to the start of the next word and then select that. The specification is getting a little more involved.

There are a number of ways in which this problem can be solved and this is the way which we provided a solution. There are perhaps dozens of other methods, no doubt more elegant than this one, but this seemed to work and the user went away happy.

From the specification it is clear that we are going to have to identify the Glossary document. This variable would then have to be held globally. This, therefore, means that a specific start-up template would have to be created. Within this template would have placed the pointer to the Glossary Template as a Public variable but we decided to hold this within a Class.

Below is the code for clsGlossary:




Option Explicit



  Private m_oGlossaryDocument As Document

  

Sub DefineGlossaryDocument()



  If Documents.Count < 2 Then

    MsgBox "Not enough documents open.  Please ensure that at least two documents are open.", _

      vbOKOnly, "Error - Set Glossary Document"

    Exit Sub

  End If

    



  If Not m_oGlossaryDocument Is Nothing Then

    If MsgBox("Do you wish to change the Glossay Document from " & _

        m_oGlossaryDocument.Name & "?", vbYesNo, "Set Glossary Document") = vbNo Then

      Exit Sub

    End If

  End If

  

  Set m_oGlossaryDocument = ActiveDocument

  MsgBox "Glossary Document is now defined to be " & m_oGlossaryDocument.Name, _

    vbOKOnly, "Set Glossary Document"



End Sub





Property Get Name() As String



  Name = m_oGlossaryDocument.Name



End Property





Property Get IsOpen() As Boolean



  Dim oDoc As Document

  Dim bRetVal As Boolean

  

  

  bRetVal = False

  If Not m_oGlossaryDocument Is Nothing Then

  For Each oDoc In Documents

    If oDoc.Name = m_oGlossaryDocument.Name Then

      bRetVal = True

      Exit For

    End If

  Next

  End If





  IsOpen = bRetVal



End Property





Public Sub CopySelection(oRange As Range)



  Dim oActiveDocument As Document

  



  If Not m_oGlossaryDocument Is Nothing Then

    Set oActiveDocument = ActiveDocument

    m_oGlossaryDocument.Activate

    Application.Selection.Range = oRange

    oActiveDocument.Activate

  End If



End Sub



The glossary document pointer is held privately within this class. Before any action can be taken again the Glossary document it has to be defined. If the Glossary document is already defined then the user is prompted to see if he wants to define a new glossary document. Of course, there will have to be at least documents open before this can be done as there is no point doing anything until the required number of documents are present.

One important property and procedure is the IsOpen() property. This checks to see if the Glossary Document is still open and the other is the CopySelection() routine which, of course, copies the text from the active document to the glossary document.

All of these routines are called from the module, modGlossary:


Option Explicit



  Public oGlossary As clsGlossary



  

  

Public Sub DefineGlossaryDocument()



  If oGlossary Is Nothing Then

    Set oGlossary = New clsGlossary

  End If



  oGlossary.DefineGlossaryDocument



End Sub





Public Sub CopySelectedText()



  Dim oRange As Range

  



  If Documents.Count < 2 Then

    MsgBox "There needs to be at least documents open to contine this operation.", _

        vbOKOnly + vbExclamation, "Error: Glossary"

    Exit Sub

  End If



  If oGlossary Is Nothing Then

    MsgBox "Please define the Glossary Document before continuing.", _

        vbOKOnly + vbExclamation, "Error: Glossary"

  Else

    If ActiveDocument.Name = oGlossary.Name Then

      MsgBox "This operation cannot work if the Glossary Document is selected.", _

          vbOKOnly + vbExclamation, "Error: Glossary"

    Else

      If Not oGlossary.IsOpen() Then

        MsgBox "The Glossay Document seems to have vanished.", _

            vbOKOnly + vbExclamation, "Error: Glossary"

      Else

        ' All of the obvious pitfalls are avoided let's move the data from one document to another

        

        Set oRange = Application.Selection.Range

        If oRange.Start = oRange.End Then

          Application.Selection.MoveRight unit:=wdWord, Count:=1, Extend:=wdExtend

          Set oRange = Application.Selection.Range

          If Trim$(oRange.Text) = "" Then

            Application.Selection.MoveRight unit:=wdWord, Count:=1, Extend:=wdExtend

            Set oRange = Application.Selection.Range

          End If

          oGlossary.CopySelection oRange

        Else

          oGlossary.CopySelection oRange

        End If

        

  

      End If

    End If

  End If



End Sub





Public Sub About()



  Dim oAbout As frmAbout

  

  

  Set oAbout = New frmAbout

  oAbout.Show

  Unload oAbout

  Set oAbout = Nothing

  

End Sub



There are three public procedures in this module. Each is linked to a button in the toolbar. The first, DefineGlossaryDocument() defines or redefines the Glossary Document. The second, CopySelectedText(), copies the text (subject to a few criteria) from the active document to the Glossary document and then last, About(), displays a simple form which shows the author's details.

The interesting part of the code is in the CopySelectedText() routine. The first two checks are to see if there are enough documents open (there has to be at least two) and that the Glossary Document is defined. If not then the user is given a warning. Then given that there is a Glossary Document defined and that there is enough documents open the check has to be made that the current document isn't the Glossary Document and that the Glossary Document is open still.

Once these tests have been performed, and passed, then the actual copying of the text can go ahead. If the selection range is zero length then the selection is moved to the right until the selection covers the remainder of the current or the whole of the next word. Once that is done then the CopySelection() method is called from the clsGlossary object.

Note that this works by passing the Range object of the selected text and not by putting the text through the clipboard. This is because the user may have something of importance within the clipboard and it is the responsibility of the programmer to ensure that the contents of the clipboard are not disturbed unless absolutely necessary.

So, in summary, we can see that the code has been expanded to take up a fair few lines. However, please take note that the core of the code which does the actual required functionality contains just a few lines and the rest of the code within the module and class is mostly concerned with defensive programming.

This code can be downloaded from here.

Updates or Comments

If there are any suggestions for updates or comments then please drop us a mail at malcolm.smith@dragondrop.com.