1 // TortoiseSVN - a Windows shell extension for easy version control
\r
3 // Copyright (C) 2003-2008 - TortoiseSVN
\r
5 // This program is free software; you can redistribute it and/or
\r
6 // modify it under the terms of the GNU General Public License
\r
7 // as published by the Free Software Foundation; either version 2
\r
8 // of the License, or (at your option) any later version.
\r
10 // This program is distributed in the hope that it will be useful,
\r
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
13 // GNU General Public License for more details.
\r
15 // You should have received a copy of the GNU General Public License
\r
16 // along with this program; if not, write to the Free Software Foundation,
\r
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\r
20 #include "resource.h"
\r
21 //#include "AppUtils.h"
\r
22 #include "..\PathUtils.h"
\r
23 #include "..\UnicodeUtils.h"
\r
25 #include "..\registry.h"
\r
26 #include ".\sciedit.h"
\r
28 using namespace std;
\r
31 void CSciEditContextMenuInterface::InsertMenuItems(CMenu&, int&) {return;}
\r
32 bool CSciEditContextMenuInterface::HandleMenuItemClick(int, CSciEdit *) {return false;}
\r
35 #define STYLE_ISSUEBOLD 11
\r
36 #define STYLE_ISSUEBOLDITALIC 12
\r
37 #define STYLE_BOLD 14
\r
38 #define STYLE_ITALIC 15
\r
39 #define STYLE_UNDERLINED 16
\r
40 #define STYLE_URL 17
\r
42 #define STYLE_MASK 0x1f
\r
44 #define SCI_ADDWORD 2000
\r
48 const char * def_enc;
\r
51 struct loc_map enc2locale[] = {
\r
52 {"28591","ISO8859-1"},
\r
53 {"28592","ISO8859-2"},
\r
54 {"28593","ISO8859-3"},
\r
55 {"28594","ISO8859-4"},
\r
56 {"28595","ISO8859-5"},
\r
57 {"28596","ISO8859-6"},
\r
58 {"28597","ISO8859-7"},
\r
59 {"28598","ISO8859-8"},
\r
60 {"28599","ISO8859-9"},
\r
61 {"28605","ISO8859-15"},
\r
64 {"1251","microsoft-cp1251"},
\r
68 IMPLEMENT_DYNAMIC(CSciEdit, CWnd)
\r
70 CSciEdit::CSciEdit(void) : m_DirectFunction(NULL)
\r
71 , m_DirectPointer(NULL)
\r
75 m_hModule = ::LoadLibrary(_T("SciLexer.DLL"));
\r
78 CSciEdit::~CSciEdit(void)
\r
80 m_personalDict.Save();
\r
82 ::FreeLibrary(m_hModule);
\r
89 void CSciEdit::Init(LONG lLanguage)
\r
91 //Setup the direct access data
\r
92 m_DirectFunction = SendMessage(SCI_GETDIRECTFUNCTION, 0, 0);
\r
93 m_DirectPointer = SendMessage(SCI_GETDIRECTPOINTER, 0, 0);
\r
94 Call(SCI_SETMARGINWIDTHN, 1, 0);
\r
95 Call(SCI_SETUSETABS, 0); //pressing TAB inserts spaces
\r
96 Call(SCI_SETWRAPVISUALFLAGS, SC_WRAPVISUALFLAG_END);
\r
97 Call(SCI_AUTOCSETIGNORECASE, 1);
\r
98 Call(SCI_SETLEXER, SCLEX_CONTAINER);
\r
99 Call(SCI_SETCODEPAGE, SC_CP_UTF8);
\r
100 Call(SCI_AUTOCSETFILLUPS, 0, (LPARAM)"\t([");
\r
101 Call(SCI_AUTOCSETMAXWIDTH, 0);
\r
102 //Set the default windows colors for edit controls
\r
103 Call(SCI_STYLESETFORE, STYLE_DEFAULT, ::GetSysColor(COLOR_WINDOWTEXT));
\r
104 Call(SCI_STYLESETBACK, STYLE_DEFAULT, ::GetSysColor(COLOR_WINDOW));
\r
105 Call(SCI_SETSELFORE, TRUE, ::GetSysColor(COLOR_HIGHLIGHTTEXT));
\r
106 Call(SCI_SETSELBACK, TRUE, ::GetSysColor(COLOR_HIGHLIGHT));
\r
107 Call(SCI_SETCARETFORE, ::GetSysColor(COLOR_WINDOWTEXT));
\r
108 Call(SCI_SETMODEVENTMASK, SC_MOD_INSERTTEXT | SC_MOD_DELETETEXT | SC_PERFORMED_UNDO | SC_PERFORMED_REDO);
\r
109 Call(SCI_INDICSETFORE, 1, 0x0000FF);
\r
110 CStringA sWordChars;
\r
111 CStringA sWhiteSpace;
\r
112 for (int i=0; i<255; ++i)
\r
114 if (i == '\r' || i == '\n')
\r
116 else if (i < 0x20 || i == ' ')
\r
117 sWhiteSpace += (char)i;
\r
118 else if (isalnum(i) || i == '\'')
\r
119 sWordChars += (char)i;
\r
121 Call(SCI_SETWORDCHARS, 0, (LPARAM)(LPCSTR)sWordChars);
\r
122 Call(SCI_SETWHITESPACECHARS, 0, (LPARAM)(LPCSTR)sWhiteSpace);
\r
123 // look for dictionary files and use them if found
\r
124 long langId = GetUserDefaultLCID();
\r
126 if ((lLanguage != 0)||(((DWORD)CRegStdWORD(_T("Software\\TortoiseSVN\\Spellchecker"), FALSE))==FALSE))
\r
128 if (!((lLanguage)&&(!LoadDictionaries(lLanguage))))
\r
132 LoadDictionaries(langId);
\r
133 DWORD lid = SUBLANGID(langId);
\r
137 langId = MAKELANGID(PRIMARYLANGID(langId), lid);
\r
139 else if (langId == 1033)
\r
143 } while ((langId)&&((pChecker==NULL)||(pThesaur==NULL)));
\r
146 Call(SCI_SETEDGEMODE, EDGE_NONE);
\r
147 Call(SCI_SETWRAPMODE, SC_WRAP_WORD);
\r
148 Call(SCI_ASSIGNCMDKEY, SCK_END, SCI_LINEENDWRAP);
\r
149 Call(SCI_ASSIGNCMDKEY, SCK_END + (SCMOD_SHIFT << 16), SCI_LINEENDWRAPEXTEND);
\r
150 Call(SCI_ASSIGNCMDKEY, SCK_HOME, SCI_HOMEWRAP);
\r
151 Call(SCI_ASSIGNCMDKEY, SCK_HOME + (SCMOD_SHIFT << 16), SCI_HOMEWRAPEXTEND);
\r
155 void CSciEdit::Init(const ProjectProperties& props)
\r
157 Init(props.lProjectLanguage);
\r
158 m_sCommand = CStringA(CUnicodeUtils::GetUTF8(props.sCheckRe));
\r
159 m_sBugID = CStringA(CUnicodeUtils::GetUTF8(props.sBugIDRe));
\r
160 m_sUrl = CStringA(CUnicodeUtils::GetUTF8(props.sUrl));
\r
162 if (props.nLogWidthMarker)
\r
164 Call(SCI_SETWRAPMODE, SC_WRAP_NONE);
\r
165 Call(SCI_SETEDGEMODE, EDGE_LINE);
\r
166 Call(SCI_SETEDGECOLUMN, props.nLogWidthMarker);
\r
170 Call(SCI_SETEDGEMODE, EDGE_NONE);
\r
171 Call(SCI_SETWRAPMODE, SC_WRAP_WORD);
\r
173 SetText(props.sLogTemplate);
\r
176 BOOL CSciEdit::LoadDictionaries(LONG lLanguageID)
\r
178 //Setup the spell checker and thesaurus
\r
180 CString sFolder = CPathUtils::GetAppDirectory();
\r
181 CString sFolderUp = CPathUtils::GetAppParentDirectory();
\r
184 GetLocaleInfo(MAKELCID(lLanguageID, SORT_DEFAULT), LOCALE_SISO639LANGNAME, buf, sizeof(buf));
\r
187 GetLocaleInfo(MAKELCID(lLanguageID, SORT_DEFAULT), LOCALE_SISO3166CTRYNAME, buf, sizeof(buf));
\r
189 if (pChecker==NULL)
\r
191 if ((PathFileExists(sFolder + sFile + _T(".aff"))) &&
\r
192 (PathFileExists(sFolder + sFile + _T(".dic"))))
\r
194 pChecker = new Hunspell(CStringA(sFolder + sFile + _T(".aff")), CStringA(sFolder + sFile + _T(".dic")));
\r
196 else if ((PathFileExists(sFolder + _T("dic\\") + sFile + _T(".aff"))) &&
\r
197 (PathFileExists(sFolder + _T("dic\\") + sFile + _T(".dic"))))
\r
199 pChecker = new Hunspell(CStringA(sFolder + _T("dic\\") + sFile + _T(".aff")), CStringA(sFolder + _T("dic\\") + sFile + _T(".dic")));
\r
201 else if ((PathFileExists(sFolderUp + sFile + _T(".aff"))) &&
\r
202 (PathFileExists(sFolderUp + sFile + _T(".dic"))))
\r
204 pChecker = new Hunspell(CStringA(sFolderUp + sFile + _T(".aff")), CStringA(sFolderUp + sFile + _T(".dic")));
\r
206 else if ((PathFileExists(sFolderUp + _T("dic\\") + sFile + _T(".aff"))) &&
\r
207 (PathFileExists(sFolderUp + _T("dic\\") + sFile + _T(".dic"))))
\r
209 pChecker = new Hunspell(CStringA(sFolderUp + _T("dic\\") + sFile + _T(".aff")), CStringA(sFolderUp + _T("dic\\") + sFile + _T(".dic")));
\r
211 else if ((PathFileExists(sFolderUp + _T("Languages\\") + sFile + _T(".aff"))) &&
\r
212 (PathFileExists(sFolderUp + _T("Languages\\") + sFile + _T(".dic"))))
\r
214 pChecker = new Hunspell(CStringA(sFolderUp + _T("Languages\\") + sFile + _T(".aff")), CStringA(sFolderUp + _T("Languages\\") + sFile + _T(".dic")));
\r
218 if (pThesaur==NULL)
\r
220 if ((PathFileExists(sFolder + _T("th_") + sFile + _T("_v2.idx"))) &&
\r
221 (PathFileExists(sFolder + _T("th_") + sFile + _T("_v2.dat"))))
\r
223 pThesaur = new MyThes(CStringA(sFolder + sFile + _T("_v2.idx")), CStringA(sFolder + sFile + _T("_v2.dat")));
\r
225 else if ((PathFileExists(sFolder + _T("dic\\th_") + sFile + _T("_v2.idx"))) &&
\r
226 (PathFileExists(sFolder + _T("dic\\th_") + sFile + _T("_v2.dat"))))
\r
228 pThesaur = new MyThes(CStringA(sFolder + _T("dic\\") + sFile + _T("_v2.idx")), CStringA(sFolder + _T("dic\\") + sFile + _T("_v2.dat")));
\r
230 else if ((PathFileExists(sFolderUp + _T("th_") + sFile + _T("_v2.idx"))) &&
\r
231 (PathFileExists(sFolderUp + _T("th_") + sFile + _T("_v2.dat"))))
\r
233 pThesaur = new MyThes(CStringA(sFolderUp + _T("th_") + sFile + _T("_v2.idx")), CStringA(sFolderUp + _T("th_") + sFile + _T("_v2.dat")));
\r
235 else if ((PathFileExists(sFolderUp + _T("dic\\th_") + sFile + _T("_v2.idx"))) &&
\r
236 (PathFileExists(sFolderUp + _T("dic\\th_") + sFile + _T("_v2.dat"))))
\r
238 pThesaur = new MyThes(CStringA(sFolderUp + _T("dic\\th_") + sFile + _T("_v2.idx")), CStringA(sFolderUp + _T("dic\\th_") + sFile + _T("_v2.dat")));
\r
240 else if ((PathFileExists(sFolderUp + _T("Languages\\th_") + sFile + _T("_v2.idx"))) &&
\r
241 (PathFileExists(sFolderUp + _T("Languages\\th_") + sFile + _T("_v2.dat"))))
\r
243 pThesaur = new MyThes(CStringA(sFolderUp + _T("Languages\\th_") + sFile + _T("_v2.idx")), CStringA(sFolderUp + _T("Languages\\th_") + sFile + _T("_v2.dat")));
\r
249 const char * encoding = pChecker->get_dic_encoding();
\r
250 ATLTRACE(encoding);
\r
251 int n = sizeof(enc2locale) / sizeof(enc2locale[0]);
\r
252 m_spellcodepage = 0;
\r
253 for (int i = 0; i < n; i++)
\r
255 if (strcmp(encoding,enc2locale[i].def_enc) == 0)
\r
257 m_spellcodepage = atoi(enc2locale[i].cp);
\r
260 m_personalDict.Init(lLanguageID);
\r
262 if ((pThesaur)||(pChecker))
\r
267 LRESULT CSciEdit::Call(UINT message, WPARAM wParam, LPARAM lParam)
\r
269 ASSERT(::IsWindow(m_hWnd)); //Window must be valid
\r
270 ASSERT(m_DirectFunction); //Direct function must be valid
\r
271 return ((SciFnDirect) m_DirectFunction)(m_DirectPointer, message, wParam, lParam);
\r
274 CString CSciEdit::StringFromControl(const CStringA& text)
\r
278 int codepage = Call(SCI_GETCODEPAGE);
\r
279 int reslen = MultiByteToWideChar(codepage, 0, text, text.GetLength(), 0, 0);
\r
280 MultiByteToWideChar(codepage, 0, text, text.GetLength(), sText.GetBuffer(reslen+1), reslen+1);
\r
281 sText.ReleaseBuffer(reslen);
\r
288 CStringA CSciEdit::StringForControl(const CString& text)
\r
292 int codepage = Call(SCI_GETCODEPAGE);
\r
293 int reslen = WideCharToMultiByte(codepage, 0, text, text.GetLength(), 0, 0, 0, 0);
\r
294 WideCharToMultiByte(codepage, 0, text, text.GetLength(), sTextA.GetBuffer(reslen), reslen, 0, 0);
\r
295 sTextA.ReleaseBuffer(reslen);
\r
299 ATLTRACE("string length %d\n", sTextA.GetLength());
\r
303 void CSciEdit::SetText(const CString& sText)
\r
305 CStringA sTextA = StringForControl(sText);
\r
306 Call(SCI_SETTEXT, 0, (LPARAM)(LPCSTR)sTextA);
\r
308 // Scintilla seems to have problems with strings that
\r
309 // aren't terminated by a newline char. Once that char
\r
310 // is there, it can be removed without problems.
\r
311 // So we add here a newline, then remove it again.
\r
312 Call(SCI_DOCUMENTEND);
\r
314 Call(SCI_DELETEBACK);
\r
317 void CSciEdit::InsertText(const CString& sText, bool bNewLine)
\r
319 CStringA sTextA = StringForControl(sText);
\r
320 Call(SCI_REPLACESEL, 0, (LPARAM)(LPCSTR)sTextA);
\r
322 Call(SCI_REPLACESEL, 0, (LPARAM)(LPCSTR)"\n");
\r
325 CString CSciEdit::GetText()
\r
327 LRESULT len = Call(SCI_GETTEXT, 0, 0);
\r
329 Call(SCI_GETTEXT, len+1, (LPARAM)(LPCSTR)sTextA.GetBuffer(len+1));
\r
330 sTextA.ReleaseBuffer();
\r
331 return StringFromControl(sTextA);
\r
334 CString CSciEdit::GetWordUnderCursor(bool bSelectWord)
\r
336 TEXTRANGEA textrange;
\r
337 int pos = Call(SCI_GETCURRENTPOS);
\r
338 textrange.chrg.cpMin = Call(SCI_WORDSTARTPOSITION, pos, TRUE);
\r
339 if ((pos == textrange.chrg.cpMin)||(textrange.chrg.cpMin < 0))
\r
341 textrange.chrg.cpMax = Call(SCI_WORDENDPOSITION, textrange.chrg.cpMin, TRUE);
\r
343 char * textbuffer = new char[textrange.chrg.cpMax - textrange.chrg.cpMin + 1];
\r
345 textrange.lpstrText = textbuffer;
\r
346 Call(SCI_GETTEXTRANGE, 0, (LPARAM)&textrange);
\r
349 Call(SCI_SETSEL, textrange.chrg.cpMin, textrange.chrg.cpMax);
\r
351 CString sRet = StringFromControl(textbuffer);
\r
352 delete [] textbuffer;
\r
356 void CSciEdit::SetFont(CString sFontName, int iFontSizeInPoints)
\r
358 Call(SCI_STYLESETFONT, STYLE_DEFAULT, (LPARAM)(LPCSTR)CStringA(sFontName));
\r
359 Call(SCI_STYLESETSIZE, STYLE_DEFAULT, iFontSizeInPoints);
\r
360 Call(SCI_STYLECLEARALL);
\r
362 LPARAM color = (LPARAM)GetSysColor(COLOR_HIGHLIGHT);
\r
363 // set the styles for the bug ID strings
\r
364 Call(SCI_STYLESETBOLD, STYLE_ISSUEBOLD, (LPARAM)TRUE);
\r
365 Call(SCI_STYLESETFORE, STYLE_ISSUEBOLD, color);
\r
366 Call(SCI_STYLESETBOLD, STYLE_ISSUEBOLDITALIC, (LPARAM)TRUE);
\r
367 Call(SCI_STYLESETITALIC, STYLE_ISSUEBOLDITALIC, (LPARAM)TRUE);
\r
368 Call(SCI_STYLESETFORE, STYLE_ISSUEBOLDITALIC, color);
\r
369 Call(SCI_STYLESETHOTSPOT, STYLE_ISSUEBOLDITALIC, (LPARAM)TRUE);
\r
371 // set the formatted text styles
\r
372 Call(SCI_STYLESETBOLD, STYLE_BOLD, (LPARAM)TRUE);
\r
373 Call(SCI_STYLESETITALIC, STYLE_ITALIC, (LPARAM)TRUE);
\r
374 Call(SCI_STYLESETUNDERLINE, STYLE_UNDERLINED, (LPARAM)TRUE);
\r
376 // set the style for URLs
\r
377 Call(SCI_STYLESETFORE, STYLE_URL, color);
\r
378 Call(SCI_STYLESETHOTSPOT, STYLE_URL, (LPARAM)TRUE);
\r
380 Call(SCI_SETHOTSPOTACTIVEUNDERLINE, (LPARAM)TRUE);
\r
383 void CSciEdit::SetAutoCompletionList(const std::set<CString>& list, const TCHAR separator)
\r
385 //copy the auto completion list.
\r
387 //SK: instead of creating a copy of that list, we could accept a pointer
\r
388 //to the list and use that instead. But then the caller would have to make
\r
389 //sure that the list persists over the lifetime of the control!
\r
390 m_autolist.clear();
\r
392 m_separator = separator;
\r
395 BOOL CSciEdit::IsMisspelled(const CString& sWord)
\r
397 // convert the string from the control to the encoding of the spell checker module.
\r
399 if (m_spellcodepage)
\r
402 buf = sWordA.GetBuffer(sWord.GetLength()*4 + 1);
\r
403 int lengthIncTerminator =
\r
404 WideCharToMultiByte(m_spellcodepage, 0, sWord, -1, buf, sWord.GetLength()*4, NULL, NULL);
\r
405 sWordA.ReleaseBuffer(lengthIncTerminator-1);
\r
408 sWordA = CStringA(sWord);
\r
409 sWordA.Trim("\'\".,");
\r
410 // words starting with a digit are treated as correctly spelled
\r
411 if (_istdigit(sWord.GetAt(0)))
\r
413 // words in the personal dictionary are correct too
\r
414 if (m_personalDict.FindWord(sWord))
\r
417 // now we actually check the spelling...
\r
418 if (!pChecker->spell(sWordA))
\r
420 // the word is marked as misspelled, we now check whether the word
\r
421 // is maybe a composite identifier
\r
422 // a composite identifier consists of multiple words, with each word
\r
423 // separated by a change in lower to uppercase letters
\r
424 if (sWord.GetLength() > 1)
\r
428 while (wordend < sWord.GetLength())
\r
430 while ((wordend < sWord.GetLength())&&(!_istupper(sWord[wordend])))
\r
432 if ((wordstart == 0)&&(wordend == sWord.GetLength()))
\r
434 // words in the auto list are also assumed correctly spelled
\r
435 if (m_autolist.find(sWord) != m_autolist.end())
\r
439 sWordA = CStringA(sWord.Mid(wordstart, wordend-wordstart));
\r
440 if ((sWordA.GetLength() > 2)&&(!pChecker->spell(sWordA)))
\r
444 wordstart = wordend;
\r
452 void CSciEdit::CheckSpelling()
\r
454 if (pChecker == NULL)
\r
457 TEXTRANGEA textrange;
\r
459 LRESULT firstline = Call(SCI_GETFIRSTVISIBLELINE);
\r
460 LRESULT lastline = firstline + Call(SCI_LINESONSCREEN);
\r
461 textrange.chrg.cpMin = Call(SCI_POSITIONFROMLINE, firstline);
\r
462 textrange.chrg.cpMax = textrange.chrg.cpMin;
\r
463 LRESULT lastpos = Call(SCI_POSITIONFROMLINE, lastline) + Call(SCI_LINELENGTH, lastline);
\r
465 lastpos = Call(SCI_GETLENGTH)-textrange.chrg.cpMin;
\r
466 while (textrange.chrg.cpMax < lastpos)
\r
468 textrange.chrg.cpMin = Call(SCI_WORDSTARTPOSITION, textrange.chrg.cpMax+1, TRUE);
\r
469 if (textrange.chrg.cpMin < textrange.chrg.cpMax)
\r
471 textrange.chrg.cpMax = Call(SCI_WORDENDPOSITION, textrange.chrg.cpMin, TRUE);
\r
472 if (textrange.chrg.cpMin == textrange.chrg.cpMax)
\r
474 textrange.chrg.cpMax++;
\r
477 ATLASSERT(textrange.chrg.cpMax >= textrange.chrg.cpMin);
\r
478 char * textbuffer = new char[textrange.chrg.cpMax - textrange.chrg.cpMin + 2];
\r
479 SecureZeroMemory(textbuffer, textrange.chrg.cpMax - textrange.chrg.cpMin + 2);
\r
480 textrange.lpstrText = textbuffer;
\r
481 textrange.chrg.cpMax++;
\r
482 Call(SCI_GETTEXTRANGE, 0, (LPARAM)&textrange);
\r
483 int len = strlen(textrange.lpstrText);
\r
486 textrange.chrg.cpMax--;
\r
487 Call(SCI_GETTEXTRANGE, 0, (LPARAM)&textrange);
\r
488 len = strlen(textrange.lpstrText);
\r
489 textrange.chrg.cpMax++;
\r
492 if (len && textrange.lpstrText[len - 1] == '.')
\r
494 // Try to ignore file names from the auto list.
\r
495 // Do do this, for each word ending with '.' we extract next word and check
\r
496 // whether the combined string is present in auto list.
\r
497 TEXTRANGEA twoWords;
\r
498 twoWords.chrg.cpMin = textrange.chrg.cpMin;
\r
499 twoWords.chrg.cpMax = Call(SCI_WORDENDPOSITION, textrange.chrg.cpMax + 1, TRUE);
\r
500 twoWords.lpstrText = new char[twoWords.chrg.cpMax - twoWords.chrg.cpMin + 1];
\r
501 SecureZeroMemory(twoWords.lpstrText, twoWords.chrg.cpMax - twoWords.chrg.cpMin + 1);
\r
502 Call(SCI_GETTEXTRANGE, 0, (LPARAM)&twoWords);
\r
503 CString sWord = StringFromControl(twoWords.lpstrText);
\r
504 delete [] twoWords.lpstrText;
\r
505 if (m_autolist.find(sWord) != m_autolist.end())
\r
507 //mark word as correct (remove the squiggle line)
\r
508 Call(SCI_STARTSTYLING, twoWords.chrg.cpMin, INDICS_MASK);
\r
509 Call(SCI_SETSTYLING, twoWords.chrg.cpMax - twoWords.chrg.cpMin, 0);
\r
510 textrange.chrg.cpMax = twoWords.chrg.cpMax;
\r
511 delete [] textbuffer;
\r
516 textrange.lpstrText[len - 1] = 0;
\r
517 textrange.chrg.cpMax--;
\r
518 if (strlen(textrange.lpstrText) > 0)
\r
520 CString sWord = StringFromControl(textrange.lpstrText);
\r
521 if ((GetStyleAt(textrange.chrg.cpMin) != STYLE_URL) && IsMisspelled(sWord))
\r
523 //mark word as misspelled
\r
524 Call(SCI_STARTSTYLING, textrange.chrg.cpMin, INDICS_MASK);
\r
525 Call(SCI_SETSTYLING, textrange.chrg.cpMax - textrange.chrg.cpMin, INDIC1_MASK);
\r
529 //mark word as correct (remove the squiggle line)
\r
530 Call(SCI_STARTSTYLING, textrange.chrg.cpMin, INDICS_MASK);
\r
531 Call(SCI_SETSTYLING, textrange.chrg.cpMax - textrange.chrg.cpMin, 0);
\r
534 delete [] textbuffer;
\r
538 void CSciEdit::SuggestSpellingAlternatives()
\r
540 if (pChecker == NULL)
\r
542 CString word = GetWordUnderCursor(true);
\r
543 Call(SCI_SETCURRENTPOS, Call(SCI_WORDSTARTPOSITION, Call(SCI_GETCURRENTPOS), TRUE));
\r
544 if (word.IsEmpty())
\r
547 int ns = pChecker->suggest(&wlst, CStringA(word));
\r
550 CString suggestions;
\r
551 for (int i=0; i < ns; i++)
\r
553 suggestions += CString(wlst[i]) + m_separator;
\r
557 suggestions.TrimRight(m_separator);
\r
558 if (suggestions.IsEmpty())
\r
560 Call(SCI_AUTOCSETSEPARATOR, (WPARAM)CStringA(m_separator).GetAt(0));
\r
561 Call(SCI_AUTOCSETDROPRESTOFWORD, 1);
\r
562 Call(SCI_AUTOCSHOW, 0, (LPARAM)(LPCSTR)StringForControl(suggestions));
\r
567 void CSciEdit::DoAutoCompletion(int nMinPrefixLength)
\r
569 if (m_autolist.size()==0)
\r
571 if (Call(SCI_AUTOCACTIVE))
\r
573 CString word = GetWordUnderCursor();
\r
574 if (word.GetLength() < nMinPrefixLength)
\r
575 return; //don't auto complete yet, word is too short
\r
576 int pos = Call(SCI_GETCURRENTPOS);
\r
577 if (pos != Call(SCI_WORDENDPOSITION, pos, TRUE))
\r
578 return; //don't auto complete if we're not at the end of a word
\r
579 CString sAutoCompleteList;
\r
582 for (std::set<CString>::const_iterator lowerit = m_autolist.lower_bound(word);
\r
583 lowerit != m_autolist.end(); ++lowerit)
\r
585 int compare = word.CompareNoCase(lowerit->Left(word.GetLength()));
\r
588 else if (compare == 0)
\r
590 sAutoCompleteList += *lowerit + m_separator;
\r
597 sAutoCompleteList.TrimRight(m_separator);
\r
598 if (sAutoCompleteList.IsEmpty())
\r
601 Call(SCI_AUTOCSETSEPARATOR, (WPARAM)CStringA(m_separator).GetAt(0));
\r
602 Call(SCI_AUTOCSHOW, word.GetLength(), (LPARAM)(LPCSTR)StringForControl(sAutoCompleteList));
\r
605 BOOL CSciEdit::OnChildNotify(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pLResult)
\r
607 if (message != WM_NOTIFY)
\r
608 return CWnd::OnChildNotify(message, wParam, lParam, pLResult);
\r
610 LPNMHDR lpnmhdr = (LPNMHDR) lParam;
\r
611 SCNotification * lpSCN = (SCNotification *)lParam;
\r
613 if(lpnmhdr->hwndFrom==m_hWnd)
\r
615 switch(lpnmhdr->code)
\r
617 case SCN_CHARADDED:
\r
619 if ((lpSCN->ch < 32)&&(lpSCN->ch != 13)&&(lpSCN->ch != 10))
\r
620 Call(SCI_DELETEBACK);
\r
623 DoAutoCompletion(3);
\r
628 case SCN_STYLENEEDED:
\r
630 int startstylepos = Call(SCI_GETENDSTYLED);
\r
631 int endstylepos = ((SCNotification *)lpnmhdr)->position;
\r
632 MarkEnteredBugID(startstylepos, endstylepos);
\r
633 StyleEnteredText(startstylepos, endstylepos);
\r
634 StyleURLs(startstylepos, endstylepos);
\r
636 WrapLines(startstylepos, endstylepos);
\r
640 case SCN_HOTSPOTCLICK:
\r
642 TEXTRANGEA textrange;
\r
643 textrange.chrg.cpMin = lpSCN->position;
\r
644 textrange.chrg.cpMax = lpSCN->position;
\r
645 DWORD style = GetStyleAt(lpSCN->position);
\r
646 while (GetStyleAt(textrange.chrg.cpMin - 1) == style)
\r
647 --textrange.chrg.cpMin;
\r
648 while (GetStyleAt(textrange.chrg.cpMax + 1) == style)
\r
649 ++textrange.chrg.cpMax;
\r
650 ++textrange.chrg.cpMax;
\r
651 char * textbuffer = new char[textrange.chrg.cpMax - textrange.chrg.cpMin + 1];
\r
652 textrange.lpstrText = textbuffer;
\r
653 Call(SCI_GETTEXTRANGE, 0, (LPARAM)&textrange);
\r
655 if (style == STYLE_URL)
\r
656 url = StringFromControl(textbuffer);
\r
660 url.Replace(_T("%BUGID%"), StringFromControl(textbuffer));
\r
662 delete [] textbuffer;
\r
663 if (!url.IsEmpty())
\r
664 ShellExecute(GetParent()->GetSafeHwnd(), _T("open"), url, NULL, NULL, SW_SHOWDEFAULT);
\r
669 return CWnd::OnChildNotify(message, wParam, lParam, pLResult);
\r
672 BEGIN_MESSAGE_MAP(CSciEdit, CWnd)
\r
674 ON_WM_CONTEXTMENU()
\r
677 void CSciEdit::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
\r
683 if ((Call(SCI_AUTOCACTIVE)==0)&&(Call(SCI_CALLTIPACTIVE)==0))
\r
684 ::SendMessage(GetParent()->GetSafeHwnd(), WM_CLOSE, 0, 0);
\r
688 CWnd::OnKeyDown(nChar, nRepCnt, nFlags);
\r
691 BOOL CSciEdit::PreTranslateMessage(MSG* pMsg)
\r
693 if (pMsg->message == WM_KEYDOWN)
\r
695 switch (pMsg->wParam)
\r
699 if (GetKeyState(VK_CONTROL) & 0x8000)
\r
701 DoAutoCompletion(1);
\r
707 // The TAB cannot be handled in OnKeyDown because it is too late by then.
\r
709 if (GetKeyState(VK_CONTROL)&0x8000)
\r
711 //Ctrl-Tab was pressed, this means we should provide the user with
\r
712 //a list of possible spell checking alternatives to the word under
\r
714 SuggestSpellingAlternatives();
\r
717 else if (!Call(SCI_AUTOCACTIVE))
\r
719 ::PostMessage(GetParent()->GetSafeHwnd(), WM_NEXTDLGCTL, GetKeyState(VK_SHIFT)&0x8000, 0);
\r
726 return CWnd::PreTranslateMessage(pMsg);
\r
729 void CSciEdit::OnContextMenu(CWnd* /*pWnd*/, CPoint point)
\r
731 int anchor = Call(SCI_GETANCHOR);
\r
732 int currentpos = Call(SCI_GETCURRENTPOS);
\r
733 int selstart = Call(SCI_GETSELECTIONSTART);
\r
734 int selend = Call(SCI_GETSELECTIONEND);
\r
736 if ((point.x == -1) && (point.y == -1))
\r
739 GetClientRect(&rect);
\r
740 ClientToScreen(&rect);
\r
741 point = rect.CenterPoint();
\r
742 pointpos = Call(SCI_GETCURRENTPOS);
\r
746 // change the cursor position to the point where the user
\r
748 CPoint clientpoint = point;
\r
749 ScreenToClient(&clientpoint);
\r
750 pointpos = Call(SCI_POSITIONFROMPOINT, clientpoint.x, clientpoint.y);
\r
752 CString sMenuItemText;
\r
754 bool bRestoreCursor = true;
\r
755 if (popup.CreatePopupMenu())
\r
757 bool bCanUndo = !!Call(SCI_CANUNDO);
\r
758 bool bCanRedo = !!Call(SCI_CANREDO);
\r
759 bool bHasSelection = (selend-selstart > 0);
\r
760 bool bCanPaste = !!Call(SCI_CANPASTE);
\r
761 UINT uEnabledMenu = MF_STRING | MF_ENABLED;
\r
762 UINT uDisabledMenu = MF_STRING | MF_GRAYED;
\r
764 // find the word under the cursor
\r
768 // setting the cursor clears the selection
\r
769 Call(SCI_SETANCHOR, pointpos);
\r
770 Call(SCI_SETCURRENTPOS, pointpos);
\r
771 sWord = GetWordUnderCursor();
\r
772 // restore the selection
\r
773 Call(SCI_SETSELECTIONSTART, selstart);
\r
774 Call(SCI_SETSELECTIONEND, selend);
\r
777 sWord = GetWordUnderCursor();
\r
778 CStringA worda = CStringA(sWord);
\r
780 int nCorrections = 1;
\r
781 bool bSpellAdded = false;
\r
782 // check if the word under the cursor is spelled wrong
\r
783 if ((pChecker)&&(!worda.IsEmpty()))
\r
786 // get the spell suggestions
\r
787 int ns = pChecker->suggest(&wlst,worda);
\r
790 // add the suggestions to the context menu
\r
791 for (int i=0; i < ns; i++)
\r
793 bSpellAdded = true;
\r
794 CString sug = CString(wlst[i]);
\r
795 popup.InsertMenu((UINT)-1, 0, nCorrections++, sug);
\r
801 // only add a separator if spelling correction suggestions were added
\r
803 popup.AppendMenu(MF_SEPARATOR);
\r
805 // also allow the user to add the word to the custom dictionary so
\r
806 // it won't show up as misspelled anymore
\r
807 if ((sWord.GetLength()<PDICT_MAX_WORD_LENGTH)&&((pChecker)&&(m_autolist.find(sWord) == m_autolist.end())&&(!pChecker->spell(worda)))&&
\r
808 (!_istdigit(sWord.GetAt(0)))&&(!m_personalDict.FindWord(sWord)))
\r
810 sMenuItemText.Format(IDS_SCIEDIT_ADDWORD, sWord);
\r
811 popup.AppendMenu(uEnabledMenu, SCI_ADDWORD, sMenuItemText);
\r
812 // another separator
\r
813 popup.AppendMenu(MF_SEPARATOR);
\r
816 // add the 'default' entries
\r
817 sMenuItemText.LoadString(IDS_SCIEDIT_UNDO);
\r
818 popup.AppendMenu(bCanUndo ? uEnabledMenu : uDisabledMenu, SCI_UNDO, sMenuItemText);
\r
819 sMenuItemText.LoadString(IDS_SCIEDIT_REDO);
\r
820 popup.AppendMenu(bCanRedo ? uEnabledMenu : uDisabledMenu, SCI_REDO, sMenuItemText);
\r
822 popup.AppendMenu(MF_SEPARATOR);
\r
824 sMenuItemText.LoadString(IDS_SCIEDIT_CUT);
\r
825 popup.AppendMenu(bHasSelection ? uEnabledMenu : uDisabledMenu, SCI_CUT, sMenuItemText);
\r
826 sMenuItemText.LoadString(IDS_SCIEDIT_COPY);
\r
827 popup.AppendMenu(bHasSelection ? uEnabledMenu : uDisabledMenu, SCI_COPY, sMenuItemText);
\r
828 sMenuItemText.LoadString(IDS_SCIEDIT_PASTE);
\r
829 popup.AppendMenu(bCanPaste ? uEnabledMenu : uDisabledMenu, SCI_PASTE, sMenuItemText);
\r
831 popup.AppendMenu(MF_SEPARATOR);
\r
833 sMenuItemText.LoadString(IDS_SCIEDIT_SELECTALL);
\r
834 popup.AppendMenu(uEnabledMenu, SCI_SELECTALL, sMenuItemText);
\r
836 popup.AppendMenu(MF_SEPARATOR);
\r
838 sMenuItemText.LoadString(IDS_SCIEDIT_SPLITLINES);
\r
839 popup.AppendMenu(bHasSelection ? uEnabledMenu : uDisabledMenu, SCI_LINESSPLIT, sMenuItemText);
\r
841 popup.AppendMenu(MF_SEPARATOR);
\r
843 int nCustoms = nCorrections;
\r
844 // now add any custom context menus
\r
845 for (INT_PTR handlerindex = 0; handlerindex < m_arContextHandlers.GetCount(); ++handlerindex)
\r
847 CSciEditContextMenuInterface * pHandler = m_arContextHandlers.GetAt(handlerindex);
\r
848 pHandler->InsertMenuItems(popup, nCustoms);
\r
850 if (nCustoms > nCorrections)
\r
852 // custom menu entries present, so add another separator
\r
853 popup.AppendMenu(MF_SEPARATOR);
\r
857 // add found thesauri to sub menu's
\r
859 thesaurs.CreatePopupMenu();
\r
861 CPtrArray menuArray;
\r
862 if ((pThesaur)&&(!worda.IsEmpty()))
\r
866 int count = pThesaur->Lookup(worda, worda.GetLength(),&pmean);
\r
869 mentry * pm = pmean;
\r
870 for (int i=0; i < count; i++)
\r
872 CMenu * submenu = new CMenu();
\r
873 menuArray.Add(submenu);
\r
874 submenu->CreateMenu();
\r
875 for (int j=0; j < pm->count; j++)
\r
877 CString sug = CString(pm->psyns[j]);
\r
878 submenu->InsertMenu((UINT)-1, 0, nThesaurs++, sug);
\r
880 thesaurs.InsertMenu((UINT)-1, MF_POPUP, (UINT_PTR)(submenu->m_hMenu), CString(pm->defn));
\r
884 if ((count > 0)&&(point.x >= 0))
\r
886 #ifdef IDS_SPELLEDIT_THESAURUS
\r
887 sMenuItemText.LoadString(IDS_SPELLEDIT_THESAURUS);
\r
888 popup.InsertMenu((UINT)-1, MF_POPUP, (UINT_PTR)thesaurs.m_hMenu, sMenuItemText);
\r
890 popup.InsertMenu((UINT)-1, MF_POPUP, (UINT_PTR)thesaurs.m_hMenu, _T("Thesaurus"));
\r
892 nThesaurs = nCustoms;
\r
896 sMenuItemText.LoadString(IDS_SPELLEDIT_NOTHESAURUS);
\r
897 popup.AppendMenu(MF_DISABLED | MF_GRAYED | MF_STRING, 0, sMenuItemText);
\r
900 pThesaur->CleanUpAfterLookup(&pmean, count);
\r
904 sMenuItemText.LoadString(IDS_SPELLEDIT_NOTHESAURUS);
\r
905 popup.AppendMenu(MF_DISABLED | MF_GRAYED | MF_STRING, 0, sMenuItemText);
\r
908 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this, 0);
\r
912 break; // no command selected
\r
913 case SCI_SELECTALL:
\r
914 bRestoreCursor = false;
\r
924 m_personalDict.AddWord(sWord);
\r
927 case SCI_LINESSPLIT:
\r
929 int marker = Call(SCI_GETEDGECOLUMN) * Call(SCI_TEXTWIDTH, 0, (LPARAM)" ");
\r
932 Call(SCI_TARGETFROMSELECTION);
\r
933 Call(SCI_LINESJOIN);
\r
934 Call(SCI_LINESSPLIT, marker);
\r
939 if (cmd < nCorrections)
\r
941 Call(SCI_SETANCHOR, pointpos);
\r
942 Call(SCI_SETCURRENTPOS, pointpos);
\r
943 GetWordUnderCursor(true);
\r
945 popup.GetMenuString(cmd, temp, 0);
\r
946 // setting the cursor clears the selection
\r
947 Call(SCI_REPLACESEL, 0, (LPARAM)(LPCSTR)StringForControl(temp));
\r
949 else if (cmd < (nCorrections+nCustoms))
\r
951 for (INT_PTR handlerindex = 0; handlerindex < m_arContextHandlers.GetCount(); ++handlerindex)
\r
953 CSciEditContextMenuInterface * pHandler = m_arContextHandlers.GetAt(handlerindex);
\r
954 if (pHandler->HandleMenuItemClick(cmd, this))
\r
959 else if (cmd <= (nThesaurs+nCorrections+nCustoms))
\r
961 Call(SCI_SETANCHOR, pointpos);
\r
962 Call(SCI_SETCURRENTPOS, pointpos);
\r
963 GetWordUnderCursor(true);
\r
965 thesaurs.GetMenuString(cmd, temp, 0);
\r
966 Call(SCI_REPLACESEL, 0, (LPARAM)(LPCSTR)StringForControl(temp));
\r
971 for (INT_PTR index = 0; index < menuArray.GetCount(); ++index)
\r
973 CMenu * pMenu = (CMenu*)menuArray[index];
\r
978 if (bRestoreCursor)
\r
980 // restore the anchor and cursor position
\r
981 Call(SCI_SETCURRENTPOS, currentpos);
\r
982 Call(SCI_SETANCHOR, anchor);
\r
986 bool CSciEdit::StyleEnteredText(int startstylepos, int endstylepos)
\r
988 bool bStyled = false;
\r
989 const int line = Call(SCI_LINEFROMPOSITION, startstylepos);
\r
990 const int line_number_end = Call(SCI_LINEFROMPOSITION, endstylepos);
\r
991 for (int line_number = line; line_number <= line_number_end; ++line_number)
\r
993 int offset = Call(SCI_POSITIONFROMLINE, line_number);
\r
994 int line_len = Call(SCI_LINELENGTH, line_number);
\r
995 char * linebuffer = new char[line_len+1];
\r
996 Call(SCI_GETLINE, line_number, (LPARAM)linebuffer);
\r
997 linebuffer[line_len] = 0;
\r
1000 while (FindStyleChars(linebuffer, '*', start, end))
\r
1002 Call(SCI_STARTSTYLING, start+offset, STYLE_MASK);
\r
1003 Call(SCI_SETSTYLING, end-start, STYLE_BOLD);
\r
1009 while (FindStyleChars(linebuffer, '^', start, end))
\r
1011 Call(SCI_STARTSTYLING, start+offset, STYLE_MASK);
\r
1012 Call(SCI_SETSTYLING, end-start, STYLE_ITALIC);
\r
1018 while (FindStyleChars(linebuffer, '_', start, end))
\r
1020 Call(SCI_STARTSTYLING, start+offset, STYLE_MASK);
\r
1021 Call(SCI_SETSTYLING, end-start, STYLE_UNDERLINED);
\r
1025 delete [] linebuffer;
\r
1030 bool CSciEdit::WrapLines(int startpos, int endpos)
\r
1032 int markerX = Call(SCI_GETEDGECOLUMN) * Call(SCI_TEXTWIDTH, 0, (LPARAM)" ");
\r
1035 Call(SCI_SETTARGETSTART, startpos);
\r
1036 Call(SCI_SETTARGETEND, endpos);
\r
1037 Call(SCI_LINESSPLIT, markerX);
\r
1043 void CSciEdit::AdvanceUTF8(const char * str, int& pos)
\r
1045 if ((str[pos] & 0xE0)==0xC0)
\r
1047 // utf8 2-byte sequence
\r
1050 else if ((str[pos] & 0xF0)==0xE0)
\r
1052 // utf8 3-byte sequence
\r
1055 else if ((str[pos] & 0xF8)==0xF0)
\r
1057 // utf8 4-byte sequence
\r
1064 bool CSciEdit::FindStyleChars(const char * line, char styler, int& start, int& end)
\r
1070 AdvanceUTF8(line, i);
\r
1074 bool bFoundMarker = false;
\r
1075 CString sULine = CUnicodeUtils::GetUnicode(line);
\r
1076 // find a starting marker
\r
1077 while (line[i] != 0)
\r
1079 if (line[i] == styler)
\r
1081 if ((line[i+1]!=0)&&(IsCharAlphaNumeric(sULine[u+1]))&&
\r
1082 (((u>0)&&(!IsCharAlphaNumeric(sULine[u-1]))) || (u==0)))
\r
1085 AdvanceUTF8(line, i);
\r
1087 bFoundMarker = true;
\r
1091 AdvanceUTF8(line, i);
\r
1094 if (!bFoundMarker)
\r
1096 // find ending marker
\r
1097 bFoundMarker = false;
\r
1098 while (line[i] != 0)
\r
1100 if (line[i] == styler)
\r
1102 if ((IsCharAlphaNumeric(sULine[u-1]))&&
\r
1103 ((((u+1)<sULine.GetLength())&&(!IsCharAlphaNumeric(sULine[u+1]))) || ((u+1) == sULine.GetLength()))
\r
1108 bFoundMarker = true;
\r
1112 AdvanceUTF8(line, i);
\r
1115 return bFoundMarker;
\r
1118 BOOL CSciEdit::MarkEnteredBugID(int startstylepos, int endstylepos)
\r
1120 if (m_sCommand.IsEmpty())
\r
1122 // get the text between the start and end position we have to style
\r
1123 const int line_number = Call(SCI_LINEFROMPOSITION, startstylepos);
\r
1124 int start_pos = Call(SCI_POSITIONFROMLINE, (WPARAM)line_number);
\r
1125 int end_pos = endstylepos;
\r
1127 if (start_pos == end_pos)
\r
1129 if (start_pos > end_pos)
\r
1131 int switchtemp = start_pos;
\r
1132 start_pos = end_pos;
\r
1133 end_pos = switchtemp;
\r
1136 char * textbuffer = new char[end_pos - start_pos + 2];
\r
1137 TEXTRANGEA textrange;
\r
1138 textrange.lpstrText = textbuffer;
\r
1139 textrange.chrg.cpMin = start_pos;
\r
1140 textrange.chrg.cpMax = end_pos;
\r
1141 Call(SCI_GETTEXTRANGE, 0, (LPARAM)&textrange);
\r
1142 CStringA msg = CStringA(textbuffer);
\r
1144 Call(SCI_STARTSTYLING, start_pos, STYLE_MASK);
\r
1146 if (!m_sBugID.IsEmpty())
\r
1148 // match with two regex strings (without grouping!)
\r
1151 const tr1::regex regCheck(m_sCommand);
\r
1152 const tr1::regex regBugID(m_sBugID);
\r
1153 const tr1::sregex_iterator end;
\r
1156 for (tr1::sregex_iterator it(s.begin(), s.end(), regCheck); it != end; ++it)
\r
1158 // clear the styles up to the match position
\r
1159 Call(SCI_SETSTYLING, it->position(0)-pos, STYLE_DEFAULT);
\r
1160 pos = it->position(0);
\r
1162 // (*it)[0] is the matched string
\r
1163 string matchedString = (*it)[0];
\r
1164 for (tr1::sregex_iterator it2(matchedString.begin(), matchedString.end(), regBugID); it2 != end; ++it2)
\r
1166 ATLTRACE(_T("matched id : %s\n"), (*it2)[0].str().c_str());
\r
1168 // bold style up to the id match
\r
1169 ATLTRACE("position = %ld\n", it2->position(0));
\r
1170 if (it2->position(0))
\r
1171 Call(SCI_SETSTYLING, it2->position(0), STYLE_ISSUEBOLD);
\r
1172 // bold and recursive style for the bug ID itself
\r
1173 if ((*it2)[0].str().size())
\r
1174 Call(SCI_SETSTYLING, (*it2)[0].str().size(), STYLE_ISSUEBOLDITALIC);
\r
1176 pos = it->position(0) + matchedString.size();
\r
1178 // bold style for the rest of the string which isn't matched
\r
1180 Call(SCI_SETSTYLING, s.size()-pos, STYLE_DEFAULT);
\r
1182 catch (exception) {}
\r
1188 const tr1::regex regCheck(m_sCommand);
\r
1189 const tr1::sregex_iterator end;
\r
1192 for (tr1::sregex_iterator it(s.begin(), s.end(), regCheck); it != end; ++it)
\r
1194 // clear the styles up to the match position
\r
1195 Call(SCI_SETSTYLING, it->position(0)-pos, STYLE_DEFAULT);
\r
1196 pos = it->position(0);
\r
1198 const tr1::smatch match = *it;
\r
1199 // we define group 1 as the whole issue text and
\r
1200 // group 2 as the bug ID
\r
1201 if (match.size() >= 2)
\r
1203 ATLTRACE(_T("matched id : %s\n"), string(match[1]).c_str());
\r
1204 Call(SCI_SETSTYLING, match[1].first-s.begin()-pos, STYLE_ISSUEBOLD);
\r
1205 Call(SCI_SETSTYLING, string(match[1]).size(), STYLE_ISSUEBOLDITALIC);
\r
1206 pos = match[1].second-s.begin();
\r
1210 catch (exception) {}
\r
1212 delete [] textbuffer;
\r
1217 bool CSciEdit::IsValidURLChar(unsigned char ch)
\r
1219 return isalnum(ch) ||
\r
1220 ch == '_' || ch == '/' || ch == ';' || ch == '?' || ch == '&' || ch == '=' ||
\r
1221 ch == '%' || ch == ':' || ch == '.' || ch == '#' || ch == '-' || ch == '+';
\r
1224 void CSciEdit::StyleURLs(int startstylepos, int endstylepos)
\r
1226 const int line_number = Call(SCI_LINEFROMPOSITION, startstylepos);
\r
1227 startstylepos = Call(SCI_POSITIONFROMLINE, (WPARAM)line_number);
\r
1229 int len = endstylepos - startstylepos + 1;
\r
1230 char* textbuffer = new char[len + 1];
\r
1231 TEXTRANGEA textrange;
\r
1232 textrange.lpstrText = textbuffer;
\r
1233 textrange.chrg.cpMin = startstylepos;
\r
1234 textrange.chrg.cpMax = endstylepos;
\r
1235 Call(SCI_GETTEXTRANGE, 0, (LPARAM)&textrange);
\r
1236 // we're dealing with utf8 encoded text here, which means one glyph is
\r
1237 // not necessarily one byte/wchar_t
\r
1238 // that's why we use CStringA to still get a correct char index
\r
1239 CStringA msg = textbuffer;
\r
1240 delete [] textbuffer;
\r
1242 int starturl = -1;
\r
1243 for(int i = 0; i <= msg.GetLength(); )
\r
1245 if ((i < len) && IsValidURLChar(msg[i]))
\r
1252 if ((starturl >= 0) && IsUrl(msg.Mid(starturl, i - starturl)))
\r
1254 ASSERT(startstylepos + i <= endstylepos);
\r
1255 Call(SCI_STARTSTYLING, startstylepos + starturl, STYLE_MASK);
\r
1256 Call(SCI_SETSTYLING, i - starturl, STYLE_URL);
\r
1260 AdvanceUTF8(msg, i);
\r
1264 bool CSciEdit::IsUrl(const CStringA& sText)
\r
1266 if (!PathIsURLA(sText))
\r
1268 if (sText.Find("://")>=0)
\r