1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2012-2014 - TortoiseGit
4 // Copyright (C) 2003-2008,2012-2014 - TortoiseSVN
5 // Copyright (C) 2012-2013 - Sven Strickroth <email@cs-ware.de>
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License
9 // as published by the Free Software Foundation; either version 2
10 // of the License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License for more details.
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software Foundation,
19 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include "LoglistCommonResource.h"
23 #include "..\PathUtils.h"
24 #include "..\UnicodeUtils.h"
26 #include "..\registry.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"},
69 IMPLEMENT_DYNAMIC(CSciEdit
, CWnd
)
71 CSciEdit::CSciEdit(void) : m_DirectFunction(NULL
)
72 , m_DirectPointer(NULL
)
78 , m_nAutoCompleteMinChars(3)
80 m_hModule
= ::LoadLibrary(_T("SciLexer_tgit.dll"));
83 CSciEdit::~CSciEdit(void)
85 m_personalDict
.Save();
87 ::FreeLibrary(m_hModule
);
94 void CSciEdit::Init(LONG lLanguage
, BOOL bLoadSpellCheck
)
96 //Setup the direct access data
97 m_DirectFunction
= SendMessage(SCI_GETDIRECTFUNCTION
, 0, 0);
98 m_DirectPointer
= SendMessage(SCI_GETDIRECTPOINTER
, 0, 0);
99 Call(SCI_SETMARGINWIDTHN
, 1, 0);
100 Call(SCI_SETUSETABS
, 0); //pressing TAB inserts spaces
101 Call(SCI_SETWRAPVISUALFLAGS
, SC_WRAPVISUALFLAG_END
);
102 Call(SCI_AUTOCSETIGNORECASE
, 1);
103 Call(SCI_SETLEXER
, SCLEX_CONTAINER
);
104 Call(SCI_SETCODEPAGE
, SC_CP_UTF8
);
105 Call(SCI_AUTOCSETFILLUPS
, 0, (LPARAM
)"\t([");
106 Call(SCI_AUTOCSETMAXWIDTH
, 0);
107 //Set the default windows colors for edit controls
108 Call(SCI_STYLESETFORE
, STYLE_DEFAULT
, ::GetSysColor(COLOR_WINDOWTEXT
));
109 Call(SCI_STYLESETBACK
, STYLE_DEFAULT
, ::GetSysColor(COLOR_WINDOW
));
110 Call(SCI_SETSELFORE
, TRUE
, ::GetSysColor(COLOR_HIGHLIGHTTEXT
));
111 Call(SCI_SETSELBACK
, TRUE
, ::GetSysColor(COLOR_HIGHLIGHT
));
112 Call(SCI_SETCARETFORE
, ::GetSysColor(COLOR_WINDOWTEXT
));
113 Call(SCI_SETMODEVENTMASK
, SC_MOD_INSERTTEXT
| SC_MOD_DELETETEXT
| SC_PERFORMED_UNDO
| SC_PERFORMED_REDO
);
114 Call(SCI_INDICSETFORE
, 1, 0x0000FF);
116 CStringA sWhiteSpace
;
117 for (int i
=0; i
<255; ++i
)
119 if (i
== '\r' || i
== '\n')
121 else if (i
< 0x20 || i
== ' ')
122 sWhiteSpace
+= (char)i
;
123 else if (isalnum(i
) || i
== '\'' || i
== '_' || i
== '-')
124 sWordChars
+= (char)i
;
126 Call(SCI_SETWORDCHARS
, 0, (LPARAM
)(LPCSTR
)sWordChars
);
127 Call(SCI_SETWHITESPACECHARS
, 0, (LPARAM
)(LPCSTR
)sWhiteSpace
);
128 m_bDoStyle
= ((DWORD
)CRegStdDWORD(_T("Software\\TortoiseGit\\StyleCommitMessages"), TRUE
))==TRUE
;
129 m_nAutoCompleteMinChars
= (int)(DWORD
)CRegStdDWORD(_T("Software\\TortoiseGit\\AutoCompleteMinChars"), 3);
130 // look for dictionary files and use them if found
131 long langId
= GetUserDefaultLCID();
135 if ((lLanguage
!= 0)||(((DWORD
)CRegStdDWORD(_T("Software\\TortoiseGit\\Spellchecker"), FALSE
))==FALSE
))
137 if (!((lLanguage
)&&(!LoadDictionaries(lLanguage
))))
141 LoadDictionaries(langId
);
142 DWORD lid
= SUBLANGID(langId
);
146 langId
= MAKELANGID(PRIMARYLANGID(langId
), lid
);
148 else if (langId
== 1033)
152 } while ((langId
)&&((pChecker
==NULL
)||(pThesaur
==NULL
)));
156 Call(SCI_SETEDGEMODE
, EDGE_NONE
);
157 Call(SCI_SETWRAPMODE
, SC_WRAP_WORD
);
158 Call(SCI_ASSIGNCMDKEY
, SCK_END
, SCI_LINEENDWRAP
);
159 Call(SCI_ASSIGNCMDKEY
, SCK_END
+ (SCMOD_SHIFT
<< 16), SCI_LINEENDWRAPEXTEND
);
160 Call(SCI_ASSIGNCMDKEY
, SCK_HOME
, SCI_HOMEWRAP
);
161 Call(SCI_ASSIGNCMDKEY
, SCK_HOME
+ (SCMOD_SHIFT
<< 16), SCI_HOMEWRAPEXTEND
);
162 CRegStdDWORD
used2d(L
"Software\\TortoiseGit\\ScintillaDirect2D", FALSE
);
163 if (SysInfo::Instance().IsWin7OrLater() && DWORD(used2d
))
165 Call(SCI_SETTECHNOLOGY
, SC_TECHNOLOGY_DIRECTWRITE
);
166 Call(SCI_SETBUFFEREDDRAW
, 0);
171 void CSciEdit::Init(const ProjectProperties
& props
)
173 Init(props
.lProjectLanguage
);
174 m_sCommand
= CStringA(CUnicodeUtils::GetUTF8(props
.sCheckRe
));
175 m_sBugID
= CStringA(CUnicodeUtils::GetUTF8(props
.sBugIDRe
));
176 m_sUrl
= CStringA(CUnicodeUtils::GetUTF8(props
.sUrl
));
178 if (props
.nLogWidthMarker
)
180 Call(SCI_SETWRAPMODE
, SC_WRAP_NONE
);
181 Call(SCI_SETEDGEMODE
, EDGE_LINE
);
182 Call(SCI_SETEDGECOLUMN
, props
.nLogWidthMarker
);
186 Call(SCI_SETEDGEMODE
, EDGE_NONE
);
187 Call(SCI_SETWRAPMODE
, SC_WRAP_WORD
);
191 BOOL
CSciEdit::LoadDictionaries(LONG lLanguageID
)
193 //Setup the spell checker and thesaurus
194 TCHAR buf
[6] = { 0 };
195 CString sFolder
= CPathUtils::GetAppDirectory();
196 CString sFolderUp
= CPathUtils::GetAppParentDirectory();
199 GetLocaleInfo(MAKELCID(lLanguageID
, SORT_DEFAULT
), LOCALE_SISO639LANGNAME
, buf
, _countof(buf
));
202 GetLocaleInfo(MAKELCID(lLanguageID
, SORT_DEFAULT
), LOCALE_SISO3166CTRYNAME
, buf
, _countof(buf
));
206 if ((PathFileExists(sFolder
+ sFile
+ _T(".aff"))) &&
207 (PathFileExists(sFolder
+ sFile
+ _T(".dic"))))
209 pChecker
= new Hunspell(CStringA(sFolder
+ sFile
+ _T(".aff")), CStringA(sFolder
+ sFile
+ _T(".dic")));
211 else if ((PathFileExists(sFolder
+ _T("dic\\") + sFile
+ _T(".aff"))) &&
212 (PathFileExists(sFolder
+ _T("dic\\") + sFile
+ _T(".dic"))))
214 pChecker
= new Hunspell(CStringA(sFolder
+ _T("dic\\") + sFile
+ _T(".aff")), CStringA(sFolder
+ _T("dic\\") + sFile
+ _T(".dic")));
216 else if ((PathFileExists(sFolderUp
+ sFile
+ _T(".aff"))) &&
217 (PathFileExists(sFolderUp
+ sFile
+ _T(".dic"))))
219 pChecker
= new Hunspell(CStringA(sFolderUp
+ sFile
+ _T(".aff")), CStringA(sFolderUp
+ sFile
+ _T(".dic")));
221 else if ((PathFileExists(sFolderUp
+ _T("dic\\") + sFile
+ _T(".aff"))) &&
222 (PathFileExists(sFolderUp
+ _T("dic\\") + sFile
+ _T(".dic"))))
224 pChecker
= new Hunspell(CStringA(sFolderUp
+ _T("dic\\") + sFile
+ _T(".aff")), CStringA(sFolderUp
+ _T("dic\\") + sFile
+ _T(".dic")));
226 else if ((PathFileExists(sFolderUp
+ _T("Languages\\") + sFile
+ _T(".aff"))) &&
227 (PathFileExists(sFolderUp
+ _T("Languages\\") + sFile
+ _T(".dic"))))
229 pChecker
= new Hunspell(CStringA(sFolderUp
+ _T("Languages\\") + sFile
+ _T(".aff")), CStringA(sFolderUp
+ _T("Languages\\") + sFile
+ _T(".dic")));
235 if ((PathFileExists(sFolder
+ _T("th_") + sFile
+ _T("_v2.idx"))) &&
236 (PathFileExists(sFolder
+ _T("th_") + sFile
+ _T("_v2.dat"))))
238 pThesaur
= new MyThes(CStringA(sFolder
+ sFile
+ _T("_v2.idx")), CStringA(sFolder
+ sFile
+ _T("_v2.dat")));
240 else if ((PathFileExists(sFolder
+ _T("dic\\th_") + sFile
+ _T("_v2.idx"))) &&
241 (PathFileExists(sFolder
+ _T("dic\\th_") + sFile
+ _T("_v2.dat"))))
243 pThesaur
= new MyThes(CStringA(sFolder
+ _T("dic\\") + sFile
+ _T("_v2.idx")), CStringA(sFolder
+ _T("dic\\") + sFile
+ _T("_v2.dat")));
245 else if ((PathFileExists(sFolderUp
+ _T("th_") + sFile
+ _T("_v2.idx"))) &&
246 (PathFileExists(sFolderUp
+ _T("th_") + sFile
+ _T("_v2.dat"))))
248 pThesaur
= new MyThes(CStringA(sFolderUp
+ _T("th_") + sFile
+ _T("_v2.idx")), CStringA(sFolderUp
+ _T("th_") + sFile
+ _T("_v2.dat")));
250 else if ((PathFileExists(sFolderUp
+ _T("dic\\th_") + sFile
+ _T("_v2.idx"))) &&
251 (PathFileExists(sFolderUp
+ _T("dic\\th_") + sFile
+ _T("_v2.dat"))))
253 pThesaur
= new MyThes(CStringA(sFolderUp
+ _T("dic\\th_") + sFile
+ _T("_v2.idx")), CStringA(sFolderUp
+ _T("dic\\th_") + sFile
+ _T("_v2.dat")));
255 else if ((PathFileExists(sFolderUp
+ _T("Languages\\th_") + sFile
+ _T("_v2.idx"))) &&
256 (PathFileExists(sFolderUp
+ _T("Languages\\th_") + sFile
+ _T("_v2.dat"))))
258 pThesaur
= new MyThes(CStringA(sFolderUp
+ _T("Languages\\th_") + sFile
+ _T("_v2.idx")), CStringA(sFolderUp
+ _T("Languages\\th_") + sFile
+ _T("_v2.dat")));
264 const char * encoding
= pChecker
->get_dic_encoding();
265 CTraceToOutputDebugString::Instance()(__FUNCTION__
": %s\n", encoding
);
266 int n
= _countof(enc2locale
);
268 for (int i
= 0; i
< n
; i
++)
270 if (strcmp(encoding
,enc2locale
[i
].def_enc
) == 0)
272 m_spellcodepage
= atoi(enc2locale
[i
].cp
);
275 m_personalDict
.Init(lLanguageID
);
277 if ((pThesaur
)||(pChecker
))
282 LRESULT
CSciEdit::Call(UINT message
, WPARAM wParam
, LPARAM lParam
)
284 ASSERT(::IsWindow(m_hWnd
)); //Window must be valid
285 ASSERT(m_DirectFunction
); //Direct function must be valid
286 return ((SciFnDirect
) m_DirectFunction
)(m_DirectPointer
, message
, wParam
, lParam
);
289 CString
CSciEdit::StringFromControl(const CStringA
& text
)
293 int codepage
= (int)Call(SCI_GETCODEPAGE
);
294 int reslen
= MultiByteToWideChar(codepage
, 0, text
, text
.GetLength(), 0, 0);
295 MultiByteToWideChar(codepage
, 0, text
, text
.GetLength(), sText
.GetBuffer(reslen
+1), reslen
+1);
296 sText
.ReleaseBuffer(reslen
);
303 CStringA
CSciEdit::StringForControl(const CString
& text
)
307 int codepage
= (int)SendMessage(SCI_GETCODEPAGE
);
308 int reslen
= WideCharToMultiByte(codepage
, 0, text
, text
.GetLength(), 0, 0, 0, 0);
309 WideCharToMultiByte(codepage
, 0, text
, text
.GetLength(), sTextA
.GetBuffer(reslen
), reslen
, 0, 0);
310 sTextA
.ReleaseBuffer(reslen
);
314 ATLTRACE("string length %d\n", sTextA
.GetLength());
318 void CSciEdit::SetText(const CString
& sText
)
320 CStringA sTextA
= StringForControl(sText
);
321 Call(SCI_SETTEXT
, 0, (LPARAM
)(LPCSTR
)sTextA
);
323 // Scintilla seems to have problems with strings that
324 // aren't terminated by a newline char. Once that char
325 // is there, it can be removed without problems.
326 // So we add here a newline, then remove it again.
327 Call(SCI_DOCUMENTEND
);
329 Call(SCI_DELETEBACK
);
332 void CSciEdit::InsertText(const CString
& sText
, bool bNewLine
)
334 CStringA sTextA
= StringForControl(sText
);
335 Call(SCI_REPLACESEL
, 0, (LPARAM
)(LPCSTR
)sTextA
);
337 Call(SCI_REPLACESEL
, 0, (LPARAM
)(LPCSTR
)"\n");
340 CString
CSciEdit::GetText()
342 LRESULT len
= Call(SCI_GETTEXT
, 0, 0);
344 Call(SCI_GETTEXT
, (WPARAM
)(len
+ 1), (LPARAM
)(LPCSTR
)sTextA
.GetBuffer((int)len
+ 1));
345 sTextA
.ReleaseBuffer();
346 return StringFromControl(sTextA
);
349 CString
CSciEdit::GetWordUnderCursor(bool bSelectWord
)
351 TEXTRANGEA textrange
;
352 int pos
= (int)Call(SCI_GETCURRENTPOS
);
353 textrange
.chrg
.cpMin
= (LONG
)Call(SCI_WORDSTARTPOSITION
, pos
, TRUE
);
354 if ((pos
== textrange
.chrg
.cpMin
)||(textrange
.chrg
.cpMin
< 0))
356 textrange
.chrg
.cpMax
= (LONG
)Call(SCI_WORDENDPOSITION
, textrange
.chrg
.cpMin
, TRUE
);
358 std::unique_ptr
<char[]> textbuffer(new char[textrange
.chrg
.cpMax
- textrange
.chrg
.cpMin
+ 1]);
359 textrange
.lpstrText
= textbuffer
.get();
360 Call(SCI_GETTEXTRANGE
, 0, (LPARAM
)&textrange
);
363 Call(SCI_SETSEL
, textrange
.chrg
.cpMin
, textrange
.chrg
.cpMax
);
365 CString sRet
= StringFromControl(textbuffer
.get());
369 void CSciEdit::SetFont(CString sFontName
, int iFontSizeInPoints
)
371 Call(SCI_STYLESETFONT
, STYLE_DEFAULT
, (LPARAM
)(LPCSTR
)CStringA(sFontName
));
372 Call(SCI_STYLESETSIZE
, STYLE_DEFAULT
, iFontSizeInPoints
);
373 Call(SCI_STYLECLEARALL
);
375 LPARAM color
= (LPARAM
)GetSysColor(COLOR_HIGHLIGHT
);
376 // set the styles for the bug ID strings
377 Call(SCI_STYLESETBOLD
, STYLE_ISSUEBOLD
, (LPARAM
)TRUE
);
378 Call(SCI_STYLESETFORE
, STYLE_ISSUEBOLD
, color
);
379 Call(SCI_STYLESETBOLD
, STYLE_ISSUEBOLDITALIC
, (LPARAM
)TRUE
);
380 Call(SCI_STYLESETITALIC
, STYLE_ISSUEBOLDITALIC
, (LPARAM
)TRUE
);
381 Call(SCI_STYLESETFORE
, STYLE_ISSUEBOLDITALIC
, color
);
382 Call(SCI_STYLESETHOTSPOT
, STYLE_ISSUEBOLDITALIC
, (LPARAM
)TRUE
);
384 // set the formatted text styles
385 Call(SCI_STYLESETBOLD
, STYLE_BOLD
, (LPARAM
)TRUE
);
386 Call(SCI_STYLESETITALIC
, STYLE_ITALIC
, (LPARAM
)TRUE
);
387 Call(SCI_STYLESETUNDERLINE
, STYLE_UNDERLINED
, (LPARAM
)TRUE
);
389 // set the style for URLs
390 Call(SCI_STYLESETFORE
, STYLE_URL
, color
);
391 Call(SCI_STYLESETHOTSPOT
, STYLE_URL
, (LPARAM
)TRUE
);
393 Call(SCI_SETHOTSPOTACTIVEUNDERLINE
, (LPARAM
)TRUE
);
396 void CSciEdit::SetAutoCompletionList(const std::set
<CString
>& list
, const TCHAR separator
)
398 //copy the auto completion list.
400 //SK: instead of creating a copy of that list, we could accept a pointer
401 //to the list and use that instead. But then the caller would have to make
402 //sure that the list persists over the lifetime of the control!
405 m_separator
= separator
;
408 BOOL
CSciEdit::IsMisspelled(const CString
& sWord
)
410 // convert the string from the control to the encoding of the spell checker module.
415 buf
= sWordA
.GetBuffer(sWord
.GetLength()*4 + 1);
416 int lengthIncTerminator
=
417 WideCharToMultiByte(m_spellcodepage
, 0, sWord
, -1, buf
, sWord
.GetLength()*4, NULL
, NULL
);
418 if (lengthIncTerminator
== 0)
419 return FALSE
; // converting to the codepage failed, assume word is spelled correctly
420 sWordA
.ReleaseBuffer(lengthIncTerminator
-1);
423 sWordA
= CStringA(sWord
);
424 sWordA
.Trim("\'\".,");
425 // words starting with a digit are treated as correctly spelled
426 if (_istdigit(sWord
.GetAt(0)))
428 // words in the personal dictionary are correct too
429 if (m_personalDict
.FindWord(sWord
))
432 // now we actually check the spelling...
433 if (!pChecker
->spell(sWordA
))
435 // the word is marked as misspelled, we now check whether the word
436 // is maybe a composite identifier
437 // a composite identifier consists of multiple words, with each word
438 // separated by a change in lower to uppercase letters
439 if (sWord
.GetLength() > 1)
443 while (wordend
< sWord
.GetLength())
445 while ((wordend
< sWord
.GetLength())&&(!_istupper(sWord
[wordend
])))
447 if ((wordstart
== 0)&&(wordend
== sWord
.GetLength()))
449 // words in the auto list are also assumed correctly spelled
450 if (m_autolist
.find(sWord
) != m_autolist
.end())
454 sWordA
= CStringA(sWord
.Mid(wordstart
, wordend
-wordstart
));
455 if ((sWordA
.GetLength() > 2)&&(!pChecker
->spell(sWordA
)))
467 void CSciEdit::CheckSpelling()
469 if (pChecker
== NULL
)
472 TEXTRANGEA textrange
;
474 LRESULT firstline
= Call(SCI_GETFIRSTVISIBLELINE
);
475 LRESULT lastline
= firstline
+ Call(SCI_LINESONSCREEN
);
476 textrange
.chrg
.cpMin
= (LONG
)Call(SCI_POSITIONFROMLINE
, firstline
);
477 textrange
.chrg
.cpMax
= (LONG
)textrange
.chrg
.cpMin
;
478 LRESULT lastpos
= Call(SCI_POSITIONFROMLINE
, lastline
) + Call(SCI_LINELENGTH
, lastline
);
480 lastpos
= Call(SCI_GETLENGTH
)-textrange
.chrg
.cpMin
;
481 while (textrange
.chrg
.cpMax
< lastpos
)
483 textrange
.chrg
.cpMin
= (LONG
)Call(SCI_WORDSTARTPOSITION
, textrange
.chrg
.cpMax
+1, TRUE
);
484 if (textrange
.chrg
.cpMin
< textrange
.chrg
.cpMax
)
486 textrange
.chrg
.cpMax
= (LONG
)Call(SCI_WORDENDPOSITION
, textrange
.chrg
.cpMin
, TRUE
);
487 if (textrange
.chrg
.cpMin
== textrange
.chrg
.cpMax
)
489 textrange
.chrg
.cpMax
++;
492 ATLASSERT(textrange
.chrg
.cpMax
>= textrange
.chrg
.cpMin
);
493 std::unique_ptr
<char[]> textbuffer(new char[textrange
.chrg
.cpMax
- textrange
.chrg
.cpMin
+ 2]);
494 SecureZeroMemory(textbuffer
.get(), textrange
.chrg
.cpMax
- textrange
.chrg
.cpMin
+ 2);
495 textrange
.lpstrText
= textbuffer
.get();
496 textrange
.chrg
.cpMax
++;
497 Call(SCI_GETTEXTRANGE
, 0, (LPARAM
)&textrange
);
498 int len
= (int)strlen(textrange
.lpstrText
);
501 textrange
.chrg
.cpMax
--;
502 Call(SCI_GETTEXTRANGE
, 0, (LPARAM
)&textrange
);
503 len
= (int)strlen(textrange
.lpstrText
);
504 textrange
.chrg
.cpMax
++;
507 if (len
&& textrange
.lpstrText
[len
- 1] == '.')
509 // Try to ignore file names from the auto list.
510 // Do do this, for each word ending with '.' we extract next word and check
511 // whether the combined string is present in auto list.
513 twoWords
.chrg
.cpMin
= (LONG
)textrange
.chrg
.cpMin
;
514 twoWords
.chrg
.cpMax
= (LONG
)Call(SCI_WORDENDPOSITION
, textrange
.chrg
.cpMax
+ 1, TRUE
);
515 std::unique_ptr
<char[]> twoWordsBuffer(new char[twoWords
.chrg
.cpMax
- twoWords
.chrg
.cpMin
+ 1]);
516 twoWords
.lpstrText
= twoWordsBuffer
.get();
517 SecureZeroMemory(twoWords
.lpstrText
, twoWords
.chrg
.cpMax
- twoWords
.chrg
.cpMin
+ 1);
518 Call(SCI_GETTEXTRANGE
, 0, (LPARAM
)&twoWords
);
519 CString sWord
= StringFromControl(twoWords
.lpstrText
);
520 if (m_autolist
.find(sWord
) != m_autolist
.end())
522 //mark word as correct (remove the squiggle line)
523 Call(SCI_STARTSTYLING
, twoWords
.chrg
.cpMin
, INDICS_MASK
);
524 Call(SCI_SETSTYLING
, twoWords
.chrg
.cpMax
- twoWords
.chrg
.cpMin
, 0);
525 textrange
.chrg
.cpMax
= twoWords
.chrg
.cpMax
;
530 textrange
.lpstrText
[len
- 1] = 0;
531 textrange
.chrg
.cpMax
--;
532 if (strlen(textrange
.lpstrText
) > 0)
534 CString sWord
= StringFromControl(textrange
.lpstrText
);
535 if ((GetStyleAt(textrange
.chrg
.cpMin
) != STYLE_URL
) && IsMisspelled(sWord
))
537 //mark word as misspelled
538 Call(SCI_STARTSTYLING
, textrange
.chrg
.cpMin
, INDICS_MASK
);
539 Call(SCI_SETSTYLING
, textrange
.chrg
.cpMax
- textrange
.chrg
.cpMin
, INDIC1_MASK
);
543 //mark word as correct (remove the squiggle line)
544 Call(SCI_STARTSTYLING
, textrange
.chrg
.cpMin
, INDICS_MASK
);
545 Call(SCI_SETSTYLING
, textrange
.chrg
.cpMax
- textrange
.chrg
.cpMin
, 0);
551 void CSciEdit::SuggestSpellingAlternatives()
553 if (pChecker
== NULL
)
555 CString word
= GetWordUnderCursor(true);
556 Call(SCI_SETCURRENTPOS
, Call(SCI_WORDSTARTPOSITION
, Call(SCI_GETCURRENTPOS
), TRUE
));
559 char ** wlst
= nullptr;
560 int ns
= pChecker
->suggest(&wlst
, CStringA(word
));
564 for (int i
=0; i
< ns
; i
++)
566 suggestions
+= CString(wlst
[i
]) + m_separator
;
570 suggestions
.TrimRight(m_separator
);
571 if (suggestions
.IsEmpty())
573 Call(SCI_AUTOCSETSEPARATOR
, (WPARAM
)CStringA(m_separator
).GetAt(0));
574 Call(SCI_AUTOCSETDROPRESTOFWORD
, 1);
575 Call(SCI_AUTOCSHOW
, 0, (LPARAM
)(LPCSTR
)StringForControl(suggestions
));
581 void CSciEdit::DoAutoCompletion(int nMinPrefixLength
)
583 if (m_autolist
.empty())
585 if (Call(SCI_AUTOCACTIVE
))
587 CString word
= GetWordUnderCursor();
588 if (word
.GetLength() < nMinPrefixLength
)
589 return; //don't auto complete yet, word is too short
590 int pos
= (int)Call(SCI_GETCURRENTPOS
);
591 if (pos
!= Call(SCI_WORDENDPOSITION
, pos
, TRUE
))
592 return; //don't auto complete if we're not at the end of a word
593 CString sAutoCompleteList
;
595 std::vector
<CString
> words
;
597 pos
= word
.Find('-');
599 CString wordLower
= word
;
600 wordLower
.MakeLower();
601 CString wordHigher
= word
;
602 wordHigher
.MakeUpper();
604 words
.push_back(wordLower
);
605 words
.push_back(wordHigher
);
609 CString s
= wordLower
.Left(pos
);
610 if (s
.GetLength() >= nMinPrefixLength
)
612 s
= wordLower
.Mid(pos
+1);
613 if (s
.GetLength() >= nMinPrefixLength
)
615 s
= wordHigher
.Left(pos
);
616 if (s
.GetLength() >= nMinPrefixLength
)
617 words
.push_back(wordHigher
.Left(pos
));
618 s
= wordHigher
.Mid(pos
+1);
619 if (s
.GetLength() >= nMinPrefixLength
)
620 words
.push_back(wordHigher
.Mid(pos
+1));
623 std::set
<CString
> wordset
;
624 for (const auto& w
: words
)
626 for (std::set
<CString
>::const_iterator lowerit
= m_autolist
.lower_bound(w
);
627 lowerit
!= m_autolist
.end(); ++lowerit
)
629 int compare
= w
.CompareNoCase(lowerit
->Left(w
.GetLength()));
632 else if (compare
== 0)
634 wordset
.insert(*lowerit
);
643 for (const auto& w
: wordset
)
644 sAutoCompleteList
+= w
+ m_separator
;
646 sAutoCompleteList
.TrimRight(m_separator
);
647 if (sAutoCompleteList
.IsEmpty())
650 Call(SCI_AUTOCSETSEPARATOR
, (WPARAM
)CStringA(m_separator
).GetAt(0));
651 Call(SCI_AUTOCSHOW
, word
.GetLength(), (LPARAM
)(LPCSTR
)StringForControl(sAutoCompleteList
));
654 BOOL
CSciEdit::OnChildNotify(UINT message
, WPARAM wParam
, LPARAM lParam
, LRESULT
* pLResult
)
656 if (message
!= WM_NOTIFY
)
657 return CWnd::OnChildNotify(message
, wParam
, lParam
, pLResult
);
659 LPNMHDR lpnmhdr
= (LPNMHDR
) lParam
;
660 SCNotification
* lpSCN
= (SCNotification
*)lParam
;
662 if(lpnmhdr
->hwndFrom
==m_hWnd
)
664 switch(lpnmhdr
->code
)
668 if ((lpSCN
->ch
< 32)&&(lpSCN
->ch
!= 13)&&(lpSCN
->ch
!= 10))
669 Call(SCI_DELETEBACK
);
672 DoAutoCompletion(m_nAutoCompleteMinChars
);
677 case SCN_STYLENEEDED
:
679 int startstylepos
= (int)Call(SCI_GETENDSTYLED
);
680 int endstylepos
= ((SCNotification
*)lpnmhdr
)->position
;
681 MarkEnteredBugID(startstylepos
, endstylepos
);
683 StyleEnteredText(startstylepos
, endstylepos
);
684 StyleURLs(startstylepos
, endstylepos
);
686 WrapLines(startstylepos
, endstylepos
);
690 case SCN_HOTSPOTCLICK
:
692 TEXTRANGEA textrange
;
693 textrange
.chrg
.cpMin
= lpSCN
->position
;
694 textrange
.chrg
.cpMax
= lpSCN
->position
;
695 DWORD style
= GetStyleAt(lpSCN
->position
);
696 while (GetStyleAt(textrange
.chrg
.cpMin
- 1) == style
)
697 --textrange
.chrg
.cpMin
;
698 while (GetStyleAt(textrange
.chrg
.cpMax
+ 1) == style
)
699 ++textrange
.chrg
.cpMax
;
700 ++textrange
.chrg
.cpMax
;
701 std::unique_ptr
<char[]> textbuffer(new char[textrange
.chrg
.cpMax
- textrange
.chrg
.cpMin
+ 1]);
702 textrange
.lpstrText
= textbuffer
.get();
703 Call(SCI_GETTEXTRANGE
, 0, (LPARAM
)&textrange
);
705 if (style
== STYLE_URL
)
706 url
= StringFromControl(textbuffer
.get());
710 url
.Replace(L
"%BUGID%", StringFromControl(textbuffer
.get()));
713 ShellExecute(GetParent()->GetSafeHwnd(), _T("open"), url
, NULL
, NULL
, SW_SHOWDEFAULT
);
718 return CWnd::OnChildNotify(message
, wParam
, lParam
, pLResult
);
721 BEGIN_MESSAGE_MAP(CSciEdit
, CWnd
)
726 void CSciEdit::OnKeyDown(UINT nChar
, UINT nRepCnt
, UINT nFlags
)
732 if ((Call(SCI_AUTOCACTIVE
)==0)&&(Call(SCI_CALLTIPACTIVE
)==0))
733 ::SendMessage(GetParent()->GetSafeHwnd(), WM_CLOSE
, 0, 0);
737 CWnd::OnKeyDown(nChar
, nRepCnt
, nFlags
);
740 BOOL
CSciEdit::PreTranslateMessage(MSG
* pMsg
)
742 if (pMsg
->message
== WM_KEYDOWN
)
744 switch (pMsg
->wParam
)
748 if (GetKeyState(VK_CONTROL
) & 0x8000)
756 // The TAB cannot be handled in OnKeyDown because it is too late by then.
758 if (GetKeyState(VK_CONTROL
)&0x8000)
760 //Ctrl-Tab was pressed, this means we should provide the user with
761 //a list of possible spell checking alternatives to the word under
763 SuggestSpellingAlternatives();
766 else if (!Call(SCI_AUTOCACTIVE
))
768 ::PostMessage(GetParent()->GetSafeHwnd(), WM_NEXTDLGCTL
, GetKeyState(VK_SHIFT
)&0x8000, 0);
775 return CWnd::PreTranslateMessage(pMsg
);
778 void CSciEdit::OnContextMenu(CWnd
* /*pWnd*/, CPoint point
)
780 int anchor
= (int)Call(SCI_GETANCHOR
);
781 int currentpos
= (int)Call(SCI_GETCURRENTPOS
);
782 int selstart
= (int)Call(SCI_GETSELECTIONSTART
);
783 int selend
= (int)Call(SCI_GETSELECTIONEND
);
785 if ((point
.x
== -1) && (point
.y
== -1))
788 GetClientRect(&rect
);
789 ClientToScreen(&rect
);
790 point
= rect
.CenterPoint();
791 pointpos
= (int)Call(SCI_GETCURRENTPOS
);
795 // change the cursor position to the point where the user
797 CPoint clientpoint
= point
;
798 ScreenToClient(&clientpoint
);
799 pointpos
= (int)Call(SCI_POSITIONFROMPOINT
, clientpoint
.x
, clientpoint
.y
);
801 CString sMenuItemText
;
803 bool bRestoreCursor
= true;
804 if (popup
.CreatePopupMenu())
806 bool bCanUndo
= !!Call(SCI_CANUNDO
);
807 bool bCanRedo
= !!Call(SCI_CANREDO
);
808 bool bHasSelection
= (selend
-selstart
> 0);
809 bool bCanPaste
= !!Call(SCI_CANPASTE
);
810 bool bIsReadOnly
= !!Call(SCI_GETREADONLY
);
811 UINT uEnabledMenu
= MF_STRING
| MF_ENABLED
;
812 UINT uDisabledMenu
= MF_STRING
| MF_GRAYED
;
814 // find the word under the cursor
818 // setting the cursor clears the selection
819 Call(SCI_SETANCHOR
, pointpos
);
820 Call(SCI_SETCURRENTPOS
, pointpos
);
821 sWord
= GetWordUnderCursor();
822 // restore the selection
823 Call(SCI_SETSELECTIONSTART
, selstart
);
824 Call(SCI_SETSELECTIONEND
, selend
);
827 sWord
= GetWordUnderCursor();
828 CStringA worda
= CStringA(sWord
);
830 int nCorrections
= 1;
831 bool bSpellAdded
= false;
832 // check if the word under the cursor is spelled wrong
833 if ((pChecker
)&&(!worda
.IsEmpty()) && !bIsReadOnly
)
835 char ** wlst
= nullptr;
836 // get the spell suggestions
837 int ns
= pChecker
->suggest(&wlst
,worda
);
840 // add the suggestions to the context menu
841 for (int i
=0; i
< ns
; i
++)
844 CString sug
= CString(wlst
[i
]);
845 popup
.InsertMenu((UINT
)-1, 0, nCorrections
++, sug
);
853 // only add a separator if spelling correction suggestions were added
855 popup
.AppendMenu(MF_SEPARATOR
);
857 // also allow the user to add the word to the custom dictionary so
858 // it won't show up as misspelled anymore
859 if ((sWord
.GetLength()<PDICT_MAX_WORD_LENGTH
)&&((pChecker
)&&(m_autolist
.find(sWord
) == m_autolist
.end())&&(!pChecker
->spell(worda
)))&&
860 (!_istdigit(sWord
.GetAt(0)))&&(!m_personalDict
.FindWord(sWord
)) && !bIsReadOnly
)
862 sMenuItemText
.Format(IDS_SCIEDIT_ADDWORD
, sWord
);
863 popup
.AppendMenu(uEnabledMenu
, SCI_ADDWORD
, sMenuItemText
);
865 popup
.AppendMenu(MF_SEPARATOR
);
868 // add the 'default' entries
869 sMenuItemText
.LoadString(IDS_SCIEDIT_UNDO
);
870 popup
.AppendMenu(bCanUndo
? uEnabledMenu
: uDisabledMenu
, SCI_UNDO
, sMenuItemText
);
871 sMenuItemText
.LoadString(IDS_SCIEDIT_REDO
);
872 popup
.AppendMenu(bCanRedo
? uEnabledMenu
: uDisabledMenu
, SCI_REDO
, sMenuItemText
);
874 popup
.AppendMenu(MF_SEPARATOR
);
876 sMenuItemText
.LoadString(IDS_SCIEDIT_CUT
);
877 popup
.AppendMenu(bHasSelection
? uEnabledMenu
: uDisabledMenu
, SCI_CUT
, sMenuItemText
);
878 sMenuItemText
.LoadString(IDS_SCIEDIT_COPY
);
879 popup
.AppendMenu(bHasSelection
? uEnabledMenu
: uDisabledMenu
, SCI_COPY
, sMenuItemText
);
880 sMenuItemText
.LoadString(IDS_SCIEDIT_PASTE
);
881 popup
.AppendMenu(bCanPaste
? uEnabledMenu
: uDisabledMenu
, SCI_PASTE
, sMenuItemText
);
883 popup
.AppendMenu(MF_SEPARATOR
);
885 sMenuItemText
.LoadString(IDS_SCIEDIT_SELECTALL
);
886 popup
.AppendMenu(uEnabledMenu
, SCI_SELECTALL
, sMenuItemText
);
888 popup
.AppendMenu(MF_SEPARATOR
);
890 sMenuItemText
.LoadString(IDS_SCIEDIT_SPLITLINES
);
891 popup
.AppendMenu(bHasSelection
? uEnabledMenu
: uDisabledMenu
, SCI_LINESSPLIT
, sMenuItemText
);
893 if (m_arContextHandlers
.GetCount() > 0)
894 popup
.AppendMenu(MF_SEPARATOR
);
896 int nCustoms
= nCorrections
;
897 // now add any custom context menus
898 for (INT_PTR handlerindex
= 0; handlerindex
< m_arContextHandlers
.GetCount(); ++handlerindex
)
900 CSciEditContextMenuInterface
* pHandler
= m_arContextHandlers
.GetAt(handlerindex
);
901 pHandler
->InsertMenuItems(popup
, nCustoms
);
904 if (nCustoms
> nCorrections
)
906 // custom menu entries present, so add another separator
907 popup
.AppendMenu(MF_SEPARATOR
);
910 // add found thesauri to sub menu's
914 if (thesaurs
.CreatePopupMenu())
916 if ((pThesaur
)&&(!worda
.IsEmpty()))
920 int count
= pThesaur
->Lookup(worda
, worda
.GetLength(),&pmean
);
924 for (int i
=0; i
< count
; i
++)
926 CMenu
* submenu
= new CMenu();
927 menuArray
.Add(submenu
);
928 submenu
->CreateMenu();
929 for (int j
=0; j
< pm
->count
; j
++)
931 CString sug
= CString(pm
->psyns
[j
]);
932 submenu
->InsertMenu((UINT
)-1, 0, nCorrections
+ nCustoms
+ (nThesaurs
++), sug
);
934 thesaurs
.InsertMenu((UINT
)-1, MF_POPUP
, (UINT_PTR
)(submenu
->m_hMenu
), CString(pm
->defn
));
938 if ((count
> 0)&&(point
.x
>= 0))
940 #ifdef IDS_SPELLEDIT_THESAURUS
941 sMenuItemText
.LoadString(IDS_SPELLEDIT_THESAURUS
);
942 popup
.InsertMenu((UINT
)-1, MF_POPUP
, (UINT_PTR
)thesaurs
.m_hMenu
, sMenuItemText
);
944 popup
.InsertMenu((UINT
)-1, MF_POPUP
, (UINT_PTR
)thesaurs
.m_hMenu
, _T("Thesaurus"));
946 nThesaurs
= nCustoms
;
950 sMenuItemText
.LoadString(IDS_SPELLEDIT_NOTHESAURUS
);
951 popup
.AppendMenu(MF_DISABLED
| MF_GRAYED
| MF_STRING
, 0, sMenuItemText
);
954 pThesaur
->CleanUpAfterLookup(&pmean
, count
);
958 sMenuItemText
.LoadString(IDS_SPELLEDIT_NOTHESAURUS
);
959 popup
.AppendMenu(MF_DISABLED
| MF_GRAYED
| MF_STRING
, 0, sMenuItemText
);
963 int cmd
= popup
.TrackPopupMenu(TPM_RETURNCMD
| TPM_LEFTALIGN
| TPM_NONOTIFY
, point
.x
, point
.y
, this, 0);
967 break; // no command selected
969 bRestoreCursor
= false;
979 m_personalDict
.AddWord(sWord
);
984 int marker
= (int)(Call(SCI_GETEDGECOLUMN
) * Call(SCI_TEXTWIDTH
, 0, (LPARAM
)" "));
987 Call(SCI_TARGETFROMSELECTION
);
989 Call(SCI_LINESSPLIT
, marker
);
994 if (cmd
< nCorrections
)
996 Call(SCI_SETANCHOR
, pointpos
);
997 Call(SCI_SETCURRENTPOS
, pointpos
);
998 GetWordUnderCursor(true);
1000 popup
.GetMenuString(cmd
, temp
, 0);
1001 // setting the cursor clears the selection
1002 Call(SCI_REPLACESEL
, 0, (LPARAM
)(LPCSTR
)StringForControl(temp
));
1004 else if (cmd
< (nCorrections
+nCustoms
))
1006 for (INT_PTR handlerindex
= 0; handlerindex
< m_arContextHandlers
.GetCount(); ++handlerindex
)
1008 CSciEditContextMenuInterface
* pHandler
= m_arContextHandlers
.GetAt(handlerindex
);
1009 if (pHandler
->HandleMenuItemClick(cmd
, this))
1014 else if (cmd
<= (nThesaurs
+nCorrections
+nCustoms
))
1016 Call(SCI_SETANCHOR
, pointpos
);
1017 Call(SCI_SETCURRENTPOS
, pointpos
);
1018 GetWordUnderCursor(true);
1020 thesaurs
.GetMenuString(cmd
, temp
, 0);
1021 Call(SCI_REPLACESEL
, 0, (LPARAM
)(LPCSTR
)StringForControl(temp
));
1026 for (INT_PTR index
= 0; index
< menuArray
.GetCount(); ++index
)
1028 CMenu
* pMenu
= (CMenu
*)menuArray
[index
];
1035 // restore the anchor and cursor position
1036 Call(SCI_SETCURRENTPOS
, currentpos
);
1037 Call(SCI_SETANCHOR
, anchor
);
1041 bool CSciEdit::StyleEnteredText(int startstylepos
, int endstylepos
)
1043 bool bStyled
= false;
1044 const int line
= (int)Call(SCI_LINEFROMPOSITION
, startstylepos
);
1045 const int line_number_end
= (int)Call(SCI_LINEFROMPOSITION
, endstylepos
);
1046 for (int line_number
= line
; line_number
<= line_number_end
; ++line_number
)
1048 int offset
= (int)Call(SCI_POSITIONFROMLINE
, line_number
);
1049 int line_len
= (int)Call(SCI_LINELENGTH
, line_number
);
1050 std::unique_ptr
<char[]> linebuffer(new char[line_len
+1]);
1051 Call(SCI_GETLINE
, line_number
, (LPARAM
)linebuffer
.get());
1052 linebuffer
[line_len
] = 0;
1055 while (FindStyleChars(linebuffer
.get(), '*', start
, end
))
1057 Call(SCI_STARTSTYLING
, start
+offset
, STYLE_MASK
);
1058 Call(SCI_SETSTYLING
, end
-start
, STYLE_BOLD
);
1064 while (FindStyleChars(linebuffer
.get(), '^', start
, end
))
1066 Call(SCI_STARTSTYLING
, start
+offset
, STYLE_MASK
);
1067 Call(SCI_SETSTYLING
, end
-start
, STYLE_ITALIC
);
1073 while (FindStyleChars(linebuffer
.get(), '_', start
, end
))
1075 Call(SCI_STARTSTYLING
, start
+offset
, STYLE_MASK
);
1076 Call(SCI_SETSTYLING
, end
-start
, STYLE_UNDERLINED
);
1084 bool CSciEdit::WrapLines(int startpos
, int endpos
)
1086 int markerX
= (int)(Call(SCI_GETEDGECOLUMN
) * Call(SCI_TEXTWIDTH
, 0, (LPARAM
)" "));
1089 Call(SCI_SETTARGETSTART
, startpos
);
1090 Call(SCI_SETTARGETEND
, endpos
);
1091 Call(SCI_LINESSPLIT
, markerX
);
1097 void CSciEdit::AdvanceUTF8(const char * str
, int& pos
)
1099 if ((str
[pos
] & 0xE0)==0xC0)
1101 // utf8 2-byte sequence
1104 else if ((str
[pos
] & 0xF0)==0xE0)
1106 // utf8 3-byte sequence
1109 else if ((str
[pos
] & 0xF8)==0xF0)
1111 // utf8 4-byte sequence
1118 bool CSciEdit::FindStyleChars(const char * line
, char styler
, int& start
, int& end
)
1124 AdvanceUTF8(line
, i
);
1128 bool bFoundMarker
= false;
1129 CString sULine
= CUnicodeUtils::GetUnicode(line
);
1130 // find a starting marker
1131 while (line
[i
] != 0)
1133 if (line
[i
] == styler
)
1135 if ((line
[i
+1]!=0)&&(IsCharAlphaNumeric(sULine
[u
+1]))&&
1136 (((u
>0)&&(!IsCharAlphaNumeric(sULine
[u
-1]))) || (u
==0)))
1139 AdvanceUTF8(line
, i
);
1141 bFoundMarker
= true;
1145 AdvanceUTF8(line
, i
);
1150 // find ending marker
1151 bFoundMarker
= false;
1152 while (line
[i
] != 0)
1154 if (line
[i
] == styler
)
1156 if ((IsCharAlphaNumeric(sULine
[u
-1]))&&
1157 ((((u
+1)<sULine
.GetLength())&&(!IsCharAlphaNumeric(sULine
[u
+1]))) || ((u
+1) == sULine
.GetLength()))
1162 bFoundMarker
= true;
1166 AdvanceUTF8(line
, i
);
1169 return bFoundMarker
;
1172 BOOL
CSciEdit::MarkEnteredBugID(int startstylepos
, int endstylepos
)
1174 if (m_sCommand
.IsEmpty())
1176 // get the text between the start and end position we have to style
1177 const int line_number
= (int)Call(SCI_LINEFROMPOSITION
, startstylepos
);
1178 int start_pos
= (int)Call(SCI_POSITIONFROMLINE
, (WPARAM
)line_number
);
1179 int end_pos
= endstylepos
;
1181 if (start_pos
== end_pos
)
1183 if (start_pos
> end_pos
)
1185 int switchtemp
= start_pos
;
1186 start_pos
= end_pos
;
1187 end_pos
= switchtemp
;
1190 std::unique_ptr
<char[]> textbuffer(new char[end_pos
- start_pos
+ 2]);
1191 TEXTRANGEA textrange
;
1192 textrange
.lpstrText
= textbuffer
.get();
1193 textrange
.chrg
.cpMin
= start_pos
;
1194 textrange
.chrg
.cpMax
= end_pos
;
1195 Call(SCI_GETTEXTRANGE
, 0, (LPARAM
)&textrange
);
1196 CStringA msg
= CStringA(textbuffer
.get());
1198 Call(SCI_STARTSTYLING
, start_pos
, STYLE_MASK
);
1202 if (!m_sBugID
.IsEmpty())
1204 // match with two regex strings (without grouping!)
1205 const std::tr1::regex
regCheck(m_sCommand
);
1206 const std::tr1::regex
regBugID(m_sBugID
);
1207 const std::tr1::sregex_iterator end
;
1208 std::string s
= msg
;
1211 // if start_pos is 0, we're styling from the beginning and let the ^ char match the beginning of the line
1212 // that way, the ^ matches the very beginning of the log message and not the beginning of further lines.
1213 // problem is: this only works *while* entering log messages. If a log message is pasted in whole or
1214 // multiple lines are pasted, start_pos can be 0 and styling goes over multiple lines. In that case, those
1215 // additional line starts also match ^
1216 for (std::tr1::sregex_iterator
it(s
.begin(), s
.end(), regCheck
, start_pos
!= 0 ? std::tr1::regex_constants::match_not_bol
: std::tr1::regex_constants::match_default
); it
!= end
; ++it
)
1218 // clear the styles up to the match position
1219 Call(SCI_SETSTYLING
, it
->position(0)-pos
, STYLE_DEFAULT
);
1221 // (*it)[0] is the matched string
1222 std::string matchedString
= (*it
)[0];
1223 LONG matchedpos
= 0;
1224 for (std::tr1::sregex_iterator
it2(matchedString
.begin(), matchedString
.end(), regBugID
); it2
!= end
; ++it2
)
1226 ATLTRACE("matched id : %s\n", std::string((*it2
)[0]).c_str());
1228 // bold style up to the id match
1229 ATLTRACE("position = %ld\n", it2
->position(0));
1230 if (it2
->position(0))
1231 Call(SCI_SETSTYLING
, it2
->position(0) - matchedpos
, STYLE_ISSUEBOLD
);
1232 // bold and recursive style for the bug ID itself
1233 if ((*it2
)[0].str().size())
1234 Call(SCI_SETSTYLING
, (*it2
)[0].str().size(), STYLE_ISSUEBOLDITALIC
);
1235 matchedpos
= (LONG
)(it2
->position(0) + (*it2
)[0].str().size());
1237 if ((matchedpos
)&&(matchedpos
< (LONG
)matchedString
.size()))
1239 Call(SCI_SETSTYLING
, matchedString
.size() - matchedpos
, STYLE_ISSUEBOLD
);
1241 pos
= (LONG
)(it
->position(0) + matchedString
.size());
1243 // bold style for the rest of the string which isn't matched
1245 Call(SCI_SETSTYLING
, s
.size()-pos
, STYLE_DEFAULT
);
1249 const std::tr1::regex
regCheck(m_sCommand
);
1250 const std::tr1::sregex_iterator end
;
1251 std::string s
= msg
;
1253 for (std::tr1::sregex_iterator
it(s
.begin(), s
.end(), regCheck
); it
!= end
; ++it
)
1255 // clear the styles up to the match position
1256 Call(SCI_SETSTYLING
, it
->position(0)-pos
, STYLE_DEFAULT
);
1257 pos
= (LONG
)it
->position(0);
1259 const std::tr1::smatch match
= *it
;
1260 // we define group 1 as the whole issue text and
1261 // group 2 as the bug ID
1262 if (match
.size() >= 2)
1264 ATLTRACE("matched id : %s\n", std::string(match
[1]).c_str());
1265 Call(SCI_SETSTYLING
, match
[1].first
-s
.begin()-pos
, STYLE_ISSUEBOLD
);
1266 Call(SCI_SETSTYLING
, std::string(match
[1]).size(), STYLE_ISSUEBOLDITALIC
);
1267 pos
= (LONG
)(match
[1].second
-s
.begin());
1272 catch (std::exception
) {}
1277 bool CSciEdit::IsValidURLChar(unsigned char ch
)
1279 return isalnum(ch
) ||
1280 ch
== '_' || ch
== '/' || ch
== ';' || ch
== '?' || ch
== '&' || ch
== '=' ||
1281 ch
== '%' || ch
== ':' || ch
== '.' || ch
== '#' || ch
== '-' || ch
== '+';
1284 void CSciEdit::StyleURLs(int startstylepos
, int endstylepos
)
1286 const int line_number
= (int)Call(SCI_LINEFROMPOSITION
, startstylepos
);
1287 startstylepos
= (int)Call(SCI_POSITIONFROMLINE
, (WPARAM
)line_number
);
1289 int len
= endstylepos
- startstylepos
+ 1;
1290 std::unique_ptr
<char[]> textbuffer(new char[len
+ 1]);
1291 TEXTRANGEA textrange
;
1292 textrange
.lpstrText
= textbuffer
.get();
1293 textrange
.chrg
.cpMin
= startstylepos
;
1294 textrange
.chrg
.cpMax
= endstylepos
;
1295 Call(SCI_GETTEXTRANGE
, 0, (LPARAM
)&textrange
);
1296 // we're dealing with utf8 encoded text here, which means one glyph is
1297 // not necessarily one byte/wchar_t
1298 // that's why we use CStringA to still get a correct char index
1299 CStringA msg
= textbuffer
.get();
1302 for(int i
= 0; i
<= msg
.GetLength(); )
1304 if ((i
< len
) && IsValidURLChar(msg
[i
]))
1311 if ((starturl
>= 0) && IsUrl(msg
.Mid(starturl
, i
- starturl
)))
1313 ASSERT(startstylepos
+ i
<= endstylepos
);
1314 Call(SCI_STARTSTYLING
, startstylepos
+ starturl
, STYLE_MASK
);
1315 Call(SCI_SETSTYLING
, i
- starturl
, STYLE_URL
);
1319 AdvanceUTF8(msg
, i
);
1323 bool CSciEdit::IsUrl(const CStringA
& sText
)
1325 if (!PathIsURLA(sText
))
1327 if (sText
.Find("://")>=0)
1332 bool CSciEdit::IsUTF8(LPVOID pBuffer
, size_t cb
)
1336 UINT16
* pVal
= (UINT16
*)pBuffer
;
1337 UINT8
* pVal2
= (UINT8
*)(pVal
+1);
1338 // scan the whole buffer for a 0x0000 sequence
1339 // if found, we assume a binary file
1340 for (size_t i
=0; i
<(cb
-2); i
=i
+2)
1342 if (0x0000 == *pVal
++)
1345 pVal
= (UINT16
*)pBuffer
;
1346 if (*pVal
== 0xFEFF)
1350 if (*pVal
== 0xBBEF)
1355 // check for illegal UTF8 chars
1356 pVal2
= (UINT8
*)pBuffer
;
1357 for (size_t i
=0; i
<cb
; ++i
)
1359 if ((*pVal2
== 0xC0)||(*pVal2
== 0xC1)||(*pVal2
>= 0xF5))
1363 pVal2
= (UINT8
*)pBuffer
;
1365 for (size_t i
=0; i
<(cb
-3); ++i
)
1367 if ((*pVal2
& 0xE0)==0xC0)
1370 if ((*pVal2
& 0xC0)!=0x80)
1374 if ((*pVal2
& 0xF0)==0xE0)
1377 if ((*pVal2
& 0xC0)!=0x80)
1380 if ((*pVal2
& 0xC0)!=0x80)
1384 if ((*pVal2
& 0xF8)==0xF0)
1387 if ((*pVal2
& 0xC0)!=0x80)
1390 if ((*pVal2
& 0xC0)!=0x80)
1393 if ((*pVal2
& 0xC0)!=0x80)
1404 void CSciEdit::SetAStyle(int style
, COLORREF fore
, COLORREF back
, int size
, const char *face
)
1406 Call(SCI_STYLESETFORE
, style
, fore
);
1407 Call(SCI_STYLESETBACK
, style
, back
);
1409 Call(SCI_STYLESETSIZE
, style
, size
);
1411 Call(SCI_STYLESETFONT
, style
, reinterpret_cast<LPARAM
>(face
));
1415 void CSciEdit::SetUDiffStyle()
1417 SetAStyle(STYLE_DEFAULT
, ::GetSysColor(COLOR_WINDOWTEXT
), ::GetSysColor(COLOR_WINDOW
),
1418 // Reusing TortoiseBlame's setting which already have an user friendly
1419 // pane in TortoiseSVN's Settings dialog, while there is no such
1420 // pane for TortoiseUDiff.
1421 CRegStdDWORD(_T("Software\\TortoiseGit\\BlameFontSize"), 10),
1422 WideToMultibyte(CRegStdString(_T("Software\\TortoiseGit\\BlameFontName"), _T("Courier New"))).c_str());
1424 Call(SCI_SETTABWIDTH
, 4);
1425 Call(SCI_SETREADONLY
, TRUE
);
1426 //LRESULT pix = Call(SCI_TEXTWIDTH, STYLE_LINENUMBER, (LPARAM)"_99999");
1427 //Call(SCI_SETMARGINWIDTHN, 0, pix);
1428 //Call(SCI_SETMARGINWIDTHN, 1);
1429 //Call(SCI_SETMARGINWIDTHN, 2);
1430 //Set the default windows colors for edit controls
1431 Call(SCI_STYLESETFORE
, STYLE_DEFAULT
, ::GetSysColor(COLOR_WINDOWTEXT
));
1432 Call(SCI_STYLESETBACK
, STYLE_DEFAULT
, ::GetSysColor(COLOR_WINDOW
));
1433 Call(SCI_SETSELFORE
, TRUE
, ::GetSysColor(COLOR_HIGHLIGHTTEXT
));
1434 Call(SCI_SETSELBACK
, TRUE
, ::GetSysColor(COLOR_HIGHLIGHT
));
1435 Call(SCI_SETCARETFORE
, ::GetSysColor(COLOR_WINDOWTEXT
));
1437 //SendEditor(SCI_SETREADONLY, FALSE);
1439 Call(EM_EMPTYUNDOBUFFER
);
1440 Call(SCI_SETSAVEPOINT
);
1442 Call(SCI_SETUNDOCOLLECTION
, 0);
1444 Call(SCI_SETUNDOCOLLECTION
, 1);
1445 Call(SCI_SETWRAPMODE
,SC_WRAP_NONE
);
1447 //::SetFocus(m_hWndEdit);
1448 Call(EM_EMPTYUNDOBUFFER
);
1449 Call(SCI_SETSAVEPOINT
);
1450 Call(SCI_GOTOPOS
, 0);
1452 Call(SCI_CLEARDOCUMENTSTYLE
, 0, 0);
1453 Call(SCI_SETSTYLEBITS
, 5, 0);
1455 //SetAStyle(SCE_DIFF_DEFAULT, RGB(0, 0, 0));
1456 SetAStyle(SCE_DIFF_COMMAND
, RGB(0x0A, 0x24, 0x36));
1457 SetAStyle(SCE_DIFF_POSITION
, RGB(0xFF, 0, 0));
1458 SetAStyle(SCE_DIFF_HEADER
, RGB(0x80, 0, 0), RGB(0xFF, 0xFF, 0x80));
1459 SetAStyle(SCE_DIFF_COMMENT
, RGB(0, 0x80, 0));
1460 Call(SCI_STYLESETBOLD
, SCE_DIFF_COMMENT
, TRUE
);
1461 SetAStyle(SCE_DIFF_DELETED
, ::GetSysColor(COLOR_WINDOWTEXT
), RGB(0xFF, 0x80, 0x80));
1462 SetAStyle(SCE_DIFF_ADDED
, ::GetSysColor(COLOR_WINDOWTEXT
), RGB(0x80, 0xFF, 0x80));
1464 Call(SCI_SETLEXER
, SCLEX_DIFF
);
1465 Call(SCI_SETKEYWORDS
, 0, (LPARAM
)"revision");
1466 Call(SCI_COLOURISE
, 0, -1);
1469 int CSciEdit::LoadFromFile(CString
&filename
)
1472 _tfopen_s(&fp
, filename
, _T("rb"));
1476 char data
[4096] = { 0 };
1477 size_t lenFile
= fread(data
, 1, sizeof(data
), fp
);
1478 bool bUTF8
= IsUTF8(data
, lenFile
);
1481 Call(SCI_ADDTEXT
, lenFile
,
1482 reinterpret_cast<LPARAM
>(static_cast<char *>(data
)));
1483 lenFile
= fread(data
, 1, sizeof(data
), fp
);
1486 Call(SCI_SETCODEPAGE
, bUTF8
? SC_CP_UTF8
: GetACP());
1493 void CSciEdit::RestyleBugIDs()
1495 int endstylepos
= (int)Call(SCI_GETLENGTH
);
1497 Call(SCI_STARTSTYLING
, 0, STYLE_MASK
);
1498 Call(SCI_SETSTYLING
, endstylepos
, STYLE_DEFAULT
);
1499 // style the bug IDs
1500 MarkEnteredBugID(0, endstylepos
);