1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2003-2008 - TortoiseSVN
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "CommonResource.h"
21 //#include "AppUtils.h"
22 #include "..\PathUtils.h"
23 #include "..\UnicodeUtils.h"
25 #include "..\registry.h"
26 #include ".\sciedit.h"
31 void CSciEditContextMenuInterface::InsertMenuItems(CMenu
&, int&) {return;}
32 bool CSciEditContextMenuInterface::HandleMenuItemClick(int, CSciEdit
*) {return false;}
35 #define STYLE_ISSUEBOLD 11
36 #define STYLE_ISSUEBOLDITALIC 12
38 #define STYLE_ITALIC 15
39 #define STYLE_UNDERLINED 16
42 #define STYLE_MASK 0x1f
44 #define SCI_ADDWORD 2000
51 struct loc_map enc2locale
[] = {
52 {"28591","ISO8859-1"},
53 {"28592","ISO8859-2"},
54 {"28593","ISO8859-3"},
55 {"28594","ISO8859-4"},
56 {"28595","ISO8859-5"},
57 {"28596","ISO8859-6"},
58 {"28597","ISO8859-7"},
59 {"28598","ISO8859-8"},
60 {"28599","ISO8859-9"},
61 {"28605","ISO8859-15"},
64 {"1251","microsoft-cp1251"},
68 IMPLEMENT_DYNAMIC(CSciEdit
, CWnd
)
70 CSciEdit::CSciEdit(void) : m_DirectFunction(NULL
)
71 , m_DirectPointer(NULL
)
75 m_hModule
= ::LoadLibrary(_T("SciLexer.DLL"));
78 CSciEdit::~CSciEdit(void)
80 m_personalDict
.Save();
82 ::FreeLibrary(m_hModule
);
89 void CSciEdit::Init(LONG lLanguage
, BOOL bLoadSpellCheck
)
91 //Setup the direct access data
92 m_DirectFunction
= SendMessage(SCI_GETDIRECTFUNCTION
, 0, 0);
93 m_DirectPointer
= SendMessage(SCI_GETDIRECTPOINTER
, 0, 0);
94 Call(SCI_SETMARGINWIDTHN
, 1, 0);
95 Call(SCI_SETUSETABS
, 0); //pressing TAB inserts spaces
96 Call(SCI_SETWRAPVISUALFLAGS
, SC_WRAPVISUALFLAG_END
);
97 Call(SCI_AUTOCSETIGNORECASE
, 1);
98 Call(SCI_SETLEXER
, SCLEX_CONTAINER
);
99 Call(SCI_SETCODEPAGE
, SC_CP_UTF8
);
100 Call(SCI_AUTOCSETFILLUPS
, 0, (LPARAM
)"\t([");
101 Call(SCI_AUTOCSETMAXWIDTH
, 0);
102 //Set the default windows colors for edit controls
103 Call(SCI_STYLESETFORE
, STYLE_DEFAULT
, ::GetSysColor(COLOR_WINDOWTEXT
));
104 Call(SCI_STYLESETBACK
, STYLE_DEFAULT
, ::GetSysColor(COLOR_WINDOW
));
105 Call(SCI_SETSELFORE
, TRUE
, ::GetSysColor(COLOR_HIGHLIGHTTEXT
));
106 Call(SCI_SETSELBACK
, TRUE
, ::GetSysColor(COLOR_HIGHLIGHT
));
107 Call(SCI_SETCARETFORE
, ::GetSysColor(COLOR_WINDOWTEXT
));
108 Call(SCI_SETMODEVENTMASK
, SC_MOD_INSERTTEXT
| SC_MOD_DELETETEXT
| SC_PERFORMED_UNDO
| SC_PERFORMED_REDO
);
109 Call(SCI_INDICSETFORE
, 1, 0x0000FF);
111 CStringA sWhiteSpace
;
112 for (int i
=0; i
<255; ++i
)
114 if (i
== '\r' || i
== '\n')
116 else if (i
< 0x20 || i
== ' ')
117 sWhiteSpace
+= (char)i
;
118 else if (isalnum(i
) || i
== '\'')
119 sWordChars
+= (char)i
;
121 Call(SCI_SETWORDCHARS
, 0, (LPARAM
)(LPCSTR
)sWordChars
);
122 Call(SCI_SETWHITESPACECHARS
, 0, (LPARAM
)(LPCSTR
)sWhiteSpace
);
123 // look for dictionary files and use them if found
124 long langId
= GetUserDefaultLCID();
128 if ((lLanguage
!= 0)||(((DWORD
)CRegStdDWORD(_T("Software\\TortoiseGit\\Spellchecker"), FALSE
))==FALSE
))
130 if (!((lLanguage
)&&(!LoadDictionaries(lLanguage
))))
134 LoadDictionaries(langId
);
135 DWORD lid
= SUBLANGID(langId
);
139 langId
= MAKELANGID(PRIMARYLANGID(langId
), lid
);
141 else if (langId
== 1033)
145 } while ((langId
)&&((pChecker
==NULL
)||(pThesaur
==NULL
)));
149 Call(SCI_SETEDGEMODE
, EDGE_NONE
);
150 Call(SCI_SETWRAPMODE
, SC_WRAP_WORD
);
151 Call(SCI_ASSIGNCMDKEY
, SCK_END
, SCI_LINEENDWRAP
);
152 Call(SCI_ASSIGNCMDKEY
, SCK_END
+ (SCMOD_SHIFT
<< 16), SCI_LINEENDWRAPEXTEND
);
153 Call(SCI_ASSIGNCMDKEY
, SCK_HOME
, SCI_HOMEWRAP
);
154 Call(SCI_ASSIGNCMDKEY
, SCK_HOME
+ (SCMOD_SHIFT
<< 16), SCI_HOMEWRAPEXTEND
);
158 void CSciEdit::Init(const ProjectProperties
& props
)
160 Init(props
.lProjectLanguage
);
161 m_sCommand
= CStringA(CUnicodeUtils::GetUTF8(props
.sCheckRe
));
162 m_sBugID
= CStringA(CUnicodeUtils::GetUTF8(props
.sBugIDRe
));
163 m_sUrl
= CStringA(CUnicodeUtils::GetUTF8(props
.sUrl
));
165 if (props
.nLogWidthMarker
)
167 Call(SCI_SETWRAPMODE
, SC_WRAP_NONE
);
168 Call(SCI_SETEDGEMODE
, EDGE_LINE
);
169 Call(SCI_SETEDGECOLUMN
, props
.nLogWidthMarker
);
173 Call(SCI_SETEDGEMODE
, EDGE_NONE
);
174 Call(SCI_SETWRAPMODE
, SC_WRAP_WORD
);
176 SetText(props
.sLogTemplate
);
179 BOOL
CSciEdit::LoadDictionaries(LONG lLanguageID
)
181 //Setup the spell checker and thesaurus
183 CString sFolder
= CPathUtils::GetAppDirectory();
184 CString sFolderUp
= CPathUtils::GetAppParentDirectory();
187 GetLocaleInfo(MAKELCID(lLanguageID
, SORT_DEFAULT
), LOCALE_SISO639LANGNAME
, buf
, _countof(buf
));
190 GetLocaleInfo(MAKELCID(lLanguageID
, SORT_DEFAULT
), LOCALE_SISO3166CTRYNAME
, buf
, _countof(buf
));
194 if ((PathFileExists(sFolder
+ sFile
+ _T(".aff"))) &&
195 (PathFileExists(sFolder
+ sFile
+ _T(".dic"))))
197 pChecker
= new Hunspell(CStringA(sFolder
+ sFile
+ _T(".aff")), CStringA(sFolder
+ sFile
+ _T(".dic")));
199 else if ((PathFileExists(sFolder
+ _T("dic\\") + sFile
+ _T(".aff"))) &&
200 (PathFileExists(sFolder
+ _T("dic\\") + sFile
+ _T(".dic"))))
202 pChecker
= new Hunspell(CStringA(sFolder
+ _T("dic\\") + sFile
+ _T(".aff")), CStringA(sFolder
+ _T("dic\\") + sFile
+ _T(".dic")));
204 else if ((PathFileExists(sFolderUp
+ sFile
+ _T(".aff"))) &&
205 (PathFileExists(sFolderUp
+ sFile
+ _T(".dic"))))
207 pChecker
= new Hunspell(CStringA(sFolderUp
+ sFile
+ _T(".aff")), CStringA(sFolderUp
+ sFile
+ _T(".dic")));
209 else if ((PathFileExists(sFolderUp
+ _T("dic\\") + sFile
+ _T(".aff"))) &&
210 (PathFileExists(sFolderUp
+ _T("dic\\") + sFile
+ _T(".dic"))))
212 pChecker
= new Hunspell(CStringA(sFolderUp
+ _T("dic\\") + sFile
+ _T(".aff")), CStringA(sFolderUp
+ _T("dic\\") + sFile
+ _T(".dic")));
214 else if ((PathFileExists(sFolderUp
+ _T("Languages\\") + sFile
+ _T(".aff"))) &&
215 (PathFileExists(sFolderUp
+ _T("Languages\\") + sFile
+ _T(".dic"))))
217 pChecker
= new Hunspell(CStringA(sFolderUp
+ _T("Languages\\") + sFile
+ _T(".aff")), CStringA(sFolderUp
+ _T("Languages\\") + sFile
+ _T(".dic")));
223 if ((PathFileExists(sFolder
+ _T("th_") + sFile
+ _T("_v2.idx"))) &&
224 (PathFileExists(sFolder
+ _T("th_") + sFile
+ _T("_v2.dat"))))
226 pThesaur
= new MyThes(CStringA(sFolder
+ sFile
+ _T("_v2.idx")), CStringA(sFolder
+ sFile
+ _T("_v2.dat")));
228 else if ((PathFileExists(sFolder
+ _T("dic\\th_") + sFile
+ _T("_v2.idx"))) &&
229 (PathFileExists(sFolder
+ _T("dic\\th_") + sFile
+ _T("_v2.dat"))))
231 pThesaur
= new MyThes(CStringA(sFolder
+ _T("dic\\") + sFile
+ _T("_v2.idx")), CStringA(sFolder
+ _T("dic\\") + sFile
+ _T("_v2.dat")));
233 else if ((PathFileExists(sFolderUp
+ _T("th_") + sFile
+ _T("_v2.idx"))) &&
234 (PathFileExists(sFolderUp
+ _T("th_") + sFile
+ _T("_v2.dat"))))
236 pThesaur
= new MyThes(CStringA(sFolderUp
+ _T("th_") + sFile
+ _T("_v2.idx")), CStringA(sFolderUp
+ _T("th_") + sFile
+ _T("_v2.dat")));
238 else if ((PathFileExists(sFolderUp
+ _T("dic\\th_") + sFile
+ _T("_v2.idx"))) &&
239 (PathFileExists(sFolderUp
+ _T("dic\\th_") + sFile
+ _T("_v2.dat"))))
241 pThesaur
= new MyThes(CStringA(sFolderUp
+ _T("dic\\th_") + sFile
+ _T("_v2.idx")), CStringA(sFolderUp
+ _T("dic\\th_") + sFile
+ _T("_v2.dat")));
243 else if ((PathFileExists(sFolderUp
+ _T("Languages\\th_") + sFile
+ _T("_v2.idx"))) &&
244 (PathFileExists(sFolderUp
+ _T("Languages\\th_") + sFile
+ _T("_v2.dat"))))
246 pThesaur
= new MyThes(CStringA(sFolderUp
+ _T("Languages\\th_") + sFile
+ _T("_v2.idx")), CStringA(sFolderUp
+ _T("Languages\\th_") + sFile
+ _T("_v2.dat")));
252 const char * encoding
= pChecker
->get_dic_encoding();
254 int n
= _countof(enc2locale
);
256 for (int i
= 0; i
< n
; i
++)
258 if (strcmp(encoding
,enc2locale
[i
].def_enc
) == 0)
260 m_spellcodepage
= atoi(enc2locale
[i
].cp
);
263 m_personalDict
.Init(lLanguageID
);
265 if ((pThesaur
)||(pChecker
))
270 LRESULT
CSciEdit::Call(UINT message
, WPARAM wParam
, LPARAM lParam
)
272 ASSERT(::IsWindow(m_hWnd
)); //Window must be valid
273 ASSERT(m_DirectFunction
); //Direct function must be valid
274 return ((SciFnDirect
) m_DirectFunction
)(m_DirectPointer
, message
, wParam
, lParam
);
277 CString
CSciEdit::StringFromControl(const CStringA
& text
)
281 int codepage
= Call(SCI_GETCODEPAGE
);
282 int reslen
= MultiByteToWideChar(codepage
, 0, text
, text
.GetLength(), 0, 0);
283 MultiByteToWideChar(codepage
, 0, text
, text
.GetLength(), sText
.GetBuffer(reslen
+1), reslen
+1);
284 sText
.ReleaseBuffer(reslen
);
291 CStringA
CSciEdit::StringForControl(const CString
& text
)
295 int codepage
= SendMessage(SCI_GETCODEPAGE
);
296 int reslen
= WideCharToMultiByte(codepage
, 0, text
, text
.GetLength(), 0, 0, 0, 0);
297 WideCharToMultiByte(codepage
, 0, text
, text
.GetLength(), sTextA
.GetBuffer(reslen
), reslen
, 0, 0);
298 sTextA
.ReleaseBuffer(reslen
);
302 ATLTRACE("string length %d\n", sTextA
.GetLength());
306 void CSciEdit::SetText(const CString
& sText
)
308 CStringA sTextA
= StringForControl(sText
);
309 Call(SCI_SETTEXT
, 0, (LPARAM
)(LPCSTR
)sTextA
);
311 // Scintilla seems to have problems with strings that
312 // aren't terminated by a newline char. Once that char
313 // is there, it can be removed without problems.
314 // So we add here a newline, then remove it again.
315 Call(SCI_DOCUMENTEND
);
317 Call(SCI_DELETEBACK
);
320 void CSciEdit::InsertText(const CString
& sText
, bool bNewLine
)
322 CStringA sTextA
= StringForControl(sText
);
323 Call(SCI_REPLACESEL
, 0, (LPARAM
)(LPCSTR
)sTextA
);
325 Call(SCI_REPLACESEL
, 0, (LPARAM
)(LPCSTR
)"\n");
328 CString
CSciEdit::GetText()
330 LRESULT len
= Call(SCI_GETTEXT
, 0, 0);
332 Call(SCI_GETTEXT
, len
+1, (LPARAM
)(LPCSTR
)sTextA
.GetBuffer(len
+1));
333 sTextA
.ReleaseBuffer();
334 return StringFromControl(sTextA
);
337 CString
CSciEdit::GetWordUnderCursor(bool bSelectWord
)
339 TEXTRANGEA textrange
;
340 int pos
= Call(SCI_GETCURRENTPOS
);
341 textrange
.chrg
.cpMin
= Call(SCI_WORDSTARTPOSITION
, pos
, TRUE
);
342 if ((pos
== textrange
.chrg
.cpMin
)||(textrange
.chrg
.cpMin
< 0))
344 textrange
.chrg
.cpMax
= Call(SCI_WORDENDPOSITION
, textrange
.chrg
.cpMin
, TRUE
);
346 char * textbuffer
= new char[textrange
.chrg
.cpMax
- textrange
.chrg
.cpMin
+ 1];
348 textrange
.lpstrText
= textbuffer
;
349 Call(SCI_GETTEXTRANGE
, 0, (LPARAM
)&textrange
);
352 Call(SCI_SETSEL
, textrange
.chrg
.cpMin
, textrange
.chrg
.cpMax
);
354 CString sRet
= StringFromControl(textbuffer
);
355 delete [] textbuffer
;
359 void CSciEdit::SetFont(CString sFontName
, int iFontSizeInPoints
)
361 Call(SCI_STYLESETFONT
, STYLE_DEFAULT
, (LPARAM
)(LPCSTR
)CStringA(sFontName
));
362 Call(SCI_STYLESETSIZE
, STYLE_DEFAULT
, iFontSizeInPoints
);
363 Call(SCI_STYLECLEARALL
);
365 LPARAM color
= (LPARAM
)GetSysColor(COLOR_HIGHLIGHT
);
366 // set the styles for the bug ID strings
367 Call(SCI_STYLESETBOLD
, STYLE_ISSUEBOLD
, (LPARAM
)TRUE
);
368 Call(SCI_STYLESETFORE
, STYLE_ISSUEBOLD
, color
);
369 Call(SCI_STYLESETBOLD
, STYLE_ISSUEBOLDITALIC
, (LPARAM
)TRUE
);
370 Call(SCI_STYLESETITALIC
, STYLE_ISSUEBOLDITALIC
, (LPARAM
)TRUE
);
371 Call(SCI_STYLESETFORE
, STYLE_ISSUEBOLDITALIC
, color
);
372 Call(SCI_STYLESETHOTSPOT
, STYLE_ISSUEBOLDITALIC
, (LPARAM
)TRUE
);
374 // set the formatted text styles
375 Call(SCI_STYLESETBOLD
, STYLE_BOLD
, (LPARAM
)TRUE
);
376 Call(SCI_STYLESETITALIC
, STYLE_ITALIC
, (LPARAM
)TRUE
);
377 Call(SCI_STYLESETUNDERLINE
, STYLE_UNDERLINED
, (LPARAM
)TRUE
);
379 // set the style for URLs
380 Call(SCI_STYLESETFORE
, STYLE_URL
, color
);
381 Call(SCI_STYLESETHOTSPOT
, STYLE_URL
, (LPARAM
)TRUE
);
383 Call(SCI_SETHOTSPOTACTIVEUNDERLINE
, (LPARAM
)TRUE
);
386 void CSciEdit::SetAutoCompletionList(const std::set
<CString
>& list
, const TCHAR separator
)
388 //copy the auto completion list.
390 //SK: instead of creating a copy of that list, we could accept a pointer
391 //to the list and use that instead. But then the caller would have to make
392 //sure that the list persists over the lifetime of the control!
395 m_separator
= separator
;
398 BOOL
CSciEdit::IsMisspelled(const CString
& sWord
)
400 // convert the string from the control to the encoding of the spell checker module.
405 buf
= sWordA
.GetBuffer(sWord
.GetLength()*4 + 1);
406 int lengthIncTerminator
=
407 WideCharToMultiByte(m_spellcodepage
, 0, sWord
, -1, buf
, sWord
.GetLength()*4, NULL
, NULL
);
408 sWordA
.ReleaseBuffer(lengthIncTerminator
-1);
411 sWordA
= CStringA(sWord
);
412 sWordA
.Trim("\'\".,");
413 // words starting with a digit are treated as correctly spelled
414 if (_istdigit(sWord
.GetAt(0)))
416 // words in the personal dictionary are correct too
417 if (m_personalDict
.FindWord(sWord
))
420 // now we actually check the spelling...
421 if (!pChecker
->spell(sWordA
))
423 // the word is marked as misspelled, we now check whether the word
424 // is maybe a composite identifier
425 // a composite identifier consists of multiple words, with each word
426 // separated by a change in lower to uppercase letters
427 if (sWord
.GetLength() > 1)
431 while (wordend
< sWord
.GetLength())
433 while ((wordend
< sWord
.GetLength())&&(!_istupper(sWord
[wordend
])))
435 if ((wordstart
== 0)&&(wordend
== sWord
.GetLength()))
437 // words in the auto list are also assumed correctly spelled
438 if (m_autolist
.find(sWord
) != m_autolist
.end())
442 sWordA
= CStringA(sWord
.Mid(wordstart
, wordend
-wordstart
));
443 if ((sWordA
.GetLength() > 2)&&(!pChecker
->spell(sWordA
)))
455 void CSciEdit::CheckSpelling()
457 if (pChecker
== NULL
)
460 TEXTRANGEA textrange
;
462 LRESULT firstline
= Call(SCI_GETFIRSTVISIBLELINE
);
463 LRESULT lastline
= firstline
+ Call(SCI_LINESONSCREEN
);
464 textrange
.chrg
.cpMin
= Call(SCI_POSITIONFROMLINE
, firstline
);
465 textrange
.chrg
.cpMax
= textrange
.chrg
.cpMin
;
466 LRESULT lastpos
= Call(SCI_POSITIONFROMLINE
, lastline
) + Call(SCI_LINELENGTH
, lastline
);
468 lastpos
= Call(SCI_GETLENGTH
)-textrange
.chrg
.cpMin
;
469 while (textrange
.chrg
.cpMax
< lastpos
)
471 textrange
.chrg
.cpMin
= Call(SCI_WORDSTARTPOSITION
, textrange
.chrg
.cpMax
+1, TRUE
);
472 if (textrange
.chrg
.cpMin
< textrange
.chrg
.cpMax
)
474 textrange
.chrg
.cpMax
= Call(SCI_WORDENDPOSITION
, textrange
.chrg
.cpMin
, TRUE
);
475 if (textrange
.chrg
.cpMin
== textrange
.chrg
.cpMax
)
477 textrange
.chrg
.cpMax
++;
480 ATLASSERT(textrange
.chrg
.cpMax
>= textrange
.chrg
.cpMin
);
481 char * textbuffer
= new char[textrange
.chrg
.cpMax
- textrange
.chrg
.cpMin
+ 2];
482 SecureZeroMemory(textbuffer
, textrange
.chrg
.cpMax
- textrange
.chrg
.cpMin
+ 2);
483 textrange
.lpstrText
= textbuffer
;
484 textrange
.chrg
.cpMax
++;
485 Call(SCI_GETTEXTRANGE
, 0, (LPARAM
)&textrange
);
486 int len
= strlen(textrange
.lpstrText
);
489 textrange
.chrg
.cpMax
--;
490 Call(SCI_GETTEXTRANGE
, 0, (LPARAM
)&textrange
);
491 len
= strlen(textrange
.lpstrText
);
492 textrange
.chrg
.cpMax
++;
495 if (len
&& textrange
.lpstrText
[len
- 1] == '.')
497 // Try to ignore file names from the auto list.
498 // Do do this, for each word ending with '.' we extract next word and check
499 // whether the combined string is present in auto list.
501 twoWords
.chrg
.cpMin
= textrange
.chrg
.cpMin
;
502 twoWords
.chrg
.cpMax
= Call(SCI_WORDENDPOSITION
, textrange
.chrg
.cpMax
+ 1, TRUE
);
503 twoWords
.lpstrText
= new char[twoWords
.chrg
.cpMax
- twoWords
.chrg
.cpMin
+ 1];
504 SecureZeroMemory(twoWords
.lpstrText
, twoWords
.chrg
.cpMax
- twoWords
.chrg
.cpMin
+ 1);
505 Call(SCI_GETTEXTRANGE
, 0, (LPARAM
)&twoWords
);
506 CString sWord
= StringFromControl(twoWords
.lpstrText
);
507 delete [] twoWords
.lpstrText
;
508 if (m_autolist
.find(sWord
) != m_autolist
.end())
510 //mark word as correct (remove the squiggle line)
511 Call(SCI_STARTSTYLING
, twoWords
.chrg
.cpMin
, INDICS_MASK
);
512 Call(SCI_SETSTYLING
, twoWords
.chrg
.cpMax
- twoWords
.chrg
.cpMin
, 0);
513 textrange
.chrg
.cpMax
= twoWords
.chrg
.cpMax
;
514 delete [] textbuffer
;
519 textrange
.lpstrText
[len
- 1] = 0;
520 textrange
.chrg
.cpMax
--;
521 if (strlen(textrange
.lpstrText
) > 0)
523 CString sWord
= StringFromControl(textrange
.lpstrText
);
524 if ((GetStyleAt(textrange
.chrg
.cpMin
) != STYLE_URL
) && IsMisspelled(sWord
))
526 //mark word as misspelled
527 Call(SCI_STARTSTYLING
, textrange
.chrg
.cpMin
, INDICS_MASK
);
528 Call(SCI_SETSTYLING
, textrange
.chrg
.cpMax
- textrange
.chrg
.cpMin
, INDIC1_MASK
);
532 //mark word as correct (remove the squiggle line)
533 Call(SCI_STARTSTYLING
, textrange
.chrg
.cpMin
, INDICS_MASK
);
534 Call(SCI_SETSTYLING
, textrange
.chrg
.cpMax
- textrange
.chrg
.cpMin
, 0);
537 delete [] textbuffer
;
541 void CSciEdit::SuggestSpellingAlternatives()
543 if (pChecker
== NULL
)
545 CString word
= GetWordUnderCursor(true);
546 Call(SCI_SETCURRENTPOS
, Call(SCI_WORDSTARTPOSITION
, Call(SCI_GETCURRENTPOS
), TRUE
));
550 int ns
= pChecker
->suggest(&wlst
, CStringA(word
));
554 for (int i
=0; i
< ns
; i
++)
556 suggestions
+= CString(wlst
[i
]) + m_separator
;
560 suggestions
.TrimRight(m_separator
);
561 if (suggestions
.IsEmpty())
563 Call(SCI_AUTOCSETSEPARATOR
, (WPARAM
)CStringA(m_separator
).GetAt(0));
564 Call(SCI_AUTOCSETDROPRESTOFWORD
, 1);
565 Call(SCI_AUTOCSHOW
, 0, (LPARAM
)(LPCSTR
)StringForControl(suggestions
));
570 void CSciEdit::DoAutoCompletion(int nMinPrefixLength
)
572 if (m_autolist
.size()==0)
574 if (Call(SCI_AUTOCACTIVE
))
576 CString word
= GetWordUnderCursor();
577 if (word
.GetLength() < nMinPrefixLength
)
578 return; //don't auto complete yet, word is too short
579 int pos
= Call(SCI_GETCURRENTPOS
);
580 if (pos
!= Call(SCI_WORDENDPOSITION
, pos
, TRUE
))
581 return; //don't auto complete if we're not at the end of a word
582 CString sAutoCompleteList
;
585 for (std::set
<CString
>::const_iterator lowerit
= m_autolist
.lower_bound(word
);
586 lowerit
!= m_autolist
.end(); ++lowerit
)
588 int compare
= word
.CompareNoCase(lowerit
->Left(word
.GetLength()));
591 else if (compare
== 0)
593 sAutoCompleteList
+= *lowerit
+ m_separator
;
600 sAutoCompleteList
.TrimRight(m_separator
);
601 if (sAutoCompleteList
.IsEmpty())
604 Call(SCI_AUTOCSETSEPARATOR
, (WPARAM
)CStringA(m_separator
).GetAt(0));
605 Call(SCI_AUTOCSHOW
, word
.GetLength(), (LPARAM
)(LPCSTR
)StringForControl(sAutoCompleteList
));
608 BOOL
CSciEdit::OnChildNotify(UINT message
, WPARAM wParam
, LPARAM lParam
, LRESULT
* pLResult
)
610 if (message
!= WM_NOTIFY
)
611 return CWnd::OnChildNotify(message
, wParam
, lParam
, pLResult
);
613 LPNMHDR lpnmhdr
= (LPNMHDR
) lParam
;
614 SCNotification
* lpSCN
= (SCNotification
*)lParam
;
616 if(lpnmhdr
->hwndFrom
==m_hWnd
)
618 switch(lpnmhdr
->code
)
622 if ((lpSCN
->ch
< 32)&&(lpSCN
->ch
!= 13)&&(lpSCN
->ch
!= 10))
623 Call(SCI_DELETEBACK
);
631 case SCN_STYLENEEDED
:
633 int startstylepos
= Call(SCI_GETENDSTYLED
);
634 int endstylepos
= ((SCNotification
*)lpnmhdr
)->position
;
635 MarkEnteredBugID(startstylepos
, endstylepos
);
636 StyleEnteredText(startstylepos
, endstylepos
);
637 StyleURLs(startstylepos
, endstylepos
);
639 WrapLines(startstylepos
, endstylepos
);
643 case SCN_HOTSPOTCLICK
:
645 TEXTRANGEA textrange
;
646 textrange
.chrg
.cpMin
= lpSCN
->position
;
647 textrange
.chrg
.cpMax
= lpSCN
->position
;
648 DWORD style
= GetStyleAt(lpSCN
->position
);
649 while (GetStyleAt(textrange
.chrg
.cpMin
- 1) == style
)
650 --textrange
.chrg
.cpMin
;
651 while (GetStyleAt(textrange
.chrg
.cpMax
+ 1) == style
)
652 ++textrange
.chrg
.cpMax
;
653 ++textrange
.chrg
.cpMax
;
654 char * textbuffer
= new char[textrange
.chrg
.cpMax
- textrange
.chrg
.cpMin
+ 1];
655 textrange
.lpstrText
= textbuffer
;
656 Call(SCI_GETTEXTRANGE
, 0, (LPARAM
)&textrange
);
658 if (style
== STYLE_URL
)
659 url
= StringFromControl(textbuffer
);
663 url
.Replace(_T("%BUGID%"), StringFromControl(textbuffer
));
665 delete [] textbuffer
;
667 ShellExecute(GetParent()->GetSafeHwnd(), _T("open"), url
, NULL
, NULL
, SW_SHOWDEFAULT
);
672 return CWnd::OnChildNotify(message
, wParam
, lParam
, pLResult
);
675 BEGIN_MESSAGE_MAP(CSciEdit
, CWnd
)
680 void CSciEdit::OnKeyDown(UINT nChar
, UINT nRepCnt
, UINT nFlags
)
686 if ((Call(SCI_AUTOCACTIVE
)==0)&&(Call(SCI_CALLTIPACTIVE
)==0))
687 ::SendMessage(GetParent()->GetSafeHwnd(), WM_CLOSE
, 0, 0);
691 CWnd::OnKeyDown(nChar
, nRepCnt
, nFlags
);
694 BOOL
CSciEdit::PreTranslateMessage(MSG
* pMsg
)
696 if (pMsg
->message
== WM_KEYDOWN
)
698 switch (pMsg
->wParam
)
702 if (GetKeyState(VK_CONTROL
) & 0x8000)
710 // The TAB cannot be handled in OnKeyDown because it is too late by then.
712 if (GetKeyState(VK_CONTROL
)&0x8000)
714 //Ctrl-Tab was pressed, this means we should provide the user with
715 //a list of possible spell checking alternatives to the word under
717 SuggestSpellingAlternatives();
720 else if (!Call(SCI_AUTOCACTIVE
))
722 ::PostMessage(GetParent()->GetSafeHwnd(), WM_NEXTDLGCTL
, GetKeyState(VK_SHIFT
)&0x8000, 0);
729 return CWnd::PreTranslateMessage(pMsg
);
732 void CSciEdit::OnContextMenu(CWnd
* /*pWnd*/, CPoint point
)
734 int anchor
= Call(SCI_GETANCHOR
);
735 int currentpos
= Call(SCI_GETCURRENTPOS
);
736 int selstart
= Call(SCI_GETSELECTIONSTART
);
737 int selend
= Call(SCI_GETSELECTIONEND
);
739 if ((point
.x
== -1) && (point
.y
== -1))
742 GetClientRect(&rect
);
743 ClientToScreen(&rect
);
744 point
= rect
.CenterPoint();
745 pointpos
= Call(SCI_GETCURRENTPOS
);
749 // change the cursor position to the point where the user
751 CPoint clientpoint
= point
;
752 ScreenToClient(&clientpoint
);
753 pointpos
= Call(SCI_POSITIONFROMPOINT
, clientpoint
.x
, clientpoint
.y
);
755 CString sMenuItemText
;
757 bool bRestoreCursor
= true;
758 if (popup
.CreatePopupMenu())
760 bool bCanUndo
= !!Call(SCI_CANUNDO
);
761 bool bCanRedo
= !!Call(SCI_CANREDO
);
762 bool bHasSelection
= (selend
-selstart
> 0);
763 bool bCanPaste
= !!Call(SCI_CANPASTE
);
764 UINT uEnabledMenu
= MF_STRING
| MF_ENABLED
;
765 UINT uDisabledMenu
= MF_STRING
| MF_GRAYED
;
767 // find the word under the cursor
771 // setting the cursor clears the selection
772 Call(SCI_SETANCHOR
, pointpos
);
773 Call(SCI_SETCURRENTPOS
, pointpos
);
774 sWord
= GetWordUnderCursor();
775 // restore the selection
776 Call(SCI_SETSELECTIONSTART
, selstart
);
777 Call(SCI_SETSELECTIONEND
, selend
);
780 sWord
= GetWordUnderCursor();
781 CStringA worda
= CStringA(sWord
);
783 int nCorrections
= 1;
784 bool bSpellAdded
= false;
785 // check if the word under the cursor is spelled wrong
786 if ((pChecker
)&&(!worda
.IsEmpty()))
789 // get the spell suggestions
790 int ns
= pChecker
->suggest(&wlst
,worda
);
793 // add the suggestions to the context menu
794 for (int i
=0; i
< ns
; i
++)
797 CString sug
= CString(wlst
[i
]);
798 popup
.InsertMenu((UINT
)-1, 0, nCorrections
++, sug
);
804 // only add a separator if spelling correction suggestions were added
806 popup
.AppendMenu(MF_SEPARATOR
);
808 // also allow the user to add the word to the custom dictionary so
809 // it won't show up as misspelled anymore
810 if ((sWord
.GetLength()<PDICT_MAX_WORD_LENGTH
)&&((pChecker
)&&(m_autolist
.find(sWord
) == m_autolist
.end())&&(!pChecker
->spell(worda
)))&&
811 (!_istdigit(sWord
.GetAt(0)))&&(!m_personalDict
.FindWord(sWord
)))
813 sMenuItemText
.Format(IDS_SCIEDIT_ADDWORD
, sWord
);
814 popup
.AppendMenu(uEnabledMenu
, SCI_ADDWORD
, sMenuItemText
);
816 popup
.AppendMenu(MF_SEPARATOR
);
819 // add the 'default' entries
820 sMenuItemText
.LoadString(IDS_SCIEDIT_UNDO
);
821 popup
.AppendMenu(bCanUndo
? uEnabledMenu
: uDisabledMenu
, SCI_UNDO
, sMenuItemText
);
822 sMenuItemText
.LoadString(IDS_SCIEDIT_REDO
);
823 popup
.AppendMenu(bCanRedo
? uEnabledMenu
: uDisabledMenu
, SCI_REDO
, sMenuItemText
);
825 popup
.AppendMenu(MF_SEPARATOR
);
827 sMenuItemText
.LoadString(IDS_SCIEDIT_CUT
);
828 popup
.AppendMenu(bHasSelection
? uEnabledMenu
: uDisabledMenu
, SCI_CUT
, sMenuItemText
);
829 sMenuItemText
.LoadString(IDS_SCIEDIT_COPY
);
830 popup
.AppendMenu(bHasSelection
? uEnabledMenu
: uDisabledMenu
, SCI_COPY
, sMenuItemText
);
831 sMenuItemText
.LoadString(IDS_SCIEDIT_PASTE
);
832 popup
.AppendMenu(bCanPaste
? uEnabledMenu
: uDisabledMenu
, SCI_PASTE
, sMenuItemText
);
834 popup
.AppendMenu(MF_SEPARATOR
);
836 sMenuItemText
.LoadString(IDS_SCIEDIT_SELECTALL
);
837 popup
.AppendMenu(uEnabledMenu
, SCI_SELECTALL
, sMenuItemText
);
839 popup
.AppendMenu(MF_SEPARATOR
);
841 sMenuItemText
.LoadString(IDS_SCIEDIT_SPLITLINES
);
842 popup
.AppendMenu(bHasSelection
? uEnabledMenu
: uDisabledMenu
, SCI_LINESSPLIT
, sMenuItemText
);
844 popup
.AppendMenu(MF_SEPARATOR
);
846 int nCustoms
= nCorrections
;
847 // now add any custom context menus
848 for (INT_PTR handlerindex
= 0; handlerindex
< m_arContextHandlers
.GetCount(); ++handlerindex
)
850 CSciEditContextMenuInterface
* pHandler
= m_arContextHandlers
.GetAt(handlerindex
);
851 pHandler
->InsertMenuItems(popup
, nCustoms
);
853 if (nCustoms
> nCorrections
)
855 // custom menu entries present, so add another separator
856 popup
.AppendMenu(MF_SEPARATOR
);
860 // add found thesauri to sub menu's
862 thesaurs
.CreatePopupMenu();
865 if ((pThesaur
)&&(!worda
.IsEmpty()))
869 int count
= pThesaur
->Lookup(worda
, worda
.GetLength(),&pmean
);
873 for (int i
=0; i
< count
; i
++)
875 CMenu
* submenu
= new CMenu();
876 menuArray
.Add(submenu
);
877 submenu
->CreateMenu();
878 for (int j
=0; j
< pm
->count
; j
++)
880 CString sug
= CString(pm
->psyns
[j
]);
881 submenu
->InsertMenu((UINT
)-1, 0, nThesaurs
++, sug
);
883 thesaurs
.InsertMenu((UINT
)-1, MF_POPUP
, (UINT_PTR
)(submenu
->m_hMenu
), CString(pm
->defn
));
887 if ((count
> 0)&&(point
.x
>= 0))
889 #ifdef IDS_SPELLEDIT_THESAURUS
890 sMenuItemText
.LoadString(IDS_SPELLEDIT_THESAURUS
);
891 popup
.InsertMenu((UINT
)-1, MF_POPUP
, (UINT_PTR
)thesaurs
.m_hMenu
, sMenuItemText
);
893 popup
.InsertMenu((UINT
)-1, MF_POPUP
, (UINT_PTR
)thesaurs
.m_hMenu
, _T("Thesaurus"));
895 nThesaurs
= nCustoms
;
899 sMenuItemText
.LoadString(IDS_SPELLEDIT_NOTHESAURUS
);
900 popup
.AppendMenu(MF_DISABLED
| MF_GRAYED
| MF_STRING
, 0, sMenuItemText
);
903 pThesaur
->CleanUpAfterLookup(&pmean
, count
);
907 sMenuItemText
.LoadString(IDS_SPELLEDIT_NOTHESAURUS
);
908 popup
.AppendMenu(MF_DISABLED
| MF_GRAYED
| MF_STRING
, 0, sMenuItemText
);
911 int cmd
= popup
.TrackPopupMenu(TPM_RETURNCMD
| TPM_LEFTALIGN
| TPM_NONOTIFY
, point
.x
, point
.y
, this, 0);
915 break; // no command selected
917 bRestoreCursor
= false;
927 m_personalDict
.AddWord(sWord
);
932 int marker
= Call(SCI_GETEDGECOLUMN
) * Call(SCI_TEXTWIDTH
, 0, (LPARAM
)" ");
935 Call(SCI_TARGETFROMSELECTION
);
937 Call(SCI_LINESSPLIT
, marker
);
942 if (cmd
< nCorrections
)
944 Call(SCI_SETANCHOR
, pointpos
);
945 Call(SCI_SETCURRENTPOS
, pointpos
);
946 GetWordUnderCursor(true);
948 popup
.GetMenuString(cmd
, temp
, 0);
949 // setting the cursor clears the selection
950 Call(SCI_REPLACESEL
, 0, (LPARAM
)(LPCSTR
)StringForControl(temp
));
952 else if (cmd
< (nCorrections
+nCustoms
))
954 for (INT_PTR handlerindex
= 0; handlerindex
< m_arContextHandlers
.GetCount(); ++handlerindex
)
956 CSciEditContextMenuInterface
* pHandler
= m_arContextHandlers
.GetAt(handlerindex
);
957 if (pHandler
->HandleMenuItemClick(cmd
, this))
962 else if (cmd
<= (nThesaurs
+nCorrections
+nCustoms
))
964 Call(SCI_SETANCHOR
, pointpos
);
965 Call(SCI_SETCURRENTPOS
, pointpos
);
966 GetWordUnderCursor(true);
968 thesaurs
.GetMenuString(cmd
, temp
, 0);
969 Call(SCI_REPLACESEL
, 0, (LPARAM
)(LPCSTR
)StringForControl(temp
));
974 for (INT_PTR index
= 0; index
< menuArray
.GetCount(); ++index
)
976 CMenu
* pMenu
= (CMenu
*)menuArray
[index
];
983 // restore the anchor and cursor position
984 Call(SCI_SETCURRENTPOS
, currentpos
);
985 Call(SCI_SETANCHOR
, anchor
);
989 bool CSciEdit::StyleEnteredText(int startstylepos
, int endstylepos
)
991 bool bStyled
= false;
992 const int line
= Call(SCI_LINEFROMPOSITION
, startstylepos
);
993 const int line_number_end
= Call(SCI_LINEFROMPOSITION
, endstylepos
);
994 for (int line_number
= line
; line_number
<= line_number_end
; ++line_number
)
996 int offset
= Call(SCI_POSITIONFROMLINE
, line_number
);
997 int line_len
= Call(SCI_LINELENGTH
, line_number
);
998 char * linebuffer
= new char[line_len
+1];
999 Call(SCI_GETLINE
, line_number
, (LPARAM
)linebuffer
);
1000 linebuffer
[line_len
] = 0;
1003 while (FindStyleChars(linebuffer
, '*', start
, end
))
1005 Call(SCI_STARTSTYLING
, start
+offset
, STYLE_MASK
);
1006 Call(SCI_SETSTYLING
, end
-start
, STYLE_BOLD
);
1012 while (FindStyleChars(linebuffer
, '^', start
, end
))
1014 Call(SCI_STARTSTYLING
, start
+offset
, STYLE_MASK
);
1015 Call(SCI_SETSTYLING
, end
-start
, STYLE_ITALIC
);
1021 while (FindStyleChars(linebuffer
, '_', start
, end
))
1023 Call(SCI_STARTSTYLING
, start
+offset
, STYLE_MASK
);
1024 Call(SCI_SETSTYLING
, end
-start
, STYLE_UNDERLINED
);
1028 delete [] linebuffer
;
1033 bool CSciEdit::WrapLines(int startpos
, int endpos
)
1035 int markerX
= Call(SCI_GETEDGECOLUMN
) * Call(SCI_TEXTWIDTH
, 0, (LPARAM
)" ");
1038 Call(SCI_SETTARGETSTART
, startpos
);
1039 Call(SCI_SETTARGETEND
, endpos
);
1040 Call(SCI_LINESSPLIT
, markerX
);
1046 void CSciEdit::AdvanceUTF8(const char * str
, int& pos
)
1048 if ((str
[pos
] & 0xE0)==0xC0)
1050 // utf8 2-byte sequence
1053 else if ((str
[pos
] & 0xF0)==0xE0)
1055 // utf8 3-byte sequence
1058 else if ((str
[pos
] & 0xF8)==0xF0)
1060 // utf8 4-byte sequence
1067 bool CSciEdit::FindStyleChars(const char * line
, char styler
, int& start
, int& end
)
1073 AdvanceUTF8(line
, i
);
1077 bool bFoundMarker
= false;
1078 CString sULine
= CUnicodeUtils::GetUnicode(line
);
1079 // find a starting marker
1080 while (line
[i
] != 0)
1082 if (line
[i
] == styler
)
1084 if ((line
[i
+1]!=0)&&(IsCharAlphaNumeric(sULine
[u
+1]))&&
1085 (((u
>0)&&(!IsCharAlphaNumeric(sULine
[u
-1]))) || (u
==0)))
1088 AdvanceUTF8(line
, i
);
1090 bFoundMarker
= true;
1094 AdvanceUTF8(line
, i
);
1099 // find ending marker
1100 bFoundMarker
= false;
1101 while (line
[i
] != 0)
1103 if (line
[i
] == styler
)
1105 if ((IsCharAlphaNumeric(sULine
[u
-1]))&&
1106 ((((u
+1)<sULine
.GetLength())&&(!IsCharAlphaNumeric(sULine
[u
+1]))) || ((u
+1) == sULine
.GetLength()))
1111 bFoundMarker
= true;
1115 AdvanceUTF8(line
, i
);
1118 return bFoundMarker
;
1121 BOOL
CSciEdit::MarkEnteredBugID(int startstylepos
, int endstylepos
)
1123 if (m_sCommand
.IsEmpty())
1125 // get the text between the start and end position we have to style
1126 const int line_number
= Call(SCI_LINEFROMPOSITION
, startstylepos
);
1127 int start_pos
= Call(SCI_POSITIONFROMLINE
, (WPARAM
)line_number
);
1128 int end_pos
= endstylepos
;
1130 if (start_pos
== end_pos
)
1132 if (start_pos
> end_pos
)
1134 int switchtemp
= start_pos
;
1135 start_pos
= end_pos
;
1136 end_pos
= switchtemp
;
1139 char * textbuffer
= new char[end_pos
- start_pos
+ 2];
1140 TEXTRANGEA textrange
;
1141 textrange
.lpstrText
= textbuffer
;
1142 textrange
.chrg
.cpMin
= start_pos
;
1143 textrange
.chrg
.cpMax
= end_pos
;
1144 Call(SCI_GETTEXTRANGE
, 0, (LPARAM
)&textrange
);
1145 CStringA msg
= CStringA(textbuffer
);
1147 Call(SCI_STARTSTYLING
, start_pos
, STYLE_MASK
);
1149 if (!m_sBugID
.IsEmpty())
1151 // match with two regex strings (without grouping!)
1154 const tr1::regex
regCheck(m_sCommand
);
1155 const tr1::regex
regBugID(m_sBugID
);
1156 const tr1::sregex_iterator end
;
1159 for (tr1::sregex_iterator
it(s
.begin(), s
.end(), regCheck
); it
!= end
; ++it
)
1161 // clear the styles up to the match position
1162 Call(SCI_SETSTYLING
, it
->position(0)-pos
, STYLE_DEFAULT
);
1163 pos
= it
->position(0);
1165 // (*it)[0] is the matched string
1166 string matchedString
= (*it
)[0];
1167 for (tr1::sregex_iterator
it2(matchedString
.begin(), matchedString
.end(), regBugID
); it2
!= end
; ++it2
)
1169 ATLTRACE(_T("matched id : %s\n"), (*it2
)[0].str().c_str());
1171 // bold style up to the id match
1172 ATLTRACE("position = %ld\n", it2
->position(0));
1173 if (it2
->position(0))
1174 Call(SCI_SETSTYLING
, it2
->position(0), STYLE_ISSUEBOLD
);
1175 // bold and recursive style for the bug ID itself
1176 if ((*it2
)[0].str().size())
1177 Call(SCI_SETSTYLING
, (*it2
)[0].str().size(), STYLE_ISSUEBOLDITALIC
);
1179 pos
= it
->position(0) + matchedString
.size();
1181 // bold style for the rest of the string which isn't matched
1183 Call(SCI_SETSTYLING
, s
.size()-pos
, STYLE_DEFAULT
);
1185 catch (exception
) {}
1191 const tr1::regex
regCheck(m_sCommand
);
1192 const tr1::sregex_iterator end
;
1195 for (tr1::sregex_iterator
it(s
.begin(), s
.end(), regCheck
); it
!= end
; ++it
)
1197 // clear the styles up to the match position
1198 Call(SCI_SETSTYLING
, it
->position(0)-pos
, STYLE_DEFAULT
);
1199 pos
= it
->position(0);
1201 const tr1::smatch match
= *it
;
1202 // we define group 1 as the whole issue text and
1203 // group 2 as the bug ID
1204 if (match
.size() >= 2)
1206 ATLTRACE(_T("matched id : %s\n"), string(match
[1]).c_str());
1207 Call(SCI_SETSTYLING
, match
[1].first
-s
.begin()-pos
, STYLE_ISSUEBOLD
);
1208 Call(SCI_SETSTYLING
, string(match
[1]).size(), STYLE_ISSUEBOLDITALIC
);
1209 pos
= match
[1].second
-s
.begin();
1213 catch (exception
) {}
1215 delete [] textbuffer
;
1220 bool CSciEdit::IsValidURLChar(unsigned char ch
)
1222 return isalnum(ch
) ||
1223 ch
== '_' || ch
== '/' || ch
== ';' || ch
== '?' || ch
== '&' || ch
== '=' ||
1224 ch
== '%' || ch
== ':' || ch
== '.' || ch
== '#' || ch
== '-' || ch
== '+';
1227 void CSciEdit::StyleURLs(int startstylepos
, int endstylepos
)
1229 const int line_number
= Call(SCI_LINEFROMPOSITION
, startstylepos
);
1230 startstylepos
= Call(SCI_POSITIONFROMLINE
, (WPARAM
)line_number
);
1232 int len
= endstylepos
- startstylepos
+ 1;
1233 char* textbuffer
= new char[len
+ 1];
1234 TEXTRANGEA textrange
;
1235 textrange
.lpstrText
= textbuffer
;
1236 textrange
.chrg
.cpMin
= startstylepos
;
1237 textrange
.chrg
.cpMax
= endstylepos
;
1238 Call(SCI_GETTEXTRANGE
, 0, (LPARAM
)&textrange
);
1239 // we're dealing with utf8 encoded text here, which means one glyph is
1240 // not necessarily one byte/wchar_t
1241 // that's why we use CStringA to still get a correct char index
1242 CStringA msg
= textbuffer
;
1243 delete [] textbuffer
;
1246 for(int i
= 0; i
<= msg
.GetLength(); )
1248 if ((i
< len
) && IsValidURLChar(msg
[i
]))
1255 if ((starturl
>= 0) && IsUrl(msg
.Mid(starturl
, i
- starturl
)))
1257 ASSERT(startstylepos
+ i
<= endstylepos
);
1258 Call(SCI_STARTSTYLING
, startstylepos
+ starturl
, STYLE_MASK
);
1259 Call(SCI_SETSTYLING
, i
- starturl
, STYLE_URL
);
1263 AdvanceUTF8(msg
, i
);
1267 bool CSciEdit::IsUrl(const CStringA
& sText
)
1269 if (!PathIsURLA(sText
))
1271 if (sText
.Find("://")>=0)
1276 bool CSciEdit::IsUTF8(LPVOID pBuffer
, size_t cb
)
1280 UINT16
* pVal
= (UINT16
*)pBuffer
;
1281 UINT8
* pVal2
= (UINT8
*)(pVal
+1);
1282 // scan the whole buffer for a 0x0000 sequence
1283 // if found, we assume a binary file
1284 for (size_t i
=0; i
<(cb
-2); i
=i
+2)
1286 if (0x0000 == *pVal
++)
1289 pVal
= (UINT16
*)pBuffer
;
1290 if (*pVal
== 0xFEFF)
1294 if (*pVal
== 0xBBEF)
1299 // check for illegal UTF8 chars
1300 pVal2
= (UINT8
*)pBuffer
;
1301 for (size_t i
=0; i
<cb
; ++i
)
1303 if ((*pVal2
== 0xC0)||(*pVal2
== 0xC1)||(*pVal2
>= 0xF5))
1307 pVal2
= (UINT8
*)pBuffer
;
1309 for (size_t i
=0; i
<(cb
-3); ++i
)
1311 if ((*pVal2
& 0xE0)==0xC0)
1314 if ((*pVal2
& 0xC0)!=0x80)
1318 if ((*pVal2
& 0xF0)==0xE0)
1321 if ((*pVal2
& 0xC0)!=0x80)
1324 if ((*pVal2
& 0xC0)!=0x80)
1328 if ((*pVal2
& 0xF8)==0xF0)
1331 if ((*pVal2
& 0xC0)!=0x80)
1334 if ((*pVal2
& 0xC0)!=0x80)
1337 if ((*pVal2
& 0xC0)!=0x80)
1348 void CSciEdit::SetAStyle(int style
, COLORREF fore
, COLORREF back
, int size
, const char *face
)
1350 Call(SCI_STYLESETFORE
, style
, fore
);
1351 Call(SCI_STYLESETBACK
, style
, back
);
1353 Call(SCI_STYLESETSIZE
, style
, size
);
1355 Call(SCI_STYLESETFONT
, style
, reinterpret_cast<LPARAM
>(face
));
1359 void CSciEdit::SetUDiffStyle()
1361 SetAStyle(STYLE_DEFAULT
, ::GetSysColor(COLOR_WINDOWTEXT
), ::GetSysColor(COLOR_WINDOW
),
1362 // Reusing TortoiseBlame's setting which already have an user friendly
1363 // pane in TortoiseSVN's Settings dialog, while there is no such
1364 // pane for TortoiseUDiff.
1365 CRegStdDWORD(_T("Software\\TortoiseGit\\BlameFontSize"), 10),
1366 WideToMultibyte(CRegStdString(_T("Software\\TortoiseGit\\BlameFontName"), _T("Courier New"))).c_str());
1368 Call(SCI_SETTABWIDTH
, 4);
1369 Call(SCI_SETREADONLY
, TRUE
);
1370 //LRESULT pix = Call(SCI_TEXTWIDTH, STYLE_LINENUMBER, (LPARAM)"_99999");
1371 //Call(SCI_SETMARGINWIDTHN, 0, pix);
1372 //Call(SCI_SETMARGINWIDTHN, 1);
1373 //Call(SCI_SETMARGINWIDTHN, 2);
1374 //Set the default windows colors for edit controls
1375 Call(SCI_STYLESETFORE
, STYLE_DEFAULT
, ::GetSysColor(COLOR_WINDOWTEXT
));
1376 Call(SCI_STYLESETBACK
, STYLE_DEFAULT
, ::GetSysColor(COLOR_WINDOW
));
1377 Call(SCI_SETSELFORE
, TRUE
, ::GetSysColor(COLOR_HIGHLIGHTTEXT
));
1378 Call(SCI_SETSELBACK
, TRUE
, ::GetSysColor(COLOR_HIGHLIGHT
));
1379 Call(SCI_SETCARETFORE
, ::GetSysColor(COLOR_WINDOWTEXT
));
1381 //SendEditor(SCI_SETREADONLY, FALSE);
1383 Call(EM_EMPTYUNDOBUFFER
);
1384 Call(SCI_SETSAVEPOINT
);
1386 Call(SCI_SETUNDOCOLLECTION
, 0);
1388 Call(SCI_SETUNDOCOLLECTION
, 1);
1389 Call(SCI_SETWRAPMODE
,SC_WRAP_NONE
);
1391 //::SetFocus(m_hWndEdit);
1392 Call(EM_EMPTYUNDOBUFFER
);
1393 Call(SCI_SETSAVEPOINT
);
1394 Call(SCI_GOTOPOS
, 0);
1396 Call(SCI_CLEARDOCUMENTSTYLE
, 0, 0);
1397 Call(SCI_SETSTYLEBITS
, 5, 0);
1399 //SetAStyle(SCE_DIFF_DEFAULT, RGB(0, 0, 0));
1400 SetAStyle(SCE_DIFF_COMMAND
, RGB(0x0A, 0x24, 0x36));
1401 SetAStyle(SCE_DIFF_POSITION
, RGB(0xFF, 0, 0));
1402 SetAStyle(SCE_DIFF_HEADER
, RGB(0x80, 0, 0), RGB(0xFF, 0xFF, 0x80));
1403 SetAStyle(SCE_DIFF_COMMENT
, RGB(0, 0x80, 0));
1404 Call(SCI_STYLESETBOLD
, SCE_DIFF_COMMENT
, TRUE
);
1405 SetAStyle(SCE_DIFF_DELETED
, ::GetSysColor(COLOR_WINDOWTEXT
), RGB(0xFF, 0x80, 0x80));
1406 SetAStyle(SCE_DIFF_ADDED
, ::GetSysColor(COLOR_WINDOWTEXT
), RGB(0x80, 0xFF, 0x80));
1408 Call(SCI_SETLEXER
, SCLEX_DIFF
);
1409 Call(SCI_SETKEYWORDS
, 0, (LPARAM
)"revision");
1410 Call(SCI_COLOURISE
, 0, -1);
1413 int CSciEdit::LoadFromFile(CString
&filename
)
1416 _tfopen_s(&fp
, filename
, _T("rb"));
1421 size_t lenFile
= fread(data
, 1, sizeof(data
), fp
);
1422 bool bUTF8
= IsUTF8(data
, lenFile
);
1425 Call(SCI_ADDTEXT
, lenFile
,
1426 reinterpret_cast<LPARAM
>(static_cast<char *>(data
)));
1427 lenFile
= fread(data
, 1, sizeof(data
), fp
);
1430 Call(SCI_SETCODEPAGE
, bUTF8
? SC_CP_UTF8
: GetACP());