Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version

A Spell Checking Engine

, 5 Feb 2001
A free spell checking engine for use in your C++ applications. Includes the current US English dictionary
// DlgSpellChecker.cpp : implementation file
//

#include "stdafx.h"
#include "FPSSpellChecker.h"
#include "DlgSpellChecker.h"

#include "FPSSpellCheckEngine.h"
#include "PrShtSpellOptions.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CDlgSpellChecker dialog

// **********************************************************
// support functions
void CheckSpellingEdit (CFPSSpellCheckEngine* pEngine, CEdit* pEdit)
{
	ASSERT(pEngine);
	ASSERT(pEdit);
	ASSERT(::IsWindow(pEdit->GetSafeHwnd()));

	CDlgSpellChecker dlg;

	dlg.m_pEdit = pEdit;
	dlg.m_pEngine = pEngine;

	dlg.DoModal();	
}

void CheckSpellingRich (CFPSSpellCheckEngine* pEngine, CRichEditCtrl* pEdit)
{
	ASSERT(pEngine);
	ASSERT(pEdit);
	ASSERT(::IsWindow(pEdit->GetSafeHwnd()));

	CDlgSpellChecker dlg;

	dlg.m_pRichEdit = pEdit;
	dlg.m_pEngine = pEngine;

	dlg.DoModal();	
}
// **********************************************************


CDlgSpellChecker::CDlgSpellChecker(CWnd* pParent /*=NULL*/)
	: CDialog(CDlgSpellChecker::IDD, pParent)
{
	m_iWordBegin = -1;
	m_iSentenceBegin = 0;
	m_iSentence = 0;
	m_iSentenceLen = 0;
	m_pEdit = NULL;
	m_pRichEdit = NULL;
	m_pEngine = NULL;

	//{{AFX_DATA_INIT(CDlgSpellChecker)
		// NOTE: the ClassWizard will add member initialization here
	//}}AFX_DATA_INIT
}

CDlgSpellChecker::~CDlgSpellChecker()
{
	CDlgSpellChecker_CHANGEALL* pEntry;
	POSITION Pos = NULL;

	Pos = m_ChangeAll.GetHeadPosition();
	while (Pos)
	{
		pEntry = m_ChangeAll.GetNext(Pos);
		ASSERT(pEntry);
		try
		{
			delete pEntry;
		}
		catch(...)
		{
			ASSERT(FALSE);
			TRACE("CDlgSpellChecker::~CDlgSpellChecker() failed to delete pEntry\n");
		}
	}
	m_ChangeAll.RemoveAll();
}

void CDlgSpellChecker::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CDlgSpellChecker)
	DDX_Control(pDX, IDC_SUGGESTIONS, m_lstSuggestions);
	DDX_Control(pDX, IDC_SENTENCE, m_editSentence);
	//}}AFX_DATA_MAP
}


BEGIN_MESSAGE_MAP(CDlgSpellChecker, CDialog)
	//{{AFX_MSG_MAP(CDlgSpellChecker)
	ON_BN_CLICKED(IDC_ADD, OnAdd)
	ON_BN_CLICKED(IDC_AUTO_CORRECT, OnAutoCorrect)
	ON_BN_CLICKED(IDC_CHANGE, OnChange)
	ON_BN_CLICKED(IDC_CHANGE_ALL, OnChangeAll)
	ON_BN_CLICKED(IDC_IGNORE, OnIgnore)
	ON_BN_CLICKED(IDC_IGNORE_ALL, OnIgnoreAll)
	ON_BN_CLICKED(IDC_OPTIONS, OnOptions)
	ON_BN_CLICKED(IDC_UNDO, OnUndo)
	ON_LBN_DBLCLK(IDC_SUGGESTIONS, OnDblclkSuggestions)
	ON_BN_CLICKED(IDC_CANCEL, OnCancel)
	ON_EN_CHANGE(IDC_SENTENCE, OnChangeSentence)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CDlgSpellChecker message handlers

void CDlgSpellChecker::OnAdd() 
{
	ASSERT(m_pEngine);
	ASSERT(m_pEngine->GetUserDic());

	m_pEngine->GetUserDic()->AddWord(m_strMisSpelledWord);
	ContinueChecking(m_iWordBegin + m_strMisSpelledWord.GetLength()+1);	
}

