
Introduction
The demo enhances a CRichEditCtrl that provides basic syntax highlighting for C/C++ or any other languages. The CRichEditCtrl also provides an interface that checks spelling using the CSAPI and gives a screen feedback by a coloring underline. This demo uses intensively the TOM interface. The check of spelling occurs only in the comments part of a source code or in strings. The control provides highlighting keywords not only in the source code but also for Doxygen in the comments.
Implementation
For a better implementation, the project has been organized into three classes. CSyntaxcolor manages comments, strings and keyword detection. CSpellingChecker manages the spelling of keywords and manages dictionaries. CSyntaxColorSpellChecker inherits from CSyntaxcolor and overloads the highlighting to provide a coloring underline feedback for the CRichEditCtrl.
Strategy
In order to avoid problems with the redraw of the control, I used Text Object Model (TOM) interface. This interface allows to manipulate runs of characters without sad effects on the screen, and gives more functions than the Win32 API. You can, for example, disable the undo during the modifications of colors. For the spelling, I used CSAPI. There is only some documentation about this API, but it eases considerably the implementation of a spell engine. It allows you to use many standardized dictionaries in your application. I am not sure if it is an official API from Microsoft.
Points of Interest
The main problem to handle syntax highlighting correctly is to manage the multi-line comments without re-computing each time colors of the complete file. The change of only one character could affect the entire file. Re-computing the complete file takes time and slows down the process of typing. To detect the modification of the multi-line comment structure, there is no clean solution. My solution has been to overload the notification EN_MSGFILTER of the CRichEditCtrl. When the user types a character able to change the state of the multi-line comments, I set a flag. And I do a fast analysis of the current line in order to know if the comment is opened or closed.
void CDemoEditorView::OnMsgfilterEdit(NMHDR* pNMHDR, LRESULT* pResult)
{
MSGFILTER *pMsgFilter = reinterpret_cast<MSGFILTER *>(pNMHDR);
if(pMsgFilter->msg == WM_CHAR)
{
switch(pMsgFilter->wParam)
{
case '*':
case '/':
{
m_bChangeAll = true;
break;
}
}
CHARRANGE cr;
GetRichEditCtrl().GetSel(cr);
if(cr.cpMax -cr.cpMin)
{
m_bChangeAll = true;
}
}
if(pMsgFilter->msg == WM_KEYDOWN)
{
switch(pMsgFilter->wParam)
{
case VK_BACK:
case VK_DELETE:
case VK_RETURN:
m_bChangeAll = true;
break;
}
}
if(m_bChangeAll)
{
CHARRANGE cr;
GetRichEditCtrl().GetSel(cr);
m_nextedComments =
m_colorizer.AnalyseMultiCommentLine(GetRichEditCtrl().LineFromChar(cr.cpMin));
}
*pResult = 0;
}
Using Colorizer
I consider the colorization of a text as a set of styles in a run of characters. A style includes all properties for a string (color, font, size, �). The ITextRange interface handles the concept of style through the functions GetSyle and SetStyle. Most of the time, this property is not used in a default implementation, but this LONG is stored in the run. I have stored in this value, the index of the highlight class. It is now very easy to quickly continue a multi line comment style when I evaluate only a part of the file. The style is stored directly in the run of characters. The main job of coloring is done in the function ColorizeRange(�). You can customize the keywords with the function AddKeyList(�). It will be easy to customize this class for HTML, XML, or other languages. You can customize the comments and string detection by changing CreateCommentsAndStringsTags(�). Also, you can change and add more highlight styles by changing CreateDefaultStyle().
void CSyntaxColor::CreateDefaultStyle()
{
m_tStyle.SetSize(styleMax);
m_tStyle[normal] = CKeyStyle(RGB(0,0,0));
m_tStyle[syntax] = CKeyStyle(RGB(0,0,255));
m_tStyle[directive] = CKeyStyle(RGB(192,192,192));
m_tStyle[pragma] = CKeyStyle(RGB(0,0,255));
m_tStyle[mcomments] = CKeyStyle(RGB(0,200,0));
m_tStyle[scomments] = CKeyStyle(RGB(0,200,0));
m_tStyle[sstring] = CKeyStyle(RGB(0,0,255));
m_tStyle[dstring] = CKeyStyle(RGB(0,0,255));
m_tStyle[highlightComments]= CKeyStyle(RGB(255,0,0),bold);
}
For each word in the NormalSlot or the CommentSlot, ColorizeRange calls OnColorKeyWord(int istart ,int iend ,LPCSTR pword, HighLightStyle style) to colorize a keyword.
Using the Speller
To implement the spelling checker in syntax colorizing, I have created a new class derived from CSyntaxcolor. The class includes an object CSpellingChecker and overload OnColorKeyWord(�). The new class continues the job of OnColorKeyWord done in CSyntaxcolor, and checks where the colorization occurs, checks the spelling, and sets the underline to signal a misspelled word.
bool CSyntaxColorSpellChecker::OnColorKeyWord(int istart, int iend,
LPCSTR pword, HightLightStyle style)
{
if(CSyntaxColor::OnColorKeyWord(istart,iend,pword,style)) return true;
long newunderline = 0;
if(style == scomments || style == mcomments ||
style == sstring || style == dstring )
{
newunderline = m_cSpeller.CheckWord(pword) ? 0: SPELL_UNDERLINE;
}
m_pRange->SetRange(istart,iend);
ITextFont *pFont;
m_pRange->GetFont(&pFont);
pFont->SetUnderline(newunderline);
pFont->Release();
return false;
}
You can change the language of the speller by passing another LID to the function Initialize(lidFrench, lidDutch,�). Just make sure before that the dictionary and the spell engine is rightly installed in the registry. CSpellingChecker also provides some tools to manage dictionaries and words suggestion through a set of functions. Suggestion word comes not only from the main dictionary (.LEX) but also from your user dictionary.
bool CheckWord(LPCSTR pWord);
bool SuggestWords(LPCSTR pWord,int max, CStringArray & tList );
bool IgnoreAlways(LPCSTR pWord);
bool AddToUserDic(LPCSTR pWord);
It is possible to add more user dictionaries (read only) in the function InitEngine, if you modify the SIB structure for each call of SuggestWords and CheckWord. These extra dictionaries can be constructed automatically from your source code, in order to check the spelling of function names, variables, etc�
The Demo
The demo just implements the three classes in a text editor. I have added syntax highlighting and spell checking in comments to a basic application created by Visual C++. The editor is configured for C++ keywords highlight and Doxygen keywords in the comments.
History
- 11/11/2004 - first version.
|
|
 |
 | This Colorizer works very well rob_toutant | 22:32 24 Nov '09 |
