PageRenderTime 61ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/src/Utils/MiscUI/SciEdit.cpp

https://gitlab.com/JeevRobinson/tortoisegit
C++ | 1686 lines | 1457 code | 118 blank | 111 comment | 349 complexity | b62df99924817b2545711ab0b3fe7b68 MD5 | raw file
Possible License(s): GPL-3.0, LGPL-3.0, MPL-2.0-no-copyleft-exception, GPL-2.0, LGPL-2.0

Large files files are truncated, but you can click here to view the full file

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

Large files files are truncated, but you can click here to view the full file