void CDlgSpellChecker::OnAutoCorrect() 
{
	// TODO: Add your control notification handler code here
	
}

void CDlgSpellChecker::OnCancel() 
{
	EndDialog(IDCANCEL);
}

void CDlgSpellChecker::OnChange() 
{
	CString strNewWord;
	int iSel = m_lstSuggestions.GetCurSel();
	if (iSel == -1)
	{
		AfxMessageBox("You must select a suggestion from the list provided.");
		return;
	}
	m_lstSuggestions.GetText(iSel, strNewWord);

	if (strNewWord[0] == '(')
	{
		AfxMessageBox("There are no suggestions for this word.");
		return;
	}

	m_editSentence.SetSel(m_iWordBegin, m_iWordBegin+m_strMisSpelledWord.GetLength());
	m_editSentence.ReplaceSel(strNewWord);

	SaveSentence();

	ChangeCancelToClose();
	ContinueChecking(m_iWordBegin+strNewWord.GetLength()+1);	
}

void CDlgSpellChecker::OnChangeAll() 
{
	CString strNewWord;
	int iSel = m_lstSuggestions.GetCurSel();
	if (iSel == -1)
	{
		AfxMessageBox("You must select a suggestion from the list provided.");
		return;
	}
	m_lstSuggestions.GetText(iSel, strNewWord);

	if (strNewWord[0] == '(')
	{
		AfxMessageBox("There are no suggestions for this word.");
		return;
	}

	AddChangeAll(m_strMisSpelledWord, strNewWord);

	m_editSentence.SetSel(m_iWordBegin, m_iWordBegin+m_strMisSpelledWord.GetLength());
	m_editSentence.ReplaceSel(strNewWord);

	SaveSentence();

	ChangeCancelToClose();
	ContinueChecking(m_iWordBegin+strNewWord.GetLength()+1);		
}

void CDlgSpellChecker::OnIgnore() 
{
	ChangeCancelToClose();
	ContinueChecking(m_iWordBegin + m_strMisSpelledWord.GetLength()+1);	
}

void CDlgSpellChecker::OnIgnoreAll() 
{
	ASSERT(m_pEngine);

	m_pEngine->IgnoreWord(m_strMisSpelledWord);
	ChangeCancelToClose();
	ContinueChecking(m_iWordBegin + m_strMisSpelledWord.GetLength()+1);	
}

void CDlgSpellChecker::OnOptions() 
{
	ASSERT(m_pEngine);

	m_pEngine->DisplayOptions();
}

void CDlgSpellChecker::OnUndo() 
{
	m_editSentence.Undo();	
}

void CDlgSpellChecker::OnDblclkSuggestions() 
{
	OnChange();
}

BOOL CDlgSpellChecker::OnInitDialog() 
{
	CDialog::OnInitDialog();

	ASSERT(m_pEngine);
	ASSERT(m_pEdit || m_pRichEdit);
	if (m_pEdit)
		ASSERT(::IsWindow(m_pEdit->GetSafeHwnd()));
	if (m_pRichEdit)
		ASSERT(::IsWindow(m_pRichEdit->GetSafeHwnd()));

	CWnd* pWnd = GetDlgItem(IDC_UNDO);
	ASSERT(pWnd);
	ASSERT(::IsWindow(pWnd->GetSafeHwnd()));
	pWnd->EnableWindow(FALSE);

	BeginSpellCheck();
	if (GetNextSentence())
	{
		// do nothing
	}
	else
	{
		AfxMessageBox("The spelling checker is complete.");
		EndDialog(IDOK);
	}
	
	return TRUE;  // return TRUE unless you set the focus to a control
	              // EXCEPTION: OCX Property Pages should return FALSE
}

void CDlgSpellChecker::BeginSpellCheck()
{
	m_iSentenceBegin = 0;
	m_iSentence = 0;

	ASSERT(m_pEdit || m_pRichEdit);
}

