OSDN Git Service

Commit DialogBox compile Okay
[tortoisegit/TortoiseGitJp.git] / Utils / MiscUI / SciEdit.cpp
1 // TortoiseSVN - a Windows shell extension for easy version control\r
2 \r
3 // Copyright (C) 2003-2008 - TortoiseSVN\r
4 \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
9 \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
14 \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
18 //\r
19 #include "StdAfx.h"\r
20 #include "resource.h"\r
21 //#include "AppUtils.h"\r
22 #include "..\PathUtils.h"\r
23 #include "..\UnicodeUtils.h"\r
24 #include <string>\r
25 #include "..\registry.h"\r
26 #include ".\sciedit.h"\r
27 \r
28 using namespace std;\r
29 \r
30 \r
31 void CSciEditContextMenuInterface::InsertMenuItems(CMenu&, int&) {return;}\r
32 bool CSciEditContextMenuInterface::HandleMenuItemClick(int, CSciEdit *) {return false;}\r
33 \r
34 \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
41 \r
42 #define STYLE_MASK 0x1f\r
43 \r
44 #define SCI_ADDWORD                     2000\r
45 \r
46 struct loc_map {\r
47         const char * cp;\r
48         const char * def_enc;\r
49 };\r
50 \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
62         {"20866","KOI8-R"},\r
63         {"21866","KOI8-U"},\r
64         {"1251","microsoft-cp1251"},\r
65         };\r
66 \r
67 \r
68 IMPLEMENT_DYNAMIC(CSciEdit, CWnd)\r
69 \r
70 CSciEdit::CSciEdit(void) : m_DirectFunction(NULL)\r
71         , m_DirectPointer(NULL)\r
72         , pChecker(NULL)\r
73         , pThesaur(NULL)\r
74 {\r
75         m_hModule = ::LoadLibrary(_T("SciLexer.DLL"));\r
76 }\r
77 \r
78 CSciEdit::~CSciEdit(void)\r
79 {\r
80         m_personalDict.Save();\r
81         if (m_hModule)\r
82                 ::FreeLibrary(m_hModule);\r
83         if (pChecker)\r
84                 delete pChecker;\r
85         if (pThesaur)\r
86                 delete pThesaur;\r
87 }\r
88 \r
89 void CSciEdit::Init(LONG lLanguage)\r
90 {\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
113         {\r
114                 if (i == '\r' || i == '\n')\r
115                         continue;\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
120         }\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
125 \r
126         if ((lLanguage != 0)||(((DWORD)CRegStdWORD(_T("Software\\TortoiseSVN\\Spellchecker"), FALSE))==FALSE))\r
127         {\r
128                 if (!((lLanguage)&&(!LoadDictionaries(lLanguage))))\r
129                 {\r
130                         do\r
131                         {\r
132                                 LoadDictionaries(langId);\r
133                                 DWORD lid = SUBLANGID(langId);\r
134                                 lid--;\r
135                                 if (lid > 0)\r
136                                 {\r
137                                         langId = MAKELANGID(PRIMARYLANGID(langId), lid);\r
138                                 }\r
139                                 else if (langId == 1033)\r
140                                         langId = 0;\r
141                                 else\r
142                                         langId = 1033;\r
143                         } while ((langId)&&((pChecker==NULL)||(pThesaur==NULL)));\r
144                 }\r
145         }\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
152 }\r
153 \r
154 \r
155 void CSciEdit::Init(const ProjectProperties& props)\r
156 {\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
161         \r
162         if (props.nLogWidthMarker)\r
163         {\r
164                 Call(SCI_SETWRAPMODE, SC_WRAP_NONE);\r
165                 Call(SCI_SETEDGEMODE, EDGE_LINE);\r
166                 Call(SCI_SETEDGECOLUMN, props.nLogWidthMarker);\r
167         }\r
168         else\r
169         {\r
170                 Call(SCI_SETEDGEMODE, EDGE_NONE);\r
171                 Call(SCI_SETWRAPMODE, SC_WRAP_WORD);\r
172         }\r
173         SetText(props.sLogTemplate);\r
174 }\r
175 \r
176 BOOL CSciEdit::LoadDictionaries(LONG lLanguageID)\r
177 {\r
178         //Setup the spell checker and thesaurus\r
179         TCHAR buf[6];\r
180         CString sFolder = CPathUtils::GetAppDirectory();\r
181         CString sFolderUp = CPathUtils::GetAppParentDirectory();\r
182         CString sFile;\r
183 \r
184         GetLocaleInfo(MAKELCID(lLanguageID, SORT_DEFAULT), LOCALE_SISO639LANGNAME, buf, sizeof(buf));\r
185         sFile = buf;\r
186         sFile += _T("_");\r
187         GetLocaleInfo(MAKELCID(lLanguageID, SORT_DEFAULT), LOCALE_SISO3166CTRYNAME, buf, sizeof(buf));\r
188         sFile += buf;\r
189         if (pChecker==NULL)\r
190         {\r
191                 if ((PathFileExists(sFolder + sFile + _T(".aff"))) &&\r
192                         (PathFileExists(sFolder + sFile + _T(".dic"))))\r
193                 {\r
194                         pChecker = new Hunspell(CStringA(sFolder + sFile + _T(".aff")), CStringA(sFolder + sFile + _T(".dic")));\r
195                 }\r
196                 else if ((PathFileExists(sFolder + _T("dic\\") + sFile + _T(".aff"))) &&\r
197                         (PathFileExists(sFolder + _T("dic\\") + sFile + _T(".dic"))))\r
198                 {\r
199                         pChecker = new Hunspell(CStringA(sFolder + _T("dic\\") + sFile + _T(".aff")), CStringA(sFolder + _T("dic\\") + sFile + _T(".dic")));\r
200                 }\r
201                 else if ((PathFileExists(sFolderUp + sFile + _T(".aff"))) &&\r
202                         (PathFileExists(sFolderUp + sFile + _T(".dic"))))\r
203                 {\r
204                         pChecker = new Hunspell(CStringA(sFolderUp + sFile + _T(".aff")), CStringA(sFolderUp + sFile + _T(".dic")));\r
205                 }\r
206                 else if ((PathFileExists(sFolderUp + _T("dic\\") + sFile + _T(".aff"))) &&\r
207                         (PathFileExists(sFolderUp + _T("dic\\") + sFile + _T(".dic"))))\r
208                 {\r
209                         pChecker = new Hunspell(CStringA(sFolderUp + _T("dic\\") + sFile + _T(".aff")), CStringA(sFolderUp + _T("dic\\") + sFile + _T(".dic")));\r
210                 }\r
211                 else if ((PathFileExists(sFolderUp + _T("Languages\\") + sFile + _T(".aff"))) &&\r
212                         (PathFileExists(sFolderUp + _T("Languages\\") + sFile + _T(".dic"))))\r
213                 {\r
214                         pChecker = new Hunspell(CStringA(sFolderUp + _T("Languages\\") + sFile + _T(".aff")), CStringA(sFolderUp + _T("Languages\\") + sFile + _T(".dic")));\r
215                 }\r
216         }\r
217 #if THESAURUS\r
218         if (pThesaur==NULL)\r
219         {\r
220                 if ((PathFileExists(sFolder + _T("th_") + sFile + _T("_v2.idx"))) &&\r
221                         (PathFileExists(sFolder + _T("th_") + sFile + _T("_v2.dat"))))\r
222                 {\r
223                         pThesaur = new MyThes(CStringA(sFolder + sFile + _T("_v2.idx")), CStringA(sFolder + sFile + _T("_v2.dat")));\r
224                 }\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
227                 {\r
228                         pThesaur = new MyThes(CStringA(sFolder + _T("dic\\") + sFile + _T("_v2.idx")), CStringA(sFolder + _T("dic\\") + sFile + _T("_v2.dat")));\r
229                 }\r
230                 else if ((PathFileExists(sFolderUp + _T("th_") + sFile + _T("_v2.idx"))) &&\r
231                         (PathFileExists(sFolderUp + _T("th_") + sFile + _T("_v2.dat"))))\r
232                 {\r
233                         pThesaur = new MyThes(CStringA(sFolderUp + _T("th_") + sFile + _T("_v2.idx")), CStringA(sFolderUp + _T("th_") + sFile + _T("_v2.dat")));\r
234                 }\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
237                 {\r
238                         pThesaur = new MyThes(CStringA(sFolderUp + _T("dic\\th_") + sFile + _T("_v2.idx")), CStringA(sFolderUp + _T("dic\\th_") + sFile + _T("_v2.dat")));\r
239                 }\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
242                 {\r
243                         pThesaur = new MyThes(CStringA(sFolderUp + _T("Languages\\th_") + sFile + _T("_v2.idx")), CStringA(sFolderUp + _T("Languages\\th_") + sFile + _T("_v2.dat")));\r
244                 }\r
245         }\r
246 #endif\r
247         if (pChecker)\r
248         {\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
254                 {\r
255                         if (strcmp(encoding,enc2locale[i].def_enc) == 0)\r
256                         {\r
257                                 m_spellcodepage = atoi(enc2locale[i].cp);\r
258                         }\r
259                 }\r
260                 m_personalDict.Init(lLanguageID);\r
261         }\r
262         if ((pThesaur)||(pChecker))\r
263                 return TRUE;\r
264         return FALSE;\r
265 }\r
266 \r
267 LRESULT CSciEdit::Call(UINT message, WPARAM wParam, LPARAM lParam)\r
268 {\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
272 }\r
273 \r
274 CString CSciEdit::StringFromControl(const CStringA& text)\r
275 {\r
276         CString sText;\r
277 #ifdef UNICODE\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
282 #else\r
283         sText = text;   \r
284 #endif\r
285         return sText;\r
286 }\r
287 \r
288 CStringA CSciEdit::StringForControl(const CString& text)\r
289 {\r
290         CStringA sTextA;\r
291 #ifdef UNICODE\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
296 #else\r
297         sTextA = text;\r
298 #endif\r
299         ATLTRACE("string length %d\n", sTextA.GetLength());\r
300         return sTextA;\r
301 }\r
302 \r
303 void CSciEdit::SetText(const CString& sText)\r
304 {\r
305         CStringA sTextA = StringForControl(sText);\r
306         Call(SCI_SETTEXT, 0, (LPARAM)(LPCSTR)sTextA);\r
307         \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
313         Call(SCI_NEWLINE);\r
314         Call(SCI_DELETEBACK);\r
315 }\r
316 \r
317 void CSciEdit::InsertText(const CString& sText, bool bNewLine)\r
318 {\r
319         CStringA sTextA = StringForControl(sText);\r
320         Call(SCI_REPLACESEL, 0, (LPARAM)(LPCSTR)sTextA);\r
321         if (bNewLine)\r
322                 Call(SCI_REPLACESEL, 0, (LPARAM)(LPCSTR)"\n");\r
323 }\r
324 \r
325 CString CSciEdit::GetText()\r
326 {\r
327         LRESULT len = Call(SCI_GETTEXT, 0, 0);\r
328         CStringA sTextA;\r
329         Call(SCI_GETTEXT, len+1, (LPARAM)(LPCSTR)sTextA.GetBuffer(len+1));\r
330         sTextA.ReleaseBuffer();\r
331         return StringFromControl(sTextA);\r
332 }\r
333 \r
334 CString CSciEdit::GetWordUnderCursor(bool bSelectWord)\r
335 {\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
340                 return CString();\r
341         textrange.chrg.cpMax = Call(SCI_WORDENDPOSITION, textrange.chrg.cpMin, TRUE);\r
342         \r
343         char * textbuffer = new char[textrange.chrg.cpMax - textrange.chrg.cpMin + 1];\r
344 \r
345         textrange.lpstrText = textbuffer;       \r
346         Call(SCI_GETTEXTRANGE, 0, (LPARAM)&textrange);\r
347         if (bSelectWord)\r
348         {\r
349                 Call(SCI_SETSEL, textrange.chrg.cpMin, textrange.chrg.cpMax);\r
350         }\r
351         CString sRet = StringFromControl(textbuffer);\r
352         delete [] textbuffer;\r
353         return sRet;\r
354 }\r
355 \r
356 void CSciEdit::SetFont(CString sFontName, int iFontSizeInPoints)\r
357 {\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
361 \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
370 \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
375 \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
379 \r
380         Call(SCI_SETHOTSPOTACTIVEUNDERLINE, (LPARAM)TRUE);\r
381 }\r
382 \r
383 void CSciEdit::SetAutoCompletionList(const std::set<CString>& list, const TCHAR separator)\r
384 {\r
385         //copy the auto completion list.\r
386         \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
391         m_autolist = list;\r
392         m_separator = separator;\r
393 }\r
394 \r
395 BOOL CSciEdit::IsMisspelled(const CString& sWord)\r
396 {\r
397         // convert the string from the control to the encoding of the spell checker module.\r
398         CStringA sWordA;\r
399         if (m_spellcodepage)\r
400         {\r
401                 char * buf;\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
406         }\r
407         else\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
412                 return FALSE;\r
413         // words in the personal dictionary are correct too\r
414         if (m_personalDict.FindWord(sWord))\r
415                 return FALSE;\r
416 \r
417         // now we actually check the spelling...\r
418         if (!pChecker->spell(sWordA))\r
419         {\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
425                 {\r
426                         int wordstart = 0;\r
427                         int wordend = 1;\r
428                         while (wordend < sWord.GetLength())\r
429                         {\r
430                                 while ((wordend < sWord.GetLength())&&(!_istupper(sWord[wordend])))\r
431                                         wordend++;\r
432                                 if ((wordstart == 0)&&(wordend == sWord.GetLength()))\r
433                                 {\r
434                                         // words in the auto list are also assumed correctly spelled\r
435                                         if (m_autolist.find(sWord) != m_autolist.end())\r
436                                                 return FALSE;\r
437                                         return TRUE;\r
438                                 }\r
439                                 sWordA = CStringA(sWord.Mid(wordstart, wordend-wordstart));\r
440                                 if ((sWordA.GetLength() > 2)&&(!pChecker->spell(sWordA)))\r
441                                 {\r
442                                         return TRUE;\r
443                                 }\r
444                                 wordstart = wordend;\r
445                                 wordend++;\r
446                         }\r
447                 }\r
448         }\r
449         return FALSE;\r
450 }\r
451 \r
452 void CSciEdit::CheckSpelling()\r
453 {\r
454         if (pChecker == NULL)\r
455                 return;\r
456         \r
457         TEXTRANGEA textrange;\r
458         \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
464         if (lastpos < 0)\r
465                 lastpos = Call(SCI_GETLENGTH)-textrange.chrg.cpMin;\r
466         while (textrange.chrg.cpMax < lastpos)\r
467         {\r
468                 textrange.chrg.cpMin = Call(SCI_WORDSTARTPOSITION, textrange.chrg.cpMax+1, TRUE);\r
469                 if (textrange.chrg.cpMin < textrange.chrg.cpMax)\r
470                         break;\r
471                 textrange.chrg.cpMax = Call(SCI_WORDENDPOSITION, textrange.chrg.cpMin, TRUE);\r
472                 if (textrange.chrg.cpMin == textrange.chrg.cpMax)\r
473                 {\r
474                         textrange.chrg.cpMax++;\r
475                         continue;\r
476                 }\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
484                 if (len == 0)\r
485                 {\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
490                         len++;\r
491                 }\r
492                 if (len && textrange.lpstrText[len - 1] == '.')\r
493                 {\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
506                         {\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
512                                 continue;\r
513                         }\r
514                 }\r
515                 if (len)\r
516                         textrange.lpstrText[len - 1] = 0;\r
517                 textrange.chrg.cpMax--;\r
518                 if (strlen(textrange.lpstrText) > 0)\r
519                 {\r
520                         CString sWord = StringFromControl(textrange.lpstrText);\r
521                         if ((GetStyleAt(textrange.chrg.cpMin) != STYLE_URL) && IsMisspelled(sWord))\r
522                         {\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
526                         }\r
527                         else\r
528                         {\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
532                         }\r
533                 }\r
534                 delete [] textbuffer;\r
535         }\r
536 }\r
537 \r
538 void CSciEdit::SuggestSpellingAlternatives()\r
539 {\r
540         if (pChecker == NULL)\r
541                 return;\r
542         CString word = GetWordUnderCursor(true);\r
543         Call(SCI_SETCURRENTPOS, Call(SCI_WORDSTARTPOSITION, Call(SCI_GETCURRENTPOS), TRUE));\r
544         if (word.IsEmpty())\r
545                 return;\r
546         char ** wlst;\r
547         int ns = pChecker->suggest(&wlst, CStringA(word));\r
548         if (ns > 0)\r
549         {\r
550                 CString suggestions;\r
551                 for (int i=0; i < ns; i++) \r
552                 {\r
553                         suggestions += CString(wlst[i]) + m_separator;\r
554                         free(wlst[i]);\r
555                 } \r
556                 free(wlst);\r
557                 suggestions.TrimRight(m_separator);\r
558                 if (suggestions.IsEmpty())\r
559                         return;\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
563         }\r
564 \r
565 }\r
566 \r
567 void CSciEdit::DoAutoCompletion(int nMinPrefixLength)\r
568 {\r
569         if (m_autolist.size()==0)\r
570                 return;\r
571         if (Call(SCI_AUTOCACTIVE))\r
572                 return;\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
580         \r
581         word.MakeUpper();\r
582         for (std::set<CString>::const_iterator lowerit = m_autolist.lower_bound(word);\r
583                 lowerit != m_autolist.end(); ++lowerit)\r
584         {\r
585                 int compare = word.CompareNoCase(lowerit->Left(word.GetLength()));\r
586                 if (compare>0)\r
587                         continue;\r
588                 else if (compare == 0)\r
589                 {\r
590                         sAutoCompleteList += *lowerit + m_separator;\r
591                 }\r
592                 else\r
593                 {\r
594                         break;\r
595                 }\r
596         }\r
597         sAutoCompleteList.TrimRight(m_separator);\r
598         if (sAutoCompleteList.IsEmpty())\r
599                 return;\r
600 \r
601         Call(SCI_AUTOCSETSEPARATOR, (WPARAM)CStringA(m_separator).GetAt(0));\r
602         Call(SCI_AUTOCSHOW, word.GetLength(), (LPARAM)(LPCSTR)StringForControl(sAutoCompleteList));\r
603 }\r
604 \r
605 BOOL CSciEdit::OnChildNotify(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pLResult)\r
606 {\r
607         if (message != WM_NOTIFY)\r
608                 return CWnd::OnChildNotify(message, wParam, lParam, pLResult);\r
609         \r
610         LPNMHDR lpnmhdr = (LPNMHDR) lParam;\r
611         SCNotification * lpSCN = (SCNotification *)lParam;\r
612         \r
613         if(lpnmhdr->hwndFrom==m_hWnd)\r
614         {\r
615                 switch(lpnmhdr->code)\r
616                 {\r
617                 case SCN_CHARADDED:\r
618                         {\r
619                                 if ((lpSCN->ch < 32)&&(lpSCN->ch != 13)&&(lpSCN->ch != 10))\r
620                                         Call(SCI_DELETEBACK);\r
621                                 else\r
622                                 {\r
623                                         DoAutoCompletion(3);\r
624                                 }\r
625                                 return TRUE;\r
626                         }\r
627                         break;\r
628                 case SCN_STYLENEEDED:\r
629                         {\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
635                                 CheckSpelling();\r
636                                 WrapLines(startstylepos, endstylepos);\r
637                                 return TRUE;\r
638                         }\r
639                         break;\r
640                 case SCN_HOTSPOTCLICK:\r
641                         {\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
654                                 CString url;\r
655                                 if (style == STYLE_URL)\r
656                                         url = StringFromControl(textbuffer);\r
657                                 else\r
658                                 {\r
659                                         url = m_sUrl;\r
660                                         url.Replace(_T("%BUGID%"), StringFromControl(textbuffer));\r
661                                 }\r
662                                 delete [] textbuffer;\r
663                                 if (!url.IsEmpty())\r
664                                         ShellExecute(GetParent()->GetSafeHwnd(), _T("open"), url, NULL, NULL, SW_SHOWDEFAULT);\r
665                         }\r
666                         break;\r
667                 }\r
668         }\r
669         return CWnd::OnChildNotify(message, wParam, lParam, pLResult);\r
670 }\r
671 \r
672 BEGIN_MESSAGE_MAP(CSciEdit, CWnd)\r
673         ON_WM_KEYDOWN()\r
674         ON_WM_CONTEXTMENU()\r
675 END_MESSAGE_MAP()\r
676 \r
677 void CSciEdit::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)\r
678 {\r
679         switch (nChar)\r
680         {\r
681         case (VK_ESCAPE):\r
682                 {\r
683                         if ((Call(SCI_AUTOCACTIVE)==0)&&(Call(SCI_CALLTIPACTIVE)==0))\r
684                                 ::SendMessage(GetParent()->GetSafeHwnd(), WM_CLOSE, 0, 0);\r
685                 }\r
686                 break;\r
687         }\r
688         CWnd::OnKeyDown(nChar, nRepCnt, nFlags);\r
689 }\r
690 \r
691 BOOL CSciEdit::PreTranslateMessage(MSG* pMsg)\r
692 {\r
693         if (pMsg->message == WM_KEYDOWN)\r
694         {\r
695                 switch (pMsg->wParam)\r
696                 {\r
697                 case VK_SPACE:\r
698                         {\r
699                                 if (GetKeyState(VK_CONTROL) & 0x8000)\r
700                                 {\r
701                                         DoAutoCompletion(1);\r
702                                         return TRUE;\r
703                                 }\r
704                         }\r
705                         break;\r
706                 case VK_TAB:\r
707                         // The TAB cannot be handled in OnKeyDown because it is too late by then.\r
708                         {\r
709                                 if (GetKeyState(VK_CONTROL)&0x8000)\r
710                                 {\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
713                                         //the cursor\r
714                                         SuggestSpellingAlternatives();\r
715                                         return TRUE;\r
716                                 }\r
717                                 else if (!Call(SCI_AUTOCACTIVE))\r
718                                 {\r
719                                         ::PostMessage(GetParent()->GetSafeHwnd(), WM_NEXTDLGCTL, GetKeyState(VK_SHIFT)&0x8000, 0);\r
720                                         return TRUE;\r
721                                 }\r
722                         }\r
723                         break;\r
724                 }\r
725         }\r
726         return CWnd::PreTranslateMessage(pMsg);\r
727 }\r
728 \r
729 void CSciEdit::OnContextMenu(CWnd* /*pWnd*/, CPoint point)\r
730 {\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
735         int pointpos = 0;\r
736         if ((point.x == -1) && (point.y == -1))\r
737         {\r
738                 CRect rect;\r
739                 GetClientRect(&rect);\r
740                 ClientToScreen(&rect);\r
741                 point = rect.CenterPoint();\r
742                 pointpos = Call(SCI_GETCURRENTPOS);\r
743         }\r
744         else\r
745         {\r
746                 // change the cursor position to the point where the user\r
747                 // right-clicked.\r
748                 CPoint clientpoint = point;\r
749                 ScreenToClient(&clientpoint);\r
750                 pointpos = Call(SCI_POSITIONFROMPOINT, clientpoint.x, clientpoint.y);\r
751         }\r
752         CString sMenuItemText;\r
753         CMenu popup;\r
754         bool bRestoreCursor = true;\r
755         if (popup.CreatePopupMenu())\r
756         {\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
763 \r
764                 // find the word under the cursor\r
765                 CString sWord;          \r
766                 if (pointpos)\r
767                 {\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
775                 }\r
776                 else\r
777                         sWord = GetWordUnderCursor();\r
778                 CStringA worda = CStringA(sWord);\r
779 \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
784                 {\r
785                         char ** wlst;\r
786                         // get the spell suggestions\r
787                         int ns = pChecker->suggest(&wlst,worda);\r
788                         if (ns > 0)\r
789                         {\r
790                                 // add the suggestions to the context menu\r
791                                 for (int i=0; i < ns; i++) \r
792                                 {\r
793                                         bSpellAdded = true;\r
794                                         CString sug = CString(wlst[i]);\r
795                                         popup.InsertMenu((UINT)-1, 0, nCorrections++, sug);\r
796                                         free(wlst[i]);\r
797                                 } \r
798                                 free(wlst);\r
799                         }\r
800                 }\r
801                 // only add a separator if spelling correction suggestions were added\r
802                 if (bSpellAdded)\r
803                         popup.AppendMenu(MF_SEPARATOR);\r
804 \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
809                 {\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
814                 }\r
815 \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
821                 \r
822                 popup.AppendMenu(MF_SEPARATOR);\r
823                 \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
830 \r
831                 popup.AppendMenu(MF_SEPARATOR);\r
832                 \r
833                 sMenuItemText.LoadString(IDS_SCIEDIT_SELECTALL);\r
834                 popup.AppendMenu(uEnabledMenu, SCI_SELECTALL, sMenuItemText);\r
835 \r
836                 popup.AppendMenu(MF_SEPARATOR);\r
837 \r
838                 sMenuItemText.LoadString(IDS_SCIEDIT_SPLITLINES);\r
839                 popup.AppendMenu(bHasSelection ? uEnabledMenu : uDisabledMenu, SCI_LINESSPLIT, sMenuItemText);\r
840 \r
841                 popup.AppendMenu(MF_SEPARATOR);\r
842 \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
846                 {\r
847                         CSciEditContextMenuInterface * pHandler = m_arContextHandlers.GetAt(handlerindex);\r
848                         pHandler->InsertMenuItems(popup, nCustoms);\r
849                 }\r
850                 if (nCustoms > nCorrections)\r
851                 {\r
852                         // custom menu entries present, so add another separator\r
853                         popup.AppendMenu(MF_SEPARATOR);\r
854                 }\r
855 \r
856 #if THESAURUS\r
857                 // add found thesauri to sub menu's\r
858                 CMenu thesaurs;\r
859                 thesaurs.CreatePopupMenu();\r
860                 int nThesaurs = 0;\r
861                 CPtrArray menuArray;\r
862                 if ((pThesaur)&&(!worda.IsEmpty()))\r
863                 {\r
864                         mentry * pmean;\r
865                         worda.MakeLower();\r
866                         int count = pThesaur->Lookup(worda, worda.GetLength(),&pmean);\r
867                         if (count)\r
868                         {\r
869                                 mentry * pm = pmean;\r
870                                 for (int  i=0; i < count; i++) \r
871                                 {\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
876                                         {\r
877                                                 CString sug = CString(pm->psyns[j]);\r
878                                                 submenu->InsertMenu((UINT)-1, 0, nThesaurs++, sug);\r
879                                         }\r
880                                         thesaurs.InsertMenu((UINT)-1, MF_POPUP, (UINT_PTR)(submenu->m_hMenu), CString(pm->defn));\r
881                                         pm++;\r
882                                 }\r
883                         }  \r
884                         if ((count > 0)&&(point.x >= 0))\r
885                         {\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
889 #else\r
890                                 popup.InsertMenu((UINT)-1, MF_POPUP, (UINT_PTR)thesaurs.m_hMenu, _T("Thesaurus"));\r
891 #endif\r
892                                 nThesaurs = nCustoms;\r
893                         }\r
894                         else\r
895                         {\r
896                                 sMenuItemText.LoadString(IDS_SPELLEDIT_NOTHESAURUS);\r
897                                 popup.AppendMenu(MF_DISABLED | MF_GRAYED | MF_STRING, 0, sMenuItemText);\r
898                         }\r
899 \r
900                         pThesaur->CleanUpAfterLookup(&pmean, count);\r
901                 }\r
902                 else\r
903                 {\r
904                         sMenuItemText.LoadString(IDS_SPELLEDIT_NOTHESAURUS);\r
905                         popup.AppendMenu(MF_DISABLED | MF_GRAYED | MF_STRING, 0, sMenuItemText);\r
906                 }\r
907 #endif\r
908                 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this, 0);\r
909                 switch (cmd)\r
910                 {\r
911                 case 0:\r
912                         break;  // no command selected\r
913                 case SCI_SELECTALL:\r
914                         bRestoreCursor = false;\r
915                         // fall through\r
916                 case SCI_UNDO:\r
917                 case SCI_REDO:\r
918                 case SCI_CUT:\r
919                 case SCI_COPY:\r
920                 case SCI_PASTE:\r
921                         Call(cmd);\r
922                         break;\r
923                 case SCI_ADDWORD:\r
924                         m_personalDict.AddWord(sWord);\r
925                         CheckSpelling();\r
926                         break;\r
927                 case SCI_LINESSPLIT:\r
928                         {\r
929                                 int marker = Call(SCI_GETEDGECOLUMN) * Call(SCI_TEXTWIDTH, 0, (LPARAM)" ");\r
930                                 if (marker)\r
931                                 {\r
932                                         Call(SCI_TARGETFROMSELECTION);\r
933                                         Call(SCI_LINESJOIN);\r
934                                         Call(SCI_LINESSPLIT, marker);\r
935                                 }\r
936                         }\r
937                         break;\r
938                 default:\r
939                         if (cmd < nCorrections)\r
940                         {\r
941                                 Call(SCI_SETANCHOR, pointpos);\r
942                                 Call(SCI_SETCURRENTPOS, pointpos);\r
943                                 GetWordUnderCursor(true);\r
944                                 CString temp;\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
948                         }\r
949                         else if (cmd < (nCorrections+nCustoms))\r
950                         {\r
951                                 for (INT_PTR handlerindex = 0; handlerindex < m_arContextHandlers.GetCount(); ++handlerindex)\r
952                                 {\r
953                                         CSciEditContextMenuInterface * pHandler = m_arContextHandlers.GetAt(handlerindex);\r
954                                         if (pHandler->HandleMenuItemClick(cmd, this))\r
955                                                 break;\r
956                                 }               \r
957                         }\r
958 #if THESAURUS\r
959                         else if (cmd <= (nThesaurs+nCorrections+nCustoms))\r
960                         {\r
961                                 Call(SCI_SETANCHOR, pointpos);\r
962                                 Call(SCI_SETCURRENTPOS, pointpos);\r
963                                 GetWordUnderCursor(true);\r
964                                 CString temp;\r
965                                 thesaurs.GetMenuString(cmd, temp, 0);\r
966                                 Call(SCI_REPLACESEL, 0, (LPARAM)(LPCSTR)StringForControl(temp));\r
967                         }\r
968 #endif\r
969                 }\r
970 #ifdef THESAURUS\r
971                 for (INT_PTR index = 0; index < menuArray.GetCount(); ++index)\r
972                 {\r
973                         CMenu * pMenu = (CMenu*)menuArray[index];\r
974                         delete pMenu;\r
975                 }\r
976 #endif\r
977         }\r
978         if (bRestoreCursor)\r
979         {\r
980                 // restore the anchor and cursor position\r
981                 Call(SCI_SETCURRENTPOS, currentpos);\r
982                 Call(SCI_SETANCHOR, anchor);\r
983         }\r
984 }\r
985 \r
986 bool CSciEdit::StyleEnteredText(int startstylepos, int endstylepos)\r
987 {\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
992         {\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
998                 int start = 0;\r
999                 int end = 0;\r
1000                 while (FindStyleChars(linebuffer, '*', start, end))\r
1001                 {\r
1002                         Call(SCI_STARTSTYLING, start+offset, STYLE_MASK);\r
1003                         Call(SCI_SETSTYLING, end-start, STYLE_BOLD);\r
1004                         bStyled = true;\r
1005                         start = end;\r
1006                 }\r
1007                 start = 0;\r
1008                 end = 0;\r
1009                 while (FindStyleChars(linebuffer, '^', start, end))\r
1010                 {\r
1011                         Call(SCI_STARTSTYLING, start+offset, STYLE_MASK);\r
1012                         Call(SCI_SETSTYLING, end-start, STYLE_ITALIC);\r
1013                         bStyled = true;\r
1014                         start = end;\r
1015                 }\r
1016                 start = 0;\r
1017                 end = 0;\r
1018                 while (FindStyleChars(linebuffer, '_', start, end))\r
1019                 {\r
1020                         Call(SCI_STARTSTYLING, start+offset, STYLE_MASK);\r
1021                         Call(SCI_SETSTYLING, end-start, STYLE_UNDERLINED);\r
1022                         bStyled = true;\r
1023                         start = end;\r
1024                 }\r
1025                 delete [] linebuffer;\r
1026         }\r
1027         return bStyled;\r
1028 }\r
1029 \r
1030 bool CSciEdit::WrapLines(int startpos, int endpos)\r
1031 {\r
1032         int markerX = Call(SCI_GETEDGECOLUMN) * Call(SCI_TEXTWIDTH, 0, (LPARAM)" ");\r
1033         if (markerX)\r
1034         {\r
1035                 Call(SCI_SETTARGETSTART, startpos);\r
1036                 Call(SCI_SETTARGETEND, endpos);\r
1037                 Call(SCI_LINESSPLIT, markerX);\r
1038                 return true;\r
1039         }\r
1040         return false;\r
1041 }\r
1042 \r
1043 void CSciEdit::AdvanceUTF8(const char * str, int& pos)\r
1044 {\r
1045         if ((str[pos] & 0xE0)==0xC0)\r
1046         {\r
1047                 // utf8 2-byte sequence\r
1048                 pos += 2;\r
1049         }\r
1050         else if ((str[pos] & 0xF0)==0xE0)\r
1051         {\r
1052                 // utf8 3-byte sequence\r
1053                 pos += 3;\r
1054         }\r
1055         else if ((str[pos] & 0xF8)==0xF0)\r
1056         {\r
1057                 // utf8 4-byte sequence\r
1058                 pos += 4;\r
1059         }\r
1060         else\r
1061                 pos++;\r
1062 }\r
1063 \r
1064 bool CSciEdit::FindStyleChars(const char * line, char styler, int& start, int& end)\r
1065 {\r
1066         int i=0;\r
1067         int u=0;\r
1068         while (i < start)\r
1069         {\r
1070                 AdvanceUTF8(line, i);\r
1071                 u++;\r
1072         }\r
1073 \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
1078         {\r
1079                 if (line[i] == styler)\r
1080                 {\r
1081                         if ((line[i+1]!=0)&&(IsCharAlphaNumeric(sULine[u+1]))&&\r
1082                                 (((u>0)&&(!IsCharAlphaNumeric(sULine[u-1]))) || (u==0)))\r
1083                         {\r
1084                                 start = i+1;\r
1085                                 AdvanceUTF8(line, i);\r
1086                                 u++;\r
1087                                 bFoundMarker = true;\r
1088                                 break;\r
1089                         }\r
1090                 }\r
1091                 AdvanceUTF8(line, i);\r
1092                 u++;\r
1093         }\r
1094         if (!bFoundMarker)\r
1095                 return false;\r
1096         // find ending marker\r
1097         bFoundMarker = false;\r
1098         while (line[i] != 0)\r
1099         {\r
1100                 if (line[i] == styler)\r
1101                 {\r
1102                         if ((IsCharAlphaNumeric(sULine[u-1]))&&\r
1103                                 ((((u+1)<sULine.GetLength())&&(!IsCharAlphaNumeric(sULine[u+1]))) || ((u+1) == sULine.GetLength()))\r
1104                                 )\r
1105                         {\r
1106                                 end = i;\r
1107                                 i++;\r
1108                                 bFoundMarker = true;\r
1109                                 break;\r
1110                         }\r
1111                 }\r
1112                 AdvanceUTF8(line, i);\r
1113                 u++;\r
1114         }\r
1115         return bFoundMarker;\r
1116 }\r
1117 \r
1118 BOOL CSciEdit::MarkEnteredBugID(int startstylepos, int endstylepos)\r
1119 {\r
1120         if (m_sCommand.IsEmpty())\r
1121                 return FALSE;\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
1126 \r
1127         if (start_pos == end_pos)\r
1128                 return FALSE;\r
1129         if (start_pos > end_pos)\r
1130         {\r
1131                 int switchtemp = start_pos;\r
1132                 start_pos = end_pos;\r
1133                 end_pos = switchtemp;\r
1134         }\r
1135 \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
1143 \r
1144         Call(SCI_STARTSTYLING, start_pos, STYLE_MASK);\r
1145 \r
1146         if (!m_sBugID.IsEmpty())\r
1147         {\r
1148                 // match with two regex strings (without grouping!)\r
1149                 try\r
1150                 {\r
1151                         const tr1::regex regCheck(m_sCommand);\r
1152                         const tr1::regex regBugID(m_sBugID);\r
1153                         const tr1::sregex_iterator end;\r
1154                         string s = msg;\r
1155                         LONG pos = 0;\r
1156                         for (tr1::sregex_iterator it(s.begin(), s.end(), regCheck); it != end; ++it)\r
1157                         {\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
1161 \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
1165                                 {\r
1166                                         ATLTRACE(_T("matched id : %s\n"), (*it2)[0].str().c_str());\r
1167 \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
1175                                 }\r
1176                                 pos = it->position(0) + matchedString.size();\r
1177                         }\r
1178                         // bold style for the rest of the string which isn't matched\r
1179                         if (s.size()-pos)\r
1180                                 Call(SCI_SETSTYLING, s.size()-pos, STYLE_DEFAULT);\r
1181                 }\r
1182                 catch (exception) {}\r
1183         }\r
1184         else\r
1185         {\r
1186                 try\r
1187                 {\r
1188                         const tr1::regex regCheck(m_sCommand);\r
1189                         const tr1::sregex_iterator end;\r
1190                         string s = msg;\r
1191                         LONG pos = 0;\r
1192                         for (tr1::sregex_iterator it(s.begin(), s.end(), regCheck); it != end; ++it)\r
1193                         {\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
1197 \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
1202                                 {\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
1207                                 }\r
1208                         }\r
1209                 }\r
1210                 catch (exception) {}\r
1211         }\r
1212         delete [] textbuffer;\r
1213 \r
1214         return FALSE;\r
1215 }\r
1216 \r
1217 bool CSciEdit::IsValidURLChar(unsigned char ch)\r
1218 {\r
1219         return isalnum(ch) ||\r
1220                 ch == '_' || ch == '/' || ch == ';' || ch == '?' || ch == '&' || ch == '=' ||\r
1221                 ch == '%' || ch == ':' || ch == '.' || ch == '#' || ch == '-' || ch == '+';\r
1222 }\r
1223 \r
1224 void CSciEdit::StyleURLs(int startstylepos, int endstylepos) \r
1225 {\r
1226         const int line_number = Call(SCI_LINEFROMPOSITION, startstylepos);\r
1227         startstylepos = Call(SCI_POSITIONFROMLINE, (WPARAM)line_number);\r
1228 \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
1241 \r
1242         int starturl = -1;\r
1243         for(int i = 0; i <= msg.GetLength(); )\r
1244         {\r
1245                 if ((i < len) && IsValidURLChar(msg[i]))\r
1246                 {\r
1247                         if (starturl < 0)\r
1248                                 starturl = i;\r
1249                 }\r
1250                 else\r
1251                 {\r
1252                         if ((starturl >= 0) && IsUrl(msg.Mid(starturl, i - starturl)))\r
1253                         {\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
1257                         }\r
1258                         starturl = -1;\r
1259                 }\r
1260                 AdvanceUTF8(msg, i);\r
1261         }\r
1262 }\r
1263 \r
1264 bool CSciEdit::IsUrl(const CStringA& sText)\r
1265 {\r
1266         if (!PathIsURLA(sText))\r
1267                 return false;\r
1268         if (sText.Find("://")>=0)\r
1269                 return true;\r
1270         return false;\r
1271 }\r