|
 |
I needed a quick and easy way to add code colorizing to a c++ program and your code did the trick. thank you for posting it.
It is surprisingly fast and efficient..would be nice if some additional editing features where included - like highlighting text and being able to press tab for indenting.
I compiled your code using visual studio 6 ( yes i am still using visual studio 6 ) and as well 2008 express addition - on Vista Home Premium - there was one thing that needed to be changed to make your code work... changing all of the '\r' to '\n' and as well "\r" to "\n" otherwise everything came out as comment color (couldn't find the end of the line)
thanks again for posting your code
|
|
|
|
 |
 | tomWave at Win98 problem vblazheyev@gmail.com | 6:36 29 Sep '05 |
|
 |
why this code does not working at win98??? HREsult is S_OK, but nothing at control...... What Im doing wrong????
void CpsRichEditCtrlWrapper::MakeUnderLine(const CHARRANGE& cr, BOOL bCheck) { CComPtr pRange = NULL; HRESULT hr = GetRangeFromTom(cr.cpMin, cr.cpMax, pRange); if (FAILED(hr)) { return; } ITextFont* pFont=NULL; pRange->GetFont(&pFont); if (bCheck) { //// --->>>>>>> here hr = pFont->SetUnderline(tomWave); ////// <---- } else { pFont->SetUnderline(0); } pFont->Release(); }
vblazheyev@gmail.com
|
|
|
|
 |
 | Can't find TOM.h solikang | 16:50 14 Jun '05 |
|
 |
Hi I downloaded the source file in this page.
I tried to compile it with Visual studio 6.0. But I can't.
The reason is absence of "TOM.h".
Can I get it?? If anyone have it, please send me.
Thanks.
solikang@gmail.com
|
|
|
|
 |
|
|
 |
|
 |
Thanks for replying me.
I can find "TOM.h" in platform SDK. So I can compile source.
I made "DemoEditor.exe" ,but it can't open file. I'll debug and trace. If you have any idea about it, please advise me. It may be very helpful to me.
And, I visit "http://home.tula.net/frazez/OfficeSpeller.exe" and download "OfficeSpeller.exe". It produces "MSSP232.DLL". I have no reference about it, so I can't use it. Is there any header file which describes this 'dll' file? If you know, please let me know also.
I appreciate for your tip once again. And I expect you will reply this article also.
|
|
|
|
 |
|
 |
The tool install a speller engine of office 97 and register it on the registry key regKey.Format("HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Shared Tools/Proofing Tools/Spelling/%d/Normal",m_Language); (Engine and Dictionary)
The Demoeditor is designed to use the spell engine of office 97, if you want to use the OfficeSpeller.exe.You have to modify the CSpellingChecker::Initialize by adding
CString engine = reg.ReadKey("Engine","C:/Program Files/Fichiers communs/Microsoft Shared/PROOF/MSSP232.DLL"); m_MdrNameLex = reg.ReadKey("Dictionary","C:/Program Files/Fichiers communs/Microsoft Shared/PROOF/MSSP2_EN.LEX");
engine.TrimRight('\"'); engine.TrimLeft('\"'); m_MdrNameLex.TrimRight('\"'); m_MdrNameLex.TrimLeft('\"');
this code remove the extra quote added by the install
Game developer, Tools Application engineer
|
|
|
|
 |
|
 |
The tool install a speller engine of office 97 and register it on the registry key regKey.Format("HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Shared Tools/Proofing Tools/Spelling/%d/Normal",m_Language); Engine and Dictionary
The editor is designed to use the spell engine of office 97, if you want to use the OfficeSpeller.exe.You have to modify the CSpellingChecker::Initialize by adding
CString engine = reg.ReadKey("Engine","C:/Program Files/Fichiers communs/Microsoft Shared/PROOF/MSSP232.DLL"); m_MdrNameLex = reg.ReadKey("Dictionary","C:/Program Files/Fichiers communs/Microsoft Shared/PROOF/MSSP2_EN.LEX");
engine.TrimRight('\"'); engine.TrimLeft('\"');
m_MdrNameLex.TrimRight('\"'); m_MdrNameLex.TrimLeft('\"'); this code remove the extra quote added by the install
Game developer, Tools Application engineer
|
|
|
|
 |
 | TOM interface is great but is incomplete? Alexandru | 3:12 27 Jan '05 |
|
 |
Does anybody know how can 'tomCharset' and 'tomLink' be accessed and modified ?
I would like to access these two text attributes (font charset and whether the text is a hotlink or not) using ITextDocument,ITextRange and ITextFont, but I can't see any member function??
If anyone has an idea, please let me know. Searching the web with Google and searching MSDN revealed nothing.
Thank you.
Alexandru Matei, mateia@easynet.ro
|
|
|
|
 |
 | Html support? are_all_nicks_taken_or_what | 16:20 6 Dec '04 |
|
 |
EXCELLENT! This is one outstanding class! Thanks a lot!
I wonder one thing though. How do I use CSyntaxColor for html syntax highlighting? Html should only be highlighted inside '<' and '>', and I don't quite see how that could easily be managed in your class. Any suggestions?
|
|
|
|
 |
 | Unhandled exception in DemoEditor.exe MayaRafi | 21:14 17 Nov '04 |
|
 |
The program failed by entering the first letter from keyboard in the function CSyntaxColor::AnalyseMultiCommentLine(..) => CNoChangeEdit::CNoChangeEdit(...) => CRichEditCtrl::GetEventMask() either in Debug and Release mode.
May
|
|
|
|
 |
|
 |
Strange, no reason to crash in this place, may be a troube with local configuration
|
|
|
|
 |
|
 |
problem is that returned value from
CSyntaxColorSpellChecker::Initialize
is not handled when condition
if(!m_cSpeller.Initialize(lidAmerican)) return false;
returns false and does not initialize
CSyntaxColor::Initialize(pCtrl);
Joshua
|
|
|
|
 |
 | A small problem: You're not allowed to use Microsoft's dictionaries! Mike Eriksson | 23:08 15 Nov '04 |
|
 |
Hello!
Thanks for the article and the code.
CSAPI *IS* an official api which makes it easier to create spellchecking software. There is one drawback, not with the api but with Microsofts licensing, You are not allowed to use Microsofts Dictionaries!!
Quote: "NOTE: CSAPI may not be used to call into Microsoft's spell check engine and lexicon. For example, third-party developers who want to incorporate spell check capabilities into their own applications may not use CSAPI to access the spell check engine and lexicons that are installed by Microsoft Office. Microsoft does not currently provide spell check utilities for use in third-party products."
More info here: http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q262605&
// Anders
Anders Eriksson Sonork 100.21825
|
|
|
|
 |
|
 |
This is a generic warning message: "Microsoft does not currently provide spell check utilities for use in third-party products."
It means that Microsoft is not supporting the development of third-party spell checking. If I paid for Microsoft Office, I am allowed to use every component, including the spell checker. So every user owning Microsoft Office can use the spell checker for personal use. If a user does not have Office installed on his/her computer, then he is not allowed to use the Office spell checker
If you read the article at http://support.microsoft.com/kb/243844/EN-US/[^], you will see Microsoft is promoting the use of its own spell checking:
Quote: "Software written to work with Microsoft Office can take advantage of the spell checking capabilities of Microsoft Word to add spell checking to their own application."
It is in the interest of Microsoft to have developers use Office components. The more users using your product, the more those users are "sticking to Office" rather than drifting away to competitors.
|
|
|
|
 |
|
 |
Hello,
you're right. You can use the spellchecking capabilities from Microsoft office. But only via the automation model (OLE, COM).
If you develop any commercial software, it is not legal to access the spell checking engine via the CSAPI.
I know this, because I had to rewrite my application...
Greetings Gerhold
P.S. The Spell Checking engine for the languages Finnish, Swedish, Norwegian, Danish and German, for instance, is written from the finnish company Lingsoft.
|
|
|
|
 |
|
 |
Gerhold wrote:
You can use the spellchecking capabilities from Microsoft office. But only via the automation model (OLE, COM).
This makes sense. The future is in COM; not in the traditional APIs. If I was Microsoft, I would not *officially* support the CSAPI. Instead, I would encourage developers to use the COM intrfaces.
|
|
|
|
 |
 | C# Code skjagini | 14:58 13 Nov '04 |
|
 |
Hi,
It will be very useful if we can get C# copy for the people we dont know vc++.
|
|
|
|
 |
 | Sample binary crashes YoSilver | 11:48 13 Nov '04 |
|
 |
Haven't downloaded the source yet, but the DemoEditor.EXE crashed when I run it.
One always gets the deserved.
http://www.silveragesoftware.com/hffr.html Update your source code with my tool HandyFile Find And Replace!
|
|
|
|
 |
|
 |
the app runs correctly on my PC, may be troubles with a specific configuration I didn't check, if you can , compile the project, run it and send me the feedback about where the program crash
Game developer, Tools Application engineer
|
|
|
|
 |
|
 |
Yes, the problem appears since american proofing is not installed on my system OK, here we go.
There are problems in your code.
You do not initialise member pointer with null. This causes access violations, for example, in case dictionary are not installed in the system.
bool CSpellingChecker::Terminate() { if(m_Handle) {
m_lpfnSpellCloseUdr(m_Handle, m_udr,FALSE); m_lpfnSpellCloseMdr(m_Handle, &m_Mdrs); m_lpfnSpellTerminate(m_Handle, FALSE); } m_Handle = 0; if(m_hinstSpell) FreeLibrary (m_hinstSpell); m_hinstSpell = 0; m_bEnable = false; return true; }
add checks here:
if(m_Handle) { if (m_lpfnSpellCloseUdr) m_lpfnSpellCloseUdr(m_Handle, m_udr,FALSE); if (m_lpfnSpellCloseMdr) m_lpfnSpellCloseMdr(m_Handle, &m_Mdrs); if (m_lpfnSpellTerminate) m_lpfnSpellTerminate(m_Handle, FALSE); }
Add 3 lines to CSpellingChecker constructor:
m_lpfnSpellCloseUdr = 0; m_lpfnSpellCloseMdr = 0; m_lpfnSpellTerminate = 0;
Modify CSyntaxColorSpellChecker::Initialize to make it call CSyntaxColor::Initialize in all cases:
bool CSyntaxColorSpellChecker::Initialize(CRichEditCtrl *pCtrl) { bool bResult = m_cSpeller.Initialize(lidAmerican); bResult &= CSyntaxColor::Initialize(pCtrl); return bResult; }
BTW, it is best to determine the sytem language and load the corresponding library instead of simply calling m_cSpeller.Initialize(lidAmerican);. In my case this call fails .
Anyway, a very good engine otherwise !
One always gets the deserved.
http://www.silveragesoftware.com/hffr.html Update your source code with my tool HandyFile Find And Replace!
|
|
|
|
 |
|
|
Last Updated 12 Nov 2004 |
Advertise |
Privacy |
Terms of Use |
Copyright ©
CodeProject, 1999-2010