BOOL CDlgSpellChecker::GetNextSentence()
{
	BOOL bReturn = FALSE;
	BOOL bContinue = TRUE;
	CString strSentence;

	CWnd* pWnd = GetDlgItem(IDC_UNDO);
	ASSERT(pWnd);
	ASSERT(::IsWindow(pWnd->GetSafeHwnd()));
	pWnd->EnableWindow(FALSE);

	while (!bReturn && GetSentence(m_iSentence, m_iSentenceBegin, strSentence))
	{
		int iPos = 0;

		while (!CheckSentence(strSentence, iPos) && !bReturn)
		{
			m_iSentenceLen = strSentence.GetLength();
			m_editSentence.SetWindowText(strSentence);
			m_editSentence.SetSel(m_iWordBegin, m_iWordBegin + m_strMisSpelledWord.GetLength());

			CDlgSpellChecker_CHANGEALL* pChange = FindChangeAll(m_strMisSpelledWord);
			if (pChange)
			{
				m_editSentence.ReplaceSel(pChange->strGood);
				m_editSentence.GetWindowText(strSentence);
				SaveSentence();

				iPos = m_iWordBegin + pChange->strGood.GetLength() + 1;
			}
			else
			{
				FindSuggestions();

				bReturn = TRUE;
			}
		}

		m_iSentence++;
	}

	return bReturn;
}

BOOL CDlgSpellChecker::GetSentence(int iSentence, int& iBeginsAt, CString& strSentence)
{
	ASSERT(iSentence >= 0);
	ASSERT(iBeginsAt >= 0);

	BOOL bReturn = FALSE;
	int iPos = 0;
	int iLen = 0;
	int iTempSentence = 0;
	CString strText;

	strSentence = "";

	if (m_pEdit)
	{
		m_pEdit->GetWindowText(strText);
		iLen = strText.GetLength();

		while (iTempSentence < iSentence+1 && iPos < iLen)
		{
			if (strSentence != "" || (strText[iPos] != ' ' && strText[iPos] != '\r' && strText[iPos] != '\n'))
			{
				if (strSentence == "") iBeginsAt = iPos;
				strSentence += strText[iPos];
			}

			switch (strText[iPos])
			{
			case '.':
			case '!':
			case '?':
				{
					iTempSentence++;

					if (iTempSentence != iSentence+1)
						strSentence = "";
					break;
				}
			}

			iPos++;
		}

		if ((iTempSentence == iSentence+1) || (iTempSentence == iSentence && strText != ""))
			bReturn = TRUE;
	}
	else if (m_pRichEdit)
	{
	}

	return bReturn;
}

BOOL CDlgSpellChecker::CheckSentence(CString &strSentence, int iStartAt)
{
	ASSERT(iStartAt >= 0);

	BOOL bReturn = TRUE;
	CString strWord;
	int iPos = iStartAt;
	int iLen = strSentence.GetLength();
	char cThisChar = 0;
	CStringList Matches;

	m_iWordBegin = -1;

	while (iPos <= iLen && bReturn)
	{
		if (iPos == iLen)
		{
			cThisChar = 0;
		}
		else
		{
			cThisChar = strSentence[iPos];
		}

		if (cThisChar == 0 || IsWordBreak(cThisChar))
		{
			if (!m_pEngine->FindWord(strWord, Matches, FALSE))
			{
				m_strMisSpelledWord = strWord;
				m_strOutWord = strWord;
				bReturn = FALSE;
			}
			else
			{
				strWord = "";
				m_iWordBegin = -1;
			}
		}
		else
		{
			if (m_iWordBegin == -1)
				m_iWordBegin = iPos;
			strWord += cThisChar;
		}

		iPos++;
	}

	return bReturn;
}

void CDlgSpellChecker::FindSuggestions()
{
	CStringList Matches;

	ASSERT(m_pEngine);

	m_pEngine->FindWord(m_strMisSpelledWord, Matches, TRUE);
	SortMatches(m_strMisSpelledWord, Matches);

	m_lstSuggestions.ResetContent();
	if (Matches.GetCount() == 0)
	{
		m_lstSuggestions.AddString("(No suggestions)");
	}
	else
	{
		POSITION Pos = NULL;
		CString strWord;

		Pos = Matches.GetHeadPosition();
		while (Pos)
		{
			strWord = Matches.GetNext(Pos);
			m_lstSuggestions.AddString(strWord);
		}
	}

}

