Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Adding Spell Check and Synonym Info to a Text Editor, using Word Automation

0.00/5 (No votes)
3 Feb 2005 1  
An article on adding spell check and synonym info functionality to an editor using MS Word Automation.

Sample Image - AutoSpellCheck.gif

Introduction

This article explains how the automation objects exposed by the Word Application can be customized to achieve similar functionality. The Spell check, Spelling suggestions and Synonyms suggestions are implemented in this project. The word suggestions are shown in a popup menu when a word is selected and right-clicked. For a correct word, it gives the synonyms list, and for a wrong word, it will give the spelling suggestions. There is a lot of scope for improvement in this application including better handling of errors and additional functionalities, but this is just to illustrate the power and extensibility of automation.

Background

This article is an extension to the article "Creating a Dictionary using Word Automation and Text-to-Speech Control". But the code is made a lot more simpler.

Using the code

The article referred above gives an explanation on how to start with Word Automation. The steps involved in creating an application like this would be:

  1. Create an SDI application using MFC.
  2. Specify CRichEditView as the base class for your CView class.

  3. Import the Word type library.

    Open Class Wizard. In the 'Automation' tab, click 'Add Class' --> 'From Type-Library'. The open file dialog asks for '*.olb' files. You can find it in the 'Microsoft Office directory'. For Office 2000, it is msword9.olb. For older versions, it is msword8.olb. Once you find the Type-Lib, open it and a new window pops up. It asks us to choose the automation objects that Word exposes. If you are not sure which classes to choose then select all and press OK. You can see that two files are being added to the project - Msword9.cpp and Msword9.h (in older versions, it is Msword8.cpp and Msword8.h). Don't forget to include Msword9.h in the file where you declare the Word objects (as in CSpellerView.h for this project).

  4. Add code to invoke methods exposed by the Automation objects. Relevant sections of code are given below.
  5. In order to have a pop-up menu that lists the synonyms/spelling suggestions, we need to handle the Right-Click event in the Rich Edit control.

Creating an Instance of Word Application

if (!oWordApp.CreateDispatch("Word.Application"))
{
    AfxMessageBox("CreateDispatch failed.", MB_OK | MB_SETFOREGROUND);
    exit(1);  
    return;
}

Check for Spelling:

First, we need to separate each word and then invoke the Spell checker.

void CSpellerView::OnSpellCheck()
{
    CString str,strRes;
    ClearUnderline(); //Clear all underlines as this is a fresh search

    m_rich.GetWindowText(str); //Get all the text from the editor

    int iStart = 0,iEnd =0, iLen=0, iStrLen;

    iEnd = str.Find(' ',0);        //find the first space

    iStrLen = str.GetLength();            
    if(iEnd <0 && iStrLen >0)
    {
        iEnd = iStrLen;
        //This is for the special case when there is only one word

    }
    for(int ndx = 0; iEnd > 0 && ndx < iStrLen; ndx ++ )
    {
        strRes = str.Mid(iStart,iEnd-iStart); //Extract the word

        if(!CheckWord(strRes))
        //check for spelling

        {
            Underline(iStart,iEnd); //Underline on error

        }
        iStart = iEnd+1;
        iEnd = str.Find(' ',iStart); //get the next word separated by ' '

    }
    strRes = str.Mid(iStart,iStrLen-1); // get the last word

    if(!CheckWord(strRes))
    //Check word checks the spelling for each word that is separated.

    {
        Underline(iStart,iStrLen);                
    }
}

Now check if the spelling is correct for each word.

Invoke the CheckSpelling() method of the Word Application object passing the string containing the word. All other parameters can be kept optional.

COleVariant vOpt((long)DISP_E_PARAMNOTFOUND, VT_ERROR);
// parameters for OLE calls...


if(oWordApp!=NULL)
{
    if(oWordApp.CheckSpelling(LPCTSTR(str),vOpt,vOpt,vOpt, 
                vOpt,vOpt,vOpt,vOpt,vOpt,vOpt,vOpt,vOpt,vOpt))
        return TRUE;
}

To get the Synonym list:

Invoke the GetSynonymInfo() method. Each word in the list is added to the popup menu also.

BOOL CSpellerView::GetSuggestions(CString argstr)
{

    try{
        
        COleVariant vOpt((long)DISP_E_PARAMNOTFOUND, 
                 VT_ERROR);// parameters for OLE calls...

        
        SynonymInfo* oSin = new SynonymInfo();
        
        oSin->AttachDispatch(oWordApp.GetSynonymInfo(LPCTSTR(argstr),vOpt));
        
        // let's find the synonym list for the first meaning only

        COleVariant vMeaning(short(1));
        COleVariant vSyno=oSin->GetSynonymList(vMeaning);
        //get it...

        
        long index=0;
        if(vSyno.vt==(VT_ARRAY|VT_BSTR))    
        {
            
            CString str;
            int syncount;

            // get the size of the list

            syncount=(vSyno.parray)->cbElements;
            long* ptr;
            HRESULT lResult;    
            if(syncount==0)
            // no synonyms... but the server is the first

            // to know this!!! and it will react

            {
                if(oSin != NULL)
                {
                    delete oSin; //Delete the synonym object

                    oSin = NULL;    
                }
                return FALSE;    
            }            
            // lock the safe-array before extraction

            lResult=SafeArrayLock(vSyno.parray);
            if(lResult)
            {
                if(oSin != NULL)
                {
                    delete oSin; //Delete the synonym object

                    oSin = NULL;    
                }
                return FALSE;
                
            }
            for(int i=0;i<syncount;i++)
            {
                
                ptr=(long*) (vSyno.parray)->pvData;
                // get the pointer to the array data

                
                str=(BSTR)ptr[i];
                // get each meaning

                
                menu.AppendMenu(MF_ENABLED|MF_STRING, 
                    i, LPCTSTR(str)); //Add each to the pop-up menu


            }
            lResult=SafeArrayUnlock(vSyno.parray);
            // done... so unlock

        }

        if(oSin != NULL)
        {
            delete oSin;
            //Delete the synonym object


            oSin = NULL;    
        }
        return TRUE;
    }
    catch(...)
    {
        return FALSE;
    }
}

Similarly, to get the spelling suggestions:

Invoke the GetSpellingSuggestions() method. Please note that it is required to open a document to check for spelling suggestions. Again, each word in the list is added to the popup menu for spelling suggestions.

BOOL CSpellerView::GetCorrections(CString argstr)
{
    
    try{
        // Parameters

        COleVariant vTrue((short)TRUE), vFalse((short)FALSE), 
                  vOpt((long)DISP_E_PARAMNOTFOUND, VT_ERROR);

        SpellingSuggestions oSpells;// The spelling suggestions collection

        SpellingSuggestion oSpell;  // For each spelling 

        
        // A document has to be opened 

        Documents oDocs(oWordApp.GetDocuments());
        _Document oDoc;  // To check the Spelling suggestions


        oDoc = oDocs.Add(vOpt, vOpt, vOpt, vTrue);
        // Add the doc to the documents collection

        
        oSpells = oWordApp.GetSpellingSuggestions((LPCTSTR)argstr, 
                  vOpt, vOpt, vOpt, vOpt, vOpt, vOpt, vOpt, 
                  vOpt, vOpt, vOpt, vOpt, vOpt, vOpt);
                  // Get the Spelling Suggestions


        long lCount = oSpells.GetCount();
        // Get the number of suggestions

        if (lCount > 0)
            {
                for(long i=1; i<= lCount; i++)
                {
                    oSpell = oSpells.Item(i); // Get each suggestion

                    menu.AppendMenu(MF_ENABLED|MF_STRING,i-1, 
                     LPCTSTR(oSpell.GetName()));
                     //Add each word to the pop-up menu

                }
            }
        oDoc.SetSaved(TRUE);     // Set the doc to be saved

        oDoc.Close(vFalse, vOpt, vOpt); // Close the document

        return TRUE;
    }
    catch(...)
    {
        return FALSE;
    }

To handle the pop-up menu:

We need to handle the PreTranslateMessage as it is difficult to track the WM_RBUTTONDOWN method for the Rich Edit control otherwise. Here's the code:

BOOL CSpellerView::PreTranslateMessage(MSG* pMsg) 
{
    if (pMsg->message == WM_RBUTTONDOWN)
    //Handle the right click event in rich edit

        OnSynonym();
        //Get the synonyms for the selected word

    return CView::PreTranslateMessage(pMsg);
}

Also, it is required to process the OnCommand() handler to know when the user selects an item in the popup menu.

BOOL CSpellerView::OnCommand(WPARAM wParam, LPARAM lParam) 
{
    // TODO: Add your specialized code here and/or call the base class

    
        UINT iID = wParam;
        CString str;    
        menu.GetMenuString(iID,str,MF_BYCOMMAND);//Find selection by ID

        if(str.GetLength() > 0)
        {
            CString sel;
            sel = m_rich.GetSelText();  //Get the selected word from the pop-up

            if(sel.Find(' ') >0 )
                str+=" ";
            m_rich.ReplaceSel(LPCTSTR(str),TRUE);
            //Replace the selection in editor


            OnSpellCheck();  //Re-Check the Spelling.

        }

    return CView::OnCommand(wParam, lParam);
}

Rest of the code deals mostly with the pop-up menu, underlining the miss-spelt word, copying the clipboard data etc. I hope this can be easily understood from the code. By the way, I haven't spell checked this article with this application. ;-) !!!

Poetry courtesy: Rabindranath Tagore.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here