PageRenderTime 61ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/src/Utils/MiscUI/SciEdit.cpp

https://github.com/csware/test
C++ | 1454 lines | 1250 code | 103 blank | 101 comment | 263 complexity | 6bcc3a9fd50dad5f6cc714bd1b04438a MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0, GPL-3.0, LGPL-3.0, MPL-2.0-no-copyleft-exception
  1. // TortoiseGit - a Windows shell extension for easy version control
  2. // Copyright (C) 2003-2008,2012-2013 - TortoiseSVN
  3. // Copyright (C) 2012 - Sven Strickroth <email@cs-ware.de>
  4. // This program is free software; you can redistribute it and/or
  5. // modify it under the terms of the GNU General Public License
  6. // as published by the Free Software Foundation; either version 2
  7. // of the License, or (at your option) any later version.
  8. // This program is distributed in the hope that it will be useful,
  9. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. // GNU General Public License for more details.
  12. // You should have received a copy of the GNU General Public License
  13. // along with this program; if not, write to the Free Software Foundation,
  14. // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  15. //
  16. #include "stdafx.h"
  17. #include "LoglistCommonResource.h"
  18. #include "..\PathUtils.h"
  19. #include "..\UnicodeUtils.h"
  20. #include <string>
  21. #include "..\registry.h"
  22. #include ".\sciedit.h"
  23. #include "SysInfo.h"
  24. using namespace std;
  25. void CSciEditContextMenuInterface::InsertMenuItems(CMenu&, int&) {return;}
  26. bool CSciEditContextMenuInterface::HandleMenuItemClick(int, CSciEdit *) {return false;}
  27. #define STYLE_ISSUEBOLD 11
  28. #define STYLE_ISSUEBOLDITALIC 12
  29. #define STYLE_BOLD 14
  30. #define STYLE_ITALIC 15
  31. #define STYLE_UNDERLINED 16
  32. #define STYLE_URL 17
  33. #define STYLE_MASK 0x1f
  34. #define SCI_ADDWORD 2000
  35. struct loc_map {
  36. const char * cp;
  37. const char * def_enc;
  38. };
  39. struct loc_map enc2locale[] = {
  40. {"28591","ISO8859-1"},
  41. {"28592","ISO8859-2"},
  42. {"28593","ISO8859-3"},
  43. {"28594","ISO8859-4"},
  44. {"28595","ISO8859-5"},
  45. {"28596","ISO8859-6"},
  46. {"28597","ISO8859-7"},
  47. {"28598","ISO8859-8"},
  48. {"28599","ISO8859-9"},
  49. {"28605","ISO8859-15"},
  50. {"20866","KOI8-R"},
  51. {"21866","KOI8-U"},
  52. {"1251","microsoft-cp1251"},
  53. {"65001","UTF-8"},
  54. };
  55. IMPLEMENT_DYNAMIC(CSciEdit, CWnd)
  56. CSciEdit::CSciEdit(void) : m_DirectFunction(NULL)
  57. , m_DirectPointer(NULL)
  58. , pChecker(NULL)
  59. , pThesaur(NULL)
  60. , m_spellcodepage(0)
  61. , m_separator(0)
  62. , m_bDoStyle(false)
  63. , m_nAutoCompleteMinChars(3)
  64. {
  65. m_hModule = ::LoadLibrary(_T("SciLexer.DLL"));
  66. }
  67. CSciEdit::~CSciEdit(void)
  68. {
  69. m_personalDict.Save();
  70. if (m_hModule)
  71. ::FreeLibrary(m_hModule);
  72. if (pChecker)
  73. delete pChecker;
  74. if (pThesaur)
  75. delete pThesaur;
  76. }
  77. void CSciEdit::Init(LONG lLanguage, BOOL bLoadSpellCheck)
  78. {
  79. //Setup the direct access data
  80. m_DirectFunction = SendMessage(SCI_GETDIRECTFUNCTION, 0, 0);
  81. m_DirectPointer = SendMessage(SCI_GETDIRECTPOINTER, 0, 0);
  82. Call(SCI_SETMARGINWIDTHN, 1, 0);
  83. Call(SCI_SETUSETABS, 0); //pressing TAB inserts spaces
  84. Call(SCI_SETWRAPVISUALFLAGS, SC_WRAPVISUALFLAG_END);
  85. Call(SCI_AUTOCSETIGNORECASE, 1);
  86. Call(SCI_SETLEXER, SCLEX_CONTAINER);
  87. Call(SCI_SETCODEPAGE, SC_CP_UTF8);
  88. Call(SCI_AUTOCSETFILLUPS, 0, (LPARAM)"\t([");
  89. Call(SCI_AUTOCSETMAXWIDTH, 0);
  90. //Set the default windows colors for edit controls
  91. Call(SCI_STYLESETFORE, STYLE_DEFAULT, ::GetSysColor(COLOR_WINDOWTEXT));
  92. Call(SCI_STYLESETBACK, STYLE_DEFAULT, ::GetSysColor(COLOR_WINDOW));
  93. Call(SCI_SETSELFORE, TRUE, ::GetSysColor(COLOR_HIGHLIGHTTEXT));
  94. Call(SCI_SETSELBACK, TRUE, ::GetSysColor(COLOR_HIGHLIGHT));
  95. Call(SCI_SETCARETFORE, ::GetSysColor(COLOR_WINDOWTEXT));
  96. Call(SCI_SETMODEVENTMASK, SC_MOD_INSERTTEXT | SC_MOD_DELETETEXT | SC_PERFORMED_UNDO | SC_PERFORMED_REDO);
  97. Call(SCI_INDICSETFORE, 1, 0x0000FF);
  98. CStringA sWordChars;
  99. CStringA sWhiteSpace;
  100. for (int i=0; i<255; ++i)
  101. {
  102. if (i == '\r' || i == '\n')
  103. continue;
  104. else if (i < 0x20 || i == ' ')
  105. sWhiteSpace += (char)i;
  106. else if (isalnum(i) || i == '\'')
  107. sWordChars += (char)i;
  108. }
  109. Call(SCI_SETWORDCHARS, 0, (LPARAM)(LPCSTR)sWordChars);
  110. Call(SCI_SETWHITESPACECHARS, 0, (LPARAM)(LPCSTR)sWhiteSpace);
  111. m_bDoStyle = ((DWORD)CRegStdDWORD(_T("Software\\TortoiseGit\\StyleCommitMessages"), TRUE))==TRUE;
  112. m_nAutoCompleteMinChars = (int)(DWORD)CRegStdDWORD(_T("Software\\TortoiseGit\\AutoCompleteMinChars"), 3);
  113. // look for dictionary files and use them if found
  114. long langId = GetUserDefaultLCID();
  115. if(bLoadSpellCheck)
  116. {
  117. if ((lLanguage != 0)||(((DWORD)CRegStdDWORD(_T("Software\\TortoiseGit\\Spellchecker"), FALSE))==FALSE))
  118. {
  119. if (!((lLanguage)&&(!LoadDictionaries(lLanguage))))
  120. {
  121. do
  122. {
  123. LoadDictionaries(langId);
  124. DWORD lid = SUBLANGID(langId);
  125. lid--;
  126. if (lid > 0)
  127. {
  128. langId = MAKELANGID(PRIMARYLANGID(langId), lid);
  129. }
  130. else if (langId == 1033)
  131. langId = 0;
  132. else
  133. langId = 1033;
  134. } while ((langId)&&((pChecker==NULL)||(pThesaur==NULL)));
  135. }
  136. }
  137. }
  138. Call(SCI_SETEDGEMODE, EDGE_NONE);
  139. Call(SCI_SETWRAPMODE, SC_WRAP_WORD);
  140. Call(SCI_ASSIGNCMDKEY, SCK_END, SCI_LINEENDWRAP);
  141. Call(SCI_ASSIGNCMDKEY, SCK_END + (SCMOD_SHIFT << 16), SCI_LINEENDWRAPEXTEND);
  142. Call(SCI_ASSIGNCMDKEY, SCK_HOME, SCI_HOMEWRAP);
  143. Call(SCI_ASSIGNCMDKEY, SCK_HOME + (SCMOD_SHIFT << 16), SCI_HOMEWRAPEXTEND);
  144. CRegStdDWORD used2d(L"Software\\TortoiseGit\\ScintillaDirect2D", FALSE);
  145. if (SysInfo::Instance().IsWin7OrLater() && DWORD(used2d))
  146. {
  147. Call(SCI_SETTECHNOLOGY, SC_TECHNOLOGY_DIRECTWRITE);
  148. Call(SCI_SETBUFFEREDDRAW, 0);
  149. }
  150. Call(SCI_SETFONTQUALITY, SC_EFF_QUALITY_DEFAULT);
  151. }
  152. void CSciEdit::Init(const ProjectProperties& props)
  153. {
  154. Init(props.lProjectLanguage);
  155. m_sCommand = CStringA(CUnicodeUtils::GetUTF8(props.sCheckRe));
  156. m_sBugID = CStringA(CUnicodeUtils::GetUTF8(props.sBugIDRe));
  157. m_sUrl = CStringA(CUnicodeUtils::GetUTF8(props.sUrl));
  158. if (props.nLogWidthMarker)
  159. {
  160. Call(SCI_SETWRAPMODE, SC_WRAP_NONE);
  161. Call(SCI_SETEDGEMODE, EDGE_LINE);
  162. Call(SCI_SETEDGECOLUMN, props.nLogWidthMarker);
  163. }
  164. else
  165. {
  166. Call(SCI_SETEDGEMODE, EDGE_NONE);
  167. Call(SCI_SETWRAPMODE, SC_WRAP_WORD);
  168. }
  169. SetText(props.sLogTemplate);
  170. }
  171. BOOL CSciEdit::LoadDictionaries(LONG lLanguageID)
  172. {
  173. //Setup the spell checker and thesaurus
  174. TCHAR buf[6];
  175. CString sFolder = CPathUtils::GetAppDirectory();
  176. CString sFolderUp = CPathUtils::GetAppParentDirectory();
  177. CString sFile;
  178. GetLocaleInfo(MAKELCID(lLanguageID, SORT_DEFAULT), LOCALE_SISO639LANGNAME, buf, _countof(buf));
  179. sFile = buf;
  180. sFile += _T("_");
  181. GetLocaleInfo(MAKELCID(lLanguageID, SORT_DEFAULT), LOCALE_SISO3166CTRYNAME, buf, _countof(buf));
  182. sFile += buf;
  183. if (pChecker==NULL)
  184. {
  185. if ((PathFileExists(sFolder + sFile + _T(".aff"))) &&
  186. (PathFileExists(sFolder + sFile + _T(".dic"))))
  187. {
  188. pChecker = new Hunspell(CStringA(sFolder + sFile + _T(".aff")), CStringA(sFolder + sFile + _T(".dic")));
  189. }
  190. else if ((PathFileExists(sFolder + _T("dic\\") + sFile + _T(".aff"))) &&
  191. (PathFileExists(sFolder + _T("dic\\") + sFile + _T(".dic"))))
  192. {
  193. pChecker = new Hunspell(CStringA(sFolder + _T("dic\\") + sFile + _T(".aff")), CStringA(sFolder + _T("dic\\") + sFile + _T(".dic")));
  194. }
  195. else if ((PathFileExists(sFolderUp + sFile + _T(".aff"))) &&
  196. (PathFileExists(sFolderUp + sFile + _T(".dic"))))
  197. {
  198. pChecker = new Hunspell(CStringA(sFolderUp + sFile + _T(".aff")), CStringA(sFolderUp + sFile + _T(".dic")));
  199. }
  200. else if ((PathFileExists(sFolderUp + _T("dic\\") + sFile + _T(".aff"))) &&
  201. (PathFileExists(sFolderUp + _T("dic\\") + sFile + _T(".dic"))))
  202. {
  203. pChecker = new Hunspell(CStringA(sFolderUp + _T("dic\\") + sFile + _T(".aff")), CStringA(sFolderUp + _T("dic\\") + sFile + _T(".dic")));
  204. }
  205. else if ((PathFileExists(sFolderUp + _T("Languages\\") + sFile + _T(".aff"))) &&
  206. (PathFileExists(sFolderUp + _T("Languages\\") + sFile + _T(".dic"))))
  207. {
  208. pChecker = new Hunspell(CStringA(sFolderUp + _T("Languages\\") + sFile + _T(".aff")), CStringA(sFolderUp + _T("Languages\\") + sFile + _T(".dic")));
  209. }
  210. }
  211. #if THESAURUS
  212. if (pThesaur==NULL)
  213. {
  214. if ((PathFileExists(sFolder + _T("th_") + sFile + _T("_v2.idx"))) &&
  215. (PathFileExists(sFolder + _T("th_") + sFile + _T("_v2.dat"))))
  216. {
  217. pThesaur = new MyThes(CStringA(sFolder + sFile + _T("_v2.idx")), CStringA(sFolder + sFile + _T("_v2.dat")));
  218. }
  219. else if ((PathFileExists(sFolder + _T("dic\\th_") + sFile + _T("_v2.idx"))) &&
  220. (PathFileExists(sFolder + _T("dic\\th_") + sFile + _T("_v2.dat"))))
  221. {
  222. pThesaur = new MyThes(CStringA(sFolder + _T("dic\\") + sFile + _T("_v2.idx")), CStringA(sFolder + _T("dic\\") + sFile + _T("_v2.dat")));
  223. }
  224. else if ((PathFileExists(sFolderUp + _T("th_") + sFile + _T("_v2.idx"))) &&
  225. (PathFileExists(sFolderUp + _T("th_") + sFile + _T("_v2.dat"))))
  226. {
  227. pThesaur = new MyThes(CStringA(sFolderUp + _T("th_") + sFile + _T("_v2.idx")), CStringA(sFolderUp + _T("th_") + sFile + _T("_v2.dat")));
  228. }
  229. else if ((PathFileExists(sFolderUp + _T("dic\\th_") + sFile + _T("_v2.idx"))) &&
  230. (PathFileExists(sFolderUp + _T("dic\\th_") + sFile + _T("_v2.dat"))))
  231. {
  232. pThesaur = new MyThes(CStringA(sFolderUp + _T("dic\\th_") + sFile + _T("_v2.idx")), CStringA(sFolderUp + _T("dic\\th_") + sFile + _T("_v2.dat")));
  233. }
  234. else if ((PathFileExists(sFolderUp + _T("Languages\\th_") + sFile + _T("_v2.idx"))) &&
  235. (PathFileExists(sFolderUp + _T("Languages\\th_") + sFile + _T("_v2.dat"))))
  236. {
  237. pThesaur = new MyThes(CStringA(sFolderUp + _T("Languages\\th_") + sFile + _T("_v2.idx")), CStringA(sFolderUp + _T("Languages\\th_") + sFile + _T("_v2.dat")));
  238. }
  239. }
  240. #endif
  241. if (pChecker)
  242. {
  243. const char * encoding = pChecker->get_dic_encoding();
  244. ATLTRACE(encoding);
  245. int n = _countof(enc2locale);
  246. m_spellcodepage = 0;
  247. for (int i = 0; i < n; i++)
  248. {
  249. if (strcmp(encoding,enc2locale[i].def_enc) == 0)
  250. {
  251. m_spellcodepage = atoi(enc2locale[i].cp);
  252. }
  253. }
  254. m_personalDict.Init(lLanguageID);
  255. }
  256. if ((pThesaur)||(pChecker))
  257. return TRUE;
  258. return FALSE;
  259. }
  260. LRESULT CSciEdit::Call(UINT message, WPARAM wParam, LPARAM lParam)
  261. {
  262. ASSERT(::IsWindow(m_hWnd)); //Window must be valid
  263. ASSERT(m_DirectFunction); //Direct function must be valid
  264. return ((SciFnDirect) m_DirectFunction)(m_DirectPointer, message, wParam, lParam);
  265. }
  266. CString CSciEdit::StringFromControl(const CStringA& text)
  267. {
  268. CString sText;
  269. #ifdef UNICODE
  270. int codepage = (int)Call(SCI_GETCODEPAGE);
  271. int reslen = MultiByteToWideChar(codepage, 0, text, text.GetLength(), 0, 0);
  272. MultiByteToWideChar(codepage, 0, text, text.GetLength(), sText.GetBuffer(reslen+1), reslen+1);
  273. sText.ReleaseBuffer(reslen);
  274. #else
  275. sText = text;
  276. #endif
  277. return sText;
  278. }
  279. CStringA CSciEdit::StringForControl(const CString& text)
  280. {
  281. CStringA sTextA;
  282. #ifdef UNICODE
  283. int codepage = (int)SendMessage(SCI_GETCODEPAGE);
  284. int reslen = WideCharToMultiByte(codepage, 0, text, text.GetLength(), 0, 0, 0, 0);
  285. WideCharToMultiByte(codepage, 0, text, text.GetLength(), sTextA.GetBuffer(reslen), reslen, 0, 0);
  286. sTextA.ReleaseBuffer(reslen);
  287. #else
  288. sTextA = text;
  289. #endif
  290. ATLTRACE("string length %d\n", sTextA.GetLength());
  291. return sTextA;
  292. }
  293. void CSciEdit::SetText(const CString& sText)
  294. {
  295. CStringA sTextA = StringForControl(sText);
  296. Call(SCI_SETTEXT, 0, (LPARAM)(LPCSTR)sTextA);
  297. // Scintilla seems to have problems with strings that
  298. // aren't terminated by a newline char. Once that char
  299. // is there, it can be removed without problems.
  300. // So we add here a newline, then remove it again.
  301. Call(SCI_DOCUMENTEND);
  302. Call(SCI_NEWLINE);
  303. Call(SCI_DELETEBACK);
  304. }
  305. void CSciEdit::InsertText(const CString& sText, bool bNewLine)
  306. {
  307. CStringA sTextA = StringForControl(sText);
  308. Call(SCI_REPLACESEL, 0, (LPARAM)(LPCSTR)sTextA);
  309. if (bNewLine)
  310. Call(SCI_REPLACESEL, 0, (LPARAM)(LPCSTR)"\n");
  311. }
  312. CString CSciEdit::GetText()
  313. {
  314. LRESULT len = Call(SCI_GETTEXT, 0, 0);
  315. CStringA sTextA;
  316. Call(SCI_GETTEXT, (WPARAM)(len + 1), (LPARAM)(LPCSTR)sTextA.GetBuffer((int)len + 1));
  317. sTextA.ReleaseBuffer();
  318. return StringFromControl(sTextA);
  319. }
  320. CString CSciEdit::GetWordUnderCursor(bool bSelectWord)
  321. {
  322. TEXTRANGEA textrange;
  323. int pos = (int)Call(SCI_GETCURRENTPOS);
  324. textrange.chrg.cpMin = (LONG)Call(SCI_WORDSTARTPOSITION, pos, TRUE);
  325. if ((pos == textrange.chrg.cpMin)||(textrange.chrg.cpMin < 0))
  326. return CString();
  327. textrange.chrg.cpMax = (LONG)Call(SCI_WORDENDPOSITION, textrange.chrg.cpMin, TRUE);
  328. char * textbuffer = new char[textrange.chrg.cpMax - textrange.chrg.cpMin + 1];
  329. textrange.lpstrText = textbuffer;
  330. Call(SCI_GETTEXTRANGE, 0, (LPARAM)&textrange);
  331. if (bSelectWord)
  332. {
  333. Call(SCI_SETSEL, textrange.chrg.cpMin, textrange.chrg.cpMax);
  334. }
  335. CString sRet = StringFromControl(textbuffer);
  336. delete [] textbuffer;
  337. return sRet;
  338. }
  339. void CSciEdit::SetFont(CString sFontName, int iFontSizeInPoints)
  340. {
  341. Call(SCI_STYLESETFONT, STYLE_DEFAULT, (LPARAM)(LPCSTR)CStringA(sFontName));
  342. Call(SCI_STYLESETSIZE, STYLE_DEFAULT, iFontSizeInPoints);
  343. Call(SCI_STYLECLEARALL);
  344. LPARAM color = (LPARAM)GetSysColor(COLOR_HIGHLIGHT);
  345. // set the styles for the bug ID strings
  346. Call(SCI_STYLESETBOLD, STYLE_ISSUEBOLD, (LPARAM)TRUE);
  347. Call(SCI_STYLESETFORE, STYLE_ISSUEBOLD, color);
  348. Call(SCI_STYLESETBOLD, STYLE_ISSUEBOLDITALIC, (LPARAM)TRUE);
  349. Call(SCI_STYLESETITALIC, STYLE_ISSUEBOLDITALIC, (LPARAM)TRUE);
  350. Call(SCI_STYLESETFORE, STYLE_ISSUEBOLDITALIC, color);
  351. Call(SCI_STYLESETHOTSPOT, STYLE_ISSUEBOLDITALIC, (LPARAM)TRUE);
  352. // set the formatted text styles
  353. Call(SCI_STYLESETBOLD, STYLE_BOLD, (LPARAM)TRUE);
  354. Call(SCI_STYLESETITALIC, STYLE_ITALIC, (LPARAM)TRUE);
  355. Call(SCI_STYLESETUNDERLINE, STYLE_UNDERLINED, (LPARAM)TRUE);
  356. // set the style for URLs
  357. Call(SCI_STYLESETFORE, STYLE_URL, color);
  358. Call(SCI_STYLESETHOTSPOT, STYLE_URL, (LPARAM)TRUE);
  359. Call(SCI_SETHOTSPOTACTIVEUNDERLINE, (LPARAM)TRUE);
  360. }
  361. void CSciEdit::SetAutoCompletionList(const std::set<CString>& list, const TCHAR separator)
  362. {
  363. //copy the auto completion list.
  364. //SK: instead of creating a copy of that list, we could accept a pointer
  365. //to the list and use that instead. But then the caller would have to make
  366. //sure that the list persists over the lifetime of the control!
  367. m_autolist.clear();
  368. m_autolist = list;
  369. m_separator = separator;
  370. }
  371. BOOL CSciEdit::IsMisspelled(const CString& sWord)
  372. {
  373. // convert the string from the control to the encoding of the spell checker module.
  374. CStringA sWordA;
  375. if (m_spellcodepage)
  376. {
  377. char * buf;
  378. buf = sWordA.GetBuffer(sWord.GetLength()*4 + 1);
  379. int lengthIncTerminator =
  380. WideCharToMultiByte(m_spellcodepage, 0, sWord, -1, buf, sWord.GetLength()*4, NULL, NULL);
  381. sWordA.ReleaseBuffer(lengthIncTerminator-1);
  382. }
  383. else
  384. sWordA = CStringA(sWord);
  385. sWordA.Trim("\'\".,");
  386. // words starting with a digit are treated as correctly spelled
  387. if (_istdigit(sWord.GetAt(0)))
  388. return FALSE;
  389. // words in the personal dictionary are correct too
  390. if (m_personalDict.FindWord(sWord))
  391. return FALSE;
  392. // now we actually check the spelling...
  393. if (!pChecker->spell(sWordA))
  394. {
  395. // the word is marked as misspelled, we now check whether the word
  396. // is maybe a composite identifier
  397. // a composite identifier consists of multiple words, with each word
  398. // separated by a change in lower to uppercase letters
  399. if (sWord.GetLength() > 1)
  400. {
  401. int wordstart = 0;
  402. int wordend = 1;
  403. while (wordend < sWord.GetLength())
  404. {
  405. while ((wordend < sWord.GetLength())&&(!_istupper(sWord[wordend])))
  406. wordend++;
  407. if ((wordstart == 0)&&(wordend == sWord.GetLength()))
  408. {
  409. // words in the auto list are also assumed correctly spelled
  410. if (m_autolist.find(sWord) != m_autolist.end())
  411. return FALSE;
  412. return TRUE;
  413. }
  414. sWordA = CStringA(sWord.Mid(wordstart, wordend-wordstart));
  415. if ((sWordA.GetLength() > 2)&&(!pChecker->spell(sWordA)))
  416. {
  417. return TRUE;
  418. }
  419. wordstart = wordend;
  420. wordend++;
  421. }
  422. }
  423. }
  424. return FALSE;
  425. }
  426. void CSciEdit::CheckSpelling()
  427. {
  428. if (pChecker == NULL)
  429. return;
  430. TEXTRANGEA textrange;
  431. LRESULT firstline = Call(SCI_GETFIRSTVISIBLELINE);
  432. LRESULT lastline = firstline + Call(SCI_LINESONSCREEN);
  433. textrange.chrg.cpMin = (LONG)Call(SCI_POSITIONFROMLINE, firstline);
  434. textrange.chrg.cpMax = (LONG)textrange.chrg.cpMin;
  435. LRESULT lastpos = Call(SCI_POSITIONFROMLINE, lastline) + Call(SCI_LINELENGTH, lastline);
  436. if (lastpos < 0)
  437. lastpos = Call(SCI_GETLENGTH)-textrange.chrg.cpMin;
  438. while (textrange.chrg.cpMax < lastpos)
  439. {
  440. textrange.chrg.cpMin = (LONG)Call(SCI_WORDSTARTPOSITION, textrange.chrg.cpMax+1, TRUE);
  441. if (textrange.chrg.cpMin < textrange.chrg.cpMax)
  442. break;
  443. textrange.chrg.cpMax = (LONG)Call(SCI_WORDENDPOSITION, textrange.chrg.cpMin, TRUE);
  444. if (textrange.chrg.cpMin == textrange.chrg.cpMax)
  445. {
  446. textrange.chrg.cpMax++;
  447. continue;
  448. }
  449. ATLASSERT(textrange.chrg.cpMax >= textrange.chrg.cpMin);
  450. char * textbuffer = new char[textrange.chrg.cpMax - textrange.chrg.cpMin + 2];
  451. SecureZeroMemory(textbuffer, textrange.chrg.cpMax - textrange.chrg.cpMin + 2);
  452. textrange.lpstrText = textbuffer;
  453. textrange.chrg.cpMax++;
  454. Call(SCI_GETTEXTRANGE, 0, (LPARAM)&textrange);
  455. int len = (int)strlen(textrange.lpstrText);
  456. if (len == 0)
  457. {
  458. textrange.chrg.cpMax--;
  459. Call(SCI_GETTEXTRANGE, 0, (LPARAM)&textrange);
  460. len = (int)strlen(textrange.lpstrText);
  461. textrange.chrg.cpMax++;
  462. len++;
  463. }
  464. if (len && textrange.lpstrText[len - 1] == '.')
  465. {
  466. // Try to ignore file names from the auto list.
  467. // Do do this, for each word ending with '.' we extract next word and check
  468. // whether the combined string is present in auto list.
  469. TEXTRANGEA twoWords;
  470. twoWords.chrg.cpMin = (LONG)textrange.chrg.cpMin;
  471. twoWords.chrg.cpMax = (LONG)Call(SCI_WORDENDPOSITION, textrange.chrg.cpMax + 1, TRUE);
  472. twoWords.lpstrText = new char[twoWords.chrg.cpMax - twoWords.chrg.cpMin + 1];
  473. SecureZeroMemory(twoWords.lpstrText, twoWords.chrg.cpMax - twoWords.chrg.cpMin + 1);
  474. Call(SCI_GETTEXTRANGE, 0, (LPARAM)&twoWords);
  475. CString sWord = StringFromControl(twoWords.lpstrText);
  476. delete [] twoWords.lpstrText;
  477. if (m_autolist.find(sWord) != m_autolist.end())
  478. {
  479. //mark word as correct (remove the squiggle line)
  480. Call(SCI_STARTSTYLING, twoWords.chrg.cpMin, INDICS_MASK);
  481. Call(SCI_SETSTYLING, twoWords.chrg.cpMax - twoWords.chrg.cpMin, 0);
  482. textrange.chrg.cpMax = twoWords.chrg.cpMax;
  483. delete [] textbuffer;
  484. continue;
  485. }
  486. }
  487. if (len)
  488. textrange.lpstrText[len - 1] = 0;
  489. textrange.chrg.cpMax--;
  490. if (strlen(textrange.lpstrText) > 0)
  491. {
  492. CString sWord = StringFromControl(textrange.lpstrText);
  493. if ((GetStyleAt(textrange.chrg.cpMin) != STYLE_URL) && IsMisspelled(sWord))
  494. {
  495. //mark word as misspelled
  496. Call(SCI_STARTSTYLING, textrange.chrg.cpMin, INDICS_MASK);
  497. Call(SCI_SETSTYLING, textrange.chrg.cpMax - textrange.chrg.cpMin, INDIC1_MASK);
  498. }
  499. else
  500. {
  501. //mark word as correct (remove the squiggle line)
  502. Call(SCI_STARTSTYLING, textrange.chrg.cpMin, INDICS_MASK);
  503. Call(SCI_SETSTYLING, textrange.chrg.cpMax - textrange.chrg.cpMin, 0);
  504. }
  505. }
  506. delete [] textbuffer;
  507. }
  508. }
  509. void CSciEdit::SuggestSpellingAlternatives()
  510. {
  511. if (pChecker == NULL)
  512. return;
  513. CString word = GetWordUnderCursor(true);
  514. Call(SCI_SETCURRENTPOS, Call(SCI_WORDSTARTPOSITION, Call(SCI_GETCURRENTPOS), TRUE));
  515. if (word.IsEmpty())
  516. return;
  517. char ** wlst;
  518. int ns = pChecker->suggest(&wlst, CStringA(word));
  519. if (ns > 0)
  520. {
  521. CString suggestions;
  522. for (int i=0; i < ns; i++)
  523. {
  524. suggestions += CString(wlst[i]) + m_separator;
  525. free(wlst[i]);
  526. }
  527. free(wlst);
  528. suggestions.TrimRight(m_separator);
  529. if (suggestions.IsEmpty())
  530. return;
  531. Call(SCI_AUTOCSETSEPARATOR, (WPARAM)CStringA(m_separator).GetAt(0));
  532. Call(SCI_AUTOCSETDROPRESTOFWORD, 1);
  533. Call(SCI_AUTOCSHOW, 0, (LPARAM)(LPCSTR)StringForControl(suggestions));
  534. }
  535. }
  536. void CSciEdit::DoAutoCompletion(int nMinPrefixLength)
  537. {
  538. if (m_autolist.empty())
  539. return;
  540. if (Call(SCI_AUTOCACTIVE))
  541. return;
  542. CString word = GetWordUnderCursor();
  543. if (word.GetLength() < nMinPrefixLength)
  544. return; //don't auto complete yet, word is too short
  545. int pos = (int)Call(SCI_GETCURRENTPOS);
  546. if (pos != Call(SCI_WORDENDPOSITION, pos, TRUE))
  547. return; //don't auto complete if we're not at the end of a word
  548. CString sAutoCompleteList;
  549. word.MakeUpper();
  550. for (std::set<CString>::const_iterator lowerit = m_autolist.lower_bound(word);
  551. lowerit != m_autolist.end(); ++lowerit)
  552. {
  553. int compare = word.CompareNoCase(lowerit->Left(word.GetLength()));
  554. if (compare>0)
  555. continue;
  556. else if (compare == 0)
  557. {
  558. sAutoCompleteList += *lowerit + m_separator;
  559. }
  560. else
  561. {
  562. break;
  563. }
  564. }
  565. sAutoCompleteList.TrimRight(m_separator);
  566. if (sAutoCompleteList.IsEmpty())
  567. return;
  568. Call(SCI_AUTOCSETSEPARATOR, (WPARAM)CStringA(m_separator).GetAt(0));
  569. Call(SCI_AUTOCSHOW, word.GetLength(), (LPARAM)(LPCSTR)StringForControl(sAutoCompleteList));
  570. }
  571. BOOL CSciEdit::OnChildNotify(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pLResult)
  572. {
  573. if (message != WM_NOTIFY)
  574. return CWnd::OnChildNotify(message, wParam, lParam, pLResult);
  575. LPNMHDR lpnmhdr = (LPNMHDR) lParam;
  576. SCNotification * lpSCN = (SCNotification *)lParam;
  577. if(lpnmhdr->hwndFrom==m_hWnd)
  578. {
  579. switch(lpnmhdr->code)
  580. {
  581. case SCN_CHARADDED:
  582. {
  583. if ((lpSCN->ch < 32)&&(lpSCN->ch != 13)&&(lpSCN->ch != 10))
  584. Call(SCI_DELETEBACK);
  585. else
  586. {
  587. DoAutoCompletion(m_nAutoCompleteMinChars);
  588. }
  589. return TRUE;
  590. }
  591. break;
  592. case SCN_STYLENEEDED:
  593. {
  594. int startstylepos = (int)Call(SCI_GETENDSTYLED);
  595. int endstylepos = ((SCNotification *)lpnmhdr)->position;
  596. MarkEnteredBugID(startstylepos, endstylepos);
  597. if (m_bDoStyle)
  598. StyleEnteredText(startstylepos, endstylepos);
  599. StyleURLs(startstylepos, endstylepos);
  600. CheckSpelling();
  601. WrapLines(startstylepos, endstylepos);
  602. return TRUE;
  603. }
  604. break;
  605. case SCN_HOTSPOTCLICK:
  606. {
  607. TEXTRANGEA textrange;
  608. textrange.chrg.cpMin = lpSCN->position;
  609. textrange.chrg.cpMax = lpSCN->position;
  610. DWORD style = GetStyleAt(lpSCN->position);
  611. while (GetStyleAt(textrange.chrg.cpMin - 1) == style)
  612. --textrange.chrg.cpMin;
  613. while (GetStyleAt(textrange.chrg.cpMax + 1) == style)
  614. ++textrange.chrg.cpMax;
  615. ++textrange.chrg.cpMax;
  616. char * textbuffer = new char[textrange.chrg.cpMax - textrange.chrg.cpMin + 1];
  617. textrange.lpstrText = textbuffer;
  618. Call(SCI_GETTEXTRANGE, 0, (LPARAM)&textrange);
  619. CString url;
  620. if (style == STYLE_URL)
  621. url = StringFromControl(textbuffer);
  622. else
  623. {
  624. url = m_sUrl;
  625. url.Replace(_T("%BUGID%"), StringFromControl(textbuffer));
  626. }
  627. delete [] textbuffer;
  628. if (!url.IsEmpty())
  629. ShellExecute(GetParent()->GetSafeHwnd(), _T("open"), url, NULL, NULL, SW_SHOWDEFAULT);
  630. }
  631. break;
  632. }
  633. }
  634. return CWnd::OnChildNotify(message, wParam, lParam, pLResult);
  635. }
  636. BEGIN_MESSAGE_MAP(CSciEdit, CWnd)
  637. ON_WM_KEYDOWN()
  638. ON_WM_CONTEXTMENU()
  639. END_MESSAGE_MAP()
  640. void CSciEdit::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
  641. {
  642. switch (nChar)
  643. {
  644. case (VK_ESCAPE):
  645. {
  646. if ((Call(SCI_AUTOCACTIVE)==0)&&(Call(SCI_CALLTIPACTIVE)==0))
  647. ::SendMessage(GetParent()->GetSafeHwnd(), WM_CLOSE, 0, 0);
  648. }
  649. break;
  650. }
  651. CWnd::OnKeyDown(nChar, nRepCnt, nFlags);
  652. }
  653. BOOL CSciEdit::PreTranslateMessage(MSG* pMsg)
  654. {
  655. if (pMsg->message == WM_KEYDOWN)
  656. {
  657. switch (pMsg->wParam)
  658. {
  659. case VK_SPACE:
  660. {
  661. if (GetKeyState(VK_CONTROL) & 0x8000)
  662. {
  663. DoAutoCompletion(1);
  664. return TRUE;
  665. }
  666. }
  667. break;
  668. case VK_TAB:
  669. // The TAB cannot be handled in OnKeyDown because it is too late by then.
  670. {
  671. if (GetKeyState(VK_CONTROL)&0x8000)
  672. {
  673. //Ctrl-Tab was pressed, this means we should provide the user with
  674. //a list of possible spell checking alternatives to the word under
  675. //the cursor
  676. SuggestSpellingAlternatives();
  677. return TRUE;
  678. }
  679. else if (!Call(SCI_AUTOCACTIVE))
  680. {
  681. ::PostMessage(GetParent()->GetSafeHwnd(), WM_NEXTDLGCTL, GetKeyState(VK_SHIFT)&0x8000, 0);
  682. return TRUE;
  683. }
  684. }
  685. break;
  686. }
  687. }
  688. return CWnd::PreTranslateMessage(pMsg);
  689. }
  690. void CSciEdit::OnContextMenu(CWnd* /*pWnd*/, CPoint point)
  691. {
  692. int anchor = (int)Call(SCI_GETANCHOR);
  693. int currentpos = (int)Call(SCI_GETCURRENTPOS);
  694. int selstart = (int)Call(SCI_GETSELECTIONSTART);
  695. int selend = (int)Call(SCI_GETSELECTIONEND);
  696. int pointpos = 0;
  697. if ((point.x == -1) && (point.y == -1))
  698. {
  699. CRect rect;
  700. GetClientRect(&rect);
  701. ClientToScreen(&rect);
  702. point = rect.CenterPoint();
  703. pointpos = (int)Call(SCI_GETCURRENTPOS);
  704. }
  705. else
  706. {
  707. // change the cursor position to the point where the user
  708. // right-clicked.
  709. CPoint clientpoint = point;
  710. ScreenToClient(&clientpoint);
  711. pointpos = (int)Call(SCI_POSITIONFROMPOINT, clientpoint.x, clientpoint.y);
  712. }
  713. CString sMenuItemText;
  714. CMenu popup;
  715. bool bRestoreCursor = true;
  716. if (popup.CreatePopupMenu())
  717. {
  718. bool bCanUndo = !!Call(SCI_CANUNDO);
  719. bool bCanRedo = !!Call(SCI_CANREDO);
  720. bool bHasSelection = (selend-selstart > 0);
  721. bool bCanPaste = !!Call(SCI_CANPASTE);
  722. bool bIsReadOnly = !!Call(SCI_GETREADONLY);
  723. UINT uEnabledMenu = MF_STRING | MF_ENABLED;
  724. UINT uDisabledMenu = MF_STRING | MF_GRAYED;
  725. // find the word under the cursor
  726. CString sWord;
  727. if (pointpos)
  728. {
  729. // setting the cursor clears the selection
  730. Call(SCI_SETANCHOR, pointpos);
  731. Call(SCI_SETCURRENTPOS, pointpos);
  732. sWord = GetWordUnderCursor();
  733. // restore the selection
  734. Call(SCI_SETSELECTIONSTART, selstart);
  735. Call(SCI_SETSELECTIONEND, selend);
  736. }
  737. else
  738. sWord = GetWordUnderCursor();
  739. CStringA worda = CStringA(sWord);
  740. int nCorrections = 1;
  741. bool bSpellAdded = false;
  742. // check if the word under the cursor is spelled wrong
  743. if ((pChecker)&&(!worda.IsEmpty()) && !bIsReadOnly)
  744. {
  745. char ** wlst;
  746. // get the spell suggestions
  747. int ns = pChecker->suggest(&wlst,worda);
  748. if (ns > 0)
  749. {
  750. // add the suggestions to the context menu
  751. for (int i=0; i < ns; i++)
  752. {
  753. bSpellAdded = true;
  754. CString sug = CString(wlst[i]);
  755. popup.InsertMenu((UINT)-1, 0, nCorrections++, sug);
  756. free(wlst[i]);
  757. }
  758. free(wlst);
  759. }
  760. }
  761. // only add a separator if spelling correction suggestions were added
  762. if (bSpellAdded)
  763. popup.AppendMenu(MF_SEPARATOR);
  764. // also allow the user to add the word to the custom dictionary so
  765. // it won't show up as misspelled anymore
  766. if ((sWord.GetLength()<PDICT_MAX_WORD_LENGTH)&&((pChecker)&&(m_autolist.find(sWord) == m_autolist.end())&&(!pChecker->spell(worda)))&&
  767. (!_istdigit(sWord.GetAt(0)))&&(!m_personalDict.FindWord(sWord)) && !bIsReadOnly)
  768. {
  769. sMenuItemText.Format(IDS_SCIEDIT_ADDWORD, sWord);
  770. popup.AppendMenu(uEnabledMenu, SCI_ADDWORD, sMenuItemText);
  771. // another separator
  772. popup.AppendMenu(MF_SEPARATOR);
  773. }
  774. // add the 'default' entries
  775. sMenuItemText.LoadString(IDS_SCIEDIT_UNDO);
  776. popup.AppendMenu(bCanUndo ? uEnabledMenu : uDisabledMenu, SCI_UNDO, sMenuItemText);
  777. sMenuItemText.LoadString(IDS_SCIEDIT_REDO);
  778. popup.AppendMenu(bCanRedo ? uEnabledMenu : uDisabledMenu, SCI_REDO, sMenuItemText);
  779. popup.AppendMenu(MF_SEPARATOR);
  780. sMenuItemText.LoadString(IDS_SCIEDIT_CUT);
  781. popup.AppendMenu(bHasSelection ? uEnabledMenu : uDisabledMenu, SCI_CUT, sMenuItemText);
  782. sMenuItemText.LoadString(IDS_SCIEDIT_COPY);
  783. popup.AppendMenu(bHasSelection ? uEnabledMenu : uDisabledMenu, SCI_COPY, sMenuItemText);
  784. sMenuItemText.LoadString(IDS_SCIEDIT_PASTE);
  785. popup.AppendMenu(bCanPaste ? uEnabledMenu : uDisabledMenu, SCI_PASTE, sMenuItemText);
  786. popup.AppendMenu(MF_SEPARATOR);
  787. sMenuItemText.LoadString(IDS_SCIEDIT_SELECTALL);
  788. popup.AppendMenu(uEnabledMenu, SCI_SELECTALL, sMenuItemText);
  789. popup.AppendMenu(MF_SEPARATOR);
  790. sMenuItemText.LoadString(IDS_SCIEDIT_SPLITLINES);
  791. popup.AppendMenu(bHasSelection ? uEnabledMenu : uDisabledMenu, SCI_LINESSPLIT, sMenuItemText);
  792. popup.AppendMenu(MF_SEPARATOR);
  793. int nCustoms = nCorrections;
  794. // now add any custom context menus
  795. for (INT_PTR handlerindex = 0; handlerindex < m_arContextHandlers.GetCount(); ++handlerindex)
  796. {
  797. CSciEditContextMenuInterface * pHandler = m_arContextHandlers.GetAt(handlerindex);
  798. pHandler->InsertMenuItems(popup, nCustoms);
  799. }
  800. if (nCustoms > nCorrections)
  801. {
  802. // custom menu entries present, so add another separator
  803. popup.AppendMenu(MF_SEPARATOR);
  804. }
  805. #if THESAURUS
  806. // add found thesauri to sub menu's
  807. CMenu thesaurs;
  808. int nThesaurs = 0;
  809. CPtrArray menuArray;
  810. if (thesaurs.CreatePopupMenu())
  811. {
  812. if ((pThesaur)&&(!worda.IsEmpty()))
  813. {
  814. mentry * pmean;
  815. worda.MakeLower();
  816. int count = pThesaur->Lookup(worda, worda.GetLength(),&pmean);
  817. if (count)
  818. {
  819. mentry * pm = pmean;
  820. for (int i=0; i < count; i++)
  821. {
  822. CMenu * submenu = new CMenu();
  823. menuArray.Add(submenu);
  824. submenu->CreateMenu();
  825. for (int j=0; j < pm->count; j++)
  826. {
  827. CString sug = CString(pm->psyns[j]);
  828. submenu->InsertMenu((UINT)-1, 0, nThesaurs++, sug);
  829. }
  830. thesaurs.InsertMenu((UINT)-1, MF_POPUP, (UINT_PTR)(submenu->m_hMenu), CString(pm->defn));
  831. pm++;
  832. }
  833. }
  834. if ((count > 0)&&(point.x >= 0))
  835. {
  836. #ifdef IDS_SPELLEDIT_THESAURUS
  837. sMenuItemText.LoadString(IDS_SPELLEDIT_THESAURUS);
  838. popup.InsertMenu((UINT)-1, MF_POPUP, (UINT_PTR)thesaurs.m_hMenu, sMenuItemText);
  839. #else
  840. popup.InsertMenu((UINT)-1, MF_POPUP, (UINT_PTR)thesaurs.m_hMenu, _T("Thesaurus"));
  841. #endif
  842. nThesaurs = nCustoms;
  843. }
  844. else
  845. {
  846. sMenuItemText.LoadString(IDS_SPELLEDIT_NOTHESAURUS);
  847. popup.AppendMenu(MF_DISABLED | MF_GRAYED | MF_STRING, 0, sMenuItemText);
  848. }
  849. pThesaur->CleanUpAfterLookup(&pmean, count);
  850. }
  851. else
  852. {
  853. sMenuItemText.LoadString(IDS_SPELLEDIT_NOTHESAURUS);
  854. popup.AppendMenu(MF_DISABLED | MF_GRAYED | MF_STRING, 0, sMenuItemText);
  855. }
  856. }
  857. #endif
  858. int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this, 0);
  859. switch (cmd)
  860. {
  861. case 0:
  862. break; // no command selected
  863. case SCI_SELECTALL:
  864. bRestoreCursor = false;
  865. // fall through
  866. case SCI_UNDO:
  867. case SCI_REDO:
  868. case SCI_CUT:
  869. case SCI_COPY:
  870. case SCI_PASTE:
  871. Call(cmd);
  872. break;
  873. case SCI_ADDWORD:
  874. m_personalDict.AddWord(sWord);
  875. CheckSpelling();
  876. break;
  877. case SCI_LINESSPLIT:
  878. {
  879. int marker = (int)(Call(SCI_GETEDGECOLUMN) * Call(SCI_TEXTWIDTH, 0, (LPARAM)" "));
  880. if (marker)
  881. {
  882. Call(SCI_TARGETFROMSELECTION);
  883. Call(SCI_LINESJOIN);
  884. Call(SCI_LINESSPLIT, marker);
  885. }
  886. }
  887. break;
  888. default:
  889. if (cmd < nCorrections)
  890. {
  891. Call(SCI_SETANCHOR, pointpos);
  892. Call(SCI_SETCURRENTPOS, pointpos);
  893. GetWordUnderCursor(true);
  894. CString temp;
  895. popup.GetMenuString(cmd, temp, 0);
  896. // setting the cursor clears the selection
  897. Call(SCI_REPLACESEL, 0, (LPARAM)(LPCSTR)StringForControl(temp));
  898. }
  899. else if (cmd < (nCorrections+nCustoms))
  900. {
  901. for (INT_PTR handlerindex = 0; handlerindex < m_arContextHandlers.GetCount(); ++handlerindex)
  902. {
  903. CSciEditContextMenuInterface * pHandler = m_arContextHandlers.GetAt(handlerindex);
  904. if (pHandler->HandleMenuItemClick(cmd, this))
  905. break;
  906. }
  907. }
  908. #if THESAURUS
  909. else if (cmd <= (nThesaurs+nCorrections+nCustoms))
  910. {
  911. Call(SCI_SETANCHOR, pointpos);
  912. Call(SCI_SETCURRENTPOS, pointpos);
  913. GetWordUnderCursor(true);
  914. CString temp;
  915. thesaurs.GetMenuString(cmd, temp, 0);
  916. Call(SCI_REPLACESEL, 0, (LPARAM)(LPCSTR)StringForControl(temp));
  917. }
  918. #endif
  919. }
  920. #ifdef THESAURUS
  921. for (INT_PTR index = 0; index < menuArray.GetCount(); ++index)
  922. {
  923. CMenu * pMenu = (CMenu*)menuArray[index];
  924. delete pMenu;
  925. }
  926. #endif
  927. }
  928. if (bRestoreCursor)
  929. {
  930. // restore the anchor and cursor position
  931. Call(SCI_SETCURRENTPOS, currentpos);
  932. Call(SCI_SETANCHOR, anchor);
  933. }
  934. }
  935. bool CSciEdit::StyleEnteredText(int startstylepos, int endstylepos)
  936. {
  937. bool bStyled = false;
  938. const int line = (int)Call(SCI_LINEFROMPOSITION, startstylepos);
  939. const int line_number_end = (int)Call(SCI_LINEFROMPOSITION, endstylepos);
  940. for (int line_number = line; line_number <= line_number_end; ++line_number)
  941. {
  942. int offset = (int)Call(SCI_POSITIONFROMLINE, line_number);
  943. int line_len = (int)Call(SCI_LINELENGTH, line_number);
  944. char * linebuffer = new char[line_len+1];
  945. Call(SCI_GETLINE, line_number, (LPARAM)linebuffer);
  946. linebuffer[line_len] = 0;
  947. int start = 0;
  948. int end = 0;
  949. while (FindStyleChars(linebuffer, '*', start, end))
  950. {
  951. Call(SCI_STARTSTYLING, start+offset, STYLE_MASK);
  952. Call(SCI_SETSTYLING, end-start, STYLE_BOLD);
  953. bStyled = true;
  954. start = end;
  955. }
  956. start = 0;
  957. end = 0;
  958. while (FindStyleChars(linebuffer, '^', start, end))
  959. {
  960. Call(SCI_STARTSTYLING, start+offset, STYLE_MASK);
  961. Call(SCI_SETSTYLING, end-start, STYLE_ITALIC);
  962. bStyled = true;
  963. start = end;
  964. }
  965. start = 0;
  966. end = 0;
  967. while (FindStyleChars(linebuffer, '_', start, end))
  968. {
  969. Call(SCI_STARTSTYLING, start+offset, STYLE_MASK);
  970. Call(SCI_SETSTYLING, end-start, STYLE_UNDERLINED);
  971. bStyled = true;
  972. start = end;
  973. }
  974. delete [] linebuffer;
  975. }
  976. return bStyled;
  977. }
  978. bool CSciEdit::WrapLines(int startpos, int endpos)
  979. {
  980. int markerX = (int)(Call(SCI_GETEDGECOLUMN) * Call(SCI_TEXTWIDTH, 0, (LPARAM)" "));
  981. if (markerX)
  982. {
  983. Call(SCI_SETTARGETSTART, startpos);
  984. Call(SCI_SETTARGETEND, endpos);
  985. Call(SCI_LINESSPLIT, markerX);
  986. return true;
  987. }
  988. return false;
  989. }
  990. void CSciEdit::AdvanceUTF8(const char * str, int& pos)
  991. {
  992. if ((str[pos] & 0xE0)==0xC0)
  993. {
  994. // utf8 2-byte sequence
  995. pos += 2;
  996. }
  997. else if ((str[pos] & 0xF0)==0xE0)
  998. {
  999. // utf8 3-byte sequence
  1000. pos += 3;
  1001. }
  1002. else if ((str[pos] & 0xF8)==0xF0)
  1003. {
  1004. // utf8 4-byte sequence
  1005. pos += 4;
  1006. }
  1007. else
  1008. pos++;
  1009. }
  1010. bool CSciEdit::FindStyleChars(const char * line, char styler, int& start, int& end)
  1011. {
  1012. int i=0;
  1013. int u=0;
  1014. while (i < start)
  1015. {
  1016. AdvanceUTF8(line, i);
  1017. u++;
  1018. }
  1019. bool bFoundMarker = false;
  1020. CString sULine = CUnicodeUtils::GetUnicode(line);
  1021. // find a starting marker
  1022. while (line[i] != 0)
  1023. {
  1024. if (line[i] == styler)
  1025. {
  1026. if ((line[i+1]!=0)&&(IsCharAlphaNumeric(sULine[u+1]))&&
  1027. (((u>0)&&(!IsCharAlphaNumeric(sULine[u-1]))) || (u==0)))
  1028. {
  1029. start = i+1;
  1030. AdvanceUTF8(line, i);
  1031. u++;
  1032. bFoundMarker = true;
  1033. break;
  1034. }
  1035. }
  1036. AdvanceUTF8(line, i);
  1037. u++;
  1038. }
  1039. if (!bFoundMarker)
  1040. return false;
  1041. // find ending marker
  1042. bFoundMarker = false;
  1043. while (line[i] != 0)
  1044. {
  1045. if (line[i] == styler)
  1046. {
  1047. if ((IsCharAlphaNumeric(sULine[u-1]))&&
  1048. ((((u+1)<sULine.GetLength())&&(!IsCharAlphaNumeric(sULine[u+1]))) || ((u+1) == sULine.GetLength()))
  1049. )
  1050. {
  1051. end = i;
  1052. i++;
  1053. bFoundMarker = true;
  1054. break;
  1055. }
  1056. }
  1057. AdvanceUTF8(line, i);
  1058. u++;
  1059. }
  1060. return bFoundMarker;
  1061. }
  1062. BOOL CSciEdit::MarkEnteredBugID(int startstylepos, int endstylepos)
  1063. {
  1064. if (m_sCommand.IsEmpty())
  1065. return FALSE;
  1066. // get the text between the start and end position we have to style
  1067. const int line_number = (int)Call(SCI_LINEFROMPOSITION, startstylepos);
  1068. int start_pos = (int)Call(SCI_POSITIONFROMLINE, (WPARAM)line_number);
  1069. int end_pos = endstylepos;
  1070. if (start_pos == end_pos)
  1071. return FALSE;
  1072. if (start_pos > end_pos)
  1073. {
  1074. int switchtemp = start_pos;
  1075. start_pos = end_pos;
  1076. end_pos = switchtemp;
  1077. }
  1078. char * textbuffer = new char[end_pos - start_pos + 2];
  1079. TEXTRANGEA textrange;
  1080. textrange.lpstrText = textbuffer;
  1081. textrange.chrg.cpMin = start_pos;
  1082. textrange.chrg.cpMax = end_pos;
  1083. Call(SCI_GETTEXTRANGE, 0, (LPARAM)&textrange);
  1084. CStringA msg = CStringA(textbuffer);
  1085. Call(SCI_STARTSTYLING, start_pos, STYLE_MASK);
  1086. if (!m_sBugID.IsEmpty())
  1087. {
  1088. // match with two regex strings (without grouping!)
  1089. try
  1090. {
  1091. const tr1::regex regCheck(m_sCommand);
  1092. const tr1::regex regBugID(m_sBugID);
  1093. const tr1::sregex_iterator end;
  1094. string s = msg;
  1095. LONG pos = 0;
  1096. for (tr1::sregex_iterator it(s.begin(), s.end(), regCheck); it != end; ++it)
  1097. {
  1098. // clear the styles up to the match position
  1099. Call(SCI_SETSTYLING, it->position(0)-pos, STYLE_DEFAULT);
  1100. pos = (LONG)it->position(0);
  1101. // (*it)[0] is the matched string
  1102. string matchedString = (*it)[0];
  1103. for (tr1::sregex_iterator it2(matchedString.begin(), matchedString.end(), regBugID); it2 != end; ++it2)
  1104. {
  1105. ATLTRACE(_T("matched id : %s\n"), (*it2)[0].str().c_str());
  1106. // bold style up to the id match
  1107. ATLTRACE("position = %ld\n", it2->position(0));
  1108. if (it2->position(0))
  1109. Call(SCI_SETSTYLING, it2->position(0), STYLE_ISSUEBOLD);
  1110. // bold and recursive style for the bug ID itself
  1111. if ((*it2)[0].str().size())
  1112. Call(SCI_SETSTYLING, (*it2)[0].str().size(), STYLE_ISSUEBOLDITALIC);
  1113. }
  1114. pos = (LONG)(it->position(0) + matchedString.size());
  1115. }
  1116. // bold style for the rest of the string which isn't matched
  1117. if (s.size()-pos)
  1118. Call(SCI_SETSTYLING, s.size()-pos, STYLE_DEFAULT);
  1119. }
  1120. catch (exception) {}
  1121. }
  1122. else
  1123. {
  1124. try
  1125. {
  1126. const tr1::regex regCheck(m_sCommand);
  1127. const tr1::sregex_iterator end;
  1128. string s = msg;
  1129. LONG pos = 0;
  1130. for (tr1::sregex_iterator it(s.begin(), s.end(), regCheck); it != end; ++it)
  1131. {
  1132. // clear the styles up to the match position
  1133. Call(SCI_SETSTYLING, it->position(0)-pos, STYLE_DEFAULT);
  1134. pos = (LONG)it->position(0);
  1135. const tr1::smatch match = *it;
  1136. // we define group 1 as the whole issue text and
  1137. // group 2 as the bug ID
  1138. if (match.size() >= 2)
  1139. {
  1140. ATLTRACE(_T("matched id : %s\n"), string(match[1]).c_str());
  1141. Call(SCI_SETSTYLING, match[1].first-s.begin()-pos, STYLE_ISSUEBOLD);
  1142. Call(SCI_SETSTYLING, string(match[1]).size(), STYLE_ISSUEBOLDITALIC);
  1143. pos = (LONG)(match[1].second-s.begin());
  1144. }
  1145. }
  1146. }
  1147. catch (exception) {}
  1148. }
  1149. delete [] textbuffer;
  1150. return FALSE;
  1151. }
  1152. bool CSciEdit::IsValidURLChar(unsigned char ch)
  1153. {
  1154. return isalnum(ch) ||
  1155. ch == '_' || ch == '/' || ch == ';' || ch == '?' || ch == '&' || ch == '=' ||
  1156. ch == '%' || ch == ':' || ch == '.' || ch == '#' || ch == '-' || ch == '+';
  1157. }
  1158. void CSciEdit::StyleURLs(int startstylepos, int endstylepos)
  1159. {
  1160. const int line_number = (int)Call(SCI_LINEFROMPOSITION, startstylepos);
  1161. startstylepos = (int)Call(SCI_POSITIONFROMLINE, (WPARAM)line_number);
  1162. int len = endstylepos - startstylepos + 1;
  1163. char* textbuffer = new char[len + 1];
  1164. TEXTRANGEA textrange;
  1165. textrange.lpstrText = textbuffer;
  1166. textrange.chrg.cpMin = startstylepos;
  1167. textrange.chrg.cpMax = endstylepos;
  1168. Call(SCI_GETTEXTRANGE, 0, (LPARAM)&textrange);
  1169. // we're dealing with utf8 encoded text here, which means one glyph is
  1170. // not necessarily one byte/wchar_t
  1171. // that's why we use CStringA to still get a correct char index
  1172. CStringA msg = textbuffer;
  1173. delete [] textbuffer;
  1174. int starturl = -1;
  1175. for(int i = 0; i <= msg.GetLength(); )
  1176. {
  1177. if ((i < len) && IsValidURLChar(msg[i]))
  1178. {
  1179. if (starturl < 0)
  1180. starturl = i;
  1181. }
  1182. else
  1183. {
  1184. if ((starturl >= 0) && IsUrl(msg.Mid(starturl, i - starturl)))
  1185. {
  1186. ASSERT(startstylepos + i <= endstylepos);
  1187. Call(SCI_STARTSTYLING, startstylepos + starturl, STYLE_MASK);
  1188. Call(SCI_SETSTYLING, i - starturl, STYLE_URL);
  1189. }
  1190. starturl = -1;
  1191. }
  1192. AdvanceUTF8(msg, i);
  1193. }
  1194. }
  1195. bool CSciEdit::IsUrl(const CStringA& sText)
  1196. {
  1197. if (!PathIsURLA(sText))
  1198. return false;
  1199. if (sText.Find("://")>=0)
  1200. return true;
  1201. return false;
  1202. }
  1203. bool CSciEdit::IsUTF8(LPVOID pBuffer, size_t cb)
  1204. {
  1205. if (cb < 2)
  1206. return true;
  1207. UINT16 * pVal = (UINT16 *)pBuffer;
  1208. UINT8 * pVal2 = (UINT8 *)(pVal+1);
  1209. // scan the whole buffer for a 0x0000 sequence
  1210. // if found, we assume a binary file
  1211. for (size_t i=0; i<(cb-2); i=i+2)
  1212. {
  1213. if (0x0000 == *pVal++)
  1214. return false;
  1215. }
  1216. pVal = (UINT16 *)pBuffer;
  1217. if (*pVal == 0xFEFF)
  1218. return false;
  1219. if (cb < 3)
  1220. return false;
  1221. if (*pVal == 0xBBEF)
  1222. {
  1223. if (*pVal2 == 0xBF)
  1224. return true;
  1225. }
  1226. // check for illegal UTF8 chars
  1227. pVal2 = (UINT8 *)pBuffer;
  1228. for (size_t i=0; i<cb; ++i)
  1229. {
  1230. if ((*pVal2 == 0xC0)||(*pVal2 == 0xC1)||(*pVal2 >= 0xF5))
  1231. return false;
  1232. pVal2++;
  1233. }
  1234. pVal2 = (UINT8 *)pBuffer;
  1235. bool bUTF8 = false;
  1236. for (size_t i=0; i<(cb-3); ++i)
  1237. {
  1238. if ((*pVal2 & 0xE0)==0xC0)
  1239. {
  1240. pVal2++;i++;
  1241. if ((*pVal2 & 0xC0)!=0x80)
  1242. return false;
  1243. bUTF8 = true;
  1244. }
  1245. if ((*pVal2 & 0xF0)==0xE0)
  1246. {
  1247. pVal2++;i++;
  1248. if ((*pVal2 & 0xC0)!=0x80)
  1249. return false;
  1250. pVal2++;i++;
  1251. if ((*pVal2 & 0xC0)!=0x80)
  1252. return false;
  1253. bUTF8 = true;
  1254. }
  1255. if ((*pVal2 & 0xF8)==0xF0)
  1256. {
  1257. pVal2++;i++;
  1258. if ((*pVal2 & 0xC0)!=0x80)
  1259. return false;
  1260. pVal2++;i++;
  1261. if ((*pVal2 & 0xC0)!=0x80)
  1262. return false;
  1263. pVal2++;i++;
  1264. if ((*pVal2 & 0xC0)!=0x80)
  1265. return false;
  1266. bUTF8 = true;
  1267. }
  1268. pVal2++;
  1269. }
  1270. if (bUTF8)
  1271. return true;
  1272. return false;
  1273. }
  1274. void CSciEdit::SetAStyle(int style, COLORREF fore, COLORREF back, int size, const char *face)
  1275. {
  1276. Call(SCI_STYLESETFORE, style, fore);
  1277. Call(SCI_STYLESETBACK, style, back);
  1278. if (size >= 1)
  1279. Call(SCI_STYLESETSIZE, style, size);
  1280. if (face)
  1281. Call(SCI_STYLESETFONT, style, reinterpret_cast<LPARAM>(face));
  1282. }
  1283. void CSciEdit::SetUDiffStyle()
  1284. {
  1285. SetAStyle(STYLE_DEFAULT, ::GetSysColor(COLOR_WINDOWTEXT), ::GetSysColor(COLOR_WINDOW),
  1286. // Reusing TortoiseBlame's setting which already have an user friendly
  1287. // pane in TortoiseSVN's Settings dialog, while there is no such
  1288. // pane for TortoiseUDiff.
  1289. CRegStdDWORD(_T("Software\\TortoiseGit\\BlameFontSize"), 10),
  1290. WideToMultibyte(CRegStdString(_T("Software\\TortoiseGit\\BlameFontName"), _T("Courier New"))).c_str());
  1291. Call(SCI_SETTABWIDTH, 4);
  1292. Call(SCI_SETREADONLY, TRUE);
  1293. //LRESULT pix = Call(SCI_TEXTWIDTH, STYLE_LINENUMBER, (LPARAM)"_99999");
  1294. //Call(SCI_SETMARGINWIDTHN, 0, pix);
  1295. //Call(SCI_SETMARGINWIDTHN, 1);
  1296. //Call(SCI_SETMARGINWIDTHN, 2);
  1297. //Set the default windows colors for edit controls
  1298. Call(SCI_STYLESETFORE, STYLE_DEFAULT, ::GetSysColor(COLOR_WINDOWTEXT));
  1299. Call(SCI_STYLESETBACK, STYLE_DEFAULT, ::GetSysColor(COLOR_WINDOW));
  1300. Call(SCI_SETSELFORE, TRUE, ::GetSysColor(COLOR_HIGHLIGHTTEXT));
  1301. Call(SCI_SETSELBACK, TRUE, ::GetSysColor(COLOR_HIGHLIGHT));
  1302. Call(SCI_SETCARETFORE, ::GetSysColor(COLOR_WINDOWTEXT));
  1303. //SendEditor(SCI_SETREADONLY, FALSE);
  1304. Call(SCI_CLEARALL);
  1305. Call(EM_EMPTYUNDOBUFFER);
  1306. Call(SCI_SETSAVEPOINT);
  1307. Call(SCI_CANCEL);
  1308. Call(SCI_SETUNDOCOLLECTION, 0);
  1309. Call(SCI_SETUNDOCOLLECTION, 1);
  1310. Call(SCI_SETWRAPMODE,SC_WRAP_NONE);
  1311. //::SetFocus(m_hWndEdit);
  1312. Call(EM_EMPTYUNDOBUFFER);
  1313. Call(SCI_SETSAVEPOINT);
  1314. Call(SCI_GOTOPOS, 0);
  1315. Call(SCI_CLEARDOCUMENTSTYLE, 0, 0);
  1316. Call(SCI_SETSTYLEBITS, 5, 0);
  1317. //SetAStyle(SCE_DIFF_DEFAULT, RGB(0, 0, 0));
  1318. SetAStyle(SCE_DIFF_COMMAND, RGB(0x0A, 0x24, 0x36));
  1319. SetAStyle(SCE_DIFF_POSITION, RGB(0xFF, 0, 0));
  1320. SetAStyle(SCE_DIFF_HEADER, RGB(0x80, 0, 0), RGB(0xFF, 0xFF, 0x80));
  1321. SetAStyle(SCE_DIFF_COMMENT, RGB(0, 0x80, 0));
  1322. Call(SCI_STYLESETBOLD, SCE_DIFF_COMMENT, TRUE);
  1323. SetAStyle(SCE_DIFF_DELETED, ::GetSysColor(COLOR_WINDOWTEXT), RGB(0xFF, 0x80, 0x80));
  1324. SetAStyle(SCE_DIFF_ADDED, ::GetSysColor(COLOR_WINDOWTEXT), RGB(0x80, 0xFF, 0x80));
  1325. Call(SCI_SETLEXER, SCLEX_DIFF);
  1326. Call(SCI_SETKEYWORDS, 0, (LPARAM)"revision");
  1327. Call(SCI_COLOURISE, 0, -1);
  1328. }
  1329. int CSciEdit::LoadFromFile(CString &filename)
  1330. {
  1331. FILE *fp = NULL;
  1332. _tfopen_s(&fp, filename, _T("rb"));
  1333. if (fp)
  1334. {
  1335. //SetTitle();
  1336. char data[4096];
  1337. size_t lenFile = fread(data, 1, sizeof(data), fp);
  1338. bool bUTF8 = IsUTF8(data, lenFile);
  1339. while (lenFile > 0)
  1340. {
  1341. Call(SCI_ADDTEXT, lenFile,
  1342. reinterpret_cast<LPARAM>(static_cast<char *>(data)));
  1343. lenFile = fread(data, 1, sizeof(data), fp);
  1344. }
  1345. fclose(fp);
  1346. Call(SCI_SETCODEPAGE, bUTF8 ? SC_CP_UTF8 : GetACP());
  1347. return 0;
  1348. }
  1349. else
  1350. return -1;
  1351. }