void CDlgSpellChecker::ContinueChecking(int iPos)
{
	CString strSentence;
	BOOL bContinue = TRUE;
	BOOL bFindNext = TRUE;
	
	m_editSentence.GetWindowText(strSentence);

	while (!CheckSentence(strSentence, iPos) && bContinue)
	{
		bContinue = FALSE;
		bFindNext = FALSE;

		m_iSentenceLen = strSentence.GetLength();
		m_editSentence.SetWindowText(strSentence);
		m_editSentence.SetSel(m_iWordBegin, m_iWordBegin + m_strMisSpelledWord.GetLength());

		CDlgSpellChecker_CHANGEALL* pChange = FindChangeAll(m_strMisSpelledWord);
		if (pChange)
		{
			m_editSentence.ReplaceSel(pChange->strGood);
			m_editSentence.GetWindowText(strSentence);
			SaveSentence();

			bFindNext = TRUE;
			bContinue = TRUE;
			iPos = m_iWordBegin + (pChange->strGood.GetLength() + 1);
		}
		else
		{
			FindSuggestions();
		}
	}

	if (bFindNext)
	{
		if (!GetNextSentence())
		{
			AfxMessageBox("The spelling checker is complete.");
			EndDialog(IDOK);
		}
	}
}

void CDlgSpellChecker::AddChangeAll(LPCSTR lpszBad, LPCSTR lpszGood)
{
	CDlgSpellChecker_CHANGEALL* pEntry = NULL;
	try
	{
		pEntry = new CDlgSpellChecker_CHANGEALL;
	}
	catch(...)
	{
		ASSERT(FALSE);
		pEntry = NULL;
	}
	ASSERT(pEntry);

	pEntry->strBad = lpszBad;
	pEntry->strGood = lpszGood;

	m_ChangeAll.AddTail(pEntry);
}

void CDlgSpellChecker::SaveSentence()
{
	CString strSentence;

	m_editSentence.GetWindowText(strSentence);

	if (m_pEdit)
	{
		ASSERT(::IsWindow(m_pEdit->GetSafeHwnd()));

		m_pEdit->SetSel(m_iSentenceBegin, m_iSentenceBegin+m_iSentenceLen);
		m_pEdit->ReplaceSel(strSentence);

		m_iSentenceLen = strSentence.GetLength();
	}
	else if (m_pRichEdit)
	{
		ASSERT(::IsWindow(m_pRichEdit->GetSafeHwnd()));

		m_pRichEdit->SetSel(m_iSentenceBegin, m_iSentenceBegin+m_iSentenceLen);
		m_pRichEdit->ReplaceSel(strSentence);

		m_iSentenceLen = strSentence.GetLength();
	}
}

void CDlgSpellChecker::OnChangeSentence() 
{
	SaveSentence();

	CWnd* pWnd = GetDlgItem(IDC_UNDO); 
	ASSERT(pWnd); 
	ASSERT(::IsWindow(pWnd->GetSafeHwnd()));
	pWnd->EnableWindow(TRUE);
}

CDlgSpellChecker_CHANGEALL* CDlgSpellChecker::FindChangeAll(LPCSTR lpszBad)
{
	ASSERT(lpszBad);
	ASSERT(AfxIsValidString(lpszBad));

	CDlgSpellChecker_CHANGEALL* pEntry;
	POSITION Pos = NULL;
	BOOL bFound = FALSE;
	CString strTestFor = lpszBad;
	CString strTest;

	strTestFor.TrimLeft(); strTestFor.TrimRight(); strTestFor.MakeUpper();

	Pos = m_ChangeAll.GetHeadPosition();
	while (Pos && !bFound)
	{
		pEntry = m_ChangeAll.GetNext(Pos);
		ASSERT(pEntry);

		strTest = pEntry->strBad;
		strTest.TrimLeft(); strTest.TrimRight(); strTest.MakeUpper();

		if (strTest == strTestFor)
			bFound = TRUE;
	}

	if (bFound)
		return pEntry;

	return NULL;
}

void CDlgSpellChecker::ChangeCancelToClose()
{
	CWnd* pWnd = GetDlgItem(IDC_CANCEL);
	ASSERT(pWnd);
	ASSERT(::IsWindow(pWnd->GetSafeHwnd()));

	pWnd->SetWindowText("Close");

}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

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

Share

About the Author

Matt Gullett
Web Developer
United States United States
No Biography provided

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.141220.1 | Last Updated 6 Feb 2001
Article Copyright 2001 by Matt Gullett
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid